Add AdventureBot social interactions and player-led groups

This commit is contained in:
Nico committed 2026-10-28 01:11:54 +01:00
1 parent 39a515dbd1
commit e333f2f9fc
29 files changed
+2003 -29

No files matched your search

+34 -13
View File
@@ -1,4 +1,5 @@
#include "AdventureCombatMgr.h"
#include "AdventureInteractionMgr.h"
#include "AdventureCombatPolicy.h"
#include "AdventureBehaviorMgr.h"
#include "ClientlessLuaEngine.h"
@@ -30,7 +31,7 @@ class AdventureCombatMgr::Fighter : public PlayerAI
public:
explicit Fighter(Player* player):PlayerAI(player) { }
void UpdateAI(uint32) override { }
void Tick(Creature* target,uint32 diff)
void Tick(Unit* target,uint32 diff)
{
_rend=_rend>diff?_rend-diff:0;_filler=_filler>diff?_filler-diff:0;
me->SetFacingToObject(target);
@@ -59,13 +60,16 @@ private:
uint32 _rend=0,_filler=0;
};
AdventureCombatMgr* AdventureCombatMgr::instance(){static AdventureCombatMgr mgr;return &mgr;}
bool AdventureCombatMgr::Valid(Player* p,Creature* c,bool training) const
bool AdventureCombatMgr::Valid(Player* p,Unit* c,bool training) const
{
return c && c->IsAlive() && c->IsInWorld() && p->IsInPhase(c) && p->CanSeeOrDetect(c) &&
p->IsValidAttackTarget(c) && !c->GetControllingPlayer() && !c->IsEvadingAttacks() &&
(training?(c->GetScriptName()=="npc_training_dummy" && c->getLevel()<=p->getLevel()+2 && !c->isWorldBoss()):c->GetScriptName()!="npc_training_dummy");
if(!c || !c->IsAlive() || !c->IsInWorld() || !p->IsInPhase(c) ||
!p->CanSeeOrDetect(c) || !p->IsValidAttackTarget(c))return false;
if(auto creature=c->ToCreature())
return !creature->IsEvadingAttacks() && (!training || !creature->GetControllingPlayer()) &&
(training?(creature->GetScriptName()=="npc_training_dummy" && creature->getLevel()<=p->getLevel()+2 && !creature->isWorldBoss()):creature->GetScriptName()!="npc_training_dummy");
return !training && c->ToPlayer(); // Native PvP/sanctuary/duel checks above.
}
bool AdventureCombatMgr::Reachable(Player* p,Creature* c) const
bool AdventureCombatMgr::Reachable(Player* p,Unit* c) const
{
if(!p->IsWithinLOSInMap(c))return false;
if(p->IsWithinMeleeRange(c))return true;
@@ -75,7 +79,7 @@ bool AdventureCombatMgr::Reachable(Player* p,Creature* c) const
}
bool AdventureCombatMgr::Safe(Player* p,Creature* c) const
{
if(!Valid(p,c,false) || p->GetExactDist(c)>40 || (c->GetVictim() && c->GetVictim()!=p))return false;
if(!Valid(p,c,false) || c->GetControllingPlayer() || p->GetExactDist(c)>40 || (c->GetVictim() && c->GetVictim()!=p))return false;
std::vector<Creature*> others;p->GetCreatureListWithEntryInGrid(others,0,35);
unsigned hostiles=0;
for(auto other:others)if(other->IsAlive() && p->IsInPhase(other) && p->IsHostileTo(other) &&
@@ -95,7 +99,7 @@ Creature* AdventureCombatMgr::Dummy(Player* p) const
return best;
}
bool AdventureCombatMgr::TrainingAvailable(Player* p) const{return Dummy(p)!=nullptr;}
bool AdventureCombatMgr::Start(Player* p,Record& r,Creature* c,bool training,bool defending,time_t until)
bool AdventureCombatMgr::Start(Player* p,Record& r,Unit* c,bool training,bool defending,time_t until)
{
if(!Valid(p,c,training) || !p->IsAlive() || p->IsBeingTeleported() || p->GetTransport() || p->IsInFlight() || p->IsCharmed())return false;
sAdventureBehaviorMgr->InterruptForCombat(p);
@@ -122,8 +126,8 @@ bool AdventureCombatMgr::Attack(Player* p,uint32 spawn)
}
void AdventureCombatMgr::DamageTaken(Player* p,Unit* attacker,uint32 damage)
{
auto c=attacker?attacker->ToCreature():nullptr;
if(!damage || !p->IsAlive() || !c || c->GetControllingPlayer())return;
auto c=attacker;
if(!damage || !p->IsAlive() || !c)return;
auto& r=_records[p->GetGUID().GetCounter()];
if(std::find(r.Damagers.begin(),r.Damagers.end(),c->GetGUID())==r.Damagers.end())r.Damagers.push_back(c->GetGUID());
}
@@ -131,6 +135,8 @@ void AdventureCombatMgr::Finish(Player* p,Record& r)
{
TC_LOG_INFO("server.adventurebot","COMBAT_END guid=%u target='%s' training=%u",p->GetGUID().GetCounter(),r.Target.ToString().c_str(),r.Training);
bool training=r.Training;
if(!training && r.Helper)sAdventureInteractionMgr->CombatHelp(p,r.Helper);
r.Helper.Clear();
p->AttackStop();p->CastStop();p->StopMoving();p->GetMotionMaster()->Clear();p->SetSelection(ObjectGuid::Empty);
sClientlessTravelMgr->CancelTravel(p->GetGUID().GetCounter());
if(training && std::all_of(p->getAttackers().begin(),p->getAttackers().end(),[](Unit* u)
@@ -253,15 +259,15 @@ bool AdventureCombatMgr::Update(Player* p,uint32 diff)
// target. Keep all subsequent damagers ordered for after that focus dies.
if(!r.Defending)while(!r.Damagers.empty())
{
auto id=r.Damagers.front();r.Damagers.pop_front();auto c=ObjectAccessor::GetCreature(*p,id);
auto id=r.Damagers.front();r.Damagers.pop_front();auto c=ObjectAccessor::GetUnit(*p,id);
if(!Valid(p,c,false))continue;
if(r.AI)Finish(p,r);
Start(p,r,c,false,true,0);break;
}
if(!r.AI && !p->IsInCombat() && !r.Cooldown)
if(!r.AI && !p->GetGroup() && !p->IsInCombat() && !r.Cooldown)
if(auto c=Proactive(p))Start(p,r,c,false,false,0);
if(!r.AI)return false;
auto c=ObjectAccessor::GetCreature(*p,r.Target);
auto c=ObjectAccessor::GetUnit(*p,r.Target);
if(!Valid(p,c,r.Training) || (r.Until && GameTime::GetGameTime()>=r.Until) || p->GetExactDist(c)>80)
{Finish(p,r);return true;}
if(!Reachable(p,c))
@@ -291,3 +297,18 @@ bool AdventureCombatMgr::Busy(Player* player) const
auto it=_records.find(player->GetGUID().GetCounter());
return it!=_records.end() && (it->second.AI || it->second.Dead || !it->second.Damagers.empty());
}
bool AdventureCombatMgr::Assist(Player* p,Unit* target)
{
if(!p->GetGroup() || Busy(p) || !Valid(p,target,false) || p->GetExactDist(target)>40 ||
!Reachable(p,target) || p->GetHealthPct()<30)return false;
if(auto c=target->ToCreature())
if(c->isWorldBoss() || c->getLevel()>p->getLevel()+2)return false;
return Start(p,_records[p->GetGUID().GetCounter()],target,false,false,0);
}
void AdventureCombatMgr::SharedTargetDamage(Player* p,ObjectGuid helper,ObjectGuid target)
{
auto it=_records.find(p->GetGUID().GetCounter());
if(it!=_records.end() && it->second.Target==target && !it->second.Training)it->second.Helper=helper;
}