384 lines
9.9 KiB
ActionScript
384 lines
9.9 KiB
ActionScript
import designs;
|
|
import design_settings;
|
|
const int DESIGN_SERIALIZE_VERSION = 1;
|
|
|
|
JSONTree@ serialize_design(const Design@ dsg, const DesignClass@ cls = null) {
|
|
JSONTree tree;
|
|
JSONNode@ root = tree.root.makeObject();
|
|
|
|
root["__VERSION__"] = DESIGN_SERIALIZE_VERSION;
|
|
root["name"] = dsg.name;
|
|
root["size"] = int(dsg.size);
|
|
root["hull"] = dsg.hull.baseHull.ident;
|
|
root["type"] = getHullTypeTag(dsg.hull);
|
|
root["gridWidth"] = dsg.hull.active.width;
|
|
root["gridHeight"] = dsg.hull.active.height;
|
|
if(dsg.forceHull)
|
|
root["forceHull"].setBool(true);
|
|
if(cls !is null)
|
|
root["class"] = cls.name;
|
|
else if(dsg.cls !is null)
|
|
root["class"] = dsg.cls.name;
|
|
|
|
JSONNode@ sysList = root["subsystems"].makeArray();
|
|
|
|
auto@ settings = cast<const DesignSettings>(dsg.settings);
|
|
if(settings !is null) {
|
|
JSONNode@ node = root["settings"].makeObject();
|
|
settings.write(node);
|
|
}
|
|
|
|
uint sysCnt = dsg.subsystemCount;
|
|
for(uint i = 0; i < sysCnt; ++i) {
|
|
const Subsystem@ sys = dsg.subsystems[i];
|
|
if(sys.type.isHull)
|
|
continue;
|
|
if(sys.type.isApplied)
|
|
continue;
|
|
|
|
JSONNode@ node = sysList.pushBack().makeObject();
|
|
node["type"] = sys.type.id;
|
|
|
|
if(sys.direction != vec3d_front()) {
|
|
JSONNode@ dir = node["direction"].makeArray();
|
|
dir.pushBack() = sys.direction.x;
|
|
dir.pushBack() = sys.direction.y;
|
|
dir.pushBack() = sys.direction.z;
|
|
}
|
|
|
|
JSONNode@ hexes = node["hexes"].makeArray();
|
|
uint hexCnt = sys.hexCount;
|
|
for(uint j = 0; j < hexCnt; ++j) {
|
|
vec2u hex = sys.hexagon(j);
|
|
JSONNode@ coord = hexes.pushBack().makeArray();
|
|
coord.pushBack() = hex.x;
|
|
coord.pushBack() = hex.y;
|
|
|
|
if(sys.module(j) !is sys.type.defaultModule)
|
|
coord.pushBack() = sys.module(j).id;
|
|
}
|
|
}
|
|
|
|
@sysList = null;
|
|
for(uint i = 0; i < sysCnt; ++i) {
|
|
const Subsystem@ sys = dsg.subsystems[i];
|
|
if(!sys.type.isApplied)
|
|
continue;
|
|
|
|
if(sysList is null)
|
|
@sysList = root["appliedSubsystems"].makeArray();
|
|
sysList.pushBack() = sys.type.id;
|
|
}
|
|
|
|
return tree;
|
|
}
|
|
|
|
void write_design(const Design@ dsg, const string& filename, const DesignClass@ cls = null, bool pretty = true) {
|
|
serialize_design(dsg, cls).writeFile(filename, pretty);
|
|
}
|
|
|
|
bool unserialize_design(JSONTree@ tree, DesignDescriptor& desc) {
|
|
JSONNode@ root = tree.root;
|
|
if(!root.isObject() || !root["name"].isString() || !root["size"].isInt() || !root["hull"].isString())
|
|
return false;
|
|
desc.name = root["name"].getString();
|
|
desc.size = root["size"].getInt();
|
|
|
|
if(desc.size < 1)
|
|
return false;
|
|
|
|
desc.forceHull = false;
|
|
auto@ force = root.findMember("forceHull");
|
|
if(force !is null && force.isBool())
|
|
desc.forceHull = force.getBool();
|
|
|
|
JSONNode@ cls = root.findMember("class");
|
|
if(cls !is null && cls.isString())
|
|
desc.className = cls.getString();
|
|
|
|
desc.hullName = root["hull"].getString();
|
|
@desc.hull = getHullDefinition(desc.hullName);
|
|
|
|
JSONNode@ sysList = root["subsystems"];
|
|
if(!sysList.isArray())
|
|
return false;
|
|
uint sysCnt = sysList.size();
|
|
for(uint i = 0; i < sysCnt; ++i) {
|
|
JSONNode@ node = sysList[i];
|
|
if(!node["type"].isString())
|
|
return false;
|
|
|
|
const SubsystemDef@ def = getSubsystemDef(node["type"].getString());
|
|
if(def is null)
|
|
continue;
|
|
desc.addSystem(def);
|
|
|
|
JSONNode@ dir = node["direction"];
|
|
if(dir.isArray() && dir.size() == 3)
|
|
desc.setDirection(vec3d(dir[0].getNumber(), dir[1].getNumber(), dir[2].getNumber()));
|
|
|
|
JSONNode@ hexes = node["hexes"];
|
|
if(!hexes.isArray())
|
|
return false;
|
|
uint hexCnt = hexes.size();
|
|
for(uint j = 0; j < hexCnt; ++j) {
|
|
JSONNode@ coord = hexes[j];
|
|
if(!coord.isArray() || coord.size() < 2)
|
|
return false;
|
|
vec2u pos(coord[0].getUint(), coord[1].getUint());
|
|
|
|
if(coord.size() == 2) {
|
|
desc.addHex(pos);
|
|
}
|
|
else {
|
|
const ModuleDef@ mod = def.module(coord[2].getString());
|
|
if(mod !is null)
|
|
desc.addHex(pos, mod);
|
|
else
|
|
desc.addHex(pos);
|
|
}
|
|
}
|
|
}
|
|
|
|
@sysList = root["appliedSubsystems"];
|
|
if(sysList !is null && sysList.isArray()) {
|
|
uint sysCnt = sysList.size();
|
|
for(uint i = 0; i < sysCnt; ++i) {
|
|
JSONNode@ node = sysList[i];
|
|
if(node is null || !node.isString())
|
|
continue;
|
|
|
|
const SubsystemDef@ def = getSubsystemDef(node.getString());
|
|
if(def !is null)
|
|
desc.applySubsystem(def);
|
|
}
|
|
}
|
|
|
|
if(desc.hull is null) {
|
|
JSONNode@ type = root.findMember("type");
|
|
if(type !is null && type.isString())
|
|
@desc.hull = getBestHull(desc, type.getString());
|
|
if(desc.hull is null)
|
|
return false;
|
|
}
|
|
|
|
JSONNode@ stNode = root.findMember("settings");
|
|
if(stNode !is null && stNode.isObject()) {
|
|
DesignSettings settings;
|
|
settings.read(stNode);
|
|
@desc.settings = settings;
|
|
}
|
|
else {
|
|
@desc.settings = null;
|
|
}
|
|
|
|
JSONNode@ w = root.findMember("gridWidth");
|
|
JSONNode@ h = root.findMember("gridHeight");
|
|
if(w !is null && w.isUint() && h !is null && h.isUint())
|
|
desc.gridSize = vec2u(w.getUint(), h.getUint());
|
|
else
|
|
desc.gridSize = getDesignGridSize(desc.hull, desc.size);
|
|
return true;
|
|
}
|
|
|
|
int upload_design(const Design@ design, const string& description = "", bool waitId = false) {
|
|
#section client
|
|
WebData dat;
|
|
dat.addPost("name", design.name);
|
|
dat.addPost("size", toString(design.size, 0));
|
|
dat.addPost("author", settings::sNickname);
|
|
dat.addPost("description", description);
|
|
dat.addPost("color", toString(design.color));
|
|
dat.addPost("data", serialize_design(design).toString());
|
|
|
|
webAPICall("designs/submit", dat);
|
|
if(waitId) {
|
|
while(!dat.completed)
|
|
sleep(100);
|
|
return toInt(dat.result);
|
|
}
|
|
#section all
|
|
return -1;
|
|
}
|
|
|
|
bool read_design(const string& filename, DesignDescriptor& desc) {
|
|
JSONTree tree;
|
|
tree.readFile(filename);
|
|
return unserialize_design(tree, desc);
|
|
}
|
|
|
|
string uniqueDesignName(string name, Empire@ emp) {
|
|
int num = 1;
|
|
string oldName = name;
|
|
int pos = oldName.findLast(" Mk");
|
|
if(pos != -1)
|
|
oldName = oldName.substr(0, pos);
|
|
|
|
while(emp.getDesign(name) !is null) {
|
|
string newName = oldName;
|
|
newName += " Mk";
|
|
appendRoman(num, newName);
|
|
|
|
name = newName;
|
|
++num;
|
|
}
|
|
return name;
|
|
}
|
|
|
|
string getHullTypeTag(const Hull@ hull) {
|
|
if(hull is null)
|
|
return "";
|
|
if(hull.hasTag("Flagship"))
|
|
return "Flagship";
|
|
if(hull.hasTag("Support"))
|
|
return "Support";
|
|
if(hull.hasTag("Satellite"))
|
|
return "Satellite";
|
|
if(hull.hasTag("Station"))
|
|
return "Station";
|
|
return "";
|
|
}
|
|
|
|
const Hull@ getBestHull(DesignDescriptor& desc, const string& hullTag, Empire@ emp = playerEmpire) {
|
|
const Shipset@ shipset;
|
|
if(emp is null || emp.shipset is null)
|
|
@shipset = getShipset("Volkur");
|
|
else
|
|
@shipset = emp.shipset;
|
|
if(shipset is null)
|
|
return null;
|
|
const Hull@ bestHull;
|
|
double bestHullDist = INFINITY;
|
|
for(uint i = 0, cnt = shipset.hullCount; i < cnt; ++i) {
|
|
const Hull@ hull = shipset.hulls[i];
|
|
|
|
//Check if it matches the tag
|
|
if(!hull.hasTag(hullTag))
|
|
continue;
|
|
if(bestHull is null)
|
|
@bestHull = hull;
|
|
|
|
//Make sure we can use this hull
|
|
if(hull.minSize >= 0 && hull.minSize > desc.size)
|
|
continue;
|
|
if(hull.maxSize >= 0 && hull.maxSize < desc.size)
|
|
continue;
|
|
|
|
//Check distance
|
|
double d = hull.getMatchDistance(desc);
|
|
if(hull is desc.hull)
|
|
d -= 0.1;
|
|
if(d < bestHullDist) {
|
|
bestHullDist = d;
|
|
@bestHull = hull;
|
|
}
|
|
}
|
|
return bestHull;
|
|
}
|
|
|
|
void describeDesign(const Design@ orig, DesignDescriptor& desc) {
|
|
desc.name = orig.name;
|
|
desc.className = orig.cls.name;
|
|
desc.gridSize = vec2u(orig.hull.gridSize);
|
|
desc.size = orig.size;
|
|
@desc.hull = orig.hull;
|
|
@desc.owner = orig.owner;
|
|
|
|
uint sysCnt = orig.subsystemCount;
|
|
for(uint i = 0; i < sysCnt; ++i) {
|
|
const Subsystem@ sys = orig.subsystems[i];
|
|
if(sys.type.isHull)
|
|
continue;
|
|
if(sys.type.isApplied) {
|
|
desc.applySubsystem(sys.type);
|
|
continue;
|
|
}
|
|
|
|
desc.addSystem(sys.type);
|
|
desc.setDirection(sys.direction);
|
|
uint hexCnt = sys.hexCount;
|
|
for(uint j = 0; j < hexCnt; ++j) {
|
|
vec2u hex = sys.hexagon(j);
|
|
desc.addHex(hex, sys.module(j));
|
|
}
|
|
}
|
|
}
|
|
|
|
void resizeDesign(const Design@ orig, int newSize, DesignDescriptor& desc) {
|
|
describeDesign(orig, desc);
|
|
desc.size = newSize;
|
|
}
|
|
|
|
class DesignSet {
|
|
DesignDescriptor[] designs;
|
|
bool limitShipset = false;
|
|
bool softLimitRetry = false;
|
|
bool log = false;
|
|
|
|
void readDirectory(const string& directory) {
|
|
FileList list(directory, "*.design", true);
|
|
uint cnt = list.length;
|
|
designs.resize(cnt);
|
|
for(uint i = 0; i < cnt; ++i)
|
|
read_design(list.path[i], designs[i]);
|
|
}
|
|
|
|
void createFor(Empire@ emp, bool overrideLimit = false) const {
|
|
bool foundAny = false;
|
|
for(uint i = 0, cnt = designs.length; i < cnt; ++i) {
|
|
DesignDescriptor desc = designs[i];
|
|
@desc.owner = emp;
|
|
string hullName = format(desc.hullName, emp.shipset.ident);
|
|
@desc.hull = getHullDefinition(hullName);
|
|
if(desc.hull is null) {
|
|
hullName = format("$1FlagTiny", emp.shipset.ident);
|
|
@desc.hull = getHullDefinition(hullName);
|
|
|
|
if(desc.hull is null) {
|
|
if(!limitShipset || overrideLimit) {
|
|
hullName = format(desc.hullName, "Volkur");
|
|
@desc.hull = getHullDefinition(hullName);
|
|
if(desc.hull is null) {
|
|
if(desc.hull is null)
|
|
@desc.hull = getHullDefinition("VolkurFlagTiny");
|
|
}
|
|
}
|
|
if(desc.hull is null)
|
|
continue;
|
|
}
|
|
}
|
|
if(limitShipset && !overrideLimit) {
|
|
if(emp.shipset is null || !emp.shipset.hasHull(desc.hull))
|
|
continue;
|
|
}
|
|
else {
|
|
if(emp.shipset !is null && !emp.shipset.hasHull(desc.hull))
|
|
@desc.hull = getBestHull(desc, getHullTypeTag(desc.hull), emp);
|
|
}
|
|
if(desc.hull is null)
|
|
continue;
|
|
if(desc.className.length == 0)
|
|
desc.className = "Default";
|
|
|
|
const Design@ dsg = makeDesign(desc);
|
|
if(log && dsg !is null && dsg.hasFatalErrors()) {
|
|
print(emp.name+" Importing "+desc.name+":");
|
|
for(uint i = 0, cnt = dsg.errorCount; i < cnt; ++i)
|
|
print(" "+dsg.errors[i].text);
|
|
}
|
|
if(dsg is null || dsg.hasFatalErrors())
|
|
continue;
|
|
if(emp.getDesign(dsg.name) !is null)
|
|
continue;
|
|
if(desc.settings !is null)
|
|
dsg.setSettings(desc.settings);
|
|
|
|
const DesignClass@ cls = emp.getDesignClass(desc.className);
|
|
emp.addDesign(cls, dsg);
|
|
foundAny = true;
|
|
}
|
|
if(softLimitRetry && !foundAny && !overrideLimit)
|
|
createFor(emp, overrideLimit=true);
|
|
}
|
|
};
|