From c7c4ff52c2cd4d41a23311a4b9b3258e5ba36e29 Mon Sep 17 00:00:00 2001 From: Nicholas Hayashi Date: Wed, 3 Feb 2021 16:48:20 -0500 Subject: [PATCH] biggggg refactor, tower cursor silhouettes --- main.lua | 244 ++++++++++------------------------------------ src/entity.lua | 6 +- src/game.lua | 257 +++++++++++++++++++++++++++++++++++++++++++++++++ src/gui.lua | 93 ------------------ src/hexyz.lua | 2 +- src/mob.lua | 10 +- src/tower.lua | 45 ++++++--- 7 files changed, 344 insertions(+), 313 deletions(-) diff --git a/main.lua b/main.lua index a1589ab..477f4fe 100644 --- a/main.lua +++ b/main.lua @@ -5,17 +5,20 @@ math.random() math.random() math.random() - --- Globals -WIN = am.window{ - width = 1920, - height = 1080, - title = "hexyz", - highdpi = true, - letterbox = true -} - -STATE = {} -- @TODO move state in here +do + local width, height, title = 1920, 1080, "hexyz" + + WIN = am.window{ + width = width, + height = height, + title = title, + highdpi = true, + letterbox = true, + --projection = projection + } + + OFF_SCREEN = vec2(width * 2, 0) -- arbitrary location garunteed to be offscreen +end -- assets and/or trivial code require "color" @@ -26,6 +29,7 @@ require "src/entity" require "src/extra" require "src/geometry" require "src/hexyz" +require "src/game" require "src/grid" require "src/gui" require "src/mob" @@ -33,212 +37,62 @@ require "src/projectile" require "src/tower" -OFF_SCREEN = vec2(WIN.width * 2) -- arbitrary pixel position that is garunteed to be off screen -PERF_STATS = false -- result of am.perf_stats() -- should be called every frame - -WORLD = false -- root scene node of everything considered to be in the game world - -- aka non gui stuff - -HEX_CURSOR = false - -TIME = 0 -- runtime of the current game in seconds (not whole program runtime) -SCORE = 0 -- score of the player -STARTING_MONEY = 50 -MONEY = STARTING_MONEY -- available resources -MOUSE = false -- position of the mouse at the start of every frame, if an action is tracking it - -- global audio settings MUSIC_VOLUME = 0.1 SFX_VOLUME = 0.1 --- game stuff -SELECTED_TOWER_TYPE = 1 +MODES = { MAIN, GAME } +CURRENT_MODE = MODES.MAIN -- top right display types local TRDTS = { - NOTHING = -1, - CENTERED_EVENQ = 0, - EVENQ = 1, - HEX = 2, - PLATFORM = 3, - PERF = 4, - SEED = 5, - TILE = 6 + NOTHING, + CENTERED_EVENQ, + EVENQ, + HEX, + PLATFORM, + PERF, + SEED, + TILE, } -local TRDT = TRDTS.SEED +local TRDT = 0 -local function select_hex(hex) - local tower = tower_on_hex(hex) - local tile = HEX_MAP.get(hex.x, hex.y) -end +function update_top_right_message(display_type) + local str = "" + if display_type == TRDTS.CENTERED_EVENQ then + str = centered_evenq.x .. "," .. centered_evenq.y .. " (cevenq)" -local function can_do_build(hex, tile, tower_type) - return can_afford_tower(MONEY, tower_type) and tower_is_buildable_on(hex, tile, tower_type) -end + elseif display_type == TRDTS.EVENQ then + str = evenq.x .. "," .. evenq.y .. " (evenq)" -local function game_action(scene) - --if SCORE < 0 then game_end() end - - TIME = TIME + am.delta_time - SCORE = SCORE + am.delta_time - MOUSE = WIN:mouse_position() - PERF_STATS = am.perf_stats() - - local hex = pixel_to_hex(MOUSE - WORLDSPACE_COORDINATE_OFFSET) - local rounded_mouse = hex_to_pixel(hex) + WORLDSPACE_COORDINATE_OFFSET - local evenq = hex_to_evenq(hex) - local centered_evenq = evenq{ y = -evenq.y } - vec2(math.floor(HEX_GRID_WIDTH/2) - , math.floor(HEX_GRID_HEIGHT/2)) - local tile = HEX_MAP.get(hex.x, hex.y) - local hot = evenq_is_interactable(evenq{ y = -evenq.y }) - - do_entity_updates() - do_mob_spawning() - do_gui_updates() - - if WIN:mouse_pressed"left" then - if hot and can_do_build(hex, tile, SELECTED_TOWER_TYPE) then - build_tower(hex, SELECTED_TOWER_TYPE) - end - end + elseif display_type == TRDTS.HEX then + str = hex.x .. "," .. hex.y .. " (hex)" - if WIN:key_pressed"escape" then - WIN.scene"game".paused = true - WIN.scene:append(am.group{ - am.rect(WIN.left, WIN.bottom, WIN.right, WIN.top, COLORS.TRANSPARENT), - am.scale(3) ^ am.text("Paused.\nEscape to Resume", COLORS.BLACK) - }:tag"pause_menu") - WIN.scene:action(function() - if WIN:key_pressed"escape" then - WIN.scene:remove"pause_menu" - WIN.scene"game".paused = false - return true - end - end) - elseif WIN:key_pressed"f1" then - TRDT = (TRDT + 1) % #table.keys(TRDTS) + elseif display_type == TRDTS.PLATFORM then + str = string.format("%s %s lang %s", am.platform, am.version, am.language()) - elseif WIN:key_pressed"f2" then - WORLD"flow_field".hidden = not WORLD"flow_field".hidden - - elseif WIN:key_pressed"tab" then - local num_of_types = #table.keys(TOWER_TYPE) - if WIN:key_down"lshift" then - select_tower_type((SELECTED_TOWER_TYPE + num_of_types - 2) % num_of_types + 1) - else - select_tower_type((SELECTED_TOWER_TYPE) % num_of_types + 1) - end - elseif WIN:key_pressed"1" then select_tower_type(TOWER_TYPE.REDEYE) - elseif WIN:key_pressed"2" then select_tower_type(2) - elseif WIN:key_pressed"3" then select_tower_type(3) - elseif WIN:key_pressed"4" then select_tower_type(4) - elseif WIN:key_pressed"5" then select_tower_type(5) - elseif WIN:key_pressed"6" then select_tower_type(6) - elseif WIN:key_pressed"7" then select_tower_type(7) - elseif WIN:key_pressed"8" then select_tower_type(8) - elseif WIN:key_pressed"9" then select_tower_type(9) - elseif WIN:key_pressed"0" then select_tower_type(10) - elseif WIN:key_pressed"minus" then select_tower_type(11) - elseif WIN:key_pressed"equals" then select_tower_type(12) - end - - if tile and hot then - HEX_CURSOR.center = rounded_mouse - else - HEX_CURSOR.center = OFF_SCREEN - end + elseif display_type == TRDTS.PERF then + str = table.tostring(PERF_STATS) - WIN.scene"score".text = string.format("SCORE: %.2f", SCORE) - WIN.scene"money".text = string.format("MONEY: %d", MONEY) + elseif display_type == TRDTS.SEED then + str = "SEED: " .. HEX_MAP.seed - do - local str = "" - if TRDT == TRDTS.CENTERED_EVENQ then - str = centered_evenq.x .. "," .. centered_evenq.y .. " (cevenq)" - - elseif TRDT == TRDTS.EVENQ then - str = evenq.x .. "," .. evenq.y .. " (evenq)" - - elseif TRDT == TRDTS.HEX then - str = hex.x .. "," .. hex.y .. " (hex)" - - elseif TRDT == TRDTS.PLATFORM then - str = string.format("%s %s lang %s", am.platform, am.version, am.language()) - - elseif TRDT == TRDTS.PERF then - str = table.tostring(PERF_STATS) - - elseif TRDT == TRDTS.SEED then - str = "SEED: " .. HEX_MAP.seed - - elseif TRDT == TRDTS.TILE then - str = table.tostring(HEX_MAP.get(hex.x, hex.y)) - end - WIN.scene"coords".text = str + elseif display_type == TRDTS.TILE then + str = table.tostring(HEX_MAP.get(hex.x, hex.y)) end - - do_day_night_cycle() + return str end -function do_day_night_cycle() - local slow = 100 - local tstep = (math.sin(TIME / 100) + 1) / PERF_STATS.avg_fps - WORLD"negative_mask".color = vec4(tstep){a=1} -end - -function game_end() - -- de-initialize stuff - delete_all_entities() - TIME = 0 - SCORE = 0 - MONEY = STARTING_MONEY - WORLD = false - - WIN.scene = am.group(am.scale(1) ^ game_scene()) -end - -function update_score(diff) SCORE = SCORE + diff end -function update_money(diff) MONEY = MONEY + diff end - -function draw_hex_cursor(map, color) - local group = am.group() - for _,h in pairs(map) do - group:append(am.circle(hex_to_pixel(h), HEX_SIZE, color, 6)) +function main_action() + if WIN:key_pressed"f1" then + TRDT = (TRDT + 1) % #table.keys(TRDTS) end - return group end -function game_scene() - local score = am.translate(WIN.left + 10, WIN.top - 20) ^ am.text("", "left"):tag"score" - local money = am.translate(WIN.left + 10, WIN.top - 40) ^ am.text("", "left"):tag"money" - local coords = am.translate(WIN.right - 10, WIN.top - 20) ^ am.text("", "right", "top"):tag"coords" - HEX_CURSOR = am.circle(OFF_SCREEN, HEX_SIZE, COLORS.TRANSPARENT, 6):tag"hex_cursor" - - local curtain = am.rect(WIN.left, WIN.bottom, WIN.right, WIN.top, COLORS.TRUE_BLACK) - curtain:action(coroutine.create(function() - am.wait(am.tween(curtain, 3, { color = vec4(0) }, am.ease.out(am.ease.hyperbola))) - WIN.scene:remove(curtain) - end)) - - -- 2227 - HEX_MAP, WORLD = random_map() - - local scene = am.group{ - WORLD, - curtain, - HEX_CURSOR, - toolbelt(), - score, - money, - coords, - }:tag"game" - - scene:action(game_action) - --scene:action(am.play(SOUNDS.TRACK1)) - - return scene +function main_scene() + return am.group():action(main_action) end -WIN.scene = am.group(game_scene()) +game_init() noglobals() diff --git a/src/entity.lua b/src/entity.lua index c39f234..adebd59 100644 --- a/src/entity.lua +++ b/src/entity.lua @@ -6,7 +6,7 @@ entity structure: TOB - number - time of birth, const hex - vec2 - current occupied hex, if any position - vec2 - current pixel position of it's translate (forced parent) node - update - function - runs every frame with itself and its index as an argument + update - function - runs every frame with itself and its index in some array as an argument node - node - scene graph node ... - any - a bunch of other shit depending on what entity type it is @@ -15,7 +15,7 @@ entity structure: function make_basic_entity(hex, node, update, position) local entity = {} - entity.TOB = TIME + entity.TOB = state.time -- usually you'll provide a hex and not a position, and the entity will spawn in the center -- of the hex. if you want an entity to exist not at the center of a hex, you can provide a @@ -37,7 +37,7 @@ end function register_entity(t, entity) table.insert(t, entity) - WORLD:append(entity.node) + state.world:append(entity.node) end -- |t| is the source table, probably MOBS, TOWERS, or PROJECTILES diff --git a/src/game.lua b/src/game.lua index 139597f..223414c 100644 --- a/src/game.lua +++ b/src/game.lua @@ -1,2 +1,259 @@ +state = false +local function get_initial_game_state() + local STARTING_MONEY = 50 + return { + world = false, -- the root scene graph node for the game 'world' + map = false, -- map of hex coords map[x][y] to some stuff at that location + + perf = {}, -- result of call to am.perf_stats, called every frame + time = 0, -- time since game started in seconds + score = 0, -- current game score + money = STARTING_MONEY, -- current money + mouse = false, -- mouse position at the start of this frame + + hex = false, -- vec2 coordinates of hexagon under mouse this frame + rounded_mouse = false, -- vec2 of pixel position of center of hexagon under mouse this frame + evenq = false, -- same as state.hex, but in a different coordinate system + centered_evenq = false, -- same as state.evenq, but with the middle of the map being 0,0 + tile = false, -- tile at position of state.hex this frame + hot = false, -- element being interacted with this frame + + selected_tower_type = 1, + } +end + +local function can_do_build(hex, tile, tower_type) + return can_afford_tower(state.money, tower_type) and tower_is_buildable_on(hex, tile, tower_type) +end + +-- initialized later, as part of the init of the toolbelt +function select_tower_type(tower_type) end + +function do_day_night_cycle() + local tstep = (math.sin(state.time / 100) + 1) / state.perf.avg_fps + state.world"negative_mask".color = vec4(tstep){a=1} +end + +local function pause_game() + WIN.scene"game".paused = true + WIN.scene"game":append(am.group{ + am.rect(WIN.left, WIN.bottom, WIN.right, WIN.top, COLORS.TRANSPARENT), + am.scale(3) ^ am.text("Paused.\nEscape to Resume", COLORS.BLACK) + }:tag"pause_menu") + WIN.scene:action(function() + if WIN:key_pressed"escape" then + WIN.scene:remove"pause_menu" + WIN.scene"game".paused = false + return true + end + end) +end + +function game_end() + delete_all_entities() + state = get_initial_game_state() + WIN.scene = main_scene() +end + +function update_score(diff) state.score = state.score + diff end +function update_money(diff) state.money = state.money + diff end + +local function game_action(scene) + if state.score < 0 then game_end() end + + state.perf = am.perf_stats() + state.time = state.time + am.delta_time + state.score = state.score + am.delta_time + state.mouse = WIN:mouse_position() + + state.hex = pixel_to_hex(state.mouse - WORLDSPACE_COORDINATE_OFFSET) + state.rounded_mouse = hex_to_pixel(state.hex) + WORLDSPACE_COORDINATE_OFFSET + state.evenq = hex_to_evenq(state.hex) + state.centered_evenq = state.evenq{ y = -state.evenq.y } - vec2(math.floor(HEX_GRID_WIDTH/2) + , math.floor(HEX_GRID_HEIGHT/2)) + state.tile = state.map.get(state.hex.x, state.hex.y) + state.hot = evenq_is_interactable(state.evenq{ y = -state.evenq.y }) + + if WIN:mouse_pressed"left" then + if state.hot and can_do_build(state.hex, state.tile, state.selected_tower_type) then + build_tower(state.hex, state.selected_tower_type) + end + end + + if WIN:key_pressed"escape" then + pause_game() + + elseif WIN:key_pressed"f2" then + WORLD"flow_field".hidden = not WORLD"flow_field".hidden + + elseif WIN:key_pressed"tab" then + local num_of_types = #table.keys(TOWER_TYPE) + if WIN:key_down"lshift" then + select_tower_type((state.selected_tower_type + num_of_types - 2) % num_of_types + 1) + else + select_tower_type((state.selected_tower_type) % num_of_types + 1) + end + elseif WIN:key_pressed"1" then select_tower_type(1) + elseif WIN:key_pressed"2" then select_tower_type(2) + elseif WIN:key_pressed"3" then select_tower_type(3) + elseif WIN:key_pressed"4" then select_tower_type(4) + elseif WIN:key_pressed"q" then select_tower_type(5) + elseif WIN:key_pressed"w" then select_tower_type(6) -- wall? + elseif WIN:key_pressed"e" then select_tower_type(7) + elseif WIN:key_pressed"r" then select_tower_type(8) + elseif WIN:key_pressed"a" then -- + elseif WIN:key_pressed"s" then -- + elseif WIN:key_pressed"d" then -- + elseif WIN:key_pressed"f" then -- + end + + do_entity_updates() + do_mob_spawning() + do_gui_updates() + do_day_night_cycle() + + WIN.scene"score".text = string.format("SCORE: %.2f", state.score) + WIN.scene"money".text = string.format("MONEY: %d", state.money) + WIN.scene"cursor".position2d = state.rounded_mouse +end + +function get_tower_tooltip_text(tower_type) + return string.format( + "%s\n%s\n%s\ncost: %d" + , get_tower_name(tower_type) + , get_tower_placement_rules_text(tower_type) + , get_tower_short_description(tower_type) + , get_tower_base_cost(tower_type) + ) +end + +local function make_game_toolbelt() + local function toolbelt_button(size, half_size, tower_texture, padding, i, offset, key_name) + local x1 = (size + padding) * i + offset.x + local y1 = offset.y + local x2 = (size + padding) * i + offset.x + size + local y2 = offset.y + size + + register_button_widget("toolbelt_tower_button" .. i, am.rect(x1, y1, x2, y2), function() select_tower_type(i) end) + + return am.translate(vec2(size + padding, 0) * i + offset) + ^ am.group{ + am.translate(0, half_size) + ^ pack_texture_into_sprite(TEXTURES.BUTTON1, size, size), + + am.translate(0, half_size) + ^ pack_texture_into_sprite(tower_texture, size, size), + + am.translate(vec2(half_size)) + ^ am.group{ + pack_texture_into_sprite(TEXTURES.BUTTON1, half_size, half_size), + am.scale(2) + ^ am.text(key_name, COLORS.BLACK) + } + } + end + + local toolbelt_height = hex_height(HEX_SIZE) * 2 + local toolbelt = am.group{ + am.translate(WIN.left + 10, WIN.bottom + toolbelt_height + 20) + ^ am.text(get_tower_tooltip_text(state.selected_tower_type), "left", "bottom"):tag"tower_tooltip", + am.rect(WIN.left, WIN.bottom, WIN.right, WIN.bottom + toolbelt_height, COLORS.TRANSPARENT) + }:tag"toolbelt" + + local padding = 15 + local size = toolbelt_height - padding + local half_size = size/2 + local offset = vec2(WIN.left + padding*3, WIN.bottom + padding/3) + local tab_button = am.translate(vec2(0, half_size) + offset) ^ am.group{ + pack_texture_into_sprite(TEXTURES.WIDER_BUTTON1, 54, 32), + pack_texture_into_sprite(TEXTURES.TAB_ICON, 25, 25) + } + toolbelt:append(tab_button) + local tower_select_square = ( + am.translate(vec2(size + padding, half_size) + offset) + ^ am.rect(-size/2-3, -size/2-3, size/2+3, size/2+3, COLORS.SUNRAY) + ):tag"tower_select_square" + toolbelt:append(tower_select_square) + + -- fill in the other tower options + local tower_type_values = { + TOWER_TYPE.REDEYE, + TOWER_TYPE.LIGHTHOUSE, + TOWER_TYPE.WALL, + TOWER_TYPE.MOAT + } + local keys = { '1', '2', '3', '4', 'q', 'w', 'e', 'r', 'a', 's', 'd', 'f' } + for i = 1, #keys do + toolbelt:append( + toolbelt_button( + size, + half_size, + get_tower_icon_texture(tower_type_values[i]), + padding, + i, + offset, + keys[i] + ) + ) + end + + select_tower_type = function(tower_type) + state.selected_tower_type = tower_type + WIN.scene:replace("cursor", (am.translate(state.rounded_mouse) ^ get_tower_cursor(tower_type)):tag"cursor") + if TOWER_SPECS[state.selected_tower_type] then + WIN.scene"tower_tooltip".text = get_tower_tooltip_text(tower_type) + local new_position = vec2((size + padding) * tower_type, size/2) + offset + WIN.scene"tower_select_square":action(am.tween(0.1, { position2d = new_position })) + + WIN.scene:action(am.play(am.sfxr_synth(SOUNDS.SELECT1), false, 1, SFX_VOLUME)) + end + end + + return toolbelt +end + +-- |color_f| can be a function that takes a hex and returns a color, or just a color +function make_hex_cursor(position, radius, color_f) + local color = type(color_f) == "userdata" and color_f or nil + local map = spiral_map(vec2(0), radius) + local group = am.group() + for _,h in pairs(map) do + group:append(am.circle(hex_to_pixel(h), HEX_SIZE, color or color_f(h), 6)) + end + return (am.translate(position) ^ group):tag"cursor" +end + +function game_scene() + local score = am.translate(WIN.left + 10, WIN.top - 20) ^ am.text("", "left"):tag"score" + local money = am.translate(WIN.left + 10, WIN.top - 40) ^ am.text("", "left"):tag"money" + local coords = am.translate(WIN.right - 10, WIN.top - 20) ^ am.text("", "right", "top"):tag"coords" + + local curtain = am.rect(WIN.left, WIN.bottom, WIN.right, WIN.top, COLORS.TRUE_BLACK) + curtain:action(coroutine.create(function() + am.wait(am.tween(curtain, 3, { color = vec4(0) }, am.ease.out(am.ease.hyperbola))) + WIN.scene:remove(curtain) + end)) + + -- 2227 + state.map, state.world = random_map() + + local scene = am.group{ + state.world, + curtain, + make_hex_cursor(OFF_SCREEN, 0, COLORS.TRANSPARENT), + make_game_toolbelt(), + score, + money + }:tag"game" + + scene:action(game_action) + + return scene +end + +function game_init() + state = get_initial_game_state() + WIN.scene = game_scene() +end diff --git a/src/gui.lua b/src/gui.lua index e623e92..14c7eca 100644 --- a/src/gui.lua +++ b/src/gui.lua @@ -37,97 +37,4 @@ function do_gui_updates() end end -local function get_tower_tooltip_text(tower_type) - return string.format( - "%s\n%s\n%s\ncost: %d" - , get_tower_name(tower_type) - , get_tower_placement_rules_text(tower_type) - , get_tower_short_description(tower_type) - , get_tower_base_cost(tower_type) - ) -end - -function toolbelt() - local function button(size, half_size, tower_texture, padding, i, offset, key_name) - local x1 = (size + padding) * i + offset.x - local y1 = offset.y - local x2 = (size + padding) * i + offset.x + size - local y2 = offset.y + size - - register_button_widget("toolbelt_tower_button" .. i, am.rect(x1, y1, x2, y2), function() select_tower_type(i) end) - - return am.translate(vec2(size + padding, 0) * i + offset) - ^ am.group{ - am.translate(0, half_size) - ^ pack_texture_into_sprite(TEXTURES.BUTTON1, size, size), - - am.translate(0, half_size) - ^ pack_texture_into_sprite(tower_texture, size, size), - - am.translate(vec2(half_size)) - ^ am.group{ - pack_texture_into_sprite(TEXTURES.BUTTON1, half_size, half_size), - am.scale(2) - ^ am.text(key_name, COLORS.BLACK) - } - } - end - - -- init the toolbelt - local toolbelt_height = hex_height(HEX_SIZE) * 2 - local toolbelt = am.group{ - am.translate(WIN.left + 10, WIN.bottom + toolbelt_height + 20) - ^ am.text(get_tower_tooltip_text(SELECTED_TOWER_TYPE), "left", "bottom"):tag"tower_tooltip", - am.rect(WIN.left, WIN.bottom, WIN.right, WIN.bottom + toolbelt_height, COLORS.TRANSPARENT) - }:tag"toolbelt" - local padding = 15 - local size = toolbelt_height - padding - local half_size = size/2 - local offset = vec2(WIN.left + padding*3, WIN.bottom + padding/3) - local tab_button = am.translate(vec2(0, half_size) + offset) ^ am.group{ - pack_texture_into_sprite(TEXTURES.WIDER_BUTTON1, 54, 32), - pack_texture_into_sprite(TEXTURES.TAB_ICON, 25, 25) - } - toolbelt:append(tab_button) - local tower_select_square = ( - am.translate(vec2(size + padding, half_size) + offset) - ^ am.rect(-size/2-3, -size/2-3, size/2+3, size/2+3, COLORS.SUNRAY) - ):tag"tower_select_square" - toolbelt:append(tower_select_square) - - -- fill in the other tower options - local tower_type_values = { - TOWER_TYPE.REDEYE, - TOWER_TYPE.LIGHTHOUSE, - TOWER_TYPE.WALL, - TOWER_TYPE.MOAT - } - local keys = { '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=' } - for i = 1, #keys do - toolbelt:append( - button( - size, - half_size, - get_tower_icon_texture(tower_type_values[i]), - padding, - i, - offset, - keys[i] - ) - ) - end - - select_tower_type = function(tower_type) - SELECTED_TOWER_TYPE = tower_type - if TOWER_SPECS[SELECTED_TOWER_TYPE] then - WIN.scene"tower_tooltip".text = get_tower_tooltip_text(tower_type) - local new_position = vec2((size + padding) * tower_type, size/2) + offset - WIN.scene"tower_select_square":action(am.tween(0.1, { position2d = new_position })) - - WIN.scene:action(am.play(am.sfxr_synth(SOUNDS.SELECT1), false, 1, SFX_VOLUME)) - end - end - - return toolbelt -end diff --git a/src/hexyz.lua b/src/hexyz.lua index f89ddfe..776d62b 100644 --- a/src/hexyz.lua +++ b/src/hexyz.lua @@ -236,7 +236,7 @@ end -- Returns Ordered Spiral Hexagonal Map of |radius| Rings from |center| function spiral_map(center, radius) - local map = {center} + local map = { center } for i = 1, radius do table.append(map, ring_map(center, i)) diff --git a/src/mob.lua b/src/mob.lua index 013f423..a074b64 100644 --- a/src/mob.lua +++ b/src/mob.lua @@ -108,16 +108,16 @@ local function update_mob(mob, mob_index) end else -- use the map's flow field - gotta find the the best neighbour - local neighbours = HEX_MAP.neighbours(mob.hex) + local neighbours = state.map.neighbours(mob.hex) if #neighbours > 0 then local first_neighbour = neighbours[1] - tile = HEX_MAP.get(first_neighbour.x, first_neighbour.y) + tile = state.map.get(first_neighbour.x, first_neighbour.y) local lowest_cost_hex = first_neighbour local lowest_cost = tile.priority or 0 for _,n in pairs(neighbours) do - tile = HEX_MAP.get(n.x, n.y) + tile = state.map.get(n.x, n.y) local current_cost = tile.priority if current_cost and current_cost < lowest_cost then @@ -141,7 +141,7 @@ local function update_mob(mob, mob_index) if mob.frame_target then -- this is supposed to achieve frame rate independence, but i have no idea if it actually does -- the constant multiplier at the beginning is how many pixels we want a mob with speed 1 to move in one frame - local rate = 4 * mob.speed / PERF_STATS.avg_fps + local rate = 4 * mob.speed / state.perf.avg_fps mob.position = mob.position + math.normalize(hex_to_pixel(mob.frame_target) - mob.position) * rate mob.node.position2d = mob.position @@ -160,7 +160,7 @@ end local function make_and_register_mob(mob_type) local mob = make_basic_entity( get_spawn_hex(), - am.rotate(TIME) ^ pack_texture_into_sprite(TEXTURES.MOB_BEEPER, MOB_SIZE, MOB_SIZE), + am.rotate(state.time) ^ pack_texture_into_sprite(TEXTURES.MOB_BEEPER, MOB_SIZE, MOB_SIZE), update_mob ) diff --git a/src/tower.lua b/src/tower.lua index 4226915..5165217 100644 --- a/src/tower.lua +++ b/src/tower.lua @@ -63,22 +63,20 @@ function get_tower_base_cost(tower_type) return TOWER_SPECS[tower_type] and TOWER_SPECS[tower_type].base_cost end -function can_afford_tower(money, tower_type) - local cost = get_tower_base_cost(tower_type) - return (money - cost) >= 0 +local function make_tower_sprite(tower_type) + return pack_texture_into_sprite(get_tower_texture(tower_type), HEX_PIXEL_WIDTH, HEX_PIXEL_HEIGHT) end -local function get_tower_update_function(tower_type) - if tower_type == TOWER_TYPE.REDEYE then - return update_tower_redeye - - elseif tower_type == TOWER_TYPE.LIGHTHOUSE then - return update_tower_lighthouse +do + local tower_cursors = {} + for _,i in pairs(TOWER_TYPE) do + tower_cursors[i] = make_tower_sprite(i) + tower_cursors[i].color = COLORS.TRANSPARENT end -end -local function make_tower_sprite(tower_type) - return pack_texture_into_sprite(get_tower_texture(tower_type), HEX_PIXEL_WIDTH, HEX_PIXEL_HEIGHT) + function get_tower_cursor(tower_type) + return tower_cursors[tower_type] + end end local function make_tower_node(tower_type) @@ -98,6 +96,21 @@ local function make_tower_node(tower_type) end end +function can_afford_tower(money, tower_type) + local cost = get_tower_base_cost(tower_type) + return (money - cost) >= 0 +end + +local function get_tower_update_function(tower_type) + if tower_type == TOWER_TYPE.REDEYE then + return update_tower_redeye + + elseif tower_type == TOWER_TYPE.LIGHTHOUSE then + return update_tower_lighthouse + end +end + + function towers_on_hex(hex) local t = {} for tower_index,tower in pairs(TOWERS) do @@ -234,7 +247,7 @@ function make_and_register_tower(hex, tower_type) tower.last_shot_time = tower.TOB tower.target_index = false - HEX_MAP[hex.x][hex.y].elevation = 2 + state.map[hex.x][hex.y].elevation = 2 elseif tower_type == TOWER_TYPE.LIGHTHOUSE then tower.range = 5 @@ -268,14 +281,14 @@ function make_and_register_tower(hex, tower_type) need_to_regen_flow_field = false elseif tower_type == TOWER_TYPE.WALL then - HEX_MAP[hex.x][hex.y].elevation = 1 + state.map[hex.x][hex.y].elevation = 1 elseif tower_type == TOWER_TYPE.MOAT then - HEX_MAP[hex.x][hex.y].elevation = -1 + state.map[hex.x][hex.y].elevation = -1 end if need_to_regen_flow_field then - generate_and_apply_flow_field(HEX_MAP, HEX_GRID_CENTER, WORLD) + generate_and_apply_flow_field(state.map, HEX_GRID_CENTER, state.world) end register_entity(TOWERS, tower)