#include "scripts/binds.h" #include "obj/object.h" #include "empire.h" #include "empire_stats.h" #include "main/references.h" #include "design/design.h" #include "vec2.h" #include "scripts/script_components.h" #include "util/stat_history.h" #include "network/network_manager.h" extern Empire* defaultEmpire; extern Empire* playerEmpire; extern Empire* spectatorEmpire; extern std::unordered_map statIndices; namespace scripts { static const Design* getDesignByName(Empire* emp, const std::string& name) { return emp->getDesign(name, true); } static const Design* getDesignById(Empire* emp, int id) { return emp->getDesign(id, true); } static unsigned getDesignCount(Empire* emp) { return emp->designIds.size(); } static unsigned getDesignClassCount(Empire* emp) { return emp->designClassIds.size(); } static Empire* empFactory() { return new Empire(); } static Empire* empFactory_b(bool unlisted) { return new Empire(unlisted ? INVALID_EMPIRE : UNLISTED_EMPIRE); } static void writeDelta(Empire* emp, net::Message& msg) { emp->writeDelta(msg); } static void readDelta(Empire* emp, net::Message& msg) { try { emp->readDelta(msg); } catch(net::MessageReadError) { scripts::throwException("Error reading from message: end of message."); } } static bool ssUnlocked(Empire* emp, const SubsystemDef* def) { threads::ReadLock lock(emp->subsystemDataMutex); Empire::SubsystemData* ssdata = emp->getSubsystemData(def); return ssdata ? ssdata->unlocked : false; } static bool ssModUnlocked(Empire* emp, const SubsystemDef* def, const SubsystemDef::ModuleDesc* mod) { threads::ReadLock lock(emp->subsystemDataMutex); Empire::SubsystemData* ssdata = emp->getSubsystemData(def); if(!ssdata) return false; if((int)ssdata->modulesUnlocked.size() <= mod->index) return false; return ssdata->modulesUnlocked[mod->index]; } static void ssSetModUnlock(Empire* emp, const SubsystemDef* def, const SubsystemDef::ModuleDesc* mod, bool val) { threads::WriteLock lock(emp->subsystemDataMutex); Empire::SubsystemData* ssdata = emp->getSubsystemData(def); if(ssdata) { if((int)ssdata->modulesUnlocked.size() <= mod->index) ssdata->modulesUnlocked.resize(mod->index+1, false); ssdata->modulesUnlocked[mod->index] = val; ssdata->delta = true; } } static void ssSetUnlock(Empire* emp, const SubsystemDef* def, bool val) { threads::WriteLock lock(emp->subsystemDataMutex); Empire::SubsystemData* ssdata = emp->getSubsystemData(def); if(ssdata) { ssdata->unlocked = val; ssdata->delta = true; } } static void removeModifier(Empire* emp, const SubsystemDef* def, unsigned id) { threads::WriteLock lock(emp->subsystemDataMutex); Empire::SubsystemData* ssdata = emp->getSubsystemData(def); if(ssdata) { ssdata->stages.erase(id); ssdata->delta = true; } } static void addModifier_d(asIScriptGeneric* f) { Empire* emp = (Empire*)f->GetObject(); threads::WriteLock lock(emp->subsystemDataMutex); const SubsystemDef* def = (const SubsystemDef*)f->GetArgAddress(0); Empire::SubsystemData* ssdata = emp->getSubsystemData(def); if(!ssdata) return; //Find the modifier std::string& modname = *(std::string*)f->GetArgAddress(1); auto it = def->modifierIds.find(modname); if(it == def->modifierIds.end()) { scripts::throwException( format("Error: Could not find subsystem modifier '$1.$2'.\n", def->id, modname).c_str()); return; } const SubsystemDef::ModifyStage* stage = it->second; //Check arguments if((int)stage->argumentNames.size() != f->GetArgCount() - 2) { scripts::throwException( format("Error: Subsystem modifier '$1.$2'" " expects $3 arguments, got $4.\n", def->id, modname, (unsigned)stage->argumentNames.size(), f->GetArgCount() - 1).c_str()); return; } //Create applied stage SubsystemDef::AppliedStage as; as.stage = stage; for(unsigned i = 0, cnt = stage->argumentNames.size(); i < cnt; ++i) as.arguments[i] = f->GetArgFloat(i+2); //Add stage to data unsigned id = ssdata->nextStageId++; ssdata->stages[id] = as; ssdata->delta = true; f->SetReturnDWord(id); } struct StatReader { const StatHistory* history; const StatEntry* entry; const StatEvent* evt; Empire* emp; unsigned id; StatReader(const StatHistory* History, Empire* Emp, unsigned ID) : history(History), entry(0), evt(0), emp(Emp), id(ID) {} ~StatReader() { if(emp) emp->unlockStatHistory(id); } int get_int() const { if(entry) return entry->asInt; else return 0; } float get_float() const { if(entry) return entry->asFloat; else return 0; } unsigned get_time() const { if(entry) return entry->time; else return 0; } unsigned get_evt_type() const { if(evt) return evt->type; else return 0; } std::string get_evt_name() const { if(evt) return evt->name; else return ""; } bool toHead() { if(!history) return false; if(const StatEntry* head = history->getHead()) { entry = head; evt = 0; return true; } else { return false; } } bool toTail() { if(!history) return false; if(const StatEntry* tail = history->getTail()) { entry = tail; evt = 0; return true; } else { return false; } } bool advance(int amount) { if(!history) return false; if(amount > 0) { evt = 0; if(entry == 0) { entry = history->getHead(); if(entry) return advance(amount-1); else return false; } do { entry = entry->next; if(entry == 0) return false; } while(--amount != 0); return entry != 0; } else if(amount < 0) { evt = 0; if(entry == 0) { entry = history->getTail(); if(entry) return advance(amount+1); else return false; } do { entry = entry->prev; if(entry == 0) return false; } while(--amount != 0); return entry != 0; } else { evt = 0; return entry != 0; } } bool advanceEvent() { if(!history) return false; if(entry) { if(evt == 0) { evt = entry->evt; return evt != 0; } else if(evt->next) { evt = evt->next; return true; } else { return false; } } return false; } static void destroy(StatReader* reader) { delete reader; } }; StatReader* getStatHistory(Empire* emp, unsigned id) { const StatHistory* history = emp->lockStatHistory(id); if(!history) { return new StatReader(0, 0, 0); } else { return new StatReader(history, emp, id); } } bool empIsHostile(Empire& from, Empire* to) { if(to == nullptr) return false; return from.hostileMask & to->mask; } bool empIsHostile_restricted(Empire& from, Empire* to) { if(to == nullptr) return false; //if(&from != playerEmpire && to != playerEmpire) // return false; return from.hostileMask & to->mask; } void empSetHostile(Empire& from, Empire& to, bool value) { threads::Lock lock(from.maskMutex); if(value) from.hostileMask |= to.mask; else from.hostileMask &= ~to.mask; } static bool empControlled(Empire& emp) { return devices.network->getCurrentPlayer().controls(&emp); } static bool empViewed(Empire& emp) { return devices.network->getCurrentPlayer().views(&emp); } void RegisterEmpireBinds(bool declarations, bool server) { if(declarations) { ClassBind emp("Empire", asOBJ_REF | asOBJ_NOCOUNT); return; } ClassBind emp("Empire"); emp.addFactory("Empire@ f()", asFUNCTION(empFactory)); emp.addFactory("Empire@ f(bool unlisted)", asFUNCTION(empFactory_b)); emp.addMember("string name", offsetof(Empire,name)); emp.addMember("Player@ player", offsetof(Empire,player)); emp.addMember("uint8 id", offsetof(Empire,id)); emp.addMember("int index", offsetof(Empire,index)); emp.addMember("uint mask", offsetof(Empire,mask)); emp.addMember("uint visionMask", offsetof(Empire,visionMask)); emp.addMember("Color color", offsetof(Empire,color)); emp.addMember("const Material@ background", offsetof(Empire,background)); emp.addMember("const Material@ portrait", offsetof(Empire,portrait)); emp.addMember("const Material@ flag", offsetof(Empire,flag)); emp.addMember("string backgroundDef", offsetof(Empire,backgroundDef)); emp.addMember("string portraitDef", offsetof(Empire,portraitDef)); emp.addMember("string flagDef", offsetof(Empire,flagDef)); emp.addMember("uint flagID", offsetof(Empire,flagID)); emp.addMember("const Shipset@ shipset", offsetof(Empire, shipset)); emp.addMember("string effectorSkin", offsetof(Empire, effectorSkin)); if(server) { emp.addMember("uint hostileMask", offsetof(Empire,hostileMask)); emp.addExternMethod("bool isHostile(Empire@ emp)", asFUNCTION(empIsHostile)); emp.addExternMethod("void setHostile(Empire& emp, bool value)", asFUNCTION(empSetHostile)); emp.addMethod("void cacheVision()", asMETHOD(Empire,cacheVision)); } else { emp.addExternMethod("bool isHostile(Empire@ emp)", asFUNCTION(empIsHostile_restricted)); emp.addExternMethod("bool get_controlled() const", asFUNCTION(empControlled)); emp.addExternMethod("bool get_viewable() const", asFUNCTION(empViewed)); } emp.addMethod("uint get_objectCount()", asMETHOD(Empire, objectCount)); emp.addMethod("Object@+ get_objects(uint i)", asMETHOD(Empire, findObject)); //Stats { Namespace ns("stat"); EnumBind statTypes("EmpireStat"); for(auto i = statIndices.begin(), end = statIndices.end(); i != end; ++i) statTypes[i->first] = i->second; } bind("string get_statName(stat::EmpireStat)", asFUNCTION(getEmpireStatName)); ClassBind stats("StatHistory", asOBJ_REF | asOBJ_SCOPED); classdoc(stats, "Accesses the history of an empire's stat. Locks the stat, so limit access to these histories."); stats.addFactory("StatHistory@ f(Empire&, stat::EmpireStat)", asFUNCTION(getStatHistory)); stats.addExternBehaviour(asBEHAVE_RELEASE, "void f()", asFUNCTION(StatReader::destroy)); stats.addMethod("bool toTail()", asMETHOD(StatReader,toTail)) doc("Advances to the most recent stat entry.", "True if there was a tail to move to."); stats.addMethod("bool toHead()", asMETHOD(StatReader,toHead)) doc("Advances to the earliest stat entry.", "True if there was a head to move to."); stats.addMethod("bool advance(int offset = 1)", asMETHOD(StatReader,advance)) doc("Advances to another stat entry.", "Number of entries to move. Positive is later in time, negative is earlier. Advancing 0 resets the event reader. If not on a valid entry, advances to a valid entry at the correspending head or tail. ", "Returns true if an entry existed at the offset."); stats.addMethod("int get_intVal() const", asMETHOD(StatReader,get_int)) doc("Returns the stat's value as an integer.", ""); stats.addMethod("float get_floatVal() const", asMETHOD(StatReader,get_float)) doc("Returns the stat's value as a float.", ""); stats.addMethod("uint get_time() const", asMETHOD(StatReader,get_time)) doc("Returns the stat's timestamp in seconds from the start of the game.", ""); stats.addMethod("bool advanceEvent()", asMETHOD(StatReader,advanceEvent)) doc("Advances to the next event for the current stat.", "True if there was an event to advance to."); stats.addMethod("uint get_eventType() const", asMETHOD(StatReader,get_evt_type)) doc("Returns the current event's type identifier.", ""); stats.addMethod("string get_eventName() const", asMETHOD(StatReader,get_evt_name)) doc("Returns the current event's name.", ""); if(server) { emp.addMethod("void recordStat(stat::EmpireStat, int)", asMETHODPR(Empire,recordStat,(unsigned,int),void)) doc("Records an int value for the specified empire stat at the current game time", "Stat index.", "Stat value."); emp.addMethod("void recordStat(stat::EmpireStat, float)", asMETHODPR(Empire,recordStat,(unsigned,float),void)) doc("Records a float value for the specified empire stat at the current game time", "Stat index.", "Stat value."); emp.addMethod("void recordStatDelta(stat::EmpireStat, int)", asMETHODPR(Empire,recordStatDelta,(unsigned,int),void)) doc("Records an int delta for the specified empire stat at the current game time", "Stat index.", "Stat delta."); emp.addMethod("void recordStatDelta(stat::EmpireStat, float)", asMETHODPR(Empire,recordStatDelta,(unsigned,float),void)) doc("Records a float delta for the specified empire stat at the current game time", "Stat index.", "Stat delta."); emp.addMethod("void recordEvent(stat::EmpireStat, uint16, const string &in)", asMETHOD(Empire,recordEvent)) doc("Records an event for the specified empire stat at the current game time", "Stat index.", "Event type.", "Event name."); } //Designs emp.addMember("ReadWriteMutex designMutex", offsetof(Empire, designMutex)); emp.addExternMethod("uint get_designCount() const", asFUNCTION(getDesignCount)); emp.addExternMethod("const Design@ getDesign(const string &in)", asFUNCTION(getDesignByName)); emp.addExternMethod("const Design@ getDesign(int id)", asFUNCTION(getDesignById)); emp.addExternMethod("const Design@ get_designs(int)", asFUNCTION(getDesignById)); emp.addExternMethod("uint get_designClassCount() const", asFUNCTION(getDesignClassCount)); emp.addMethod("const DesignClass@ getDesignClass(int id)", asMETHODPR(Empire, getDesignClass, (int), DesignClass*)); emp.addMethod("const DesignClass@ getDesignClass(const string &in, bool add = true)", asMETHODPR(Empire, getDesignClass, (const std::string&, bool), DesignClass*)); emp.addMethod("bool addDesign(const DesignClass& cls, const Design&)", asMETHOD(Empire, addDesign)); emp.addMethod("bool changeDesign(const Design&, const Design&, const DesignClass@ cls = null)", asMETHODPR(Empire, changeDesign, (const Design*, const Design*, DesignClass*), bool)); emp.addMethod("const Design@+ updateDesign(const Design&, bool onlyOutdated = false)", asMETHODPR(Empire, updateDesign, (const Design*, bool), const Design*)); emp.addMethod("void flagDesignOld(const Design&)", asMETHODPR(Empire, flagDesignOld, (const Design*), void)) doc("Marks a design as out of date, to be updated whenever it is requested for construction.", ""); emp.addMethod("bool get_valid()", asMETHOD(Empire, valid)); //Subsystem modifiers emp.addMember("ReadWriteMutex subsystemDataMutex", offsetof(Empire, subsystemDataMutex)); emp.addExternMethod("bool isUnlocked(const SubsystemDef& sys, const ModuleDef& mod)", asFUNCTION(ssModUnlocked)); emp.addExternMethod("bool isUnlocked(const SubsystemDef& sys)", asFUNCTION(ssUnlocked)); emp.addExternMethod("void setUnlocked(const SubsystemDef& sys, const ModuleDef& mod, bool unlocked)", asFUNCTION(ssSetModUnlock)); emp.addExternMethod("void setUnlocked(const SubsystemDef& sys, bool unlocked)", asFUNCTION(ssSetUnlock)); emp.addExternMethod("void removeModifier(const SubsystemDef& sys, uint id)", asFUNCTION(removeModifier)); emp.addExternMethod("void writeDelta(Message& msg)", asFUNCTION(writeDelta)); emp.addExternMethod("void readDelta(Message& msg)", asFUNCTION(readDelta)); std::string dargs; for(unsigned i = 0; i <= MODIFY_STAGE_MAXARGS; ++i) { emp.addGenericMethod( format("uint addModifier(const SubsystemDef& def, const string&in$1)", dargs).c_str(), asFUNCTION(addModifier_d)); dargs += ", float"; } //Global empire lists bindGlobal("Empire@ playerEmpire", &playerEmpire) doc("The player's current empire."); bindGlobal("Empire@ spectatorEmpire", &spectatorEmpire); bindGlobal("Empire@ defaultEmpire", &defaultEmpire); bind("uint getEmpireCount()", asFUNCTION(Empire::getEmpireCount)); bind("Empire@ getEmpire(uint index)", asFUNCTION(Empire::getEmpireByIndex)); bind("Empire@ getEmpireByID(uint8 id)", asFUNCTION(Empire::getEmpireByID)); RegisterEmpireComponentWrappers(emp, server); } };