import saving; import hooks; import util.formatting; from resources import MoneyType; #section server from construction.Constructible import Constructible; #section all export ConstructionType, Construction; export getConstructionTypeCount, getConstructionType; export Targets, TargetType; tidy class ConstructionType { uint id = 0; string ident; string name; string description; string category; Sprite icon; Targets targets; int buildCost = 0; int maintainCost = 0; double laborCost = 0; double timeCost = 0; bool alwaysBorrowable = false; bool inContext = false; array hooks; string formatTooltip(Object& obj) const { string tt = format("[font=Medium]$1[/font]\n$2\n", name, description); int build = getBuildCost(obj); int maint = getMaintainCost(obj); if(build != 0 || maint != 0) tt += format("[nl/][vspace=6/][img=ResourceIcon::0;22][b][color=#d1cb6a]$1:[/color][/b][offset=120]$2[/offset][/img]", locale::COST, formatMoney(build, maint)); double labor = getLaborCost(obj); if(labor != 0) tt += format("[nl/][vspace=6/][img=ResourceIcon::6;22][b][color=#b1b4b6]$1:[/color][/b][offset=120]$2[/offset][/img]", locale::RESOURCE_LABOR, standardize(labor, true)); double time = getTimeCost(obj); if(time != 0) tt += format("[nl/][vspace=6/][img=ContextIcons::1;22][b][color=#b1b4b6]$1:[/color][/b][offset=120]$2[/offset][/img]", locale::BUILD_TIME, formatTime(time)); string vname, vvalue; Sprite vicon; Color color; for(uint i = 0, cnt = hooks.length; i < cnt; ++i) { if(hooks[i].getVariable(obj, this, vicon, vname, vvalue, color)) { tt += format("[nl/][vspace=6/][img=$1;22][b][color=$4]$2:[/color][/b] [offset=110][hspace=10/]$3[/offset][/img]", getSpriteDesc(vicon), vname, vvalue, toString(color)); color = colors::White; } } return tt; } string formatCosts(Object& obj, const Targets@ targs = null) const { string costs; int build = getBuildCost(obj, targs); int maint = getMaintainCost(obj, targs); if(build != 0 || maint != 0) costs += formatMoney(build, maint); double labor = getLaborCost(obj, targs); if(labor != 0) { if(costs.length != 0) costs += ", "; costs += standardize(labor, true); costs += " "+locale::RESOURCE_LABOR; } string value; for(uint i = 0, cnt = hooks.length; i < cnt; ++i) { if(hooks[i].formatCost(obj, this, targs, value)) { if(costs.length != 0) costs += ", "; costs += value; value = ""; } } return costs; } int getBuildCost(Object& obj, const Targets@ targs = null) const { int cost = buildCost; for(uint i = 0, cnt = hooks.length; i < cnt; ++i) hooks[i].getBuildCost(obj, this, targs, cost); return cost; } int getMaintainCost(Object& obj, const Targets@ targs = null) const { int cost = maintainCost; for(uint i = 0, cnt = hooks.length; i < cnt; ++i) hooks[i].getMaintainCost(obj, this, targs, cost); return cost; } double getLaborCost(Object& obj, const Targets@ targs = null) const { double cost = laborCost; for(uint i = 0, cnt = hooks.length; i < cnt; ++i) hooks[i].getLaborCost(obj, this, targs, cost); return cost; } double getTimeCost(Object& obj, const Targets@ targs = null) const { double cost = timeCost; return cost; } bool checkTargets(Object& obj, const Targets@ check) const { if(check is null) { if(targets.length == 0) return true; else return false; } if(check.targets.length != targets.length) return false; for(uint i = 0, cnt = check.targets.length; i < cnt; ++i) { if(!isValidTarget(obj, i, check.targets[i])) return false; } return true; } bool isValidTarget(Object& obj, uint index, const Target@ targ) const { if(index >= targets.length) return false; if(targ.type != targets[index].type) return false; if(!targ.filled) return false; if(targets[index].filled && targets[index] != targ) return false; for(uint i = 0, cnt = hooks.length; i < cnt; ++i) { if(!hooks[i].isValidTarget(obj, this, index, targ)) return false; } return true; } bool getTargetError(Object& obj, uint index, const Target@ targ, string& str) const { if(index >= targets.length) return false; if(targ.type != targets[index].type) return false; if(!targ.filled) return false; if(targets[index].filled && targets[index] != targ) return false; for(uint i = 0, cnt = hooks.length; i < cnt; ++i) { if(!hooks[i].isValidTarget(obj, this, index, targ)) { str = hooks[i].getFailReason(obj, this, index, targ); return true; } } return false; } string getTargetError(Object& obj, const Targets@ check) const { string str; for(uint i = 0, cnt = check.targets.length; i < cnt; ++i) { if(getTargetError(obj, i, check.targets[i], str)) return str; } return str; } bool canBuild(Object& obj, const Targets@ targs = null, bool ignoreCost = false) const { if(targs !is null && !checkTargets(obj, targs)) return false; for(uint i = 0, cnt = hooks.length; i < cnt; ++i) { if(!hooks[i].canBuild(obj, this, targs, ignoreCost)) return false; } if(!ignoreCost && !alwaysBorrowable) { int build = getBuildCost(obj, targs); if(!obj.owner.canPay(build)) return false; } return true; } }; interface IConstructionHook { #section server void start(Construction@ cons, Constructible@ qitem, any@ data) const; void cancel(Construction@ cons, Constructible@ qitem, any@ data) const; void finish(Construction@ cons, Constructible@ qitem, any@ data) const; void tick(Construction@ cons, Constructible@ qitem, any@ data, double time) const; #section all void save(Construction@ cons, any@ data, SaveFile& file) const; void load(Construction@ cons, any@ data, SaveFile& file) const; string getFailReason(Object& obj, const ConstructionType@ cons, uint index, const Target@ targ) const; bool isValidTarget(Object& obj, const ConstructionType@ cons, uint index, const Target@ targ) const; bool canBuild(Object& obj, const ConstructionType@ cons, const Targets@ targs, bool ignoreCost) const; void getBuildCost(Object& obj, const ConstructionType@ cons, const Targets@ targs, int& cost) const; void getMaintainCost(Object& obj, const ConstructionType@ cons, const Targets@ targs, int& cost) const; void getLaborCost(Object& obj, const ConstructionType@ cons, const Targets@ targs, double& cost) const; bool consume(Construction@ cons, any@ data, const Targets@ targs) const; void reverse(Construction@ cons, any@ data, const Targets@ targs, bool cancel = false) const; bool getVariable(Object& obj, const ConstructionType@ cons, Sprite& sprt, string& name, string& value, Color& color) const; bool formatCost(Object& obj, const ConstructionType@ cons, const Targets@ targs, string& value) const; bool getCost(Object& obj, const ConstructionType@ cons, const Targets@ targs, string& value, Sprite& icon) const; } class ConstructionHook : Hook, IConstructionHook { #section server void start(Construction@ cons, Constructible@ qitem, any@ data) const {} void cancel(Construction@ cons, Constructible@ qitem, any@ data) const {} void finish(Construction@ cons, Constructible@ qitem, any@ data) const {} void tick(Construction@ cons, Constructible@ qitem, any@ data, double time) const {} #section all void save(Construction@ cons, any@ data, SaveFile& file) const {} void load(Construction@ cons, any@ data, SaveFile& file) const {} bool consume(Construction@ cons, any@ data, const Targets@ targs) const { return true; } void reverse(Construction@ cons, any@ data, const Targets@ targs, bool cancel) const {} string getFailReason(Object& obj, const ConstructionType@ cons, uint index, const Target@ targ) const { return ""; } bool isValidTarget(Object& obj, const ConstructionType@ cons, uint index, const Target@ targ) const { return true; } bool canBuild(Object& obj, const ConstructionType@ cons, const Targets@ targs, bool ignoreCost) const { return true; } void getBuildCost(Object& obj, const ConstructionType@ cons, const Targets@ targs, int& cost) const {} void getMaintainCost(Object& obj, const ConstructionType@ cons, const Targets@ targs, int& cost) const {} void getLaborCost(Object& obj, const ConstructionType@ cons, const Targets@ targs, double& cost) const {} bool getVariable(Object& obj, const ConstructionType@ cons, Sprite& sprt, string& name, string& value, Color& color) const { return false; } bool formatCost(Object& obj, const ConstructionType@ cons, const Targets@ targs, string& value) const { return false; } bool getCost(Object& obj, const ConstructionType@ cons, const Targets@ targs, string& value, Sprite& icon) const { return false; } }; tidy class Construction : Savable { const ConstructionType@ type; Object@ obj; array data; Targets targets; Construction() { } Construction(const ConstructionType@ type) { @this.type = type; data.length = type.hooks.length; targets = Targets(type.targets); } void save(SaveFile& file) { file.writeIdentifier(SI_ConstructionType, type.id); file << obj; file << targets; for(uint i = 0, cnt = type.hooks.length; i < cnt; ++i) type.hooks[i].save(this, data[i], file); } void load(SaveFile& file) { uint tid = file.readIdentifier(SI_ConstructionType); @type = getConstructionType(tid); data.length = type.hooks.length; file >> obj; file >> targets; for(uint i = 0, cnt = type.hooks.length; i < cnt; ++i) type.hooks[i].load(this, data[i], file); } #section server void start(Constructible@ qitem) { for(uint i = 0, cnt = type.hooks.length; i < cnt; ++i) type.hooks[i].start(this, qitem, data[i]); } void cancel(Constructible@ qitem) { for(uint i = 0, cnt = type.hooks.length; i < cnt; ++i) { type.hooks[i].cancel(this, qitem, data[i]); type.hooks[i].reverse(this, data[i], targets, true); } } void finish(Constructible@ qitem) { for(uint i = 0, cnt = type.hooks.length; i < cnt; ++i) type.hooks[i].finish(this, qitem, data[i]); if(qitem.maintainCost != 0) obj.owner.modMaintenance(qitem.maintainCost, MoT_Misc); } void tick(Constructible@ qitem, double time) { for(uint i = 0, cnt = type.hooks.length; i < cnt; ++i) type.hooks[i].tick(this, qitem, data[i], time); } #section all }; array constructions; dictionary constructionIdents; int getConstructionID(const string& ident) { ConstructionType@ type; constructionIdents.get(ident, @type); if(type !is null) return int(type.id); return -1; } string getConstructionIdent(int id) { if(id < 0 || id >= int(constructions.length)) return "-"; return constructions[id].ident; } string getConstructionName(int id) { if(id < 0 || id >= int(constructions.length)) return "-"; return constructions[id].name; } Sprite getConstructionIcon(int id) { if(id < 0 || id >= int(constructions.length)) return Sprite(); return constructions[id].icon; } uint getConstructionTypeCount() { return constructions.length; } const ConstructionType@ getConstructionType(uint id) { if(id < constructions.length) return constructions[id]; else return null; } const ConstructionType@ getConstructionType(const string& name) { ConstructionType@ cons; constructionIdents.get(name, @cons); return cons; } void addConstructionType(ConstructionType@ cons) { cons.id = constructions.length; constructions.insertLast(cons); constructionIdents.set(cons.ident, @cons); } void saveIdentifiers(SaveFile& file) { for(uint i = 0, cnt = constructions.length; i < cnt; ++i) { ConstructionType@ type = constructions[i]; file.addIdentifier(SI_ConstructionType, type.id, type.ident); } } void parseLine(string& line, ConstructionType@ type, ReadFile@ file) { //Hook line auto@ hook = cast(parseHook(line, "construction_effects::", instantiate=false, file=file)); if(hook !is null) type.hooks.insertLast(hook); } void loadConstructions(const string& filename) { ReadFile file(filename, true); string key, value; ConstructionType@ type; uint index = 0; while(file++) { key = file.key; value = file.value; if(file.fullLine) { string line = file.line; parseLine(line, type, file); } else if(key.equals_nocase("Construction")) { if(type !is null) addConstructionType(type); @type = ConstructionType(); type.ident = value; } else if(type is null) { file.error("Missing Construction ID line"); } else if(key.equals_nocase("Name")) { type.name = localize(value); } else if(key.equals_nocase("Description")) { type.description = localize(value); } else if(key.equals_nocase("Icon")) { type.icon = getSprite(value); } else if(key.equals_nocase("Target")) { parseTarget(type.targets, value); } else if(key.equals_nocase("Build Cost")) { type.buildCost = toInt(value); } else if(key.equals_nocase("Maintenance Cost")) { type.maintainCost = toInt(value); } else if(key.equals_nocase("Labor Cost")) { type.laborCost = toDouble(value); } else if(key.equals_nocase("Time Cost")) { type.timeCost = toDouble(value); } else if(key.equals_nocase("Always Borrowable")) { type.alwaysBorrowable = toBool(value); } else if(key.equals_nocase("Category")) { type.category = value; } else if(key.equals_nocase("In Context")) { type.inContext = toBool(value); } else { string line = file.line; parseLine(line, type, file); } } if(type !is null) addConstructionType(type); } void preInit() { FileList list("data/constructions", "*.txt", true); for(uint i = 0, cnt = list.length; i < cnt; ++i) loadConstructions(list.path[i]); } void init() { for(uint i = 0, cnt = constructions.length; i < cnt; ++i) { auto@ type = constructions[i]; for(uint n = 0, ncnt = type.hooks.length; n < ncnt; ++n) { if(!cast(type.hooks[n]).instantiate()) error("Could not instantiate hook "+addrstr(type.hooks[n])+" on construction "+type.ident); else cast(type.hooks[n]).initTargets(type.targets); } } }