Changeset View
Standalone View
binaries/data/mods/public/simulation/components/UnitAI.js
Show First 20 Lines • Show All 324 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 407 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 969 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: 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 239 Lines • ▼ Show 20 Lines | "GATHER": { | |||||||||
if (msg.likelyFailure || msg.obstructed && this.RelaxedMaxRangeCheck(this.order.data, this.DefaultRelaxedMaxRange) || | if (msg.likelyFailure || msg.obstructed && this.RelaxedMaxRangeCheck(this.order.data, this.DefaultRelaxedMaxRange) || | |||||||||
this.CheckRange(this.order.data)) | this.CheckRange(this.order.data)) | |||||||||
this.SetNextState("GATHERING"); | this.SetNextState("GATHERING"); | |||||||||
}, | }, | |||||||||
}, | }, | |||||||||
"GATHERING": { | "GATHERING": { | |||||||||
"enter": function() { | "enter": function() { | |||||||||
this.gatheringTarget = this.order.data.target || INVALID_ENTITY; // deleted in "leave". | let cmpResourceGatherer = Engine.QueryInterface(this.entity, IID_ResourceGatherer); | |||||||||
if (!cmpResourceGatherer) | ||||||||||
{ | ||||||||||
this.FinishOrder(); | ||||||||||
return true; | ||||||||||
} | ||||||||||
Done Inline ActionsProbably not needed anymore. Freagarach: Probably not needed anymore. | ||||||||||
// Check if the resource is full. | let gatheringTarget = this.order.data.target || INVALID_ENTITY; | |||||||||
// Will only be added if we're not already in. | if (!this.CheckTargetRange(gatheringTarget, IID_ResourceGatherer)) | |||||||||
let cmpSupply = Engine.QueryInterface(this.gatheringTarget, IID_ResourceSupply); | ||||||||||
if (!cmpSupply || !cmpSupply.AddGatherer(this.entity)) | ||||||||||
{ | { | |||||||||
this.SetNextState("FINDINGNEWTARGET"); | this.OnOutOfRange(); | |||||||||
return true; | return true; | |||||||||
} | } | |||||||||
// If this order was forced, the player probably gave it, but now we've reached the target | // If this order was forced, the player probably gave it, but now we've reached the target | |||||||||
// switch to an unforced order (can be interrupted by attacks) | // switch to an unforced order (can be interrupted by attacks). | |||||||||
this.order.data.force = false; | this.order.data.force = false; | |||||||||
this.order.data.autoharvest = true; | this.order.data.autoharvest = true; | |||||||||
// Calculate timing based on gather rates | let callback = { | |||||||||
// This allows the gather rate to control how often we gather, instead of how much. | "iid": IID_UnitAI, | |||||||||
let cmpResourceGatherer = Engine.QueryInterface(this.entity, IID_ResourceGatherer); | "outOfRange": "OnOutOfRange", | |||||||||
let rate = cmpResourceGatherer.GetTargetGatherRate(this.gatheringTarget); | "targetInvalidated": "OnTargetInvalidated", | |||||||||
"inventoryFilled": "OnInventoryFilled" | ||||||||||
if (!rate) | }; | |||||||||
{ | if (!cmpResourceGatherer.StartGathering(gatheringTarget, callback)) | |||||||||
// Try to find another target if the current one stopped existing | ||||||||||
if (!Engine.QueryInterface(this.gatheringTarget, IID_Identity)) | ||||||||||
{ | { | |||||||||
this.SetNextState("FINDINGNEWTARGET"); | this.SetNextState("FINDINGNEWTARGET"); | |||||||||
Done Inline Actions
Freagarach: | ||||||||||
return true; | return true; | |||||||||
} | } | |||||||||
// No rate, give up on gathering | ||||||||||
this.FinishOrder(); | ||||||||||
return true; | ||||||||||
} | ||||||||||
// Scale timing interval based on rate, and start timer | ||||||||||
// The offset should be at least as long as the repeat time so we use the same value for both. | ||||||||||
let offset = 1000 / rate; | ||||||||||
this.StartTimer(offset, offset); | ||||||||||
// We want to start the gather animation as soon as possible, | ||||||||||
// but only if we're actually at the target and it's still alive | ||||||||||
// (else it'll look like we're chopping empty air). | ||||||||||
// (If it's not alive, the Timer handler will deal with sending us | ||||||||||
// off to a different target.) | ||||||||||
if (this.CheckTargetRange(this.gatheringTarget, IID_ResourceGatherer)) | ||||||||||
{ | ||||||||||
this.StopMoving(); | this.StopMoving(); | |||||||||
this.SetDefaultAnimationVariant(); | this.SetDefaultAnimationVariant(); | |||||||||
this.FaceTowardsTarget(this.order.data.target); | this.FaceTowardsTarget(this.order.data.target); | |||||||||
this.SelectAnimation("gather_" + this.order.data.type.specific); | this.SelectAnimation("gather_" + this.order.data.type.specific); | |||||||||
Done Inline ActionsHere or in cmpResourceGatherer? Freagarach: Here or in `cmpResourceGatherer`? | ||||||||||
} | this.StartTimer(1000, 1000); | |||||||||
return false; | return false; | |||||||||
}, | }, | |||||||||
"leave": function() { | "leave": function() { | |||||||||
this.StopTimer(); | this.StopTimer(); | |||||||||
// Don't use ownership because this is called after a conversion/resignation | let cmpResourceGatherer = Engine.QueryInterface(this.entity, IID_ResourceGatherer); | |||||||||
// and the ownership would be invalid then. | if (cmpResourceGatherer) | |||||||||
let cmpSupply = Engine.QueryInterface(this.gatheringTarget, IID_ResourceSupply); | cmpResourceGatherer.StopGathering(); | |||||||||
if (cmpSupply) | ||||||||||
cmpSupply.RemoveGatherer(this.entity); | ||||||||||
delete this.gatheringTarget; | ||||||||||
this.ResetAnimation(); | this.ResetAnimation(); | |||||||||
}, | }, | |||||||||
"Timer": function(msg) { | "InventoryFilled": function(msg) { | |||||||||
Done Inline ActionsCan be removed. Freagarach: Can be removed. | ||||||||||
let resourceTemplate = this.order.data.template; | let nearestDropsite = this.FindNearestDropsite(this.order.data.type.generic); | |||||||||
let resourceType = this.order.data.type; | if (!nearestDropsite) | |||||||||
this.FinishOrder(); | ||||||||||
// TODO: we are leaking information here - if the target died in FOW, we'll know it's dead | this.PushOrderFront("ReturnResource", { "target": nearestDropsite, "force": false }); | |||||||||
// straight away. | ||||||||||
// Seems one would have to listen to ownership changed messages to make it work correctly | ||||||||||
// but that's likely prohibitively expansive performance wise. | ||||||||||
Done Inline ActionsDon't like the fact we have two timers running now. Since this patch will set the defaults for many future patches we have to think about the design. Wouldn't it be much cleaner if the gather components sends a message when it is out of range? Then unitAI can decide how to act upon that. This also allows (in theory) for "multi-actions" (e.g. walking and gathering) since in that case we can simply ignore the out of range messages. bb: Don't like the fact we have two timers running now. Since this patch will set the defaults for… | ||||||||||
Done Inline ActionsIt is also possible to "soft code" a dependency on unitAI, by fetching the component and calling ProcessMessage directly. This is probably better when we want to have a channel between two components only. Can't comment on the rest so far, but I need to look at this patch. wraitii: It is also possible to "soft code" a dependency on unitAI, by fetching the component and… | ||||||||||
let cmpSupply = Engine.QueryInterface(this.gatheringTarget, IID_ResourceSupply); | }, | |||||||||
// If we can't gather from the target, find a new one. | ||||||||||
if (!cmpSupply || !cmpSupply.IsAvailableTo(this.entity) || | ||||||||||
!this.CanGather(this.gatheringTarget)) | ||||||||||
{ | ||||||||||
this.SetNextState("FINDINGNEWTARGET"); | ||||||||||
return; | ||||||||||
} | ||||||||||
if (!this.CheckTargetRange(this.gatheringTarget, IID_ResourceGatherer)) | "OutOfRange": function(msg) { | |||||||||
{ | if (this.MoveToTargetRange(this.order.data.target, IID_ResourceGatherer)) | |||||||||
// Try to follow the target | ||||||||||
if (this.MoveToTargetRange(this.gatheringTarget, IID_ResourceGatherer)) | ||||||||||
this.SetNextState("APPROACHING"); | this.SetNextState("APPROACHING"); | |||||||||
// Our target is no longer visible - go to its last known position first | // Our target is no longer visible - go to its last known position first | |||||||||
Done Inline ActionsThis sounds like useless, we should check whether we can gather from a target anyway, when approaching an old target (the target can exhaust too while returning the resource) bb: This sounds like useless, we should check whether we can gather from a target anyway, when… | ||||||||||
// and then hopefully it will become visible. | // and then hopefully it will become visible. | |||||||||
else if (!this.CheckTargetVisible(this.gatheringTarget) && this.order.data.lastPos) | else if (!this.CheckTargetVisible(this.order.data.target) && this.order.data.lastPos) | |||||||||
Done Inline ActionsWe could get it from the order data. Freagarach: We could get it from the order data. | ||||||||||
this.PushOrderFront("Walk", { | this.PushOrderFront("Walk", { | |||||||||
"x": this.order.data.lastPos.x, | "x": this.order.data.lastPos.x, | |||||||||
"z": this.order.data.lastPos.z, | "z": this.order.data.lastPos.z, | |||||||||
"force": this.order.data.force | "force": this.order.data.force | |||||||||
}); | }); | |||||||||
else | else | |||||||||
this.SetNextState("FINDINGNEWTARGET"); | this.SetNextState("FINDINGNEWTARGET"); | |||||||||
return; | }, | |||||||||
} | ||||||||||
// Gather the resources: | ||||||||||
let cmpResourceGatherer = Engine.QueryInterface(this.entity, IID_ResourceGatherer); | ||||||||||
// Try to gather treasure | ||||||||||
if (cmpResourceGatherer.TryInstantGather(this.gatheringTarget)) | ||||||||||
return; | ||||||||||
// If we've already got some resources but they're the wrong type, | "TargetInvalidated": function(msg) { | |||||||||
// drop them first to ensure we're only ever carrying one type | this.SetNextState("FINDINGNEWTARGET"); | |||||||||
if (cmpResourceGatherer.IsCarryingAnythingExcept(resourceType.generic)) | }, | |||||||||
cmpResourceGatherer.DropResources(); | ||||||||||
"Timer": function(msg) { | ||||||||||
this.FaceTowardsTarget(this.order.data.target); | this.FaceTowardsTarget(this.order.data.target); | |||||||||
// Collect from the target | ||||||||||
let status = cmpResourceGatherer.PerformGather(this.gatheringTarget); | ||||||||||
// If we've collected as many resources as possible, | ||||||||||
// return to the nearest dropsite | ||||||||||
if (status.filled) | ||||||||||
{ | ||||||||||
let nearestDropsite = this.FindNearestDropsite(resourceType.generic); | ||||||||||
if (nearestDropsite) | ||||||||||
{ | ||||||||||
// (Keep this Gather order on the stack so we'll | ||||||||||
// continue gathering after returning) | ||||||||||
// However mark our target as invalid if it's exhausted, so we don't waste time | ||||||||||
// trying to gather from it. | ||||||||||
if (status.exhausted) | ||||||||||
this.order.data.target = INVALID_ENTITY; | ||||||||||
this.PushOrderFront("ReturnResource", { "target": nearestDropsite, "force": false }); | ||||||||||
return; | ||||||||||
} | ||||||||||
// Oh no, couldn't find any drop sites. Give up on gathering. | ||||||||||
this.FinishOrder(); | ||||||||||
return; | ||||||||||
} | ||||||||||
if (status.exhausted) | ||||||||||
this.SetNextState("FINDINGNEWTARGET"); | ||||||||||
}, | }, | |||||||||
Done Inline ActionsThis is a pain, might be good to have a function in the position component that follows the rotation of a unit (so it faces keeps facing to a target whatever movement is present). Needs some proper design there probably bb: This is a pain, might be good to have a function in the position component that follows the… | ||||||||||
Done Inline ActionsSince all gatherables in vanilla are either static or to be killed before gathering, we could consider just ditching this call? Freagarach: Since all gatherables in vanilla are either static or to be killed before gathering, we could… | ||||||||||
Done Inline ActionsFreagarach: D2870. | ||||||||||
}, | }, | |||||||||
"FINDINGNEWTARGET": { | "FINDINGNEWTARGET": { | |||||||||
"enter": function() { | "enter": function() { | |||||||||
let previousTarget = this.order.data.target; | let previousTarget = this.order.data.target; | |||||||||
let resourceTemplate = this.order.data.template; | let resourceTemplate = this.order.data.template; | |||||||||
let resourceType = this.order.data.type; | let resourceType = this.order.data.type; | |||||||||
Show All 37 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 791 Lines • ▼ Show 20 Lines | "FEEDING": { | |||||||||
}, | }, | |||||||||
}, | }, | |||||||||
"FLEEING": "INDIVIDUAL.FLEEING", | "FLEEING": "INDIVIDUAL.FLEEING", | |||||||||
"COMBAT": "INDIVIDUAL.COMBAT", | "COMBAT": "INDIVIDUAL.COMBAT", | |||||||||
"WALKING": "INDIVIDUAL.WALKING", // reuse the same walking behaviour for animals | "WALKING": "INDIVIDUAL.WALKING", // reuse the same walking behaviour for animals | |||||||||
// only used for domestic animals | // only used for domestic animals | |||||||||
Lint: ESLintBear (indent) Expected indentation of 2 tabs but found 7. Lint: ESLintBear (indent): `Expected indentation of 2 tabs but found 7.` | ||||||||||
"CHEERING": { | "CHEERING": { | |||||||||
"enter": function() { | "enter": function() { | |||||||||
this.SelectAnimation("promotion"); | this.SelectAnimation("promotion"); | |||||||||
this.StartTimer(2800); | this.StartTimer(2800); | |||||||||
return false; | return false; | |||||||||
}, | }, | |||||||||
▲ Show 20 Lines • Show All 59 Lines • ▼ Show 20 Lines | ||||||||||
UnitAI.prototype.IsFormationMember = function() | UnitAI.prototype.IsFormationMember = function() | |||||||||
{ | { | |||||||||
return (this.formationController != INVALID_ENTITY); | return (this.formationController != INVALID_ENTITY); | |||||||||
}; | }; | |||||||||
UnitAI.prototype.IsAnimal = function() | UnitAI.prototype.IsAnimal = function() | |||||||||
{ | { | |||||||||
return (this.template.NaturalBehaviour ? true : false); | return (this.template.NaturalBehaviour ? true : false); | |||||||||
Lint: ESLintBear (no-unneeded-ternary) Unnecessary use of boolean literals in conditional expression. Lint: ESLintBear (no-unneeded-ternary): `Unnecessary use of boolean literals in conditional expression.` | ||||||||||
}; | }; | |||||||||
UnitAI.prototype.IsDangerousAnimal = function() | UnitAI.prototype.IsDangerousAnimal = function() | |||||||||
{ | { | |||||||||
return (this.IsAnimal() && (this.template.NaturalBehaviour == "violent" || | return (this.IsAnimal() && (this.template.NaturalBehaviour == "violent" || | |||||||||
this.template.NaturalBehaviour == "aggressive")); | this.template.NaturalBehaviour == "aggressive")); | |||||||||
}; | }; | |||||||||
▲ Show 20 Lines • Show All 123 Lines • ▼ Show 20 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; | |||||||||
} | } | |||||||||
}; | }; | |||||||||
/** | /** | |||||||||
▲ Show 20 Lines • Show All 112 Lines • ▼ Show 20 Lines | UnitAI.prototype.SetupAttackRangeQuery = function(enable = true) | |||||||||
let range = this.GetQueryRange(IID_Attack); | let range = this.GetQueryRange(IID_Attack); | |||||||||
this.losAttackRangeQuery = cmpRangeManager.CreateActiveQuery(this.entity, range.min, range.max, players, IID_Resistance, cmpRangeManager.GetEntityFlagMask("normal")); | this.losAttackRangeQuery = cmpRangeManager.CreateActiveQuery(this.entity, range.min, range.max, players, IID_Resistance, cmpRangeManager.GetEntityFlagMask("normal")); | |||||||||
if (enable) | if (enable) | |||||||||
cmpRangeManager.EnableActiveQuery(this.losAttackRangeQuery); | cmpRangeManager.EnableActiveQuery(this.losAttackRangeQuery); | |||||||||
}; | }; | |||||||||
//// FSM linkage functions //// | //// FSM linkage functions //// | |||||||||
Lint: ESLintBear (spaced-comment) Expected space or tab after '//' in comment. Lint: ESLintBear (spaced-comment): `Expected space or tab after '//' in comment.` | ||||||||||
// Setting the next state to the current state will leave/re-enter the top-most substate. | // Setting the next state to the current state will leave/re-enter the top-most substate. | |||||||||
UnitAI.prototype.SetNextState = function(state) | UnitAI.prototype.SetNextState = function(state) | |||||||||
{ | { | |||||||||
this.UnitFsm.SetNextState(this, state); | this.UnitFsm.SetNextState(this, state); | |||||||||
}; | }; | |||||||||
UnitAI.prototype.DeferMessage = function(msg) | UnitAI.prototype.DeferMessage = function(msg) | |||||||||
▲ Show 20 Lines • Show All 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 before '}'. Lint: ESLintBear (object-curly-spacing): `A space is required before '}'.` | ||||||||||
Lint: ESLintBear (object-curly-spacing) A space is required after '{'. Lint: ESLintBear (object-curly-spacing): `A space is required after '{'.` | ||||||||||
Engine.PostMessage(this.entity, MT_UnitAIOrderDataChanged, { "to": this.GetOrderData() }); | Engine.PostMessage(this.entity, MT_UnitAIOrderDataChanged, { "to": this.GetOrderData() }); | |||||||||
return; | return; | |||||||||
} | } | |||||||||
this.PushOrder(type, data); | this.PushOrder(type, data); | |||||||||
} | } | |||||||||
Engine.PostMessage(this.entity, MT_UnitAIOrderDataChanged, { "to": this.GetOrderData() }); | Engine.PostMessage(this.entity, MT_UnitAIOrderDataChanged, { "to": this.GetOrderData() }); | |||||||||
}; | }; | |||||||||
▲ Show 20 Lines • Show All 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 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 '}'.` | ||||||||||
}; | }; | |||||||||
/** | /** | |||||||||
* 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 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.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 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.OnOutOfRange = function(msg) | ||||||||||
{ | ||||||||||
this.UnitFsm.ProcessMessage(this, { "type": "OutOfRange", "data": msg }); | ||||||||||
}; | ||||||||||
UnitAI.prototype.OnTargetInvalidated = function(msg) | ||||||||||
{ | ||||||||||
this.UnitFsm.ProcessMessage(this, { "type": "TargetInvalidated", "data": msg }); | ||||||||||
}; | ||||||||||
UnitAI.prototype.OnInventoryFilled = function(msg) | ||||||||||
{ | ||||||||||
this.UnitFsm.ProcessMessage(this, { "type": "InventoryFilled", "data": msg }); | ||||||||||
}; | ||||||||||
//// 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: 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.GetStance().respondChaseBeyondVision || this.CheckTargetIsInVisionRange(target)) | && (this.GetStance().respondChaseBeyondVision || this.CheckTargetIsInVisionRange(target)) | |||||||||
Lint: ESLintBear (operator-linebreak) '&&' should be placed at the end of the line. Lint: ESLintBear (operator-linebreak): `'&&' should be placed at the end of the line.` | ||||||||||
Lint: JSHintBear Misleading line break before '&&'; readers may interpret this as an expression boundary. Lint: JSHintBear: `Misleading line break before '&&'; readers may interpret this as an expression boundary.` | ||||||||||
); | ); | |||||||||
if (!target) | if (!target) | |||||||||
return false; | return false; | |||||||||
this.PushOrderFront("Attack", { "target": target, "force": false, "allowCapture": true }); | this.PushOrderFront("Attack", { "target": target, "force": false, "allowCapture": true }); | |||||||||
return true; | return true; | |||||||||
}; | }; | |||||||||
▲ Show 20 Lines • Show All 46 Lines • ▼ Show 20 Lines | ||||||||||
}; | }; | |||||||||
/** | /** | |||||||||
* Try to respond to healable entities. | * Try to respond to healable entities. | |||||||||
* Returns true if it responded. | * Returns true if it responded. | |||||||||
*/ | */ | |||||||||
UnitAI.prototype.RespondToHealableEntities = function(ents) | UnitAI.prototype.RespondToHealableEntities = function(ents) | |||||||||
{ | { | |||||||||
let ent = ents.find(ent => this.CanHeal(ent)); | let ent = ents.find(ent => this.CanHeal(ent)); | |||||||||
Lint: ESLintBear (no-shadow) 'ent' is already declared in the upper scope. Lint: ESLintBear (no-shadow): `'ent' is already declared in the upper scope.` | ||||||||||
if (!ent) | if (!ent) | |||||||||
return false; | return false; | |||||||||
this.PushOrderFront("Heal", { "target": ent, "force": false }); | this.PushOrderFront("Heal", { "target": ent, "force": false }); | |||||||||
return true; | return true; | |||||||||
}; | }; | |||||||||
/** | /** | |||||||||
Show All 9 Lines | if (force) | |||||||||
return false; | return false; | |||||||||
// If we are guarding/escorting, don't abandon as long as the guarded unit is in target range of the attacker | // If we are guarding/escorting, don't abandon as long as the guarded unit is in target range of the attacker | |||||||||
if (this.isGuardOf) | if (this.isGuardOf) | |||||||||
{ | { | |||||||||
let cmpUnitAI = Engine.QueryInterface(target, IID_UnitAI); | let cmpUnitAI = Engine.QueryInterface(target, IID_UnitAI); | |||||||||
let cmpAttack = Engine.QueryInterface(target, IID_Attack); | let cmpAttack = Engine.QueryInterface(target, IID_Attack); | |||||||||
if (cmpUnitAI && cmpAttack && | if (cmpUnitAI && cmpAttack && | |||||||||
cmpAttack.GetAttackTypes().some(type => cmpUnitAI.CheckTargetAttackRange(this.isGuardOf, type))) | cmpAttack.GetAttackTypes().some(type => cmpUnitAI.CheckTargetAttackRange(this.isGuardOf, type))) | |||||||||
Lint: ESLintBear (no-shadow) 'type' is already declared in the upper scope. Lint: ESLintBear (no-shadow): `'type' is already declared in the upper scope.` | ||||||||||
return false; | return false; | |||||||||
Lint: ESLintBear (indent) Expected indentation of 3 tabs but found 4. Lint: ESLintBear (indent): `Expected indentation of 3 tabs but found 4.` | ||||||||||
} | } | |||||||||
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 20 Lines • Show All 135 Lines • ▼ Show 20 Lines | if (!orderData) | |||||||||
orderData = this.order.data; | orderData = this.order.data; | |||||||||
let cmpPosition = Engine.QueryInterface(orderData.target, IID_Position); | let cmpPosition = Engine.QueryInterface(orderData.target, IID_Position); | |||||||||
if (cmpPosition && cmpPosition.IsInWorld()) | if (cmpPosition && cmpPosition.IsInWorld()) | |||||||||
orderData.lastPos = cmpPosition.GetPosition(); | orderData.lastPos = cmpPosition.GetPosition(); | |||||||||
}; | }; | |||||||||
UnitAI.prototype.SetHeldPosition = function(x, z) | UnitAI.prototype.SetHeldPosition = function(x, z) | |||||||||
{ | { | |||||||||
this.heldPosition = {"x": x, "z": z}; | this.heldPosition = {"x": x, "z": z}; | |||||||||
Lint: ESLintBear (object-curly-spacing) A space is required before '}'. Lint: ESLintBear (object-curly-spacing): `A space is required before '}'.` | ||||||||||
Lint: ESLintBear (object-curly-spacing) A space is required after '{'. Lint: ESLintBear (object-curly-spacing): `A space is required after '{'.` | ||||||||||
}; | }; | |||||||||
UnitAI.prototype.SetHeldPositionOnEntity = function(entity) | UnitAI.prototype.SetHeldPositionOnEntity = function(entity) | |||||||||
{ | { | |||||||||
var cmpPosition = Engine.QueryInterface(this.entity, IID_Position); | var cmpPosition = Engine.QueryInterface(this.entity, IID_Position); | |||||||||
if (!cmpPosition || !cmpPosition.IsInWorld()) | if (!cmpPosition || !cmpPosition.IsInWorld()) | |||||||||
return; | return; | |||||||||
var pos = cmpPosition.GetPosition(); | var pos = cmpPosition.GetPosition(); | |||||||||
Show All 10 Lines | UnitAI.prototype.WalkToHeldPosition = function() | |||||||||
if (this.heldPosition) | if (this.heldPosition) | |||||||||
{ | { | |||||||||
this.AddOrder("Walk", { "x": this.heldPosition.x, "z": this.heldPosition.z, "force": false }, false); | this.AddOrder("Walk", { "x": this.heldPosition.x, "z": this.heldPosition.z, "force": false }, false); | |||||||||
return true; | return true; | |||||||||
} | } | |||||||||
return false; | return false; | |||||||||
}; | }; | |||||||||
//// Helper functions //// | //// Helper functions //// | |||||||||
Lint: ESLintBear (spaced-comment) Expected space or tab after '//' in comment. Lint: ESLintBear (spaced-comment): `Expected space or tab after '//' in comment.` | ||||||||||
/** | /** | |||||||||
* General getter for ranges. | * General getter for ranges. | |||||||||
* | * | |||||||||
* @param {number} iid | * @param {number} iid | |||||||||
* @param {string} type - [Optional] | * @param {string} type - [Optional] | |||||||||
* @return {Object | undefined} - The range in the form | * @return {Object | undefined} - The range in the form | |||||||||
* { "min": number, "max": number } | * { "min": number, "max": number } | |||||||||
* Object."elevationBonus": number may be present when iid == IID_Attack. | * Object."elevationBonus": number may be present when iid == IID_Attack. | |||||||||
* Returns undefined when the entity does not have the requested component. | * Returns undefined when the entity does not have the requested component. | |||||||||
*/ | */ | |||||||||
UnitAI.prototype.GetRange = function(iid, type) | UnitAI.prototype.GetRange = function(iid, type) | |||||||||
{ | { | |||||||||
let component = Engine.QueryInterface(this.entity, iid); | let component = Engine.QueryInterface(this.entity, iid); | |||||||||
if (!component) | if (!component) | |||||||||
return undefined; | return undefined; | |||||||||
return component.GetRange(type); | return component.GetRange(type); | |||||||||
} | } | |||||||||
Lint: ESLintBear (semi) Missing semicolon. Lint: ESLintBear (semi): `Missing semicolon.` | ||||||||||
Lint: JSHintBear Missing semicolon. Lint: JSHintBear: `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: 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.` | ||||||||||
}; | }; | |||||||||
//// Animal specific functions //// | //// Animal specific functions //// | |||||||||
Lint: ESLintBear (spaced-comment) Expected space or tab after '//' in comment. Lint: ESLintBear (spaced-comment): `Expected space or tab after '//' in comment.` | ||||||||||
UnitAI.prototype.MoveRandomly = function(distance) | UnitAI.prototype.MoveRandomly = function(distance) | |||||||||
{ | { | |||||||||
// To minimize drift all across the map, animals describe circles | // To minimize drift all across the map, animals describe circles | |||||||||
// approximated by polygons. | // approximated by polygons. | |||||||||
// And to avoid getting stuck in obstacles or narrow spaces, each side | // And to avoid getting stuck in obstacles or narrow spaces, each side | |||||||||
// of the polygon is obtained by trying to go away from a point situated | // of the polygon is obtained by trying to go away from a point situated | |||||||||
// half a meter backwards of the current position, after rotation. | // half a meter backwards of the current position, after rotation. | |||||||||
Show All 34 Lines | UnitAI.prototype.SetFacePointAfterMove = function(val) | |||||||||
if (cmpMotion) | if (cmpMotion) | |||||||||
cmpMotion.SetFacePointAfterMove(val); | cmpMotion.SetFacePointAfterMove(val); | |||||||||
}; | }; | |||||||||
UnitAI.prototype.GetFacePointAfterMove = function() | UnitAI.prototype.GetFacePointAfterMove = function() | |||||||||
{ | { | |||||||||
let cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion); | let cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion); | |||||||||
return cmpUnitMotion && cmpUnitMotion.GetFacePointAfterMove(); | return cmpUnitMotion && cmpUnitMotion.GetFacePointAfterMove(); | |||||||||
} | } | |||||||||
Lint: JSHintBear Missing semicolon. Lint: JSHintBear: `Missing semicolon.` | ||||||||||
Lint: ESLintBear (semi) Missing semicolon. Lint: ESLintBear (semi): `Missing semicolon.` | ||||||||||
UnitAI.prototype.AttackEntitiesByPreference = function(ents) | UnitAI.prototype.AttackEntitiesByPreference = function(ents) | |||||||||
{ | { | |||||||||
if (!ents.length) | if (!ents.length) | |||||||||
return false; | return false; | |||||||||
var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack); | var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack); | |||||||||
if (!cmpAttack) | if (!cmpAttack) | |||||||||
▲ Show 20 Lines • Show All 102 Lines • Show Last 20 Lines |
Method 'Order.WalkToTarget' expected no return value.