Index: binaries/data/mods/public/simulation/components/BuildingAI.js
===================================================================
--- binaries/data/mods/public/simulation/components/BuildingAI.js (revision 14795)
+++ binaries/data/mods/public/simulation/components/BuildingAI.js (working copy)
@@ -146,7 +146,6 @@
*/
BuildingAI.prototype.OnRangeUpdate = function(msg)
{
-
var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
if (!cmpAttack)
return;
Index: binaries/data/mods/public/simulation/components/Foundation.js
===================================================================
--- binaries/data/mods/public/simulation/components/Foundation.js (revision 14795)
+++ binaries/data/mods/public/simulation/components/Foundation.js (working copy)
@@ -202,6 +202,10 @@
// (via CCmpTemplateManager). Now we need to remove that temporary
// blocker-disabling, so that we'll perform standard unit blocking instead.
cmpObstruction.SetDisableBlockMovementPathfinding(false, false, -1);
+
+ // Call the related trigger event
+ var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
+ cmpTrigger.CallEvent("OnConstructionStarted", {"foundation": this.entity, "template": this.finalTemplateName});
}
// Switch foundation to scaffold variant
Index: binaries/data/mods/public/simulation/components/PlayerManager.js
===================================================================
--- binaries/data/mods/public/simulation/components/PlayerManager.js (revision 14795)
+++ binaries/data/mods/public/simulation/components/PlayerManager.js (working copy)
@@ -31,6 +31,9 @@
return INVALID_ENTITY;
};
+/**
+ * Get the number of players (including gaia)
+ */
PlayerManager.prototype.GetNumPlayers = function()
{
return this.playerEntities.length;
Index: binaries/data/mods/public/simulation/components/ProductionQueue.js
===================================================================
--- binaries/data/mods/public/simulation/components/ProductionQueue.js (revision 14795)
+++ binaries/data/mods/public/simulation/components/ProductionQueue.js (working copy)
@@ -288,6 +288,10 @@
"timeTotal": time*1000,
"timeRemaining": time*1000,
});
+
+ // Call the related trigger event
+ var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
+ cmpTrigger.CallEvent("OnTrainingQueued", {"playerid": cmpPlayer.GetPlayerID(), "unitTemplate": templateName, "count": count, "metadata": metadata, "trainerEntity": this.entity});
}
else if (type == "technology")
{
@@ -324,6 +328,10 @@
"timeTotal": time*1000,
"timeRemaining": time*1000,
});
+
+ // Call the related trigger event
+ var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
+ cmpTrigger.CallEvent("OnResearchQueued", {"playerid": cmpPlayer.GetPlayerID(), "technologyTemplate": templateName, "researcherEntity": this.entity});
}
else
{
Index: binaries/data/mods/public/simulation/components/ResourceTrickle.js
===================================================================
--- binaries/data/mods/public/simulation/components/ResourceTrickle.js (revision 14795)
+++ binaries/data/mods/public/simulation/components/ResourceTrickle.js (working copy)
@@ -57,7 +57,6 @@
if (cmpPlayer)
for (var resource in rates)
cmpPlayer.AddResource(resource, rates[resource]);
-
};
Engine.RegisterComponentType(IID_ResourceTrickle, "ResourceTrickle", ResourceTrickle);
Index: binaries/data/mods/public/simulation/components/TechnologyManager.js
===================================================================
--- binaries/data/mods/public/simulation/components/TechnologyManager.js (revision 14795)
+++ binaries/data/mods/public/simulation/components/TechnologyManager.js (working copy)
@@ -286,6 +286,10 @@
return;
}
+ // Call the related trigger event
+ var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
+ cmpTrigger.CallEvent("OnResearchFinished", {"researcher": this.entity, "tech": tech});
+
var modifiedComponents = {};
this.researchedTechs[tech] = template;
// store the modifications in an easy to access structure
Index: binaries/data/mods/public/simulation/components/Trigger.js
===================================================================
--- binaries/data/mods/public/simulation/components/Trigger.js (revision 0)
+++ binaries/data/mods/public/simulation/components/Trigger.js (working copy)
@@ -0,0 +1,220 @@
+function Trigger() {}
+
+Trigger.prototype.Schema =
+ "";
+
+Trigger.prototype.Init = function()
+{
+ // Each event has its own set of actions determined by the map maker.
+ var eventNames = [
+ "OnEntityTookDamage",
+ "OnEntityKilled",
+ "OnStructureBuilt",
+ "OnConstructionStarted",
+ "OnTrainingFinished",
+ "OnTrainingQueued",
+ "OnResearchFinished",
+ "OnResearchQueued",
+ "OnTimer",
+ "OnUnitIssuedOrder"];
+ for each (var eventName in eventNames)
+ this["event" + eventName + "Actions"] = [];
+
+ this.eventOnUnitRangeFromEntityData = {};
+};
+
+/**
+ * Start the update timer, will cause the "Always" events to be triggered every timer tick
+ */
+Trigger.prototype.StartUpdateTimer = function(delay, interval)
+{
+ // Stop the last timer before starting a new one
+ if (this.timer)
+ this.StopUpdateTimer();
+
+ // Start the update timer
+ var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
+ this.timer = cmpTimer.SetInterval(this.entity, IID_Trigger, "TimerUpdate", delay, interval, null);
+}
+
+/**
+ * Stop the update timer, will cause the "Always" events to never be triggered again
+ */
+Trigger.prototype.StopUpdateTimer = function()
+{
+ // Call the update timer
+ if (!this.timer)
+ return;
+ var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
+ cmpTimer.CancelTimer(this.timer);
+ this.timer = undefined;
+}
+
+/**
+ * Binds the "action" function to one of the implemented events.
+ *
+ * @param eventName Name of the event (see the list in init)
+ * @param action Functionname of a function available under the g_Triggers global object
+ */
+Trigger.prototype.RegisterTrigger = function(eventName, action)
+{
+ var eventString = "event" + eventName + "Actions";
+ if (this[eventString])
+ this[eventString].push(action);
+ else
+ error("event " + eventName + " not found as an existing trigger event.");
+};
+
+/*
+ * This is an event with more than just the key argument,
+ * so we should have a special register function for it.
+ *
+ * @param entity Entity id around which the range is checked
+ * @param radius Radius of the range
+ * @param action Functionname to execute on a range change (entities entered or left)
+ * @param players Array of player ids (0 to 8). Or null if you want to check for all players
+ * @param componentID Only entities with this component id will be noticed. 0 for all entities
+ */
+Trigger.prototype.RegisterOnUnitRangeFromEntityTrigger = function(entity, radius, action, players, componentID)
+{
+ // If the players parameter is not specified use all players.
+ if (!players)
+ {
+ var numPlayers = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetNumPlayers;
+ players = [];
+ for (var i = 0; i < numPlayers; i++)
+ players.push(i);
+ }
+
+ var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
+
+ var rangeQueryID = cmpRangeManager.CreateActiveQuery(entity, 0, radius, players, componentID || 0, cmpRangeManager.GetEntityFlagMask("normal"));
+
+ cmpRangeManager.EnableActiveQuery(rangeQueryID);
+
+ this.eventOnUnitRangeFromEntityData[action] = {
+ "action": action,
+ "entity": entity,
+ "radius": radius,
+ "currentCollection": [],
+ "rangeQuery": rangeQueryID,
+ "players": players};
+}
+
+Trigger.prototype.DisableRangeTrigger = function(action)
+{
+ var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
+ cmpRangeManager.DisableActiveQuery(this.eventOnUnitRangeFromEntityData[action].rangeQuery);
+}
+
+Trigger.prototype.EnableRangeTrigger = function(action)
+{
+ var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
+ cmpRangeManager.EnableActiveQuery(this.eventOnUnitRangeFromEntityData[action].rangeQuery);
+}
+
+Trigger.prototype.RemoveTrigger = function(eventName, action)
+{
+ var eventString = "event" + eventName + "Actions";
+ if (this[eventString])
+ {
+ var index = this[eventString].indexOf(action);
+ if (index > -1)
+ this[eventString].splice(index, 1);
+ }
+ else
+ error("event " + eventName + " not found as an existing trigger event.");
+}
+
+/**
+ * This function executes the actions bound to the events
+ * It's either called directlty from other simulation scripts,
+ * or from message listeners in this file
+ *
+ * @param eventName Name of the event (see the list in init)
+ * @param data Data object that will be passed to the actions
+ */
+Trigger.prototype.CallEvent = function(eventName, data)
+{
+ var eventString = "event" + eventName + "Actions";
+ if (!this[eventString])
+ error("Unknown trigger event "+eventName);
+ else
+ this[eventString].forEach(function(action){this.DoAction({"action": action, "data": data});}, this);
+}
+
+Trigger.prototype.OnGlobalConstructionFinished = function(msg)
+{
+ this.CallEvent("OnStructureBuilt", {"building": msg.newentity});
+}
+
+Trigger.prototype.OnGlobalTrainingFinished = function(msg)
+{
+ this.CallEvent("OnTrainingFinished", msg);
+ // The data for this one is {"entities": createdEnts,
+ // "owner": cmpOwnership.GetOwner(),
+ // "metadata": metadata}
+ // See function "SpawnUnits" in ProductionQueue for more details
+}
+
+Trigger.prototype.TimerUpdate = function(data, lateness)
+{
+ this.CallEvent("OnTimer", lateness);
+}
+
+Trigger.prototype.OnGlobalRangeUpdate = function(msg)
+{
+ // search the right query
+ for (var action in this.eventOnUnitRangeFromEntityData)
+ {
+ if (msg.tag == this.eventOnUnitRangeFromEntityData[action].rangeQuery)
+ {
+ var updatedQuery = this.eventOnUnitRangeFromEntityData[action];
+ break;
+ }
+ }
+
+ if (!updatedQuery)
+ return;
+
+ for each (var entity in msg.removed)
+ {
+ var index = updatedQuery.currentCollection.indexOf(entity);
+ if (index > -1)
+ updatedQuery.currentCollection.splice(index, 1);
+ }
+
+ updatedQuery.currentCollection.concat(msg.added);
+
+ var data = {
+ "currentCollection": updatedQuery.currentCollection.slice(),
+ "added": msg.added,
+ "removed": msg.removed};
+
+ this.DoAction({"action": updatedQuery.action, "data": data});
+}
+
+/**
+ * Execute a function after a certain delay
+ * @param time The delay expressed in milleseconds
+ * @param action Name of the action function
+ * @param data Data object that will be passed to the action function
+ */
+Trigger.prototype.DoAfterDelay = function(time, action, data)
+{
+ var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
+ return cmpTimer.SetTimeout(SYSTEM_ENTITY, IID_Trigger, "DoAction", time, {"action": action, "data": data});
+}
+
+/**
+ * Called by the trigger listeners to exucute the actual action. Including sanity checks.
+ */
+Trigger.prototype.DoAction = function(msg)
+{
+ if (g_Triggers[msg.action])
+ g_Triggers[msg.action](msg.data);
+ else
+ error("called a trigger action '" + msg.action + "' that wasn't added to the g_Triggers global");
+}
+
+Engine.RegisterComponentType(IID_Trigger, "Trigger", Trigger);
Index: binaries/data/mods/public/simulation/components/UnitAI.js
===================================================================
--- binaries/data/mods/public/simulation/components/UnitAI.js (revision 14795)
+++ binaries/data/mods/public/simulation/components/UnitAI.js (working copy)
@@ -199,6 +199,9 @@
cmpUnitMotion.MoveToFormationOffset(msg.data.target, msg.data.x, msg.data.z);
this.SetNextStateAlwaysEntering("FORMATIONMEMBER.WALKING");
+
+ var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
+ cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.FormationWalk", "metadata": msg});
},
// Special orders:
@@ -225,6 +228,9 @@
// We are already at the target, or can't move at all
this.FinishOrder();
}
+
+ var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
+ cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.LeaveFoundation", "metadata": msg});
},
// Individual orders:
@@ -247,7 +253,9 @@
this.SetNextState("ANIMAL.IDLE");
else
this.SetNextState("INDIVIDUAL.IDLE");
-
+
+ var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
+ cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Stop", "metadata": msg});
},
"Order.Walk": function(msg) {
@@ -258,6 +266,9 @@
return;
}
+ var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
+ cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Walk", "metadata": msg});
+
// For packable units:
// 1. If packed, we can move.
// 2. If unpacked, we first need to pack, then follow case 1.
@@ -284,6 +295,9 @@
return;
}
+ var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
+ cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.WalkAndFight", "metadata": msg});
+
// For packable units:
// 1. If packed, we can move.
// 2. If unpacked, we first need to pack, then follow case 1.
@@ -310,7 +324,10 @@
this.FinishOrder();
return;
}
-
+
+ var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
+ cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.WalkToTarget", "metadata": msg, "target": this.order.data.target});
+
// For packable units:
// 1. If packed, we can move.
// 2. If unpacked, we first need to pack, then follow case 1.
@@ -369,6 +386,9 @@
this.StopMoving();
this.SetNextState("INDIVIDUAL.PICKUP.LOADING");
}
+
+ var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
+ cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.PickupUnit", "metadata": msg, "target": this.order.data.target});
},
"Order.Guard": function(msg) {
@@ -382,6 +402,9 @@
this.SetNextState("INDIVIDUAL.GUARD.ESCORTING");
else
this.SetNextState("INDIVIDUAL.GUARD.GUARDING");
+
+ var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
+ cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Guard", "metadata": msg, "target": this.order.data.target});
},
"Order.Flee": function(msg) {
@@ -390,6 +413,9 @@
var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
if (cmpUnitMotion.MoveToTargetRange(this.order.data.target, distance, -1))
{
+ var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
+ cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Flee", "metadata": msg, "target": this.order.data.target});
+
// We've started fleeing from the given target
if (this.IsAnimal())
this.SetNextState("ANIMAL.FLEEING");
@@ -402,6 +428,8 @@
this.StopMoving();
this.FinishOrder();
}
+
+
},
"Order.Attack": function(msg) {
@@ -422,6 +450,10 @@
}
this.order.data.attackType = type;
+ // Call "OnUnitIssuedOrder" event of the triggers.
+ var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
+ cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Attack", "metadata": msg, "target": this.order.data.target});
+
// If we are already at the target, try attacking it from here
if (this.CheckTargetAttackRange(this.order.data.target, this.order.data.attackType))
{
@@ -506,6 +538,7 @@
// We can't reach the target, and can't move towards it,
// so abandon this attack order
this.FinishOrder();
+
},
"Order.Heal": function(msg) {
@@ -526,6 +559,10 @@
// Check if the target is in range
if (this.CheckTargetRange(this.order.data.target, IID_Heal))
{
+ // Call "OnUnitIssuedOrder" event of the triggers.
+ var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
+ cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Heal", "metadata": msg, "target": this.order.data.target});
+
this.StopMoving();
this.SetNextState("INDIVIDUAL.HEAL.HEALING");
return;
@@ -542,6 +579,10 @@
// Try to move within heal range
if (this.MoveToTargetRange(this.order.data.target, IID_Heal))
{
+ // Call "OnUnitIssuedOrder" event of the triggers.
+ var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
+ cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Heal", "metadata": msg, "target": this.order.data.target});
+
// We've started walking to the given point
this.SetNextState("INDIVIDUAL.HEAL.APPROACHING");
return;
@@ -599,6 +640,10 @@
this.StopMoving();
this.SetNextStateAlwaysEntering("INDIVIDUAL.GATHER.GATHERING");
}
+
+ // Call "OnUnitIssuedOrder" event of the triggers.
+ var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
+ cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Gather", "metadata": msg, "target": this.order.data.target});
},
"Order.GatherNearPosition": function(msg) {
@@ -605,6 +650,10 @@
// Move the unit to the position to gather from.
this.MoveToPoint(this.order.data.x, this.order.data.z);
this.SetNextState("INDIVIDUAL.GATHER.WALKING");
+
+ // Call "OnUnitIssuedOrder" event of the triggers.
+ var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
+ cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.GatherNearPosition", "metadata": msg, "target": this.order.data.target});
},
"Order.ReturnResource": function(msg) {
@@ -628,6 +677,10 @@
// Try to move to the dropsite
if (this.MoveToTargetRange(this.order.data.target, IID_ResourceGatherer))
{
+ // Call "OnUnitIssuedOrder" event of the triggers.
+ var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
+ cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.ReturnResource", "metadata": msg, "target": this.order.data.target});
+
// We've started walking to the target
this.SetNextState("INDIVIDUAL.RETURNRESOURCE.APPROACHING");
return;
@@ -660,6 +713,10 @@
this.waypoints = undefined;
if (this.MoveToMarket(nextMarket))
{
+ // Call "OnUnitIssuedOrder" event of the triggers.
+ var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
+ cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Trade", "metadata": msg, "target": this.order.data.target});
+
// We've started walking to the next market
this.SetNextState(state);
}
@@ -682,9 +739,17 @@
this.StopMoving();
this.SetNextState("INDIVIDUAL.REPAIR.REPAIRING");
}
+
+ // Call "OnUnitIssuedOrder" event of the triggers.
+ var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
+ cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Repair", "metadata": msg, "target": this.order.data.target});
},
"Order.Garrison": function(msg) {
+ // Call "OnUnitIssuedOrder" event of the triggers.
+ var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
+ cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Garrison", "metadata": msg, "target": this.order.data.target});
+
// For packable units:
// 1. If packed, we can move to the garrison target.
// 2. If unpacked, we first need to pack, then follow case 1.
@@ -709,6 +774,10 @@
"Order.Autogarrison": function(msg) {
this.SetNextState("INDIVIDUAL.AUTOGARRISON");
+
+ // Call "OnUnitIssuedOrder" event of the triggers.
+ var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
+ cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Autogarrison", "metadata": msg});
},
"Order.Alert": function(msg) {
@@ -722,10 +791,18 @@
this.ReplaceOrder("Garrison", {"target": this.alertGarrisoningTarget});
else
this.FinishOrder();
+
+ // Call "OnUnitIssuedOrder" event of the triggers.
+ var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
+ cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Alert", "metadata": msg});
},
"Order.Cheering": function(msg) {
this.SetNextState("INDIVIDUAL.CHEERING");
+
+ // Call "OnUnitIssuedOrder" event of the triggers.
+ var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
+ cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Cheering", "metadata": msg});
},
"Order.Pack": function(msg) {
@@ -733,6 +810,10 @@
{
this.StopMoving();
this.SetNextState("INDIVIDUAL.PACKING");
+
+ // Call "OnUnitIssuedOrder" event of the triggers.
+ var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
+ cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Pack", "metadata": msg});
}
},
@@ -741,6 +822,10 @@
{
this.StopMoving();
this.SetNextState("INDIVIDUAL.UNPACKING");
+
+ // Call "OnUnitIssuedOrder" event of the triggers.
+ var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
+ cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.Unpack", "metadata": msg});
}
},
@@ -749,6 +834,10 @@
if (cmpPack && cmpPack.IsPacking() && !cmpPack.IsPacked())
cmpPack.CancelPack();
this.FinishOrder();
+
+ // Call "OnUnitIssuedOrder" event of the triggers.
+ var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
+ cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.CancelPack", "metadata": msg});
},
"Order.CancelUnpack": function(msg) {
@@ -756,6 +845,10 @@
if (cmpPack && cmpPack.IsPacking() && cmpPack.IsPacked())
cmpPack.CancelPack();
this.FinishOrder();
+
+ // Call "OnUnitIssuedOrder" event of the triggers.
+ var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
+ cmpTrigger.CallEvent("OnUnitIssuedOrder", {"entity": this.entity, "order": "Order.CancelUnpack", "metadata": msg});
},
// States for the special entity representing a group of units moving in formation:
@@ -820,7 +913,7 @@
var cmpTargetUnitAI = Engine.QueryInterface(target, IID_UnitAI);
if (cmpTargetUnitAI && cmpTargetUnitAI.IsFormationMember())
target = cmpTargetUnitAI.GetFormationController();
-
+
var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
// Check if we are already in range, otherwise walk there
if (!this.CheckTargetAttackRange(target, target))
@@ -849,6 +942,7 @@
this.FinishOrder();
return;
}
+
// Check if we are already in range, otherwise walk there
if (!this.CheckGarrisonRange(msg.data.target))
{
@@ -926,7 +1020,7 @@
}
this.CallMemberFunction("GatherNearPosition", [msg.data.x, msg.data.z, msg.data.type, msg.data.template, false]);
-
+
this.SetNextStateAlwaysEntering("MEMBER");
},
@@ -945,7 +1039,7 @@
}
this.CallMemberFunction("Heal", [msg.data.target, false]);
-
+
this.SetNextStateAlwaysEntering("MEMBER");
},
@@ -1199,7 +1293,7 @@
// Stop moving as soon as the formation disbands
this.StopMoving();
-
+
// If the controller handled an order but some members rejected it,
// they will have no orders and be in the FORMATIONMEMBER.IDLE state.
if (this.orderQueue.length)
Index: binaries/data/mods/public/simulation/components/interfaces/Trigger.js
===================================================================
--- binaries/data/mods/public/simulation/components/interfaces/Trigger.js (revision 0)
+++ binaries/data/mods/public/simulation/components/interfaces/Trigger.js (working copy)
@@ -0,0 +1 @@
+Engine.RegisterInterface("Trigger");
Index: binaries/data/mods/public/simulation/helpers/Damage.js
===================================================================
--- binaries/data/mods/public/simulation/helpers/Damage.js (revision 14795)
+++ binaries/data/mods/public/simulation/helpers/Damage.js (working copy)
@@ -74,6 +74,12 @@
// Damage the target
var targetState = cmpDamageReceiver.TakeDamage(data.strengths.hack * data.multiplier, data.strengths.pierce * data.multiplier, data.strengths.crush * data.multiplier, data.attacker);
+ // Call the related trigger event
+ // We have to call the event instead of listening to it by messages because the message is sent
+ // after the target gets killed, which is not our intended case
+ var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
+ cmpTrigger.CallEvent("OnEntityTookDamage", {"attackerEntity": data.attacker, "attackedEntity": data.target, "type":data.type, "ammount":-targetState.change});
+
// If the target was killed run some cleanup
if (targetState.killed)
Damage.TargetKilled(data.attacker, data.target);
@@ -110,6 +116,7 @@
// Call RangeManager with dummy entity and return the result.
var rangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
var rangeQuery = rangeManager.ExecuteQueryAroundPos(origin, 0, radius, players, IID_DamageReceiver);
+
return rangeQuery;
};
@@ -133,6 +140,10 @@
var cmpLooter = Engine.QueryInterface(killerEntity, IID_Looter);
if (cmpLooter)
cmpLooter.Collect(targetEntity);
+
+ // Call the related trigger event
+ var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
+ cmpTrigger.CallEvent("OnEntityKilled", {"killerEntity": killerEntity, "killedEntity": targetEntity});
};
Engine.RegisterGlobal("Damage", Damage);
Index: binaries/data/mods/public/simulation/helpers/TriggerHelpers.js
===================================================================
--- binaries/data/mods/public/simulation/helpers/TriggerHelpers.js (revision 0)
+++ binaries/data/mods/public/simulation/helpers/TriggerHelpers.js (working copy)
@@ -0,0 +1,131 @@
+// Contains standardized functions suitable for using in trigger scripts.
+// Do not use them in any other simulation script.
+
+// the global triggers variable used to exchange messages with the map scripts
+var g_Triggers = {}
+Engine.RegisterGlobal("g_Triggers", g_Triggers);
+
+/**
+ * A function to get the owner of an entity.
+ * Returns the ID of a player. Returns 0 if the owner is Gaia.
+ */
+function GetEntityOwner(entity)
+{
+ var cmpOwnership = Engine.QueryInterface(entity, IID_Ownership)
+ if (!cmpOwnership)
+ return null;
+ return cmpOwnership.GetOwner();
+}
+
+/**
+ * A function to get the generic name of an entity
+ */
+function GetEntityGenericName(entity)
+{
+ var cmpIdentity = Engine.QueryInterface(entity, IID_Identity);
+ if (!cmpIdentity)
+ return null;
+ return cmpIdentity.GetGenericName();
+}
+
+/**
+ * A function to get a list of the classes of an entity
+ */
+function GetEntityClassesList(entity)
+{
+ var cmpIdentity = Engine.QueryInterface(entity, IID_Identity);
+ if (!cmpIdentity)
+ return null;
+ return cmpIdentity.GetClassesList();
+}
+
+/**
+ * A function to determine if an entity has a specific class
+ */
+function EntityHasClass(entity, classname)
+{
+ var classes = GetEntityClassesList(entity);
+ return (classes && classes.indexOf(classname) != -1);
+}
+
+/**
+ * Returns the entity id of a player based on the id of the player
+ * Useful when one wants to change a player's properties.
+ */
+function GetPlayerEntityByID(id)
+{
+ var cmpPlayerMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
+ return cmpPlayerMan.GetPlayerByID(id);
+}
+
+/**
+ * Can be used to "force" a building to spawn a group of entities.
+ * Only works for buildings that can already train units.
+ */
+function BuildingSpawnUnits(entity, template, count)
+{
+ var cmpProductionQueue = Engine.QueryInterface(entity, IID_ProductionQueue);
+ cmpProductionQueue.SpawnUnits(template, count, null);
+}
+
+/**
+ * Shows a message in the top center of the screen
+ */
+function PushNotification(players, message)
+{
+ var cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
+ for each (var player in players)
+ cmpGUIInterface.PushNotification({"player": player, "message": message});
+}
+
+/**
+ * Returns the resource type that can be gathered from an entity
+ */
+function GetEntityResourceType(entity)
+{
+ var cmpResourceSupply = Engine.QueryInterface(entity, IID_ResourceSupply);
+ if (!cmpResourceSupply)
+ return null;
+ return cmpResourceSupply.GetType();
+}
+
+/**
+ * Returns a list of entities owned by the player
+ */
+function GetEntitiesByPlayer(playerID)
+{
+ var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
+ return cmpRangeManager.GetEntitiesByPlayer(playerID);
+}
+
+/**
+ * Wins the game for a player
+ */
+function SetPlayerWon(playerID)
+{
+ var playerEnt = GetPlayerEntityByID(playerID);
+ var cmpPlayer = Engine.QueryInterface(playerEnt, IID_Player);
+ cmpPlayer.SetState("won")
+}
+
+/**
+ * Defeats a player
+ */
+function DefeatPlayer(playerID)
+{
+ var playerEnt = GetPlayerEntityByID(playerID);
+ Engine.PostMessage(playerEnt, MT_PlayerDefeated, { "playerId": playerID } );
+}
+
+Engine.RegisterGlobal("GetEntityOwner", GetEntityOwner);
+Engine.RegisterGlobal("GetEntityGenericName", GetEntityGenericName);
+Engine.RegisterGlobal("GetEntityClassesList", GetEntityClassesList);
+Engine.RegisterGlobal("EntityHasClass", EntityHasClass);
+Engine.RegisterGlobal("GetPlayerEntityByID", GetPlayerEntityByID);
+Engine.RegisterGlobal("BuildingSpawnUnits", BuildingSpawnUnits);
+Engine.RegisterGlobal("PushNotification", PushNotification);
+Engine.RegisterGlobal("GetEntityResourceType", GetEntityResourceType);
+Engine.RegisterGlobal("GetEntitiesByPlayer", GetEntitiesByPlayer);
+Engine.RegisterGlobal("SetPlayerWon", SetPlayerWon);
+Engine.RegisterGlobal("DefeatPlayer", DefeatPlayer);
+
Index: source/simulation2/Simulation2.cpp
===================================================================
--- source/simulation2/Simulation2.cpp (revision 14795)
+++ source/simulation2/Simulation2.cpp (working copy)
@@ -133,6 +133,7 @@
LOAD_SCRIPTED_COMPONENT("PlayerManager");
LOAD_SCRIPTED_COMPONENT("TechnologyTemplateManager");
LOAD_SCRIPTED_COMPONENT("Timer");
+ LOAD_SCRIPTED_COMPONENT("Trigger");
LOAD_SCRIPTED_COMPONENT("ValueModificationManager");
#undef LOAD_SCRIPTED_COMPONENT
@@ -748,6 +749,16 @@
if (!m->m_StartupScript.empty())
GetScriptInterface().LoadScript(L"map startup script", m->m_StartupScript);
+
+ // Load the trigger script after we have loaded the simulation and the map.
+ if (GetScriptInterface().HasProperty(m->m_MapSettings.get(), "TriggerScript"))
+ {
+ std::string script_name;
+ GetScriptInterface().GetProperty(m->m_MapSettings.get(), "TriggerScript", script_name);
+
+ script_name = "maps/scripts/" + script_name;
+ m->m_ComponentManager.LoadScript(script_name.data());
+ }
}
int CSimulation2::ProgressiveLoad()