#priority init 5500 #section game import saving; import hooks; #section menu class Hook {}; #section all export Trait; export getTrait, getTraitCount; export TraitCategory; export getTraitCategory, getTraitCategoryCount; tidy final class Trait { uint id; const TraitCategory@ category; string ident; string name; string description; string unique; Sprite icon; Color color; int order = 0; int cost = 0; int gives = 0; bool defaultTrait = false; bool available = true; bool aiSupport = true; string dlc; array hooks; array hookDefs; array conflicts; array conflictDefs; void preInit(Empire& emp, array& data) const { data.length = hooks.length; for(uint i = 0, cnt = hooks.length; i < cnt; ++i) hooks[i].preInit(emp, data[i]); } void init(Empire& emp, array& data) const { for(uint i = 0, cnt = hooks.length; i < cnt; ++i) hooks[i].init(emp, data[i]); } void postInit(Empire& emp, array& data) const { data.length = hooks.length; for(uint i = 0, cnt = hooks.length; i < cnt; ++i) hooks[i].postInit(emp, data[i]); } void tick(Empire& emp, array& data, double time) const { for(uint i = 0, cnt = hooks.length; i < cnt; ++i) hooks[i].tick(emp, data[i], time); } void save(array& data, SaveFile& file) const { for(uint i = 0, cnt = hooks.length; i < cnt; ++i) hooks[i].save(data[i], file); } void load(array& data, SaveFile& file) const { data.length = hooks.length; for(uint i = 0, cnt = hooks.length; i < cnt; ++i) hooks[i].load(data[i], file); } void addConflict(const Trait@ other) { if(conflicts.find(other) != -1) return; conflicts.insertLast(other); } bool get_hasDLC() const { if(dlc.length == 0) return true; return hasDLC(dlc); } bool hasConflicts(const array& list) const { for(uint i = 0, cnt = list.length; i < cnt; ++i) { auto@ other = list[i]; if(other is this) continue; if(conflicts.find(other) != -1) return true; if(unique.length != 0 && unique == other.unique) return true; } return false; } int opCmp(const Trait@ other) const { if(order < other.order) return -1; if(order > other.order) return 1; return 0; } bool opEquals(const Trait@ other) const { return other.id == id; } }; interface ITraitEffect { void preInit(Empire& emp, any@ data) const; void init(Empire& emp, any@ data) const; void postInit(Empire& emp, any@ data) const; void tick(Empire& emp, any@ data, double time) const; void save(any@ data, SaveFile& file) const; void load(any@ data, SaveFile& file) const; }; class TraitEffect : Hook, ITraitEffect { void preInit(Empire& emp, any@ data) const {} void init(Empire& emp, any@ data) const {} void postInit(Empire& emp, any@ data) const {} void tick(Empire& emp, any@ data, double time) const {} void save(any@ data, SaveFile& file) const {} void load(any@ data, SaveFile& file) const {} }; array traits; dictionary idents; const Trait@ getTrait(uint id) { if(id >= traits.length) return null; return traits[id]; } const Trait@ getTrait(const string& ident) { Trait@ def; if(idents.get(ident, @def)) return def; return null; } int getTraitID(const string& ident) { auto@ type = getTrait(ident); if(type is null) return -1; return int(type.id); } string getTraitIdent(int id) { auto@ type = getTrait(id); if(type is null) return ""; return type.ident; } uint getTraitCount() { return traits.length; } void addTrait(Trait@ type) { type.id = traits.length; traits.insertLast(type); idents.set(type.ident, @type); } tidy final class TraitCategory { uint id; string ident; string name; TraitCategory(const string& ident) { this.ident = ident; name = localize("#TRAIT_CAT_"+ident); if(name[0] == '#') name = ident; } }; array categories; dictionary catIdents; const TraitCategory@ getTraitCategory(uint id) { if(id >= categories.length) return null; return categories[id]; } const TraitCategory@ getTraitCategory(const string& ident, bool create = false) { TraitCategory@ def; if(catIdents.get(ident, @def)) return def; if(create) return addTraitCategory(TraitCategory(ident)); return null; } uint getTraitCategoryCount() { return categories.length; } TraitCategory@ addTraitCategory(TraitCategory@ type) { type.id = categories.length; categories.insertLast(type); catIdents.set(type.ident, @type); return type; } void saveIdentifiers(SaveFile& file) { #section game for(uint i = 0, cnt = traits.length; i < cnt; ++i) { auto type = traits[i]; file.addIdentifier(SI_Trait, type.id, type.ident); } #section all } void parseLine(string& line, Trait@ trait, ReadFile@ file) { #section game //Hook line trait.hookDefs.insertLast(line); #section all } void loadTraits(const string& filename) { ReadFile file(filename, true); string key, value; Trait@ trait; uint index = 0; while(file++) { key = file.key; value = file.value; if(file.fullLine) { string line = file.line; parseLine(line, trait, file); } else if(key.equals_nocase("Trait")) { if(trait !is null) addTrait(trait); @trait = Trait(); trait.ident = value; } else if(trait is null) { file.error("Missing trait ID' line"); } else if(key.equals_nocase("Name")) { trait.name = localize(value); } else if(key.equals_nocase("Description")) { trait.description = localize(value); } else if(key.equals_nocase("Costs Points")) { trait.cost = toInt(value); } else if(key.equals_nocase("Gives Points")) { trait.gives = toInt(value); } else if(key.equals_nocase("Category")) { @trait.category = getTraitCategory(value, create=true); } else if(key.equals_nocase("Icon")) { trait.icon = getSprite(value); } else if(key.equals_nocase("Color")) { trait.color = toColor(value); } else if(key.equals_nocase("Order")) { trait.order = toInt(value); } else if(key.equals_nocase("Unique")) { trait.unique = value; } else if(key.equals_nocase("Default")) { trait.defaultTrait = toBool(value); } else if(key.equals_nocase("Available")) { trait.available = toBool(value); } else if(key.equals_nocase("Conflict")) { trait.conflictDefs.insertLast(value); } else if(key.equals_nocase("DLC")) { trait.dlc = value; } else if(key.equals_nocase("AI Support")) { trait.aiSupport = toBool(value); } else { string line = file.line; parseLine(line, trait, file); } } if(trait !is null) addTrait(trait); } export initTraits; bool initialized = false; void initTraits() { if(initialized) return; initialized = true; FileList list("data/traits", "*.txt", true); for(uint i = 0, cnt = list.length; i < cnt; ++i) loadTraits(list.path[i]); } void preInit() { initTraits(); #section game for(uint i = 0, cnt = traits.length; i < cnt; ++i) { auto@ trait = traits[i]; for(uint n = 0, ncnt = trait.hookDefs.length; n < ncnt; ++n) { auto@ hook = cast(parseHook(trait.hookDefs[n], "trait_effects::", instantiate=false)); if(hook !is null) trait.hooks.insertLast(hook); } } #section all } void init() { for(uint i = 0, cnt = traits.length; i < cnt; ++i) { auto@ trait = traits[i]; #section game for(uint n = 0, ncnt = trait.hooks.length; n < ncnt; ++n) { if(!cast(trait.hooks[n]).instantiate()) error("Could not instantiate hook "+addrstr(trait.hooks[n])+" on trait "+trait.ident); } #section all for(uint n = 0, ncnt = trait.conflictDefs.length; n < ncnt; ++n) { Trait@ other; if(!idents.get(trait.conflictDefs[n], @other)) { error("Could not find conflict trait "+trait.conflictDefs[n]+" in trait "+trait.ident); continue; } trait.addConflict(other); other.addConflict(trait); } } }