"""Execute the production recovery state machine with native-item adapters.""" from pathlib import Path import subprocess,tempfile root=Path(__file__).resolve().parents[2] source=(root/'src/server/game/World/AdventureSupplyMgr.cpp').read_text() def function(signature): a=source.index(signature);b=source.index('{',a);e=b+1;depth=1 while depth:depth+=(source[e]=='{')-(source[e]=='}');e+=1 return source[a:e] header=(root/'src/server/game/World/AdventureSupplyMgr.h').read_text() header='\n'.join(x for x in header.splitlines() if not x.startswith('#include')).replace('private:','public:') code=r''' #include "AdventureSupplyPolicy.h" #include #include #include #include #include #include using uint32=unsigned; #define TC_GAME_API #define TC_LOG_INFO(...) ((void)0) #define TC_LOG_WARN(...) ((void)0) constexpr int POWER_MANA=0,POWER_RAGE=1,CURRENT_CHANNELED_SPELL=0,CURRENT_GENERIC_SPELL=1,UNIT_STAND_STATE_STAND=0,UNIT_STAND_STATE_SIT=1; struct Guid{unsigned GetCounter(){return 26;}}; struct Spell{struct Info{unsigned Id=1;}info;Info* GetSpellInfo(){return &info;}}; struct Player{ unsigned hp=100,maxhp=100,mana=100,maxmana=100,type=POWER_RAGE;bool alive=true,combat=false,casting=false,aura=false,bandageAllowed=true,busy=false,foodAllowed=true; int posture=0,stops=0;Spell spell;std::unordered_map items; Guid GetGUID(){return {};}bool IsInCombat(){return combat;}bool IsAlive(){return alive;}bool IsBeingTeleported(){return false;}bool GetTransport(){return false;}bool IsInFlight(){return false;}bool IsFlying(){return false;}bool IsCharmed(){return false;}bool IsFalling(){return false;} unsigned GetHealth(){return hp;}unsigned GetMaxHealth(){return maxhp;}unsigned GetPower(int){return mana;}unsigned GetMaxPower(int){return maxmana;}unsigned GetPowerType(){return type;} unsigned GetItemCount(unsigned entry){return items[entry];}void StopMoving(){++stops;}void SetStandState(int s){posture=s;} Spell*GetCurrentSpell(int slot){return casting && slot==0?&spell:nullptr;} bool HasAura(unsigned id,Guid){return aura && spell.info.Id==id;} void InterruptNonMeleeSpells(bool,unsigned id){if(spell.info.Id==id)casting=false;} void RemoveAurasDueToSpell(unsigned id,Guid){if(spell.info.Id==id)aura=false;} }; namespace GameTime{time_t now=100;time_t GetGameTime(){return now;}} unsigned today=20261027;unsigned Day(time_t){return today;} struct Behavior{int interrupts=0;void InterruptForCombat(Player*){++interrupts;}}behavior;auto sAdventureBehaviorMgr=&behavior; struct Lua{void DismountGroundMount(Player*,char const*){}}lua;auto sClientlessLuaEngine=&lua; '''+header+r''' std::vector uses;int stocks=0;unsigned savedDay=0; void AdventureSupplyMgr::Online(Player*p){auto&r=_records[26];r.Loaded=true;r.LastDay=savedDay;} bool AdventureSupplyMgr::Stock(Player*,Record&r,unsigned date){++stocks;r.LastDay=date;r.AfterCombat=false;savedDay=date;return true;} AdventureSupplyMgr::UseResult AdventureSupplyMgr::TryUse(Player*p,Record&r,Use use){ uses.push_back(use);if(p->busy)return UseResult::Busy; if((use==Use::Bandage && !p->bandageAllowed)||(use==Use::Food && !p->foodAllowed))return UseResult::Unavailable; if(!p->combat){r.Active=use;r.Spell=unsigned(use);p->spell.info.Id=r.Spell;p->aura=true;r.Requested=GameTime::now;} return UseResult::Started; } '''+function('void AdventureSupplyMgr::Cancel(')+'\n'+function('bool AdventureSupplyMgr::Update(')+r''' int main(){ using U=AdventureSupplyPolicy::Use;using namespace AdventureSupplyPolicy; assert(!MissingTenPercent(91,100));assert(MissingTenPercent(90,100));assert(!MissingTenPercent(1,0)); assert(!MissingTenPercent(4725,5249));assert(MissingTenPercent(4724,5249)); assert(TopUp(19,20)==1 && TopUp(20,20)==0 && TopUp(21,20)==0); AdventureSupplyMgr mgr;Player p;auto tick=[&]{GameTime::now+=3;return mgr.Update(&p,500,false);}; assert(!tick() && stocks==1);assert(!tick() && stocks==1); p.hp=91;assert(!tick() && uses.empty()); // At the exact threshold, bandage has priority over food. p.hp=90;assert(tick() && uses.back()==U::Bandage); p.aura=false;p.hp=95;assert(!tick() && uses.size()==1); // no food below10% missing // New recovery: bandage finishes but health deficit still10% or more. p.hp=40;assert(tick() && uses.back()==U::Bandage); p.aura=false;p.hp=75;assert(tick() && uses.back()==U::Food); auto count=uses.size();p.hp=95;assert(tick() && uses.size()==count); // keep current meal to full p.hp=100;assert(!tick() && !p.aura && p.posture==UNIT_STAND_STATE_STAND); // Missing First Aid / native bandage lockout: immediate food fallback. p.hp=80;p.bandageAllowed=false;uses.clear();assert(tick());assert((uses==std::vector{U::Bandage,U::Food})); p.hp=100;assert(!tick()); // A busy global cooldown must not count as a failed bandage attempt. p.bandageAllowed=true;p.hp=80;p.busy=true;uses.clear();assert(tick() && uses.back()==U::Bandage); assert(!mgr._records[26].BandageTried);p.busy=false;assert(tick() && uses.back()==U::Bandage); // Incoming combat interrupts only owned recovery; combat never eats/bandages. p.combat=true;uses.clear();assert(!tick() && uses.back()==U::HealingPotion && !p.aura); p.hp=100;p.type=POWER_MANA;p.mana=89;uses.clear();assert(!tick() && uses.back()==U::ManaPotion); // Exactly one top-up after the actual combat interval, not per enemy/tick. auto previous=stocks;p.combat=false;assert(tick() && stocks==previous+1 && uses.back()==U::Drink); assert(tick() && stocks==previous+1);p.mana=100;assert(!tick()); // Warriors never drink; mana-only recovery starts at10% missing as well. p.type=POWER_RAGE;p.mana=0;uses.clear();assert(!tick() && uses.empty()); p.type=POWER_MANA;p.mana=91;assert(!tick());p.mana=90;assert(tick() && uses.back()==U::Drink); p.mana=100;assert(!tick()); // Persisted day blocks logout/login farming; next day allows a single top-up. mgr._records.clear();uses.clear();previous=stocks;assert(!tick() && stocks==previous); ++today;assert(!tick() && stocks==previous+1);assert(!tick() && stocks==previous+1); // Dead or combat-reaction-controlled bots never start a meal. p.hp=40;p.alive=false;uses.clear();assert(!tick() && uses.empty());p.alive=true; GameTime::now+=3;assert(!mgr.Update(&p,500,true) && uses.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 supplies: exact10% boundary, bandage-before-food, native lockout fallback, recovery completion, combat potions, mana-only drinking and daily/post-combat top-ups passed.')