79 lines
6.8 KiB
Python
79 lines
6.8 KiB
Python
"""Run native profession classification with deterministic world-data doubles."""
|
|
from pathlib import Path
|
|
import subprocess,tempfile
|
|
root=Path(__file__).resolve().parents[2]
|
|
s=(root/'src/server/game/World/AdventureProfessionTargets.cpp').read_text()
|
|
code=r'''
|
|
#include "AdventureProfessionPolicy.h"
|
|
#include <vector>
|
|
#include <deque>
|
|
#include <cmath>
|
|
#include <cassert>
|
|
constexpr int CORPSE=1,CREATURE_TYPE_BEAST=1,UNIT_FIELD_FLAGS=0,UNIT_FLAG_SKINNABLE=1;
|
|
constexpr int GAMEOBJECT_TYPE_CHEST=3,GAMEOBJECT_TYPE_SPELL_FOCUS=8,GO_READY=0,LOCK_KEY_SKILL=2,MAX_LOCK_CASE=2;
|
|
using LockType=unsigned;unsigned SkillByLockType(unsigned type){return type==1?186:type==2?182:0;}
|
|
struct Creature;struct GameObject;
|
|
struct Guid{std::string ToString(){return "source";}bool IsEmpty(){return true;}};
|
|
struct WorldObject{virtual ~WorldObject()=default;bool world=true;int map=0;float z=0;bool IsInWorld(){return world;}int GetMapId(){return map;}float GetPositionZ(){return z;}virtual Creature* ToCreature(){return nullptr;}virtual GameObject* ToGameObject(){return nullptr;}Guid GetGUID(){return {};}};
|
|
struct Creature:WorldObject{
|
|
bool alive=true,combat=false,skin=false,trainer=false,vendor=false;int death=CORPSE,type=CREATURE_TYPE_BEAST;
|
|
Creature* ToCreature()override{return this;}bool IsTrainer(){return trainer;}bool IsAlive(){return alive;}int getDeathState(){return death;}int GetCreatureType(){return type;}bool HasFlag(int,int){return skin;}bool IsInCombat(){return combat;}bool IsVendor(){return vendor;}Guid GetCharmerOrOwnerGUID(){return {};}int GetEntry(){return 1;}
|
|
};
|
|
struct GameObject:WorldObject{
|
|
int type=GAMEOBJECT_TYPE_CHEST,loot=GO_READY,entry=1;bool spawned=true;
|
|
struct Info{unsigned lock=1;unsigned GetLockId(){return lock;}}info;
|
|
GameObject* ToGameObject()override{return this;}bool isSpawned(){return spawned;}int GetGoType(){return type;}int getLootState(){return loot;}Info* GetGOInfo(){return &info;}int GetEntry(){return entry;}
|
|
};
|
|
struct Position{float distance=0,z=0;float GetExactDist(WorldObject*)const{return distance;}float GetPositionZ()const{return z;}};
|
|
struct AdventureSocialContext{std::string Intent="profession";AdventureProfessionPolicy::Professions PrimaryProfessions{"mining","blacksmithing"};int MapId=0;Position Anchor;std::deque<std::string> TargetHistory;};
|
|
std::vector<Creature*> creatures;std::vector<GameObject*> objects;
|
|
struct Player{bool phase=true,visible=true,los=true,friendly=true;bool IsInPhase(WorldObject*){return phase;}bool CanSeeOrDetect(WorldObject*){return visible;}bool IsWithinLOSInMap(WorldObject*){return los;}bool IsFriendlyTo(Creature*){return friendly;}void GetCreatureListWithEntryInGrid(std::vector<Creature*>& v,int,int){v=creatures;}void GetGameObjectListWithEntryInGrid(std::vector<GameObject*>&v,int,int){v=objects;}};
|
|
namespace Trainer{enum class Type{Tradeskill,Other};struct Spell{unsigned ReqSkillLine=0,SpellId=1;};struct Data{Type type=Type::Tradeskill;std::vector<Spell> spells;Type GetTrainerType(){return type;}auto const& GetSpells(){return spells;}};}
|
|
struct ObjectMgr{Trainer::Data trainer;int GetCreatureDefaultTrainer(int){return 1;}auto GetTrainer(int){return &trainer;}}objectMgr;auto sObjectMgr=&objectMgr;
|
|
struct Ability{unsigned SkillLine=0;}ability;
|
|
struct SpellMgr{std::vector<std::pair<int,Ability*>> entries;auto GetSkillLineAbilityMapBounds(unsigned){return std::make_pair(entries.begin(),entries.end());}}spellMgr;auto sSpellMgr=&spellMgr;
|
|
struct Lock{unsigned Type[2]={LOCK_KEY_SKILL,0},Index[2]={1,0};}lockEntry;
|
|
struct LockStore{Lock* LookupEntry(unsigned id){return id?&lockEntry:nullptr;}}sLockStore;
|
|
struct Locations{std::string station="anvil";bool enabled=true;bool IsWorkstation(int,std::string const& role){return enabled&&role==station;}}locations;auto sAdventureLocationMgr=&locations;
|
|
struct AdventureProfessionTargets{static bool Valid(Player*,WorldObject*,std::string const&,AdventureSocialContext const&);static std::vector<WorldObject*> Find(Player*,std::string const&,AdventureSocialContext&);};
|
|
'''+s[s.index('namespace\n{'):]+r'''
|
|
int main(){
|
|
Player p;GameObject go;AdventureSocialContext c;objects={&go};
|
|
auto valid=[&](char const* kind){return AdventureProfessionTargets::Valid(&p,&go,kind,c);};
|
|
assert(valid("work_mining"));assert(!valid("work_herbalism"));
|
|
lockEntry.Index[0]=2;assert(!valid("work_mining"));
|
|
c.PrimaryProfessions={"herbalism","alchemy"};assert(valid("work_herbalism"));assert(!valid("work_blacksmithing"));
|
|
c.PrimaryProfessions={"mining","blacksmithing"};lockEntry.Index[0]=1;
|
|
p.phase=false;assert(!valid("work_mining"));p.phase=true;
|
|
p.visible=false;assert(!valid("work_mining"));p.visible=true;
|
|
go.spawned=false;assert(!valid("work_mining"));go.spawned=true;
|
|
go.loot=1;assert(!valid("work_mining"));go.loot=0;
|
|
go.info.lock=0;assert(!valid("work_mining"));go.info.lock=1;
|
|
c.Anchor.distance=201;assert(!valid("work_mining"));c.Anchor.distance=0;
|
|
go.type=GAMEOBJECT_TYPE_SPELL_FOCUS;p.visible=false;
|
|
assert(valid("work_blacksmithing"));assert(!valid("work_smelting"));
|
|
locations.station="forge";assert(valid("work_smelting"));assert(!valid("work_blacksmithing"));
|
|
locations.enabled=false;assert(!valid("work_smelting"));locations.enabled=true;
|
|
go.type=GAMEOBJECT_TYPE_CHEST;p.visible=true;
|
|
assert(AdventureProfessionTargets::Find(&p,"work_mining",c).size()==1);
|
|
c.TargetHistory.push_back("work:source");assert(AdventureProfessionTargets::Find(&p,"work_mining",c).empty());
|
|
Creature npc;creatures={&npc};c.PrimaryProfessions={"skinning"};
|
|
assert(!AdventureProfessionTargets::Valid(&p,&npc,"work_skinning",c));npc.alive=false;npc.skin=true;
|
|
assert(AdventureProfessionTargets::Valid(&p,&npc,"work_skinning",c));npc.type=2;
|
|
assert(!AdventureProfessionTargets::Valid(&p,&npc,"work_skinning",c));npc.type=1;npc.death=2;
|
|
assert(!AdventureProfessionTargets::Valid(&p,&npc,"work_skinning",c));
|
|
npc.alive=true;npc.trainer=true;objectMgr.trainer.spells={{186,1}};c.PrimaryProfessions={"mining"};
|
|
assert(AdventureProfessionTargets::Valid(&p,&npc,"profession_trainer",c));
|
|
c.PrimaryProfessions.clear();assert(!AdventureProfessionTargets::Valid(&p,&npc,"profession_trainer",c));
|
|
objectMgr.trainer.spells={{185,1}};assert(AdventureProfessionTargets::Valid(&p,&npc,"profession_trainer",c));
|
|
objectMgr.trainer.spells={{0,1}};spellMgr.entries={{1,&ability}};ability.SkillLine=182;
|
|
assert(!AdventureProfessionTargets::Valid(&p,&npc,"profession_trainer",c));c.PrimaryProfessions={"herbalism"};
|
|
assert(AdventureProfessionTargets::Valid(&p,&npc,"profession_trainer",c));
|
|
}
|
|
'''
|
|
with tempfile.TemporaryDirectory(prefix='vcore-profession-targets-') 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('Production profession targets: native locks, phase/spawn/loot, stations, corpses, history and trainers passed.')
|