Page Menu
Home
Phabricator (Chris)
Search
Configure Global Search
Log In
Files
F86467
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
158 KB
Referenced Files
None
Subscribers
None
View Options
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
Details
Attached
Mime Type
text/x-diff
Expires
Fri, Sep 12, 8:12 PM (1 d, 15 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
42864
Default Alt Text
(158 KB)
Attached To
Mode
R80 StarRuler2-Source
Attached
Detach File
Event Timeline
Log In to Comment