hexyz is tower defense game, and a lua library for dealing with hexagonal grids
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

314 lines
9.4 KiB

6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
  1. --[[============================================================================
  2. ----- GENERALLY USEFUL FUNCTIONS -----
  3. ============================================================================]]--
  4. -- rounds numbers. would've been cool to have math.round in lua.
  5. local function round(n)
  6. return n % 1 >= 0.5 and math.ceil(n) or math.floor(n)
  7. end
  8. --[[============================================================================
  9. ----- HEX CONSTANTS AND UTILITY FUNCTIONS -----
  10. ============================================================================]]--
  11. -- all possible vector directions from a given hex by edge
  12. local CUBE_DIRECTIONS = {vec2( 0 , 1),
  13. vec2( 1 , 0),
  14. vec2( 1 , -1),
  15. vec2( 0 , -1),
  16. vec2(-1 , 0),
  17. vec2(-1 , 1)}
  18. -- return hex vector direction via integer index |direction|.
  19. function cube_direction(direction)
  20. return CUBE_DIRECTIONS[(direction % 6) % 6 + 1]
  21. end
  22. -- return hexagon adjacent to |hex| in integer index |direction|.
  23. function cube_neighbour(hex, direction)
  24. return hex + CUBE_DIRECTIONS[(direction % 6) % 6 + 1]
  25. end
  26. -- return cube coords at location 60deg away to the left; counter-clockwise
  27. function cube_rotate_left(hex)
  28. return vec2(hex.x + hex.y, -hex.x)
  29. end
  30. -- return cube coords at location 60deg away to the right; clockwise
  31. function cube_rotate_right(hex)
  32. return vec2(-hex.y, hex.x + hex.y)
  33. end
  34. -- rounds a float coordinate trio |x, y, z| to nearest integer coordinate trio
  35. local function cube_round(x, y, z)
  36. local rx = round(x)
  37. local ry = round(y)
  38. local rz = round(z) or round(-x - y)
  39. local xdelta = math.abs(rx - x)
  40. local ydelta = math.abs(ry - y)
  41. local zdelta = math.abs(rz - z or round(-x - y))
  42. if xdelta > ydelta and xdelta > zdelta then
  43. rx = -ry - rz
  44. elseif ydelta > zdelta then
  45. ry = -rx - rz
  46. else
  47. rz = -rx - ry
  48. end
  49. return vec2(rx, ry)
  50. end
  51. --[[============================================================================
  52. ----- ORIENTATION & LAYOUT -----
  53. ============================================================================]]--
  54. -- forward & inverse matrices used for the flat orientation
  55. local FLAT = {M = mat2(3.0/2.0, 0.0, 3.0^0.5/2.0, 3.0^0.5 ),
  56. W = mat2(2.0/3.0, 0.0, -1.0/3.0 , 3.0^0.5/3.0),
  57. start_angle = 0.0}
  58. -- forward & inverse matrices used for the pointy orientation
  59. local POINTY = {M = mat2(3.0^0.5, 3.0^0.5/2.0, 0.0, 3.0/2.0),
  60. W = mat2(3.0^0.5/3.0, -1.0/3.0, 0.0, 2.0/3.0),
  61. start_angle = 0.5}
  62. -- stores layout: information that does not pertain to map shape
  63. function layout(origin, size, orientation)
  64. return {origin = origin or vec2(0),
  65. size = size or vec2(11),
  66. orientation = orientation or FLAT}
  67. end
  68. -- hex to screen
  69. function cube_to_pixel(cube, layout)
  70. local M = layout.orientation.M
  71. local x = (M[1][1] * cube[1] + M[1][2] * cube[2]) * layout.size[1]
  72. local y = (M[2][1] * cube[1] + M[2][2] * cube[2]) * layout.size[2]
  73. return vec2(x + layout.origin[1], y + layout.origin[2])
  74. end
  75. -- screen to hex
  76. function pixel_to_cube(pix, layout)
  77. local W = layout.orientation.W
  78. local pix = (pix - layout.origin) / layout.size
  79. local s = W[1][1] * pix[1] + W[1][2] * pix[2]
  80. local t = W[2][1] * pix[1] + W[2][2] * pix[2]
  81. return cube_round(s, t, -s - t)
  82. end
  83. -- TODO test, learn am.draw
  84. function hex_corner_offset(corner, layout)
  85. local angle = 2.0 * math.pi * layout.orientation.start_angle + corner / 6
  86. return vec2(layout.size[1] * math.cos(angle),
  87. layout.size[2] * math.sin(angle))
  88. end
  89. -- TODO this thing
  90. function hex_corners(hex, layout)
  91. local corners = {}
  92. end
  93. -- offset coordinates are prettier to look at
  94. function cube_to_offset(cube)
  95. return vec2(cube[1], -cube[1] - cube[2] + (cube[1] + (cube[1] % 2)) / 2)
  96. end
  97. -- back to cube coordinates
  98. function offset_to_cube(off)
  99. return vec2(off[1], off[2] - off[1] * (off[1] % 2) / 2)
  100. end
  101. --[[============================================================================
  102. ----- MAPS & STORAGE -----
  103. MAPS STORE CUBE COORDINATES. MAPS STORE CUBE COORDINATES. MAPS STORE CUBE COOR
  104. This means, you are not to draw using the coordinates stored in your map.
  105. You are to draw using the cube_to_pixel of those coordinates.
  106. If you wish to draw a hexagon to the screen, you must first use cube_to_pixel
  107. to retrieve the center of the hexagon on each set of cube coordinates stored
  108. in your map. Then, depending on how you are going to draw, either call
  109. am.circle with |sides| = 6, or gather the vertices with hex_corners and
  110. use am.draw - TODO, haven't used am.draw yet.
  111. Information about the maps' dimensions are stored in a metatable, so you can
  112. retrieve details about maps after they are created.
  113. ----- NOISE -----
  114. To simplify terrain generation, unordered, hash-like maps automatically
  115. calculate and store perlin noise as their values. You can modify the nature
  116. of the noise by providing different |frequencies| as a tables of values, for
  117. example: {1, 2, 4, 8} or {1, 0.5, 0.25, 0.125}. These just increase the
  118. complexity of the curvature of the noise. The default is {1}.
  119. ----- TODO -----
  120. TODO make all functions work regardless of layout. as it stands, they kind
  121. of do, just not always nicely.
  122. ============================================================================]]--
  123. ----- ORDERED MAPS -----
  124. -- returns ordered ring-shaped map of |radius| from |center|.
  125. function ring_map(center, radius)
  126. local map = {}
  127. local mt = {__index={center=center, radius=radius}}
  128. setmetatable(map, mt)
  129. local walk = center + CUBE_DIRECTIONS[6] * radius
  130. for i = 1, 6 do
  131. for j = 1, radius do
  132. table.insert(map, walk)
  133. walk = cube_neighbour(walk, i)
  134. end
  135. end
  136. return map
  137. end
  138. -- returns ordered hexagonal map of |radius| rings from |center|.
  139. -- the only difference between spiral_map and hexagonal_map is that
  140. -- spiral_map is ordered, in a spiral path from the |center|.
  141. function spiral_map(center, radius)
  142. local map = {center}
  143. local mt = {__index={center=center, radius=radius}}
  144. setmetatable(map, mt)
  145. for i = 1, radius do
  146. table.append(map, ring_map(center, i))
  147. end
  148. return map
  149. end
  150. ----- UNORDERED, HASH-LIKE MAPS -----
  151. -- returns unordered parallelogram-shaped map of |width| and |height| with perlin noise
  152. function parallelogram_map(width, height)
  153. local map = {}
  154. local mt = {__index={width=width, height=height}}
  155. setmetatable(map, mt)
  156. for i = 0, width do
  157. for j = 0, height do
  158. map[vec2(i, j)] = true
  159. end
  160. end
  161. return map
  162. end
  163. -- returns unordered triangular map of |size| with perlin noise
  164. function triangular_map(size)
  165. local map = {}
  166. local mt = {__index={size=size}}
  167. setmetatable(map, mt)
  168. for i = 0, size do
  169. for j = size - s, size do
  170. map[vec2(i, j)] = true
  171. end
  172. end
  173. return map
  174. end
  175. -- returns unordered hexagonal map of |radius| with perlin noise
  176. function hexagonal_map(radius)
  177. local map = {}
  178. local mt = {__index={radius=radius}}
  179. setmetatable(map, mt)
  180. for i = -radius, radius do
  181. local j1 = math.max(-radius, -i - radius)
  182. local j2 = math.min(radius, -i + radius)
  183. for j = j1, j2 do
  184. map[vec2(i, j)] = true
  185. end
  186. end
  187. return map
  188. end
  189. -- returns unordered rectangular map of |width| and |height| with perlin noise
  190. function rectangular_map(width, height, frequencies)
  191. local map = {}
  192. local mt = {__index={width=width, height=height}}
  193. local frequencies = frequencies or {1}
  194. setmetatable(map, mt)
  195. for i = 0, width do
  196. for j = 0, height do
  197. -- calculate noise
  198. local idelta = assert(i / width, "width must be greater than 0")
  199. local jdelta = assert(j / height, "height must be greater than 0")
  200. local noise = 0
  201. for _,freq in pairs(frequencies) do
  202. noise = noise + 1/freq * math.perlin(vec2(freq * idelta,
  203. freq * jdelta))
  204. end
  205. -- this is what makes it a rectangle
  206. local hex = vec2(i, j - math.floor(i/2))
  207. -- store hex in the map paired with its associated noise value
  208. map[hex] = noise
  209. end
  210. end
  211. return map
  212. end
  213. --[[============================================================================
  214. ----- NOISE -----
  215. ============================================================================]]--
  216. function simplex_map(frequency, exponent, width, height)
  217. local map = {}
  218. for i = 0, height do
  219. for j = 0, width do
  220. local idelta = i/width - 0.5
  221. local jdelta = j/height - 0.5
  222. map[vec2(i, j)] = math.simplex(idelta, jdelta)
  223. end
  224. end
  225. end
  226. --[[============================================================================
  227. ----- PATHFINDING -----
  228. ============================================================================]]--
  229. --[[============================================================================
  230. ----- TESTS -----
  231. ============================================================================]]--
  232. function test_all()
  233. print("it works trust me")
  234. end