Improve AdventureBot recruitment, world presence and performance

Manage Arthoran as a persistent level-60 tank profile, add Worgen forms and mounts, enable PvP and recruit through native LFG with preparation actions and player invitations.

Reduce repeated route and flight-planning work, bound failed-travel retries and add runtime timing diagnostics. Include 86 passing tests and measurements with 110 online bots.
This commit is contained in:
Nico committed 2026-10-29 01:36:35 +01:00
1 parent cac4142ec1
commit 0173a434f4
39 files changed
+1958 -74

No files matched your search

+44 -3
View File
@@ -17,6 +17,8 @@
#include "Log.h"
#include "Player.h"
#include "Random.h"
#include "GameTime.h"
#include "Map.h"
#include <lua.hpp>
#include <algorithm>
#include <cmath>
@@ -230,6 +232,7 @@ public:
{ sAdventureBehaviorMgr->IntentChanged(player, intent); }
void Update(Player* player, uint32 diff) override
{
sClientlessLuaEngine->UpdateWorldPresence(player,sAdventureBehaviorMgr->InteractionIntent(player));
sAdventureGroupTravelMgr->Observe(player,diff);
sAdventurePopulationMgr->Maintain(player,diff);
if(sAdventureGroupTravelMgr->Airborne(player)){sAdventureGroupTravelMgr->Follow(player);return;}
@@ -246,7 +249,7 @@ public:
void OnDamageTaken(Player* player, Unit* attacker, uint32 damage) override
{ sAdventureCombatMgr->DamageTaken(player, attacker, damage); }
void OnPlayerLogout(Player* player) override
{ sAdventurePopulationMgr->Offline(player); sAdventureInteractionMgr->Offline(player); sAdventureSupplyMgr->Offline(player); sAdventureCombatMgr->Offline(player); sAdventureBehaviorMgr->Offline(player); }
{ sClientlessLuaEngine->ForgetWorldPresence(player->GetGUID().GetCounter()); sAdventurePopulationMgr->Offline(player); sAdventureInteractionMgr->Offline(player); sAdventureSupplyMgr->Offline(player); sAdventureCombatMgr->Offline(player); sAdventureBehaviorMgr->Offline(player); }
};
}
@@ -472,6 +475,7 @@ bool ClientlessLuaEngine::LoadAdventureProfile(Player* player)
{
profile.AutonomousBehavior = ReadBoolField(_state, -1, "enabled", false);
profile.DailySchedule = ReadBoolField(_state, -1, "daily_schedule", false);
profile.AllowRetreat = ReadBoolField(_state, -1, "allow_retreat", true);
profile.InitialIntent = ReadStringField(_state, -1, "initial_intent");
profile.InitialLocation = ReadStringField(_state, -1, "initial_location");
profile.Locale = ReadStringField(_state, -1, "locale", "enUS");
@@ -563,20 +567,56 @@ uint32 ClientlessLuaEngine::SelectCompanionSpell(uint32 guid) const
return 0;
}
void ClientlessLuaEngine::UpdateWorldPresence(Player* player, std::string const& intent, bool arrived)
{
if (!player || !player->IsInWorld()) return;
player->AddCharacterFlag(CHARACTER_FLAG_PVP_DESIRED);
player->SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_IN_PVP);
if (!player->IsPvP() || player->pvpInfo.EndTimer) player->UpdatePvP(true,true);
if (player->getRace()!=RACE_WORGEN) return;
if (!player->HasSpell(SPELL_TWO_FORMS_RACIAL)) player->LearnSpell(SPELL_TWO_FORMS_RACIAL,false);
if (player->getLevel()>=20 && !player->HasSpell(458)) player->LearnSpell(458,false);
bool active=intent=="adventure" || intent=="instance" || intent=="raid" || player->IsInCombat() || player->GetMap()->IsDungeon();
auto guid=player->GetGUID().GetCounter();
auto wanted=_worgenForms.find(guid);
if (arrived && !active) wanted=_worgenForms.insert_or_assign(guid,urand(0,1)!=0).first;
if (active) wanted=_worgenForms.insert_or_assign(guid,true).first;
if (wanted==_worgenForms.end() || !player->IsAlive() || player->IsMounted() ||
player->GetTransport() || player->IsInFlight() || player->IsFlying() || player->IsBeingTeleported() ||
player->HasUnitState(UNIT_STATE_CASTING)) return;
bool altered=player->HasAuraType(SPELL_AURA_WORGEN_ALTERED_FORM);
if (altered==wanted->second || _worgenNextAttempt[guid]>GameTime::GetGameTime()) return;
_worgenNextAttempt[guid]=GameTime::GetGameTime()+5;
if (wanted->second) player->CastSpell(player,SPELL_ALTERED_FORM_RACIAL,true);
else if (player->HasSpell(SPELL_TWO_FORMS_RACIAL)) player->CastSpell(player,SPELL_TWO_FORMS_RACIAL,true);
TC_LOG_INFO("server.adventurebot","WORGEN_FORM guid=%u intent='%s' desired=%u arrived=%u",guid,intent.c_str(),wanted->second,arrived);
}
bool ClientlessLuaEngine::PrepareGroundMount(Player* player, float travelDistance, char const* context)
{
if (!player || !player->IsInWorld())
return false;
ClientlessAdventureProfile const* profile = GetAdventureProfile(player->GetGUID().GetCounter());
if (!profile || !profile->CanGroundMount || profile->GroundMounts.empty())
if (!profile || !profile->CanGroundMount || (profile->GroundMounts.empty() && player->getRace()!=RACE_WORGEN))
return false;
if (travelDistance < profile->GroundMountMinDistance || !player->IsOutdoors())
return false;
if (player->IsMounted())
return true;
uint32 const displayId = SelectGroundMountDisplayId(player->GetGUID().GetCounter());
if (player->getRace()==RACE_WORGEN && player->HasAuraType(SPELL_AURA_WORGEN_ALTERED_FORM) && player->getLevel()>=20)
{
if (!player->HasSpell(87840)) player->LearnSpell(87840,false);
player->CastSpell(player,87840,true);
if (player->HasAura(87840) && player->IsMounted())
{
TC_LOG_INFO("server.adventurebot.navigation","GROUND_RUNNING_WILD guid=%u",player->GetGUID().GetCounter());
return true;
}
return false;
}
uint32 const displayId = player->getRace()==RACE_WORGEN?2404:SelectGroundMountDisplayId(player->GetGUID().GetCounter());
if (!displayId)
return false;
@@ -598,6 +638,7 @@ void ClientlessLuaEngine::DismountGroundMount(Player* player, char const* reason
return;
uint32 const oldDisplay = player->GetMountID();
player->RemoveAurasDueToSpell(87840);
player->Dismount();
player->UpdateSpeed(MOVE_RUN);