Index: binaries/data/mods/public/simulation/components/Promotion.js =================================================================== --- binaries/data/mods/public/simulation/components/Promotion.js +++ binaries/data/mods/public/simulation/components/Promotion.js @@ -40,12 +40,6 @@ // Save the entity id. this.promotedUnitEntity = ChangeEntityTemplate(this.entity, promotedTemplateName); - - let cmpPosition = Engine.QueryInterface(this.promotedUnitEntity, IID_Position); - let cmpUnitAI = Engine.QueryInterface(this.promotedUnitEntity, IID_UnitAI); - - if (cmpPosition && cmpPosition.IsInWorld() && cmpUnitAI) - cmpUnitAI.Cheer(); }; Promotion.prototype.IncreaseXp = function(amount) Index: binaries/data/mods/public/simulation/components/UnitAI.js =================================================================== --- binaries/data/mods/public/simulation/components/UnitAI.js +++ binaries/data/mods/public/simulation/components/UnitAI.js @@ -636,8 +636,8 @@ this.isGarrisoned = false; }, - "Order.Cheering": function(msg) { - this.SetNextState("INDIVIDUAL.CHEERING"); + "Order.Cheer": function () { + return { "discardOrder": true }; }, "Order.Pack": function(msg) { @@ -1428,6 +1428,9 @@ }, "IDLE": { + "Order.Cheer": function(){ + this.SetNextState("CHEERING"); + }, "enter": function() { // Switch back to idle animation to guarantee we won't // get stuck with an incorrect animation @@ -1859,6 +1862,7 @@ this.order.data.target = target; } + this.shouldCheer = false; if (!this.CanAttack(target)) { this.SetNextState("COMBAT.FINDINGNEWTARGET"); @@ -1908,8 +1912,13 @@ this.FaceTowardsTarget(this.order.data.target); let cmpBuildingAI = Engine.QueryInterface(this.entity, IID_BuildingAI); - if (cmpBuildingAI) + if (cmpBuildingAI) { cmpBuildingAI.SetUnitAITarget(this.order.data.target); + this.shouldCheer = false; + } else { + let cmpUnitAI = Engine.QueryInterface(this.order.data.target, IID_UnitAI); + this.shouldCheer = cmpUnitAI && !cmpUnitAI.IsAnimal(); + } }, "leave": function() { @@ -2020,6 +2029,11 @@ if (this.GetStance().respondHoldGround) this.WalkToHeldPosition(); + if (this.shouldCheer) { + this.Cheer(); + this.TellFriendlyUnitsToCheer(); + } + return true; }, }, @@ -2990,19 +3004,23 @@ "CHEERING": { "enter": function() { - // Unit is invulnerable while cheering - var cmpResistance = Engine.QueryInterface(this.entity, IID_Resistance); - cmpResistance.SetInvulnerability(true); this.SelectAnimation("promotion"); - this.StartTimer(2800, 2800); + this.StartTimer(2800); return false; }, + "LosRangeUpdate": function(msg) { + if (this.GetStance().targetVisibleEnemies) + this.AttackEntitiesByPreference(msg.data.added); + }, + + "LosHealRangeUpdate": function(msg) { + this.RespondToHealableEntities(msg.data.added); + }, + "leave": function() { this.StopTimer(); this.ResetAnimation(); - var cmpResistance = Engine.QueryInterface(this.entity, IID_Resistance); - cmpResistance.SetInvulnerability(false); }, "Timer": function(msg) { @@ -3383,8 +3401,8 @@ if (msg.to != INVALID_PLAYER && msg.from != INVALID_PLAYER) { // 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 - if (this.isGarrisoned || this.IsPacking() || this.orderQueue[0] && this.orderQueue[0].type == "Cheering") + // except if garrisoned or or (un)packing, in which case we only clear the order queue + if (this.isGarrisoned || this.IsPacking()) { this.orderQueue.length = Math.min(this.orderQueue.length, 1); Engine.PostMessage(this.entity, MT_UnitAIOrderDataChanged, { "to": this.GetOrderData() }); @@ -3652,14 +3670,8 @@ UnitAI.prototype.PushOrderFront = function(type, data) { var order = { "type": type, "data": data }; - // If current order is cheering then add new order after it - // same thing if current order if packing/unpacking - if (this.order && this.order.type == "Cheering") - { - var cheeringOrder = this.orderQueue.shift(); - this.orderQueue.unshift(cheeringOrder, order); - } - else if (this.order && this.IsPacking()) + // If current order is packing/unpacking then add new order after it + if (this.order && this.IsPacking()) { var packingOrder = this.orderQueue.shift(); this.orderQueue.unshift(packingOrder, order); @@ -3726,17 +3738,9 @@ let garrisonHolder = this.IsGarrisoned() && type != "Ungarrison" ? this.GetGarrisonHolder() : null; - // Special cases of orders that shouldn't be replaced: - // 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) + // Do not replace Packing/unpacking unless it is cancel order // TODO: maybe a better way of doing this would be to use priority levels - if (this.order && this.order.type == "Cheering") - { - var order = { "type": type, "data": data }; - var cheeringOrder = this.orderQueue.shift(); - this.orderQueue = [cheeringOrder, order]; - } - else if (this.IsPacking() && type != "CancelPack" && type != "CancelUnpack") + if (this.IsPacking() && type != "CancelPack" && type != "CancelUnpack") { var order = { "type": type, "data": data }; var packingOrder = this.orderQueue.shift(); @@ -3830,13 +3834,7 @@ } // 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 = []; this.AddOrders(this.workOrders); Engine.PostMessage(this.entity, MT_UnitAIOrderDataChanged, { "to": this.GetOrderData() }); @@ -5405,12 +5403,21 @@ this.AddOrder("Flee", { "target": target, "force": false }, queued); }; -/** - * Adds cheer order to the queue. Forced so it won't be interrupted by attacks. - */ +UnitAI.prototype.TellFriendlyUnitsToCheer = function() +{ + 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 + let nearby = cmpRangeManager.ExecuteQuery(this.entity, 0, 10, [cmpOwnership.GetOwner()], IID_UnitAI); + for (let i = 0; i < nearby.length; ++i) + Engine.QueryInterface(nearby[i], IID_UnitAI).Cheer(); +} + UnitAI.prototype.Cheer = function() { - this.AddOrder("Cheering", { "force": true }, false); + this.PushOrderFront("Cheer", { "force": false }); }; UnitAI.prototype.Pack = function(queued)