diff --git a/src/AiFactory.cpp b/src/AiFactory.cpp index d107b0dc..dadf327f 100644 --- a/src/AiFactory.cpp +++ b/src/AiFactory.cpp @@ -279,11 +279,11 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa } if (sPlayerbotAIConfig->autoSaveMana) { - engine->addStrategy("smana", false); + engine->addStrategy("save mana", false); } if (sPlayerbotAIConfig->autoAvoidAoe && facade->HasRealPlayerMaster()) { - engine->addStrategy("aaoe", false); + engine->addStrategy("avoid aoe", false); } engine->addStrategy("formation", false); switch (player->getClass()) @@ -388,6 +388,12 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa break; } + if (PlayerbotAI::IsTank(player, true)) { + engine->addStrategy("tank face", false); + } + if (PlayerbotAI::IsMelee(player, true) && PlayerbotAI::IsDps(player, true)) { + engine->addStrategy("behind", false); + } if (facade->IsRealPlayer() || sRandomPlayerbotMgr->IsRandomBot(player)) { @@ -599,7 +605,7 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const if (sPlayerbotAIConfig->autoSaveMana) { - nonCombatEngine->addStrategy("smana", false); + nonCombatEngine->addStrategy("save mana", false); } if ((sRandomPlayerbotMgr->IsRandomBot(player)) && !player->InBattleground()) { diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index 5443c9a6..b2c35166 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -314,6 +314,8 @@ void PlayerbotAI::UpdateAI(uint32 elapsed, bool minimal) AllowActivity(); Spell* currentSpell = bot->GetCurrentSpell(CURRENT_GENERIC_SPELL); + if (!currentSpell) + currentSpell = bot->GetCurrentSpell(CURRENT_CHANNELED_SPELL); if (currentSpell && currentSpell->getState() == SPELL_STATE_PREPARING) { if (currentSpell->m_targets.GetUnitTarget() && !currentSpell->m_targets.GetUnitTarget()->IsAlive() && @@ -326,9 +328,9 @@ void PlayerbotAI::UpdateAI(uint32 elapsed, bool minimal) } if (nextTransportCheck > elapsed) - nextTransportCheck -= elapsed; - else - nextTransportCheck = 0; + nextTransportCheck -= elapsed; + else + nextTransportCheck = 0; if (!nextTransportCheck) { @@ -1093,7 +1095,9 @@ void PlayerbotAI::HandleBotOutgoingPacket(WorldPacket const& packet) horizontalSpeed = 0.11f; } verticalSpeed = -verticalSpeed; - + // high vertical may result in stuck as bot can not handle gravity + if (verticalSpeed > 35.0f) + break; // stop casting InterruptSpell(); @@ -1102,7 +1106,7 @@ void PlayerbotAI::HandleBotOutgoingPacket(WorldPacket const& packet) bot->GetMotionMaster()->Clear(); Unit* currentTarget = GetAiObjectContext()->GetValue("current target")->Get(); - bot->GetMotionMaster()->MoveKnockbackFromForPlayer(bot->GetPositionX() + vcos, bot->GetPositionY() + vsin, + bot->GetMotionMaster()->MoveKnockbackFromForPlayer(bot->GetPositionX() - vcos, bot->GetPositionY() - vsin, horizontalSpeed, verticalSpeed); // bot->AddUnitMovementFlag(MOVEMENTFLAG_FALLING); @@ -2023,7 +2027,7 @@ bool PlayerbotAI::IsDps(Player* player, bool bySpec) { return true; } - if (tab == DRUID_TAB_FERAL && !IsTank(player)) + if (tab == DRUID_TAB_FERAL && !IsTank(player, bySpec)) { return true; } @@ -2088,6 +2092,25 @@ bool PlayerbotAI::IsMainTank(Player* player) return false; } +uint32 PlayerbotAI::GetGroupTankNum(Player* player) +{ + Group* group = player->GetGroup(); + if (!group) + { + return 0; + } + uint32 result = 0; + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (IsTank(member) && member->IsAlive()) + { + result++; + } + } + return result; +} + bool PlayerbotAI::IsAssistTank(Player* player) { return IsTank(player) && !IsMainTank(player); } bool PlayerbotAI::IsAssistTankOfIndex(Player* player, int index) diff --git a/src/PlayerbotAI.h b/src/PlayerbotAI.h index 20dd42b2..093ebf41 100644 --- a/src/PlayerbotAI.h +++ b/src/PlayerbotAI.h @@ -410,6 +410,7 @@ public: static bool IsCombo(Player* player, bool bySpec = false); static bool IsRangedDps(Player* player, bool bySpec = false); static bool IsMainTank(Player* player); + static uint32 GetGroupTankNum(Player* player); bool IsAssistTank(Player* player); bool IsAssistTankOfIndex(Player* player, int index); bool IsHealAssistantOfIndex(Player* player, int index); diff --git a/src/PlayerbotMgr.cpp b/src/PlayerbotMgr.cpp index d1b92d7a..34e107d1 100644 --- a/src/PlayerbotMgr.cpp +++ b/src/PlayerbotMgr.cpp @@ -525,7 +525,10 @@ void PlayerbotHolder::OnBotLogin(Player* const bot) sGroupMgr->AddGroup(newGroup); newGroup->AddMember(bot); } - + // if (master) + // { + // // bot->TeleportTo(master); + // } uint32 accountId = bot->GetSession()->GetAccountId(); bool isRandomAccount = sPlayerbotAIConfig->IsInRandomAccountList(accountId); diff --git a/src/strategy/StrategyContext.h b/src/strategy/StrategyContext.h index 2679a59d..da3d8e42 100644 --- a/src/strategy/StrategyContext.h +++ b/src/strategy/StrategyContext.h @@ -60,8 +60,7 @@ public: creators["gather"] = &StrategyContext::gather; creators["emote"] = &StrategyContext::emote; creators["passive"] = &StrategyContext::passive; - // creators["conserve mana"] = &StrategyContext::conserve_mana; - creators["smana"] = &StrategyContext::auto_save_mana; + creators["save mana"] = &StrategyContext::auto_save_mana; creators["food"] = &StrategyContext::food; creators["chat"] = &StrategyContext::chat; creators["default"] = &StrategyContext::world_packet; @@ -113,7 +112,8 @@ public: creators["group"] = &StrategyContext::group; creators["guild"] = &StrategyContext::guild; creators["grind"] = &StrategyContext::grind; - creators["aaoe"] = &StrategyContext::avoid_aoe; + creators["avoid aoe"] = &StrategyContext::avoid_aoe; + creators["tank face"] = &StrategyContext::tank_face; creators["move random"] = &StrategyContext::move_random; creators["formation"] = &StrategyContext::combat_formation; creators["move from group"] = &StrategyContext::move_from_group; @@ -179,6 +179,7 @@ private: static Strategy* guild (PlayerbotAI* botAI) { return new GuildStrategy(botAI); } static Strategy* grind(PlayerbotAI* botAI) { return new GrindingStrategy(botAI); } static Strategy* avoid_aoe(PlayerbotAI* botAI) { return new AvoidAoeStrategy(botAI); } + static Strategy* tank_face(PlayerbotAI* botAI) { return new TankFaceStrategy(botAI); } static Strategy* move_random(PlayerbotAI* ai) { return new MoveRandomStrategy(ai); } static Strategy* combat_formation(PlayerbotAI* ai) { return new CombatFormationStrategy(ai); } static Strategy* move_from_group(PlayerbotAI* botAI) { return new MoveFromGroupStrategy(botAI); } diff --git a/src/strategy/actions/ActionContext.h b/src/strategy/actions/ActionContext.h index 1b798f29..e195e2f9 100644 --- a/src/strategy/actions/ActionContext.h +++ b/src/strategy/actions/ActionContext.h @@ -91,8 +91,9 @@ public: creators["reach party member to resurrect"] = &ActionContext::reach_party_member_to_resurrect; creators["flee"] = &ActionContext::flee; creators["flee with pet"] = &ActionContext::flee_with_pet; - creators["aaoe"] = &ActionContext::avoid_aoe; + creators["avoid aoe"] = &ActionContext::avoid_aoe; creators["combat formation move"] = &ActionContext::combat_formation_move; + creators["tank face"] = &ActionContext::tank_face; creators["disperse set"] = &ActionContext::disperse_set; creators["gift of the naaru"] = &ActionContext::gift_of_the_naaru; creators["shoot"] = &ActionContext::shoot; @@ -276,6 +277,7 @@ private: static Action* flee_with_pet(PlayerbotAI* botAI) { return new FleeWithPetAction(botAI); } static Action* avoid_aoe(PlayerbotAI* botAI) { return new AvoidAoeAction(botAI); } static Action* combat_formation_move(PlayerbotAI* botAI) { return new CombatFormationMoveAction(botAI); } + static Action* tank_face(PlayerbotAI* botAI) { return new TankFaceAction(botAI); } static Action* disperse_set(PlayerbotAI* botAI) { return new DisperseSetAction(botAI); } static Action* gift_of_the_naaru(PlayerbotAI* botAI) { return new CastGiftOfTheNaaruAction(botAI); } static Action* lifeblood(PlayerbotAI* botAI) { return new CastLifeBloodAction(botAI); } diff --git a/src/strategy/actions/MovementActions.cpp b/src/strategy/actions/MovementActions.cpp index dff079fb..07570e2a 100644 --- a/src/strategy/actions/MovementActions.cpp +++ b/src/strategy/actions/MovementActions.cpp @@ -24,6 +24,7 @@ #include "ObjectDefines.h" #include "ObjectGuid.h" #include "PathGenerator.h" +#include "PlayerbotAI.h" #include "PlayerbotAIConfig.h" #include "Playerbots.h" #include "Position.h" @@ -202,7 +203,7 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, return false; float distance = vehicleBase->GetExactDist(x, y, z); // use vehicle distance, not bot - if (distance > sPlayerbotAIConfig->contactDistance) + if (distance > 0.01f) { MotionMaster& mm = *vehicleBase->GetMotionMaster(); // need to move vehicle, not bot mm.Clear(); @@ -217,7 +218,7 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, else if (exact_waypoint || disableMoveSplinePath || !generatePath) { float distance = bot->GetExactDist(x, y, z); - if (distance > sPlayerbotAIConfig->contactDistance) + if (distance > 0.01f) { if (bot->IsSitState()) bot->SetStandState(UNIT_STAND_STATE_STAND); @@ -247,7 +248,7 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, return false; } float distance = bot->GetExactDist(x, y, modifiedZ); - if (distance > sPlayerbotAIConfig->contactDistance) + if (distance > 0.01f) { if (bot->IsSitState()) bot->SetStandState(UNIT_STAND_STATE_STAND); @@ -1882,16 +1883,7 @@ bool AvoidAoeAction::AvoidGameObjectWithDamage() float radius = (float)goInfo->trap.diameter / 2 + go->GetCombatReach(); if (!radius || radius > sPlayerbotAIConfig->maxAoeAvoidRadius) continue; - // for (int i = 0; i < MAX_SPELL_EFFECTS; i++) { - // if (spellInfo->Effects[i].Effect == SPELL_EFFECT_APPLY_AURA) { - // if (spellInfo->Effects[i].ApplyAuraName == SPELL_AURA_PERIODIC_DAMAGE) { - // radius = spellInfo->Effects[i].CalcRadius(); - // break; - // } - // } else if (spellInfo->Effects[i].Effect == SPELL_EFFECT_SCHOOL_DAMAGE) { - // break; - // } - // } + if (bot->GetDistance(go) > radius) { continue; @@ -2140,7 +2132,7 @@ bool MovementAction::FleePosition(Position pos, float radius) bool MovementAction::CheckLastFlee(float curAngle, std::list& infoList) { uint32 curTS = getMSTime(); - curAngle = fmod(curAngle, 2 * M_PI); + curAngle = Position::NormalizeOrientation(curAngle); while (!infoList.empty()) { if (infoList.size() > 10 || infoList.front().timestamp + 5000 < curTS) @@ -2159,7 +2151,7 @@ bool MovementAction::CheckLastFlee(float curAngle, std::list& infoList { continue; } - float revAngle = fmod(info.angle + M_PI, 2 * M_PI); + float revAngle = Position::NormalizeOrientation(info.angle + M_PI); // angle too close if (fabs(revAngle - curAngle) < M_PI / 4) { @@ -2179,13 +2171,14 @@ bool CombatFormationMoveAction::isUseful() { return false; } - float dis = AI_VALUE(float, "disperse distance"); - return dis > 0.0f; + return true; } bool CombatFormationMoveAction::Execute(Event event) { float dis = AI_VALUE(float, "disperse distance"); + if (dis <= 0.0f) + return false; Player* playerToLeave = NearestGroupMember(dis); if (playerToLeave && bot->GetExactDist(playerToLeave) < dis) { @@ -2197,7 +2190,7 @@ bool CombatFormationMoveAction::Execute(Event event) return false; } -Position CombatFormationMoveAction::AverageGroupPos(float dis) +Position CombatFormationMoveAction::AverageGroupPos(float dis, bool ranged, bool self) { float averageX = 0, averageY = 0, averageZ = 0; int cnt = 0; @@ -2210,10 +2203,19 @@ Position CombatFormationMoveAction::AverageGroupPos(float dis) for (Group::member_citerator itr = groupSlot.begin(); itr != groupSlot.end(); itr++) { Player* member = ObjectAccessor::FindPlayer(itr->guid); - if (!member || !member->IsAlive() || member->GetMapId() != bot->GetMapId() || member->IsCharmed() || + if (!member) + continue; + + if (!self && member == bot) + continue; + + if (ranged && !PlayerbotAI::IsRanged(member)) + continue; + + if (!member->IsAlive() || member->GetMapId() != bot->GetMapId() || member->IsCharmed() || sServerFacade->GetDistance2d(bot, member) > dis) continue; - cnt++; + averageX += member->GetPositionX(); averageY += member->GetPositionY(); averageZ += member->GetPositionZ(); @@ -2224,6 +2226,59 @@ Position CombatFormationMoveAction::AverageGroupPos(float dis) return Position(averageX, averageY, averageZ); } +float CombatFormationMoveAction::AverageGroupAngle(Unit* from, bool ranged, bool self) +{ + Group* group = bot->GetGroup(); + if (!from || !group) + { + return 0.0f; + } + // float average = 0.0f; + float sumX = 0.0f; + float sumY = 0.0f; + int cnt = 0; + Group::MemberSlotList const& groupSlot = group->GetMemberSlots(); + for (Group::member_citerator itr = groupSlot.begin(); itr != groupSlot.end(); itr++) + { + Player* member = ObjectAccessor::FindPlayer(itr->guid); + if (!member) + continue; + + if (!self && member == bot) + continue; + + if (ranged && !PlayerbotAI::IsRanged(member)) + continue; + + if (!member->IsAlive() || member->GetMapId() != bot->GetMapId() || member->IsCharmed() || + sServerFacade->GetDistance2d(bot, member) > sPlayerbotAIConfig->sightDistance) + continue; + + cnt++; + sumX += member->GetPositionX() - from->GetPositionX(); + sumY += member->GetPositionY() - from->GetPositionY(); + } + if (cnt == 0) + return 0.0f; + + // unnecessary division + // sumX /= cnt; + // sumY /= cnt; + + return atan2(sumY, sumX); +} + +Position CombatFormationMoveAction::GetNearestPosition(const std::vector& positions) +{ + Position result; + for (const Position& pos : positions) + { + if (bot->GetExactDist(pos) < bot->GetExactDist(result)) + result = pos; + } + return result; +} + Player* CombatFormationMoveAction::NearestGroupMember(float dis) { float nearestDis = 10000.0f; @@ -2249,6 +2304,74 @@ Player* CombatFormationMoveAction::NearestGroupMember(float dis) return result; } +bool TankFaceAction::Execute(Event event) +{ + Unit* target = AI_VALUE(Unit*, "current target"); + if (!target) + return false; + + if (!bot->GetGroup()) + return false; + + if (!bot->IsWithinMeleeRange(target)) + return false; + + if (!AI_VALUE2(bool, "has aggro", "current target")) + return false; + + float averageAngle = AverageGroupAngle(target, true); + + if (averageAngle == 0.0f) + return false; + + float deltaAngle = Position::NormalizeOrientation(averageAngle - target->GetAngle(bot)); + if (deltaAngle > M_PI) + deltaAngle -= 2.0f * M_PI; // -PI..PI + + float tolerable = M_PI_2; + + if (fabs(deltaAngle) > tolerable) + return false; + + float goodAngle1 = Position::NormalizeOrientation(averageAngle + M_PI * 3 / 5); + float goodAngle2 = Position::NormalizeOrientation(averageAngle - M_PI * 3 / 5); + + // if dist < bot->GetMeleeRange(target) / 2, target will move backward + float dist = std::max(bot->GetExactDist(target), bot->GetMeleeRange(target) / 2) - bot->GetCombatReach() - target->GetCombatReach(); + std::vector availablePos; + float x, y, z; + target->GetNearPoint(bot, x, y, z, 0.0f, dist, goodAngle1); + if (bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), + x, y, z)) + { + /// @todo: movement control now is a mess, prepare to rewrite + std::list& infoList = AI_VALUE(std::list&, "recently flee info"); + Position pos(x, y, z); + float angle = bot->GetAngle(&pos); + if (CheckLastFlee(angle, infoList)) + { + availablePos.push_back(Position(x, y, z)); + } + } + target->GetNearPoint(bot, x, y, z, 0.0f, dist, goodAngle2); + if (bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), + x, y, z)) + { + std::list& infoList = AI_VALUE(std::list&, "recently flee info"); + Position pos(x, y, z); + float angle = bot->GetAngle(&pos); + if (CheckLastFlee(angle, infoList)) + { + availablePos.push_back(Position(x, y, z)); + } + availablePos.push_back(Position(x, y, z)); + } + if (availablePos.empty()) + return false; + Position nearest = GetNearestPosition(availablePos); + return MoveTo(bot->GetMapId(), nearest.GetPositionX(), nearest.GetPositionY(), nearest.GetPositionZ(), false, false, false, true, MovementPriority::MOVEMENT_COMBAT); +} + bool DisperseSetAction::Execute(Event event) { std::string const text = event.getParam(); @@ -2388,25 +2511,58 @@ bool SetBehindTargetAction::Execute(Event event) if (!target) return false; - float angle = GetFollowAngle() / 3 + target->GetOrientation() + M_PI; + if (target->GetVictim() == bot) + return false; - // return ChaseTo(target, 0.f, angle); + if (!bot->IsWithinMeleeRange(target)) + return false; - float distance = sPlayerbotAIConfig->contactDistance; - float x = target->GetPositionX() + cos(angle) * distance; - float y = target->GetPositionY() + sin(angle) * distance; - float z = target->GetPositionZ(); - bot->UpdateGroundPositionZ(x, y, z); + float deltaAngle = Position::NormalizeOrientation(target->GetOrientation() - target->GetAngle(bot)); + if (deltaAngle > M_PI) + deltaAngle -= 2.0f * M_PI; // -PI..PI - return MoveTo(bot->GetMapId(), x, y, z); -} + float tolerable = M_PI_2; -bool SetBehindTargetAction::isUseful() { return !AI_VALUE2(bool, "behind", "current target"); } + if (fabs(deltaAngle) > tolerable) + return false; -bool SetBehindTargetAction::isPossible() -{ - Unit* target = AI_VALUE(Unit*, "current target"); - return target && !(target->GetVictim() && target->GetVictim()->GetGUID() == bot->GetGUID()); + float goodAngle1 = Position::NormalizeOrientation(target->GetOrientation() + M_PI * 3 / 5); + float goodAngle2 = Position::NormalizeOrientation(target->GetOrientation() - M_PI * 3 / 5); + + float dist = std::max(bot->GetExactDist(target), bot->GetMeleeRange(target) / 2) - bot->GetCombatReach() - target->GetCombatReach(); + std::vector availablePos; + float x, y, z; + target->GetNearPoint(bot, x, y, z, 0.0f, dist, goodAngle1); + if (bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), + x, y, z)) + { + /// @todo: movement control now is a mess, prepare to rewrite + std::list& infoList = AI_VALUE(std::list&, "recently flee info"); + Position pos(x, y, z); + float angle = bot->GetAngle(&pos); + if (CheckLastFlee(angle, infoList)) + { + availablePos.push_back(Position(x, y, z)); + } + availablePos.push_back(Position(x, y, z)); + } + target->GetNearPoint(bot, x, y, z, 0.0f, dist, goodAngle2); + if (bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), + x, y, z)) + { + std::list& infoList = AI_VALUE(std::list&, "recently flee info"); + Position pos(x, y, z); + float angle = bot->GetAngle(&pos); + if (CheckLastFlee(angle, infoList)) + { + availablePos.push_back(Position(x, y, z)); + } + availablePos.push_back(Position(x, y, z)); + } + if (availablePos.empty()) + return false; + Position nearest = GetNearestPosition(availablePos); + return MoveTo(bot->GetMapId(), nearest.GetPositionX(), nearest.GetPositionY(), nearest.GetPositionZ(), false, false, false, true, MovementPriority::MOVEMENT_COMBAT); } bool MoveOutOfCollisionAction::Execute(Event event) diff --git a/src/strategy/actions/MovementActions.h b/src/strategy/actions/MovementActions.h index 7edf119b..7b03f034 100644 --- a/src/strategy/actions/MovementActions.h +++ b/src/strategy/actions/MovementActions.h @@ -99,7 +99,7 @@ class AvoidAoeAction : public MovementAction { public: AvoidAoeAction(PlayerbotAI* botAI, int moveInterval = 1000) - : MovementAction(botAI, "aaoe"), moveInterval(moveInterval) + : MovementAction(botAI, "avoid aoe"), moveInterval(moveInterval) { } @@ -115,11 +115,12 @@ protected: int moveInterval; }; + class CombatFormationMoveAction : public MovementAction { public: - CombatFormationMoveAction(PlayerbotAI* botAI, int moveInterval = 1000) - : MovementAction(botAI, "combat formation move"), moveInterval(moveInterval) + CombatFormationMoveAction(PlayerbotAI* botAI, std::string name = "combat formation move", int moveInterval = 1000) + : MovementAction(botAI, name), moveInterval(moveInterval) { } @@ -127,12 +128,22 @@ public: bool Execute(Event event) override; protected: - Position AverageGroupPos(float dis = sPlayerbotAIConfig->sightDistance); + Position AverageGroupPos(float dis = sPlayerbotAIConfig->sightDistance, bool ranged = false, bool self = false); Player* NearestGroupMember(float dis = sPlayerbotAIConfig->sightDistance); + float AverageGroupAngle(Unit* from, bool ranged = false, bool self = false); + Position GetNearestPosition(const std::vector& positions); int lastMoveTimer = 0; int moveInterval; }; +class TankFaceAction : public CombatFormationMoveAction +{ +public: + TankFaceAction(PlayerbotAI* botAI) : CombatFormationMoveAction(botAI, "tank face") {} + + bool Execute(Event event) override; +}; + class DisperseSetAction : public Action { public: @@ -178,14 +189,12 @@ public: bool isPossible() override; }; -class SetBehindTargetAction : public MovementAction +class SetBehindTargetAction : public CombatFormationMoveAction { public: - SetBehindTargetAction(PlayerbotAI* botAI) : MovementAction(botAI, "set behind") {} + SetBehindTargetAction(PlayerbotAI* botAI) : CombatFormationMoveAction(botAI, "set behind") {} bool Execute(Event event) override; - bool isUseful() override; - bool isPossible() override; }; class MoveOutOfCollisionAction : public MovementAction diff --git a/src/strategy/generic/CombatStrategy.cpp b/src/strategy/generic/CombatStrategy.cpp index 9d2d9932..bd1e3838 100644 --- a/src/strategy/generic/CombatStrategy.cpp +++ b/src/strategy/generic/CombatStrategy.cpp @@ -70,7 +70,7 @@ AvoidAoeStrategy::AvoidAoeStrategy(PlayerbotAI* botAI) : Strategy(botAI) {} NextAction** AvoidAoeStrategy::getDefaultActions() { - return NextAction::array(0, new NextAction("aaoe", ACTION_EMERGENCY), nullptr); + return NextAction::array(0, new NextAction("avoid aoe", ACTION_EMERGENCY), nullptr); } void AvoidAoeStrategy::InitTriggers(std::vector& triggers) @@ -85,6 +85,17 @@ void AvoidAoeStrategy::InitMultipliers(std::vector& multipliers) // multipliers.push_back(new AvoidAoeStrategyMultiplier(botAI)); } +TankFaceStrategy::TankFaceStrategy(PlayerbotAI* botAI) : Strategy(botAI) {} + +NextAction** TankFaceStrategy::getDefaultActions() +{ + return NextAction::array(0, new NextAction("tank face", ACTION_MOVE), nullptr); +} + +void TankFaceStrategy::InitTriggers(std::vector& triggers) +{ +} + NextAction** CombatFormationStrategy::getDefaultActions() { return NextAction::array(0, new NextAction("combat formation move", ACTION_NORMAL), nullptr); diff --git a/src/strategy/generic/CombatStrategy.h b/src/strategy/generic/CombatStrategy.h index e96a3eaf..9005f6f5 100644 --- a/src/strategy/generic/CombatStrategy.h +++ b/src/strategy/generic/CombatStrategy.h @@ -23,12 +23,21 @@ class AvoidAoeStrategy : public Strategy { public: explicit AvoidAoeStrategy(PlayerbotAI* ai); - const std::string getName() override { return "aaoe"; } + const std::string getName() override { return "avoid aoe"; } NextAction** getDefaultActions() override; void InitMultipliers(std::vector& multipliers) override; void InitTriggers(std::vector& triggers) override; }; +class TankFaceStrategy : public Strategy +{ +public: + explicit TankFaceStrategy(PlayerbotAI* ai); + const std::string getName() override { return "tank face"; } + NextAction** getDefaultActions() override; + void InitTriggers(std::vector& triggers) override; +}; + class CombatFormationStrategy : public Strategy { public: diff --git a/src/strategy/generic/ConserveManaStrategy.h b/src/strategy/generic/ConserveManaStrategy.h index d63daae6..0f389ca3 100644 --- a/src/strategy/generic/ConserveManaStrategy.h +++ b/src/strategy/generic/ConserveManaStrategy.h @@ -10,46 +10,10 @@ class PlayerbotAI; -// Yunfan: deprecate old save mana method. - -// class ConserveManaMultiplier : public Multiplier -// { -// public: -// ConserveManaMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "conserve mana") { } - -// float GetValue(Action* action) override; -// }; - -// class SaveManaMultiplier : public Multiplier -// { -// public: -// SaveManaMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "save mana") { } - -// float GetValue(Action* action) override; -// }; - -// class ConserveManaStrategy : public Strategy -// { -// public: -// ConserveManaStrategy(PlayerbotAI* botAI) : Strategy(botAI) { } - -// void InitMultipliers(std::vector& multipliers) override; -// std::string const getName() override { return "conserve mana"; } -// }; - -// class HealerSaveManaStrategy : public Strategy -// { -// public: -// HealerSaveManaStrategy(PlayerbotAI* botAI) : Strategy(botAI) { } - -// void InitMultipliers(std::vector& multipliers) override; -// std::string const getName() override { return "healer save mana"; } -// }; - class HealerAutoSaveManaMultiplier : public Multiplier { public: - HealerAutoSaveManaMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "smana") {} + HealerAutoSaveManaMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "save mana") {} float GetValue(Action* action) override; }; @@ -60,7 +24,7 @@ public: HealerAutoSaveManaStrategy(PlayerbotAI* botAI) : Strategy(botAI) {} void InitMultipliers(std::vector& multipliers) override; - std::string const getName() override { return "smana"; } + std::string const getName() override { return "save mana"; } }; #endif diff --git a/src/strategy/raids/naxxramas/RaidNaxxMultipliers.cpp b/src/strategy/raids/naxxramas/RaidNaxxMultipliers.cpp index bdf5831e..0bb3b876 100644 --- a/src/strategy/raids/naxxramas/RaidNaxxMultipliers.cpp +++ b/src/strategy/raids/naxxramas/RaidNaxxMultipliers.cpp @@ -27,7 +27,7 @@ float GrobbulusMultiplier::GetValue(Action* action) { return 1.0f; } - if (dynamic_cast(action)) + if (dynamic_cast(action) || dynamic_cast(action)) { return 0.0f; } @@ -48,7 +48,7 @@ float HeiganDanceMultiplier::GetValue(Action* action) uint32 curr_dance = eventMap->GetNextEventTime(4); uint32 curr_timer = eventMap->GetTimer(); uint32 curr_erupt = eventMap->GetNextEventTime(3); - if (dynamic_cast(action)) + if (dynamic_cast(action)) { return 0.0f; } @@ -87,7 +87,8 @@ float LoathebGenericMultiplier::GetValue(Action* action) context->GetValue("neglect threat")->Set(true); if (botAI->GetState() == BOT_STATE_COMBAT && (dynamic_cast(action) || dynamic_cast(action) || - dynamic_cast(action) || dynamic_cast(action))) + dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action))) { return 0.0f; } @@ -113,7 +114,8 @@ float ThaddiusGenericMultiplier::GetValue(Action* action) if (helper.IsPhasePet() && (dynamic_cast(action) || dynamic_cast(action) || dynamic_cast(action) || - dynamic_cast(action) || dynamic_cast(action))) + dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action))) { return 0.0f; } @@ -151,7 +153,8 @@ float SapphironGenericMultiplier::GetValue(Action* action) { return 1.0f; } - if (dynamic_cast(action) || dynamic_cast(action)) + if (dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action)) { return 0.0f; } diff --git a/src/strategy/values/IsBehindValue.cpp b/src/strategy/values/IsBehindValue.cpp index c803f681..80feef36 100644 --- a/src/strategy/values/IsBehindValue.cpp +++ b/src/strategy/values/IsBehindValue.cpp @@ -4,6 +4,7 @@ */ #include "IsBehindValue.h" +#include #include "Playerbots.h" @@ -14,8 +15,10 @@ bool IsBehindValue::Calculate() return false; float targetOrientation = target->GetOrientation(); - float orientation = bot->GetOrientation(); - float distance = bot->GetDistance(target); - return distance <= ATTACK_DISTANCE && abs(targetOrientation - orientation) < M_PI / 2; + float deltaAngle = Position::NormalizeOrientation(targetOrientation - target->GetAngle(bot)); + if (deltaAngle > M_PI) + deltaAngle -= 2.0f * M_PI; // -PI..PI + + return fabs(deltaAngle) > M_PI_2; } diff --git a/src/strategy/values/IsFacingValue.cpp b/src/strategy/values/IsFacingValue.cpp index 9ac841d6..c3e62586 100644 --- a/src/strategy/values/IsFacingValue.cpp +++ b/src/strategy/values/IsFacingValue.cpp @@ -4,6 +4,7 @@ */ #include "IsFacingValue.h" +#include #include "Playerbots.h" @@ -13,5 +14,5 @@ bool IsFacingValue::Calculate() if (!target) return false; - return bot->HasInArc(CAST_ANGLE_IN_FRONT, target); + return bot->HasInArc(M_PI_2, target); } diff --git a/src/strategy/values/RtiTargetValue.cpp b/src/strategy/values/RtiTargetValue.cpp index c3d3ae34..99279f9d 100644 --- a/src/strategy/values/RtiTargetValue.cpp +++ b/src/strategy/values/RtiTargetValue.cpp @@ -5,6 +5,7 @@ #include "RtiTargetValue.h" +#include "AttackersValue.h" #include "Playerbots.h" #include "ServerFacade.h" @@ -61,7 +62,7 @@ Unit* RtiTargetValue::Calculate() //////////////////////////////////////////////////////end: delete below check Unit* unit = botAI->GetUnit(guid); - if (!unit || unit->isDead() || !bot->IsWithinLOSInMap(unit) || + if (!unit || unit->isDead() || !bot->IsWithinLOSInMap(unit) || !AttackersValue::IsValidTarget(unit, bot) || sServerFacade->IsDistanceGreaterThan(sServerFacade->GetDistance2d(bot, unit), sPlayerbotAIConfig->sightDistance)) return nullptr; diff --git a/src/strategy/values/TankTargetValue.cpp b/src/strategy/values/TankTargetValue.cpp index c1c55466..2ee48639 100644 --- a/src/strategy/values/TankTargetValue.cpp +++ b/src/strategy/values/TankTargetValue.cpp @@ -73,6 +73,15 @@ public: bool IsBetter(Unit* new_unit, Unit* old_unit) { Player* bot = botAI->GetBot(); + // if group has multiple tanks, main tank just focus on the current target + Unit* currentTarget = botAI->GetAiObjectContext()->GetValue("current target")->Get(); + if (currentTarget && botAI->IsMainTank(bot) && botAI->GetGroupTankNum(bot) > 1) + { + if (old_unit == currentTarget) + return false; + if (new_unit == currentTarget) + return true; + } float new_threat = new_unit->GetThreatMgr().GetThreat(bot); float old_threat = old_unit->GetThreatMgr().GetThreat(bot); float new_dis = bot->GetDistance(new_unit);