#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 #include #include #include 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 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 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> sysQueue; foreach(it, subsystems) sysQueue.push(std::pair(-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); 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(); } //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()) { 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()); 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(); }