summaryrefslogtreecommitdiff
path: root/client/movement.lua
diff options
context:
space:
mode:
Diffstat (limited to 'client/movement.lua')
-rw-r--r--client/movement.lua92
1 files changed, 92 insertions, 0 deletions
diff --git a/client/movement.lua b/client/movement.lua
new file mode 100644
index 0000000..78af5f0
--- /dev/null
+++ b/client/movement.lua
@@ -0,0 +1,92 @@
+local Pos = require"common.coords".Pos
+
+local function sign(x)
+ if x == 0 then return 0
+ elseif x < 0 then return -1
+ elseif x > 0 then return 1
+ end
+end
+local function clamp(x,minv,maxv)
+ return math.min(math.max(x,minv),maxv)
+end
+
+
+
+-- https://iquilezles.org/articles/distgradfunctions2d/
+local function iqz_hex_sdgf(px,py, r)
+ local kx,ky,kz = -0.866025404,0.5,0.577350269
+ local sx,sy = sign(px),sign(py)
+ px = math.abs(px)
+ py = math.abs(py)
+ local w = kx*px+ky*py
+
+ local _1 = 2*math.min(w,0)
+ px = px - _1*kx
+ py = py - _1*ky
+
+ px = px - clamp(px,-kz*r,kz*r)
+ py = py - r
+
+ d = math.sqrt(px*px+py*py)*sign(py)
+
+ local gx,gy
+ if w<0 then
+ gx = -ky*px - kx*py
+ gy = -kx*px + ky*py
+ else
+ gx,gy = px,py
+ end
+
+ -- dist, gradx, grady
+ return d, sx*gx/d, sy*gy/d
+
+end
+
+
+-- rotate by 30 degrees
+local c30,s30 = math.cos(math.rad(30)), math.sin(math.rad(30))
+local function t_in(x,y) return c30*x-s30*y, s30*x+c30*y end
+local function t_out(x,y) return c30*x+s30*y, -s30*x+c30*y end
+
+-- above sdf has flat top not pointy-top hexagons
+-- so need to rotate
+-- also, 'r' parameter in the above seems to be distance to side
+-- but in our unit system, 1 unit is distance to vertex
+-- conversion factor between these is also cos(30deg)
+-- this is distance to hex at 0,0
+local function hex_sdgf_00(pos)
+ local tpx,tpy = t_in(pos.x,pos.y)
+ local d,gx,gy = iqz_hex_sdgf(tpx,tpy,c30)
+ return d, t_out(gx,gy)
+end
+
+local function hex_sdgf(pos, hex)
+ return hex_sdgf_00(pos-hex:to_pos())
+end
+
+local PLAYER_SIZE = 0.35
+local function collide_with_terrain(old_pos, try_pos, map, tries_remaining)
+ tries_remaining = tries_remaining or 3
+ if tries_remaining <= 0 then return old_pos end
+ local try_h = try_pos:to_hex():round()
+ for near_h in try_h:iter_neighbours(1) do
+ local tile = map:at(near_h)
+ if tile then
+ local d,gx,gy = hex_sdgf(try_pos, near_h)
+ if d < PLAYER_SIZE then
+ local push_dist = PLAYER_SIZE - d
+ local push_dir = Pos:make(gx,gy)
+ local new_try_pos = try_pos + (push_dist*push_dir)
+ return collide_with_terrain(old_pos,new_try_pos,map,tries_remaining-1)
+ end
+ end
+ end
+
+ return try_pos
+end
+
+
+return {
+ collide_with_terrain = collide_with_terrain,
+ hex_sdgf=hex_sdgf
+}