Ticket #52: Triggers.4.patch

File Triggers.4.patch, 37.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        "Always"
     20    ];
     21       
     22    // Each event has its own set of actions determined by the map maker.
     23    for each (var eventName in eventNames)
     24        this["event" + eventName + "Actions"] = [];
     25     
     26    this.eventOnUnitRangeFromEntityData = {};
     27
     28    // We are going to have all of the triggers here to be able to enable/disbale them in runtime.
     29    this.triggerEnabled = {};
     30};
     31
     32/**
     33 * Start the update timer, will cause the "Always" events to be triggered every timer tick
     34 */
     35Trigger.prototype.StartUpdateTimer = function(delay, interval)
     36{
     37    // Stop the last timer before starting a new one
     38    if (this.timer !== undefined)
     39        this.StopUpdateTimer();
     40
     41    // Start the update timer
     42    var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
     43    this.timer = cmpTimer.SetInterval(this.entity, IID_Trigger, "TimerUpdate", delay, interval, undefined);
     44}
     45
     46/**
     47 * Stop the update timer, will cause the "Always" events to never be triggered again
     48 */
     49Trigger.prototype.StopUpdateTimer = function()
     50{
     51    // Call the update timer
     52    if (this.timer !== undefined)
     53    {
     54        var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
     55        cmpTimer.CancelTimer(this.timer);
     56        this.timer = undefined;
     57    }
     58}
     59
     60/**  Binds the "action" function to one of the implemented events.
     61 *
     62 * @param eventName Name of the event (see the list in init) 
     63 * @param action Functionname of a function available under the g_TriggersData global object
     64 */
     65Trigger.prototype.RegisterTrigger = function(event, action, enabled)
     66{
     67    var eventString = "event" + event + "Actions";
     68    if (this[eventString])
     69    {
     70        this[eventString].push(action);
     71        this.triggerEnabled[[event, action]] = enabled
     72    }
     73    else
     74        error("Invalid trigger event \"" + event + "\".")
     75};
     76
     77/** 
     78 * This is an event with more than just the key argument, 
     79 * so we should have a special register function for it.
     80 *
     81 * @param event Either "entered" or "left" based on the wanter behaviour.
     82 * @param entity Entity id around which the range is checked
     83 * @param radius Radius of the range
     84 * @param action Functionname to execute on a range change (entities entered or left)
     85 * @param players Array of player ids (0 to 8). Or null if you want to check for all players
     86 * @param componentID Only entities with this component id will be noticed. 0 for all entities
     87 * @param enabled Determines whether if the trigger is active by default
     88 */
     89Trigger.prototype.RegisterOnUnitRangeFromEntityTrigger = function(event, entity, radius, action, players, component_id, enabled)
     90{
     91    // If the players parameter is not specified use all players.
     92    if (!players)
     93    {
     94        var playerEntities = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetAllPlayerEntities();
     95        players = [];
     96        for each (var pentity in playerEntities)
     97            players.push(Engine.QueryInterface(pentity, IID_Player).GetPlayerID());
     98    }
     99   
     100    // 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.
     101    // For the purpose of finding out the intersection of the previous and the current rangeQuery faster, we are sorting the current one.
     102    this.eventOnUnitRangeFromEntityData[action] = {"event": event, "action": action, "entity": entity, "radius": radius, "currentCollection": [], "players": players};
     103   
     104    var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
     105   
     106    this.eventOnUnitRangeFromEntityData[action].rangeQuery = cmpRangeManager.CreateActiveQuery(entity, 0, radius, players, component_id, cmpRangeManager.GetEntityFlagMask("normal"));
     107    if (enabled)
     108        cmpRangeManager.EnableActiveQuery(this.eventOnUnitRangeFromEntityData[action].rangeQuery);
     109}
     110
     111// Range triggers are different in nature, and should have their own Enable and disable functions
     112Trigger.prototype.DisableRangeTrigger = function(action)
     113{
     114    var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
     115    cmpRangeManager.DisableActiveQuery(this.eventOnUnitRangeFromEntityData[action].rangeQuery);
     116}
     117
     118Trigger.prototype.EnableRangeTrigger = function(action)
     119{
     120    var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
     121    cmpRangeManager.EnableActiveQuery(this.eventOnUnitRangeFromEntityData[action].rangeQuery);
     122}
     123
     124// Disable trigger
     125Trigger.prototype.DisableTrigger = function(event, action)
     126{
     127    if (this.triggerEnabled[[event, action]] !== undefined)
     128        this.triggerEnabled[[event, action]] = false;
     129    else
     130        warn("Trigger key doesn't exist:" + event + " & " + action);
     131}
     132
     133// Enable trigger
     134Trigger.prototype.EnableTrigger = function(event, action)
     135{
     136    if (this.triggerEnabled[[event, action]] !== undefined)
     137        this.triggerEnabled[[event, action]] = true;
     138    else
     139        warn("Trigger key doesn't exist:" + event + " & " + action);
     140}
     141
     142/**
     143 * This function executes the actions bound to the events
     144 * It's either called directlty from other simulation scripts, 
     145 * or from message listeners in this file
     146 *
     147 * @param event Name of the event (see the list in init)
     148 * @param data Data object that will be passed to the actions
     149 */
     150Trigger.prototype.CallEvent = function(event, data)
     151{
     152    // A special case, when a unit dies, the range event associated with it should also be removed
     153    if (event == "OnEntityKilled")
     154    {
     155        for (var i in this.eventOnEntityKilledActions)
     156        {
     157            if (this.triggerEnabled[[event, i]])    // Check if it is enabled
     158                g_TriggerData[this.eventOnEntityKilledActions[i]](data); // The data for this one is {"killerEntity": killerEntity, "killedEntity": targetEntity}
     159       
     160            // Remove the "OnUnitFromRangeEntity" triggers which have this entity as their argument
     161            for (var j in this.eventOnUnitRangeFromEntityData)
     162                if (this.eventOnUnitRangeFromEntityData[j].entity == data.killedEntity)
     163                    {
     164                        DisableRangeTrigger(j);
     165                        delete this.eventOnUnitRangeFromEntityData[j];
     166                    }
     167        }
     168       
     169        return;
     170    }
     171   
     172    var actions = this["event" + event + "Actions"];
     173   
     174    if (actions === undefined)
     175    {
     176        warn("Unknown trigger event:\"" + event + "\".");
     177        return;
     178    }
     179   
     180    for (var i in actions)
     181    {
     182        if (this.triggerEnabled[[event, actions[i]]])
     183            g_TriggerData[this["event" + event + "Actions"][i]](data);
     184    }
     185}
     186
     187// Handles "OnStructureBuilt" event.
     188Trigger.prototype.OnGlobalConstructionFinished = function(msg)
     189{
     190    this.CallEvent("OnStructureBuilt", {"building": msg.newentity}); // The data for this one is {"building": constructedBuilding}
     191}
     192
     193// Handles "OnTrainingFinished" event.
     194Trigger.prototype.OnGlobalTrainingFinished = function(msg)
     195{
     196    this.CallEvent("OnTrainingFinished", msg);
     197    // The data for this one is {"entities": createdEnts,
     198    //                           "owner": cmpOwnership.GetOwner(),
     199    //                           "metadata": metadata}
     200    // See function "SpawnUnits" in ProductionQueue for more details
     201}
     202
     203// Handles an event that occurs by the interval the map designer specifies
     204Trigger.prototype.TimerUpdate = function(data, lateness)
     205{
     206    // Handle "Always" event.
     207    this.CallEvent("Always", {}); // This event has no data
     208}
     209
     210// Handles "OnUnitRangeFromEntity"
     211Trigger.prototype.OnGlobalRangeUpdate = function(msg)
     212{
     213    // search the right query
     214    for (var action in this.eventOnUnitRangeFromEntityData)
     215    {
     216        if (msg.tag == this.eventOnUnitRangeFromEntityData[action].rangeQuery)
     217        {
     218            var updatedQuery = this.eventOnUnitRangeFromEntityData[action];
     219            break;
     220        }
     221    }
     222   
     223    if (!updatedQuery)
     224        return;
     225
     226    for each (var entity in msg.removed)
     227    {
     228        var index = updatedQuery.currentCollection.indexOf(entity);
     229            if (index > -1)
     230                updatedQuery.currentCollection.splice(index, 1);
     231    }
     232       
     233    for each (var entity in msg.added)
     234        updatedQuery.currentCollection.push(entity);
     235       
     236    if (updatedQuery.event == "entered")
     237        for each (var entity in msg.added)
     238            g_TriggerData[updatedQuery.action]({"entity": entity});
     239    else if (updatedQuery.event == "left")
     240        for each (var entity in msg.removed)
     241            g_TriggerData[updatedQuery.action]({"entity": entity});
     242}
     243
     244/**
     245 * Execute a function after a certain delay
     246 * @param time The delay expressed in milleseconds
     247 * @param action Name of the action function
     248 * @param data Data object that will be passed to the action function
     249 */
     250Trigger.prototype.DoAfterDelay = function(miliseconds, action, data)
     251{
     252    var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
     253    return cmpTimer.SetTimeout(SYSTEM_ENTITY, IID_Trigger, "DoAction", miliseconds, {"action": action, "data": data});
     254}
     255
     256/**
     257 * Called by the trigger listeners to exucute the actual action. Including sanity checks.
     258 */
     259Trigger.prototype.DoAction = function(msg)
     260{
     261    if (g_TriggerData[msg.action])
     262        g_TriggerData[msg.action](msg.data);
     263    else
     264        error("called a trigger action '" + msg.action + "' that wasn't added to the g_TriggersData global");
     265}
     266
     267Engine.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
     114Engine.RegisterGlobal("GetEntityOwner", GetEntityOwner);
     115Engine.RegisterGlobal("GetEntityGenericName", GetEntityGenericName);
     116Engine.RegisterGlobal("GetEntityClassesList", GetEntityClassesList);
     117Engine.RegisterGlobal("EntityHasClass", EntityHasClass);
     118Engine.RegisterGlobal("GetPlayerEntityByID", GetPlayerEntityByID);
     119Engine.RegisterGlobal("BuildingSpawnUnits", BuildingSpawnUnits);
     120Engine.RegisterGlobal("PushGUINotificationInTopCenter", PushGUINotificationInTopCenter);
     121Engine.RegisterGlobal("GetEntityResourceType", GetEntityResourceType);
     122Engine.RegisterGlobal("GetEntitiesByPlayer", GetEntitiesByPlayer);
     123Engine.RegisterGlobal("SetPlayerWon", SetPlayerWon);
     124Engine.RegisterGlobal("DefeatPlayer", DefeatPlayer);
     125 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()