diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index e469db3a..ef5a742b 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -341,6 +341,13 @@ AiPlayerbot.GlobalCooldown = 500 # Max wait time when moving AiPlayerbot.MaxWaitForMove = 5000 +# Disables use of MoveSplinePath for bot movement, will result in more erratic bot movement but means stun/snare/root/etc +# will work on bots (they wont reliably work when MoveSplinePath is enabled, though slowing effects still work ok) +# Default: 0 - MoveSplinePath enabled +# 1 - MoveSplinePath disabled in BG/Arena only +# 2 - MoveSplinePath disabled everywhere +AiPlayerbot.DisableMoveSplinePath = 0 + # Max search time for movement (higher for better movement on slopes) # default: 3 AiPlayerbot.MaxMovementSearchTime = 3 diff --git a/src/PlayerbotAIConfig.cpp b/src/PlayerbotAIConfig.cpp index 91e69210..79531200 100644 --- a/src/PlayerbotAIConfig.cpp +++ b/src/PlayerbotAIConfig.cpp @@ -53,6 +53,7 @@ bool PlayerbotAIConfig::Initialize() globalCoolDown = sConfigMgr->GetOption("AiPlayerbot.GlobalCooldown", 1500); maxWaitForMove = sConfigMgr->GetOption("AiPlayerbot.MaxWaitForMove", 5000); + disableMoveSplinePath = sConfigMgr->GetOption("AiPlayerbot.DisableMoveSplinePath", 0); maxMovementSearchTime = sConfigMgr->GetOption("AiPlayerbot.MaxMovementSearchTime", 3); expireActionTime = sConfigMgr->GetOption("AiPlayerbot.ExpireActionTime", 5000); dispelAuraDuration = sConfigMgr->GetOption("AiPlayerbot.DispelAuraDuration", 7000); @@ -272,6 +273,9 @@ bool PlayerbotAIConfig::Initialize() equipmentPersistence = sConfigMgr->GetOption("AiPlayerbot.EquipmentPersistence", false); equipmentPersistenceLevel = sConfigMgr->GetOption("AiPlayerbot.EquipmentPersistenceLevel", 80); groupInvitationPermission = sConfigMgr->GetOption("AiPlayerbot.GroupInvitationPermission", 1); + allowSummonInCombat = sConfigMgr->GetOption("AiPlayerbot.AllowSummonInCombat", true); + allowSummonWhenMasterIsDead = sConfigMgr->GetOption("AiPlayerbot.AllowSummonWhenMasterIsDead", true); + allowSummonWhenBotIsDead = sConfigMgr->GetOption("AiPlayerbot.AllowSummonWhenBotIsDead", true); botReviveWhenSummon = sConfigMgr->GetOption("AiPlayerbot.BotReviveWhenSummon", 1); botRepairWhenSummon = sConfigMgr->GetOption("AiPlayerbot.BotRepairWhenSummon", true); autoInitOnly = sConfigMgr->GetOption("AiPlayerbot.AutoInitOnly", false); diff --git a/src/PlayerbotAIConfig.h b/src/PlayerbotAIConfig.h index d2488458..3fb31410 100644 --- a/src/PlayerbotAIConfig.h +++ b/src/PlayerbotAIConfig.h @@ -54,8 +54,8 @@ class PlayerbotAIConfig bool enabled; bool allowGuildBots, allowPlayerBots; - uint32 globalCoolDown, reactDelay, maxWaitForMove, maxMovementSearchTime, expireActionTime, - dispelAuraDuration, passiveDelay, repeatDelay, + uint32 globalCoolDown, reactDelay, maxWaitForMove, disableMoveSplinePath, maxMovementSearchTime, + expireActionTime, dispelAuraDuration, passiveDelay, repeatDelay, errorDelay, rpgDelay, sitDelay, returnDelay, lootDelay; float sightDistance, spellDistance, reactDistance, grindDistance, lootDistance, shootDistance, fleeDistance, tooCloseDistance, meleeDistance, followDistance, whisperDistance, contactDistance, @@ -214,6 +214,9 @@ class PlayerbotAIConfig bool equipmentPersistence; int32 equipmentPersistenceLevel; int32 groupInvitationPermission; + bool allowSummonInCombat; + bool allowSummonWhenMasterIsDead; + bool allowSummonWhenBotIsDead; int32 botReviveWhenSummon; bool botRepairWhenSummon; bool autoInitOnly; diff --git a/src/RandomPlayerbotFactory.cpp b/src/RandomPlayerbotFactory.cpp index 20ef853b..6721c8e4 100644 --- a/src/RandomPlayerbotFactory.cpp +++ b/src/RandomPlayerbotFactory.cpp @@ -55,9 +55,9 @@ RandomPlayerbotFactory::RandomPlayerbotFactory(uint32 accountId) : accountId(acc availableRaces[CLASS_PRIEST].push_back(RACE_NIGHTELF); availableRaces[CLASS_PRIEST].push_back(RACE_TROLL); availableRaces[CLASS_PRIEST].push_back(RACE_UNDEAD_PLAYER); - availableRaces[CLASS_PRIEST].push_back(RACE_DRAENEI); if(expansion >= EXPANSION_THE_BURNING_CRUSADE) { + availableRaces[CLASS_PRIEST].push_back(RACE_DRAENEI); availableRaces[CLASS_PRIEST].push_back(RACE_BLOODELF); } diff --git a/src/strategy/actions/BattleGroundTactics.cpp b/src/strategy/actions/BattleGroundTactics.cpp index 226cce6f..80eff17b 100644 --- a/src/strategy/actions/BattleGroundTactics.cpp +++ b/src/strategy/actions/BattleGroundTactics.cpp @@ -40,12 +40,16 @@ Position const WS_FLAG_HIDE_ALLIANCE_2 = { 1540.286f, 1476.026f, 352.692f, 2.91f Position const WS_FLAG_HIDE_ALLIANCE_3 = { 1495.807f, 1466.774f, 352.350f, 1.50f }; std::vector const WS_FLAG_HIDE_HORDE = { WS_FLAG_HIDE_HORDE_1 , WS_FLAG_HIDE_HORDE_2, WS_FLAG_HIDE_HORDE_3 }; std::vector const WS_FLAG_HIDE_ALLIANCE = { WS_FLAG_HIDE_ALLIANCE_1 , WS_FLAG_HIDE_ALLIANCE_2, WS_FLAG_HIDE_ALLIANCE_3 }; + Position const AB_WAITING_POS_HORDE = { 702.884f, 703.045f, -16.115f, 0.77f }; Position const AB_WAITING_POS_ALLIANCE = { 1286.054f, 1282.500f, -15.697f, 3.95f }; + Position const AV_WAITING_POS_ALLIANCE = { 793.627f, -493.814f, 99.689f, 3.09f }; Position const AV_WAITING_POS_HORDE = { -1381.865f, -544.872f, 54.773f, 0.76f }; -Position const AV_ICEBLOOD_GARRISON_WAITING_ALLIANCE = { -492.17f, -187.077f, 57.1342f, 2.77f }; -Position const AV_STONEHEARTH_WAITING_HORDE = { 28.1264f, -302.593f, 15.076f, 2.96f }; +Position const AV_ICEBLOOD_GARRISON_WAITING_ALLIANCE = { -523.105f, -182.178f, 57.956f, 2.77f }; +Position const AV_ICEBLOOD_GARRISON_ATTACKING_ALLIANCE = { -545.288f, -167.932f, 57.012f, 2.77f }; +Position const AV_STONEHEARTH_WAITING_HORDE = { -36.399f, -306.403f, 15.565f, 2.96f }; +Position const AV_STONEHEARTH_ATTACKING_HORDE = { -55.210f, -288.546f, 15.578f, 2.96f }; Position const EY_WAITING_POS_HORDE = { 1809.102f, 1540.854f, 1267.142f, 6.18f }; Position const EY_WAITING_POS_ALLIANCE = { 2526.020f, 1596.787f, 1270.127f, 3.14f }; @@ -799,7 +803,16 @@ BattleBotPath vPath_AB_Farm_to_LumberMill = BattleBotPath vPath_AV_Horde_Cave_to_Tower_Point_Crossroad = { - { -885.928f, -536.612f, 55.1936f, nullptr }, + { -1362.395f, -529.615f, 52.636f, nullptr }, + { -1327.036f, -511.374f, 51.138f, nullptr }, + { -1277.047f, -516.327f, 50.667f, nullptr }, + { -1214.901f, -529.350f, 52.251f, nullptr }, + { -1151.129f, -545.598f, 51.990f, nullptr }, + { -1085.775f, -538.719f, 47.905f, nullptr }, + { -1038.552f, -517.946f, 43.876f, nullptr }, + { -981.371f, -494.593f, 41.127f, nullptr }, + { -930.598f, -463.751f, 43.060f, nullptr }, + { -887.138f, -475.816f, 44.374f, nullptr }, { -880.957f, -525.119f, 53.6791f, nullptr }, { -839.408f, -499.746f, 49.7505f, nullptr }, { -820.21f, -469.193f, 49.4085f, nullptr }, @@ -2262,48 +2275,48 @@ std::vector const vPaths_HordeMine = &vPath_AV_Coldtooth_Mine_Entrance_to_Coldtooth_Mine_Boss, }; -static uint32 AV_HordeAttackObjectives[] = +static std::pair AV_HordeAttackObjectives[] = { // Attack - { BG_AV_NODES_STONEHEART_BUNKER }, - { BG_AV_NODES_STONEHEART_GRAVE }, - { BG_AV_NODES_ICEWING_BUNKER }, - { BG_AV_NODES_STORMPIKE_GRAVE }, - { BG_AV_NODES_DUNBALDAR_SOUTH }, - { BG_AV_NODES_DUNBALDAR_NORTH }, - { BG_AV_NODES_FIRSTAID_STATION } + { BG_AV_NODES_STONEHEART_BUNKER, BG_AV_OBJECT_FLAG_A_STONEHEART_BUNKER }, + { BG_AV_NODES_STONEHEART_GRAVE, BG_AV_OBJECT_FLAG_A_STONEHEART_GRAVE }, + { BG_AV_NODES_ICEWING_BUNKER, BG_AV_OBJECT_FLAG_A_ICEWING_BUNKER }, + { BG_AV_NODES_STORMPIKE_GRAVE, BG_AV_OBJECT_FLAG_A_STORMPIKE_GRAVE }, + { BG_AV_NODES_DUNBALDAR_SOUTH, BG_AV_OBJECT_FLAG_A_DUNBALDAR_SOUTH }, + { BG_AV_NODES_DUNBALDAR_NORTH, BG_AV_OBJECT_FLAG_A_DUNBALDAR_NORTH }, + { BG_AV_NODES_FIRSTAID_STATION, BG_AV_OBJECT_FLAG_A_FIRSTAID_STATION } }; -static uint32 AV_HordeDefendObjectives[] = +static std::pair AV_HordeDefendObjectives[] = { // Defend - { BG_AV_NODES_FROSTWOLF_GRAVE }, - { BG_AV_NODES_FROSTWOLF_ETOWER }, - { BG_AV_NODES_FROSTWOLF_WTOWER }, - { BG_AV_NODES_TOWER_POINT }, - { BG_AV_NODES_ICEBLOOD_TOWER }, + { BG_AV_NODES_FROSTWOLF_GRAVE, BG_AV_OBJECT_FLAG_H_FROSTWOLF_GRAVE }, + { BG_AV_NODES_FROSTWOLF_ETOWER, BG_AV_OBJECT_FLAG_H_FROSTWOLF_ETOWER }, + { BG_AV_NODES_FROSTWOLF_WTOWER, BG_AV_OBJECT_FLAG_H_FROSTWOLF_WTOWER }, + { BG_AV_NODES_TOWER_POINT, BG_AV_OBJECT_FLAG_H_TOWER_POINT }, + { BG_AV_NODES_ICEBLOOD_TOWER, BG_AV_OBJECT_FLAG_H_ICEBLOOD_TOWER }, }; -static uint32 AV_AllianceAttackObjectives[] = +static std::pair AV_AllianceAttackObjectives[] = { // Attack - { BG_AV_NODES_ICEBLOOD_TOWER }, - { BG_AV_NODES_ICEBLOOD_GRAVE }, - { BG_AV_NODES_TOWER_POINT }, - { BG_AV_NODES_FROSTWOLF_GRAVE }, - { BG_AV_NODES_FROSTWOLF_ETOWER }, - { BG_AV_NODES_FROSTWOLF_WTOWER }, - { BG_AV_NODES_FROSTWOLF_HUT }, + { BG_AV_NODES_ICEBLOOD_TOWER, BG_AV_OBJECT_FLAG_H_ICEBLOOD_TOWER}, + { BG_AV_NODES_ICEBLOOD_GRAVE, BG_AV_OBJECT_FLAG_H_ICEBLOOD_GRAVE}, + { BG_AV_NODES_TOWER_POINT, BG_AV_OBJECT_FLAG_H_TOWER_POINT }, + { BG_AV_NODES_FROSTWOLF_GRAVE, BG_AV_OBJECT_FLAG_H_FROSTWOLF_GRAVE }, + { BG_AV_NODES_FROSTWOLF_ETOWER, BG_AV_OBJECT_FLAG_H_FROSTWOLF_ETOWER }, + { BG_AV_NODES_FROSTWOLF_WTOWER, BG_AV_OBJECT_FLAG_H_FROSTWOLF_WTOWER }, + { BG_AV_NODES_FROSTWOLF_HUT, BG_AV_OBJECT_FLAG_H_FROSTWOLF_HUT }, }; -static uint32 AV_AllianceDefendObjectives[] = +static std::pair AV_AllianceDefendObjectives[] = { // Defend - { BG_AV_NODES_STORMPIKE_GRAVE }, - { BG_AV_NODES_DUNBALDAR_SOUTH }, - { BG_AV_NODES_DUNBALDAR_NORTH }, - { BG_AV_NODES_ICEWING_BUNKER }, - { BG_AV_NODES_STONEHEART_BUNKER }, + { BG_AV_NODES_STORMPIKE_GRAVE, BG_AV_OBJECT_FLAG_A_STORMPIKE_GRAVE }, + { BG_AV_NODES_DUNBALDAR_SOUTH, BG_AV_OBJECT_FLAG_A_DUNBALDAR_SOUTH }, + { BG_AV_NODES_DUNBALDAR_NORTH, BG_AV_OBJECT_FLAG_A_DUNBALDAR_NORTH }, + { BG_AV_NODES_ICEWING_BUNKER, BG_AV_OBJECT_FLAG_A_ICEWING_BUNKER }, + { BG_AV_NODES_STONEHEART_BUNKER, BG_AV_OBJECT_FLAG_A_STONEHEART_BUNKER }, }; static uint32 AB_AttackObjectives[] = @@ -2937,7 +2950,7 @@ bool BGTactics::selectObjective(bool reset) alterValleyBG->GetAVNodeInfo(BG_AV_NODES_STONEHEART_BUNKER).TotalOwnerId != TEAM_ALLIANCE && alterValleyBG->GetAVNodeInfo(BG_AV_NODES_FIRSTAID_STATION).TotalOwnerId != TEAM_ALLIANCE) { - if (Creature* pVanndar = bg->GetBGCreature(AV_NPC_A_BOSS)) + if (Creature* pVanndar = bg->GetBGCreature(AV_CPLACE_TRIGGER17)) { BgObjective = pVanndar; endBoss = true; @@ -2952,8 +2965,9 @@ bool BGTactics::selectObjective(bool reset) // Only go to Snowfall Graveyard if already close to it. // Need to fix AV script - if (!BgObjective && supporter && (alterValleyBG->GetAVNodeInfo(BG_AV_NODES_SNOWFALL_GRAVE).OwnerId == TEAM_ALLIANCE || - alterValleyBG->GetAVNodeInfo(BG_AV_NODES_SNOWFALL_GRAVE).OwnerId == TEAM_HORDE || alterValleyBG->GetAVNodeInfo(BG_AV_NODES_SNOWFALL_GRAVE).OwnerId == TEAM_OTHER)) + if (!BgObjective && supporter && + (alterValleyBG->GetAVNodeInfo(BG_AV_NODES_SNOWFALL_GRAVE).OwnerId == TEAM_ALLIANCE || + alterValleyBG->GetAVNodeInfo(BG_AV_NODES_SNOWFALL_GRAVE).OwnerId == TEAM_OTHER)) { if (GameObject* pGO = bg->GetBGObject(BG_AV_NODES_SNOWFALL_GRAVE)) if (bot->IsWithinDist(pGO, 200.f)) @@ -2967,19 +2981,20 @@ bool BGTactics::selectObjective(bool reset) if (!BgObjective && alterValleyBG->IsCaptainAlive(0)) { - if (Creature* pBalinda = bg->GetBGCreature(AV_NPC_A_CAPTAIN)) + if (Creature* pBalinda = bg->GetBGCreature(AV_CPLACE_TRIGGER16)) { if (pBalinda->getDeathState() != DeathState::Dead) { - uint32 attackCount = 0; - attackCount += getDefendersCount(AV_STONEHEARTH_WAITING_HORDE, 10.0f, false); + uint32 attackCount = getDefendersCount(AV_STONEHEARTH_WAITING_HORDE, 10.0f, false) + getDefendersCount(AV_STONEHEARTH_ATTACKING_HORDE, 10.0f, false); // prepare to attack Captain if (attackCount < 10 && !pBalinda->IsInCombat()) { // get in position to attack Captain - pos.Set(AV_STONEHEARTH_WAITING_HORDE.GetPositionX(), AV_STONEHEARTH_WAITING_HORDE.GetPositionY(), - AV_STONEHEARTH_WAITING_HORDE.GetPositionZ(), bg->GetMapId()); + pos.Set(AV_STONEHEARTH_WAITING_HORDE.GetPositionX(), + AV_STONEHEARTH_WAITING_HORDE.GetPositionY(), + AV_STONEHEARTH_WAITING_HORDE.GetPositionZ(), + bg->GetMapId()); std::ostringstream out; out << "Taking position at Stonehearth!"; @@ -2987,6 +3002,12 @@ bool BGTactics::selectObjective(bool reset) } else { + // they need help getting there (or did before I fixed the target creature, will leave in anyway, as it probably makes it more robust) + pos.Set(AV_STONEHEARTH_ATTACKING_HORDE.GetPositionX(), + AV_STONEHEARTH_ATTACKING_HORDE.GetPositionY(), + AV_STONEHEARTH_ATTACKING_HORDE.GetPositionZ(), + bg->GetMapId()); + std::ostringstream out; out << "Attacking Balinda!"; //bot->Say(out.str(), LANG_UNIVERSAL); @@ -3007,9 +3028,9 @@ bool BGTactics::selectObjective(bool reset) { for (const auto& objective : AV_HordeDefendObjectives) { - if (!BgObjective && alterValleyBG->GetAVNodeInfo(objective).OwnerId == TEAM_ALLIANCE) + if (!BgObjective && alterValleyBG->GetAVNodeInfo(objective.first).OwnerId == TEAM_ALLIANCE) { - if (GameObject* pGO = bg->GetBGObject(objective)) + if (GameObject* pGO = bg->GetBGObject(objective.second)) if (bot->IsWithinDist(pGO, 400.0f)) { BgObjective = pGO; @@ -3022,7 +3043,8 @@ bool BGTactics::selectObjective(bool reset) } // Mine capture (need paths & script fix) - if (!BgObjective && supporter && !endBoss && (alterValleyBG->GetMineOwner(AV_NORTH_MINE) == TEAM_ALLIANCE || alterValleyBG->GetMineOwner(AV_NORTH_MINE) == TEAM_OTHER) && + if (!BgObjective && supporter && !endBoss && + (alterValleyBG->GetMineOwner(AV_NORTH_MINE) == TEAM_ALLIANCE || alterValleyBG->GetMineOwner(AV_NORTH_MINE) == TEAM_OTHER) && alterValleyBG->GetAVNodeInfo(BG_AV_NODES_STORMPIKE_GRAVE).OwnerId != TEAM_ALLIANCE) { if (Creature* mBossNeutral = bg->GetBGCreature(AV_CPLACE_MINE_N_3)) @@ -3055,10 +3077,9 @@ bool BGTactics::selectObjective(bool reset) for (const auto& objective : AV_HordeAttackObjectives) { if ((!BgObjective/* || (supporter && objective.first == BG_AV_NODES_STONEHEART_BUNKER && !bg->IsActiveEvent(BG_AV_NODE_CAPTAIN_DEAD_A, 0))*/) && - (alterValleyBG->GetAVNodeInfo(objective).OwnerId == TEAM_ALLIANCE || alterValleyBG->GetAVNodeInfo(objective).TotalOwnerId == TEAM_ALLIANCE || - alterValleyBG->GetAVNodeInfo(objective).OwnerId == TEAM_OTHER)) + alterValleyBG->GetAVNodeInfo(objective.first).TotalOwnerId == TEAM_ALLIANCE)//need to check TotalOwnerId for attack objectives { - if (GameObject* pGO = bg->GetBGObject(objective)) + if (GameObject* pGO = bg->GetBGObject(objective.second)) { BgObjective = pGO; //std::ostringstream out; @@ -3073,11 +3094,13 @@ bool BGTactics::selectObjective(bool reset) { bool endBoss = false; // End boss - if (alterValleyBG->GetAVNodeInfo(BG_AV_NODES_ICEBLOOD_TOWER).OwnerId != TEAM_HORDE && alterValleyBG->GetAVNodeInfo(BG_AV_NODES_TOWER_POINT).OwnerId != TEAM_HORDE && - alterValleyBG->GetAVNodeInfo(BG_AV_NODES_FROSTWOLF_ETOWER).OwnerId != TEAM_HORDE && alterValleyBG->GetAVNodeInfo(BG_AV_NODES_FROSTWOLF_WTOWER).OwnerId != TEAM_HORDE && - alterValleyBG->GetAVNodeInfo(BG_AV_NODES_FROSTWOLF_HUT).OwnerId != TEAM_HORDE) + if (alterValleyBG->GetAVNodeInfo(BG_AV_NODES_ICEBLOOD_TOWER).TotalOwnerId != TEAM_HORDE && + alterValleyBG->GetAVNodeInfo(BG_AV_NODES_TOWER_POINT).TotalOwnerId != TEAM_HORDE && + alterValleyBG->GetAVNodeInfo(BG_AV_NODES_FROSTWOLF_ETOWER).TotalOwnerId != TEAM_HORDE && + alterValleyBG->GetAVNodeInfo(BG_AV_NODES_FROSTWOLF_WTOWER).TotalOwnerId != TEAM_HORDE && + alterValleyBG->GetAVNodeInfo(BG_AV_NODES_FROSTWOLF_HUT).TotalOwnerId != TEAM_HORDE) { - if (Creature* pDrek = bg->GetBGCreature(AV_NPC_H_BOSS)) + if (Creature* pDrek = bg->GetBGCreature(AV_CPLACE_TRIGGER19)) { BgObjective = pDrek; endBoss = true; @@ -3091,9 +3114,9 @@ bool BGTactics::selectObjective(bool reset) bool supporter = role < 3; // Only go to Snowfall Graveyard if already close to it. - if (!BgObjective && supporter && (alterValleyBG->GetAVNodeInfo(BG_AV_NODES_SNOWFALL_GRAVE).OwnerId == TEAM_HORDE || - alterValleyBG->GetAVNodeInfo(BG_AV_NODES_SNOWFALL_GRAVE).TotalOwnerId == TEAM_HORDE || - alterValleyBG->GetAVNodeInfo(BG_AV_NODES_SNOWFALL_GRAVE).TotalOwnerId == TEAM_OTHER)) + if (!BgObjective && supporter && + (alterValleyBG->GetAVNodeInfo(BG_AV_NODES_SNOWFALL_GRAVE).OwnerId == TEAM_HORDE || + alterValleyBG->GetAVNodeInfo(BG_AV_NODES_SNOWFALL_GRAVE).OwnerId == TEAM_OTHER)) { if (GameObject* pGO = bg->GetBGObject(BG_AV_NODES_SNOWFALL_GRAVE)) if (bot->IsWithinDist(pGO, 200.f)) @@ -3110,9 +3133,9 @@ bool BGTactics::selectObjective(bool reset) { for (const auto& objective : AV_AllianceDefendObjectives) { - if (!BgObjective && alterValleyBG->GetAVNodeInfo(objective).OwnerId == TEAM_HORDE) + if (!BgObjective && alterValleyBG->GetAVNodeInfo(objective.first).OwnerId == TEAM_HORDE) { - if (GameObject* pGO = bg->GetBGObject(objective)) + if (GameObject* pGO = bg->GetBGObject(objective.second)) { BgObjective = pGO; //std::ostringstream out; out << "Defending Node #" << objective.first; @@ -3123,8 +3146,8 @@ bool BGTactics::selectObjective(bool reset) } // Mine capture (need paths & script fix) - if (!BgObjective && supporter && !endBoss && (alterValleyBG->GetMineOwner(AV_SOUTH_MINE) == TEAM_HORDE || - alterValleyBG->GetMineOwner(AV_SOUTH_MINE) == TEAM_OTHER) && + if (!BgObjective && supporter && !endBoss && + (alterValleyBG->GetMineOwner(AV_SOUTH_MINE) == TEAM_HORDE || alterValleyBG->GetMineOwner(AV_SOUTH_MINE) == TEAM_OTHER) && alterValleyBG->GetAVNodeInfo(BG_AV_NODES_FROSTWOLF_GRAVE).TotalOwnerId != TEAM_HORDE) { if (Creature* mBossNeutral = bg->GetBGCreature(AV_CPLACE_MINE_S_3)) @@ -3158,17 +3181,18 @@ bool BGTactics::selectObjective(bool reset) if (alterValleyBG->IsCaptainAlive(1)) { - if (Creature* pGalvangar = bg->GetBGCreature(AV_NPC_H_CAPTAIN)) + if (Creature* pGalvangar = bg->GetBGCreature(AV_CPLACE_TRIGGER18)) { - uint32 attackCount = 0; - attackCount += getDefendersCount(AV_ICEBLOOD_GARRISON_WAITING_ALLIANCE, 10.0f, false); + uint32 attackCount = getDefendersCount(AV_ICEBLOOD_GARRISON_WAITING_ALLIANCE, 10.0f, false) + getDefendersCount(AV_ICEBLOOD_GARRISON_ATTACKING_ALLIANCE, 10.0f, false); // prepare to attack Captain if (attackCount < 10 && !pGalvangar->IsInCombat()) { // get in position to attack Captain - pos.Set(AV_ICEBLOOD_GARRISON_WAITING_ALLIANCE.GetPositionX(), AV_ICEBLOOD_GARRISON_WAITING_ALLIANCE.GetPositionY(), - AV_ICEBLOOD_GARRISON_WAITING_ALLIANCE.GetPositionZ(), bg->GetMapId()); + pos.Set(AV_ICEBLOOD_GARRISON_WAITING_ALLIANCE.GetPositionX(), + AV_ICEBLOOD_GARRISON_WAITING_ALLIANCE.GetPositionY(), + AV_ICEBLOOD_GARRISON_WAITING_ALLIANCE.GetPositionZ(), + bg->GetMapId()); //std::ostringstream out; //out << "Taking position at Iceblood Outpost!"; @@ -3176,6 +3200,12 @@ bool BGTactics::selectObjective(bool reset) } else { + // they need help getting there (or did before I fixed the target creature, will leave in anyway, as it probably makes it more robust) + pos.Set(AV_ICEBLOOD_GARRISON_ATTACKING_ALLIANCE.GetPositionX(), + AV_ICEBLOOD_GARRISON_ATTACKING_ALLIANCE.GetPositionY(), + AV_ICEBLOOD_GARRISON_ATTACKING_ALLIANCE.GetPositionZ(), + bg->GetMapId()); + //std::ostringstream out; // out << "Attacking Galvangar!"; //bot->Say(out.str(), LANG_UNIVERSAL); @@ -3187,11 +3217,9 @@ bool BGTactics::selectObjective(bool reset) for (const auto& objective : AV_AllianceAttackObjectives) { - if (alterValleyBG->GetAVNodeInfo(objective).OwnerId == TEAM_HORDE || - alterValleyBG->GetAVNodeInfo(objective).TotalOwnerId == TEAM_HORDE || - alterValleyBG->GetAVNodeInfo(objective).TotalOwnerId == TEAM_OTHER) + if (alterValleyBG->GetAVNodeInfo(objective.first).TotalOwnerId == TEAM_HORDE)//need to check TotalOwnerId for attack objectives { - if (GameObject* pGO = bg->GetBGObject(objective)) + if (GameObject* pGO = bg->GetBGObject(objective.second)) { float const distance = sqrt(bot->GetDistance(pGO)); if (attackObjectiveDistance > distance) @@ -4124,7 +4152,7 @@ bool BGTactics::moveToObjective() } // don't try to move if already close - if (sqrt(bot->GetDistance(pos.x, pos.y, pos.z)) < 5.0f) + if (sqrt(bot->GetDistance(pos.x, pos.y, pos.z)) < 2.0f) { resetObjective(); @@ -4134,8 +4162,8 @@ bool BGTactics::moveToObjective() //std::ostringstream out; out << "Moving to objective " << pos.x << ", " << pos.y << ", Distance: " << sServerFacade->GetDistance2d(bot, pos.x, pos.y); //bot->Say(out.str(), LANG_UNIVERSAL); - // more precise position for wsg - if (bgType == BATTLEGROUND_WS) + // more precise position for wsg and AV (flags in AV towers require precision) + if (bgType == BATTLEGROUND_WS || bgType == BATTLEGROUND_AV) return MoveTo(bot->GetMapId(), pos.x, pos.y, pos.z); else return MoveNear(bot->GetMapId(), pos.x, pos.y, pos.z, 3.0f); @@ -4162,99 +4190,84 @@ bool BGTactics::selectObjectiveWp(std::vector const& vPaths) if (bgType == BATTLEGROUND_WS /* && (bot->HasAura(BG_WS_SPELL_WARSONG_FLAG) || bot->HasAura(BG_WS_SPELL_SILVERWING_FLAG))*/) return wsgPaths(); - BattleBotPath* pClosestPath = nullptr; - uint32 closestPoint = 0; - float closestDistanceToTarget = FLT_MAX; - bool reverse = false; - float maxDistanceToPoint = 50.0f; - if (bgType == BATTLEGROUND_IC) - maxDistanceToPoint = 80.0f; + float chosenPathScore = FLT_MAX;//lower score is better + BattleBotPath* chosenPath = nullptr; + uint32 chosenPathPoint = 0; + bool chosenPathReverse = false; - for (auto const& pPath : vPaths) + float botDistanceLimit = 50.0f; // limit for how far path can be from bot + float botDistanceScoreSubtract = 8.0f; // path score modifier - lower = less likely to chose a further path (it's basically the distance from bot that's ignored) + float botDistanceScoreMultiply = 3.0f; // path score modifier - higher = less likely to chose a further path (it's basically a multiplier on distance from bot - makes distance from bot more signifcant than distance from destination) + + if (bgType == BATTLEGROUND_IC) + botDistanceLimit = 80.0f; + else if (bgType == BATTLEGROUND_AB) + { + botDistanceScoreSubtract = 2.0f; + botDistanceScoreMultiply = 4.0f; + } + + for (auto const& path : vPaths) { // skip mine paths of own faction - if (bot->GetTeamId() == TEAM_ALLIANCE && std::find(vPaths_AllyMine.begin(), vPaths_AllyMine.end(), pPath) != vPaths_AllyMine.end()) + if (bot->GetTeamId() == TEAM_ALLIANCE && std::find(vPaths_AllyMine.begin(), vPaths_AllyMine.end(), path) != vPaths_AllyMine.end()) continue; - if (bot->GetTeamId() == TEAM_HORDE && std::find(vPaths_HordeMine.begin(), vPaths_HordeMine.end(), pPath) != vPaths_HordeMine.end()) + if (bot->GetTeamId() == TEAM_HORDE && std::find(vPaths_HordeMine.begin(), vPaths_HordeMine.end(), path) != vPaths_HordeMine.end()) continue; - BattleBotWaypoint& lastPoint = ((*pPath)[pPath->size() - 1]); - float const distanceFromPathEndToTarget = sqrt(Position(pos.x, pos.y, pos.z, 0.f).GetExactDist(lastPoint.x, lastPoint.y, lastPoint.z)); - if (closestDistanceToTarget > distanceFromPathEndToTarget) + BattleBotWaypoint& startPoint = ((*path)[0]); + float const startPointDistToDestination = sqrt(Position(pos.x, pos.y, pos.z, 0.f).GetExactDist(startPoint.x, startPoint.y, startPoint.z)); + BattleBotWaypoint& endPoint = ((*path)[path->size() - 1]); + float const endPointDistToDestination = sqrt(Position(pos.x, pos.y, pos.z, 0.f).GetExactDist(endPoint.x, endPoint.y, endPoint.z)); + + bool reverse = startPointDistToDestination < endPointDistToDestination; + + // dont travel reverse if it's a reverse paths + if (reverse && std::find(vPaths_NoReverseAllowed.begin(), vPaths_NoReverseAllowed.end(), path) != vPaths_NoReverseAllowed.end()) + continue; + + int closestPointIndex = -1; + float closestPointDistToBot = FLT_MAX; + for (uint32 i = 0; i < path->size(); i++) { - float closestDistanceFromMeToPoint = FLT_MAX; - - for (uint32 i = 0; i < pPath->size(); i++) + BattleBotWaypoint& waypoint = ((*path)[i]); + float const distToBot = sqrt(bot->GetDistance(waypoint.x, waypoint.y, waypoint.z)); + if (closestPointDistToBot > distToBot) { - BattleBotWaypoint& waypoint = ((*pPath)[i]); - float const distanceFromMeToPoint = sqrt(bot->GetDistance(waypoint.x, waypoint.y, waypoint.z)); - if (distanceFromMeToPoint < maxDistanceToPoint && closestDistanceFromMeToPoint > distanceFromMeToPoint) - { - reverse = false; - pClosestPath = pPath; - closestPoint = i; - closestDistanceToTarget = distanceFromPathEndToTarget; - closestDistanceFromMeToPoint = distanceFromMeToPoint; - } + closestPointDistToBot = distToBot; + closestPointIndex = i; } } - // skip no reverse paths - if (std::find(vPaths_NoReverseAllowed.begin(), vPaths_NoReverseAllowed.end(), pPath) != vPaths_NoReverseAllowed.end()) + // don't pick path where bot is already closest to the paths closest point to target (it means path cant lead it anywhere) + // don't pick path where closest point is too far away + if (closestPointIndex == (reverse ? 0 : path->size() - 1) || closestPointDistToBot > botDistanceLimit) continue; - // skip mine paths of own faction - if (bot->GetTeamId() == TEAM_ALLIANCE && std::find(vPaths_AllyMine.begin(), vPaths_AllyMine.end(), pPath) != vPaths_AllyMine.end()) - continue; - if (bot->GetTeamId() == TEAM_HORDE && std::find(vPaths_HordeMine.begin(), vPaths_HordeMine.end(), pPath) != vPaths_HordeMine.end()) - continue; + // creates a score based on dist-to-bot and dist-to-destination, where lower is better, and dist-to-bot is more important (when its beyond a certain distance) + // dist-to-bot is more important because otherwise they cant reach it at all (or will fly through air with MM::MovePoint()), also bot may need to use multiple + // paths (one after another) anyway + float distToDestination = reverse ? startPointDistToDestination : endPointDistToDestination; + float pathScore = (closestPointDistToBot < botDistanceScoreSubtract ? 0.0f : ((closestPointDistToBot - botDistanceScoreSubtract) * botDistanceScoreMultiply)) + distToDestination; - { - BattleBotWaypoint& firstPoint = ((*pPath)[0]); - float const distanceFromPathBeginToTarget = sqrt(Position(pos.x, pos.y, pos.z, 0).GetExactDist(firstPoint.x, firstPoint.y, firstPoint.z)); - if (closestDistanceToTarget > distanceFromPathBeginToTarget) - { - float closestDistanceFromMeToPoint = FLT_MAX; + //LOG_INFO("playerbots", "bot={}\t{:6.1f}\t{:4.1f}\t{:4.1f}\t{}", bot->GetName(), pathScore, closestPointDistToBot, distToDestination, vPaths_AB_name[pathNum]); - for (uint32 i = 0; i < pPath->size(); i++) - { - BattleBotWaypoint& waypoint = ((*pPath)[i]); - float const distanceFromMeToPoint = sqrt(bot->GetDistance(waypoint.x, waypoint.y, waypoint.z)); - if (distanceFromMeToPoint < maxDistanceToPoint && closestDistanceFromMeToPoint > distanceFromMeToPoint) - { - reverse = true; - pClosestPath = pPath; - closestPoint = i; - closestDistanceToTarget = distanceFromPathBeginToTarget; - closestDistanceFromMeToPoint = distanceFromMeToPoint; - } - } - } + if (chosenPathScore > pathScore) { + chosenPathScore = pathScore; + chosenPath = path; + chosenPathPoint = closestPointIndex; + chosenPathReverse = reverse; } } - if (!pClosestPath) + if (!chosenPath) return false; - // Prevent picking last point of path. - // It means we are already there. - if (reverse) - { - if (closestPoint == 0) - return false; - } - else - { - if (closestPoint == pClosestPath->size() - 1) - return false; - } + //LOG_INFO("playerbots", "bot={} {}", bot->GetName(), vPaths_AB_name[chosenPathNum]); - BattleBotPath* currentPath = pClosestPath; - uint32 currentPoint = reverse ? closestPoint + 1 : closestPoint - 1; - - return moveToObjectiveWp(currentPath, currentPoint, reverse); + return moveToObjectiveWp(chosenPath, chosenPathPoint, chosenPathReverse); return false; } @@ -4961,9 +4974,9 @@ bool ArenaTactics::moveToCenter(Battleground* bg) case BATTLEGROUND_DS: if (!MoveTo(bg->GetMapId(), 1291.58f + frand(-5, +5), 790.87f + frand(-5, +5), 7.8f, false, true)) { // they like to hang around at the tip of the pipes doing nothing, so we just teleport them down - if (bot->GetDistance(1333.07f, 817.18f, 13.35f) < 2) + if (bot->GetDistance(1333.07f, 817.18f, 13.35f) < 4) bot->TeleportTo(bg->GetMapId(), 1330.96f, 816.75f, 3.2f, bot->GetOrientation()); - if (bot->GetDistance(1250.13f, 764.79f, 13.34f) < 2) + if (bot->GetDistance(1250.13f, 764.79f, 13.34f) < 4) bot->TeleportTo(bg->GetMapId(), 1252.19f, 765.41f, 3.2f, bot->GetOrientation()); } break; diff --git a/src/strategy/actions/MovementActions.cpp b/src/strategy/actions/MovementActions.cpp index f86d539e..6a67e01b 100644 --- a/src/strategy/actions/MovementActions.cpp +++ b/src/strategy/actions/MovementActions.cpp @@ -163,7 +163,9 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, // } bool generatePath = !bot->HasAuraType(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED) && !bot->IsFlying() && !bot->HasUnitMovementFlag(MOVEMENTFLAG_SWIMMING) && !bot->IsInWater(); - if (!generatePath) { + bool disableMoveSplinePath = sPlayerbotAIConfig->disableMoveSplinePath >= 2 || + (sPlayerbotAIConfig->disableMoveSplinePath == 1 && bot->InBattleground()); + if (disableMoveSplinePath || !generatePath) { float distance = bot->GetExactDist(x, y, z); if (distance > sPlayerbotAIConfig->contactDistance) { diff --git a/src/strategy/actions/UseMeetingStoneAction.cpp b/src/strategy/actions/UseMeetingStoneAction.cpp index 0f908530..f07ca217 100644 --- a/src/strategy/actions/UseMeetingStoneAction.cpp +++ b/src/strategy/actions/UseMeetingStoneAction.cpp @@ -179,28 +179,25 @@ bool SummonAction::Teleport(Player* summoner, Player* player) if (sPlayerbotAIConfig->botRepairWhenSummon) // .conf option to repair bot gear when summoned 0 = off, 1 = on bot->DurabilityRepairAll(false, 1.0f, false); - if (sPlayerbotAIConfig->botReviveWhenSummon < 2) + if (master->IsInCombat() && !sPlayerbotAIConfig->allowSummonInCombat) { - if (master->IsInCombat()) - { - botAI->TellError("You cannot summon me while you're in combat"); - return false; - } - - if (!master->IsAlive()) - { - botAI->TellError("You cannot summon me while you're dead"); - return false; - } - - if (bot->isDead() && !bot->HasPlayerFlag(PLAYER_FLAGS_GHOST)) - { - botAI->TellError("You cannot summon me while I'm dead, you need to release my spirit first"); - return false; - } + botAI->TellError("You cannot summon me while you're in combat"); + return false; } - if (sPlayerbotAIConfig->botReviveWhenSummon > 0 && bot->isDead()) + if (!master->IsAlive() && !sPlayerbotAIConfig->allowSummonWhenMasterIsDead) + { + botAI->TellError("You cannot summon me while you're dead"); + return false; + } + + if (bot->isDead() && !bot->HasPlayerFlag(PLAYER_FLAGS_GHOST) && !sPlayerbotAIConfig->allowSummonWhenBotIsDead) + { + botAI->TellError("You cannot summon me while I'm dead, you need to release my spirit first"); + return false; + } + + if (sPlayerbotAIConfig->botReviveWhenSummon == 2 || (sPlayerbotAIConfig->botReviveWhenSummon == 1 && !master->IsInCombat() && master->IsAlive())) { bot->ResurrectPlayer(1.0f, false); botAI->TellMasterNoFacing("I live, again!"); diff --git a/src/strategy/values/AttackersValue.cpp b/src/strategy/values/AttackersValue.cpp index 527b8def..cae2223a 100644 --- a/src/strategy/values/AttackersValue.cpp +++ b/src/strategy/values/AttackersValue.cpp @@ -47,8 +47,21 @@ GuidVector AttackersValue::Calculate() if (bot->duel && bot->duel->Opponent) result.push_back(bot->duel->Opponent->GetGUID()); - - return result; + + // workaround for bots of same faction not fighting in arena + if (bot->InArena()) + { + GuidVector possibleTargets = AI_VALUE(GuidVector, "possible targets"); + for (ObjectGuid const guid : possibleTargets) + { + Unit* unit = botAI->GetUnit(guid); + if (unit && unit->IsPlayer() && IsValidTarget(unit, bot)) { + result.push_back(unit->GetGUID()); + } + } + } + + return result; } void AttackersValue::AddAttackersOf(Group* group, std::unordered_set& targets)