--[[ This file is derived from code from 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 portal_transforms = {} local portal_traces = {} local portal_traces_mcache = {} portal_traces_enabled = true portal_transforms_performed = {} function trace_portal_get_transform(cx, cy, cz) local xlen,ylen,zlen xlen,ylen,zlen = common.map_get_dims() if cy > ylen-1 then cy = ylen-1 end if not(portal_traces[cz] and portal_traces[cz][cx] and portal_traces[cz][cx][cy]) then return nil end local tidx = portal_traces[cz][cx][cy] local tf = portal_transforms[tidx] return tf end function trace_portal_transform(tf, cx, cy, cz, vx, vy, vz, noclamp) --print("ENTRY", cx, cy, cz, vx, vy, vz) -- Get origins local cx1 = tf[1][1] + 0.5 + tf[1][4]*0.5 local cy1 = tf[1][2] + 0.5 + tf[1][5]*0.5 local cz1 = tf[1][3] + 0.5 + tf[1][6]*0.5 local cx2 = tf[2][1] + 0.5 + tf[2][4]*0.5 local cy2 = tf[2][2] + 0.5 + tf[2][5]*0.5 local cz2 = tf[2][3] + 0.5 + tf[2][6]*0.5 -- Get normals local nx1 = tf[1][4] local ny1 = tf[1][5] local nz1 = tf[1][6] local nx2 = tf[2][4] local ny2 = tf[2][5] local nz2 = tf[2][6] -- Get sky vectors local sx1 = tf[1][7] local sy1 = tf[1][8] local sz1 = tf[1][9] local sx2 = tf[2][7] local sy2 = tf[2][8] local sz2 = tf[2][9] -- Get horiz vectors local hx1 = ny1*sz1-nz1*sy1 local hy1 = nz1*sx1-nx1*sz1 local hz1 = nx1*sy1-ny1*sx1 local hx2 = ny2*sz2-nz2*sy2 local hy2 = nz2*sx2-nx2*sz2 local hz2 = nx2*sy2-ny2*sx2 -- Get offsets local no1 = nx1*cx1 + ny1*cy1 + nz1*cz1 local no2 = nx2*cx2 + ny2*cy2 + nz2*cz2 local so1 = sx1*cx1 + sy1*cy1 + sz1*cz1 local so2 = sx2*cx2 + sy2*cy2 + sz2*cz2 local ho1 = hx1*cx1 + hy1*cy1 + hz1*cz1 local ho2 = hx2*cx2 + hy2*cy2 + hz2*cz2 --print("NORMS", nx1, ny1, nz1, nx2, ny2, nz2, no1) --print("SKIES", sx1, sy1, sz1, sx2, sy2, sz2, so1) --print("HORIZ", hx1, hy1, hz1, hx2, hy2, hz2, ho1) -- Get source offsets local noP = -math.abs(nx1*cx + ny1*cy + nz1*cz - no1) local soP = (sx1*cx + sy1*cy + sz1*cz - so1) local hoP = -(hx1*cx + hy1*cy + hz1*cz - ho1) soP, hoP = 0, 0 -- centre-suction if necessary if ny1 > 0 or ny2 > 0 then noP = noP - 0.1 end -- Update position cx = (nx2*noP + sx2*soP + hx2*hoP) + no2*nx2 + so2*sx2 + ho2*hx2 cy = (ny2*noP + sy2*soP + hy2*hoP) + no2*ny2 + so2*sy2 + ho2*hy2 cz = (nz2*noP + sz2*soP + hz2*hoP) + no2*nz2 + so2*sz2 + ho2*hz2 -- Get direction offsets local noV = (nx1*vx + ny1*vy + nz1*vz) local soV = -(sx1*vx + sy1*vy + sz1*vz) local hoV = (hx1*vx + hy1*vy + hz1*vz) if not noclamp then noV = math.max(0.1, math.abs(noV)) end -- Update direction vx = -(nx2*noV + sx2*soV + hx2*hoV) vy = -(ny2*noV + sy2*soV + hy2*hoV) vz = -(nz2*noV + sz2*soV + hz2*hoV) -- Return! --print("EXIT ", cx, cy, cz, vx, vy, vz) return cx, cy, cz, vx, vy, vz end local function trace_portal_set_mark(pid, portal_select, cx, cy, cz) cx = math.floor(cx) cy = math.floor(cy) cz = math.floor(cz) local l = portal_traces if nil == l[cz] then l[cz] = {} end if nil == l[cz][cx] then l[cz][cx] = {} end l[cz][cx][cy] = pid*2+portal_select --print(cx, cy, cz) end local function trace_portal_insert(p, pid, portal_select) local cx, cy, cz = p[1], p[2], p[3] local dx, dy, dz = p[4], p[5], p[6] local sx, sy, sz = p[7], p[8], p[9] local hx = dy*sz-dz*sy local hy = dz*sx-dx*sz local hz = dx*sy-dy*sx -- Allocate extra space for dropdown local cymax = (dy~=0 and 3) or 0 -- Mark area local i for i=0,cymax do trace_portal_set_mark(pid, portal_select, cx , cy , cz ) trace_portal_set_mark(pid, portal_select, cx-sx, cy-sy, cz-sz) trace_portal_set_mark(pid, portal_select, cx+sx, cy+sy, cz+sz) trace_portal_set_mark(pid, portal_select, cx +hx, cy +hy, cz +hz) trace_portal_set_mark(pid, portal_select, cx-sx+hx, cy-sy+hy, cz-sz+hz) trace_portal_set_mark(pid, portal_select, cx+sx+hx, cy+sy+hy, cz+sz+hz) trace_portal_set_mark(pid, portal_select, cx -hx, cy -hy, cz -hz) trace_portal_set_mark(pid, portal_select, cx-sx-hx, cy-sy-hy, cz-sz-hz) trace_portal_set_mark(pid, portal_select, cx+sx-hx, cy+sy-hy, cz+sz-hz) cy = cy + dy end end local function trace_portal_setup() local i portal_traces = {} portal_transforms = {} portal_traces_mcache = {} if not portal_traces_enabled then return end -- Gather portal info for i=1,players.max do local plr = players[i] if plr then local p1 = plr.portal_list[1] local p2 = plr.portal_list[2] if p1 and p2 then -- Create transformation! local t1 = {p1, p2, idx=i} local t2 = {p2, p1, idx=i} t1.inv = t2 t2.inv = t1 -- Save transformation! portal_transforms[i*2+1] = t1 portal_transforms[i*2+2] = t2 -- TODO! (instead we're drilling the map) -- Place marks which indicate the transformation thing! trace_portal_insert(p1, i, 1) trace_portal_insert(p2, i, 2) end end end end local function map_pillar_get_fake_core(x, z) -- Check if we have a portal list if not (portal_traces[z] and portal_traces[z][x]) then return common.map_pillar_get(x, z) end -- Check if we have a cache local l = nil if portal_traces_mcache[z] and portal_traces_mcache[z][x] then l = portal_traces_mcache[z][x] return l end --print("TRACES EXIST", x, z) -- We have to edit because of this portal list l = l or common.map_pillar_get(x, z) local pl = portal_traces[z][x] -- Unpack local ul = map_pillar_raw_unpack(l) -- Replace things local xlen,ylen,zlen xlen,ylen,zlen = common.map_get_dims() local i for i=1,ylen do if pl[i-1] then --print("DELETE", x, i, z) ul[i] = nil end end -- Repack l = map_pillar_raw_pack(ul) -- Dump this into the cache if not portal_traces_mcache[z] then portal_traces_mcache[z] = {} end portal_traces_mcache[z][x] = l -- Return return l end local function map_pillar_get_fake(x, z) local l = map_pillar_get_fake_core(x, z) local i = 1 -- Follow to end while l[i+0] ~= 0 do i = i + l[i+0]*4 end -- Check top local xlen,ylen,zlen xlen,ylen,zlen = common.map_get_dims() if l[i+1] >= ylen-1 and trace_portal_get_transform(x,ylen-1,z) then l[i+1] = l[i+1] + 10 l[i+2] = l[i+2] + 10 end return l end -- need to copy-paste these functions from lib_vector.lua in order to mangle them -- Map helpers function trace_gap(x,y,z) local xlen,ylen,zlen xlen,ylen,zlen = common.map_get_dims() local l = map_pillar_get_fake(math.floor(x), math.floor(z)) local i = 1 local h1,h2 h1 = nil while true do h2 = l[i+1] if h2 == ylen-1 then h2 = ylen end if h2 >= ylen and trace_portal_get_transform(x,ylen-1,z) then return h1, nil end if y < l[i+1] or l[i] == 0 then return h1, h2 end i = i + l[i]*4 if y < l[i+3] then return h1, h2 end h1 = l[i+3] h2 = l[i+1] end end function box_is_clear(x1,y1,z1,x2,y2,z2,canwrap) local x,z,i x1 = math.floor(x1) y1 = math.floor(y1) z1 = math.floor(z1) x2 = math.floor(x2) y2 = math.floor(y2) z2 = math.floor(z2) local xlen,ylen,zlen xlen,ylen,zlen = common.map_get_dims() if not canwrap then if x1 < 0 or z1 < 0 then return false elseif x2 >= xlen or z2 >= zlen then return false end end for z=z1,z2 do for x=x1,x2 do local l = map_pillar_get_fake(x, z) i = 1 while true do if y2 >= ylen and trace_portal_get_transform(x,ylen-1,z) then break end if l[i+1] == ylen-1 and y2 < ylen then break end if y2 < l[i+1] then break end if l[i] == 0 then return false end i = i + l[i]*4 if y1 < l[i+3] then return false end end end end return true end -- Ray tracing function trace_map_ray_dist(x1,y1,z1, vx,vy,vz, maxdist, nil_on_maxdist) if nil_on_maxdist == nil then nil_on_maxdist = true end local function depsilon(d) if d < 0.0000001 then return 0.0000001 else return d end end local xlen,ylen,zlen xlen,ylen,zlen = common.map_get_dims() -- direction local gx,gy,gz if vx < 0 then gx = -1 else gx = 1 end if vy < 0 then gy = -1 else gy = 1 end if vz < 0 then gz = -1 else gz = 1 end vx = vx * gx vy = vy * gy vz = vz * gz -- cell local cx,cy,cz if not x1 then return nil end cx = math.floor(x1) cy = math.floor(y1) cz = math.floor(z1) -- subpos local sx,sy,sz sx = x1-cx sy = y1-cy sz = z1-cz if gx >= 0 then sx = 1-sx end if gy >= 0 then sy = 1-sy end if gz >= 0 then sz = 1-sz end local dist = 0 local pillar, npillar npillar = map_pillar_get_fake(cx,cz) pillar = npillar trace_portal_setup() -- Have to do this! while true do local tx = sx/depsilon(vx) local ty = sy/depsilon(vy) local tz = sz/depsilon(vz) local t,d local ncx,ncy,ncz ncx,ncy,ncz = cx,cy,cz if tx < ty and tx < tz then t = tx d = 0 ncx = cx + gx npillar = map_pillar_get_fake(ncx,ncz) elseif ty < tx and ty < tz then t = ty d = 1 ncy = cy + gy else t = tz d = 2 ncz = cz + gz npillar = map_pillar_get_fake(ncx,ncz) end dist = dist + t if dist > maxdist and nil_on_maxdist then return nil, nil, nil, nil, nil, nil, nil elseif dist > maxdist and not nil_on_maxdist then return dist, cx, cy, cz, ncx, ncy, ncz end local i=1 while true do if ncy < npillar[i+1] then break end if npillar[i] == 0 then return dist, cx, cy, cz, ncx, ncy, ncz end i = i + npillar[i]*4 if ncy < npillar[i+3] then return dist, cx, cy, cz, ncx, ncy, ncz end end sx = sx - vx*t sy = sy - vy*t sz = sz - vz*t cx,cy,cz = ncx,ncy,ncz if d == 0 then sx = 1 elseif d == 1 then sy = 1 else sz = 1 end pillar = npillar end end function trace_map_box(x1,y1,z1, x2,y2,z2, bx1,by1,bz1, bx2,by2,bz2, canwrap) local function depsilon(d) if d < 0.0000001 then return 0.0000001 else return d end end -- delta local dx,dy,dz dx = x2-x1 dy = y2-y1 dz = z2-z1 -- offsets local fx,fy,fz if dx < 0 then fx = bx1 else fx = bx2 end if dy < 0 then fy = by1 else fy = by2 end if dz < 0 then fz = bz1 else fz = bz2 end -- direction local gx,gy,gz if dx < 0 then gx = -1 else gx = 1 end if dy < 0 then gy = -1 else gy = 1 end if dz < 0 then gz = -1 else gz = 1 end dx = dx * gx dy = dy * gy dz = dz * gz -- combined box size local bcx,bcy,bcz bcx = (bx2-bx1) bcy = (by2-by1) bcz = (bz2-bz1) -- top left offset (note, incorrect name!) local tlx,tly,tlz if gx >= 0 then tlx = 0.999 else tlx = 0.001 end if gy >= 0 then tly = 0.999 else tly = 0.001 end if gz >= 0 then tlz = 0.999 else tlz = 0.001 end -- apply offset x1 = x1 + fx y1 = y1 + fy z1 = z1 + fz bx1 = bx1 - fx by1 = by1 - fy bz1 = bz1 - fz bx2 = bx2 - fx by2 = by2 - fy bz2 = bz2 - fz -- cell local cx,cy,cz cx = math.floor(x1) cy = math.floor(y1) cz = math.floor(z1) -- target cell local tcx,tcy,tcz tcx = math.floor(x2+fx+gx*0.002) tcy = math.floor(y2+fy+gy*0.002) tcz = math.floor(z2+fz+gz*0.002) -- sub deltas local sx, sy, sz sx = (x1 % 1.0) - 0.001 sy = (y1 % 1.0) - 0.001 sz = (z1 % 1.0) - 0.001 if gx >= 0 then sx = 1-sx end if gy >= 0 then sy = 1-sy end if gz >= 0 then sz = 1-sz end -- restricted x/y/z local rx,ry,rz rx = nil ry = nil rz = nil trace_portal_setup() -- Have to do this! -- TODO: unset these when another boundary is crossed local i local iend = ( math.abs(tcx-cx) + math.abs(tcy-cy) + math.abs(tcz-cz) ) portal_transforms_performed = {} for i=1,iend do -- get the time it takes to hit the boundary local tx = sx/depsilon(dx) local ty = sy/depsilon(dy) local tz = sz/depsilon(dz) local t, d, ck if tx < ty and tx < tz then -- X first d = 0 t = tx elseif ty < tx and ty < tz then -- Y first d = 1 t = ty else -- Z first d = 2 t = tz end sx = sx - t*dx sy = sy - t*dy sz = sz - t*dz x1 = rx or x1 + t*dx*gx y1 = ry or y1 + t*dy*gy z1 = rz or z1 + t*dz*gz if d == 0 then -- X first sx = 1.0 ck = rx or box_is_clear(cx+gx,y1+by1,z1+bz1,cx+gx,y1+by2,z1+bz2,canwrap) if not ck then rx = cx + tlx end if not rx then cx = cx + gx end elseif d == 1 then -- Y first sy = 1.0 ck = ry or box_is_clear(x1+bx1,cy+gy,z1+bz1,x1+bx2,cy+gy,z1+bz2,canwrap) if not ck then ry = cy + tly end if not ry then cy = cy + gy end else -- Z first sz = 1.0 ck = rz or box_is_clear(x1+bx1,y1+by1,cz+gz,x1+bx2,y1+by2,cz+gz,canwrap) if not ck then rz = cz + tlz end if not rz then cz = cz + gz end end --if not ck then return x1-bx1, y1-by1, z1-bz1 end --[[ local tf = trace_portal_get_transform(cx, cy, cz) if tf and portal_transforms_performed[#portal_transforms_performed] ~= tf and portal_transforms_performed[#portal_transforms_performed] ~= tf.inv then table.insert(portal_transforms_performed, tf) cx, cy, cz, dx, dy, dz = trace_portal_transform(tf, cx, cy, cz, dx, dy, dz) x1, y1, z1, gx, gy, gz = trace_portal_transform(tf, x1, y1, z1, gx, gy, gz) x2, y2, z2 = trace_portal_transform(tf, x2, y2, z2, 0, 0, 0) if dx < 0 then sx = 1.0 - sx; dx = -dx end if dy < 0 then sy = 1.0 - sy; dy = -dy end if dz < 0 then sz = 1.0 - sz; dz = -dz end end ]] end -- if rx then rx = rx - fx end if ry then ry = ry - fy end if rz then rz = rz - fz end return rx or x2, ry or y2, rz or z2 end do local s_map_block_get = map_block_get function map_block_get(x,y,z,...) local xlen,ylen,zlen xlen,ylen,zlen = common.map_get_dims() if y >= ylen and trace_portal_get_transform(x,ylen-1,z) then return nil end return s_map_block_get(x,y,z,...) end end