Changeset View
Standalone View
binaries/data/mods/public/simulation/components/UnitAI.js
Show First 20 Lines • Show All 607 Lines • ▼ Show 20 Lines | UnitAI.prototype.UnitFsmSpec = { | ||||
}, | }, | ||||
"Order.Garrison": function(msg) { | "Order.Garrison": function(msg) { | ||||
if (this.IsTurret()) | if (this.IsTurret()) | ||||
{ | { | ||||
this.SetNextState("IDLE"); | this.SetNextState("IDLE"); | ||||
return; | return; | ||||
} | } | ||||
else if (this.IsGarrisoned()) | else if (this.isGarrisoned) | ||||
{ | { | ||||
this.SetNextState("INDIVIDUAL.GARRISON.GARRISONED"); | this.SetNextState("INDIVIDUAL.GARRISON.GARRISONED"); | ||||
return; | return; | ||||
} | } | ||||
// For packable units: | // For packable units: | ||||
// 1. If packed, we can move to the garrison target. | // 1. If packed, we can move to the garrison target. | ||||
// 2. If unpacked, we first need to pack, then follow case 1. | // 2. If unpacked, we first need to pack, then follow case 1. | ||||
if (this.CanPack()) | if (this.CanPack()) | ||||
{ | { | ||||
this.PushOrderFront("Pack", { "force": true }); | this.PushOrderFront("Pack", { "force": true }); | ||||
return; | return; | ||||
} | } | ||||
this.SetNextState("INDIVIDUAL.GARRISON.APPROACHING"); | this.SetNextState("INDIVIDUAL.GARRISON.APPROACHING"); | ||||
}, | }, | ||||
"Order.Ungarrison": function() { | "Order.Ungarrison": function() { | ||||
this.FinishOrder(); | this.FinishOrder(); | ||||
this.isGarrisoned = false; | |||||
}, | }, | ||||
"Order.Cheering": function(msg) { | "Order.Cheering": function(msg) { | ||||
this.SetNextState("INDIVIDUAL.CHEERING"); | this.SetNextState("INDIVIDUAL.CHEERING"); | ||||
}, | }, | ||||
"Order.Pack": function(msg) { | "Order.Pack": function(msg) { | ||||
if (this.CanPack()) | if (this.CanPack()) | ||||
▲ Show 20 Lines • Show All 2,248 Lines • ▼ Show 20 Lines | "GARRISON": { | ||||
"enter": function() { | "enter": function() { | ||||
let target = this.order.data.target; | let target = this.order.data.target; | ||||
if (!target) | if (!target) | ||||
{ | { | ||||
this.FinishOrder(); | this.FinishOrder(); | ||||
return true; | return true; | ||||
} | } | ||||
if (this.IsGarrisoned()) | if (this.isGarrisoned) | ||||
Freagarach: Notice this fixes a bug present in SVN.
(Look at the selection state of an entity on a wall… | |||||
return false; | return false; | ||||
// Check that we can garrison here | // Check that we can garrison here | ||||
if (this.CanGarrison(target)) | if (this.CanGarrison(target)) | ||||
// Check that we're in range of the garrison target | // Check that we're in range of the garrison target | ||||
if (this.CheckGarrisonRange(target)) | if (this.CheckGarrisonRange(target)) | ||||
{ | { | ||||
var cmpGarrisonHolder = Engine.QueryInterface(target, IID_GarrisonHolder); | var cmpGarrisonHolder = Engine.QueryInterface(target, IID_GarrisonHolder); | ||||
// Check that garrisoning succeeds | // Check that garrisoning succeeds | ||||
if (cmpGarrisonHolder.Garrison(this.entity)) | if (cmpGarrisonHolder.Garrison(this.entity)) | ||||
{ | { | ||||
this.isGarrisoned = true; | |||||
if (this.formationController) | if (this.formationController) | ||||
{ | { | ||||
var cmpFormation = Engine.QueryInterface(this.formationController, IID_Formation); | var cmpFormation = Engine.QueryInterface(this.formationController, IID_Formation); | ||||
if (cmpFormation) | if (cmpFormation) | ||||
{ | { | ||||
// disable rearrange for this removal, | // disable rearrange for this removal, | ||||
// but enable it again for the next | // but enable it again for the next | ||||
// move command | // move command | ||||
▲ Show 20 Lines • Show All 321 Lines • ▼ Show 20 Lines | UnitAI.prototype.Init = function() | ||||
this.lastAttacked = undefined; | this.lastAttacked = undefined; | ||||
this.lastHealed = undefined; | this.lastHealed = undefined; | ||||
this.SetStance(this.template.DefaultStance); | this.SetStance(this.template.DefaultStance); | ||||
}; | }; | ||||
UnitAI.prototype.IsTurret = function() | UnitAI.prototype.IsTurret = function() | ||||
{ | { | ||||
if (!this.IsGarrisoned()) | if (!this.isGarrisoned) | ||||
return false; | return false; | ||||
var cmpPosition = Engine.QueryInterface(this.entity, IID_Position); | var cmpPosition = Engine.QueryInterface(this.entity, IID_Position); | ||||
return cmpPosition && cmpPosition.GetTurretParent() != INVALID_ENTITY; | return cmpPosition && cmpPosition.GetTurretParent() != INVALID_ENTITY; | ||||
}; | }; | ||||
UnitAI.prototype.IsFormationController = function() | UnitAI.prototype.IsFormationController = function() | ||||
{ | { | ||||
return (this.template.FormationController == "true"); | return (this.template.FormationController == "true"); | ||||
Show All 36 Lines | UnitAI.prototype.IsHealer = function() | ||||
return Engine.QueryInterface(this.entity, IID_Heal); | return Engine.QueryInterface(this.entity, IID_Heal); | ||||
}; | }; | ||||
UnitAI.prototype.IsIdle = function() | UnitAI.prototype.IsIdle = function() | ||||
{ | { | ||||
return this.isIdle; | return this.isIdle; | ||||
}; | }; | ||||
/** | |||||
* Whether the entity is garrisoned (according to UnitAI). | |||||
* This is only called from outside UnitAI for performance reasons, | |||||
* if in the future it should return anything else than just the member all calls to | |||||
* this.isGarrisoned here in UnitAI ought to be updated. | |||||
Done Inline ActionsI don't really understand what you're trying to say here. (it also seems to me like you should just query Garrisonable from outside now instead?) wraitii: I don't really understand what you're trying to say here.
(it also seems to me like you should… | |||||
* | |||||
* @return {boolean} Whether the entity is garrisoned. | |||||
*/ | |||||
UnitAI.prototype.IsGarrisoned = function() | UnitAI.prototype.IsGarrisoned = function() | ||||
{ | { | ||||
return this.isGarrisoned; | return this.isGarrisoned; | ||||
}; | }; | ||||
UnitAI.prototype.SetGarrisoned = function() | UnitAI.prototype.SetGarrisoned = function(garrisoned) | ||||
{ | { | ||||
this.isGarrisoned = true; | this.isGarrisoned = garrisoned; | ||||
}; | }; | ||||
UnitAI.prototype.GetGarrisonHolder = function() | UnitAI.prototype.GetGarrisonHolder = function() | ||||
{ | { | ||||
if (this.IsGarrisoned()) | if (this.isGarrisoned) | ||||
{ | { | ||||
for (let order of this.orderQueue) | for (let order of this.orderQueue) | ||||
if (order.type == "Garrison") | if (order.type == "Garrison") | ||||
return order.data.target; | return order.data.target; | ||||
} | } | ||||
return INVALID_ENTITY; | return INVALID_ENTITY; | ||||
}; | }; | ||||
▲ Show 20 Lines • Show All 53 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 cheering 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() || this.orderQueue[0] && this.orderQueue[0].type == "Cheering") | ||||
Done Inline ActionsLess function calls = more performance, and in this case the accessed variable is a property of the this instance, so the getter call doesnt seem necessary as long as the getter can be assumed to do nothing but return the member. 1600 units running one more function call = 1600 more function calls. (now imagine it does two nested iterations around the modified code and you get 160.000 new function calls with a one line difference if youre unlucky or didnt check/consider) elexis: Less function calls = more performance, and in this case the accessed variable is a property of… | |||||
{ | { | ||||
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 176 Lines • ▼ Show 20 Lines | if (!this.orderQueue.length) | ||||
let template = cmpTemplateManager.GetCurrentTemplateName(this.entity); | let template = cmpTemplateManager.GetCurrentTemplateName(this.entity); | ||||
error("FinishOrder called for entity " + this.entity + " (" + template + ") when order queue is empty\n" + stack); | error("FinishOrder called for entity " + this.entity + " (" + template + ") when order queue is empty\n" + stack); | ||||
} | } | ||||
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 | ||||
▲ Show 20 Lines • Show All 130 Lines • ▼ Show 20 Lines | UnitAI.prototype.ReplaceOrder = function(type, data) | ||||
if (data && data.force) | 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: | // Special cases of orders that shouldn't be replaced: | ||||
// 1. Cheering - we're invulnerable, add order after we finish | // 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) | // 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.order && this.order.type == "Cheering") | ||||
{ | { | ||||
var order = { "type": type, "data": data }; | var order = { "type": type, "data": data }; | ||||
▲ Show 20 Lines • Show All 81 Lines • ▼ Show 20 Lines | UnitAI.prototype.UpdateWorkOrders = function(type) | ||||
} | } | ||||
}; | }; | ||||
UnitAI.prototype.BackToWork = function() | UnitAI.prototype.BackToWork = function() | ||||
{ | { | ||||
if (this.workOrders.length == 0) | if (this.workOrders.length == 0) | ||||
return false; | return false; | ||||
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") | if (this.order && this.order.type == "Cheering") | ||||
▲ Show 20 Lines • Show All 1,053 Lines • ▼ Show 20 Lines | if (this.expectedRoute) | ||||
this.expectedRoute = undefined; | this.expectedRoute = undefined; | ||||
if (queued) | if (queued) | ||||
this.PushOrder(type, data); | this.PushOrder(type, data); | ||||
else | else | ||||
{ | { | ||||
// May happen if an order arrives on the same turn the unit is garrisoned | // May happen if an order arrives on the same turn the unit is garrisoned | ||||
// in that case, just forget the order as this will lead to an infinite loop | // in that case, just forget the order as this will lead to an infinite loop | ||||
if (this.IsGarrisoned() && !this.IsTurret() && type != "Ungarrison") | if (this.isGarrisoned && !this.IsTurret() && type != "Ungarrison") | ||||
return; | return; | ||||
this.ReplaceOrder(type, data); | this.ReplaceOrder(type, data); | ||||
} | } | ||||
}; | }; | ||||
/** | /** | ||||
* Adds guard/escort order to the queue, forced by the player. | * Adds guard/escort order to the queue, forced by the player. | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 209 Lines • ▼ Show 20 Lines | UnitAI.prototype.Garrison = function(target, queued) | ||||
this.AddOrder("Garrison", { "target": target, "force": true }, queued); | this.AddOrder("Garrison", { "target": target, "force": true }, queued); | ||||
}; | }; | ||||
/** | /** | ||||
* Adds ungarrison order to the queue. | * Adds ungarrison order to the queue. | ||||
*/ | */ | ||||
UnitAI.prototype.Ungarrison = function() | UnitAI.prototype.Ungarrison = function() | ||||
{ | { | ||||
if (this.IsGarrisoned()) | if (this.isGarrisoned) | ||||
this.AddOrder("Ungarrison", null, false); | this.AddOrder("Ungarrison", null, false); | ||||
}; | }; | ||||
/** | /** | ||||
* Adds a garrison order for units that are already garrisoned in the garrison holder. | * Adds a garrison order for units that are already garrisoned in the garrison holder. | ||||
*/ | */ | ||||
UnitAI.prototype.Autogarrison = function(target) | UnitAI.prototype.Autogarrison = function(target) | ||||
{ | { | ||||
this.isGarrisoned = true; | |||||
this.PushOrderFront("Garrison", { "target": target }); | this.PushOrderFront("Garrison", { "target": target }); | ||||
Done Inline ActionsThe other places in which the member is set, the garrison function is performed immediately after. Here it is a PushOrderFront, but it could also be that some code clears the order queue or does another PushOrderFront before this command is processed. In that case the unit would have the garrisoned state but not have been garrisoned, correct? elexis: The other places in which the member is set, the garrison function is performed immediately… | |||||
Done Inline ActionsAutogarrison Is called either from the ProductionQueue.js or from Transform.js. The former means no orders are present. The latter is called when the garrison holder transforms and thus the entities ought to take their orders with them when being put into the GARRISON-state. Freagarach: `Autogarrison` Is called either from the `ProductionQueue.js` or from `Transform.js`. The… | |||||
}; | }; | ||||
/** | /** | ||||
* Adds gather order to the queue, forced by the player | * Adds gather order to the queue, forced by the player | ||||
* until the target is reached | * until the target is reached | ||||
*/ | */ | ||||
UnitAI.prototype.Gather = function(target, queued) | UnitAI.prototype.Gather = function(target, queued) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 945 Lines • Show Last 20 Lines |
Notice this fixes a bug present in SVN.
(Look at the selection state of an entity on a wall when promoting.)