Changeset View
Standalone View
binaries/data/mods/public/simulation/components/UnitAI.js
Show First 20 Lines • Show All 140 Lines • ▼ Show 20 Lines | var g_OrdersCancelUnpacking = new Set([ | ||||
"WalkToTarget", | "WalkToTarget", | ||||
"Patrol", | "Patrol", | ||||
"Garrison" | "Garrison" | ||||
]); | ]); | ||||
// When leaving a foundation, we want to be clear of it by this distance. | // When leaving a foundation, we want to be clear of it by this distance. | ||||
var g_LeaveFoundationRange = 4; | var g_LeaveFoundationRange = 4; | ||||
UnitAI.prototype.notifyToCheerInRange = 30; | |||||
Freagarach: Capital `n`? | |||||
Done Inline Actionsrather no, it is variable and not function. Silier: rather no, it is variable and not function. | |||||
// See ../helpers/FSM.js for some documentation of this FSM specification syntax | // See ../helpers/FSM.js for some documentation of this FSM specification syntax | ||||
UnitAI.prototype.UnitFsmSpec = { | UnitAI.prototype.UnitFsmSpec = { | ||||
// Default event handlers: | // Default event handlers: | ||||
"MovementUpdate": function(msg) { | "MovementUpdate": function(msg) { | ||||
// ignore spurious movement messages | // ignore spurious movement messages | ||||
// (these can happen when stopping moving at the same time | // (these can happen when stopping moving at the same time | ||||
▲ Show 20 Lines • Show All 184 Lines • ▼ Show 20 Lines | if (this.CanPack()) | ||||
return; | return; | ||||
} | } | ||||
if (this.CheckRange(this.order.data)) | if (this.CheckRange(this.order.data)) | ||||
{ | { | ||||
// We are already at the target, or can't move at all | // We are already at the target, or can't move at all | ||||
this.FinishOrder(); | this.FinishOrder(); | ||||
return true; | return true; | ||||
Lint: ESLintBear (consistent-return) Method 'Order.WalkToTarget' expected no return value. Lint: ESLintBear (consistent-return): `Method 'Order.WalkToTarget' expected no return value.` | |||||
} | } | ||||
// It's not too bad if we don't arrive at exactly the right position. | // It's not too bad if we don't arrive at exactly the right position. | ||||
this.order.data.relaxed = true; | this.order.data.relaxed = true; | ||||
if (this.IsAnimal()) | if (this.IsAnimal()) | ||||
this.SetNextState("ANIMAL.WALKING"); | this.SetNextState("ANIMAL.WALKING"); | ||||
else | else | ||||
▲ Show 20 Lines • Show All 298 Lines • ▼ Show 20 Lines | "Order.Garrison": function(msg) { | ||||
else | else | ||||
this.SetNextState("INDIVIDUAL.GARRISON.APPROACHING"); | this.SetNextState("INDIVIDUAL.GARRISON.APPROACHING"); | ||||
}, | }, | ||||
"Order.Ungarrison": function() { | "Order.Ungarrison": function() { | ||||
this.FinishOrder(); | this.FinishOrder(); | ||||
this.isGarrisoned = false; | this.isGarrisoned = false; | ||||
}, | }, | ||||
"Order.Cheering": function(msg) { | "Order.Cheer": function(msg) { | ||||
if (this.IsFormationMember()) | return { "discardOrder": true }; | ||||
Not Done Inline Actions- Freagarach: -` ` | |||||
this.SetNextState("FORMATIONMEMBER.CHEERING"); | |||||
else | |||||
this.SetNextState("INDIVIDUAL.CHEERING"); | |||||
}, | }, | ||||
"Order.Pack": function(msg) { | "Order.Pack": function(msg) { | ||||
if (this.CanPack()) | if (this.CanPack()) | ||||
this.SetNextState("INDIVIDUAL.PACKING"); | this.SetNextState("INDIVIDUAL.PACKING"); | ||||
}, | }, | ||||
"Order.Unpack": function(msg) { | "Order.Unpack": function(msg) { | ||||
▲ Show 20 Lines • Show All 114 Lines • ▼ Show 20 Lines | "Order.Garrison": function(msg) { | ||||
if (!this.CheckGarrisonRange(msg.data.target)) | if (!this.CheckGarrisonRange(msg.data.target)) | ||||
{ | { | ||||
if (!this.CheckTargetVisible(msg.data.target)) | if (!this.CheckTargetVisible(msg.data.target)) | ||||
{ | { | ||||
this.FinishOrder(); | this.FinishOrder(); | ||||
return; | return; | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
Lint: ESLintBear (no-else-return) Unnecessary 'else' after 'return'. Lint: ESLintBear (no-else-return): `Unnecessary 'else' after 'return'.` | |||||
this.SetNextState("GARRISON.APPROACHING"); | this.SetNextState("GARRISON.APPROACHING"); | ||||
return; | return; | ||||
} | } | ||||
} | } | ||||
this.SetNextState("GARRISON.GARRISONING"); | this.SetNextState("GARRISON.GARRISONING"); | ||||
}, | }, | ||||
▲ Show 20 Lines • Show All 246 Lines • ▼ Show 20 Lines | "PATROL": { | ||||
if (this.orderQueue.length == 1) | if (this.orderQueue.length == 1) | ||||
this.PushOrder("Patrol", this.patrolStartPosOrder); | this.PushOrder("Patrol", this.patrolStartPosOrder); | ||||
this.PushOrder(this.order.type, this.order.data); | this.PushOrder(this.order.type, this.order.data); | ||||
this.FinishOrder(); | this.FinishOrder(); | ||||
}, | }, | ||||
}, | }, | ||||
"GARRISON":{ | "GARRISON":{ | ||||
Lint: ESLintBear (key-spacing) Missing space before value for key 'GARRISON'. Lint: ESLintBear (key-spacing): `Missing space before value for key 'GARRISON'.` | |||||
"APPROACHING": { | "APPROACHING": { | ||||
"enter": function() { | "enter": function() { | ||||
if (!this.MoveToGarrisonRange(this.order.data.target)) | if (!this.MoveToGarrisonRange(this.order.data.target)) | ||||
{ | { | ||||
this.FinishOrder(); | this.FinishOrder(); | ||||
return true; | return true; | ||||
} | } | ||||
let cmpFormation = Engine.QueryInterface(this.entity, IID_Formation); | let cmpFormation = Engine.QueryInterface(this.entity, IID_Formation); | ||||
▲ Show 20 Lines • Show All 413 Lines • ▼ Show 20 Lines | "GuardedAttacked": function(msg) { | ||||
{ | { | ||||
this.orderQueue.splice(1, 1); | this.orderQueue.splice(1, 1); | ||||
Engine.PostMessage(this.entity, MT_UnitAIOrderDataChanged, { "to": this.GetOrderData() }); | Engine.PostMessage(this.entity, MT_UnitAIOrderDataChanged, { "to": this.GetOrderData() }); | ||||
} | } | ||||
} | } | ||||
}, | }, | ||||
"IDLE": { | "IDLE": { | ||||
"Order.Cheer": function() { | |||||
Not Done Inline Actions+ Freagarach: +` ` | |||||
this.SetNextState("CHEERING"); | |||||
Not Done Inline ActionsPerhaps also check if we're really idle? Freagarach: Perhaps also check if we're really idle? | |||||
Done Inline ActionsNo order does that Silier: No order does that | |||||
Not Done Inline ActionsExample: Unit calls finishOrder and enters this idle state, with enemy nearby. The rangeQuery won't be reset until the timer expires, but we can be put in cheering state when that order arrives at the same turn. Freagarach: Example: Unit calls finishOrder and enters this idle state, with enemy nearby. The rangeQuery… | |||||
Done Inline ActionsDescription of idle says: If we entered the idle state we must have nothing better to do. Silier: Description of idle says: ` If we entered the idle state we must have nothing better to do`. | |||||
Not Done Inline Actions
Which would mean always having reveiced one blow before being able to strike back. That being said, missed cheers is also not nice indeed. We'll see how this unfolds. Freagarach: > Unit will react back when being attacked and it just does not initiate attack right away… | |||||
}, | |||||
Not Done Inline Actionscomma Silier: comma | |||||
Not Done Inline Actions+\n Freagarach: +`\n` | |||||
"enter": function() { | "enter": function() { | ||||
// Switch back to idle animation to guarantee we won't | // Switch back to idle animation to guarantee we won't | ||||
// get stuck with an incorrect animation | // get stuck with an incorrect animation | ||||
this.SelectAnimation("idle"); | this.SelectAnimation("idle"); | ||||
// Idle is the default state. If units try, from the IDLE.enter sub-state, to | // Idle is the default state. If units try, from the IDLE.enter sub-state, to | ||||
// begin another order, and that order fails (calling FinishOrder), they might | // begin another order, and that order fails (calling FinishOrder), they might | ||||
// end up in an infinite loop. To avoid this, all methods that could put the unit in | // end up in an infinite loop. To avoid this, all methods that could put the unit in | ||||
▲ Show 20 Lines • Show All 434 Lines • ▼ Show 20 Lines | "COMBAT": { | ||||
// if the target is a formation, save the attacking formation, and pick a member | // if the target is a formation, save the attacking formation, and pick a member | ||||
if (cmpFormation) | if (cmpFormation) | ||||
{ | { | ||||
this.order.data.formationTarget = target; | this.order.data.formationTarget = target; | ||||
target = cmpFormation.GetClosestMember(this.entity); | target = cmpFormation.GetClosestMember(this.entity); | ||||
this.order.data.target = target; | this.order.data.target = target; | ||||
} | } | ||||
this.shouldCheer = false; | |||||
if (!this.CanAttack(target)) | if (!this.CanAttack(target)) | ||||
{ | { | ||||
this.SetNextState("COMBAT.FINDINGNEWTARGET"); | this.SetNextState("COMBAT.FINDINGNEWTARGET"); | ||||
return true; | return true; | ||||
} | } | ||||
if (!this.CheckTargetAttackRange(target, this.order.data.attackType)) | if (!this.CheckTargetAttackRange(target, this.order.data.attackType)) | ||||
{ | { | ||||
Show All 33 Lines | "COMBAT": { | ||||
// TODO: we should probably only bother syncing projectile attacks, not melee | // TODO: we should probably only bother syncing projectile attacks, not melee | ||||
// 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.attackTimers.prepare; | this.resyncAnimation = prepare != this.attackTimers.prepare; | ||||
this.FaceTowardsTarget(this.order.data.target); | this.FaceTowardsTarget(this.order.data.target); | ||||
let cmpBuildingAI = Engine.QueryInterface(this.entity, IID_BuildingAI); | let cmpBuildingAI = Engine.QueryInterface(this.entity, IID_BuildingAI); | ||||
if (cmpBuildingAI) | if (cmpBuildingAI) | ||||
Not Done Inline Actions+/n Freagarach: +`/n` | |||||
Not Done Inline ActionsEarly return to drop the else ? Stan: Early return to drop the else ? | |||||
cmpBuildingAI.SetUnitAITarget(this.order.data.target); | cmpBuildingAI.SetUnitAITarget(this.order.data.target); | ||||
else | |||||
Not Done Inline ActionsIsn't it still false here? Freagarach: Isn't it still false here? | |||||
{ | |||||
Not Done Inline ActionsIdem. Freagarach: Idem. | |||||
let cmpUnitAI = Engine.QueryInterface(this.order.data.target, IID_UnitAI); | |||||
this.shouldCheer = cmpUnitAI && (!cmpUnitAI.IsAnimal() || cmpUnitAI.IsDangerousAnimal()); | |||||
Not Done Inline Actions(This is probably because of hunting? But just to mention it: we won't cheer after killing a lion as well.) Freagarach: (This is probably because of hunting? But just to mention it: we won't cheer after killing a… | |||||
Not Done Inline ActionsDon't we want to cheer also whenever we've destroyed a structure? Freagarach: Don't we want to cheer also whenever we've destroyed a structure? | |||||
Done Inline Actionsgood question. Silier: good question.
I do not have clear answer for that.
As structures are entities in los, they… | |||||
Not Done Inline ActionsI think we'd want to. We can force orders anyway and queued orders also prevent cheering, so it won't disturb gameplay. Freagarach: I think we'd want to. We can force orders anyway and queued orders also prevent cheering, so it… | |||||
Done Inline Actionsanother thought, Silier: another thought,
maybe it would be good to put that to identity component? cmpIdentity. | |||||
Not Done Inline ActionsI would say just cheer always, even when killing an animal. Hunting won't be affected since there is an order afterwards. And I guess people will be generally delighted when coming out of combat state ;) Freagarach: I would say just cheer always, even when killing an animal. Hunting won't be affected since… | |||||
Not Done Inline ActionsWhat about having cheering classes? MatchClassList(happywhenhavingkilled, this.target.classes) Stan: What about having cheering classes? MatchClassList(happywhenhavingkilled, this.target.classes) | |||||
Not Done Inline ActionsAnd no "happywhenhavingkilled" means not cheering (e.g. rams etc.). Freagarach: And no "happywhenhavingkilled" means not cheering (e.g. rams etc.). | |||||
Done Inline Actions
Thought about that, but in my opinion that would add too big overhead compared to that we want to add just fancy animation. Silier: >What about having cheering classes?
>MatchClassList(happywhenhavingkilled, this.target. | |||||
Not Done Inline ActionsSo we can just cheer always and when user find it strange we can react on that? Freagarach: So we can just cheer always and when user find it strange we can react on that? | |||||
Done Inline Actionsnot always, cheering after killing chickens/sheep/deer is weird enough already since non dangerous animals does not end up in range queries. Silier: not always, cheering after killing chickens/sheep/deer is weird enough already since non… | |||||
Not Done Inline ActionscmpUnitAI && (!cmpUnitAI.IsAnimal() || cmpUnitAI.IsDangerousAnimal());? Freagarach: `cmpUnitAI && (!cmpUnitAI.IsAnimal() || cmpUnitAI.IsDangerousAnimal());`? | |||||
} | |||||
return false; | return false; | ||||
}, | }, | ||||
"leave": function() { | "leave": function() { | ||||
let cmpBuildingAI = Engine.QueryInterface(this.entity, IID_BuildingAI); | let cmpBuildingAI = Engine.QueryInterface(this.entity, IID_BuildingAI); | ||||
if (cmpBuildingAI) | if (cmpBuildingAI) | ||||
cmpBuildingAI.SetUnitAITarget(0); | cmpBuildingAI.SetUnitAITarget(0); | ||||
this.StopTimer(); | this.StopTimer(); | ||||
▲ Show 20 Lines • Show All 61 Lines • ▼ Show 20 Lines | "COMBAT": { | ||||
}, | }, | ||||
// 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 we are capturing and are attacked by something that we would not capture, attack that entity instead | // If we are capturing and are attacked by something that we would not capture, attack that entity instead | ||||
if (this.order.data.attackType == "Capture" && (this.GetStance().targetAttackersAlways || !this.order.data.force) | if (this.order.data.attackType == "Capture" && (this.GetStance().targetAttackersAlways || !this.order.data.force) | ||||
&& this.order.data.target != msg.data.attacker && this.GetBestAttackAgainst(msg.data.attacker, true) != "Capture") | && this.order.data.target != msg.data.attacker && this.GetBestAttackAgainst(msg.data.attacker, true) != "Capture") | ||||
Lint: JSHintBear Misleading line break before '&&'; readers may interpret this as an expression boundary. Lint: JSHintBear: `Misleading line break before '&&'; readers may interpret this as an expression boundary.` | |||||
Lint: ESLintBear (operator-linebreak) '&&' should be placed at the end of the line. Lint: ESLintBear (operator-linebreak): `'&&' should be placed at the end of the line.` | |||||
this.RespondToTargetedEntities([msg.data.attacker]); | this.RespondToTargetedEntities([msg.data.attacker]); | ||||
}, | }, | ||||
}, | }, | ||||
"FINDINGNEWTARGET": { | "FINDINGNEWTARGET": { | ||||
"Order.Cheer": function() { | |||||
this.SetNextState("CHEERING"); | |||||
}, | |||||
Not Done Inline Actions+\n. Freagarach: +`\n`. | |||||
"enter": function() { | "enter": function() { | ||||
// Try to find the formation the target was a part of. | // Try to find the formation the target was a part of. | ||||
let cmpFormation = Engine.QueryInterface(this.order.data.target, IID_Formation); | let cmpFormation = Engine.QueryInterface(this.order.data.target, IID_Formation); | ||||
if (!cmpFormation) | if (!cmpFormation) | ||||
cmpFormation = Engine.QueryInterface(this.order.data.formationTarget || INVALID_ENTITY, IID_Formation); | cmpFormation = Engine.QueryInterface(this.order.data.formationTarget || INVALID_ENTITY, IID_Formation); | ||||
// If the target is a formation, pick closest member. | // If the target is a formation, pick closest member. | ||||
if (cmpFormation) | if (cmpFormation) | ||||
Show All 15 Lines | "COMBAT": { | ||||
return true; | return true; | ||||
} | } | ||||
// See if we can switch to a new nearby enemy | // See if we can switch to a new nearby enemy | ||||
if (this.FindNewTargets()) | if (this.FindNewTargets()) | ||||
return true; | return true; | ||||
// Return to our original position | // Return to our original position | ||||
if (this.GetStance().respondHoldGround) | if (this.GetStance().respondHoldGround) | ||||
this.WalkToHeldPosition(); | this.WalkToHeldPosition(); | ||||
Not Done Inline ActionsDo we want to cheer before this or after this? Freagarach: Do we want to cheer before this or after this? | |||||
if (this.shouldCheer) | |||||
Not Done Inline ActionsThink you want this to be in the "leave" call, if we don't have a next order or something like that, rather. wraitii: Think you want this to be in the "leave" call, if we don't have a next order or something like… | |||||
Not Done Inline Actions+/n Freagarach: +`/n` | |||||
Done Inline ActionsActually I think this is the best place, If this would be in leave, that would be called even when you would give order to stop or you change order to walk Silier: Actually I think this is the best place, If this would be in leave, that would be called even… | |||||
{ | |||||
this.Cheer(); | |||||
this.CallPlayerOwnedEntitiesFunctionInRange("Cheer", [], this.notifyToCheerInRange); | |||||
Not Done Inline ActionsPerhaps make the magic 30 part of the prototype? Freagarach: Perhaps make the magic `30` part of the prototype? | |||||
Done Inline ActionsStill would be magic, but maybe better Silier: Still would be magic, but maybe better | |||||
} | |||||
return true; | return true; | ||||
}, | }, | ||||
}, | }, | ||||
"CHASING": { | "CHASING": { | ||||
"Order.MoveToChasingPoint": function(msg) { | "Order.MoveToChasingPoint": function(msg) { | ||||
if (this.CheckPointRangeExplicit(msg.data.x, msg.data.z, 0, msg.data.max)) | if (this.CheckPointRangeExplicit(msg.data.x, msg.data.z, 0, msg.data.max)) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 102 Lines • ▼ Show 20 Lines | "COMBAT": { | ||||
return false; | return false; | ||||
}, | }, | ||||
"leave": function() { | "leave": function() { | ||||
this.StopMoving(); | this.StopMoving(); | ||||
}, | }, | ||||
"MovementUpdate": function(msg) { | "MovementUpdate": function(msg) { | ||||
// If it looks like the path is failing, and we are close enough (3 tiles) from wanted range | // If it looks like the path is failing, and we are close enough (3 tiles) from wanted range | ||||
// stop anyways. This avoids pathing for an unreachable goal and reduces lag considerably. | // stop anyways. This avoids pathing for an unreachable goal and reduces lag considerably. | ||||
if (msg.likelyFailure || | if (msg.likelyFailure || | ||||
Lint: ESLintBear (no-trailing-spaces) Trailing spaces not allowed. Lint: ESLintBear (no-trailing-spaces): `Trailing spaces not allowed.` | |||||
msg.obstructed && this.RelaxedMaxRangeCheck(this.order.data, this.order.data.max + this.DefaultRelaxedMaxRange) || | msg.obstructed && this.RelaxedMaxRangeCheck(this.order.data, this.order.data.max + this.DefaultRelaxedMaxRange) || | ||||
!msg.obstructed && this.CheckRange(this.order.data)) | !msg.obstructed && this.CheckRange(this.order.data)) | ||||
this.FinishOrder(); | this.FinishOrder(); | ||||
}, | }, | ||||
}, | }, | ||||
}, | }, | ||||
}, | }, | ||||
▲ Show 20 Lines • Show All 285 Lines • ▼ Show 20 Lines | "GATHER": { | ||||
if (previousTarget == ent) | if (previousTarget == ent) | ||||
return false; | return false; | ||||
if (type.generic == "treasure" && resourceType.generic == "treasure") | if (type.generic == "treasure" && resourceType.generic == "treasure") | ||||
return true; | return true; | ||||
return type.specific == resourceType.specific && | return type.specific == resourceType.specific && | ||||
(type.specific != "meat" || resourceTemplate == template); | (type.specific != "meat" || resourceTemplate == template); | ||||
}); | }); | ||||
Lint: ESLintBear (indent) Expected indentation of 6 tabs but found 5. Lint: ESLintBear (indent): `Expected indentation of 6 tabs but found 5.` | |||||
if (nearbyResource) | if (nearbyResource) | ||||
{ | { | ||||
this.PerformGather(nearbyResource, false, false); | this.PerformGather(nearbyResource, false, false); | ||||
return true; | return true; | ||||
} | } | ||||
// Failing that, try to move there and se if we are more lucky: maybe there are resources in FOW. | // Failing that, try to move there and se if we are more lucky: maybe there are resources in FOW. | ||||
▲ Show 20 Lines • Show All 588 Lines • ▼ Show 20 Lines | "GARRISON": { | ||||
"leave": function() { | "leave": function() { | ||||
} | } | ||||
}, | }, | ||||
}, | }, | ||||
"CHEERING": { | "CHEERING": { | ||||
"enter": function() { | "enter": function() { | ||||
// Unit is invulnerable while cheering | |||||
var cmpResistance = Engine.QueryInterface(this.entity, IID_Resistance); | |||||
cmpResistance.SetInvulnerability(true); | |||||
this.SelectAnimation("promotion"); | this.SelectAnimation("promotion"); | ||||
Not Done Inline ActionsShould be renamed in a seperate diff to cheering I guess. Freagarach: Should be renamed in a seperate diff to cheering I guess. | |||||
this.StartTimer(2800, 2800); | this.StartTimer(2800); | ||||
return false; | return false; | ||||
}, | }, | ||||
"leave": function() { | "leave": function() { | ||||
this.StopTimer(); | this.StopTimer(); | ||||
this.ResetAnimation(); | this.ResetAnimation(); | ||||
if (this.formationAnimationVariant) | }, | ||||
this.SetAnimationVariant(this.formationAnimationVariant) | |||||
else | "LosRangeUpdate": function(msg) { | ||||
this.SetDefaultAnimationVariant(); | if (msg && msg.data && msg.data.added && msg.data.added.length) | ||||
var cmpResistance = Engine.QueryInterface(this.entity, IID_Resistance); | this.RespondToSightedEntities(msg.data.added); | ||||
cmpResistance.SetInvulnerability(false); | }, | ||||
"LosHealRangeUpdate": function(msg) { | |||||
if (msg && msg.data && msg.data.added && msg.data.added.length) | |||||
this.RespondToHealableEntities(msg.data.added); | |||||
}, | |||||
"LosAttackRangeUpdate": function(msg) { | |||||
if (msg && msg.data && msg.data.added && msg.data.added.length && this.GetStance().targetVisibleEnemies) | |||||
this.AttackEntitiesByPreference(msg.data.added); | |||||
}, | }, | ||||
"Timer": function(msg) { | "Timer": function(msg) { | ||||
this.FinishOrder(); | this.FinishOrder(); | ||||
}, | }, | ||||
}, | }, | ||||
"PACKING": { | "PACKING": { | ||||
▲ Show 20 Lines • Show All 193 Lines • ▼ Show 20 Lines | "FEEDING": { | ||||
}, | }, | ||||
}, | }, | ||||
"FLEEING": "INDIVIDUAL.FLEEING", // reuse the same fleeing behaviour for animals | "FLEEING": "INDIVIDUAL.FLEEING", // reuse the same fleeing behaviour for animals | ||||
"COMBAT": "INDIVIDUAL.COMBAT", // reuse the same combat behaviour for animals | "COMBAT": "INDIVIDUAL.COMBAT", // reuse the same combat behaviour for animals | ||||
"WALKING": "INDIVIDUAL.WALKING", // reuse the same walking behaviour for animals | "WALKING": "INDIVIDUAL.WALKING", // reuse the same walking behaviour for animals | ||||
// only used for domestic animals | // only used for domestic animals | ||||
Lint: ESLintBear (indent) Expected indentation of 2 tabs but found 7. Lint: ESLintBear (indent): `Expected indentation of 2 tabs but found 7.` | |||||
"CHEERING": { | |||||
"enter": function() { | |||||
this.SelectAnimation("promotion"); | |||||
this.StartTimer(2800); | |||||
StanUnsubmitted Not Done Inline ActionsConstant? Stan: Constant? | |||||
return false; | |||||
}, | |||||
"leave": function() { | |||||
this.StopTimer(); | |||||
this.ResetAnimation(); | |||||
}, | |||||
"LosRangeUpdate": function(msg) { | |||||
if (msg && msg.data && msg.data.added && msg.data.added.length) | |||||
this.RespondToSightedEntities(msg.data.added); | |||||
}, | |||||
"LosAttackRangeUpdate": function(msg) { | |||||
if (this.template.NaturalBehaviour == "violent" && msg && msg.data && msg.data.added && msg.data.added.length) | |||||
this.AttackVisibleEntity(msg.data.added); | |||||
}, | |||||
"Timer": function(msg) { | |||||
this.FinishOrder(); | |||||
}, | |||||
}, | |||||
// Reuse the same garrison behaviour for animals. | // Reuse the same garrison behaviour for animals. | ||||
"GARRISON": "INDIVIDUAL.GARRISON", | "GARRISON": "INDIVIDUAL.GARRISON", | ||||
}, | }, | ||||
}; | }; | ||||
UnitAI.prototype.Init = function() | UnitAI.prototype.Init = function() | ||||
{ | { | ||||
this.orderQueue = []; // current order is at the front of the list | this.orderQueue = []; // current order is at the front of the list | ||||
Show All 32 Lines | |||||
UnitAI.prototype.IsFormationMember = function() | UnitAI.prototype.IsFormationMember = function() | ||||
{ | { | ||||
return (this.formationController != INVALID_ENTITY); | return (this.formationController != INVALID_ENTITY); | ||||
}; | }; | ||||
UnitAI.prototype.IsAnimal = function() | UnitAI.prototype.IsAnimal = function() | ||||
{ | { | ||||
return (this.template.NaturalBehaviour ? true : false); | return (this.template.NaturalBehaviour ? true : false); | ||||
Lint: ESLintBear (no-unneeded-ternary) Unnecessary use of boolean literals in conditional expression. Lint: ESLintBear (no-unneeded-ternary): `Unnecessary use of boolean literals in conditional expression.` | |||||
}; | }; | ||||
UnitAI.prototype.IsDangerousAnimal = function() | UnitAI.prototype.IsDangerousAnimal = function() | ||||
{ | { | ||||
return (this.IsAnimal() && (this.template.NaturalBehaviour == "violent" || | return (this.IsAnimal() && (this.template.NaturalBehaviour == "violent" || | ||||
this.template.NaturalBehaviour == "aggressive")); | this.template.NaturalBehaviour == "aggressive")); | ||||
}; | }; | ||||
▲ Show 20 Lines • Show All 113 Lines • ▼ Show 20 Lines | UnitAI.prototype.OnOwnershipChanged = function(msg) | ||||
if (this.isGuardOf && (msg.to == INVALID_PLAYER || !IsOwnedByMutualAllyOfEntity(this.entity, this.isGuardOf))) | if (this.isGuardOf && (msg.to == INVALID_PLAYER || !IsOwnedByMutualAllyOfEntity(this.entity, this.isGuardOf))) | ||||
this.RemoveGuard(); | this.RemoveGuard(); | ||||
// If the unit isn't being created or dying, reset stance and clear orders | // If the unit isn't being created or dying, reset stance and clear orders | ||||
if (msg.to != INVALID_PLAYER && msg.from != INVALID_PLAYER) | if (msg.to != INVALID_PLAYER && msg.from != INVALID_PLAYER) | ||||
{ | { | ||||
// Switch to a virgin state to let states execute their leave handlers. | // Switch to a virgin state to let states execute their leave handlers. | ||||
// except if garrisoned or cheering or (un)packing, in which case we only clear the order queue | // Except if garrisoned or (un)packing, in which case we only clear the order queue. | ||||
if (this.isGarrisoned || this.IsPacking() || this.orderQueue[0] && this.orderQueue[0].type == "Cheering") | if (this.isGarrisoned || this.IsPacking()) | ||||
Not Done Inline Actions+. Freagarach: +`.` | |||||
Not Done Inline Actions-or Freagarach: -`or` | |||||
{ | { | ||||
this.orderQueue.length = Math.min(this.orderQueue.length, 1); | this.orderQueue.length = Math.min(this.orderQueue.length, 1); | ||||
Engine.PostMessage(this.entity, MT_UnitAIOrderDataChanged, { "to": this.GetOrderData() }); | Engine.PostMessage(this.entity, MT_UnitAIOrderDataChanged, { "to": this.GetOrderData() }); | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
let index = this.GetCurrentState().indexOf("."); | let index = this.GetCurrentState().indexOf("."); | ||||
if (index != -1) | if (index != -1) | ||||
this.UnitFsm.SwitchToNextState(this, this.GetCurrentState().slice(0,index)); | this.UnitFsm.SwitchToNextState(this, this.GetCurrentState().slice(0,index)); | ||||
Lint: ESLintBear (comma-spacing) A space is required after ','. Lint: ESLintBear (comma-spacing): `A space is required after ','.` | |||||
this.Stop(false); | this.Stop(false); | ||||
} | } | ||||
this.workOrders = []; | this.workOrders = []; | ||||
let cmpTrader = Engine.QueryInterface(this.entity, IID_Trader); | let cmpTrader = Engine.QueryInterface(this.entity, IID_Trader); | ||||
if (cmpTrader) | if (cmpTrader) | ||||
cmpTrader.StopTrading(); | cmpTrader.StopTrading(); | ||||
Show All 40 Lines | |||||
UnitAI.prototype.OnPickupCanceled = function(msg) | UnitAI.prototype.OnPickupCanceled = function(msg) | ||||
{ | { | ||||
for (let i = 0; i < this.orderQueue.length; ++i) | for (let i = 0; i < this.orderQueue.length; ++i) | ||||
{ | { | ||||
if (this.orderQueue[i].type != "PickupUnit" || this.orderQueue[i].data.target != msg.entity) | if (this.orderQueue[i].type != "PickupUnit" || this.orderQueue[i].data.target != msg.entity) | ||||
continue; | continue; | ||||
if (i == 0) | if (i == 0) | ||||
this.UnitFsm.ProcessMessage(this, {"type": "PickupCanceled", "data": msg}); | this.UnitFsm.ProcessMessage(this, {"type": "PickupCanceled", "data": msg}); | ||||
Lint: ESLintBear (object-curly-spacing) A space is required before '}'. Lint: ESLintBear (object-curly-spacing): `A space is required before '}'.` | |||||
Lint: ESLintBear (object-curly-spacing) A space is required after '{'. Lint: ESLintBear (object-curly-spacing): `A space is required after '{'.` | |||||
else | else | ||||
this.orderQueue.splice(i, 1); | this.orderQueue.splice(i, 1); | ||||
Engine.PostMessage(this.entity, MT_UnitAIOrderDataChanged, { "to": this.GetOrderData() }); | Engine.PostMessage(this.entity, MT_UnitAIOrderDataChanged, { "to": this.GetOrderData() }); | ||||
break; | break; | ||||
} | } | ||||
}; | }; | ||||
/** | /** | ||||
▲ Show 20 Lines • Show All 112 Lines • ▼ Show 20 Lines | UnitAI.prototype.SetupAttackRangeQuery = function(enable = true) | ||||
let range = this.GetQueryRange(IID_Attack); | let range = this.GetQueryRange(IID_Attack); | ||||
this.losAttackRangeQuery = cmpRangeManager.CreateActiveQuery(this.entity, range.min, range.max, players, IID_Resistance, cmpRangeManager.GetEntityFlagMask("normal")); | this.losAttackRangeQuery = cmpRangeManager.CreateActiveQuery(this.entity, range.min, range.max, players, IID_Resistance, cmpRangeManager.GetEntityFlagMask("normal")); | ||||
if (enable) | if (enable) | ||||
cmpRangeManager.EnableActiveQuery(this.losAttackRangeQuery); | cmpRangeManager.EnableActiveQuery(this.losAttackRangeQuery); | ||||
}; | }; | ||||
//// FSM linkage functions //// | //// FSM linkage functions //// | ||||
Lint: ESLintBear (spaced-comment) Expected space or tab after '//' in comment. Lint: ESLintBear (spaced-comment): `Expected space or tab after '//' in comment.` | |||||
// Setting the next state to the current state will leave/re-enter the top-most substate. | // Setting the next state to the current state will leave/re-enter the top-most substate. | ||||
UnitAI.prototype.SetNextState = function(state) | UnitAI.prototype.SetNextState = function(state) | ||||
{ | { | ||||
this.UnitFsm.SetNextState(this, state); | this.UnitFsm.SetNextState(this, state); | ||||
}; | }; | ||||
UnitAI.prototype.DeferMessage = function(msg) | UnitAI.prototype.DeferMessage = function(msg) | ||||
▲ Show 20 Lines • Show All 105 Lines • ▼ Show 20 Lines | |||||
/** | /** | ||||
* Add an order onto the front of the queue, | * Add an order onto the front of the queue, | ||||
* and execute it immediately. | * and execute it immediately. | ||||
*/ | */ | ||||
UnitAI.prototype.PushOrderFront = function(type, data, ignorePacking = false) | UnitAI.prototype.PushOrderFront = function(type, data, ignorePacking = false) | ||||
{ | { | ||||
var order = { "type": type, "data": data }; | var order = { "type": type, "data": data }; | ||||
// If current order is cheering then add new order after it | // If current order is packing/unpacking then add new order after it. | ||||
// same thing if current order if packing/unpacking | if (!ignorePacking && this.order && this.IsPacking()) | ||||
Not Done Inline Actions+. Freagarach: +`.` | |||||
if (this.order && this.order.type == "Cheering") | |||||
{ | |||||
var cheeringOrder = this.orderQueue.shift(); | |||||
this.orderQueue.unshift(cheeringOrder, order); | |||||
} | |||||
else if (!ignorePacking && this.order && this.IsPacking()) | |||||
{ | { | ||||
var packingOrder = this.orderQueue.shift(); | var packingOrder = this.orderQueue.shift(); | ||||
this.orderQueue.unshift(packingOrder, order); | this.orderQueue.unshift(packingOrder, order); | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
this.orderQueue.unshift(order); | this.orderQueue.unshift(order); | ||||
this.order = order; | this.order = order; | ||||
Show All 27 Lines | UnitAI.prototype.PushOrderAfterForced = function(type, data) | ||||
else | else | ||||
{ | { | ||||
for (let i = 1; i < this.orderQueue.length; ++i) | for (let i = 1; i < this.orderQueue.length; ++i) | ||||
{ | { | ||||
if (this.orderQueue[i].data && this.orderQueue[i].data.force) | if (this.orderQueue[i].data && this.orderQueue[i].data.force) | ||||
continue; | continue; | ||||
if (this.orderQueue[i].type == type) | if (this.orderQueue[i].type == type) | ||||
continue; | continue; | ||||
this.orderQueue.splice(i, 0, {"type": type, "data": data}); | this.orderQueue.splice(i, 0, {"type": type, "data": data}); | ||||
Lint: ESLintBear (object-curly-spacing) A space is required before '}'. Lint: ESLintBear (object-curly-spacing): `A space is required before '}'.` | |||||
Lint: ESLintBear (object-curly-spacing) A space is required after '{'. Lint: ESLintBear (object-curly-spacing): `A space is required after '{'.` | |||||
Engine.PostMessage(this.entity, MT_UnitAIOrderDataChanged, { "to": this.GetOrderData() }); | Engine.PostMessage(this.entity, MT_UnitAIOrderDataChanged, { "to": this.GetOrderData() }); | ||||
return; | return; | ||||
} | } | ||||
this.PushOrder(type, data); | this.PushOrder(type, data); | ||||
} | } | ||||
Engine.PostMessage(this.entity, MT_UnitAIOrderDataChanged, { "to": this.GetOrderData() }); | Engine.PostMessage(this.entity, MT_UnitAIOrderDataChanged, { "to": this.GetOrderData() }); | ||||
}; | }; | ||||
▲ Show 20 Lines • Show All 61 Lines • ▼ Show 20 Lines | if (data && data.force) | ||||
if (this.IsFormationController()) | if (this.IsFormationController()) | ||||
this.CallMemberFunction("UpdateWorkOrders", [type]); | this.CallMemberFunction("UpdateWorkOrders", [type]); | ||||
else | else | ||||
this.UpdateWorkOrders(type); | this.UpdateWorkOrders(type); | ||||
} | } | ||||
let garrisonHolder = this.IsGarrisoned() && type != "Ungarrison" ? this.GetGarrisonHolder() : null; | let garrisonHolder = this.IsGarrisoned() && type != "Ungarrison" ? this.GetGarrisonHolder() : null; | ||||
// Special cases of orders that shouldn't be replaced: | // Do not replace packing/unpacking unless it is cancel order. | ||||
Not Done Inline ActionsI think you can clear these two comments and just say something like Packing/Unpacking shouldn't be replaced unless we're explicitly doing that or something. wraitii: I think you can clear these two comments and just say something like `Packing/Unpacking… | |||||
Not Done Inline Actions(un)packing? +. Freagarach: `(un)packing`? +`.` | |||||
Not Done Inline ActionsP -> p and the dot. Freagarach: `P` -> `p` and the dot. | |||||
// 1. Cheering - we're invulnerable, add order after we finish | |||||
// 2. Packing/unpacking - we're immobile, add order after we finish (unless it's cancel) | |||||
// TODO: maybe a better way of doing this would be to use priority levels | // TODO: maybe a better way of doing this would be to use priority levels | ||||
if (this.order && this.order.type == "Cheering") | if (this.IsPacking() && type != "CancelPack" && type != "CancelUnpack") | ||||
{ | |||||
var order = { "type": type, "data": data }; | |||||
var cheeringOrder = this.orderQueue.shift(); | |||||
this.orderQueue = [cheeringOrder, order]; | |||||
} | |||||
else if (this.IsPacking() && type != "CancelPack" && type != "CancelUnpack") | |||||
{ | { | ||||
var order = { "type": type, "data": data }; | var order = { "type": type, "data": data }; | ||||
var packingOrder = this.orderQueue.shift(); | var packingOrder = this.orderQueue.shift(); | ||||
if (type == "Attack") | if (type == "Attack") | ||||
{ | { | ||||
// The Attack order is able to handle a packing unit, while other orders can't. | // The Attack order is able to handle a packing unit, while other orders can't. | ||||
this.orderQueue = [packingOrder]; | this.orderQueue = [packingOrder]; | ||||
this.PushOrderFront(type, data, true); | this.PushOrderFront(type, data, true); | ||||
Show All 38 Lines | for (let order of this.orderQueue) | ||||
if (order.data) | if (order.data) | ||||
orders.push(clone(order.data)); | orders.push(clone(order.data)); | ||||
return orders; | return orders; | ||||
}; | }; | ||||
UnitAI.prototype.UpdateWorkOrders = function(type) | UnitAI.prototype.UpdateWorkOrders = function(type) | ||||
{ | { | ||||
var isWorkType = type => type == "Gather" || type == "Trade" || type == "Repair" || type == "ReturnResource"; | var isWorkType = type => type == "Gather" || type == "Trade" || type == "Repair" || type == "ReturnResource"; | ||||
Lint: ESLintBear (no-shadow) 'type' is already declared in the upper scope. Lint: ESLintBear (no-shadow): `'type' is already declared in the upper scope.` | |||||
// If we are being re-affected to a work order, forget the previous ones | // If we are being re-affected to a work order, forget the previous ones | ||||
if (isWorkType(type)) | if (isWorkType(type)) | ||||
{ | { | ||||
this.workOrders = []; | this.workOrders = []; | ||||
return; | return; | ||||
} | } | ||||
Show All 14 Lines | if (cmpUnitAI) | ||||
this.workOrders = cmpUnitAI.orderQueue.slice(i); | this.workOrders = cmpUnitAI.orderQueue.slice(i); | ||||
return; | return; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
// If nothing found, take the unit orders | // If nothing found, take the unit orders | ||||
for (var i = 0; i < this.orderQueue.length; ++i) | for (var i = 0; i < this.orderQueue.length; ++i) | ||||
Lint: JSHintBear 'i' is already defined. Lint: JSHintBear: `'i' is already defined.` | |||||
{ | { | ||||
if (isWorkType(this.orderQueue[i].type)) | if (isWorkType(this.orderQueue[i].type)) | ||||
{ | { | ||||
this.workOrders = this.orderQueue.slice(i); | this.workOrders = this.orderQueue.slice(i); | ||||
return; | return; | ||||
} | } | ||||
} | } | ||||
}; | }; | ||||
UnitAI.prototype.BackToWork = function() | UnitAI.prototype.BackToWork = function() | ||||
{ | { | ||||
if (this.workOrders.length == 0) | if (this.workOrders.length == 0) | ||||
return false; | return false; | ||||
if (this.IsGarrisoned()) | if (this.IsGarrisoned()) | ||||
{ | { | ||||
let cmpGarrisonHolder = Engine.QueryInterface(this.GetGarrisonHolder(), IID_GarrisonHolder); | let cmpGarrisonHolder = Engine.QueryInterface(this.GetGarrisonHolder(), IID_GarrisonHolder); | ||||
if (!cmpGarrisonHolder || !cmpGarrisonHolder.PerformEject([this.entity], false)) | if (!cmpGarrisonHolder || !cmpGarrisonHolder.PerformEject([this.entity], false)) | ||||
return false; | return false; | ||||
} | } | ||||
// Clear the order queue considering special orders not to avoid | // Clear the order queue considering special orders not to avoid | ||||
if (this.order && this.order.type == "Cheering") | |||||
{ | |||||
var cheeringOrder = this.orderQueue.shift(); | |||||
this.orderQueue = [cheeringOrder]; | |||||
} | |||||
else | |||||
this.orderQueue = []; | this.orderQueue = []; | ||||
Not Done Inline Actionsthis comment can be changed too. wraitii: this comment can be changed too. | |||||
Not Done Inline ActionsComment unnecessary now. Freagarach: Comment unnecessary now. | |||||
this.AddOrders(this.workOrders); | this.AddOrders(this.workOrders); | ||||
Engine.PostMessage(this.entity, MT_UnitAIOrderDataChanged, { "to": this.GetOrderData() }); | Engine.PostMessage(this.entity, MT_UnitAIOrderDataChanged, { "to": this.GetOrderData() }); | ||||
// And if the unit is in a formation, remove it from the formation | // And if the unit is in a formation, remove it from the formation | ||||
if (this.IsFormationMember()) | if (this.IsFormationMember()) | ||||
{ | { | ||||
var cmpFormation = Engine.QueryInterface(this.formationController, IID_Formation); | var cmpFormation = Engine.QueryInterface(this.formationController, IID_Formation); | ||||
Show All 21 Lines | |||||
}; | }; | ||||
UnitAI.prototype.TimerHandler = function(data, lateness) | UnitAI.prototype.TimerHandler = function(data, lateness) | ||||
{ | { | ||||
// Reset the timer | // Reset the timer | ||||
if (data.timerRepeat === undefined) | if (data.timerRepeat === undefined) | ||||
this.timer = undefined; | this.timer = undefined; | ||||
this.UnitFsm.ProcessMessage(this, {"type": "Timer", "data": data, "lateness": lateness}); | this.UnitFsm.ProcessMessage(this, {"type": "Timer", "data": data, "lateness": lateness}); | ||||
Lint: ESLintBear (object-curly-spacing) A space is required before '}'. Lint: ESLintBear (object-curly-spacing): `A space is required before '}'.` | |||||
Lint: ESLintBear (object-curly-spacing) A space is required after '{'. Lint: ESLintBear (object-curly-spacing): `A space is required after '{'.` | |||||
}; | }; | ||||
/** | /** | ||||
* Set up the UnitAI timer to run after 'offset' msecs, and then | * Set up the UnitAI timer to run after 'offset' msecs, and then | ||||
* every 'repeat' msecs until StopTimer is called. A "Timer" message | * every 'repeat' msecs until StopTimer is called. A "Timer" message | ||||
* will be sent each time the timer runs. | * will be sent each time the timer runs. | ||||
*/ | */ | ||||
UnitAI.prototype.StartTimer = function(offset, repeat) | UnitAI.prototype.StartTimer = function(offset, repeat) | ||||
▲ Show 20 Lines • Show All 69 Lines • ▼ Show 20 Lines | UnitAI.prototype.OnGlobalEntityRenamed = function(msg) | ||||
Engine.PostMessage(this.entity, MT_UnitAIOrderDataChanged, { "to": this.GetOrderData() }); | Engine.PostMessage(this.entity, MT_UnitAIOrderDataChanged, { "to": this.GetOrderData() }); | ||||
}; | }; | ||||
UnitAI.prototype.OnAttacked = function(msg) | UnitAI.prototype.OnAttacked = function(msg) | ||||
{ | { | ||||
if (msg.fromStatusEffect) | if (msg.fromStatusEffect) | ||||
return; | return; | ||||
this.UnitFsm.ProcessMessage(this, {"type": "Attacked", "data": msg}); | this.UnitFsm.ProcessMessage(this, {"type": "Attacked", "data": msg}); | ||||
Lint: ESLintBear (object-curly-spacing) A space is required before '}'. Lint: ESLintBear (object-curly-spacing): `A space is required before '}'.` | |||||
Lint: ESLintBear (object-curly-spacing) A space is required after '{'. Lint: ESLintBear (object-curly-spacing): `A space is required after '{'.` | |||||
}; | }; | ||||
UnitAI.prototype.OnGuardedAttacked = function(msg) | UnitAI.prototype.OnGuardedAttacked = function(msg) | ||||
{ | { | ||||
this.UnitFsm.ProcessMessage(this, {"type": "GuardedAttacked", "data": msg.data}); | this.UnitFsm.ProcessMessage(this, {"type": "GuardedAttacked", "data": msg.data}); | ||||
Lint: ESLintBear (object-curly-spacing) A space is required before '}'. Lint: ESLintBear (object-curly-spacing): `A space is required before '}'.` | |||||
Lint: ESLintBear (object-curly-spacing) A space is required after '{'. Lint: ESLintBear (object-curly-spacing): `A space is required after '{'.` | |||||
}; | }; | ||||
UnitAI.prototype.OnHealthChanged = function(msg) | UnitAI.prototype.OnHealthChanged = function(msg) | ||||
{ | { | ||||
this.UnitFsm.ProcessMessage(this, {"type": "HealthChanged", "from": msg.from, "to": msg.to}); | this.UnitFsm.ProcessMessage(this, {"type": "HealthChanged", "from": msg.from, "to": msg.to}); | ||||
Lint: ESLintBear (object-curly-spacing) A space is required before '}'. Lint: ESLintBear (object-curly-spacing): `A space is required before '}'.` | |||||
Lint: ESLintBear (object-curly-spacing) A space is required after '{'. Lint: ESLintBear (object-curly-spacing): `A space is required after '{'.` | |||||
}; | }; | ||||
UnitAI.prototype.OnRangeUpdate = function(msg) | UnitAI.prototype.OnRangeUpdate = function(msg) | ||||
{ | { | ||||
if (msg.tag == this.losRangeQuery) | if (msg.tag == this.losRangeQuery) | ||||
this.UnitFsm.ProcessMessage(this, { "type": "LosRangeUpdate", "data": msg }); | this.UnitFsm.ProcessMessage(this, { "type": "LosRangeUpdate", "data": msg }); | ||||
else if (msg.tag == this.losHealRangeQuery) | else if (msg.tag == this.losHealRangeQuery) | ||||
this.UnitFsm.ProcessMessage(this, { "type": "LosHealRangeUpdate", "data": msg }); | this.UnitFsm.ProcessMessage(this, { "type": "LosHealRangeUpdate", "data": msg }); | ||||
else if (msg.tag == this.losAttackRangeQuery) | else if (msg.tag == this.losAttackRangeQuery) | ||||
this.UnitFsm.ProcessMessage(this, { "type": "LosAttackRangeUpdate", "data": msg }); | this.UnitFsm.ProcessMessage(this, { "type": "LosAttackRangeUpdate", "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}); | ||||
Lint: ESLintBear (object-curly-spacing) A space is required before '}'. Lint: ESLintBear (object-curly-spacing): `A space is required before '}'.` | |||||
Lint: ESLintBear (object-curly-spacing) A space is required after '{'. Lint: ESLintBear (object-curly-spacing): `A space is required after '{'.` | |||||
}; | }; | ||||
//// Helper functions to be called by the FSM //// | //// Helper functions to be called by the FSM //// | ||||
Lint: ESLintBear (spaced-comment) Expected space or tab after '//' in comment. Lint: ESLintBear (spaced-comment): `Expected space or tab after '//' in comment.` | |||||
UnitAI.prototype.GetWalkSpeed = function() | UnitAI.prototype.GetWalkSpeed = function() | ||||
{ | { | ||||
let cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion); | let cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion); | ||||
if (!cmpUnitMotion) | if (!cmpUnitMotion) | ||||
return 0; | return 0; | ||||
return cmpUnitMotion.GetWalkSpeed(); | return cmpUnitMotion.GetWalkSpeed(); | ||||
}; | }; | ||||
▲ Show 20 Lines • Show All 692 Lines • ▼ Show 20 Lines | |||||
/** | /** | ||||
* Try to find one of the given entities which can be attacked, | * Try to find one of the given entities which can be attacked, | ||||
* and start attacking it. | * and start attacking it. | ||||
* Returns true if it found something to attack. | * Returns true if it found something to attack. | ||||
*/ | */ | ||||
UnitAI.prototype.AttackVisibleEntity = function(ents) | UnitAI.prototype.AttackVisibleEntity = function(ents) | ||||
{ | { | ||||
var target = ents.find(target => this.CanAttack(target)); | var target = ents.find(target => this.CanAttack(target)); | ||||
Lint: ESLintBear (no-shadow) 'target' is already declared in the upper scope. Lint: ESLintBear (no-shadow): `'target' is already declared in the upper scope.` | |||||
if (!target) | if (!target) | ||||
return false; | return false; | ||||
this.PushOrderFront("Attack", { "target": target, "force": false, "allowCapture": true }); | this.PushOrderFront("Attack", { "target": target, "force": false, "allowCapture": true }); | ||||
return true; | return true; | ||||
}; | }; | ||||
/** | /** | ||||
* Try to find one of the given entities which can be attacked | * Try to find one of the given entities which can be attacked | ||||
* and which is close to the hold position, and start attacking it. | * and which is close to the hold position, and start attacking it. | ||||
* Returns true if it found something to attack. | * Returns true if it found something to attack. | ||||
*/ | */ | ||||
UnitAI.prototype.AttackEntityInZone = function(ents) | UnitAI.prototype.AttackEntityInZone = function(ents) | ||||
{ | { | ||||
var target = ents.find(target => | var target = ents.find(target => | ||||
Lint: ESLintBear (no-shadow) 'target' is already declared in the upper scope. Lint: ESLintBear (no-shadow): `'target' is already declared in the upper scope.` | |||||
this.CanAttack(target) | this.CanAttack(target) | ||||
&& this.CheckTargetDistanceFromHeldPosition(target, IID_Attack, this.GetBestAttackAgainst(target, true)) | && this.CheckTargetDistanceFromHeldPosition(target, IID_Attack, this.GetBestAttackAgainst(target, true)) | ||||
Lint: ESLintBear (operator-linebreak) '&&' should be placed at the end of the line. Lint: ESLintBear (operator-linebreak): `'&&' should be placed at the end of the line.` | |||||
Lint: JSHintBear Misleading line break before '&&'; readers may interpret this as an expression boundary. Lint: JSHintBear: `Misleading line break before '&&'; readers may interpret this as an expression boundary.` | |||||
&& (this.GetStance().respondChaseBeyondVision || this.CheckTargetIsInVisionRange(target)) | && (this.GetStance().respondChaseBeyondVision || this.CheckTargetIsInVisionRange(target)) | ||||
Lint: ESLintBear (operator-linebreak) '&&' should be placed at the end of the line. Lint: ESLintBear (operator-linebreak): `'&&' should be placed at the end of the line.` | |||||
Lint: JSHintBear Misleading line break before '&&'; readers may interpret this as an expression boundary. Lint: JSHintBear: `Misleading line break before '&&'; readers may interpret this as an expression boundary.` | |||||
); | ); | ||||
if (!target) | if (!target) | ||||
return false; | return false; | ||||
this.PushOrderFront("Attack", { "target": target, "force": false, "allowCapture": true }); | this.PushOrderFront("Attack", { "target": target, "force": false, "allowCapture": true }); | ||||
return true; | return true; | ||||
}; | }; | ||||
▲ Show 20 Lines • Show All 46 Lines • ▼ Show 20 Lines | |||||
}; | }; | ||||
/** | /** | ||||
* Try to respond to healable entities. | * Try to respond to healable entities. | ||||
* Returns true if it responded. | * Returns true if it responded. | ||||
*/ | */ | ||||
UnitAI.prototype.RespondToHealableEntities = function(ents) | UnitAI.prototype.RespondToHealableEntities = function(ents) | ||||
{ | { | ||||
let ent = ents.find(ent => this.CanHeal(ent)); | let ent = ents.find(ent => this.CanHeal(ent)); | ||||
Lint: ESLintBear (no-shadow) 'ent' is already declared in the upper scope. Lint: ESLintBear (no-shadow): `'ent' is already declared in the upper scope.` | |||||
if (!ent) | if (!ent) | ||||
return false; | return false; | ||||
this.PushOrderFront("Heal", { "target": ent, "force": false }); | this.PushOrderFront("Heal", { "target": ent, "force": false }); | ||||
return true; | return true; | ||||
}; | }; | ||||
/** | /** | ||||
Show All 9 Lines | if (force) | ||||
return false; | return false; | ||||
// If we are guarding/escorting, don't abandon as long as the guarded unit is in target range of the attacker | // If we are guarding/escorting, don't abandon as long as the guarded unit is in target range of the attacker | ||||
if (this.isGuardOf) | if (this.isGuardOf) | ||||
{ | { | ||||
let cmpUnitAI = Engine.QueryInterface(target, IID_UnitAI); | let cmpUnitAI = Engine.QueryInterface(target, IID_UnitAI); | ||||
let cmpAttack = Engine.QueryInterface(target, IID_Attack); | let cmpAttack = Engine.QueryInterface(target, IID_Attack); | ||||
if (cmpUnitAI && cmpAttack && | if (cmpUnitAI && cmpAttack && | ||||
cmpAttack.GetAttackTypes().some(type => cmpUnitAI.CheckTargetAttackRange(this.isGuardOf, type))) | cmpAttack.GetAttackTypes().some(type => cmpUnitAI.CheckTargetAttackRange(this.isGuardOf, type))) | ||||
Lint: ESLintBear (no-shadow) 'type' is already declared in the upper scope. Lint: ESLintBear (no-shadow): `'type' is already declared in the upper scope.` | |||||
return false; | return false; | ||||
Lint: ESLintBear (indent) Expected indentation of 3 tabs but found 4. Lint: ESLintBear (indent): `Expected indentation of 3 tabs but found 4.` | |||||
} | } | ||||
// Stop if we're in hold-ground mode and it's too far from the holding point | // Stop if we're in hold-ground mode and it's too far from the holding point | ||||
if (this.GetStance().respondHoldGround) | if (this.GetStance().respondHoldGround) | ||||
if (!this.CheckTargetDistanceFromHeldPosition(target, iid, type)) | if (!this.CheckTargetDistanceFromHeldPosition(target, iid, type)) | ||||
return true; | return true; | ||||
// Stop if it's left our vision range, unless we're especially persistent. | // Stop if it's left our vision range, unless we're especially persistent. | ||||
Show All 14 Lines | if (!this.AbleToMove()) | ||||
return false; | return false; | ||||
if (this.GetStance().respondChase) | if (this.GetStance().respondChase) | ||||
return true; | return true; | ||||
// If we are guarding/escorting, chase at least as long as the guarded unit is in target range of the attacker | // If we are guarding/escorting, chase at least as long as the guarded unit is in target range of the attacker | ||||
if (this.isGuardOf) | if (this.isGuardOf) | ||||
{ | { | ||||
let cmpUnitAI = Engine.QueryInterface(target, IID_UnitAI); | let cmpUnitAI = Engine.QueryInterface(target, IID_UnitAI); | ||||
Lint: ESLintBear (no-multi-spaces) Multiple spaces found before 'Engine'. Lint: ESLintBear (no-multi-spaces): `Multiple spaces found before 'Engine'.` | |||||
let cmpAttack = Engine.QueryInterface(target, IID_Attack); | let cmpAttack = Engine.QueryInterface(target, IID_Attack); | ||||
if (cmpUnitAI && cmpAttack && | if (cmpUnitAI && cmpAttack && | ||||
cmpAttack.GetAttackTypes().some(type => cmpUnitAI.CheckTargetAttackRange(this.isGuardOf, type))) | cmpAttack.GetAttackTypes().some(type => cmpUnitAI.CheckTargetAttackRange(this.isGuardOf, type))) | ||||
return true; | return true; | ||||
} | } | ||||
return force; | return force; | ||||
}; | }; | ||||
//// External interface functions //// | //// External interface functions //// | ||||
Lint: ESLintBear (spaced-comment) Expected space or tab after '//' in comment. Lint: ESLintBear (spaced-comment): `Expected space or tab after '//' in comment.` | |||||
UnitAI.prototype.SetFormationController = function(ent) | UnitAI.prototype.SetFormationController = function(ent) | ||||
{ | { | ||||
this.formationController = ent; | this.formationController = ent; | ||||
// Set obstruction group, so we can walk through members | // Set obstruction group, so we can walk through members | ||||
// of our own formation (or ourself if not in formation) | // of our own formation (or ourself if not in formation) | ||||
var cmpObstruction = Engine.QueryInterface(this.entity, IID_Obstruction); | var cmpObstruction = Engine.QueryInterface(this.entity, IID_Obstruction); | ||||
▲ Show 20 Lines • Show All 140 Lines • ▼ Show 20 Lines | UnitAI.prototype.Guard = function(target, queued) | ||||
// if we already had an old guard order, do nothing if the target is the same | // if we already had an old guard order, do nothing if the target is the same | ||||
// and the order is running, otherwise remove the previous order | // and the order is running, otherwise remove the previous order | ||||
if (this.isGuardOf) | if (this.isGuardOf) | ||||
{ | { | ||||
if (this.isGuardOf == target && this.order && this.order.type == "Guard") | if (this.isGuardOf == target && this.order && this.order.type == "Guard") | ||||
return; | return; | ||||
else | else | ||||
this.RemoveGuard(); | this.RemoveGuard(); | ||||
Lint: ESLintBear (no-else-return) Unnecessary 'else' after 'return'. Lint: ESLintBear (no-else-return): `Unnecessary 'else' after 'return'.` | |||||
} | } | ||||
this.AddOrder("Guard", { "target": target, "force": false }, queued); | this.AddOrder("Guard", { "target": target, "force": false }, queued); | ||||
}; | }; | ||||
UnitAI.prototype.AddGuard = function(target) | UnitAI.prototype.AddGuard = function(target) | ||||
{ | { | ||||
if (!this.CanGuard()) | if (!this.CanGuard()) | ||||
▲ Show 20 Lines • Show All 316 Lines • ▼ Show 20 Lines | |||||
{ | { | ||||
let cmpTrader = Engine.QueryInterface(this.entity, IID_Trader); | let cmpTrader = Engine.QueryInterface(this.entity, IID_Trader); | ||||
if (!cmpTrader) | if (!cmpTrader) | ||||
return; | return; | ||||
cmpTrader.RemoveTargetMarket(target); | cmpTrader.RemoveTargetMarket(target); | ||||
if (this.IsFormationController()) | if (this.IsFormationController()) | ||||
this.CallMemberFunction("CancelSetupTradeRoute", [target]); | this.CallMemberFunction("CancelSetupTradeRoute", [target]); | ||||
} | } | ||||
Lint: ESLintBear (semi) Missing semicolon. Lint: ESLintBear (semi): `Missing semicolon.` | |||||
Lint: JSHintBear Missing semicolon. Lint: JSHintBear: `Missing semicolon.` | |||||
/** | /** | ||||
* Adds trade order to the queue. Either walk to the first market, or | * Adds trade order to the queue. Either walk to the first market, or | ||||
* start a new route. Not forced, so it can be interrupted by attacks. | * start a new route. Not forced, so it can be interrupted by attacks. | ||||
* The possible route may be given directly as a SetupTradeRoute argument | * The possible route may be given directly as a SetupTradeRoute argument | ||||
* if coming from a RallyPoint, or through this.expectedRoute if a user command. | * if coming from a RallyPoint, or through this.expectedRoute if a user command. | ||||
*/ | */ | ||||
UnitAI.prototype.SetupTradeRoute = function(target, source, route, queued) | UnitAI.prototype.SetupTradeRoute = function(target, source, route, queued) | ||||
{ | { | ||||
if (!this.CanTrade(target)) | if (!this.CanTrade(target)) | ||||
{ | { | ||||
this.WalkToTarget(target, queued); | this.WalkToTarget(target, queued); | ||||
return; | return; | ||||
} | } | ||||
// AI has currently no access to BackToWork | // AI has currently no access to BackToWork | ||||
let cmpPlayer = QueryOwnerInterface(this.entity); | let cmpPlayer = QueryOwnerInterface(this.entity); | ||||
if (cmpPlayer && cmpPlayer.IsAI() && !this.IsFormationController() && | if (cmpPlayer && cmpPlayer.IsAI() && !this.IsFormationController() && | ||||
this.workOrders.length && this.workOrders[0].type == "Trade") | this.workOrders.length && this.workOrders[0].type == "Trade") | ||||
{ | { | ||||
let cmpTrader = Engine.QueryInterface(this.entity, IID_Trader); | let cmpTrader = Engine.QueryInterface(this.entity, IID_Trader); | ||||
if (cmpTrader.HasBothMarkets() && | if (cmpTrader.HasBothMarkets() && | ||||
Lint: ESLintBear (no-trailing-spaces) Trailing spaces not allowed. Lint: ESLintBear (no-trailing-spaces): `Trailing spaces not allowed.` | |||||
(cmpTrader.GetFirstMarket() == target && cmpTrader.GetSecondMarket() == source || | (cmpTrader.GetFirstMarket() == target && cmpTrader.GetSecondMarket() == source || | ||||
cmpTrader.GetFirstMarket() == source && cmpTrader.GetSecondMarket() == target)) | cmpTrader.GetFirstMarket() == source && cmpTrader.GetSecondMarket() == target)) | ||||
{ | { | ||||
this.BackToWork(); | this.BackToWork(); | ||||
return; | return; | ||||
} | } | ||||
} | } | ||||
▲ Show 20 Lines • Show All 140 Lines • ▼ Show 20 Lines | |||||
/** | /** | ||||
* Adds flee order to the queue, not forced, so it can be | * Adds flee order to the queue, not forced, so it can be | ||||
* interrupted by attacks. | * interrupted by attacks. | ||||
*/ | */ | ||||
UnitAI.prototype.Flee = function(target, queued) | UnitAI.prototype.Flee = function(target, queued) | ||||
{ | { | ||||
this.AddOrder("Flee", { "target": target, "force": false }, queued); | this.AddOrder("Flee", { "target": target, "force": false }, queued); | ||||
}; | }; | ||||
/** | |||||
* Pushes a cheer order to the front of the queue. Forced so it won't be interrupted by attacks. | |||||
*/ | |||||
UnitAI.prototype.Cheer = function() | UnitAI.prototype.Cheer = function() | ||||
Not Done Inline ActionsThis could be made more generic perhaps? (To also allow group behavious such as a pack of wild animals attacking instead of being slaughtered one by one.) Freagarach: This could be made more generic perhaps? (To also allow group behavious such as a pack of wild… | |||||
{ | { | ||||
this.PushOrderFront("Cheering", { "force": true }); | this.PushOrderFront("Cheer", { "force": false }); | ||||
}; | }; | ||||
UnitAI.prototype.Pack = function(queued) | UnitAI.prototype.Pack = function(queued) | ||||
{ | { | ||||
Not Done Inline Actions// Keep that range small so units within reasonable distance cheer.? Freagarach: `// Keep that range small so units within reasonable distance cheer.`? | |||||
// Check that we can pack | // Check that we can pack | ||||
if (this.CanPack()) | if (this.CanPack()) | ||||
Not Done Inline ActionsInline? Freagarach: Inline? | |||||
this.AddOrder("Pack", { "force": true }, queued); | this.AddOrder("Pack", { "force": true }, queued); | ||||
Not Done Inline Actions+; Freagarach: +`;` | |||||
}; | }; | ||||
UnitAI.prototype.Unpack = function(queued) | UnitAI.prototype.Unpack = function(queued) | ||||
{ | { | ||||
// Check that we can unpack | // Check that we can unpack | ||||
if (this.CanUnpack()) | if (this.CanUnpack()) | ||||
this.AddOrder("Unpack", { "force": true }, queued); | this.AddOrder("Unpack", { "force": true }, queued); | ||||
}; | }; | ||||
▲ Show 20 Lines • Show All 121 Lines • ▼ Show 20 Lines | for (var ent of cmpFormation.members) | ||||
{ | { | ||||
if (!cmpUnitAI.CanAttack(targ)) | if (!cmpUnitAI.CanAttack(targ)) | ||||
continue; | continue; | ||||
if (this.order.data.targetClasses) | if (this.order.data.targetClasses) | ||||
{ | { | ||||
var cmpIdentity = Engine.QueryInterface(targ, IID_Identity); | var cmpIdentity = Engine.QueryInterface(targ, IID_Identity); | ||||
var targetClasses = this.order.data.targetClasses; | var targetClasses = this.order.data.targetClasses; | ||||
if (targetClasses.attack && cmpIdentity | if (targetClasses.attack && cmpIdentity | ||||
&& !MatchesClassList(cmpIdentity.GetClassesList(), targetClasses.attack)) | && !MatchesClassList(cmpIdentity.GetClassesList(), targetClasses.attack)) | ||||
Lint: JSHintBear Misleading line break before '&&'; readers may interpret this as an expression boundary. Lint: JSHintBear: `Misleading line break before '&&'; readers may interpret this as an expression boundary.` | |||||
Lint: ESLintBear (operator-linebreak) '&&' should be placed at the end of the line. Lint: ESLintBear (operator-linebreak): `'&&' should be placed at the end of the line.` | |||||
continue; | continue; | ||||
if (targetClasses.avoid && cmpIdentity | if (targetClasses.avoid && cmpIdentity | ||||
&& MatchesClassList(cmpIdentity.GetClassesList(), targetClasses.avoid)) | && MatchesClassList(cmpIdentity.GetClassesList(), targetClasses.avoid)) | ||||
Lint: JSHintBear Misleading line break before '&&'; readers may interpret this as an expression boundary. Lint: JSHintBear: `Misleading line break before '&&'; readers may interpret this as an expression boundary.` | |||||
Lint: ESLintBear (operator-linebreak) '&&' should be placed at the end of the line. Lint: ESLintBear (operator-linebreak): `'&&' should be placed at the end of the line.` | |||||
continue; | continue; | ||||
// Only used by the AIs to prevent some choices of targets | // Only used by the AIs to prevent some choices of targets | ||||
if (targetClasses.vetoEntities && targetClasses.vetoEntities[targ]) | if (targetClasses.vetoEntities && targetClasses.vetoEntities[targ]) | ||||
continue; | continue; | ||||
} | } | ||||
this.PushOrderFront("Attack", { "target": targ, "force": false, "allowCapture": this.order.data.allowCapture }); | this.PushOrderFront("Attack", { "target": targ, "force": false, "allowCapture": this.order.data.allowCapture }); | ||||
return true; | return true; | ||||
} | } | ||||
} | } | ||||
return false; | return false; | ||||
} | } | ||||
var targets = this.GetTargetsFromUnit(); | var targets = this.GetTargetsFromUnit(); | ||||
Lint: JSHintBear 'targets' is already defined. Lint: JSHintBear: `'targets' is already defined.` | |||||
for (var targ of targets) | for (var targ of targets) | ||||
Lint: JSHintBear 'targ' is already defined. Lint: JSHintBear: `'targ' is already defined.` | |||||
{ | { | ||||
if (!this.CanAttack(targ)) | if (!this.CanAttack(targ)) | ||||
continue; | continue; | ||||
if (this.order.data.targetClasses) | if (this.order.data.targetClasses) | ||||
{ | { | ||||
var cmpIdentity = Engine.QueryInterface(targ, IID_Identity); | var cmpIdentity = Engine.QueryInterface(targ, IID_Identity); | ||||
Lint: JSHintBear 'cmpIdentity' is already defined. Lint: JSHintBear: `'cmpIdentity' is already defined.` | |||||
var targetClasses = this.order.data.targetClasses; | var targetClasses = this.order.data.targetClasses; | ||||
Lint: JSHintBear 'targetClasses' is already defined. Lint: JSHintBear: `'targetClasses' is already defined.` | |||||
if (cmpIdentity && targetClasses.attack | if (cmpIdentity && targetClasses.attack | ||||
&& !MatchesClassList(cmpIdentity.GetClassesList(), targetClasses.attack)) | && !MatchesClassList(cmpIdentity.GetClassesList(), targetClasses.attack)) | ||||
Lint: JSHintBear Misleading line break before '&&'; readers may interpret this as an expression boundary. Lint: JSHintBear: `Misleading line break before '&&'; readers may interpret this as an expression boundary.` | |||||
Lint: ESLintBear (operator-linebreak) '&&' should be placed at the end of the line. Lint: ESLintBear (operator-linebreak): `'&&' should be placed at the end of the line.` | |||||
continue; | continue; | ||||
if (cmpIdentity && targetClasses.avoid | if (cmpIdentity && targetClasses.avoid | ||||
&& MatchesClassList(cmpIdentity.GetClassesList(), targetClasses.avoid)) | && MatchesClassList(cmpIdentity.GetClassesList(), targetClasses.avoid)) | ||||
Lint: JSHintBear Misleading line break before '&&'; readers may interpret this as an expression boundary. Lint: JSHintBear: `Misleading line break before '&&'; readers may interpret this as an expression boundary.` | |||||
Lint: ESLintBear (operator-linebreak) '&&' should be placed at the end of the line. Lint: ESLintBear (operator-linebreak): `'&&' should be placed at the end of the line.` | |||||
continue; | continue; | ||||
// Only used by the AIs to prevent some choices of targets | // Only used by the AIs to prevent some choices of targets | ||||
if (targetClasses.vetoEntities && targetClasses.vetoEntities[targ]) | if (targetClasses.vetoEntities && targetClasses.vetoEntities[targ]) | ||||
continue; | continue; | ||||
} | } | ||||
this.PushOrderFront("Attack", { "target": targ, "force": false, "allowCapture": this.order.data.allowCapture }); | this.PushOrderFront("Attack", { "target": targ, "force": false, "allowCapture": this.order.data.allowCapture }); | ||||
return true; | return true; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 135 Lines • ▼ Show 20 Lines | if (!orderData) | ||||
orderData = this.order.data; | orderData = this.order.data; | ||||
let cmpPosition = Engine.QueryInterface(orderData.target, IID_Position); | let cmpPosition = Engine.QueryInterface(orderData.target, IID_Position); | ||||
if (cmpPosition && cmpPosition.IsInWorld()) | if (cmpPosition && cmpPosition.IsInWorld()) | ||||
orderData.lastPos = cmpPosition.GetPosition(); | orderData.lastPos = cmpPosition.GetPosition(); | ||||
}; | }; | ||||
UnitAI.prototype.SetHeldPosition = function(x, z) | UnitAI.prototype.SetHeldPosition = function(x, z) | ||||
{ | { | ||||
this.heldPosition = {"x": x, "z": z}; | this.heldPosition = {"x": x, "z": z}; | ||||
Lint: ESLintBear (object-curly-spacing) A space is required before '}'. Lint: ESLintBear (object-curly-spacing): `A space is required before '}'.` | |||||
Lint: ESLintBear (object-curly-spacing) A space is required after '{'. Lint: ESLintBear (object-curly-spacing): `A space is required after '{'.` | |||||
}; | }; | ||||
UnitAI.prototype.SetHeldPositionOnEntity = function(entity) | UnitAI.prototype.SetHeldPositionOnEntity = function(entity) | ||||
{ | { | ||||
var cmpPosition = Engine.QueryInterface(this.entity, IID_Position); | var cmpPosition = Engine.QueryInterface(this.entity, IID_Position); | ||||
if (!cmpPosition || !cmpPosition.IsInWorld()) | if (!cmpPosition || !cmpPosition.IsInWorld()) | ||||
return; | return; | ||||
var pos = cmpPosition.GetPosition(); | var pos = cmpPosition.GetPosition(); | ||||
Show All 10 Lines | UnitAI.prototype.WalkToHeldPosition = function() | ||||
if (this.heldPosition) | if (this.heldPosition) | ||||
{ | { | ||||
this.AddOrder("Walk", { "x": this.heldPosition.x, "z": this.heldPosition.z, "force": false }, false); | this.AddOrder("Walk", { "x": this.heldPosition.x, "z": this.heldPosition.z, "force": false }, false); | ||||
return true; | return true; | ||||
} | } | ||||
return false; | return false; | ||||
}; | }; | ||||
//// Helper functions //// | //// Helper functions //// | ||||
Lint: ESLintBear (spaced-comment) Expected space or tab after '//' in comment. Lint: ESLintBear (spaced-comment): `Expected space or tab after '//' in comment.` | |||||
/** | /** | ||||
* General getter for ranges. | * General getter for ranges. | ||||
* | * | ||||
* @param {number} iid | * @param {number} iid | ||||
* @param {string} type - [Optional] | * @param {string} type - [Optional] | ||||
* @return {Object | undefined} - The range in the form | * @return {Object | undefined} - The range in the form | ||||
* { "min": number, "max": number } | * { "min": number, "max": number } | ||||
* Object."elevationBonus": number may be present when iid == IID_Attack. | * Object."elevationBonus": number may be present when iid == IID_Attack. | ||||
* Returns undefined when the entity does not have the requested component. | * Returns undefined when the entity does not have the requested component. | ||||
*/ | */ | ||||
UnitAI.prototype.GetRange = function(iid, type) | UnitAI.prototype.GetRange = function(iid, type) | ||||
{ | { | ||||
let component = Engine.QueryInterface(this.entity, iid); | let component = Engine.QueryInterface(this.entity, iid); | ||||
if (!component) | if (!component) | ||||
return undefined; | return undefined; | ||||
return component.GetRange(type); | return component.GetRange(type); | ||||
} | } | ||||
Lint: JSHintBear Missing semicolon. Lint: JSHintBear: `Missing semicolon.` | |||||
Lint: ESLintBear (semi) Missing semicolon. Lint: ESLintBear (semi): `Missing semicolon.` | |||||
UnitAI.prototype.CanAttack = function(target) | UnitAI.prototype.CanAttack = function(target) | ||||
{ | { | ||||
// Formation controllers should always respond to commands | // Formation controllers should always respond to commands | ||||
// (then the individual units can make up their own minds) | // (then the individual units can make up their own minds) | ||||
if (this.IsFormationController()) | if (this.IsFormationController()) | ||||
return true; | return true; | ||||
▲ Show 20 Lines • Show All 159 Lines • ▼ Show 20 Lines | |||||
}; | }; | ||||
UnitAI.prototype.IsPacking = function() | UnitAI.prototype.IsPacking = function() | ||||
{ | { | ||||
var cmpPack = Engine.QueryInterface(this.entity, IID_Pack); | var cmpPack = Engine.QueryInterface(this.entity, IID_Pack); | ||||
return cmpPack && cmpPack.IsPacking(); | return cmpPack && cmpPack.IsPacking(); | ||||
}; | }; | ||||
//// Formation specific functions //// | //// Formation specific functions //// | ||||
Lint: ESLintBear (spaced-comment) Expected space or tab after '//' in comment. Lint: ESLintBear (spaced-comment): `Expected space or tab after '//' in comment.` | |||||
UnitAI.prototype.IsAttackingAsFormation = function() | UnitAI.prototype.IsAttackingAsFormation = function() | ||||
{ | { | ||||
var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack); | var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack); | ||||
return cmpAttack && cmpAttack.CanAttackAsFormation() | return cmpAttack && cmpAttack.CanAttackAsFormation() | ||||
&& this.GetCurrentState() == "FORMATIONCONTROLLER.COMBAT.ATTACKING"; | && this.GetCurrentState() == "FORMATIONCONTROLLER.COMBAT.ATTACKING"; | ||||
Lint: JSHintBear Misleading line break before '&&'; readers may interpret this as an expression boundary. Lint: JSHintBear: `Misleading line break before '&&'; readers may interpret this as an expression boundary.` | |||||
Lint: ESLintBear (operator-linebreak) '&&' should be placed at the end of the line. Lint: ESLintBear (operator-linebreak): `'&&' should be placed at the end of the line.` | |||||
}; | }; | ||||
//// Animal specific functions //// | //// Animal specific functions //// | ||||
Lint: ESLintBear (spaced-comment) Expected space or tab after '//' in comment. Lint: ESLintBear (spaced-comment): `Expected space or tab after '//' in comment.` | |||||
UnitAI.prototype.MoveRandomly = function(distance) | UnitAI.prototype.MoveRandomly = function(distance) | ||||
{ | { | ||||
// To minimize drift all across the map, animals describe circles | // To minimize drift all across the map, animals describe circles | ||||
// approximated by polygons. | // approximated by polygons. | ||||
// And to avoid getting stuck in obstacles or narrow spaces, each side | // And to avoid getting stuck in obstacles or narrow spaces, each side | ||||
// of the polygon is obtained by trying to go away from a point situated | // of the polygon is obtained by trying to go away from a point situated | ||||
// half a meter backwards of the current position, after rotation. | // half a meter backwards of the current position, after rotation. | ||||
Show All 34 Lines | UnitAI.prototype.SetFacePointAfterMove = function(val) | ||||
if (cmpMotion) | if (cmpMotion) | ||||
cmpMotion.SetFacePointAfterMove(val); | cmpMotion.SetFacePointAfterMove(val); | ||||
}; | }; | ||||
UnitAI.prototype.GetFacePointAfterMove = function() | UnitAI.prototype.GetFacePointAfterMove = function() | ||||
{ | { | ||||
let cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion); | let cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion); | ||||
return cmpUnitMotion && cmpUnitMotion.GetFacePointAfterMove(); | return cmpUnitMotion && cmpUnitMotion.GetFacePointAfterMove(); | ||||
} | } | ||||
Lint: JSHintBear Missing semicolon. Lint: JSHintBear: `Missing semicolon.` | |||||
Lint: ESLintBear (semi) Missing semicolon. Lint: ESLintBear (semi): `Missing semicolon.` | |||||
UnitAI.prototype.AttackEntitiesByPreference = function(ents) | UnitAI.prototype.AttackEntitiesByPreference = function(ents) | ||||
{ | { | ||||
if (!ents.length) | if (!ents.length) | ||||
return false; | return false; | ||||
var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack); | var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack); | ||||
if (!cmpAttack) | if (!cmpAttack) | ||||
▲ Show 20 Lines • Show All 56 Lines • ▼ Show 20 Lines | UnitAI.prototype.CallMemberFunction = function(funcname, args, resetWaitingEntities = true) | ||||
cmpFormation.GetMembers().forEach(ent => { | cmpFormation.GetMembers().forEach(ent => { | ||||
let cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI); | let cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI); | ||||
cmpUnitAI[funcname].apply(cmpUnitAI, args); | cmpUnitAI[funcname].apply(cmpUnitAI, args); | ||||
}); | }); | ||||
}; | }; | ||||
/** | /** | ||||
* Call obj.funcname(args) on UnitAI components owned by player in given range. | |||||
*/ | |||||
UnitAI.prototype.CallPlayerOwnedEntitiesFunctionInRange = function(funcname, args, range) | |||||
Not Done Inline Actionsfriendly Freagarach: `friendly` | |||||
{ | |||||
let cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership); | |||||
if (!cmpOwnership) | |||||
return; | |||||
let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); | |||||
let nearby = cmpRangeManager.ExecuteQuery(this.entity, 0, range, [cmpOwnership.GetOwner()], IID_UnitAI); | |||||
Not Done Inline ActionsFriendly != owned. Freagarach: Friendly != owned.
So either the function ought to be renamed or we should be able to specify… | |||||
Not Done Inline ActionsCan owner be INVALID_PLAYER? Stan: Can owner be INVALID_PLAYER? | |||||
for (let i = 0; i < nearby.length; ++i) | |||||
Not Done Inline Actions\n Freagarach: `\n` | |||||
FreagarachUnsubmitted Not Done Inline Actionsfor (let ent of nearby)? Freagarach: `for (let ent of nearby)`? | |||||
Not Done Inline Actions^ Freagarach: ^ | |||||
{ | |||||
let cmpUnitAI = Engine.QueryInterface(nearby[i], IID_UnitAI); | |||||
cmpUnitAI[funcname].apply(cmpUnitAI, args); | |||||
} | |||||
}; | |||||
/** | |||||
* Call obj.functname(args) on UnitAI components of all formation members, | * Call obj.functname(args) on UnitAI components of all formation members, | ||||
* and return true if all calls return true. | * and return true if all calls return true. | ||||
*/ | */ | ||||
UnitAI.prototype.TestAllMemberFunction = function(funcname, args) | UnitAI.prototype.TestAllMemberFunction = function(funcname, args) | ||||
{ | { | ||||
var cmpFormation = Engine.QueryInterface(this.entity, IID_Formation); | var cmpFormation = Engine.QueryInterface(this.entity, IID_Formation); | ||||
if (!cmpFormation) | if (!cmpFormation) | ||||
return false; | return false; | ||||
Show All 10 Lines |
Capital n?