Open source Star Ruler 2 source code!
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
final class ConVar : ConsoleCommand {
|
||||
string name;
|
||||
double value;
|
||||
|
||||
ConVar(const string& Name, double initial = 0) {
|
||||
name = Name;
|
||||
value = initial;
|
||||
addConsoleCommand(Name, this);
|
||||
}
|
||||
|
||||
void execute(const string& args) {
|
||||
if(args.length != 0)
|
||||
value = toDouble(args);
|
||||
print(name + " = " + value);
|
||||
}
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,383 @@
|
||||
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);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,300 @@
|
||||
string formatEmpireName(Empire@ emp, Empire@ contactCheck = null) {
|
||||
if(emp is null)
|
||||
return "(n/a)";
|
||||
if(emp is defaultEmpire)
|
||||
return locale::EMPIRE_UNIVERSE;
|
||||
if(contactCheck !is null) {
|
||||
if(contactCheck.ContactMask & emp.mask == 0)
|
||||
return "[color=#aaa]???[/color]";
|
||||
}
|
||||
return format("[color=$1]$2[/color]", toString(emp.color), bbescape(emp.name));
|
||||
}
|
||||
|
||||
string formatObject(Object@ obj, bool showOwner = true, bool showIcon = false) {
|
||||
if(obj is null)
|
||||
return "(n/a)";
|
||||
string text;
|
||||
if(showIcon)
|
||||
text += format("[obj_icon=$1/] ", toString(obj.id));
|
||||
if(showOwner) {
|
||||
if(obj.isRegion) {
|
||||
Empire@ primary = obj.visiblePrimaryEmpire;
|
||||
if(primary !is null)
|
||||
text += format("[color=$1]$2[/color]", toString(primary.color), formatObjectName(obj));
|
||||
}
|
||||
else if(obj.owner !is null)
|
||||
text += format("[color=$1]$2[/color]", toString(obj.owner.color), formatObjectName(obj));
|
||||
}
|
||||
else {
|
||||
text += formatObjectName(obj);
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
string formatGameTime(double time, bool dispSeconds = true) {
|
||||
int hours = time / 60.0 / 60.0;
|
||||
int minutes = (time - (hours * 60.0 * 60.0)) / 60.0;
|
||||
int seconds = (time - (hours * 60.0 * 60.0 + minutes * 60.0));
|
||||
|
||||
string text;
|
||||
if(hours < 10) {
|
||||
text += "0";
|
||||
text += toString(hours);
|
||||
}
|
||||
else {
|
||||
text += toString(hours);
|
||||
}
|
||||
text += ":";
|
||||
if(minutes < 10) {
|
||||
text += "0";
|
||||
text += toString(minutes);
|
||||
}
|
||||
else {
|
||||
text += toString(minutes);
|
||||
}
|
||||
|
||||
if(dispSeconds) {
|
||||
text += ":";
|
||||
if(seconds < 10) {
|
||||
text += "0";
|
||||
text += toString(seconds);
|
||||
}
|
||||
else {
|
||||
text += toString(seconds);
|
||||
}
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
string formatTimeStamp(double time, bool dispSeconds = true) {
|
||||
return format("[color=#888]$1[/color]", formatGameTime(time, dispSeconds));
|
||||
}
|
||||
|
||||
string formatInfluenceCost(string option, int influence) {
|
||||
return format(locale::OPTION_INFLUENCE_COST, option, toString(influence));
|
||||
}
|
||||
|
||||
string formatMoney(int money, bool colored = false, bool roundUp = true) {
|
||||
string text;
|
||||
if(money < 0) {
|
||||
if(colored)
|
||||
text += "[color=#f00]";
|
||||
text += "-";
|
||||
}
|
||||
|
||||
text += "§";
|
||||
int am = abs(money);
|
||||
if(am == 0)
|
||||
text += "0";
|
||||
else if(am < 1000)
|
||||
text += toString(am)+"k";
|
||||
else if(am < 1000000)
|
||||
text += standardize(double(am) / 1000.0, true, true)+"M";
|
||||
else
|
||||
text += standardize(double(am) / 1000000.0, true, true)+"B";
|
||||
|
||||
if(money < 0 && colored)
|
||||
text += "[/color]";
|
||||
return text;
|
||||
}
|
||||
|
||||
string formatMoneyChange(int money, bool colored = false) {
|
||||
string text;
|
||||
if(money < 0) {
|
||||
if(colored)
|
||||
text += "[color=#f00]";
|
||||
text += "-";
|
||||
}
|
||||
else {
|
||||
if(colored)
|
||||
text += "[color=#0f0]";
|
||||
text += "+";
|
||||
}
|
||||
|
||||
text += "§";
|
||||
int am = abs(money);
|
||||
if(am == 0)
|
||||
text += "0";
|
||||
else if(am < 1000)
|
||||
text += toString(am)+"k";
|
||||
else if(am < 1000000)
|
||||
text += toString(double(am) / 1000.0, 2)+"M";
|
||||
else
|
||||
text += toString(double(am) / 1000000.0, 1)+"B";
|
||||
|
||||
if(colored)
|
||||
text += "[/color]";
|
||||
return text;
|
||||
}
|
||||
|
||||
string formatMoney(int build, int maintain, bool hideZeroMaintenance = true) {
|
||||
if(maintain == 0 && hideZeroMaintenance)
|
||||
return formatMoney(build);
|
||||
return formatMoney(build)+" / "+formatMoney(maintain);
|
||||
}
|
||||
|
||||
string formatTimeRate(double time, double atRate, bool tenthPrecision = false) {
|
||||
if(atRate == 0)
|
||||
return locale::NEVER;
|
||||
return formatTime(time / atRate, tenthPrecision);
|
||||
}
|
||||
|
||||
string formatTime(double time, bool tenthPrecision = false) {
|
||||
if(time == INFINITY) {
|
||||
return locale::NEVER;
|
||||
}
|
||||
else if(time <= 0) {
|
||||
return "";
|
||||
}
|
||||
else if(time > 60) {
|
||||
int mins = floor(time / 60.0);
|
||||
int secs = time % 60;
|
||||
|
||||
if(secs == 0)
|
||||
return format(locale::TIME_M, toString(mins));
|
||||
else
|
||||
return format(locale::TIME_MS, toString(mins), toString(secs));
|
||||
}
|
||||
else if(tenthPrecision) {
|
||||
return format(locale::TIME_S,
|
||||
toString(time, time < 10 ? 1 : 0));
|
||||
}
|
||||
else {
|
||||
return format(locale::TIME_S,
|
||||
toString(time, 0));
|
||||
}
|
||||
}
|
||||
|
||||
string formatShortTime(double time) {
|
||||
if(time == INFINITY || time <= 0)
|
||||
return format(locale::TIME_MS_SHORT, "--", "--");
|
||||
double mins = floor(time / 60.0);
|
||||
double secs = time % 60;
|
||||
if(secs < 10.0)
|
||||
return format(locale::TIME_MS_SHORT, toString(mins, 0), "0"+toString(floor(secs), 0));
|
||||
else
|
||||
return format(locale::TIME_MS_SHORT, toString(mins, 0), toString(floor(secs), 0));
|
||||
}
|
||||
|
||||
string formatEstTime(double time) {
|
||||
if(time == INFINITY) {
|
||||
return "-";
|
||||
}
|
||||
else if(time <= 0) {
|
||||
return "";
|
||||
}
|
||||
else if(time > 60) {
|
||||
return format(locale::TIME_M, toString(round(time / 60.0), 0));
|
||||
}
|
||||
else {
|
||||
return format(locale::TIME_S, toString(time, 0));
|
||||
}
|
||||
}
|
||||
|
||||
string formatMinuteRate(double rate) {
|
||||
rate *= 60.0;
|
||||
return standardize(rate, true)+locale::PER_MINUTE;
|
||||
}
|
||||
|
||||
string formatMinuteRate(double rate, const string& unit) {
|
||||
rate *= 60.0;
|
||||
return standardize(rate, true)+unit+locale::PER_MINUTE;
|
||||
}
|
||||
|
||||
string formatIncomeRate(double rate, bool perMinute = false) {
|
||||
string unit = locale::PER_SECOND;
|
||||
if(perMinute) {
|
||||
rate *= 60.0;
|
||||
unit = locale::PER_MINUTE;
|
||||
}
|
||||
if(rate < 0)
|
||||
return format("[color=#f88]$1$2[/color]", standardize(rate, true), unit);
|
||||
else if(rate == 0)
|
||||
return format("[color=#bbb]±0$1[/color]", unit);
|
||||
else
|
||||
return format("[color=#8f8]+$1$2[/color]", standardize(rate, true), unit);
|
||||
}
|
||||
|
||||
string formatRate(double rate) {
|
||||
if(rate < 0.2) {
|
||||
rate *= 60.0;
|
||||
return standardize(rate, true)+locale::PER_MINUTE;
|
||||
}
|
||||
else {
|
||||
return standardize(rate, true)+locale::PER_SECOND;
|
||||
}
|
||||
}
|
||||
|
||||
string formatRate(double rate, const string& unit) {
|
||||
if(rate < 0.2) {
|
||||
rate *= 60.0;
|
||||
return standardize(rate, true)+unit+locale::PER_MINUTE;
|
||||
}
|
||||
else {
|
||||
return standardize(rate, true)+unit+locale::PER_SECOND;
|
||||
}
|
||||
}
|
||||
|
||||
string formatEffect(const string& effect, const string& magnitude) {
|
||||
return format("$1\n[right][b]$2[/b][/right]", effect, magnitude);
|
||||
}
|
||||
|
||||
string formatPosEffect(const string& effect, const string& magnitude) {
|
||||
return format("$1\n[right][b][color=#0f0]$2[/color][/b][/right]", effect, magnitude);
|
||||
}
|
||||
|
||||
string formatNegEffect(const string& effect, const string& magnitude) {
|
||||
return format("$1\n[right][b][color=#f00]$2[/color][/b][/right]", effect, magnitude);
|
||||
}
|
||||
|
||||
string formatMagEffect(const string& effect, double amt) {
|
||||
string magnitude;
|
||||
if(amt < 0.0)
|
||||
magnitude = "[color=#f00]-"+standardize(amt, true)+"[/color]";
|
||||
else
|
||||
magnitude = "[color=#0f0]+"+standardize(amt, true)+"[/color]";
|
||||
return formatEffect(effect, magnitude);
|
||||
}
|
||||
|
||||
string formatPctEffect(const string& effect, float pct) {
|
||||
string magnitude;
|
||||
if(pct < 0.f)
|
||||
magnitude = "[color=#f00]-"+toString(pct*-100.f, 0)+"%[/color]";
|
||||
else
|
||||
magnitude = "[color=#0f0]+"+toString(pct*100.f, 0)+"%[/color]";
|
||||
|
||||
return formatEffect(effect, magnitude);
|
||||
}
|
||||
|
||||
string formatPctEffect(const string& effect, float pct, const string& mod) {
|
||||
string magnitude;
|
||||
if(pct < 0.f)
|
||||
magnitude = mod+" [color=#f00]-"+toString(pct*-100.f, 0)+"%[/color]";
|
||||
else
|
||||
magnitude = mod+" [color=#0f0]+"+toString(pct*100.f, 0)+"%[/color]";
|
||||
|
||||
return formatEffect(effect, magnitude);
|
||||
}
|
||||
|
||||
string formatObjectName(Object& obj) {
|
||||
if(obj.isShip)
|
||||
return formatShipName(cast<Ship>(obj));
|
||||
else
|
||||
return obj.name;
|
||||
}
|
||||
|
||||
string formatShipName(Ship& ship) {
|
||||
if(ship.named)
|
||||
return format("$1 ($2)", ship.name, standardize(ship.blueprint.design.size, true));
|
||||
return formatShipName(ship.blueprint.design);
|
||||
}
|
||||
|
||||
string formatShipName(const Design@ dsg) {
|
||||
if(dsg is null)
|
||||
return "-";
|
||||
string name = dsg.name;
|
||||
if(dsg.next() !is null)
|
||||
name += format(locale::REV_SPEC, toString(dsg.revision));
|
||||
name = format("$1 ($2)", name, standardize(dsg.size, true));
|
||||
return name;
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
class Poisson2D {
|
||||
array<vec2d> points;
|
||||
array<vec2d> grid;
|
||||
array<vec2d> output;
|
||||
vec2u gridSize;
|
||||
double cell;
|
||||
double circleRadius = INFINITY;
|
||||
|
||||
//TODO: Use a dequeue (list?)
|
||||
array<vec2d> queue;
|
||||
|
||||
Poisson2D() {
|
||||
}
|
||||
|
||||
Poisson2D(double width, double height, double distance, uint order = 30) {
|
||||
generate(width, height, distance, order);
|
||||
}
|
||||
|
||||
void generate(double width, double height, double distance, uint order = 30) {
|
||||
cell = distance / sqrt(2.0);
|
||||
gridSize = vec2u(ceil(width/cell), ceil(height)/cell);
|
||||
grid.length = gridSize.x * gridSize.y;
|
||||
for(uint i = 0, cnt = grid.length; i < cnt; ++i)
|
||||
grid[i] = vec2d(INFINITY, INFINITY);
|
||||
queue.reserve(order * gridSize.x);
|
||||
|
||||
//Generate a first point
|
||||
vec2d start = vec2d(randomd(width*0.2, width*0.8), randomd(height*0.2, height*0.8));
|
||||
queue.insertLast(start);
|
||||
points.insertLast(start);
|
||||
grid[gridIndex(start)] = start;
|
||||
|
||||
//Process the grid
|
||||
while(queue.length != 0) {
|
||||
uint index = randomi(0, queue.length-1);
|
||||
vec2d point = queue[index];
|
||||
queue.removeAt(index);
|
||||
|
||||
for(uint n = 0; n < order; ++n) {
|
||||
vec2d other = point + random2d(distance, distance*2.0);
|
||||
|
||||
if(!validPosition(other))
|
||||
continue;
|
||||
if(circleRadius != INFINITY && other.distanceToSQ(vec2d(width/2, height/2)) > circleRadius * circleRadius)
|
||||
continue;
|
||||
if(!checkDistance(other, distance * distance))
|
||||
continue;
|
||||
|
||||
queue.insertLast(other);
|
||||
grid[gridIndex(other)] = other;
|
||||
points.insertLast(other);
|
||||
}
|
||||
}
|
||||
|
||||
//Shuffle the points
|
||||
for(int i = points.length - 1; i >= 0; --i) {
|
||||
int swapIndex = randomi(0, i);
|
||||
|
||||
auto first = points[i];
|
||||
auto second = points[swapIndex];
|
||||
|
||||
points[i] = second;
|
||||
points[swapIndex] = first;
|
||||
}
|
||||
}
|
||||
|
||||
bool checkDistance(const vec2d& pos, double distSQ) {
|
||||
vec2u coords = gridCoords(pos);
|
||||
for(int x = -2; x <= 2; ++x) {
|
||||
for(int y = -2; y <= 2; ++y) {
|
||||
if(pointDistance(pos, vec2u(vec2i(coords) + vec2i(x, y))) < distSQ)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
double pointDistance(const vec2d& pos, const vec2u& coords) {
|
||||
if(coords.x >= gridSize.x || coords.y >= gridSize.y)
|
||||
return INFINITY;
|
||||
uint index = coords.x + coords.y * gridSize.x;
|
||||
return grid[index].distanceToSQ(pos);
|
||||
}
|
||||
|
||||
bool validPosition(const vec2d& pos) {
|
||||
if(pos.x < 0 || pos.y < 0)
|
||||
return false;
|
||||
vec2u coords = gridCoords(pos);
|
||||
return coords.x < gridSize.x && coords.y < gridSize.y;
|
||||
}
|
||||
|
||||
vec2u gridCoords(const vec2d& pos) {
|
||||
return vec2u(pos.x / cell, pos.y / cell);
|
||||
}
|
||||
|
||||
uint gridIndex(const vec2d& pos) {
|
||||
return int(pos.x / cell) + int(pos.y / cell) * gridSize.x;
|
||||
}
|
||||
|
||||
uint gridIndex(const vec2u& coords) {
|
||||
return coords.x + coords.y* gridSize.x;
|
||||
}
|
||||
|
||||
uint get_length() {
|
||||
return points.length;
|
||||
}
|
||||
|
||||
vec2d opIndex(uint index) {
|
||||
return points[index];
|
||||
}
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user