summaryrefslogtreecommitdiff
path: root/client/movement.lua
blob: 5cc6b85e285f7d8362a54938963eccc1649e4267 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
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

	local 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 = require"common.constants".PLAYER_SIZE
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 ~= 0 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
}