diff --git a/README.md b/README.md index 02e557b0..c7e70bd7 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,8 @@ Welcome to the Playerbots Module for AzerothCore, a work in progress project bas If you encounter any errors or experience crashes, we kindly request that you report them as GitHub issues. Your valuable feedback will help us improve and enhance this project collaboratively. +You can also get more information in our [discord](https://discord.gg/NQm5QShwf9). + ## Installation Please note that this module requires specific custom changes to AzerothCore. To ensure compatibility, you must compile it with a custom branch from my fork, which can be found here: [liyunfan1223/azerothcore-wotlk/tree/Playerbot](https://github.com/liyunfan1223/azerothcore-wotlk/tree/Playerbot). @@ -48,7 +50,9 @@ It's essential to note that there is still a significant amount of work to be do ## Addon -For enhanced control over the bots and to simplify command usage, you can also make use of our addon: [Unbot Addon](https://github.com/liyunfan1223/unbot-addon). Currently, this addon offers better support only for Simplified Chinese client. +For enhanced control over the bots and to simplify command usage, you can also make use of our addon: +- Chinese version: [Unbot Addon (zh)](https://github.com/liyunfan1223/unbot-addon). +- English version (maintained by @Revision): [Unbot Addon (en)](https://github.com/noisiver/unbot-addon/tree/english). ## Frequently Asked Questions diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index 88cffdce..fa867e68 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -97,6 +97,10 @@ AiPlayerbot.SummonWhenGroup = 1 AiPlayerbot.RandomBotShowHelmet = 1 AiPlayerbot.RandomBotShowCloak = 1 +# Fix the level of random bot (won't level up by grinding) +# Default: 0 (disable) +AiPlayerbot.RandomBotFixedLevel = 0 + # Disable random levels for randombots # Every bots started on the specified level and level up by killing mobs. AiPlayerbot.DisableRandomLevels = 0 @@ -125,7 +129,7 @@ AiPlayerbot.MinEnchantingBotLevel = 60 AiPlayerbot.LimitEnchantExpansion = 1 # Randombots checking players gear score level and deny the group invite if it's too low -# Default: 1 (enabled) +# Default: 0 (disabled) AiPlayerbot.GearScoreCheck = 0 # Quest that will be completed and rewarded to all random bots @@ -338,8 +342,8 @@ AiPlayerbot.AutoSaveMana = 1 AiPlayerbot.SaveManaThreshold = 60 # Enable auto avoid aoe (experimental) -# Default: 0 (disable) -AiPlayerbot.AutoAvoidAoe = 0 +# Default: 1 (enable) +AiPlayerbot.AutoAvoidAoe = 1 # Tell which spell is avoiding (experimental) # Default: 1 (enable) diff --git a/src/PlayerbotAIConfig.cpp b/src/PlayerbotAIConfig.cpp index 87b85830..96142144 100644 --- a/src/PlayerbotAIConfig.cpp +++ b/src/PlayerbotAIConfig.cpp @@ -91,7 +91,7 @@ bool PlayerbotAIConfig::Initialize() mediumMana = sConfigMgr->GetOption("AiPlayerbot.MediumMana", 40); autoSaveMana = sConfigMgr->GetOption("AiPlayerbot.AutoSaveMana", true); saveManaThreshold = sConfigMgr->GetOption("AiPlayerbot.SaveManaThreshold", 60); - autoAvoidAoe = sConfigMgr->GetOption("AiPlayerbot.AutoAvoidAoe", false); + autoAvoidAoe = sConfigMgr->GetOption("AiPlayerbot.AutoAvoidAoe", true); tellWhenAvoidAoe = sConfigMgr->GetOption("AiPlayerbot.TellWhenAvoidAoe", true); randomGearLoweringChance = sConfigMgr->GetOption("AiPlayerbot.RandomGearLoweringChance", 0.15f); @@ -265,6 +265,7 @@ bool PlayerbotAIConfig::Initialize() // SPP switches enableGreet = sConfigMgr->GetOption("AiPlayerbot.EnableGreet", true); summonWhenGroup = sConfigMgr->GetOption("AiPlayerbot.SummonWhenGroup", true); + randomBotFixedLevel = sConfigMgr->GetOption("AiPlayerbot.RandomBotFixedLevel", false); disableRandomLevels = sConfigMgr->GetOption("AiPlayerbot.DisableRandomLevels", false); randomBotRandomPassword = sConfigMgr->GetOption("AiPlayerbot.RandomBotRandomPassword", true); downgradeMaxLevelBot = sConfigMgr->GetOption("AiPlayerbot.DowngradeMaxLevelBot", true); diff --git a/src/PlayerbotAIConfig.h b/src/PlayerbotAIConfig.h index 5356bc49..cc5e72be 100644 --- a/src/PlayerbotAIConfig.h +++ b/src/PlayerbotAIConfig.h @@ -174,6 +174,7 @@ class PlayerbotAIConfig bool summonWhenGroup; bool randomBotShowHelmet; bool randomBotShowCloak; + bool randomBotFixedLevel; bool disableRandomLevels; uint32 playerbotsXPrate; bool disableDeathKnightLogin; diff --git a/src/PlayerbotFactory.cpp b/src/PlayerbotFactory.cpp index 6f7f0dfb..672082c1 100644 --- a/src/PlayerbotFactory.cpp +++ b/src/PlayerbotFactory.cpp @@ -2502,8 +2502,7 @@ void PlayerbotFactory::InitAmmo() case ITEM_SUBCLASS_WEAPON_CROSSBOW: subClass = ITEM_SUBCLASS_ARROW; break; - case ITEM_SUBCLASS_WEAPON_THROWN: - subClass = ITEM_SUBCLASS_THROWN; + default: break; } @@ -2512,7 +2511,7 @@ void PlayerbotFactory::InitAmmo() uint32 entry = sRandomItemMgr->GetAmmo(level, subClass); uint32 count = bot->GetItemCount(entry); - uint32 maxCount = 5000; + uint32 maxCount = 6000; if (count < maxCount / 2) { @@ -3879,10 +3878,9 @@ float PlayerbotFactory::CalculateItemScore(uint32 item_id, Player* bot) score *= 0.1; } // spec with double hand - // fury with titan's grip, fury without duel wield, arms, bear, retribution, blood dk - if (isDoubleHand && - ((cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY && bot->CanTitanGrip()) || - (cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY && !bot->CanDualWield()) || + // fury without duel wield, arms, bear, retribution, blood dk + if (isDoubleHand && + ((cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY && !bot->CanDualWield()) || (cls == CLASS_WARRIOR && tab == WARRIOR_TAB_ARMS) || (cls == CLASS_DRUID && tab == 1) || (cls == CLASS_PALADIN && tab == 2) || @@ -3890,6 +3888,11 @@ float PlayerbotFactory::CalculateItemScore(uint32 item_id, Player* bot) (cls == CLASS_SHAMAN && tab == 1 && !bot->CanDualWield()))) { score *= 10; } + // fury with titan's grip + if (isDoubleHand && proto->SubClass != ITEM_SUBCLASS_WEAPON_POLEARM && + (cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY && bot->CanTitanGrip())) { + score *= 10; + } } if (proto->Class == ITEM_CLASS_WEAPON) { if (cls == CLASS_HUNTER && proto->SubClass == ITEM_SUBCLASS_WEAPON_THROWN) { diff --git a/src/PlayerbotMgr.cpp b/src/PlayerbotMgr.cpp index 97659583..099350f2 100644 --- a/src/PlayerbotMgr.cpp +++ b/src/PlayerbotMgr.cpp @@ -448,6 +448,12 @@ void PlayerbotHolder::OnBotLogin(Player* const bot) uint32 accountId = bot->GetSession()->GetAccountId(); bool isRandomAccount = sPlayerbotAIConfig->IsInRandomAccountList(accountId); + if (isRandomAccount && sPlayerbotAIConfig->randomBotFixedLevel) { + bot->SetPlayerFlag(PLAYER_FLAGS_NO_XP_GAIN); + } else if (isRandomAccount && !sPlayerbotAIConfig->randomBotFixedLevel) { + bot->RemovePlayerFlag(PLAYER_FLAGS_NO_XP_GAIN); + } + bot->SaveToDB(false, false); if (master && isRandomAccount && master->GetLevel() < bot->GetLevel()) { // PlayerbotFactory factory(bot, master->getLevel()); diff --git a/src/RandomItemMgr.cpp b/src/RandomItemMgr.cpp index f2ff7bcb..e02b865b 100644 --- a/src/RandomItemMgr.cpp +++ b/src/RandomItemMgr.cpp @@ -2159,6 +2159,9 @@ void RandomItemMgr::BuildEquipCacheNew() if (IsTestItem(itemId)) { continue; } + if (itemId == 22784) { // Sunwell Orb + continue; + } equipCacheNew[proto->RequiredLevel][proto->InventoryType].push_back(itemId); } } @@ -2199,9 +2202,7 @@ RandomItemList RandomItemMgr::Query(uint32 level, uint8 clazz, uint8 slot, uint3 void RandomItemMgr::BuildAmmoCache() { - uint32 maxLevel = sPlayerbotAIConfig->randomBotMaxLevel; - if (maxLevel > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)) - maxLevel = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); + uint32 maxLevel = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); LOG_INFO("server.loading", "Building ammo cache for {} levels", maxLevel); @@ -2238,9 +2239,9 @@ uint32 RandomItemMgr::GetAmmo(uint32 level, uint32 subClass) void RandomItemMgr::BuildPotionCache() { - uint32 maxLevel = sPlayerbotAIConfig->randomBotMaxLevel; - if (maxLevel > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)) - maxLevel = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); + uint32 maxLevel = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); + // if (maxLevel > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)) + // maxLevel = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); LOG_INFO("server.loading", "Building potion cache for {} levels", maxLevel); diff --git a/src/RandomPlayerbotMgr.cpp b/src/RandomPlayerbotMgr.cpp index 1c320ebd..334b6499 100644 --- a/src/RandomPlayerbotMgr.cpp +++ b/src/RandomPlayerbotMgr.cpp @@ -1190,6 +1190,10 @@ void RandomPlayerbotMgr::RandomTeleport(Player* bot, std::vector& AreaTableEntry const* zone = sAreaTableStore.LookupEntry(map->GetZoneId(bot->GetPhaseMask(), x, y, z)); if (!zone) continue; + + AreaTableEntry const* area = sAreaTableStore.LookupEntry(map->GetAreaId(bot->GetPhaseMask(), x, y, z)); + if (!area) + continue; // Do not teleport to enemy zones if level is low if (zone->team == 4 && bot->GetTeamId() == TEAM_ALLIANCE) @@ -1218,9 +1222,14 @@ void RandomPlayerbotMgr::RandomTeleport(Player* bot, std::vector& if (bot->GetLevel() <= 18 && (loc.GetMapId() != pInfo->mapId || dis > 10000.0f)) { continue; } - - LOG_INFO("playerbots", "Random teleporting bot {} (level {}) to {} {},{},{} ({}/{} locations)", - bot->GetName().c_str(), bot->GetLevel(), zone->area_name[0], x, y, z, i + 1, tlocs.size()); + LocaleConstant locale = sWorld->GetDefaultDbcLocale(); + + LOG_INFO("playerbots", "Random teleporting bot {} (level {}) to Map: {} ({}) Zone: {} ({}) Area: {} ({}) {},{},{} ({}/{} locations)", + bot->GetName().c_str(), bot->GetLevel(), + map->GetId(), map->GetMapName(), + zone->ID, zone->area_name[locale], + area->ID, area->area_name[locale], + x, y, z, i + 1, tlocs.size()); if (hearth) { @@ -1248,9 +1257,7 @@ void RandomPlayerbotMgr::RandomTeleport(Player* bot, std::vector& void RandomPlayerbotMgr::PrepareTeleportCache() { - uint8 maxLevel = sPlayerbotAIConfig->randomBotMaxLevel; - if (maxLevel > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)) - maxLevel = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); + uint32 maxLevel = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); LOG_INFO("playerbots", "Preparing random teleport caches for {} levels...", maxLevel); diff --git a/src/TravelMgr.cpp b/src/TravelMgr.cpp index 755d1103..f010df99 100644 --- a/src/TravelMgr.cpp +++ b/src/TravelMgr.cpp @@ -1495,6 +1495,8 @@ void TravelTarget::setStatus(TravelStatus status) break; case TRAVEL_STATUS_COOLDOWN: statusTime = tDestination->getCooldownDelay(); + default: + break; } } @@ -3687,7 +3689,7 @@ void TravelMgr::LoadQuestTravelTable() } } else - "all"; + out << "all"; out << "\n"; } diff --git a/src/strategy/Engine.cpp b/src/strategy/Engine.cpp index 12328b6d..cef411c6 100644 --- a/src/strategy/Engine.cpp +++ b/src/strategy/Engine.cpp @@ -144,7 +144,7 @@ bool Engine::DoNextAction(Unit* unit, uint32 depth, bool minimal) ActionBasket* basket = nullptr; time_t currentTime = time(nullptr); - aiObjectContext->Update(); + // aiObjectContext->Update(); ProcessTriggers(minimal); PushDefaultActions(); @@ -469,7 +469,7 @@ bool Engine::HasStrategy(std::string const name) void Engine::ProcessTriggers(bool minimal) { - std::unordered_map fires; + // std::unordered_map fires; for (std::vector::iterator i = triggers.begin(); i != triggers.end(); i++) { TriggerNode* node = *i; @@ -499,21 +499,22 @@ void Engine::ProcessTriggers(bool minimal) if (!event) continue; - fires[trigger] = event; + // fires[trigger] = event; LogAction("T:%s", trigger->getName().c_str()); + MultiplyAndPush(node->getHandlers(), 0.0f, false, event, "trigger"); } } - for (std::vector::iterator i = triggers.begin(); i != triggers.end(); i++) - { - TriggerNode* node = *i; - Trigger* trigger = node->getTrigger(); - Event event = fires[trigger]; - if (!event) - continue; + // for (std::vector::iterator i = triggers.begin(); i != triggers.end(); i++) + // { + // TriggerNode* node = *i; + // Trigger* trigger = node->getTrigger(); + // if (fires.find(trigger) == fires.end()) + // continue; - MultiplyAndPush(node->getHandlers(), 0.0f, false, event, "trigger"); - } + // Event event = fires[trigger]; + + // } for (std::vector::iterator i = triggers.begin(); i != triggers.end(); i++) { diff --git a/src/strategy/actions/ChangeTalentsAction.cpp b/src/strategy/actions/ChangeTalentsAction.cpp index e0f3b8db..69a6b19f 100644 --- a/src/strategy/actions/ChangeTalentsAction.cpp +++ b/src/strategy/actions/ChangeTalentsAction.cpp @@ -23,10 +23,15 @@ bool ChangeTalentsAction::Execute(Event event) if (param.find("help") != std::string::npos) { out << TalentsHelp(); } else if (param.find("switch") != std::string::npos) { - if (param.find("switch 1")) { + if (param.find("switch 1") != std::string::npos) { bot->ActivateSpec(0); out << "Active first talent"; - } else if (param.find("switch 2")) { + } else if (param.find("switch 2") != std::string::npos) { + if (bot->GetSpecsCount() == 1 && bot->GetLevel() >= sWorld->getIntConfig(CONFIG_MIN_DUALSPEC_LEVEL)) + { + bot->CastSpell(bot, 63680, true, nullptr, nullptr, bot->GetGUID()); + bot->CastSpell(bot, 63624, true, nullptr, nullptr, bot->GetGUID()); + } bot->ActivateSpec(1); out << "Active second talent"; } diff --git a/src/strategy/actions/ChatActionContext.h b/src/strategy/actions/ChatActionContext.h index 2930dae0..8fb48c64 100644 --- a/src/strategy/actions/ChatActionContext.h +++ b/src/strategy/actions/ChatActionContext.h @@ -115,6 +115,7 @@ class ChatActionContext : public NamedObjectContext creators["de"] = &ChatActionContext::dead; creators["trainer"] = &ChatActionContext::trainer; creators["maintenance"] = &ChatActionContext::maintenance; + creators["remove glyph"] = &ChatActionContext::remove_glyph; creators["autogear"] = &ChatActionContext::autogear; creators["equip upgrade"] = &ChatActionContext::equip_upgrade; creators["attack my target"] = &ChatActionContext::attack_my_target; @@ -215,6 +216,7 @@ class ChatActionContext : public NamedObjectContext static Action* attack_my_target(PlayerbotAI* botAI) { return new AttackMyTargetAction(botAI); } static Action* trainer(PlayerbotAI* botAI) { return new TrainerAction(botAI); } static Action* maintenance(PlayerbotAI* botAI) { return new MaintenanceAction(botAI); } + static Action* remove_glyph(PlayerbotAI* botAI) { return new RemoveGlyphAction(botAI); } static Action* autogear(PlayerbotAI* botAI) { return new AutoGearAction(botAI); } static Action* equip_upgrade(PlayerbotAI* botAI) { return new EquipUpgradeAction(botAI); } static Action* co(PlayerbotAI* botAI) { return new ChangeCombatStrategyAction(botAI); } diff --git a/src/strategy/actions/ChatShortcutActions.cpp b/src/strategy/actions/ChatShortcutActions.cpp index 92e259fd..be1c4496 100644 --- a/src/strategy/actions/ChatShortcutActions.cpp +++ b/src/strategy/actions/ChatShortcutActions.cpp @@ -33,6 +33,7 @@ bool FollowChatShortcutAction::Execute(Event event) botAI->Reset(); botAI->ChangeStrategy("+follow,-passive,-grind", BOT_STATE_NON_COMBAT); botAI->ChangeStrategy("-follow,-passive,-grind", BOT_STATE_COMBAT); + botAI->GetAiObjectContext()->GetValue("prioritized targets")->Set({}); PositionMap& posMap = context->GetValue("position")->Get(); PositionInfo pos = posMap["return"]; diff --git a/src/strategy/actions/MovementActions.cpp b/src/strategy/actions/MovementActions.cpp index 7ab12027..f2652168 100644 --- a/src/strategy/actions/MovementActions.cpp +++ b/src/strategy/actions/MovementActions.cpp @@ -1645,34 +1645,107 @@ bool AvoidAoeAction::AvoidUnitWithDamageAura() return false; } -bool AvoidAoeAction::FleePosition(Position pos, float radius, std::string name) +Position AvoidAoeAction::BestPositionForMelee(Position pos, float radius) { Unit* currentTarget = AI_VALUE(Unit*, "current target"); - std::vector possibleAngles; + std::vector possibleAngles; if (currentTarget) { // Normally, move to left or right is the best position - float angleLeft = bot->GetAngle(currentTarget) + M_PI / 2; - float angleRight = bot->GetAngle(currentTarget) - M_PI / 2; - possibleAngles.push_back(angleLeft); - possibleAngles.push_back(angleRight); + bool isTanking = (currentTarget->CanFreeMove()) && (currentTarget->GetVictim() == bot); + float angle = bot->GetAngle(currentTarget); + float angleLeft = angle + (float)M_PI / 2; + float angleRight = angle - (float)M_PI / 2; + possibleAngles.push_back({angleLeft, false}); + possibleAngles.push_back({angleRight, false}); + possibleAngles.push_back({angle, true}); + if (isTanking) { + possibleAngles.push_back({angle + (float)M_PI, false}); + possibleAngles.push_back({bot->GetAngle(&pos) - (float)M_PI, false}); + } } else { - float angleTo = bot->GetAngle(&pos) - M_PI; - possibleAngles.push_back(angleTo); + float angleTo = bot->GetAngle(&pos) - (float)M_PI; + possibleAngles.push_back({angleTo, false}); } float farestDis = 0.0f; Position bestPos; - for (float &angle : possibleAngles) { + for (CheckAngle &checkAngle : possibleAngles) { + float angle = checkAngle.angle; + bool strict = checkAngle.strict; float fleeDis = sPlayerbotAIConfig->fleeDistance; Position fleePos{bot->GetPositionX() + cos(angle) * fleeDis, bot->GetPositionY() + sin(angle) * fleeDis, bot->GetPositionZ()}; - // todo (Yunfan): check carefully + if (strict && currentTarget + && fleePos.GetExactDist(currentTarget) - currentTarget->GetCombatReach() > sPlayerbotAIConfig->tooCloseDistance) { + continue; + } if (pos.GetExactDist(fleePos) > farestDis) { farestDis = pos.GetExactDist(fleePos); bestPos = fleePos; } } if (farestDis > 0.0f) { + return bestPos; + } + return Position(); +} + +Position AvoidAoeAction::BestPositionForRanged(Position pos, float radius) +{ + Unit* currentTarget = AI_VALUE(Unit*, "current target"); + std::vector possibleAngles; + float angleToTarget = 0.0f; + float angleFleeFromCenter = bot->GetAngle(&pos) - (float)M_PI; + if (currentTarget) { + // Normally, move to left or right is the best position + angleToTarget = bot->GetAngle(currentTarget); + float angleLeft = angleToTarget + (float)M_PI / 2; + float angleRight = angleToTarget - (float)M_PI / 2; + possibleAngles.push_back({angleLeft, false}); + possibleAngles.push_back({angleRight, false}); + possibleAngles.push_back({angleToTarget + (float)M_PI, true}); + possibleAngles.push_back({angleToTarget, true}); + possibleAngles.push_back({angleFleeFromCenter, true}); + } else { + possibleAngles.push_back({angleFleeFromCenter, false}); + } + float farestDis = 0.0f; + Position bestPos; + for (CheckAngle &checkAngle : possibleAngles) { + float angle = checkAngle.angle; + bool strict = checkAngle.strict; + float fleeDis = sPlayerbotAIConfig->fleeDistance; + Position fleePos{bot->GetPositionX() + cos(angle) * fleeDis, + bot->GetPositionY() + sin(angle) * fleeDis, + bot->GetPositionZ()}; + if (strict && currentTarget + && fleePos.GetExactDist(currentTarget) - currentTarget->GetCombatReach() > sPlayerbotAIConfig->spellDistance) { + continue; + } + if (strict && currentTarget + && fleePos.GetExactDist(currentTarget) - currentTarget->GetCombatReach() < (sPlayerbotAIConfig->tooCloseDistance)) { + continue; + } + if (pos.GetExactDist(fleePos) > farestDis) { + farestDis = pos.GetExactDist(fleePos); + bestPos = fleePos; + } + } + if (farestDis > 0.0f) { + return bestPos; + } + return Position(); +} + +bool AvoidAoeAction::FleePosition(Position pos, float radius, std::string name) +{ + Position bestPos; + if (botAI->IsMelee(bot)) { + bestPos = BestPositionForMelee(pos, radius); + } else { + bestPos = BestPositionForRanged(pos, radius); + } + if (bestPos != Position()) { if (MoveTo(bot->GetMapId(), bestPos.GetPositionX(), bestPos.GetPositionY(), bestPos.GetPositionZ(), false, false, true)) { if (sPlayerbotAIConfig->tellWhenAvoidAoe && lastTellTimer < time(NULL) - 10) { lastTellTimer = time(NULL); diff --git a/src/strategy/actions/MovementActions.h b/src/strategy/actions/MovementActions.h index c53bf25e..f26991a4 100644 --- a/src/strategy/actions/MovementActions.h +++ b/src/strategy/actions/MovementActions.h @@ -78,11 +78,14 @@ class AvoidAoeAction : public MovementAction bool AvoidAuraWithDynamicObj(); bool AvoidGameObjectWithDamage(); bool AvoidUnitWithDamageAura(); - // Position PositionForTank(Position pos, float radius); - // Position PositionForMelee(Position pos, float radius); - // Position PositionForRanged(Position pos, float radius); + Position BestPositionForMelee(Position pos, float radius); + Position BestPositionForRanged(Position pos, float radius); bool FleePosition(Position pos, float radius, std::string name); time_t lastTellTimer = 0; + struct CheckAngle { + float angle; + bool strict; + }; }; class RunAwayAction : public MovementAction diff --git a/src/strategy/actions/TrainerAction.cpp b/src/strategy/actions/TrainerAction.cpp index de3fea5d..2454e563 100644 --- a/src/strategy/actions/TrainerAction.cpp +++ b/src/strategy/actions/TrainerAction.cpp @@ -175,6 +175,15 @@ bool MaintenanceAction::Execute(Event event) return true; } +bool RemoveGlyphAction::Execute(Event event) +{ + for (uint32 slotIndex = 0; slotIndex < MAX_GLYPH_SLOT_INDEX; ++slotIndex) + { + bot->SetGlyph(slotIndex, 0, true); + } + return true; +} + bool AutoGearAction::Execute(Event event) { if (!sPlayerbotAIConfig->autoGearCommand) { diff --git a/src/strategy/actions/TrainerAction.h b/src/strategy/actions/TrainerAction.h index ea1f6e66..5ea6300c 100644 --- a/src/strategy/actions/TrainerAction.h +++ b/src/strategy/actions/TrainerAction.h @@ -35,6 +35,13 @@ class MaintenanceAction : public Action bool Execute(Event event) override; }; +class RemoveGlyphAction : public Action +{ + public: + RemoveGlyphAction(PlayerbotAI* botAI) : Action(botAI, "remove glyph") { } + bool Execute(Event event) override; +}; + class AutoGearAction : public Action { public: diff --git a/src/strategy/actions/UseMeetingStoneAction.cpp b/src/strategy/actions/UseMeetingStoneAction.cpp index 422fc3e9..3ef46e21 100644 --- a/src/strategy/actions/UseMeetingStoneAction.cpp +++ b/src/strategy/actions/UseMeetingStoneAction.cpp @@ -81,8 +81,10 @@ bool SummonAction::Execute(Event event) pet->GetCharmInfo()->IsReturning(); } - if (master->GetSession()->GetSecurity() >= SEC_PLAYER) + if (master->GetSession()->GetSecurity() >= SEC_PLAYER) { + botAI->GetAiObjectContext()->GetValue("prioritized targets")->Set({}); return Teleport(master, bot); + } if (SummonUsingGos(master, bot) || SummonUsingNpcs(master, bot)) { diff --git a/src/strategy/actions/XpGainAction.cpp b/src/strategy/actions/XpGainAction.cpp index fbf313cb..31322360 100644 --- a/src/strategy/actions/XpGainAction.cpp +++ b/src/strategy/actions/XpGainAction.cpp @@ -4,6 +4,7 @@ #include "XpGainAction.h" #include "Event.h" +#include "PlayerbotAIConfig.h" #include "Playerbots.h" bool XpGainAction::Execute(Event event) diff --git a/src/strategy/generic/ChatCommandHandlerStrategy.cpp b/src/strategy/generic/ChatCommandHandlerStrategy.cpp index bbb1c1a4..4da74e5b 100644 --- a/src/strategy/generic/ChatCommandHandlerStrategy.cpp +++ b/src/strategy/generic/ChatCommandHandlerStrategy.cpp @@ -89,6 +89,7 @@ ChatCommandHandlerStrategy::ChatCommandHandlerStrategy(PlayerbotAI* botAI) : Pas supported.push_back("de"); supported.push_back("trainer"); supported.push_back("maintenance"); + supported.push_back("remove glyph"); supported.push_back("autogear"); supported.push_back("equip upgrade"); supported.push_back("chat"); diff --git a/src/strategy/triggers/ChatTriggerContext.h b/src/strategy/triggers/ChatTriggerContext.h index c0721390..af52cd7e 100644 --- a/src/strategy/triggers/ChatTriggerContext.h +++ b/src/strategy/triggers/ChatTriggerContext.h @@ -55,6 +55,7 @@ class ChatTriggerContext : public NamedObjectContext creators["de"] = &ChatTriggerContext::dead; creators["trainer"] = &ChatTriggerContext::trainer; creators["maintenance"] = &ChatTriggerContext::maintenance; + creators["remove glyph"] = &ChatTriggerContext::remove_glyph; creators["autogear"] = &ChatTriggerContext::autogear; creators["equip upgrade"] = &ChatTriggerContext::equip_upgrade; creators["attack"] = &ChatTriggerContext::attack; @@ -168,6 +169,7 @@ class ChatTriggerContext : public NamedObjectContext static Trigger* attack(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "attack"); } static Trigger* trainer(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "trainer"); } static Trigger* maintenance(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "maintenance"); } + static Trigger* remove_glyph(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "remove glyph"); } static Trigger* autogear(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "autogear"); } static Trigger* equip_upgrade(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "equip upgrade"); } static Trigger* co(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "co"); } diff --git a/src/strategy/values/AoeValues.cpp b/src/strategy/values/AoeValues.cpp index b27fc307..e3f44bb2 100644 --- a/src/strategy/values/AoeValues.cpp +++ b/src/strategy/values/AoeValues.cpp @@ -125,7 +125,8 @@ Aura* AreaDebuffValue::Calculate() Unit::AuraEffectList const& aurasPeriodicDamagePercent = bot->GetAuraEffectsByType(SPELL_AURA_PERIODIC_DAMAGE_PERCENT); Unit::AuraEffectList const& aurasPeriodicTriggerSpell = bot->GetAuraEffectsByType(SPELL_AURA_PERIODIC_TRIGGER_SPELL); Unit::AuraEffectList const& aurasPeriodicTriggerWithValueSpell = bot->GetAuraEffectsByType(SPELL_AURA_PERIODIC_TRIGGER_SPELL_WITH_VALUE); - for (const Unit::AuraEffectList& list : {aurasPeriodicDamage, aurasPeriodicDamagePercent, aurasPeriodicTriggerSpell, aurasPeriodicTriggerWithValueSpell}) { + Unit::AuraEffectList const& aurasDummy = bot->GetAuraEffectsByType(SPELL_AURA_DUMMY); + for (const Unit::AuraEffectList& list : {aurasPeriodicDamage, aurasPeriodicDamagePercent, aurasPeriodicTriggerSpell, aurasPeriodicTriggerWithValueSpell, aurasDummy}) { for (auto i = list.begin(); i != list.end(); ++i) { AuraEffect* aurEff = *i; diff --git a/src/strategy/values/AttackersValue.cpp b/src/strategy/values/AttackersValue.cpp index 41ff417e..527b8def 100644 --- a/src/strategy/values/AttackersValue.cpp +++ b/src/strategy/values/AttackersValue.cpp @@ -104,7 +104,7 @@ void AttackersValue::RemoveNonThreating(std::unordered_set& targets) for(std::unordered_set::iterator tIter = targets.begin(); tIter != targets.end();) { Unit* unit = *tIter; - if(bot->GetMapId() != unit->GetMapId() || !hasRealThreat(unit) || !IsValidTarget(unit, bot) || !bot->IsWithinLOSInMap(unit)) + if(bot->GetMapId() != unit->GetMapId() || !hasRealThreat(unit) || !IsValidTarget(unit, bot)) { std::unordered_set::iterator tIter2 = tIter; ++tIter; @@ -113,15 +113,6 @@ void AttackersValue::RemoveNonThreating(std::unordered_set& targets) else ++tIter; } - // Unit* unit = *tIter; - // if (!IsValidTarget(unit, bot) || !bot->IsWithinLOSInMap(unit)) - // { - // std::unordered_set::iterator tIter2 = tIter; - // ++tIter; - // targets.erase(tIter2); - // } - // else - // ++tIter; } bool AttackersValue::hasRealThreat(Unit *attacker) @@ -160,7 +151,7 @@ bool AttackersValue::IsPossibleTarget(Unit* attacker, Player* bot, float range) // bool inCannon = botAI->IsInVehicle(false, true); // bool enemy = botAI->GetAiObjectContext()->GetValue("enemy player target")->Get(); - return attacker && + return attacker && attacker->IsVisible() && attacker->IsInWorld() && attacker->GetMapId() == bot->GetMapId() && !attacker->isDead() && @@ -183,10 +174,9 @@ bool AttackersValue::IsPossibleTarget(Unit* attacker, Player* bot, float range) bool AttackersValue::IsValidTarget(Unit *attacker, Player *bot) { - return attacker->IsVisible() && - IsPossibleTarget(attacker, bot) && - (attacker->GetThreatMgr().getCurrentVictim() || attacker->GetGuidValue(UNIT_FIELD_TARGET) || - attacker->GetGUID().IsPlayer() || attacker->GetGUID() == GET_PLAYERBOT_AI(bot)->GetAiObjectContext()->GetValue("pull target")->Get()); + return IsPossibleTarget(attacker, bot) && bot->IsWithinLOSInMap(attacker); + // (attacker->GetThreatMgr().getCurrentVictim() || attacker->GetGuidValue(UNIT_FIELD_TARGET) || + // attacker->GetGUID().IsPlayer() || attacker->GetGUID() == GET_PLAYERBOT_AI(bot)->GetAiObjectContext()->GetValue("pull target")->Get()); } bool PossibleAddsValue::Calculate() diff --git a/src/strategy/values/DpsTargetValue.cpp b/src/strategy/values/DpsTargetValue.cpp index fc8f6ce1..05f6199b 100644 --- a/src/strategy/values/DpsTargetValue.cpp +++ b/src/strategy/values/DpsTargetValue.cpp @@ -71,6 +71,12 @@ class CasterFindTargetSmartStrategy : public FindTargetStrategy void CheckAttacker(Unit* attacker, ThreatMgr* threatMgr) override { + if (Group* group = botAI->GetBot()->GetGroup()) + { + ObjectGuid guid = group->GetTargetIcon(4); + if (guid && attacker->GetGUID() == guid) + return; + } if (!attacker->IsAlive()) { return; } @@ -138,6 +144,12 @@ class NonCasterFindTargetSmartStrategy : public FindTargetStrategy void CheckAttacker(Unit* attacker, ThreatMgr* threatMgr) override { + if (Group* group = botAI->GetBot()->GetGroup()) + { + ObjectGuid guid = group->GetTargetIcon(4); + if (guid && attacker->GetGUID() == guid) + return; + } if (!attacker->IsAlive()) { return; } @@ -193,6 +205,12 @@ class ComboFindTargetSmartStrategy : public FindTargetStrategy void CheckAttacker(Unit* attacker, ThreatMgr* threatMgr) override { + if (Group* group = botAI->GetBot()->GetGroup()) + { + ObjectGuid guid = group->GetTargetIcon(4); + if (guid && attacker->GetGUID() == guid) + return; + } if (!attacker->IsAlive()) { return; } diff --git a/src/strategy/values/InvalidTargetValue.cpp b/src/strategy/values/InvalidTargetValue.cpp index d2499c52..c940424a 100644 --- a/src/strategy/values/InvalidTargetValue.cpp +++ b/src/strategy/values/InvalidTargetValue.cpp @@ -27,9 +27,7 @@ bool InvalidTargetValue::Calculate() target->isFeared() || target->HasUnitState(UNIT_STATE_ISOLATED) || target->IsFriendlyTo(bot) || - !AttackersValue::IsValidTarget(target, bot) || - // !bot->IsWithinDistInMap(target, sPlayerbotAIConfig->sightDistance) || - !bot->IsWithinLOSInMap(target); + !AttackersValue::IsValidTarget(target, bot); } return !target;