From f34fb5614c6855ea3bb3b76e63824f494e52d606 Mon Sep 17 00:00:00 2001 From: wraitii Date: Tue, 5 Jan 2021 10:12:47 +0000 Subject: [PATCH] Improve behaviour of formations stuck within other units. Large units risk being stuck between other units. This is true in general, but particularly weird with formations, since individual units may well not be stuck, only the invisible formation controller. This alleviates the issue by ordering units to move individually when the controller appears stuck. It introduces a new "VERY_OBSTRUCTED" unit motion message, which triggers when a unit has failed to move for several turns. Reported By: Angen Reviewed By: Freagarach Fixes #4935 Differential Revision: https://code.wildfiregames.com/D3209 This was SVN commit r24511. --- .../public/simulation/components/UnitAI.js | 23 +++++++++++++++++-- source/simulation2/MessageTypes.h | 3 ++- .../simulation2/components/CCmpUnitMotion.cpp | 9 +++++++- .../scripting/MessageTypeConversions.cpp | 4 +++- 4 files changed, 34 insertions(+), 5 deletions(-) diff --git a/binaries/data/mods/public/simulation/components/UnitAI.js b/binaries/data/mods/public/simulation/components/UnitAI.js index a881ba8788..1978380de5 100644 --- a/binaries/data/mods/public/simulation/components/UnitAI.js +++ b/binaries/data/mods/public/simulation/components/UnitAI.js @@ -957,13 +957,30 @@ UnitAI.prototype.UnitFsmSpec = { }, "leave": function() { + this.StopTimer(); this.StopMoving(); }, "MovementUpdate": function(msg) { + if (msg.veryObstructed && !this.timer) + { + // It's possible that the controller (with large clearance) + // is stuck, but not the individual units. + // Ask them to move individually for a little while. + this.CallMemberFunction("MoveTo", [this.order.data]); + this.StartTimer(3000); + return; + } + else if (this.timer) + return; if (msg.likelyFailure || this.CheckRange(this.order.data)) this.FinishOrder(); }, + + "Timer": function() { + // Reenter to reset the pathfinder state. + this.SetNextState("WALKING"); + } }, "WALKINGANDFIGHTING": { @@ -1240,8 +1257,8 @@ UnitAI.prototype.UnitFsmSpec = { "MEMBER": { "OrderTargetRenamed": function(msg) { // In general, don't react - we don't want to send spurious messages to members. - // This looks odd for hunting hwoever because we wait for all - // entities to ahve clumped around the dead resource before proceeding + // This looks odd for hunting however because we wait for all + // entities to have clumped around the dead resource before proceeding // so explicitly handle this case. if (this.order && this.order.data && this.order.data.hunting && this.order.data.target == msg.data.newentity && @@ -4222,6 +4239,8 @@ UnitAI.prototype.StopTimer = function() UnitAI.prototype.OnMotionUpdate = function(msg) { + if (msg.veryObstructed) + msg.obstructed = true; this.UnitFsm.ProcessMessage(this, Object.assign({ "type": "MovementUpdate" }, msg)); }; diff --git a/source/simulation2/MessageTypes.h b/source/simulation2/MessageTypes.h index 98275915a6..0ada42baf4 100644 --- a/source/simulation2/MessageTypes.h +++ b/source/simulation2/MessageTypes.h @@ -327,7 +327,8 @@ public: enum UpdateType { LIKELY_SUCCESS, // UnitMotion considers it is arrived at destination. LIKELY_FAILURE, // UnitMotion says it cannot reach the destination. - OBSTRUCTED, // UitMotion was obstructed. This does not mean stuck, but can be a hint to run range checks. + OBSTRUCTED, // UnitMotion was obstructed. This does not mean stuck, but can be a hint to run range checks. + VERY_OBSTRUCTED, // Sent when obstructed several time in a row. LENGTH }; diff --git a/source/simulation2/components/CCmpUnitMotion.cpp b/source/simulation2/components/CCmpUnitMotion.cpp index 9d25d61a8c..8e1ef43569 100644 --- a/source/simulation2/components/CCmpUnitMotion.cpp +++ b/source/simulation2/components/CCmpUnitMotion.cpp @@ -105,6 +105,12 @@ static const u8 MAX_FAILED_MOVEMENTS = 40; static const u8 ALTERNATE_PATH_TYPE_DELAY = 3; static const u8 ALTERNATE_PATH_TYPE_EVERY = 6; +/** + * After this many failed computations, start sending "VERY_OBSTRUCTED" messages instead. + * Should probably be larger than ALTERNATE_PATH_TYPE_DELAY. + */ +static const u8 VERY_OBSTRUCTED_THRESHOLD = 10; + static const CColor OVERLAY_COLOR_LONG_PATH(1, 1, 1, 1); static const CColor OVERLAY_COLOR_SHORT_PATH(1, 0, 0, 1); @@ -557,7 +563,8 @@ private: if (IsFormationMember() && IsFormationControllerMoving()) return; - CMessageMotionUpdate msg(CMessageMotionUpdate::OBSTRUCTED); + CMessageMotionUpdate msg(m_FailedMovements >= VERY_OBSTRUCTED_THRESHOLD ? + CMessageMotionUpdate::VERY_OBSTRUCTED : CMessageMotionUpdate::OBSTRUCTED); GetSimContext().GetComponentManager().PostMessage(GetEntityId(), msg); } diff --git a/source/simulation2/scripting/MessageTypeConversions.cpp b/source/simulation2/scripting/MessageTypeConversions.cpp index 344e318c48..a6d3a69441 100644 --- a/source/simulation2/scripting/MessageTypeConversions.cpp +++ b/source/simulation2/scripting/MessageTypeConversions.cpp @@ -265,7 +265,7 @@ CMessage* CMessageTerritoryPositionChanged::FromJSVal(const ScriptInterface& scr //////////////////////////////// const std::array CMessageMotionUpdate::UpdateTypeStr = { { - "likelySuccess", "likelyFailure", "obstructed" + "likelySuccess", "likelyFailure", "obstructed", "veryObstructed" } }; JS::Value CMessageMotionUpdate::ToJSVal(const ScriptInterface& scriptInterface) const @@ -290,6 +290,8 @@ CMessage* CMessageMotionUpdate::FromJSVal(const ScriptInterface& scriptInterface return new CMessageMotionUpdate(CMessageMotionUpdate::LIKELY_FAILURE); if (updateString == L"obstructed") return new CMessageMotionUpdate(CMessageMotionUpdate::OBSTRUCTED); + if (updateString == L"veryObstructed") + return new CMessageMotionUpdate(CMessageMotionUpdate::VERY_OBSTRUCTED); LOGWARNING("CMessageMotionUpdate::FromJSVal passed wrong updateString"); return NULL;