Changeset View
Changeset View
Standalone View
Standalone View
binaries/data/mods/public/simulation/components/UnitAI.js
Show First 20 Lines • Show All 333 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.` | |||||
} | } | ||||
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 | ||||
this.SetNextState("INDIVIDUAL.WALKING"); | this.SetNextState("INDIVIDUAL.WALKING"); | ||||
▲ Show 20 Lines • Show All 425 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 207 Lines • ▼ Show 20 Lines | "WALKINGANDFIGHTING": { | ||||
}, | }, | ||||
"leave": function() { | "leave": function() { | ||||
this.StopMoving(); | this.StopMoving(); | ||||
this.StopTimer(); | this.StopTimer(); | ||||
}, | }, | ||||
"Timer": function(msg) { | "Timer": function(msg) { | ||||
Engine.ProfileStart("FindWalkAndFightTargets"); | |||||
this.FindWalkAndFightTargets(); | this.FindWalkAndFightTargets(); | ||||
Engine.ProfileStop(); | |||||
}, | }, | ||||
"MovementUpdate": function(msg) { | "MovementUpdate": function(msg) { | ||||
if (msg.likelyFailure || this.CheckRange(this.order.data)) | if (msg.likelyFailure || this.CheckRange(this.order.data)) | ||||
this.FinishOrder(); | this.FinishOrder(); | ||||
}, | }, | ||||
}, | }, | ||||
▲ Show 20 Lines • Show All 1,128 Lines • ▼ Show 20 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: 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() { | ||||
if (!this.cheeringTime) | if (!this.cheeringTime) | ||||
return { "discardOrder": true }; | return { "discardOrder": true }; | ||||
Show All 19 Lines | "COMBAT": { | ||||
return true; | return true; | ||||
} | } | ||||
// Can't reach it, no longer owned by enemy, or it doesn't exist any more - give up | // Can't reach it, no longer owned by enemy, or it doesn't exist any more - give up | ||||
// except if in WalkAndFight mode where we look for more enemies around before moving again. | // except if in WalkAndFight mode where we look for more enemies around before moving again. | ||||
if (this.FinishOrder()) | if (this.FinishOrder()) | ||||
{ | { | ||||
if (this.IsWalkingAndFighting()) | if (this.IsWalkingAndFighting()) | ||||
{ | |||||
Engine.ProfileStart("FindWalkAndFightTargets"); | |||||
this.FindWalkAndFightTargets(); | this.FindWalkAndFightTargets(); | ||||
Engine.ProfileStop(); | |||||
} | |||||
return true; | return true; | ||||
} | } | ||||
if (this.FindNewTargets()) | if (this.FindNewTargets()) | ||||
return true; | return true; | ||||
if (this.GetStance().respondHoldGround) | if (this.GetStance().respondHoldGround) | ||||
this.WalkToHeldPosition(); | this.WalkToHeldPosition(); | ||||
▲ Show 20 Lines • Show All 3,789 Lines • ▼ Show 20 Lines | UnitAI.prototype.FindNewTargets = function() | ||||
let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); | let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); | ||||
return this.AttackEntitiesByPreference(cmpRangeManager.ResetActiveQuery(this.losAttackRangeQuery)); | return this.AttackEntitiesByPreference(cmpRangeManager.ResetActiveQuery(this.losAttackRangeQuery)); | ||||
}; | }; | ||||
UnitAI.prototype.FindWalkAndFightTargets = function() | UnitAI.prototype.FindWalkAndFightTargets = function() | ||||
{ | { | ||||
if (this.IsFormationController()) | if (this.IsFormationController()) | ||||
{ | { | ||||
var cmpUnitAI; | let cmpUnitAI; | ||||
var cmpFormation = Engine.QueryInterface(this.entity, IID_Formation); | let cmpFormation = Engine.QueryInterface(this.entity, IID_Formation); | ||||
for (var ent of cmpFormation.members) | for (let ent of cmpFormation.members) | ||||
{ | { | ||||
if (!(cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI))) | if (!(cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI))) | ||||
continue; | continue; | ||||
var targets = cmpUnitAI.GetTargetsFromUnit(); | if (cmpUnitAI.FindWalkAndFightTargets()) | ||||
for (var targ of targets) | return true; | ||||
} | |||||
return false; | |||||
} | |||||
let cmpAttack = Engine.QueryInterface(this.entity, IID_Attack); | |||||
let entities; | |||||
if (!this.losAttackRangeQuery || !this.GetStance().targetVisibleEnemies || !cmpAttack) | |||||
entities = []; | |||||
else | |||||
{ | { | ||||
if (!cmpUnitAI.CanAttack(targ)) | let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); | ||||
continue; | entities = cmpRangeManager.ResetActiveQuery(this.losAttackRangeQuery); | ||||
} | |||||
let attackfilter = e => { | |||||
if (this.order.data.targetClasses) | if (this.order.data.targetClasses) | ||||
{ | { | ||||
var cmpIdentity = Engine.QueryInterface(targ, IID_Identity); | let cmpIdentity = Engine.QueryInterface(e, IID_Identity); | ||||
var targetClasses = this.order.data.targetClasses; | let targetClasses = this.order.data.targetClasses; | ||||
if (targetClasses.attack && cmpIdentity | if (cmpIdentity && targetClasses.attack && | ||||
&& !MatchesClassList(cmpIdentity.GetClassesList(), targetClasses.attack)) | !MatchesClassList(cmpIdentity.GetClassesList(), targetClasses.attack)) | ||||
continue; | return false; | ||||
if (targetClasses.avoid && cmpIdentity | if (cmpIdentity && targetClasses.avoid && | ||||
&& MatchesClassList(cmpIdentity.GetClassesList(), targetClasses.avoid)) | MatchesClassList(cmpIdentity.GetClassesList(), targetClasses.avoid)) | ||||
continue; | return false; | ||||
// 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[e]) | ||||
continue; | return false; | ||||
} | } | ||||
this.PushOrderFront("Attack", { "target": targ, "force": false, "allowCapture": this.order.data.allowCapture }); | let cmpOwnership = Engine.QueryInterface(e, IID_Ownership); | ||||
if (cmpOwnership && cmpOwnership.GetOwner() > 0) | |||||
return true; | |||||
let cmpUnitAI = Engine.QueryInterface(e, IID_UnitAI); | |||||
return cmpUnitAI && (!cmpUnitAI.IsAnimal() || cmpUnitAI.IsDangerousAnimal()); | |||||
}; | |||||
let prefs = {}; | |||||
let bestPref = null; | |||||
let targets = []; | |||||
let pref; | |||||
for (let v of entities) | |||||
{ | |||||
if (this.CanAttack(v) && attackfilter(v)) | |||||
{ | |||||
pref = cmpAttack.GetPreference(v); | |||||
if (pref === 0) | |||||
{ | |||||
this.PushOrderFront("Attack", { "target": v, "force": false, "allowCapture": this.order.data.allowCapture }); | |||||
return true; | return true; | ||||
} | } | ||||
targets.push(v); | |||||
} | } | ||||
return false; | prefs[v] = pref; | ||||
if (pref !== null && (bestPref === null || pref < bestPref)) | |||||
bestPref = pref; | |||||
} | } | ||||
var targets = this.GetTargetsFromUnit(); | for (let targ of targets) | ||||
for (var targ of targets) | |||||
{ | |||||
if (!this.CanAttack(targ)) | |||||
continue; | |||||
if (this.order.data.targetClasses) | |||||
{ | { | ||||
var cmpIdentity = Engine.QueryInterface(targ, IID_Identity); | if (prefs[targ] !== bestPref) | ||||
var targetClasses = this.order.data.targetClasses; | |||||
if (cmpIdentity && targetClasses.attack | |||||
&& !MatchesClassList(cmpIdentity.GetClassesList(), targetClasses.attack)) | |||||
continue; | |||||
if (cmpIdentity && targetClasses.avoid | |||||
&& MatchesClassList(cmpIdentity.GetClassesList(), targetClasses.avoid)) | |||||
continue; | |||||
// Only used by the AIs to prevent some choices of targets | |||||
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; | ||||
Done Inline Actionsold method: then return the first attackable unit wraitii: old method: then return the first attackable unit | |||||
} | } | ||||
// healers on a walk-and-fight order should heal injured units | // healers on a walk-and-fight order should heal injured units | ||||
if (this.IsHealer()) | if (this.IsHealer()) | ||||
return this.FindNewHealTargets(); | return this.FindNewHealTargets(); | ||||
return false; | return false; | ||||
}; | }; | ||||
UnitAI.prototype.GetTargetsFromUnit = function() | |||||
{ | |||||
if (!this.losAttackRangeQuery) | |||||
return []; | |||||
if (!this.GetStance().targetVisibleEnemies) | |||||
return []; | |||||
let cmpAttack = Engine.QueryInterface(this.entity, IID_Attack); | |||||
if (!cmpAttack) | |||||
return []; | |||||
let attackfilter = function(e) { | |||||
let cmpOwnership = Engine.QueryInterface(e, IID_Ownership); | |||||
if (cmpOwnership && cmpOwnership.GetOwner() > 0) | |||||
return true; | |||||
let cmpUnitAI = Engine.QueryInterface(e, IID_UnitAI); | |||||
return cmpUnitAI && (!cmpUnitAI.IsAnimal() || cmpUnitAI.IsDangerousAnimal()); | |||||
}; | |||||
let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); | |||||
let entities = cmpRangeManager.ResetActiveQuery(this.losAttackRangeQuery); | |||||
let targets = entities.filter(function(v) { return cmpAttack.CanAttack(v) && attackfilter(v); }) | |||||
.sort(function(a, b) { return cmpAttack.CompareEntitiesByPreference(a, b); }); | |||||
Done Inline ActionsOld method: filtering + sorting right away (probably o(n) and o(n log n)) wraitii: Old method: filtering + sorting right away (probably o(n) and o(n log n)) | |||||
return targets; | |||||
}; | |||||
UnitAI.prototype.GetQueryRange = function(iid) | UnitAI.prototype.GetQueryRange = function(iid) | ||||
{ | { | ||||
let ret = { "min": 0, "max": 0 }; | let ret = { "min": 0, "max": 0 }; | ||||
let cmpVision = Engine.QueryInterface(this.entity, IID_Vision); | let cmpVision = Engine.QueryInterface(this.entity, IID_Vision); | ||||
if (!cmpVision) | if (!cmpVision) | ||||
return ret; | return ret; | ||||
let visionRange = cmpVision.GetRange(); | let visionRange = cmpVision.GetRange(); | ||||
if (iid === IID_Vision) | if (iid === IID_Vision) | ||||
{ | { | ||||
ret.max = visionRange; | ret.max = visionRange; | ||||
return ret; | return ret; | ||||
} | } | ||||
if (this.GetStance().respondStandGround) | if (this.GetStance().respondStandGround) | ||||
Done Inline ActionsNew method: return the first unit at preference 0, otherwise O(n) for filtering, then O(k) for returning - there's no sorting, so it's likely faster anyways, but most units have rather large preferred classes which lead to very good results. wraitii: New method: return the first unit at preference 0, otherwise O(n) for filtering, then O(k) for… | |||||
{ | { | ||||
let range = this.GetRange(iid); | let range = this.GetRange(iid); | ||||
if (!range) | if (!range) | ||||
return ret; | return ret; | ||||
ret.min = range.min; | ret.min = range.min; | ||||
ret.max = Math.min(range.max, visionRange); | ret.max = Math.min(range.max, visionRange); | ||||
} | } | ||||
else if (this.GetStance().respondChase) | else if (this.GetStance().respondChase) | ||||
▲ Show 20 Lines • Show All 462 Lines • Show Last 20 Lines |
Wildfire Games · Phabricator
Method 'Order.WalkToTarget' expected no return value.