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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
|
local class = require"common.class"
local CHUNK_SIZE = require"common.constants".CHUNK_SIZE
-- Hex: q,r,s. invariant that q+r+s=0
-- add, subtract
-- constructor takes 3 positions and rounds to closest hex centre.
-- round to nearest int, rounding .5 away from 0.
local function round(x)
if x<0 then
return math.ceil(x-0.5)
else
return math.floor(x+0.5)
end
end
local Pos, Hex, ChunkPos
local SR3 = math.sqrt(3)
Hex = class()
function Hex.new(cls)
return setmetatable({},cls)
end
function Hex.init(self,q,r,s)
s=s or -q-r
assert(q+r+s==0,"hex coord doesn't meet invariant")
self.q=q
self.r=r
self.s=s
return self
end
function Hex.make(cls,...)
return cls:new():init(...)
end
function Hex.round(self)
-- return a new Hex rounded to integer coordinates
local fq,fr,fs = self.q,self.r,self.s
-- round all to nearest integer
-- find which was changed the most, reset that one to be coherent with the other two.
local abs = math.abs
local rq,rr,rs = round(fq),round(fr),round(fs)
local dq,dr,ds = abs(fq-rq), abs(fr-rr), abs(fs-rs)
if dq>dr and dq>ds then
rq = -rr-rs
elseif dr>ds then
rr = -rq-rs
else
rs = -rq-rr
end
return Hex:make(rq,rr,rs)
end
function Hex.to_pos(self,into)
into = into or Pos:new()
local x = self.q*SR3 + self.r*(SR3/2)
local y = self.r*(3/2)
return into:init(x,y)
end
function Hex.containing_chunk(self)
local u = math.floor(self.q/CHUNK_SIZE)
local v = math.floor(self.r/CHUNK_SIZE)
return ChunkPos:make(u,v)
end
function Hex.chunk_and_offset(self)
local cp = self:containing_chunk()
local tl = cp:extents()
return cp, (self-tl)
end
function Hex.offset_in_chunk(self)
local cp,offs = self:chunk_and_offset()
return offs
end
function Hex.__add(self,other) return Hex:make(self.q+other.q, self.r+other.r, self.s+other.s) end
function Hex.__sub(self,other) return Hex:make(self.q-other.q, self.r-other.r, self.s-other.s) end
function Hex.__tostring(self) return string.format("H(%.2f,%.2f)",self.q,self.r) end
function Hex.__eq(a,b) return a.q==b.q and a.r==b.r end
Pos = class()
function Pos.new(cls)
return setmetatable({},cls)
end
function Pos.init(self,x,y)
self.x = x
self.y = y
return self
end
function Pos.make(cls,...)
return cls:new():init(...)
end
function Pos.__add(self,other) return Pos:make(self.x+other.x,self.y+other.y) end
function Pos.__sub(self,other) return Pos:make(self.x-other.x,self.y-other.y) end
function Pos.__mul(a,b)
if type(a) == "number" then
return Pos:make(a*b.x,a*b.y)
elseif type(b) == "number" then
return Pos:make(a.x*b,a.y*b)
else
error("can only multiply Pos by scalar")
end
end
function Pos.__div(a,b)
assert(type(b) == "number","can only divide Pos by scalar, and can't divide scalar by Pos")
return a*(1/b)
end
function Pos.__eq(a,b) return a.x==b.x and a.y==b.y end
function Pos.lensq(self) return self.x^2 + self.y^2 end
function Pos.len(self) return math.sqrt(self:lensq()) end
function Pos.norm(self) return self/self:len() end
function Pos.dot(self,other) return self.x*other.x + self.y*other.y end
function Pos.to_hex(self,into)
into = into or Hex:new()
local q = self.x*(SR3/3) - self.y*(1/3)
local r = (2/3)*self.y
return into:init(q,r,-q-r)
end
function Pos.__tostring(self) return string.format("(%.2f,%.2f)",self.x,self.y) end
-- represents coordinates of a chunk
-- ie pair of integers. the chunk at spawn is C(0,0), the one to the right of that is C(1,0), etc
ChunkPos = class()
function ChunkPos.make(cls,u,v)
return setmetatable({u=u,v=v},cls)
end
function ChunkPos.__add(self,other) return ChunkPos:make(self.u+other.u,self.v+other.v) end
function ChunkPos.__sub(self,other) return ChunkPos:make(self.u-other.u,self.v-other.v) end
function ChunkPos.__tostring(self) return string.format("C(%d,%d)",self.u,self.v) end
function ChunkPos.__eq(a,b) return a.u==b.u and a.v==b.v end
function ChunkPos.extents(self)
-- returns Hex of topleft and bottomright
local tlq,tlr = self.u*CHUNK_SIZE, self.v*CHUNK_SIZE
local brq,brr = (self.u+1)*CHUNK_SIZE -1, (self.v+1)*CHUNK_SIZE -1
return Hex:make(tlq,tlr), Hex:make(brq,brr)
end
function ChunkPos.filename(self)
-- filename of chunk with that cp
return "world/c_"..self.u.."_"..self.v..".dat"
end
return {Hex=Hex,Pos=Pos,ChunkPos=ChunkPos}
|