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.

249 lines
6.5 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
6 years ago
6 years ago
6 years ago
6 years ago
  1. ----- [[ GENERALLY USEFUL FUNCTIONS ]] -----------------------------------------
  2. -- rounds numbers. would've been cool to have math.round in lua.
  3. local function round(n)
  4. return n % 1 >= 0.5 and math.ceil(n) or math.floor(n)
  5. end
  6. ----- [[ HEX CONSTANTS & UTILITY FUNCTIONS ]] ----------------------------------
  7. -- all possible vector directions from a given hex by edge
  8. local CUBE_DIRECTIONS = {vec2( 1 , 0),
  9. vec2( 1 , -1),
  10. vec2( 0 , -1),
  11. vec2(-1 , 0),
  12. vec2(-1 , 1),
  13. vec2( 0 , 1)}
  14. -- return hex vector direction via integer index |direction|.
  15. function cube_direction(direction)
  16. return CUBE_DIRECTIONS[(6 + (direction % 6)) % 6 + 1]
  17. end
  18. -- return hexagon adjacent to |hex| in integer index |direction|.
  19. function cube_neighbour(hex, direction)
  20. return hex + HEX_DIRECTIONS[(6 + (direction % 6)) % 6 + 1]
  21. end
  22. -- TODO rotations are different depending on the coordinate system you use.
  23. -- implement this for cube/axial, and doubled.
  24. function cube_rotate_left(hex)
  25. end
  26. function cube_rotate_right(hex)
  27. end
  28. -- rounds a float coordinate trio |x, y, z| to its nearest integer coordinate trio.
  29. -- TODO make work with a table {x, y, z} and vec3(x, y, z)
  30. function cube_round(x, y, z)
  31. local rx = round(x)
  32. local ry = round(y)
  33. local rz = round(z) or round(-x - y)
  34. local xdelta = math.abs(rx - x)
  35. local ydelta = math.abs(ry - y)
  36. local zdelta = math.abs(rz - z)
  37. if xdelta > ydelta and xdelta > zdelta then
  38. rx = -ry - rz
  39. elseif ydelta > zdelta then
  40. ry = -rx - rz
  41. else
  42. rz = -rx - ry
  43. end
  44. return vec2(rx, ry)
  45. end
  46. ----- [[ LAYOUT, ORIENTATION & COORDINATE CONVERSION ]] -----------------------
  47. -- forward & inverse matrices used for the flat orientation.
  48. local FLAT = {M = mat2(3.0/2.0, 0.0, 3.0^0.5/2.0, 3.0^0.5 ),
  49. W = mat2(2.0/3.0, 0.0, -1.0/3.0 , 3.0^0.5/3.0),
  50. start_angle = 0.0}
  51. -- forward & inverse matrices used for the pointy orientation.
  52. local POINTY = {M = mat2(3.0^0.5, 3.0^0.5/2.0, 0.0, 3.0/2.0),
  53. W = mat2(3.0^0.5/3.0, -1.0/3.0, 0.0, 2.0/3.0),
  54. start_angle = 0.5}
  55. -- stores layout: information that does not pertain to map shape
  56. function layout(origin, size, orientation)
  57. return {origin = origin or vec2(0),
  58. size = size or vec2(11),
  59. orientation = orientation or FLAT}
  60. end
  61. -- hex to screen
  62. function cube_to_pixel(cube, layout)
  63. local M = layout.orientation.M
  64. local x = (M[1][1] * cube[1] + M[1][2] * cube[2]) * layout.size[1]
  65. local y = (M[2][1] * cube[1] + M[2][2] * cube[2]) * layout.size[2]
  66. return vec2(x + layout.origin[1], y + layout.origin[2])
  67. end
  68. -- screen to hex
  69. function pixel_to_cube(pix, layout)
  70. local W = layout.orientation.W
  71. local pix = (pix - layout.origin) / layout.size
  72. local s = W[1][1] * pix[1] + W[1][2] * pix[2]
  73. local t = W[2][1] * pix[1] + W[2][2] * pix[2]
  74. return cube_round(s, t, -s - t)
  75. end
  76. -- TODO test, learn am.draw
  77. function hex_corner_offset(corner, layout)
  78. local angle = 2.0 * math.pi * layout.orientation.start_angle + corner / 6
  79. return vec2(layout.size[1] * math.cos(angle),
  80. layout.size[2] * math.sin(angle))
  81. end
  82. -- TODO this thing
  83. function hex_corners(hex, layout)
  84. local corners = {}
  85. end
  86. function cube_to_offset(cube)
  87. return vec2(cube[1], -cube[1] - cube[2] + (cube[1] + (cube[1] % 2)) / 2)
  88. end
  89. function offset_to_cube(off)
  90. return vec2(off[1], off[2] - off[1] * (off[1] % 2) / 2)
  91. end
  92. ----- [[ MAP STORAGE & RETRIEVAL ]] --------------------------------------------
  93. --[[
  94. TODO make all functions work regardless of layout. as it stands, they kind
  95. of do, just not always nicely.
  96. ]]
  97. -- returns ordered ring-shaped map of |radius| from |center|.
  98. function ring_map(center, radius)
  99. local map = {}
  100. local mt = {__index={center=center, radius=radius}}
  101. setmetatable(map, mt)
  102. local walk = center + HEX_DIRECTIONS[6] * radius
  103. for i = 1, 6 do
  104. for j = 1, radius do
  105. table.insert(map, walk)
  106. walk = hex_neighbour(walk, i)
  107. end
  108. end
  109. return map
  110. end
  111. --[[ returns ordered hexagonal map of |radius| rings from |center|.
  112. the only difference between hex_spiral_map and hex_hexagonal_map is that
  113. hex_spiral_map is ordered, in a spiral path from the |center|.
  114. ]]
  115. function spiral_map(center, radius)
  116. local map = {center}
  117. local mt = {__index={center=center, radius=radius}}
  118. setmetatable(map, mt)
  119. for i = 1, radius do
  120. table.append(map, hex_ring_map(center, i))
  121. end
  122. return map
  123. end
  124. -- returns unordered parallelogram-shaped map of |width| and |height|.
  125. function parallelogram_map(width, height)
  126. local map = {}
  127. local mt = {__index={width=width, height=height}}
  128. setmetatable(map, mt)
  129. for i = 0, width do
  130. for j = 0, height do
  131. map[vec2(i, -j)] = true
  132. end
  133. end
  134. return map
  135. end
  136. -- returns unordered triangular map of |size|.
  137. function triangular_map(size)
  138. local map = {}
  139. local mt = {__index={size=size}}
  140. setmetatable(map, mt)
  141. for i = 0, size do
  142. for j = size - s, size do
  143. map[vec2(i, j)] = true
  144. end
  145. end
  146. return map
  147. end
  148. -- returns unordered hexagonal map of |radius|.
  149. function hexagonal_map(radius)
  150. local map = {}
  151. local mt = {__index={radius=radius}}
  152. setmetatable(map, mt)
  153. for i = -radius, radius do
  154. local j1 = math.max(-radius, -i - radius)
  155. local j2 = math.min(radius, -i + radius)
  156. for j = j1, j2 do
  157. map[vec2(i, j)] = true
  158. end
  159. end
  160. return map
  161. end
  162. -- returns unordered rectangular map of |width| and |height|.
  163. function rectangular_map(width, height)
  164. local map = {}
  165. local mt = {__index={width=width, height=height}}
  166. setmetatable(map, mt)
  167. for i = 0, width do
  168. for j = 0, height do
  169. map[vec2(i, -j - math.floor(i/2))] = true
  170. end
  171. end
  172. return map
  173. end
  174. ----- [[ TESTS ]] --------------------------------------------------------------
  175. function test_all()
  176. test_rectangular_map()
  177. end
  178. function test_rectangular_map()
  179. local map = rectangular_map(8, 5)
  180. local layout = layout()
  181. for hex,_ in pairs(map) do
  182. print(cube_to_pixel(hex, layout))
  183. end
  184. for i = 0, 10 do
  185. local mouse = pixel_to_cube(vec2(math.random(map.width) * 22,
  186. math.random(map.height) * 10),
  187. layout)
  188. print(mouse)
  189. end
  190. end