Changeset View
Standalone View
binaries/data/mods/public/simulation/components/UnitAI.js
Show First 20 Lines • Show All 140 Lines • ▼ Show 20 Lines | var g_OrdersCancelUnpacking = new Set([ | ||||
"WalkToTarget", | "WalkToTarget", | ||||
"Patrol", | "Patrol", | ||||
"Garrison" | "Garrison" | ||||
]); | ]); | ||||
// When leaving a foundation, we want to be clear of it by this distance. | // When leaving a foundation, we want to be clear of it by this distance. | ||||
var g_LeaveFoundationRange = 4; | var g_LeaveFoundationRange = 4; | ||||
// See ../helpers/FSM.js for some documentation of this FSM specification syntax | // See ../helpers/FSM.js for some documentation of this FSM specification syntax | ||||
Freagarach: Capital `n`? | |||||
Done Inline Actionsrather no, it is variable and not function. Silier: rather no, it is variable and not function. | |||||
UnitAI.prototype.UnitFsmSpec = { | UnitAI.prototype.UnitFsmSpec = { | ||||
// Default event handlers: | // Default event handlers: | ||||
"MovementUpdate": function(msg) { | "MovementUpdate": function(msg) { | ||||
// ignore spurious movement messages | // ignore spurious movement messages | ||||
// (these can happen when stopping moving at the same time | // (these can happen when stopping moving at the same time | ||||
// as switching states) | // as switching states) | ||||
▲ Show 20 Lines • Show All 493 Lines • ▼ Show 20 Lines | "Order.Garrison": function(msg) { | ||||
else | else | ||||
this.SetNextState("INDIVIDUAL.GARRISON.APPROACHING"); | this.SetNextState("INDIVIDUAL.GARRISON.APPROACHING"); | ||||
}, | }, | ||||
"Order.Ungarrison": function() { | "Order.Ungarrison": function() { | ||||
this.FinishOrder(); | this.FinishOrder(); | ||||
this.isGarrisoned = false; | this.isGarrisoned = false; | ||||
}, | }, | ||||
"Order.Cheering": function(msg) { | "Order.Cheer": function(msg) { | ||||
if (this.IsFormationMember()) | return { "discardOrder": true }; | ||||
Not Done Inline Actions- Freagarach: -` ` | |||||
this.SetNextState("FORMATIONMEMBER.CHEERING"); | |||||
else | |||||
this.SetNextState("INDIVIDUAL.CHEERING"); | |||||
}, | }, | ||||
"Order.Pack": function(msg) { | "Order.Pack": function(msg) { | ||||
if (this.CanPack()) | if (this.CanPack()) | ||||
this.SetNextState("INDIVIDUAL.PACKING"); | this.SetNextState("INDIVIDUAL.PACKING"); | ||||
}, | }, | ||||
"Order.Unpack": function(msg) { | "Order.Unpack": function(msg) { | ||||
▲ Show 20 Lines • Show All 799 Lines • ▼ Show 20 Lines | "GuardedAttacked": function(msg) { | ||||
{ | { | ||||
this.orderQueue.splice(1, 1); | this.orderQueue.splice(1, 1); | ||||
Engine.PostMessage(this.entity, MT_UnitAIOrderDataChanged, { "to": this.GetOrderData() }); | Engine.PostMessage(this.entity, MT_UnitAIOrderDataChanged, { "to": this.GetOrderData() }); | ||||
} | } | ||||
} | } | ||||
}, | }, | ||||
"IDLE": { | "IDLE": { | ||||
"Order.Cheer": function() { | |||||
Not Done Inline Actions+ Freagarach: +` ` | |||||
if (this.isIdle) | |||||
Not Done Inline ActionsPerhaps also check if we're really idle? Freagarach: Perhaps also check if we're really idle? | |||||
Done Inline ActionsNo order does that Silier: No order does that | |||||
Not Done Inline ActionsExample: Unit calls finishOrder and enters this idle state, with enemy nearby. The rangeQuery won't be reset until the timer expires, but we can be put in cheering state when that order arrives at the same turn. Freagarach: Example: Unit calls finishOrder and enters this idle state, with enemy nearby. The rangeQuery… | |||||
Done Inline ActionsDescription of idle says: If we entered the idle state we must have nothing better to do. Silier: Description of idle says: ` If we entered the idle state we must have nothing better to do`. | |||||
Not Done Inline Actions
Which would mean always having reveiced one blow before being able to strike back. That being said, missed cheers is also not nice indeed. We'll see how this unfolds. Freagarach: > Unit will react back when being attacked and it just does not initiate attack right away… | |||||
this.SetNextState("CHEERING"); | |||||
Not Done Inline Actionscomma Silier: comma | |||||
}, | |||||
Not Done Inline Actions+\n Freagarach: +`\n` | |||||
"enter": function() { | "enter": function() { | ||||
// Switch back to idle animation to guarantee we won't | // Switch back to idle animation to guarantee we won't | ||||
// get stuck with an incorrect animation | // get stuck with an incorrect animation | ||||
this.SelectAnimation("idle"); | this.SelectAnimation("idle"); | ||||
// Idle is the default state. If units try, from the IDLE.enter sub-state, to | // Idle is the default state. If units try, from the IDLE.enter sub-state, to | ||||
// begin another order, and that order fails (calling FinishOrder), they might | // begin another order, and that order fails (calling FinishOrder), they might | ||||
// end up in an infinite loop. To avoid this, all methods that could put the unit in | // end up in an infinite loop. To avoid this, all methods that could put the unit in | ||||
▲ Show 20 Lines • Show All 429 Lines • ▼ Show 20 Lines | "COMBAT": { | ||||
// if the target is a formation, save the attacking formation, and pick a member | // if the target is a formation, save the attacking formation, and pick a member | ||||
if (cmpFormation) | if (cmpFormation) | ||||
{ | { | ||||
this.order.data.formationTarget = target; | this.order.data.formationTarget = target; | ||||
target = cmpFormation.GetClosestMember(this.entity); | target = cmpFormation.GetClosestMember(this.entity); | ||||
this.order.data.target = target; | this.order.data.target = target; | ||||
} | } | ||||
this.shouldCheer = false; | |||||
if (!this.CanAttack(target)) | if (!this.CanAttack(target)) | ||||
{ | { | ||||
this.SetNextState("COMBAT.FINDINGNEWTARGET"); | this.SetNextState("COMBAT.FINDINGNEWTARGET"); | ||||
return true; | return true; | ||||
} | } | ||||
if (!this.CheckTargetAttackRange(target, this.order.data.attackType)) | if (!this.CheckTargetAttackRange(target, this.order.data.attackType)) | ||||
{ | { | ||||
Show All 33 Lines | "COMBAT": { | ||||
// TODO: we should probably only bother syncing projectile attacks, not melee | // TODO: we should probably only bother syncing projectile attacks, not melee | ||||
// If using a non-default prepare time, re-sync the animation when the timer runs. | // If using a non-default prepare time, re-sync the animation when the timer runs. | ||||
this.resyncAnimation = prepare != this.attackTimers.prepare; | this.resyncAnimation = prepare != this.attackTimers.prepare; | ||||
this.FaceTowardsTarget(this.order.data.target); | this.FaceTowardsTarget(this.order.data.target); | ||||
let cmpBuildingAI = Engine.QueryInterface(this.entity, IID_BuildingAI); | let cmpBuildingAI = Engine.QueryInterface(this.entity, IID_BuildingAI); | ||||
if (cmpBuildingAI) | if (cmpBuildingAI) | ||||
Not Done Inline Actions+/n Freagarach: +`/n` | |||||
Not Done Inline ActionsEarly return to drop the else ? Stan: Early return to drop the else ? | |||||
cmpBuildingAI.SetUnitAITarget(this.order.data.target); | cmpBuildingAI.SetUnitAITarget(this.order.data.target); | ||||
else | |||||
Not Done Inline ActionsIsn't it still false here? Freagarach: Isn't it still false here? | |||||
{ | |||||
Not Done Inline ActionsIdem. Freagarach: Idem. | |||||
let cmpUnitAI = Engine.QueryInterface(this.order.data.target, IID_UnitAI); | |||||
this.shouldCheer = cmpUnitAI && !cmpUnitAI.IsAnimal(); | |||||
Not Done Inline Actions(This is probably because of hunting? But just to mention it: we won't cheer after killing a lion as well.) Freagarach: (This is probably because of hunting? But just to mention it: we won't cheer after killing a… | |||||
Not Done Inline ActionsDon't we want to cheer also whenever we've destroyed a structure? Freagarach: Don't we want to cheer also whenever we've destroyed a structure? | |||||
Done Inline Actionsgood question. Silier: good question.
I do not have clear answer for that.
As structures are entities in los, they… | |||||
Not Done Inline ActionsI think we'd want to. We can force orders anyway and queued orders also prevent cheering, so it won't disturb gameplay. Freagarach: I think we'd want to. We can force orders anyway and queued orders also prevent cheering, so it… | |||||
Done Inline Actionsanother thought, Silier: another thought,
maybe it would be good to put that to identity component? cmpIdentity. | |||||
Not Done Inline ActionsI would say just cheer always, even when killing an animal. Hunting won't be affected since there is an order afterwards. And I guess people will be generally delighted when coming out of combat state ;) Freagarach: I would say just cheer always, even when killing an animal. Hunting won't be affected since… | |||||
Not Done Inline ActionsWhat about having cheering classes? MatchClassList(happywhenhavingkilled, this.target.classes) Stan: What about having cheering classes? MatchClassList(happywhenhavingkilled, this.target.classes) | |||||
Not Done Inline ActionsAnd no "happywhenhavingkilled" means not cheering (e.g. rams etc.). Freagarach: And no "happywhenhavingkilled" means not cheering (e.g. rams etc.). | |||||
Done Inline Actions
Thought about that, but in my opinion that would add too big overhead compared to that we want to add just fancy animation. Silier: >What about having cheering classes?
>MatchClassList(happywhenhavingkilled, this.target. | |||||
Not Done Inline ActionsSo we can just cheer always and when user find it strange we can react on that? Freagarach: So we can just cheer always and when user find it strange we can react on that? | |||||
Done Inline Actionsnot always, cheering after killing chickens/sheep/deer is weird enough already since non dangerous animals does not end up in range queries. Silier: not always, cheering after killing chickens/sheep/deer is weird enough already since non… | |||||
Not Done Inline ActionscmpUnitAI && (!cmpUnitAI.IsAnimal() || cmpUnitAI.IsDangerousAnimal());? Freagarach: `cmpUnitAI && (!cmpUnitAI.IsAnimal() || cmpUnitAI.IsDangerousAnimal());`? | |||||
} | |||||
return false; | return false; | ||||
}, | }, | ||||
"leave": function() { | "leave": function() { | ||||
let cmpBuildingAI = Engine.QueryInterface(this.entity, IID_BuildingAI); | let cmpBuildingAI = Engine.QueryInterface(this.entity, IID_BuildingAI); | ||||
if (cmpBuildingAI) | if (cmpBuildingAI) | ||||
cmpBuildingAI.SetUnitAITarget(0); | cmpBuildingAI.SetUnitAITarget(0); | ||||
this.StopTimer(); | this.StopTimer(); | ||||
▲ Show 20 Lines • Show All 62 Lines • ▼ Show 20 Lines | "COMBAT": { | ||||
&& 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]); | ||||
}, | }, | ||||
}, | }, | ||||
"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); | ||||
Not Done Inline Actions+\n. Freagarach: +`\n`. | |||||
if (!cmpFormation) | if (!cmpFormation) | ||||
cmpFormation = Engine.QueryInterface(this.order.data.formationTarget || INVALID_ENTITY, IID_Formation); | cmpFormation = Engine.QueryInterface(this.order.data.formationTarget || INVALID_ENTITY, IID_Formation); | ||||
// If the target is a formation, pick closest member. | // If the target is a formation, pick closest member. | ||||
if (cmpFormation) | if (cmpFormation) | ||||
{ | { | ||||
let filter = (t) => this.CanAttack(t); | let filter = (t) => this.CanAttack(t); | ||||
this.order.data.formationTarget = this.order.data.target; | this.order.data.formationTarget = this.order.data.target; | ||||
Show All 12 Lines | "COMBAT": { | ||||
return true; | return true; | ||||
} | } | ||||
// See if we can switch to a new nearby enemy | // See if we can switch to a new nearby enemy | ||||
if (this.FindNewTargets()) | if (this.FindNewTargets()) | ||||
return true; | return true; | ||||
// Return to our original position | // Return to our original position | ||||
if (this.GetStance().respondHoldGround) | if (this.GetStance().respondHoldGround) | ||||
this.WalkToHeldPosition(); | this.WalkToHeldPosition(); | ||||
Not Done Inline ActionsDo we want to cheer before this or after this? Freagarach: Do we want to cheer before this or after this? | |||||
if (this.shouldCheer) | |||||
Not Done Inline ActionsThink you want this to be in the "leave" call, if we don't have a next order or something like that, rather. wraitii: Think you want this to be in the "leave" call, if we don't have a next order or something like… | |||||
Done Inline ActionsActually I think this is the best place, If this would be in leave, that would be called even when you would give order to stop or you change order to walk Silier: Actually I think this is the best place, If this would be in leave, that would be called even… | |||||
Not Done Inline Actions+/n Freagarach: +`/n` | |||||
{ | |||||
this.Cheer(); | |||||
this.CallFrienldyUnitsFunctionInRange("Cheer", [], 30); | |||||
Not Done Inline ActionsPerhaps make the magic 30 part of the prototype? Freagarach: Perhaps make the magic `30` part of the prototype? | |||||
Done Inline ActionsStill would be magic, but maybe better Silier: Still would be magic, but maybe better | |||||
} | |||||
return true; | return true; | ||||
}, | }, | ||||
}, | }, | ||||
"CHASING": { | "CHASING": { | ||||
"Order.MoveToChasingPoint": function(msg) { | "Order.MoveToChasingPoint": function(msg) { | ||||
if (this.CheckPointRangeExplicit(msg.data.x, msg.data.z, 0, msg.data.max)) | if (this.CheckPointRangeExplicit(msg.data.x, msg.data.z, 0, msg.data.max)) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 1,030 Lines • ▼ Show 20 Lines | "GARRISON": { | ||||
"leave": function() { | "leave": function() { | ||||
} | } | ||||
}, | }, | ||||
}, | }, | ||||
"CHEERING": { | "CHEERING": { | ||||
"enter": function() { | "enter": function() { | ||||
// Unit is invulnerable while cheering | |||||
var cmpResistance = Engine.QueryInterface(this.entity, IID_Resistance); | |||||
cmpResistance.SetInvulnerability(true); | |||||
this.SelectAnimation("promotion"); | this.SelectAnimation("promotion"); | ||||
Not Done Inline ActionsShould be renamed in a seperate diff to cheering I guess. Freagarach: Should be renamed in a seperate diff to cheering I guess. | |||||
this.StartTimer(2800, 2800); | this.StartTimer(2800); | ||||
return false; | return false; | ||||
}, | }, | ||||
"leave": function() { | "leave": function() { | ||||
this.StopTimer(); | this.StopTimer(); | ||||
this.ResetAnimation(); | this.ResetAnimation(); | ||||
if (this.formationAnimationVariant) | 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); | }, | ||||
cmpResistance.SetInvulnerability(false); | |||||
"LosRangeUpdate": function(msg) { | |||||
if (this.GetStance().targetVisibleEnemies) | |||||
this.AttackEntitiesByPreference(msg.data.added); | |||||
}, | |||||
"LosHealRangeUpdate": function(msg) { | |||||
this.RespondToHealableEntities(msg.data.added); | |||||
}, | }, | ||||
"Timer": function(msg) { | "Timer": function(msg) { | ||||
this.FinishOrder(); | this.FinishOrder(); | ||||
}, | }, | ||||
}, | }, | ||||
"PACKING": { | "PACKING": { | ||||
▲ Show 20 Lines • Show All 210 Lines • ▼ Show 20 Lines | "ANIMAL": { | ||||
"COMBAT": "INDIVIDUAL.COMBAT", // reuse the same combat behaviour for animals | "COMBAT": "INDIVIDUAL.COMBAT", // reuse the same combat behaviour for animals | ||||
"WALKING": "INDIVIDUAL.WALKING", // reuse the same walking behaviour for animals | "WALKING": "INDIVIDUAL.WALKING", // reuse the same walking behaviour for animals | ||||
// only used for domestic animals | // only used for domestic animals | ||||
// Reuse the same garrison behaviour for animals. | // Reuse the same garrison behaviour for animals. | ||||
"GARRISON": "INDIVIDUAL.GARRISON", | "GARRISON": "INDIVIDUAL.GARRISON", | ||||
}, | }, | ||||
}; | }; | ||||
Not Done Inline ActionsConstant? Stan: Constant? | |||||
UnitAI.prototype.Init = function() | UnitAI.prototype.Init = function() | ||||
{ | { | ||||
this.orderQueue = []; // current order is at the front of the list | this.orderQueue = []; // current order is at the front of the list | ||||
this.order = undefined; // always == this.orderQueue[0] | this.order = undefined; // always == this.orderQueue[0] | ||||
this.formationController = INVALID_ENTITY; // entity with IID_Formation that we belong to | this.formationController = INVALID_ENTITY; // entity with IID_Formation that we belong to | ||||
this.isGarrisoned = false; | this.isGarrisoned = false; | ||||
this.isIdle = false; | this.isIdle = false; | ||||
▲ Show 20 Lines • Show All 146 Lines • ▼ Show 20 Lines | UnitAI.prototype.OnOwnershipChanged = function(msg) | ||||
if (this.isGuardOf && (msg.to == INVALID_PLAYER || !IsOwnedByMutualAllyOfEntity(this.entity, this.isGuardOf))) | if (this.isGuardOf && (msg.to == INVALID_PLAYER || !IsOwnedByMutualAllyOfEntity(this.entity, this.isGuardOf))) | ||||
this.RemoveGuard(); | this.RemoveGuard(); | ||||
// If the unit isn't being created or dying, reset stance and clear orders | // If the unit isn't being created or dying, reset stance and clear orders | ||||
if (msg.to != INVALID_PLAYER && msg.from != INVALID_PLAYER) | if (msg.to != INVALID_PLAYER && msg.from != INVALID_PLAYER) | ||||
{ | { | ||||
// Switch to a virgin state to let states execute their leave handlers. | // Switch to a virgin state to let states execute their leave handlers. | ||||
// except if garrisoned or cheering or (un)packing, in which case we only clear the order queue | // Except if garrisoned or (un)packing, in which case we only clear the order queue. | ||||
if (this.isGarrisoned || this.IsPacking() || this.orderQueue[0] && this.orderQueue[0].type == "Cheering") | if (this.isGarrisoned || this.IsPacking()) | ||||
Not Done Inline Actions+. Freagarach: +`.` | |||||
Not Done Inline Actions-or Freagarach: -`or` | |||||
{ | { | ||||
this.orderQueue.length = Math.min(this.orderQueue.length, 1); | this.orderQueue.length = Math.min(this.orderQueue.length, 1); | ||||
Engine.PostMessage(this.entity, MT_UnitAIOrderDataChanged, { "to": this.GetOrderData() }); | Engine.PostMessage(this.entity, MT_UnitAIOrderDataChanged, { "to": this.GetOrderData() }); | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
let index = this.GetCurrentState().indexOf("."); | let index = this.GetCurrentState().indexOf("."); | ||||
if (index != -1) | if (index != -1) | ||||
▲ Show 20 Lines • Show All 255 Lines • ▼ Show 20 Lines | |||||
/** | /** | ||||
* Add an order onto the front of the queue, | * Add an order onto the front of the queue, | ||||
* and execute it immediately. | * and execute it immediately. | ||||
*/ | */ | ||||
UnitAI.prototype.PushOrderFront = function(type, data, ignorePacking = false) | UnitAI.prototype.PushOrderFront = function(type, data, ignorePacking = false) | ||||
{ | { | ||||
var order = { "type": type, "data": data }; | var order = { "type": type, "data": data }; | ||||
// If current order is cheering then add new order after it | // If current order is packing/unpacking then add new order after it. | ||||
// same thing if current order if packing/unpacking | if (!ignorePacking && this.order && this.IsPacking()) | ||||
Not Done Inline Actions+. Freagarach: +`.` | |||||
if (this.order && this.order.type == "Cheering") | |||||
{ | |||||
var cheeringOrder = this.orderQueue.shift(); | |||||
this.orderQueue.unshift(cheeringOrder, order); | |||||
} | |||||
else if (!ignorePacking && this.order && this.IsPacking()) | |||||
{ | { | ||||
var packingOrder = this.orderQueue.shift(); | var packingOrder = this.orderQueue.shift(); | ||||
this.orderQueue.unshift(packingOrder, order); | this.orderQueue.unshift(packingOrder, order); | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
this.orderQueue.unshift(order); | this.orderQueue.unshift(order); | ||||
this.order = order; | this.order = order; | ||||
▲ Show 20 Lines • Show All 106 Lines • ▼ Show 20 Lines | if (data && data.force) | ||||
if (this.IsFormationController()) | if (this.IsFormationController()) | ||||
this.CallMemberFunction("UpdateWorkOrders", [type]); | this.CallMemberFunction("UpdateWorkOrders", [type]); | ||||
else | else | ||||
this.UpdateWorkOrders(type); | this.UpdateWorkOrders(type); | ||||
} | } | ||||
let garrisonHolder = this.IsGarrisoned() && type != "Ungarrison" ? this.GetGarrisonHolder() : null; | let garrisonHolder = this.IsGarrisoned() && type != "Ungarrison" ? this.GetGarrisonHolder() : null; | ||||
// Special cases of orders that shouldn't be replaced: | // Do not replace Packing/unpacking unless it is cancel order | ||||
Not Done Inline ActionsI think you can clear these two comments and just say something like Packing/Unpacking shouldn't be replaced unless we're explicitly doing that or something. wraitii: I think you can clear these two comments and just say something like `Packing/Unpacking… | |||||
Not Done Inline Actions(un)packing? +. Freagarach: `(un)packing`? +`.` | |||||
Not Done Inline ActionsP -> p and the dot. Freagarach: `P` -> `p` and the dot. | |||||
// 1. Cheering - we're invulnerable, add order after we finish | |||||
// 2. Packing/unpacking - we're immobile, add order after we finish (unless it's cancel) | |||||
// TODO: maybe a better way of doing this would be to use priority levels | // TODO: maybe a better way of doing this would be to use priority levels | ||||
if (this.order && this.order.type == "Cheering") | if (this.IsPacking() && type != "CancelPack" && type != "CancelUnpack") | ||||
{ | |||||
var order = { "type": type, "data": data }; | |||||
var cheeringOrder = this.orderQueue.shift(); | |||||
this.orderQueue = [cheeringOrder, order]; | |||||
} | |||||
else if (this.IsPacking() && type != "CancelPack" && type != "CancelUnpack") | |||||
{ | { | ||||
var order = { "type": type, "data": data }; | var order = { "type": type, "data": data }; | ||||
var packingOrder = this.orderQueue.shift(); | var packingOrder = this.orderQueue.shift(); | ||||
if (type == "Attack") | if (type == "Attack") | ||||
{ | { | ||||
// The Attack order is able to handle a packing unit, while other orders can't. | // The Attack order is able to handle a packing unit, while other orders can't. | ||||
this.orderQueue = [packingOrder]; | this.orderQueue = [packingOrder]; | ||||
this.PushOrderFront(type, data, true); | this.PushOrderFront(type, data, true); | ||||
▲ Show 20 Lines • Show All 91 Lines • ▼ Show 20 Lines | UnitAI.prototype.BackToWork = function() | ||||
if (this.IsGarrisoned()) | if (this.IsGarrisoned()) | ||||
{ | { | ||||
let cmpGarrisonHolder = Engine.QueryInterface(this.GetGarrisonHolder(), IID_GarrisonHolder); | let cmpGarrisonHolder = Engine.QueryInterface(this.GetGarrisonHolder(), IID_GarrisonHolder); | ||||
if (!cmpGarrisonHolder || !cmpGarrisonHolder.PerformEject([this.entity], false)) | if (!cmpGarrisonHolder || !cmpGarrisonHolder.PerformEject([this.entity], false)) | ||||
return false; | return false; | ||||
} | } | ||||
// Clear the order queue considering special orders not to avoid | // Clear the order queue considering special orders not to avoid | ||||
if (this.order && this.order.type == "Cheering") | |||||
{ | |||||
var cheeringOrder = this.orderQueue.shift(); | |||||
this.orderQueue = [cheeringOrder]; | |||||
} | |||||
else | |||||
this.orderQueue = []; | this.orderQueue = []; | ||||
Not Done Inline Actionsthis comment can be changed too. wraitii: this comment can be changed too. | |||||
Not Done Inline ActionsComment unnecessary now. Freagarach: Comment unnecessary now. | |||||
this.AddOrders(this.workOrders); | this.AddOrders(this.workOrders); | ||||
Engine.PostMessage(this.entity, MT_UnitAIOrderDataChanged, { "to": this.GetOrderData() }); | Engine.PostMessage(this.entity, MT_UnitAIOrderDataChanged, { "to": this.GetOrderData() }); | ||||
// And if the unit is in a formation, remove it from the formation | // And if the unit is in a formation, remove it from the formation | ||||
if (this.IsFormationMember()) | if (this.IsFormationMember()) | ||||
{ | { | ||||
var cmpFormation = Engine.QueryInterface(this.formationController, IID_Formation); | var cmpFormation = Engine.QueryInterface(this.formationController, IID_Formation); | ||||
▲ Show 20 Lines • Show All 1,622 Lines • ▼ Show 20 Lines | |||||
/** | /** | ||||
* Adds flee order to the queue, not forced, so it can be | * Adds flee order to the queue, not forced, so it can be | ||||
* interrupted by attacks. | * interrupted by attacks. | ||||
*/ | */ | ||||
UnitAI.prototype.Flee = function(target, queued) | UnitAI.prototype.Flee = function(target, queued) | ||||
{ | { | ||||
this.AddOrder("Flee", { "target": target, "force": false }, queued); | this.AddOrder("Flee", { "target": target, "force": false }, queued); | ||||
}; | }; | ||||
/** | |||||
* Pushes a cheer order to the front of the queue. Forced so it won't be interrupted by attacks. | |||||
*/ | |||||
UnitAI.prototype.Cheer = function() | UnitAI.prototype.Cheer = function() | ||||
Not Done Inline ActionsThis could be made more generic perhaps? (To also allow group behavious such as a pack of wild animals attacking instead of being slaughtered one by one.) Freagarach: This could be made more generic perhaps? (To also allow group behavious such as a pack of wild… | |||||
{ | { | ||||
this.PushOrderFront("Cheering", { "force": true }); | this.PushOrderFront("Cheer", { "force": false }); | ||||
}; | }; | ||||
UnitAI.prototype.Pack = function(queued) | UnitAI.prototype.Pack = function(queued) | ||||
{ | { | ||||
Not Done Inline Actions// Keep that range small so units within reasonable distance cheer.? Freagarach: `// Keep that range small so units within reasonable distance cheer.`? | |||||
// Check that we can pack | // Check that we can pack | ||||
if (this.CanPack()) | if (this.CanPack()) | ||||
Not Done Inline ActionsInline? Freagarach: Inline? | |||||
this.AddOrder("Pack", { "force": true }, queued); | this.AddOrder("Pack", { "force": true }, queued); | ||||
Not Done Inline Actions+; Freagarach: +`;` | |||||
}; | }; | ||||
UnitAI.prototype.Unpack = function(queued) | UnitAI.prototype.Unpack = function(queued) | ||||
{ | { | ||||
// Check that we can unpack | // Check that we can unpack | ||||
if (this.CanUnpack()) | if (this.CanUnpack()) | ||||
this.AddOrder("Unpack", { "force": true }, queued); | this.AddOrder("Unpack", { "force": true }, queued); | ||||
}; | }; | ||||
▲ Show 20 Lines • Show All 599 Lines • ▼ Show 20 Lines | UnitAI.prototype.CallMemberFunction = function(funcname, args) | ||||
cmpFormation.GetMembers().forEach(ent => { | cmpFormation.GetMembers().forEach(ent => { | ||||
var cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI); | var cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI); | ||||
cmpUnitAI[funcname].apply(cmpUnitAI, args); | cmpUnitAI[funcname].apply(cmpUnitAI, args); | ||||
}); | }); | ||||
}; | }; | ||||
/** | /** | ||||
* Call obj.funcname(args) on UnitAI components owned by player in given range. | |||||
*/ | |||||
UnitAI.prototype.CallFrienldyUnitsFunctionInRange = function(funcname, args, range) | |||||
Not Done Inline Actionsfriendly Freagarach: `friendly` | |||||
{ | |||||
let cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership); | |||||
if (!cmpOwnership) | |||||
return; | |||||
let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); | |||||
let nearby = cmpRangeManager.ExecuteQuery(this.entity, 0, range, [cmpOwnership.GetOwner()], IID_UnitAI); | |||||
Not Done Inline ActionsFriendly != owned. Freagarach: Friendly != owned.
So either the function ought to be renamed or we should be able to specify… | |||||
Not Done Inline ActionsCan owner be INVALID_PLAYER? Stan: Can owner be INVALID_PLAYER? | |||||
for (let i = 0; i < nearby.length; ++i) { | |||||
Not Done Inline Actions\n Freagarach: `\n` | |||||
Not Done Inline Actions^ Freagarach: ^ | |||||
Not Done Inline Actionsfor (let ent of nearby)? Freagarach: `for (let ent of nearby)`? | |||||
let cmpUnitAI = Engine.QueryInterface(nearby[i], IID_UnitAI); | |||||
cmpUnitAI[funcname].apply(cmpUnitAI, args); | |||||
} | |||||
}; | |||||
/** | |||||
* Call obj.functname(args) on UnitAI components of all formation members, | * Call obj.functname(args) on UnitAI components of all formation members, | ||||
* and return true if all calls return true. | * and return true if all calls return true. | ||||
*/ | */ | ||||
UnitAI.prototype.TestAllMemberFunction = function(funcname, args) | UnitAI.prototype.TestAllMemberFunction = function(funcname, args) | ||||
{ | { | ||||
var cmpFormation = Engine.QueryInterface(this.entity, IID_Formation); | var cmpFormation = Engine.QueryInterface(this.entity, IID_Formation); | ||||
if (!cmpFormation) | if (!cmpFormation) | ||||
return false; | return false; | ||||
Show All 10 Lines |
Capital n?