Changeset View
Standalone View
binaries/data/mods/public/simulation/components/UnitAI.js
function UnitAI() {} | function UnitAI() {} | ||||
UnitAI.prototype.Schema = | UnitAI.prototype.Schema = | ||||
"<a:help>Controls the unit's movement, attacks, etc, in response to commands from the player.</a:help>" + | "<a:help>Controls the unit's movement, attacks, etc, in response to commands from the player.</a:help>" + | ||||
"<a:example/>" + | "<a:example/>" + | ||||
"<element name='DefaultStance'>" + | "<element name='DefaultStance'>" + | ||||
"<choice>" + | "<choice>" + | ||||
"<value>violent</value>" + | "<value>violent</value>" + | ||||
"<value>aggressive</value>" + | "<value>aggressive</value>" + | ||||
"<value>defensive</value>" + | "<value>defensive</value>" + | ||||
"<value>passive</value>" + | "<value>passive</value>" + | ||||
"<value>standground</value>" + | "<value>standground</value>" + | ||||
"<value>skittish</value>" + | |||||
"<value>passive-defensive</value>" + | |||||
Nescio: What does this mean? And why is it listed at the bottom? | |||||
Not Done Inline ActionsThis means that the entity will retaliate but not initiate in any attack. It list listed at the bottom because I added it lastly ^^ Freagarach: This means that the entity will retaliate but not initiate in any attack. It list listed at the… | |||||
Not Done Inline ActionsThe idea is that if you don't bother it, it won't bother you. wraitii: The idea is that if you don't bother it, it won't bother you.
I feel like English must have a… | |||||
Not Done Inline ActionsTechnically it's neutral, one might also use indifferent https://www.synonyms.com/synonym/Neutral#:~:text=Princeton%27s%20WordNet%20%281.86%20%2F%2013%20votes%29Rate%20these%20synonyms%3A,impersonal%2C%20apathetic%2C%20electroneutral%2C%20deaf%20...%20More%20items...%20 Stan: Technically it's neutral, one might also use indifferent https://www.synonyms. | |||||
"</choice>" + | "</choice>" + | ||||
"</element>" + | "</element>" + | ||||
"<element name='FormationController'>" + | "<element name='FormationController'>" + | ||||
"<data type='boolean'/>" + | "<data type='boolean'/>" + | ||||
"</element>" + | "</element>" + | ||||
"<element name='FleeDistance'>" + | "<element name='FleeDistance'>" + | ||||
"<ref name='positiveDecimal'/>" + | "<ref name='positiveDecimal'/>" + | ||||
"</element>" + | "</element>" + | ||||
"<element name='CanGuard'>" + | "<element name='CanGuard'>" + | ||||
"<data type='boolean'/>" + | "<data type='boolean'/>" + | ||||
"</element>" + | "</element>" + | ||||
"<element name='CanPatrol'>" + | "<element name='CanPatrol'>" + | ||||
"<data type='boolean'/>" + | "<data type='boolean'/>" + | ||||
"</element>" + | "</element>" + | ||||
"<optional>" + | "<optional>" + | ||||
"<interleave>" + | "<interleave>" + | ||||
"<element name='NaturalBehaviour' a:help='Behaviour of the unit in the absence of player commands (intended for animals)'>" + | |||||
"<choice>" + | |||||
"<value a:help='Will actively attack any unit it encounters, even if not threatened'>violent</value>" + | |||||
"<value a:help='Will attack nearby units if it feels threatened (if they linger within LOS for too long)'>aggressive</value>" + | |||||
"<value a:help='Will attack nearby units if attacked'>defensive</value>" + | |||||
"<value a:help='Will never attack units but will attempt to flee when attacked'>passive</value>" + | |||||
"<value a:help='Will never attack units. Will typically attempt to flee for short distances when units approach'>skittish</value>" + | |||||
"<value a:help='Will never attack units and will not attempt to flee when attacked'>domestic</value>" + | |||||
"</choice>" + | |||||
"</element>" + | |||||
"<element name='RoamDistance'>" + | "<element name='RoamDistance'>" + | ||||
"<ref name='positiveDecimal'/>" + | "<ref name='positiveDecimal'/>" + | ||||
"</element>" + | "</element>" + | ||||
"<element name='RoamTimeMin'>" + | "<element name='RoamTimeMin'>" + | ||||
"<ref name='positiveDecimal'/>" + | "<ref name='positiveDecimal'/>" + | ||||
"</element>" + | "</element>" + | ||||
"<element name='RoamTimeMax'>" + | "<element name='RoamTimeMax'>" + | ||||
"<ref name='positiveDecimal'/>" + | "<ref name='positiveDecimal'/>" + | ||||
Show All 9 Lines | |||||
// Unit stances. | // Unit stances. | ||||
// There some targeting options: | // There some targeting options: | ||||
// targetVisibleEnemies: anything in vision range is a viable target | // targetVisibleEnemies: anything in vision range is a viable target | ||||
// targetAttackersAlways: anything that hurts us is a viable target, | // targetAttackersAlways: anything that hurts us is a viable target, | ||||
// possibly overriding user orders! | // possibly overriding user orders! | ||||
// There are some response options, triggered when targets are detected: | // There are some response options, triggered when targets are detected: | ||||
// respondFlee: run away | // respondFlee: run away | ||||
// respondFleeOnSight: run away when an enemy is sighted | |||||
// respondChase: start chasing after the enemy | // respondChase: start chasing after the enemy | ||||
// respondChaseBeyondVision: start chasing, and don't stop even if it's out | // respondChaseBeyondVision: start chasing, and don't stop even if it's out | ||||
// of this unit's vision range (though still visible to the player) | // of this unit's vision range (though still visible to the player) | ||||
// respondStandGround: attack enemy but don't move at all | // respondStandGround: attack enemy but don't move at all | ||||
// respondHoldGround: attack enemy but don't move far from current position | // respondHoldGround: attack enemy but don't move far from current position | ||||
// TODO: maybe add targetAggressiveEnemies (don't worry about lone scouts, | // TODO: maybe add targetAggressiveEnemies (don't worry about lone scouts, | ||||
// do worry around armies slaughtering the guy standing next to you), etc. | // do worry around armies slaughtering the guy standing next to you), etc. | ||||
var g_Stances = { | var g_Stances = { | ||||
"violent": { | "violent": { | ||||
"targetVisibleEnemies": true, | "targetVisibleEnemies": true, | ||||
"targetAttackersAlways": true, | "targetAttackersAlways": true, | ||||
"respondFlee": false, | "respondFlee": false, | ||||
"respondFleeOnSight": false, | |||||
"respondChase": true, | "respondChase": true, | ||||
"respondChaseBeyondVision": true, | "respondChaseBeyondVision": true, | ||||
"respondStandGround": false, | "respondStandGround": false, | ||||
"respondHoldGround": false, | "respondHoldGround": false, | ||||
"selectable": true | "selectable": true | ||||
}, | }, | ||||
"aggressive": { | "aggressive": { | ||||
"targetVisibleEnemies": true, | "targetVisibleEnemies": true, | ||||
"targetAttackersAlways": false, | "targetAttackersAlways": false, | ||||
"respondFlee": false, | "respondFlee": false, | ||||
"respondFleeOnSight": false, | |||||
"respondChase": true, | "respondChase": true, | ||||
"respondChaseBeyondVision": false, | "respondChaseBeyondVision": false, | ||||
"respondStandGround": false, | "respondStandGround": false, | ||||
"respondHoldGround": false, | "respondHoldGround": false, | ||||
"selectable": true | "selectable": true | ||||
}, | }, | ||||
"defensive": { | "defensive": { | ||||
"targetVisibleEnemies": true, | "targetVisibleEnemies": true, | ||||
"targetAttackersAlways": false, | "targetAttackersAlways": false, | ||||
"respondFlee": false, | "respondFlee": false, | ||||
"respondFleeOnSight": false, | |||||
"respondChase": false, | "respondChase": false, | ||||
"respondChaseBeyondVision": false, | "respondChaseBeyondVision": false, | ||||
"respondStandGround": false, | "respondStandGround": false, | ||||
"respondHoldGround": true, | "respondHoldGround": true, | ||||
"selectable": true | "selectable": true | ||||
}, | }, | ||||
"passive": { | "passive": { | ||||
"targetVisibleEnemies": false, | "targetVisibleEnemies": false, | ||||
"targetAttackersAlways": false, | "targetAttackersAlways": false, | ||||
"respondFlee": true, | "respondFlee": true, | ||||
"respondFleeOnSight": false, | |||||
"respondChase": false, | "respondChase": false, | ||||
"respondChaseBeyondVision": false, | "respondChaseBeyondVision": false, | ||||
"respondStandGround": false, | "respondStandGround": false, | ||||
"respondHoldGround": false, | "respondHoldGround": false, | ||||
"selectable": true | "selectable": true | ||||
}, | }, | ||||
"standground": { | "standground": { | ||||
"targetVisibleEnemies": true, | "targetVisibleEnemies": true, | ||||
"targetAttackersAlways": false, | "targetAttackersAlways": false, | ||||
"respondFlee": false, | "respondFlee": false, | ||||
"respondFleeOnSight": false, | |||||
"respondChase": false, | "respondChase": false, | ||||
"respondChaseBeyondVision": false, | "respondChaseBeyondVision": false, | ||||
"respondStandGround": true, | "respondStandGround": true, | ||||
"respondHoldGround": false, | "respondHoldGround": false, | ||||
"selectable": true | "selectable": true | ||||
}, | }, | ||||
"skittish": { | |||||
"targetVisibleEnemies": false, | |||||
"targetAttackersAlways": false, | |||||
"respondFlee": true, | |||||
"respondFleeOnSight": true, | |||||
"respondChase": false, | |||||
"respondChaseBeyondVision": false, | |||||
"respondStandGround": false, | |||||
"respondHoldGround": false, | |||||
"selectable": false | |||||
}, | |||||
"passive-defensive": { | |||||
"targetVisibleEnemies": false, | |||||
"targetAttackersAlways": false, | |||||
"respondFlee": false, | |||||
"respondFleeOnSight": false, | |||||
"respondChase": false, | |||||
"respondChaseBeyondVision": false, | |||||
"respondStandGround": false, | |||||
"respondHoldGround": true, | |||||
"selectable": false | |||||
}, | |||||
"none": { | "none": { | ||||
// Only to be used by AI or trigger scripts | // Only to be used by AI or trigger scripts | ||||
"targetVisibleEnemies": false, | "targetVisibleEnemies": false, | ||||
"targetAttackersAlways": false, | "targetAttackersAlways": false, | ||||
"respondFlee": false, | "respondFlee": false, | ||||
"respondFleeOnSight": false, | |||||
"respondChase": false, | "respondChase": false, | ||||
"respondChaseBeyondVision": false, | "respondChaseBeyondVision": false, | ||||
"respondStandGround": false, | "respondStandGround": false, | ||||
"respondHoldGround": false, | "respondHoldGround": false, | ||||
"selectable": false | "selectable": false | ||||
} | } | ||||
}; | }; | ||||
▲ Show 20 Lines • Show All 117 Lines • ▼ Show 20 Lines | "Order.Stop": function(msg) { | ||||
{ | { | ||||
this.FinishOrder(); | this.FinishOrder(); | ||||
return; | return; | ||||
} | } | ||||
this.StopMoving(); | this.StopMoving(); | ||||
this.FinishOrder(); | this.FinishOrder(); | ||||
if (this.IsAnimal()) | if (this.IsFormationMember()) | ||||
Done Inline Actionscould ternary this Stan: could ternary this | |||||
Done Inline ActionsDoesn't improve readability. Freagarach: Doesn't improve readability. | |||||
this.SetNextState("ANIMAL.IDLE"); | |||||
else if (this.IsFormationMember()) | |||||
this.SetNextState("FORMATIONMEMBER.IDLE"); | this.SetNextState("FORMATIONMEMBER.IDLE"); | ||||
else | else | ||||
this.SetNextState("INDIVIDUAL.IDLE"); | this.SetNextState("INDIVIDUAL.IDLE"); | ||||
}, | }, | ||||
"Order.Walk": function(msg) { | "Order.Walk": function(msg) { | ||||
// Let players move captured domestic animals around | // Let players move captured domestic animals around | ||||
if (this.IsAnimal() && !this.IsDomestic() || !this.AbleToMove()) | if (this.IsAnimal() && !this.IsDomestic() || !this.AbleToMove()) | ||||
{ | { | ||||
this.FinishOrder(); | this.FinishOrder(); | ||||
return; | return; | ||||
} | } | ||||
if (this.CanPack()) | if (this.CanPack()) | ||||
{ | { | ||||
this.PushOrderFront("Pack", { "force": true }); | this.PushOrderFront("Pack", { "force": true }); | ||||
return; | return; | ||||
} | } | ||||
this.SetHeldPosition(this.order.data.x, this.order.data.z); | this.SetHeldPosition(this.order.data.x, this.order.data.z); | ||||
this.order.data.relaxed = true; | this.order.data.relaxed = true; | ||||
if (this.IsAnimal()) | |||||
this.SetNextState("ANIMAL.WALKING"); | |||||
else | |||||
this.SetNextState("INDIVIDUAL.WALKING"); | this.SetNextState("INDIVIDUAL.WALKING"); | ||||
}, | }, | ||||
"Order.WalkAndFight": function(msg) { | "Order.WalkAndFight": function(msg) { | ||||
// Let players move captured domestic animals around | // Let players move captured domestic animals around | ||||
if (this.IsAnimal() && !this.IsDomestic() || !this.AbleToMove()) | if (this.IsAnimal() && !this.IsDomestic() || !this.AbleToMove()) | ||||
{ | { | ||||
this.FinishOrder(); | this.FinishOrder(); | ||||
return; | return; | ||||
} | } | ||||
if (this.CanPack()) | if (this.CanPack()) | ||||
{ | { | ||||
this.PushOrderFront("Pack", { "force": true }); | this.PushOrderFront("Pack", { "force": true }); | ||||
return; | return; | ||||
} | } | ||||
this.SetHeldPosition(this.order.data.x, this.order.data.z); | this.SetHeldPosition(this.order.data.x, this.order.data.z); | ||||
this.order.data.relaxed = true; | this.order.data.relaxed = true; | ||||
if (this.IsAnimal()) | |||||
this.SetNextState("ANIMAL.WALKING"); // WalkAndFight not applicable for animals | |||||
else | |||||
this.SetNextState("INDIVIDUAL.WALKINGANDFIGHTING"); | this.SetNextState("INDIVIDUAL.WALKINGANDFIGHTING"); | ||||
}, | }, | ||||
"Order.WalkToTarget": function(msg) { | "Order.WalkToTarget": function(msg) { | ||||
// Let players move captured domestic animals around | // Let players move captured domestic animals around | ||||
if (this.IsAnimal() && !this.IsDomestic() || !this.AbleToMove()) | if (this.IsAnimal() && !this.IsDomestic() || !this.AbleToMove()) | ||||
{ | { | ||||
this.FinishOrder(); | this.FinishOrder(); | ||||
return; | return; | ||||
} | } | ||||
if (this.CanPack()) | if (this.CanPack()) | ||||
{ | { | ||||
this.PushOrderFront("Pack", { "force": true }); | this.PushOrderFront("Pack", { "force": true }); | ||||
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.` | |||||
} | } | ||||
this.order.data.relaxed = true; | this.order.data.relaxed = true; | ||||
if (this.IsAnimal()) | |||||
this.SetNextState("ANIMAL.WALKING"); | |||||
else | |||||
this.SetNextState("INDIVIDUAL.WALKING"); | this.SetNextState("INDIVIDUAL.WALKING"); | ||||
}, | }, | ||||
"Order.PickupUnit": function(msg) { | "Order.PickupUnit": function(msg) { | ||||
let cmpGarrisonHolder = Engine.QueryInterface(this.entity, IID_GarrisonHolder); | let cmpGarrisonHolder = Engine.QueryInterface(this.entity, IID_GarrisonHolder); | ||||
if (!cmpGarrisonHolder || cmpGarrisonHolder.IsFull()) | if (!cmpGarrisonHolder || cmpGarrisonHolder.IsFull()) | ||||
{ | { | ||||
this.FinishOrder(); | this.FinishOrder(); | ||||
return; | return; | ||||
Show All 28 Lines | "Order.Guard": function(msg) { | ||||
if (!this.CheckTargetRangeExplicit(this.isGuardOf, 0, this.guardRange)) | if (!this.CheckTargetRangeExplicit(this.isGuardOf, 0, this.guardRange)) | ||||
this.SetNextState("INDIVIDUAL.GUARD.ESCORTING"); | this.SetNextState("INDIVIDUAL.GUARD.ESCORTING"); | ||||
else | else | ||||
this.SetNextState("INDIVIDUAL.GUARD.GUARDING"); | this.SetNextState("INDIVIDUAL.GUARD.GUARDING"); | ||||
}, | }, | ||||
"Order.Flee": function(msg) { | "Order.Flee": function(msg) { | ||||
if (this.IsAnimal()) | |||||
this.SetNextState("ANIMAL.FLEEING"); | |||||
else | |||||
this.SetNextState("INDIVIDUAL.FLEEING"); | this.SetNextState("INDIVIDUAL.FLEEING"); | ||||
}, | }, | ||||
"Order.Attack": function(msg) { | "Order.Attack": function(msg) { | ||||
if (!this.TargetIsAlive(this.order.data.target)) | if (!this.TargetIsAlive(this.order.data.target)) | ||||
{ | { | ||||
this.FinishOrder(); | this.FinishOrder(); | ||||
return; | return; | ||||
} | } | ||||
Show All 18 Lines | if (this.CheckTargetAttackRange(this.order.data.target, this.order.data.attackType)) | ||||
this.PushOrderFront("Unpack", { "force": true }); | this.PushOrderFront("Unpack", { "force": true }); | ||||
return; | return; | ||||
} | } | ||||
// Cancel any current packing order. | // Cancel any current packing order. | ||||
if (!this.EnsureCorrectPackStateForAttack(false)) | if (!this.EnsureCorrectPackStateForAttack(false)) | ||||
return; | return; | ||||
if (this.IsAnimal()) | |||||
this.SetNextState("ANIMAL.COMBAT.ATTACKING"); | |||||
else | |||||
this.SetNextState("INDIVIDUAL.COMBAT.ATTACKING"); | this.SetNextState("INDIVIDUAL.COMBAT.ATTACKING"); | ||||
return; | return; | ||||
} | } | ||||
// If we're hunting, that's a special case where we should continue attacking our target. | // If we're hunting, that's a special case where we should continue attacking our target. | ||||
if (this.GetStance().respondStandGround && !this.order.data.force && !this.order.data.hunting || !this.AbleToMove()) | if (this.GetStance().respondStandGround && !this.order.data.force && !this.order.data.hunting || !this.AbleToMove()) | ||||
{ | { | ||||
this.FinishOrder(); | this.FinishOrder(); | ||||
return; | return; | ||||
} | } | ||||
if (this.CanPack()) | if (this.CanPack()) | ||||
{ | { | ||||
this.PushOrderFront("Pack", { "force": true }); | this.PushOrderFront("Pack", { "force": true }); | ||||
return; | return; | ||||
} | } | ||||
// If we're currently packing/unpacking, make sure we are packed, so we can move. | // If we're currently packing/unpacking, make sure we are packed, so we can move. | ||||
if (!this.EnsureCorrectPackStateForAttack(true)) | if (!this.EnsureCorrectPackStateForAttack(true)) | ||||
return; | return; | ||||
if (this.IsAnimal()) | |||||
this.SetNextState("ANIMAL.COMBAT.APPROACHING"); | |||||
else | |||||
this.SetNextState("INDIVIDUAL.COMBAT.APPROACHING"); | this.SetNextState("INDIVIDUAL.COMBAT.APPROACHING"); | ||||
}, | }, | ||||
"Order.Patrol": function(msg) { | "Order.Patrol": function(msg) { | ||||
if (this.IsAnimal() || !this.AbleToMove()) | if (this.IsAnimal() || !this.AbleToMove()) | ||||
Done Inline ActionsShouldn't the patrol order override the roaming? It's basically ordered roaming Stan: Shouldn't the patrol order override the roaming? It's basically ordered roaming | |||||
Done Inline ActionsI'm not sure what you mean here, but I guess this check can be ditched. Freagarach: I'm not sure what you mean here, but I guess this check can be ditched. | |||||
{ | { | ||||
this.FinishOrder(); | this.FinishOrder(); | ||||
return; | return; | ||||
} | } | ||||
if (this.CanPack()) | if (this.CanPack()) | ||||
{ | { | ||||
this.PushOrderFront("Pack", { "force": true }); | this.PushOrderFront("Pack", { "force": true }); | ||||
▲ Show 20 Lines • Show All 131 Lines • ▼ Show 20 Lines | UnitAI.prototype.UnitFsmSpec = { | ||||
"Order.Garrison": function(msg) { | "Order.Garrison": function(msg) { | ||||
if (!this.AbleToMove()) | if (!this.AbleToMove()) | ||||
{ | { | ||||
this.SetNextState("IDLE"); | this.SetNextState("IDLE"); | ||||
return; | return; | ||||
} | } | ||||
else if (this.IsGarrisoned()) | else if (this.IsGarrisoned()) | ||||
{ | { | ||||
if (this.IsAnimal()) | |||||
this.SetNextState("ANIMAL.GARRISON.GARRISONED"); | |||||
else | |||||
this.SetNextState("INDIVIDUAL.GARRISON.GARRISONED"); | this.SetNextState("INDIVIDUAL.GARRISON.GARRISONED"); | ||||
return; | return; | ||||
} | } | ||||
if (this.CanPack()) | if (this.CanPack()) | ||||
{ | { | ||||
this.PushOrderFront("Pack", { "force": true }); | this.PushOrderFront("Pack", { "force": true }); | ||||
return; | return; | ||||
} | } | ||||
if (this.IsAnimal()) | |||||
this.SetNextState("ANIMAL.GARRISON.APPROACHING"); | |||||
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.Cheer": function(msg) { | "Order.Cheer": function(msg) { | ||||
▲ Show 20 Lines • Show All 121 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 235 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 237 Lines • ▼ Show 20 Lines | "Order.LeaveFoundation": function(msg) { | ||||
this.FinishOrder(); | this.FinishOrder(); | ||||
return; | return; | ||||
} | } | ||||
this.order.data.min = g_LeaveFoundationRange; | this.order.data.min = g_LeaveFoundationRange; | ||||
this.SetNextState("WALKINGTOPOINT"); | this.SetNextState("WALKINGTOPOINT"); | ||||
}, | }, | ||||
"enter": function() { | "enter": function() { | ||||
if (this.IsAnimal()) | |||||
{ | |||||
warn("Entity " + this.entity + " was put in FORMATIONMEMBER state but is an animal"); | |||||
this.FinishOrder(); | |||||
this.SetNextState("ANIMAL.IDLE"); | |||||
return true; | |||||
} | |||||
let cmpFormation = Engine.QueryInterface(this.formationController, IID_Formation); | let cmpFormation = Engine.QueryInterface(this.formationController, IID_Formation); | ||||
if (cmpFormation) | if (cmpFormation) | ||||
{ | { | ||||
this.formationAnimationVariant = cmpFormation.GetFormationAnimationVariant(this.entity); | this.formationAnimationVariant = cmpFormation.GetFormationAnimationVariant(this.entity); | ||||
if (this.formationAnimationVariant) | if (this.formationAnimationVariant) | ||||
this.SetAnimationVariant(this.formationAnimationVariant); | this.SetAnimationVariant(this.formationAnimationVariant); | ||||
else | else | ||||
this.SetDefaultAnimationVariant(); | this.SetDefaultAnimationVariant(); | ||||
▲ Show 20 Lines • Show All 70 Lines • ▼ Show 20 Lines | "WALKINGTOPOINT": { | ||||
this.FinishOrder(); | this.FinishOrder(); | ||||
}, | }, | ||||
}, | }, | ||||
}, | }, | ||||
// States for entities not part of a formation: | // States for entities not part of a formation: | ||||
"INDIVIDUAL": { | "INDIVIDUAL": { | ||||
"enter": function() { | |||||
if (this.IsAnimal()) | |||||
error("Animal got moved into INDIVIDUAL.* state"); | |||||
return false; | |||||
}, | |||||
"Attacked": function(msg) { | "Attacked": function(msg) { | ||||
if (this.GetStance().targetAttackersAlways || !this.order || !this.order.data || !this.order.data.force) | if (this.GetStance().targetAttackersAlways || !this.order || !this.order.data || !this.order.data.force) | ||||
this.RespondToTargetedEntities([msg.data.attacker]); | this.RespondToTargetedEntities([msg.data.attacker]); | ||||
}, | }, | ||||
"GuardedAttacked": function(msg) { | "GuardedAttacked": function(msg) { | ||||
// do nothing if we have a forced order in queue before the guard order | // do nothing if we have a forced order in queue before the guard order | ||||
for (var i = 0; i < this.orderQueue.length; ++i) | for (var i = 0; i < this.orderQueue.length; ++i) | ||||
▲ Show 20 Lines • Show All 89 Lines • ▼ Show 20 Lines | "IDLE": { | ||||
// We check for idleness to prevent an entity to react only to newly seen entities | // We check for idleness to prevent an entity to react only to newly seen entities | ||||
// when receiving a Los*RangeUpdate on the same turn as the entity becomes idle | // when receiving a Los*RangeUpdate on the same turn as the entity becomes idle | ||||
// since this.FindNew*Targets is called in the timer. | // since this.FindNew*Targets is called in the timer. | ||||
"LosRangeUpdate": function(msg) { | "LosRangeUpdate": function(msg) { | ||||
if (this.isIdle && msg && msg.data && msg.data.added && msg.data.added.length) | if (this.isIdle && msg && msg.data && msg.data.added && msg.data.added.length) | ||||
this.RespondToSightedEntities(msg.data.added); | this.RespondToSightedEntities(msg.data.added); | ||||
}, | }, | ||||
Done Inline ActionsRefs. D1880. Freagarach: Refs. D1880. | |||||
"LosHealRangeUpdate": function(msg) { | "LosHealRangeUpdate": function(msg) { | ||||
if (this.isIdle && msg && msg.data && msg.data.added && msg.data.added.length) | if (this.isIdle && msg && msg.data && msg.data.added && msg.data.added.length) | ||||
this.RespondToHealableEntities(msg.data.added); | this.RespondToHealableEntities(msg.data.added); | ||||
}, | }, | ||||
"LosAttackRangeUpdate": function(msg) { | "LosAttackRangeUpdate": function(msg) { | ||||
if (this.isIdle && msg && msg.data && msg.data.added && msg.data.added.length && this.GetStance().targetVisibleEnemies) | if (this.isIdle && msg && msg.data && msg.data.added && msg.data.added.length && this.GetStance().targetVisibleEnemies) | ||||
this.AttackEntitiesByPreference(msg.data.added); | this.AttackEntitiesByPreference(msg.data.added); | ||||
Show All 30 Lines | "IDLE": { | ||||
this.GetStance().respondHoldGround && this.heldPosition && | this.GetStance().respondHoldGround && this.heldPosition && | ||||
!this.CheckPointRangeExplicit(this.heldPosition.x, this.heldPosition.z, 0, 10) && | !this.CheckPointRangeExplicit(this.heldPosition.x, this.heldPosition.z, 0, 10) && | ||||
this.WalkToHeldPosition()) | this.WalkToHeldPosition()) | ||||
return; | return; | ||||
this.isIdle = true; | this.isIdle = true; | ||||
Engine.PostMessage(this.entity, MT_UnitIdleChanged, { "idle": this.isIdle }); | Engine.PostMessage(this.entity, MT_UnitIdleChanged, { "idle": this.isIdle }); | ||||
} | } | ||||
// Go linger first to prevent all roaming entities | |||||
// to move all at the same time on map init. | |||||
if (this.template.RoamDistance) | |||||
Done Inline Actionsthis should go before if (!this.isIdle) Silier: this should go before if (!this.isIdle) | |||||
Done Inline ActionsWhy? Aren't we considered idle when merely lingering around? Freagarach: Why? Aren't we considered idle when merely lingering around? | |||||
Done Inline Actionshmm, depends on definition of idle but ok Silier: hmm, depends on definition of idle but ok
ah. else would not work ranges, I did not notice that… | |||||
this.SetNextState("LINGERING"); | |||||
}, | |||||
"ROAMING": { | |||||
"enter": function() { | |||||
this.SetFacePointAfterMove(false); | |||||
this.MoveRandomly(+this.template.RoamDistance); | |||||
this.StartTimer(randIntInclusive(+this.template.RoamTimeMin, +this.template.RoamTimeMax)); | |||||
return false; | |||||
}, | |||||
"leave": function() { | |||||
this.StopMoving(); | |||||
this.StopTimer(); | |||||
this.SetFacePointAfterMove(true); | |||||
}, | |||||
"Timer": function(msg) { | |||||
this.SetNextState("LINGERING"); | |||||
}, | |||||
"MovementUpdate": function() { | |||||
this.MoveRandomly(+this.template.RoamDistance); | |||||
}, | |||||
}, | |||||
"LINGERING": { | |||||
"enter": function() { | |||||
// ToDo: rename animations? | |||||
this.SelectAnimation("feeding"); | |||||
Not Done Inline ActionsQuestion 1: How big is that patch? Else we could tackle it before this one. Stan: Question 1: How big is that patch? Else we could tackle it before this one.
Question 2: Does it… | |||||
Not Done Inline ActionsA1: No clue yet, but the patch makes no sense without this one I guess ^^ Freagarach: A1: No clue yet, but the patch makes no sense without this one I guess ^^
A2: I guess not… | |||||
this.StartTimer(randIntInclusive(+this.template.FeedTimeMin, +this.template.FeedTimeMax)); | |||||
return false; | |||||
}, | |||||
"leave": function() { | |||||
this.ResetAnimation(); | |||||
this.StopTimer(); | |||||
}, | |||||
"Timer": function(msg) { | |||||
this.SetNextState("ROAMING"); | |||||
}, | |||||
}, | }, | ||||
}, | }, | ||||
"WALKING": { | "WALKING": { | ||||
"enter": function() { | "enter": function() { | ||||
if (!this.MoveTo(this.order.data)) | if (!this.MoveTo(this.order.data)) | ||||
{ | { | ||||
this.FinishOrder(); | this.FinishOrder(); | ||||
▲ Show 20 Lines • Show All 385 Lines • ▼ Show 20 Lines | "COMBAT": { | ||||
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) | ||||
{ | { | ||||
cmpBuildingAI.SetUnitAITarget(this.order.data.target); | cmpBuildingAI.SetUnitAITarget(this.order.data.target); | ||||
return false; | return false; | ||||
} | } | ||||
this.shouldCheer = true; | |||||
let cmpUnitAI = Engine.QueryInterface(this.order.data.target, IID_UnitAI); | |||||
this.shouldCheer = cmpUnitAI && (!cmpUnitAI.IsAnimal() || cmpUnitAI.IsDangerousAnimal()); | |||||
Done Inline Actionspoint is to not cheer after killing chicken or any non-attacking animal, removal is wrong Silier: point is to not cheer after killing chicken or any non-attacking animal, removal is wrong | |||||
Done Inline ActionsAgreed. You can test though, entities won't cheer after killing those kind of animals :) Freagarach: Agreed. You can test though, entities won't cheer after killing those kind of animals :) | |||||
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); | ||||
Show All 24 Lines | "COMBAT": { | ||||
let cmpBuildingAI = Engine.QueryInterface(this.entity, IID_BuildingAI); | let cmpBuildingAI = Engine.QueryInterface(this.entity, IID_BuildingAI); | ||||
if (!cmpBuildingAI) | if (!cmpBuildingAI) | ||||
{ | { | ||||
let cmpAttack = Engine.QueryInterface(this.entity, IID_Attack); | let cmpAttack = Engine.QueryInterface(this.entity, IID_Attack); | ||||
cmpAttack.PerformAttack(attackType, target); | cmpAttack.PerformAttack(attackType, target); | ||||
} | } | ||||
// PerformAttack might have triggered messages that moved us to another state. | // PerformAttack might have triggered messages that moved us to another state. | ||||
// (use 'ends with' to handle animals/formation members copying our state). | // (use 'ends with' to handle formation members copying our state). | ||||
if (!this.GetCurrentState().endsWith("COMBAT.ATTACKING")) | if (!this.GetCurrentState().endsWith("COMBAT.ATTACKING")) | ||||
return; | return; | ||||
// Check we can still reach the target for the next attack | // Check we can still reach the target for the next attack | ||||
if (this.CheckTargetAttackRange(target, attackType)) | if (this.CheckTargetAttackRange(target, attackType)) | ||||
{ | { | ||||
if (this.resyncAnimation) | if (this.resyncAnimation) | ||||
Show All 18 Lines | "COMBAT": { | ||||
this.SetNextState("FINDINGNEWTARGET"); | this.SetNextState("FINDINGNEWTARGET"); | ||||
}, | }, | ||||
// 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.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() { | "Order.Cheer": function() { | ||||
this.SetNextState("CHEERING"); | this.SetNextState("CHEERING"); | ||||
}, | }, | ||||
▲ Show 20 Lines • Show All 439 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 678 Lines • ▼ Show 20 Lines | "PICKUP": { | ||||
return false; | return false; | ||||
}, | }, | ||||
"PickupCanceled": function() { | "PickupCanceled": function() { | ||||
this.FinishOrder(); | this.FinishOrder(); | ||||
}, | }, | ||||
}, | }, | ||||
}, | }, | ||||
}, | |||||
"ANIMAL": { | |||||
"Attacked": function(msg) { | |||||
if (this.template.NaturalBehaviour == "skittish" || | |||||
this.template.NaturalBehaviour == "passive") | |||||
{ | |||||
this.Flee(msg.data.attacker, false); | |||||
} | |||||
else if (this.IsDangerousAnimal() || this.template.NaturalBehaviour == "defensive") | |||||
{ | |||||
if (this.CanAttack(msg.data.attacker)) | |||||
this.Attack(msg.data.attacker, false); | |||||
} | |||||
else if (this.template.NaturalBehaviour == "domestic") | |||||
Done Inline ActionsNote that in real life domestic animals would run if hurt. It is just that slaughtering is so quick they don't need to. (Hence I propose to increase slaughtering damage to kill domestic animals in one go.) Freagarach: Note that in real life domestic animals would run if hurt. It is just that slaughtering is so… | |||||
{ | |||||
// Never flee, stop what we were doing | |||||
this.SetNextState("IDLE"); | |||||
} | |||||
}, | |||||
"Order.LeaveFoundation": function(msg) { | |||||
// Move a tile outside the building | |||||
if (this.CheckTargetRangeExplicit(msg.data.target, g_LeaveFoundationRange, -1)) | |||||
{ | |||||
this.FinishOrder(); | |||||
return; | |||||
} | } | ||||
this.order.data.min = g_LeaveFoundationRange; | |||||
this.SetNextState("WALKING"); | |||||
}, | |||||
"IDLE": { | |||||
// (We need an IDLE state so that FinishOrder works) | |||||
"enter": function() { | |||||
this.SetNextState("FEEDING"); | |||||
return true; | |||||
}, | |||||
}, | |||||
"ROAMING": { | |||||
"enter": function() { | |||||
this.SetFacePointAfterMove(false); | |||||
this.MoveRandomly(+this.template.RoamDistance); | |||||
this.StartTimer(randIntInclusive(+this.template.RoamTimeMin, +this.template.RoamTimeMax)); | |||||
return false; | |||||
}, | |||||
"leave": function() { | |||||
this.StopMoving(); | |||||
this.StopTimer(); | |||||
this.SetFacePointAfterMove(true); | |||||
}, | |||||
"LosRangeUpdate": function(msg) { | |||||
if (msg && msg.data && msg.data.added && msg.data.added.length) | |||||
this.RespondToSightedEntities(msg.data.added); | |||||
}, | |||||
"LosAttackRangeUpdate": function(msg) { | |||||
if (this.IsDangerousAnimal() && msg && msg.data && msg.data.added && msg.data.added.length) | |||||
this.AttackVisibleEntity(msg.data.added); | |||||
// TODO: if two units enter our range together, we'll attack the | |||||
// first and then the second won't trigger another LosAttackRangeUpdate | |||||
// so we won't notice it. Probably we should do something with | |||||
// ResetActiveQuery in ROAMING.enter/FEEDING.enter in order to | |||||
// find any units that are already in range. | |||||
Done Inline ActionsIs fixed by moving it. Freagarach: Is fixed by moving it. | |||||
}, | |||||
"Timer": function(msg) { | |||||
this.SetNextState("FEEDING"); | |||||
}, | |||||
"MovementUpdate": function() { | |||||
this.MoveRandomly(+this.template.RoamDistance); | |||||
}, | |||||
}, | |||||
"FEEDING": { | |||||
"enter": function() { | |||||
this.SelectAnimation("feeding"); | |||||
this.StopMoving(); | |||||
this.StartTimer(randIntInclusive(+this.template.FeedTimeMin, +this.template.FeedTimeMax)); | |||||
return false; | |||||
}, | |||||
"leave": function() { | |||||
this.ResetAnimation(); | |||||
this.StopTimer(); | |||||
}, | |||||
"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.SetNextState("ROAMING"); | |||||
}, | |||||
}, | |||||
"FLEEING": "INDIVIDUAL.FLEEING", | |||||
"COMBAT": "INDIVIDUAL.COMBAT", | |||||
"WALKING": "INDIVIDUAL.WALKING", // reuse the same walking behaviour for animals | |||||
// only used for domestic animals | |||||
"CHEERING": { | |||||
"enter": function() { | |||||
this.SelectAnimation("promotion"); | |||||
this.StartTimer(2800); | |||||
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(); | |||||
}, | |||||
}, | |||||
"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 | ||||
this.order = undefined; // always == this.orderQueue[0] | this.order = undefined; // always == this.orderQueue[0] | ||||
this.formationController = INVALID_ENTITY; // entity with IID_Formation that we belong to | this.formationController = INVALID_ENTITY; // entity with IID_Formation that we belong to | ||||
this.isGarrisoned = false; | this.isGarrisoned = false; | ||||
Show All 27 Lines | UnitAI.prototype.IsFormationController = function() | ||||
return (this.template.FormationController == "true"); | return (this.template.FormationController == "true"); | ||||
}; | }; | ||||
UnitAI.prototype.IsFormationMember = function() | UnitAI.prototype.IsFormationMember = function() | ||||
{ | { | ||||
return (this.formationController != INVALID_ENTITY); | return (this.formationController != INVALID_ENTITY); | ||||
}; | }; | ||||
/** | |||||
* ToDo: Make this not needed by fixing gaia | |||||
* range queries in BuildingAI and UnitAI. | |||||
*/ | |||||
UnitAI.prototype.IsAnimal = function() | UnitAI.prototype.IsAnimal = function() | ||||
Done Inline ActionsCould become isRoaming or something like that? Stan: Could become isRoaming or something like that? | |||||
Done Inline ActionsI want to be explicit why this function is here. Freagarach: I want to be explicit why this function is here. | |||||
{ | { | ||||
return (this.template.NaturalBehaviour ? true : false); | return !!this.template.RoamDistance; | ||||
Done Inline Actionsthis.template.RoamDistance > 0; ? Stan: ```lang=js
this.template.RoamDistance > 0;
```
? | |||||
Done Inline ActionsBut if there is no roaming distance at all in the template, this will error out, right? Freagarach: But if there is no roaming distance at all in the template, this will error out, right? | |||||
}; | |||||
UnitAI.prototype.IsDangerousAnimal = function() | |||||
{ | |||||
return (this.IsAnimal() && (this.template.NaturalBehaviour == "violent" || | |||||
this.template.NaturalBehaviour == "aggressive")); | |||||
}; | }; | ||||
Done Inline ActionsDo we have a convention for TODO (ToDo, Todo, toDo..) Stan: Do we have a convention for TODO (ToDo, Todo, toDo..) | |||||
Done Inline Actionsseems a bit of a weird function tbh. I'm guessing it's part of the WIP, but don't remove animal only stuff just to add it back with an if wraitii: seems a bit of a weird function tbh. I'm guessing it's part of the WIP, but don't remove animal… | |||||
Done Inline ActionsYeah, we want animals to move from foundations, but I'm not sure what a proper way of checking that would be. Perhaps just check if the entity stance is passive (if it is non-allied) and if that is true move from the foundation? Would mean lions and such would stay, which seems okay to me. Freagarach: Yeah, we want animals to move from foundations, but I'm not sure what a proper way of checking… | |||||
// ToDo: Can be removed after D1960. | |||||
Done Inline ActionsTODO: ? Stan: TODO: ? | |||||
UnitAI.prototype.IsDomestic = function() | UnitAI.prototype.IsDomestic = function() | ||||
{ | { | ||||
var cmpIdentity = Engine.QueryInterface(this.entity, IID_Identity); | var cmpIdentity = Engine.QueryInterface(this.entity, IID_Identity); | ||||
return cmpIdentity && cmpIdentity.HasClass("Domestic"); | return cmpIdentity && cmpIdentity.HasClass("Domestic"); | ||||
}; | }; | ||||
UnitAI.prototype.IsHealer = function() | UnitAI.prototype.IsHealer = function() | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 75 Lines • ▼ Show 20 Lines | UnitAI.prototype.IsWalkingAndFighting = function() | ||||
if (this.IsFormationMember()) | if (this.IsFormationMember()) | ||||
return false; | return false; | ||||
return this.orderQueue.length > 0 && (this.orderQueue[0].type == "WalkAndFight" || this.orderQueue[0].type == "Patrol"); | return this.orderQueue.length > 0 && (this.orderQueue[0].type == "WalkAndFight" || this.orderQueue[0].type == "Patrol"); | ||||
}; | }; | ||||
UnitAI.prototype.OnCreate = function() | UnitAI.prototype.OnCreate = function() | ||||
{ | { | ||||
if (this.IsAnimal()) | if (this.IsFormationController()) | ||||
this.UnitFsm.Init(this, "ANIMAL.FEEDING"); | |||||
else if (this.IsFormationController()) | |||||
this.UnitFsm.Init(this, "FORMATIONCONTROLLER.IDLE"); | this.UnitFsm.Init(this, "FORMATIONCONTROLLER.IDLE"); | ||||
else | else | ||||
this.UnitFsm.Init(this, "INDIVIDUAL.IDLE"); | this.UnitFsm.Init(this, "INDIVIDUAL.IDLE"); | ||||
this.isIdle = true; | this.isIdle = true; | ||||
}; | }; | ||||
UnitAI.prototype.OnDiplomacyChanged = function(msg) | UnitAI.prototype.OnDiplomacyChanged = function(msg) | ||||
{ | { | ||||
Show All 21 Lines | if (msg.to != INVALID_PLAYER && msg.from != INVALID_PLAYER) | ||||
{ | { | ||||
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 37 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 after '{'. Lint: ESLintBear (object-curly-spacing): `A space is required after '{'.` | |||||
Lint: ESLintBear (object-curly-spacing) A space is required before '}'. Lint: ESLintBear (object-curly-spacing): `A space is required before '}'.` | |||||
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; | ||||
} | } | ||||
}; | }; | ||||
/** | /** | ||||
* Wrapper function that sets up the LOS, healer and attack range queries. | * Wrapper function that sets up the LOS, healer and attack range queries. | ||||
* This should be called whenever our ownership changes. | * This should be called whenever our ownership changes. | ||||
*/ | */ | ||||
UnitAI.prototype.SetupRangeQueries = function() | UnitAI.prototype.SetupRangeQueries = function() | ||||
{ | { | ||||
// Only skittish animals use this for now. | // Only skittish animals use this for now. | ||||
if (this.template.NaturalBehaviour && this.template.NaturalBehaviour == "skittish") | if (this.GetStance().respondFleeOnSight) | ||||
this.SetupLOSRangeQuery(); | this.SetupLOSRangeQuery(); | ||||
if (this.IsHealer()) | if (this.IsHealer()) | ||||
this.SetupHealRangeQuery(); | this.SetupHealRangeQuery(); | ||||
if (Engine.QueryInterface(this.entity, IID_Attack)) | if (Engine.QueryInterface(this.entity, IID_Attack)) | ||||
this.SetupAttackRangeQuery(); | this.SetupAttackRangeQuery(); | ||||
}; | }; | ||||
▲ Show 20 Lines • Show All 97 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 149 Lines • ▼ Show 20 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 after '{'. Lint: ESLintBear (object-curly-spacing): `A space is required after '{'.` | |||||
Lint: ESLintBear (object-curly-spacing) A space is required before '}'. Lint: ESLintBear (object-curly-spacing): `A space is required before '}'.` | |||||
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 115 Lines • ▼ Show 20 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 (isWorkType(type)) | if (isWorkType(type)) | ||||
{ | { | ||||
this.workOrders = []; | this.workOrders = []; | ||||
return; | return; | ||||
} | } | ||||
if (this.workOrders.length) | if (this.workOrders.length) | ||||
return; | return; | ||||
Show All 10 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; | ||||
} | } | ||||
} | } | ||||
}; | }; | ||||
▲ Show 20 Lines • Show All 42 Lines • ▼ Show 20 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 681 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 All 31 Lines | |||||
* @param {number} ents - An array of the IDs of the spotted entities. | * @param {number} ents - An array of the IDs of the spotted entities. | ||||
* @return {boolean} - Whether we responded. | * @return {boolean} - Whether we responded. | ||||
*/ | */ | ||||
UnitAI.prototype.RespondToSightedEntities = function(ents) | UnitAI.prototype.RespondToSightedEntities = function(ents) | ||||
{ | { | ||||
if (!ents || !ents.length) | if (!ents || !ents.length) | ||||
return false; | return false; | ||||
if (this.template.NaturalBehaviour && this.template.NaturalBehaviour == "skittish") | if (this.GetStance().respondFleeOnSight) | ||||
{ | { | ||||
this.Flee(ents[0], false); | this.Flee(ents[0], false); | ||||
return true; | return true; | ||||
} | } | ||||
return false; | return false; | ||||
}; | }; | ||||
/** | /** | ||||
* 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.` | |||||
} | } | ||||
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. | ||||
if (!this.GetStance().respondChaseBeyondVision) | if (!this.GetStance().respondChaseBeyondVision) | ||||
Show All 13 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 137 Lines • ▼ Show 20 Lines | UnitAI.prototype.Guard = function(target, queued) | ||||
if (target === this.entity) | if (target === this.entity) | ||||
return; | return; | ||||
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 301 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 286 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: 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.` | |||||
continue; | continue; | ||||
if (targetClasses.avoid && cmpIdentity | if (targetClasses.avoid && cmpIdentity | ||||
&& MatchesClassList(cmpIdentity.GetClassesList(), targetClasses.avoid)) | && MatchesClassList(cmpIdentity.GetClassesList(), targetClasses.avoid)) | ||||
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.` | |||||
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: 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.` | |||||
continue; | continue; | ||||
if (cmpIdentity && targetClasses.avoid | if (cmpIdentity && targetClasses.avoid | ||||
&& MatchesClassList(cmpIdentity.GetClassesList(), targetClasses.avoid)) | && MatchesClassList(cmpIdentity.GetClassesList(), targetClasses.avoid)) | ||||
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.` | |||||
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 All 12 Lines | UnitAI.prototype.GetTargetsFromUnit = function() | ||||
if (!this.GetStance().targetVisibleEnemies) | if (!this.GetStance().targetVisibleEnemies) | ||||
return []; | return []; | ||||
let cmpAttack = Engine.QueryInterface(this.entity, IID_Attack); | let cmpAttack = Engine.QueryInterface(this.entity, IID_Attack); | ||||
if (!cmpAttack) | if (!cmpAttack) | ||||
return []; | return []; | ||||
let attackfilter = function(e) { | let attackfilter = function(e) { | ||||
if (!cmpAttack.CanAttack(e)) | |||||
return false; | |||||
Done Inline ActionsThis introduces a rather early check. I'm not saying it's bad per se, but it might be quite inconsistent, I know some other functions run rather late ones instead. wraitii: This introduces a rather early check. I'm not saying it's bad per se, but it might be quite… | |||||
Done Inline ActionsIt's only called below, so it's not so early ? Also prevents the creation of an extra function and more indirection see l5925 Stan: It's only called below, so it's not so early ? Also prevents the creation of an extra function… | |||||
let cmpOwnership = Engine.QueryInterface(e, IID_Ownership); | let cmpOwnership = Engine.QueryInterface(e, IID_Ownership); | ||||
if (cmpOwnership && cmpOwnership.GetOwner() > 0) | if (cmpOwnership && cmpOwnership.GetOwner() > 0) | ||||
return true; | return true; | ||||
// Only attack Gaia animals that will attack (us). | |||||
let cmpUnitAI = Engine.QueryInterface(e, IID_UnitAI); | let cmpUnitAI = Engine.QueryInterface(e, IID_UnitAI); | ||||
return cmpUnitAI && (!cmpUnitAI.IsAnimal() || cmpUnitAI.IsDangerousAnimal()); | return cmpUnitAI && (!cmpUnitAI.IsAnimal() || cmpUnitAI.GetStance().targetVisibleEnemies); | ||||
Done Inline ActionsSame problem as BuildingAI. Freagarach: Same problem as `BuildingAI`. | |||||
Done Inline ActionsMaybe this should be a function of it's own since you use it thrice? Stan: Maybe this should be a function of it's own since you use it thrice? | |||||
}; | }; | ||||
Done Inline ActionsRefs. rP14494. Freagarach: Refs. rP14494. | |||||
Done Inline ActionsNotice the duplication with below. One could make this stance dependant to only attack entities with cmpAttack of neutral players. (For future reference.) Freagarach: Notice the duplication with below. One could make this stance dependant to only attack entities… | |||||
let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); | let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); | ||||
Done Inline Actionsinline ? Maybe could also merge with above, although might hurt readability Stan: inline ? Maybe could also merge with above, although might hurt readability | |||||
let entities = cmpRangeManager.ResetActiveQuery(this.losAttackRangeQuery); | let entities = cmpRangeManager.ResetActiveQuery(this.losAttackRangeQuery); | ||||
let targets = entities.filter(function(v) { return cmpAttack.CanAttack(v) && attackfilter(v); }) | let targets = entities.filter(attackfilter).sort(function(a, b) { | ||||
Done Inline ActionsWhy is cmpAttack not inside the attack filter function? Stan: Why is cmpAttack not inside the attack filter function? | |||||
Done Inline ActionsCan't you just do: entities.filter(attackfilter); Stan: Can't you just do:
```lang=js
entities.filter(attackfilter);
``` | |||||
.sort(function(a, b) { return cmpAttack.CompareEntitiesByPreference(a, b); }); | return cmpAttack.CompareEntitiesByPreference(a, b); | ||||
}); | |||||
return targets; | return targets; | ||||
}; | }; | ||||
UnitAI.prototype.GetQueryRange = function(iid) | UnitAI.prototype.GetQueryRange = function(iid) | ||||
{ | { | ||||
let ret = { "min": 0, "max": 0 }; | let ret = { "min": 0, "max": 0 }; | ||||
▲ Show 20 Lines • Show All 95 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 after '{'. Lint: ESLintBear (object-curly-spacing): `A space is required after '{'.` | |||||
Lint: ESLintBear (object-curly-spacing) A space is required before '}'. Lint: ESLintBear (object-curly-spacing): `A space is required before '}'.` | |||||
}; | }; | ||||
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 148 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 //// | |||||
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, 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. | ||||
// We also add a fluctuation on the length of each side of the polygon (dist) | // We also add a fluctuation on the length of each side of the polygon (dist) | ||||
// which, in addition to making the move more random, helps escaping narrow spaces | // which, in addition to making the move more random, helps escaping narrow spaces | ||||
// with bigger values of dist. | // with bigger values of dist. | ||||
Show All 30 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); | let cmpAttack = Engine.QueryInterface(this.entity, IID_Attack); | ||||
if (!cmpAttack) | if (!cmpAttack) | ||||
return false; | return false; | ||||
var attackfilter = function(e) { | let attackfilter = function(e) { | ||||
var cmpOwnership = Engine.QueryInterface(e, IID_Ownership); | if (!cmpAttack.CanAttack(e)) | ||||
return false; | |||||
let cmpOwnership = Engine.QueryInterface(e, IID_Ownership); | |||||
if (cmpOwnership && cmpOwnership.GetOwner() > 0) | if (cmpOwnership && cmpOwnership.GetOwner() > 0) | ||||
return true; | return true; | ||||
var cmpUnitAI = Engine.QueryInterface(e, IID_UnitAI); | |||||
return cmpUnitAI && (!cmpUnitAI.IsAnimal() || cmpUnitAI.IsDangerousAnimal()); | // Only attack Gaia animals that will attack (us) automatically. | ||||
let cmpUnitAI = Engine.QueryInterface(e, IID_UnitAI); | |||||
Done Inline ActionsSame ^^ Stan: Same ^^ | |||||
return cmpUnitAI && (!cmpUnitAI.IsAnimal() || cmpUnitAI.GetStance().targetVisibleEnemies); | |||||
Done Inline ActionsIdem. Freagarach: Idem. | |||||
}; | }; | ||||
let entsByPreferences = {}; | let entsByPreferences = {}; | ||||
let preferences = []; | let preferences = []; | ||||
let entsWithoutPref = []; | let entsWithoutPref = []; | ||||
for (let ent of ents) | for (let ent of ents) | ||||
{ | { | ||||
if (!attackfilter(ent)) | if (!attackfilter(ent)) | ||||
▲ Show 20 Lines • Show All 86 Lines • Show Last 20 Lines |
What does this mean? And why is it listed at the bottom?