91 lines
6.2 KiB
Python
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.')
|