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.

348 lines
9.9 KiB

4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
3 years ago
4 years ago
4 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
3 years ago
4 years ago
4 years ago
4 years ago
3 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
5 years ago
4 years ago
5 years ago
  1. math.randomseed(os.time())
  2. math.random()
  3. math.random()
  4. math.random()
  5. math.random()
  6. -- @TODO
  7. -- settings menu
  8. -- -- make the volume icon clickable
  9. -- -- music volume slider or number input box
  10. -- -- sfx volume slider or number input box
  11. -- -- allow different resolution options, as long as you are 4:3
  12. -- serialization
  13. -- -- allow saving by name
  14. -- -- allow loading by name
  15. -- -- investigate saving as lua instead, and having as a consequence a less janky map serialization - we don't care about exploitability
  16. -- sound
  17. -- -- fix the non-seamless loop in the soundtrack
  18. -- -- more trax
  19. -- game
  20. -- -- allow selecting of tiles, if tower is selected then allow sell/upgrade
  21. -- -- new game menu allowing set seed
  22. -- -- make art, birds-eye-ify the redeye tower and lighthouse maybe?
  23. -- map editor?
  24. -- -- paint terrain elevation levels
  25. -- -- place tiles of set elevation
  26. -- -- place towers
  27. -- -- move home?
  28. -- aspect ratios seem like a huge mess
  29. -- for now, i think we should enforce 4:3
  30. local RESOLUTION_OPTIONS = {
  31. { width = 1440, height = 1080 },
  32. { width = 1400, height = 1050 }, -- seems like a good default one
  33. { width = 1280, height = 960 },
  34. { width = 1152, height = 864 },
  35. { width = 1024, height = 768 },
  36. { width = 960, height = 720 },
  37. { width = 832, height = 624 },
  38. { width = 800, height = 600 },
  39. }
  40. local DEFAULT_RESOLUTION = RESOLUTION_OPTIONS[2]
  41. settings = am.load_state("settings", "json") or {
  42. fullscreen = false,
  43. window_width = DEFAULT_RESOLUTION.width,
  44. window_height = DEFAULT_RESOLUTION.height,
  45. music_volume = 0.2,
  46. sfx_volume = 0.1,
  47. sound_on = true
  48. }
  49. do
  50. win = am.window{
  51. width = settings.window_width,
  52. height = settings.window_height,
  53. title = "hexyz",
  54. mode = settings.fullscreen and "fullscreen" or "windowed",
  55. resizable = false,
  56. highdpi = true,
  57. letterbox = true,
  58. show_cursor = true,
  59. }
  60. end
  61. -- asset interfaces and/or trivial code
  62. require "conf"
  63. require "color"
  64. require "sound"
  65. require "texture"
  66. require "src/entity"
  67. require "src/extra"
  68. require "src/geometry"
  69. require "src/hexyz"
  70. require "src/game"
  71. require "src/gui"
  72. require "src/grid"
  73. require "src/mob"
  74. require "src/projectile"
  75. require "src/tower"
  76. local sound_toggle_node_tag = "sound-on-off-icon"
  77. local function make_sound_toggle_node(on)
  78. local sprite
  79. if on then
  80. sprite = pack_texture_into_sprite(TEXTURES.SOUND_ON1, 40, 30)
  81. else
  82. sprite = pack_texture_into_sprite(TEXTURES.SOUND_OFF, 40, 30)
  83. end
  84. return (am.translate(win.right - 30, win.top - 60) ^ sprite)
  85. :tag(sound_toggle_node_tag)
  86. :action(function()
  87. end)
  88. end
  89. local cached_music_volume = 0.2
  90. local cached_sfx_volume = 0.1
  91. local function toggle_mute()
  92. settings.sound_on = not settings.sound_on
  93. if settings.sound_on then
  94. settings.music_volume = cached_music_volume
  95. settings.sfx_volume = cached_sfx_volume
  96. else
  97. cached_music_volume = settings.music_volume
  98. cached_sfx_volume = settings.sfx_volume
  99. settings.music_volume = 0
  100. settings.sfx_volume = 0
  101. end
  102. update_music_volume(settings.music_volume)
  103. win.scene:replace(sound_toggle_node_tag, make_sound_toggle_node(settings.sound_on))
  104. end
  105. -- text popup in the middle of the screen that dissapates, call from anywhere
  106. function alert(message, color)
  107. win.scene:append(
  108. am.scale(3) ^ am.text(message, color or COLORS.WHITE)
  109. :action(coroutine.create(function(self)
  110. am.wait(am.tween(self, 1, { color = vec4(0) }, am.ease_out))
  111. win.scene:remove(self)
  112. end))
  113. )
  114. end
  115. function unpause(root_node)
  116. win.scene("game").paused = false
  117. win.scene:remove(root_node)
  118. end
  119. function main_action(self)
  120. if win:key_pressed("escape") then
  121. if game then
  122. unpause(self)
  123. else
  124. --win:close()
  125. end
  126. elseif win:key_pressed("f4") then
  127. win:close()
  128. elseif win:key_pressed("m") then
  129. toggle_mute()
  130. end
  131. if self"hex_backdrop" then
  132. self"hex_backdrop""rotate".angle = math.wrapf(self"hex_backdrop""rotate".angle - 0.005 * am.delta_time, math.pi*2)
  133. end
  134. end
  135. function make_main_scene_toolbelt()
  136. local include_save_option = game
  137. local include_unpause_option = game
  138. local options = {
  139. false,
  140. {
  141. texture = TEXTURES.NEW_GAME_HEX,
  142. action = function()
  143. win.scene:remove"menu"
  144. game_init()
  145. end
  146. },
  147. false,
  148. include_save_option and {
  149. texture = TEXTURES.SAVE_GAME_HEX,
  150. action = function()
  151. game_save()
  152. alert("succesfully saved!")
  153. end
  154. } or false,
  155. false,
  156. {
  157. texture = TEXTURES.LOAD_GAME_HEX,
  158. action = function()
  159. local save = am.load_state("save", "json")
  160. if save then
  161. win.scene:remove("menu")
  162. game_init(save)
  163. else
  164. alert("no saved games")
  165. end
  166. end
  167. },
  168. false and {
  169. texture = TEXTURES.MAP_EDITOR_HEX,
  170. action = function() alert("not yet :)") end
  171. },
  172. include_unpause_option and {
  173. texture = TEXTURES.UNPAUSE_HEX,
  174. action = function() unpause(win.scene("menu")) end
  175. } or false,
  176. false and {
  177. texture = TEXTURES.SETTINGS_HEX,
  178. action = function() alert("not yet :)") end
  179. },
  180. {
  181. texture = TEXTURES.QUIT_HEX,
  182. action = function() win:close() end
  183. },
  184. false
  185. }
  186. -- calculate the dimensions of the whole grid
  187. local spacing = 150
  188. local grid_width = 6
  189. local grid_height = 2
  190. local hhs = hex_horizontal_spacing(spacing)
  191. local hvs = hex_vertical_spacing(spacing)
  192. local grid_pixel_width = grid_width * hhs
  193. local grid_pixel_height = grid_height * hvs
  194. local pixel_offset = vec2(-grid_pixel_width/2, win.bottom + hex_height(spacing)/2 + 20)
  195. local map = hex_rectangular_map(grid_width, grid_height, HEX_ORIENTATION.POINTY)
  196. local group = am.group()
  197. local option_index = 1
  198. for i,_ in pairs(map) do
  199. for j,_ in pairs(map[i]) do
  200. local hex = vec2(i, j)
  201. local position = hex_to_pixel(hex, vec2(spacing), HEX_ORIENTATION.POINTY)
  202. local option = options[option_index]
  203. local texture = option and option.texture or TEXTURES.SHADED_HEX
  204. local color = option and COLORS.TRANSPARENT or vec4(0.3)
  205. local node = am.translate(position)
  206. ^ pack_texture_into_sprite(texture, texture.width, texture.height, color)
  207. hex_map_set(map, i, j, {
  208. node = node,
  209. option = option
  210. })
  211. local tile = hex_map_get(map, i, j)
  212. local selected = false
  213. node:action(function(self)
  214. local mouse = win:mouse_position()
  215. local hex_ = pixel_to_hex(mouse - pixel_offset, vec2(spacing), HEX_ORIENTATION.POINTY)
  216. if tile.option then
  217. if hex == hex_ then
  218. if not selected then
  219. play_sfx(SOUNDS.SELECT1)
  220. end
  221. selected = true
  222. tile.node"sprite".color = vec4(1)
  223. if win:mouse_pressed("left") then
  224. tile.option.action()
  225. end
  226. else
  227. selected = false
  228. tile.node"sprite".color = COLORS.TRANSPARENT
  229. end
  230. end
  231. end)
  232. group:append(node)
  233. option_index = option_index + 1
  234. end
  235. end
  236. return am.translate(pixel_offset) ^ group
  237. end
  238. function main_scene(do_backdrop, do_logo)
  239. local group = am.group()
  240. if do_backdrop then
  241. local map = hex_hexagonal_map(30)
  242. local hex_backdrop = (am.rotate(0) ^ am.group()):tag"hex_backdrop"
  243. for i,_ in pairs(map) do
  244. for j,n in pairs(map[i]) do
  245. local color = map_elevation_color(n)
  246. color = color{a=color.a - 0.1}
  247. local node = am.translate(hex_to_pixel(vec2(i, j), vec2(HEX_SIZE)))
  248. ^ am.circle(vec2(0), HEX_SIZE, vec4(0), 6)
  249. node"circle":action(am.tween(0.6, { color = color }))
  250. hex_backdrop:append(node)
  251. end
  252. end
  253. group:append(hex_backdrop)
  254. else
  255. group:append(
  256. pack_texture_into_sprite(TEXTURES.CURTAIN, win.width, win.height)
  257. )
  258. end
  259. -- version/author info
  260. group:append(
  261. am.translate(win.right - 10, win.bottom + 10)
  262. ^ am.text(string.format("v%s, by %s", version, author), COLORS.WHITE, "right", "bottom")
  263. )
  264. group:append(
  265. make_sound_toggle_node(settings.sound_on)
  266. )
  267. if do_logo then
  268. local position = vec2(0, win.top - 20 - TEXTURES.LOGO.height/2)
  269. local logo =
  270. am.translate(position)
  271. ^ pack_texture_into_sprite(TEXTURES.LOGO, TEXTURES.LOGO.width, TEXTURES.LOGO.height)
  272. local selected = false
  273. logo:action(function(self)
  274. local mouse = win:mouse_position()
  275. if math.distance(mouse, position) < TEXTURES.LOGO.height/2 then
  276. selected = true
  277. self"sprite".color = vec4(1)
  278. if win:mouse_pressed("left") then
  279. vplay_sfx(math.random(1000000000))
  280. end
  281. else
  282. selected = false
  283. self"sprite".color = vec4(0.95)
  284. end
  285. end)
  286. group:append(logo)
  287. end
  288. group:append(make_main_scene_toolbelt())
  289. group:action(main_action)
  290. return group:tag"menu"
  291. end
  292. win.scene = am.group(
  293. main_scene(true, true)
  294. )
  295. play_track(SOUNDS.MAIN_THEME)
  296. noglobals()