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.

317 lines
9.3 KiB

4 years ago
4 years ago
4 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 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
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
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
4 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
4 years ago
5 years ago
3 years ago
3 years ago
4 years ago
5 years ago
  1. -- all 4:3 aspect ratio
  2. local RESOLUTION_OPTIONS = {
  3. { width = 1440, height = 1080 },
  4. { width = 1400, height = 1050 }, -- seems like a good default one
  5. { width = 1280, height = 960 },
  6. { width = 1152, height = 864 },
  7. { width = 1024, height = 768 },
  8. { width = 960, height = 720 },
  9. { width = 832, height = 624 },
  10. { width = 800, height = 600 },
  11. }
  12. local DEFAULT_RESOLUTION = RESOLUTION_OPTIONS[2]
  13. SETTINGS = am.load_state("settings", "json") or {
  14. fullscreen = false,
  15. window_width = DEFAULT_RESOLUTION.width,
  16. window_height = DEFAULT_RESOLUTION.height,
  17. music_volume = 0.2,
  18. sfx_volume = 0.1,
  19. sound_on = true
  20. }
  21. win = am.window{
  22. width = SETTINGS.window_width,
  23. height = SETTINGS.window_height,
  24. title = "hexyz",
  25. mode = SETTINGS.fullscreen and "fullscreen" or "windowed",
  26. resizable = true, -- as long as the aspect ratio is maintained via letterboxing
  27. highdpi = true,
  28. letterbox = true,
  29. show_cursor = true,
  30. clear_color = vec4(0),
  31. }
  32. -- top right display types
  33. -- different scenes overlay different content in the top right of the screen
  34. -- f1 toggles what is displayed in the top right of the screen in some scenes
  35. TRDTS = {
  36. NOTHING = 0,
  37. CENTERED_EVENQ = 1,
  38. EVENQ = 2,
  39. HEX = 3,
  40. PLATFORM = 4,
  41. PERF = 5,
  42. SEED = 6,
  43. TILE = 7,
  44. MOUSE = 8
  45. }
  46. function make_top_right_display_node()
  47. return am.translate(win.right - 10, win.top - 15)
  48. ^ am.text("", "right", "top"):tag"top_right_display"
  49. end
  50. require "conf"
  51. -- library/standard code (ours)
  52. require "lib/random"
  53. require "lib/extra"
  54. require "lib/memory"
  55. require "lib/geometry"
  56. require "lib/gui"
  57. require "lib/color"
  58. require "lib/sound"
  59. require "lib/texture"
  60. -- other internal dependencies
  61. require "src/hexyz"
  62. require "src/grid"
  63. require "src/game"
  64. require "src/tower"
  65. require "src/mob"
  66. require "src/map-editor"
  67. require "src/entity"
  68. require "src/projectile"
  69. function main_action(self)
  70. if win:key_pressed("escape") then
  71. if game then
  72. unpause(self)
  73. else
  74. --win:close()
  75. end
  76. elseif win:key_pressed("f4") then
  77. win:close()
  78. end
  79. if self"hex_backdrop" then
  80. self"hex_backdrop""rotate".angle = math.wrapf(self"hex_backdrop""rotate".angle - 0.005 * am.delta_time, math.pi*2)
  81. end
  82. end
  83. function main_scene(do_backdrop, do_logo)
  84. local group = am.group()
  85. if do_backdrop then
  86. local map = hex_hexagonal_map(30)
  87. local hex_backdrop = (am.rotate(0) ^ am.group()):tag"hex_backdrop"
  88. for i,_ in pairs(map) do
  89. for j,n in pairs(map[i]) do
  90. local color = map_elevation_to_color(n)
  91. color = color{a=color.a - 0.1}
  92. local node = am.translate(hex_to_pixel(vec2(i, j), vec2(HEX_SIZE)))
  93. ^ am.circle(vec2(0), HEX_SIZE, vec4(0), 6)
  94. node"circle":action(am.tween(0.6, { color = color }))
  95. hex_backdrop:append(node)
  96. end
  97. end
  98. group:append(hex_backdrop)
  99. else
  100. group:append(
  101. pack_texture_into_sprite(TEXTURES.CURTAIN, win.width, win.height)
  102. )
  103. end
  104. -- version/author info
  105. group:append(
  106. am.translate(win.right - 10, win.bottom + 10)
  107. ^ am.text(string.format("v%s, by %s", version, author), COLORS.WHITE, "right", "bottom")
  108. )
  109. if do_logo then
  110. local position = vec2(0, win.top - 20 - TEXTURES.LOGO.height/2)
  111. local logo =
  112. am.translate(position)
  113. ^ pack_texture_into_sprite(TEXTURES.LOGO)
  114. local selected = false
  115. logo:action(function(self)
  116. local mouse = win:mouse_position()
  117. if math.distance(mouse, position) < TEXTURES.LOGO.height/2 then
  118. selected = true
  119. self"sprite".color = vec4(1)
  120. if win:mouse_pressed("left") then
  121. vplay_sfx(math.floor(math.random() * 1000000000))
  122. end
  123. else
  124. selected = false
  125. self"sprite".color = vec4(0.95)
  126. end
  127. end)
  128. group:append(logo)
  129. end
  130. local max
  131. if not math.log10 then
  132. max = math.ceil(math.log(HEX_GRID_WIDTH * HEX_GRID_HEIGHT, 10))
  133. else
  134. max = math.ceil(math.log10(HEX_GRID_WIDTH * HEX_GRID_HEIGHT))
  135. end
  136. local seed_textfield, get_seed_textfield_value = gui_make_textfield{
  137. position = vec2(win.left + 190, 50),
  138. dimensions = vec2(90, 40),
  139. max = max,
  140. validate = function(string)
  141. return not string.match(string, "%D")
  142. end,
  143. }
  144. group:append(
  145. seed_textfield
  146. )
  147. group:append(
  148. am.translate(win.left + 80, 50) ^ pack_texture_into_sprite(TEXTURES.SEED_COLON_TEXT)
  149. )
  150. local main_scene_options = {
  151. false,
  152. {
  153. texture = TEXTURES.NEW_GAME_HEX,
  154. action = function()
  155. game_init(nil, tonumber(get_seed_textfield_value()))
  156. end
  157. },
  158. false,
  159. false,
  160. false,
  161. {
  162. texture = TEXTURES.LOAD_GAME_HEX,
  163. action = function()
  164. local save = am.load_state("save", "json")
  165. if save then
  166. game_init(save)
  167. else
  168. gui_alert("no saved games")
  169. end
  170. end
  171. },
  172. {
  173. texture = TEXTURES.MAP_EDITOR_HEX,
  174. action = function()
  175. map_editor_init()
  176. end
  177. },
  178. false,
  179. {
  180. texture = TEXTURES.SETTINGS_HEX,
  181. action = function()
  182. gui_alert("not yet :)")
  183. end
  184. },
  185. {
  186. texture = TEXTURES.QUIT_HEX,
  187. action = function()
  188. win:close()
  189. end
  190. },
  191. false
  192. }
  193. group:append(make_scene_menu(main_scene_options, "main_menu"))
  194. group:action(main_action)
  195. return group
  196. end
  197. function make_scene_menu(scene_options, tag, do_curtain)
  198. -- calculate the dimensions of the whole grid
  199. local spacing = 150
  200. local grid_width = 6
  201. local grid_height = 2
  202. local hhs = hex_horizontal_spacing(spacing)
  203. local hvs = hex_vertical_spacing(spacing)
  204. local grid_pixel_width = grid_width * hhs
  205. local grid_pixel_height = grid_height * hvs
  206. local pixel_offset = vec2(-grid_pixel_width/2, win.bottom + hex_height(spacing)/2 + 20)
  207. -- generate a map of hexagons (the menu is made up of two rows of hexes) and populate their locations with buttons from the provided options
  208. local map = hex_rectangular_map(grid_width, grid_height, HEX_ORIENTATION.POINTY)
  209. local group = am.group():tag(tag or "menu")
  210. if do_curtain then
  211. group:append(pack_texture_into_sprite(TEXTURES.CURTAIN, win.width, win.height))
  212. end
  213. local menu = am.group()
  214. local option_index = 1
  215. for i,_ in pairs(map) do
  216. for j,_ in pairs(map[i]) do
  217. local hex = vec2(i, j)
  218. local position = hex_to_pixel(hex, vec2(spacing), HEX_ORIENTATION.POINTY)
  219. local option = scene_options[option_index]
  220. local texture = option and option.texture or TEXTURES.SHADED_HEX
  221. local color = COLORS.TRANSPARENT3
  222. local node = am.translate(position)
  223. ^ pack_texture_into_sprite(texture, texture.width, texture.height, color)
  224. hex_map_set(map, i, j, {
  225. node = node,
  226. option = option
  227. })
  228. local tile = hex_map_get(map, i, j)
  229. local selected = false
  230. node:action(function(self)
  231. local mouse = win:mouse_position()
  232. local hex_ = pixel_to_hex(mouse - pixel_offset, vec2(spacing), HEX_ORIENTATION.POINTY)
  233. if tile.option then
  234. if tile.option.keys and tile.option.action then
  235. for _,key in pairs(win:keys_pressed()) do
  236. if table.find(tile.option.keys, function(_key) return _key == key end) then
  237. tile.option.action()
  238. end
  239. end
  240. end
  241. if hex == hex_ then
  242. if not selected then
  243. play_sfx(SOUNDS.SELECT1)
  244. end
  245. selected = true
  246. tile.node"sprite".color = vec4(1)
  247. if win:mouse_pressed("left") then
  248. tile.option.action()
  249. end
  250. else
  251. selected = false
  252. tile.node"sprite".color = COLORS.TRANSPARENT3
  253. end
  254. end
  255. end)
  256. menu:append(node)
  257. option_index = option_index + 1
  258. end
  259. end
  260. group:append(am.translate(pixel_offset) ^ menu)
  261. return group
  262. end
  263. function switch_context(scene, action)
  264. win.scene:remove("menu")
  265. if action then
  266. win.scene:replace("context", scene:action(action):tag"context")
  267. else
  268. win.scene:replace("context", scene:tag"context")
  269. end
  270. end
  271. function init()
  272. init_entity_specs()
  273. switch_context(main_scene(true, true))
  274. end
  275. win.scene = am.group(am.group():tag"context")
  276. init()
  277. noglobals()