Changeset View
Standalone View
binaries/data/mods/public/simulation/components/TurretHolder.js
Show All 14 Lines | for (let point in points) | ||||
this.turretPoints.push({ | this.turretPoints.push({ | ||||
"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, | |||||
"fixed": points[point].Fixed && points[point].Fixed == "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(turretPoint) | |||||
{ | |||||
// This position is already occupied. | |||||
if (turretPoint.entity) | |||||
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); | |||||
Done Inline Actions@wraitii position is handled here. Stan: @wraitii position is handled here. | |||||
if (cmpEntOwnership) | |||||
cmpEntOwnership.SetOwner(cmpOwnership.GetOwner()); | |||||
} | |||||
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 | |||||
if (turretPoint.fixed) | |||||
return this.OccupyTurretPoint(ent, turretPoint) || Engine.DestroyEntity(ent); | |||||
// For now, require a GarrisonHolder if it is not a fixed turret. | |||||
// That is needed to be able to unload. | |||||
let cmpGarrisonHolder = Engine.QueryInterface(this.entity, IID_GarrisonHolder); | |||||
if (!cmpGarrisonHolder) | |||||
{ | |||||
error("For now we require a GarrisonHolder to use non-fixed subunits on entity " + this.entity + "."); | |||||
this.LeaveTurretPoint(ent, turretPoint); | |||||
Engine.DestroyEntity(ent); | |||||
return false; | |||||
} | |||||
return cmpGarrisonHolder.Garrison(ent, false, true) || 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; | ||||
} | } | ||||
/** | /** | ||||
* @return {number[]} - An array of the entityIDs of the subunits this entity has. | |||||
*/ | |||||
GetSubunits() | |||||
{ | |||||
let subunits = []; | |||||
for (let turretPoint of this.turretPoints) | |||||
if (turretPoint.entity) | |||||
subunits.push(turretPoint.entity); | |||||
return subunits; | |||||
} | |||||
/** | |||||
* @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); | ||||
} | } | ||||
/** | /** | ||||
* 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) | ||||
{ | { | ||||
if (this.OccupiesTurretPoint(entity)) | |||||
return false; | |||||
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)) | |||||
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 => this.AllowedToOccupyTurretPoint(entity, turret)); | ||||
if (!turretPoint) | if (!turretPoint) | ||||
return false; | return false; | ||||
turretPoint.entity = entity; | turretPoint.entity = entity; | ||||
// Angle of turrets: | |||||
// Angle of subunits: | |||||
// 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 occupying and thus quite often was from inside to | ||||
// outside, except when garrisoning from outWorld where we take as default PI. | // outside, except when occupying from outOfWorld 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); | let cmpUnitMotion = Engine.QueryInterface(entity, IID_UnitMotion); | ||||
Show All 13 Lines | Engine.PostMessage(this.entity, MT_TurretsChanged, { | ||||
"added": [entity], | "added": [entity], | ||||
"removed": [] | "removed": [] | ||||
}); | }); | ||||
return true; | return true; | ||||
} | } | ||||
/** | /** | ||||
* Remove the entity from a turret. | * Remove the entity from a turret point. | ||||
* @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} requestedTurretPoint - Optionally the turret point 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 | ||||
Show All 25 Lines | Engine.PostMessage(this.entity, MT_TurretsChanged, { | ||||
"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} requestedTurretPoint - Optionally the turret point 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.turretPoints.some(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. | ||||
*/ | */ | ||||
GetOccupiedTurret(entity) | GetOccupiedTurretPoint(entity) | ||||
{ | { | ||||
return this.turretPoints.find(turretPoint => turretPoint.entity == entity); | return this.turretPoints.find(turretPoint => turretPoint.entity == entity); | ||||
} | } | ||||
/** | /** | ||||
* Get the range of the subunits given the IID and optionally type. | |||||
* @param {number} iid - The IID to get the range for. | |||||
* @param {string} type - [Optional] | |||||
* @return {Object | undefined} - The range in the form | |||||
* { "min": number, "max": number } | |||||
* Object."elevationBonus": number may be present when iid == IID_Attack. | |||||
* Returns undefined when the entity does not have the requested component. | |||||
*/ | |||||
GetSubunitsRange(iid, type) | |||||
{ | |||||
let result = { | |||||
"min": Infinity, | |||||
"max": -1, | |||||
"elevationBonus": Infinity | |||||
}; | |||||
for (let point of this.turretPoints) | |||||
{ | |||||
if (!point.entity) | |||||
continue; | |||||
let component = Engine.QueryInterface(point.entity, iid); | |||||
if (!component) | |||||
continue; | |||||
let range = component.GetRange(type); | |||||
result.min = Math.min(result.min, range.min); | |||||
result.max = Math.max(result.max, range.max + point.offset.z); | |||||
result.elevationBonus = Math.min(result.elevationBonus, range.elevationBonus || 0); | |||||
} | |||||
return result.max == -1 ? undefined : result; | |||||
} | |||||
/** | |||||
* Checks whether one of this entities subunits can perform the given action on the given target. | |||||
* Restricted to entities with UnitAI for now. | |||||
* | |||||
* @param {string} action - The desired action to check. | |||||
* @param {number} target - The entity ID of the target to perform the action upon. | |||||
* @param {string[]} types - An optional array of the desired types to use (e.g. "Capture" for attack). | |||||
* | |||||
* @return {boolean} Whether at least one subunit can perform the given action. | |||||
*/ | |||||
CanAnySubunitPerform(action, target, types) | |||||
{ | |||||
return this.GetSubunits().some(entity => { | |||||
let cmpUnitAI = Engine.QueryInterface(entity, IID_UnitAI); | |||||
return cmpUnitAI && cmpUnitAI["Can" + action](target, types); | |||||
}); | |||||
}; | |||||
Lint: JSHintBear Unnecessary semicolon. Lint: JSHintBear: `Unnecessary semicolon.` | |||||
Lint: ESLintBear (no-extra-semi) Unnecessary semicolon. Lint: ESLintBear (no-extra-semi): `Unnecessary semicolon.` | |||||
/** | |||||
* We process EntityRenamed here because we need to be sure that we receive | * We process EntityRenamed here because we need to be sure that we receive | ||||
* it after it is processed by GarrisonHolder.js. | * it after it is processed by GarrisonHolder.js. | ||||
* ToDo: Make this not needed by fully separating TurretHolder from GarrisonHolder. | * ToDo: Make this not needed by fully separating TurretHolder from GarrisonHolder. | ||||
* That means an entity with TurretHolder should not need a GarrisonHolder | * That means an entity with TurretHolder should not need a GarrisonHolder | ||||
* for e.g. the garrisoning logic. | * for e.g. the garrisoning logic. | ||||
* | * | ||||
* @param {number} from - The entity to substitute. | * @param {number} from - The entity to substitute. | ||||
* @param {number} to - The entity to subtitute with. | * @param {number} to - The entity to subtitute with. | ||||
*/ | */ | ||||
SwapEntities(from, to) | SwapEntities(from, to) | ||||
{ | { | ||||
let turretPoint = this.GetOccupiedTurret(from); | let turretPoint = this.GetOccupiedTurretPoint(from); | ||||
if (turretPoint) | if (turretPoint) | ||||
this.LeaveTurret(from, turretPoint); | this.LeaveTurretPoint(from, turretPoint); | ||||
let cmpGarrisonHolder = Engine.QueryInterface(this.entity, IID_GarrisonHolder); | let cmpGarrisonHolder = Engine.QueryInterface(this.entity, IID_GarrisonHolder); | ||||
if (cmpGarrisonHolder && cmpGarrisonHolder.IsGarrisoned(to)) | if (cmpGarrisonHolder && cmpGarrisonHolder.IsGarrisoned(to)) | ||||
this.OccupyTurret(to, turretPoint); | this.OccupyTurretPoint(to, turretPoint); | ||||
} | |||||
OnOwnershipChanged(msg) | |||||
{ | |||||
// If we died, take fixed subunits with us. | |||||
// Other ownership changes are handled by GarrisonHolder.js. | |||||
if (msg.to == INVALID_PLAYER) | |||||
for (let point of this.turretPoints) | |||||
if (point.entity != null && point.fixed) | |||||
{ | |||||
let cmpHealth = Engine.QueryInterface(point.entity, IID_Health); | |||||
if (cmpHealth) | |||||
cmpHealth.Kill(); | |||||
else | |||||
Engine.DestroyEntity(point.entity); | |||||
} | |||||
else | |||||
Lint: ESLintBear (indent) Expected indentation of 4 tabs but found 2. Lint: ESLintBear (indent): `Expected indentation of 4 tabs but found 2.` | |||||
{ | |||||
Lint: ESLintBear (indent) Expected indentation of 4 tabs but found 2. Lint: ESLintBear (indent): `Expected indentation of 4 tabs but found 2.` | |||||
// If the turret point is forced and the garrisonholder did not die, | |||||
Lint: ESLintBear (indent) Expected indentation of 5 tabs but found 3. Lint: ESLintBear (indent): `Expected indentation of 5 tabs but found 3.` | |||||
// transfer the turrets ownership as well. | |||||
Lint: ESLintBear (indent) Expected indentation of 5 tabs but found 3. Lint: ESLintBear (indent): `Expected indentation of 5 tabs but found 3.` | |||||
for (let point of this.turretPoints) | |||||
Lint: ESLintBear (no-shadow) 'point' is already declared in the upper scope. Lint: ESLintBear (no-shadow): `'point' is already declared in the upper scope.` | |||||
Lint: ESLintBear (indent) Expected indentation of 5 tabs but found 3. Lint: ESLintBear (indent): `Expected indentation of 5 tabs but found 3.` | |||||
if (point.entity != null && point.fixed) | |||||
Lint: ESLintBear (indent) Expected indentation of 6 tabs but found 4. Lint: ESLintBear (indent): `Expected indentation of 6 tabs but found 4.` | |||||
{ | |||||
Lint: ESLintBear (indent) Expected indentation of 6 tabs but found 4. Lint: ESLintBear (indent): `Expected indentation of 6 tabs but found 4.` | |||||
let cmpEntOwnership = Engine.QueryInterface(point.entity, IID_Ownership) | |||||
Lint: JSHintBear Missing semicolon. Lint: JSHintBear: `Missing semicolon.` | |||||
Lint: ESLintBear (semi) Missing semicolon. Lint: ESLintBear (semi): `Missing semicolon.` | |||||
Lint: ESLintBear (indent) Expected indentation of 7 tabs but found 5. Lint: ESLintBear (indent): `Expected indentation of 7 tabs but found 5.` | |||||
if (cmpEntOwnership) | |||||
Lint: ESLintBear (indent) Expected indentation of 7 tabs but found 5. Lint: ESLintBear (indent): `Expected indentation of 7 tabs but found 5.` | |||||
cmpEntOwnership.SetOwner(msg.to); | |||||
Lint: ESLintBear (indent) Expected indentation of 8 tabs but found 6. Lint: ESLintBear (indent): `Expected indentation of 8 tabs but found 6.` | |||||
} | |||||
Lint: ESLintBear (indent) Expected indentation of 6 tabs but found 4. Lint: ESLintBear (indent): `Expected indentation of 6 tabs but found 4.` | |||||
} | |||||
Lint: ESLintBear (indent) Expected indentation of 4 tabs but found 2. Lint: ESLintBear (indent): `Expected indentation of 4 tabs but found 2.` | |||||
// We were created, create any subunits now. | |||||
// This has to be done here for Ownership ought to be initialised. | |||||
if (msg.from == INVALID_PLAYER) | |||||
for (let point of this.turretPoints) | |||||
if (point.template) | |||||
this.CreateSubunit(point); | |||||
} | } | ||||
OnGarrisonedUnitsChanged(msg) | OnGarrisonedUnitsChanged(msg) | ||||
{ | { | ||||
// Ignore renaming for that is handled seperately | // Ignore renaming for that is handled seperately | ||||
// (i.e. called directly from GarrisonHolder.js). | // (i.e. called directly from GarrisonHolder.js). | ||||
if (msg.renamed) | if (msg.renamed) | ||||
return; | return; | ||||
for (let entity of msg.removed) | for (let entity of msg.removed) | ||||
this.LeaveTurret(entity); | this.LeaveTurretPoint(entity); | ||||
for (let entity of msg.added) | for (let entity of msg.added) | ||||
this.OccupyTurret(entity); | this.OccupyTurretPoint(entity); | ||||
} | } | ||||
} | } | ||||
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>" + | ||||
"<optional>" + | |||||
"<interleave>" + | |||||
"<element name='Template'>" + | |||||
"<text/>" + | |||||
"</element>" + | |||||
"<element name='Fixed' a:help='Whether this template is tied to the garrison position (i.e. not allowed to ungarrison).'>" + | |||||
"<data type='boolean'/>" + | |||||
"</element>" + | |||||
"</interleave>" + | |||||
"</optional>" + | |||||
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>" + | ||||
"<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>" + | ||||
"<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'/>" + | ||||
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>" + | "</element>" + | ||||
"</optional>" + | "</optional>" + | ||||
"</interleave>" + | "</interleave>" + | ||||
"</element>" + | "</element>" + | ||||
"</oneOrMore>" + | "</oneOrMore>" + | ||||
"</element>"; | "</element>"; | ||||
Engine.RegisterComponentType(IID_TurretHolder, "TurretHolder", TurretHolder); | Engine.RegisterComponentType(IID_TurretHolder, "TurretHolder", TurretHolder); |
Not a great name + I think you should just inline that in OwnershipChanged, since it's called only at template creation.