View Issue Details

IDProjectCategoryView StatusLast Update
0013167Dwarf FortressDwarf Mode -- Interface, Roomspublic2025-05-15 10:21
ReporterAirToob Assigned To 
PriorityhighSeveritymajorReproducibilityalways
Status newResolutionopen 
PlatformPCOSWindowsOS Version11
Product Version51.11 
Summary0013167: Bedrooms not automatically unassigned on death, results in tedious manual cleanup
DescriptionExample: I just got a burst of 21 deaths (mostly tavern brawl).

So what I have to do now:

Look at alerts, note the name of the first (next) dead dwarf.
Open Zones menu, search for the first part of that name.
From the list of matches, identify the bedroom belonging to the dead dwarf.
Open the details for that bedroom, remove the assignment to that dwarf.
Escape out.
Repeat steps 1. to 5. N times, where this time N=21 - this is indescribably tedious and time consuming.
This is in addition to the usual check on Nobles/Administrators, Hospital positions and Squad members for new vacancies.

SUGGESTION (Probably not new)

EITHER provide a DFHack one-shot command to unassign the bedrooms of all dead entities
OR Fix the game somehow that automatic unassignment of dead owners' bedrooms happens automatically again

Whichever, could this be fixed PDQ, please?
Steps To ReproduceWait for some entity to die
TagsNo tags attached.

Activities

Kijiro

2025-05-12 12:52

reporter   ~0042691

Last edited: 2025-05-12 12:53

Can confirm, just noticed this when I saw I had about 200 bedrooms, 198 assigned but only 175 live Dwarves.

To reproduce:

> Create a bedroom
> Wait for a Dwarf to claim the bedroom
> When a Dwarf claims the bedroom, kill them via any means. Preferably while they are on the map (not abroad)
----> The bedroom will still be claimed by the Dwarf, whose portrait is now naked

Maybe this is this a consequence of the recent code change that stopped units from unclaiming their beds when they left the map?

My save file:

https://dffd.bay12games.com/file.php?id=17419

andrewzeidell

2025-05-14 21:10

reporter   ~0042698

Last edited: 2025-05-14 21:20

This can be fixed via a custom DFHack lua script. I have not tested the ramifications of doing this, as I was up late toying around. One unfortunate effect is that it unassigns tomb zones, but the dwarves are still listed as having graves. I'm going to potentially ruin my save testing this! This script scans all bedroom and tomb zones in your fortress and automatically unassigns any that are still linked to dwarves who have died. It works even in the stripped-down DFHack version bundled with the Steam release, without needing plugins or hidden fields. While the zones are cleared of ownership, the dwarves are still considered to have graves, so burial records remain intact — this prevents issues like "ghost" ownership or cluttered assignment lists without interfering with slab inscriptions or memorialization.

Use notepad++ or somthing similar to save this as a lua script called 'clean_dead_beds' and run from the DFHack launcher:

local isDead = dfhack.units.isDead
local cleared = 0

for _, bld in ipairs(df.global.world.buildings.all) do
  if bld:getType() == df.building_type.Civzone then
    -- skip tombs by checking for the tomb settings field
    local is_tomb = pcall(function() return bld.zone_settings.tomb end)
    if is_tomb then
      -- skip this one
    else
      local success, assigned_unit_id = pcall(function() return bld.assigned_unit_id end)
      if success and assigned_unit_id and assigned_unit_id ~= -1 then
        local unit = df.unit.find(assigned_unit_id)
        if not unit or isDead(unit) then
          print(string.format("Clearing dead unit %d from civzone ID %d", assigned_unit_id, bld.id))
          bld.assigned_unit_id = -1
          cleared = cleared + 1
        end
      end
    end
  end
end

print("Done. Cleared", cleared, "civzone assignments from dead dwarves.")

andrewzeidell

2025-05-14 21:12

reporter   ~0042699

I saved these under the C:\Program Files (x86)\Steam\steamapps\common\Dwarf Fortress\hack\scripts folder to run in DFHack. DFHack provides a lot of useful tools and has been around almost as long as dwarf fortress, you can install it on the Steam version by searching the store for DFHack. I hope that this doesn't engender too much unintentional !!FUN!! as I just winged it after a few beers and 20 years of programming experience.

