--[[ This file is part of Ice Lua Components. Ice Lua Components is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Ice Lua Components is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with Ice Lua Components. If not, see . ]] local map_cache = nil local map_cache_aerate = nil -- returns a list consisting of {t,r,g,b} tuplets function map_pillar_raw_unpack(tpack) local xlen,ylen,zlen xlen,ylen,zlen = common.map_get_dims() local t = {} local i,j,y i = 1 y = 0 while true do -- fill with air while y < tpack[i+1] do t[y+1] = nil y = y + 1 end -- fill with top data j = i + 4 while y <= tpack[i+2] do t[y+1] = {tpack[j+3],tpack[j+2],tpack[j+1],tpack[j+0]} y = y + 1 j = j + 4 end -- check if end if tpack[i] == 0 then -- fill the rest with invisible while y < ylen do t[y+1] = false y = y + 1 end -- that's it break end local ntr = tpack[i]-1-(tpack[i+2]-tpack[i+1]+1) i = i + 4*tpack[i] ntr = tpack[i+3] - ntr -- fill with invisible while y < ntr do t[y+1] = false y = y + 1 end -- fill with bottom data while y < tpack[i+3] do t[y+1] = {tpack[j+3],tpack[j+2],tpack[j+1],tpack[j+0]} y = y + 1 j = j + 4 end end return t end function map_pillar_raw_get(x,z) if map_cache then local o = map_hashcoord2(x,z) map_cache[o] = map_cache[o] or { x = x, z = z, l = map_pillar_raw_unpack(common.map_pillar_get(x,z)), } return map_cache[o].l end return map_pillar_raw_unpack(common.map_pillar_get(x,z)) end function map_pillar_raw_pack(t) local xlen,ylen,zlen xlen,ylen,zlen = common.map_get_dims() local tpack = {} local y,i local rmode = 0 local n,s,e,a a = 0 i = nil y = 0 while true do -- skip air while t[y+1] == nil and y < ylen do y = y + 1 end if i then tpack[i] = n end if y >= ylen then -- inject empty run i = #tpack+1 tpack[i+0] = 0 tpack[i+1] = ylen tpack[i+2] = ylen-1 tpack[i+3] = a break end -- allocate slot i = #tpack+1 tpack[i+0] = 0 tpack[i+1] = y tpack[i+2] = 0 tpack[i+3] = a -- copy top run n = 1 while t[y+1] do tpack[#tpack+1] = t[y+1][4] tpack[#tpack+1] = t[y+1][3] tpack[#tpack+1] = t[y+1][2] tpack[#tpack+1] = t[y+1][1] y = y + 1 n = n + 1 end tpack[i+2] = y-1 -- skip dirt while t[y+1] == false do y = y + 1 if y >= ylen then break end end if y >= ylen then break end -- build bottom run while t[y+1] do tpack[#tpack+1] = t[y+1][4] tpack[#tpack+1] = t[y+1][3] tpack[#tpack+1] = t[y+1][2] tpack[#tpack+1] = t[y+1][1] n = n + 1 y = y + 1 end a = y end return tpack end function map_pillar_raw_set(x,z,t) if map_cache then map_cache[map_hashcoord2(x,z)] = { x = x, z = z, l = t, } return end local tpack = map_pillar_raw_pack(t) common.map_pillar_set(x,z,tpack) if img_overview and tpack[5] then -- TODO: check for wrapping local r,g,b b = tpack[5] g = tpack[6] r = tpack[7] local c = argb_split_to_merged(r,g,b) common.img_pixel_set(img_overview, x, z, c) common.img_pixel_set(img_overview_hmap, x, z, tpack[2]) end end function map_cache_start() map_cache = map_cache or {} map_cache_aerate = map_cache_aerate or {} end function map_cache_end() local _, m local old_map_cache_aerate = map_cache_aerate map_cache_aerate = nil for _, m in pairs(old_map_cache_aerate or {}) do map_pillar_aerate(m.x, m.z) end local old_map_cache = map_cache map_cache = nil for _, m in pairs(old_map_cache or {}) do map_pillar_raw_set(m.x, m.z, m.l) end end function map_block_aerate(x,y,z) return ({ 1, 64+math.sin((x+z-y)*math.pi/4)*8, 32-math.sin((x-z+y)*math.pi/4)*8, 0 }) end function map_pillar_aerate(x,z) if map_cache_aerate then map_cache_aerate[map_hashcoord2(x,z)] = {x = x, z = z} return end local xlen,ylen,zlen xlen,ylen,zlen = common.map_get_dims() local t = map_pillar_raw_get(x,z) local l = { map_pillar_raw_get(x-1,z), map_pillar_raw_get(x+1,z), map_pillar_raw_get(x,z-1), map_pillar_raw_get(x,z+1), } local y for y=1,ylen do if t[y] then if l[1][y] ~= nil and l[2][y] ~= nil and l[3][y] ~= nil and l[4][y] ~= nil and t[y-1] ~= nil and (y == ylen or t[y+1] ~= nil) then t[y] = false end elseif t[y] == false then if l[1][y] == nil or l[2][y] == nil or l[3][y] == nil or l[4][y] == nil or t[y-1] == nil or (y ~= ylen and t[y+1] == nil) then t[y] = map_block_aerate(x,y-1,z) end end end map_pillar_raw_set(x,z,t) end function map_hashcoord3(x,y,z) local xlen,ylen,zlen xlen,ylen,zlen = common.map_get_dims() return (y % ylen) + ylen*((x % xlen) + xlen*(z % zlen)) end function map_hashcoord2(x,z) local xlen,ylen,zlen xlen,ylen,zlen = common.map_get_dims() return (x % xlen) +xlen*(z % zlen) end function map_chkdisbrk(x,y,z) -- A* ftw local loadq = { {x-1,y,z}, {x+1,y,z}, {x,y-1,z}, {x,y+1,z}, {x,y,z-1}, {x,y,z+1}, } local tmap = {} local pmap = {} local plist = {} local ptag = {} local ptaglist = {} local nukeq = {} local xlen,ylen,zlen xlen,ylen,zlen = common.map_get_dims() -- build chunks local i,j for i=1,#loadq do local prio,tx,ty,tz tx,ty,tz = loadq[i][1],loadq[i][2],loadq[i][3] if not pmap[map_hashcoord2(tx,tz)] then pmap[map_hashcoord2(tx,tz)] = map_pillar_raw_get(tx,tz) plist[#plist+1] = {tx,tz} end if (not tmap[map_hashcoord3(tx,ty,tz)]) and pmap[map_hashcoord2(tx,tz)][ty+1] ~= nil then local pq = collect_new_prioq(function(p,q) return p[1] < q[1] end) tmap[map_hashcoord3(tx,ty,tz)] = { heur = -ty, dist = 0, i = i, } pq.push({-ty,tx,ty,tz}) local nukeasm = {} while nukeasm and not pq.empty() do local c = pq.pop() prio,tx,ty,tz = c[1],c[2],c[3],c[4] --print(prio,tx,ty,tz) local tm = tmap[map_hashcoord3(tx,ty,tz)] if prio <= tm.heur + tm.dist then --print(i,prio,tx,ty,tz) nukeasm[#nukeasm+1] = {tx,ty,tz} if not pmap[map_hashcoord2(tx,tz)] then pmap[map_hashcoord2(tx,tz)] = map_pillar_raw_get(tx,tz) plist[#plist+1] = {tx,tz} end local nb = { {tx-1,ty,tz}, {tx+1,ty,tz}, {tx,ty-1,tz}, {tx,ty+1,tz}, {tx,ty,tz-1}, {tx,ty,tz+1}, } local dist = tm.dist+1 for j=1,6 do local cx,cy,cz = nb[j][1], nb[j][2], nb[j][3] local cm = tmap[map_hashcoord3(cx,cy,cz)] if cy == ylen or (cm and cm.i ~= i) then --print("BAIL!") nukeasm = nil break end if not pmap[map_hashcoord2(cx,cz)] then pmap[map_hashcoord2(cx,cz)] = map_pillar_raw_get(cx,cz) plist[#plist+1] = {cx,cz} end if pmap[map_hashcoord2(cx,cz)][cy+1] ~= nil then local heur = -cy if not cm then cm = { heur = heur, dist = dist, i = i, } tmap[map_hashcoord3(cx,cy,cz)] = cm pq.push({heur+dist,cx,cy,cz}) else if cm.heur+cm.dist > heur+dist then cm.heur = heur cm.dist = dist pq.push({heur+dist,cx,cy,cz}) end end end end end end if nukeasm then nukeq[#nukeq+1] = nukeasm --print(#nukeq,#nukeasm) end end end -- nuke it all -- TODO: assemble falling PMFs and drop the buggers local brokestuff = false for i=1,#nukeq do local tx,ty,tz local nl = nukeq[i] if #nl > 0 then brokestuff = true end local lpmf = {} local pmfx, pmfy, pmfz = nil, nil, nil for j=1,#nl do local c = nl[j] tx,ty,tz = c[1],c[2],c[3] local hc2 = map_hashcoord2(tx,tz) local cl = pmap[hc2][ty+1] if cl and cl ~= true then if not pmfx then pmfx, pmfy, pmfz = tx, ty, tz end local r,g,b r,g,b = cl[2], cl[3], cl[4] if #lpmf < 4000 then lpmf[#lpmf+1] = { radius=16, x = (tx - pmfx)*32, y = (ty - pmfy)*32, z = (tz - pmfz)*32, r = r, g = g, b = b, } end end if not ptag[hc2] then ptag[hc2] = true ptaglist[#ptaglist+1] = {tx,tz} end pmap[hc2][ty+1] = nil end if #lpmf > 0 then local mdl = common.model_new(1) mdl = common.model_bone_new(mdl, #lpmf) common.model_bone_set(mdl, 0, "fall", lpmf) particles_add(new_particle({ x = pmfx + 0.5, y = pmfy + 0.5, z = pmfz + 0.5, model = mdl, free_model = true, noclip = true, vry = math.random()*2-1, vrx = 0.4, vry2 = math.random()*2-1, size = 8, })) end end if brokestuff and client then client.wav_play_global(wav_grif,x+0.5,y+0.5,z+0.5) end -- apply nukings local nptag = {} local nptaglist = {} for i=1,#ptaglist do local tx,tz local c = ptaglist[i] tx,tz = c[1], c[2] map_pillar_raw_set(tx,tz,pmap[map_hashcoord2(tx,tz)]) if not nptag[map_hashcoord2(tx,tz)] then nptag[map_hashcoord2(tx,tz)] = true nptaglist[#nptaglist+1] = {tx,tz} end end -- aerate for i=1,#nptaglist do local tx,tz local c = nptaglist[i] tx,tz = c[1], c[2] map_pillar_aerate(tx,tz) end end function map_block_get(x,y,z) local xlen,ylen,zlen xlen,ylen,zlen = common.map_get_dims() if y < 0 then return nil end if y >= ylen then return false end if map_cache then local t = map_pillar_raw_get(x,z) return t[y+1] else local l = common.map_pillar_get(x,z) local i = 1 local ltop = 0 while true do -- Check if air if y < l[i+1] then return nil end -- Check lower colour group if y <= l[i+2] then i = i + ((y-l[i+1])+1)*4 return {l[i+3], l[i+2], l[i+1], l[i+0]} end -- Check N if l[i+0] == 0 then -- Solid invisible return false end -- Calculate ltop ltop = (l[i+0] - 1) - (l[i+2] - l[i+1] + 1) -- Advance i = i + l[i+0]*4 -- Check for ceiling clash if y < l[i+3] then -- Check upper colour group if y < l[i+3]-ltop then -- Solid invisible return false else -- Coloured solid i = i + (y-l[i+3])*4 return {l[i+3], l[i+2], l[i+1], l[i+0]} end end end end end function map_block_set(x,y,z,typ,r,g,b) local xlen,ylen,zlen xlen,ylen,zlen = common.map_get_dims() if y < 0 or y >= ylen then return end local t = map_pillar_raw_get(x,z) t[y+1] = {typ, r, g, b} map_pillar_raw_set(x,z,t) map_pillar_aerate(x,z) map_pillar_aerate(x-1,z) map_pillar_aerate(x+1,z) map_pillar_aerate(x,z-1) map_pillar_aerate(x,z+1) if bhealth_clear then bhealth_clear(x,y,z,false) end end function map_block_paint(x,y,z,typ,r,g,b) local xlen,ylen,zlen xlen,ylen,zlen = common.map_get_dims() if y < 0 or y >= ylen then return end local t = map_pillar_raw_get(x,z) if t[y+1] then t[y+1] = {typ, r, g, b} map_pillar_raw_set(x,z,t) end end function map_block_break(x,y,z) local xlen,ylen,zlen xlen,ylen,zlen = common.map_get_dims() if y < 0 or y >= ylen-1 then return false end local t = map_pillar_raw_get(x,z) if t[y+1] == nil then return false end t[y+1] = nil map_pillar_raw_set(x,z,t) map_pillar_aerate(x,z) map_pillar_aerate(x-1,z) map_pillar_aerate(x+1,z) map_pillar_aerate(x,z-1) map_pillar_aerate(x,z+1) map_chkdisbrk(x,y,z) bhealth_clear(x,y,z,false) return true end function map_block_delete(x,y,z) local xlen,ylen,zlen xlen,ylen,zlen = common.map_get_dims() if y < 0 or y >= ylen-1 then return end local t = map_pillar_raw_get(x,z) t[y+1] = nil map_pillar_raw_set(x,z,t) map_pillar_aerate(x,z) map_pillar_aerate(x-1,z) map_pillar_aerate(x+1,z) map_pillar_aerate(x,z-1) map_pillar_aerate(x,z+1) end function map_block_pick(x,y,z) local xlen,ylen,zlen xlen,ylen,zlen = common.map_get_dims() if x < 0 or x >= xlen then return end if y < 0 or y >= ylen then return end if z < 0 or z >= zlen then return end local t = map_pillar_raw_get(x,z) local c = t[y+1] if c==nil then error(x..","..y..","..z) end if c==false then return nil end return c[1],c[2],c[3],c[4] end --checks for neighbors function map_is_buildable(x, y, z) local xlen,ylen,zlen xlen,ylen,zlen = common.map_get_dims() --warning! a long condition if map_block_get(x,y,z) == nil then if map_block_get(x + 1,y,z) ~= nil or map_block_get(x - 1,y,z) ~= nil or map_block_get(x,y + 1,z) ~= nil or map_block_get(x,y - 1,z) ~= nil or map_block_get(x,y,z - 1) ~= nil or map_block_get(x,y,z + 1) ~= nil then if x >=0 and x < xlen and y >= 0 and y < ylen - 2 and z >= 0 and z < zlen then for i=1,players.max do local p = players[i] if p and p.alive then if math.floor(p.x) == x and math.floor(p.y) == y and math.floor(p.z) == z then return false end end end return true end end else return false; end end