diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index aeb6de17..69ae080a 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -18,6 +18,7 @@ # COMBAT # CHEATS # SPELLS +# FLIGHTPATH # PLAYERBOT RNDBOT SPECIFIC SETTINGS # GENERAL # LEVELS @@ -495,6 +496,28 @@ AiPlayerbot.OpenGoSpell = 6477 # ################################################################################################### +#################################################################################################### +# FLIGHTPATH +# +# + +# Min random delay before the 1st follower bot clicks the flight-master (ms) +AiPlayerbot.BotTaxiDelayMinMs = 350 + +# Upper bound for the overall taxi delay window (ms) – larger spreads big raids +AiPlayerbot.BotTaxiDelayMaxMs = 5000 + +# Fixed gap added per group-slot so bots never take off together (ms) +AiPlayerbot.BotTaxiGapMs = 200 + +# Extra small randomness added to each gap so launches don’t look robotic (ms) +AiPlayerbot.BotTaxiGapJitterMs = 100 + +# +# +# +################################################################################################### + ####################################### # # # PLAYERBOT RNDBOT SPECIFIC SETTINGS # diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index 5869747c..5ac8b46e 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -19,6 +19,7 @@ #include "CreatureData.h" #include "EmoteAction.h" #include "Engine.h" +#include "EventProcessor.h" #include "ExternalEventHelper.h" #include "GameObjectData.h" #include "GameTime.h" @@ -6279,3 +6280,23 @@ SpellFamilyNames PlayerbotAI::Class2SpellFamilyName(uint8 cls) } return SPELLFAMILY_GENERIC; } + +void PlayerbotAI::AddTimedEvent(std::function callback, uint32 delayMs) +{ + class LambdaEvent final : public BasicEvent + { + std::function _cb; + public: + explicit LambdaEvent(std::function cb) : _cb(std::move(cb)) {} + bool Execute(uint64 /*execTime*/, uint32 /*diff*/) override + { + _cb(); + return true; // remove after execution + } + }; + + // Every Player already owns an EventMap called m_Events + bot->m_Events.AddEvent( + new LambdaEvent(std::move(callback)), + bot->m_Events.CalculateTime(delayMs)); +} \ No newline at end of file diff --git a/src/PlayerbotAI.h b/src/PlayerbotAI.h index 487a6716..394f7ce2 100644 --- a/src/PlayerbotAI.h +++ b/src/PlayerbotAI.h @@ -590,6 +590,9 @@ public: NewRpgStatistic rpgStatistic; std::unordered_set lowPriorityQuest; + // Schedules a callback to run once after milliseconds. + void AddTimedEvent(std::function callback, uint32 delayMs); + private: static void _fillGearScoreData(Player* player, Item* item, std::vector* gearScore, uint32& twoHandScore, bool mixed = false); diff --git a/src/PlayerbotAIConfig.cpp b/src/PlayerbotAIConfig.cpp index e665ce06..fbcd59c9 100644 --- a/src/PlayerbotAIConfig.cpp +++ b/src/PlayerbotAIConfig.cpp @@ -331,6 +331,12 @@ bool PlayerbotAIConfig::Initialize() useFlyMountAtMinLevel = sConfigMgr->GetOption("AiPlayerbot.UseFlyMountAtMinLevel", 60); useFastFlyMountAtMinLevel = sConfigMgr->GetOption("AiPlayerbot.UseFastFlyMountAtMinLevel", 70); + // stagger bot flightpath takeoff + delayMin = sConfigMgr->GetOption("AiPlayerbot.BotTaxiDelayMinMs", 350u); + delayMax = sConfigMgr->GetOption("AiPlayerbot.BotTaxiDelayMaxMs", 5000u); + gapMs = sConfigMgr->GetOption("AiPlayerbot.BotTaxiGapMs", 200u); + gapJitterMs = sConfigMgr->GetOption("AiPlayerbot.BotTaxiGapJitterMs", 100u); + LOG_INFO("server.loading", "Loading TalentSpecs..."); for (uint32 cls = 1; cls < MAX_CLASSES; ++cls) diff --git a/src/PlayerbotAIConfig.h b/src/PlayerbotAIConfig.h index 59799699..6a32c95a 100644 --- a/src/PlayerbotAIConfig.h +++ b/src/PlayerbotAIConfig.h @@ -348,6 +348,12 @@ public: uint32 useFlyMountAtMinLevel; uint32 useFastFlyMountAtMinLevel; + // stagger flightpath takeoff + uint32 delayMin; + uint32 delayMax; + uint32 gapMs; + uint32 gapJitterMs; + std::string const GetTimestampStr(); bool hasLog(std::string const fileName) { diff --git a/src/strategy/actions/TaxiAction.cpp b/src/strategy/actions/TaxiAction.cpp index 954eb417..965a2f79 100644 --- a/src/strategy/actions/TaxiAction.cpp +++ b/src/strategy/actions/TaxiAction.cpp @@ -8,6 +8,8 @@ #include "Event.h" #include "LastMovementValue.h" #include "Playerbots.h" +#include "PlayerbotAIConfig.h" +#include "Config.h" bool TaxiAction::Execute(Event event) { @@ -48,6 +50,36 @@ bool TaxiAction::Execute(Event event) } } + // stagger bot takeoff + uint32 delayMin = sConfigMgr->GetOption("AiPlayerbot.BotTaxiDelayMinMs", 350u, false); + uint32 delayMax = sConfigMgr->GetOption("AiPlayerbot.BotTaxiDelayMaxMs", 5000u, false); + uint32 gapMs = sConfigMgr->GetOption("AiPlayerbot.BotTaxiGapMs", 200u, false); + uint32 gapJitterMs = sConfigMgr->GetOption("AiPlayerbot.BotTaxiGapJitterMs", 100u, false); + + // Only for follower bots + if (botAI->HasRealPlayerMaster()) + { + uint32 index = botAI->GetGroupSlotIndex(bot); + uint32 delay = delayMin + index * gapMs + urand(0, gapJitterMs); + + delay = std::min(delay, delayMax); + + // Store the npc’s GUID so we can re-acquire the pointer later + ObjectGuid npcGuid = npc->GetGUID(); + + // schedule the take-off + botAI->AddTimedEvent( + [bot = bot, &movement, npcGuid]() -> void + { + if (Creature* npcPtr = ObjectAccessor::GetCreature(*bot, npcGuid)) + if (!movement.taxiNodes.empty()) + bot->ActivateTaxiPathTo(movement.taxiNodes, npcPtr); + }, + delay); + botAI->SetNextCheckDelay(delay + 50); + return true; + } + if (param == "?") { botAI->TellMasterNoFacing("=== Taxi ===");