LCOV - code coverage report
Current view: top level - simulation/components - Trigger.js (source / functions) Hit Total Coverage
Test: lcov.info Lines: 36 140 25.7 %
Date: 2023-04-02 12:52:40 Functions: 3 27 11.1 %

          Line data    Source code
       1             : function Trigger() {}
       2             : 
       3           4 : Trigger.prototype.Schema =
       4             :         "<a:component type='system'/><empty/>";
       5             : 
       6             : /**
       7             :  * Events we're able to receive and call handlers for.
       8             :  */
       9           4 : Trigger.prototype.eventNames =
      10             : [
      11             :         "OnCinemaPathEnded",
      12             :         "OnCinemaQueueEnded",
      13             :         "OnConstructionStarted",
      14             :         "OnDiplomacyChanged",
      15             :         "OnDeserialized",
      16             :         "OnInitGame",
      17             :         "OnInterval",
      18             :         "OnEntityRenamed",
      19             :         "OnOwnershipChanged",
      20             :         "OnPlayerCommand",
      21             :         "OnPlayerDefeated",
      22             :         "OnPlayerWon",
      23             :         "OnRange",
      24             :         "OnResearchFinished",
      25             :         "OnResearchQueued",
      26             :         "OnStructureBuilt",
      27             :         "OnTrainingFinished",
      28             :         "OnTrainingQueued",
      29             :         "OnTreasureCollected"
      30             : ];
      31             : 
      32           4 : Trigger.prototype.Init = function()
      33             : {
      34             :         // Difficulty used by trigger scripts (as defined in data/settings/trigger_difficulties.json).
      35           4 :         this.difficulty = undefined;
      36             : 
      37           4 :         this.triggerPoints = {};
      38             : 
      39             :         // Each event has its own set of actions determined by the map maker.
      40           4 :         this.triggers = {};
      41           4 :         for (const eventName of this.eventNames)
      42          76 :                 this.triggers[eventName] = {};
      43             : };
      44             : 
      45           4 : Trigger.prototype.RegisterTriggerPoint = function(ref, ent)
      46             : {
      47           0 :         if (!this.triggerPoints[ref])
      48           0 :                 this.triggerPoints[ref] = [];
      49           0 :         this.triggerPoints[ref].push(ent);
      50             : };
      51             : 
      52           4 : Trigger.prototype.RemoveRegisteredTriggerPoint = function(ref, ent)
      53             : {
      54           0 :         if (!this.triggerPoints[ref])
      55             :         {
      56           0 :                 warn("no trigger points found with ref "+ref);
      57           0 :                 return;
      58             :         }
      59           0 :         const i = this.triggerPoints[ref].indexOf(ent);
      60           0 :         if (i == -1)
      61             :         {
      62           0 :                 warn("entity " + ent + " wasn't found under the trigger points with ref "+ref);
      63           0 :                 return;
      64             :         }
      65           0 :         this.triggerPoints[ref].splice(i, 1);
      66             : };
      67             : 
      68           4 : Trigger.prototype.GetTriggerPoints = function(ref)
      69             : {
      70           0 :         return this.triggerPoints[ref] || [];
      71             : };
      72             : 
      73             : /**
      74             :  * Create a trigger listening on a specific event.
      75             :  *
      76             :  * @param {string} event - One of eventNames
      77             :  * @param {string} name - Name of the trigger.
      78             :  *     If no action is specified in triggerData, the action will be the trigger name.
      79             :  * @param {Object} triggerData - f.e. enabled or not, delay for timers, range for range triggers.
      80             :  * @param {Object} customData - User-defined data that will be forwarded to the action.
      81             :  *
      82             :  * @example
      83             :  * triggerData = { enabled: true, interval: 1000, delay: 500 }
      84             :  *
      85             :  * General settings:
      86             :  *     enabled = false       * If the trigger is enabled by default.
      87             :  *     action = name         * The function (on Trigger) to call. Defaults to the trigger name.
      88             :  *
      89             :  * Range trigger:
      90             :  *     entities = [id1, id2] * Ids of the source
      91             :  *     players = [1,2,3,...] * list of player ids
      92             :  *     minRange = 0          * Minimum range for the query
      93             :  *     maxRange = -1         * Maximum range for the query (-1 = no maximum)
      94             :  *     requiredComponent = 0 * Required component id the entities will have
      95             :  */
      96           4 : Trigger.prototype.RegisterTrigger = function(event, name, triggerData, customData = undefined)
      97             : {
      98           0 :         if (!this.triggers[event])
      99             :         {
     100           0 :                 warn("Trigger.js: Invalid trigger event \"" + event + "\".");
     101           0 :                 return;
     102             :         }
     103           0 :         if (this.triggers[event][name])
     104             :         {
     105           0 :                 warn("Trigger.js: Trigger \"" + name + "\" has been registered before. Aborting...");
     106           0 :                 return;
     107             :         }
     108             :         // clone the data to be sure it's only modified locally
     109             :         // We could run into triggers overwriting each other's data otherwise.
     110             :         // F.e. getting the wrong timer tag
     111           0 :         triggerData = clone(triggerData) || { "enabled": false };
     112           0 :         if (!triggerData.action)
     113           0 :                 triggerData.action = name;
     114             : 
     115           0 :         this.triggers[event][name] = { "triggerData": triggerData, "customData": customData };
     116             : 
     117             :         // setup range query
     118           0 :         if (event == "OnRange")
     119             :         {
     120           0 :                 if (!triggerData.entities)
     121             :                 {
     122           0 :                         warn("Trigger.js: Range triggers should carry extra data");
     123           0 :                         return;
     124             :                 }
     125           0 :                 triggerData.queries = [];
     126           0 :                 for (const ent of triggerData.entities)
     127             :                 {
     128           0 :                         const cmpTriggerPoint = Engine.QueryInterface(ent, IID_TriggerPoint);
     129           0 :                         if (!cmpTriggerPoint)
     130             :                         {
     131           0 :                                 warn("Trigger.js: Range triggers must be defined on trigger points");
     132           0 :                                 continue;
     133             :                         }
     134           0 :                         triggerData.queries.push(cmpTriggerPoint.RegisterRangeTrigger(name, triggerData));
     135             :                 }
     136             :         }
     137             : 
     138           0 :         if (triggerData.enabled)
     139           0 :                 this.EnableTrigger(event, name);
     140             : };
     141             : 
     142           4 : Trigger.prototype.DisableTrigger = function(event, name)
     143             : {
     144           0 :         if (!this.triggers[event][name])
     145             :         {
     146           0 :                 warn("Trigger.js: Disabling unknown trigger " + name);
     147           0 :                 return;
     148             :         }
     149             : 
     150           0 :         const triggerData = this.triggers[event][name].triggerData;
     151             :         // special casing interval and range triggers for performance
     152           0 :         if (event == "OnInterval")
     153             :         {
     154           0 :                 if (!triggerData.timer) // don't disable it a second time
     155           0 :                         return;
     156           0 :                 const cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
     157           0 :                 cmpTimer.CancelTimer(triggerData.timer);
     158           0 :                 triggerData.timer = null;
     159             :         }
     160           0 :         else if (event == "OnRange")
     161             :         {
     162           0 :                 if (!triggerData.queries)
     163             :                 {
     164           0 :                         warn("Trigger.js: Range query wasn't set up before trying to disable it.");
     165           0 :                         return;
     166             :                 }
     167           0 :                 const cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
     168           0 :                 for (const query of triggerData.queries)
     169           0 :                         cmpRangeManager.DisableActiveQuery(query);
     170             :         }
     171             : 
     172           0 :         triggerData.enabled = false;
     173             : };
     174             : 
     175           4 : Trigger.prototype.EnableTrigger = function(event, name)
     176             : {
     177           0 :         if (!this.triggers[event][name])
     178             :         {
     179           0 :                 warn("Trigger.js: Enabling unknown trigger " + name);
     180           0 :                 return;
     181             :         }
     182           0 :         const triggerData = this.triggers[event][name].triggerData;
     183             :         // special casing interval and range triggers for performance
     184           0 :         if (event == "OnInterval")
     185             :         {
     186           0 :                 if (triggerData.timer) // don't enable it a second time
     187           0 :                         return;
     188           0 :                 if (!triggerData.interval)
     189             :                 {
     190           0 :                         warn("Trigger.js: An interval trigger should have an intervel in its data");
     191           0 :                         return;
     192             :                 }
     193           0 :                 const cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
     194           0 :                 triggerData.timer = cmpTimer.SetInterval(this.entity, IID_Trigger, "DoAction",
     195             :                         triggerData.delay || 0, triggerData.interval, { "action": name });
     196             :         }
     197           0 :         else if (event == "OnRange")
     198             :         {
     199           0 :                 if (!triggerData.queries)
     200             :                 {
     201           0 :                         warn("Trigger.js: Range query wasn't set up before");
     202           0 :                         return;
     203             :                 }
     204           0 :                 const cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
     205           0 :                 for (const query of triggerData.queries)
     206           0 :                         cmpRangeManager.EnableActiveQuery(query);
     207             :         }
     208             : 
     209           0 :         triggerData.enabled = true;
     210             : };
     211             : 
     212           4 : Trigger.prototype.OnGlobalInitGame = function(msg)
     213             : {
     214           0 :         this.CallEvent("OnInitGame", {});
     215             : };
     216             : 
     217           4 : Trigger.prototype.OnGlobalConstructionFinished = function(msg)
     218             : {
     219           0 :         this.CallEvent("OnStructureBuilt", { "building": msg.newentity, "foundation": msg.entity });
     220             : };
     221             : 
     222           4 : Trigger.prototype.OnGlobalTrainingFinished = function(msg)
     223             : {
     224           0 :         this.CallEvent("OnTrainingFinished", msg);
     225             :         // The data for this one is {"entities": createdEnts,
     226             :         //                                                       "owner": cmpOwnership.GetOwner(),
     227             :         //                                                       "metadata": metadata}
     228             :         // See function "SpawnUnits" in ProductionQueue for more details
     229             : };
     230             : 
     231           4 : Trigger.prototype.OnGlobalResearchFinished = function(msg)
     232             : {
     233           0 :         this.CallEvent("OnResearchFinished", msg);
     234             :         // The data for this one is { "player": playerID, "tech": tech }
     235             : };
     236             : 
     237           4 : Trigger.prototype.OnGlobalCinemaPathEnded = function(msg)
     238             : {
     239           0 :         this.CallEvent("OnCinemaPathEnded", msg);
     240             : };
     241             : 
     242           4 : Trigger.prototype.OnGlobalCinemaQueueEnded = function(msg)
     243             : {
     244           0 :         this.CallEvent("OnCinemaQueueEnded", msg);
     245             : };
     246             : 
     247           4 : Trigger.prototype.OnGlobalDeserialized = function(msg)
     248             : {
     249           0 :         this.CallEvent("OnDeserialized", msg);
     250             : };
     251             : 
     252           4 : Trigger.prototype.OnGlobalEntityRenamed = function(msg)
     253             : {
     254           0 :         this.CallEvent("OnEntityRenamed", msg);
     255             : };
     256             : 
     257           4 : Trigger.prototype.OnGlobalOwnershipChanged = function(msg)
     258             : {
     259           0 :         this.CallEvent("OnOwnershipChanged", msg);
     260             :         // data is {"entity": ent, "from": playerId, "to": playerId}
     261             : };
     262             : 
     263           4 : Trigger.prototype.OnGlobalPlayerDefeated = function(msg)
     264             : {
     265           0 :         this.CallEvent("OnPlayerDefeated", msg);
     266             : };
     267             : 
     268           4 : Trigger.prototype.OnGlobalPlayerWon = function(msg)
     269             : {
     270           0 :         this.CallEvent("OnPlayerWon", msg);
     271             : };
     272             : 
     273           4 : Trigger.prototype.OnGlobalDiplomacyChanged = function(msg)
     274             : {
     275           0 :         this.CallEvent("OnDiplomacyChanged", msg);
     276             : };
     277             : 
     278             : /**
     279             :  * Execute a function after a certain delay.
     280             :  *
     281             :  * @param {number} time - Delay in milliseconds.
     282             :  * @param {string} action - Name of the action function.
     283             :  * @param {Object} eventData - Arbitrary object that will be passed to the action function.
     284             :  * @return {number} The ID of the timer, so it can be stopped later.
     285             :  */
     286           4 : Trigger.prototype.DoAfterDelay = function(time, action, eventData)
     287             : {
     288           0 :         const cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
     289           0 :         return cmpTimer.SetTimeout(SYSTEM_ENTITY, IID_Trigger, "DoAction", time, {
     290             :                 "action": action,
     291             :                 "eventData": eventData
     292             :         });
     293             : };
     294             : 
     295             : /**
     296             :  * Execute a function each time a certain delay has passed.
     297             :  *
     298             :  * @param {number} interval - Interval in milleseconds between consecutive calls.
     299             :  * @param {string} action - Name of the action function.
     300             :  * @param {Object} eventData - Arbitrary object that will be passed to the action function.
     301             :  * @param {number} [start] - Optional initial delay in milleseconds before starting the calls.
     302             :  *                           If not given, interval will be used.
     303             :  * @return {number} the ID of the timer, so it can be stopped later.
     304             :  */
     305           4 : Trigger.prototype.DoRepeatedly = function(time, action, eventData, start)
     306             : {
     307           0 :         const cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
     308           0 :         return cmpTimer.SetInterval(SYSTEM_ENTITY, IID_Trigger, "DoAction", start !== undefined ? start : time, time, {
     309             :                 "action": action,
     310             :                 "eventData": eventData
     311             :         });
     312             : };
     313             : 
     314             : /**
     315             :  * This function executes the actions bound to the events.
     316             :  * It's either called directlty from other simulation scripts,
     317             :  * or from message listeners in this file
     318             :  *
     319             :  * @param {string} event - One of eventNames
     320             :  * @param {Object} data - will be passed to the actions
     321             :  */
     322           4 : Trigger.prototype.CallEvent = function(event, eventData)
     323             : {
     324           7 :         if (!this.triggers[event])
     325             :         {
     326           0 :                 warn("Trigger.js: Unknown trigger event called:\"" + event + "\".");
     327           0 :                 return;
     328             :         }
     329             : 
     330           7 :         for (const name in this.triggers[event])
     331           0 :                 if (this.triggers[event][name].triggerData.enabled)
     332           0 :                         this.DoAction({
     333             :                                 "action": this.triggers[event][name].triggerData.action,
     334             :                                 "eventData": eventData,
     335             :                                 "customData": this.triggers[event][name].customData,
     336             :                                 "triggerData": this.triggers[event][name].triggerData
     337             :                         });
     338             : };
     339             : 
     340             : /**
     341             :  * Call the action method of a trigger with the given event Data.
     342             :  * By default, call the trigger even if it is currently disabled.
     343             :  */
     344           4 : Trigger.prototype.CallTrigger = function(event, name, eventData, evenIfDisabled = true)
     345             : {
     346           0 :         if (!this.triggers[event]?.[name])
     347             :         {
     348           0 :                 warn(`Trigger.js: called a trigger '${name}' for event '${event}' that wasn't found`);
     349           0 :                 return;
     350             :         }
     351             : 
     352           0 :         if (!evenIfDisabled && !this.triggers[event][name].triggerData.enabled)
     353           0 :                 return;
     354             : 
     355           0 :         this.DoAction({
     356             :                 "action": this.triggers[event][name].triggerData.action,
     357             :                 "eventData": eventData,
     358             :                 "customData": this.triggers[event][name].customData,
     359             :                 "triggerData": this.triggers[event][name].triggerData
     360             :         });
     361             : };
     362             : 
     363             : 
     364             : /**
     365             :  * Called by the trigger listeners to execute the actual action. Including sanity checks.
     366             :  * Intended for internal use, prefer CallEvent or CallTrigger.
     367             :  */
     368           4 : Trigger.prototype.DoAction = function(msg)
     369             : {
     370           0 :         if (this[msg.action])
     371           0 :                 this[msg.action](msg?.eventData, msg?.customData, msg?.triggerData);
     372             :         else
     373           0 :                 warn("Trigger.js: called a trigger action '" + msg.action + "' that wasn't found");
     374             : };
     375             : 
     376             : /**
     377             :  * Level of difficulty used by trigger scripts.
     378             :  */
     379           4 : Trigger.prototype.GetDifficulty = function()
     380             : {
     381           0 :         return this.difficulty;
     382             : };
     383             : 
     384           4 : Trigger.prototype.SetDifficulty = function(diff)
     385             : {
     386           0 :         this.difficulty = diff;
     387             : };
     388             : 
     389           4 : Engine.RegisterSystemComponentType(IID_Trigger, "Trigger", Trigger);

Generated by: LCOV version 1.14