Changeset View
Standalone View
binaries/data/mods/public/simulation/components/TurretHolder.js
Show All 13 Lines | for (let point in points) | ||||
"name": point, | "name": point, | ||||
"offset": { | "offset": { | ||||
"x": +points[point].X, | "x": +points[point].X, | ||||
"y": +points[point].Y, | "y": +points[point].Y, | ||||
"z": +points[point].Z | "z": +points[point].Z | ||||
}, | }, | ||||
"allowedClasses": points[point].AllowedClasses, | "allowedClasses": points[point].AllowedClasses, | ||||
"angle": points[point].Angle ? +points[point].Angle * Math.PI / 180 : null, | "angle": points[point].Angle ? +points[point].Angle * Math.PI / 180 : null, | ||||
"entity": null | "entity": null, | ||||
"template": points[point].Template, | |||||
"ejectable": "Ejectable" in points[point] ? points[point].Ejectable == "true" : true | |||||
}); | }); | ||||
} | } | ||||
/** | /** | ||||
* Add a subunit as specified in the template. | |||||
* This function creates an entity and places it on the turret point. | |||||
* | |||||
* @param {Object} turretPoint - A turret point to (re)create the predefined subunit for. | |||||
wraitii: Not a great name + I think you should just inline that in OwnershipChanged, since it's called… | |||||
* | |||||
* @return {boolean} - Whether the turret creation has succeeded. | |||||
*/ | |||||
CreateSubunit(turretPointName) | |||||
{ | |||||
let turretPoint = this.TurretPointByName(turretPointName); | |||||
if (!turretPoint || turretPoint.entity || this.initTurrets && this.initTurrets.has(turretPointName)) | |||||
Done Inline ActionsYou can go wraitii: You can go
`turretPoint?.entity || this.initTurrets?.has(...)` now | |||||
return false; | |||||
let ent = Engine.AddEntity(turretPoint.template); | |||||
let cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership); | |||||
if (cmpOwnership) | |||||
{ | |||||
let cmpEntOwnership = Engine.QueryInterface(ent, IID_Ownership); | |||||
if (cmpEntOwnership) | |||||
Done Inline Actions@wraitii position is handled here. Stan: @wraitii position is handled here. | |||||
cmpEntOwnership.SetOwner(cmpOwnership.GetOwner()); | |||||
} | |||||
let cmpTurretable = Engine.QueryInterface(ent, IID_Turretable); | |||||
Done Inline ActionsThis is already defined? I'm assuming for prop points? wraitii: This is already defined? I'm assuming for prop points? | |||||
Done Inline ActionsCurrently you can't access the prop points' positions anywhere. I might make a patch for that. Stan: Currently you can't access the prop points' positions anywhere. I might make a patch for that. | |||||
Done Inline ActionsSo this currently fails? wraitii: So this currently fails? | |||||
Done Inline ActionsNo we specify those points in the turretholder tag like it's done for the garrison holder. Stan: No we specify those points in the turretholder tag like it's done for the garrison holder. | |||||
Done Inline ActionsI meant "where's the c++ code change?" Does this function already exist? wraitii: I meant "where's the c++ code change?" Does this function already exist? | |||||
Done Inline ActionsAh yes it's used by the visible garrisoning Stan: Ah yes it's used by the visible garrisoning | |||||
return cmpTurretable && cmpTurretable.OccupyTurret(this.entity, turretPoint.name, turretPoint.ejectable) || Engine.DestroyEntity(ent); | |||||
} | |||||
/** | |||||
* @return {Object[]} - An array of the turret points this entity has. | * @return {Object[]} - An array of the turret points this entity has. | ||||
*/ | */ | ||||
GetTurretPoints() | GetTurretPoints() | ||||
{ | { | ||||
return this.turretPoints; | return this.turretPoints; | ||||
} | } | ||||
/** | /** | ||||
* @param {number} entity - The entity to check for. | * @param {number} entity - The entity to check for. | ||||
* @param {Object} turretPoint - The turret point to use. | * @param {Object} turretPoint - The turret point to use. | ||||
* | * | ||||
* @return {boolean} - Whether the entity is allowed to occupy the specified turret point. | * @return {boolean} - Whether the entity is allowed to occupy the specified turret point. | ||||
*/ | */ | ||||
AllowedToOccupyTurret(entity, turretPoint) | AllowedToOccupyTurretPoint(entity, turretPoint) | ||||
{ | { | ||||
if (!turretPoint || turretPoint.entity) | if (!turretPoint || turretPoint.entity) | ||||
return false; | return false; | ||||
if (!IsOwnedByMutualAllyOfEntity(entity, this.entity)) | if (!IsOwnedByMutualAllyOfEntity(entity, this.entity)) | ||||
return false; | return false; | ||||
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. | * @param {number} entity - The entity to check for. | ||||
* @return {boolean} - Whether the entity is allowed to occupy any turret point. | * @return {boolean} - Whether the entity is allowed to occupy any turret point. | ||||
*/ | */ | ||||
CanOccupy(entity) | CanOccupy(entity) | ||||
{ | { | ||||
return !!this.turretPoints.find(turretPoint => this.AllowedToOccupyTurret(entity, turretPoint)); | return !!this.turretPoints.find(turretPoint => this.AllowedToOccupyTurretPoint(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) | OccupyTurretPoint(entity, requestedTurretPoint) | ||||
{ | { | ||||
let cmpPositionOccupant = Engine.QueryInterface(entity, IID_Position); | let cmpPositionOccupant = Engine.QueryInterface(entity, IID_Position); | ||||
if (!cmpPositionOccupant) | if (!cmpPositionOccupant) | ||||
return false; | return false; | ||||
let cmpPositionSelf = Engine.QueryInterface(this.entity, IID_Position); | let cmpPositionSelf = Engine.QueryInterface(this.entity, IID_Position); | ||||
if (!cmpPositionSelf) | if (!cmpPositionSelf) | ||||
return false; | return false; | ||||
if (this.OccupiesTurret(entity)) | if (this.OccupiesTurretPoint(entity)) | ||||
return false; | return false; | ||||
let turretPoint; | let turretPoint; | ||||
if (requestedTurretPoint) | if (requestedTurretPoint) | ||||
{ | { | ||||
if (this.AllowedToOccupyTurret(entity, requestedTurretPoint)) | if (this.AllowedToOccupyTurretPoint(entity, requestedTurretPoint)) | ||||
turretPoint = requestedTurretPoint; | turretPoint = requestedTurretPoint; | ||||
} | } | ||||
else | else | ||||
turretPoint = this.turretPoints.find(turret => !turret.entity && this.AllowedToOccupyTurret(entity, turret)); | turretPoint = this.turretPoints.find(turret => !turret.entity && this.AllowedToOccupyTurretPoint(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. | ||||
Show All 16 Lines | OccupyTurretPoint(entity, requestedTurretPoint) | ||||
return true; | return true; | ||||
} | } | ||||
/** | /** | ||||
* @param {number} entity - The entityID of the entity. | * @param {number} entity - The entityID of the entity. | ||||
* @param {String} turretName - The name of the turret point to occupy. | * @param {String} turretName - The name of the turret point to occupy. | ||||
* @return {boolean} - Whether the occupation has succeeded. | * @return {boolean} - Whether the occupation has succeeded. | ||||
*/ | */ | ||||
OccupyNamedTurret(entity, turretName) | OccupyNamedTurretPoint(entity, turretName) | ||||
{ | { | ||||
return this.OccupyTurret(entity, this.turretPoints.find(turret => turret.name == turretName)); | return this.OccupyTurretPoint(entity, this.TurretPointByName(turretName)); | ||||
} | |||||
/** | |||||
* @param {string} turretPointName - The name of the requested turret point. | |||||
* @return {Object} - The requested turret point. | |||||
*/ | |||||
TurretPointByName(turretPointName) | |||||
{ | |||||
return this.turretPoints.find(turret => turret.name == turretPointName); | |||||
} | } | ||||
/** | /** | ||||
* Remove the entity from a turret. | * Remove the entity from a turret. | ||||
* @param {number} entity - The specific entity to eject. | * @param {number} entity - The specific entity to eject. | ||||
* @param {Object} turret - Optionally the turret to abandon. | * @param {Object} turret - Optionally the turret to abandon. | ||||
* | * | ||||
* @return {boolean} - Whether the entity was occupying a/the turret before. | * @return {boolean} - Whether the entity was occupying a/the turret before. | ||||
*/ | */ | ||||
LeaveTurret(entity, requestedTurretPoint) | LeaveTurretPoint(entity, requestedTurretPoint) | ||||
{ | { | ||||
let turretPoint; | let turretPoint; | ||||
if (requestedTurretPoint) | if (requestedTurretPoint) | ||||
{ | { | ||||
if (requestedTurretPoint.entity == entity) | if (requestedTurretPoint.entity == entity) | ||||
turretPoint = requestedTurretPoint; | turretPoint = requestedTurretPoint; | ||||
} | } | ||||
else | else | ||||
turretPoint = this.turretPoints.find(turret => turret.entity == entity); | turretPoint = this.GetOccupiedTurretPoint(entity); | ||||
if (!turretPoint) | if (!turretPoint || !turretPoint.ejectable) | ||||
return false; | return false; | ||||
turretPoint.entity = null; | turretPoint.entity = null; | ||||
let cmpPositionEntity = Engine.QueryInterface(entity, IID_Position); | |||||
if (cmpPositionEntity) | |||||
cmpPositionEntity.SetTurretParent(INVALID_ENTITY, new Vector3D()); | |||||
Engine.PostMessage(this.entity, MT_TurretsChanged, { | Engine.PostMessage(this.entity, MT_TurretsChanged, { | ||||
"added": [], | "added": [], | ||||
"removed": [entity] | "removed": [entity] | ||||
}); | }); | ||||
return true; | return true; | ||||
} | } | ||||
/** | /** | ||||
* @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) | OccupiesTurretPoint(entity, requestedTurretPoint) | ||||
{ | { | ||||
return requestedTurretPoint ? requestedTurretPoint.entity == entity : | return requestedTurretPoint ? requestedTurretPoint.entity == entity : | ||||
this.turretPoints.some(turretPoint => turretPoint.entity == entity); | !!this.GetOccupiedTurretPoint(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. | ||||
*/ | */ | ||||
GetOccupiedTurret(entity) | GetOccupiedTurretPoint(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) | GetOccupiedTurretPointName(entity) | ||||
{ | { | ||||
let turret = this.GetOccupiedTurret(entity); | let turret = this.GetOccupiedTurretPoint(entity); | ||||
return turret ? turret.name : ""; | return turret ? turret.name : ""; | ||||
} | } | ||||
/** | /** | ||||
* @return {number[]} - The turretted entityIDs. | * @return {number[]} - The turretted entityIDs. | ||||
*/ | */ | ||||
GetEntities() | GetEntities() | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 101 Lines • ▼ Show 20 Lines | class TurretHolder | ||||
* Initialise turreted units. | * Initialise turreted units. | ||||
*/ | */ | ||||
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.OccupyNamedTurret(entity, turretPointName)) | { | ||||
let cmpTurretable = Engine.QueryInterface(entity, IID_Turretable); | |||||
if (!cmpTurretable || !cmpTurretable.OccupyTurret(this.entity, turretPointName, this.TurretPointByName(turretPointName).ejectable)) | |||||
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 }. | * @param {Object} msg - { "entity": number, "newentity": number }. | ||||
*/ | */ | ||||
OnEntityRenamed(msg) | OnEntityRenamed(msg) | ||||
{ | { | ||||
for (let entity of this.GetEntities()) | for (let entity of this.GetEntities()) | ||||
{ | { | ||||
let cmpTurretable = Engine.QueryInterface(entity, IID_Turretable); | let cmpTurretable = Engine.QueryInterface(entity, IID_Turretable); | ||||
if (!cmpTurretable) | if (!cmpTurretable) | ||||
continue; | continue; | ||||
let currentPoint = this.GetOccupiedTurretName(entity); | let currentPoint = this.GetOccupiedTurretPointName(entity); | ||||
cmpTurretable.LeaveTurret(true); | cmpTurretable.LeaveTurret(true); | ||||
cmpTurretable.OccupyTurret(msg.newentity, currentPoint); | cmpTurretable.OccupyTurret(msg.newentity, currentPoint); | ||||
} | } | ||||
} | } | ||||
/** | /** | ||||
* @param {Object} msg - { "entity": number, "from": number, "to": number }. | * @param {Object} msg - { "entity": number, "from": number, "to": number }. | ||||
*/ | */ | ||||
OnOwnershipChanged(msg) | OnOwnershipChanged(msg) | ||||
{ | { | ||||
let entities = this.GetEntities(); | if (msg.to === INVALID_PLAYER) | ||||
if (!entities.length) | { | ||||
this.EjectOrKill(this.GetEntities()); | |||||
return; | return; | ||||
} | |||||
if (msg.to == INVALID_PLAYER) | for (let point of this.turretPoints) | ||||
this.EjectOrKill(entities); | |||||
else | |||||
for (let entity of entities.filter(entity => !IsOwnedByMutualAllyOfEntity(entity, this.entity))) | |||||
{ | { | ||||
let cmpTurretable = Engine.QueryInterface(entity, IID_Turretable); | // If we were created, create any subunits now. | ||||
// This has to be done here (instead of on Init) | |||||
// for Ownership ought to be initialised. | |||||
if (point.template && msg.from === INVALID_PLAYER) | |||||
{ | |||||
this.CreateSubunit(point.name); | |||||
continue; | |||||
} | |||||
if (!point.entity) | |||||
continue; | |||||
if (!point.ejectable) | |||||
{ | |||||
let cmpTurretOwnership = Engine.QueryInterface(point.entity, IID_Ownership); | |||||
if (cmpTurretOwnership) | |||||
cmpTurretOwnership.SetOwner(msg.to); | |||||
} | |||||
else if (!IsOwnedByMutualAllyOfEntity(point.entity, this.entity)) | |||||
{ | |||||
let cmpTurretable = Engine.QueryInterface(point.entity, IID_Turretable); | |||||
if (cmpTurretable) | if (cmpTurretable) | ||||
cmpTurretable.LeaveTurret(); | 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>" + | ||||
Done Inline ActionsI don't think you should put the SpawnTemplate and turret type in the turretPoints. What I'd suggest instead is something like this (SpawnTurrets isn't a great name though): <TurretHolder> <SpawnTurrets> <Bouddica> <Template>units/brit_hero_boudicca_sword</Template> <Point>Rider</Point> </Bouddica> </SpawnTurrets> <TurretPoints> <Rider> <Garrisonable/> <X>0</X> <Y>1.4</Y> <Z>-2.5</Z> <Angle>0</Angle> </Rider> </TurretPoints> </TurretHolder> (Point might be required at the moment, but it ought to be facultative in the future). wraitii: I don't think you should put the `SpawnTemplate` and turret type in the turretPoints.
The… | |||||
Done Inline ActionsI'd say that something like a "flying drone assistant" a la C&C Generals, is actually just a unselectable entity guarding us. Freagarach: I'd say that something like a "flying drone assistant" a la C&C Generals, is actually just a… | |||||
Done Inline ActionsWhat if you can select it and order it to attack? wraitii: What if you can select it and order it to attack? | |||||
Done Inline ActionsThen it is a common entity? Freagarach: Then it is a common entity? | |||||
"<element name='X'>" + | "<element name='X'>" + | ||||
"<data type='decimal'/>" + | "<data type='decimal'/>" + | ||||
"</element>" + | "</element>" + | ||||
"<element name='Y'>" + | "<element name='Y'>" + | ||||
"<data type='decimal'/>" + | "<data type='decimal'/>" + | ||||
"</element>" + | "</element>" + | ||||
"<element name='Z'>" + | "<element name='Z'>" + | ||||
"<data type='decimal'/>" + | "<data type='decimal'/>" + | ||||
"</element>" + | "</element>" + | ||||
"<optional>" + | "<optional>" + | ||||
"<interleave>" + | |||||
"<element name='Template'>" + | |||||
"<text/>" + | |||||
"</element>" + | |||||
"<element name='Ejectable' a:help='Whether this template is tied to the turret position (i.e. not allowed to leave the turret point).'>" + | |||||
"<data type='boolean'/>" + | |||||
"</element>" + | |||||
"</interleave>" + | |||||
"</optional>" + | |||||
"<optional>" + | |||||
Done Inline ActionsInconsistent: seems to me you can just put AllowedClasses & Angle in the optional interleave? Or just give each its own optional? wraitii: Inconsistent: seems to me you can just put AllowedClasses & Angle in the optional interleave? | |||||
Done Inline ActionsWell, the idea is that when you don't have a template, ejectable is useless. Freagarach: Well, the idea is that when you don't have a template, ejectable is useless. | |||||
"<element name='AllowedClasses' a:help='If specified, only entities matching the given classes will be able to use this turret.'>" + | "<element name='AllowedClasses' a:help='If specified, only entities matching the given classes will be able to use this turret.'>" + | ||||
"<attribute name='datatype'>" + | "<attribute name='datatype'>" + | ||||
"<value>tokens</value>" + | "<value>tokens</value>" + | ||||
"</attribute>" + | "</attribute>" + | ||||
"<text/>" + | "<text/>" + | ||||
"</element>" + | "</element>" + | ||||
"</optional>"+ | "</optional>"+ | ||||
"<optional>" + | "<optional>" + | ||||
Show All 20 Lines |
Not a great name + I think you should just inline that in OwnershipChanged, since it's called only at template creation.