Changeset View
Standalone View
binaries/data/mods/public/simulation/components/UnitAI.js
Show First 20 Lines • Show All 171 Lines • ▼ Show 20 Lines | UnitAI.prototype.UnitFsmSpec = { | ||||
"TradingCanceled": function(msg) { | "TradingCanceled": function(msg) { | ||||
// ignore | // ignore | ||||
}, | }, | ||||
"GuardedAttacked": function(msg) { | "GuardedAttacked": function(msg) { | ||||
// ignore | // ignore | ||||
}, | }, | ||||
"ValueModification": function(msg) { | |||||
// ignore | |||||
}, | |||||
// Formation handlers: | // Formation handlers: | ||||
"FormationLeave": function(msg) { | "FormationLeave": function(msg) { | ||||
// ignore when we're not in FORMATIONMEMBER | // ignore when we're not in FORMATIONMEMBER | ||||
}, | }, | ||||
// Called when being told to walk as part of a formation | // Called when being told to walk as part of a formation | ||||
"Order.FormationWalk": function(msg) { | "Order.FormationWalk": function(msg) { | ||||
▲ Show 20 Lines • Show All 1,742 Lines • ▼ Show 20 Lines | "COMBAT": { | ||||
} | } | ||||
} | } | ||||
var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack); | var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack); | ||||
this.attackTimers = cmpAttack.GetTimers(this.order.data.attackType); | this.attackTimers = cmpAttack.GetTimers(this.order.data.attackType); | ||||
// If the repeat time since the last attack hasn't elapsed, | // If the repeat time since the last attack hasn't elapsed, | ||||
// delay this attack to avoid attacking too fast. | // delay this attack to avoid attacking too fast. | ||||
var prepare = this.attackTimers.prepare; | let prepare = this.attackTimers.prepare; | ||||
let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); | |||||
if (this.lastAttacked) | if (this.lastAttacked) | ||||
{ | { | ||||
var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); | let repeatLeft = this.lastAttacked + this.attackTimers.repeat - cmpTimer.GetTime(); | ||||
var repeatLeft = this.lastAttacked + this.attackTimers.repeat - cmpTimer.GetTime(); | |||||
prepare = Math.max(prepare, repeatLeft); | prepare = Math.max(prepare, repeatLeft); | ||||
} | } | ||||
this.lastAttacked = cmpTimer.GetTime() + prepare - this.attackTimers.repeat; | |||||
bb: (cost me some time but then considered correct) | |||||
templeUnsubmitted Not Done Inline ActionsYeah, maybe I should've added a comment there. For anyone else reading the code: It's possible for the attack rate to be modified before we make the first attack, i.e. during the prepare phase. The easiest way to handle that is to treat this as if we were in the repeat phase, by figuring out what would have been the lastAttacked time (although of course we didn't actually attack then). Then the math in the ValueModification section will be correct. temple: Yeah, maybe I should've added a comment there. For anyone else reading the code: It's possible… | |||||
this.oldAttackType = this.order.data.attackType; | this.oldAttackType = this.order.data.attackType; | ||||
// add prefix + no capital first letter for attackType | // add prefix + no capital first letter for attackType | ||||
var animationName = "attack_" + this.order.data.attackType.toLowerCase(); | var animationName = "attack_" + this.order.data.attackType.toLowerCase(); | ||||
if (this.IsFormationMember()) | if (this.IsFormationMember()) | ||||
{ | { | ||||
var cmpFormation = Engine.QueryInterface(this.formationController, IID_Formation); | var cmpFormation = Engine.QueryInterface(this.formationController, IID_Formation); | ||||
if (cmpFormation) | if (cmpFormation) | ||||
▲ Show 20 Lines • Show All 116 Lines • ▼ Show 20 Lines | "COMBAT": { | ||||
return; | return; | ||||
} | } | ||||
// Return to our original position | // Return to our original position | ||||
if (this.GetStance().respondHoldGround) | if (this.GetStance().respondHoldGround) | ||||
this.WalkToHeldPosition(); | this.WalkToHeldPosition(); | ||||
}, | }, | ||||
"ValueModification": function(msg) { | |||||
// Restart the timer if the attack repeat time has changed | |||||
if (msg.data.valueNames.indexOf("Attack/" + this.oldAttackType + "/RepeatTime") != -1) | |||||
{ | |||||
let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); | |||||
let cmpAttack = Engine.QueryInterface(this.entity, IID_Attack); | |||||
bbUnsubmitted Not Done Inline Actionsno need to check since we are attacking => ok bb: no need to check since we are attacking => ok | |||||
let attackTimers = cmpAttack.GetTimers(this.order.data.attackType); | |||||
let oldRepeatLeft = this.lastAttacked + this.attackTimers.repeat - cmpTimer.GetTime(); | |||||
let scale = attackTimers.repeat / this.attackTimers.repeat; | |||||
let repeatLeft = Math.round(scale * oldRepeatLeft); | |||||
bbUnsubmitted Not Done Inline Actionscuz of round players could cheat with 0.5 ms => meh but it is no crime to use non integer values, is it? (as the value modification could change to that anyway) bb: cuz of round players could cheat with 0.5 ms => meh but it is no crime to use non integer… | |||||
templeUnsubmitted Not Done Inline ActionsI just saw things like xx.000001 and was worried maybe something bad could happen if some timer interval was 0.000001. Not sure exactly what that bad thing would be though, so I'll remove the rounding. temple: I just saw things like xx.000001 and was worried maybe something bad could happen if some timer… | |||||
this.attackTimers = attackTimers; | |||||
this.lastAttacked = cmpTimer.GetTime() + repeatLeft - this.attackTimers.repeat; | |||||
this.StopTimer(); | |||||
this.StartTimer(repeatLeft, this.attackTimers.repeat); | |||||
this.SetAnimationSync(repeatLeft, this.attackTimers.repeat); | |||||
} | |||||
}, | |||||
// TODO: respond to target deaths immediately, rather than waiting | // TODO: respond to target deaths immediately, rather than waiting | ||||
// until the next Timer event | // until the next Timer event | ||||
"Attacked": function(msg) { | "Attacked": function(msg) { | ||||
if (this.order.data.target != msg.data.attacker) | if (this.order.data.target != msg.data.attacker) | ||||
{ | { | ||||
// If we're attacked by a close enemy, stronger than our current target, | // If we're attacked by a close enemy, stronger than our current target, | ||||
// we choose to attack it, but only if we're not forced to target something else | // we choose to attack it, but only if we're not forced to target something else | ||||
▲ Show 20 Lines • Show All 477 Lines • ▼ Show 20 Lines | "HEAL": { | ||||
"HEALING": { | "HEALING": { | ||||
"enter": function() { | "enter": function() { | ||||
var cmpHeal = Engine.QueryInterface(this.entity, IID_Heal); | var cmpHeal = Engine.QueryInterface(this.entity, IID_Heal); | ||||
this.healTimers = cmpHeal.GetTimers(); | this.healTimers = cmpHeal.GetTimers(); | ||||
// If the repeat time since the last heal hasn't elapsed, | // If the repeat time since the last heal hasn't elapsed, | ||||
// delay the action to avoid healing too fast. | // delay the action to avoid healing too fast. | ||||
var prepare = this.healTimers.prepare; | let prepare = this.healTimers.prepare; | ||||
let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); | |||||
if (this.lastHealed) | if (this.lastHealed) | ||||
{ | { | ||||
var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); | let repeatLeft = this.lastHealed + this.healTimers.repeat - cmpTimer.GetTime(); | ||||
var repeatLeft = this.lastHealed + this.healTimers.repeat - cmpTimer.GetTime(); | |||||
prepare = Math.max(prepare, repeatLeft); | prepare = Math.max(prepare, repeatLeft); | ||||
} | } | ||||
this.lastHealed = cmpTimer.GetTime() + prepare - this.healTimers.repeat; | |||||
this.SelectAnimation("heal", false, 1.0, "heal"); | this.SelectAnimation("heal", false, 1.0, "heal"); | ||||
this.SetAnimationSync(prepare, this.healTimers.repeat); | this.SetAnimationSync(prepare, this.healTimers.repeat); | ||||
this.StartTimer(prepare, this.healTimers.repeat); | this.StartTimer(prepare, this.healTimers.repeat); | ||||
// If using a non-default prepare time, re-sync the animation when the timer runs. | // If using a non-default prepare time, re-sync the animation when the timer runs. | ||||
this.resyncAnimation = (prepare != this.healTimers.prepare) ? true : false; | this.resyncAnimation = (prepare != this.healTimers.prepare) ? true : false; | ||||
▲ Show 20 Lines • Show All 43 Lines • ▼ Show 20 Lines | "HEAL": { | ||||
// Heal another one | // Heal another one | ||||
if (this.FindNewHealTargets()) | if (this.FindNewHealTargets()) | ||||
return; | return; | ||||
// Return to our original position | // Return to our original position | ||||
if (this.GetStance().respondHoldGround) | if (this.GetStance().respondHoldGround) | ||||
this.WalkToHeldPosition(); | this.WalkToHeldPosition(); | ||||
}, | }, | ||||
"ValueModification": function(msg) { | |||||
// Restart the timer if the attack repeat time has changed | |||||
if (msg.data.valueNames.indexOf("Heal/Rate") != -1) | |||||
{ | |||||
let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer); | |||||
let cmpHeal = Engine.QueryInterface(this.entity, IID_Heal); | |||||
let healTimers = cmpHeal.GetTimers(); | |||||
let oldRepeatLeft = this.lastHealed + this.healTimers.repeat - cmpTimer.GetTime(); | |||||
let scale = healTimers.repeat / this.healTimers.repeat; | |||||
let repeatLeft = Math.round(scale * oldRepeatLeft); | |||||
this.healTimers = healTimers; | |||||
this.lastHealed = cmpTimer.GetTime() + repeatLeft - this.healTimers.repeat; | |||||
this.StopTimer(); | |||||
this.StartTimer(repeatLeft, this.healTimers.repeat); | |||||
this.SetAnimationSync(repeatLeft, this.healTimers.repeat); | |||||
} | |||||
}, | |||||
}, | }, | ||||
"CHASING": { | "CHASING": { | ||||
"enter": function () { | "enter": function () { | ||||
this.SelectAnimation("move"); | this.SelectAnimation("move"); | ||||
this.StartTimer(1000, 1000); | this.StartTimer(1000, 1000); | ||||
}, | }, | ||||
"leave": function () { | "leave": function () { | ||||
this.StopTimer(); | this.StopTimer(); | ||||
▲ Show 20 Lines • Show All 1,450 Lines • ▼ Show 20 Lines | else if (msg.tag == this.losHealRangeQuery) | ||||
this.UnitFsm.ProcessMessage(this, {"type": "LosHealRangeUpdate", "data": msg}); | this.UnitFsm.ProcessMessage(this, {"type": "LosHealRangeUpdate", "data": msg}); | ||||
}; | }; | ||||
UnitAI.prototype.OnPackFinished = function(msg) | UnitAI.prototype.OnPackFinished = function(msg) | ||||
{ | { | ||||
this.UnitFsm.ProcessMessage(this, {"type": "PackFinished", "packed": msg.packed}); | this.UnitFsm.ProcessMessage(this, {"type": "PackFinished", "packed": msg.packed}); | ||||
}; | }; | ||||
UnitAI.prototype.OnValueModification = function(msg) | |||||
{ | |||||
this.UnitFsm.ProcessMessage(this, {"type": "ValueModification", "data": msg}); | |||||
bbUnsubmitted Not Done Inline ActionsbbLint was warning, but then saw the lines above.... bb: bbLint was warning, but then saw the lines above.... | |||||
}; | |||||
//// Helper functions to be called by the FSM //// | //// Helper functions to be called by the FSM //// | ||||
UnitAI.prototype.GetWalkSpeed = function() | UnitAI.prototype.GetWalkSpeed = function() | ||||
{ | { | ||||
var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion); | var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion); | ||||
return cmpUnitMotion.GetWalkSpeed(); | return cmpUnitMotion.GetWalkSpeed(); | ||||
}; | }; | ||||
▲ Show 20 Lines • Show All 2,029 Lines • Show Last 20 Lines |
(cost me some time but then considered correct)