mirror of
https://github.com/mod-playerbots/mod-playerbots.git
synced 2026-01-13 00:58:33 +00:00
Feat. Add Fishing action and fish with master. (#1433)
### Update :Thank you to @notOrrytrout from prompting me to work on this. Its been a huge learning experience. With @notOrrytrout I started working on enabling bot fishing with master, but also on their own. The first commit didnt crash, showing that it was possible to have a bot cast when master does. Currently it compiles but crashes when you try to fish with a bot in the group, whether the bot has fishing or not. It makes me think that the check in FishingValues is broken somehow, but I cant figure out how. --------- Co-authored-by: bash <31279994+hermensbas@users.noreply.github.com>
This commit is contained in:
@@ -25,6 +25,7 @@
|
|||||||
# CHEATS
|
# CHEATS
|
||||||
# SPELLS
|
# SPELLS
|
||||||
# FLIGHTPATH
|
# FLIGHTPATH
|
||||||
|
# PROFESSIONS
|
||||||
# RANDOMBOT-SPECIFIC SETTINGS
|
# RANDOMBOT-SPECIFIC SETTINGS
|
||||||
# GENERAL
|
# GENERAL
|
||||||
# LEVELS
|
# LEVELS
|
||||||
@@ -579,6 +580,24 @@ AiPlayerbot.BotTaxiGapJitterMs = 100
|
|||||||
#
|
#
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
|
|
||||||
|
####################################################################################################
|
||||||
|
# PROFESSIONS
|
||||||
|
# Random bots currently do not get professions.
|
||||||
|
#
|
||||||
|
|
||||||
|
# EnableFishingWithMaster automatically adds the 'master fishing' strategy to bots that can fish that can.
|
||||||
|
# Default: 1 (Enabled)
|
||||||
|
AiPlayerbot.EnableFishingWithMaster = 1
|
||||||
|
#FishingDistance sets how far a bot without a master will search for water, while FishingDistanceFromMaster limits it to a closer range, and overrides the following distance to the same value. EndFishingWithMaster sets the distance from water a bot needs to have to automatically drop the 'master fishing' strategy.
|
||||||
|
AiPlayerbot.FishingDistanceFromMaster = 10.0
|
||||||
|
AiPlayerbot.FishingDistance = 40.0
|
||||||
|
AiPlayerbot.EndFishingWithMaster = 30.0
|
||||||
|
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
####################################################################################################
|
||||||
|
|
||||||
#######################################
|
#######################################
|
||||||
# #
|
# #
|
||||||
# RANDOMBOT-SPECIFIC SETTINGS #
|
# RANDOMBOT-SPECIFIC SETTINGS #
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
DELETE FROM ai_playerbot_texts WHERE name IN ('no_fishing_pole_error');
|
||||||
|
DELETE FROM ai_playerbot_texts_chance WHERE name IN ('no_fishing_pole_error');
|
||||||
|
|
||||||
|
INSERT INTO ai_playerbot_texts (id, name, text, say_type, reply_type, text_loc1, text_loc2, text_loc3, text_loc4, text_loc5, text_loc6, text_loc7, text_loc8) VALUES
|
||||||
|
(1736, 'no_fishing_pole_error', "I don't have a Fishing Pole", 0, 0,
|
||||||
|
"낚싯대가 없습니다",
|
||||||
|
"Je n’ai pas de canne à pêche",
|
||||||
|
"Ich habe keine Angelrute",
|
||||||
|
"我沒有釣魚竿",
|
||||||
|
"我没有钓鱼竿",
|
||||||
|
"No tengo una caña de pescar",
|
||||||
|
"No tengo una caña de pescar",
|
||||||
|
"У меня нет удочки");
|
||||||
|
|
||||||
|
INSERT INTO ai_playerbot_texts_chance (name, probability) VALUES ('no_fishing_pole_error', 100);
|
||||||
@@ -222,6 +222,11 @@ bool PlayerbotAIConfig::Initialize()
|
|||||||
|
|
||||||
EnableICCBuffs = sConfigMgr->GetOption<bool>("AiPlayerbot.EnableICCBuffs", true);
|
EnableICCBuffs = sConfigMgr->GetOption<bool>("AiPlayerbot.EnableICCBuffs", true);
|
||||||
|
|
||||||
|
//////////////////////////// Professions
|
||||||
|
fishingDistanceFromMaster = sConfigMgr->GetOption<float>("AiPlayerbot.FishingDistanceFromMaster", 10.0f);
|
||||||
|
endFishingWithMaster = sConfigMgr->GetOption<float>("AiPlayerbot.EndFishingWithMaster", 30.0f);
|
||||||
|
fishingDistance = sConfigMgr->GetOption<float>("AiPlayerbot.FishingDistance", 40.0f);
|
||||||
|
enableFishingWithMaster = sConfigMgr->GetOption<bool>("AiPlayerbot.EnableFishingWithMaster", true);
|
||||||
//////////////////////////// CHAT
|
//////////////////////////// CHAT
|
||||||
enableBroadcasts = sConfigMgr->GetOption<bool>("AiPlayerbot.EnableBroadcasts", true);
|
enableBroadcasts = sConfigMgr->GetOption<bool>("AiPlayerbot.EnableBroadcasts", true);
|
||||||
randomBotTalk = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotTalk", false);
|
randomBotTalk = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotTalk", false);
|
||||||
|
|||||||
@@ -145,6 +145,10 @@ public:
|
|||||||
// Cooldown (seconds) between reagent-missing RP warnings, per bot & per buff. Default: 30
|
// Cooldown (seconds) between reagent-missing RP warnings, per bot & per buff. Default: 30
|
||||||
int32 rpWarningCooldown;
|
int32 rpWarningCooldown;
|
||||||
|
|
||||||
|
// Professions
|
||||||
|
bool enableFishingWithMaster;
|
||||||
|
float fishingDistanceFromMaster, fishingDistance, endFishingWithMaster;
|
||||||
|
|
||||||
// chat
|
// chat
|
||||||
bool randomBotTalk;
|
bool randomBotTalk;
|
||||||
bool randomBotEmote;
|
bool randomBotEmote;
|
||||||
|
|||||||
@@ -218,6 +218,11 @@ bool WorldPosition::isUnderWater()
|
|||||||
: false;
|
: false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
bool WorldPosition::IsValid()
|
||||||
|
{
|
||||||
|
return !(GetMapId() == MAPID_INVALID && GetPositionX() == 0 && GetPositionY() == 0 && GetPositionZ() == 0);
|
||||||
|
}
|
||||||
|
|
||||||
WorldPosition WorldPosition::relPoint(WorldPosition* center)
|
WorldPosition WorldPosition::relPoint(WorldPosition* center)
|
||||||
{
|
{
|
||||||
return WorldPosition(GetMapId(), GetPositionX() - center->GetPositionX(), GetPositionY() - center->GetPositionY(),
|
return WorldPosition(GetMapId(), GetPositionX() - center->GetPositionX(), GetPositionY() - center->GetPositionY(),
|
||||||
|
|||||||
@@ -141,6 +141,7 @@ public:
|
|||||||
bool isOverworld();
|
bool isOverworld();
|
||||||
bool isInWater();
|
bool isInWater();
|
||||||
bool isUnderWater();
|
bool isUnderWater();
|
||||||
|
bool IsValid();
|
||||||
|
|
||||||
WorldPosition relPoint(WorldPosition* center);
|
WorldPosition relPoint(WorldPosition* center);
|
||||||
WorldPosition offset(WorldPosition* center);
|
WorldPosition offset(WorldPosition* center);
|
||||||
|
|||||||
@@ -120,6 +120,8 @@ public:
|
|||||||
creators["formation"] = &StrategyContext::combat_formation;
|
creators["formation"] = &StrategyContext::combat_formation;
|
||||||
creators["move from group"] = &StrategyContext::move_from_group;
|
creators["move from group"] = &StrategyContext::move_from_group;
|
||||||
creators["worldbuff"] = &StrategyContext::world_buff;
|
creators["worldbuff"] = &StrategyContext::world_buff;
|
||||||
|
creators["use bobber"] = &StrategyContext::bobber_strategy;
|
||||||
|
creators["master fishing"] = &StrategyContext::master_fishing;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -188,6 +190,8 @@ private:
|
|||||||
static Strategy* combat_formation(PlayerbotAI* botAI) { return new CombatFormationStrategy(botAI); }
|
static Strategy* combat_formation(PlayerbotAI* botAI) { return new CombatFormationStrategy(botAI); }
|
||||||
static Strategy* move_from_group(PlayerbotAI* botAI) { return new MoveFromGroupStrategy(botAI); }
|
static Strategy* move_from_group(PlayerbotAI* botAI) { return new MoveFromGroupStrategy(botAI); }
|
||||||
static Strategy* world_buff(PlayerbotAI* botAI) { return new WorldBuffStrategy(botAI); }
|
static Strategy* world_buff(PlayerbotAI* botAI) { return new WorldBuffStrategy(botAI); }
|
||||||
|
static Strategy* bobber_strategy(PlayerbotAI* botAI) { return new UseBobberStrategy(botAI); }
|
||||||
|
static Strategy* master_fishing(PlayerbotAI* botAI) { return new MasterFishingStrategy(botAI); }
|
||||||
};
|
};
|
||||||
|
|
||||||
class MovementStrategyContext : public NamedObjectContext<Strategy>
|
class MovementStrategyContext : public NamedObjectContext<Strategy>
|
||||||
|
|||||||
@@ -64,6 +64,7 @@
|
|||||||
#include "WorldBuffAction.h"
|
#include "WorldBuffAction.h"
|
||||||
#include "XpGainAction.h"
|
#include "XpGainAction.h"
|
||||||
#include "NewRpgAction.h"
|
#include "NewRpgAction.h"
|
||||||
|
#include "FishingAction.h"
|
||||||
#include "CancelChannelAction.h"
|
#include "CancelChannelAction.h"
|
||||||
|
|
||||||
class PlayerbotAI;
|
class PlayerbotAI;
|
||||||
@@ -191,6 +192,11 @@ public:
|
|||||||
creators["buy tabard"] = &ActionContext::buy_tabard;
|
creators["buy tabard"] = &ActionContext::buy_tabard;
|
||||||
creators["guild manage nearby"] = &ActionContext::guild_manage_nearby;
|
creators["guild manage nearby"] = &ActionContext::guild_manage_nearby;
|
||||||
creators["clean quest log"] = &ActionContext::clean_quest_log;
|
creators["clean quest log"] = &ActionContext::clean_quest_log;
|
||||||
|
creators["move near water"] = &ActionContext::move_near_water;
|
||||||
|
creators["go fishing"] = &ActionContext::go_fishing;
|
||||||
|
creators["use fishing bobber"] = &ActionContext::use_fishing_bobber;
|
||||||
|
creators["end master fishing"] = &ActionContext::end_master_fishing;
|
||||||
|
creators["remove bobber strategy"] = &ActionContext::remove_bobber_strategy;
|
||||||
creators["roll"] = &ActionContext::roll_action;
|
creators["roll"] = &ActionContext::roll_action;
|
||||||
creators["cancel channel"] = &ActionContext::cancel_channel;
|
creators["cancel channel"] = &ActionContext::cancel_channel;
|
||||||
|
|
||||||
@@ -380,6 +386,11 @@ private:
|
|||||||
static Action* buy_tabard(PlayerbotAI* botAI) { return new BuyTabardAction(botAI); }
|
static Action* buy_tabard(PlayerbotAI* botAI) { return new BuyTabardAction(botAI); }
|
||||||
static Action* guild_manage_nearby(PlayerbotAI* botAI) { return new GuildManageNearbyAction(botAI); }
|
static Action* guild_manage_nearby(PlayerbotAI* botAI) { return new GuildManageNearbyAction(botAI); }
|
||||||
static Action* clean_quest_log(PlayerbotAI* botAI) { return new CleanQuestLogAction(botAI); }
|
static Action* clean_quest_log(PlayerbotAI* botAI) { return new CleanQuestLogAction(botAI); }
|
||||||
|
static Action* move_near_water(PlayerbotAI* botAI) { return new MoveNearWaterAction(botAI); }
|
||||||
|
static Action* go_fishing(PlayerbotAI* botAI) { return new FishingAction(botAI);}
|
||||||
|
static Action* use_fishing_bobber(PlayerbotAI* botAI) { return new UseBobberAction(botAI);}
|
||||||
|
static Action* end_master_fishing(PlayerbotAI* botAI) { return new EndMasterFishingAction(botAI); }
|
||||||
|
static Action* remove_bobber_strategy(PlayerbotAI* botAI) { return new RemoveBobberStrategyAction(botAI); }
|
||||||
static Action* roll_action(PlayerbotAI* botAI) { return new RollAction(botAI); }
|
static Action* roll_action(PlayerbotAI* botAI) { return new RollAction(botAI); }
|
||||||
|
|
||||||
// BG Tactics
|
// BG Tactics
|
||||||
|
|||||||
@@ -344,6 +344,27 @@ bool EquipUpgradesAction::Execute(Event event)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (event.GetSource() == "item push result")
|
||||||
|
{
|
||||||
|
WorldPacket p(event.getPacket());
|
||||||
|
p.rpos(0);
|
||||||
|
ObjectGuid playerGuid;
|
||||||
|
uint32 received, created, sendChatMessage, itemSlot, itemId;
|
||||||
|
uint8 bagSlot;
|
||||||
|
|
||||||
|
p >> playerGuid;
|
||||||
|
p >> received;
|
||||||
|
p >> created;
|
||||||
|
p >> sendChatMessage;
|
||||||
|
p >> bagSlot;
|
||||||
|
p >> itemSlot;
|
||||||
|
p >> itemId;
|
||||||
|
|
||||||
|
ItemTemplate const* item = sObjectMgr->GetItemTemplate(itemId);
|
||||||
|
if (item->Class == ITEM_CLASS_TRADE_GOODS && item->SubClass == ITEM_SUBCLASS_MEAT)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
CollectItemsVisitor visitor;
|
CollectItemsVisitor visitor;
|
||||||
IterateItems(&visitor, ITERATE_ITEMS_IN_BAGS);
|
IterateItems(&visitor, ITERATE_ITEMS_IN_BAGS);
|
||||||
|
|
||||||
|
|||||||
492
src/strategy/actions/FishingAction.cpp
Normal file
492
src/strategy/actions/FishingAction.cpp
Normal file
@@ -0,0 +1,492 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||||
|
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "FishingAction.h"
|
||||||
|
#include "FishValues.h"
|
||||||
|
#include "Event.h"
|
||||||
|
|
||||||
|
#include "GridNotifiers.h"
|
||||||
|
#include "GridNotifiersImpl.h"
|
||||||
|
#include "ItemPackets.h"
|
||||||
|
#include "LastMovementValue.h"
|
||||||
|
#include "Map.h"
|
||||||
|
#include "MovementActions.h"
|
||||||
|
#include "Object.h"
|
||||||
|
#include "PlayerbotAI.h"
|
||||||
|
#include "PlayerbotTextMgr.h"
|
||||||
|
#include "Playerbots.h"
|
||||||
|
#include "Position.h"
|
||||||
|
|
||||||
|
uint32 const FISHING_SPELL = 7620;
|
||||||
|
uint32 const FISHING_POLE = 6256;
|
||||||
|
uint32 const FISHING_BOBBER = 35591;
|
||||||
|
float const MIN_DISTANCE_TO_WATER = 10.0f; // Minimum spell distance
|
||||||
|
float const MAX_DISTANCE_TO_WATER = 20.0f; // Maximum spell distance
|
||||||
|
float const HEIGHT_ABOVE_WATER_TOLERANCE = 1.0f; // Can stand in up to 1 unit of water and still fish.
|
||||||
|
float const SEARCH_INCREMENT = 2.5f;
|
||||||
|
float const HEIGHT_SEARCH_BUFFER = 10.0f; // Height buffer to prevent potentially missing the model the bot is standing on.
|
||||||
|
float const SEARCH_LAND_BUFFER = 0.5f;
|
||||||
|
uint32 const FISHING_LOCATION_TIMEOUT = 180000; //Three minutes
|
||||||
|
|
||||||
|
static bool IsFishingPole(Item* const item)
|
||||||
|
{
|
||||||
|
if (!item)
|
||||||
|
return false;
|
||||||
|
const ItemTemplate* proto = item->GetTemplate();
|
||||||
|
return proto && proto->Class == ITEM_CLASS_WEAPON &&
|
||||||
|
proto->SubClass == ITEM_SUBCLASS_WEAPON_FISHING_POLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
float HasFishableWaterOrLand(float x, float y, float z, Map* map, uint32 phaseMask, bool checkForLand=false)
|
||||||
|
{
|
||||||
|
if (!map)
|
||||||
|
return INVALID_HEIGHT;
|
||||||
|
|
||||||
|
LiquidData const& liq = map->GetLiquidData(phaseMask, x, y, z+HEIGHT_ABOVE_WATER_TOLERANCE, DEFAULT_COLLISION_HEIGHT, MAP_ALL_LIQUIDS);
|
||||||
|
float ground = map->GetHeight(phaseMask, x, y, z + HEIGHT_SEARCH_BUFFER, true);
|
||||||
|
if (liq.Entry == MAP_LIQUID_TYPE_NO_WATER)
|
||||||
|
{
|
||||||
|
if (checkForLand)
|
||||||
|
return ground;
|
||||||
|
return INVALID_HEIGHT;
|
||||||
|
}
|
||||||
|
if (checkForLand)
|
||||||
|
{
|
||||||
|
if (ground > liq.Level - HEIGHT_ABOVE_WATER_TOLERANCE)
|
||||||
|
return ground;
|
||||||
|
return INVALID_HEIGHT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (liq.Level + HEIGHT_ABOVE_WATER_TOLERANCE > ground)
|
||||||
|
{
|
||||||
|
if (abs(liq.DepthLevel) < 0.5f) // too shallow to fish in.
|
||||||
|
return INVALID_HEIGHT;
|
||||||
|
return liq.Level;
|
||||||
|
}
|
||||||
|
return INVALID_HEIGHT;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HasLosToWater(Player* bot, float wx, float wy, float waterZ)
|
||||||
|
{
|
||||||
|
float z = bot->GetCollisionHeight() + bot->GetPositionZ();
|
||||||
|
return bot->GetMap()->isInLineOfSight(
|
||||||
|
bot->GetPositionX(), bot->GetPositionY(), z,
|
||||||
|
wx, wy, waterZ,
|
||||||
|
bot->GetPhaseMask(),
|
||||||
|
LINEOFSIGHT_ALL_CHECKS,
|
||||||
|
VMAP::ModelIgnoreFlags::Nothing);
|
||||||
|
}
|
||||||
|
|
||||||
|
WorldPosition FindLandFromPosition(PlayerbotAI* botAI, float startDistance, float endDistance, float increment, float orientation, WorldPosition targetPos, float fishingSearchWindow, bool checkLOS = true)
|
||||||
|
{
|
||||||
|
Player* bot = botAI->GetBot();
|
||||||
|
Map* map = bot->GetMap();
|
||||||
|
uint32 phaseMask = bot->GetPhaseMask();
|
||||||
|
Player* master = botAI->GetMaster();
|
||||||
|
|
||||||
|
float targetX = targetPos.GetPositionX();
|
||||||
|
float targetY = targetPos.GetPositionY();
|
||||||
|
float targetZ = targetPos.GetPositionZ();
|
||||||
|
|
||||||
|
for (float dist = startDistance; dist <= endDistance; dist += increment)
|
||||||
|
{
|
||||||
|
//step backwards from position to bot to find edge of shore.
|
||||||
|
float checkX = targetX - dist * cos(orientation);
|
||||||
|
float checkY = targetY - dist * sin(orientation);
|
||||||
|
|
||||||
|
float groundZ = map->GetHeight(phaseMask, checkX, checkY, targetZ + HEIGHT_SEARCH_BUFFER, true);
|
||||||
|
|
||||||
|
if (groundZ == INVALID_HEIGHT)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
LiquidData const& liq = map->GetLiquidData(phaseMask, checkX, checkY, targetZ, DEFAULT_COLLISION_HEIGHT, MAP_ALL_LIQUIDS);
|
||||||
|
if (liq.Entry == MAP_LIQUID_TYPE_NO_WATER || groundZ > liq.DepthLevel + HEIGHT_ABOVE_WATER_TOLERANCE)
|
||||||
|
{
|
||||||
|
if (checkLOS)
|
||||||
|
{
|
||||||
|
bool hasLOS = map->isInLineOfSight(checkX, checkY, groundZ, targetX, targetY, targetZ, phaseMask, LINEOFSIGHT_ALL_CHECKS, VMAP::ModelIgnoreFlags::Nothing);
|
||||||
|
if (!hasLOS)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Add a distance check for the position to prevent the bot from moving out of range to the master.
|
||||||
|
if (master && botAI->HasStrategy("follow", BOT_STATE_NON_COMBAT) && master->GetDistance(checkX, checkY, groundZ) > fishingSearchWindow - SEARCH_LAND_BUFFER)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
return WorldPosition(bot->GetMapId(), checkX, checkY, groundZ);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return WorldPosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
WorldPosition FindLandRadialFromPosition (PlayerbotAI* botAI, WorldPosition targetPos, float startDistance, float endDistance, float increment, float fishingSearchWindow, int angles = 16)
|
||||||
|
{
|
||||||
|
Player* bot = botAI->GetBot();
|
||||||
|
const int numDirections = angles;
|
||||||
|
std::vector<WorldPosition> boundaryPoints;
|
||||||
|
Player* master = botAI->GetMaster();
|
||||||
|
if (!master)
|
||||||
|
return WorldPosition();
|
||||||
|
|
||||||
|
Map* map = bot->GetMap();
|
||||||
|
uint32 phaseMask = bot->GetPhaseMask();
|
||||||
|
|
||||||
|
float targetX = targetPos.GetPositionX();
|
||||||
|
float targetY = targetPos.GetPositionY();
|
||||||
|
float targetZ = targetPos.GetPositionZ();
|
||||||
|
|
||||||
|
for (float dist = startDistance; dist <= endDistance; dist += increment)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < numDirections; ++i)
|
||||||
|
{
|
||||||
|
float angle = (2.0f * M_PI * i) / numDirections;
|
||||||
|
float checkX = targetX - cos(angle) * dist;
|
||||||
|
float checkY = targetY - sin(angle) * dist;
|
||||||
|
|
||||||
|
float groundZ = HasFishableWaterOrLand(checkX, checkY, targetZ, map, phaseMask, true);
|
||||||
|
|
||||||
|
if (groundZ == INVALID_HEIGHT)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (map->isInLineOfSight(checkX, checkY, groundZ, targetX, targetY, targetZ, phaseMask, LINEOFSIGHT_ALL_CHECKS, VMAP::ModelIgnoreFlags::Nothing) && master->GetDistance(checkX, checkY, groundZ) > fishingSearchWindow - SEARCH_LAND_BUFFER)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
boundaryPoints.emplace_back(WorldPosition(bot->GetMapId(), checkX, checkY, groundZ));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!boundaryPoints.empty())
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (boundaryPoints.empty())
|
||||||
|
return WorldPosition();
|
||||||
|
|
||||||
|
if (boundaryPoints.size() == 1)
|
||||||
|
return boundaryPoints[0];
|
||||||
|
|
||||||
|
float minDistance = FLT_MAX;
|
||||||
|
WorldLocation closestPoint = WorldPosition();
|
||||||
|
for (auto const& pos : boundaryPoints)
|
||||||
|
{
|
||||||
|
float distance = bot->GetExactDist2d(&pos);
|
||||||
|
if (distance < minDistance)
|
||||||
|
{
|
||||||
|
minDistance = distance;
|
||||||
|
closestPoint = pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return closestPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
WorldPosition FindWaterRadial(Player* bot, float x, float y, float z, Map* map, uint32 phaseMask, float minDistance, float maxDistance, float increment, bool checkLOS, int numDirections)
|
||||||
|
{
|
||||||
|
std::vector<WorldPosition> boundaryPoints;
|
||||||
|
|
||||||
|
float dist = minDistance;
|
||||||
|
while (dist <= maxDistance)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < numDirections; ++i)
|
||||||
|
{
|
||||||
|
float angle = (2.0f * M_PI * i) / numDirections;
|
||||||
|
float checkX = x + cos(angle) * dist;
|
||||||
|
float checkY = y + sin(angle) * dist;
|
||||||
|
|
||||||
|
float waterZ = HasFishableWaterOrLand(checkX, checkY, z, map, phaseMask);
|
||||||
|
|
||||||
|
if (waterZ == INVALID_HEIGHT)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (checkLOS && !HasLosToWater(bot, checkX, checkY, waterZ))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
boundaryPoints.emplace_back(WorldPosition(bot->GetMapId(), checkX, checkY, waterZ));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!boundaryPoints.empty())
|
||||||
|
break;
|
||||||
|
|
||||||
|
dist += increment;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (boundaryPoints.empty())
|
||||||
|
return WorldPosition();
|
||||||
|
|
||||||
|
if (boundaryPoints.size() == 1)
|
||||||
|
return boundaryPoints[0];
|
||||||
|
// return the central point in the identified positions in to try to be perpendicular to the shore.
|
||||||
|
return boundaryPoints[boundaryPoints.size() / 2];
|
||||||
|
}
|
||||||
|
|
||||||
|
WorldPosition FindFishingHole(PlayerbotAI* botAI)
|
||||||
|
{
|
||||||
|
Player* player = botAI->GetBot();
|
||||||
|
GuidVector gos = PAI_VALUE(GuidVector, "nearest game objects no los");
|
||||||
|
GameObject* nearestFishingHole = nullptr;
|
||||||
|
float minDist = std::numeric_limits<float>::max();
|
||||||
|
for (auto const& guid : gos)
|
||||||
|
{
|
||||||
|
GameObject* go = botAI->GetGameObject(guid);
|
||||||
|
if (!go)
|
||||||
|
continue;
|
||||||
|
if (go->GetGoType() == GAMEOBJECT_TYPE_FISHINGHOLE)
|
||||||
|
{
|
||||||
|
float dist = player->GetDistance2d(go);
|
||||||
|
if (dist < minDist)
|
||||||
|
{
|
||||||
|
minDist = dist;
|
||||||
|
nearestFishingHole = go;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (nearestFishingHole)
|
||||||
|
return WorldPosition(nearestFishingHole->GetMapId(), nearestFishingHole->GetPositionX(), nearestFishingHole->GetPositionY(), nearestFishingHole->GetPositionZ());
|
||||||
|
|
||||||
|
return WorldPosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MoveNearWaterAction::Execute(Event event)
|
||||||
|
{
|
||||||
|
WorldPosition landSpot = AI_VALUE(WorldPosition, "fishing spot");
|
||||||
|
if (landSpot.IsValid())
|
||||||
|
return MoveTo(landSpot.GetMapId(), landSpot.GetPositionX(), landSpot.GetPositionY(), landSpot.GetPositionZ());
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MoveNearWaterAction::isUseful()
|
||||||
|
{
|
||||||
|
if (!AI_VALUE(bool, "can fish"))
|
||||||
|
return false;
|
||||||
|
FishingSpotValue* fishingSpotValueObject = (FishingSpotValue*)context->GetValue<WorldPosition>("fishing spot");
|
||||||
|
WorldPosition pos = fishingSpotValueObject->Get();
|
||||||
|
return !pos.IsValid() || fishingSpotValueObject->IsStale(FISHING_LOCATION_TIMEOUT) || pos != bot->GetPosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MoveNearWaterAction::isPossible()
|
||||||
|
{
|
||||||
|
Player* master = botAI->GetMaster();
|
||||||
|
float fishingSearchWindow;
|
||||||
|
|
||||||
|
if (master)
|
||||||
|
fishingSearchWindow = sPlayerbotAIConfig->fishingDistanceFromMaster;
|
||||||
|
else
|
||||||
|
fishingSearchWindow = sPlayerbotAIConfig->fishingDistance;
|
||||||
|
|
||||||
|
WorldPosition fishingHole = FindFishingHole(botAI);
|
||||||
|
|
||||||
|
if (fishingHole.IsValid())
|
||||||
|
{
|
||||||
|
float distance = bot->GetExactDist2d(&fishingHole);
|
||||||
|
bool hasLOS = bot->IsWithinLOS(fishingHole.GetPositionX(), fishingHole.GetPositionY(), fishingHole.GetPositionZ());
|
||||||
|
// Water spot is in range, and we have LOS to it. Set bot position to fishing spot and do not move
|
||||||
|
if (distance >= MIN_DISTANCE_TO_WATER &&
|
||||||
|
distance <= MAX_DISTANCE_TO_WATER && hasLOS)
|
||||||
|
{
|
||||||
|
SET_AI_VALUE(WorldPosition, "fishing spot", WorldPosition(WorldPosition(bot->GetMapId(), bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ())));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Water spot is out of range, lets look for a spot to move to for the fishing hole.
|
||||||
|
if (distance > MAX_DISTANCE_TO_WATER || distance < MIN_DISTANCE_TO_WATER)
|
||||||
|
{
|
||||||
|
float angle = bot->GetAngle(fishingHole.GetPositionX(), fishingHole.GetPositionY());
|
||||||
|
WorldPosition landSpot = FindLandRadialFromPosition(botAI, fishingHole, MIN_DISTANCE_TO_WATER, MAX_DISTANCE_TO_WATER, SEARCH_INCREMENT, fishingSearchWindow, 32);
|
||||||
|
if (landSpot.IsValid())
|
||||||
|
{
|
||||||
|
SET_AI_VALUE(WorldPosition, "fishing spot", landSpot);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Lets find some water where we can fish.
|
||||||
|
WorldPosition water = FindWaterRadial(
|
||||||
|
bot, bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(),
|
||||||
|
bot->GetMap(), bot->GetPhaseMask(),
|
||||||
|
MIN_DISTANCE_TO_WATER,
|
||||||
|
fishingSearchWindow + MAX_DISTANCE_TO_WATER,
|
||||||
|
SEARCH_INCREMENT, false);
|
||||||
|
|
||||||
|
if (!water.IsValid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
bool hasLOS = bot->IsWithinLOS(water.GetPositionX(), water.GetPositionY(), water.GetPositionZ());
|
||||||
|
float angle = bot->GetAngle(water.GetPositionX(), water.GetPositionY());
|
||||||
|
WorldPosition landSpot =
|
||||||
|
FindLandFromPosition(botAI, 0.0f, MAX_DISTANCE_TO_WATER, 1.0f, angle, water, fishingSearchWindow, false);
|
||||||
|
|
||||||
|
if (landSpot.IsValid())
|
||||||
|
{
|
||||||
|
SET_AI_VALUE(WorldPosition, "fishing spot", landSpot);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EquipFishingPoleAction::Execute(Event event)
|
||||||
|
{
|
||||||
|
if (!_pole)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
WorldPacket eqPacket(CMSG_AUTOEQUIP_ITEM_SLOT, 2);
|
||||||
|
eqPacket << _pole->GetGUID() << uint8(EQUIPMENT_SLOT_MAINHAND);
|
||||||
|
WorldPackets::Item::AutoEquipItemSlot nicePacket(std::move(eqPacket));
|
||||||
|
nicePacket.Read();
|
||||||
|
bot->GetSession()->HandleAutoEquipItemSlotOpcode(nicePacket);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EquipFishingPoleAction::isUseful()
|
||||||
|
{
|
||||||
|
Item* mainHand = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND);
|
||||||
|
if (IsFishingPole(mainHand))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (uint8 slot = INVENTORY_SLOT_ITEM_START; slot < INVENTORY_SLOT_ITEM_END; ++slot)
|
||||||
|
{
|
||||||
|
if (Item* item = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot))
|
||||||
|
{
|
||||||
|
if (IsFishingPole(item))
|
||||||
|
{
|
||||||
|
_pole = item;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint8 bag = INVENTORY_SLOT_BAG_START; bag < INVENTORY_SLOT_BAG_END; ++bag)
|
||||||
|
{
|
||||||
|
if (Bag* pBag = bot->GetBagByPos(bag))
|
||||||
|
{
|
||||||
|
for (uint32 j = 0; j < pBag->GetBagSize(); ++j)
|
||||||
|
{
|
||||||
|
if (Item* item = pBag->GetItemByPos(j))
|
||||||
|
{
|
||||||
|
if (IsFishingPole(item))
|
||||||
|
{
|
||||||
|
_pole = item;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sRandomPlayerbotMgr->IsRandomBot(bot))
|
||||||
|
{
|
||||||
|
bot->StoreNewItemInBestSlots(FISHING_POLE, 1); // Try to get a fishing pole
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Player* master = botAI->GetMaster();
|
||||||
|
if (!master)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::string masterName = master->GetName();
|
||||||
|
std::string text = sPlayerbotTextMgr->GetBotTextOrDefault(
|
||||||
|
"no_fishing_pole_error", "I don't have a Fishing Pole",{});
|
||||||
|
botAI->Whisper(text, masterName);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FishingAction::Execute(Event event)
|
||||||
|
{
|
||||||
|
WorldPosition target = WorldPosition();
|
||||||
|
WorldPosition fishingHole = FindFishingHole(botAI);
|
||||||
|
if (fishingHole.IsValid())
|
||||||
|
{
|
||||||
|
Position pos = fishingHole;
|
||||||
|
float distance = bot->GetExactDist2d(&pos);
|
||||||
|
bool hasLOS = bot->IsWithinLOS(fishingHole.GetPositionX(), fishingHole.GetPositionY(), fishingHole.GetPositionZ());
|
||||||
|
if (distance < MAX_DISTANCE_TO_WATER &&
|
||||||
|
distance > MIN_DISTANCE_TO_WATER && hasLOS)
|
||||||
|
target = fishingHole;
|
||||||
|
}
|
||||||
|
if (!target.IsValid())
|
||||||
|
{
|
||||||
|
target = FindWaterRadial(bot, bot->GetPositionX(), bot->GetPositionY(),
|
||||||
|
bot->GetPositionZ(), bot->GetMap(), bot->GetPhaseMask(),
|
||||||
|
MIN_DISTANCE_TO_WATER, MAX_DISTANCE_TO_WATER, SEARCH_INCREMENT, true, 32);
|
||||||
|
if (!target.IsValid())
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Position pos = target;
|
||||||
|
|
||||||
|
if (!bot->HasInArc(1.0, &pos, 1.0))
|
||||||
|
{
|
||||||
|
float angle = bot->GetAngle(pos.GetPositionX(), pos.GetPositionY());
|
||||||
|
bot->SetOrientation(angle);
|
||||||
|
bot->SendMovementFlagUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
EquipFishingPoleAction equipAction(botAI);
|
||||||
|
if (equipAction.isUseful())
|
||||||
|
return equipAction.Execute(event);
|
||||||
|
|
||||||
|
botAI->CastSpell(FISHING_SPELL, bot);
|
||||||
|
botAI->ChangeStrategy("+use bobber", BOT_STATE_NON_COMBAT);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FishingAction::isUseful()
|
||||||
|
{
|
||||||
|
if (!AI_VALUE(bool, "can fish"))
|
||||||
|
return false;
|
||||||
|
FishingSpotValue* fishingSpotValueObject = (FishingSpotValue*)context->GetValue<WorldPosition>("fishing spot");
|
||||||
|
WorldPosition pos = fishingSpotValueObject->Get();
|
||||||
|
|
||||||
|
return pos.IsValid() && !fishingSpotValueObject->IsStale(FISHING_LOCATION_TIMEOUT) && pos == bot->GetPosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UseBobberAction::isUseful()
|
||||||
|
{
|
||||||
|
return AI_VALUE(bool, "can use fishing bobber");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UseBobberAction::Execute(Event event)
|
||||||
|
{
|
||||||
|
GuidVector gos = AI_VALUE(GuidVector, "nearest game objects no los");
|
||||||
|
for (auto const& guid : gos)
|
||||||
|
{
|
||||||
|
if (GameObject* go = botAI->GetGameObject(guid))
|
||||||
|
{
|
||||||
|
if (go->GetEntry() != FISHING_BOBBER)
|
||||||
|
continue;
|
||||||
|
if (go->GetOwnerGUID() != bot->GetGUID())
|
||||||
|
continue;
|
||||||
|
if (go->getLootState() == GO_READY)
|
||||||
|
{
|
||||||
|
go->Use(bot);
|
||||||
|
botAI->ChangeStrategy("-use bobber", BOT_STATE_NON_COMBAT);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EndMasterFishingAction::Execute(Event event)
|
||||||
|
{
|
||||||
|
botAI->ChangeStrategy("-master fishing", BOT_STATE_NON_COMBAT);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EndMasterFishingAction::isUseful()
|
||||||
|
{
|
||||||
|
FishingSpotValue* fishingSpotValueObject = (FishingSpotValue*)context->GetValue<WorldPosition>("fishing spot");
|
||||||
|
WorldPosition pos = fishingSpotValueObject->Get();
|
||||||
|
if (pos.IsValid() && !fishingSpotValueObject->IsStale(FISHING_LOCATION_TIMEOUT) && pos == bot->GetPosition())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
WorldPosition nearWater = FindWaterRadial(bot, bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(),
|
||||||
|
bot->GetMap(), bot->GetPhaseMask(), MIN_DISTANCE_TO_WATER, sPlayerbotAIConfig->endFishingWithMaster, 10.0f);
|
||||||
|
return !nearWater.IsValid();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RemoveBobberStrategyAction::Execute(Event event)
|
||||||
|
{
|
||||||
|
botAI->ChangeStrategy("-use bobber", BOT_STATE_NON_COMBAT);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
71
src/strategy/actions/FishingAction.h
Normal file
71
src/strategy/actions/FishingAction.h
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||||
|
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _PLAYERBOT_FISHINGACTION_H
|
||||||
|
#define _PLAYERBOT_FISHINGACTION_H
|
||||||
|
|
||||||
|
#include "Action.h"
|
||||||
|
#include "MovementActions.h"
|
||||||
|
#include "Event.h"
|
||||||
|
#include "Playerbots.h"
|
||||||
|
|
||||||
|
extern const uint32 FISHING_SPELL;
|
||||||
|
extern const uint32 FISHING_POLE;
|
||||||
|
extern const uint32 FISHING_BOBBER;
|
||||||
|
|
||||||
|
WorldPosition FindWaterRadial(Player* bot, float x, float y, float z, Map* map, uint32 phaseMask, float minDistance, float maxDistance, float increment, bool checkLOS=false, int numDirections = 16);
|
||||||
|
|
||||||
|
class PlayerbotAI;
|
||||||
|
|
||||||
|
class FishingAction : public Action
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FishingAction(PlayerbotAI* botAI) : Action(botAI, "go fishing"){}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
bool isUseful() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class EquipFishingPoleAction : public Action
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
EquipFishingPoleAction(PlayerbotAI* botAI) : Action(botAI, "equip fishing pole") {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
bool isUseful() override;
|
||||||
|
private:
|
||||||
|
Item* _pole = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MoveNearWaterAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MoveNearWaterAction(PlayerbotAI* botAI): MovementAction(botAI, "move near water") {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
bool isUseful() override;
|
||||||
|
bool isPossible() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class UseBobberAction : public Action
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
UseBobberAction(PlayerbotAI* botAI) : Action(botAI, "use fishing bobber") {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
bool isUseful() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class EndMasterFishingAction : public Action
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
EndMasterFishingAction(PlayerbotAI* botAI) : Action(botAI, "end master fishing") {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
bool isUseful() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class RemoveBobberStrategyAction : public Action
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RemoveBobberStrategyAction(PlayerbotAI* botAI) : Action(botAI, "remove bobber strategy") {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
@@ -97,6 +97,8 @@ bool FollowAction::isUseful()
|
|||||||
|
|
||||||
distance = bot->GetDistance(loc.GetPositionX(), loc.GetPositionY(), loc.GetPositionZ());
|
distance = bot->GetDistance(loc.GetPositionX(), loc.GetPositionY(), loc.GetPositionZ());
|
||||||
}
|
}
|
||||||
|
if (botAI->HasStrategy("master fishing", BOT_STATE_NON_COMBAT))
|
||||||
|
return sServerFacade->IsDistanceGreaterThan(distance, sPlayerbotAIConfig->fishingDistanceFromMaster);
|
||||||
|
|
||||||
return sServerFacade->IsDistanceGreaterThan(distance, formation->GetMaxDistance());
|
return sServerFacade->IsDistanceGreaterThan(distance, formation->GetMaxDistance());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,8 @@
|
|||||||
#include "PositionValue.h"
|
#include "PositionValue.h"
|
||||||
#include "ByteBuffer.h"
|
#include "ByteBuffer.h"
|
||||||
|
|
||||||
|
std::set<uint32> const FISHING_SPELLS = {7620, 7731, 7732, 18248, 33095, 51294};
|
||||||
|
|
||||||
Creature* SeeSpellAction::CreateWps(Player* wpOwner, float x, float y, float z, float o, uint32 entry, Creature* lastWp,
|
Creature* SeeSpellAction::CreateWps(Player* wpOwner, float x, float y, float z, float o, uint32 entry, Creature* lastWp,
|
||||||
bool important)
|
bool important)
|
||||||
{
|
{
|
||||||
@@ -57,6 +59,16 @@ bool SeeSpellAction::Execute(Event event)
|
|||||||
// if (!botAI->HasStrategy("RTSC", botAI->GetState()))
|
// if (!botAI->HasStrategy("RTSC", botAI->GetState()))
|
||||||
// return false;
|
// return false;
|
||||||
|
|
||||||
|
if (FISHING_SPELLS.find(spellId) != FISHING_SPELLS.end())
|
||||||
|
{
|
||||||
|
if (AI_VALUE(bool, "can fish") && sPlayerbotAIConfig->enableFishingWithMaster)
|
||||||
|
{
|
||||||
|
botAI->ChangeStrategy("+master fishing", BOT_STATE_NON_COMBAT);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (spellId != RTSC_MOVE_SPELL)
|
if (spellId != RTSC_MOVE_SPELL)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|||||||
@@ -27,3 +27,11 @@ void RevealStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
|||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
new TriggerNode("often", NextAction::array(0, new NextAction("reveal gathering item", 50.0f), nullptr)));
|
new TriggerNode("often", NextAction::array(0, new NextAction("reveal gathering item", 50.0f), nullptr)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UseBobberStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||||
|
{
|
||||||
|
triggers.push_back(
|
||||||
|
new TriggerNode("can use fishing bobber", NextAction::array(0, new NextAction("use fishing bobber", 20.0f), nullptr)));
|
||||||
|
triggers.push_back(
|
||||||
|
new TriggerNode("random", NextAction::array(0, new NextAction("remove bobber strategy", 20.0f), nullptr)));
|
||||||
|
}
|
||||||
|
|||||||
@@ -37,4 +37,13 @@ public:
|
|||||||
std::string const getName() override { return "reveal"; }
|
std::string const getName() override { return "reveal"; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class UseBobberStrategy : public Strategy
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
UseBobberStrategy(PlayerbotAI* botAI) : Strategy(botAI){}
|
||||||
|
uint32 GetType() const override { return STRATEGY_TYPE_NONCOMBAT; }
|
||||||
|
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||||
|
std::string const getName() override {return "use bobber";}
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -35,5 +35,15 @@ void MountStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
|||||||
|
|
||||||
void WorldBuffStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
void WorldBuffStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||||
{
|
{
|
||||||
triggers.push_back(new TriggerNode("need world buff", NextAction::array(0, new NextAction("world buff", 1.0f), NULL)));
|
triggers.push_back(new TriggerNode("need world buff", NextAction::array(0, new NextAction("world buff", 1.0f), nullptr)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void MasterFishingStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||||
|
{
|
||||||
|
triggers.push_back(new TriggerNode("very often", NextAction::array(0, new NextAction("move near water" , 10.0f), nullptr)));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("very often", NextAction::array(0, new NextAction("go fishing" , 10.0f), nullptr)));
|
||||||
|
|
||||||
|
triggers.push_back(new TriggerNode("random", NextAction::array(0, new NextAction("end master fishing", 12.0f),
|
||||||
|
new NextAction("equip upgrades", 6.0f), nullptr)));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,4 +58,12 @@ public:
|
|||||||
std::string const getName() override { return "worldbuff"; }
|
std::string const getName() override { return "worldbuff"; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class MasterFishingStrategy : public Strategy
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MasterFishingStrategy(PlayerbotAI* botAI) : Strategy(botAI){}
|
||||||
|
uint32 GetType() const override { return STRATEGY_TYPE_NONCOMBAT; }
|
||||||
|
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||||
|
std::string const getName() override {return "master fishing";}
|
||||||
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ void WorldPacketHandlerStrategy::InitTriggers(std::vector<TriggerNode*>& trigger
|
|||||||
// relevance), nullptr)));
|
// relevance), nullptr)));
|
||||||
triggers.push_back(new TriggerNode("group list", NextAction::array(0, new NextAction("reset botAI", relevance), nullptr)));
|
triggers.push_back(new TriggerNode("group list", NextAction::array(0, new NextAction("reset botAI", relevance), nullptr)));
|
||||||
triggers.push_back(new TriggerNode("see spell", NextAction::array(0, new NextAction("see spell", relevance), nullptr)));
|
triggers.push_back(new TriggerNode("see spell", NextAction::array(0, new NextAction("see spell", relevance), nullptr)));
|
||||||
|
|
||||||
triggers.push_back(new TriggerNode("release spirit", NextAction::array(0, new NextAction("release", relevance), nullptr)));
|
triggers.push_back(new TriggerNode("release spirit", NextAction::array(0, new NextAction("release", relevance), nullptr)));
|
||||||
triggers.push_back(new TriggerNode("revive from corpse", NextAction::array(0, new NextAction("revive from corpse", relevance), nullptr)));
|
triggers.push_back(new TriggerNode("revive from corpse", NextAction::array(0, new NextAction("revive from corpse", relevance), nullptr)));
|
||||||
triggers.push_back(new TriggerNode("master loot roll", NextAction::array(0, new NextAction("master loot roll", relevance), nullptr)));
|
triggers.push_back(new TriggerNode("master loot roll", NextAction::array(0, new NextAction("master loot roll", relevance), nullptr)));
|
||||||
|
|||||||
11
src/strategy/triggers/FishingTriggers.cpp
Normal file
11
src/strategy/triggers/FishingTriggers.cpp
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||||
|
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "FishingTriggers.h"
|
||||||
|
#include "Playerbots.h"
|
||||||
|
|
||||||
|
bool CanFishTrigger::IsActive() { return AI_VALUE(bool, "can fish"); }
|
||||||
|
|
||||||
|
bool CanUseFishingBobberTrigger::IsActive() { return AI_VALUE(bool, "can use fishing bobber");}
|
||||||
25
src/strategy/triggers/FishingTriggers.h
Normal file
25
src/strategy/triggers/FishingTriggers.h
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||||
|
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _PLAYERBOT_FISHING_TRIGGER_H
|
||||||
|
#define _PLAYERBOT_FISHING_TRIGGER_H
|
||||||
|
|
||||||
|
#include "GenericTriggers.h"
|
||||||
|
|
||||||
|
class CanFishTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CanFishTrigger(PlayerbotAI* ai) : Trigger(ai, "can fish") {};
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CanUseFishingBobberTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CanUseFishingBobberTrigger(PlayerbotAI* ai) : Trigger(ai, "can use fishing bobber") {};
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
#define _PLAYERBOT_TRIGGERCONTEXT_H
|
#define _PLAYERBOT_TRIGGERCONTEXT_H
|
||||||
|
|
||||||
#include "CureTriggers.h"
|
#include "CureTriggers.h"
|
||||||
|
#include "FishingTriggers.h"
|
||||||
#include "GenericTriggers.h"
|
#include "GenericTriggers.h"
|
||||||
#include "GuildTriggers.h"
|
#include "GuildTriggers.h"
|
||||||
#include "LfgTriggers.h"
|
#include "LfgTriggers.h"
|
||||||
@@ -226,6 +227,8 @@ public:
|
|||||||
creators["do quest status"] = &TriggerContext::do_quest_status;
|
creators["do quest status"] = &TriggerContext::do_quest_status;
|
||||||
creators["travel flight status"] = &TriggerContext::travel_flight_status;
|
creators["travel flight status"] = &TriggerContext::travel_flight_status;
|
||||||
creators["can self resurrect"] = &TriggerContext::can_self_resurrect;
|
creators["can self resurrect"] = &TriggerContext::can_self_resurrect;
|
||||||
|
creators["can fish"] = &TriggerContext::can_fish;
|
||||||
|
creators["can use fishing bobber"] = &TriggerContext::can_use_fishing_bobber;
|
||||||
creators["new pet"] = &TriggerContext::new_pet;
|
creators["new pet"] = &TriggerContext::new_pet;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -425,6 +428,8 @@ private:
|
|||||||
static Trigger* do_quest_status(PlayerbotAI* botAI) { return new NewRpgStatusTrigger(botAI, RPG_DO_QUEST); }
|
static Trigger* do_quest_status(PlayerbotAI* botAI) { return new NewRpgStatusTrigger(botAI, RPG_DO_QUEST); }
|
||||||
static Trigger* travel_flight_status(PlayerbotAI* botAI) { return new NewRpgStatusTrigger(botAI, RPG_TRAVEL_FLIGHT); }
|
static Trigger* travel_flight_status(PlayerbotAI* botAI) { return new NewRpgStatusTrigger(botAI, RPG_TRAVEL_FLIGHT); }
|
||||||
static Trigger* can_self_resurrect(PlayerbotAI* ai) { return new SelfResurrectTrigger(ai); }
|
static Trigger* can_self_resurrect(PlayerbotAI* ai) { return new SelfResurrectTrigger(ai); }
|
||||||
|
static Trigger* can_fish(PlayerbotAI* ai) { return new CanFishTrigger(ai); }
|
||||||
|
static Trigger* can_use_fishing_bobber(PlayerbotAI* ai) { return new CanUseFishingBobberTrigger(ai); }
|
||||||
static Trigger* new_pet(PlayerbotAI* ai) { return new NewPetTrigger(ai); }
|
static Trigger* new_pet(PlayerbotAI* ai) { return new NewPetTrigger(ai); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
55
src/strategy/values/FishValues.cpp
Normal file
55
src/strategy/values/FishValues.cpp
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||||
|
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "FishValues.h"
|
||||||
|
#include "PlayerbotAI.h"
|
||||||
|
#include "RandomPlayerbotMgr.h"
|
||||||
|
#include "Map.h"
|
||||||
|
#include "Spell.h"
|
||||||
|
#include "FishingAction.h"
|
||||||
|
|
||||||
|
bool CanFishValue::Calculate()
|
||||||
|
{
|
||||||
|
int32 SkillFishing = bot->GetSkillValue(SKILL_FISHING);
|
||||||
|
|
||||||
|
if (SkillFishing == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (bot->isSwimming())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (bot->IsInCombat())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CanUseFishingBobberValue::Calculate()
|
||||||
|
{
|
||||||
|
GuidVector gos = AI_VALUE(GuidVector, "nearest game objects no los");
|
||||||
|
for (auto const& guid : gos)
|
||||||
|
{
|
||||||
|
if (GameObject* go = botAI->GetGameObject(guid))
|
||||||
|
{
|
||||||
|
if (go->GetEntry() != FISHING_BOBBER)
|
||||||
|
continue;
|
||||||
|
if (go->GetOwnerGUID() != bot->GetGUID())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (go->getLootState() == GO_READY)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Not ready yet → delay next check
|
||||||
|
time_t bobberActiveTime = go->GetRespawnTime() - FISHING_BOBBER_READY_TIME;
|
||||||
|
if (bobberActiveTime > time(0))
|
||||||
|
botAI->SetNextCheckDelay((bobberActiveTime - time(0)) * IN_MILLISECONDS + 500);
|
||||||
|
else
|
||||||
|
botAI->SetNextCheckDelay(1000);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
47
src/strategy/values/FishValues.h
Normal file
47
src/strategy/values/FishValues.h
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||||
|
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _PLAYERBOT_FISHVALUES_H
|
||||||
|
#define _PLAYERBOT_FISHVALUES_H
|
||||||
|
|
||||||
|
#include "Value.h"
|
||||||
|
#include "TravelMgr.h"
|
||||||
|
#include "NamedObjectContext.h"
|
||||||
|
|
||||||
|
class PlayerbotAI;
|
||||||
|
|
||||||
|
class CanFishValue : public BoolCalculatedValue
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CanFishValue(PlayerbotAI* botAI) : BoolCalculatedValue(botAI, "can fish") {};
|
||||||
|
bool Calculate() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CanUseFishingBobberValue : public BoolCalculatedValue
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CanUseFishingBobberValue(PlayerbotAI* botAI) : BoolCalculatedValue(botAI, "can use fishing bobber") {};
|
||||||
|
bool Calculate() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FishingSpotValue : public ManualSetValue<WorldPosition>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FishingSpotValue(PlayerbotAI* botAI, WorldPosition const& pos = WorldPosition(), std::string const& name = "fishing spot")
|
||||||
|
: ManualSetValue<WorldPosition>(botAI, pos, name) {}
|
||||||
|
|
||||||
|
void Set(WorldPosition val) override
|
||||||
|
{
|
||||||
|
value = val;
|
||||||
|
_setTime = getMSTime();
|
||||||
|
}
|
||||||
|
uint32 lastUpdated() const {return _setTime;}
|
||||||
|
bool IsStale(uint32 maxDuration) const { return getMSTime() - _setTime > maxDuration; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint32 _setTime = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -27,6 +27,7 @@
|
|||||||
#include "EnemyHealerTargetValue.h"
|
#include "EnemyHealerTargetValue.h"
|
||||||
#include "EnemyPlayerValue.h"
|
#include "EnemyPlayerValue.h"
|
||||||
#include "EstimatedLifetimeValue.h"
|
#include "EstimatedLifetimeValue.h"
|
||||||
|
#include "FishValues.h"
|
||||||
#include "Formations.h"
|
#include "Formations.h"
|
||||||
#include "GrindTargetValue.h"
|
#include "GrindTargetValue.h"
|
||||||
#include "GroupValues.h"
|
#include "GroupValues.h"
|
||||||
@@ -312,6 +313,10 @@ public:
|
|||||||
creators["last flee angle"] = &ValueContext::last_flee_angle;
|
creators["last flee angle"] = &ValueContext::last_flee_angle;
|
||||||
creators["last flee timestamp"] = &ValueContext::last_flee_timestamp;
|
creators["last flee timestamp"] = &ValueContext::last_flee_timestamp;
|
||||||
creators["recently flee info"] = &ValueContext::recently_flee_info;
|
creators["recently flee info"] = &ValueContext::recently_flee_info;
|
||||||
|
|
||||||
|
creators["can fish"] = &ValueContext::can_fish;
|
||||||
|
creators["can use fishing bobber"] = &ValueContext::can_use_fishing_bobber;
|
||||||
|
creators["fishing spot"] = &ValueContext::fishing_spot;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -555,6 +560,9 @@ private:
|
|||||||
static UntypedValue* last_flee_angle(PlayerbotAI* ai) { return new LastFleeAngleValue(ai); }
|
static UntypedValue* last_flee_angle(PlayerbotAI* ai) { return new LastFleeAngleValue(ai); }
|
||||||
static UntypedValue* last_flee_timestamp(PlayerbotAI* ai) { return new LastFleeTimestampValue(ai); }
|
static UntypedValue* last_flee_timestamp(PlayerbotAI* ai) { return new LastFleeTimestampValue(ai); }
|
||||||
static UntypedValue* recently_flee_info(PlayerbotAI* ai) { return new RecentlyFleeInfo(ai); }
|
static UntypedValue* recently_flee_info(PlayerbotAI* ai) { return new RecentlyFleeInfo(ai); }
|
||||||
|
static UntypedValue* can_fish(PlayerbotAI* ai) { return new CanFishValue(ai); }
|
||||||
|
static UntypedValue* can_use_fishing_bobber(PlayerbotAI* ai) { return new CanUseFishingBobberValue(ai); }
|
||||||
|
static UntypedValue* fishing_spot(PlayerbotAI* ai) { return new FishingSpotValue(ai); }
|
||||||
// -------------------------------------------------------
|
// -------------------------------------------------------
|
||||||
// Flag for cutom glyphs : true when /w bot glyph equip
|
// Flag for cutom glyphs : true when /w bot glyph equip
|
||||||
// -------------------------------------------------------
|
// -------------------------------------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user