diff --git a/main.lua b/main.lua index e1039d7..1705bbc 100644 --- a/main.lua +++ b/main.lua @@ -15,6 +15,7 @@ do title = title, highdpi = true, letterbox = true, + --mode = "fullscreen", --projection = projection } @@ -32,17 +33,14 @@ require "src/geometry" require "src/hexyz" require "src/game" require "src/grid" -require "src/gui" require "src/mob" require "src/projectile" require "src/tower" - -- global audio settings MUSIC_VOLUME = 0.1 SFX_VOLUME = 0.1 - function main_action() end function main_scene() end diff --git a/res/thing.png b/res/thing.png index 1f33b03..b358fee 100644 Binary files a/res/thing.png and b/res/thing.png differ diff --git a/src/game.lua b/src/game.lua index 8340c2b..21fa8da 100644 --- a/src/game.lua +++ b/src/game.lua @@ -38,7 +38,7 @@ local function get_initial_game_state(seed) money = STARTING_MONEY, -- current money current_wave = 1, - time_until_next_wave = 15, + time_until_next_wave = 0, time_until_next_break = 0, spawning = false, spawn_chance = 55, @@ -142,7 +142,7 @@ local function game_action(scene) state.time = state.time + am.delta_time state.score = state.score + am.delta_time - --state.spawn_chance = math.clamp(state.spawn_chance - math.floor(state.time / 100), 1, 25) + state.spawn_chance = math.clamp(state.spawn_chance - math.floor(state.time / 100), 1, 25) if state.spawning then state.time_until_next_break = state.time_until_next_break - am.delta_time @@ -187,15 +187,15 @@ local function game_action(scene) local node = WIN.scene("cursor"):child(2) node.color = COLORS.CLARET node:action(am.tween(0.1, { color = COLORS.TRANSPARENT })) - alert("breaks flow field") play_sfx(SOUNDS.BIRD2) + alert("breaks flow field") elseif cost > state.money then local node = WIN.scene("cursor"):child(2) node.color = COLORS.CLARET node:action(am.tween(0.1, { color = COLORS.TRANSPARENT })) - alert("not enough $$$$") play_sfx(SOUNDS.BIRD2) + alert("not enough $$$$") else update_money(-cost) @@ -232,15 +232,15 @@ local function game_action(scene) else select_toolbelt_button((state.selected_toolbelt_button) % table.count(TOWER_TYPE) + 1) end - elseif WIN:key_pressed"1" then select_toolbelt_button(1) - elseif WIN:key_pressed"2" then select_toolbelt_button(2) - elseif WIN:key_pressed"3" then select_toolbelt_button(3) - elseif WIN:key_pressed"4" then select_toolbelt_button(4) - elseif WIN:key_pressed"q" then select_toolbelt_button(5) - elseif WIN:key_pressed"w" then select_toolbelt_button(6) - elseif WIN:key_pressed"e" then select_toolbelt_button(7) - elseif WIN:key_pressed"r" then select_toolbelt_button(8) - elseif WIN:key_pressed"a" then select_toolbelt_button(9) + elseif WIN:key_pressed"1" then select_toolbelt_button( 1) + elseif WIN:key_pressed"2" then select_toolbelt_button( 2) + elseif WIN:key_pressed"3" then select_toolbelt_button( 3) + elseif WIN:key_pressed"4" then select_toolbelt_button( 4) + elseif WIN:key_pressed"q" then select_toolbelt_button( 5) + elseif WIN:key_pressed"w" then select_toolbelt_button( 6) + elseif WIN:key_pressed"e" then select_toolbelt_button( 7) + elseif WIN:key_pressed"r" then select_toolbelt_button( 8) + elseif WIN:key_pressed"a" then select_toolbelt_button( 9) elseif WIN:key_pressed"s" then select_toolbelt_button(10) elseif WIN:key_pressed"d" then select_toolbelt_button(11) elseif WIN:key_pressed"f" then select_toolbelt_button(12) @@ -248,7 +248,6 @@ local function game_action(scene) do_entity_updates() do_mob_spawning(state.spawn_chance) - do_gui_updates() do_day_night_cycle() if interactable then @@ -379,7 +378,6 @@ local function make_game_toolbelt() end select_tower_type = function(tower_type) - log(tower_type) state.selected_tower_type = tower_type if get_tower_spec(tower_type) then @@ -475,9 +473,11 @@ end function game_init() state = get_initial_game_state() - build_tower(HEX_GRID_CENTER, TOWER_TYPE.RADAR) - -- @HACK to make the center tile passable even though there's a tower on it - state.map.get(HEX_GRID_CENTER.x, HEX_GRID_CENTER.y).elevation = 0 + local home_tower = build_tower(HEX_GRID_CENTER, TOWER_TYPE.RADAR) + for _,h in pairs(home_tower.hexes) do + -- @HACK to make the center tile(s) passable even though there's a tower on it + state.map.get(h.x, h.y).elevation = 0 + end WIN.scene:remove("game") WIN.scene:append(game_scene()) diff --git a/src/grid.lua b/src/grid.lua index 8ce13a8..ffded8d 100644 --- a/src/grid.lua +++ b/src/grid.lua @@ -130,13 +130,14 @@ function apply_flow_field(map, flow_field, world) end function building_tower_breaks_flow_field(tower_type, hex) - local hexes = spiral_map(hex, get_tower_size(tower_type)) local original_elevations = {} + local all_impassable = true + local hexes = spiral_map(hex, get_tower_size(tower_type)) for _,h in pairs(hexes) do local tile = state.map.get(h.x, h.y) - if not mob_can_pass_through(nil, h, tile) then - return false + if all_impassable and mob_can_pass_through(nil, h) then + all_impassable = false end table.insert(original_elevations, tile.elevation) @@ -145,9 +146,18 @@ function building_tower_breaks_flow_field(tower_type, hex) tile.elevation = 999 end + -- if no mobs can pass over any of the tiles we're building on + -- there is no need to regenerate the flow field, or do anything more + -- (besides return all the tile's elevations back to their original state) + if all_impassable then + for i,h in pairs(hexes) do + state.map.get(h.x, h.y).elevation = original_elevations[i] + end + return false + end + local flow_field = generate_flow_field(state.map, HEX_GRID_CENTER) local result = not hex_map_get(flow_field, 0, 0) - log(result) for i,h in pairs(hexes) do state.map.get(h.x, h.y).elevation = original_elevations[i] @@ -164,7 +174,14 @@ function random_map(seed) -- tone to give the appearance of light or darkness -- @NOTE replace this with a shader program -- interestingly, if it's colored white, it almost gives the impression of a winter biome - local neg_mask = am.rect(0, 0, HEX_GRID_PIXEL_WIDTH, HEX_GRID_PIXEL_HEIGHT, COLORS.TRUE_BLACK):tag"negative_mask" + local neg_mask = am.rect( + 0, + 0, + HEX_GRID_PIXEL_WIDTH, + HEX_GRID_PIXEL_HEIGHT, + COLORS.TRUE_BLACK + ) + :tag"negative_mask" local world = am.group(neg_mask):tag"world" for i,_ in pairs(map) do @@ -209,6 +226,7 @@ function random_map(seed) getmetatable(map).__index.neighbours = function(hex) return table.filter(hex_neighbours(hex), function(_hex) + --local interactable = evenq_is_in_interactable_region(hex_to_evenq(_hex)) local tile = map.get(_hex.x, _hex.y) return tile and tile_is_medium_elevation(tile) end) diff --git a/src/gui.lua b/src/gui.lua deleted file mode 100644 index 14c7eca..0000000 --- a/src/gui.lua +++ /dev/null @@ -1,40 +0,0 @@ - -local hot, active = false, false -local widgets = {} - -function get_widgets() return widgets end - -function register_widget(id, poll) - widgets[id] = { id = id, poll = poll } -end - -function set_hot(id) - if not active then hot = { id = id } end -end - -function register_button_widget(id, rect, onclick) - register_widget(id, function(i) - local click = false - - if active and active.id == id then - if WIN:mouse_released"left" then - if hot and hot.id == id then click = true end - active = false - end - elseif hot and hot.id == id then - if WIN:mouse_pressed"left" then active = { id = id } end - end - - if point_in_rect(WIN:mouse_position(), rect) then set_hot(id) end - - if click then onclick() end - end) -end - -function do_gui_updates() - for i,w in pairs(widgets) do - w.poll(i) - end -end - - diff --git a/src/projectile.lua b/src/projectile.lua index 980127d..2c993a6 100644 --- a/src/projectile.lua +++ b/src/projectile.lua @@ -10,7 +10,7 @@ PROJECTILE_TYPE = { local PROJECTILE_SPECS = { [PROJECTILE_TYPE.SHELL] = { velocity = 13, - damage = 20, + damage = 15, hitbox_radius = 20 }, [PROJECTILE_TYPE.LASER] = { @@ -105,10 +105,10 @@ local function update_projectile_shell(projectile, projectile_index) local tile = state.map.get(projectile.hex.x, projectile.hex.y) if tile and tile.elevation >= projectile.props.z then - do_explode = true + --do_explode = true elseif projectile.props.z <= 0 then - do_explode = true + --do_explode = true end if do_explode then @@ -200,9 +200,12 @@ function get_projectile_update_function(projectile_type) end function make_and_register_projectile(hex, projectile_type, vector) - local projectile = make_basic_entity(hex - , make_projectile_node(projectile_type, vector) - , get_projectile_update_function(projectile_type)) + local projectile = make_basic_entity( + hex, + make_projectile_node(projectile_type, vector), + get_projectile_update_function(projectile_type) + ) + projectile.type = projectile_type projectile.vector = vector diff --git a/src/tower.lua b/src/tower.lua index af6e185..b90f319 100644 --- a/src/tower.lua +++ b/src/tower.lua @@ -30,7 +30,7 @@ TOWER_SPECS = { }, [TOWER_TYPE.HOWITZER] = { name = "Howitzer", - placement_rules_text = "Place on non-Water", + placement_rules_text = "Place on non-Water, non-Mountain or on Walls", short_description = "Fires artillery. Range increases with elevation of terrain underneath.", texture = TEXTURES.TOWER_HOWITZER, icon_texture = TEXTURES.TOWER_HOWITZER_ICON, @@ -73,7 +73,7 @@ TOWER_SPECS = { cost = 20, range = 0, fire_rate = 1, - size = 1, + size = 0, height = 1, }, [TOWER_TYPE.LIGHTHOUSE] = { @@ -133,7 +133,9 @@ local function make_tower_node(tower_type) elseif tower_type == TOWER_TYPE.HOWITZER then return am.group{ pack_texture_into_sprite(TEXTURES.HEX_FLOWER, HEX_FLOWER_DIMENSIONS.x, HEX_FLOWER_DIMENSIONS.y), - am.rotate(0) ^ pack_texture_into_sprite(TEXTURES.CANNON1, 100, 100) + am.rotate(state.time or 0) ^ am.group{ + pack_texture_into_sprite(TEXTURES.CANNON1, 100, 100) + } } elseif tower_type == TOWER_TYPE.LIGHTHOUSE then return am.group{ @@ -286,7 +288,7 @@ function tower_type_is_buildable_on(hex, tile, tower_type) end end end - return not (blocked or has_water) + return not (blocked or has_water or has_mountain) elseif tower_type == TOWER_TYPE.REDEYE then if not mobs_blocking and towers_blocking then @@ -359,6 +361,7 @@ end function update_tower_howitzer(tower, tower_index) if not tower.target_index then + -- we don't have a target for index,mob in pairs(MOBS) do if mob then local d = math.distance(mob.hex, tower.hex) @@ -368,19 +371,23 @@ function update_tower_howitzer(tower, tower_index) end end end + tower.node("rotate").angle = math.wrapf(tower.node("rotate").angle + 0.2 * am.delta_time, math.pi*2) else + -- we should have a target if MOBS[tower.target_index] == false then + -- the target we have was invalidated tower.target_index = false - elseif (state.time - tower.last_shot_time) > tower.fire_rate then + else + -- the target we have is valid local mob = MOBS[tower.target_index] - local theta = math.atan((tower.hex.y - mob.hex.y)/(tower.hex.x - mob.hex.x)) + local vector = math.normalize(mob.position - tower.position) - if (theta - tower.node("rotate").angle) < 0.1 then + if (state.time - tower.last_shot_time) > tower.fire_rate then local projectile = make_and_register_projectile( tower.hex, PROJECTILE_TYPE.SHELL, - math.normalize(mob.position - tower.position) + vector ) -- @HACK, the projectile will explode if it encounters something taller than it, @@ -391,11 +398,13 @@ function update_tower_howitzer(tower, tower_index) tower.last_shot_time = state.time play_sfx(SOUNDS.EXPLOSION2) end - else - tower.node("rotate").angle = tower.node("rotate").angle + state.time * 0.5 + + local theta = math.rad(90) - math.atan((tower.position.y - mob.position.y)/(tower.position.x - mob.position.x)) + local diff = tower.node("rotate").angle - theta + + tower.node("rotate").angle = -theta + math.pi/2 end end - tower.node("rotate").angle = math.wrapf(tower.node("rotate").angle, math.pi*2) end function update_tower_lighthouse(tower, tower_index) @@ -449,7 +458,11 @@ function make_and_register_tower(hex, tower_type) tower.fire_rate = spec.fire_rate tower.last_shot_time = -spec.fire_rate tower.size = spec.size - tower.hexes = spiral_map(tower.hex, tower.size) + if tower.size == 0 then + tower.hexes = { tower.hex } + else + tower.hexes = spiral_map(tower.hex, tower.size) + end tower.height = spec.height for _,h in pairs(tower.hexes) do @@ -466,8 +479,10 @@ function make_and_register_tower(hex, tower_type) end function build_tower(hex, tower_type) - make_and_register_tower(hex, tower_type) + local tower = make_and_register_tower(hex, tower_type) vplay_sfx(SOUNDS.EXPLOSION4) + + return tower end function delete_all_towers()