Index: binaries/data/mods/public/simulation/components/Cost.js =================================================================== --- binaries/data/mods/public/simulation/components/Cost.js +++ binaries/data/mods/public/simulation/components/Cost.js @@ -44,9 +44,7 @@ Cost.prototype.GetBuildTime = function() { - var cmpPlayer = QueryOwnerInterface(this.entity); - var buildTime = (+this.template.BuildTime) * cmpPlayer.cheatTimeMultiplier; - return ApplyValueModificationsToEntity("Cost/BuildTime", buildTime, this.entity); + return ApplyValueModificationsToEntity("Cost/BuildTime", +this.template.BuildTime, this.entity); }; Cost.prototype.GetResourceCosts = function(owner) Index: binaries/data/mods/public/simulation/components/ModifiersManager.js =================================================================== --- /dev/null +++ binaries/data/mods/public/simulation/components/ModifiersManager.js @@ -0,0 +1,251 @@ +function ModifiersManager() {} + +ModifiersManager.prototype.Schema = + ""; + +ModifiersManager.prototype.Serialize = function() +{ + // The cache will be affected by property reads from the GUI and other places so we shouldn't + // serialize it. + + var ret = {}; + for (var i in this) + { + if (this.hasOwnProperty(i)) + ret[i] = this[i]; + } + ret.cachedValues = new Map(); + return ret; +}; + +ModifiersManager.prototype.Init = function() +{ + // TODO: + // add a way to show an icon for a given modification ID. + + // By property name only. + this.globalModifiers = new Map(); + // By property name and entity. + this.localModifiers = new Map(); + + // By entity + this.cachedValues = new Map(); +}; + +// Wrapper for AddGlobalModifier, to set multiple modifiers with the same ID and priority easily +ModifiersManager.prototype.AddGlobalModifiers = function(modificationID, priority, modificationArray) +{ + for (let propertyName in modificationArray) + this.AddGlobalModifier(propertyName, modificationID, priority, modificationArray[propertyName]); +} + +// Wrapper for RemoveGlobalModifier, to remove any modifier with this ID +ModifiersManager.prototype.RemoveGlobalModifiers = function(modificationID) +{ + for (let propertyName of this.globalModifiers) + this.RemoveGlobalModifier(propertyName[0], modificationID); +} + +// Wrapper for HasGlobalModifier, will return true if there is at least one modifier with this ID for any property. +ModifiersManager.prototype.HasAnyGlobalModifierWithID = function(modificationID) +{ + for (let propertyName of this.globalModifiers) + if (this.HasGlobalModifier(propertyName[0], modificationID)) + return true; + return false; +} + +ModifiersManager.prototype.UpdateGlobalModifier = function(propertyName, modificationID, priority, modification) +{ + warn("this function is TODO"); +} + +ModifiersManager.prototype.AddGlobalModifier = function(propertyName, modificationID, priority, modification) +{ + if (!this.globalModifiers.get(propertyName)) + this.globalModifiers.set(propertyName, {"version": 0, "modifications": []}); + + let modifier = this.globalModifiers.get(propertyName); + + // check whether we already have that modifier + let existing = modifier.modifications.filter(modification => { return modification.ID == modificationID; }); + + if (existing.length > 1) + { + error("Code bug: two global modifiers have the same modificationID " + modificationID); + return; + } + + if (existing.length) + ++existing[0].count; + else + { + // create a new modifier at the correct position. + let index = 0; + // TODO: do a binary search if this ever becomes a performance problem. + for (index = 0; index < modifier.modifications.length; ++index) + // in case of equal priority, insert in chronological order. + if (modifier.modifications[index].priority > priority) + break; + modifier.modifications.splice(index, 0, {"ID": modificationID, "priority": priority, "count": 1, "effect": modification}); + } + + // bump version to invalidate caches + ++modifier.version; + + this.SendGlobalModificationMessages(propertyName); +} + +ModifiersManager.prototype.RemoveGlobalModifier = function(propertyName, modificationID) +{ + if (!this.globalModifiers.get(propertyName)) + return; + + let modifier = this.globalModifiers.get(propertyName); + + // remove modifier. + let size = modifier.modifications.length; + let element = modifier.modifications.filter(modification => { return modification.ID == modificationID; }); + + if (!element.length || --element[0].count) + return; + + modifier.modifications = modifier.modifications.filter(modification => { return modification.count; }); + + // bump version to invalidate caches + ++modifier.version; + + this.SendGlobalModificationMessages(propertyName); +} + +ModifiersManager.prototype.SendGlobalModificationMessages = function(propertyName) +{ + // send messages + let cmpPlayer = Engine.QueryInterface(this.entity, IID_Player); + if (!cmpPlayer || cmpPlayer.GetPlayerID() === undefined) + return; + let playerID = cmpPlayer.GetPlayerID(); + + // TODO: would be nice to do this optionally in a meta-component with several property names or something. + Engine.PostMessage(SYSTEM_ENTITY, MT_TemplateModification, { "player": playerID, "component": propertyName.split("/")[0], "valueNames": [propertyName]}); + // AIInterface wants the entities potentially affected. TODO: improve on this + let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); + let ents = cmpRangeManager.GetEntitiesByPlayer(playerID); + Engine.BroadcastMessage(MT_ValueModification, { "entities": ents, "component": propertyName.split("/")[0], "valueNames": [propertyName]}); + +} + +ModifiersManager.prototype.HasGlobalModifier = function(propertyName, modificationID) +{ + if (!this.globalModifiers.get(propertyName)) + return false; + + return this.globalModifiers.get(propertyName).modifications.some(modification => { return modification.ID == modificationID; }); +} + +// Caching layer in front of ApplyModificationsWorker +// Note: be careful with the type of curValue, if it should be a numerical +// value and is derived from template data, you must convert the string +// from the template to a number using the + operator, before calling +// this function! +ModifiersManager.prototype.ApplyModifications = function(propertyName, originalValue, ent) +{ + let value = originalValue; + // sanity check + if (Array.isArray(originalValue)) + value = originalValue.slice(); + else if (typeof originalValue == "object") + { + error("ModifiersManager ApplyModifications called with an object"); + return; + } + + if (!this.cachedValues.get(ent)) + this.cachedValues.set(ent, new Map()); + if (!this.cachedValues.get(ent).get(propertyName)) + this.cachedValues.get(ent).set(propertyName, { "version": 0, "value": value}); + + let propertyValue = this.cachedValues.get(ent).get(propertyName); + + // global modifiers + if (this.globalModifiers.get(propertyName)) + if (propertyValue.version < this.globalModifiers.get(propertyName).version) + { + let cmpIdentity = Engine.QueryInterface(ent, IID_Identity); + if (!cmpIdentity) + return originalValue; + + let modifications = {}; + modifications[propertyName] = []; // TODO: change this once GetTechModifiedProperty is updated I guess. + for (let modifier of this.globalModifiers.get(propertyName).modifications) + modifications[propertyName].push(modifier.effect); + + let newValue = GetTechModifiedProperty(modifications, cmpIdentity.GetClassesList(), propertyName, value); + + this.cachedValues.get(ent).get(propertyName).version = this.globalModifiers.get(propertyName).version; + this.cachedValues.get(ent).get(propertyName).value = newValue; + } + + // TODO: local modifiers go here. + + return this.cachedValues.get(ent).get(propertyName).value; +}; + +// Alternative version of ApplyModifications, applies to templates instead of entities +ModifiersManager.prototype.ApplyModificationsTemplate = function(propertyName, originalValue, template) +{ + if (!template || !template.Identity || !this.globalModifiers.has(propertyName)) + return originalValue; + + let modifications = {}; + modifications[propertyName] = []; // TODO: change this once GetTechModifiedProperty is updated I guess. + for (let modifier of this.globalModifiers.get(propertyName).modifications) + modifications[propertyName].push(modifier.effect); + + return GetTechModifiedProperty(modifications, GetIdentityClasses(template.Identity), propertyName, originalValue); +}; + +ModifiersManager.prototype.OnGlobalOwnershipChanged = function(msg) +{ + // Send ValueModification messages to new entities. + // TODO: this is quite wasteful, we should cache templates affected by technologies and 360 quickscope it. + + let playerID = (Engine.QueryInterface(this.entity, IID_Player)).GetPlayerID(); + if (msg.to != playerID) + return; + + let cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager); + let template = cmpTemplateManager.GetCurrentTemplateName(msg.entity); + + // TODO : save existing modifiers if from !== -1 in a clever way. + if (msg.from != -1) + return; + + let cmpIdentity = Engine.QueryInterface(msg.entity, IID_Identity); + if (!cmpIdentity) + return; + + let classes = cmpIdentity.GetClassesList(); + + // local modifiers will be added by the relevant components, so no need to check for them here. + let modifiedComponents = {}; + for (let property of this.globalModifiers) + { + // We only need to find one one tech per component for a match + var modifications = property[1].modifications; + var component = property[0].split("/")[0]; + for (let modif of modifications) + if (DoesModificationApply(modif.effect, classes)) + { + if (!modifiedComponents[component]) + modifiedComponents[component] = []; + modifiedComponents[component].push(property[0]); + } + } + + // Send message(s) to the entity so it knows about researched techs + for (let component in modifiedComponents) + Engine.PostMessage(msg.entity, MT_ValueModification, { "entities": [msg.entity], "component": component, "valueNames": modifiedComponents[component] }); +} + +Engine.RegisterComponentType(IID_ModifiersManager, "ModifiersManager", ModifiersManager); Index: binaries/data/mods/public/simulation/components/Pack.js =================================================================== --- binaries/data/mods/public/simulation/components/Pack.js +++ binaries/data/mods/public/simulation/components/Pack.js @@ -98,8 +98,7 @@ { let cmpPlayer = QueryOwnerInterface(this.entity, IID_Player); - return ApplyValueModificationsToEntity("Pack/Time", +this.template.Time, this.entity) * - cmpPlayer.GetCheatTimeMultiplier(); + return ApplyValueModificationsToEntity("Pack/Time", +this.template.Time, this.entity); }; Pack.prototype.GetElapsedTime = function() Index: binaries/data/mods/public/simulation/components/Player.js =================================================================== --- binaries/data/mods/public/simulation/components/Player.js +++ binaries/data/mods/public/simulation/components/Player.js @@ -40,7 +40,6 @@ this.gatherRateMultiplier = 1; this.tradeRateMultiplier = 1; this.cheatsEnabled = false; - this.cheatTimeMultiplier = 1; this.panelEntities = []; this.resourceNames = {}; this.disabledTemplates = {}; @@ -755,16 +754,6 @@ return this.cheatsEnabled; }; -Player.prototype.SetCheatTimeMultiplier = function(time) -{ - this.cheatTimeMultiplier = time; -}; - -Player.prototype.GetCheatTimeMultiplier = function() -{ - return this.cheatTimeMultiplier; -}; - Player.prototype.TributeResource = function(player, amounts) { var cmpPlayer = QueryPlayerIDInterface(player); 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 @@ -343,7 +343,7 @@ } let techCostMultiplier = this.GetTechCostMultiplier(); - let time = techCostMultiplier.time * template.researchTime * cmpPlayer.GetCheatTimeMultiplier(); + let time = techCostMultiplier.time * template.researchTime; let cost = {}; for (let res in template.cost) @@ -512,7 +512,7 @@ var batchTimeModifier = ApplyValueModificationsToEntity("ProductionQueue/BatchTimeModifier", +this.template.BatchTimeModifier, this.entity); // TODO: work out what equation we should use here. - return Math.pow(batchSize, batchTimeModifier) * cmpPlayer.GetCheatTimeMultiplier(); + return Math.pow(batchSize, batchTimeModifier); }; ProductionQueue.prototype.OnOwnershipChanged = function(msg) Index: binaries/data/mods/public/simulation/components/ResourceGatherer.js =================================================================== --- binaries/data/mods/public/simulation/components/ResourceGatherer.js +++ binaries/data/mods/public/simulation/components/ResourceGatherer.js @@ -248,10 +248,6 @@ if (rate == 0 && type.generic) rate = this.GetGatherRate(type.generic); - let cmpPlayer = QueryOwnerInterface(this.entity, IID_Player); - let cheatMultiplier = cmpPlayer ? cmpPlayer.GetCheatTimeMultiplier() : 1; - rate = rate / cheatMultiplier; - if ("Mirages" in cmpResourceSupply) return rate; Index: binaries/data/mods/public/simulation/components/TechnologyManager.js =================================================================== --- binaries/data/mods/public/simulation/components/TechnologyManager.js +++ binaries/data/mods/public/simulation/components/TechnologyManager.js @@ -3,37 +3,12 @@ TechnologyManager.prototype.Schema = ""; -TechnologyManager.prototype.Serialize = function() -{ - // The modifications cache will be affected by property reads from the GUI and other places so we shouldn't - // serialize it. - - var ret = {}; - for (var i in this) - { - if (this.hasOwnProperty(i)) - ret[i] = this[i]; - } - ret.modificationCache = {}; - return ret; -}; - TechnologyManager.prototype.Init = function() { this.researchedTechs = {}; // technologies which have been researched this.researchQueued = {}; // technologies which are queued for research this.researchStarted = {}; // technologies which are being researched currently (non-queued) - // This stores the modifications to unit stats from researched technologies - // Example data: {"ResourceGatherer/Rates/food.grain": [ - // {"multiply": 1.15, "affects": ["FemaleCitizen", "Infantry Swordsman"]}, - // {"add": 2} - // ]} - this.modifications = {}; - this.modificationCache = {}; // Caches the values after technologies have been applied - // e.g. { "Attack/Melee/Hack" : {5: {"origValue": 8, "newValue": 10}, 7: {"origValue": 9, "newValue": 12}, ...}, ...} - // where 5 and 7 are entity id's - this.classCounts = {}; // stores the number of entities of each Class this.typeCountsByClass = {}; // stores the number of entities of each type for each class i.e. // {"someClass": {"unit/spearman": 2, "unit/cav": 5} "someOtherClass":...} @@ -197,31 +172,6 @@ this.typeCountsByClass[cls][template] += 1; } } - - // Newly created entity, check if any researched techs might apply - // (only do this for new entities because even if an entity is converted or captured, - // we want it to maintain whatever technologies previously applied) - if (msg.from == -1) - { - var modifiedComponents = {}; - for (var name in this.modifications) - { - // We only need to find one one tech per component for a match - var modifications = this.modifications[name]; - var component = name.split("/")[0]; - for (let modif of modifications) - if (DoesModificationApply(modif, classes)) - { - if (!modifiedComponents[component]) - modifiedComponents[component] = []; - modifiedComponents[component].push(name); - } - } - - // Send mesage(s) to the entity so it knows about researched techs - for (var component in modifiedComponents) - Engine.PostMessage(msg.entity, MT_ValueModification, { "entities": [msg.entity], "component": component, "valueNames": modifiedComponents[component] }); - } } if (msg.from == playerID) { @@ -247,8 +197,6 @@ } } } - - this.clearModificationCache(msg.entity); } }; @@ -265,24 +213,24 @@ return; } - var modifiedComponents = {}; this.researchedTechs[tech] = template; // store the modifications in an easy to access structure if (template.modifications) { - let derivedModifiers = DeriveModificationsFromTech(template); - for (let modifierPath in derivedModifiers) + let priorityLevel = template.priority || 5; + let supersedes = template.supersedes || undefined; + while (supersedes) { - if (!this.modifications[modifierPath]) - this.modifications[modifierPath] = []; - this.modifications[modifierPath] = this.modifications[modifierPath].concat(derivedModifiers[modifierPath]); - - let component = modifierPath.split("/")[0]; - if (!modifiedComponents[component]) - modifiedComponents[component] = []; - modifiedComponents[component].push(modifierPath); - this.modificationCache[modifierPath] = {}; + ++priorityLevel; + let supertemplate = this.GetTechnologyTemplate(supersedes); + supersedes = supertemplate.supersedes || undefined; } + + let cmpModManager = Engine.QueryInterface(this.entity, IID_ModifiersManager); + let derivedModifiers = DeriveModificationsFromTech(template); + for (let modifierPath in derivedModifiers) + for (let modifier of derivedModifiers[modifierPath]) + cmpModManager.AddGlobalModifier(modifierPath, "tech/"+tech, priorityLevel, modifier); } if (template.replaces && template.replaces.length > 0) @@ -323,51 +271,13 @@ // always send research finished message Engine.PostMessage(this.entity, MT_ResearchFinished, {"player": playerID, "tech": tech}); - + /* for (var component in modifiedComponents) { Engine.PostMessage(SYSTEM_ENTITY, MT_TemplateModification, { "player": playerID, "component": component, "valueNames": modifiedComponents[component]}); Engine.BroadcastMessage(MT_ValueModification, { "entities": ents, "component": component, "valueNames": modifiedComponents[component]}); } -}; - -// Clears the cached data for an entity from the modifications cache -TechnologyManager.prototype.clearModificationCache = function(ent) -{ - for (var valueName in this.modificationCache) - delete this.modificationCache[valueName][ent]; -}; - -// Caching layer in front of ApplyModificationsWorker -// Note: be careful with the type of curValue, if it should be a numerical -// value and is derived from template data, you must convert the string -// from the template to a number using the + operator, before calling -// this function! -TechnologyManager.prototype.ApplyModifications = function(valueName, curValue, ent) -{ - if (!this.modificationCache[valueName]) - this.modificationCache[valueName] = {}; - - if (!this.modificationCache[valueName][ent] || this.modificationCache[valueName][ent].origValue != curValue) - { - let cmpIdentity = Engine.QueryInterface(ent, IID_Identity); - if (!cmpIdentity) - return curValue; - this.modificationCache[valueName][ent] = { - "origValue": curValue, - "newValue": GetTechModifiedProperty(this.modifications, cmpIdentity.GetClassesList(), valueName, curValue) - }; - } - - return this.modificationCache[valueName][ent].newValue; -}; - -// Alternative version of ApplyModifications, applies to templates instead of entities -TechnologyManager.prototype.ApplyModificationsTemplate = function(valueName, curValue, template) -{ - if (!template || !template.Identity) - return curValue; - return GetTechModifiedProperty(this.modifications, GetIdentityClasses(template.Identity), valueName, curValue); + */ }; // Marks a technology as being queued for research @@ -412,12 +322,6 @@ return undefined; }; -// Get helper data for tech modifications -TechnologyManager.prototype.GetTechModifications = function() -{ - return this.modifications; -}; - // called by GUIInterface for PlayerData. AI use. TechnologyManager.prototype.GetQueuedResearch = function() { Index: binaries/data/mods/public/simulation/components/Upgrade.js =================================================================== --- binaries/data/mods/public/simulation/components/Upgrade.js +++ binaries/data/mods/public/simulation/components/Upgrade.js @@ -275,8 +275,7 @@ return 0; let cmpPlayer = QueryPlayerIDInterface(this.owner, IID_Player); - return ApplyValueModificationsToEntity("Upgrade/Time", +this.template[choice].Time, this.entity) * - cmpPlayer.GetCheatTimeMultiplier(); + return ApplyValueModificationsToEntity("Upgrade/Time", +this.template[choice].Time, this.entity); }; Upgrade.prototype.GetElapsedTime = function() Index: binaries/data/mods/public/simulation/components/interfaces/ModifiersManager.js =================================================================== --- /dev/null +++ binaries/data/mods/public/simulation/components/interfaces/ModifiersManager.js @@ -0,0 +1 @@ +Engine.RegisterInterface("ModifiersManager"); Index: binaries/data/mods/public/simulation/components/tests/test_Attack.js =================================================================== --- binaries/data/mods/public/simulation/components/tests/test_Attack.js +++ binaries/data/mods/public/simulation/components/tests/test_Attack.js @@ -3,7 +3,7 @@ Engine.LoadComponentScript("interfaces/Auras.js"); Engine.LoadComponentScript("interfaces/AuraManager.js"); Engine.LoadComponentScript("interfaces/Capturable.js"); -Engine.LoadComponentScript("interfaces/TechnologyManager.js"); +Engine.LoadComponentScript("interfaces/ModifiersManager.js"); Engine.LoadComponentScript("interfaces/Formation.js"); Engine.LoadComponentScript("interfaces/Attack.js"); Engine.LoadComponentScript("Attack.js"); Index: binaries/data/mods/public/simulation/components/tests/test_Auras.js =================================================================== --- binaries/data/mods/public/simulation/components/tests/test_Auras.js +++ binaries/data/mods/public/simulation/components/tests/test_Auras.js @@ -2,7 +2,7 @@ Engine.LoadHelperScript("ValueModification.js"); Engine.LoadComponentScript("interfaces/Auras.js"); Engine.LoadComponentScript("interfaces/AuraManager.js"); -Engine.LoadComponentScript("interfaces/TechnologyManager.js"); +Engine.LoadComponentScript("interfaces/ModifiersManager.js"); Engine.LoadComponentScript("Auras.js"); Engine.LoadComponentScript("AuraManager.js"); Index: binaries/data/mods/public/simulation/components/tests/test_Capturable.js =================================================================== --- binaries/data/mods/public/simulation/components/tests/test_Capturable.js +++ binaries/data/mods/public/simulation/components/tests/test_Capturable.js @@ -5,7 +5,7 @@ Engine.LoadComponentScript("interfaces/Capturable.js"); Engine.LoadComponentScript("interfaces/GarrisonHolder.js"); Engine.LoadComponentScript("interfaces/StatisticsTracker.js"); -Engine.LoadComponentScript("interfaces/TechnologyManager.js"); +Engine.LoadComponentScript("interfaces/ModifiersManager.js"); Engine.LoadComponentScript("interfaces/TerritoryDecay.js"); Engine.LoadComponentScript("interfaces/Timer.js"); Engine.LoadComponentScript("Capturable.js"); Index: binaries/data/mods/public/simulation/components/tests/test_Damage.js =================================================================== --- binaries/data/mods/public/simulation/components/tests/test_Damage.js +++ binaries/data/mods/public/simulation/components/tests/test_Damage.js @@ -11,7 +11,7 @@ Engine.LoadComponentScript("interfaces/Player.js"); Engine.LoadComponentScript("interfaces/Promotion.js"); Engine.LoadComponentScript("interfaces/Sound.js"); -Engine.LoadComponentScript("interfaces/TechnologyManager.js"); +Engine.LoadComponentScript("interfaces/ModifiersManager.js"); Engine.LoadComponentScript("interfaces/Timer.js"); Engine.LoadComponentScript("Attack.js"); Engine.LoadComponentScript("Damage.js"); Index: binaries/data/mods/public/simulation/components/tests/test_ModifiersManager.js =================================================================== --- /dev/null +++ binaries/data/mods/public/simulation/components/tests/test_ModifiersManager.js @@ -0,0 +1,67 @@ +Engine.LoadComponentScript("interfaces/AuraManager.js"); +Engine.LoadComponentScript("interfaces/ModifiersManager.js"); +Engine.LoadComponentScript("ModifiersManager.js"); +Engine.LoadHelperScript("Player.js"); +Engine.LoadHelperScript("ValueModification.js"); + +let cmpModManager = ConstructComponent(2, "ModifiersManager", {}); + +cmpModManager.Init(); + +AddMock(2, IID_AuraManager, { + "ApplyModifications": function(a, value, b) { return value; }, + "ApplyTemplateModifications": function(a, value, b) { return value; }, +}); +AddMock(SYSTEM_ENTITY, IID_PlayerManager, { + "GetPlayerByID": () => 2, +}); +// create ownership, set to "1" for any entity used below, +// otherwise QueryOwnerInterface fails +let entitiesToTest = [5,6,7,8]; +for (let ent of entitiesToTest) +{ + AddMock(ent, IID_Ownership, { + "GetOwner": () => 1 + }); +} +// hack + +cmpModManager.AddGlobalModifier("Test_A", "Test_A_0", 4, {"affects":["Structure"], "add": 10 }); +cmpModManager.AddGlobalModifier("Test_A", "Test_A_1", 4, {"affects":["Infantry"], "add": 5 }); +cmpModManager.AddGlobalModifier("Test_A", "Test_A_2", 4, {"affects":["Unit"], "add": 3 }); + +TS_ASSERT_EQUALS(cmpModManager.globalModifiers.get("Test_A").version, 3); + +AddMock(5, IID_Identity, { + "GetClassesList": function() { return "Structure"} +}); +AddMock(6, IID_Identity, { + "GetClassesList": function() { return "Infantry"} +}); +AddMock(7, IID_Identity, { + "GetClassesList": function() { return "Unit"} +}); +AddMock(8, IID_Identity, { + "GetClassesList": function() { return "Structure Unit"} +}); + +TS_ASSERT_EQUALS(ApplyValueModificationsToEntity("Test_A", 5, 5), 15); +TS_ASSERT_EQUALS(ApplyValueModificationsToEntity("Test_A", 5, 6), 10); +TS_ASSERT_EQUALS(ApplyValueModificationsToEntity("Test_A", 5, 7), 8); +TS_ASSERT_EQUALS(ApplyValueModificationsToEntity("Test_A", 5, 8), 18); + +TS_ASSERT_EQUALS(ApplyValueModificationsToEntity("Test_B", 5, 8), 5); + +cmpModManager.RemoveGlobalModifiers("Test_A_0"); +TS_ASSERT_EQUALS(cmpModManager.globalModifiers.get("Test_A").version, 4); +TS_ASSERT_EQUALS(ApplyValueModificationsToEntity("Test_A", 5, 5), 5); + +cmpModManager.AddGlobalModifiers("Test_A_0", 4, { + "Test_A" : {"affects":["Structure"], "add": 10 }, + "Test_B" : {"affects":["Structure"], "add": 8 }, +}); + +TS_ASSERT_EQUALS(ApplyValueModificationsToEntity("Test_A", 5, 5), 15); +TS_ASSERT_EQUALS(ApplyValueModificationsToEntity("Test_B", 5, 8), 13); + +// TODO: test ownership changes, updating. \ No newline at end of file Index: binaries/data/mods/public/simulation/components/tests/test_Pack.js =================================================================== --- binaries/data/mods/public/simulation/components/tests/test_Pack.js +++ binaries/data/mods/public/simulation/components/tests/test_Pack.js @@ -2,7 +2,7 @@ Engine.LoadHelperScript("Sound.js"); Engine.LoadHelperScript("Transform.js"); Engine.LoadHelperScript("ValueModification.js"); -Engine.LoadComponentScript("interfaces/TechnologyManager.js"); +Engine.LoadComponentScript("interfaces/ModifiersManager.js"); Engine.LoadComponentScript("interfaces/AuraManager.js"); Engine.LoadComponentScript("interfaces/Capturable.js"); Engine.LoadComponentScript("interfaces/GarrisonHolder.js"); Index: binaries/data/mods/public/simulation/components/tests/test_Player.js =================================================================== --- binaries/data/mods/public/simulation/components/tests/test_Player.js +++ binaries/data/mods/public/simulation/components/tests/test_Player.js @@ -2,7 +2,7 @@ Engine.LoadHelperScript("ValueModification.js"); Engine.LoadComponentScript("interfaces/AuraManager.js"); Engine.LoadComponentScript("interfaces/Player.js"); -Engine.LoadComponentScript("interfaces/TechnologyManager.js"); +Engine.LoadComponentScript("interfaces/ModifiersManager.js"); Engine.LoadComponentScript("Player.js"); Resources = { Index: binaries/data/mods/public/simulation/components/tests/test_Technologies.js =================================================================== --- binaries/data/mods/public/simulation/components/tests/test_Technologies.js +++ /dev/null @@ -1,510 +0,0 @@ -// TODO: Move this to a folder of tests for GlobalScripts (once one is created) - -// No requirements set in template -let template = {}; -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), []); - -/** - * First, the basics: - */ - -// Technology Requirement -template.requirements = { "tech": "expected_tech" }; -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["expected_tech"] }]); - -// Entity Requirement: Count of entities matching given class -template.requirements = { "entity": { "class": "Village", "number": 5 } }; -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "entities": [{ "class": "Village", "number": 5, "check": "count" }] }]); - -// Entity Requirement: Count of entities matching given class -template.requirements = { "entity": { "class": "Village", "numberOfTypes": 5 } }; -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "entities": [{ "class": "Village", "number": 5, "check": "variants" }] }]); - -// Single `civ` -template.requirements = { "civ": "athen" }; -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), []); -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "spart"), false); - -// Single `notciv` -template.requirements = { "notciv": "athen" }; -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), false); -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "spart"), []); - - -/** - * Basic `all`s: - */ - -// Multiple techs -template.requirements = { "all": [{ "tech": "tech_A" }, { "tech": "tech_B" }, { "tech": "tech_C" }] }; -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["tech_A", "tech_B", "tech_C"] }]); - -// Multiple entity definitions -template.requirements = { - "all": [ - { "entity": { "class": "class_A", "number": 5 } }, - { "entity": { "class": "class_B", "number": 5 } }, - { "entity": { "class": "class_C", "number": 5 } } - ] -}; -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), - [{ "entities": [{ "class": "class_A", "number": 5, "check": "count" }, { "class": "class_B", "number": 5, "check": "count" }, { "class": "class_C", "number": 5, "check": "count" }] }]); - -// A `tech` and an `entity` -template.requirements = { "all": [{ "tech": "tech_A" }, { "entity": { "class": "class_B", "number": 5, "check": "count" } }] }; -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["tech_A"], "entities": [{ "class": "class_B", "number": 5, "check": "count" }] }]); - -// Multiple `civ`s -template.requirements = { "all": [{ "civ": "civ_A"}, { "civ": "civ_B"}, { "civ": "civ_C"}] }; -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_A"), []); -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_B"), []); -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_C"), []); -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_D"), false); - -// Multiple `notciv`s -template.requirements = { "all": [{ "notciv": "civ_A"}, { "notciv": "civ_B"}, { "notciv": "civ_C"}] }; -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_A"), false); -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_B"), false); -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_C"), false); -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_D"), []); - -// A `civ` with a tech/entity -template.requirements = { "all": [{ "civ": "athen" }, { "tech": "expected_tech" }] }; -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["expected_tech"] }]); -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "spart"), false); - -template.requirements = { "all": [{ "civ": "athen" }, { "entity": { "class": "Village", "number": 5 } }] }; -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "entities": [{ "class": "Village", "number": 5, "check": "count" }] }]); -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "spart"), false); - -template.requirements = { "all": [{ "civ": "athen" }, { "entity": { "class": "Village", "numberOfTypes": 5 } }] }; -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "entities": [{ "class": "Village", "number": 5, "check": "variants" }] }]); -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "spart"), false); - -// A `notciv` with a tech/entity -template.requirements = { "all": [{ "notciv": "athen" }, { "tech": "expected_tech" }] }; -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), false); -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "spart"), [{ "techs": ["expected_tech"] }]); - -template.requirements = { "all": [{ "notciv": "athen" }, { "entity": { "class": "Village", "number": 5 } }] }; -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), false); -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "spart"), [{ "entities": [{ "class": "Village", "number": 5, "check": "count" }] }]); - -template.requirements = { "all": [{ "notciv": "athen" }, { "entity": { "class": "Village", "numberOfTypes": 5 } }] }; -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), false); -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "spart"), [{ "entities": [{ "class": "Village", "number": 5, "check": "variants" }] }]); - - -/** - * Basic `any`s: - */ - -// Multiple techs -template.requirements = { "any": [{ "tech": "tech_A" }, { "tech": "tech_B" }, { "tech": "tech_C" }] }; -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["tech_A"] }, { "techs": ["tech_B"] }, { "techs": ["tech_C"] }]); - -// Multiple entity definitions -template.requirements = { - "any": [ - { "entity": { "class": "class_A", "number": 5 } }, - { "entity": { "class": "class_B", "number": 5 } }, - { "entity": { "class": "class_C", "number": 5 } } - ] -}; -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [ - { "entities": [{ "class": "class_A", "number": 5, "check": "count" }] }, - { "entities": [{ "class": "class_B", "number": 5, "check": "count" }] }, - { "entities": [{ "class": "class_C", "number": 5, "check": "count" }] } -]); - -// A tech or an entity -template.requirements = { "any": [{ "tech": "tech_A" }, { "entity": { "class": "class_B", "number": 5, "check": "count" } }] }; -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["tech_A"] }, { "entities": [{ "class": "class_B", "number": 5, "check": "count" }] }]); - -// Multiple `civ`s -template.requirements = { "any": [{ "civ": "civ_A" }, { "civ": "civ_B" }, { "civ": "civ_C" }] }; -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_A"), []); -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_B"), []); -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_C"), []); -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_D"), false); - -// Multiple `notciv`s -template.requirements = { "any": [{ "notciv": "civ_A" }, { "notciv": "civ_B" }, { "notciv": "civ_C" }] }; -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_A"), false); -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_B"), false); -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_C"), false); -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_D"), []); - -// A `civ` or a tech/entity -template.requirements = { "any": [{ "civ": "athen" }, { "tech": "expected_tech" }] }; -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), []); -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "spart"), [{ "techs": ["expected_tech"] }]); - -template.requirements = { "any": [{ "civ": "athen" }, { "entity": { "class": "Village", "number": 5 } }] }; -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), []); -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "spart"), [{ "entities": [{ "class": "Village", "number": 5, "check": "count" }] }]); - -template.requirements = { "any": [{ "civ": "athen" }, { "entity": { "class": "Village", "numberOfTypes": 5 } }] }; -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), []); -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "spart"), [{ "entities": [{ "class": "Village", "number": 5, "check": "variants" }] }]); - -// A `notciv` or a tech -template.requirements = { "any": [{ "notciv": "athen" }, { "tech": "expected_tech" }] }; -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), false); -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "spart"), [{ "techs": ["expected_tech"] }]); - -template.requirements = { "any": [{ "notciv": "athen" }, { "entity": { "class": "Village", "number": 5 } }] }; -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), false); -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "spart"), [{ "entities": [{ "class": "Village", "number": 5, "check": "count" }] }]); - -template.requirements = { "any": [{ "notciv": "athen" }, { "entity": { "class": "Village", "numberOfTypes": 5 } }] }; -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), false); -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "spart"), [{ "entities": [{ "class": "Village", "number": 5, "check": "variants" }] }]); - - -/** - * Complicated `all`s, part 1 - an `all` inside an `all`: - */ - -// Techs -template.requirements = { - "all": [ - { "all": [{ "tech": "tech_A" }, { "tech": "tech_B" }] }, - { "all": [{ "tech": "tech_C" }, { "tech": "tech_D" }] } - ] -}; -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["tech_A", "tech_B", "tech_C", "tech_D"] }]); - -// Techs and entities -template.requirements = { - "all": [ - { "all": [{ "tech": "tech_A" }, { "entity": { "class": "class_A", "number": 5 } }] }, - { "all": [{ "entity": { "class": "class_B", "numberOfTypes": 5 } }, { "tech": "tech_B" }] } - ] -}; -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ - "techs": ["tech_A", "tech_B"], - "entities": [{ "class": "class_A", "number": 5, "check": "count" }, { "class": "class_B", "number": 5, "check": "variants" }] -}]); - -// Two `civ`s, without and with a tech -template.requirements = { - "all": [ - { "all": [{ "civ": "athen" }, { "civ": "spart" }] } - ] -}; -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), []); -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), false); - -template.requirements = { - "all": [ - { "tech": "required_tech" }, - { "all": [{ "civ": "athen" }, { "civ": "spart" }] } - ] -}; -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["required_tech"] }]); -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), false); - -// Two `notciv`s, without and with a tech -template.requirements = { - "all": [ - { "all": [{ "notciv": "athen" }, { "notciv": "spart" }] } - ] -}; -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), false); -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), []); - -template.requirements = { - "all": [ - { "tech": "required_tech" }, - { "all": [{ "notciv": "athen" }, { "notciv": "spart" }] } - ] -}; -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), false); -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), [{ "techs": ["required_tech"] }]); - -// Inner `all` has a tech and a `civ`/`notciv` -template.requirements = { - "all": [ - { "all": [{ "tech": "tech_A" }, { "civ": "maur" }] }, - { "tech": "tech_B" } - ] -}; -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["tech_B"] }]); -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), [{ "techs": ["tech_A", "tech_B"] }]); - -template.requirements = { - "all": [ - { "all": [{ "tech": "tech_A" }, { "notciv": "maur" }] }, - { "tech": "tech_B" } - ] -}; -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["tech_A", "tech_B"] }]); -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), [{ "techs": ["tech_B"] }]); - - -/** - * Complicated `all`s, part 2 - an `any` inside an `all`: - */ - -// Techs -template.requirements = { - "all": [ - { "any": [{ "tech": "tech_A" }, { "tech": "tech_B" }] }, - { "any": [{ "tech": "tech_C" }, { "tech": "tech_D" }] } - ] -}; -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [ - { "techs": ["tech_A", "tech_C"] }, - { "techs": ["tech_A", "tech_D"] }, - { "techs": ["tech_B", "tech_C"] }, - { "techs": ["tech_B", "tech_D"] } -]); - -// Techs and entities -template.requirements = { - "all": [ - { "any": [{ "tech": "tech_A" }, { "entity": { "class": "class_A", "number": 5 } }] }, - { "any": [{ "entity": { "class": "class_B", "numberOfTypes": 5 } }, { "tech": "tech_B" }] } - ] -}; -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [ - { "techs": ["tech_A"], "entities": [{ "class": "class_B", "number": 5, "check": "variants" }] }, - { "techs": ["tech_A", "tech_B"] }, - { "entities": [{ "class": "class_A", "number": 5, "check": "count" }, { "class": "class_B", "number": 5, "check": "variants" }] }, - { "entities": [{ "class": "class_A", "number": 5, "check": "count" }], "techs": ["tech_B"] } -]); - -// Two `civ`s, without and with a tech -template.requirements = { - "all": [ - { "any": [{ "civ": "athen" }, { "civ": "spart" }] } - ] -}; -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), []); -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), false); - -template.requirements = { - "all": [ - { "tech": "required_tech" }, - { "any": [{ "civ": "athen" }, { "civ": "spart" }] } - ] -}; -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["required_tech"] }]); -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), false); - -// Two `notciv`s, without and with a tech -template.requirements = { - "all": [ - { "any": [{ "notciv": "athen" }, { "notciv": "spart" }] } - ] -}; -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), false); -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), []); - -template.requirements = { - "all": [ - { "tech": "required_tech" }, - { "any": [{ "notciv": "athen" }, { "notciv": "spart" }] } - ] -}; -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), false); -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), [{ "techs": ["required_tech"] }]); - - -/** - * Complicated `any`s, part 1 - an `all` inside an `any`: - */ - -// Techs -template.requirements = { - "any": [ - { "all": [{ "tech": "tech_A" }, { "tech": "tech_B" }] }, - { "all": [{ "tech": "tech_C" }, { "tech": "tech_D" }] } - ] -}; -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [ - { "techs": ["tech_A", "tech_B"] }, - { "techs": ["tech_C", "tech_D"] } -]); - -// Techs and entities -template.requirements = { - "any": [ - { "all": [{ "tech": "tech_A" }, { "entity": { "class": "class_A", "number": 5 } }] }, - { "all": [{ "entity": { "class": "class_B", "numberOfTypes": 5 } }, { "tech": "tech_B" }] } - ] -}; -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [ - { "techs": ["tech_A"], "entities": [{ "class": "class_A", "number": 5, "check": "count" }] }, - { "entities": [{ "class": "class_B", "number": 5, "check": "variants" }], "techs": ["tech_B"] } -]); - -// Two `civ`s, without and with a tech -template.requirements = { - "any": [ - { "all": [{ "civ": "athen" }, { "civ": "spart" }] } - ] -}; -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), []); -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), false); - -template.requirements = { - "any": [ - { "tech": "required_tech" }, - { "all": [{ "civ": "athen" }, { "civ": "spart" }] } - ] -}; -// Note: these requirements don't really make sense, as the `any` makes the `civ`s in the the inner `all` irrelevant. -// We test it anyway as a precursor to later tests. -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["required_tech"] }]); -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), [{ "techs": ["required_tech"] }]); - -// Two `notciv`s, without and with a tech -template.requirements = { - "any": [ - { "all": [{ "notciv": "athen" }, { "notciv": "spart" }] } - ] -}; -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), false); -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), []); - -template.requirements = { - "any": [ - { "tech": "required_tech" }, - { "all": [{ "notciv": "athen" }, { "notciv": "spart" }] } - ] -}; -// Note: these requirements have a result that might seen unexpected at first glance. -// This is because the `notciv`s are rendered irrelevant by the `any`, and they have nothing else to operate on. -// We test it anyway as a precursor for later tests. -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["required_tech"] }]); -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), [{ "techs": ["required_tech"] }]); - -// Inner `all` has a tech and a `civ`/`notciv` -template.requirements = { - "any": [ - { "all": [{ "civ": "civA" }, { "tech": "tech1" }] }, - { "tech": "tech2" } - ] -}; -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civA"), [{ "techs": ["tech1"] }, { "techs": ["tech2"] }]); -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civB"), [{ "techs": ["tech2"] }]); - -template.requirements = { - "any": [ - { "all": [{ "notciv": "civA" }, { "tech": "tech1" }] }, - { "tech": "tech2" } - ] -}; -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civA"), [{ "techs": ["tech2"] }]); -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civB"), [{ "techs": ["tech1"] }, { "techs": ["tech2"] }]); - - -/** - * Complicated `any`s, part 2 - an `any` inside an `any`: - */ - -// Techs -template.requirements = { - "any": [ - { "any": [{ "tech": "tech_A" }, { "tech": "tech_B" }] }, - { "any": [{ "tech": "tech_C" }, { "tech": "tech_D" }] } - ] -}; -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [ - { "techs": ["tech_A"] }, - { "techs": ["tech_B"] }, - { "techs": ["tech_C"] }, - { "techs": ["tech_D"] } -]); - -// Techs and entities -template.requirements = { - "any": [ - { "any": [{ "tech": "tech_A" }, { "entity": { "class": "class_A", "number": 5 } }] }, - { "any": [{ "entity": { "class": "class_B", "numberOfTypes": 5 } }, { "tech": "tech_B" }] } - ] -}; -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [ - { "techs": ["tech_A"] }, - { "entities": [{ "class": "class_A", "number": 5, "check": "count" }] }, - { "entities": [{ "class": "class_B", "number": 5, "check": "variants" }] }, - { "techs": ["tech_B"] } -]); - -// Two `civ`s, without and with a tech -template.requirements = { - "any": [ - { "any": [{ "civ": "athen" }, { "civ": "spart" }] } - ] -}; -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), []); -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), false); - -template.requirements = { - "any": [ - { "tech": "required_tech" }, - { "any": [{ "civ": "athen" }, { "civ": "spart" }] } - ] -}; -// These requirements may not make sense, as the `civ`s are unable to restrict the requirements due to the outer `any` -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["required_tech"] }]); -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), [{ "techs": ["required_tech"] }]); - -// Two `notciv`s, without and with a tech -template.requirements = { - "any": [ - { "any": [{ "notciv": "athen" }, { "notciv": "spart" }] } - ] -}; -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), false); -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), []); - -template.requirements = { - "any": [ - { "tech": "required_tech" }, - { "any": [{ "notciv": "athen" }, { "notciv": "spart" }] } - ] -}; -// These requirements may not make sense, as the `notciv`s are made irrelevant by the outer `any` -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["required_tech"] }]); -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), [{ "techs": ["required_tech"] }]); - - -/** - * Further tests - */ - -template.requirements = { - "all": [ - { "tech": "tech1" }, - { "any": [{ "civ": "civA" }, { "civ": "civB" }] }, - { "notciv": "civC" } - ] -}; -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civA"), [{ "techs": ["tech1"] }]); -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civC"), false); - -template.requirements = { - "any": [ - { "all": [{ "civ": "civA" }, { "tech": "tech1" }] }, - { "all": [{ "civ": "civB" }, { "tech": "tech2" }] } - ] -}; -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civA"), [{ "techs": ["tech1"] }]); -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civB"), [{ "techs": ["tech2"] }]); -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civC"), false); - -template.requirements = { - "any": [ - { "all": [{ "civ": "civA" }, { "tech": "tech1" }] }, - { "all": [ - { "any": [{ "civ": "civB" }, { "civ": "civC" }] }, - { "tech": "tech2" } - ] } - ] -}; -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civA"), [{ "techs": ["tech1"] }]); -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civC"), [{ "techs": ["tech2"] }]); -TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civD"), false); Index: binaries/data/mods/public/simulation/components/tests/test_Technologies_effects.js =================================================================== --- /dev/null +++ binaries/data/mods/public/simulation/components/tests/test_Technologies_effects.js @@ -0,0 +1,45 @@ +// TODO: Move this to a folder of tests for GlobalScripts (once one is created) + + +// This tests the GetTechModifiedProperty function. + +let add = { + "Test_A": [{"add": 10, "affects": "Unit"}] +} + +let add_add = { + "Test_A": [{"add": 10, "affects": "Unit"}, {"add": 5, "affects": "Unit"}] +} + +let add_mul_add = { + "Test_A": [{"add": 10, "affects": "Unit"}, {"multiply": 2, "affects": "Unit"}, {"add": 5, "affects": "Unit"}] +} + +let add_replace = { + "Test_A": [{"add": 10, "affects": "Unit"}, {"replace": 10, "affects": "Unit"}] +} + +let replace_add = { + "Test_A": [{"replace": 10, "affects": "Unit"},{"add": 10, "affects": "Unit"}] +} + +let replace_replace = { + "Test_A": [{"replace": 10, "affects": "Unit"},{"replace": 30, "affects": "Unit"}] +} + +let replace_nonnum = { + "Test_A": [{"replace": "alpha", "affects": "Unit"}] +} + +TS_ASSERT_EQUALS(GetTechModifiedProperty(add, "Unit", "Test_A", 5), 15); +TS_ASSERT_EQUALS(GetTechModifiedProperty(add_add, "Unit", "Test_A", 5), 20); +TS_ASSERT_EQUALS(GetTechModifiedProperty(add_add, "Other", "Test_A", 5), 5); + + +TS_ASSERT_EQUALS(GetTechModifiedProperty(add_mul_add, "Unit", "Test_A", 5), 35); + +TS_ASSERT_EQUALS(GetTechModifiedProperty(add_replace, "Unit", "Test_A", 5), 10); +TS_ASSERT_EQUALS(GetTechModifiedProperty(replace_replace, "Unit", "Test_A", 5), 30); + +TS_ASSERT_EQUALS(GetTechModifiedProperty(replace_nonnum, "Unit", "Test_A", "beta"), "alpha"); + Index: binaries/data/mods/public/simulation/components/tests/test_Technologies_reqs.js =================================================================== --- /dev/null +++ binaries/data/mods/public/simulation/components/tests/test_Technologies_reqs.js @@ -0,0 +1,510 @@ +// TODO: Move this to a folder of tests for GlobalScripts (once one is created) + +// No requirements set in template +let template = {}; +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), []); + +/** + * First, the basics: + */ + +// Technology Requirement +template.requirements = { "tech": "expected_tech" }; +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["expected_tech"] }]); + +// Entity Requirement: Count of entities matching given class +template.requirements = { "entity": { "class": "Village", "number": 5 } }; +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "entities": [{ "class": "Village", "number": 5, "check": "count" }] }]); + +// Entity Requirement: Count of entities matching given class +template.requirements = { "entity": { "class": "Village", "numberOfTypes": 5 } }; +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "entities": [{ "class": "Village", "number": 5, "check": "variants" }] }]); + +// Single `civ` +template.requirements = { "civ": "athen" }; +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), []); +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "spart"), false); + +// Single `notciv` +template.requirements = { "notciv": "athen" }; +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), false); +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "spart"), []); + + +/** + * Basic `all`s: + */ + +// Multiple techs +template.requirements = { "all": [{ "tech": "tech_A" }, { "tech": "tech_B" }, { "tech": "tech_C" }] }; +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["tech_A", "tech_B", "tech_C"] }]); + +// Multiple entity definitions +template.requirements = { + "all": [ + { "entity": { "class": "class_A", "number": 5 } }, + { "entity": { "class": "class_B", "number": 5 } }, + { "entity": { "class": "class_C", "number": 5 } } + ] +}; +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), + [{ "entities": [{ "class": "class_A", "number": 5, "check": "count" }, { "class": "class_B", "number": 5, "check": "count" }, { "class": "class_C", "number": 5, "check": "count" }] }]); + +// A `tech` and an `entity` +template.requirements = { "all": [{ "tech": "tech_A" }, { "entity": { "class": "class_B", "number": 5, "check": "count" } }] }; +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["tech_A"], "entities": [{ "class": "class_B", "number": 5, "check": "count" }] }]); + +// Multiple `civ`s +template.requirements = { "all": [{ "civ": "civ_A"}, { "civ": "civ_B"}, { "civ": "civ_C"}] }; +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_A"), []); +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_B"), []); +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_C"), []); +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_D"), false); + +// Multiple `notciv`s +template.requirements = { "all": [{ "notciv": "civ_A"}, { "notciv": "civ_B"}, { "notciv": "civ_C"}] }; +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_A"), false); +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_B"), false); +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_C"), false); +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_D"), []); + +// A `civ` with a tech/entity +template.requirements = { "all": [{ "civ": "athen" }, { "tech": "expected_tech" }] }; +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["expected_tech"] }]); +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "spart"), false); + +template.requirements = { "all": [{ "civ": "athen" }, { "entity": { "class": "Village", "number": 5 } }] }; +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "entities": [{ "class": "Village", "number": 5, "check": "count" }] }]); +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "spart"), false); + +template.requirements = { "all": [{ "civ": "athen" }, { "entity": { "class": "Village", "numberOfTypes": 5 } }] }; +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "entities": [{ "class": "Village", "number": 5, "check": "variants" }] }]); +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "spart"), false); + +// A `notciv` with a tech/entity +template.requirements = { "all": [{ "notciv": "athen" }, { "tech": "expected_tech" }] }; +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), false); +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "spart"), [{ "techs": ["expected_tech"] }]); + +template.requirements = { "all": [{ "notciv": "athen" }, { "entity": { "class": "Village", "number": 5 } }] }; +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), false); +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "spart"), [{ "entities": [{ "class": "Village", "number": 5, "check": "count" }] }]); + +template.requirements = { "all": [{ "notciv": "athen" }, { "entity": { "class": "Village", "numberOfTypes": 5 } }] }; +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), false); +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "spart"), [{ "entities": [{ "class": "Village", "number": 5, "check": "variants" }] }]); + + +/** + * Basic `any`s: + */ + +// Multiple techs +template.requirements = { "any": [{ "tech": "tech_A" }, { "tech": "tech_B" }, { "tech": "tech_C" }] }; +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["tech_A"] }, { "techs": ["tech_B"] }, { "techs": ["tech_C"] }]); + +// Multiple entity definitions +template.requirements = { + "any": [ + { "entity": { "class": "class_A", "number": 5 } }, + { "entity": { "class": "class_B", "number": 5 } }, + { "entity": { "class": "class_C", "number": 5 } } + ] +}; +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [ + { "entities": [{ "class": "class_A", "number": 5, "check": "count" }] }, + { "entities": [{ "class": "class_B", "number": 5, "check": "count" }] }, + { "entities": [{ "class": "class_C", "number": 5, "check": "count" }] } +]); + +// A tech or an entity +template.requirements = { "any": [{ "tech": "tech_A" }, { "entity": { "class": "class_B", "number": 5, "check": "count" } }] }; +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["tech_A"] }, { "entities": [{ "class": "class_B", "number": 5, "check": "count" }] }]); + +// Multiple `civ`s +template.requirements = { "any": [{ "civ": "civ_A" }, { "civ": "civ_B" }, { "civ": "civ_C" }] }; +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_A"), []); +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_B"), []); +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_C"), []); +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_D"), false); + +// Multiple `notciv`s +template.requirements = { "any": [{ "notciv": "civ_A" }, { "notciv": "civ_B" }, { "notciv": "civ_C" }] }; +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_A"), false); +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_B"), false); +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_C"), false); +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_D"), []); + +// A `civ` or a tech/entity +template.requirements = { "any": [{ "civ": "athen" }, { "tech": "expected_tech" }] }; +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), []); +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "spart"), [{ "techs": ["expected_tech"] }]); + +template.requirements = { "any": [{ "civ": "athen" }, { "entity": { "class": "Village", "number": 5 } }] }; +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), []); +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "spart"), [{ "entities": [{ "class": "Village", "number": 5, "check": "count" }] }]); + +template.requirements = { "any": [{ "civ": "athen" }, { "entity": { "class": "Village", "numberOfTypes": 5 } }] }; +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), []); +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "spart"), [{ "entities": [{ "class": "Village", "number": 5, "check": "variants" }] }]); + +// A `notciv` or a tech +template.requirements = { "any": [{ "notciv": "athen" }, { "tech": "expected_tech" }] }; +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), false); +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "spart"), [{ "techs": ["expected_tech"] }]); + +template.requirements = { "any": [{ "notciv": "athen" }, { "entity": { "class": "Village", "number": 5 } }] }; +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), false); +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "spart"), [{ "entities": [{ "class": "Village", "number": 5, "check": "count" }] }]); + +template.requirements = { "any": [{ "notciv": "athen" }, { "entity": { "class": "Village", "numberOfTypes": 5 } }] }; +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), false); +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "spart"), [{ "entities": [{ "class": "Village", "number": 5, "check": "variants" }] }]); + + +/** + * Complicated `all`s, part 1 - an `all` inside an `all`: + */ + +// Techs +template.requirements = { + "all": [ + { "all": [{ "tech": "tech_A" }, { "tech": "tech_B" }] }, + { "all": [{ "tech": "tech_C" }, { "tech": "tech_D" }] } + ] +}; +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["tech_A", "tech_B", "tech_C", "tech_D"] }]); + +// Techs and entities +template.requirements = { + "all": [ + { "all": [{ "tech": "tech_A" }, { "entity": { "class": "class_A", "number": 5 } }] }, + { "all": [{ "entity": { "class": "class_B", "numberOfTypes": 5 } }, { "tech": "tech_B" }] } + ] +}; +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ + "techs": ["tech_A", "tech_B"], + "entities": [{ "class": "class_A", "number": 5, "check": "count" }, { "class": "class_B", "number": 5, "check": "variants" }] +}]); + +// Two `civ`s, without and with a tech +template.requirements = { + "all": [ + { "all": [{ "civ": "athen" }, { "civ": "spart" }] } + ] +}; +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), []); +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), false); + +template.requirements = { + "all": [ + { "tech": "required_tech" }, + { "all": [{ "civ": "athen" }, { "civ": "spart" }] } + ] +}; +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["required_tech"] }]); +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), false); + +// Two `notciv`s, without and with a tech +template.requirements = { + "all": [ + { "all": [{ "notciv": "athen" }, { "notciv": "spart" }] } + ] +}; +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), false); +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), []); + +template.requirements = { + "all": [ + { "tech": "required_tech" }, + { "all": [{ "notciv": "athen" }, { "notciv": "spart" }] } + ] +}; +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), false); +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), [{ "techs": ["required_tech"] }]); + +// Inner `all` has a tech and a `civ`/`notciv` +template.requirements = { + "all": [ + { "all": [{ "tech": "tech_A" }, { "civ": "maur" }] }, + { "tech": "tech_B" } + ] +}; +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["tech_B"] }]); +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), [{ "techs": ["tech_A", "tech_B"] }]); + +template.requirements = { + "all": [ + { "all": [{ "tech": "tech_A" }, { "notciv": "maur" }] }, + { "tech": "tech_B" } + ] +}; +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["tech_A", "tech_B"] }]); +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), [{ "techs": ["tech_B"] }]); + + +/** + * Complicated `all`s, part 2 - an `any` inside an `all`: + */ + +// Techs +template.requirements = { + "all": [ + { "any": [{ "tech": "tech_A" }, { "tech": "tech_B" }] }, + { "any": [{ "tech": "tech_C" }, { "tech": "tech_D" }] } + ] +}; +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [ + { "techs": ["tech_A", "tech_C"] }, + { "techs": ["tech_A", "tech_D"] }, + { "techs": ["tech_B", "tech_C"] }, + { "techs": ["tech_B", "tech_D"] } +]); + +// Techs and entities +template.requirements = { + "all": [ + { "any": [{ "tech": "tech_A" }, { "entity": { "class": "class_A", "number": 5 } }] }, + { "any": [{ "entity": { "class": "class_B", "numberOfTypes": 5 } }, { "tech": "tech_B" }] } + ] +}; +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [ + { "techs": ["tech_A"], "entities": [{ "class": "class_B", "number": 5, "check": "variants" }] }, + { "techs": ["tech_A", "tech_B"] }, + { "entities": [{ "class": "class_A", "number": 5, "check": "count" }, { "class": "class_B", "number": 5, "check": "variants" }] }, + { "entities": [{ "class": "class_A", "number": 5, "check": "count" }], "techs": ["tech_B"] } +]); + +// Two `civ`s, without and with a tech +template.requirements = { + "all": [ + { "any": [{ "civ": "athen" }, { "civ": "spart" }] } + ] +}; +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), []); +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), false); + +template.requirements = { + "all": [ + { "tech": "required_tech" }, + { "any": [{ "civ": "athen" }, { "civ": "spart" }] } + ] +}; +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["required_tech"] }]); +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), false); + +// Two `notciv`s, without and with a tech +template.requirements = { + "all": [ + { "any": [{ "notciv": "athen" }, { "notciv": "spart" }] } + ] +}; +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), false); +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), []); + +template.requirements = { + "all": [ + { "tech": "required_tech" }, + { "any": [{ "notciv": "athen" }, { "notciv": "spart" }] } + ] +}; +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), false); +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), [{ "techs": ["required_tech"] }]); + + +/** + * Complicated `any`s, part 1 - an `all` inside an `any`: + */ + +// Techs +template.requirements = { + "any": [ + { "all": [{ "tech": "tech_A" }, { "tech": "tech_B" }] }, + { "all": [{ "tech": "tech_C" }, { "tech": "tech_D" }] } + ] +}; +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [ + { "techs": ["tech_A", "tech_B"] }, + { "techs": ["tech_C", "tech_D"] } +]); + +// Techs and entities +template.requirements = { + "any": [ + { "all": [{ "tech": "tech_A" }, { "entity": { "class": "class_A", "number": 5 } }] }, + { "all": [{ "entity": { "class": "class_B", "numberOfTypes": 5 } }, { "tech": "tech_B" }] } + ] +}; +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [ + { "techs": ["tech_A"], "entities": [{ "class": "class_A", "number": 5, "check": "count" }] }, + { "entities": [{ "class": "class_B", "number": 5, "check": "variants" }], "techs": ["tech_B"] } +]); + +// Two `civ`s, without and with a tech +template.requirements = { + "any": [ + { "all": [{ "civ": "athen" }, { "civ": "spart" }] } + ] +}; +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), []); +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), false); + +template.requirements = { + "any": [ + { "tech": "required_tech" }, + { "all": [{ "civ": "athen" }, { "civ": "spart" }] } + ] +}; +// Note: these requirements don't really make sense, as the `any` makes the `civ`s in the the inner `all` irrelevant. +// We test it anyway as a precursor to later tests. +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["required_tech"] }]); +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), [{ "techs": ["required_tech"] }]); + +// Two `notciv`s, without and with a tech +template.requirements = { + "any": [ + { "all": [{ "notciv": "athen" }, { "notciv": "spart" }] } + ] +}; +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), false); +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), []); + +template.requirements = { + "any": [ + { "tech": "required_tech" }, + { "all": [{ "notciv": "athen" }, { "notciv": "spart" }] } + ] +}; +// Note: these requirements have a result that might seen unexpected at first glance. +// This is because the `notciv`s are rendered irrelevant by the `any`, and they have nothing else to operate on. +// We test it anyway as a precursor for later tests. +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["required_tech"] }]); +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), [{ "techs": ["required_tech"] }]); + +// Inner `all` has a tech and a `civ`/`notciv` +template.requirements = { + "any": [ + { "all": [{ "civ": "civA" }, { "tech": "tech1" }] }, + { "tech": "tech2" } + ] +}; +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civA"), [{ "techs": ["tech1"] }, { "techs": ["tech2"] }]); +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civB"), [{ "techs": ["tech2"] }]); + +template.requirements = { + "any": [ + { "all": [{ "notciv": "civA" }, { "tech": "tech1" }] }, + { "tech": "tech2" } + ] +}; +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civA"), [{ "techs": ["tech2"] }]); +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civB"), [{ "techs": ["tech1"] }, { "techs": ["tech2"] }]); + + +/** + * Complicated `any`s, part 2 - an `any` inside an `any`: + */ + +// Techs +template.requirements = { + "any": [ + { "any": [{ "tech": "tech_A" }, { "tech": "tech_B" }] }, + { "any": [{ "tech": "tech_C" }, { "tech": "tech_D" }] } + ] +}; +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [ + { "techs": ["tech_A"] }, + { "techs": ["tech_B"] }, + { "techs": ["tech_C"] }, + { "techs": ["tech_D"] } +]); + +// Techs and entities +template.requirements = { + "any": [ + { "any": [{ "tech": "tech_A" }, { "entity": { "class": "class_A", "number": 5 } }] }, + { "any": [{ "entity": { "class": "class_B", "numberOfTypes": 5 } }, { "tech": "tech_B" }] } + ] +}; +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [ + { "techs": ["tech_A"] }, + { "entities": [{ "class": "class_A", "number": 5, "check": "count" }] }, + { "entities": [{ "class": "class_B", "number": 5, "check": "variants" }] }, + { "techs": ["tech_B"] } +]); + +// Two `civ`s, without and with a tech +template.requirements = { + "any": [ + { "any": [{ "civ": "athen" }, { "civ": "spart" }] } + ] +}; +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), []); +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), false); + +template.requirements = { + "any": [ + { "tech": "required_tech" }, + { "any": [{ "civ": "athen" }, { "civ": "spart" }] } + ] +}; +// These requirements may not make sense, as the `civ`s are unable to restrict the requirements due to the outer `any` +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["required_tech"] }]); +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), [{ "techs": ["required_tech"] }]); + +// Two `notciv`s, without and with a tech +template.requirements = { + "any": [ + { "any": [{ "notciv": "athen" }, { "notciv": "spart" }] } + ] +}; +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), false); +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), []); + +template.requirements = { + "any": [ + { "tech": "required_tech" }, + { "any": [{ "notciv": "athen" }, { "notciv": "spart" }] } + ] +}; +// These requirements may not make sense, as the `notciv`s are made irrelevant by the outer `any` +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["required_tech"] }]); +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), [{ "techs": ["required_tech"] }]); + + +/** + * Further tests + */ + +template.requirements = { + "all": [ + { "tech": "tech1" }, + { "any": [{ "civ": "civA" }, { "civ": "civB" }] }, + { "notciv": "civC" } + ] +}; +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civA"), [{ "techs": ["tech1"] }]); +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civC"), false); + +template.requirements = { + "any": [ + { "all": [{ "civ": "civA" }, { "tech": "tech1" }] }, + { "all": [{ "civ": "civB" }, { "tech": "tech2" }] } + ] +}; +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civA"), [{ "techs": ["tech1"] }]); +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civB"), [{ "techs": ["tech2"] }]); +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civC"), false); + +template.requirements = { + "any": [ + { "all": [{ "civ": "civA" }, { "tech": "tech1" }] }, + { "all": [ + { "any": [{ "civ": "civB" }, { "civ": "civC" }] }, + { "tech": "tech2" } + ] } + ] +}; +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civA"), [{ "techs": ["tech1"] }]); +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civC"), [{ "techs": ["tech2"] }]); +TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civD"), false); Index: binaries/data/mods/public/simulation/components/tests/test_VisionSharing.js =================================================================== --- binaries/data/mods/public/simulation/components/tests/test_VisionSharing.js +++ binaries/data/mods/public/simulation/components/tests/test_VisionSharing.js @@ -2,6 +2,7 @@ Engine.LoadHelperScript("ValueModification.js"); Engine.LoadComponentScript("interfaces/GarrisonHolder.js"); Engine.LoadComponentScript("interfaces/TechnologyManager.js"); +Engine.LoadComponentScript("interfaces/ModifiersManager.js"); Engine.LoadComponentScript("interfaces/AuraManager.js"); Engine.LoadComponentScript("interfaces/Timer.js"); Engine.LoadComponentScript("interfaces/VisionSharing.js"); @@ -116,6 +117,9 @@ AddMock(14, IID_TechnologyManager, { "CanProduce": entity => false, +}); + +AddMock(14, IID_ModifiersManager, { "ApplyModificationsTemplate": (valueName, curValue, template) => curValue }); @@ -125,8 +129,12 @@ AddMock(14, IID_TechnologyManager, { "CanProduce": entity => entity == "special/spy", +}); + +AddMock(14, IID_ModifiersManager, { "ApplyModificationsTemplate": (valueName, curValue, template) => curValue }); + AddMock(14, IID_Player, { "GetSpyCostMultiplier": () => 1, "TrySubtractResources": costs => false Index: binaries/data/mods/public/simulation/helpers/Cheat.js =================================================================== --- binaries/data/mods/public/simulation/helpers/Cheat.js +++ binaries/data/mods/public/simulation/helpers/Cheat.js @@ -69,7 +69,23 @@ cmpProductionQueue.SpawnUnits(input.templates[i % input.templates.length], 1, null); return; case "fastactions": - cmpPlayer.SetCheatTimeMultiplier((cmpPlayer.GetCheatTimeMultiplier() == 1) ? 0.01 : 1); + let cmpModManager = Engine.QueryInterface(playerEnt, IID_ModifiersManager); + if (!cmpModManager) + return; + if (cmpModManager.HasAnyGlobalModifierWithID("cheat/fastactions")) + { + warn("here"); + cmpModManager.RemoveGlobalModifiers("cheat/fastactions") + } + else + cmpModManager.AddGlobalModifiers("cheat/fastactions", 1000, { + "Cost/BuildTime" : {"affects": [["Structure"], ["Unit"]], "multiply": 0.01}, + "ResourceGatherer/BaseSpeed" : {"affects": [["Structure"], ["Unit"]], "multiply": 1000.0}, + "Pack/Time" : {"affects": [["Structure"], ["Unit"]], "multiply": 0.01}, + "Upgrade/Time" : {"affects": [["Structure"], ["Unit"]], "multiply": 0.01}, + "ProductionQueue/TechCostMultiplier/time" : {"affects": [["Structure"], ["Unit"]], "multiply": 0.01} + } + ); return; case "changespeed": cmpPlayer.SetCheatTimeMultiplier(input.parameter); Index: binaries/data/mods/public/simulation/helpers/ValueModification.js =================================================================== --- binaries/data/mods/public/simulation/helpers/ValueModification.js +++ binaries/data/mods/public/simulation/helpers/ValueModification.js @@ -3,9 +3,9 @@ function ApplyValueModificationsToEntity(tech_type, current_value, entity) { let value = current_value; - let cmpTechnologyManager = QueryOwnerInterface(entity, IID_TechnologyManager); - if (cmpTechnologyManager) - value = cmpTechnologyManager.ApplyModifications(tech_type, current_value, entity); + let cmpModManager = QueryOwnerInterface(entity, IID_ModifiersManager); + if (cmpModManager) + value = cmpModManager.ApplyModifications(tech_type, current_value, entity); let cmpAuraManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_AuraManager); if (!cmpAuraManager) @@ -24,9 +24,9 @@ function ApplyValueModificationsToTemplate(tech_type, current_value, playerID, template) { let value = current_value; - let cmpTechnologyManager = QueryPlayerIDInterface(playerID, IID_TechnologyManager); - if (cmpTechnologyManager) - value = cmpTechnologyManager.ApplyModificationsTemplate(tech_type, current_value, template); + let cmpModManager = QueryPlayerIDInterface(playerID, IID_ModifiersManager); + if (cmpModManager) + value = cmpModManager.ApplyModificationsTemplate(tech_type, current_value, template); let cmpAuraManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_AuraManager); if (!cmpAuraManager) Index: binaries/data/mods/public/simulation/templates/special/player.xml =================================================================== --- binaries/data/mods/public/simulation/templates/special/player.xml +++ binaries/data/mods/public/simulation/templates/special/player.xml @@ -57,6 +57,7 @@ Player Player + unlock_shared_los unlock_shared_dropsites