Files
Viano-Core/tests/clientless/test_adventure_social_fire.py
T

91 lines
6.2 KiB
Python

"""Compile native fire detection/provisioning and casting prerequisites from production."""
from pathlib import Path
import subprocess,tempfile
root=Path(__file__).resolve().parents[2]
s=(root/'src/server/game/World/AdventureSocialActions.cpp').read_text()
def function(signature):
a=s.index(signature);b=s.index('{',a);depth=1;e=b+1
while depth:depth+=(s[e]=='{')-(s[e]=='}');e+=1
return s[a:e]
code=r'''
#include "AdventureBehaviorPolicy.h"
#include "AdventureProfessionPolicy.h"
#include <cassert>
#include "AdventureExplorationPolicy.h"
#include <cstdint>
#include <unordered_map>
using uint32=uint32_t;
using AdventureBehaviorPolicy::Normalize;
constexpr uint32 CookingFire=818;
struct Guid{std::string ToString(){return "fire";}};
struct GameObject{Guid GetGUID(){return {};}bool active=true,bounds=true;uint32 spell=818;std::string name="Localised unknown name";float distance=2;bool isSpawned(){return active;}std::string GetName(){return name;}uint32 GetSpellId(){return spell;}};
struct AdventureSocialContext{std::string Intent,LocationCategory;bool AllowCreateFire=true,Festival=false;std::vector<std::string> TargetHistory;AdventureProfessionPolicy::Professions PrimaryProfessions;};
struct AdventureMicroAction{std::string Requirement;};
constexpr int GAMEOBJECT_TYPE_CHAIR=7;
struct History{bool cooldown=false;bool HasCooldown(uint32 id){assert(id==818);return cooldown;}};
struct Player {
uint32 area=1,zone=1;uint32 GetAreaId(){return area;}uint32 GetZoneId(){return zone;}
bool learned=false,outdoors=true,water=false,falling=false,casting=false;int learns=0;History history;std::vector<GameObject*> objects;
bool HasSpell(uint32 id){assert(id==818);return learned;}void LearnSpell(uint32 id,bool dependent){assert(id==818&&!dependent);learned=true;++learns;}
bool IsOutdoors(){return outdoors;}bool IsInWater(){return water;}bool IsFalling(){return falling;}bool IsNonMeleeSpellCast(bool){return casting;}
History* GetSpellHistory(){return &history;}float GetExactDist(GameObject* go){return go->distance;}
void GetGameObjectListWithEntryInGrid(std::vector<GameObject*>& out,int,float){out=objects;}
};
bool InBounds(Player*,GameObject* go,AdventureSocialContext&){return go->bounds;}
enum class AreaFlags:uint32 {LinkedChat=0x100,AllowTradeChannel=8};
struct Flags{uint32 value=0;bool HasFlag(AreaFlags flag)const{return (value&uint32(flag))!=0;}};
struct Area{uint32 ParentAreaID=0,flags=0;Flags GetFlags()const{return {flags};}};
struct Areas{std::unordered_map<uint32,Area> rows{{1,{}},{2,{0,0x100}},{3,{2,0}},{4,{0,8}}};
Area const* LookupEntry(uint32 id){auto i=rows.find(id);return i==rows.end()?nullptr:&i->second;}}areas;
auto& sAreaTableStore=areas;
struct Spells{bool exists=true;void* GetSpellInfo(uint32 id){assert(id==818);return exists?this:nullptr;}}spells;auto sSpellMgr=&spells;
std::vector<GameObject*> Objects(Player*,int,AdventureSocialContext&){return {};}
std::vector<GameObject*> People(Player*,std::string const&,AdventureSocialContext&){return {};}
std::vector<GameObject*> InspectionObjects(Player*,AdventureSocialContext&){return {};}
struct AdventureProfessionTargets{static std::vector<GameObject*> Find(Player*,std::string const&,AdventureSocialContext&){return {};}};
class AdventureSocialActions{public:static void Prepare(Player*);static bool Available(Player*,AdventureMicroAction const&,AdventureSocialContext&);};
'''
code+=function('GameObject * Fire(')+'\n'+function('bool CanMakeFire(')+'\n'+function('void AdventureSocialActions::Prepare(')+'\n'+function('bool AdventureSocialActions::Available(')
code+=r'''
int main(){
Player p;AdventureSocialContext c;GameObject fire;
assert(!CanMakeFire(&p));AdventureSocialActions::Prepare(&p);assert(p.learned&&p.learns==1&&CanMakeFire(&p));
AdventureSocialActions::Prepare(&p);assert(p.learns==1); // idempotent, no profession query available
p.history.cooldown=true;assert(!CanMakeFire(&p));p.history.cooldown=false;
p.outdoors=false;assert(!CanMakeFire(&p));p.outdoors=true;
p.water=true;assert(!CanMakeFire(&p));p.water=false;p.falling=true;assert(!CanMakeFire(&p));p.falling=false;
// City rules apply even outdoors, including a city parent's unflagged district.
p.area=p.zone=2;assert(!CanMakeFire(&p));
p.area=3;p.zone=1;assert(!CanMakeFire(&p));
p.area=1;p.zone=2;assert(!CanMakeFire(&p));
p.area=p.zone=4;assert(!CanMakeFire(&p)); // trade-channel town flag
p.area=999;p.zone=1;assert(!CanMakeFire(&p)); // unknown data never grants permission
areas.rows[5]={5,0};p.area=5;assert(!CanMakeFire(&p)); // malformed hierarchy
p.area=p.zone=1;assert(CanMakeFire(&p));
p.objects={&fire};p.area=p.zone=2;
assert(AdventureSocialActions::Available(&p,{"fire"},c)); // existing city fire remains usable
assert(!AdventureSocialActions::Available(&p,{"make_fire"},c));
p.outdoors=false;assert(AdventureSocialActions::Available(&p,{"fire"},c));p.outdoors=true;
p.objects.clear();assert(!AdventureSocialActions::Available(&p,{"fire"},c));
p.area=p.zone=1;
p.objects={&fire};assert(Fire(&p,c)==&fire); // native spell ID works regardless of locale/name
fire.spell=0;assert(Fire(&p,c)==nullptr);fire.name="Basic Campfire";assert(Fire(&p,c)==&fire);
// Rest may create a fire only at an authored ground-rest location. Existing
// fires remain usable in a tavern or at another suitable resting location.
fire.spell=818;c.AllowCreateFire=false;
assert(AdventureSocialActions::Available(&p,{"fire"},c));
assert(!AdventureSocialActions::Available(&p,{"make_fire"},c));
p.objects.clear();assert(!AdventureSocialActions::Available(&p,{"fire"},c));
c.AllowCreateFire=true;assert(AdventureSocialActions::Available(&p,{"fire"},c));
assert(AdventureSocialActions::Available(&p,{"make_fire"},c));
p.outdoors=false;assert(!AdventureSocialActions::Available(&p,{"fire"},c));p.outdoors=true;
p.objects={&fire};
fire.active=false;assert(Fire(&p,c)==nullptr);fire.active=true;fire.bounds=false;assert(Fire(&p,c)==nullptr);
}
'''
with tempfile.TemporaryDirectory(prefix='vcore-social-fire-') as folder:
cpp=Path(folder)/'test.cpp';binary=Path(folder)/'test';cpp.write_text(code)
subprocess.run(['c++','-std=c++17','-I',str(root/'src/server/game/World'),str(cpp),'-o',str(binary)],check=True)
subprocess.run([str(binary)],check=True)
print('Shared fire: native detection/provisioning, buildings/city/parent/zone restrictions, existing fires and outdoor/water checks passed.')