feat(playerbots): staggered taxi take-off for bots (#1281)

* feat(playerbots): staggered taxi take-off for bots

Adds four new configurable settings to playerbots.conf:

- AiPlayerbot.BotTaxiDelayMinMs:   Min random delay before the 1st follower bot clicks the flight-master
- AiPlayerbot.BotTaxiDelayMaxMs:   Upper bound for the overall taxi delay window – larger spreads big raids
- AiPlayerbot.BotTaxiGapMs:        Fixed gap added per group-slot so bots never take off together
- AiPlayerbot.BotTaxiGapJitterMs:  Extra small randomness added to each gap so launches don’t look robotic

These options allow server owners to fine-tune how bots queue up and take off from flight masters, making their behavior appear more natural.

Closes #1017 : Bots use Flight master nearly the same time.

* fixed build errors

Was missing a header and variable declarations.
This commit is contained in:
Type1Error
2025-05-10 11:45:15 +00:00
committed by GitHub
parent 43b0852438
commit 95c572bf48
6 changed files with 91 additions and 0 deletions

View File

@@ -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 dont look robotic (ms)
AiPlayerbot.BotTaxiGapJitterMs = 100
#
#
#
###################################################################################################
#######################################
# #
# PLAYERBOT RNDBOT SPECIFIC SETTINGS #

View File

@@ -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<void()> callback, uint32 delayMs)
{
class LambdaEvent final : public BasicEvent
{
std::function<void()> _cb;
public:
explicit LambdaEvent(std::function<void()> 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));
}

View File

@@ -590,6 +590,9 @@ public:
NewRpgStatistic rpgStatistic;
std::unordered_set<uint32> lowPriorityQuest;
// Schedules a callback to run once after <delayMs> milliseconds.
void AddTimedEvent(std::function<void()> callback, uint32 delayMs);
private:
static void _fillGearScoreData(Player* player, Item* item, std::vector<uint32>* gearScore, uint32& twoHandScore,
bool mixed = false);

View File

@@ -331,6 +331,12 @@ bool PlayerbotAIConfig::Initialize()
useFlyMountAtMinLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.UseFlyMountAtMinLevel", 60);
useFastFlyMountAtMinLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.UseFastFlyMountAtMinLevel", 70);
// stagger bot flightpath takeoff
delayMin = sConfigMgr->GetOption<uint32>("AiPlayerbot.BotTaxiDelayMinMs", 350u);
delayMax = sConfigMgr->GetOption<uint32>("AiPlayerbot.BotTaxiDelayMaxMs", 5000u);
gapMs = sConfigMgr->GetOption<uint32>("AiPlayerbot.BotTaxiGapMs", 200u);
gapJitterMs = sConfigMgr->GetOption<uint32>("AiPlayerbot.BotTaxiGapJitterMs", 100u);
LOG_INFO("server.loading", "Loading TalentSpecs...");
for (uint32 cls = 1; cls < MAX_CLASSES; ++cls)

View File

@@ -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)
{

View File

@@ -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<uint32>("AiPlayerbot.BotTaxiDelayMinMs", 350u, false);
uint32 delayMax = sConfigMgr->GetOption<uint32>("AiPlayerbot.BotTaxiDelayMaxMs", 5000u, false);
uint32 gapMs = sConfigMgr->GetOption<uint32>("AiPlayerbot.BotTaxiGapMs", 200u, false);
uint32 gapJitterMs = sConfigMgr->GetOption<uint32>("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 npcs 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 ===");