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 @@ -35,7 +35,6 @@ this.gatherRateMultiplier = 1; this.tradeRateMultiplier = 1; this.cheatsEnabled = false; - this.cheatTimeMultiplier = 1; this.heroes = []; this.resourceNames = {}; this.disabledTemplates = {}; @@ -751,16 +750,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,7 +213,6 @@ return; } - var modifiedComponents = {}; this.researchedTechs[tech] = template; // store the modifications in an easy to access structure if (template.modifications) @@ -288,8 +235,6 @@ for (var i in template.modifications) { var modification = template.modifications[i]; - if (!this.modifications[modification.value]) - this.modifications[modification.value] = []; var modAffects = affects.slice(); if (modification.affects) @@ -306,12 +251,17 @@ if (j !== "value" && j !== "affects") mod[j] = modification[j]; - this.modifications[modification.value].push(mod); - var component = modification.value.split("/")[0]; - if (!modifiedComponents[component]) - modifiedComponents[component] = []; - modifiedComponents[component].push(modification.value); - this.modificationCache[modification.value] = {}; + let priorityLevel = template.priority || 5; + let supersedes = template.supersedes || undefined; + while (supersedes) + { + ++priorityLevel; + let supertemplate = this.GetTechnologyTemplate(supersedes); + supersedes = supertemplate.supersedes || undefined; + } + + let cmpModManager = Engine.QueryInterface(this.entity, IID_ModifiersManager); + cmpModManager.AddGlobalModifier(modification.value, "tech/"+tech, priorityLevel, mod); } } @@ -353,51 +303,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 @@ -442,12 +354,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 @@ -141,8 +141,7 @@ { hasCosts = true; let cmpPlayer = QueryPlayerIDInterface(this.owner, IID_Player); - cost.time = ApplyValueModificationsToTemplate("Upgrade/Time", +choice.Time, this.owner, entType) * - cmpPlayer.GetCheatTimeMultiplier(); + cost.time = ApplyValueModificationsToTemplate("Upgrade/Time", +choice.Time, this.owner, entType); } ret.push({ "entity": entType, @@ -276,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_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/data/settings/victory_times.json~catchup to SVN =================================================================== --- /dev/null +++ binaries/data/mods/public/simulation/data/settings/victory_times.json~catchup to SVN @@ -0,0 +1,4 @@ +{ + "Times": [0, 1, 3, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 75, 90, 105, 120], + "Default": 20 +} \ No newline at end of file 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