andrewzeidell

2025-05-15 05:18

reporter   ~0042702

Okay! I have a better alternative. This checks all bedroom zones in the fortress, and if any are still assigned to dead dwarves, it automatically unassigns them. No need to cross-reference alerts, zones, or assignments manually. It also includes a --dry-run option so you can preview what would change before committing. IT doesn't unassign tombs now!

This will unassign any dead dwarves from their bedrooms and print what it did.

Want to preview first? Use the "--dry-run" argument, so clean_dead_beds --dry-run in the DFHack launcher, or just run clean_dead_beds - or whatever you save it as

Hope this saves others from the tedium. Would love to see this kind of cleanup eventually integrated into base game behavior or as a standard DFHack plugin. In the meantime, this makes life much easier.

Here's the lua script to be put into DF Hack's scripts folder, (saved as a .lua):

local args = {...}
local dry_run = args[1] == '--dry-run'

local isDead = dfhack.units.isDead

-- Step 1: Build lookup of bed positions
local beds_by_pos = {}
for _, bld in ipairs(df.global.world.buildings.all) do
  if bld:getType() == df.building_type.Bed then
    local key = string.format("%d,%d,%d", bld.centerx, bld.centery, bld.z)
    beds_by_pos[key] = true
  end
end

-- Step 2: Iterate zones and check conditions
local unassigned_count = 0

for _, bld in ipairs(df.global.world.buildings.all) do
  if bld:getType() == df.building_type.Civzone and bld.assigned_unit_id ~= -1 then
    local unit = df.unit.find(bld.assigned_unit_id)
    if unit and isDead(unit) then
      local has_bed = false
      for x = bld.x1, bld.x2 do
        for y = bld.y1, bld.y2 do
          local key = string.format("%d,%d,%d", x, y, bld.z)
          if beds_by_pos[key] then
            has_bed = true
            break
          end
        end
        if has_bed then break end
      end

      if has_bed then
        if dry_run then
          print(string.format("[DRY RUN] Zone %d would be unassigned from unit %d (was BEDROOM)", bld.id, bld.assigned_unit_id))
        else
          print(string.format("Zone %d unassigned from unit %d (was BEDROOM)", bld.id, bld.assigned_unit_id))
          bld.assigned_unit_id = -1
        end
        unassigned_count = unassigned_count + 1
      end
    end
  end
end

-- Step 3: Summary
if dry_run then
  print(string.format("Dry run complete. %d bedroom assignments would be removed.", unassigned_count))
else
  print(string.format("Done. %d bedroom assignments removed.", unassigned_count))
end

Quietust

2025-05-15 07:12

reporter   ~0042703

Last edited: 2025-05-15 07:16

Side note: there's a much easier way to detect if a civzone is a bedroom - just check if its .type is equal to df.civzone_type.Bedroom. Alternatively, just iterate across df.global.world.buildings.other.ZONE_BEDROOM.

Also, using DFHack is only a workaround, as it doesn't actually fix the underlying issue.

andrewzeidell

2025-05-15 10:21

reporter   ~0042704

Yeah, it was a pretty terrible algorithm for detecting bedrooms, I'm pretty unfamiliar with the data structures, thanks!

By noting it is only a workaround, I presume you mean because it cleans up rooms after the fact, but doesn't cure the bug if rooms not being unassigned? If so, I agree, this is a management solution and not a true fix, and would need to be run each time an issue arises.

Add Note

Note

Issue History

Date Modified Username Field Change
2025-05-09 04:05 AirToob New Issue
2025-05-12 12:52 Kijiro Note Added: 0042691
2025-05-12 12:53 Kijiro Note Edited: 0042691
2025-05-14 21:10 andrewzeidell Note Added: 0042698
2025-05-14 21:12 andrewzeidell Note Added: 0042699
2025-05-14 21:20 andrewzeidell Note Edited: 0042698
2025-05-15 05:18 andrewzeidell Note Added: 0042702
2025-05-15 07:12 Quietust Note Added: 0042703
2025-05-15 07:13 Quietust Note Edited: 0042703
2025-05-15 07:16 Quietust Note Edited: 0042703
2025-05-15 10:21 andrewzeidell Note Added: 0042704