Ticket #52: Triggers.5.patch

File Triggers.5.patch, 39.9 KB (added by O.Davoodi, 10 years ago)
  • binaries/data/mods/public/simulation/components/BuildingAI.js

     
    146146 */
    147147BuildingAI.prototype.OnRangeUpdate = function(msg)
    148148{
    149 
    150149    var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
    151150    if (!cmpAttack)
    152151        return;
  • binaries/data/mods/public/simulation/components/Foundation.js

     
    202202            // (via CCmpTemplateManager). Now we need to remove that temporary
    203203            // blocker-disabling, so that we'll perform standard unit blocking instead.
    204204            cmpObstruction.SetDisableBlockMovementPathfinding(false, false, -1);
     205           
     206            // Call the related trigger event
     207            var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
     208            cmpTrigger.CallEvent("OnConstructionStarted", {"foundation": this.entity, "template": this.finalTemplateName});
    205209        }
    206210
    207211        // Switch foundation to scaffold variant
  • binaries/data/mods/public/simulation/components/interfaces/Trigger.js

     
     1Engine.RegisterInterface("Trigger");
  • binaries/data/mods/public/simulation/components/ProductionQueue.js

     
    288288                "timeTotal": time*1000,
    289289                "timeRemaining": time*1000,
    290290            });
     291           
     292            // Call the related trigger event
     293            var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
     294            cmpTrigger.CallEvent("OnTrainingQueued", {"playerid": cmpPlayer.GetPlayerID(), "unitTemplate": templateName, "count": count, "metadata": metadata, "trainerEntity": this.entity});
    291295        }
    292296        else if (type == "technology")
    293297        {
     
    324328                "timeTotal": time*1000,
    325329                "timeRemaining": time*1000,
    326330            });
     331           
     332            // Call the related trigger event
     333            var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
     334            cmpTrigger.CallEvent("OnResearchQueued", {"playerid": cmpPlayer.GetPlayerID(), "technologyTemplate": templateName, "researcherEntity": this.entity});
    327335        }
    328336        else
    329337        {
  • binaries/data/mods/public/simulation/components/ResourceTrickle.js

     
    5757    if (cmpPlayer)
    5858        for (var resource in rates)
    5959            cmpPlayer.AddResource(resource, rates[resource]);
    60    
    6160};
    6261
    6362Engine.RegisterComponentType(IID_ResourceTrickle, "ResourceTrickle", ResourceTrickle);
  • binaries/data/mods/public/simulation/components/TechnologyManager.js

     
    283283        return;
    284284    }
    285285   
     286    // Call the related trigger event
     287    var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
     288    cmpTrigger.CallEvent("OnResearchFinished", {"researcher": this.entity, "tech": tech});
     289   
    286290    var modifiedComponents = {};
    287291    this.researchedTechs[tech] = template;
    288292    // store the modifications in an easy to access structure
  • binaries/data/mods/public/simulation/components/Trigger.js

     
     1function Trigger() {};
     2
     3Trigger.prototype.Schema =
     4    "<a:component type='system'/><empty/>";
     5
     6Trigger.prototype.Init = function()
     7{
     8    var eventNames = [
     9        "OnEntityTookDamage", 
     10        "OnEntityKilled", 
     11        "OnStructureBuilt", 
     12        "OnConstructionStarted", 
     13        "OnTrainingFinished", 
     14        "OnTrainingQueued", 
     15        "OnResearchFinished", 
     16        "OnResearchQueued", 
     17        "OnUnitIssuedOrder",
     18        "OnUnitsIssuedOrder"
     19    ];
     20       
     21    // Each event has its own set of actions determined by the map maker.
     22    for each (var eventName in eventNames)
     23        this["event" + eventName + "Actions"] = [];
     24     
     25    // Special events
     26    this.eventOnUnitRangeFromEntityData = {};
     27    this.eventOnIntervalData = {};
     28   
     29    // We are going to have all of the triggers here to be able to enable/disbale them in runtime.
     30    this.triggerEnabled = {};
     31   
     32    // We should define the trigger variables here so that they can be serialized
     33    this.triggerVariables = {};
     34   
     35    // To prevent the lose of trigger variables after a save, they "should" be defined in "InitTriggers" function, which is the starting point of a trigger script
     36    var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
     37    cmpTimer.SetTimeout(SYSTEM_ENTITY, IID_Trigger, "DoAction", 0, {"action": "InitTriggers", "data": {}});
     38};
     39
     40// Global variables that are going to be used in triggers should be defined here. If not, They'll be lost after by saving/loading.
     41Trigger.prototype.SetVariable = function(name, value)
     42{
     43    this.triggerVariables[name] = value;
     44}
     45
     46Trigger.prototype.GetVariable = function(name)
     47{
     48    return this.triggerVariables[name];
     49}
     50
     51/**  Binds the "action" function to one of the implemented events.
     52 *
     53 * @param eventName Name of the event (see the list in init) 
     54 * @param action Functionname of a function available under the g_TriggersData global object
     55 */
     56Trigger.prototype.RegisterTrigger = function(event, action, enabled)
     57{
     58    var eventString = "event" + event + "Actions";
     59    if (this[eventString])
     60    {
     61        this[eventString].push(action);
     62        this.triggerEnabled[[event, action]] = enabled
     63    }
     64    else
     65        error("Invalid trigger event \"" + event + "\".")
     66};
     67
     68// Disable trigger
     69Trigger.prototype.DisableTrigger = function(event, action)
     70{
     71    if (this.triggerEnabled[[event, action]] !== undefined)
     72        this.triggerEnabled[[event, action]] = false;
     73    else
     74        warn("Trigger key doesn't exist:" + event + " & " + action);
     75}
     76
     77// Enable trigger
     78Trigger.prototype.EnableTrigger = function(event, action)
     79{
     80    if (this.triggerEnabled[[event, action]] !== undefined)
     81        this.triggerEnabled[[event, action]] = true;
     82    else
     83        warn("Trigger key doesn't exist:" + event + " & " + action);
     84}
     85
     86/** 
     87 * This is an event with more than just the key argument, 
     88 * so we should have a special register function for it.
     89 *
     90 * @param event Either "entered" or "left" based on the wanter behaviour.
     91 * @param entity Entity id around which the range is checked
     92 * @param radius Radius of the range
     93 * @param action Functionname to execute on a range change (entities entered or left)
     94 * @param players Array of player ids (0 to 8). Or null if you want to check for all players
     95 * @param componentID Only entities with this component id will be noticed. 0 for all entities
     96 * @param enabled Determines whether if the trigger is active by default
     97 */
     98Trigger.prototype.RegisterOnUnitRangeFromEntityTrigger = function(event, entity, radius, action, players, component_id, enabled)
     99{
     100    // If the players parameter is not specified use all players.
     101    if (!players)
     102    {
     103        var playerEntities = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetAllPlayerEntities();
     104        players = [];
     105        for each (var pentity in playerEntities)
     106            players.push(Engine.QueryInterface(pentity, IID_Player).GetPlayerID());
     107    }
     108   
     109    // To determine if a unit has "moved" or "was inside" the range, we should know if it was in the range before. We have "currentCollection" for this purpose.
     110    // For the purpose of finding out the intersection of the previous and the current rangeQuery faster, we are sorting the current one.
     111    this.eventOnUnitRangeFromEntityData[action] = {"event": event, "action": action, "entity": entity, "radius": radius, "currentCollection": [], "players": players};
     112   
     113    var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
     114   
     115    this.eventOnUnitRangeFromEntityData[action].rangeQuery = cmpRangeManager.CreateActiveQuery(entity, 0, radius, players, component_id, cmpRangeManager.GetEntityFlagMask("normal"));
     116    if (enabled)
     117        cmpRangeManager.EnableActiveQuery(this.eventOnUnitRangeFromEntityData[action].rangeQuery);
     118}
     119
     120
     121// Range triggers are different in nature, and should have their own Enable and disable functions
     122Trigger.prototype.DisableRangeTrigger = function(action)
     123{
     124    var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
     125    cmpRangeManager.DisableActiveQuery(this.eventOnUnitRangeFromEntityData[action].rangeQuery);
     126}
     127
     128Trigger.prototype.EnableRangeTrigger = function(action)
     129{
     130    var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
     131    cmpRangeManager.EnableActiveQuery(this.eventOnUnitRangeFromEntityData[action].rangeQuery);
     132}
     133
     134/** 
     135 * This is an event with more than just the key argument, 
     136 * so we should have a special register function for it.
     137 *
     138 * @param action Functionname to execute on interval
     139 * @param delay How many miliseconds before the first call?
     140 * @param interval How many miliseconds between each call?
     141 * @param enabled Determines whether if the trigger is active by default
     142 */
     143Trigger.prototype.RegisterOnIntervalTrigger = function(action, delay, interval, enabled)
     144{
     145    // If the trigger was active by default, start the update timer
     146    if (enabled)
     147    {
     148        var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
     149        this.eventOnIntervalData[action] = cmpTimer.SetInterval(this.entity, IID_Trigger, "TimerUpdate", delay, interval, {"action" : action});
     150    }
     151    else // If not, save it for future when we activate the trigger
     152        this.eventOnIntervalData[action] = [delay, interval, {"action" : action}];
     153}
     154
     155// Interval triggers are different in nature, and should have their own Enable and disable functions
     156Trigger.prototype.DisableIntervalTrigger = function(action)
     157{
     158    var timer = this.eventOnIntervalData[action];
     159    // Check if it is active
     160    if (typeof (timer) === 'number')
     161    {
     162        var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
     163        cmpTimer.CancelTimer(timer);
     164    }
     165}
     166
     167Trigger.prototype.EnableIntervalTrigger = function(action)
     168{
     169    var timer = this.eventOnIntervalData[action];
     170    // Check if it is inactive
     171    if (typeof (timer) !== 'number')
     172    {
     173        var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
     174        this.eventOnIntervalData[action] = cmpTimer.SetInterval(this.entity, IID_Trigger, "TimerUpdate", timer[0], timer[1], timer[2]);
     175    }
     176}
     177
     178/**
     179 * This function executes the actions bound to the events
     180 * It's either called directlty from other simulation scripts, 
     181 * or from message listeners in this file
     182 *
     183 * @param event Name of the event (see the list in init)
     184 * @param data Data object that will be passed to the actions
     185 */
     186Trigger.prototype.CallEvent = function(event, data)
     187{
     188    // A special case, when a unit dies, the range event associated with it should also be removed
     189    if (event == "OnEntityKilled")
     190    {
     191        for (var i in this.eventOnEntityKilledActions)
     192        {
     193            if (this.triggerEnabled[[event, i]])    // Check if it is enabled
     194                g_TriggerData[this.eventOnEntityKilledActions[i]](data); // The data for this one is {"killerEntity": killerEntity, "killedEntity": targetEntity}
     195       
     196            // Remove the "OnUnitFromRangeEntity" triggers which have this entity as their argument
     197            for (var j in this.eventOnUnitRangeFromEntityData)
     198                if (this.eventOnUnitRangeFromEntityData[j].entity == data.killedEntity)
     199                    {
     200                        DisableRangeTrigger(j);
     201                        delete this.eventOnUnitRangeFromEntityData[j];
     202                    }
     203        }
     204        return;
     205    }
     206   
     207    var actions = this["event" + event + "Actions"];
     208   
     209    if (actions === undefined)
     210    {
     211        warn("Unknown trigger event:\"" + event + "\".");
     212        return;
     213    }
     214   
     215    for (var i in actions)
     216    {
     217        if (this.triggerEnabled[[event, actions[i]]])
     218            g_TriggerData[this["event" + event + "Actions"][i]](data);
     219    }
     220}
     221
     222// Handles "OnStructureBuilt" event.
     223Trigger.prototype.OnGlobalConstructionFinished = function(msg)
     224{
     225    this.CallEvent("OnStructureBuilt", {"building": msg.newentity}); // The data for this one is {"building": constructedBuilding}
     226}
     227
     228// Handles "OnTrainingFinished" event.
     229Trigger.prototype.OnGlobalTrainingFinished = function(msg)
     230{
     231    this.CallEvent("OnTrainingFinished", msg);
     232    // The data for this one is {"entities": createdEnts,
     233    //                           "owner": cmpOwnership.GetOwner(),
     234    //                           "metadata": metadata}
     235    // See function "SpawnUnits" in ProductionQueue for more details
     236}
     237
     238// Handle "OnInterval" event.
     239Trigger.prototype.TimerUpdate = function(data, lateness)
     240{
     241    g_TriggerData[data.action](lateness);
     242}
     243
     244// Handles "OnUnitRangeFromEntity"
     245Trigger.prototype.OnGlobalRangeUpdate = function(msg)
     246{
     247    // search the right query
     248    for (var action in this.eventOnUnitRangeFromEntityData)
     249    {
     250        if (msg.tag == this.eventOnUnitRangeFromEntityData[action].rangeQuery)
     251        {
     252            var updatedQuery = this.eventOnUnitRangeFromEntityData[action];
     253            break;
     254        }
     255    }
     256   
     257    if (!updatedQuery)
     258        return;
     259
     260    for each (var entity in msg.removed)
     261    {
     262        var index = updatedQuery.currentCollection.indexOf(entity);
     263            if (index > -1)
     264                updatedQuery.currentCollection.splice(index, 1);
     265    }
     266       
     267    for each (var entity in msg.added)
     268        updatedQuery.currentCollection.push(entity);
     269       
     270    if (updatedQuery.event == "entered")
     271        for each (var entity in msg.added)
     272            g_TriggerData[updatedQuery.action]({"entity": entity});
     273    else if (updatedQuery.event == "left")
     274        for each (var entity in msg.removed)
     275            g_TriggerData[updatedQuery.action]({"entity": entity});
     276}
     277
     278/**
     279 * Execute a function after a certain delay
     280 * @param time The delay expressed in milleseconds
     281 * @param action Name of the action function
     282 * @param data Data object that will be passed to the action function
     283 */
     284Trigger.prototype.DoAfterDelay = function(miliseconds, action, data)
     285{
     286    var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
     287    return cmpTimer.SetTimeout(SYSTEM_ENTITY, IID_Trigger, "DoAction", miliseconds, {"action": action, "data": data});
     288}
     289
     290/**
     291 * Called by the trigger listeners to exucute the actual action. Including sanity checks.
     292 */
     293Trigger.prototype.DoAction = function(msg)
     294{
     295    if (typeof g_TriggerData === 'undefined')
     296    {
     297        // We don't raise an error if g_TriggerData is undefined because it always happens in maps without triggers
     298        log ("No trigger scripts loaded for this map.");
     299    }
     300    else
     301    {
     302        if (g_TriggerData[msg.action])
     303            g_TriggerData[msg.action](msg.data);
     304        else
     305            error("called a trigger action '" + msg.action + "' that wasn't added to the g_TriggersData global");
     306    }
     307   
     308}
     309
     310Engine.RegisterComponentType(IID_Trigger, "Trigger", Trigger);
  • binaries/data/mods/public/simulation/components/UnitAI.js

     
    199199        cmpUnitMotion.MoveToFormationOffset(msg.data.target, msg.data.x, msg.data.z);
    200200
    201201        this.SetNextStateAlwaysEntering("FORMATIONMEMBER.WALKING");
     202       
     203        var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
     204        cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.FormationWalk", "metadata": msg});
    202205    },
    203206
    204207    // Special orders:
     
    225228            // We are already at the target, or can't move at all
    226229            this.FinishOrder();
    227230        }
     231       
     232        var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
     233        cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.LeaveFoundation", "metadata": msg});
    228234    },
    229235
    230236    // Individual orders:
     
    247253            this.SetNextState("ANIMAL.IDLE");
    248254        else
    249255            this.SetNextState("INDIVIDUAL.IDLE");
    250 
     256       
     257        var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
     258        cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Stop", "metadata": msg});
    251259    },
    252260
    253261    "Order.Walk": function(msg) {
     
    258266            return;
    259267        }
    260268
     269        var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
     270        cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Walk", "metadata": msg});
     271       
    261272        // For packable units:
    262273        // 1. If packed, we can move.
    263274        // 2. If unpacked, we first need to pack, then follow case 1.
     
    284295            return;
    285296        }
    286297
     298        var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
     299        cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.WalkAndFight", "metadata": msg});
     300       
    287301        // For packable units:
    288302        // 1. If packed, we can move.
    289303        // 2. If unpacked, we first need to pack, then follow case 1.
     
    310324            this.FinishOrder();
    311325            return;
    312326        }
    313 
     327       
     328        var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
     329        cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.WalkToTarget", "metadata": msg, "target": this.order.data.target});
     330       
    314331        // For packable units:
    315332        // 1. If packed, we can move.
    316333        // 2. If unpacked, we first need to pack, then follow case 1.
     
    358375            this.StopMoving();
    359376            this.SetNextState("INDIVIDUAL.PICKUP.LOADING");
    360377        }
     378       
     379        var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
     380        cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.PickupUnit", "metadata": msg, "target": this.order.data.target});
    361381    },
    362382
    363383    "Order.Guard": function(msg) {
     
    371391            this.SetNextState("INDIVIDUAL.GUARD.ESCORTING");
    372392        else
    373393            this.SetNextState("INDIVIDUAL.GUARD.GUARDING");
     394       
     395        var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
     396        cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Guard", "metadata": msg, "target": this.order.data.target});
    374397    },
    375398
    376399    "Order.Flee": function(msg) {
     
    379402        var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
    380403        if (cmpUnitMotion.MoveToTargetRange(this.order.data.target, distance, -1))
    381404        {
     405            var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
     406            cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Flee", "metadata": msg, "target": this.order.data.target});
     407       
    382408            // We've started fleeing from the given target
    383409            if (this.IsAnimal())
    384410                this.SetNextState("ANIMAL.FLEEING");
     
    391417            this.StopMoving();
    392418            this.FinishOrder();
    393419        }
     420       
     421
    394422    },
    395423
    396424    "Order.Attack": function(msg) {
     
    411439        }
    412440        this.order.data.attackType = type;
    413441
     442        // Call "OnUnitIssuedOrder" event of the triggers.
     443        var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
     444        cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Attack", "metadata": msg, "target": this.order.data.target});
     445       
    414446        // If we are already at the target, try attacking it from here
    415447        if (this.CheckTargetAttackRange(this.order.data.target, this.order.data.attackType))
    416448        {
     
    495527        // We can't reach the target, and can't move towards it,
    496528        // so abandon this attack order
    497529        this.FinishOrder();
     530       
    498531    },
    499532
    500533    "Order.Heal": function(msg) {
     
    515548        // Check if the target is in range
    516549        if (this.CheckTargetRange(this.order.data.target, IID_Heal))
    517550        {
     551            // Call "OnUnitIssuedOrder" event of the triggers.
     552            var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
     553            cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Heal", "metadata": msg, "target": this.order.data.target});
     554       
    518555            this.StopMoving();
    519556            this.SetNextState("INDIVIDUAL.HEAL.HEALING");
    520557            return;
     
    531568        // Try to move within heal range
    532569        if (this.MoveToTargetRange(this.order.data.target, IID_Heal))
    533570        {
     571            // Call "OnUnitIssuedOrder" event of the triggers.
     572            var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
     573            cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Heal", "metadata": msg, "target": this.order.data.target});
     574       
    534575            // We've started walking to the given point
    535576            this.SetNextState("INDIVIDUAL.HEAL.APPROACHING");
    536577            return;
     
    588629            this.StopMoving();
    589630            this.SetNextStateAlwaysEntering("INDIVIDUAL.GATHER.GATHERING");
    590631        }
     632       
     633        // Call "OnUnitIssuedOrder" event of the triggers.
     634        var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
     635        cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Gather", "metadata": msg, "target": this.order.data.target});
    591636    },
    592637
    593638    "Order.GatherNearPosition": function(msg) {
    594639        // Move the unit to the position to gather from.
    595640        this.MoveToPoint(this.order.data.x, this.order.data.z);
    596641        this.SetNextState("INDIVIDUAL.GATHER.WALKING");
     642       
     643        // Call "OnUnitIssuedOrder" event of the triggers.
     644        var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
     645        cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.GatherNearPosition", "metadata": msg});
    597646    },
    598647
    599648    "Order.ReturnResource": function(msg) {
     
    617666        // Try to move to the dropsite
    618667        if (this.MoveToTargetRange(this.order.data.target, IID_ResourceGatherer))
    619668        {
     669            // Call "OnUnitIssuedOrder" event of the triggers.
     670            var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
     671            cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.ReturnResource", "metadata": msg, "target": this.order.data.target});
     672       
    620673            // We've started walking to the target
    621674            this.SetNextState("INDIVIDUAL.RETURNRESOURCE.APPROACHING");
    622675            return;
     
    649702        this.waypoints = undefined;
    650703        if (this.MoveToMarket(nextMarket))
    651704        {
     705            // Call "OnUnitIssuedOrder" event of the triggers.
     706            var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
     707            cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Trade", "metadata": msg, "target": this.order.data.target});
     708       
    652709            // We've started walking to the next market
    653710            this.SetNextState(state);
    654711        }
     
    671728            this.StopMoving();
    672729            this.SetNextState("INDIVIDUAL.REPAIR.REPAIRING");
    673730        }
     731       
     732        // Call "OnUnitIssuedOrder" event of the triggers.
     733        var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
     734        cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Repair", "metadata": msg, "target": this.order.data.target});
    674735    },
    675736
    676737    "Order.Garrison": function(msg) {
     738        // Call "OnUnitIssuedOrder" event of the triggers.
     739        var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
     740        cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Garrison", "metadata": msg, "target": this.order.data.target});
     741   
    677742        // For packable units:
    678743        // 1. If packed, we can move to the garrison target.
    679744        // 2. If unpacked, we first need to pack, then follow case 1.
     
    698763
    699764    "Order.Autogarrison": function(msg) {
    700765        this.SetNextState("INDIVIDUAL.AUTOGARRISON");
     766       
     767        // Call "OnUnitIssuedOrder" event of the triggers.
     768        var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
     769        cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Autogarrison", "metadata": msg});
    701770    },
    702771
    703772    "Order.Alert": function(msg) {
     
    711780            this.ReplaceOrder("Garrison", {"target": this.alertGarrisoningTarget});
    712781        else
    713782            this.FinishOrder();
     783       
     784        // Call "OnUnitIssuedOrder" event of the triggers.
     785        var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
     786        cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Alert", "metadata": msg});
    714787    }, 
    715788
    716789    "Order.Cheering": function(msg) {
    717790        this.SetNextState("INDIVIDUAL.CHEERING");
     791       
     792        // Call "OnUnitIssuedOrder" event of the triggers.
     793        var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
     794        cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Cheering", "metadata": msg});
    718795    },
    719796
    720797    "Order.Pack": function(msg) {
     
    722799        {
    723800            this.StopMoving();
    724801            this.SetNextState("INDIVIDUAL.PACKING");
     802           
     803            // Call "OnUnitIssuedOrder" event of the triggers.
     804            var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
     805            cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Pack", "metadata": msg});
    725806        }
    726807    },
    727808
     
    730811        {
    731812            this.StopMoving();
    732813            this.SetNextState("INDIVIDUAL.UNPACKING");
     814           
     815            // Call "OnUnitIssuedOrder" event of the triggers.
     816            var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
     817            cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Unpack", "metadata": msg});
    733818        }
    734819    },
    735820
     
    738823        if (cmpPack && cmpPack.IsPacking() && !cmpPack.IsPacked())
    739824            cmpPack.CancelPack();
    740825        this.FinishOrder();
     826       
     827        // Call "OnUnitIssuedOrder" event of the triggers.
     828        var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
     829        cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.CancelPack", "metadata": msg});
    741830    },
    742831
    743832    "Order.CancelUnpack": function(msg) {
     
    745834        if (cmpPack && cmpPack.IsPacking() && cmpPack.IsPacked())
    746835            cmpPack.CancelPack();
    747836        this.FinishOrder();
     837       
     838        // Call "OnUnitIssuedOrder" event of the triggers.
     839        var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
     840        cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.CancelUnpack", "metadata": msg});
    748841    },
    749842
    750843    // States for the special entity representing a group of units moving in formation:
     
    809902            var cmpTargetUnitAI = Engine.QueryInterface(target, IID_UnitAI);
    810903            if (cmpTargetUnitAI && cmpTargetUnitAI.IsFormationMember())
    811904                target = cmpTargetUnitAI.GetFormationController();
    812 
     905           
    813906            var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
    814907            // Check if we are already in range, otherwise walk there
    815908            if (!this.CheckTargetAttackRange(target, target))
     
    838931                this.FinishOrder();
    839932                return;
    840933            }
     934           
    841935            // Check if we are already in range, otherwise walk there
    842936            if (!this.CheckGarrisonRange(msg.data.target))
    843937            {
     
    9151009            }
    9161010
    9171011            this.CallMemberFunction("GatherNearPosition", [msg.data.x, msg.data.z, msg.data.type, msg.data.template, false]);
    918 
     1012           
    9191013            this.SetNextStateAlwaysEntering("MEMBER");
    9201014        },
    9211015
     
    9341028            }
    9351029
    9361030            this.CallMemberFunction("Heal", [msg.data.target, false]);
    937 
     1031           
    9381032            this.SetNextStateAlwaysEntering("MEMBER");
    9391033        },
    9401034
     
    11881282
    11891283            // Stop moving as soon as the formation disbands
    11901284            this.StopMoving();
    1191 
     1285           
    11921286            // If the controller handled an order but some members rejected it,
    11931287            // they will have no orders and be in the FORMATIONMEMBER.IDLE state.
    11941288            if (this.orderQueue.length)
  • binaries/data/mods/public/simulation/helpers/Commands.js

     
    8787        break;
    8888
    8989    case "walk":
     90        // Handle OnUnitsIssuedOrder event of the triggers
     91        if (entities.length > 1)
     92        {
     93            var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
     94            cmpTrigger.CallEvent("OnUnitsIssuedOrder", {"entities": entities, "order": "Order.Walk", "metadata": {"x": cmd.x, "z": cmd.z, "queued": cmd.queued}});
     95        }
     96       
    9097        GetFormationUnitAIs(entities, player).forEach(function(cmpUnitAI) {
    9198            cmpUnitAI.Walk(cmd.x, cmd.z, cmd.queued);
    9299        });
    93100        break;
    94101
    95102    case "attack-walk":
     103        // Handle OnUnitsIssuedOrder event of the triggers
     104        if (entities.length > 1)
     105        {
     106            var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
     107            cmpTrigger.CallEvent("OnUnitsIssuedOrder", {"entities": entities, "order": "Order.WalkAndFight", "metadata": {"x": cmd.x, "z": cmd.z, "queued": cmd.queued}});
     108        }
     109       
    96110        GetFormationUnitAIs(entities, player).forEach(function(cmpUnitAI) {
    97111            cmpUnitAI.WalkAndFight(cmd.x, cmd.z, cmd.queued);
    98112        });
     
    104118            // This check is for debugging only!
    105119            warn("Invalid command: attack target is not owned by enemy of player "+player+": "+uneval(cmd));
    106120        }
    107 
     121       
     122        // Handle OnUnitsIssuedOrder event of the triggers
     123        if (entities.length > 1)
     124        {
     125            var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
     126            cmpTrigger.CallEvent("OnUnitsIssuedOrder", {"entities": entities, "order": "Order.Attack", "metadata": {"target": cmd.target, "queued": cmd.queued}});
     127        }
     128       
    108129        // See UnitAI.CanAttack for target checks
    109130        GetFormationUnitAIs(entities, player).forEach(function(cmpUnitAI) {
    110131            cmpUnitAI.Attack(cmd.target, cmd.queued);
     
    118139            warn("Invalid command: heal target is not owned by player "+player+" or their ally: "+uneval(cmd));
    119140        }
    120141
     142        // Handle OnUnitsIssuedOrder event of the triggers
     143        if (entities.length > 1)
     144        {
     145            var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
     146            cmpTrigger.CallEvent("OnUnitsIssuedOrder", {"entities": entities, "order": "Order.Heal", "metadata": {"target": cmd.target, "queued": cmd.queued}});
     147        }
     148       
    121149        // See UnitAI.CanHeal for target checks
    122150        GetFormationUnitAIs(entities, player).forEach(function(cmpUnitAI) {
    123151            cmpUnitAI.Heal(cmd.target, cmd.queued);
     
    132160            warn("Invalid command: repair target is not owned by ally of player "+player+": "+uneval(cmd));
    133161        }
    134162
     163        // Handle OnUnitsIssuedOrder event of the triggers
     164        if (entities.length > 1)
     165        {
     166            var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
     167            cmpTrigger.CallEvent("OnUnitsIssuedOrder", {"entities": entities, "order": "Order.Repair", "metadata": {"target": cmd.target, "autocontinue": cmd.autocontinue, "queued": cmd.queued}});
     168        }
     169       
    135170        // See UnitAI.CanRepair for target checks
    136171        GetFormationUnitAIs(entities, player).forEach(function(cmpUnitAI) {
    137172            cmpUnitAI.Repair(cmd.target, cmd.autocontinue, cmd.queued);
     
    145180            warn("Invalid command: resource is not owned by gaia or player "+player+": "+uneval(cmd));
    146181        }
    147182
     183        // Handle OnUnitsIssuedOrder event of the triggers
     184        if (entities.length > 1)
     185        {
     186            var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
     187            cmpTrigger.CallEvent("OnUnitsIssuedOrder", {"entities": entities, "order": "Order.Gather", "metadata": {"target": cmd.target, "queued": cmd.queued}});
     188        }
     189       
    148190        // See UnitAI.CanGather for target checks
    149191        GetFormationUnitAIs(entities, player).forEach(function(cmpUnitAI) {
    150192            cmpUnitAI.Gather(cmd.target, cmd.queued);
     
    152194        break;
    153195       
    154196    case "gather-near-position":
     197        // Handle OnUnitsIssuedOrder event of the triggers
     198        if (entities.length > 1)
     199        {
     200            var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
     201            cmpTrigger.CallEvent("OnUnitsIssuedOrder", {"entities": entities, "order": "Order.GatherNearPosition", "metadata": {"x": cmd.x, "z": cmd.z, "type": cmd.resourceType, "template": cmd.resourceTemplate, "queued": cmd.queued}});
     202        }
     203   
    155204        GetFormationUnitAIs(entities, player).forEach(function(cmpUnitAI) {
    156205            cmpUnitAI.GatherNearPosition(cmd.x, cmd.z, cmd.resourceType, cmd.resourceTemplate, cmd.queued);
    157206        });
     
    165214            warn("Invalid command: dropsite is not owned by player "+player+": "+uneval(cmd));
    166215        }
    167216
     217        // Handle OnUnitsIssuedOrder event of the triggers
     218        if (entities.length > 1)
     219        {
     220            var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
     221            cmpTrigger.CallEvent("OnUnitsIssuedOrder", {"entities": entities, "order": "Order.ReturnResource", "metadata": {"target": cmd.target, "queued": cmd.queued}});
     222        }
     223       
    168224        // See UnitAI.CanReturnResource for target checks
    169225        GetFormationUnitAIs(entities, player).forEach(function(cmpUnitAI) {
    170226            cmpUnitAI.ReturnResource(cmd.target, cmd.queued);
     
    325381        // Verify that the building can be controlled by the player or is mutualAlly
    326382        if (CanControlUnitOrIsAlly(cmd.target, player, controlAllUnits))
    327383        {
     384           
     385            // Handle OnUnitsIssuedOrder event of the triggers
     386            if (entities.length > 1)
     387            {
     388                var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
     389                cmpTrigger.CallEvent("OnUnitsIssuedOrder", {"entities": entities, "order": "Order.Garrison", "metadata": {"target": cmd.target, "queued": cmd.queued}});
     390            }
     391       
    328392            GetFormationUnitAIs(entities, player).forEach(function(cmpUnitAI) {
    329393                cmpUnitAI.Garrison(cmd.target, cmd.queued);
    330394            });
  • binaries/data/mods/public/simulation/helpers/Damage.js

     
    7474    // Damage the target
    7575    var targetState = cmpDamageReceiver.TakeDamage(data.strengths.hack * data.multiplier, data.strengths.pierce * data.multiplier, data.strengths.crush * data.multiplier, data.attacker);
    7676
     77    // Call the related trigger event
     78    // We have to call the event instead of listening to it by messages because the message is sent
     79    // after the target gets killed, which is not our intended case
     80    var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
     81    cmpTrigger.CallEvent("OnEntityTookDamage", {"attackerEntity": data.attacker, "attackedEntity": data.target, "type":data.type, "ammount":-targetState.change});
     82   
    7783    // If the target was killed run some cleanup
    7884    if (targetState.killed)
    7985        Damage.TargetKilled(data.attacker, data.target);
     
    110116    // Call RangeManager with dummy entity and return the result.
    111117    var rangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
    112118    var rangeQuery = rangeManager.ExecuteQueryAroundPos(origin, 0, radius, players, IID_DamageReceiver);
     119   
    113120    return rangeQuery;
    114121};
    115122
     
    133140    var cmpLooter = Engine.QueryInterface(killerEntity, IID_Looter);
    134141    if (cmpLooter)
    135142        cmpLooter.Collect(targetEntity);
     143       
     144    // Call the related trigger event
     145    var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
     146    cmpTrigger.CallEvent("OnEntityKilled", {"killerEntity": killerEntity, "killedEntity": targetEntity});
    136147};
    137148
    138149Engine.RegisterGlobal("Damage", Damage);
  • binaries/data/mods/public/simulation/helpers/TriggerHelpers.js

     
     1// Contains standardized functions suitable for using in trigger scripts.
     2// Do not use them in any other simulation script.
     3
     4/**
     5 * A function to get the owner of an entity.
     6 * Returns the ID of a player. Returns 0 if the owner is Gaia.
     7 */
     8function GetEntityOwner(entity)
     9{
     10    return Engine.QueryInterface(entity, IID_Ownership).GetOwner()
     11}
     12
     13/**
     14 * A function to get the generic name of an entity
     15 */
     16function GetEntityGenericName(entity)
     17{
     18    var cmpIdentity = Engine.QueryInterface(entity, IID_Identity);
     19    if (!cmpIdentity)
     20        return undefined;
     21    return cmpIdentity.GetGenericName();
     22}
     23
     24/**
     25 * A function to get a list of the classes of an entity
     26 */
     27function GetEntityClassesList(entity)
     28{
     29    var cmpIdentity = Engine.QueryInterface(entity, IID_Identity);
     30    if (!cmpIdentity)
     31        return undefined;
     32    return cmpIdentity.GetClassesList();
     33}
     34
     35/**
     36 * A function to determine if an entity has a specific class
     37 */
     38function EntityHasClass(entity, classname)
     39{
     40    var classes = GetEntityClassesList(entity);
     41    return (classes && classes.indexOf(classname) != -1);
     42}
     43
     44/**
     45 * Returns the entity id of a player based on the id of the player
     46 * Useful when one wants to change a player's properties.
     47 */
     48function GetPlayerEntityByID(id)
     49{
     50    var cmpPlayerMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
     51    return cmpPlayerMan.GetPlayerByID(id);
     52}
     53
     54/**
     55 * Can be used to "force" a building to spawn a group of entities.
     56 * Only works for buildings that can already train units.
     57 */
     58function BuildingSpawnUnits(entity, template, count)
     59{
     60    var cmpProductionQueue = Engine.QueryInterface(entity, IID_ProductionQueue);
     61    if (!cmpProductionQueue)
     62        return;
     63    cmpProductionQueue.SpawnUnits(template, count, null);
     64}
     65
     66/**
     67 * Shows a message in the top center of the screen
     68 */
     69function PushGUINotificationInTopCenter(player, message)
     70{
     71    var cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
     72    cmpGUIInterface.PushNotification({"player": player, "message": message});
     73}
     74
     75/**
     76 * Returns the resource type that can be gathered from an entity
     77 */
     78function GetEntityResourceType(entity)
     79{
     80    var cmpResourceSupply = Engine.QueryInterface(entity, IID_ResourceSupply);
     81    if (!cmpResourceSupply)
     82        return undefined;
     83    return cmpResourceSupply.GetType();
     84}
     85
     86/**
     87 * Returns a list of entities owned by the player
     88 */
     89function GetEntitiesByPlayer(playerID)
     90{
     91    var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
     92    return cmpRangeManager.GetEntitiesByPlayer(playerID);
     93}
     94
     95/**
     96 * Wins the game for a player
     97 */
     98function SetPlayerWon(playerID)
     99{
     100    var playerEnt = GetPlayerEntityByID(playerID);
     101    var cmpPlayer = Engine.QueryInterface(playerEnt, IID_Player);
     102    cmpPlayer.SetState("won")
     103}
     104
     105/**
     106 * Defeats a player
     107 */
     108function DefeatPlayer(playerID)
     109{
     110    var playerEnt = GetPlayerEntityByID(playerID);
     111    Engine.PostMessage(playerEnt, MT_PlayerDefeated, { "playerId": playerID } );
     112}
     113
     114/**
     115 * Returns the system trigger component.
     116 */
     117function GetTriggerComponent()
     118{
     119    return Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
     120}
     121
     122Engine.RegisterGlobal("GetEntityOwner", GetEntityOwner);
     123Engine.RegisterGlobal("GetEntityGenericName", GetEntityGenericName);
     124Engine.RegisterGlobal("GetEntityClassesList", GetEntityClassesList);
     125Engine.RegisterGlobal("EntityHasClass", EntityHasClass);
     126Engine.RegisterGlobal("GetPlayerEntityByID", GetPlayerEntityByID);
     127Engine.RegisterGlobal("BuildingSpawnUnits", BuildingSpawnUnits);
     128Engine.RegisterGlobal("PushGUINotificationInTopCenter", PushGUINotificationInTopCenter);
     129Engine.RegisterGlobal("GetEntityResourceType", GetEntityResourceType);
     130Engine.RegisterGlobal("GetEntitiesByPlayer", GetEntitiesByPlayer);
     131Engine.RegisterGlobal("SetPlayerWon", SetPlayerWon);
     132Engine.RegisterGlobal("DefeatPlayer", DefeatPlayer);
     133Engine.RegisterGlobal("GetTriggerComponent", GetTriggerComponent);
     134 No newline at end of file
  • source/simulation2/Simulation2.cpp

     
    133133            LOAD_SCRIPTED_COMPONENT("PlayerManager");
    134134            LOAD_SCRIPTED_COMPONENT("TechnologyTemplateManager");
    135135            LOAD_SCRIPTED_COMPONENT("Timer");
     136            LOAD_SCRIPTED_COMPONENT("Trigger");
    136137            LOAD_SCRIPTED_COMPONENT("ValueModificationManager");
    137138
    138139#undef LOAD_SCRIPTED_COMPONENT
     
    748749
    749750    if (!m->m_StartupScript.empty())
    750751        GetScriptInterface().LoadScript(L"map startup script", m->m_StartupScript);
     752   
     753    // Load the trigger script after we have loaded the simulation and the map.
     754    if (GetScriptInterface().HasProperty(m->m_MapSettings.get(), "TriggerScript"))
     755    {
     756        std::string script_name;
     757        GetScriptInterface().GetProperty(m->m_MapSettings.get(), "TriggerScript", script_name);
     758
     759        script_name = "maps/" + script_name;
     760        m->m_ComponentManager.LoadScript(script_name.data());
     761    }
    751762}
    752763
    753764int CSimulation2::ProgressiveLoad()