Ticket #52: Triggers.2.patch

File Triggers.2.patch, 35.2 KB (added by O.Davoodi, 10 years ago)

Newest patch. Ready for review.

  • binaries/data/mods/public/maps/scripts/test.js

     
     1var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
     2
     3// Test rigger to log the kills and defeat a player if his/her hero dies
     4function trigger1_Action(data)
     5{
     6    var cmpKilled = Engine.QueryInterface(data.killedEntity, IID_Identity);
     7    var killedPlayerId = GetEntityOwner(data.killedEntity);
     8    var killerPlayerId = GetEntityOwner(data.killerEntity);
     9    var cmpKiller = Engine.QueryInterface(data.killerEntity, IID_Identity);
     10    log("A player " + killerPlayerId + "'s " + GetEntityGenericName(data.killerEntity) + " killed a player " + killedPlayerId + "'s " + GetEntityGenericName(data.killedEntity) + ".");
     11   
     12    if (EntityHasClass(data.killedEntity, "Hero"))
     13    {
     14        var playerEnt = GetPlayerEntityByID(killedPlayerId);
     15        Engine.PostMessage(playerEnt, MT_PlayerDefeated, { "playerId": killedPlayerId } );
     16    }
     17}
     18
     19// Test trigger to check the "OnUnitRangeFromEntity" event. If entity 473 (Cyrus) gets near the entity 273 (The enemy embassy), 5 swordsman are spawned. This is done only once
     20function trigger2_Action(data)
     21{
     22    if (data.entity == 473)
     23    {
     24        BuildingSpawnUnits(273, "units/cart_infantry_swordsman_2_b", 5);
     25       
     26        log("Ok. Finish it.");
     27        cmpTrigger.DisableTrigger("trigger2");
     28    }
     29}
     30
     31// Test trigger to check the orders given by the player. Only works for the hero and the elite archer
     32function trigger3_Action(data)
     33{
     34    if (data.entity == 473)
     35    {
     36        PushGUINotificationInTopCenter(1, "Your hero is ordered: " + data.order);
     37    }
     38    else if (data.entity == 474)
     39    {
     40        PushGUINotificationInTopCenter(1, "Your archer is ordered: " + data.order);
     41    }
     42}
     43
     44// The set of global trigger actions. The name must always be "g_TriggerActions"
     45var g_TriggerActions = {
     46    "trigger1_Action": trigger1_Action,
     47    "trigger2_Action": trigger2_Action,
     48    "trigger3_Action": trigger3_Action
     49};
     50
     51// Make the trigger actions global so that we can use it in the trigger component.
     52Engine.RegisterGlobal("g_TriggerActions", g_TriggerActions);
     53
     54// Register the triggers
     55cmpTrigger.RegisterTrigger("trigger1", "OnEntityKilled", "trigger1_Action");
     56cmpTrigger.RegisterOnUnitRangeFromEntityTrigger("entered", "trigger2", 273, 40, "trigger2_Action", [0, 1, 2], IID_DamageReceiver);
     57cmpTrigger.RegisterTrigger("trigger3", "OnUnitIssuedOrder", "trigger3_Action");
     58 No newline at end of file
  • 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    // Each event has its own set of actions determined by the map maker.
     9    this.eventOnEntityTookDamageActions = {};
     10    this.eventOnEntityKilledActions = {};
     11    this.eventOnStructureBuiltActions = {};
     12    this.eventOnConstructionStartedActions = {};
     13    this.eventOnTrainingFinishedActions = {};
     14    this.eventOnTrainingQueuedActions = {};
     15    this.eventOnResearchFinishedActions = {};
     16    this.eventOnResearchQueuedActions = {};
     17    this.eventAlwaysActions = {};
     18    this.eventOnUnitRangeFromEntityData = {};
     19    this.eventOnUnitIssuedOrderActions = {};
     20   
     21    // We are going to have all of the triggers here to be able to enable/disbale them in runtime.
     22    this.triggerEnabled = {};
     23   
     24    // Queries for OnUnitFromEntity events
     25    this.activeRangeQueries = {};
     26};
     27
     28Trigger.prototype.StartUpdateTimer = function(interval, delay)
     29{
     30    delay = (delay === undefined ? 0 : delay);
     31   
     32    // Stop the last timer before starting a new one
     33    if (this.timer !== undefined)
     34        this.StopUpdateTimer();
     35
     36    // Start the update timer
     37    var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
     38    this.timer = cmpTimer.SetInterval(this.entity, IID_Trigger, "TimerUpdate", delay, interval, undefined);
     39}
     40
     41Trigger.prototype.StopUpdateTimer = function()
     42{
     43    // Call the update timer
     44    if (this.timer !== undefined)
     45    {
     46        var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
     47        cmpTimer.CancelTimer(this.timer);
     48        this.timer = undefined;
     49    }
     50}
     51
     52// Binds the "action" function to one of the implemented events. "name" is a string for further access
     53// to the trigger for enabling/disabling.
     54Trigger.prototype.RegisterTrigger = function(name, event, action, enabled)
     55{
     56    enabled = (enabled !== undefined ? enabled : true);
     57    var validEvent = true;
     58    switch (event)
     59    {
     60    case "OnEntityTookDamage":
     61        this.eventOnEntityTookDamageActions[name] = action;
     62        break;
     63    case "OnEntityKilled":
     64        this.eventOnEntityKilledActions[name] = action;
     65        break;
     66    case "OnUnitIssuedOrder":
     67        this.eventOnUnitIssuedOrderActions[name] = action;
     68        break;
     69    case "OnStructureBuilt":
     70        this.eventOnStructureBuiltActions[name] = action;
     71        break;
     72    case "OnConstructionStarted":
     73        this.eventOnConstructionStartedActions[name] = action;
     74        break;
     75    case "OnTrainingFinished":
     76        this.eventOnTrainingFinishedActions[name] = action;
     77        break;
     78    case "OnTrainingQueued":
     79        this.eventOnTrainingQueuedActions[name] = action;
     80        break;
     81    case "OnResearchFinished":
     82        this.eventOnResearchFinishedActions[name] = action;
     83        break;
     84    case "OnResearchQueued":
     85        this.eventOnResearchQueuedActions[name] = action;
     86        break;
     87    case "Always":
     88        this.eventAlwaysActions[name] = action;
     89        break;
     90    default:
     91        validEvent = false;
     92        break;
     93    }
     94    if (validEvent)
     95        this.triggerEnabled[name] = enabled
     96    else
     97        error("Invalid trigger event \"" + event + "\".");
     98};
     99
     100// This is an event with more than just the name argument, so we should have a special register function for it.
     101Trigger.prototype.RegisterOnUnitRangeFromEntityTrigger = function(event, name, entity, radius, action, players, component_id, enabled)
     102{
     103    enabled = (enabled !== undefined ? enabled : true);
     104    component_id = (component_id !== undefined ? component_id : 0);
     105   
     106    // If the players parameter is not specified use all players.
     107    if (!players)
     108    {
     109        var playerEntities = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetAllPlayerEntities();
     110        players = [];
     111        for each (var pentity in playerEntities)
     112            players.push(Engine.QueryInterface(pentity, IID_Player).GetPlayerID());
     113    }
     114   
     115    // 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.
     116    // For the purpose of finding out the intersection of the previous and the current rangeQuery faster, we are sorting the current one.
     117    this.eventOnUnitRangeFromEntityData[name] = {"event": event, "action": action, "entity": entity, "radius": radius, "currentCollection": [], "players": players};
     118    this.triggerEnabled[name] = enabled;
     119   
     120    var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
     121   
     122    this.activeRangeQueries[name] = cmpRangeManager.CreateActiveQuery(entity, 0, radius, players, component_id, cmpRangeManager.GetEntityFlagMask("normal"));
     123    cmpRangeManager.EnableActiveQuery(this.activeRangeQueries[name]);
     124}
     125
     126// Disable trigger
     127Trigger.prototype.DisableTrigger = function(name)
     128{
     129    if (this.triggerEnabled[name] !== undefined)
     130        this.triggerEnabled[name] = false;
     131    else
     132        warn("Trigger doesn't exist:" + name);
     133}
     134
     135// Enable trigger
     136Trigger.prototype.EnableTrigger = function(name)
     137{
     138    if (this.triggerEnabled[name] !== undefined)
     139        this.triggerEnabled[name] = true;
     140    else
     141        warn("Trigger doesn't exist:" + name);
     142}
     143
     144// To handle the events that cannot be tracked by messages, this function should be called in the simulation code.
     145Trigger.prototype.CallEvent = function(event, data)
     146{
     147    if (event == "OnEntityKilled")
     148    {
     149        for (var i in this.eventOnEntityKilledActions)
     150        {
     151            if (this.triggerEnabled[i])    // Check if it is enabled
     152                g_TriggerActions[this.eventOnEntityKilledActions[i]](data); // The data for this one is {"killerEntity": killerEntity, "killedEntity": targetEntity}
     153       
     154            // Remove the "OnUnitFromRangeEntity" triggers which have this entity as their argument
     155            for (var j in this.eventOnUnitRangeFromEntityData)
     156            {
     157                if (this.eventOnUnitRangeFromEntityData[j].entity == data.killedEntity)
     158                {
     159                    delete this.eventOnUnitRangeFromEntityData[j];
     160                    delete this.triggerEnabled[j];
     161                }
     162            }
     163        }
     164       
     165        return;
     166    }
     167   
     168    if (this["event" + event + "Actions"] === undefined)
     169    {
     170        warn("Invalid trigger event \"" + event + "\" called.");
     171        return;
     172    }
     173   
     174    for (var i in this["event" + event + "Actions"])
     175        if (this.triggerEnabled[i])
     176            g_TriggerActions[this["event" + event + "Actions"][i]](data);
     177}
     178
     179// Handles "OnStructureBuilt" event.
     180Trigger.prototype.OnGlobalConstructionFinished = function(msg)
     181{
     182    for (var i in this.eventOnStructureBuiltActions)
     183        if (this.triggerEnabled[i])    // Check if it is enabled
     184            g_TriggerActions[this.eventOnStructureBuiltActions[i]]({"building": msg.newentity}); // The data for this one is {"building": constructedBuilding}
     185}
     186
     187// Handles "OnTrainingFinished" event.
     188Trigger.prototype.OnGlobalTrainingFinished = function(msg)
     189{
     190    for (var i in this.eventOnTrainingFinishedActions)
     191        if (this.triggerEnabled[i])    // Check if it is enabled
     192            g_TriggerActions[this.eventOnTrainingFinishedActions[i]](msg); // The data for this one is {"entities": createdEnts,
     193                                                                     // "owner": cmpOwnership.GetOwner(),
     194                                                                     // "metadata": metadata,}
     195                                                                     // See function "SpawnUnits" in ProductionQueue for more details
     196}
     197
     198// Handles an event that occurs by the interval the map designer specifies
     199Trigger.prototype.TimerUpdate = function(data, lateness)
     200{
     201    // Handle "Always" event.
     202    for (var i in this.eventAlwaysActions)
     203        if (this.triggerEnabled[i])    // Check if it is enabled
     204            g_TriggerActions[this.eventAlwaysActions[i]](); // This function event has no data
     205}
     206
     207// Handles "OnUnitRangeFromEntity"
     208Trigger.prototype.OnGlobalRangeUpdate = function(msg)
     209{
     210    var updatedRange = undefined;
     211    for (var name in this.activeRangeQueries)
     212    {
     213        if (msg.tag == this.activeRangeQueries[name])
     214        {
     215            updatedRange = name;
     216            break;
     217        }
     218    }
     219   
     220    if (updatedRange === undefined)
     221        return;
     222
     223    for each (var entity in msg.removed)
     224    {
     225        var index = this.eventOnUnitRangeFromEntityData[name].currentCollection.indexOf(entity);
     226            if (index > -1)
     227                this.eventOnUnitRangeFromEntityData[name].currentCollection.splice(index, 1);
     228        this.eventOnUnitRangeFromEntityData[name].currentCollection.push(entity);
     229    }
     230       
     231    for each (var entity in msg.added)
     232        this.eventOnUnitRangeFromEntityData[name].currentCollection.push(entity);
     233       
     234    if (this.triggerEnabled[name])    // Check if it is enabled
     235    {
     236        if (this.eventOnUnitRangeFromEntityData[name].event == "moved")
     237        {
     238            for each (var entity in this.eventOnUnitRangeFromEntityData[name].currentCollection)
     239            {
     240                var cmpUnitMotion = Engine.QueryInterface(entity, IID_UnitMotion);
     241                if (cmpUnitMotion && cmpUnitMotion.IsMoving())
     242                    g_TriggerActions[this.eventOnUnitRangeFromEntityData[name].action]({"entity": entity});
     243            }
     244        }
     245        else if (this.eventOnUnitRangeFromEntityData[name].event == "entered")
     246        {
     247            for each (var entity in msg.added)
     248            {
     249                g_TriggerActions[this.eventOnUnitRangeFromEntityData[name].action]({"entity": entity});
     250            }
     251        }
     252        else if (this.eventOnUnitRangeFromEntityData[name].event == "left")
     253        {
     254            for each (var entity in msg.removed)
     255            {
     256                g_TriggerActions[this.eventOnUnitRangeFromEntityData[name].action]({"entity": entity});
     257            }
     258        }
     259        else if (this.eventOnUnitRangeFromEntityData[name].event == "was inside")
     260        {
     261            for each (var entity in this.eventOnUnitRangeFromEntityData[name].currentCollection)
     262            {
     263                g_TriggerActions[this.eventOnUnitRangeFromEntityData[name].action]({"entity": entity});
     264            }
     265        }
     266    }
     267}
     268
     269// These two functions are for handling one time timers in the triggers.
     270Trigger.prototype.DoAfterDelay = function(miliseconds, action, data)
     271{
     272    var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
     273    return cmpTimer.SetTimeout(SYSTEM_ENTITY, IID_Trigger, "DoAction", miliseconds, {"action": action, "data": data});
     274}
     275
     276Trigger.prototype.DoAction = function(data)
     277{
     278    g_TriggerActions[data.action](data);
     279}
     280
     281Engine.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, "target": this.order.data.target});
    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/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    return cmpIdentity.GetGenericName();
     20}
     21
     22/**
     23 * A function to get a list of the classes of an entity
     24 */
     25function GetEntityClassesList(entity)
     26{
     27    var cmpIdentity = Engine.QueryInterface(entity, IID_Identity);
     28    return cmpIdentity.GetClassesList();
     29}
     30
     31/**
     32 * A function to determine if an entity has a specific class
     33 */
     34function EntityHasClass(entity, classname)
     35{
     36    var classes = GetEntityClassesList(entity);
     37    return (classes && classes.indexOf(classname) != -1);
     38}
     39
     40/**
     41 * Returns the entity id of a player based on the id of the player
     42 * Useful when one wants to change a player's properties.
     43 */
     44function GetPlayerEntityByID(id)
     45{
     46    var cmpPlayerMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
     47    return cmpPlayerMan.GetPlayerByID(id);
     48}
     49
     50/**
     51 * Can be used to "force" a building to spawn a group of entities.
     52 * Only works for buildings that can already train units.
     53 */
     54function BuildingSpawnUnits(entity, template, count)
     55{
     56    var cmpProductionQueue = Engine.QueryInterface(entity, IID_ProductionQueue);
     57    cmpProductionQueue.SpawnUnits(template, count, null);
     58}
     59
     60/**
     61 * Shows a message in the top center of the screen
     62 */
     63function PushGUINotificationInTopCenter(player, message)
     64{
     65    var cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
     66    cmpGUIInterface.PushNotification({"player": player, "message": message});
     67}
     68
     69/**
     70 * Returns the resource type that can be gathered from an entity
     71 */
     72function GetEntityResourceType(entity)
     73{
     74    var cmpResourceSupply = Engine.QueryInterface(entity, IID_ResourceSupply);
     75    if (!cmpResourceSupply)
     76        return undefined;
     77    return cmpResourceSupply.GetType();
     78}
     79
     80/**
     81 * Returns a list of entities owned by the player
     82 */
     83function GetEntitiesByPlayer(playerID)
     84{
     85    var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
     86    return cmpRangeManager.GetEntitiesByPlayer(playerID);
     87}
     88
     89/**
     90 * Wins the game for a player
     91 */
     92function SetPlayerWon(playerID)
     93{
     94    var playerEnt = GetPlayerEntityByID(playerID);
     95    var cmpPlayer = Engine.QueryInterface(playerEnt, IID_Player);
     96    cmpPlayer.SetState("won")
     97}
     98
     99/**
     100 * Defeats a player
     101 */
     102function DefeatPlayer(playerID)
     103{
     104    var playerEnt = GetPlayerEntityByID(playerID);
     105    Engine.PostMessage(playerEnt, MT_PlayerDefeated, { "playerId": playerID } );
     106}
     107
     108Engine.RegisterGlobal("GetEntityOwner", GetEntityOwner);
     109Engine.RegisterGlobal("GetEntityGenericName", GetEntityGenericName);
     110Engine.RegisterGlobal("GetEntityClassesList", GetEntityClassesList);
     111Engine.RegisterGlobal("EntityHasClass", EntityHasClass);
     112Engine.RegisterGlobal("GetPlayerEntityByID", GetPlayerEntityByID);
     113Engine.RegisterGlobal("BuildingSpawnUnits", BuildingSpawnUnits);
     114Engine.RegisterGlobal("PushGUINotificationInTopCenter", PushGUINotificationInTopCenter);
     115Engine.RegisterGlobal("GetEntityResourceType", GetEntityResourceType);
     116Engine.RegisterGlobal("GetEntitiesByPlayer", GetEntitiesByPlayer);
     117Engine.RegisterGlobal("SetPlayerWon", SetPlayerWon);
     118Engine.RegisterGlobal("DefeatPlayer", DefeatPlayer);
     119 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/scripts/" + script_name;
     760        m->m_ComponentManager.LoadScript(script_name.data());
     761    }
    751762}
    752763
    753764int CSimulation2::ProgressiveLoad()