107 lines
7.7 KiB
Python
107 lines
7.7 KiB
Python
"""Execute economic role selection against production discovery and cue policy."""
|
|
from pathlib import Path
|
|
import subprocess,tempfile
|
|
root=Path(__file__).resolve().parents[2]
|
|
source=(root/'src/server/game/World/AdventureSocialActions.cpp').read_text()
|
|
def function(sig):
|
|
a=source.index(sig);b=source.index('{',a);depth=1;e=b+1
|
|
while depth:depth+=(source[e]=='{')-(source[e]=='}');e+=1
|
|
return source[a:e]
|
|
code=r'''
|
|
#include "AdventureEconomyPolicy.h"
|
|
#include <algorithm>
|
|
#include "AdventureProfessionPolicy.h"
|
|
#include <cassert>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <cmath>
|
|
constexpr int UNIT_NPC_FLAGS=0,UNIT_NPC_FLAG_AUCTIONEER=2097152;
|
|
enum class HighGuid{Unit};
|
|
struct Guid{int value=1;Guid(int v=1):value(v){}Guid(HighGuid,unsigned,unsigned low):value(low){}unsigned GetCounter(){return value;}bool IsEmpty(){return value==0;}};
|
|
using ObjectGuid=Guid;
|
|
struct WorldObject{
|
|
void* ToGameObject(){return nullptr;}
|
|
bool inWorld=true,phase=true;unsigned map=0;float x=0,z=0;
|
|
bool IsInWorld()const{return inWorld;}unsigned GetMapId()const{return map;}float GetPositionX()const{return x;}float GetPositionY()const{return 0;}float GetPositionZ()const{return z;}float GetCollisionHeight()const{return 2;}
|
|
};
|
|
struct Unit:WorldObject{
|
|
bool repair=false,vendor=false,banker=false,auction=false,inn=false,alive=true,combat=false,flying=false,transit=false,critter=false,totem=false,friendly=true,spline=false;Guid owner{0},guid;
|
|
bool IsArmorer(){return repair;}bool IsVendor(){return vendor;}bool IsBanker(){return banker;}bool HasFlag(int,int){return auction;}bool IsInnkeeper(){return inn;}
|
|
bool IsAlive(){return alive;}bool IsInCombat(){return combat;}bool IsInFlight(){return transit;}bool IsFlying(){return flying;}bool GetTransport(){return transit;}
|
|
bool IsCritter(){return critter;}bool IsTotem(){return totem;}Guid GetCharmerOrOwnerGUID(){return owner;}Guid GetGUID(){return guid;}bool IsSplineEnabled(){return spline;}
|
|
};
|
|
struct Creature:Unit{};
|
|
namespace VMAP{enum class ModelIgnoreFlags{Nothing};}
|
|
constexpr int LINEOFSIGHT_ALL_CHECKS=0;
|
|
struct NativeMap{bool clear=true;int queries=0;bool isInLineOfSight(int,float,float,float fromZ,float,float,float toZ,int,VMAP::ModelIgnoreFlags){++queries;assert(fromZ==2&&toZ==2);return clear;}}nativeMap;
|
|
struct Player:Unit{
|
|
NativeMap* GetMap(){return &nativeMap;}int GetPhaseShift(){return 0;}
|
|
std::vector<Creature*> creatures;std::vector<Player*> players;int playerQueries=0;bool los=true;
|
|
bool IsInPhase(WorldObject const* u){return u->phase;}bool IsFriendlyTo(Unit*u){return u->friendly;}bool IsWithinLOSInMap(WorldObject*){return los;}
|
|
void GetCreatureListWithEntryInGrid(std::vector<Creature*>&out,int,float){out=creatures;}
|
|
void GetPlayerListInGrid(std::vector<Player*>&out,float){out=players;++playerQueries;}
|
|
};
|
|
struct Anchor{float GetExactDist(WorldObject const*u)const{return std::hypot(u->x,u->z);}float GetPositionZ()const{return 0;}};
|
|
struct AdventureSocialContext{Anchor Anchor;unsigned MapId=0;std::string ServiceKind,Intent="economy";};
|
|
enum class ClientlessEntityType{Creature,Player};
|
|
constexpr unsigned CLIENTLESS_CAP_REPAIR=256,CLIENTLESS_CAP_VENDOR=1,CLIENTLESS_CAP_BANKER=8,CLIENTLESS_CAP_AUCTIONEER=32;
|
|
struct Entity{ClientlessEntityType Type=ClientlessEntityType::Creature;bool Alive=true;unsigned Capabilities=0,Entry=0,Guid=0;};
|
|
struct Snapshot{float Radius=0;unsigned ScanAgeMs=1000;std::vector<Entity> Entities;};
|
|
Player* observer=nullptr;
|
|
struct Perception{Snapshot snapshot;Snapshot* GetSnapshot(unsigned){return &snapshot;}
|
|
void ScanNow(Player*p,float radius){observer=p;snapshot={};snapshot.Radius=radius;
|
|
for(auto u:p->creatures)if(std::hypot(u->x,u->z)<=radius) snapshot.Entities.push_back({ClientlessEntityType::Creature,u->alive,unsigned(u->vendor?1:0)|unsigned(u->banker?8:0)|unsigned(u->auction?32:0)|unsigned(u->repair?256:0),0,unsigned(u->guid.value)});
|
|
}}perception;auto sClientlessPerceptionMgr=&perception;
|
|
namespace ObjectAccessor{Creature* GetCreature(Player&p,ObjectGuid guid){for(auto u:p.creatures)if(u->guid.value==guid.value)return u;return nullptr;}}
|
|
struct Managed{bool IsManaged(unsigned){return true;}}managed;auto sClientlessPlayerMgr=&managed;
|
|
'''.replace('Anchor Anchor;','struct Anchor Anchor;')
|
|
for sig in ('bool InBounds(', 'bool Friendly(', 'bool ServiceMatches(', 'bool TargetVisible(', 'std::vector < Unit * > People('):code+=function(sig)+'\n'
|
|
code+=r'''
|
|
int main(){
|
|
using namespace AdventureEconomyPolicy;using namespace AdventureSocialFlow;
|
|
Player p,bot;Creature vendor,banker,auction,inn,guard,repair;
|
|
repair.repair=true;vendor.vendor=true;banker.banker=true;auction.auction=true;inn.inn=true;
|
|
p.creatures={&vendor,&banker,&auction,&inn,&guard,&repair};int nextGuid=10;for(auto u:p.creatures)u->guid.value=++nextGuid;p.players={&bot};AdventureSocialContext c;
|
|
for(auto kind:{"vendor_visit","goods","trade","vendor_compare"}){
|
|
auto people=People(&p,kind,c);assert(people.size()==1&&people[0]==&vendor);
|
|
}
|
|
for(auto kind:{"bank_visit","banking"})assert(People(&p,kind,c)==std::vector<Unit*>{&banker});
|
|
for(auto kind:{"auction_visit","auctions"})assert(People(&p,kind,c)==std::vector<Unit*>{&auction});
|
|
for(auto kind:{"service_wait","service_finish"})assert(People(&p,kind,c).size()==4);
|
|
c.ServiceKind=LocationRequirement("economy_bank");
|
|
assert(People(&p,"goods",c).empty());assert(People(&p,"service_wait",c)==std::vector<Unit*>{&banker});
|
|
c.ServiceKind=LocationRequirement("economy_auction");
|
|
assert(People(&p,"banking",c).empty());assert(People(&p,"service_wait",c)==std::vector<Unit*>{&auction});
|
|
c.ServiceKind="";
|
|
vendor.x=75;assert(People(&p,"goods",c)==std::vector<Unit*>{&vendor});vendor.x=0;
|
|
assert(p.playerQueries==0); // Clientless bots are never mistaken for service NPCs.
|
|
vendor.friendly=false;assert(People(&p,"goods",c).empty());vendor.friendly=true;
|
|
vendor.alive=false;assert(People(&p,"goods",c).empty());vendor.alive=true;
|
|
vendor.combat=true;assert(People(&p,"goods",c).empty());vendor.combat=false;
|
|
vendor.owner.value=5;assert(People(&p,"goods",c).empty());vendor.owner.value=0;
|
|
vendor.phase=false;assert(People(&p,"goods",c).empty());vendor.phase=true;
|
|
vendor.x=201;assert(People(&p,"goods",c).empty());vendor.x=0;
|
|
vendor.z=26;assert(People(&p,"goods",c).empty());vendor.z=0;
|
|
p.los=false;assert(People(&p,"goods",c)==std::vector<Unit*>{&vendor});p.los=true; // Discovery precedes the MMAP approach and final LOS check.
|
|
p.los=false;nativeMap.clear=true;
|
|
assert(TargetVisible(&p,&banker,"banking"));assert(nativeMap.queries==1);
|
|
assert(!TargetVisible(&p,&banker,"npc")); // Ordinary social visibility is preserved.
|
|
nativeMap.clear=false;assert(!TargetVisible(&p,&banker,"banking")); // A real wall still blocks the scene.
|
|
nativeMap.clear=true;p.los=true;
|
|
assert(People(&p,"order",c)==std::vector<Unit*>{&inn}); // Preserve ordinary innkeeper interactions.
|
|
assert(People(&p,"repair",c)==std::vector<Unit*>{&repair});
|
|
for(auto kind:{"repair","vendor_visit","goods","trade","vendor_compare","bank_visit","banking","auction_visit","auctions"}){
|
|
assert(Economic(kind));auto steps=Steps(kind,20000);
|
|
assert(steps.front().Action==Cue::Greet&&steps.back().Action==Cue::Farewell);
|
|
for(auto step:steps)assert(step.Action!=Cue::Fire&&step.Action!=Cue::Dance&&step.Action!=Cue::Seat);
|
|
}
|
|
assert(!Economic("npc")&&Steps("unknown",10000).empty());
|
|
assert(RequiredService("supplies")==Service::None&&Economic("supplies"));
|
|
}
|
|
'''
|
|
with tempfile.TemporaryDirectory(prefix='vcore-economy-') 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('Economy: native service roles, friendly/phase/LOS/bounds gates, no bot service targets and coherent performative cues passed.')
|