Index: binaries/data/mods/public/globalscripts/Technologies.js =================================================================== --- binaries/data/mods/public/globalscripts/Technologies.js +++ binaries/data/mods/public/globalscripts/Technologies.js @@ -13,8 +13,9 @@ * modification arrays, retrieved from the intended player's TechnologyManager. * @param classes Array contianing the class list of the template. * @param propertyName String encoding the name of the value. - * @param propertyValue Number storing the original value. Can also be - * non-numberic, but then only "replace" techs can be supported. + * @param propertyValue Variable storing the original value. + * This can be a numerical value, or a non-numerical value. + * If it is non-numerical, only "replace" and "tokens" are supported. */ function GetTechModifiedProperty(currentTechModifications, classes, propertyName, propertyValue) { @@ -29,6 +30,8 @@ continue; if (modification.replace !== undefined) return modification.replace; + if (modification.tokens !== undefined) + return HandleTokens(propertyValue, modification.tokens); if (modification.multiply) multiply *= modification.multiply; else if (modification.add) @@ -119,6 +122,37 @@ return MatchesClassList(classes, modification.affects); } +/** + * Returns a modified list of tokens. + * Supports "A>B" to replace A by B, "-A" to remove A, and the rest will add tokens. + */ +function HandleTokens(propertyValue, modification) +{ + let tokens = propertyValue.split(/\s+/); + let newTokens = modification.split(/\s+/); + + for (let token of newTokens) + { + if (token.search(">") !== -1) + { + let [oldToken, newToken] = token.split(">"); + let index = tokens.indexOf(oldToken); + if (index !== -1) + tokens[index] = newToken; + } + else if (token[0] == "-") + { + let index = tokens.indexOf(token.substr(1)); + if (index !== -1) + tokens.splice(index,1); + } + else + tokens.push(token); + } + + return tokens.join(" "); +} + /** * Derives the technology requirements from a given technology template. * Takes into account the `supersedes` attribute. Index: binaries/data/mods/public/gui/session/messages.js =================================================================== --- binaries/data/mods/public/gui/session/messages.js +++ binaries/data/mods/public/gui/session/messages.js @@ -419,6 +419,12 @@ if (player == Engine.GetPlayerID()) openDialog(notification.dialogName, notification.data, player); }, + "forceselectionrefresh": function(notification, player) + { + if (player != Engine.GetPlayerID()) + return; + g_Selection.onChange(); + }, "resetselectionpannel": function(notification, player) { if (player != Engine.GetPlayerID()) Index: binaries/data/mods/public/simulation/ai/common-api/entity.js =================================================================== --- binaries/data/mods/public/simulation/ai/common-api/entity.js +++ binaries/data/mods/public/simulation/ai/common-api/entity.js @@ -15,8 +15,7 @@ this._tpCache = new Map(); }, - // helper function to return a template value, optionally adjusting for tech. - // TODO: there's no support for "_string" values here. + // Helper function to return a template value, adjusting for tech. "get": function(string) { let value = this._template; Index: binaries/data/mods/public/simulation/components/AIInterface.js =================================================================== --- binaries/data/mods/public/simulation/components/AIInterface.js +++ binaries/data/mods/public/simulation/components/AIInterface.js @@ -251,7 +251,7 @@ if (!ended) continue; // item now contains the template value for this. - let oldValue = +item; + let oldValue = +item == item ? +item : item; let newValue = ApplyValueModificationsToTemplate(valName, oldValue, msg.player, template); // Apply the same roundings as in the components if (valName === "Player/MaxPopulation" || valName === "Cost/Population" || @@ -299,7 +299,7 @@ if (!ended) continue; // "item" now contains the unmodified template value for this. - let oldValue = +item; + let oldValue = +item == item ? +item : item; let newValue = ApplyValueModificationsToEntity(valName, oldValue, ent); // Apply the same roundings as in the components if (valName === "Player/MaxPopulation" || valName === "Cost/Population" || Index: binaries/data/mods/public/simulation/components/ProductionQueue.js =================================================================== --- binaries/data/mods/public/simulation/components/ProductionQueue.js +++ binaries/data/mods/public/simulation/components/ProductionQueue.js @@ -88,6 +88,8 @@ if (!string) return; + string = ApplyValueModificationsToEntity("ProductionQueue/Entities/_string", string, this.entity); + // Replace the "{civ}" and "{native}" codes with the owner's civ ID and entity's civ ID. let cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager); let cmpPlayer = QueryOwnerInterface(this.entity); @@ -819,8 +821,15 @@ // if the promotion requirements of units is changed, // update the entities list so that automatically promoted units are shown // appropriately in the list - if (msg.component == "Promotion") + if (msg.component == "Promotion" || msg.component == "ProductionQueue") this.CalculateEntitiesList(); + + let cmpPlayer = QueryOwnerInterface(this.entity, IID_Player); + let cmpGuiInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface); + cmpGuiInterface.PushNotification({ + "type": "forceselectionrefresh", + "players": [cmpPlayer.GetPlayerID()] + }); }; ProductionQueue.prototype.OnDisabledTemplatesChanged = function(msg) Index: binaries/data/mods/public/simulation/data/technologies/successors/upgrade_mace_silvershields.json =================================================================== --- binaries/data/mods/public/simulation/data/technologies/successors/upgrade_mace_silvershields.json +++ binaries/data/mods/public/simulation/data/technologies/successors/upgrade_mace_silvershields.json @@ -8,8 +8,11 @@ "researchTime": 40, "tooltip": "Upgrade Shield Bearer Champion Infantry to Silver Shields, with greater health and armor.", "modifications": [ - { "value": "Promotion/RequiredXp", "replace": 0 } + { + "value": "ProductionQueue/Entities/_string", + "tokens": "units/{civ}_champion_infantry_a_barracks>units/{civ}_champion_infantry_e_barracks units/{civ}_champion_infantry_a>units/{civ}_champion_infantry_e" + } ], - "affects": ["Champion Infantry"], + "affects": ["Structure"], "soundComplete": "interface/alarm/alarm_upgradearmory.xml" } Index: binaries/data/mods/public/simulation/data/technologies/tech_demo.json =================================================================== --- /dev/null +++ binaries/data/mods/public/simulation/data/technologies/tech_demo.json @@ -0,0 +1,17 @@ +{ + "autoResearch": true, + "genericName": "Gather Training", + "description": "Training for workers to increase farm gathering speed.", + "cost": { "food": 0, "wood": 0, "stone": 0, "metal": 0 }, + "icon": "farming_training.png", + "researchTime": 0, + "tooltip": "Workers +15% farming rate.", + "modifications": [ + { + "value": "ProductionQueue/Entities/_string", + "tokens": "units/{civ}_support_female_citizen>units/{civ}_infantry_spearman_b" + } + ], + "affects": ["Structure"], + "soundComplete": "interface/alarm/alarm_upgradearmory.xml" +}