From 1eca01d9e8264e64732cf8aa6494dedd81540767 Mon Sep 17 00:00:00 2001 From: Nicholas Hayashi Date: Sat, 6 Feb 2021 15:12:41 -0500 Subject: [PATCH] dont show tower cursor if you cant build it --- sound.lua | 1 + src/game.lua | 167 +++++++++++++++++++++++++++----------------------- src/grid.lua | 17 ++--- src/tower.lua | 11 ++-- 4 files changed, 103 insertions(+), 93 deletions(-) diff --git a/sound.lua b/sound.lua index 7cbba6a..42d2a9e 100644 --- a/sound.lua +++ b/sound.lua @@ -13,6 +13,7 @@ SOUNDS = { SELECT1 = 76036806, PUSH2 = 57563308, BIRD1 = 50838307, + BIRD2 = 16549407, RANDOM1 = 85363309, RANDOM2 = 15482409, RANDOM3 = 58658009, diff --git a/src/game.lua b/src/game.lua index ab809a9..7cb8583 100644 --- a/src/game.lua +++ b/src/game.lua @@ -2,6 +2,14 @@ state = {} +function game_end() + delete_all_entities() + game_init() +end + +function update_score(diff) state.score = state.score + diff end +function update_money(diff) state.money = state.money + diff end + -- top right display types local TRDTS = { NOTHING = 0, @@ -26,31 +34,23 @@ local function get_initial_game_state(seed) 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 = false, selected_toolbelt_button = 9, - top_right_display_type = TRDTS.SEED, + selected_top_right_display_type = TRDTS.SEED, } end -local function get_top_right_display_text(display_type) +local function get_top_right_display_text(hex, evenq, centered_evenq, display_type) local str = "" if display_type == TRDTS.CENTERED_EVENQ then - str = state.centered_evenq.x .. "," .. state.centered_evenq.y .. " (cevenq)" + str = centered_evenq.x .. "," .. centered_evenq.y .. " (cevenq)" elseif display_type == TRDTS.EVENQ then - str = state.evenq.x .. "," .. state.evenq.y .. " (evenq)" + str = evenq.x .. "," .. evenq.y .. " (evenq)" elseif display_type == TRDTS.HEX then - str = state.hex.x .. "," .. state.hex.y .. " (hex)" + str = hex.x .. "," .. hex.y .. " (hex)" elseif display_type == TRDTS.PLATFORM then str = string.format("%s %s lang %s", am.platform, am.version, am.language()) @@ -62,29 +62,20 @@ local function get_top_right_display_text(display_type) str = "SEED: " .. state.map.seed elseif display_type == TRDTS.TILE then - str = table.tostring(state.map.get(state.hex.x, state.hex.y)) + str = table.tostring(state.map.get(hex.x, hex.y)) end return str end -local function can_do_build(hex, tile, tower_type) - if making_hex_unwalkable_breaks_flow_field(hex, tile) then - return false +local function handle_left_click(hex, tile, tower_type, toolbelt_button) + if play_error_sound then + play_sfx(SOUNDS.BIRD2) end - - if not can_afford_tower(state.money, tower_type) then - return false - end - - if not tower_is_buildable_on(hex, tile, tower_type) then + if flash_tower_cursor then local node = WIN.scene("cursor"):child(1):child(2) node.color = COLORS.CLARET node:action(am.tween(0.1, { color = COLORS.TRANSPARENT })) - - return false end - - return true end -- initialized later, as part of the init of the toolbelt @@ -92,7 +83,7 @@ function select_tower_type(tower_type) end function select_toolbelt_button(i) state.selected_toolbelt_button = i - if i < 9 then + if i <= 10 then select_tower_type(i) else select_tower_type(nil) @@ -124,34 +115,31 @@ local function game_pause() end) end -function game_end() - delete_all_entities() - game_init() -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() return true 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 }) + local mouse = WIN:mouse_position() + 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 = state.map.get(hex.x, hex.y) + + local interactable = evenq_is_in_interactable_region(evenq{ y = -evenq.y }) + local buildable = tower_type_is_buildable_on(hex, tile, state.selected_tower_type) + local firable = false if WIN:mouse_pressed"left" then - if state.hot and state.selected_tower_type and can_do_build(state.hex, state.tile, state.selected_tower_type) then - update_money(-get_tower_cost(state.selected_tower_type)) - build_tower(state.hex, state.selected_tower_type) + if interactable then + if buildable then + update_money(-get_tower_cost(state.selected_tower_type)) + build_tower(hex, state.selected_tower_type) + end end end @@ -189,14 +177,19 @@ local function game_action(scene) do_gui_updates() do_day_night_cycle() - WIN.scene"top_right_display".text = get_top_right_display_text(state.top_right_display_type) - WIN.scene"score".text = string.format("SCORE: %.2f", state.score) - WIN.scene"money".text = string.format("MONEY: %d", state.money) - if state.hot then - WIN.scene"cursor".hidden = false - WIN.scene"cursor".position2d = state.rounded_mouse + WIN.scene("top_right_display").text = get_top_right_display_text(hex, evenq, centered_evenq, state.selected_top_right_display_type) + WIN.scene("score").text = string.format("SCORE: %.2f", state.score) + WIN.scene("money").text = string.format("MONEY: %d", state.money) + if interactable then + WIN.scene("cursor").hidden = false + + if buildable then + WIN.scene("cursor_translate").position2d = rounded_mouse + else + WIN.scene("cursor").hidden = true + end else - WIN.scene"cursor".hidden = true + WIN.scene("cursor").hidden = true end end @@ -207,7 +200,9 @@ local function make_game_toolbelt() 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) + 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{ @@ -231,19 +226,29 @@ local function make_game_toolbelt() local name = get_tower_name(tower_type) local placement_rules = get_tower_placement_rules_text(tower_type) local short_desc = get_tower_short_description(tower_type) - local base_cost = get_tower_cost(tower_type) + local cost = get_tower_cost(tower_type) - if not (name or placement_rules or short_desc or base_cost) then + if not (name or placement_rules or short_desc or cost) then return am.group():tag"tower_tooltip_text" end - local white = COLORS.PALE_SILVER - return (am.translate(WIN.left + 10, WIN.bottom + toolbelt_height + 20) ^ am.group{ - am.translate(0, 60) ^ am.text(name, white, "left"):tag"tower_name", - am.translate(0, 40) ^ am.text(placement_rules, white, "left"):tag"tower_placement_rules", - am.translate(0, 20) ^ am.text(short_desc, white, "left"):tag"tower_short_description", - am.translate(0, 0) ^ am.text(string.format("cost: %d", base_cost), white, "left"):tag"tower_base_cost" - }):tag"tower_tooltip_text" + local color = COLORS.PALE_SILVER + return (am.translate(WIN.left + 10, WIN.bottom + toolbelt_height + 20) + ^ am.group{ + am.translate(0, 60) + ^ am.text(name, color, "left"):tag"tower_name", + + am.translate(0, 40) + ^ am.text(placement_rules, color, "left"):tag"tower_placement_rules", + + am.translate(0, 20) + ^ am.text(short_desc, color, "left"):tag"tower_short_description", + + am.translate(0, 0) + ^ am.text(string.format("cost: %d", cost), color, "left"):tag"tower_cost" + } + ) + :tag"tower_tooltip_text" end local toolbelt = am.group{ am.group():tag"tower_tooltip_text", @@ -298,20 +303,20 @@ local function make_game_toolbelt() WIN.scene:replace("tower_tooltip_text", get_tower_tooltip_text_node(tower_type)) local new_position = vec2((size + padding) * tower_type, size/2) + offset - if WIN.scene"tower_select_square".hidden then - WIN.scene"tower_select_square".position2d = new_position - WIN.scene"tower_select_square".hidden = false + if toolbelt("tower_select_square").hidden then + toolbelt("tower_select_square").position2d = new_position + toolbelt("tower_select_square").hidden = false else - WIN.scene"tower_select_square":action(am.tween(0.1, { position2d = new_position })) + toolbelt("tower_select_square"):action(am.tween(0.1, { position2d = new_position })) end - WIN.scene:replace("cursor", (am.translate(state.rounded_mouse) ^ get_tower_cursor(tower_type)):tag"cursor") + WIN.scene:replace("cursor", get_tower_cursor(tower_type):tag"cursor") WIN.scene:action(am.play(am.sfxr_synth(SOUNDS.SELECT1), false, 1, SFX_VOLUME)) else - WIN.scene"tower_select_square".hidden = true + toolbelt("tower_select_square").hidden = true - WIN.scene:replace("cursor", make_hex_cursor(state.rounded_mouse, 0, COLORS.TRANSPARENT)) + WIN.scene:replace("cursor", make_hex_cursor(0, COLORS.TRANSPARENT)) end end @@ -320,7 +325,7 @@ end -- |color_f| can be a function that takes a hex and returns a color, or just a color -- |action_f| should be an action that operates on the group node or nil -function make_hex_cursor(position, radius, color_f, action_f) +function make_hex_cursor(radius, color_f, action_f) local color = type(color_f) == "userdata" and color_f or nil local map = spiral_map(vec2(0), radius) local group = am.group() @@ -334,23 +339,29 @@ function make_hex_cursor(position, radius, color_f, action_f) group:action(action_f) end - return (am.translate(position) ^ group):tag"cursor" + return 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 top_right_display = am.translate(WIN.right - 10, WIN.top - 20) ^ am.text("", "right", "top"):tag"top_right_display" + 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 top_right_display = am.translate(WIN.right - 10, WIN.top - 20) + ^ am.text("", "right", "top"):tag"top_right_display" 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) + return true end)) local scene = am.group{ state.world, - make_hex_cursor(OFF_SCREEN, 0, COLORS.TRANSPARENT), + am.translate(OFF_SCREEN):tag"cursor_translate" ^ make_hex_cursor(0, COLORS.TRANSPARENT), score, money, top_right_display, @@ -367,7 +378,7 @@ function game_init() state = get_initial_game_state() build_tower(HEX_GRID_CENTER, TOWER_TYPE.RADAR) - WIN.scene:remove"game" + WIN.scene:remove("game") WIN.scene:append(game_scene()) end diff --git a/src/grid.lua b/src/grid.lua index 7573f84..7c9a9f6 100644 --- a/src/grid.lua +++ b/src/grid.lua @@ -38,14 +38,15 @@ end -- transform coordinates by this to pretend 0,0 is elsewhere WORLDSPACE_COORDINATE_OFFSET = -HEX_GRID_PIXEL_DIMENSIONS/2 --- the outer edges of the map are not interactable, most action occurs in the center-ish -HEX_GRID_INTERACTABLE_REGION_PADDING = 4 -function evenq_is_interactable(evenq) +-- the outer edges of the map are not interactable +-- the interactable region is defined with this function and constant +HEX_GRID_INTERACTABLE_REGION_MARGIN = 4 +function evenq_is_in_interactable_region(evenq) return point_in_rect(evenq, { - x1 = HEX_GRID_INTERACTABLE_REGION_PADDING, - x2 = HEX_GRID_WIDTH - HEX_GRID_INTERACTABLE_REGION_PADDING, - y1 = HEX_GRID_INTERACTABLE_REGION_PADDING, - y2 = HEX_GRID_HEIGHT - HEX_GRID_INTERACTABLE_REGION_PADDING + x1 = HEX_GRID_INTERACTABLE_REGION_MARGIN, + x2 = HEX_GRID_WIDTH - HEX_GRID_INTERACTABLE_REGION_MARGIN, + y1 = HEX_GRID_INTERACTABLE_REGION_MARGIN, + y2 = HEX_GRID_HEIGHT - HEX_GRID_INTERACTABLE_REGION_MARGIN }) end @@ -81,7 +82,7 @@ function making_hex_unwalkable_breaks_flow_field(hex, tile) local flow_field = generate_flow_field(state.map, HEX_GRID_CENTER) local result = not hex_map_get(flow_field, 0, 0) tile.elevation = original_elevation - return result + return result, flow_field end function grid_cost(map, from, to) diff --git a/src/tower.lua b/src/tower.lua index 5f4fb56..b21638f 100644 --- a/src/tower.lua +++ b/src/tower.lua @@ -129,7 +129,7 @@ do end) tower_cursors[i] = am.group{ - make_hex_cursor(vec2(0), get_tower_range(i), vec4(0), coroutine_), + make_hex_cursor(get_tower_range(i), vec4(0), coroutine_), tower_sprite } end @@ -179,11 +179,6 @@ local function make_tower_node(tower_type) end end -function can_afford_tower(money, tower_type) - local cost = get_tower_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 @@ -207,7 +202,9 @@ function tower_on_hex(hex) return table.find(TOWERS, function(tower) return tower.hex == hex end) end -function tower_is_buildable_on(hex, tile, tower_type) +function tower_type_is_buildable_on(hex, tile, tower_type) + if not tower_type then return false end + local blocking_towers = towers_on_hex(hex) local blocking_mobs = mobs_on_hex(hex)