-- remove_street_tiles_progressive.lua -- Place in media/lua/client/ -- Removes tiles in radius-6 "blocks" per tick until total radius ~60 is covered. local BLOCK_RADIUS = 6 -- radius processed each tick local TOTAL_RADIUS = 60 -- final total radius local BLOCK_STEP = (BLOCK_RADIUS * 2) + 1 -- 13 when BLOCK_RADIUS=6 local function isDebugModeEnabled() if type(isDebugEnabled) == "function" then return isDebugEnabled() == true end return false end local function isAdminPlayer(player) if not isClient() then return true end if not player or not player.getAccessLevel then return false end local accessLevel = tostring(player:getAccessLevel() or ""):lower() return accessLevel == "admin" end local function makeBlocksList(totalRadius, step) local blocks = {} local start = -totalRadius local finish = totalRadius for oy = start, finish, step do for ox = start, finish, step do table.insert(blocks, {ox = ox, oy = oy}) end end return blocks end local function tryRemoveObjFromSquare(sq, obj) if not sq or not obj then return false end local deleted = false if sq.RemoveTileObject then pcall(function() sq:RemoveTileObject(obj) end) deleted = true end if not deleted and sq.transmitRemoveItemFromSquare then pcall(function() sq:transmitRemoveItemFromSquare(obj) end) deleted = true end if not deleted and obj.removeFromWorld then pcall(function() obj:removeFromWorld() end) deleted = true end return deleted end local function processBlockAt(centerX, centerY, centerZ, blockOffsetX, blockOffsetY, player, stats) -- blockOffset are offsets relative to center (e.g. -60, -47, -34, ...) local baseX = centerX + blockOffsetX local baseY = centerY + blockOffsetY for dx = -BLOCK_RADIUS, BLOCK_RADIUS do for dy = -BLOCK_RADIUS, BLOCK_RADIUS do local x = baseX + dx local y = baseY + dy local sq = pcall(function() return getCell():getGridSquare(x, y, centerZ) end) and getCell():getGridSquare(x, y, centerZ) or nil if sq then local objs = nil local ok = pcall(function() objs = sq:getObjects() end) if ok and objs and objs.size and objs:size() > 0 then local n = objs:size() for i = n - 1, 0, -1 do local obj = nil local ok2, o = pcall(function() return objs:get(i) end) obj = ok2 and o or nil if obj then local ok3, spriteName = pcall(function() local spr = obj.getSprite and obj:getSprite() return (spr and spr.getName) and spr:getName() or nil end) spriteName = ok3 and spriteName or nil if spriteName and string.find(string.lower(tostring(spriteName)), "street", 1, false) then if tryRemoveObjFromSquare(sq, obj) then stats.removed = stats.removed + 1 if stats.examples < stats.maxExamples then print(string.format("REMOVE_PROGRESS: removed @ %d,%d -> '%s'", x, y, tostring(spriteName))) stats.examples = stats.examples + 1 end end end end end end end end end end local function startProgressiveRemoval(player) if not player then return end if not isAdminPlayer(player) then return end local px = math.floor(player:getX()) local py = math.floor(player:getY()) local pz = math.floor(player:getZ() or 0) local blocks = makeBlocksList(TOTAL_RADIUS, BLOCK_STEP) local totalBlocks = #blocks if totalBlocks == 0 then if player.Say then player:Say("Nothing to process.") end return end local stats = { removed = 0, examples = 0, maxExamples = 10 } local index = 1 if player.Say then player:Say("Starting progressive removal (radius "..tostring(TOTAL_RADIUS)..").") end print("REMOVE_PROGRESS: start at "..px..","..py.." totalBlocks="..tostring(totalBlocks)) local function onTick() -- Process one block per tick if index > totalBlocks then Events.OnTick.Remove(onTick) local msg = "Removal complete: removed "..tostring(stats.removed).." tiles (filter 'street')." print("REMOVE_PROGRESS: "..msg) if player.Say then player:Say(msg) end return end local b = blocks[index] processBlockAt(px, py, pz, b.ox, b.oy, player, stats) -- Periodic message every 10 blocks if index % 10 == 0 then local progressMsg = string.format("Progress: block %d/%d - removed so far: %d", index, totalBlocks, stats.removed) print("REMOVE_PROGRESS: "..progressMsg) if player.Say then player:Say(progressMsg) end end index = index + 1 end Events.OnTick.Add(onTick) end -- Hook into the world context menu local function onFillWorldObjectContextMenu(playerOrNum, context, worldObjects, test) if test then return end local player if type(playerOrNum) == "number" then player = getSpecificPlayer(playerOrNum) else player = playerOrNum end if not player then return end if not isDebugModeEnabled() then return end if not isAdminPlayer(player) then return end context:addOption("Progressively remove 'street' tiles (radius "..tostring(TOTAL_RADIUS)..")", worldObjects, function() startProgressiveRemoval(player) end) end Events.OnFillWorldObjectContextMenu.Add(onFillWorldObjectContextMenu)