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.

173 lines
5.5 KiB

  1. ----- [[ AXIAL/CUBE COORDINATE SYSTEM FOR AMULET/LUA]] -------------------------
  2. --[[ author@churchianity.ca
  3. -- INTRODUCTION
  4. this is a library for making grids of hexagons using lua.
  5. it has made use of exclusively standard lua 5.2 functionality,
  6. making it as portable as possible. it doesn't even use a point
  7. class, (or classes/metatables at all) simply returning tables
  8. of integers, which can later be unpacked into your amulet
  9. vectors, or whatever else you want to use.
  10. this can result in some nasty looking lines with lots of table
  11. unpacks, but if your graphics library likes traditional lua
  12. types, you will be better off.
  13. it supports triangular, hexagonal, rectangular, and
  14. parallelogram map shapes.
  15. it supports non-regular hexagons, though it's trickier to get
  16. working in amulet. TODO work on this.
  17. -- NOTE ON ORIENTATION + AMULET
  18. because of the way amulet draws hexagons (amulet essentially
  19. draws a 6-sided circle from a centerpoint, instead of of a
  20. series of lines connecting points), the flat orientation is
  21. default and recommended. other orientations are possible
  22. with am.rotate, but can cause aliasing issues. TODO work on this.
  23. -- RESOURCES USED TO DEVELOP THIS LIBRARY
  24. https://redblobgames.com/grid/hexagons - simply amazing. amit is a god.
  25. http://amulet.xyz/doc - amulet documentation
  26. TODO that place that had the inner circle/outer circle ratio??
  27. ]]
  28. ----- [[ GENERALLY USEFUL FUNCTIONS ]] -----------------------------------------
  29. -- just incase you don't already have a rounding function.
  30. function round(n)
  31. return n % 1 >= 0.5 and math.ceil(n) or math.floor(n)
  32. end
  33. ---- [[ HEX CONSTANTS ]] -------------------------------------------------------
  34. -- all possible vector directions from a given hex by edge
  35. HEX_DIRECTIONS = {{ 1 , 0},
  36. { 1 , -1},
  37. { 0 , -1},
  38. {-1 , 0},
  39. {-1 , 1},
  40. { 0 , 1}}
  41. -- HEX UTILITY FUNCTIONS -------------------------------------------------------
  42. function hex_round(s, t)
  43. rs = round(s)
  44. rt = round(t)
  45. rz = round(-s - t)
  46. sdelta = math.abs(rs - s)
  47. tdelta = math.abs(rt - t)
  48. zdelta = math.abs(rz + s + t)
  49. if sdelta > tdelta and sdelta > zdelta then
  50. rs = -rt - rz
  51. elseif tdelta > zdelta then
  52. rt = -rs - rz
  53. else
  54. rz = -rs - rt
  55. end
  56. return {rs, rt}
  57. end
  58. ----- [[ LAYOUT, ORIENTATION & COORDINATE CONVERSION ]] -----------------------
  59. -- forward & inverse matrices used for the flat orientation.
  60. FLAT_ORIENTATION = {3.0/2.0, 0.0, 3.0^0.5/2.0, 3.0^0.5,
  61. 2.0/3.0, 0.0, -1.0/3.0 , 3.0^0.5/3.0}
  62. -- forward & inverse matrices used for the pointy orientation.
  63. POINTY_ORIENTATION = {3.0^0.5, 3.0^0.5/2.0, 0.0, 3.0/2.0,
  64. 3.0^0.5/3.0, -1.0/3.0, 0.0, 2.0/3.0}
  65. -- layout.
  66. function layout(size, orientation, origin, width, height, radius)
  67. return {size = size or {11, 11},
  68. orientation = orientation or FLAT_ORIENTATION,
  69. origin = origin or {0, 0},
  70. width = width or 45,
  71. height = height or 31,
  72. radius = radius or width or 6}
  73. end
  74. -- hex to screen
  75. function hex_to_pixel(s, t, layout)
  76. M = layout.orientation
  77. x = (M[1] * s + M[2] * t) * layout.size[1]
  78. y = (M[3] * s + M[4] * t) * layout.size[2]
  79. return {x + layout.origin[1], y + layout.origin[2]}
  80. end
  81. -- screen to hex
  82. function pixel_to_hex(x, y, layout)
  83. M = layout.orientation
  84. px = {(x - layout.origin[1]) / layout.size[1],
  85. (y - layout.origin[2]) / layout.size[2]}
  86. s = M[5] * px[1] + M[6] * px[2]
  87. t = M[7] * px[1] + M[8] * px[2]
  88. return hex_round(s, t)
  89. end
  90. ----- [[ MAP STORAGE & RETRIEVAL ]] --------------------------------------------
  91. --[[ all functions return a table of tables; a map of points
  92. storage functions take a range of hex coordinates, and return pixel ones.
  93. retrieval functions do the opposite.
  94. everything except map shape is determined by layout.
  95. pick a pair of functions based on the shape of map you want to use.
  96. it is not advised to use a single layout instance with multiple shapes. ]]
  97. -- returns parallelogram-shaped map. width and height are used.
  98. function ogram_map_store(layout)
  99. map = {}
  100. for s = 0, layout.width do
  101. for t = 0, layout.height do
  102. table.insert(map, hex_to_pixel(s, t, layout))
  103. end
  104. end
  105. return map
  106. end
  107. -- returns triangular map. radius is used as the triangle side length.
  108. function tri_map_store(layout)
  109. map = {}
  110. for s = 0, layout.radius do
  111. for t = layout.radius - s, layout.radius do
  112. table.insert(map, hex_to_pixel(s, t, layout))
  113. end
  114. end
  115. return map
  116. end
  117. -- returns hexagonal map. length of map is radius * 2 + 1
  118. function hex_map_store(layout)
  119. map = {}
  120. for s = -layout.radius, layout.radius do
  121. t1 = math.max(-layout.radius, -s - layout.radius)
  122. t2 = math.min(layout.radius, -s + layout.radius)
  123. for t = t1, t2 do
  124. table.insert(map, hex_to_pixel(s, t, layout))
  125. end
  126. end
  127. return map
  128. end
  129. -- returns rectangular map. width and height are used.
  130. function rect_map_store(layout)
  131. map = {}
  132. for s = 0, layout.width do
  133. soffset = math.floor(s / 2)
  134. for t = -soffset, layout.height - soffset do
  135. table.insert(map, hex_to_pixel(s, t, layout))
  136. end
  137. end
  138. return map
  139. end