Index: ps/trunk/binaries/data/mods/public/simulation/components/Turretable.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/components/Turretable.js +++ ps/trunk/binaries/data/mods/public/simulation/components/Turretable.js @@ -76,10 +76,7 @@ let cmpUnitAI = Engine.QueryInterface(this.entity, IID_UnitAI); if (cmpUnitAI) - { - cmpUnitAI.SetGarrisoned(); cmpUnitAI.SetTurretStance(); - } let cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion); if (cmpUnitMotion) @@ -138,7 +135,6 @@ if (cmpUnitAI) { cmpUnitAI.Ungarrison(); - cmpUnitAI.UnsetGarrisoned(); cmpUnitAI.ResetTurretStance(); } Index: ps/trunk/binaries/data/mods/public/simulation/components/UnitAI.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/components/UnitAI.js +++ ps/trunk/binaries/data/mods/public/simulation/components/UnitAI.js @@ -496,8 +496,9 @@ if (this.MustKillGatherTarget(msg.data.target)) { + const bestAttack = Engine.QueryInterface(this.entity, IID_Attack)?.GetBestAttackAgainst(msg.data.target, false); // Make sure we can attack the target, else we'll get very stuck - if (!this.GetBestAttackAgainst(msg.data.target, false)) + if (!bestAttack) { // Oops, we can't attack at all - give up // TODO: should do something so the player knows why this failed @@ -523,6 +524,9 @@ return ACCEPT_ORDER; } + if (!this.AbleToMove() && !this.CheckTargetRange(msg.data.target, IID_Attack, bestAttack)) + return this.FinishOrder(); + this.PushOrderFront("Attack", { "target": msg.data.target, "force": !!msg.data.force, "hunting": true, "allowCapture": false }); return ACCEPT_ORDER; } @@ -3449,11 +3453,14 @@ this.SetStance(this.template.DefaultStance); }; -UnitAI.prototype.IsTurret = function() +/** + * @param {cmpTurretable} cmpTurretable - Optionally the component to save a query here. + * @return {boolean} - Whether we are occupying a turret point. + */ +UnitAI.prototype.IsTurret = function(cmpTurretable) { - if (!this.isGarrisoned) - return false; - let cmpTurretable = Engine.QueryInterface(this.entity, IID_Turretable); + if (!cmpTurretable) + cmpTurretable = Engine.QueryInterface(this.entity, IID_Turretable); return cmpTurretable && cmpTurretable.HolderID() != INVALID_ENTITY; }; @@ -3555,7 +3562,7 @@ */ UnitAI.prototype.AbleToMove = function(cmpUnitMotion) { - if (this.isImmobile || this.IsTurret()) + if (this.isImmobile) return false; if (!cmpUnitMotion) @@ -3868,9 +3875,8 @@ this.orderQueue.shift(); this.order = this.orderQueue[0]; - let cmpPosition = Engine.QueryInterface(this.entity, IID_Position); if (this.orderQueue.length && (this.isGarrisoned || this.IsFormationController() || - cmpPosition && cmpPosition.IsInWorld())) + Engine.QueryInterface(this.entity, IID_Position)?.IsInWorld())) { let ret = this.UnitFsm.ProcessMessage(this, { "type": "Order."+this.order.type, @@ -4157,18 +4163,12 @@ if (this.workOrders.length == 0) return false; - if (this.isGarrisoned) - { - if (this.IsTurret()) - { - let cmpTurretable = Engine.QueryInterface(this.entity, IID_Turretable); - if (!cmpTurretable || !cmpTurretable.LeaveTurret()) - return false; - } - let cmpGarrisonable = Engine.QueryInterface(this.entity, IID_Garrisonable); - if (!cmpGarrisonable || !cmpGarrisonable.UnGarrison(false)) - return false; - } + if (this.isGarrisoned && !Engine.QueryInterface(this.entity, IID_Garrisonable)?.UnGarrison(false)) + return false; + + const cmpTurretable = Engine.QueryInterface(this.entity, IID_Turretable); + if (this.IsTurret(cmpTurretable) && !cmpTurretable.LeaveTurret()) + return false; this.orderQueue = []; @@ -4870,16 +4870,19 @@ */ UnitAI.prototype.CheckTargetVisible = function(target) { - var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership); + if (this.isGarrisoned) + return false; + + const cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership); if (!cmpOwnership) return false; - var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); + const cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); if (!cmpRangeManager) return false; // Entities that are hidden and miraged are considered visible - var cmpFogging = Engine.QueryInterface(target, IID_Fogging); + const cmpFogging = Engine.QueryInterface(target, IID_Fogging); if (cmpFogging && cmpFogging.IsMiraged(cmpOwnership.GetOwner())) return true; @@ -5540,7 +5543,7 @@ UnitAI.prototype.Garrison = function(target, queued, pushFront) { // Not allowed to garrison when occupying a turret, at the moment. - if (this.isGarrisoned) + if (this.isGarrisoned || this.IsTurret()) return; if (target == this.entity) return; @@ -5557,7 +5560,7 @@ */ UnitAI.prototype.Ungarrison = function() { - if (!this.isGarrisoned) + if (!this.isGarrisoned && !this.IsTurret()) return; this.AddOrder("Ungarrison", null, false); }; @@ -5927,6 +5930,7 @@ UnitAI.prototype.SetTurretStance = function() { + this.SetImmobile(); this.previousStance = undefined; if (this.GetStance().respondStandGround) return; @@ -5942,6 +5946,7 @@ UnitAI.prototype.ResetTurretStance = function() { + this.SetMobile(); if (!this.previousStance) return; this.SwitchToStance(this.previousStance); Index: ps/trunk/binaries/data/mods/public/simulation/components/tests/test_UnitAI.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/components/tests/test_UnitAI.js +++ ps/trunk/binaries/data/mods/public/simulation/components/tests/test_UnitAI.js @@ -14,6 +14,7 @@ Engine.LoadComponentScript("interfaces/ResourceSupply.js"); Engine.LoadComponentScript("interfaces/ResourceGatherer.js"); Engine.LoadComponentScript("interfaces/Timer.js"); +Engine.LoadComponentScript("interfaces/Turretable.js"); Engine.LoadComponentScript("interfaces/UnitAI.js"); Engine.LoadComponentScript("Formation.js"); Engine.LoadComponentScript("UnitAI.js");