Page MenuHomePhabricator (Chris)

No OneTemporary

Size
158 KB
Referenced Files
None
Subscribers
None
diff --git a/source/game/design/design.cpp b/source/game/design/design.cpp
index 0fd1260..d295ec6 100644
--- a/source/game/design/design.cpp
+++ b/source/game/design/design.cpp
@@ -1,1266 +1,1266 @@
#include "design/design.h"
#include "empire.h"
#include "network/message.h"
#include "util/save_file.h"
#include "main/references.h"
#include "main/logging.h"
#include "util/format.h"
#include "network/message.h"
#include <unordered_map>
#include <algorithm>
#include <queue>
#include <assert.h>
Design::Design(const Design::Descriptor& desc)
: hull(0), stateCount(0), effectorStateCount(0), dataCount(0), effectorCount(0),
initialized(false), id(-1), owner(0), used(false), obsolete(false), outdated(false), revision(0), cls(0), totalHP(0), forceHull(false), data(nullptr), clientData(nullptr), serverData(nullptr) {
init(desc);
}
Design::Design(net::Message& msg)
: hull(0), stateCount(0), effectorStateCount(0), dataCount(0), effectorCount(0),
initialized(false), id(-1), owner(0), used(false), obsolete(false), outdated(false), revision(0), cls(0), totalHP(0), forceHull(false), data(nullptr), clientData(nullptr), serverData(nullptr) {
init(msg);
}
Design::Design()
: hull(0), stateCount(0), effectorStateCount(0), dataCount(0), effectorCount(0),
initialized(false), id(-1), owner(0), used(false), obsolete(false), outdated(false), revision(0), cls(0), totalHP(0), forceHull(false), data(nullptr), clientData(nullptr), serverData(nullptr) {
}
void Design::init(const Design::Descriptor& desc) {
if(desc.hull == nullptr)
throw "Null Hull@ when creating design";
//Make an appropriate hull
HullDef* dynHull = nullptr;
if(!desc.staticHull) {
vec2i gridSize = desc.gridSize;
dynHull = new HullDef(*desc.hull);
dynHull->baseHull = desc.hull->baseHull;
hull = dynHull;
dynHull->activeCount = 0;
dynHull->exteriorCount = 0;
dynHull->gridSize = gridSize;
dynHull->gridOffset = recti();
dynHull->active.resize(gridSize.x, gridSize.y);
dynHull->active.clear(false);
dynHull->exterior.resize(gridSize.x, gridSize.y);
dynHull->exterior.clear(0);
dynHull->exteriorCount = 0;
//Add all used hexes to the dynamic hull
foreach(it, desc.systems) {
for(size_t i = 0, cnt = it->hexes.size(); i < cnt; ++i) {
vec2u v = it->hexes[i];
if(dynHull->active.valid(v)) {
dynHull->active[v] = true;
dynHull->activeCount += 1;
if(it->type->passExterior)
dynHull->exterior[v] |= HullDef::flagExteriorPass;
if(it->type->fauxExterior)
dynHull->exterior[v] |= HullDef::flagExteriorFaux;
}
}
}
dynHull->calculateExterior();
//Check for connectedness
if(!dynHull->checkConnected()) {
std::string errMsg = devices.locale.localize("ERROR_NOT_CONNECTED");
errors.push_back(DesignError(true, errMsg));
}
}
else {
hull = desc.hull;
hull->grab();
}
//Take over simple data
size = desc.size;
name = desc.name;
owner = desc.owner;
forceHull = desc.forceHull;
initialized = true;
numTags.insert(hull->numTags.begin(), hull->numTags.end());
if(owner && owner->shipset && !owner->shipset->hasHull(hull->baseHull)) {
std::string errMsg = devices.locale.localize("ERROR_HULL_NOT_IN_SHIPSET");
errors.push_back(DesignError(true, errMsg));
}
if(size < hull->minSize) {
std::string errMsg = format(devices.locale.localize("ERROR_MINIMUM_SIZE").c_str(),
hull->name, hull->minSize);
errors.push_back(DesignError(true, errMsg));
}
else if(hull->maxSize > 0 && size > hull->maxSize) {
std::string errMsg = format(devices.locale.localize("ERROR_MAXIMUM_SIZE").c_str(),
hull->name, hull->maxSize);
errors.push_back(DesignError(true, errMsg));
}
//Lock the things we need
if(owner)
owner->subsystemDataMutex.readLock();
grid.resize(hull->gridSize);
grid.clear(-1);
hexIndex.resize(hull->gridSize);
hexIndex.clear(-1);
hexStatusIndex.resize(hull->gridSize);
hexStatusIndex.clear(-1);
if(hull->activeCount != 0)
hexSize = size / (double)hull->activeCount;
else
hexSize = 0.0;
interiorHexes = 0;
exteriorHexes = 0;
usedHexCount = 0;
totalHP = 0.0;
for(unsigned i = 0; i < 4; ++i)
quadrantTotalHP[i] = 0.0;
//Add subsystems from the hull
std::vector<Descriptor::System> systems = desc.systems;
foreach(it, hull->subsystems) {
auto* type = getSubsystemDef(*it);
if(type) {
Descriptor::System sys;
sys.type = type;
sys.hexes.push_back(vec2u(-1, -1));
sys.modules.push_back(type->defaultModule);
systems.push_back(sys);
}
}
//Add applied subsystems
int appliedTag = desc.appliedSystems.empty() ? -1 : getSysTagIndex("Applied");
for(size_t i = 0, cnt = desc.appliedSystems.size(); i < cnt; ++i) {
auto* type = desc.appliedSystems[i];
if(type) {
Descriptor::System sys;
sys.type = type;
sys.hexes.push_back(vec2u(-1, -1));
sys.modules.push_back(type->defaultModule);
systems.push_back(sys);
if(!type->hasTag(appliedTag)) {
std::string errMsg = format(devices.locale.localize("ERROR_CANNOT_APPLY").c_str(), type->name);
errors.push_back(DesignError(true, errMsg));
}
else {
for(unsigned n = 0, ncnt = type->getTagValueCount(appliedTag); n < ncnt; ++n) {
std::string tag = type->getTagValue(appliedTag, n);
for(size_t j = 0; j < i; ++j) {
if(desc.appliedSystems[j] && desc.appliedSystems[j]->hasTagValue(appliedTag, tag)) {
std::string errMsg = format(devices.locale.localize("ERROR_ONE_APPLIED").c_str(), devices.locale.localize(format("APPLIED_$1", tag)));
errors.push_back(DesignError(true, errMsg));
}
}
}
}
}
}
//Initialize subsystems
HexGrid<bool> connected(grid.width, grid.height);
subsystems.resize(systems.size());
cropMin = vec2u(grid.width, grid.height);
cropMax = vec2u(0, 0);
for(unsigned index = 0; index < systems.size(); ++index) {
const Design::Descriptor::System& sd = systems[index];
if(!sd.type)
break;
numTags.insert(sd.type->numTags.begin(), sd.type->numTags.end());
Subsystem* sys = &subsystems[index];
sys->init(*sd.type);
sys->inDesign = this;
sys->index = index;
sys->direction = sd.direction;
sys->hexes.reserve(sd.hexes.size());
sys->stateOffset = stateCount;
sys->effectorOffset = effectorCount;
auto modCnt = sys->type->modules.size();
sys->moduleCounts.resize(modCnt);
for(size_t i = 0; i < modCnt; ++i)
sys->moduleCounts[i] = 0;
//Set up effector states
int effStates = 0;
for(size_t i = 0, cnt = sd.type->effectors.size(); i < cnt; ++i) {
sys->effectors[i].stateOffset = effectorStateCount;
effStates += sd.type->effectors[i].type->stateCount;
}
//Copy hexes
int hexInd = 0;
for(size_t i = 0, cnt = sd.hexes.size(); i < cnt; ++i) {
vec2u v = sd.hexes[i];
auto* mod = sd.modules[i];
if(!mod)
mod = sys->type->defaultModule;
if(!sd.type->isHull && !sd.type->isApplied) {
if(v.x >= grid.width || v.y >= grid.height)
continue;
if(!hull->active[v])
continue;
if(grid[v] != -1)
continue;
if(hull->isExterior(v)) {
++sys->exteriorHexes;
++exteriorHexes;
}
else {
++interiorHexes;
}
grid[v] = index;
hexIndex[v] = hexInd++;
hexStatusIndex[v] = usedHexCount++;
cropMin.x = std::min(cropMin.x, v.x);
cropMin.y = std::min(cropMin.y, v.y);
cropMax.x = std::max(cropMax.x, v.x);
cropMax.y = std::max(cropMax.y, v.y);
}
sys->hexes.push_back(v);
sys->modules.push_back(mod);
hexes.push_back(v);
++sys->moduleCounts[mod->index];
}
unsigned efftrCount = (unsigned)sd.type->effectors.size();
for(unsigned i = 0; i < efftrCount; ++i) {
sys->effectors[i].inDesign = this;
sys->effectors[i].subsysIndex = sys->index;
}
stateCount += (unsigned)sd.type->states.size();
effectorCount += efftrCount;
effectorStateCount += effStates;
if(sys->type->isContiguous && !sys->type->isHull && !sys->type->isApplied && !sys->hexes.empty()) {
connected.zero();
sys->markConnected(connected, sys->hexes[0]);
for(size_t i = 1, cnt = sys->hexes.size(); i < cnt; ++i) {
if(!connected[sys->hexes[i]]) {
std::string errMsg = format(devices.locale.localize("ERROR_NOT_CONTIGUOUS").c_str(), sys->type->name);
errors.push_back(DesignError(true, errMsg, sys));
sys->hasErrors = true;
break;
}
}
}
}
//Prepare ship variables
unsigned shipVarCnt = getShipVariableCount();
shipVariables = new float[shipVarCnt];
memset(shipVariables, 0, shipVarCnt * sizeof(float));
//Store calculated hex size
if((unsigned)ShV_HexSize < shipVarCnt)
shipVariables[ShV_HexSize] = hexSize;
//Evaluate subsystem variables
std::priority_queue<std::pair<int,Subsystem*>> sysQueue;
foreach(it, subsystems)
sysQueue.push(std::pair<int,Subsystem*>(-it->type->ordering, &*it));
Colorf colf(0.f, 0.f, 0.f, 0.f);
while(!sysQueue.empty()) {
auto* sys = sysQueue.top().second;
sys->initVariables(this);
sysQueue.pop();
float* sysSize = sys->variable(SV_Size);
if(sysSize) {
float relSize = *sysSize / (float)size;
Colorf sysColor(sys->type->typeColor);
sysColor *= sysColor.a;
sysColor.a = 1.f;
colf += sysColor * (relSize * relSize);
}
}
colf.a = 1.f;
float hue = colf.getHue();
colf.fromHSV(hue, 1.f, 1.f);
color = Color(colf);
colf.fromHSV(hue, 0.6f, 0.6f);
dullColor = Color(colf);
//Evaluate adjacency mods
foreach(it, subsystems)
it->applyAdjacencies(this);
//Evaluate subsystem asserts
foreach(it, subsystems)
it->evaluateAsserts(this);
//Evaluate subsystem posts
foreach(it, subsystems)
it->evaluatePost(this);
//Evaluate subsystem effects
foreach(it, subsystems) {
it->initEffects(this);
if(owner)
it->skinEffectors(*owner);
it->dataOffset = dataCount;
dataCount += it->hookClasses.size();
}
//Calculate total HP
foreach(it, subsystems) {
auto& sys = *it;
unsigned hexCnt = (unsigned)sys.hexes.size();
for(unsigned j = 0; j < hexCnt; ++j) {
float* hexVal = sys.hexVariable(HV_HP, j);
if(hexVal) {
totalHP += *hexVal;
quadrantTotalHP[getQuadrant(sys.hexes[j])] += *hexVal;
}
}
}
//Record possibly altered hex size
if((unsigned)ShV_HexSize < shipVarCnt)
hexSize = shipVariables[ShV_HexSize];
//Unlock the things we locked
if(owner)
owner->subsystemDataMutex.release();
//Create a secondary damage order list for globalDamage events
buildDamageOrder();
}
void Design::buildDamageOrder() {
damageOrder.clear();
foreach(it, subsystems) {
auto* sys = &*it;
if(sys->hasGlobalDamage())
damageOrder.push_back(sys);
}
std::sort(damageOrder.begin(), damageOrder.end(), [](Subsystem* first, Subsystem* second) -> bool {
return first->type->damageOrder < second->type->damageOrder;
});
}
bool Design::hasFatalErrors() const {
foreach(it, errors)
if(it->fatal)
return true;
return false;
}
bool Design::hasTag(const std::string& tag) const {
foreach(it, subsystems)
if(it->type->hasTag(tag))
return true;
return false;
}
bool Design::hasTag(int index) const {
auto it = numTags.find(index);
return it != numTags.end();
}
const Design* Design::newest() const {
const Design* cur = this;
if(original)
cur = original;
while(cur->newer)
cur = cur->newer;
while(cur->updated)
cur = cur->updated;
return cur;
}
const Design* Design::next() const {
if(original)
return original->newer;
return newer;
}
const Design* Design::mostUpdated() const {
const Design* cur = this;
while(cur->updated)
cur = cur->updated;
return cur;
}
const Design* Design::base() const {
return original ? original.ptr : this;
}
Design::~Design() {
delete[] shipVariables;
hull->drop();
if(original == nullptr || original == this) {
if(data) {
delete data;
data = nullptr;
}
if(clientData) {
clientData->Release();
clientData = nullptr;
}
if(serverData) {
serverData->Release();
serverData = nullptr;
}
}
}
unsigned Design::getQuadrant(const vec2u& pos) const {
unsigned quad = 0;
int dist = pos.y - cropMin.y;
int d = cropMax.x - pos.x;
if(d < dist) {
quad = 1;
dist = d;
}
d = cropMax.y - pos.y;
if(d < dist) {
quad = 2;
dist = d;
}
d = pos.x - cropMin.x;
if(d < dist) {
quad = 3;
dist = d;
}
return quad;
}
void Design::makeDistanceMap(Image& img, vec2i pos, vec2i size) const {
int xspac = 1;
int yspac = 1 + ceil(double(size.y) * 0.1);
double xrat = double(size.x-xspac-xspac) / double(hull->active.width) / 0.75;
double yrat = double(size.y-yspac-yspac) / double(hull->active.height);
double distFact = 40.0 / yrat;
for(int y = yspac; y < size.y-yspac; ++y) {
for(int x = xspac; x < size.x-xspac; ++x) {
vec2d pctPos(double(x-xspac) / double(size.x-xspac-xspac), double(y-yspac) / double(size.y-yspac-yspac));
vec2d grid(double(x-xspac) / xrat, double(y-yspac) / yrat);
vec2i gridPos = hull->active.getGridPosition(grid);
double dist = 100.0;
if(hull->active.valid(gridPos) && hull->active[gridPos]) {
vec2d gridPct = hull->active.getEffectivePosition(gridPos);
gridPct.x += 0.75 * 0.5;
gridPct.y += 0.5;
gridPct.x /= 0.75 * double(hull->active.width);
gridPct.y /= double(hull->active.height);
dist = std::min(pctPos.distanceTo(gridPct), dist);
}
for(unsigned i = 0; i < 6; ++i) {
vec2u pos = vec2u(gridPos);
if(hull->active.advance(pos.x, pos.y, (HexGridAdjacency)i)) {
if(hull->active.valid(pos) && hull->active[pos]) {
vec2d gridPct = hull->active.getEffectivePosition(pos);
gridPct.x += 0.75 * 0.5;
gridPct.y += 0.5;
gridPct.x /= 0.75 * double(hull->active.width);
gridPct.y /= double(hull->active.height);
dist = std::min(pctPos.distanceTo(gridPct), dist);
}
}
}
Color& col = img.get_rgba(x+pos.x, pos.y+y);
col.color = 0xff0000ff;
dist *= distFact;
if(dist < 0.8)
col.a = 0xff;
else
col.a = 0x00;
if(dist > 0.6 && dist < 1.5)
col.b = 0xff;
else
col.b = 0x00;
if(dist < 0.5)
col.g = 0xff;
else
col.g = 255.0 * std::max(1.0 - (dist-0.5)*2.0, 0.0);
}
}
}
void Design::toDescriptor(Design::Descriptor& desc) const {
desc.owner = owner;
desc.hull = hull;
if(hull)
hull->grab();
desc.name = name;
desc.size = size;
desc.forceHull = forceHull;
desc.gridSize = hull->gridSize;
if(cls)
desc.className = cls->name;
else
desc.className = "";
unsigned short sysCnt = (unsigned short)subsystems.size();
desc.systems.reserve(sysCnt);
for(unsigned short i = 0; i < sysCnt; ++i) {
auto& sys = subsystems[i];
if(sys.type->isHull)
continue;
if(sys.type->isApplied) {
desc.appliedSystems.push_back(sys.type);
continue;
}
desc.systems.push_back(Design::Descriptor::System());
Design::Descriptor::System& sdesc = desc.systems.back();
sdesc.direction = sys.direction;
sdesc.type = sys.type;
unsigned short hexCnt = (unsigned short)sys.hexes.size();
sdesc.hexes.resize(hexCnt);
sdesc.modules.resize(hexCnt);
for(unsigned short j = 0; j < hexCnt; ++j) {
sdesc.hexes[j] = sys.hexes[j];
sdesc.modules[j] = sys.modules[j];
}
}
}
void Design::write(net::Message& msg) const {
if(owner)
msg << owner->id;
else
msg << INVALID_EMPIRE;
msg.writeSmall(hull->baseHull->id);
msg << name;
msg << size;
msg << forceHull;
msg << obsolete;
msg << revision;
msg.writeSmall(hull->gridSize.width);
msg.writeSmall(hull->gridSize.height);
msg.writeSmall((unsigned)subsystems.size());
for(unsigned i = 0, sysCnt = (unsigned)subsystems.size(); i < sysCnt; ++i) {
auto& sys = subsystems[i];
if(sys.type->isHull) {
msg.writeSignedSmall(-1);
continue;
}
msg.writeSignedSmall(sys.type->index);
if(sys.type->isApplied)
continue;
msg.writeDirection(sys.direction.x, sys.direction.y, sys.direction.z);
msg.writeSmall((unsigned)sys.hexes.size());
vec2u gridSize = vec2u(hull->gridSize);
for(unsigned short j = 0, hexCnt = (unsigned)sys.hexes.size(); j < hexCnt; ++j) {
vec2u pos = sys.hexes[j];
msg.writeLimited(pos.x, gridSize.width);
msg.writeLimited(pos.y, gridSize.height);
msg.writeSignedSmall(sys.modules[j]->index);
}
}
//Script data
if(data != nullptr) {
msg.write1();
char* pData; net::msize_t size;
data->getAsPacket(pData, size);
msg.writeSmall(size);
msg.write(pData, size);
}
else {
msg.write0();
}
}
void Design::init(net::Message& msg) {
Descriptor desc;
//Read empire
unsigned char empID;
msg >> empID;
desc.owner = Empire::getEmpireByID(empID);
//Read hull
desc.hull = getHullDefinition(msg.readSmall());
if(desc.hull)
desc.hull->grab();
//Get other blueprint data
msg >> desc.name;
msg >> desc.size;
msg >> desc.forceHull;
msg >> obsolete;
msg >> revision;
desc.gridSize.x = msg.readSmall();
desc.gridSize.y = msg.readSmall();
//Read subsystems
unsigned sysCnt = msg.readSmall();
desc.systems.reserve(sysCnt);
for(unsigned i = 0; i < sysCnt; ++i) {
int sysIndex = msg.readSignedSmall();
if(sysIndex == -1)
continue;
auto* type = getSubsystemDef(sysIndex);
if(type->isApplied) {
desc.appliedSystems.push_back(type);
continue;
}
desc.systems.push_back(Design::Descriptor::System());
Design::Descriptor::System& sys = desc.systems.back();
sys.type = type;
msg.readDirection(sys.direction.x, sys.direction.y, sys.direction.z);
unsigned hexCnt = msg.readSmall();
sys.hexes.resize(hexCnt);
sys.modules.resize(hexCnt);
for(unsigned j = 0; j < hexCnt; ++j) {
unsigned x = msg.readLimited(desc.gridSize.width);
unsigned y = msg.readLimited(desc.gridSize.height);
sys.hexes[j] = vec2u(x, y);
int modIndex = msg.readSignedSmall();
if(sys.type == 0 || modIndex < 0 || modIndex >= (int)sys.type->modules.size())
sys.modules[j] = 0;
else
sys.modules[j] = sys.type->modules[modIndex];
}
}
//Initialize design
init(desc);
//Script data
if(msg.readBit()) {
net::msize_t size = msg.readSmall();
data = new net::Message();
if(size > 0) {
char* buffer = (char*)malloc(size);
msg.read(buffer, size);
data->setPacket(buffer, size);
free(buffer);
}
bindData();
}
}
static asIScriptObject* buildDataForEngine(scripts::Manager* man, void* msg, unsigned call) {
//Create the object
asITypeInfo* cls = man->getClass("design_settings", "DesignSettings");
if(cls == nullptr)
return nullptr;
asIScriptFunction* func = cls->GetFactoryByIndex(0);
if(!func)
return nullptr;
asIScriptObject* ptr = 0;
{
scripts::Call cl = man->call(func);
cl.call(ptr);
if(ptr)
ptr->AddRef();
}
//Read the mesasge
auto* readFunc = (asIScriptFunction*)man->engine->GetUserData(call);
if(ptr && readFunc) {
scripts::Call cl = man->call(readFunc);
cl.setObject(ptr);
cl.push(msg);
cl.call();
}
return ptr;
}
void Design::bindData() {
if(data) {
data->rewind();
clientData = buildDataForEngine(devices.scripts.client, data, scripts::EDID_SerializableRead);
data->rewind();
serverData = buildDataForEngine(devices.scripts.server, data, scripts::EDID_SerializableRead);
data->rewind();
}
}
Design::Design(SaveFile& file) : initialized(true), data(nullptr), clientData(nullptr), serverData(nullptr) {
file >> name >> size >> hexSize >> interiorHexes >> exteriorHexes
>> stateCount >> effectorStateCount >> effectorCount >> usedHexCount
>> used >> obsolete >> id >> revision >> totalHP >> color >> dullColor;
if(file >= SFV_0002)
file >> outdated;
else
outdated = false;
built.set_basic(file);
active.set_basic(file);
if(file >= SFV_0008)
file >> dataCount;
else
dataCount = 0;
if(file >= SFV_0016)
file >> forceHull;
else
forceHull = false;
unsigned hullID;
if(file >= SFV_0004) {
hullID = file.readIdentifier(SI_Hull);
}
else {
unsigned oldID = file;
auto* set = getShipset("Original_r3000");
hullID = set->hulls[oldID]->id;
}
hull = getHullDefinition(hullID);
if(hull == 0)
throw SaveFileError("Design lacks hull");
numTags.insert(hull->numTags.begin(), hull->numTags.end());
owner = Empire::getEmpireByID(file);
if(owner == 0)
throw SaveFileError("Design lacks owner");
//if(owner->shipset && !owner->shipset->hasHull(hull))
// throw SaveFileError("Hull is not present in the owner's shipset");
if(file >= SFV_0013) {
bool dynamic = file;
if(dynamic) {
auto* dynHull = new HullDef(*hull);
file >> dynHull->gridSize;
file >> dynHull->activeCount;
file >> dynHull->exteriorCount;
dynHull->gridOffset = recti();
dynHull->active.resize(dynHull->gridSize.x, dynHull->gridSize.y);
dynHull->active.clear(false);
dynHull->exterior.resize(dynHull->gridSize.x, dynHull->gridSize.y);
dynHull->exterior.clear(0);
for(size_t i = 0, cnt = dynHull->active.length(); i < cnt; ++i) {
file >> dynHull->active[i];
file >> dynHull->exterior[i];
}
dynHull->baseHull = hull;
hull = dynHull;
}
else {
hull->grab();
}
}
else {
hull->grab();
}
//Hex grids
grid.resize(hull->gridSize);
hexIndex.resize(hull->gridSize);
hexStatusIndex.resize(hull->gridSize);
file.read(grid.data, sizeof(int) * grid.width * grid.height);
file.read(hexIndex.data, sizeof(int) * hexIndex.width * hexIndex.height);
file.read(hexStatusIndex.data, sizeof(int) * hexStatusIndex.width * hexStatusIndex.height);
unsigned hexesCount = file;
hexes.resize(hexesCount);
- file.read(&hexes.front(), sizeof(vec2u) * hexesCount);
+ file.read(hexes.data(), sizeof(vec2u) * hexesCount);
cropMin = vec2u(grid.width, grid.height);
cropMax = vec2u(0, 0);
foreach(h, hexes) {
if(hull->active.valid(*h)) {
cropMin.x = std::min(cropMin.x, h->x);
cropMin.y = std::min(cropMin.y, h->y);
cropMax.x = std::max(cropMax.x, h->x);
cropMax.y = std::max(cropMax.y, h->y);
}
}
unsigned shipVarCount = getShipVariableCount();
shipVariables = new float[shipVarCount]();
unsigned cnt = file;
for(unsigned i = 0; i < cnt; ++i) {
int index = file.readIdentifier(SI_ShipVar);
if(index != -1)
file >> shipVariables[index];
else
file.read<float>();
}
//Subsystems
unsigned subsysCount = file;
subsystems.resize(subsysCount);
for(unsigned i = 0; i < subsysCount; ++i) {
auto& sys = subsystems[i];
sys.init(file);
sys.inDesign = this;
sys.index = i;
auto efftrcnt = sys.type->effectors.size();
for(size_t j = 0; j < efftrcnt; ++j) {
sys.effectors[j].inDesign = this;
sys.effectors[j].subsysIndex = i;
}
numTags.insert(sys.type->numTags.begin(), sys.type->numTags.end());
}
if(file >= SFV_0020) {
for(unsigned i = 0; i < 4; ++i)
file >> quadrantTotalHP[i];
}
else {
for(unsigned i = 0; i < 4; ++i)
quadrantTotalHP[i] = 0.0;
foreach(it, subsystems) {
auto& sys = *it;
unsigned hexCnt = (unsigned)sys.hexes.size();
for(unsigned j = 0; j < hexCnt; ++j) {
float* hexVal = sys.hexVariable(HV_HP, j);
if(hexVal)
quadrantTotalHP[getQuadrant(sys.hexes[j])] += *hexVal;
}
}
}
for(unsigned i = 0; i < subsysCount; ++i)
subsystems[i].postLoad(this);
//Script data
if(file >= SFV_0018) {
if(file.read<bool>()) {
unsigned size = file;
SaveMessage msg(file);
if(size > 0) {
char* buffer = (char*)malloc(size);
file.read(buffer, size);
msg.setPacket(buffer, size);
free(buffer);
}
serverData = buildDataForEngine(devices.scripts.server, &msg, scripts::EDID_SavableRead);
data = new net::Message();
auto* writeFunc = (asIScriptFunction*)devices.scripts.server->engine->GetUserData(scripts::EDID_SerializableWrite);
if(writeFunc) {
scripts::Call cl = devices.scripts.server->call(writeFunc);
cl.setObject(serverData);
cl.push(data);
cl.call();
}
clientData = buildDataForEngine(devices.scripts.client, data, scripts::EDID_SerializableRead);
}
}
buildDamageOrder();
}
void Design::save(SaveFile& file) const {
file << name << size << hexSize << interiorHexes << exteriorHexes
<< stateCount << effectorStateCount << effectorCount << usedHexCount
<< used << obsolete << id << revision << totalHP << color << dullColor;
file << outdated;
file << built.get() << active.get();
file << dataCount;
file << forceHull;
file.writeIdentifier(SI_Hull, hull->baseHull->id);
file << owner->id;
if(hull != hull->baseHull) {
file << true;
file << hull->gridSize;
file << hull->activeCount << hull->exteriorCount;
for(size_t i = 0, cnt = hull->active.length(); i < cnt; ++i) {
file << hull->active[i];
file << hull->exterior[i];
}
}
else
file << false;
//Hex grids
file.write(grid.data, sizeof(int) * grid.width * grid.height);
file.write(hexIndex.data, sizeof(int) * hexIndex.width * hexIndex.height);
file.write(hexStatusIndex.data, sizeof(int) * hexStatusIndex.width * hexStatusIndex.height);
file << (unsigned)hexes.size();
- file.write(&hexes.front(), sizeof(vec2u) * (unsigned)hexes.size());
+ file.write(hexes.data(), sizeof(vec2u) * (unsigned)hexes.size());
unsigned cnt = getShipVariableCount();
file << cnt;
for(unsigned i = 0; i < cnt; ++i) {
file.writeIdentifier(SI_ShipVar, i);
file << shipVariables[i];
}
//Subsystems
file << unsigned(subsystems.size());
for(unsigned i = 0; i < subsystems.size(); ++i)
subsystems[i].save(file);
for(unsigned i = 0; i < 4; ++i)
file << quadrantTotalHP[i];
//Script data
if(serverData != nullptr && (original == nullptr || original == this)) {
SaveMessage msg(file);
auto* writeFunc = (asIScriptFunction*)devices.scripts.server->engine->GetUserData(scripts::EDID_SavableWrite);
if(writeFunc) {
file << true;
{
scripts::Call cl = devices.scripts.server->call(writeFunc);
cl.setObject(serverData);
cl.push(&msg);
cl.call();
}
char* pData; net::msize_t size;
msg.getAsPacket(pData, size);
file << size;
file.write(pData, size);
}
else {
file << false;
}
}
else {
file << false;
}
}
void Design::writeData(net::Message& msg) const {
msg.writeSmall(hull->baseHull->id);
msg << name;
msg << size;
msg << hexSize;
msg.writeSmall(interiorHexes);
msg.writeSmall(exteriorHexes);
msg.writeSmall(usedHexCount);
msg.writeSmall(stateCount);
msg.writeSmall(effectorStateCount);
msg.writeSmall(effectorCount);
msg << initialized;
msg << totalHP;
for(unsigned i = 0; i < 4; ++i)
msg << quadrantTotalHP[i];
msg << color;
msg << dullColor;
msg << forceHull;
if(hull != hull->baseHull) {
msg.write1();
msg.writeSmall(hull->gridSize.x);
msg.writeSmall(hull->gridSize.y);
}
else {
msg.write0();
}
msg.writeSmall((unsigned)numTags.size());
foreach(it, numTags)
msg.writeSmall(*it);
msg.writeSmall((unsigned)subsystems.size());
foreach(it, subsystems)
it->writeData(msg);
for(unsigned i = 0, cnt = (unsigned)grid.length(); i < cnt; ++i) {
if(grid[i] == -1) {
msg.write0();
}
else {
msg.write1();
msg.writeSmall(grid[i]);
}
if(hexIndex[i] == -1) {
msg.write0();
}
else {
msg.write1();
msg.writeSmall(hexIndex[i]);
}
if(hexStatusIndex[i] == -1) {
msg.write0();
}
else {
msg.write1();
msg.writeSmall(hexStatusIndex[i]);
}
}
msg.writeSmall((unsigned)hexes.size());
foreach(it, hexes) {
msg.writeSmall(it->x);
msg.writeSmall(it->y);
}
for(size_t i = 0, cnt = getShipVariableCount(); i < cnt; ++i)
msg << shipVariables[i];
msg << id;
msg.writeSmall(owner->id);
msg << used;
msg << obsolete;
msg << revision;
msg.writeSmall(built.get());
msg.writeSmall(active.get());
if(newer) {
msg.write1();
msg.writeSmall(newer->id);
}
else {
msg.write0();
}
if(original) {
msg.write1();
msg.writeSmall(original->id);
}
else {
msg.write0();
}
if(updated) {
msg.write1();
msg.writeSmall(updated->id);
}
else {
msg.write0();
}
msg << cls->name;
//Script data
if(data != nullptr) {
msg.write1();
char* pData; net::msize_t size;
data->getAsPacket(pData, size);
msg.writeSmall(size);
msg.write(pData, size);
}
else {
msg.write0();
}
}
void Design::initData(net::Message& msg) {
hull = getHullDefinition(msg.readSmall());
msg >> name;
msg >> size;
msg >> hexSize;
interiorHexes = msg.readSmall();
exteriorHexes = msg.readSmall();
usedHexCount = msg.readSmall();
stateCount = msg.readSmall();
effectorStateCount = msg.readSmall();
effectorCount = msg.readSmall();
dataCount = 0;
msg >> initialized;
msg >> totalHP;
for(unsigned i = 0; i < 4; ++i)
msg >> quadrantTotalHP[i];
msg >> color;
msg >> dullColor;
msg >> forceHull;
HullDef* dynHull = nullptr;
if(msg.readBit()) {
dynHull = new HullDef(*hull);
dynHull->baseHull = hull;
hull = dynHull;
dynHull->gridSize.x = msg.readSmall();
dynHull->gridSize.y = msg.readSmall();
dynHull->activeCount = 0;
dynHull->exteriorCount = 0;
dynHull->gridOffset = recti();
dynHull->active.resize(dynHull->gridSize.x, dynHull->gridSize.y);
dynHull->active.clear(false);
dynHull->exterior.resize(dynHull->gridSize.x, dynHull->gridSize.y);
dynHull->exterior.clear(0);
}
unsigned cnt = msg.readSmall();
for(unsigned i = 0; i < cnt; ++i)
numTags.insert(msg.readSmall());
subsystems.resize(msg.readSmall());
for(size_t i = 0, cnt = subsystems.size(); i < cnt; ++i) {
auto* sys = &subsystems[i];
sys->~Subsystem();
new(sys) Subsystem(msg);
sys->inDesign = this;
sys->index = (unsigned)i;
unsigned efftrCount = (unsigned)sys->type->effectors.size();
for(unsigned n = 0; n < efftrCount; ++n) {
sys->effectors[n].inDesign = this;
sys->effectors[n].subsysIndex = (unsigned)i;
sys->effectors[n].effectorIndex = n;
}
}
grid.resize(hull->gridSize);
hexIndex.resize(hull->gridSize);
hexStatusIndex.resize(hull->gridSize);
for(unsigned i = 0, cnt = (unsigned)grid.length(); i < cnt; ++i) {
if(msg.readBit()) {
grid[i] = msg.readSmall();
assert(grid[i] < 0 || grid[i] < (int)subsystems.size());
}
else
grid[i] = -1;
if(msg.readBit())
hexIndex[i] = msg.readSmall();
else
hexIndex[i] = -1;
if(msg.readBit())
hexStatusIndex[i] = msg.readSmall();
else
hexStatusIndex[i] = -1;
}
cropMin = vec2u(grid.width, grid.height);
cropMax = vec2u(0, 0);
hexes.resize(msg.readSmall());
for(size_t i = 0, cnt = hexes.size(); i < cnt; ++i) {
hexes[i].x = msg.readSmall();
hexes[i].y = msg.readSmall();
if(dynHull) {
if(dynHull->active.valid(hexes[i]))
dynHull->active[hexes[i]] = true;
}
if(hull->active.valid(hexes[i])) {
cropMin.x = std::min(cropMin.x, hexes[i].x);
cropMin.y = std::min(cropMin.y, hexes[i].y);
cropMax.x = std::max(cropMax.x, hexes[i].x);
cropMax.y = std::max(cropMax.y, hexes[i].y);
}
}
shipVariables = new float[getShipVariableCount()];
for(size_t i = 0, cnt = getShipVariableCount(); i < cnt; ++i)
msg >> shipVariables[i];
msg >> id;
unsigned ownerId = msg.readSmall();
owner = Empire::getEmpireByID(ownerId);
msg >> used;
msg >> obsolete;
msg >> revision;
built = msg.readSmall();
active = msg.readSmall();
if(msg.readBit())
newer = owner->getDesignMake(msg.readSmall());
if(msg.readBit())
original = owner->getDesignMake(msg.readSmall());
if(msg.readBit())
updated = owner->getDesignMake(msg.readSmall());
std::string clsname;
msg >> clsname;
cls = owner->getDesignClass(clsname);
if(dynHull)
dynHull->calculateExterior();
//Script data
if(msg.readBit()) {
net::msize_t size = msg.readSmall();
data = new net::Message();
if(size > 0) {
char* buffer = (char*)malloc(size);
msg.read(buffer, size);
data->setPacket(buffer, size);
free(buffer);
}
bindData();
}
buildDamageOrder();
}
diff --git a/source/game/design/subsystem.cpp b/source/game/design/subsystem.cpp
index d1d13c2..fa03a91 100644
--- a/source/game/design/subsystem.cpp
+++ b/source/game/design/subsystem.cpp
@@ -1,3970 +1,3970 @@
#include "design/subsystem.h"
#include "design/design.h"
#include "design/hull.h"
#include "main/initialization.h"
#include "main/references.h"
#include "network/network_manager.h"
#include "main/logging.h"
#include "scripts/manager.h"
#include "str_util.h"
#include "util/format.h"
#include "util/save_file.h"
#include "threads.h"
#include "compat/misc.h"
#include "empire.h"
#include "obj/blueprint.h"
#include <algorithm>
#include <unordered_map>
#include <queue>
std::vector<SubsystemDef*> subsystems;
umap<std::string, int> subsystemIndices;
umap<std::string, int> variableIndices;
umap<std::string, int> hexVariableIndices;
umap<std::string, int> shipVariableIndices;
umap<std::string, int> numericTags;
asIScriptFunction* Subsystem::ScriptInitFunction;
asIScriptFunction* Subsystem::ScriptHookFunctions[EH_COUNT];
std::vector<SubsystemDef::ModuleDesc*> ModulesByID;
std::vector<SubsystemDef::ModifyStage*> ModifiersByID;
int getSubsystemDefCount() {
return (int)subsystems.size();
}
void enumerateVariables(std::function<void(const std::string&,int)> cb) {
foreach(it, variableIndices)
cb(it->first, it->second);
}
void enumerateHexVariables(std::function<void(const std::string&,int)> cb) {
foreach(it, hexVariableIndices)
cb(it->first, it->second);
}
void enumerateShipVariables(std::function<void(const std::string&,int)> cb) {
foreach(it, shipVariableIndices)
cb(it->first, it->second);
}
void enumerateSysTags(std::function<void(const std::string&,int)> cb) {
foreach(it, numericTags)
cb(it->first, it->second);
}
const SubsystemDef* getSubsystemDef(const std::string& name) {
auto it = subsystemIndices.find(name);
if(it == subsystemIndices.end())
return 0;
return subsystems[it->second];
}
const SubsystemDef* getSubsystemDef(int id) {
if(id < 0 || id >= (int)subsystems.size())
return 0;
return subsystems[id];
}
int getVariableIndex(const std::string& name) {
auto it = variableIndices.find(name);
if(it == variableIndices.end())
return -1;
return it->second;
}
int getHexVariableIndex(const std::string& name) {
auto it = hexVariableIndices.find(name);
if(it == hexVariableIndices.end())
return -1;
return it->second;
}
unsigned getShipVariableCount() {
return (unsigned)shipVariableIndices.size();
}
int getShipVariableIndex(const std::string& name) {
auto it = shipVariableIndices.find(name);
if(it == shipVariableIndices.end())
return -1;
return it->second;
}
int getSysTagIndex(const std::string& name, bool create) {
auto it = numericTags.find(name);
if(it == numericTags.end()) {
if(create) {
unsigned index = numericTags.size();
numericTags[name] = index;
return index;
}
return -1;
}
return it->second;
}
const std::string errStr = "N/A";
const std::string& getVariableId(int index) {
//TODO: Make not slow
foreach(it, variableIndices) {
if(it->second == index)
return it->first;
}
return errStr;
}
const std::string& getHexVariableId(int index) {
//TODO: Make not slow
foreach(it, hexVariableIndices) {
if(it->second == index)
return it->first;
}
return errStr;
}
const std::string& getShipVariableId(int index) {
//TODO: Make not slow
foreach(it, shipVariableIndices) {
if(it->second == index)
return it->first;
}
return errStr;
}
enum VariableType {
VTF_BaseVariable = 1 << 31,
VT_ConstantVariable = 0 << 24,
VT_SubsystemVariable = 1 << 24,
VT_HexVariable = 2 << 24,
VT_ModuleCount = 3 << 24,
VT_ModuleExists = 4 << 24,
VT_Argument = 5 << 24,
VT_ShipVariable = 6 << 24,
VT_GameConfig = 7 << 24,
VT_SumVariable = 8 << 24,
VT_HexSumVariable = 9 << 24,
VT_TagCountVariable = 10 << 24,
VT_AdjacentTag = 11 << 24,
VT_AdjacentSubsystem = 12 << 24,
};
enum ConstantVariable { //:P
CV_Hexes,
CV_InteriorHexes,
CV_ExteriorHexes,
CV_HexSize,
CV_HexExterior,
CV_ShipSize,
CV_ShipTotalHexes,
CV_ShipUsedHexes,
CV_ShipEmptyHexes,
CV_AdjacentActive,
CV_AdjacentThis,
CV_IsCore,
};
Threaded(const SubsystemDef*) formulaSubsystem = 0;
Threaded(const SubsystemDef::ModifyStage*) formulaModifier = 0;
Threaded(bool) formulaQuiet = false;
static int formulaVarIndex(const std::string* nameptr) {
std::string name = *nameptr;
//Arguments
if(formulaModifier) {
auto it = formulaModifier->argumentNames.find(name);
if(it != formulaModifier->argumentNames.end())
return VT_Argument | it->second;
}
//Constant variables
if(name == "Hexes")
return VT_ConstantVariable | CV_Hexes;
else if(name == "InteriorHexes")
return VT_ConstantVariable | CV_InteriorHexes;
else if(name == "ExteriorHexes")
return VT_ConstantVariable | CV_ExteriorHexes;
else if(name == "HexSize")
return VT_ShipVariable | ShV_HexSize;
else if(name == "ShipSize")
return VT_ConstantVariable | CV_ShipSize;
else if(name == "ShipUsedHexes")
return VT_ConstantVariable | CV_ShipUsedHexes;
else if(name == "ShipTotalHexes")
return VT_ConstantVariable | CV_ShipTotalHexes;
else if(name == "ShipEmptyHexes")
return VT_ConstantVariable | CV_ShipEmptyHexes;
else if(name == "IsCore")
return VT_ConstantVariable | CV_IsCore;
//Game configuration
if(name[0] == '$') {
auto it = gameConfig.indices.find(name.substr(1));
if(it != gameConfig.indices.end())
return VT_GameConfig | (int)it->second;
}
unsigned flags = 0;
if(name.compare(0, 6, "Base::") == 0) {
flags |= VTF_BaseVariable;
name = name.substr(6);
}
if(name.find('.') != std::string::npos) {
//Hex variables
if(name.compare(0, 4, "Hex.") == 0) {
//Constant hex variables
std::string varname = name.substr(4, name.size() - 4);
if(varname == "Exterior")
return VT_ConstantVariable | CV_HexExterior;
//Hex formula variables
int i = getHexVariableIndex(varname);
if(i < 0) {
if(!formulaQuiet)
error("Error: Formula: Invalid variable '%s'.", name.c_str());
return -1;
}
if(formulaSubsystem) {
if(i >= (int)formulaSubsystem->hexVariableIndices.size() ||
formulaSubsystem->hexVariableIndices[i] < 0) {
if(!formulaQuiet)
error("Error: Formula: Subsystem '%s' does not have variable '%s'.",
formulaSubsystem->id.c_str(), name.c_str());
return -1;
}
}
return VT_HexVariable | flags | i;
}
if(name.compare(0, 9, "Adjacent.") == 0) {
//Constant hex variables
std::string varname = name.substr(9, name.size() - 9);
if(varname == "Active")
return VT_ConstantVariable | CV_AdjacentActive;
if(varname == "This")
return VT_ConstantVariable | CV_AdjacentThis;
//Tags
if(varname.compare(0, 4, "Tag.")) {
int i = getSysTagIndex(varname.substr(4, varname.size()-4));
if(i < 0) {
if(!formulaQuiet)
error("Error: Formula: Invalid tag '%s'.", name.c_str());
return -1;
}
return VT_AdjacentTag | i;
}
//Subsystems
const SubsystemDef* def = getSubsystemDef(varname);
if(def != nullptr)
return VT_AdjacentSubsystem | def->index;
}
//Ship variables
if(name.compare(0, 5, "Ship.") == 0) {
//Constant hex variables
std::string varname = name.substr(5, name.size() - 5);
//Ship formula variables
int i = getShipVariableIndex(varname);
if(i < 0) {
if(!formulaQuiet)
error("Error: Formula: Invalid variable '%s'.", name.c_str());
return -1;
}
return VT_ShipVariable | flags | i;
}
//Sum variables
if(name.compare(0, 4, "Sum.") == 0) {
//Constant hex variables
std::string varname = name.substr(4, name.size() - 4);
//Ship formula variables
int i = getVariableIndex(varname);
if(i < 0) {
if(!formulaQuiet)
error("Error: Formula: Invalid variable for sum '%s'.", name.c_str());
return -1;
}
return VT_SumVariable | flags | i;
}
//Hex sum variables
if(name.compare(0, 7, "HexSum.") == 0) {
//Constant hex variables
std::string varname = name.substr(7, name.size() - 7);
//Hex formula variables
int i = getHexVariableIndex(varname);
if(i < 0) {
if(!formulaQuiet)
error("Error: Formula: Invalid variable '%s'.", name.c_str());
return -1;
}
return VT_HexSumVariable | flags | i;
}
//Tag count variable
if(name.compare(0, 9, "TagCount.") == 0) {
std::string varname = name.substr(9, name.size() - 9);
int i = getSysTagIndex(varname);
if(i < 0) {
if(!formulaQuiet)
error("Error: Formula: Invalid tag '%s'.", name.c_str());
return -1;
}
return VT_TagCountVariable | i;
}
//Module metavariables
if(formulaSubsystem) {
for(unsigned i = 0, cnt = (unsigned)formulaSubsystem->modules.size(); i < cnt; ++i) {
SubsystemDef::ModuleDesc& mod = *formulaSubsystem->modules[i];
if(name.size() <= mod.id.size() + 1)
continue;
if(name.compare(0, mod.id.size(), mod.id)) {
size_t pos = mod.id.size();
if(name[pos] == '.') {
if(name.compare(pos + 1, name.size() - pos - 1, "Count")) {
return VT_ModuleCount | i;
}
else if(name.compare(pos + 1, name.size() - pos - 1, "Exists")) {
return VT_ModuleExists | i;
}
}
}
}
}
}
//Subsystem variables
int i = getVariableIndex(name);
if(i < 0) {
if(!formulaQuiet)
error("Error: Formula: Invalid variable '%s'.\n", name.c_str());
return -1;
}
if(formulaSubsystem) {
if(i >= (int)formulaSubsystem->variableIndices.size() ||
formulaSubsystem->variableIndices[i] < 0) {
if(!formulaQuiet)
error("Error: Formula: Subsystem '%s' does not have variable '%s'.",
formulaSubsystem->id.c_str(), name.c_str());
return -1;
}
}
return VT_SubsystemVariable | flags | i;
}
Formula* parseFormula(const std::string& str, const SubsystemDef* def, const SubsystemDef::ModifyStage* modifier) {
formulaSubsystem = def;
formulaModifier = modifier;
Formula* f = Formula::fromInfix(str.c_str(), formulaVarIndex);
formulaModifier = 0;
formulaSubsystem = 0;
return f;
}
int SV_Size = -1, HV_Resistance = -1, HV_HP = -1, ShV_HexSize = -1;
struct TemplateBlock {
std::vector<std::pair<int,std::string>> conditions;
std::vector<std::string> lines;
};
bool conditionMatches(const SubsystemDef* cur, std::vector<std::pair<int,std::string>>& conditions) {
bool passesAll = true;
foreach(c, conditions) {
int type = c->first;
bool negation = false;
if(type & TC_NOT) {
type &= ~TC_NOT;
negation = true;
}
bool pass = true;
switch(type) {
case TC_Tag:
if(!cur->hasTag(c->second))
pass = false;
break;
case TC_Modifier:
if(cur->modifierIds.find(c->second) == cur->modifierIds.end())
pass = false;
break;
case TC_Variable: {
int globalIndex = getVariableIndex(c->second);
if(globalIndex < 0) {
pass = false;
}
else {
if((unsigned)globalIndex >= cur->variableIndices.size()) {
pass = false;
}
else if(cur->variableIndices[globalIndex] < 0) {
pass = false;
}
}
} break;
case TC_HexVariable: {
int globalIndex = getHexVariableIndex(c->second);
if(globalIndex < 0) {
pass = false;
}
else {
if((unsigned)globalIndex >= cur->hexVariableIndices.size()) {
pass = false;
}
else if(cur->hexVariableIndices[globalIndex] < 0) {
pass = false;
}
}
} break;
case TC_ShipVariable: {
int globalIndex = getShipVariableIndex(c->second);
if(globalIndex < 0) {
pass = false;
}
else {
if((unsigned)globalIndex >= cur->shipVariableIndices.size()) {
pass = false;
}
else if(cur->shipVariableIndices[globalIndex] < 0) {
pass = false;
}
}
} break;
case TC_Subsystem: {
pass = cur->id == c->second;
} break;
}
if(!negation) {
if(!pass)
passesAll = false;
}
else {
if(pass)
passesAll = false;
}
}
return passesAll;
}
void parseConditions(const std::string& value, std::vector<std::pair<int,std::string>>& conditions) {
std::vector<std::string> conds;
split(value, conds, ',', true);
foreach(it, conds) {
std::vector<std::string> parts;
split(*it, parts, '/', true);
std::string value;
int cond = 0;
if(parts[0][0] == '!') {
parts[0] = parts[0].substr(1);
cond = TC_NOT;
}
if(parts.size() == 2) {
value = parts[1];
if(parts[0] == "tag") {
cond |= TC_Tag;
}
else if(parts[0] == "mod") {
cond |= TC_Modifier;
}
else if(parts[0] == "var") {
cond |= TC_Variable;
}
else if(parts[0] == "hexVar") {
cond |= TC_HexVariable;
}
else if(parts[0] == "shipVar") {
cond |= TC_ShipVariable;
}
else {
error(" Invalid condition: %s", it->c_str());
continue;
}
}
else {
cond |= TC_Subsystem;
value = parts[0];
}
conditions.push_back(
std::pair<int,std::string>(
cond, value));
}
}
void parseShipModifier(SubsystemDef::ShipModifier& mod, const std::string& value) {
std::string conds;
std::string func;
//Separate conditions
auto sep = value.find("::");
if(sep != std::string::npos) {
conds = value.substr(0,sep);
func = value.substr(sep+2);
}
else {
func = value;
}
parseConditions(conds, mod.conditions);
//Check if we have any arguments
std::vector<std::string> args;
std::string name;
if(funcSplit(func, name, args)) {
mod.modifyName = name;
for(size_t i = 0, cnt = args.size(); i < cnt && i < MODIFY_STAGE_MAXARGS; ++i)
mod.str_arguments.push_back(args[i]);
}
else {
mod.modifyName = func;
}
}
DataHandler* sysHandler = 0;
static SubsystemDef* def = 0;
static SubsystemDef::ModuleDesc* mod = 0;
static SubsystemDef::ModifyStage* stage = 0;
static SubsystemDef::Effect* eff = 0;
static SubsystemDef::Effector* efftr = 0;
static SubsystemDef::Assert* ass = 0;
static std::vector<TemplateBlock*> templates;
static TemplateBlock* temp = 0;
static bool inTemplate = false;
static int modStage = 1;
static int overrideHexArcLimitTag = -1;
void clearSubsystemDefinitions() {
foreach(it, subsystems)
delete *it;
subsystems.clear();
subsystemIndices.clear();
variableIndices.clear();
numericTags.clear();
foreach(it, templates)
delete *it;
templates.clear();
def = 0;
mod = 0;
stage = 0;
eff = 0;
efftr = 0;
ass = 0;
temp = 0;
inTemplate = false;
modStage = 1;
ModulesByID.clear();
ModifiersByID.clear();
}
void loadSubsystemDefinitions(const std::string& filename) {
modStage = 1;
def = 0;
mod = 0;
stage = 0;
eff = 0;
efftr = 0;
ass = 0;
temp = 0;
inTemplate = false;
overrideHexArcLimitTag = getSysTagIndex("OverrideHexArcLimit", true);
if(sysHandler != 0) {
sysHandler->read(filename);
SV_Size = getVariableIndex("Size");
HV_Resistance = getHexVariableIndex("Resistance");
HV_HP = getHexVariableIndex("HP");
ShV_HexSize = getShipVariableIndex("HexSize");
return;
}
sysHandler = new DataHandler();
DataHandler& handler = *sysHandler;
handler.defaultHandler([&](std::string& key, std::string& value) {
error(handler.position());
error(" Invalid line format.");
});
{
auto& templateBlock = handler.block("Template");
templateBlock.openBlock([&](std::string& value) -> bool {
temp = new TemplateBlock();
templates.push_back(temp);
parseConditions(value, temp->conditions);
return true;
});
templateBlock.lineHandler([&](std::string& line) {
temp->lines.push_back(line);
});
}
{
auto& subsysBlock = handler.block("Subsystem");
subsysBlock.openBlock([&](std::string& id) -> bool {
def = new SubsystemDef();
def->index = (int)subsystems.size();
def->id = id;
def->variableIndices.resize(variableIndices.size());
modStage = 1;
for(unsigned i = 0; i < def->variableIndices.size(); ++i)
def->variableIndices[i] = -1;
subsystems.push_back(def);
subsystemIndices[id] = def->index;
return true;
});
subsysBlock.closeBlock([&]() {
def = 0;
});
auto getVariable = [&](std::string& name) -> SubsystemDef::Variable& {
bool dependent = false;
if(name.compare(0, 4, "out ") == 0) {
name = name.substr(4);
dependent = true;
}
//Check if it is a hex variable
if(name.compare(0, 4, "Hex.") == 0) {
//Remove hex part
name = name.substr(4, name.size() - 4);
//Find global index
int index;
auto it = hexVariableIndices.find(name);
if(it == hexVariableIndices.end()) {
index = (int)hexVariableIndices.size();
hexVariableIndices[name] = index;
}
else {
index = it->second;
}
//Check if we have the variable
if((int)def->hexVariableIndices.size() <= index) {
int prevLen = (int)def->hexVariableIndices.size();
def->hexVariableIndices.resize(index + 1);
for(int i = prevLen; i <= index; ++i)
def->hexVariableIndices[i] = -1;
}
else {
if(def->hexVariableIndices[index] != -1) {
return def->hexVariables[def->hexVariableIndices[index]];
}
}
def->hexVariableIndices[index] = (int)def->hexVariables.size();
SubsystemDef::Variable var;
var.name = name;
var.index = index;
var.formula = 0;
var.type = SVT_HexVariable;
var.dependent = dependent;
def->hexVariables.push_back(var);
return def->hexVariables.back();
}
//Check if it is a ship variable
else if(name.compare(0, 5, "Ship.") == 0) {
//Remove hex part
name = name.substr(5, name.size() - 5);
//Find global index
int index;
auto it = shipVariableIndices.find(name);
if(it == shipVariableIndices.end()) {
index = (int)shipVariableIndices.size();
shipVariableIndices[name] = index;
}
else {
index = it->second;
}
//Check if we have the variable
if((int)def->shipVariableIndices.size() <= index) {
int prevLen = def->shipVariableIndices.size();
def->shipVariableIndices.resize(index + 1);
for(int i = prevLen; i <= index; ++i)
def->shipVariableIndices[i] = -1;
}
else {
if(def->shipVariableIndices[index] != -1) {
return def->shipVariables[def->shipVariableIndices[index]];
}
}
def->shipVariableIndices[index] = def->shipVariables.size();
SubsystemDef::Variable var;
var.name = name;
var.index = index;
var.formula = 0;
var.type = SVT_ShipVariable;
var.dependent = dependent;
def->shipVariables.push_back(var);
return def->shipVariables.back();
}
else {
//Find global index
int index;
auto it = variableIndices.find(name);
if(it == variableIndices.end()) {
index = variableIndices.size();
variableIndices[name] = index;
}
else {
index = it->second;
}
//Check if we have the variable
if((int)def->variableIndices.size() <= index) {
int prevLen = def->variableIndices.size();
def->variableIndices.resize(index + 1);
for(int i = prevLen; i <= index; ++i)
def->variableIndices[i] = -1;
}
else {
if(def->variableIndices[index] != -1) {
return def->variables[def->variableIndices[index]];
}
}
def->variableIndices[index] = def->variables.size();
SubsystemDef::Variable var;
var.name = name;
var.index = index;
var.formula = 0;
var.type = SVT_SubsystemVariable;
var.dependent = dependent;
def->variables.push_back(var);
return def->variables.back();
}
};
subsysBlock("Name", [&](std::string& value) {
def->name = devices.locale.localize(value);
});
subsysBlock("BaseColor", [&](std::string& value) {
def->baseColor = toColor(value);
});
subsysBlock("TypeColor", [&](std::string& value) {
def->typeColor = toColor(value);
});
subsysBlock("Elevation", [&](std::string& value) {
def->elevation = toNumber<int>(value);
});
subsysBlock("Description", [&](std::string& value) {
def->description = devices.locale.localize(value);
});
subsysBlock("Picture", [&](std::string& value) {
def->picMat = value;
});
auto addTags = [&](std::vector<std::string>& tags) {
foreach(t, tags) {
std::string tag, value;
auto pos = t->find(':');
if(pos != std::string::npos) {
tag = t->substr(0, pos);
if(pos < t->size()-1)
value = t->substr(pos+1);
}
else {
tag = *t;
}
def->tags.insert(tag);
auto it = numericTags.find(tag);
int index = -1;
if(it != numericTags.end()) {
index = it->second;
}
else {
index = numericTags.size();
numericTags[tag] = index;
}
def->numTags.insert(index);
if(!value.empty())
def->tagValues[index].push_back(value);
}
};
subsysBlock("Tags", [&](std::string& value) {
std::vector<std::string> tags;
split(value, tags, ',', true);
addTags(tags);
def->isContiguous = def->tags.find("NonContiguous") == def->tags.end();
def->hasCore = def->tags.find("NoCore") == def->tags.end();
def->alwaysTakeDamage = def->tags.find("AlwaysTakeDamage") != def->tags.end();
def->hexLimitArc = def->tags.find("HexLimitArc") != def->tags.end();
def->isHull = def->tags.find("HullSystem") != def->tags.end();
def->isApplied = def->tags.find("Applied") != def->tags.end();
def->exteriorCore = def->tags.find("ExteriorCore") != def->tags.end();
def->defaultUnlock = def->tags.find("DefaultUnlock") != def->tags.end();
def->passExterior = def->tags.find("PassExterior") != def->tags.end();
def->fauxExterior = def->tags.find("FauxExterior") != def->tags.end();
});
subsysBlock("Hull", [&](std::string& value) {
std::vector<std::string> tags;
split(value, tags, ',', true);
foreach(t, tags) {
def->hullTags.push_back(*t);
*t += "Hull";
}
addTags(tags);
});
subsysBlock("EvaluationOrder", [&](std::string& value) {
def->ordering = toNumber<int>(value);
});
subsysBlock("DamageOrder", [&](std::string& value) {
def->damageOrder = toNumber<int>(value);
});
subsysBlock("OnCheckErrors", [&](std::string& value) {
def->def_onCheckErrors = value;
});
subsysBlock("State", [&](std::string& value) {
std::vector<std::string> args;
split(value, args, '=', true);
if(args.size() != 2) {
error(handler.position());
error(" Invalid state format.");
return;
}
SubsystemDef::StateDesc desc;
toLowercase(args[0]);
if(args[0] == "int")
desc.type = BT_Int;
else if(args[0] == "double")
desc.type = BT_Double;
else if(args[0] == "bool")
desc.type = BT_Bool;
desc.formula = 0;
desc.str_formula = args[1];
def->states.push_back(desc);
});
subsysBlock("Hook", [&](std::string& value) {
def->hooks.push_back(value);
});
subsysBlock("AddShipModifier", [&](std::string& value) {
SubsystemDef::ShipModifier mod;
parseShipModifier(mod, value);
def->shipModifiers.push_back(mod);
});
subsysBlock("AddAdjacentModifier", [&](std::string& value) {
SubsystemDef::ShipModifier mod;
parseShipModifier(mod, value);
def->adjacentModifiers.push_back(mod);
});
subsysBlock("AddPostModifier", [&](std::string& value) {
SubsystemDef::ShipModifier mod;
parseShipModifier(mod, value);
def->postModifiers.push_back(mod);
});
subsysBlock.defaultHandler([&](std::string& key, std::string& value) {
if(!value.empty() && value[0] == '=') {
SubsystemDef::Variable& var = getVariable(key);
value = value.substr(1, value.size() - 1);
var.formula = 0;
var.str_formula = value;
}
else {
error(handler.position());
error(" Invalid line format.");
}
});
{
auto& defaultsBlock = subsysBlock.block("Defaults");
defaultsBlock.defaultHandler([&](std::string& key, std::string& value) {
if(!value.empty() && value[0] == '=') {
SubsystemDef::Variable& var = getVariable(key);
if(var.formula == 0 && var.str_formula.empty()) {
value = value.substr(1, value.size() - 1);
var.formula = 0;
var.str_formula = value;
}
}
else {
error(handler.position());
error(" Invalid line format.");
}
});
}
{
auto& modifyBlock = subsysBlock.block("Modifier");
modifyBlock.openBlock([&](std::string& value) -> bool {
std::vector<std::string> args;
std::string name;
//Check if we have any arguments
if(!funcSplit(value, name, args)) {
name = value;
args.clear();
}
//Check for duplicates
auto it = def->modifierIds.find(name);
if(it != def->modifierIds.end()) {
//Ignored silently in templates so subsystems can
//override modifiers from templates
if(!inTemplate) {
error(handler.position());
error(" Duplicate modifier '%s'.", name.c_str());
}
stage = 0;
return true;
}
//Make the stage
stage = new SubsystemDef::ModifyStage();
stage->umodifid = ModifiersByID.size();
stage->index = def->modifiers.size();
for(unsigned i = 0, cnt = args.size(); i < cnt; ++i)
stage->argumentNames[args[i]] = i;
def->modifiers.push_back(stage);
def->modifierIds[name] = stage;
stage->stage = ++modStage;
ModifiersByID.push_back(stage);
return true;
});
modifyBlock("Stage", [&](std::string& value) {
if(!stage)
return;
stage->stage = toNumber<short>(value) << 16 | modStage;
});
modifyBlock.closeBlock([&]() {
stage = 0;
});
modifyBlock.defaultHandler([&](std::string& key, std::string& value) {
if(!stage)
return;
if(!value.empty() && value[0] == '=') {
SubsystemDef::Variable& var = getVariable(key);
value = value.substr(1, value.size() - 1);
switch(var.type) {
case SVT_SubsystemVariable: {
auto it = stage->variables.find(var.index);
if(it != stage->variables.end()) {
error(handler.position());
error(" Duplicate variable modifier.");
return;
}
stage->str_variables.push_back(std::pair<int,std::string>(var.index, value));
} break;
case SVT_HexVariable: {
auto it = stage->hexVariables.find(var.index);
if(it != stage->hexVariables.end()) {
error(handler.position());
error(" Duplicate variable modifier.");
return;
}
stage->str_hexVariables.push_back(std::pair<int,std::string>(var.index, value));
} break;
case SVT_ShipVariable: {
auto it = stage->shipVariables.find(var.index);
if(it != stage->shipVariables.end()) {
error(handler.position());
error(" Duplicate variable modifier.");
return;
}
stage->str_shipVariables.push_back(std::pair<int,std::string>(var.index, value));
} break;
}
}
else {
error(handler.position());
error(" Invalid line format.");
}
});
}
{
auto& moduleBlock = subsysBlock.block("Module");
moduleBlock.openBlock([&](std::string& value) -> bool {
auto it = def->moduleIndices.find(value);
if(it != def->moduleIndices.end()) {
if(inTemplate) {
mod = def->modules[it->second];
return true;
}
error(handler.position());
error(" Duplicate module.");
return false;
}
mod = new SubsystemDef::ModuleDesc();
def->modules.push_back(mod);
mod->index = def->modules.size() - 1;
mod->id = value;
mod->umodident = def->id + "::" + mod->id;
mod->umodid = ModulesByID.size();
ModulesByID.push_back(mod);
if(value == "Default") {
def->defaultModule = mod;
mod->defaultUnlock = true;
}
else if(value == "Core") {
def->coreModule = mod;
mod->defaultUnlock = true;
mod->required = true;
mod->vital = true;
mod->unique = true;
}
def->moduleIndices[value] = mod->index;
return true;
});
moduleBlock.closeBlock([&]() {
mod = 0;
stage = 0;
});
moduleBlock("Name", [&](std::string& value) {
mod->name = devices.locale.localize(value);
});
moduleBlock("Description", [&](std::string& value) {
mod->description = devices.locale.localize(value);
});
moduleBlock("Color", [&](std::string& value) {
mod->color = toColor(value);
});
moduleBlock("Required", [&](std::string& value) {
mod->required = toBool(value);
});
moduleBlock("Unique", [&](std::string& value) {
mod->unique = toBool(value);
});
moduleBlock("Vital", [&](std::string& value) {
mod->vital = toBool(value);
});
moduleBlock("DefaultUnlock", [&](std::string& value) {
mod->defaultUnlock = toBool(value);
});
moduleBlock("Sprite", [&](std::string& value) {
mod->spriteMat = value;
});
moduleBlock("DrawMode", [&](std::string& value) {
mod->drawMode = toNumber<int>(value);
});
moduleBlock("Hook", [&](std::string& value) {
mod->hooks.push_back(value);
});
moduleBlock("OnEnable", [&](std::string& value) {
mod->def_onEnable = value;
});
moduleBlock("OnDisable", [&](std::string& value) {
mod->def_onDisable = value;
});
moduleBlock("AddModifier", [&](std::string& value) {
mod->str_appliedStages.push_back(value);
});
moduleBlock("AddUniqueModifier", [&](std::string& value) {
mod->str_uniqueAppliedStages.push_back(value);
});
moduleBlock("AddHexModifier", [&](std::string& value) {
mod->str_hexAppliedStages.push_back(value);
});
moduleBlock("OnCheckErrors", [&](std::string& value) {
mod->def_onCheckErrors = value;
});
moduleBlock("Tags", [&](std::string& value) {
std::vector<std::string> tags;
split(value, tags, ',', true);
foreach(t, tags) {
std::string tag, value;
auto pos = t->find(':');
if(pos != std::string::npos) {
tag = t->substr(0, pos);
if(pos < t->size()-1)
value = t->substr(pos+1);
}
else {
tag = *t;
}
mod->tags.insert(tag);
auto it = numericTags.find(tag);
int index = -1;
if(it != numericTags.end()) {
index = it->second;
}
else {
index = numericTags.size();
numericTags[tag] = index;
}
mod->numTags.insert(index);
if(!value.empty())
mod->tagValues[index].push_back(value);
}
});
moduleBlock("AddAdjacentModifier", [&](std::string& value) {
SubsystemDef::ShipModifier m;
parseShipModifier(m, value);
mod->adjacentModifiers.push_back(m);
});
auto addModification = [&](std::string& key, std::string& value) {
SubsystemDef::Variable& var = getVariable(key);
//Create a default stage at 0
if(!stage) {
mod->modifiers.push_back(SubsystemDef::ModifyStage());
stage = &mod->modifiers.back();
stage->stage = ++modStage;
}
switch(var.type) {
case SVT_SubsystemVariable: {
auto it = stage->variables.find(var.index);
if(it != stage->variables.end()) {
error(handler.position());
error(" Duplicate variable modifier.");
return;
}
stage->str_variables.push_back(std::pair<int,std::string>(var.index, value));
} break;
case SVT_HexVariable: {
auto it = stage->hexVariables.find(var.index);
if(it != stage->hexVariables.end()) {
error(handler.position());
error(" Duplicate variable modifier.");
return;
}
stage->str_hexVariables.push_back(std::pair<int,std::string>(var.index, value));
} break;
case SVT_ShipVariable: {
auto it = stage->shipVariables.find(var.index);
if(it != stage->shipVariables.end()) {
error(handler.position());
error(" Duplicate variable modifier.");
return;
}
stage->str_shipVariables.push_back(std::pair<int,std::string>(var.index, value));
} break;
}
};
moduleBlock.defaultHandler([&](std::string& key, std::string& value) {
if(!value.empty() && value[0] == '=') {
value = value.substr(1, value.size()-1);
addModification(key, value);
}
else {
error(handler.position());
error(" Invalid line format.");
}
});
{
auto& assertBlock = moduleBlock.block("Assert");
assertBlock.openBlock([&](std::string& value) -> bool {
mod->asserts.push_back(SubsystemDef::Assert());
ass = &mod->asserts.back();
ass->fatal = true;
ass->unique = false;
ass->formula = 0;
ass->str_formula = value;
ass->message = "Assertion Failed";
return true;
});
assertBlock("Unique", [&](std::string& value) {
ass->unique = toBool(value);
});
assertBlock("Fatal", [&](std::string& value) {
ass->fatal = toBool(value);
});
assertBlock("Message", [&](std::string& value) {
ass->message = devices.locale.localize(value);
});
assertBlock.closeBlock([&]() {
ass = 0;
});
}
{
auto& requiresBlock = moduleBlock.block("Requires");
requiresBlock.openBlock([&](std::string& value) -> bool {
return true;
});
requiresBlock.lineHandler([&](std::string& line) {
auto pos = line.find("=");
if(pos == line.npos) {
error(handler.position());
error(" Invalid line format.");
return;
}
std::vector<std::string> args;
split(line, args, '=', true);
if(args.size() != 2) {
error(handler.position());
error(" Invalid line format.");
return;
}
//Add the variable modification
std::string vname = "Ship.REQUIRES_" + args[0];
std::string vformula = format("Ship.REQUIRES_$1 + $2", args[0], args[1]);
addModification(vname, vformula);
//Add the assert block
SubsystemDef::Assert ass;
ass.fatal = true;
ass.unique = true;
ass.formula = 0;
ass.str_formula = format("Ship.$1 >= Ship.REQUIRES_$1", args[0]);
ass.message = devices.locale.localize(format("ERROR_INSUFFICIENT_$1", args[0]));
mod->asserts.push_back(ass);
});
}
{
auto& providesBlock = moduleBlock.block("Provides");
providesBlock.openBlock([&](std::string& value) -> bool {
return true;
});
providesBlock.lineHandler([&](std::string& line) {
auto pos = line.find("=");
if(pos == line.npos) {
error(handler.position());
error(" Invalid line format.");
return;
}
std::vector<std::string> args;
split(line, args, '=', true);
if(args.size() != 2) {
error(handler.position());
error(" Invalid line format.");
return;
}
//Add the variable modification
std::string vname = "Ship." + args[0];
std::string vformula = format("Ship.$1 + $2", args[0], args[1]);
addModification(vname, vformula);
});
}
{
auto& modifyBlock = moduleBlock.block("Modify");
modifyBlock.openBlock([&](std::string& value) -> bool {
short number = 0;
bool unique = false;
if(streq_nocase(value, "unique")) {
unique = true;
}
else {
if(streq_nocase(value, "unique ", 0, 7)) {
unique = true;
value = value.substr(7, value.size() - 7);
}
if(streq_nocase(value, "stage ", 0, 6)) {
number = toNumber<short>(value.substr(6, value.size() - 6));
}
else if(!value.empty()) {
error(handler.position());
error(" Invalid stage specification, assuming stage 0.");
}
}
if(unique) {
mod->uniqueModifiers.push_back(SubsystemDef::ModifyStage());
stage = &mod->uniqueModifiers.back();
}
else {
mod->modifiers.push_back(SubsystemDef::ModifyStage());
stage = &mod->modifiers.back();
}
stage->stage = number << 16 | ++modStage;
return true;
});
modifyBlock.closeBlock([&]() {
stage = 0;
});
modifyBlock.defaultHandler([&](std::string& key, std::string& value) {
if(!value.empty() && value[0] == '=') {
SubsystemDef::Variable& var = getVariable(key);
value = value.substr(1, value.size() - 1);
switch(var.type) {
case SVT_SubsystemVariable: {
auto it = stage->variables.find(var.index);
if(it != stage->variables.end()) {
error(handler.position());
error(" Duplicate variable modifier.");
return;
}
stage->str_variables.push_back(std::pair<int,std::string>(var.index, value));
} break;
case SVT_HexVariable: {
auto it = stage->hexVariables.find(var.index);
if(it != stage->hexVariables.end()) {
error(handler.position());
error(" Duplicate variable modifier.");
return;
}
stage->str_hexVariables.push_back(std::pair<int,std::string>(var.index, value));
} break;
case SVT_ShipVariable: {
auto it = stage->shipVariables.find(var.index);
if(it != stage->shipVariables.end()) {
error(handler.position());
error(" Duplicate variable modifier.");
return;
}
stage->str_shipVariables.push_back(std::pair<int,std::string>(var.index, value));
} break;
}
}
else {
error(handler.position());
error(" Invalid line format.");
}
});
}
{
auto& effectBlock = moduleBlock.block("Effect");
effectBlock.openBlock([&](std::string& value) -> bool {
const EffectDef* type = getEffectDefinition(value);
if(!type) {
eff = 0;
error(handler.position());
error(" Unknown effect %s.", value.c_str());
return false;
}
mod->effects.push_back(SubsystemDef::Effect());
eff = &mod->effects[mod->effects.size() - 1];
eff->type = type;
eff->values.resize(type->valueCount);
eff->str_values.resize(type->valueCount);
for(unsigned i = 0; i < type->valueCount; ++i)
eff->values[i] = 0;
return true;
});
effectBlock.closeBlock([&]() {
eff = 0;
});
effectBlock.lineHandler([&](std::string& line) {
auto pos = line.find("=");
if(pos == line.npos) {
error(handler.position());
error(" Invalid line format.");
return;
}
std::vector<std::string> args;
split(line, args, '=', true);
if(args.size() != 2) {
error(handler.position());
error(" Invalid line format.");
return;
}
auto it = eff->type->valueNames.find(args[0]);
if(it == eff->type->valueNames.end()) {
error(handler.position());
error(" Unknown effect value '%s'.", args[0].c_str());
return;
}
eff->str_values[it->second] = args[1];
});
}
}
{
auto& effectBlock = subsysBlock.block("Effect");
effectBlock.openBlock([&](std::string& value) -> bool {
const EffectDef* type = getEffectDefinition(value);
if(!type) {
eff = 0;
error(handler.position());
error(" Unknown effect %s.", value.c_str());
return false;
}
def->effects.push_back(SubsystemDef::Effect());
eff = &def->effects[def->effects.size() - 1];
eff->type = type;
eff->values.resize(type->valueCount);
eff->str_values.resize(type->valueCount);
for(unsigned i = 0; i < type->valueCount; ++i)
eff->values[i] = 0;
return true;
});
effectBlock.closeBlock([&]() {
eff = 0;
});
effectBlock.lineHandler([&](std::string& line) {
auto pos = line.find("=");
if(pos == line.npos) {
error(handler.position());
error(" Invalid line format.");
return;
}
std::vector<std::string> args;
split(line, args, '=', true);
if(args.size() != 2) {
error(handler.position());
error(" Invalid line format.");
return;
}
auto it = eff->type->valueNames.find(args[0]);
if(it == eff->type->valueNames.end()) {
error(handler.position());
error(" Unknown effect value '%s'.", args[0].c_str());
return;
}
eff->str_values[it->second] = args[1];
});
}
{
auto& effectorBlock = subsysBlock.block("Effector");
effectorBlock.openBlock([&](std::string& value) -> bool {
const EffectorDef* type = getEffectorDefinition(value);
if(!type) {
efftr = 0;
error(handler.position());
error(" Unknown effector %s.", value.c_str());
return false;
}
def->effectors.push_back(SubsystemDef::Effector());
efftr = &def->effectors[def->effectors.size() - 1];
efftr->type = type;
efftr->enabled = true;
efftr->values.resize(type->valueCount);
efftr->str_values.resize(type->valueCount);
efftr->skinIndex = 0;
for(unsigned i = 0; i < type->valueCount; ++i)
efftr->values[i] = 0;
return true;
});
effectorBlock("Disabled", [&](std::string& value) {
efftr->enabled = !toBool(value);
});
effectorBlock("Skin", [&](std::string& value) {
auto s = efftr->type->skinNames.find(value);
if(s != efftr->type->skinNames.end()) {
efftr->skinIndex = s->second;
efftr->skinName = value;
}
else
error("Effector '%s' has no skin '%s'", efftr->type->name.c_str(), value.c_str());
});
effectorBlock.closeBlock([&]() {
efftr = 0;
});
effectorBlock.lineHandler([&](std::string& line) {
auto pos = line.find("=");
if(pos == line.npos) {
error(handler.position());
error(" Invalid line format.");
return;
}
std::vector<std::string> args;
split(line, args, '=', true);
if(args.size() != 2) {
error(handler.position());
error(" Invalid line format.");
return;
}
auto it = efftr->type->valueNames.find(args[0]);
if(it == efftr->type->valueNames.end()) {
error(handler.position());
error(" Unknown effector value '%s'.\n", args[0].c_str());
return;
}
efftr->str_values[it->second] = args[1];
});
}
{
auto& assertBlock = subsysBlock.block("Assert");
assertBlock.openBlock([&](std::string& value) -> bool {
def->asserts.push_back(SubsystemDef::Assert());
ass = &def->asserts.back();
ass->fatal = true;
ass->unique = false;
ass->formula = 0;
ass->str_formula = value;
ass->message = "Assertion Failed";
return true;
});
assertBlock("Unique", [&](std::string& value) {
ass->unique = toBool(value);
});
assertBlock("Fatal", [&](std::string& value) {
ass->fatal = toBool(value);
});
assertBlock("Message", [&](std::string& value) {
ass->message = devices.locale.localize(value);
});
assertBlock.closeBlock([&]() {
ass = 0;
});
}
{
auto& requiresBlock = subsysBlock.block("Requires");
requiresBlock.openBlock([&](std::string& value) -> bool {
return true;
});
requiresBlock.lineHandler([&](std::string& line) {
auto pos = line.find("=");
if(pos == line.npos) {
error(handler.position());
error(" Invalid line format.");
return;
}
std::vector<std::string> args;
split(line, args, '=', true);
if(args.size() != 2) {
error(handler.position());
error(" Invalid line format.");
return;
}
//Add the variable modification
std::string vname = "Ship.REQUIRES_" + args[0];
auto& var = getVariable(vname);
var.str_formula = format("Ship.REQUIRES_$1 + $2",
args[0], args[1]);
//Add the assert block
SubsystemDef::Assert ass;
ass.fatal = true;
ass.unique = true;
ass.formula = 0;
ass.str_formula = format("Ship.$1 >= Ship.REQUIRES_$1", args[0]);
ass.message = devices.locale.localize(format("ERROR_INSUFFICIENT_$1", args[0]));
def->asserts.push_back(ass);
});
}
{
auto& providesBlock = subsysBlock.block("Provides");
providesBlock.openBlock([&](std::string& value) -> bool {
return true;
});
providesBlock.lineHandler([&](std::string& line) {
auto pos = line.find("=");
if(pos == line.npos) {
error(handler.position());
error(" Invalid line format.");
return;
}
std::vector<std::string> args;
split(line, args, '=', true);
if(args.size() != 2) {
error(handler.position());
error(" Invalid line format.");
return;
}
//Add the variable modification
std::string vname = "Ship." + args[0];
auto& var = getVariable(vname);
var.str_formula = format("Ship.$1 + $2", args[0], args[1]);
});
}
}
handler.read(filename);
SV_Size = getVariableIndex("Size");
HV_Resistance = getHexVariableIndex("Resistance");
HV_HP = getHexVariableIndex("HP");
ShV_HexSize = getShipVariableIndex("HexSize");
}
void bindSubsystemHooks() {
foreach(it, subsystems) {
SubsystemDef* def = (*it);
if(!def->def_onCheckErrors.empty())
def->scr_onCheckErrors = devices.scripts.server->getFunction(def->def_onCheckErrors,
"(Design& design, Subsystem& sys)", "bool");
foreach(it, def->modules) {
auto* m = *it;
if(!m->def_onCheckErrors.empty())
m->scr_onCheckErrors = devices.scripts.server->getFunction(m->def_onCheckErrors,
"(Design& design, Subsystem& sys, const vec2u& hex)", "bool");
}
if(!devices.network->isClient) {
foreach(it, def->modules) {
auto* m = *it;
if(!m->def_onEnable.empty()) {
m->scr_onEnable = devices.scripts.server->getFunction(m->def_onEnable,
"(Event& evt, const vec2u& position)", "void");
}
else {
m->scr_onEnable = 0;
}
if(!m->def_onDisable.empty()) {
m->scr_onDisable = devices.scripts.server->getFunction(m->def_onDisable,
"(Event& evt, const vec2u& position)", "void");
}
else {
m->scr_onDisable = 0;
}
}
}
}
}
void executeSubsystemTemplates() {
foreach(it, subsystems) {
SubsystemDef* cur = (*it);
//Add templated code
foreach(t, templates) {
if(conditionMatches(cur, (*t)->conditions)) {
modStage = 1;
def = cur;
mod = 0;
stage = 0;
eff = 0;
efftr = 0;
ass = 0;
temp = 0;
inTemplate = true;
sysHandler->enterBlock("Subsystem");
foreach(l, (*t)->lines)
sysHandler->feed(*l);
sysHandler->end();
}
}
}
}
void finalizeSubsystems() {
foreach(it, subsystems)
(*it)->finalize();
}
void bindSubsystemMaterials() {
foreach(it, subsystems) {
SubsystemDef* def = (*it);
foreach(m, def->modules)
(*m)->sprite = devices.library.getSprite((*m)->spriteMat);
if(!def->picMat.empty())
def->picture = devices.library.getSprite(def->picMat);
else if(def->coreModule)
def->picture = def->coreModule->sprite;
else if(def->defaultModule)
def->picture = def->defaultModule->sprite;
}
}
SubsystemDef::SubsystemDef() : index(-1), ordering(0), damageOrder(0), elevation(0),
defaultModule(0), coreModule(0), typeColor((unsigned)0x00000000),
scr_onCheckErrors(0),
hasCore(false), isContiguous(true), exteriorCore(false),
defaultUnlock(false), isHull(false), isApplied(false), hexLimitArc(false),
passExterior(false), fauxExterior(false) {
}
SubsystemDef::~SubsystemDef() {
foreach(it, variables) {
delete it->formula;
}
foreach(it, effects) {
foreach(f, it->values)
delete *f;
}
foreach(it, states)
delete it->formula;
}
void SubsystemDef::finalize() {
//Create default module
if(!defaultModule) {
SubsystemDef::ModuleDesc* mod = new SubsystemDef::ModuleDesc();
modules.push_back(mod);
mod->index = modules.size() - 1;
mod->id = "Default";
mod->umodident = id + "::" + mod->id;
mod->umodid = ModulesByID.size();
mod->defaultUnlock = true;
ModulesByID.push_back(mod);
defaultModule = mod;
moduleIndices["Default"] = mod->index;
}
//Create core module
if(hasCore && !coreModule) {
SubsystemDef::ModuleDesc* mod = new SubsystemDef::ModuleDesc();
modules.push_back(mod);
mod->index = modules.size() - 1;
mod->id = "Core";
mod->umodident = id + "::" + mod->id;
mod->umodid = ModulesByID.size();
mod->defaultUnlock = true;
ModulesByID.push_back(mod);
coreModule = mod;
moduleIndices["Core"] = mod->index;
}
auto loadFormula = [this](const std::string& str) -> Formula* {
try {
formulaQuiet = false;
return Formula::fromInfix(str.c_str(), formulaVarIndex, false);
}
catch(FormulaError err) {
error("Error in Subsystem '%s' formula '%s': %s", id.c_str(), str.c_str(), err.msg.c_str());
return Formula::fromInfix("0");
}
};
auto loadFormula_quiet = [this](const std::string& str) -> Formula* {
try {
formulaQuiet = true;
return Formula::fromInfix(str.c_str(), formulaVarIndex, false);
}
catch(FormulaError err) {
return nullptr;
}
};
auto addModifier = [this](const std::string& name, const std::string& argname, int prior) -> ModifyStage* {
//Check for duplicates
auto it = modifierIds.find(name);
if(it != modifierIds.end())
return nullptr;
//Make the stage
auto* stage = new ModifyStage();
stage->index = modifiers.size();
stage->umodifid = ModifiersByID.size();
stage->argumentNames[argname] = 0;
stage->stage = prior;
ModifiersByID.push_back(stage);
modifiers.push_back(stage);
modifierIds[name] = stage;
return stage;
};
auto addVariableModifiers = [this,addModifier](SubsystemDef::Variable& var) {
auto* stage = addModifier(var.name+"Factor", "factor", 30);
if(stage) {
switch(var.type) {
case SVT_SubsystemVariable:
stage->str_variables.push_back(std::pair<int,std::string>(var.index, var.name+" * factor"));
break;
case SVT_HexVariable:
stage->str_hexVariables.push_back(std::pair<int,std::string>(var.index, "Hex."+var.name+" * factor"));
break;
}
}
stage = addModifier("AddBase"+var.name+"Factor", "factor", -20);
if(stage) {
switch(var.type) {
case SVT_SubsystemVariable:
stage->str_variables.push_back(std::pair<int,std::string>(var.index, var.name+" + Base::"+var.name+" * factor"));
break;
case SVT_HexVariable:
stage->str_hexVariables.push_back(std::pair<int,std::string>(var.index, "Hex."+var.name+" + Base::Hex."+var.name+" * factor"));
break;
}
}
stage = addModifier("Add"+var.name, "amount", -10);
if(stage) {
switch(var.type) {
case SVT_SubsystemVariable:
stage->str_variables.push_back(std::pair<int,std::string>(var.index, var.name+" + amount"));
break;
case SVT_HexVariable:
stage->str_hexVariables.push_back(std::pair<int,std::string>(var.index, "Hex."+var.name+" + amount"));
break;
}
}
};
//Parse all formulas
formulaSubsystem = this;
foreach(it, variables) {
it->formula = loadFormula(it->str_formula);
addVariableModifiers(*it);
}
foreach(it, hexVariables) {
it->formula = loadFormula(it->str_formula);
addVariableModifiers(*it);
}
foreach(it, shipVariables)
it->formula = loadFormula(it->str_formula);
foreach(it, states)
it->formula = loadFormula(it->str_formula);
foreach(it, asserts)
it->formula = loadFormula(it->str_formula);
foreach(it, shipModifiers)
for(unsigned i = 0, cnt = it->str_arguments.size(); i < cnt; ++i)
it->stage.formulas[i] = loadFormula(it->str_arguments[i]);
foreach(it, postModifiers)
for(unsigned i = 0, cnt = it->str_arguments.size(); i < cnt; ++i)
it->stage.formulas[i] = loadFormula(it->str_arguments[i]);
foreach(it, adjacentModifiers)
for(unsigned i = 0, cnt = it->str_arguments.size(); i < cnt; ++i)
it->stage.formulas[i] = loadFormula(it->str_arguments[i]);
foreach(it, hooks)
for(unsigned i = 0, cnt = it->str_args.size(); i < cnt; ++i)
it->formulas[i] = loadFormula_quiet(it->str_args[i]);
foreach(it, effects)
for(unsigned i = 0, cnt = it->str_values.size(); i < cnt; ++i)
it->values[i] = loadFormula(it->str_values[i]);
foreach(it, effectors)
for(unsigned i = 0, cnt = it->str_values.size(); i < cnt; ++i)
it->values[i] = loadFormula(it->str_values[i]);
foreach(it, modules) {
foreach(m, (*it)->modifiers) {
foreach(v, m->str_variables)
m->variables[v->first] = loadFormula(v->second);
foreach(v, m->str_hexVariables)
m->hexVariables[v->first] = loadFormula(v->second);
foreach(v, m->str_shipVariables)
m->shipVariables[v->first] = loadFormula(v->second);
}
foreach(ass, (*it)->asserts)
ass->formula = loadFormula(ass->str_formula);
foreach(h, (*it)->hooks)
for(unsigned i = 0, cnt = h->str_args.size(); i < cnt; ++i)
h->formulas[i] = loadFormula_quiet(h->str_args[i]);
}
foreach(m, modifiers) {
formulaModifier = *m;
foreach(v, (*m)->str_variables)
(*m)->variables[v->first] = loadFormula(v->second);
foreach(v, (*m)->str_hexVariables)
(*m)->hexVariables[v->first] = loadFormula(v->second);
foreach(v, (*m)->str_shipVariables)
(*m)->shipVariables[v->first] = loadFormula(v->second);
}
formulaModifier = 0;
//Check that we have values for all effect variables
for(auto i = effects.begin(), end = effects.end(); i != end; ++i) {
for(unsigned n = 0; n < i->type->valueCount; ++n) {
auto& desc = i->type->values[n];
if(i->values[n] == 0 && !desc.defaultValue) {
error("Error: Subsystem '%s' effect '%s' is missing values", id.c_str(), i->type->name.c_str());
break;
}
}
}
//Check that we have values for all effector variables
for(auto i = effectors.begin(), end = effectors.end(); i != end; ++i) {
for(unsigned n = 0; n < i->type->valueCount; ++n) {
auto& desc = i->type->values[n];
if(i->values[n] == 0 && !desc.defaultValue) {
error("Error: Subsystem '%s' effector '%s' is missing values", id.c_str(), i->type->name.c_str());
break;
}
}
}
//Parse module applied stages
auto makeAppliedStage = [&](std::string& line, std::vector<SubsystemDef::AppliedStage>& list) {
std::string name;
std::vector<std::string> args;
bool isOptional = false;
if(line.compare(0, 9, "optional ") == 0) {
line = line.substr(9);
isOptional = true;
}
if(!funcSplit(line, name, args)) {
name = line;
args.clear();
}
auto it = modifierIds.find(name);
if(it == modifierIds.end()) {
if(!isOptional) {
error("Error: could not add modifier '%s.%s'.",
id.c_str(), name.c_str());
}
return;
}
const SubsystemDef::ModifyStage* stage = it->second;
if(stage->argumentNames.size() != args.size()) {
error("Error: Modifier '%s.%s' requires %d argument(s), %d given.",
id.c_str(), name.c_str(),
stage->argumentNames.size(), args.size());
return;
}
list.push_back(SubsystemDef::AppliedStage());
SubsystemDef::AppliedStage& as = list.back();
as.stage = stage;
for(unsigned i = 0, cnt = args.size(); i < cnt; ++i)
as.formulas[i] = Formula::fromInfix(args[i].c_str(), formulaVarIndex);
};
foreach(m, modules) {
foreach(it, (*m)->str_appliedStages)
makeAppliedStage(*it, (*m)->appliedStages);
foreach(it, (*m)->str_uniqueAppliedStages)
makeAppliedStage(*it, (*m)->uniqueAppliedStages);
foreach(it, (*m)->str_hexAppliedStages)
makeAppliedStage(*it, (*m)->hexAppliedStages);
foreach(it, (*m)->adjacentModifiers)
for(unsigned i = 0, cnt = it->str_arguments.size(); i < cnt; ++i)
it->stage.formulas[i] = loadFormula(it->str_arguments[i]);
foreach(it, (*m)->effects)
for(unsigned i = 0, cnt = it->str_values.size(); i < cnt; ++i)
it->values[i] = loadFormula(it->str_values[i]);
//Check that we have values for all effect variables
for(auto i = (*m)->effects.begin(), end = (*m)->effects.end(); i != end; ++i) {
for(unsigned n = 0; n < i->type->valueCount; ++n) {
auto& desc = i->type->values[n];
if(i->values[n] == 0 && !desc.defaultValue) {
error("Error: Subsystem '%s', module '%s', effect '%s' is missing values", id.c_str(),
(*m)->name.c_str(), i->type->name.c_str());
break;
}
}
}
}
formulaSubsystem = 0;
}
bool SubsystemDef::hasHullTag(const std::string& tag) const {
foreach(it, hullTags)
if(tag == *it)
return true;
return false;
}
bool SubsystemDef::canUseOn(const HullDef* hull) const {
if(!hull)
return false;
foreach(it, hullTags)
if(hull->hasTag(*it))
return true;
return false;
}
bool SubsystemDef::onCheckErrors(Design* design, Subsystem* sys) const {
if(!scr_onCheckErrors)
return false;
scripts::Call cl = devices.scripts.server->call(scr_onCheckErrors);
bool ret = false;
if(cl.valid()) {
cl.push(design);
cl.push(sys);
cl.call(ret);
}
return ret;
}
bool SubsystemDef::ModuleDesc::onCheckErrors(Design* design, Subsystem* sys, const vec2u& hex) const {
if(!scr_onCheckErrors)
return false;
scripts::Call cl = devices.scripts.server->call(scr_onCheckErrors);
bool ret = false;
if(cl.valid()) {
cl.push(design);
cl.push(sys);
cl.push(&hex);
cl.call(ret);
}
return ret;
}
void SubsystemDef::ModuleDesc::onEnable(EffectEvent& evt, const vec2u& position) const {
if(!scr_onEnable)
return;
scripts::Call cl = devices.scripts.server->call(scr_onEnable);
if(cl.valid()) {
cl.push(&evt);
cl.push(&position);
cl.call();
}
}
void SubsystemDef::ModuleDesc::onDisable(EffectEvent& evt, const vec2u& position) const {
if(!scr_onDisable)
return;
scripts::Call cl = devices.scripts.server->call(scr_onDisable);
if(cl.valid()) {
cl.push(&evt);
cl.push(&position);
cl.call();
}
}
bool SubsystemDef::hasTag(const std::string& tag) const {
auto it = tags.find(tag);
return it != tags.end();
}
bool SubsystemDef::hasTag(int index) const {
auto it = numTags.find(index);
return it != numTags.end();
}
static std::string ERR = "ERR";
const std::string& SubsystemDef::getTagValue(int index, unsigned num) const {
auto it = tagValues.find(index);
if(it != tagValues.end()) {
if(num >= it->second.size())
return ERR;
return it->second[num];
}
return ERR;
}
unsigned SubsystemDef::getTagValueCount(int index) const {
auto it = tagValues.find(index);
if(it != tagValues.end())
return it->second.size();
return 0;
}
bool SubsystemDef::hasTagValue(int index, const std::string& value) const {
auto it = tagValues.find(index);
if(it != tagValues.end()) {
for(size_t i = 0, cnt = it->second.size(); i < cnt; ++i) {
if(it->second[i] == value)
return true;
}
}
return false;
}
bool SubsystemDef::ModuleDesc::hasTag(const std::string& tag) const {
auto it = tags.find(tag);
return it != tags.end();
}
bool SubsystemDef::ModuleDesc::hasTag(int index) const {
auto it = numTags.find(index);
return it != numTags.end();
}
const std::string& SubsystemDef::ModuleDesc::getTagValue(int index, unsigned num) const {
auto it = tagValues.find(index);
if(it != tagValues.end()) {
if(num >= it->second.size())
return ERR;
return it->second[num];
}
return ERR;
}
unsigned SubsystemDef::ModuleDesc::getTagValueCount(int index) const {
auto it = tagValues.find(index);
if(it != tagValues.end())
return it->second.size();
return 0;
}
bool SubsystemDef::ModuleDesc::hasTagValue(int index, const std::string& value) const {
auto it = tagValues.find(index);
if(it != tagValues.end()) {
for(size_t i = 0, cnt = it->second.size(); i < cnt; ++i) {
if(it->second[i] == value)
return true;
}
}
return false;
}
struct LookupData {
Design* design;
Subsystem* system;
int hexIndex;
float args[MODIFY_STAGE_MAXARGS];
};
Threaded(LookupData) lookupData;
static double formulaVariable(void* user, const std::string* name) {
return 0.0;
}
static double formulaIndex(void* user, int index) {
if(index == -1)
return 0.0;
bool isBase = (index & VTF_BaseVariable) != 0;
VariableType type = (VariableType)(index & 0x7f000000);
index &= 0x00ffffff;
switch(type) {
case VT_ConstantVariable:
switch(index) {
case CV_Hexes:
return lookupData.system->hexes.size();
case CV_InteriorHexes:
return lookupData.system->hexes.size() - lookupData.system->exteriorHexes;
case CV_ExteriorHexes:
return lookupData.system->exteriorHexes;
case CV_HexSize:
return lookupData.design->hexSize;
case CV_ShipSize:
return lookupData.design->size;
case CV_ShipTotalHexes:
return lookupData.design->hull->activeCount;
case CV_ShipUsedHexes:
return lookupData.design->usedHexCount;
case CV_ShipEmptyHexes:
return lookupData.design->hull->activeCount - lookupData.design->usedHexCount;
case CV_IsCore:
if(lookupData.hexIndex < 0 || (unsigned)lookupData.hexIndex >= lookupData.system->hexes.size())
return 0.0;
return (lookupData.system->modules[lookupData.hexIndex] == lookupData.system->type->coreModule) ? 1.0 : 0.0;
case CV_HexExterior:
if(lookupData.hexIndex < 0)
return 0.0;
return lookupData.design->hull->isExterior(lookupData.system->hexes[lookupData.hexIndex]) ? 1.0 : 0.0;
case CV_AdjacentActive: {
if(lookupData.hexIndex < 0 || (unsigned)lookupData.hexIndex >= lookupData.system->hexes.size())
return 0.0;
const Design * design = lookupData.design;
vec2u hex = lookupData.system->hexes[lookupData.hexIndex];
double count = 0;
for(unsigned d = 0; d < 6; ++d) {
vec2u pos = hex;
if(!design->hull->active.advance(pos.x, pos.y, HexGridAdjacency(d)))
continue;
if(!design->hull->active.get(pos))
continue;
count += 1.0;
}
return count;
}
case CV_AdjacentThis: {
if(lookupData.hexIndex < 0 || (unsigned)lookupData.hexIndex >= lookupData.system->hexes.size())
return 0.0;
const Design * design = lookupData.design;
vec2u hex = lookupData.system->hexes[lookupData.hexIndex];
double count = 0;
for(unsigned d = 0; d < 6; ++d) {
vec2u pos = hex;
if(!design->hull->active.advance(pos.x, pos.y, HexGridAdjacency(d)))
continue;
if(!design->hull->active.get(pos))
continue;
int sysId = design->grid.get(pos);
if((unsigned)sysId >= design->subsystems.size())
continue;
if(&design->subsystems[sysId] != lookupData.system)
continue;
count += 1.0;
}
return count;
}
}
return 0.0;
case VT_GameConfig:
if((size_t)index < gameConfig.count)
return gameConfig.values[index];
return 0.0;
case VT_ModuleCount:
if(index < 0 || index >= (int)lookupData.system->moduleCounts.size())
return 0.0;
return lookupData.system->moduleCounts[index];
case VT_ModuleExists:
if(index < 0 || index >= (int)lookupData.system->moduleCounts.size())
return 0.0;
return lookupData.system->moduleCounts[index] > 0 ? 1.0 : 0.0;
case VT_HexVariable:
//Make sure we're in a hex context
if(lookupData.hexIndex < 0)
return 0.0;
//Lookup local index
if(index < (int)lookupData.system->type->hexVariableIndices.size())
index = lookupData.system->type->hexVariableIndices[index];
else
index = -1;
//Lookup variable
if(index < 0) {
return 0.0;
}
else {
unsigned baseIndex = lookupData.hexIndex * lookupData.system->type->hexVariables.size();
if(isBase)
return lookupData.system->hexBaseVariables[baseIndex + index];
else
return lookupData.system->hexVariables[baseIndex + index];
}
case VT_ShipVariable:
//Lookup variable
if(index < 0) {
return 0.0;
}
else {
return lookupData.design->shipVariables[index];
}
case VT_SubsystemVariable:
//Lookup local index
if(index < (int)lookupData.system->type->variableIndices.size())
index = lookupData.system->type->variableIndices[index];
else
index = -1;
//Lookup variable
if(index < 0)
return 0.0;
else if(isBase)
return lookupData.system->baseVariables[index];
else
return lookupData.system->variables[index];
case VT_SumVariable: {
if(lookupData.design == nullptr)
return 0.0;
double value = 0.0;
for(size_t i = 0, cnt = lookupData.design->subsystems.size(); i < cnt; ++i) {
auto* sys = &lookupData.design->subsystems[i];
if(sys->variables == nullptr || sys->type == nullptr)
continue;
//Lookup local index
int localIndex = -1;
if((unsigned)index < (unsigned)sys->type->variableIndices.size())
localIndex = sys->type->variableIndices[index];
if(localIndex >= 0) {
if(isBase)
value += sys->baseVariables[localIndex];
else
value += sys->variables[localIndex];
}
}
return value;
}
case VT_HexSumVariable: {
if(lookupData.design == nullptr)
return 0.0;
double value = 0.0;
for(size_t i = 0, cnt = lookupData.design->subsystems.size(); i < cnt; ++i) {
auto* sys = &lookupData.design->subsystems[i];
if(sys->hexVariables == nullptr || sys->type == nullptr)
continue;
//Lookup local index
int localIndex = -1;
if((unsigned)index < (unsigned)sys->type->hexVariableIndices.size())
localIndex = sys->type->hexVariableIndices[index];
if(localIndex < 0)
continue;
for(size_t h = 0, hexCnt = sys->hexes.size(); h < hexCnt; ++h) {
if(sys == lookupData.system && h >= (unsigned)lookupData.hexIndex)
break;
int ind = h * sys->type->hexVariables.size() + localIndex;
if(isBase)
value += sys->hexBaseVariables[ind];
else
value += sys->hexVariables[ind];
}
}
return value;
}
case VT_TagCountVariable: {
if(lookupData.design == nullptr)
return 0.0;
double value = 0.0;
for(size_t i = 0, cnt = lookupData.design->subsystems.size(); i < cnt; ++i) {
if(lookupData.design->subsystems[i].type->hasTag(index))
value += 1.0;
}
return value;
}
case VT_Argument:
if(index >= 0 && index < MODIFY_STAGE_MAXARGS)
return lookupData.args[index];
else
return 0.0;
case VT_AdjacentTag: {
if(lookupData.hexIndex < 0 || (unsigned)lookupData.hexIndex >= lookupData.system->hexes.size())
return 0.0;
const Design * design = lookupData.design;
vec2u hex = lookupData.system->hexes[lookupData.hexIndex];
double count = 0;
for(unsigned d = 0; d < 6; ++d) {
vec2u pos = hex;
if(!design->hull->active.advance(pos.x, pos.y, HexGridAdjacency(d)))
continue;
if(!design->hull->active.get(pos))
continue;
int sysId = design->grid.get(pos);
if((unsigned)sysId >= design->subsystems.size())
continue;
if(!design->subsystems[sysId].type->hasTag(index))
continue;
count += 1.0;
}
return count;
}
case VT_AdjacentSubsystem: {
if(lookupData.hexIndex < 0 || (unsigned)lookupData.hexIndex >= lookupData.system->hexes.size())
return 0.0;
const Design * design = lookupData.design;
vec2u hex = lookupData.system->hexes[lookupData.hexIndex];
double count = 0;
for(unsigned d = 0; d < 6; ++d) {
vec2u pos = hex;
if(!design->hull->active.advance(pos.x, pos.y, HexGridAdjacency(d)))
continue;
if(!design->hull->active.get(pos))
continue;
int sysId = design->grid.get(pos);
if((unsigned)sysId >= design->subsystems.size())
continue;
if(design->subsystems[sysId].type->index != index)
continue;
count += 1.0;
}
return count;
}
}
return 0.0;
}
void Subsystem::init(const SubsystemDef& Def)
{
type = &Def;
unsigned effCount = Def.effects.size();
effects.resize(effCount);
for(unsigned i = 0; i < effCount; ++i)
effects[i].type = Def.effects[i].type;
unsigned efftrCount = Def.effectors.size();
effectors = (Effector*)malloc(efftrCount * sizeof(Effector));
for(unsigned i = 0; i < efftrCount; ++i) {
new(&effectors[i]) Effector(*Def.effectors[i].type);
effectors[i].enabled = Def.effectors[i].enabled;
effectors[i].effectorIndex = i;
effectors[i].skinIndex = Def.effectors[i].skinIndex;
}
defaults = new BasicType[Def.states.size()];
}
Subsystem::Subsystem()
: type(nullptr), exteriorHexes(0), hasErrors(false), variables(0), hexVariables(0),
inDesign(nullptr), index(0), defaults(nullptr), effectors(nullptr)
{
}
Subsystem::Subsystem(const SubsystemDef& Def)
: type(nullptr), exteriorHexes(0), hasErrors(false), variables(0), hexVariables(0),
inDesign(nullptr), index(0), defaults(nullptr), effectors(nullptr)
{
init(Def);
}
void Subsystem::skinEffectors(Empire& emp) {
if(emp.effectorSkin.empty())
return;
unsigned efftrCount = type->effectors.size();
for(unsigned i = 0; i < efftrCount; ++i) {
auto& efftr = effectors[i];
std::string ident = type->effectors[i].skinName;
if(!ident.empty())
ident += "/";
ident += emp.effectorSkin;
auto it = efftr.type.skinNames.find(ident);
if(it != efftr.type.skinNames.end())
efftr.skinIndex = it->second;
}
}
void SubsystemDef::ModifyStage::applyVariables(Subsystem* sys) const {
foreach(v, variables) {
int localIndex = sys->type->variableIndices[v->first];
Formula* formula = v->second;
if(formula)
sys->variables[localIndex] = (float)formula->evaluate(&formulaVariable, &lookupData, &formulaIndex);
}
}
void SubsystemDef::ModifyStage::applyHexVariables(Subsystem* sys, int hexIndex) const {
foreach(v, hexVariables) {
int localIndex = sys->type->hexVariableIndices[v->first];
localIndex += hexIndex * sys->type->hexVariables.size();
Formula* formula = v->second;
if(formula)
sys->hexVariables[localIndex] = (float)formula->evaluate(&formulaVariable, &lookupData, &formulaIndex);
}
}
void SubsystemDef::ModifyStage::applyShipVariables(Design* dsg, Subsystem* sys) const {
foreach(v, shipVariables) {
int index = v->first;
Formula* formula = v->second;
if(formula)
dsg->shipVariables[index] = (float)formula->evaluate(&formulaVariable, &lookupData, &formulaIndex);
}
}
struct RunStage {
int priority;
int amount;
const SubsystemDef::ModifyStage* stage;
float args[MODIFY_STAGE_MAXARGS];
RunStage(int p, int a, const SubsystemDef::ModifyStage* s)
: priority(p), amount(a), stage(s) {
}
RunStage(int a, const SubsystemDef::AppliedStage& as)
: priority(as.stage->stage), amount(a), stage(as.stage) {
for(unsigned i = 0; i < MODIFY_STAGE_MAXARGS; ++i) {
if(as.formulas[i])
args[i] = (float)as.formulas[i]->evaluate(&formulaVariable, &lookupData, &formulaIndex);
else
args[i] = as.arguments[i];
}
}
bool operator<(const RunStage& other) const {
return priority < other.priority;
}
};
void applyShipModifier(const SubsystemDef* type, SubsystemDef::ShipModifier& mod, std::vector<RunStage>& modStages) {
auto fnd = type->modifierIds.find(mod.modifyName);
if(fnd == type->modifierIds.end())
return;
if(!conditionMatches(type, mod.conditions))
return;
SubsystemDef::AppliedStage applied;
for(unsigned i = 0; i < MODIFY_STAGE_MAXARGS; ++i)
applied.arguments[i] = mod.stage.arguments[i];
applied.stage = fnd->second;
modStages.push_back(RunStage(1, applied));
}
void Subsystem::initVariables(Design* design) {
//Make sure the lookup data matches
lookupData.design = design;
lookupData.system = this;
lookupData.hexIndex = -1;
//Get subsystem data from empire
Empire::SubsystemData* ssdata = 0;
if(design->owner) {
ssdata = design->owner->getSubsystemData(type);
//Add an error if this subsystem is not unlocked
if(!ssdata || !ssdata->unlocked) {
std::string errMsg = format(devices.locale.localize("ERROR_NOT_UNLOCKED").c_str(), type->name);
design->errors.push_back(DesignError(true, errMsg, this));
hasErrors = true;
}
}
//Hold a priority queue of considered modify stages
std::vector<RunStage> modStages;
//Evaluate variables
variables = new float[type->variables.size()];
baseVariables = new float[type->variables.size()];
memset(variables, 0, sizeof(float) * type->variables.size());
memset(baseVariables, 0, sizeof(float) * type->variables.size());
//Do modify stages from modules
unsigned modCnt = type->modules.size();
for(unsigned i = 0; i < modCnt; ++i) {
if(moduleCounts[i] > 0) {
auto& mod = *type->modules[i];
//Unique inline modifiers
foreach(it, mod.uniqueModifiers)
modStages.push_back(RunStage(it->stage, 1, &*it));
//Unique applied stages
foreach(it, mod.uniqueAppliedStages)
modStages.push_back(RunStage(1, *it));
//Inline modifiers
foreach(it, mod.modifiers)
modStages.push_back(RunStage(it->stage, moduleCounts[i], &*it));
//Applied stages
foreach(it, mod.appliedStages)
modStages.push_back(RunStage(moduleCounts[i], *it));
}
}
//Ship modifiers
foreach(it, design->modifiers)
applyShipModifier(type, *it, modStages);
//Do modify stages from empire
if(ssdata) {
foreach(it, ssdata->stages)
modStages.push_back(RunStage(1, it->second));
}
std::sort(modStages.begin(), modStages.end());
for(unsigned i = 0; i < type->variables.size(); ++i) {
auto* formula = type->variables[i].formula;
if(formula)
variables[i] = (float)formula->evaluate(&formulaVariable, &lookupData, &formulaIndex);
else
variables[i] = 0;
baseVariables[i] = variables[i];
foreach(it, modStages) {
RunStage& rs = *it;
bool hasArgs = false;
foreach(v, rs.stage->variables) {
unsigned localIndex = type->variableIndices[v->first];
Formula* formula = v->second;
if(localIndex == i && formula) {
if(!hasArgs) {
memcpy(&lookupData.args, &rs.args, sizeof(rs.args));
hasArgs = true;
}
for(int repeats = 0; repeats < rs.amount; ++repeats)
variables[localIndex] = (float)formula->evaluate(&formulaVariable, &lookupData, &formulaIndex);
}
}
}
}
//Evaluate ship variables
for(unsigned i = 0; i < type->shipVariables.size(); ++i) {
auto* formula = type->shipVariables[i].formula;
int index = type->shipVariables[i].index;
if(formula)
design->shipVariables[index] = (float)formula->evaluate(&formulaVariable, &lookupData, &formulaIndex);
}
//Check if all modules are unlocked
for(unsigned i = 0; i < modCnt; ++i) {
if(moduleCounts[i] > 0) {
auto& mod = *type->modules[i];
if(ssdata) {
if(ssdata->modulesUnlocked.size() <= (unsigned)mod.index || !ssdata->modulesUnlocked[mod.index]) {
std::string errMsg = format(devices.locale.localize("ERROR_MODULE_NOT_UNLOCKED").c_str(), mod.name);
design->errors.push_back(DesignError(true, errMsg, this, &mod));
hasErrors = true;
}
}
}
}
//Run all the modify stages
foreach(it, modStages) {
RunStage& rs = *it;
memcpy(&lookupData.args, &rs.args, sizeof(rs.args));
for(int i = 0; i < rs.amount; ++i)
rs.stage->applyShipVariables(design, this);
}
//Dump
//for(unsigned j = 0; j < type->variables.size(); ++j) {
//printf("%s.%s = %g\n", type->id.c_str(),
//type->variables[j].name.c_str(), variables[j]);
//}
//Recompute mod stages without hex-specific ones
modStages.clear();
for(unsigned i = 0; i < modCnt; ++i) {
if(moduleCounts[i] > 0) {
auto& mod = *type->modules[i];
//Unique applied stages
foreach(it, mod.uniqueAppliedStages)
if(!it->stage->hexVariables.empty())
modStages.push_back(RunStage(1, *it));
//Applied stages
foreach(it, mod.appliedStages)
if(!it->stage->hexVariables.empty())
modStages.push_back(RunStage(moduleCounts[i], *it));
}
}
//Ship modifiers
foreach(it, design->modifiers)
applyShipModifier(type, *it, modStages);
//Do modify stages from empire
if(ssdata) {
foreach(it, ssdata->stages)
if(!it->second.stage->hexVariables.empty())
modStages.push_back(RunStage(1, it->second));
}
std::sort(modStages.begin(), modStages.end());
//Evaluate hex variables
unsigned varCnt = type->hexVariables.size();
unsigned hexCnt = hexes.size();
hexVariables = new float[varCnt * hexCnt];
hexBaseVariables = new float[varCnt * hexCnt];
hexAdjacentModifiers.resize(hexCnt);
hexEffects.resize(hexes.size());
bool foundCore = false;
for(unsigned i = 0; i < hexCnt; ++i) {
unsigned base = varCnt * i;
lookupData.hexIndex = i;
//Cache the position of the core
if(type->hasCore && modules[i] == type->coreModule) {
core = hexes[i];
foundCore = true;
}
//Initialize all the default formulas for a hex
for(unsigned j = 0; j < varCnt; ++j) {
auto* formula = type->hexVariables[j].formula;
if(formula)
hexVariables[base + j] = (float)formula->evaluate(&formulaVariable, &lookupData, &formulaIndex);
else
hexVariables[base + j] = 0;
hexBaseVariables[base + j] = hexVariables[base + j];
}
//Add the modify stages
auto* mod = modules[i];
foreach(it, mod->uniqueModifiers) {
if(!it->hexVariables.empty()) {
it->applyHexVariables(this, i);
}
}
foreach(it, mod->modifiers) {
if(!it->hexVariables.empty()) {
it->applyHexVariables(this, i);
}
}
foreach(it, mod->hexAppliedStages) {
if(!it->stage->hexVariables.empty()) {
RunStage rs(1, *it);
memcpy(&lookupData.args, &rs.args, sizeof(float) * MODIFY_STAGE_MAXARGS);
rs.stage->applyHexVariables(this, i);
}
}
//Add adjacent modifiers
foreach(it, mod->adjacentModifiers) {
SubsystemDef::ShipModifier mod;
mod.modifyName = it->modifyName;
mod.conditions = it->conditions;
for(unsigned j = 0; j < MODIFY_STAGE_MAXARGS; ++j) {
if(it->stage.formulas[j])
mod.stage.arguments[j] = it->stage.formulas[j]->evaluate(&formulaVariable, &lookupData, &formulaIndex);
}
hexAdjacentModifiers[i].push_back(mod);
}
//Run the modify stages
foreach(it, modStages) {
RunStage& rs = *it;
memcpy(&lookupData.args, &rs.args, sizeof(float) * MODIFY_STAGE_MAXARGS);
for(int j = 0; j < rs.amount; ++j)
rs.stage->applyHexVariables(this, i);
}
//Run dependent variables again
for(unsigned j = 0; j < varCnt; ++j) {
if(!type->hexVariables[j].dependent)
continue;
auto* formula = type->hexVariables[j].formula;
if(formula)
hexVariables[base + j] = (float)formula->evaluate(&formulaVariable, &lookupData, &formulaIndex);
}
//Dump
//for(unsigned j = 0; j < varCnt; ++j) {
//printf("%s[%d,%d].%s = %g\n", type->id.c_str(),
//hexes[i].x, hexes[i].y,
//type->hexVariables[j].name.c_str(),
//hexVariables[base + j]);
//}
}
//Re-evaluate variables marked as dependent to get the correct values
for(unsigned i = 0; i < type->variables.size(); ++i) {
if(!type->variables[i].dependent)
continue;
auto* formula = type->variables[i].formula;
if(formula)
variables[i] = (float)formula->evaluate(&formulaVariable, &lookupData, &formulaIndex);
}
//Add ship modifiers
foreach(it, type->shipModifiers) {
SubsystemDef::ShipModifier mod;
mod.modifyName = it->modifyName;
mod.conditions = it->conditions;
for(unsigned i = 0; i < MODIFY_STAGE_MAXARGS; ++i) {
if(it->stage.formulas[i])
mod.stage.arguments[i] = it->stage.formulas[i]->evaluate(&formulaVariable, &lookupData, &formulaIndex);
}
design->modifiers.push_back(mod);
}
//Add adjacent modifiers
foreach(it, type->adjacentModifiers) {
SubsystemDef::ShipModifier mod;
mod.modifyName = it->modifyName;
mod.conditions = it->conditions;
for(unsigned i = 0; i < MODIFY_STAGE_MAXARGS; ++i) {
if(it->stage.formulas[i])
mod.stage.arguments[i] = it->stage.formulas[i]->evaluate(&formulaVariable, &lookupData, &formulaIndex);
}
adjacentModifiers.push_back(mod);
}
//Add an error if this subsystem should have a core, but doesn't
if(type->hasCore && !foundCore) {
std::string errMsg = format(devices.locale.localize("ERROR_NO_CORE").c_str(), type->name);
design->errors.push_back(DesignError(true, errMsg, this));
hasErrors = true;
}
//Add an error if we needed an exterior core, but don't
if(type->hasCore && foundCore && type->exteriorCore && !design->hull->isExterior(core)) {
std::string errMsg = format(devices.locale.localize("ERROR_NEEDS_EXTERIOR_CORE").c_str(), type->name);
design->errors.push_back(DesignError(true, errMsg, this, 0, core));
hasErrors = true;
}
//Add an error if any required modules are not present
for(unsigned i = 0, modCnt = type->modules.size(); i < modCnt; ++i) {
auto* mod = type->modules[i];
if(mod->required && moduleCounts[i] == 0 && mod != type->coreModule) {
std::string errMsg = format(devices.locale.localize("ERROR_REQUIRED_MODULE").c_str(), type->name, mod->name);
design->errors.push_back(DesignError(true, errMsg, this, mod));
hasErrors = true;
}
if(mod->unique && moduleCounts[i] > 1) {
std::string errMsg = format(devices.locale.localize("ERROR_UNIQUE_MODULE").c_str(), type->name, mod->name);
design->errors.push_back(DesignError(true, errMsg, this, mod));
hasErrors = true;
}
}
//Add an error if we can't use this subsystem on this hull
if(!type->canUseOn(design->hull)) {
std::string errMsg = format(devices.locale.localize("ERROR_INVALID_HULL").c_str(), type->name);
design->errors.push_back(DesignError(true, errMsg, this));
hasErrors = true;
}
}
void Subsystem::applyAdjacencies(Design* design) {
lookupData.design = design;
lookupData.system = this;
std::vector<RunStage> modStages;
for(size_t n = 0, hexCnt = hexes.size(); n < hexCnt; ++n) {
vec2u hex = hexes[n];
lookupData.hexIndex = n;
if(!design->hull->active.valid(hex))
continue;
//Check all adjacent hexes
for(unsigned i = 0; i < 6; ++i) {
vec2u pos = hex;
if(!design->hull->active.advance(pos.x, pos.y, HexGridAdjacency(i)))
continue;
if(!design->hull->active.get(pos))
continue;
int sysId = design->grid.get(pos);
if((unsigned)sysId >= design->subsystems.size())
continue;
auto& otherSys = design->subsystems[sysId];
int hexId = design->hexIndex.get(pos);
if((unsigned)hexId >= otherSys.hexes.size())
continue;
for(size_t j = 0, jcnt = otherSys.adjacentModifiers.size(); j < jcnt; ++j)
applyShipModifier(this->type, otherSys.adjacentModifiers[j], modStages);
for(size_t j = 0, jcnt = otherSys.hexAdjacentModifiers[hexId].size(); j < jcnt; ++j)
applyShipModifier(this->type, otherSys.hexAdjacentModifiers[hexId][j], modStages);
}
std::sort(modStages.begin(), modStages.end());
//Run all the modify stages
foreach(it, modStages) {
RunStage& rs = *it;
memcpy(&lookupData.args, &rs.args, sizeof(rs.args));
for(int i = 0; i < rs.amount; ++i) {
rs.stage->applyVariables(this);
rs.stage->applyHexVariables(this, n);
}
}
modStages.clear();
}
}
extern double effVariable(void* effector, const std::string* name);
SubsystemDef::HookDesc::HookDesc(const std::string& str) {
if(!funcSplit(str, name, str_args))
name = str;
formulas.resize(str_args.size());
argValues.resize(str_args.size());
for(size_t i = 0, cnt = str_args.size(); i < cnt; ++i) {
formulas[i] = nullptr;
argValues[i] = 0.0;
}
}
void Subsystem::initEffects(Design* design) {
//Make sure the lookup data matches
lookupData.design = design;
lookupData.system = this;
lookupData.hexIndex = -1;
//Evaluate effect values
for(unsigned i = 0; i < type->effects.size(); ++i) {
for(unsigned j = 0; j < type->effects[i].values.size(); ++j) {
if(auto* formula = type->effects[i].values[j])
effects[i].values[j] = formula->evaluate(&formulaVariable, &lookupData, &formulaIndex);
else if(effects[i].type->values[j].defaultValue)
effects[i].values[j] = effects[i].type->values[j].defaultValue->evaluate();
}
}
//Evaluate effects from modules
for(unsigned i = 0; i < hexes.size(); ++i) {
auto* module = modules[i];
if(module->effects.empty())
continue;
lookupData.hexIndex = i;
for(unsigned n = 0; n < module->effects.size(); ++n) {
auto& def = module->effects[n];
Effect eff(def.type);
for(unsigned j = 0; j < def.values.size(); ++j) {
if(auto* formula = def.values[j])
eff.values[j] = formula->evaluate(&formulaVariable, &lookupData, &formulaIndex);
else if(eff.type->values[j].defaultValue)
eff.values[j] = eff.type->values[j].defaultValue->evaluate();
}
unsigned id = effects.size();
effects.push_back(eff);
hexEffects[i].push_back(id);
}
}
lookupData.hexIndex = -1;
for(unsigned i = 0; i < type->effectors.size(); ++i) {
//Evaluate effector values
for(unsigned j = 0; j < type->effectors[i].values.size(); ++j) {
if(auto* formula = type->effectors[i].values[j])
effectors[i].values[j] = formula->evaluate(&formulaVariable, &lookupData, &formulaIndex);
else if(effectors[i].type.values[j].defaultValue)
effectors[i].values[j] = effectors[i].type.values[j].defaultValue->evaluate(effVariable, &effectors[i]);
}
effectors[i].initValues();
//Set relative positions of the effectors
effectors[i].setRelativePosition(core, design->hull, direction);
//Deal with hex-limited angles
if(type->hexLimitArc && design->hull->active.valid(core) && !design->hasTag(overrideHexArcLimitTag)) {
auto& eff = effectors[i];
//Make sure the turret angle is within bounds
vec2d flatAngle(eff.turretAngle.x, -eff.turretAngle.z);
double rad = flatAngle.radians();
unsigned dir = HexGrid<>::AdjacencyFromRadians(rad);
if(!design->hull->isExteriorInDirection(core, dir)) {
double diff = rad - HexGrid<>::RadiansFromAdjacency(HexGridAdjacency(dir));
if(diff >= twopi / 6.0 * 0.5 * 0.99)
dir = (dir+1)%6;
else if(diff <= -twopi / 6.0 * 0.5 * 0.99)
dir = (6+dir-1)%6;
if(!design->hull->isExteriorInDirection(core, dir)) {
std::string errMsg = format(devices.locale.localize("ERROR_HEX_LIMIT_ARC").c_str(), type->name);
design->errors.push_back(DesignError(false, errMsg, this));
hasErrors = true;
eff.fireArc = -1.0;
}
}
//Move the turret angle to the center position so we scrunch the firing arc
if(eff.fireArc > 0) {
double specRad = HexGrid<>::RadiansFromAdjacency(HexGridAdjacency(dir));
double minRad = specRad - (twopi / 12.0);
double arc;
bool shouldChange = false;
arc = eff.fireArc - (rad - minRad);
for(unsigned n = 1; n <= 3 && arc > 0; ++n) {
unsigned chkDir = (6 + dir - n) % 6;
if(!design->hull->isExteriorInDirection(core, chkDir)) {
shouldChange = true;
minRad -= std::min(twopi / 48.0, arc);
break;
}
minRad -= std::min(twopi / 6.0, arc);
arc -= twopi / 6.0;
}
double maxRad = specRad + (twopi / 12.0);
arc = eff.fireArc - (maxRad - rad);
for(unsigned n = 1; n <= 3 && arc > 0; ++n) {
unsigned chkDir = (dir + n) % 6;
if(!design->hull->isExteriorInDirection(core, chkDir)) {
shouldChange = true;
maxRad += std::min(twopi / 48.0, arc);
break;
}
maxRad += std::min(twopi / 6.0, arc);
arc -= twopi / 6.0;
}
if(shouldChange) {
double arcRad = (maxRad - minRad) * 0.5;
eff.fireArc = std::min(arcRad, eff.fireArc);
vec2d newPos(1.0, 0.0);
newPos.rotate(minRad + (arcRad - eff.fireArc) * 0.5 + arcRad);
eff.turretAngle.x = newPos.x;
eff.turretAngle.z = -newPos.y;
}
}
}
//Set relative size of the effector
effectors[i].relativeSize = 3.0 * sqrt((double)hexes.size() / (double)design->hull->activeCount);
}
//Evaluate states
for(unsigned i = 0; i < type->states.size(); ++i) {
double value = 0;
if(auto* formula = type->states[i].formula)
value = formula->evaluate(&formulaVariable, &lookupData, &formulaIndex);
defaults[i].type = type->states[i].type;
switch(type->states[i].type) {
default:
case BT_Double:
defaults[i].decimal = value;
break;
case BT_Int:
defaults[i].integer = (int)value;
break;
case BT_Bool:
defaults[i].boolean = (bool)(value != 0.0);
break;
}
}
//Initialize hooks
for(size_t i = 0, cnt = type->hooks.size(); i < cnt; ++i)
addHook(design, type->hooks[i]);
for(size_t i = 0, cnt = modules.size(); i < cnt; ++i) {
auto* module = modules[i];
if(module) {
for(size_t n = 0, ncnt = module->hooks.size(); n < ncnt; ++n)
addHook(design, module->hooks[n]);
}
}
}
void Subsystem::addHook(Design* design, const SubsystemDef::HookDesc& hook) {
if(devices.scripts.server == devices.scripts.cache_shadow)
return;
std::vector<std::string> parts;
split(hook.name, parts, "::");
asITypeInfo* cls = nullptr;
if(parts.size() == 1)
cls = devices.scripts.server->getClass("subsystem_effects", parts[0].c_str());
else if(parts.size() == 2)
cls = devices.scripts.server->getClass(parts[0].c_str(), parts[1].c_str());
if(cls == nullptr)
return;
asIScriptObject* obj = (asIScriptObject*)devices.scripts.server->engine->CreateScriptObject(cls);
if(obj == nullptr)
return;
for(unsigned i = 0, cnt = hook.formulas.size(); i < cnt; ++i) {
if(hook.formulas[i])
hook.argValues[i] = hook.formulas[i]->evaluate(&formulaVariable, &lookupData, &formulaIndex);
else
hook.argValues[i] = 0.0;
}
auto call = devices.scripts.server->call(ScriptInitFunction);
bool retVal = false;
call.setObject(obj);
call.push(design);
call.push(this);
call.push(&hook.str_args);
call.push(&hook.argValues);
call.call(retVal);
if(retVal)
hookClasses.push_back(obj);
else
obj->Release();
}
void Subsystem::evaluatePost(Design* design) {
//Make sure the lookup data matches
lookupData.design = design;
lookupData.system = this;
lookupData.hexIndex = -1;
//Do any post-ship modifiers
std::vector<RunStage> stages;
foreach(it, type->postModifiers) {
SubsystemDef::ShipModifier mod;
mod.modifyName = it->modifyName;
mod.conditions = it->conditions;
for(unsigned i = 0; i < MODIFY_STAGE_MAXARGS; ++i) {
if(it->stage.formulas[i])
mod.stage.arguments[i] = it->stage.formulas[i]->evaluate(&formulaVariable, &lookupData, &formulaIndex);
}
applyShipModifier(type, mod, stages);
}
std::sort(stages.begin(), stages.end());
//Run all the modify stages
foreach(it, stages) {
memcpy(&lookupData.args, it->args, sizeof(it->args));
for(int i = 0; i < it->amount; ++i)
it->stage->applyVariables(this);
}
foreach(it, stages) {
memcpy(&lookupData.args, it->args, sizeof(it->args));
for(int i = 0; i < it->amount; ++i)
it->stage->applyShipVariables(design, this);
}
unsigned hexCnt = hexes.size();
for(unsigned i = 0; i < hexCnt; ++i) {
lookupData.hexIndex = i;
foreach(it, stages) {
memcpy(&lookupData.args, it->args, sizeof(it->args));
for(int n = 0; n < it->amount; ++n)
it->stage->applyHexVariables(this, i);
}
}
}
void Subsystem::evaluateAsserts(Design* design) {
//Make sure the lookup data matches
lookupData.design = design;
lookupData.system = this;
lookupData.hexIndex = -1;
auto checkAssert = [](Subsystem* sys, Design* design, const SubsystemDef::Assert& ass) {
if(!ass.formula)
return;
double val = ass.formula->evaluate(&formulaVariable, &lookupData, &formulaIndex);
if(val <= 0.0) {
//Check uniqueness
std::string msg = format(ass.message.c_str(), sys->type->name.c_str());
if(ass.unique) {
bool duplicate = false;
foreach(e, design->errors) {
if(e->fatal != ass.fatal)
continue;
if(e->text != msg)
continue;
duplicate = true;
break;
}
if(duplicate)
return;
design->errors.push_back(DesignError(
ass.fatal, msg, 0, 0));
sys->hasErrors = true;
}
else {
design->errors.push_back(DesignError(
ass.fatal, msg, sys, 0));
sys->hasErrors = true;
}
}
};
//Check any assertion errors
foreach(it, type->asserts) {
const SubsystemDef::Assert& ass = *it;
checkAssert(this, design, ass);
}
//Check hex asserts
for(unsigned i = 0, hexCount = (unsigned)hexes.size(); i < hexCount; ++i) {
lookupData.hexIndex = i;
foreach(it, modules[i]->asserts) {
const SubsystemDef::Assert& ass = *it;
checkAssert(this, design, ass);
}
if(modules[i]->onCheckErrors(design, this, hexes[i]))
hasErrors = true;
}
//Add any errors from the script check
if(type->onCheckErrors(design, this))
hasErrors = true;
}
void Subsystem::ownerChange(EffectEvent& event, Empire* prevEmpire, Empire* newEmpire) const {
for(unsigned i = 0; i < effects.size(); ++i)
effects[i].ownerChange(event, prevEmpire, newEmpire);
if(!hookClasses.empty()) {
SubsystemEvent evt;
evt.obj = event.obj;
evt.subsystem = this;
evt.blueprint = nullptr;
evt.design = inDesign;
evt.data = nullptr;
evt.efficiency = event.efficiency;
evt.partiality = event.partiality;
if(evt.obj != nullptr && evt.obj->type->blueprintOffset != 0)
evt.blueprint = (Blueprint*)(((size_t)evt.obj) + evt.obj->type->blueprintOffset);
for(size_t i = 0, cnt = hookClasses.size(); i < cnt; ++i) {
if(evt.blueprint != nullptr)
evt.data = evt.blueprint->data[dataOffset+i];
auto cl = devices.scripts.server->call(ScriptHookFunctions[EH_Owner_Change]);
cl.setObject(hookClasses[i]);
cl.push(&evt);
cl.push(prevEmpire);
cl.push(newEmpire);
cl.call();
}
}
}
void Subsystem::call(EffectHook hook, EffectEvent& event) const {
for(unsigned i = 0; i < effects.size(); ++i)
effects[i].call(hook, event);
if(!hookClasses.empty()) {
SubsystemEvent evt;
evt.obj = event.obj;
evt.subsystem = this;
evt.design = inDesign;
evt.blueprint = nullptr;
evt.data = nullptr;
evt.efficiency = event.efficiency;
evt.partiality = event.partiality;
if(evt.obj != nullptr && evt.obj->type->blueprintOffset != 0)
evt.blueprint = (Blueprint*)(((size_t)evt.obj) + evt.obj->type->blueprintOffset);
for(size_t i = 0, cnt = hookClasses.size(); i < cnt; ++i) {
if(evt.blueprint != nullptr)
evt.data = evt.blueprint->data[dataOffset+i];
auto cl = devices.scripts.server->call(ScriptHookFunctions[hook]);
cl.setObject(hookClasses[i]);
cl.push(&evt);
cl.call();
}
}
}
void Subsystem::save(EffectEvent& event, SaveMessage& msg) const {
if(!hookClasses.empty()) {
SubsystemEvent evt;
evt.obj = event.obj;
evt.subsystem = this;
evt.design = inDesign;
evt.blueprint = nullptr;
evt.data = nullptr;
evt.efficiency = event.efficiency;
evt.partiality = event.partiality;
if(evt.obj != nullptr && evt.obj->type->blueprintOffset != 0)
evt.blueprint = (Blueprint*)(((size_t)evt.obj) + evt.obj->type->blueprintOffset);
for(size_t i = 0, cnt = hookClasses.size(); i < cnt; ++i) {
if(evt.blueprint != nullptr)
evt.data = evt.blueprint->data[dataOffset+i];
auto cl = devices.scripts.server->call(ScriptHookFunctions[EH_Save]);
cl.setObject(hookClasses[i]);
cl.push(&evt);
cl.push(&msg);
cl.call();
}
}
}
void Subsystem::load(EffectEvent& event, SaveMessage& msg) const {
if(!hookClasses.empty()) {
SubsystemEvent evt;
evt.obj = event.obj;
evt.subsystem = this;
evt.design = inDesign;
evt.blueprint = nullptr;
evt.data = nullptr;
evt.efficiency = event.efficiency;
evt.partiality = event.partiality;
if(evt.obj != nullptr && evt.obj->type->blueprintOffset != 0)
evt.blueprint = (Blueprint*)(((size_t)evt.obj) + evt.obj->type->blueprintOffset);
for(size_t i = 0, cnt = hookClasses.size(); i < cnt; ++i) {
if(evt.blueprint != nullptr && dataOffset+i < inDesign->dataCount)
evt.data = evt.blueprint->data[dataOffset+i];
auto cl = devices.scripts.server->call(ScriptHookFunctions[EH_Load]);
cl.setObject(hookClasses[i]);
cl.push(&evt);
cl.push(&msg);
cl.call();
}
}
}
void Subsystem::tick(EffectEvent& event) const {
//Tick Effects
EffectStatus status = event.status;
for(unsigned i = 0; i < effects.size(); ++i) {
effects[i].call(EH_Tick, event);
//Cancel when any effect suspends or continues
if(status == ES_Suspended) {
if(event.status != ES_Suspended)
return;
}
else {
if(event.status == ES_Suspended)
return;
}
}
if(!hookClasses.empty() && event.status == ES_Active) {
SubsystemEvent evt;
evt.obj = event.obj;
evt.subsystem = this;
evt.design = inDesign;
evt.blueprint = nullptr;
evt.data = nullptr;
evt.efficiency = event.efficiency;
evt.partiality = event.partiality;
if(evt.obj != nullptr && evt.obj->type->blueprintOffset != 0)
evt.blueprint = (Blueprint*)(((size_t)evt.obj) + evt.obj->type->blueprintOffset);
for(size_t i = 0, cnt = hookClasses.size(); i < cnt; ++i) {
if(evt.blueprint != nullptr)
evt.data = evt.blueprint->data[dataOffset+i];
auto cl = devices.scripts.server->call(ScriptHookFunctions[EH_Tick]);
cl.setObject(hookClasses[i]);
cl.push(&evt);
cl.push(event.time);
cl.call();
}
}
}
DamageEventStatus Subsystem::damage(DamageEvent& event, const vec2u& position) const {
for(unsigned i = 0; i < type->effects.size(); ++i) {
switch(effects[i].damage(event, position)) {
case DE_Continue:
break;
case DE_SkipHex:
return DE_SkipHex;
case DE_EndDamage:
return DE_EndDamage;
}
}
if(inDesign != nullptr && inDesign->hull->active.valid(position)) {
unsigned hexIndex = (unsigned)inDesign->hexIndex[position];
if(hexIndex < hexEffects.size()) {
auto& hexEffs = hexEffects[hexIndex];
for(unsigned i = 0; i < hexEffs.size(); ++i) {
switch(effects[hexEffs[i]].damage(event, position)) {
case DE_Continue:
break;
case DE_SkipHex:
return DE_SkipHex;
case DE_EndDamage:
return DE_EndDamage;
}
}
}
}
if(!hookClasses.empty()) {
SubsystemEvent evt;
evt.obj = event.obj;
evt.subsystem = this;
evt.design = inDesign;
evt.blueprint = nullptr;
evt.data = nullptr;
if(evt.obj != nullptr && evt.obj->type->blueprintOffset != 0)
evt.blueprint = (Blueprint*)(((size_t)evt.obj) + evt.obj->type->blueprintOffset);
for(size_t i = 0, cnt = hookClasses.size(); i < cnt; ++i) {
if(evt.blueprint != nullptr)
evt.data = evt.blueprint->data[dataOffset+i];
auto cl = devices.scripts.server->call(ScriptHookFunctions[EH_Damage]);
unsigned retVal = 0;
cl.setObject(hookClasses[i]);
cl.push(&evt);
cl.push(&event);
cl.push(&position);
cl.call(retVal);
switch(retVal) {
case DE_Continue:
break;
case DE_SkipHex:
return DE_SkipHex;
case DE_EndDamage:
return DE_EndDamage;
}
}
}
return DE_Continue;
}
bool Subsystem::hasGlobalDamage() const {
if(!hookClasses.empty())
return true;
for(unsigned i = 0; i < effects.size(); ++i) {
if(effects[i].type->hooks[EH_GlobalDamage])
return true;
}
return false;
}
DamageEventStatus Subsystem::globalDamage(DamageEvent& event, vec2u& position, vec2d& endPoint) const {
for(unsigned i = 0; i < effects.size(); ++i) {
switch(effects[i].globalDamage(event, position, endPoint)) {
case DE_Continue:
break;
case DE_SkipHex:
return DE_SkipHex;
case DE_EndDamage:
return DE_EndDamage;
}
}
if(!hookClasses.empty()) {
SubsystemEvent evt;
evt.obj = event.obj;
evt.subsystem = this;
evt.design = inDesign;
evt.blueprint = nullptr;
evt.data = nullptr;
if(evt.obj != nullptr && evt.obj->type->blueprintOffset != 0)
evt.blueprint = (Blueprint*)(((size_t)evt.obj) + evt.obj->type->blueprintOffset);
for(size_t i = 0, cnt = hookClasses.size(); i < cnt; ++i) {
if(evt.blueprint != nullptr)
evt.data = evt.blueprint->data[dataOffset+i];
auto cl = devices.scripts.server->call(ScriptHookFunctions[EH_GlobalDamage]);
unsigned retVal = 0;
cl.setObject(hookClasses[i]);
cl.push(&evt);
cl.push(&event);
cl.push(&position);
cl.push(&endPoint);
cl.call(retVal);
switch(retVal) {
case DE_Continue:
break;
case DE_SkipHex:
return DE_SkipHex;
case DE_EndDamage:
return DE_EndDamage;
}
}
}
return DE_Continue;
}
float* Subsystem::variable(int index) {
if(index < 0)
return 0;
if((size_t)index >= type->variableIndices.size() || type->variableIndices[index] < 0)
return 0;
return &variables[type->variableIndices[index]];
}
const float* Subsystem::variable(int index) const {
//NOTE: Legit
return ((Subsystem*)this)->variable(index);
}
float* Subsystem::hexVariable(int index, int hexIndex) {
if(index < 0 || hexIndex < 0 || (unsigned)hexIndex >= hexes.size())
return 0;
if((size_t)index >= type->hexVariableIndices.size() || type->hexVariableIndices[index] < 0)
return 0;
int base = hexIndex * type->hexVariables.size();
int localIndex = type->hexVariableIndices[index];
return &hexVariables[base + localIndex];
}
const float* Subsystem::hexVariable(int index, int hexIndex) const {
//NOTE: Legit
return ((Subsystem*)this)->hexVariable(index, hexIndex);
}
void Subsystem::markConnected(HexGrid<bool>& grid, vec2u hex) {
if(!grid.valid(hex) || grid[hex])
return;
grid[hex] = true;
for(unsigned i = 0; i < 6; ++i) {
vec2u pos = hex;
if(grid.advance(pos, HexGridAdjacency(i))) {
if(inDesign->grid[pos] == int(this->index))
markConnected(grid, pos);
}
}
}
Subsystem::~Subsystem() {
delete[] variables;
delete[] hexVariables;
}
void Subsystem::init(SaveFile& file) {
int typeID = file.readIdentifier(SI_Subsystem);
type = getSubsystemDef(typeID);
if(type == 0)
throw SaveFileError("Subsystem definition does not exist");
file >> core >> exteriorHexes >> stateOffset >> effectorOffset;
file >> direction;
if(file >= SFV_0008)
file >> dataOffset;
else
dataOffset = 0;
unsigned hexCount = file;
hexes.resize(hexCount);
- file.read(&hexes.front(), hexes.size() * sizeof(vec2u));
+ file.read(hexes.data(), hexes.size() * sizeof(vec2u));
hexEffects.resize(hexCount);
if(file >= SFV_0010) {
for(unsigned i = 0; i < hexCount; ++i) {
unsigned cnt = file;
hexEffects[i].resize(cnt);
for(unsigned j = 0; j < cnt; ++j)
file >> hexEffects[i][j];
}
}
moduleCounts.resize(type->modules.size(), 0);
unsigned moduleCount = file;
modules.resize(moduleCount);
for(unsigned i = 0; i < modules.size(); ++i) {
int modIndex = -1;
if(file >= SFV_0012) {
int index = file.readIdentifier(SI_SubsystemModule);
if(index >= 0 && index < (int)ModulesByID.size())
modIndex = ModulesByID[index]->index;
}
else {
int index = file;
//Sorry, no actual way to do this nicely
if(index == 0) {
auto it = type->moduleIndices.find("Default");
if(it != type->moduleIndices.end())
modIndex = it->second;
}
else if(index == 1) {
auto it = type->moduleIndices.find("Core");
if(it != type->moduleIndices.end())
modIndex = it->second;
}
else {
auto it = type->moduleIndices.find("Bulkhead");
if(it != type->moduleIndices.end())
modIndex = it->second;
}
}
if(modIndex < 0 || modIndex >= (int)type->modules.size())
modIndex = type->defaultModule->index;
modules[i] = type->modules[modIndex];
moduleCounts[modIndex] += 1;
}
if(file >= SFV_0010) {
unsigned cnt = file;
effects.resize(cnt);
for(unsigned i = 0; i < cnt; ++i) {
effects[i].type = getEffectDefinition(file.readIdentifier(SI_Effect));
file.read(effects[i].values, sizeof(double) * EFFECT_MAX_VALUES);
}
}
else {
effects.resize(type->effects.size());
for(unsigned i = 0; i < type->effects.size(); ++i) {
effects[i].type = type->effects[i].type;
file.read(effects[i].values, sizeof(double) * effects[i].type->valueCount);
}
}
effectors = (Effector*)malloc(type->effectors.size() * sizeof(Effector));
for(unsigned i = 0; i < type->effectors.size(); ++i) {
new(&effectors[i]) Effector(*type->effectors[i].type);
Effector& effector = effectors[i];
effector.effectorIndex = i;
effector.load(file);
}
defaults = new BasicType[type->states.size()];
file.read(defaults, sizeof(BasicType) * type->states.size());
variables = new float[type->variables.size()]();
baseVariables = new float[type->variables.size()]();
hexVariables = new float[type->hexVariables.size() * hexCount]();
hexBaseVariables = new float[type->hexVariables.size() * hexCount]();
unsigned cnt = file;
for(unsigned i = 0; i < cnt; ++i) {
int globIndex = file.readIdentifier(SI_SubsystemVar);
int index = -1;
if((size_t)globIndex < type->variableIndices.size() && type->variableIndices[globIndex] >= 0)
index = type->variableIndices[globIndex];
if(index != -1) {
file >> variables[index];
file >> baseVariables[index];
}
else {
file.read<float>();
file.read<float>();
}
}
unsigned varCnt = file;
for(unsigned i = 0; i < varCnt; ++i) {
int globIndex = file.readIdentifier(SI_HexVar);
int index = -1;
if((size_t)globIndex < type->hexVariableIndices.size() && type->hexVariableIndices[globIndex] >= 0)
index = type->hexVariableIndices[globIndex];
for(unsigned j = 0; j < hexCount; ++j) {
int base = j * varCnt;
if(index != -1) {
file >> hexVariables[base + index];
file >> hexBaseVariables[base + index];
}
else {
file.read<float>();
file.read<float>();
}
}
}
}
void Subsystem::postLoad(Design* design) {
lookupData.design = design;
lookupData.system = this;
lookupData.hexIndex = -1;
//Initialize hooks
for(size_t i = 0, cnt = type->hooks.size(); i < cnt; ++i)
addHook(design, type->hooks[i]);
for(size_t i = 0, cnt = modules.size(); i < cnt; ++i) {
auto* module = modules[i];
if(module) {
for(size_t n = 0, ncnt = module->hooks.size(); n < ncnt; ++n)
addHook(design, module->hooks[n]);
}
}
}
Subsystem::Subsystem(SaveFile& file)
: type(nullptr), exteriorHexes(0), hasErrors(false), variables(0), hexVariables(0),
inDesign(nullptr), index(0), defaults(nullptr), effectors(nullptr)
{
init(file);
}
void Subsystem::save(SaveFile& file) const {
file.writeIdentifier(SI_Subsystem, type->index);
file << core << exteriorHexes << stateOffset << effectorOffset;
file << direction;
file << dataOffset;
file << unsigned(hexes.size());
- file.write(&hexes.front(), hexes.size() * sizeof(vec2u));
+ file.write(hexes.data(), hexes.size() * sizeof(vec2u));
for(unsigned i = 0; i < hexes.size(); ++i) {
file << (unsigned)hexEffects[i].size();
for(unsigned j = 0; j < hexEffects[i].size(); ++j)
file << hexEffects[i][j];
}
file << unsigned(modules.size());
for(unsigned i = 0; i < modules.size(); ++i)
file.writeIdentifier(SI_SubsystemModule, modules[i]->umodid);
file << (unsigned)effects.size();
for(unsigned i = 0; i < effects.size(); ++i) {
file.writeIdentifier(SI_Effect, effects[i].type->id);
file.write(effects[i].values, sizeof(double) * EFFECT_MAX_VALUES);
}
for(unsigned i = 0; i < type->effectors.size(); ++i) {
Effector& effector = effectors[i];
effector.save(file);
}
file.write(defaults, sizeof(BasicType) * type->states.size());
unsigned cnt = type->variables.size();
file << cnt;
for(unsigned i = 0; i < cnt; ++i) {
file.writeIdentifier(SI_SubsystemVar, type->variables[i].index);
file << variables[i];
file << baseVariables[i];
}
unsigned varCnt = type->hexVariables.size();
unsigned hexCnt = hexes.size();
file << varCnt;
for(unsigned i = 0; i < varCnt; ++i) {
file.writeIdentifier(SI_HexVar, type->hexVariables[i].index);
for(unsigned j = 0; j < hexCnt; ++j) {
int base = j * varCnt;
file << hexVariables[base + i];
file << hexBaseVariables[base + i];
}
}
}
void Subsystem::writeData(net::Message& msg) const {
msg.writeSmall(type->index);
msg.writeSmall(hexes.size());
for(size_t i = 0, cnt = hexes.size(); i < cnt; ++i) {
msg.writeSmall(hexes[i].x);
msg.writeSmall(hexes[i].y);
msg.writeSmall(modules[i]->index);
}
for(size_t i = 0, cnt = type->modules.size(); i < cnt; ++i)
msg.writeSmall(moduleCounts[i]);
msg.writeSmall(core.x);
msg.writeSmall(core.y);
msg.writeDirection(direction.x, direction.y, direction.z);
msg.writeSmall(exteriorHexes);
msg.writeSmall(stateOffset);
msg.writeSmall(effectorOffset);
for(size_t i = 0, cnt = type->variables.size(); i < cnt; ++i) {
msg << variables[i];
msg << baseVariables[i];
}
unsigned varCnt = type->hexVariables.size() * hexes.size();
for(size_t i = 0; i < varCnt; ++i) {
msg << hexVariables[i];
msg << hexBaseVariables[i];
}
for(size_t i = 0; i < type->states.size(); ++i) {
switch(type->states[i].type) {
default:
case BT_Double:
msg << defaults[i].decimal;
break;
case BT_Int:
msg << defaults[i].integer;
break;
case BT_Bool:
msg << defaults[i].boolean;
break;
}
}
msg << (unsigned)effects.size();
for(size_t i = 0, cnt = effects.size(); i < cnt; ++i)
effects[i].writeData(msg);
for(size_t i = 0, cnt = type->effectors.size(); i < cnt; ++i)
effectors[i].writeData(msg);
}
Subsystem::Subsystem(net::Message& msg) : hasErrors(false), inDesign(nullptr), index(0) {
unsigned sysId = msg.readSmall();
type = getSubsystemDef(sysId);
hexes.resize(msg.readSmall());
modules.resize(hexes.size());
hexEffects.resize(hexes.size());
for(size_t i = 0, cnt = hexes.size(); i < cnt; ++i) {
hexes[i].x = msg.readSmall();
hexes[i].y = msg.readSmall();
modules[i] = type->modules[msg.readSmall()];
}
moduleCounts.resize(type->modules.size());
for(size_t i = 0, cnt = type->modules.size(); i < cnt; ++i)
moduleCounts[i] = msg.readSmall();
core.x = msg.readSmall();
core.y = msg.readSmall();
msg.readDirection(direction.x, direction.y, direction.z);
exteriorHexes = msg.readSmall();
stateOffset = msg.readSmall();
effectorOffset = msg.readSmall();
dataOffset = 0;
variables = new float[type->variables.size()];
baseVariables = new float[type->variables.size()];
for(size_t i = 0, cnt = type->variables.size(); i < cnt; ++i) {
msg >> variables[i];
msg >> baseVariables[i];
}
unsigned varCnt = type->hexVariables.size() * hexes.size();
hexVariables = new float[varCnt];
hexBaseVariables = new float[varCnt];
for(size_t i = 0; i < varCnt; ++i) {
msg >> hexVariables[i];
msg >> hexBaseVariables[i];
}
defaults = new BasicType[type->states.size()];
for(size_t i = 0; i < type->states.size(); ++i) {
defaults[i].type = type->states[i].type;
switch(type->states[i].type) {
default:
case BT_Double:
msg >> defaults[i].decimal;
break;
case BT_Int:
msg >> defaults[i].integer;
break;
case BT_Bool:
msg >> defaults[i].boolean;
break;
}
}
unsigned cnt = 0;
msg >> cnt;
effects.resize(cnt);
for(size_t i = 0; i < cnt; ++i)
effects[i].readData(msg);
effectors = (Effector*)malloc(type->effectors.size() * sizeof(Effector));
for(size_t i = 0, cnt = type->effectors.size(); i < cnt; ++i)
new(&effectors[i]) Effector(msg);
}
diff --git a/source/game/render/bmf_loader.cpp b/source/game/render/bmf_loader.cpp
index b64b171..06ca661 100644
--- a/source/game/render/bmf_loader.cpp
+++ b/source/game/render/bmf_loader.cpp
@@ -1,265 +1,265 @@
#include "bmf_loader.h"
#include <fstream>
#include <map>
#include <string.h>
//BMF Specification (version 0):
//uint32 == "BMF "
//uint32: version number (0 for current version)
//
//uint32: vertex count
//Repeat <vertex count>:
// float x,y,z: vertex[n] coordinates
//
//uint32: normal count
//Repeat <normal count>:
// float x,y,z: normal[n] values
//
//uint32: uv count
//Repeat <uv count>:
// float u,v: uv[n] coordinates
//
//uint32: face count
//Repeat <face count>:
// If <vertex count> <= 0xffff
// uint16 v1,v2,v3: face[n] vertex indices
// Else
// uint32 v1,v2,v3: face[n] vertex indices
//
// If <normal count> > 0
// If <normal count> <= 0xffff
// uint16 n1,n2,n3: face[n] normal indices
// Else
// uint32 n1,n2,n3: face[n] normal indices
//
// If <uv count> > 0
// If <uv count> <= 0xffff
// uint16 u1,u2,u3: face[n] uv indices
// Else
// uint32 u1,u2,u3: face[n] uv indices
//First 4 characters of any Binary Mesh File
const char* bmfHead = "BMF ";
namespace render {
namespace bmf {
struct VertexIndex {
unsigned a, b, c;
VertexIndex() : a(0), b(0), c(0) {}
VertexIndex(unsigned A, unsigned B, unsigned C) : a(A), b(B), c(C) {}
bool operator<(const VertexIndex& other) const {
return memcmp(this, &other, sizeof(unsigned) * 3) < 0;
}
};
}
using namespace bmf;
struct UV {
float u, v;
};
void loadBinaryMesh(const char* filename, Mesh& mesh) {
static_assert(sizeof(vec3f) == 12, "vec3f must be the size of 3 floats");
static_assert(sizeof(UV) == 8, "UV must be the size of 2 floats");
std::vector<vec3f> vertices;
std::vector<vec3f> normals;
std::vector<UV> uvs;
std::ifstream file(filename, std::ios_base::binary | std::ios_base::in);
if(!file.is_open())
return;
char buff[4];
file.read(buff, 4);
if(file.fail() || strncmp(bmfHead, buff, 4) != 0)
return;
unsigned version;
file.read((char*)&version, sizeof(version));
if(file.fail() || version != 0)
return;
//Vertices
unsigned count;
file.read((char*)&count, sizeof(count));
if(file.fail() || count == 0)
return;
vertices.resize(count);
- file.read((char*)&vertices.front(), sizeof(vec3f) * count);
+ file.read((char*)vertices.data(), sizeof(vec3f) * count);
//Normals
file.read((char*)&count, sizeof(count));
if(file.fail())
return;
normals.resize(count);
- file.read((char*)&normals.front(), sizeof(vec3f) * count);
+ file.read((char*)normals.data(), sizeof(vec3f) * count);
//UVs
file.read((char*)&count, sizeof(count));
if(file.fail())
return;
uvs.resize(count);
- file.read((char*)&uvs.front(), sizeof(UV) * count);
+ file.read((char*)uvs.data(), sizeof(UV) * count);
//Faces
file.read((char*)&count, sizeof(count));
if(file.fail())
return;
std::map<VertexIndex,unsigned> vertexMap;
mesh.faces.reserve(count);
mesh.vertices.reserve(count);
for(unsigned i = 0; i < count; ++i) {
Mesh::Face face;
Vertex vertex[3];
VertexIndex vertIndices[3];
//Load vertex position indices
for(unsigned j = 0; j < 3; ++j) {
unsigned index;
if(vertices.size() <= 0xffff) {
unsigned short v;
file.read((char*)&v, sizeof(v));
index = v;
}
else {
file.read((char*)&index, sizeof(index));
}
if(index >= vertices.size())
index = 0;
vertex[j].position = vertices[index];
vertIndices[j].a = index;
}
//Load vertex normal indices
if(!normals.empty()) {
for(unsigned j = 0; j < 3; ++j) {
unsigned index;
if(normals.size() <= 0xffff) {
unsigned short v;
file.read((char*)&v, sizeof(v));
index = v;
}
else {
file.read((char*)&index, sizeof(index));
}
if(index >= normals.size())
index = 0;
vertex[j].normal = normals[index];
vertIndices[j].b = index;
}
}
//Load vertex uv indices
if(!uvs.empty()) {
for(unsigned j = 0; j < 3; ++j) {
unsigned index;
if(uvs.size() <= 0xffff) {
unsigned short v;
file.read((char*)&v, sizeof(v));
index = v;
}
else {
file.read((char*)&index, sizeof(index));
}
if(index >= uvs.size())
index = 0;
vertex[j].u = uvs[index].u;
vertex[j].v = uvs[index].v;
vertIndices[j].c = index;
}
}
if(file.fail())
return;
//Automatically fuse identical vertices and store results in mesh
unsigned indices[3];
for(unsigned j = 0; j < 3; ++j) {
auto previous = vertexMap.find(vertIndices[j]);
if(previous != vertexMap.end()) {
indices[j] = previous->second;
}
else {
indices[j] = (unsigned)mesh.vertices.size();
mesh.vertices.push_back(vertex[j]);
vertexMap[vertIndices[j]] = indices[j];
}
}
face.a = indices[0];
face.b = indices[1];
face.c = indices[2];
mesh.faces.push_back(face);
}
}
bool saveBinaryMesh(const char* filename, Mesh& mesh) {
std::ofstream file(filename, std::ios_base::binary | std::ios_base::out);
if(!file.is_open())
return false;
file.write(bmfHead, 4);
unsigned version = 0;
file.write((char*)&version, sizeof(version));
//TODO: Writes duplicate data unnecessarily
unsigned count;
count = (unsigned)mesh.vertices.size();
file.write((char*)&count, sizeof(count));
for(unsigned i = 0; i < count; ++i)
file.write((char*)&mesh.vertices[i].position, sizeof(vec3f));
count = (unsigned)mesh.vertices.size();
file.write((char*)&count, sizeof(count));
for(unsigned i = 0; i < count; ++i)
file.write((char*)&mesh.vertices[i].normal, sizeof(vec3f));
count = (unsigned)mesh.vertices.size();
file.write((char*)&count, sizeof(count));
for(unsigned i = 0; i < count; ++i) {
file.write((char*)&mesh.vertices[i].u, sizeof(float));
file.write((char*)&mesh.vertices[i].v, sizeof(float));
}
bool shortIndices = mesh.vertices.size() <= 0xffff;
count = (unsigned)mesh.faces.size();
file.write((char*)&count, sizeof(count));
for(unsigned i = 0; i < count; ++i) {
Mesh::Face face = mesh.faces[i];
if(shortIndices) {
unsigned short data[] = {face.a, face.b, face.c, face.a, face.b, face.c, face.a, face.b, face.c};
file.write((char*)data, sizeof(data));
}
else {
unsigned data[] = {face.a, face.b, face.c, face.a, face.b, face.c, face.a, face.b, face.c};
file.write((char*)data, sizeof(data));
}
}
return true;
}
};
diff --git a/source/game/render/gl_vertexBuffer.cpp b/source/game/render/gl_vertexBuffer.cpp
index 3a04192..bfeaf07 100644
--- a/source/game/render/gl_vertexBuffer.cpp
+++ b/source/game/render/gl_vertexBuffer.cpp
@@ -1,316 +1,316 @@
#include "vertexBuffer.h"
#include "compat/gl.h"
#include "main/references.h"
#include "compat/misc.h"
#include <unordered_map>
const unsigned maxVerts = 2048, maxInds = 4096;
unsigned drawnSteps = 0, bufferFlushes = 0;
namespace render {
unsigned vbFloatLimit = 24, vbMaxSteps = 200;
unsigned vbFlushCounts[FC_COUNT] = {0,0,0,0};
extern const RenderMesh* lastRenderedMesh;
extern float* shaderUniforms;
VertexBufferTCV tcv;
threads::Mutex bufferLock;
extern bool useVertexArrays();
inline bool getRequestSize(unsigned& indexCount, unsigned& vertCount, unsigned polygons, unsigned polyType) {
switch(polyType) {
case PT_Lines:
indexCount = polygons * 2;
vertCount = polygons * 2;
return true;
case PT_LineStrip:
indexCount = polygons * 2;
vertCount = polygons + 1;
return true;
case PT_Triangles:
indexCount = polygons * 3;
vertCount = polygons * 3;
return false;
case PT_Quads:
indexCount = polygons * 6;
vertCount = polygons * 4;
return false;
NO_DEFAULT
}
}
void buildIndices(std::vector<unsigned short>& indices, unsigned indexCount, unsigned prevVerts, unsigned polygons, unsigned polyType) {
//Fill out indices according to the polygon type
auto prevInds = indices.size();
indices.resize(prevInds + indexCount);
unsigned short* pIndices = &indices.at(0);
switch(polyType) {
case PT_Lines:
for(unsigned i = 0; i < polygons * 2; ++i)
pIndices[prevInds + i] = prevVerts + i;
break;
case PT_LineStrip:
for(unsigned i = 0; i < polygons; ++i) {
pIndices[prevInds + (i*2)] = prevVerts + i;
pIndices[prevInds + (i*2) + 1] = prevVerts + i + 1;
}
break;
case PT_Triangles:
for(unsigned i = 0; i < polygons * 3; ++i)
pIndices[prevInds + i] = prevVerts + i;
break;
case PT_Quads:
for(unsigned i = 0; i < polygons; ++i) {
auto off = prevInds + (i*6);
auto vOff = prevVerts + (i*4);
pIndices[off] = vOff;
pIndices[off+1] = vOff+2;
pIndices[off+2] = vOff+1;
pIndices[off+3] = vOff;
pIndices[off+4] = vOff+3;
pIndices[off+5] = vOff+2;
}
break;
NO_DEFAULT
}
}
void renderVertexBuffers() {
tcv.draw();
}
VertexBufferTCV::VertexBufferTCV()
: last(0)
{
vertices.reserve(maxVerts);
indices.reserve(maxInds);
steps.reserve(vbMaxSteps);
shaderBufferSize = 40000;
shaderBuffer = new float[shaderBufferSize];
pNextShaderBuffer = shaderBuffer;
}
VertexBufferTCV::~VertexBufferTCV() {
for(auto i = vertBuffer.begin(), end = vertBuffer.end(); i != end; ++i)
glDeleteBuffers(1, &*i);
for(auto i = indBuffer.begin(), end = indBuffer.end(); i != end; ++i)
glDeleteBuffers(1, &*i);
delete[] shaderBuffer;
}
void VertexBufferTCV::flushRetainingLast(FlushCause reason) {
RenderStep step = *last;
float* prevBuffer = pNextShaderBuffer;
//We may have added a step that we decided we can't render, we just pop the empty step
if(step.vertEnd == 0)
steps.pop_back();
draw(reason);
step.vertStart = 0;
step.vertEnd = 0;
step.indexOffset = 0;
if(step.shaderCache) {
memcpy(pNextShaderBuffer, step.shaderCache, (prevBuffer - step.shaderCache) * sizeof(float));
auto count = prevBuffer - step.shaderCache;
step.shaderCache = pNextShaderBuffer;
pNextShaderBuffer += count;
}
steps.push_back(step);
last = &steps.back();
}
void VertexBufferTCV::duplicateLast() {
if(steps.size() != steps.capacity()) {
steps.push_back(*last);
last = &steps.back();
last->indexOffset = (unsigned short)indices.size();
last->vertStart = (unsigned short)vertices.size();
}
else {
flushRetainingLast(FC_StepLimit);
}
}
VertexTCV* VertexBufferTCV::request(unsigned polygons, unsigned polyType) {
unsigned indexCount = 0;
unsigned vertCount = 0;
bool isLines = getRequestSize(indexCount, vertCount, polygons, polyType);
unsigned prevVerts = (unsigned)vertices.size();
if(prevVerts + vertCount > maxVerts
|| indices.size() + indexCount > maxInds)
{
flushRetainingLast(FC_VertexLimit);
last->lines = isLines;
prevVerts = 0;
}
if(last->lines == isLines) {
vertices.resize(prevVerts + vertCount);
buildIndices(indices, indexCount, prevVerts, polygons, polyType);
if(last->vertEnd)
last->vertEnd += vertCount;
else
last->vertEnd = last->vertStart + vertCount - 1;
return &vertices[prevVerts];
}
else if(last->vertEnd != 0) {
duplicateLast();
last->lines = isLines;
vertices.resize(prevVerts + vertCount);
buildIndices(indices, indexCount, prevVerts, polygons, polyType);
last->vertEnd = last->vertStart + vertCount - 1;
return &vertices[prevVerts];
}
else {
last->lines = isLines;
vertices.resize(prevVerts + vertCount);
buildIndices(indices, indexCount, prevVerts, polygons, polyType);
last->vertEnd = last->vertStart + vertCount - 1;
return &vertices[prevVerts];
}
}
void VertexBufferTCV::draw(FlushCause reason) {
if(vertices.empty())
return;
if(vertBuffer.empty()) {
GLuint buffs[24];
glGenBuffers(24, buffs);
for(unsigned i = 0; i < 12; ++i)
vertBuffer.push_back(buffs[i]);
for(unsigned i = 12; i < 24; ++i)
indBuffer.push_back(buffs[i]);
}
{
lastRenderedMesh = reinterpret_cast<const render::RenderMesh*>(this);
if(useVertexArrays())
glBindVertexArray(0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indBuffer.front());
glBindBuffer(GL_ARRAY_BUFFER, vertBuffer.front());
indBuffer.push_back(indBuffer.front());
indBuffer.pop_front();
vertBuffer.push_back(vertBuffer.front());
vertBuffer.pop_front();
if(!useVertexArrays()) {
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glDisableVertexAttribArray(2);
glDisableVertexAttribArray(3);
glDisableVertexAttribArray(4);
glDisableVertexAttribArray(5);
}
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(2, GL_FLOAT, sizeof(VertexTCV), (void*)offsetof(VertexTCV, uv));
glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(VertexTCV), (void*)offsetof(VertexTCV, col));
glVertexPointer(3, GL_FLOAT, sizeof(VertexTCV), (void*)offsetof(VertexTCV, pos));
}
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned short),
&indices[0], GL_STREAM_DRAW);
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(VertexTCV), &vertices[0], GL_STREAM_DRAW);
drawnSteps += (unsigned)steps.size();
bufferFlushes += 1;
vbFlushCounts[reason] += 1;
- RenderStep* pSteps = &steps.front();
+ RenderStep* pSteps = steps.data();
for(unsigned i = 0, cnt = (unsigned)steps.size(); i < cnt; ++i) {
auto& step = pSteps[i];
shaderUniforms = step.shaderCache;
devices.render->switchToRenderState(*step.material);
unsigned count = ((i+1) < cnt ? pSteps[i+1].indexOffset : (unsigned short)indices.size()) - step.indexOffset;
glDrawRangeElements(step.lines ? GL_LINES : GL_TRIANGLES, step.vertStart, step.vertEnd,
count, GL_UNSIGNED_SHORT, (void*)(step.indexOffset * sizeof(unsigned short)));
}
shaderUniforms = 0;
vertices.clear();
indices.clear();
steps.clear();
last = 0;
pNextShaderBuffer = shaderBuffer;
}
VertexBufferTCV* VertexBufferTCV::fetch(const RenderState* mat) {
auto& vbuff = tcv;
if(vbuff.last == 0 || vbuff.last->material != mat) {
if(vbuff.steps.size() == vbMaxSteps)
vbuff.draw(FC_StepLimit);
RenderStep step;
step.material = mat;
step.lines = false;
step.indexOffset = (unsigned short)tcv.indices.size();
step.vertStart = tcv.steps.empty() ? 0 : tcv.steps.back().vertEnd + 1;
step.vertEnd = 0;
if(mat->shader && !mat->shader->constant) {
step.shaderCache = tcv.pNextShaderBuffer;
mat->shader->saveDynamicVars(tcv.pNextShaderBuffer);
tcv.pNextShaderBuffer += mat->shader->dynamicFloats;
}
else {
step.shaderCache = 0;
}
vbuff.steps.push_back(step);
vbuff.last = &vbuff.steps.back();
}
else if(mat->shader && !mat->shader->constant) {
auto* buffer = vbuff.pNextShaderBuffer;
unsigned floats = mat->shader->dynamicFloats;
if(buffer + floats >= vbuff.shaderBuffer + vbuff.shaderBufferSize) {
//Flush the buffer and re-fetch the step
vbuff.draw(FC_ShaderLimit);
return fetch(mat);
}
else {
mat->shader->saveDynamicVars(buffer);
auto* lastStep = vbuff.last;
//When a shader uses a small enough amount of data, check to see if it's duplicated
//If it is, we can batch it into one step
if(floats > vbFloatLimit || memcmp(lastStep->shaderCache, buffer, floats * sizeof(float)) != 0) {
tcv.steps.push_back(*lastStep);
lastStep = &tcv.steps.back();
tcv.last = lastStep;
lastStep->shaderCache = buffer;
lastStep->indexOffset = (unsigned short)tcv.indices.size();
lastStep->vertStart = lastStep->vertEnd + 1;
lastStep->vertEnd = 0;
vbuff.pNextShaderBuffer += floats;
}
}
}
return &vbuff;
}
};

File Metadata

Mime Type
text/x-diff
Expires
Fri, Sep 12, 8:12 PM (1 d, 13 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
42864
Default Alt Text
(158 KB)

Event Timeline