Changeset View
Standalone View
binaries/data/mods/public/simulation/components/UnitAI.js
Show First 20 Lines • Show All 551 Lines • ▼ Show 20 Lines | if (this.MustKillGatherTarget(this.order.data.target)) | ||||
"z": data.lastPos.z, | "z": data.lastPos.z, | ||||
"type": data.type, | "type": data.type, | ||||
"template": data.template | "template": data.template | ||||
}); | }); | ||||
} | } | ||||
return; | return; | ||||
} | } | ||||
this.PushOrderFront("Attack", { "target": this.order.data.target, "force": !!this.order.data.force, "hunting": true, "allowCapture": false }); | this.PushOrderFront("Attack", { | ||||
"target": this.order.data.target, | |||||
"force": !!this.order.data.force, | |||||
"hunting": true, | |||||
"allowCapture": false | |||||
}); | |||||
return; | return; | ||||
} | } | ||||
this.RememberTargetPosition(); | this.RememberTargetPosition(); | ||||
if (!this.order.data.initPos) | if (!this.order.data.initPos) | ||||
this.order.data.initPos = this.order.data.lastPos; | this.order.data.initPos = this.order.data.lastPos; | ||||
if (this.CheckTargetRange(this.order.data.target, IID_ResourceGatherer)) | if (this.CheckTargetRange(this.order.data.target, IID_ResourceGatherer)) | ||||
▲ Show 20 Lines • Show All 214 Lines • ▼ Show 20 Lines | "Order.Garrison": function(msg) { | ||||
// Check if we are already in range, otherwise walk there | // Check if we are already in range, otherwise walk there | ||||
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 | |||||
{ | |||||
this.SetNextState("GARRISON.APPROACHING"); | this.SetNextState("GARRISON.APPROACHING"); | ||||
return; | return; | ||||
} | |||||
} | } | ||||
elexis: Duplication should be avoided. I guess the return is not worth to speak of.
It could become `if… | |||||
this.SetNextState("GARRISON.GARRISONING"); | this.SetNextState("GARRISON.GARRISONING"); | ||||
}, | }, | ||||
"Order.Gather": function(msg) { | "Order.Gather": function(msg) { | ||||
if (this.MustKillGatherTarget(msg.data.target)) | if (this.MustKillGatherTarget(msg.data.target)) | ||||
{ | { | ||||
// The target was visible when this order was given, | // The target was visible when this order was given, | ||||
Show All 14 Lines | "Order.Gather": function(msg) { | ||||
"x": data.lastPos.x, | "x": data.lastPos.x, | ||||
"z": data.lastPos.z, | "z": data.lastPos.z, | ||||
"type": data.type, | "type": data.type, | ||||
"template": data.template | "template": data.template | ||||
}); | }); | ||||
} | } | ||||
return; | return; | ||||
} | } | ||||
this.PushOrderFront("Attack", { "target": msg.data.target, "force": !!msg.data.force, "hunting": true, "allowCapture": false, "min": 0, "max": 10 }); | this.PushOrderFront("Attack", { | ||||
"target": msg.data.target, | |||||
"force": !!msg.data.force, | |||||
"hunting": true, | |||||
"allowCapture": false, | |||||
"min": 0, | |||||
"max": 10 | |||||
}); | |||||
return; | return; | ||||
} | } | ||||
// TODO: on what should we base this range? | // TODO: on what should we base this range? | ||||
// Check if we are already in range, otherwise walk there | // Check if we are already in range, otherwise walk there | ||||
if (!this.CheckTargetRangeExplicit(msg.data.target, 0, 10)) | if (!this.CheckTargetRangeExplicit(msg.data.target, 0, 10)) | ||||
{ | { | ||||
if (!this.CanGather(msg.data.target) || !this.CheckTargetVisible(msg.data.target)) | if (!this.CanGather(msg.data.target) || !this.CheckTargetVisible(msg.data.target)) | ||||
▲ Show 20 Lines • Show All 219 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": { | ||||
"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 961 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 we are capturing and are attacked by something that we would not capture, attack that entity instead | // If we are capturing and are attacked by something that we would not capture, attack that entity instead | ||||
if (this.order.data.attackType == "Capture" && (this.GetStance().targetAttackersAlways || !this.order.data.force) | if (this.order.data.attackType == "Capture" && (this.GetStance().targetAttackersAlways || !this.order.data.force) && | ||||
&& this.order.data.target != msg.data.attacker && this.GetBestAttackAgainst(msg.data.attacker, true) != "Capture") | this.order.data.target != msg.data.attacker && this.GetBestAttackAgainst(msg.data.attacker, true) != "Capture") | ||||
this.RespondToTargetedEntities([msg.data.attacker]); | this.RespondToTargetedEntities([msg.data.attacker]); | ||||
}, | }, | ||||
Done Inline Actionsyess about the && We had an unwritten law (as in someone had a strong opinion) on whitespace for the secnod line of if statements: \tif (x && \t z) That's same amount of tabs and 4 spaces (i.e. same character count as if (). I doubt one can make an ESlint rule for that. I guess I dont care if I commit the tab or 4 spaces. Aside, I would recommend to put every of these conjunctions (https://en.wikipedia.org/wiki/Conjunctive_normal_form) on a separate line, so it's easier to view the structure: 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") elexis: yess about the &&
We had an unwritten law (as in someone had a strong opinion) on whitespace… | |||||
Done Inline Actions(Done) On a related note, I think this makes a good case for always including curly braces. In the following it is not obvious at first that the last line is not part of the conditional. "Attacked": function(msg) { // If we are capturing and are attacked by something that we would not capture, attack that entity instead if (this.order.data.attackType == "Capture" && (this.GetStance().targetAttackersAlways || !this.order.data.force) && this.order.data.target != msg.data.attacker && this.GetBestAttackAgainst(msg.data.attacker, true) != "Capture") this.RespondToTargetedEntities([msg.data.attacker]); }, Krinkle: (Done)
On a related note, I think this makes a good case for always including curly braces. | |||||
Done Inline ActionsI suppose anyone who really wants to learn the content of the line will discover it very quickly, since every if statement has a consequent, so it can only be the last one. For these cases it certainly isnt controversial to add the curly braces. elexis: I suppose anyone who really wants to learn the content of the line will discover it very… | |||||
Not Done Inline ActionsDoes the linter allow us to make the last tab for those 3 lines 4 spaces? elexis: Does the linter allow us to make the last tab for those 3 lines 4 spaces? | |||||
Not Done Inline ActionsNo, it does not. Outside strings and comments, the only place where spaces are allowed for indentation is for subsequent lines of a block comment: \t /** \t _ * … \t _ * … \t _ */ I do personally dislike that the conditional body and conditions align directly. There's a number of different styles to avoid that, though. The easiest one would be to insert the braces which helps set the two apart. Before "Attacked": function(msg) { if (this.order.data.attackType == "Capture" && (this.GetStance().….force) && this.order.data.target != msg.data.attacker && this.GetBestAttackAgainst(msg…,) != "Capture") this.RespondToTargetedEntities([msg.data.attacker]); }, After "Attacked": function(msg) { if (this.order.data.attackType == "Capture" && (this.GetStance().….force) && this.order.data.target != msg.data.attacker && this.GetBestAttackAgainst(msg.…) != "Capture") { this.RespondToTargetedEntities([msg.data.attacker]); } }, Krinkle: No, it does not. Outside strings and comments, the only place where spaces are allowed for… | |||||
Not Done Inline ActionsIt's more that the conditions of the if align with the if. I suppose the linter policy derives from our coding conventions, and the code derives from our coding conventions, not the coding conventions from the linter. This was objected in several instances before, http://irclogs.wildfiregames.com/ 2016/2016-01/2016-01-02-QuakeNet-#0ad-dev.log:17:47 <@leper> use spaces to align the condition for that while if you have to 2016/2016-01/2016-01-19-QuakeNet-#0ad-dev.log:19:06 <@leper> elexis: one tab and align with spaces 2016/2016-03/2016-03-15-QuakeNet-#0ad-dev.log:18:03 <@leper> 1 tab, spaces to align 2016/2016-04/2016-04-12-QuakeNet-#0ad-dev.log:15:50 <@leper> elexis: the second line of the if should use spaces to align with the first line 2016/2016-05/2016-05-17-QuakeNet-#0ad-dev.log:20:49 < elexis> leper: there are so many spaces that the second line of the if statement has more indentation than the return, so it doesnt look like its aligned Ive discussed it with Philip in 2016 too and then became convinced that it's okay to align the conditions of the if statement with spaces. If we decide to drop that style, then we can probably identify all cases and change all cases in one step. I find 319 matches in JS files for (1 tab + 4 space) and 416 for 1 tab + 3 space. So we should decide one way or the other, but what the linter supports sounds like something that shouldn't be decisive, the linter should adapt to whatever we come up with. elexis: It's more that the conditions of the if align with the if.
I suppose the linter policy derives… | |||||
Not Done Inline ActionsI agree about code and convention first, linter second. I'm bringing it up because it's not entirely arbitrary. The vast majority of JavaScript code in the world, as developed professionally by people who work on it daily and whom might be interested in contributing here, they generally interpret the language and stylistically prefer one of the ways provided by the linter. Because they too held code first and linter second, and implemented the rule in question. At this point it's a trade off to be made:
What is your recommendation for the short-term, to get us to a point where ESLint is warning-free? Disable the rule in this file, directory or block? Change indentation? Krinkle: I agree about code and convention first, linter second.
I'm bringing it up because it's not… | |||||
}, | }, | ||||
"FINDINGNEWTARGET": { | "FINDINGNEWTARGET": { | ||||
"enter": function() { | "enter": function() { | ||||
// Try to find the formation the target was a part of. | // Try to find the formation the target was a part of. | ||||
let cmpFormation = Engine.QueryInterface(this.order.data.target, IID_Formation); | let cmpFormation = Engine.QueryInterface(this.order.data.target, IID_Formation); | ||||
if (!cmpFormation) | if (!cmpFormation) | ||||
cmpFormation = Engine.QueryInterface(this.order.data.formationTarget || INVALID_ENTITY, IID_Formation); | cmpFormation = Engine.QueryInterface(this.order.data.formationTarget || INVALID_ENTITY, IID_Formation); | ||||
▲ Show 20 Lines • Show All 719 Lines • ▼ Show 20 Lines | "REPAIR": { | ||||
return true; | return true; | ||||
} | } | ||||
// Check if the target is still repairable. | // Check if the target is still repairable. | ||||
let cmpHealth = Engine.QueryInterface(this.repairTarget, IID_Health); | let cmpHealth = Engine.QueryInterface(this.repairTarget, IID_Health); | ||||
if (cmpHealth && cmpHealth.GetHitpoints() >= cmpHealth.GetMaxHitpoints()) | if (cmpHealth && cmpHealth.GetHitpoints() >= cmpHealth.GetMaxHitpoints()) | ||||
{ | { | ||||
// The building was already finished/fully repaired before we arrived; | // The building was already finished/fully repaired before we arrived; | ||||
// let the ConstructionFinished handler handle this. | // let the ConstructionFinished handler handle this. | ||||
this.OnGlobalConstructionFinished({"entity": this.repairTarget, "newentity": this.repairTarget}); | this.OnGlobalConstructionFinished({ "entity": this.repairTarget, "newentity": this.repairTarget }); | ||||
Done Inline Actions(I'm a fan of having each property on a separate line if it's more than one property, so that one can see all property names without having to read the values for instance.) elexis: (I'm a fan of having each property on a separate line if it's more than one property, so that… | |||||
Done Inline ActionsDone. I changed it for this line, and also for a few other ones in the same file where an object used more than ~ 100 chars and had multiple keys. Krinkle: Done. I changed it for this line, and also for a few other ones in the same file where an… | |||||
return true; | return true; | ||||
} | } | ||||
this.StopMoving(); | this.StopMoving(); | ||||
let cmpBuilderList = QueryBuilderListInterface(this.repairTarget); | let cmpBuilderList = QueryBuilderListInterface(this.repairTarget); | ||||
if (cmpBuilderList) | if (cmpBuilderList) | ||||
cmpBuilderList.AddBuilder(this.entity); | cmpBuilderList.AddBuilder(this.entity); | ||||
▲ Show 20 Lines • Show All 281 Lines • ▼ Show 20 Lines | "CHEERING": { | ||||
this.StartTimer(2800, 2800); | this.StartTimer(2800, 2800); | ||||
return false; | return false; | ||||
}, | }, | ||||
"leave": function() { | "leave": function() { | ||||
this.StopTimer(); | this.StopTimer(); | ||||
this.ResetAnimation(); | this.ResetAnimation(); | ||||
if (this.formationAnimationVariant) | if (this.formationAnimationVariant) | ||||
this.SetAnimationVariant(this.formationAnimationVariant) | this.SetAnimationVariant(this.formationAnimationVariant); | ||||
else | else | ||||
this.SetDefaultAnimationVariant(); | this.SetDefaultAnimationVariant(); | ||||
var cmpResistance = Engine.QueryInterface(this.entity, IID_Resistance); | var cmpResistance = Engine.QueryInterface(this.entity, IID_Resistance); | ||||
cmpResistance.SetInvulnerability(false); | cmpResistance.SetInvulnerability(false); | ||||
}, | }, | ||||
"Timer": function(msg) { | "Timer": function(msg) { | ||||
this.FinishOrder(); | this.FinishOrder(); | ||||
▲ Show 20 Lines • Show All 204 Lines • ▼ Show 20 Lines | "FEEDING": { | ||||
} | } | ||||
}, | }, | ||||
"Timer": function(msg) { | "Timer": function(msg) { | ||||
this.SetNextState("ROAMING"); | this.SetNextState("ROAMING"); | ||||
}, | }, | ||||
}, | }, | ||||
"FLEEING": "INDIVIDUAL.FLEEING", // reuse the same fleeing behaviour for animals | // reuse the same fleeing behaviour for animals | ||||
"FLEEING": "INDIVIDUAL.FLEEING", | |||||
"COMBAT": "INDIVIDUAL.COMBAT", // reuse the same combat behaviour for animals | // reuse the same combat behaviour for animals | ||||
"COMBAT": "INDIVIDUAL.COMBAT", | |||||
"WALKING": "INDIVIDUAL.WALKING", // reuse the same walking behaviour for animals | // reuse the same walking behaviour for animals | ||||
// only used for domestic animals | // only used for domestic animals | ||||
"WALKING": "INDIVIDUAL.WALKING", | |||||
// Reuse the same garrison behaviour for animals. | // Reuse the same garrison behaviour for animals. | ||||
"GARRISON": "INDIVIDUAL.GARRISON", | "GARRISON": "INDIVIDUAL.GARRISON", | ||||
}, | }, | ||||
}; | }; | ||||
UnitAI.prototype.Init = function() | UnitAI.prototype.Init = function() | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 45 Lines • ▼ Show 20 Lines | |||||
UnitAI.prototype.ResetFinishOrder = function() | UnitAI.prototype.ResetFinishOrder = function() | ||||
{ | { | ||||
this.finishedOrder = false; | this.finishedOrder = false; | ||||
}; | }; | ||||
UnitAI.prototype.IsAnimal = function() | UnitAI.prototype.IsAnimal = function() | ||||
{ | { | ||||
return (this.template.NaturalBehaviour ? true : false); | return this.template.NaturalBehaviour !== undefined; | ||||
}; | }; | ||||
Done Inline ActionsUnneeded parentheses (no ESlint warning about that by any chance?) It warns about ? true : false? Nice. I remember someone not liking the !! way (I liked it because it made the code much shorter in some places), perhaps one can use != undefined, but then one would have to make sure that only falsy values equal to undefined are passed (which probably is the case but I wouldnt want to rely on assumptions) , whatever elexis: Unneeded parentheses (no ESlint warning about that by any chance?)
It warns about `? true… | |||||
Done Inline ActionsI'll remove the parenthesis for now. As follow-up later:
Krinkle: I'll remove the parenthesis for now.
As follow-up later:
1. Look into a rule for redundant… | |||||
Done Inline ActionsNatural behaviour is an optionnal a choice tag. Which means it can either be present or not <UnitAI> <NaturalBehaviour/> </UnitAI> Here this.template.NaturalBehaviour === undefined //true "NaturalBehaviour" in this.template == true // true I think there is no check for whether text in there is correct. <UnitAI> <NaturalBehaviour>defensive<NaturalBehaviour/> </UnitAI> this.template.NaturalBehaviour === undefined //false this.template.NaturalBehaviour.length === "defensive".length // true "NaturalBehaviour" in this.template == true // true Stan: Natural behaviour is an optionnal a choice tag.
Which means it can either be present or not… | |||||
Done Inline Actions!== undefined might more reflect the intended meaning. We can make sure that its never false, null or 0. So as far as I see the object is a string or undefined, but never 0, null, or false. Add to that the circumstancial evidence of all those .template == "false" and related boolean checks, for example elexis: `!== undefined` might more reflect the intended meaning.
We can make sure that its never false… | |||||
Done Inline ActionsThose are nominal changes. I'm quite interested in making the XML to JS translation of some of these values more strict and easier to use (in particular around boolean values), but would be okay to handle that separately? Krinkle: Those are nominal changes. I'm quite interested in making the XML to JS translation of some of… | |||||
Done Inline ActionsUsing` !== undefined` instead of !! in this line would be more consistent with the rest of the template parsing, no? elexis: Using` !== undefined` instead of `!!` in this line would be more consistent with the rest of… | |||||
Done Inline ActionsIt casts to a boolean on this line in trunk, and this patch currently does not change that. Is it okay if I audit those in a separate patch so that we can isolate those and the testing required for them? Krinkle: It casts to a boolean on this line in trunk, and this patch currently does not change that.
Is… | |||||
Done Inline Actionsreturn this.template.NaturalBehaviour !== undefined; also returns a boolean. elexis: `return this.template.NaturalBehaviour !== undefined;` also returns a boolean.
The last line is… | |||||
Done Inline ActionsI don't mind changing it to !== undefined if you're comfortable with that and confident that this change in behaviour is not a problem. Boolean casting to false accepts many more values than merely undefined. I currently do not know this code base well enough to confidently say the difference won't be observable and cause bugs. Hence I would lean towards doing that code change separate from a style change. But if we know where it's used, okay with spending time on testing it as part of review, I will update it then. I optimise my patches for needing very little testing from you because my short-term goal is not to change the code behaviour, but to make it pass the linter, so that it can start providing useful feedback about real changes in behaviour and slowly raising our bar of standard quality. This is only the first phase to clear out the noise. But I am getting used to now that you either don't trust that style changes won't cause bugs and want to test extensively regardless, or that you prefer to do it all in one go within a given area of code. That's not how I usually work, but it's no problem, I'll adapt :) Krinkle: I don't mind changing it to `!== undefined` if you're comfortable with that and confident that… | |||||
Done Inline ActionsI've done a full audit and indeed NaturalBehaviour is only ever assigned a string, and thus yields the undefined value is accessed otherwise. Krinkle: I've done a full audit and indeed `NaturalBehaviour` is only ever assigned a string, and thus… | |||||
UnitAI.prototype.IsDangerousAnimal = function() | UnitAI.prototype.IsDangerousAnimal = function() | ||||
{ | { | ||||
return (this.IsAnimal() && (this.template.NaturalBehaviour == "violent" || | return (this.IsAnimal() && (this.template.NaturalBehaviour == "violent" || | ||||
Not Done Inline ActionsThis IsAnimal() check appears redundant. Would it be okay to remove as part of this change? Krinkle: This `IsAnimal()` check appears redundant. Would it be okay to remove as part of this change? | |||||
this.template.NaturalBehaviour == "aggressive")); | this.template.NaturalBehaviour == "aggressive")); | ||||
}; | }; | ||||
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"); | ||||
}; | }; | ||||
▲ Show 20 Lines • Show All 94 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)); | ||||
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 39 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 | |||||
}); | |||||
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 normal and healer range queries. | // Wrapper function that sets up the normal and healer range queries. | ||||
▲ Show 20 Lines • Show All 70 Lines • ▼ Show 20 Lines | UnitAI.prototype.SetupHealRangeQuery = function(enable = true) | ||||
this.losHealRangeQuery = cmpRangeManager.CreateActiveQuery(this.entity, range.min, range.max, players, IID_Health, cmpRangeManager.GetEntityFlagMask("injured")); | this.losHealRangeQuery = cmpRangeManager.CreateActiveQuery(this.entity, range.min, range.max, players, IID_Health, cmpRangeManager.GetEntityFlagMask("injured")); | ||||
if (enable) | if (enable) | ||||
cmpRangeManager.EnableActiveQuery(this.losHealRangeQuery); | cmpRangeManager.EnableActiveQuery(this.losHealRangeQuery); | ||||
}; | }; | ||||
//// FSM linkage functions //// | // FSM linkage functions | ||||
Done Inline ActionsWheres that hash coming from? "Markdown-esque", I see. elexis: Wheres that hash coming from? "Markdown-esque", I see.
Well, coding conventions say something… | |||||
Done Inline ActionsSounds good to me. Krinkle: Sounds good to me. | |||||
Done Inline Actionsping elexis: ping | |||||
// 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 All 29 Lines | UnitAI.prototype.FinishOrder = function() | ||||
} | } | ||||
this.orderQueue.shift(); | this.orderQueue.shift(); | ||||
this.order = this.orderQueue[0]; | this.order = this.orderQueue[0]; | ||||
let cmpPosition = Engine.QueryInterface(this.entity, IID_Position); | let cmpPosition = Engine.QueryInterface(this.entity, IID_Position); | ||||
if (this.orderQueue.length && (this.IsGarrisoned() || cmpPosition && cmpPosition.IsInWorld())) | if (this.orderQueue.length && (this.IsGarrisoned() || cmpPosition && cmpPosition.IsInWorld())) | ||||
{ | { | ||||
let ret = this.UnitFsm.ProcessMessage(this, | let ret = this.UnitFsm.ProcessMessage(this, { | ||||
{ "type": "Order."+this.order.type, "data": this.order.data } | "type": "Order."+this.order.type, | ||||
); | "data": this.order.data | ||||
}); | |||||
Engine.PostMessage(this.entity, MT_UnitAIOrderDataChanged, { "to": this.GetOrderData() }); | Engine.PostMessage(this.entity, MT_UnitAIOrderDataChanged, { "to": this.GetOrderData() }); | ||||
// If the order was rejected then immediately take it off | // If the order was rejected then immediately take it off | ||||
// and process the remaining queue | // and process the remaining queue | ||||
if (ret && ret.discardOrder) | if (ret && ret.discardOrder) | ||||
return this.FinishOrder(); | return this.FinishOrder(); | ||||
Show All 35 Lines | |||||
{ | { | ||||
var order = { "type": type, "data": data }; | var order = { "type": type, "data": data }; | ||||
this.orderQueue.push(order); | this.orderQueue.push(order); | ||||
// If we didn't already have an order, then process this new one | // If we didn't already have an order, then process this new one | ||||
if (this.orderQueue.length == 1) | if (this.orderQueue.length == 1) | ||||
{ | { | ||||
this.order = order; | this.order = order; | ||||
let ret = this.UnitFsm.ProcessMessage(this, | let ret = this.UnitFsm.ProcessMessage(this, { | ||||
{ "type": "Order."+this.order.type, "data": this.order.data } | "type": "Order."+this.order.type, | ||||
); | "data": this.order.data | ||||
}); | |||||
// If the order was rejected then immediately take it off | // If the order was rejected then immediately take it off | ||||
// and process the remaining queue | // and process the remaining queue | ||||
if (ret && ret.discardOrder) | if (ret && ret.discardOrder) | ||||
this.FinishOrder(); | this.FinishOrder(); | ||||
} | } | ||||
Engine.PostMessage(this.entity, MT_UnitAIOrderDataChanged, { "to": this.GetOrderData() }); | Engine.PostMessage(this.entity, MT_UnitAIOrderDataChanged, { "to": this.GetOrderData() }); | ||||
Show All 17 Lines | UnitAI.prototype.PushOrderFront = function(type, data, ignorePacking = false) | ||||
{ | { | ||||
var packingOrder = this.orderQueue.shift(); | var packingOrder = this.orderQueue.shift(); | ||||
this.orderQueue.unshift(packingOrder, order); | this.orderQueue.unshift(packingOrder, order); | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
this.orderQueue.unshift(order); | this.orderQueue.unshift(order); | ||||
this.order = order; | this.order = order; | ||||
let ret = this.UnitFsm.ProcessMessage(this, | let ret = this.UnitFsm.ProcessMessage(this, { | ||||
{ "type": "Order."+this.order.type, "data": this.order.data } | "type": "Order."+this.order.type, | ||||
); | "data": this.order.data | ||||
}); | |||||
// If the order was rejected then immediately take it off again; | // If the order was rejected then immediately take it off again; | ||||
// assume the previous active order is still valid (the short-lived | // assume the previous active order is still valid (the short-lived | ||||
// new order hasn't changed state or anything) so we can carry on | // new order hasn't changed state or anything) so we can carry on | ||||
// as if nothing had happened | // as if nothing had happened | ||||
if (ret && ret.discardOrder) | if (ret && ret.discardOrder) | ||||
{ | { | ||||
this.orderQueue.shift(); | this.orderQueue.shift(); | ||||
Show All 16 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 }); | ||||
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 225 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 }); | ||||
Done Inline Actions(same) elexis: (same) | |||||
}; | }; | ||||
/** | /** | ||||
* 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 All 28 Lines | UnitAI.prototype.OnMotionUpdate = function(msg) | ||||
this.UnitFsm.ProcessMessage(this, Object.assign({ "type": "MovementUpdate" }, msg)); | this.UnitFsm.ProcessMessage(this, Object.assign({ "type": "MovementUpdate" }, msg)); | ||||
}; | }; | ||||
UnitAI.prototype.OnGlobalConstructionFinished = function(msg) | UnitAI.prototype.OnGlobalConstructionFinished = function(msg) | ||||
{ | { | ||||
// TODO: This is a bit inefficient since every unit listens to every | // TODO: This is a bit inefficient since every unit listens to every | ||||
// construction message - ideally we could scope it to only the one we're building | // construction message - ideally we could scope it to only the one we're building | ||||
this.UnitFsm.ProcessMessage(this, {"type": "ConstructionFinished", "data": msg}); | this.UnitFsm.ProcessMessage(this, { "type": "ConstructionFinished", "data": msg }); | ||||
Done Inline Actions(same) elexis: (same) | |||||
}; | }; | ||||
UnitAI.prototype.OnGlobalEntityRenamed = function(msg) | UnitAI.prototype.OnGlobalEntityRenamed = function(msg) | ||||
{ | { | ||||
let changed = false; | let changed = false; | ||||
for (let order of this.orderQueue) | for (let order of this.orderQueue) | ||||
{ | { | ||||
if (order.data && order.data.target && order.data.target == msg.entity) | if (order.data && order.data.target && order.data.target == msg.entity) | ||||
Show All 14 Lines | if (changed) | ||||
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 }); | ||||
}; | }; | ||||
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 }); | ||||
}; | }; | ||||
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 }); | ||||
}; | }; | ||||
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 }); | ||||
}; | }; | ||||
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 }); | ||||
}; | }; | ||||
//// Helper functions to be called by the FSM //// | // Helper functions to be called by the FSM | ||||
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 649 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 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 => | ||||
this.CanAttack(target) | this.CanAttack(target) && | ||||
&& this.CheckTargetDistanceFromHeldPosition(target, IID_Attack, this.GetBestAttackAgainst(target, true)) | this.CheckTargetDistanceFromHeldPosition(target, IID_Attack, this.GetBestAttackAgainst(target, true)) && | ||||
&& (this.GetStance().respondChaseBeyondVision || this.CheckTargetIsInVisionRange(target)) | (this.GetStance().respondChaseBeyondVision || this.CheckTargetIsInVisionRange(target)) | ||||
); | ); | ||||
Done Inline ActionsThat ); on a separate line looks wrong to me, doesn't it for you? elexis: That `);` on a separate line looks wrong to me, doesn't it for you?
I understand why scopes… | |||||
Done Inline ActionsI generally follow a rule that closing symbols must align in indentation with opening symbols. This makes it possible to read large amounts of code faster and/or with greater accuracy due to there being less visual ambiguity. In this case these three lines together make up the body of the lambda passed to Array#find. Removing the line break would make it hard to distinguish whether between find() && … ( || ) and find( => … && … ( || ) ). The idea of aligning with the opening symbol was already present here before my change, and indeed to my knowledge there is no rule available to require or disallow that. It's a judgement call. Krinkle: I generally follow a rule that closing symbols must align in indentation with opening symbols. | |||||
Done Inline ActionsI understand the idea, I committed some of them too some day ago, but I've changed my mind, because those aren't objects, aren't arrays, aren't function scopes. It doesn't make it any less readable if the closing parentheses is the same line as the function call. It would also allow for var x = func(\n\tbar\n);`, but it's not a space that is opened like it is with objects, arrays or function scopes where one can insert arbitrary elements. meh elexis: I understand the idea, I committed some of them too some day ago, but I've changed my mind… | |||||
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 45 Lines • ▼ Show 20 Lines | |||||
{ | { | ||||
// Forced orders shouldn't be interrupted. | // Forced orders shouldn't be interrupted. | ||||
if (force) | 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) | ||||
{ | { | ||||
var cmpUnitAI = Engine.QueryInterface(target, IID_UnitAI); | var cmpUnitAI = Engine.QueryInterface(target, IID_UnitAI); | ||||
var cmpAttack = Engine.QueryInterface(target, IID_Attack); | var 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 false; | return false; | ||||
} | } | ||||
// Stop if we're in hold-ground mode and it's too far from the holding point | // Stop if we're in hold-ground mode and it's too far from the holding point | ||||
if (this.GetStance().respondHoldGround) | if (this.GetStance().respondHoldGround) | ||||
{ | { | ||||
if (!this.CheckTargetDistanceFromHeldPosition(target, iid, type)) | if (!this.CheckTargetDistanceFromHeldPosition(target, iid, type)) | ||||
return true; | return true; | ||||
} | } | ||||
Show All 21 Lines | if (this.IsTurret()) | ||||
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); | ||||
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; | ||||
} | } | ||||
if (force) | if (force) | ||||
return true; | return true; | ||||
return false; | return false; | ||||
}; | }; | ||||
//// External interface functions //// | // External interface functions | ||||
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 139 Lines • ▼ Show 20 Lines | UnitAI.prototype.Guard = function(target, queued) | ||||
} | } | ||||
// if we already had an old guard order, do nothing if the target is the same | // if we already had an old guard order, do nothing if the target is the same | ||||
// and the order is running, otherwise remove the previous order | // and the order is running, otherwise remove the previous order | ||||
if (this.isGuardOf) | if (this.isGuardOf) | ||||
{ | { | ||||
if (this.isGuardOf == target && this.order && this.order.type == "Guard") | if (this.isGuardOf == target && this.order && this.order.type == "Guard") | ||||
return; | return; | ||||
else | |||||
this.RemoveGuard(); | this.RemoveGuard(); | ||||
} | } | ||||
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 324 Lines • ▼ Show 20 Lines | UnitAI.prototype.SetupTradeRoute = function(target, source, route, queued) | ||||
} | } | ||||
// 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() && | ||||
(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 264 Lines • ▼ Show 20 Lines | for (var ent of cmpFormation.members) | ||||
for (var targ of targets) | for (var targ of targets) | ||||
{ | { | ||||
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)) | ||||
continue; | continue; | ||||
if (targetClasses.avoid && cmpIdentity | if (targetClasses.avoid && cmpIdentity && | ||||
&& MatchesClassList(cmpIdentity.GetClassesList(), targetClasses.avoid)) | MatchesClassList(cmpIdentity.GetClassesList(), targetClasses.avoid)) | ||||
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(); | ||||
for (var targ of targets) | for (var targ of targets) | ||||
{ | { | ||||
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); | ||||
var targetClasses = this.order.data.targetClasses; | var targetClasses = this.order.data.targetClasses; | ||||
if (cmpIdentity && targetClasses.attack | if (cmpIdentity && targetClasses.attack && | ||||
&& !MatchesClassList(cmpIdentity.GetClassesList(), targetClasses.attack)) | !MatchesClassList(cmpIdentity.GetClassesList(), targetClasses.attack)) | ||||
continue; | continue; | ||||
if (cmpIdentity && targetClasses.avoid | if (cmpIdentity && targetClasses.avoid && | ||||
&& MatchesClassList(cmpIdentity.GetClassesList(), targetClasses.avoid)) | MatchesClassList(cmpIdentity.GetClassesList(), targetClasses.avoid)) | ||||
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 125 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 }; | ||||
Not Done Inline Actions(I suppose this should be a vector some time) elexis: (I suppose this should be a vector some time) | |||||
Not Done Inline ActionsWould that be a 3D one with a useless y component or a 2D with y used as z ? Stan: Would that be a 3D one with a useless y component or a 2D with y used as z ? | |||||
}; | }; | ||||
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 | ||||
/** | /** | ||||
* 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); | ||||
} | }; | ||||
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 181 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 | ||||
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"; | ||||
}; | }; | ||||
//// Animal specific functions //// | // 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, 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 20 Lines • Show All 119 Lines • Show Last 20 Lines |
Duplication should be avoided. I guess the return is not worth to speak of.
It could become if (x) doY(); else doZ(); return;, on the other hand someone could say that its just a coincidence that theres a return in both cases.
Diff is ok.