Changeset View
Standalone View
binaries/data/mods/public/simulation/components/ProximityAttack.js
- This file was added.
function ProximityAttack() {} | |||||
ProximityAttack.prototype.Schema = | |||||
"<a:help>Whether a unit or building inflicts damage to nearby units.</a:help>" + | |||||
"<a:example>" + | |||||
"<Damage>" + | |||||
"<Hack>0.0</Hack>" + | |||||
"<Pierce>10.0</Pierce>" + | |||||
"<Crush>50.0</Crush>" + | |||||
"</Damage>" + | |||||
"<MinRange>0</MinRange>" + | |||||
"<MaxRange>10</MaxRange>" + | |||||
"<FriendlyFire>true</FriendlyFire>" + | |||||
"<Shape>Circular</Shape>" + | |||||
"<OnlyWhenMoving>true</OnlyWhenMoving>" + | |||||
"<PrepareTime>0</PrepareTime>" + | |||||
"<RepeatTime>200</RepeatTime>" + | |||||
"<Delay>1000</Delay>" + | |||||
"<Bonuses>" + | |||||
"<Bonus1>" + | |||||
"<Classes>Support</Classes>" + | |||||
"<Multiplier>2</Multiplier>" + | |||||
"</Bonus1>" + | |||||
"</Bonuses>" + | |||||
"</a:example>" + | |||||
Attacking.BuildAttackEffectsSchema() + | |||||
"<optional>" + | |||||
"<element name='MinRange' a:help='Minimal range affected by the proximity damage'><ref name='nonNegativeDecimal'/></element>" + | |||||
"</optional>" + | |||||
"<element name='MaxRange' a:help='Maximum range of the area affected by the proximity damage'><ref name='nonNegativeDecimal'/></element>" + | |||||
"<element name='FriendlyFire' a:help='Whether the proximity damage can hurt non enemy units'><data type='boolean'/></element>" + | |||||
"<element name='Shape' a:help='Shape of the proximity damage, can be circular'><text/></element>" + | |||||
"<element name='OnlyWhenMoving' a:help='Whether the proximity damage is only applied when moving'><data type='boolean'/></element>" + | |||||
"<element name='PrepareTime' a:help='Time from the entering of the attack area until the attack actually occurs (in milliseconds).'>" + | |||||
"<data type='nonNegativeInteger'/>" + | |||||
"</element>" + | |||||
"<element name='RepeatTime' a:help='Time between attacks (in milliseconds).'>" + | |||||
"<data type='positiveInteger'/>" + | |||||
"</element>" + | |||||
"<element name='Delay' a:help='Delay of the damage in milliseconds.'><ref name='nonNegativeDecimal'/></element>" + | |||||
"<optional>" + | |||||
"<element name='RangeOverlay'>" + | |||||
"<interleave>" + | |||||
"<element name='LineTexture'><text/></element>" + | |||||
"<element name='LineTextureMask'><text/></element>" + | |||||
"<element name='LineThickness'><ref name='nonNegativeDecimal'/></element>" + | |||||
"</interleave>" + | |||||
"</element>" + | |||||
"</optional>"; | |||||
ProximityAttack.prototype.Init = function() | |||||
{ | |||||
}; | |||||
ProximityAttack.prototype.Serialize = function() | |||||
{ | |||||
return { "ProximityAttackTimer": this.ProximityAttackTimer }; | |||||
}; | |||||
ProximityAttack.prototype.Deserialize = function(data) | |||||
{ | |||||
this.ProximityAttackTimer = data.ProximityAttackTimer; | |||||
}; | |||||
/** | |||||
* Work out the range value with technology effects. | |||||
* @return {object} - The min/max range values of the proximity damage. | |||||
*/ | |||||
ProximityAttack.prototype.GetRange = function() | |||||
{ | |||||
return { | |||||
"min": ApplyValueModificationsToEntity("ProximityAttack/MinRange", +(this.template.MinRange || 0), this.entity), | |||||
"max": ApplyValueModificationsToEntity("ProximityAttack/MaxRange", +this.template.MaxRange, this.entity) | |||||
}; | |||||
}; | |||||
/** | |||||
* Work out the timer value with technology effects. | |||||
* @return {object} - The prepare/repeat timers of the proximity damage. | |||||
*/ | |||||
ProximityAttack.prototype.GetTimers = function() | |||||
{ | |||||
return { | |||||
"prepare": ApplyValueModificationsToEntity("ProximityAttack/PrepareTime", +this.template.PrepareTime, this.entity), | |||||
"repeat": ApplyValueModificationsToEntity("ProximityAttack/RepeatTime", +this.template.RepeatTime, this.entity) | |||||
}; | |||||
}; | |||||
ProximityAttack.prototype.GetAttackEffectsData = function() | |||||
{ | |||||
return Attacking.GetAttackEffectsData("ProximityAttack/", this.template, this.entity); | |||||
}; | |||||
/** | |||||
* Returns whether friendly fire is enabled for this proximity attack. | |||||
* This function is needed for the GUI. | |||||
* @return {boolean} - Whether friendly fire is enabled for this proximity attack. | |||||
*/ | |||||
ProximityAttack.prototype.GetFriendlyFire = function() | |||||
{ | |||||
return this.template.FriendlyFire; | |||||
}; | |||||
ProximityAttack.prototype.CauseProximityAttack = function() | |||||
{ | |||||
// Return when this entity is otherworldly or garrisoned | |||||
Stan: Not sure the comment is useful. Also what about visible garrisoning ? | |||||
FreagarachAuthorUnsubmitted Not Done Inline ActionsVisisbly garrisoned units may still do proximity damage right? Or wrong? ;) Freagarach: Visisbly garrisoned units may still do proximity damage right? Or wrong? ;) | |||||
StanUnsubmitted Not Done Inline ActionsRight :) Stan: Right :) | |||||
FreagarachAuthorUnsubmitted Not Done Inline ActionsAnd visibly garrisoned entities still have a position, and InWorld? Freagarach: And visibly garrisoned entities still have a position, and `InWorld`? | |||||
let cmpPosition = Engine.QueryInterface(this.entity, IID_Position); | |||||
if (!cmpPosition || !cmpPosition.IsInWorld()) | |||||
return; | |||||
let owner; | |||||
let cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership); | |||||
if (cmpOwnership) | |||||
owner = cmpOwnership.GetOwner(); | |||||
if (owner == INVALID_PLAYER) | |||||
StanUnsubmitted Not Done Inline ActionsShouldn't we abort ? Stan: Shouldn't we abort ? | |||||
FreagarachAuthorUnsubmitted Not Done Inline ActionsGood question, deathDamage does not abort and here I'm not sure what would be logical. Freagarach: Good question, deathDamage does not abort and here I'm not sure what would be logical. | |||||
StanUnsubmitted Not Done Inline ActionsWell the question is what is it gonna damage ? Stan: Well the question is what is it gonna damage ? | |||||
FreagarachAuthorUnsubmitted Not Done Inline ActionsEverything I guess, but that depends on the outcome of the function below. I need to check that. Freagarach: Everything I guess, but that depends on the outcome of the function below. I need to check that. | |||||
warn("Unit causing proximity damage does not have any owner."); | |||||
// Get an array of the players which ought to be damaged. | |||||
let playersToDamage = Attacking.GetPlayersToDamage(owner, this.template.FriendlyFire != "false"); | |||||
StanUnsubmitted Not Done Inline ActionsOne could check whether there are players to damage :) Stan: One could check whether there are players to damage :) | |||||
let radii = this.GetRange(); | |||||
// Call the Damage component to find out which entities to damage and damage them. | |||||
Attacking.CauseDamageOverArea({ | |||||
"type": "Proximity", | |||||
"attackData": this.GetAttackEffectsData(), | |||||
"origin": cmpPosition.GetPosition2D(), | |||||
"attacker": this.entity, | |||||
"attackerOwner": owner, | |||||
"minRange": radii.min, | |||||
"maxRange": radii.max, | |||||
"shape": this.template.Shape, | |||||
"playersToDamage": playersToDamage | |||||
}); | |||||
}; | |||||
// Start the proximity damage timer when no timer exists. | |||||
StanUnsubmitted Not Done Inline ActionsJSDOC Stan: JSDOC | |||||
FreagarachAuthorUnsubmitted Not Done Inline ActionsNot sure what to write then, there is no parameter it accepts, nor does it return anything? Freagarach: Not sure what to write then, there is no parameter it accepts, nor does it return anything? | |||||
StanUnsubmitted Not Done Inline ActionsJust /** * Your comment */ Stan: Just
```
/**
* Your comment
*/
``` | |||||
FreagarachAuthorUnsubmitted Not Done Inline ActionsAh, that is pretty simple indeed xD Freagarach: Ah, that is pretty simple indeed xD | |||||
ProximityAttack.prototype.CheckTimer = function() | |||||
{ | |||||
// If the unit only causes proximity damage whilst moving, disable timer when not moving. | |||||
if (this.template.OnlyWhenMoving != "false") | |||||
{ | |||||
let cmpPosition = Engine.QueryInterface(this.entity, IID_Position); | |||||
if (!cmpPosition || cmpPosition.IsInWorld() && !Vector3D.isEqualTo(cmpPosition.GetPreviousPosition(), cmpPosition.GetPosition())) | |||||
{ | |||||
// We don't need a timer, disable if one exists. | |||||
if (this.ProximityAttackTimer) | |||||
{ | |||||
let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); | |||||
cmpTimer.CancelTimer(this.ProximityAttackTimer); | |||||
delete this.ProximityAttackTimer; | |||||
} | |||||
return; | |||||
} | |||||
} | |||||
// We need a timer, enable if one doesn't exist. | |||||
if (this.ProximityAttackTimer) | |||||
return; | |||||
let timers = this.GetTimers(); | |||||
let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); | |||||
this.ProximityAttackTimer = cmpTimer.SetInterval(this.entity, IID_ProximityAttack, "CauseProximityAttack", timers.prepare, timers.repeat, undefined); | |||||
}; | |||||
/** | |||||
* Check timer when a unit changes motion. | |||||
* @param {{ "msg.to": string }} - The state to which the unit has changed. | |||||
*/ | |||||
ProximityAttack.prototype.OnUnitAIStateChanged = function(msg) | |||||
{ | |||||
this.CheckTimer(); | |||||
StanUnsubmitted Not Done Inline ActionsShouldn't you check for msg.to being directed to you or something ? Stan: Shouldn't you check for msg.to being directed to you or something ? | |||||
FreagarachAuthorUnsubmitted Not Done Inline ActionsIdeally, we don't check for UnitAI-state to change, but we check for the motion to change, but that is currently not possible. In practice, nigh all UnitAI-state changes either start or stop movement, so this is a hack currently. I'll update the JSDOC to explicit that. Freagarach: Ideally, we don't check for UnitAI-state to change, but we check for the motion to change, but… | |||||
}; | |||||
/** | |||||
* Check timer when a unit changes ownership. | |||||
* @param {{ "msg.from": number, "msg.to": number }} - From which player to which player the ownership is changed. | |||||
*/ | |||||
ProximityAttack.prototype.OnOwnershipChanged = function(msg) | |||||
{ | |||||
if (msg.to == INVALID_PLAYER) | |||||
{ | |||||
if (this.ProximityAttackTimer) | |||||
{ | |||||
let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); | |||||
cmpTimer.CancelTimer(this.ProximityAttackTimer); | |||||
delete this.ProximityAttackTimer; | |||||
} | |||||
return; | |||||
} | |||||
this.CheckTimer(); | |||||
}; | |||||
Engine.RegisterComponentType(IID_ProximityAttack, "ProximityAttack", ProximityAttack); |
Not sure the comment is useful. Also what about visible garrisoning ?