dfhack/scripts/fix/item-occupancy.lua

132 lines
3.7 KiB
Lua

-- Verify item occupancy and block item list integrity.
--[[=begin
fix/item-occupancy
==================
Diagnoses and fixes issues with nonexistant 'items occupying site', usually
caused by `autodump` bugs or other hacking mishaps. Checks that:
#. Item has ``flags.on_ground`` <=> it is in the correct block item list
#. A tile has items in block item list <=> it has ``occupancy.item``
#. The block item lists are sorted
=end]]
local utils = require 'utils'
function check_block_items(fix)
local cnt = 0
local icnt = 0
local found = {}
local found_somewhere = {}
local should_fix = false
local can_fix = true
for _,block in ipairs(df.global.world.map.map_blocks) do
local itable = {}
local bx,by,bz = pos2xyz(block.map_pos)
-- Scan the block item vector
local last_id = nil
local resort = false
for _,id in ipairs(block.items) do
local item = df.item.find(id)
local ix,iy,iz = pos2xyz(item.pos)
local dx,dy,dz = ix-bx,iy-by,iz-bz
-- Check sorted order
if last_id and last_id >= id then
print(bx,by,bz,last_id,id,'block items not sorted')
resort = true
else
last_id = id
end
-- Check valid coordinates and flags
if not item.flags.on_ground then
print(bx,by,bz,id,dx,dy,'in block & not on ground')
elseif dx < 0 or dx >= 16 or dy < 0 or dy >= 16 or dz ~= 0 then
found_somewhere[id] = true
print(bx,by,bz,id,dx,dy,dz,'invalid pos')
can_fix = false
else
found[id] = true
itable[dx + dy*16] = true;
-- Check missing occupancy
if not block.occupancy[dx][dy].item then
print(bx,by,bz,dx,dy,'item & not occupied')
if fix then
block.occupancy[dx][dy].item = true
else
should_fix = true
end
end
end
end
-- Sort the vector if needed
if resort then
if fix then
utils.sort_vector(block.items)
else
should_fix = true
end
end
icnt = icnt + #block.items
-- Scan occupancy for spurious marks
for x=0,15 do
local ocx = block.occupancy[x]
for y=0,15 do
if ocx[y].item and not itable[x + y*16] then
print(bx,by,bz,x,y,'occupied & no item')
if fix then
ocx[y].item = false
else
should_fix = true
end
end
end
end
cnt = cnt + 256
end
-- Check if any items are missing from blocks
for _,item in ipairs(df.global.world.items.all) do
if item.flags.on_ground and not found[item.id] then
can_fix = false
if not found_somewhere[item.id] then
print(id,item.pos.x,item.pos.y,item.pos.z,'on ground & not in block')
end
end
end
-- Report
print(cnt.." tiles and "..icnt.." items checked.")
if should_fix and can_fix then
print("Use 'fix/item-occupancy --fix' to fix the listed problems.")
elseif should_fix then
print("The problems are too severe to be fixed by this script.")
end
end
local opt = ...
local fix = false
if opt then
if opt == '--fix' then
fix = true
else
qerror('Invalid option: '..opt)
end
end
print("Checking item occupancy - this will take a few seconds.")
check_block_items(fix)