Changeset View
Standalone View
binaries/data/mods/public/simulation/components/UnitAI.js
Show First 20 Lines • Show All 126 Lines • ▼ Show 20 Lines | "none": { | ||||
"respondChase": false, | "respondChase": false, | ||||
"respondChaseBeyondVision": false, | "respondChaseBeyondVision": false, | ||||
"respondStandGround": false, | "respondStandGround": false, | ||||
"respondHoldGround": false, | "respondHoldGround": false, | ||||
"selectable": false | "selectable": false | ||||
} | } | ||||
}; | }; | ||||
// 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 487 Lines • ▼ Show 20 Lines | "Order.Garrison": function(msg) { | ||||
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 () { | ||||
FreagarachUnsubmitted Not Done Inline Actions- Freagarach: -` ` | |||||
this.SetNextState("INDIVIDUAL.CHEERING"); | return { "discardOrder": true }; | ||||
}, | }, | ||||
"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 774 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(){ | |||||
FreagarachUnsubmitted Not Done Inline Actions+ Freagarach: +` ` | |||||
this.SetNextState("CHEERING"); | |||||
FreagarachUnsubmitted Not Done Inline ActionsPerhaps also check if we're really idle? Freagarach: Perhaps also check if we're really idle? | |||||
SilierAuthorUnsubmitted Done Inline ActionsNo order does that Silier: No order does that | |||||
FreagarachUnsubmitted 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… | |||||
SilierAuthorUnsubmitted 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`. | |||||
FreagarachUnsubmitted 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… | |||||
}, | |||||
Not Done Inline Actionscomma Silier: comma | |||||
"enter": function() { | "enter": function() { | ||||
Not Done Inline Actions+\n Freagarach: +`\n` | |||||
// 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 | ||||
// a new state are done on the next turn. | // a new state are done on the next turn. | ||||
▲ Show 20 Lines • Show All 414 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) { | ||||
FreagarachUnsubmitted Not Done Inline Actions+/n Freagarach: +`/n` | |||||
StanUnsubmitted 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); | ||||
this.shouldCheer = false; | |||||
Not Done Inline ActionsIsn't it still false here? Freagarach: Isn't it still false here? | |||||
} else { | |||||
FreagarachUnsubmitted 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());`? | |||||
} | |||||
}, | }, | ||||
"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(); | ||||
this.SetDefaultAnimationVariant(); | this.SetDefaultAnimationVariant(); | ||||
▲ 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(); | ||||
FreagarachUnsubmitted 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… | |||||
FreagarachUnsubmitted Not Done Inline Actions+/n Freagarach: +`/n` | |||||
this.Cheer(); | |||||
this.TellFriendlyUnitsToCheer(); | |||||
} | |||||
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": { | ||||
"enter": function() { | "enter": function() { | ||||
if (!this.MoveToTargetAttackRange(this.order.data.target, this.order.data.attackType)) | if (!this.MoveToTargetAttackRange(this.order.data.target, this.order.data.attackType)) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 954 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; | ||||
}, | }, | ||||
"LosRangeUpdate": function(msg) { | |||||
if (this.GetStance().targetVisibleEnemies) | |||||
this.AttackEntitiesByPreference(msg.data.added); | |||||
}, | |||||
"LosHealRangeUpdate": function(msg) { | |||||
this.RespondToHealableEntities(msg.data.added); | |||||
}, | |||||
"leave": function() { | "leave": function() { | ||||
this.StopTimer(); | this.StopTimer(); | ||||
this.ResetAnimation(); | this.ResetAnimation(); | ||||
var cmpResistance = Engine.QueryInterface(this.entity, IID_Resistance); | |||||
cmpResistance.SetInvulnerability(false); | |||||
}, | }, | ||||
"Timer": function(msg) { | "Timer": function(msg) { | ||||
this.FinishOrder(); | this.FinishOrder(); | ||||
}, | }, | ||||
}, | }, | ||||
"PACKING": { | "PACKING": { | ||||
▲ Show 20 Lines • Show All 205 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 | ||||
}, | }, | ||||
}; | }; | ||||
UnitAI.prototype.Init = function() | UnitAI.prototype.Init = function() | ||||
Not Done Inline ActionsConstant? Stan: Constant? | |||||
{ | { | ||||
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; | ||||
this.finishedOrder = false; // used to find if all formation members finished the order | this.finishedOrder = false; // used to find if all formation members finished the order | ||||
▲ Show 20 Lines • Show All 142 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 or (un)packing, in which case we only clear the order queue | ||||
FreagarachUnsubmitted Not Done Inline Actions+. Freagarach: +`.` | |||||
FreagarachUnsubmitted Not Done Inline Actions-or Freagarach: -`or` | |||||
if (this.isGarrisoned || this.IsPacking() || this.orderQueue[0] && this.orderQueue[0].type == "Cheering") | if (this.isGarrisoned || this.IsPacking()) | ||||
{ | { | ||||
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 251 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) | UnitAI.prototype.PushOrderFront = function(type, data) | ||||
{ | { | ||||
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 | ||||
FreagarachUnsubmitted Not Done Inline Actions+. Freagarach: +`.` | |||||
// same thing if current order if packing/unpacking | if (this.order && this.IsPacking()) | ||||
if (this.order && this.order.type == "Cheering") | |||||
{ | |||||
var cheeringOrder = this.orderQueue.shift(); | |||||
this.orderQueue.unshift(cheeringOrder, order); | |||||
} | |||||
else if (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 50 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(); | ||||
this.orderQueue = [packingOrder, order]; | this.orderQueue = [packingOrder, order]; | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
this.orderQueue = []; | this.orderQueue = []; | ||||
▲ Show 20 Lines • Show All 76 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,552 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); | ||||
}; | }; | ||||
/** | UnitAI.prototype.TellFriendlyUnitsToCheer = function() | ||||
FreagarachUnsubmitted 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… | |||||
* Adds cheer order to the queue. Forced so it won't be interrupted by attacks. | { | ||||
*/ | let cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership); | ||||
if (!cmpOwnership) | |||||
return; | |||||
let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); | |||||
// Keep that range small so units cheer in reasonable distance | |||||
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.`? | |||||
let nearby = cmpRangeManager.ExecuteQuery(this.entity, 0, 10, [cmpOwnership.GetOwner()], IID_UnitAI); | |||||
for (let i = 0; i < nearby.length; ++i) | |||||
Not Done Inline ActionsInline? Freagarach: Inline? | |||||
Engine.QueryInterface(nearby[i], IID_UnitAI).Cheer(); | |||||
} | |||||
FreagarachUnsubmitted Not Done Inline Actions+; Freagarach: +`;` | |||||
UnitAI.prototype.Cheer = function() | UnitAI.prototype.Cheer = function() | ||||
{ | { | ||||
this.AddOrder("Cheering", { "force": true }, false); | this.PushOrderFront("Cheer", { "force": false }); | ||||
}; | }; | ||||
UnitAI.prototype.Pack = function(queued) | UnitAI.prototype.Pack = function(queued) | ||||
{ | { | ||||
// Check that we can pack | // Check that we can pack | ||||
if (this.CanPack()) | if (this.CanPack()) | ||||
this.AddOrder("Pack", { "force": true }, queued); | this.AddOrder("Pack", { "force": true }, queued); | ||||
}; | }; | ||||
▲ Show 20 Lines • Show All 639 Lines • ▼ Show 20 Lines | 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.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. | ||||
*/ | */ | ||||
Not Done Inline Actionsfriendly Freagarach: `friendly` | |||||
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; | ||||
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? | |||||
return cmpFormation.GetMembers().every(ent => { | return cmpFormation.GetMembers().every(ent => { | ||||
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)`? | |||||
var cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI); | var cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI); | ||||
return cmpUnitAI[funcname].apply(cmpUnitAI, args); | return cmpUnitAI[funcname].apply(cmpUnitAI, args); | ||||
}); | }); | ||||
}; | }; | ||||
UnitAI.prototype.UnitFsm = new FSM(UnitAI.prototype.UnitFsmSpec); | UnitAI.prototype.UnitFsm = new FSM(UnitAI.prototype.UnitFsmSpec); | ||||
Engine.RegisterComponentType(IID_UnitAI, "UnitAI", UnitAI); | Engine.RegisterComponentType(IID_UnitAI, "UnitAI", UnitAI); |
Capital n?