diff --git a/changelog.txt b/changelog.txt index 10f3f37b29..dcde0e75a8 100644 --- a/changelog.txt +++ b/changelog.txt @@ -33,6 +33,8 @@ Template for new versions: ## Fixes ## Misc Improvements +- `fix/loyaltycascade`: now also breaks up brawls and other intra-fort conflicts that *look* like loyalty cascades +- `makeown`: remove selected unit from any current conflicts so they don't just start attacking other citizens when you make them a citizen of your fort ## Removed diff --git a/docs/fix/loyaltycascade.rst b/docs/fix/loyaltycascade.rst index 39899aae3f..d4448f8c56 100644 --- a/docs/fix/loyaltycascade.rst +++ b/docs/fix/loyaltycascade.rst @@ -6,7 +6,9 @@ fix/loyaltycascade :tags: fort bugfix units This tool neutralizes loyalty cascades by fixing units who consider their own -civilization to be the enemy. +civilization to be the enemy. It will also halt all fighting on the map that +involves your citizens, though "real" enemies will re-engage in combat after a +short delay. Usage ----- diff --git a/docs/makeown.rst b/docs/makeown.rst index 7e2a53ba8a..136e8f5100 100644 --- a/docs/makeown.rst +++ b/docs/makeown.rst @@ -6,7 +6,8 @@ makeown :tags: fort armok units Select a unit in the UI and run this tool to converts that unit to be a fortress -citizen (if sentient). It also removes their foreign affiliation, if any. +citizen (if sentient). It also removes their foreign affiliation, if any, and +removes the unit from any current conflict they are engaged in. This tool also fixes :bug:`10921`, where you request workers from your holdings, but they come with the "Merchant" profession and are unable to diff --git a/fix/loyaltycascade.lua b/fix/loyaltycascade.lua index 768fbdfcf2..3409750cb4 100644 --- a/fix/loyaltycascade.lua +++ b/fix/loyaltycascade.lua @@ -1,4 +1,5 @@ -- Prevents a "loyalty cascade" (intra-fort civil war) when a citizen is killed. +-- Also breaks up brawls and other conflicts. local makeown = reqscript('makeown') @@ -76,7 +77,7 @@ local function fixUnit(unit) makeown.clear_enemy_status(unit) end - return false + return makeown.remove_from_conflict(unit) or fixed end local count = 0 @@ -87,7 +88,7 @@ for _, unit in pairs(dfhack.units.getCitizens()) do end if count > 0 then - print(('Fixed %s units from a loyalty cascade.'):format(count)) + print(('Fixed %s units with loyalty issues.'):format(count)) else print('No loyalty cascade found.') end diff --git a/makeown.lua b/makeown.lua index 244c83a93c..f7657f3250 100644 --- a/makeown.lua +++ b/makeown.lua @@ -81,6 +81,35 @@ function clear_enemy_status(unit) if status_cache.next_slot > status_slot then status_cache.next_slot = status_slot end + + return true +end + +function remove_from_conflict(unit) + -- Remove the unit from any conflict activity + -- They will prompty re-engage if there is an actual enemy around + local to_remove = {} + for act_idx,act_id in ipairs(unit.activities) do + local act = df.activity_entry.find(act_id) + if not act or act.type ~= df.activity_entry_type.Conflict then goto continue end + for _,ev in ipairs(act.events) do + if ev:getType() ~= df.activity_event_type.Conflict then goto next_ev end + for _,side in ipairs(ev.sides) do + utils.erase_sorted(side.histfig_ids, unit.hist_figure_id) + utils.erase_sorted(side.unit_ids, unit.id) + end + ::next_ev:: + end + table.insert(to_remove, 1, act_idx) + ::continue:: + end + + for _,act_idx in ipairs(to_remove) do + unit.activities:erase(act_idx) + end + + -- return whether we removed unit from any conflicts + return #to_remove > 0 end local prof_map = { @@ -162,7 +191,7 @@ local function fix_unit(unit) end clear_enemy_status(unit) - + remove_from_conflict(unit) cancel_hostile_jobs(unit.job.current_job) end