0ad/binaries/data/mods/public/simulation/components/Trigger.js
Yves 89aef0b6eb Cinematic camera core functionality. Patch by Vladislav Belov.
Based on existing code that was still around from an old(not working)
implementation.
Supports basic control from trigger scirpts (queue, start and stop
camera paths) and works in multiplayer.

This was SVN commit r17594.
2016-01-03 12:41:04 +00:00

288 lines
7.8 KiB
JavaScript

function Trigger() {}
Trigger.prototype.Schema =
"<a:component type='system'/><empty/>";
/**
* Events we're able to receive and call handlers for
*/
Trigger.prototype.eventNames =
[
"StructureBuilt",
"ConstructionStarted",
"TrainingFinished",
"TrainingQueued",
"ResearchFinished",
"ResearchQueued",
"OwnershipChanged",
"PlayerCommand",
"Interval",
"Range",
"TreasureCollected",
"CinemaPathEnded",
"CinemaQueueEnded"
];
Trigger.prototype.Init = function()
{
this.triggerPoints = {};
// Each event has its own set of actions determined by the map maker.
for each (var eventName in this.eventNames)
this["On" + eventName + "Actions"] = {};
};
Trigger.prototype.RegisterTriggerPoint = function(ref, ent)
{
if (!this.triggerPoints[ref])
this.triggerPoints[ref] = [];
this.triggerPoints[ref].push(ent);
};
Trigger.prototype.RemoveRegisteredTriggerPoint = function(ref, ent)
{
if (!this.triggerPoints[ref])
{
warn("no trigger points found with ref "+ref);
return;
}
var i = this.triggerPoints[ref].indexOf(ent);
if (i == -1)
{
warn("entity " + ent + " wasn't found under the trigger points with ref "+ref);
return;
}
this.triggerPoints[ref].splice(i, 1);
};
Trigger.prototype.GetTriggerPoints = function(ref)
{
return this.triggerPoints[ref] || [];
};
/** Binds the "action" function to one of the implemented events.
*
* @param event Name of the event (see the list in init)
* @param action Functionname of a function available under this object
* @param Extra Data for the trigger (enabled or not, delay for timers, range for range triggers ...)
*
* Interval triggers example:
* data = {enabled: true, interval: 1000, delay: 500}
*
* Range trigger:
* data.entities = [id1, id2] * Ids of the source
* data.players = [1,2,3,...] * list of player ids
* data.minRange = 0 * Minimum range for the query
* data.maxRange = -1 * Maximum range for the query (-1 = no maximum)
* data.requiredComponent = 0 * Required component id the entities will have
* data.enabled = false * If the query is enabled by default
*/
Trigger.prototype.RegisterTrigger = function(event, action, data)
{
var eventString = event + "Actions";
if (!this[eventString])
{
warn("Trigger.js: Invalid trigger event \"" + event + "\".");
return;
}
if (this[eventString][action])
{
warn("Trigger.js: Trigger \"" + action + "\" has been registered before. Aborting...");
return;
}
// clone the data to be sure it's only modified locally
// We could run into triggers overwriting each other's data otherwise.
// F.e. getting the wrong timer tag
data = clone(data) || {"enabled": false};
this[eventString][action] = data;
// setup range query
if (event == "OnRange")
{
if (!data.entities)
{
warn("Trigger.js: Range triggers should carry extra data");
return;
}
data.queries = [];
for (var ent of data.entities)
{
var cmpTriggerPoint = Engine.QueryInterface(ent, IID_TriggerPoint);
if (!cmpTriggerPoint)
{
warn("Trigger.js: Range triggers must be defined on trigger points");
continue;
}
data.queries.push(cmpTriggerPoint.RegisterRangeTrigger(action, data));
}
}
if (data.enabled)
this.EnableTrigger(event, action);
};
// Disable trigger
Trigger.prototype.DisableTrigger = function(event, action)
{
var eventString = event + "Actions";
if (!this[eventString][action])
{
warn("Trigger.js: Disabling unknown trigger");
return;
}
var data = this[eventString][action];
// special casing interval and range triggers for performance
if (event == "OnInterval")
{
if (!data.timer) // don't disable it a second time
return;
var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
cmpTimer.CancelTimer(data.timer);
data.timer = null;
}
else if (event == "OnRange")
{
if (!data.queries)
{
warn("Trigger.js: Range query wasn't set up before trying to disable it.");
return;
}
var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
for (var query of data.queries)
cmpRangeManager.DisableActiveQuery(query);
}
data.enabled = false;
};
// Enable trigger
Trigger.prototype.EnableTrigger = function(event, action)
{
var eventString = event + "Actions";
if (!this[eventString][action])
{
warn("Trigger.js: Enabling unknown trigger");
return;
}
var data = this[eventString][action];
// special casing interval and range triggers for performance
if (event == "OnInterval")
{
if (data.timer) // don't enable it a second time
return;
if (!data.interval)
{
warn("Trigger.js: An interval trigger should have an intervel in its data");
return;
}
var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
var timer = cmpTimer.SetInterval(this.entity, IID_Trigger, "DoAction", data.delay || 0, data.interval, {"action" : action});
data.timer = timer;
}
else if (event == "OnRange")
{
if (!data.queries)
{
warn("Trigger.js: Range query wasn't set up before");
return;
}
var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
for (var query of data.queries)
cmpRangeManager.EnableActiveQuery(query);
}
data.enabled = true;
};
/**
* 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 event Name of the event (see the list in init)
* @param data Data object that will be passed to the actions
*/
Trigger.prototype.CallEvent = function(event, data)
{
var eventString = "On" + event + "Actions";
if (!this[eventString])
{
warn("Trigger.js: Unknown trigger event called:\"" + event + "\".");
return;
}
for (var action in this[eventString])
{
if (this[eventString][action].enabled)
this.DoAction({"action": action, "data":data});
}
};
// Handles "OnStructureBuilt" event.
Trigger.prototype.OnGlobalConstructionFinished = function(msg)
{
this.CallEvent("StructureBuilt", {"building": msg.newentity}); // The data for this one is {"building": constructedBuilding}
};
// Handles "OnTrainingFinished" event.
Trigger.prototype.OnGlobalTrainingFinished = function(msg)
{
this.CallEvent("TrainingFinished", msg);
// The data for this one is {"entities": createdEnts,
// "owner": cmpOwnership.GetOwner(),
// "metadata": metadata}
// See function "SpawnUnits" in ProductionQueue for more details
};
// Handles "OnTrainingFinished" event.
Trigger.prototype.OnGlobalResearchFinished = function(msg)
{
this.CallEvent("ResearchFinished", msg);
// The data for this one is {"player": playerID,
// "tech": tech}
};
// Handles "OnCinemaPathEnded" event.
Trigger.prototype.OnGlobalCinemaPathEnded = function(msg)
{
this.CallEvent("CinemaPathEnded", msg);
}
// Handles "OnCinemaQueueEnded" event.
Trigger.prototype.OnGlobalCinemaQueueEnded = function(msg)
{
this.CallEvent("CinemaQueueEnded", msg);
}
Trigger.prototype.OnGlobalOwnershipChanged = function(msg)
{
this.CallEvent("OwnershipChanged", msg);
// data is {"entity": ent, "from": playerId, "to": playerId}
};
/**
* 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(miliseconds, action, data)
{
var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
return cmpTimer.SetTimeout(SYSTEM_ENTITY, IID_Trigger, "DoAction", miliseconds, {"action": action, "data": data});
};
/**
* Called by the trigger listeners to exucute the actual action. Including sanity checks.
*/
Trigger.prototype.DoAction = function(msg)
{
if (this[msg.action])
this[msg.action](msg.data || null);
else
warn("Trigger.js: called a trigger action '" + msg.action + "' that wasn't found");
};
Engine.RegisterSystemComponentType(IID_Trigger, "Trigger", Trigger);