Changeset View
Changeset View
Standalone View
Standalone View
binaries/data/mods/public/simulation/components/TurretHolder.js
/** | /** | ||||
* This class holds the functions regarding entities being visible on | * This class holds the functions regarding entities being visible on | ||||
* another entity, but tied to their parents location. | * another entity, but tied to their parents location. | ||||
* Currently renaming and changing ownership are still managed by GarrisonHolder.js, | |||||
* but in the future these components should be independent. | |||||
*/ | */ | ||||
class TurretHolder | class TurretHolder | ||||
{ | { | ||||
Init() | Init() | ||||
{ | { | ||||
this.turretPoints = []; | this.turretPoints = []; | ||||
let points = this.template.TurretPoints; | let points = this.template.TurretPoints; | ||||
Show All 36 Lines | AllowedToOccupyTurret(entity, turretPoint) | ||||
if (!turretPoint.allowedClasses) | if (!turretPoint.allowedClasses) | ||||
return true; | return true; | ||||
let cmpIdentity = Engine.QueryInterface(entity, IID_Identity); | let cmpIdentity = Engine.QueryInterface(entity, IID_Identity); | ||||
return cmpIdentity && MatchesClassList(cmpIdentity.GetClassesList(), turretPoint.allowedClasses._string); | return cmpIdentity && MatchesClassList(cmpIdentity.GetClassesList(), turretPoint.allowedClasses._string); | ||||
} | } | ||||
/** | /** | ||||
* @param {number} entity - The entity to check for. | |||||
* @return {boolean} - Whether the entity is allowed to occupy any turret point. | |||||
*/ | |||||
CanOccupy(entity) | |||||
{ | |||||
return !!this.turretPoints.find(turretPoint => this.AllowedToOccupyTurret(entity, turretPoint)); | |||||
} | |||||
/** | |||||
* Occupy a turret point with the given entity. | * Occupy a turret point with the given entity. | ||||
* @param {number} entity - The entity to use. | * @param {number} entity - The entity to use. | ||||
* @param {Object} requestedTurretPoint - Optionally the specific turret point to occupy. | * @param {Object} requestedTurretPoint - Optionally the specific turret point to occupy. | ||||
* | * | ||||
* @return {boolean} - Whether the occupation was successful. | * @return {boolean} - Whether the occupation was successful. | ||||
*/ | */ | ||||
OccupyTurret(entity, requestedTurretPoint) | OccupyTurret(entity, requestedTurretPoint) | ||||
{ | { | ||||
Show All 16 Lines | OccupyTurret(entity, requestedTurretPoint) | ||||
} | } | ||||
else | else | ||||
turretPoint = this.turretPoints.find(turret => !turret.entity && this.AllowedToOccupyTurret(entity, turret)); | turretPoint = this.turretPoints.find(turret => !turret.entity && this.AllowedToOccupyTurret(entity, turret)); | ||||
if (!turretPoint) | if (!turretPoint) | ||||
return false; | return false; | ||||
turretPoint.entity = entity; | turretPoint.entity = entity; | ||||
// Angle of turrets: | // Angle of turrets: | ||||
// Renamed entities (turretPoint != undefined) should keep their angle. | // Renamed entities (turretPoint != undefined) should keep their angle. | ||||
// Otherwise if an angle is given in the turretPoint, use it. | // Otherwise if an angle is given in the turretPoint, use it. | ||||
// If no such angle given (usually walls for which outside/inside not well defined), we keep | // If no such angle given (usually walls for which outside/inside not well defined), we keep | ||||
// the current angle as it was used for garrisoning and thus quite often was from inside to | // the current angle as it was used for garrisoning and thus quite often was from inside to | ||||
// outside, except when garrisoning from outWorld where we take as default PI. | // outside, except when garrisoning from outWorld where we take as default PI. | ||||
if (!turretPoint && turretPoint.angle != null) | if (!turretPoint && turretPoint.angle != null) | ||||
cmpPositionOccupant.SetYRotation(cmpPositionSelf.GetRotation().y + turretPoint.angle); | cmpPositionOccupant.SetYRotation(cmpPositionSelf.GetRotation().y + turretPoint.angle); | ||||
else if (!turretPoint && !cmpPosition.IsInWorld()) | else if (!turretPoint && !cmpPosition.IsInWorld()) | ||||
cmpPositionOccupant.SetYRotation(cmpPositionSelf.GetRotation().y + Math.PI); | cmpPositionOccupant.SetYRotation(cmpPositionSelf.GetRotation().y + Math.PI); | ||||
cmpPositionOccupant.SetTurretParent(this.entity, turretPoint.offset); | cmpPositionOccupant.SetTurretParent(this.entity, turretPoint.offset); | ||||
let cmpUnitMotion = Engine.QueryInterface(entity, IID_UnitMotion); | |||||
if (cmpUnitMotion) | |||||
cmpUnitMotion.SetFacePointAfterMove(false); | |||||
let cmpUnitAI = Engine.QueryInterface(entity, IID_UnitAI); | |||||
if (cmpUnitAI) | |||||
cmpUnitAI.SetTurretStance(); | |||||
// Remove the unit's obstruction to avoid interfering with pathing. | |||||
let cmpObstruction = Engine.QueryInterface(entity, IID_Obstruction); | |||||
if (cmpObstruction) | |||||
cmpObstruction.SetActive(false); | |||||
Engine.PostMessage(this.entity, MT_TurretsChanged, { | Engine.PostMessage(this.entity, MT_TurretsChanged, { | ||||
"added": [entity], | "added": [entity], | ||||
"removed": [] | "removed": [] | ||||
}); | }); | ||||
return true; | return true; | ||||
} | } | ||||
Show All 23 Lines | if (requestedTurretPoint) | ||||
turretPoint = requestedTurretPoint; | turretPoint = requestedTurretPoint; | ||||
} | } | ||||
else | else | ||||
turretPoint = this.turretPoints.find(turret => turret.entity == entity); | turretPoint = this.turretPoints.find(turret => turret.entity == entity); | ||||
if (!turretPoint) | if (!turretPoint) | ||||
return false; | return false; | ||||
let cmpPositionEntity = Engine.QueryInterface(entity, IID_Position); | |||||
cmpPositionEntity.SetTurretParent(INVALID_ENTITY, new Vector3D()); | |||||
let cmpUnitMotionEntity = Engine.QueryInterface(entity, IID_UnitMotion); | |||||
if (cmpUnitMotionEntity) | |||||
cmpUnitMotionEntity.SetFacePointAfterMove(true); | |||||
let cmpUnitAIEntity = Engine.QueryInterface(entity, IID_UnitAI); | |||||
if (cmpUnitAIEntity) | |||||
cmpUnitAIEntity.ResetTurretStance(); | |||||
turretPoint.entity = null; | turretPoint.entity = null; | ||||
// Reset the obstruction flags to template defaults. | let cmpPositionEntity = Engine.QueryInterface(entity, IID_Position); | ||||
let cmpObstruction = Engine.QueryInterface(entity, IID_Obstruction); | if (cmpPositionEntity) | ||||
if (cmpObstruction) | cmpPositionEntity.SetTurretParent(INVALID_ENTITY, new Vector3D()); | ||||
cmpObstruction.SetActive(true); | |||||
Engine.PostMessage(this.entity, MT_TurretsChanged, { | Engine.PostMessage(this.entity, MT_TurretsChanged, { | ||||
"added": [], | "added": [], | ||||
"removed": [entity] | "removed": [entity] | ||||
}); | }); | ||||
return true; | return true; | ||||
} | } | ||||
wraitii: Does that mean "failure" in the above function? Weird API. | |||||
Done Inline ActionsFreagarach: #5811 | |||||
/** | /** | ||||
* @param {number} entity - The entity's id. | * @param {number} entity - The entity's id. | ||||
* @param {Object} turret - Optionally the turret to check. | * @param {Object} turret - Optionally the turret to check. | ||||
* | * | ||||
* @return {boolean} - Whether the entity is positioned on a turret of this entity. | * @return {boolean} - Whether the entity is positioned on a turret of this entity. | ||||
*/ | */ | ||||
OccupiesTurret(entity, requestedTurretPoint) | OccupiesTurret(entity, requestedTurretPoint) | ||||
{ | { | ||||
return requestedTurretPoint ? requestedTurretPoint.entity == entity : | return requestedTurretPoint ? requestedTurretPoint.entity == entity : | ||||
this.turretPoints.some(turretPoint => turretPoint.entity == entity); | this.turretPoints.some(turretPoint => turretPoint.entity == entity); | ||||
Done Inline ActionsWould probably be better in the Position helper if the code is identical enough wraitii: Would probably be better in the Position helper if the code is identical enough | |||||
Done Inline ActionsYeah, I reckoned that :) Freagarach: Yeah, I reckoned that :) | |||||
} | } | ||||
/** | /** | ||||
* @param {number} entity - The entity's id. | * @param {number} entity - The entity's id. | ||||
* @return {Object} - The turret this entity is positioned on, if applicable. | * @return {Object} - The turret this entity is positioned on, if applicable. | ||||
*/ | */ | ||||
GetOccupiedTurret(entity) | GetOccupiedTurret(entity) | ||||
{ | { | ||||
return this.turretPoints.find(turretPoint => turretPoint.entity == entity); | return this.turretPoints.find(turretPoint => turretPoint.entity == entity); | ||||
} | } | ||||
/** | /** | ||||
* @param {number} entity - The entity's id. | * @param {number} entity - The entity's id. | ||||
* @return {Object} - The turret this entity is positioned on, if applicable. | * @return {Object} - The turret this entity is positioned on, if applicable. | ||||
*/ | */ | ||||
GetOccupiedTurretName(entity) | GetOccupiedTurretName(entity) | ||||
{ | { | ||||
return this.GetOccupiedTurret(entity).name || ""; | let turret = this.GetOccupiedTurret(entity); | ||||
return turret ? turret.name : ""; | |||||
} | } | ||||
/** | /** | ||||
* @return {number[]} - The turretted entityIDs. | * @return {number[]} - The turretted entityIDs. | ||||
*/ | */ | ||||
GetEntities() | GetEntities() | ||||
{ | { | ||||
let entities = []; | let entities = []; | ||||
for (let turretPoint of this.turretPoints) | for (let turretPoint of this.turretPoints) | ||||
if (turretPoint.entity) | if (turretPoint.entity) | ||||
entities.push(turretPoint.entity); | entities.push(turretPoint.entity); | ||||
return entities; | return entities; | ||||
} | } | ||||
/** | /** | ||||
* @return {boolean} - Whether all the turret points are occupied. | |||||
*/ | |||||
IsFull() | |||||
{ | |||||
return !!this.turretPoints.find(turretPoint => turretPoint.entity == null); | |||||
} | |||||
/** | |||||
* @return {Object} - Max and min ranges at which entities can occupy any turret. | |||||
*/ | |||||
GetLoadingRange() | |||||
{ | |||||
return { "min": 0, "max": +(this.template.LoadingRange || 2) }; | |||||
} | |||||
/** | |||||
* @param {number} ent - The entity ID of the turret to be potentially picked up. | |||||
* @return {boolean} - Whether this entity can pick the specified entity up. | |||||
*/ | |||||
CanPickup(ent) | |||||
{ | |||||
if (!this.template.Pickup || this.IsFull()) | |||||
return false; | |||||
let cmpOwner = Engine.QueryInterface(this.entity, IID_Ownership); | |||||
return !!cmpOwner && IsOwnedByPlayer(cmpOwner.GetOwner(), ent); | |||||
} | |||||
/** | |||||
* @param {number[]} entities - The entities to ask to leave or to kill. | |||||
*/ | |||||
EjectOrKill(entities) | |||||
{ | |||||
let removedEntities = []; | |||||
for (let entity of entities) | |||||
{ | |||||
let cmpTurretable = Engine.QueryInterface(entity, IID_Turretable); | |||||
if (!cmpTurretable || !cmpTurretable.LeaveTurret(true)) | |||||
{ | |||||
let cmpHealth = Engine.QueryInterface(entity, IID_Health); | |||||
if (cmpHealth) | |||||
cmpHealth.Kill(); | |||||
else | |||||
Engine.DestroyEntity(entity); | |||||
removedEntities.push(entity); | |||||
} | |||||
} | |||||
if (removedEntities.length) | |||||
Engine.PostMessage(this.entity, MT_TurretsChanged, { | |||||
"added": [], | |||||
"removed": removedEntities | |||||
}); | |||||
} | |||||
/** | |||||
* Sets an init turret, present from game start. (E.g. set in Atlas.) | * Sets an init turret, present from game start. (E.g. set in Atlas.) | ||||
* @param {String} turretName - The name of the turret point to be used. | * @param {String} turretName - The name of the turret point to be used. | ||||
* @param {number} entity - The entity-ID to be placed. | * @param {number} entity - The entity-ID to be placed. | ||||
*/ | */ | ||||
SetInitEntity(turretName, entity) | SetInitEntity(turretName, entity) | ||||
{ | { | ||||
if (!this.initTurrets) | if (!this.initTurrets) | ||||
this.initTurrets = new Map(); | this.initTurrets = new Map(); | ||||
if (this.initTurrets.has(turretName)) | if (this.initTurrets.has(turretName)) | ||||
warn("The turret position " + turretName + " of entity " + | warn("The turret position " + turretName + " of entity " + | ||||
this.entity + " is already set! Overwriting."); | this.entity + " is already set! Overwriting."); | ||||
this.initTurrets.set(turretName, entity); | this.initTurrets.set(turretName, entity); | ||||
} | } | ||||
/** | /** | ||||
* @param {number} from - The entity to substitute. | |||||
* @param {number} to - The entity to subtitute with. | |||||
*/ | |||||
SwapEntities(from, to) | |||||
{ | |||||
let turretPoint = this.GetOccupiedTurret(from); | |||||
if (turretPoint) | |||||
{ | |||||
this.LeaveTurret(from, turretPoint); | |||||
this.OccupyTurret(to, turretPoint); | |||||
} | |||||
} | |||||
/** | |||||
* Update list of turreted entities when a game inits. | * Update list of turreted entities when a game inits. | ||||
*/ | */ | ||||
OnGlobalSkirmishReplacerReplaced(msg) | OnGlobalSkirmishReplacerReplaced(msg) | ||||
{ | { | ||||
if (!this.initTurrets) | if (!this.initTurrets) | ||||
return; | return; | ||||
if (msg.entity == this.entity) | if (msg.entity == this.entity) | ||||
{ | { | ||||
let cmpTurretHolder = Engine.QueryInterface(msg.newentity, IID_TurretHolder); | let cmpTurretHolder = Engine.QueryInterface(msg.newentity, IID_TurretHolder); | ||||
if (cmpTurretHolder) | if (cmpTurretHolder) | ||||
cmpTurretHolder.initTurrets = this.initTurrets; | cmpTurretHolder.initTurrets = this.initTurrets; | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
let entityIndex = this.initTurrets.indexOf(msg.entity); | let entityIndex = this.initTurrets.indexOf(msg.entity); | ||||
if (entityIndex != -1) | if (entityIndex != -1) | ||||
this.initTurrets[entityIndex] = msg.newentity; | this.initTurrets[entityIndex] = msg.newentity; | ||||
} | } | ||||
} | } | ||||
/** | /** | ||||
* Initialise the turreted units. | * Initialise turreted units. | ||||
* Really ugly, but because GarrisonHolder is processed earlier, and also turrets | |||||
* entities on init, we can find an entity that already is present. | |||||
* In that case we reject and occupy. | |||||
*/ | */ | ||||
OnGlobalInitGame(msg) | OnGlobalInitGame(msg) | ||||
{ | { | ||||
if (!this.initTurrets) | if (!this.initTurrets) | ||||
return; | return; | ||||
for (let [turretPointName, entity] of this.initTurrets) | for (let [turretPointName, entity] of this.initTurrets) | ||||
{ | |||||
if (this.OccupiesTurret(entity)) | |||||
this.LeaveTurret(entity); | |||||
if (!this.OccupyNamedTurret(entity, turretPointName)) | if (!this.OccupyNamedTurret(entity, turretPointName)) | ||||
warn("Entity " + entity + " could not occupy the turret point " + | warn("Entity " + entity + " could not occupy the turret point " + | ||||
turretPointName + " of turret holder " + this.entity + "."); | turretPointName + " of turret holder " + this.entity + "."); | ||||
} | |||||
delete this.initTurrets; | delete this.initTurrets; | ||||
} | } | ||||
/** | |||||
* @param {Object} msg - { "entity": number, "newentity": number }. | |||||
*/ | |||||
OnEntityRenamed(msg) | |||||
{ | |||||
for (let entity of this.GetEntities()) | |||||
{ | |||||
let cmpTurretable = Engine.QueryInterface(entity, IID_Turretable); | |||||
if (!cmpTurretable) | |||||
continue; | |||||
let currentPoint = this.GetOccupiedTurretName(entity); | |||||
cmpTurretable.LeaveTurret(true); | |||||
cmpTurretable.OccupyTurret(msg.newentity, currentPoint); | |||||
} | |||||
} | |||||
/** | |||||
* @param {Object} msg - { "entity": number, "from": number, "to": number }. | |||||
*/ | |||||
OnOwnershipChanged(msg) | |||||
{ | |||||
let entities = this.GetEntities(); | |||||
if (!entities.length) | |||||
return; | |||||
if (msg.to == INVALID_PLAYER) | |||||
this.EjectOrKill(entities); | |||||
else | |||||
for (let entity of entities.filter(entity => !IsOwnedByMutualAllyOfEntity(entity, this.entity))) | |||||
{ | |||||
let cmpTurretable = Engine.QueryInterface(entity, IID_Turretable); | |||||
if (cmpTurretable) | |||||
cmpTurretable.LeaveTurret(); | |||||
} | |||||
} | |||||
} | } | ||||
TurretHolder.prototype.Schema = | TurretHolder.prototype.Schema = | ||||
"<element name='TurretPoints' a:help='Points that will be used to visibly garrison a unit.'>" + | "<element name='TurretPoints' a:help='Points that will be used to visibly garrison a unit.'>" + | ||||
"<oneOrMore>" + | "<oneOrMore>" + | ||||
"<element a:help='Element containing the offset coordinates.'>" + | "<element a:help='Element containing the offset coordinates.'>" + | ||||
"<anyName/>" + | "<anyName/>" + | ||||
"<interleave>" + | "<interleave>" + | ||||
Show All 17 Lines | "<oneOrMore>" + | ||||
"<optional>" + | "<optional>" + | ||||
"<element name='Angle' a:help='Angle in degrees relative to the turretHolder direction.'>" + | "<element name='Angle' a:help='Angle in degrees relative to the turretHolder direction.'>" + | ||||
"<data type='decimal'/>" + | "<data type='decimal'/>" + | ||||
"</element>" + | "</element>" + | ||||
"</optional>" + | "</optional>" + | ||||
"</interleave>" + | "</interleave>" + | ||||
"</element>" + | "</element>" + | ||||
"</oneOrMore>" + | "</oneOrMore>" + | ||||
"</element>"; | "</element>" + | ||||
"<optional>" + | |||||
"<element name='LoadingRange' a:help='The maximum distance from this holder at which entities are allowed to occupy a turret point. Should be about 2.0 for land entities and preferably greater for ships.'>" + | |||||
"<ref name='nonNegativeDecimal'/>" + | |||||
"</element>" + | |||||
"</optional>" | |||||
"<optional>" + | |||||
"<element name='Pickup' a:help='This entity will try to move to pick up units to be turreted.'>" + | |||||
"<data type='boolean'/>" + | |||||
"</element>" + | |||||
"</optional>"; | |||||
Engine.RegisterComponentType(IID_TurretHolder, "TurretHolder", TurretHolder); | Engine.RegisterComponentType(IID_TurretHolder, "TurretHolder", TurretHolder); |
Wildfire Games · Phabricator
Does that mean "failure" in the above function? Weird API.