I've tried to figure out why meetings get blocked, and how to get them going again. The number of samples so far is a single save from DFFD. What I've found from that save is as follows:
- A visitor identified by the save provider is blocking visitor meetings. As expected, that visitor displays an interest in joining the fortress.
- Killing the visitor gets meetings going again.
- The visitor's meeting field shows that she wants to conduct a meeting with the fortress. This is normal for all petitioners.
- The visitor's "specific_refs" list is odd, though. She's first scheduled to meet one dorf (presumably the former mayor), and then the current mayor. Both of those dorfs have one entry each in their specific_refs lists, which is a pointer to their respective meeting pointed to by the visitor's list.
- Appointing a third dorf as the mayor gets meetings going, up to a point. That point is when the stalling visitor gets scheduled for a meeting, which shows up as a third entry in her list, and one in the new mayor's. She doesn't attend that meeting either, so the meetings stall again.
- Appointing the former mayor as mayor again (suggested as a solution on the forum) did nothing. The stalling visitor didn't go to her first meeting either. A guess is that some internal state causes her to never consider the first meeting again, possibly when the second one was added (which probably is where DF goes wrong: the first meeting should have been removed from the lists of both participants on the election of a new mayor).
- Using DFHack to remove the meeting entries from the 3 dorfs in question did nothing on its own. I've tried various (probably all sensible) combinations.
- However, removing all the bugged meetings from all the participants AND appointing a third mayor sort of did the trick: meetings resumed, and the stalling visitor was processed properly. Sort of, however, means both of the (now) former mayors remain bugged: they would not perform any meetings when instated into office.
- Killing and reviving the stalling visitor seems to work, though. Somehow the cleanup following the death of the stalling visitor cleans out the specific_ref entries as well as whatever it is that blocks the mayor and former mayor from conducting meetings. When revived, the stalling visitor eventually went to a petition meeting with the elected mayor.
- The stalling situation seems to be easily reproduced: just replace the expedition leader/mayor as a visitor is to attend a meeting (as shows by the unit screen).
- If you don't want the stalling visitor to join, I've successfully unblocked the meetings by inducing the visitor to "head for the forest" as a (hopefully) less disruptive alternative to death-and-resurrection.
- Later development showed there were additional structures involved in df.global.ui. The latest version of the unblockmeetings script doesn't kill anyone.
I've created a couple of scripts that are useful in the situation examined, but they have limitations: I've failed to get the kill/revive script to return the possessions to the inventory of the stalling visitor (DF crashes when I try), so the possessions lie in a pile on the ground, together with a corpse I haven't tried to dispose of. The kill/revive script is based on exterminate.rb, and should thus work on vampires, but I haven't had any to test it with.
Script findmeetingblockers.lua
function findmeetingblockers ()
local count = 0
if not dfhack.isWorldLoaded () or not dfhack.isMapLoaded () then
dfhack.color (COLOR_RED)
dfhack.print("Error: This script requires an embark to be loaded.")
dfhack.color(COLOR_RESET)
dfhack.println()
return
end
for i, unit in ipairs (df.global.world.units.all) do
if #unit.specific_refs ~= 0 then
count = count + 1
dfhack.println ("Possible meeting blocker: " .. dfhack.TranslateName (unit.name, false) .. " " .. dfhack.TranslateName (unit.name, true))
end
end
if count == 0 then
dfhack.println ("No potential meeting blockers found. Your problem may lie elsewhere.")
else
dfhack.println ()
dfhack.println ("Note that the list above probably contains one or more current and/or former leaders/mayors.")
dfhack.println ("You're looking for visitors, not leaders. Note that assassination and revival of diplomats is untested.")
end
end
findmeetingblockers ()
This script is run on the embark to try to find who the blocker is. It will list everyone with entries in the specific_ref list, in particular the mayor(s), and I haven't done anything to try to remove legitimate causes for entries in that list (I don't know what they are).
Script unblockmeetings.lua
-- Work around for blocked meetings where a change of position holder causes the meeting activities to
-- stall. The somewhat heavy handed approch wipes all meeting activities from the participants and the
-- internal store and relies on DF's ability to rebuild the ones that should actually be there.
--
--[====[
unblockmeetings
===============
--
]====]
function unblockmeetings ()
if not dfhack.isWorldLoaded () or not dfhack.isMapLoaded () then
dfhack.printerr ("Error: This script requires an embark to be loaded.")
return
end
dfhack.println ("Deleting " .. tostring (#df.global.ui.meeting_requests) .. " meeting requests")
for i = #df.global.ui.meeting_requests - 1, 0, -1 do
df.global.ui.meeting_requests:erase (i)
end
dfhack.println ("Deleting " .. tostring (#df.global.ui.activities) .. " activities")
for i = #df.global.ui.activities - 1, 0, -1 do
for k = #df.global.ui.activities .unit_actor.specific_refs - 1, 0, -1 do
if df.global.ui.activities .unit_actor.specific_refs [k].type == 4 then -- ACTIVITY
dfhack.println ("Removing actor ACTIVITY from " .. dfhack.TranslateName (df.global.ui.activities .unit_actor.name, true))
df.global.ui.activities .unit_actor.specific_refs:erase (k)
end
end
for k = #df.global.ui.activities .unit_noble.specific_refs - 1, 0, -1 do
if df.global.ui.activities .unit_noble.specific_refs [k].type == 4 then -- ACTIVITY
dfhack.println ("Removing noble ACTIVITY from " .. dfhack.TranslateName (df.global.ui.activities .unit_noble.name, true))
df.global.ui.activities .unit_noble.specific_refs:erase (k)
end
end
df.global.ui.activities:erase (i)
end
end
unblockmeetings ()
This script is executed when a unit is selected in the unit list or with 'v' and kills and revives the unit. Due to the need for DF to run to perform the kill cleanup before the unit is revived, DF will have to run for 3 ticks for the script to finish, so you run the script, deselect the unit, and then either single step DF thrice, or just resume it.
getlost.lua
function getlost ()
local unit = dfhack.gui.getSelectedUnit (true)
unit.flags1.forest = true
end
getlost ()
This script tells the selected unit to head for the forest. Unfortunately, it doesn't override the socializing done by over staying visitors, but bugged ones, such as a visitor who's gotten the petition foiled don't do any socializing, and seem to be subject to the suggestion to get lost. As usual, I have no idea if this script has side effects.
Caveat emptor: Limited testing of the scripts, and no knowledge of what side effects the unblockmeetings script may have.
Edit: Updated with the latest (non lethal) version of unblockmeetings, as well as added an additional point on the known info list.