From a9e09e9eacbe91c4bc91a46f8d3fff07211aff65 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 25 Dec 2024 05:46:34 -0800 Subject: [PATCH] allow messengers to rescue stuck squads --- docs/fix/stuck-squad.rst | 20 +++++++------- fix/stuck-squad.lua | 44 ++++++++++++++++++++++++------- internal/notify/notifications.lua | 7 ++--- 3 files changed, 50 insertions(+), 21 deletions(-) diff --git a/docs/fix/stuck-squad.rst b/docs/fix/stuck-squad.rst index ec0691395..21a2d5048 100644 --- a/docs/fix/stuck-squad.rst +++ b/docs/fix/stuck-squad.rst @@ -2,27 +2,29 @@ fix/stuck-squad =============== .. dfhack-tool:: - :summary: Allow squads returning from missions to rescue lost squads. + :summary: Allow squads and messengers to rescue lost squads. :tags: fort bugfix military Occasionally, squads that you send out on a mission get stuck on the world map. They lose their ability to navigate and are unable to return to your fortress. -This tool allows another of your squads that is (successfully) returning from a -mission to rescue the lost squad along the way and bring them home. +This tool allows a messenger that is returning from a holding or any other of +your squads that is returning from a mission to rescue the lost squad along the +way and bring them home. This fix is enabled by default in the DFHack `control panel `, or you can run it as needed. However, it -is still up to you to send out another squad that can be tasked with the rescue -mission. You can send the squad out on a relatively innocuous mission, like -"Demand one-time tribute", to minimize risk to the squad. +is still up to you to send out a messenger or squad that can be tasked with the +rescue. If you have a holding that is linked to your fort, you can send out a +messenger -- you don't have to actually request any workers. Otherwise, you can +send a squad out on a mission with minimal risk, like "Demand one-time tribute". This tool is integrated with `gui/notify`, so you will get a notification in -the DFHack notification panel when a squad is stuck and there are no squads -currently out traveling that can rescue them. +the DFHack notification panel when a squad is stuck and there are no squads or +messengers currently out traveling that can rescue them. Note that there might be other reasons why your squad appears missing -- if it got wiped out in combat and nobody survived to report back, for example -- but -this tool should fix the cases that are actual bugs. +this tool should allow you to recover from the cases that are actual bugs. Usage ----- diff --git a/fix/stuck-squad.lua b/fix/stuck-squad.lua index 987fabbdc..7bfc739b0 100644 --- a/fix/stuck-squad.lua +++ b/fix/stuck-squad.lua @@ -16,10 +16,18 @@ end local function is_army_valid_and_returning(army) local controller = get_top_controller(army.controller) - if not controller or controller.goal ~= df.army_controller_goal_type.SITE_INVASION then - return false, false + if not controller then return false, false end + if controller.goal == df.army_controller_goal_type.SITE_INVASION then + return true, controller.data.goal_site_invasion.flag.RETURNING_HOME + elseif controller.goal == df.army_controller_goal_type.MAKE_REQUEST then + return true, controller.data.goal_make_request.flag.RETURNING_HOME end - return true, controller.data.goal_site_invasion.flag.RETURNING_HOME + return false, false +end + +local function get_hf_army(hf) + if not hf then return end + return df.army.find(hf.info and hf.info.whereabouts and hf.info.whereabouts.army_id or -1) end -- need to check all squad positions since some members may have died @@ -28,7 +36,7 @@ local function get_squad_army(squad) for _,sp in ipairs(squad.positions) do local hf = df.historical_figure.find(sp.occupant) if not hf then goto continue end - local army = df.army.find(hf.info and hf.info.whereabouts and hf.info.whereabouts.army_id or -1) + local army = get_hf_army(hf) if army then return army end ::continue:: end @@ -58,6 +66,24 @@ function scan_fort_armies() end ::continue:: end + + if #stuck_armies == 0 then return stuck_armies, nil, nil end + + -- prefer returning with a messenger if one is readily available + for _,messenger in ipairs(dfhack.units.getUnitsByNobleRole('Messenger')) do + local army = get_hf_army(df.historical_figure.find(messenger.hist_figure_id)) + if not army then goto continue end + local valid, returning = is_army_valid_and_returning(army) + if valid then + if returning then + returning_army = {army=army} + else + outbound_army = {army=army} + end + end + ::continue:: + end + return stuck_armies, outbound_army, returning_army end @@ -67,14 +93,14 @@ local function unstick_armies() if not returning_army then local instructions = outbound_army and ('Please wait for %s to complete their objective and run this command again when they are on their way home.'):format( - dfhack.df2console(dfhack.military.getSquadName(outbound_army.squad.id))) - or 'Please send a squad out on a mission that will return to the fort, and'.. + outbound_army.squad and dfhack.df2console(dfhack.military.getSquadName(outbound_army.squad.id)) or 'the messenger') + or 'Please send a squad or a messenger out on a mission that will return to the fort, and'.. ' run this command again when they are on the way home.' - qerror(('%d stuck arm%s found, but no returning armies found to rescue them!\n%s'):format( - #stuck_armies, #stuck_armies == 1 and 'y' or 'ies', instructions)) + qerror(('%d stuck squad%s found, but no returning squads or messengers are available to rescue them!\n%s'):format( + #stuck_armies, #stuck_armies == 1 and '' or 's', instructions)) return end - local returning_squad_name = dfhack.df2console(dfhack.military.getSquadName(returning_army.squad.id)) + local returning_squad_name = returning_army.squad and dfhack.df2console(dfhack.military.getSquadName(returning_army.squad.id)) or 'the messenger' for _,stuck in ipairs(stuck_armies) do print(('fix/stuck-squad: Squad rescue operation underway! %s is rescuing %s'):format( returning_squad_name, dfhack.military.getSquadName(stuck.squad.id))) diff --git a/internal/notify/notifications.lua b/internal/notify/notifications.lua index 76c835497..0d312769d 100644 --- a/internal/notify/notifications.lua +++ b/internal/notify/notifications.lua @@ -323,9 +323,10 @@ NOTIFICATIONS_BY_IDX = { end, on_click=function() local message = 'A squad is lost on the world map and needs rescue!\n\n' .. - 'Please send a squad out on a mission that will return to the fort (e.g.\n' .. - 'a Demand one-time tribute mission, but not a Conquer and occupy mission).\n' .. - 'They will rescue the stuck squad on their way home.' + 'Please send a messenger to a holding or a squad out on a mission\n' .. + 'that will return to the fort (e.g. a Demand one-time tribute mission,\n' .. + 'but not a Conquer and occupy mission). They will rescue the stuck\n' .. + 'squad on their way home.' if not repeat_util.isScheduled('control-panel/fix/stuck-squad') then message = message .. '\n\n' .. 'Please enable fix/stuck-squad in the DFHack control panel to enable\n'..