Index: binaries/data/mods/public/simulation/components/Attack.js =================================================================== --- binaries/data/mods/public/simulation/components/Attack.js +++ binaries/data/mods/public/simulation/components/Attack.js @@ -749,16 +749,14 @@ Attack.prototype.OnValueModification = function(msg) { - if (msg.component != "Attack") - return; - - let cmpUnitAI = Engine.QueryInterface(this.entity, IID_UnitAI); - if (!cmpUnitAI) - return; - - if (this.GetAttackTypes().some(type => - msg.valueNames.indexOf("Attack/" + type + "/MaxRange") != -1)) - cmpUnitAI.UpdateRangeQueries(); + if (this.GetAttackTypes().some(type => { + if (msg.valueNames.indexOf("Attack/" + type + "/MaxRange") !== -1) + return true; + if (msg.valueNames.indexOf("Attack/" + type + "/MinRange") !== -1) + return true; + return false; + })) + Engine.PostMessage(this.entity, MT_AttackRangeChanged); }; Attack.prototype.GetRangeOverlays = function(type = "Ranged") Index: binaries/data/mods/public/simulation/components/AutoBuildable.js =================================================================== --- binaries/data/mods/public/simulation/components/AutoBuildable.js +++ binaries/data/mods/public/simulation/components/AutoBuildable.js @@ -67,9 +67,6 @@ OnValueModification(msg) { - if (msg.component != "AutoBuildable") - return; - this.UpdateRate(); } Index: binaries/data/mods/public/simulation/components/Builder.js =================================================================== --- binaries/data/mods/public/simulation/components/Builder.js +++ binaries/data/mods/public/simulation/components/Builder.js @@ -196,7 +196,7 @@ Builder.prototype.OnValueModification = function(msg) { - if (msg.component != "Builder" || !msg.valueNames.some(name => name.endsWith('_string'))) + if (!msg.valueNames.some(name => name.endsWith('_string'))) return; // Token changes may require selection updates. Index: binaries/data/mods/public/simulation/components/BuildingAI.js =================================================================== --- binaries/data/mods/public/simulation/components/BuildingAI.js +++ binaries/data/mods/public/simulation/components/BuildingAI.js @@ -83,13 +83,10 @@ }; /** - * React on Attack value modifications, as it might influence the range. + * React on changes to the attack range. */ -BuildingAI.prototype.OnValueModification = function(msg) +BuildingAI.prototype.OnAttackRangeChanged = function(msg) { - if (msg.component != "Attack") - return; - this.targetUnits = []; this.SetupRangeQuery(); this.SetupGaiaRangeQuery(); Index: binaries/data/mods/public/simulation/components/Capturable.js =================================================================== --- binaries/data/mods/public/simulation/components/Capturable.js +++ binaries/data/mods/public/simulation/components/Capturable.js @@ -307,8 +307,7 @@ Capturable.prototype.OnValueModification = function(msg) { - if (msg.component == "Capturable") - this.UpdateCachedValuesAndNotify(); + this.UpdateCachedValuesAndNotify(); }; Capturable.prototype.OnGarrisonedUnitsChanged = function(msg) 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 @@ -62,9 +62,6 @@ Cost.prototype.OnValueModification = function(msg) { - if (msg.component != "Cost") - return; - // Foundations shouldn't have a pop cost. var cmpFoundation = Engine.QueryInterface(this.entity, IID_Foundation); if (cmpFoundation) Index: binaries/data/mods/public/simulation/components/GarrisonHolder.js =================================================================== --- binaries/data/mods/public/simulation/components/GarrisonHolder.js +++ binaries/data/mods/public/simulation/components/GarrisonHolder.js @@ -526,9 +526,6 @@ GarrisonHolder.prototype.OnValueModification = function(msg) { - if (msg.component != "GarrisonHolder") - return; - if (msg.valueNames.indexOf("GarrisonHolder/List/_string") !== -1) { this.allowedClasses = ApplyValueModificationsToEntity("GarrisonHolder/List/_string", this.template.List._string, this.entity); Index: binaries/data/mods/public/simulation/components/Heal.js =================================================================== --- binaries/data/mods/public/simulation/components/Heal.js +++ binaries/data/mods/public/simulation/components/Heal.js @@ -265,14 +265,8 @@ Heal.prototype.OnValueModification = function(msg) { - if (msg.component != "Heal" || msg.valueNames.indexOf("Heal/Range") === -1) - return; - - let cmpUnitAI = Engine.QueryInterface(this.entity, IID_UnitAI); - if (!cmpUnitAI) - return; - - cmpUnitAI.UpdateRangeQueries(); + if (msg.valueNames.indexOf("Heal/Range") !== -1) + Engine.PostMessage(this.entity, MT_AttackRangeChanged); }; Engine.RegisterComponentType(IID_Heal, "Heal", Heal); Index: binaries/data/mods/public/simulation/components/Health.js =================================================================== --- binaries/data/mods/public/simulation/components/Health.js +++ binaries/data/mods/public/simulation/components/Health.js @@ -484,8 +484,7 @@ Health.prototype.OnValueModification = function(msg) { - if (msg.component == "Health") - this.RecalculateValues(); + this.RecalculateValues(); }; Health.prototype.OnOwnershipChanged = function(msg) Index: binaries/data/mods/public/simulation/components/ModifiersManager.js =================================================================== --- binaries/data/mods/public/simulation/components/ModifiersManager.js +++ binaries/data/mods/public/simulation/components/ModifiersManager.js @@ -60,7 +60,7 @@ this.SendPlayerModifierMessages(propertyName, cmpPlayer.GetPlayerID()); } else - Engine.PostMessage(entity, MT_ValueModification, { "entities": [entity], "component": propertyName.split("/")[0], "valueNames": [propertyName] }); + Engine.PostValueModification(entity, { "entities": [entity], "component": propertyName.split("/")[0], "valueNames": [propertyName] }) }; ModifiersManager.prototype.SendPlayerModifierMessages = function(propertyName, player) @@ -71,7 +71,7 @@ // TODO: improve on this let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); let ents = cmpRangeManager.GetEntitiesByPlayer(player); - Engine.BroadcastMessage(MT_ValueModification, { "entities": ents, "component": propertyName.split("/")[0], "valueNames": [propertyName] }); + ents.forEach(ent => Engine.PostValueModification(ent, { "entities": [ent], "component": propertyName.split("/")[0], "valueNames": [propertyName] })); }; ModifiersManager.prototype.InvalidatePlayerEntCache = function(valueCache, propertyName, entsMap) @@ -251,7 +251,7 @@ } for (let component in modifiedComponents) - Engine.PostMessage(msg.entity, MT_ValueModification, { "entities": [msg.entity], "component": component, "valueNames": modifiedComponents[component] }); + Engine.PostValueModification(msg.entity, { "entities": [msg.entity], "component": component, "valueNames": modifiedComponents[component] }); }; /** 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 @@ -856,9 +856,6 @@ Player.prototype.OnValueModification = function(msg) { - if (msg.component != "Player") - return; - if (msg.valueNames.indexOf("Player/SpyCostMultiplier") != -1) this.spyCostMultiplier = ApplyValueModificationsToEntity("Player/SpyCostMultiplier", +this.template.SpyCostMultiplier, this.entity); Index: binaries/data/mods/public/simulation/components/Population.js =================================================================== --- binaries/data/mods/public/simulation/components/Population.js +++ binaries/data/mods/public/simulation/components/Population.js @@ -46,9 +46,6 @@ Population.prototype.OnValueModification = function(msg) { - if (msg.component != "Population") - return; - // Foundations shouldn't give a pop bonus. if (Engine.QueryInterface(this.entity, IID_Foundation)) return; Index: binaries/data/mods/public/simulation/components/Promotion.js =================================================================== --- binaries/data/mods/public/simulation/components/Promotion.js +++ binaries/data/mods/public/simulation/components/Promotion.js @@ -131,11 +131,9 @@ Promotion.prototype.OnValueModification = function(msg) { - if (msg.component != "Promotion") - return; - this.ComputeTrickleRate(); this.IncreaseXp(0); + Engine.PostMessage(this.entity, MT_PromotionReqChanged); }; Engine.RegisterComponentType(IID_Promotion, "Promotion", Promotion); Index: binaries/data/mods/public/simulation/components/RangeOverlayManager.js =================================================================== --- binaries/data/mods/public/simulation/components/RangeOverlayManager.js +++ binaries/data/mods/public/simulation/components/RangeOverlayManager.js @@ -69,14 +69,10 @@ this.RegenerateRangeOverlays(false); }; -RangeOverlayManager.prototype.OnValueModification = function(msg) +RangeOverlayManager.prototype.OnAttackRangeChanged = function(msg) { - if (msg.valueNames.indexOf("Heal/Range") == -1 && - msg.valueNames.indexOf("Attack/Ranged/MinRange") == -1 && - msg.valueNames.indexOf("Attack/Ranged/MaxRange") == -1) - return; - - this.UpdateRangeOverlays(msg.component); + this.UpdateRangeOverlays("Attack"); + this.UpdateRangeOverlays("Heal"); this.RegenerateRangeOverlays(false); }; 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 @@ -469,9 +469,6 @@ // and when our owner change because owners can had different techs. ResourceGatherer.prototype.OnValueModification = function(msg) { - if (msg.component != "ResourceGatherer") - return; - // NB: at the moment, 0 A.D. always uses the fast path, the other is mod support. if (msg.valueNames.length === 1) { Index: binaries/data/mods/public/simulation/components/ResourceSupply.js =================================================================== --- binaries/data/mods/public/simulation/components/ResourceSupply.js +++ binaries/data/mods/public/simulation/components/ResourceSupply.js @@ -436,9 +436,6 @@ */ ResourceSupply.prototype.OnValueModification = function(msg) { - if (msg.component != "ResourceSupply") - return; - this.RecalculateValues(); }; Index: binaries/data/mods/public/simulation/components/ResourceTrickle.js =================================================================== --- binaries/data/mods/public/simulation/components/ResourceTrickle.js +++ binaries/data/mods/public/simulation/components/ResourceTrickle.js @@ -57,9 +57,6 @@ ResourceTrickle.prototype.OnValueModification = function(msg) { - if (msg.component != "ResourceTrickle") - return; - this.CheckTimer(); }; Index: binaries/data/mods/public/simulation/components/Trainer.js =================================================================== --- binaries/data/mods/public/simulation/components/Trainer.js +++ binaries/data/mods/public/simulation/components/Trainer.js @@ -675,18 +675,14 @@ this.CalculateEntitiesMap(); }; -Trainer.prototype.OnValueModification = function(msg) -{ - // If the promotion requirements of units is changed, - // update the entities list so that automatically promoted units are shown - // appropriately in the list. - if (msg.component != "Promotion" && (msg.component != "Trainer" || - !msg.valueNames.some(val => val.startsWith("Trainer/Entities/")))) - return; - if (msg.entities.indexOf(this.entity) === -1) - return; +Trainer.prototype.OnPromotionReqChanged = function() +{ + this.OnValueModification(); +}; +Trainer.prototype.OnValueModification = function() +{ // This also updates the queued production if necessary. this.CalculateEntitiesMap(); Index: binaries/data/mods/public/simulation/components/Treasure.js =================================================================== --- binaries/data/mods/public/simulation/components/Treasure.js +++ binaries/data/mods/public/simulation/components/Treasure.js @@ -103,8 +103,6 @@ Treasure.prototype.OnValueModification = function(msg) { - if (msg.component != "Treasure") - return; this.ComputeReward(); }; Index: binaries/data/mods/public/simulation/components/UnitAI.js =================================================================== --- binaries/data/mods/public/simulation/components/UnitAI.js +++ binaries/data/mods/public/simulation/components/UnitAI.js @@ -4344,6 +4344,11 @@ this.UnitFsm.ProcessMessage(this, { "type": "PackFinished", "packed": msg.packed }); }; +UnitAI.prototype.OnAttackRangeChanged = function(msg) +{ + this.UpdateRangeQueries(); +}; + /** * A general function to process messages sent from components. * @param {string} type - The type of message to process. Index: binaries/data/mods/public/simulation/components/Upkeep.js =================================================================== --- binaries/data/mods/public/simulation/components/Upkeep.js +++ binaries/data/mods/public/simulation/components/Upkeep.js @@ -97,9 +97,6 @@ Upkeep.prototype.OnValueModification = function(msg) { - if (msg.component != "Upkeep") - return; - this.CheckTimer(); }; Index: binaries/data/mods/public/simulation/components/interfaces/Attack.js =================================================================== --- /dev/null +++ binaries/data/mods/public/simulation/components/interfaces/Attack.js @@ -0,0 +1,4 @@ +/** + * Message sent from Attack/Heal component when the attack range changes. + */ +Engine.RegisterMessageType("AttackRangeChanged"); Index: binaries/data/mods/public/simulation/components/interfaces/Promotion.js =================================================================== --- binaries/data/mods/public/simulation/components/interfaces/Promotion.js +++ binaries/data/mods/public/simulation/components/interfaces/Promotion.js @@ -5,3 +5,9 @@ * sent from Promotion component whenever the experience changes. */ Engine.RegisterMessageType("ExperienceChanged"); + +/** + * Message of the form {} + * sent from Promotion component when the Promotion requirements change. + */ +Engine.RegisterMessageType("PromotionReqChanged"); Index: binaries/data/mods/public/simulation/components/tests/test_Population.js =================================================================== --- binaries/data/mods/public/simulation/components/tests/test_Population.js +++ binaries/data/mods/public/simulation/components/tests/test_Population.js @@ -46,8 +46,7 @@ cmpPlayer = AddMock(player, IID_Player, { "AddPopulationBonuses": () => TS_ASSERT(false) }); -cmpPopulation.OnValueModification({ "component": "bogus" }); -cmpPopulation.OnValueModification({ "component": "Population" }); +cmpPopulation.OnValueModification(); // Test changes. AddMock(entity, IID_Ownership, { @@ -65,11 +64,11 @@ // Foundations don't count yet. AddMock(entity, IID_Foundation, {}); -cmpPopulation.OnValueModification({ "component": "Population" }); +cmpPopulation.OnValueModification(); TS_ASSERT_EQUALS(spy._called, 0); DeleteMock(entity, IID_Foundation); -cmpPopulation.OnValueModification({ "component": "Population" }); +cmpPopulation.OnValueModification(); TS_ASSERT_EQUALS(spy._called, 1); // Reset to no bonus. @@ -81,7 +80,7 @@ (valueName, currentValue, entity) => currentValue + difference ); spy = new Spy(cmpPlayer, "AddPopulationBonuses"); -cmpPopulation.OnValueModification({ "component": "Population" }); +cmpPopulation.OnValueModification(); TS_ASSERT_EQUALS(spy._called, 1); // Test negative change. @@ -95,7 +94,7 @@ }); spy = new Spy(cmpPlayer, "AddPopulationBonuses"); -cmpPopulation.OnValueModification({ "component": "Population" }); +cmpPopulation.OnValueModification(); TS_ASSERT_EQUALS(spy._called, 1); // Test newly created entities also get affected by modifications. Index: binaries/data/mods/public/simulation/components/tests/test_Promotion.js =================================================================== --- binaries/data/mods/public/simulation/components/tests/test_Promotion.js +++ binaries/data/mods/public/simulation/components/tests/test_Promotion.js @@ -118,6 +118,6 @@ // Test valuemodification applies. modifier = 10; -cmpPromotion.OnValueModification({ "component": "Promotion" }); +cmpPromotion.OnValueModification(); cmpTimer.OnUpdate({ "turnLength": 4 }); TS_ASSERT_EQUALS(cmpPromotion.GetCurrentXp(), 90); Index: binaries/data/mods/public/simulation/components/tests/test_ResourceTrickle.js =================================================================== --- binaries/data/mods/public/simulation/components/tests/test_ResourceTrickle.js +++ binaries/data/mods/public/simulation/components/tests/test_ResourceTrickle.js @@ -74,7 +74,7 @@ }; Engine.RegisterGlobal("ApplyValueModificationsToEntity", ApplyValueModificationsToEntity); // Calling OnValueModification will reset the timer, which can then be called, thus increasing the resources of the player. -cmpResourceTrickle.OnValueModification({ "component": "ResourceTrickle" }); +cmpResourceTrickle.OnValueModification(); TS_ASSERT_UNEVAL_EQUALS(cmpResourceTrickle.GetRates(), { "food": 1 }); cmpTimer.OnUpdate({ "turnLength": turnLength }); TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 301, "metal": 300 }); @@ -83,7 +83,7 @@ // Reset the trickle modification. ApplyValueModificationsToEntity = (valueName, currentValue, entity) => currentValue; Engine.RegisterGlobal("ApplyValueModificationsToEntity", ApplyValueModificationsToEntity); -cmpResourceTrickle.OnValueModification({ "component": "ResourceTrickle" }); +cmpResourceTrickle.OnValueModification(); TS_ASSERT_UNEVAL_EQUALS(cmpResourceTrickle.GetRates(), {}); TS_ASSERT_EQUALS(cmpResourceTrickle.ComputeRates(), false); cmpTimer.OnUpdate({ "turnLength": turnLength }); @@ -98,7 +98,7 @@ return currentValue; }; Engine.RegisterGlobal("ApplyValueModificationsToEntity", ApplyValueModificationsToEntity); -cmpResourceTrickle.OnValueModification({ "component": "ResourceTrickle" }); +cmpResourceTrickle.OnValueModification(); TS_ASSERT_EQUALS(cmpResourceTrickle.GetInterval(), 400); cmpTimer.OnUpdate({ "turnLength": turnLength }); TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 301, "metal": 300 }); @@ -115,7 +115,7 @@ return currentValue; }; Engine.RegisterGlobal("ApplyValueModificationsToEntity", ApplyValueModificationsToEntity); -cmpResourceTrickle.OnValueModification({ "component": "ResourceTrickle" }); +cmpResourceTrickle.OnValueModification(); TS_ASSERT_EQUALS(cmpResourceTrickle.GetInterval(), 0); cmpTimer.OnUpdate({ "turnLength": turnLength }); TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 302, "metal": 300 }); @@ -134,7 +134,7 @@ return currentValue; }; Engine.RegisterGlobal("ApplyValueModificationsToEntity", ApplyValueModificationsToEntity); -cmpResourceTrickle.OnValueModification({ "component": "ResourceTrickle" }); +cmpResourceTrickle.OnValueModification(); TS_ASSERT_EQUALS(cmpResourceTrickle.GetInterval(), 100); cmpTimer.OnUpdate({ "turnLength": turnLength }); TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 305, "metal": 300 }); @@ -153,7 +153,7 @@ return currentValue; }; Engine.RegisterGlobal("ApplyValueModificationsToEntity", ApplyValueModificationsToEntity); -cmpResourceTrickle.OnValueModification({ "component": "ResourceTrickle" }); +cmpResourceTrickle.OnValueModification(); TS_ASSERT_EQUALS(cmpResourceTrickle.GetInterval(), -1); cmpTimer.OnUpdate({ "turnLength": turnLength }); TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 309, "metal": 300 }); @@ -168,7 +168,7 @@ return currentValue; }; Engine.RegisterGlobal("ApplyValueModificationsToEntity", ApplyValueModificationsToEntity); -cmpResourceTrickle.OnValueModification({ "component": "ResourceTrickle" }); +cmpResourceTrickle.OnValueModification(); TS_ASSERT_EQUALS(cmpResourceTrickle.GetInterval(), 200); cmpTimer.OnUpdate({ "turnLength": turnLength }); TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 310, "metal": 300 }); Index: binaries/data/mods/public/simulation/components/tests/test_Upkeep.js =================================================================== --- binaries/data/mods/public/simulation/components/tests/test_Upkeep.js +++ binaries/data/mods/public/simulation/components/tests/test_Upkeep.js @@ -76,7 +76,7 @@ }; Engine.RegisterGlobal("ApplyValueModificationsToEntity", ApplyValueModificationsToEntity); // Calling OnValueModification will reset the timer, which can then be called, thus decreasing the resources of the player. -cmpUpkeep.OnValueModification({ "component": "Upkeep" }); +cmpUpkeep.OnValueModification(); TS_ASSERT_UNEVAL_EQUALS(cmpUpkeep.GetRates(), { "food": 1 }); cmpTimer.OnUpdate({ "turnLength": turnLength }); TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 299, "metal": 300 }); @@ -85,7 +85,7 @@ // Reset the pay modification. ApplyValueModificationsToEntity = (valueName, currentValue, entity) => currentValue; Engine.RegisterGlobal("ApplyValueModificationsToEntity", ApplyValueModificationsToEntity); -cmpUpkeep.OnValueModification({ "component": "Upkeep" }); +cmpUpkeep.OnValueModification(); TS_ASSERT_UNEVAL_EQUALS(cmpUpkeep.GetRates(), {}); TS_ASSERT_EQUALS(cmpUpkeep.ComputeRates(), false); cmpTimer.OnUpdate({ "turnLength": turnLength }); @@ -100,7 +100,7 @@ return currentValue; }; Engine.RegisterGlobal("ApplyValueModificationsToEntity", ApplyValueModificationsToEntity); -cmpUpkeep.OnValueModification({ "component": "Upkeep" }); +cmpUpkeep.OnValueModification(); TS_ASSERT_EQUALS(cmpUpkeep.GetInterval(), 400); cmpTimer.OnUpdate({ "turnLength": turnLength }); TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 299, "metal": 300 }); @@ -117,7 +117,7 @@ return currentValue; }; Engine.RegisterGlobal("ApplyValueModificationsToEntity", ApplyValueModificationsToEntity); -cmpUpkeep.OnValueModification({ "component": "Upkeep" }); +cmpUpkeep.OnValueModification(); TS_ASSERT_EQUALS(cmpUpkeep.GetInterval(), 0); cmpTimer.OnUpdate({ "turnLength": turnLength }); TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 298, "metal": 300 }); @@ -136,7 +136,7 @@ return currentValue; }; Engine.RegisterGlobal("ApplyValueModificationsToEntity", ApplyValueModificationsToEntity); -cmpUpkeep.OnValueModification({ "component": "Upkeep" }); +cmpUpkeep.OnValueModification(); TS_ASSERT_EQUALS(cmpUpkeep.GetInterval(), 100); cmpTimer.OnUpdate({ "turnLength": turnLength }); TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 295, "metal": 300 }); @@ -155,7 +155,7 @@ return currentValue; }; Engine.RegisterGlobal("ApplyValueModificationsToEntity", ApplyValueModificationsToEntity); -cmpUpkeep.OnValueModification({ "component": "Upkeep" }); +cmpUpkeep.OnValueModification(); TS_ASSERT_EQUALS(cmpUpkeep.GetInterval(), -1); cmpTimer.OnUpdate({ "turnLength": turnLength }); TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 291, "metal": 300 }); @@ -170,7 +170,7 @@ return currentValue; }; Engine.RegisterGlobal("ApplyValueModificationsToEntity", ApplyValueModificationsToEntity); -cmpUpkeep.OnValueModification({ "component": "Upkeep" }); +cmpUpkeep.OnValueModification(); TS_ASSERT_EQUALS(cmpUpkeep.GetInterval(), 200); cmpTimer.OnUpdate({ "turnLength": turnLength }); TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 290, "metal": 300 }); @@ -189,7 +189,7 @@ return currentValue; }; Engine.RegisterGlobal("ApplyValueModificationsToEntity", ApplyValueModificationsToEntity); -cmpUpkeep.OnValueModification({ "component": "Upkeep" }); +cmpUpkeep.OnValueModification(); TS_ASSERT_EQUALS(cmpUpkeep.GetInterval(), 200); cmpTimer.OnUpdate({ "turnLength": turnLength }); TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 287, "metal": 298 }); @@ -210,7 +210,7 @@ return currentValue; }; Engine.RegisterGlobal("ApplyValueModificationsToEntity", ApplyValueModificationsToEntity); -cmpUpkeep.OnValueModification({ "component": "Upkeep" }); +cmpUpkeep.OnValueModification(); TS_ASSERT_EQUALS(cmpUpkeep.GetInterval(), 200); cmpTimer.OnUpdate({ "turnLength": turnLength * 285 }); TS_ASSERT_UNEVAL_EQUALS(cmpPlayer.GetResourceCounts(), { "food": 0, "metal": 294 }); Index: source/simulation2/system/ComponentManager.h =================================================================== --- source/simulation2/system/ComponentManager.h +++ source/simulation2/system/ComponentManager.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2022 Wildfire Games. +/* Copyright (C) 2023 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -248,6 +248,13 @@ */ void BroadcastMessage(const CMessage& msg); + /** + * Send a MT_ValueModification message, targeted at a particular entity. + * The message will be received by the specific modified components of all entities, + * then sent as a general message. + */ + void PostValueModification(entity_id_t ent, const CMessage& msg); + /** * Resets the dynamic simulation state (deletes all entities, resets entity ID counters; * doesn't unload/reload component scripts). @@ -284,6 +291,7 @@ std::vector Script_GetComponentsWithInterface(int iid); void Script_PostMessage(int ent, int mtid, JS::HandleValue data); void Script_BroadcastMessage(int mtid, JS::HandleValue data); + void Script_PostValueModification(int ent, JS::HandleValue data); int Script_AddEntity(const std::wstring& templateName); int Script_AddLocalEntity(const std::wstring& templateName); const CParamNode& Script_GetTemplate(const std::string& templateName); Index: source/simulation2/system/ComponentManager.cpp =================================================================== --- source/simulation2/system/ComponentManager.cpp +++ source/simulation2/system/ComponentManager.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2022 Wildfire Games. +/* Copyright (C) 2023 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -83,6 +83,7 @@ ScriptFunction::Register<&CComponentManager::Script_GetComponentsWithInterface, Getter>(rq, "GetComponentsWithInterface"); ScriptFunction::Register<&CComponentManager::Script_PostMessage, Getter>(rq, "PostMessage"); ScriptFunction::Register<&CComponentManager::Script_BroadcastMessage, Getter>(rq, "BroadcastMessage"); + ScriptFunction::Register<&CComponentManager::Script_PostValueModification, Getter>(rq, "PostValueModification"); ScriptFunction::Register<&CComponentManager::Script_AddEntity, Getter>(rq, "AddEntity"); ScriptFunction::Register<&CComponentManager::Script_AddLocalEntity, Getter>(rq, "AddLocalEntity"); ScriptFunction::Register<&CComponentManager::QueryInterface, Getter>(rq, "QueryInterface"); @@ -461,6 +462,15 @@ delete msg; } +void CComponentManager::Script_PostValueModification(int ent, JS::HandleValue data) +{ + CMessage* msg = CMessageValueModification::FromJSVal(m_ScriptInterface, data); + + PostValueModification(ent, *msg); + + delete msg; +} + int CComponentManager::Script_AddEntity(const std::wstring& templateName) { // TODO: should validate the string to make sure it doesn't contain scary characters @@ -1041,6 +1051,35 @@ SendGlobalMessage(INVALID_ENTITY, msg); } +void CComponentManager::PostValueModification(entity_id_t ent, const CMessage& msg) +{ + ENSURE(msg.GetType() == MT_ValueModification); + + // Send the message to components of all entities that subscribed locally to this message + std::map >::const_iterator it; + it = m_LocalMessageSubscriptions.find(msg.GetType()); + if (it != m_LocalMessageSubscriptions.end()) + { + const CMessageValueModification& msgData = static_cast (msg); + const ComponentTypeId cid = m_ComponentTypeIdsByName[CStrW(msgData.component).ToUTF8()]; + // Don't send the message if the component didn't subscribe. + // TODO: this should use a better data structure. + if (std::find(it->second.begin(), it->second.end(), cid) != it->second.end()) + { + std::map >::const_iterator emap = m_ComponentsByTypeId.find(cid); + if (emap != m_ComponentsByTypeId.end()) + { + std::map::const_iterator eit = emap->second.find(ent); + if (eit != emap->second.end()) + eit->second->HandleMessage(msg, false); + } + } + } + + SendGlobalMessage(ent, msg); +} + + void CComponentManager::SendGlobalMessage(entity_id_t ent, const CMessage& msg) { PROFILE2_IFSPIKE("SendGlobalMessage", 0.001);