Files
Viano-Core/tests/clientless/test_adventure_combat_exercise.py
T
Nico c0c799c4b6
GCC PCH / build (push) Canceled after 0s
clang PCH / build (push) Canceled after 0s
Add AdventureBot combat reactions, training and corpse recovery
2026-10-27 23:17:05 +01:00

63 lines
4.2 KiB
Python

"""Execute the production bounded PlayerAI exercise and native selection and cleanup."""
from pathlib import Path
import subprocess,tempfile
root=Path(__file__).resolve().parents[2]
s=(root/'src/server/scripts/Custom/viano_bot_commands.cpp').read_text()
a=s.index(' class Exercise : public PlayerAI');b=s.index(' std::unordered_map<uint32,std::unique_ptr<Exercise>>',a)
code=r'''
#include <cassert>
#include <cstdint>
#include <set>
#include <string>
#include <vector>
using uint32=uint32_t;
#define TC_LOG_INFO(...) ((void)0)
constexpr int UNIT_STATE_CASTING=1;
struct ObjectGuid{int value=1;bool operator==(ObjectGuid const&o)const{return value==o.value;}static const ObjectGuid Empty;};const ObjectGuid ObjectGuid::Empty{0};
struct Creature{ObjectGuid guid;bool alive=true,phase=true,rend=false;std::string script="npc_training_dummy";ObjectGuid GetGUID(){return guid;}bool IsAlive(){return alive;}std::string GetScriptName(){return script;}bool GetAuraApplicationOfRankedSpell(uint32,ObjectGuid){return rend;}}target;
struct Motion{int clears=0;void Clear(){++clears;}};
struct Player{
bool alive=true,charmed=false,casting=false;float distance=12;std::set<uint32> auras,phases{1,169};std::vector<uint32> casts;Creature* victim=nullptr;int swings=0,stops=0;Motion motion;
bool IsAlive(){return alive;}bool IsCharmed(){return charmed;}bool IsInPhase(Creature*c){return c->phase;}
float GetExactDist(Creature*){return distance;}bool HasUnitState(int){return casting;}void SetFacingToObject(Creature*){}
bool HasAura(uint32 id){return auras.count(id);}bool IsWithinMeleeRange(Creature*){return distance<4;}
Creature* GetVictim(){return victim;}ObjectGuid GetGUID(){return {26};}
void AttackStop(){++stops;victim=nullptr;}void CastStop(){casting=false;}void StopMoving(){}Motion* GetMotionMaster(){return &motion;}
void SetSelection(ObjectGuid){}
};
namespace ObjectAccessor{Creature* GetCreature(Player&,ObjectGuid){return &target;}}
struct PhasingHandler{static void RemovePhase(Player* p,uint32 id,bool){p->phases.erase(id);}};
constexpr int SPELL_CAST_OK=0;
struct SpellCastTargets{void SetUnitTarget(void*){}};
struct Spell{
Player* player;std::set<uint32>* available;struct Info{uint32 Id;}info;
auto GetSpellInfo(){return &info;}
int prepare(SpellCastTargets const&){auto id=info.Id;player->casts.push_back(id);available->erase(id);if(id==2457||id==6673)player->auras.insert(id);if(id==100)player->distance=2;if(id==772)target.rend=true;delete this;return SPELL_CAST_OK;}
};
struct PlayerAI{
Player* me;explicit PlayerAI(Player*p):me(p){}virtual ~PlayerAI()=default;virtual void UpdateAI(uint32)=0;
struct TargetedSpell{Spell* first=nullptr;void* second=nullptr;explicit operator bool()const{return first!=nullptr;}};
std::set<uint32> available{2457,100,6673,12294,772,7384,1464,78};
template<class T>TargetedSpell VerifySpellCast(uint32 id,T* t){return available.count(id)?TargetedSpell{new Spell{me,&available,{id}},t}:TargetedSpell{};}
void AttackStart(Creature*c){me->victim=c;}void DoAutoAttackIfReady(){++me->swings;}
};
'''+s[a:b]+r'''
int main(){
Player p;Exercise exercise(&p,&target,20);
for(unsigned i=0;i<24;++i)assert(exercise.Tick(500));
assert((p.casts==std::vector<uint32>{2457,100,6673,12294,772,7384,1464,78}));assert(p.swings>0);
p.casting=true;auto count=p.casts.size();assert(exercise.Tick(500)&&p.casts.size()==count);
p.distance=61;assert(!exercise.Tick(500));exercise.Stop();
assert(p.phases.count(169)&&p.phases.count(1)&&p.stops==1&&p.motion.clears==1);
Player q;q.phases={1,169};Exercise withoutAddedPhase(&q,&target,10);withoutAddedPhase.Stop();assert(q.phases.count(169));
Player hostile;target.script="not_a_training_dummy";Exercise rejected(&hostile,&target,10);assert(!rejected.Tick(500)&&hostile.casts.empty());
target.script="npc_training_dummy";hostile.charmed=true;assert(!rejected.Tick(500));
hostile.charmed=false;hostile.alive=false;assert(!rejected.Tick(500));
}
'''
with tempfile.TemporaryDirectory() as tmp:
src=Path(tmp)/'exercise.cpp';exe=Path(tmp)/'exercise';src.write_text(code)
subprocess.run(['g++','-std=c++17',str(src),'-o',str(exe)],check=True)
subprocess.run([str(exe)],check=True)
print('Production exercise: stance/charge/melee/casts, native validation delegation, bounded lifetime, invalid targets and unchanged native phases passed.')