Files
Viano-Core/tests/clientless/test_adventure_class_rotation.py
T
Nico a758239da9
clang PCH / build (push) Canceled after 0s
GCC PCH / build (push) Canceled after 0s
Implement AdventureBot class rotations, group roles and instance encounters
2026-10-28 22:37:18 +01:00

59 lines
4.2 KiB
Python

"""Exercise production spell conditions, resources, procs and stable solo choices."""
from pathlib import Path
import subprocess,tempfile
root=Path(__file__).resolve().parents[2]
s=(root/'src/server/game/World/AdventureCombatMgr.cpp').read_text();a=s.index(' bool Allowed(');b=s.index('{',a);e=b+1;depth=1
while depth:depth+=(s[e]=='{')-(s[e]=='}');e+=1
method=s[a:e]
code=r'''
#include "AdventureRotationPolicy.h"
#include <cassert>
#include <set>
using uint32=unsigned;
constexpr unsigned CLASS_SHAMAN=7,POWER_HOLY_POWER=1;
struct Aura{unsigned stacks=7,charges=7;unsigned GetStackAmount(){return stacks;}unsigned GetCharges(){return charges;}}aura;
struct Unit{
std::set<unsigned>auras;float hp=100;bool casting=false;
bool HasAura(unsigned id){return auras.count(id);}bool GetAuraApplicationOfRankedSpell(unsigned id,unsigned){return auras.count(id);}
float GetHealthPct(){return hp;}bool HasUnitState(unsigned){return casting;}
};
constexpr unsigned UNIT_STATE_CASTING=1;
struct Player:Unit{
unsigned combo=0,holy=0,power=0,max=100,klass=1,spec=0;
unsigned GetComboPoints(){return combo;}unsigned GetPower(unsigned type){return type==POWER_HOLY_POWER?holy:power;}
unsigned GetPowerType(){return 2;}unsigned GetMaxPower(unsigned){return max;}unsigned getClass(){return klass;}unsigned GetGUID(){return 1;}
Aura*GetAura(unsigned id){return HasAura(id)?&aura:nullptr;}
};
unsigned GetPlayerSpec(Player*p){return p->spec;}
struct Fighter{Player*me;
'''+method+r'''
};
int main(){
using namespace AdventureRotationPolicy;Player p;Unit target;Fighter fighter{&p};
for(unsigned klass:{1,2,3,4,5,6,7,8,9,11}){
std::set<unsigned>choices;for(unsigned seed=0;seed<300;++seed){auto spec=SoloSpec(klass,seed);assert(spec==SoloSpec(klass,seed));choices.insert(spec);assert(!Damage(klass,spec).empty());}
if(klass==2 || klass==5)assert(choices==std::set<unsigned>{2});
if(klass==1 || klass==4 || klass==7 || klass==8 || klass==11)assert((choices==std::set<unsigned>{0,1}));
if(klass==3 || klass==6)assert((choices==std::set<unsigned>{1,2}));
if(klass==9)assert((choices==std::set<unsigned>{0,1,2}));
}
assert(fighter.Allowed({45477,Condition::MissingAura,false,55095},&target));target.auras.insert(55095);assert(!fighter.Allowed({45477,Condition::MissingAura,false,55095},&target));
Action finisher{2098,Condition::Always,false,0,5};p.combo=4;assert(!fighter.Allowed(finisher,&target));p.combo=5;assert(fighter.Allowed(finisher,&target));
Action proc{11366,Condition::Always,false,0,0,48108};assert(!fighter.Allowed(proc,&target));p.auras.insert(48108);assert(fighter.Allowed(proc,&target));
assert(!fighter.Allowed({5308,Condition::Execute},&target));target.hp=20;assert(fighter.Allowed({5308,Condition::Execute},&target));
p.holy=2;assert(!fighter.Allowed({85256},&target));p.holy=3;assert(fighter.Allowed({85256},&target));p.holy=0;p.auras.insert(90174);assert(fighter.Allowed({85256},&target));
p.hp=34;assert(fighter.Allowed({871,Condition::Emergency,true},&p));p.hp=35;assert(!fighter.Allowed({871,Condition::Emergency,true},&p));
p.power=69;assert(!fighter.Allowed({78,Condition::Resource},&target));p.power=70;assert(fighter.Allowed({78,Condition::Resource},&target));
p.klass=7;p.spec=0;assert(!fighter.Allowed({8042},&target));p.auras.insert(324);assert(fighter.Allowed({8042},&target));aura.stacks=9;aura.charges=6;assert(!fighter.Allowed({8042},&target));
p.spec=1;p.auras.insert(53817);aura.stacks=4;assert(!fighter.Allowed({403},&target));aura.stacks=5;assert(fighter.Allowed({403},&target));
p.auras.insert(67483);assert(!fighter.Allowed({2912},&target));p.auras.erase(67483);assert(fighter.Allowed({2912},&target));
assert(Ranged(3,1) && Ranged(5,2) && !Ranged(1,2) && !Ranged(11,1));
for(unsigned klass:{2,5,7,11})for(float hp:{20.f,60.f,85.f})assert(!Healing(klass,klass==5?1:2,hp).empty());
}
'''
with tempfile.TemporaryDirectory() as d:
p=Path(d);(p/'test.cpp').write_text(code)
subprocess.run(['g++','-std=c++17','-I',str(root/'src/server/game/World'),str(p/'test.cpp'),'-o',str(p/'test')],check=True)
subprocess.run([str(p/'test')],check=True)
print('Production class rotations: persistent PvE solo pools, native rank aura IDs, combo/holy power/proc/execute/resource gates, Eclipse and healer coverage passed')