63 lines
4.2 KiB
Python
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 ⌖}}
|
|
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.')
|