Index: ps/trunk/binaries/data/mods/public/simulation/components/Gate.js
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/components/Gate.js (revision 23710)
+++ ps/trunk/binaries/data/mods/public/simulation/components/Gate.js (revision 23711)
@@ -1,294 +1,280 @@
function Gate() {}
Gate.prototype.Schema =
"Controls behavior of wall gates" +
"" +
"20" +
"" +
"" +
"" +
"";
/**
* Initialize Gate component
*/
Gate.prototype.Init = function()
{
this.allies = [];
this.ignoreList = [];
this.opened = false;
this.locked = false;
};
-/**
- * Handle the renaming case (from long-wall to gate)
- * because that does not trigger ownershipchange or diplomacy change
- * and units don't get added to the ignorelist.
- */
-Gate.prototype.OnEntityRenamed = function(msg)
-{
- let cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
- if (!cmpOwnership || cmpOwnership.GetOwner() == INVALID_PLAYER)
- return;
-
- this.SetupRangeQuery();
-};
-
Gate.prototype.OnOwnershipChanged = function(msg)
{
if (msg.to != INVALID_PLAYER)
{
this.SetupRangeQuery(msg.to);
// Set the initial state, but don't play unlocking sound
if (!this.locked)
this.UnlockGate(true);
}
};
Gate.prototype.OnDiplomacyChanged = function(msg)
{
let cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
if (cmpOwnership && cmpOwnership.GetOwner() == msg.player)
{
this.allies = [];
this.ignoreList = [];
this.SetupRangeQuery(msg.player);
}
};
/**
* Cleanup on destroy
*/
Gate.prototype.OnDestroy = function()
{
// Clean up range query
var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
if (this.unitsQuery)
cmpRangeManager.DestroyActiveQuery(this.unitsQuery);
// Cancel the closing-blocked timer if it's running.
if (this.timer)
{
var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
cmpTimer.CancelTimer(this.timer);
this.timer = undefined;
}
};
/**
* Setup the range query to detect units coming in & out of range
*/
Gate.prototype.SetupRangeQuery = function(owner)
{
var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
if (this.unitsQuery)
cmpRangeManager.DestroyActiveQuery(this.unitsQuery);
// Only allied units can make the gate open.
var players = QueryPlayerIDInterface(owner).GetAllies();
var range = this.GetPassRange();
if (range > 0)
{
// Only find entities with IID_UnitAI interface
this.unitsQuery = cmpRangeManager.CreateActiveQuery(this.entity, 0, range, players, IID_UnitAI, cmpRangeManager.GetEntityFlagMask("normal"));
cmpRangeManager.EnableActiveQuery(this.unitsQuery);
}
};
/**
* Called when units enter or leave range
*/
Gate.prototype.OnRangeUpdate = function(msg)
{
if (msg.tag != this.unitsQuery)
return;
if (msg.added.length > 0)
for (let entity of msg.added)
{
// Ignore entities that cannot move as those won't be able to go through the gate.
let unitAI = Engine.QueryInterface(entity, IID_UnitAI);
if (!unitAI.AbleToMove())
this.ignoreList.push(entity);
this.allies.push(entity);
}
if (msg.removed.length > 0)
for (let entity of msg.removed)
{
let index = this.ignoreList.indexOf(entity);
if (index !== -1)
this.ignoreList.splice(index, 1);
this.allies.splice(this.allies.indexOf(entity), 1);
}
this.OperateGate();
};
Gate.prototype.OnGlobalUnitAbleToMoveChanged = function(msg)
{
if (this.allies.indexOf(msg.entity) === -1)
return;
let index = this.ignoreList.indexOf(msg.entity);
if (msg.ableToMove && index !== -1)
this.ignoreList.splice(index, 1);
else if (!msg.ableToMove && index === -1)
this.ignoreList.push(msg.entity);
this.OperateGate();
};
/**
* Get the range in which units are detected
*/
Gate.prototype.GetPassRange = function()
{
return +this.template.PassRange;
};
Gate.prototype.ShouldOpen = function()
{
return this.allies.some(ent => this.ignoreList.indexOf(ent) === -1);
};
/**
* Attempt to open or close the gate.
* An ally must be in range to open the gate, but an unlocked gate will only close
* if there are no allies in range and no units are inside the gate's obstruction.
*/
Gate.prototype.OperateGate = function()
{
// Cancel the closing-blocked timer if it's running.
if (this.timer)
{
var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
cmpTimer.CancelTimer(this.timer);
this.timer = undefined;
}
if (this.opened && (this.locked || !this.ShouldOpen()))
this.CloseGate();
else if (!this.opened && this.ShouldOpen())
this.OpenGate();
};
Gate.prototype.IsLocked = function()
{
return this.locked;
};
/**
* Lock the gate, with sound. It will close at the next opportunity.
*/
Gate.prototype.LockGate = function()
{
this.locked = true;
// Delete animal corpses to prevent units trying to gather the unreachable entity
let cmpObstruction = Engine.QueryInterface(this.entity, IID_Obstruction);
if (cmpObstruction && cmpObstruction.GetBlockMovementFlag())
for (let ent of cmpObstruction.GetEntitiesDeletedUponConstruction())
Engine.DestroyEntity(ent);
// If the door is closed, enable 'block pathfinding'
// Else 'block pathfinding' will be enabled the next time the gate close
if (!this.opened)
{
if (cmpObstruction)
cmpObstruction.SetDisableBlockMovementPathfinding(false, false, 0);
}
else
this.OperateGate();
// TODO: Possibly move the lock/unlock sounds to UI? Needs testing
PlaySound("gate_locked", this.entity);
};
/**
* Unlock the gate, with sound. May open the gate if allied units are within range.
* If quiet is true, no sound will be played (used for initial setup).
*/
Gate.prototype.UnlockGate = function(quiet)
{
var cmpObstruction = Engine.QueryInterface(this.entity, IID_Obstruction);
if (!cmpObstruction)
return;
// Disable 'block pathfinding'
cmpObstruction.SetDisableBlockMovementPathfinding(this.opened, true, 0);
this.locked = false;
// TODO: Possibly move the lock/unlock sounds to UI? Needs testing
if (!quiet)
PlaySound("gate_unlocked", this.entity);
// If the gate is closed, open it if necessary
if (!this.opened)
this.OperateGate();
};
/**
* Open the gate if unlocked, with sound and animation.
*/
Gate.prototype.OpenGate = function()
{
// Do not open the gate if it has been locked
if (this.locked)
return;
var cmpObstruction = Engine.QueryInterface(this.entity, IID_Obstruction);
if (!cmpObstruction)
return;
// Disable 'block movement'
cmpObstruction.SetDisableBlockMovementPathfinding(true, true, 0);
this.opened = true;
PlaySound("gate_opening", this.entity);
var cmpVisual = Engine.QueryInterface(this.entity, IID_Visual);
if (cmpVisual)
cmpVisual.SelectAnimation("gate_opening", true, 1.0);
};
/**
* Close the gate, with sound and animation.
*
* The gate may fail to close due to unit obstruction. If this occurs, the
* gate will start a timer and attempt to close on each simulation update.
*/
Gate.prototype.CloseGate = function()
{
let cmpObstruction = Engine.QueryInterface(this.entity, IID_Obstruction);
if (!cmpObstruction)
return;
// The gate can't be closed if there are entities colliding with it.
let collisions = cmpObstruction.GetEntitiesBlockingMovement();
if (collisions.length)
{
if (!this.timer)
{
// Set an "instant" timer which will run on the next simulation turn.
let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
this.timer = cmpTimer.SetTimeout(this.entity, IID_Gate, "OperateGate", 0);
}
return;
}
// If we ordered the gate to be locked, enable 'block movement' and 'block pathfinding'
if (this.locked)
cmpObstruction.SetDisableBlockMovementPathfinding(false, false, 0);
// Else just enable 'block movement'
else
cmpObstruction.SetDisableBlockMovementPathfinding(false, true, 0);
this.opened = false;
PlaySound("gate_closing", this.entity);
let cmpVisual = Engine.QueryInterface(this.entity, IID_Visual);
if (cmpVisual)
cmpVisual.SelectAnimation("gate_closing", true, 1.0);
};
Engine.RegisterComponentType(IID_Gate, "Gate", Gate);