-- 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)