|
|
@ -2,24 +2,11 @@ |
|
|
|
game = false -- flag to tell if there is a game running |
|
|
|
game_state = {} |
|
|
|
|
|
|
|
-- top right display types |
|
|
|
-- f1 toggles what is displayed in the top right of the screen |
|
|
|
local TRDTS = { |
|
|
|
NOTHING = 0, |
|
|
|
CENTERED_EVENQ = 1, |
|
|
|
EVENQ = 2, |
|
|
|
HEX = 3, |
|
|
|
PLATFORM = 4, |
|
|
|
PERF = 5, |
|
|
|
SEED = 6, |
|
|
|
TILE = 7, |
|
|
|
} |
|
|
|
|
|
|
|
local function get_initial_game_state(seed) |
|
|
|
local STARTING_MONEY = 75 |
|
|
|
|
|
|
|
local map = random_map(seed) |
|
|
|
local world = make_hex_grid_scene(map) |
|
|
|
local world = make_hex_grid_scene(map, true) |
|
|
|
|
|
|
|
return { |
|
|
|
map = map, -- map of hex coords map[x][y] to a 'tile' |
|
|
@ -47,14 +34,6 @@ local function get_initial_game_state(seed) |
|
|
|
} |
|
|
|
end |
|
|
|
|
|
|
|
local function get_wave_timer_text() |
|
|
|
if game_state.spawning then |
|
|
|
return string.format("WAVE (%d) OVER: %.2f", game_state.current_wave, game_state.time_until_next_break) |
|
|
|
else |
|
|
|
return string.format("NEXT WAVE (%d): %.2f", game_state.current_wave, game_state.time_until_next_wave) |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
local function get_top_right_display_text(hex, evenq, centered_evenq, display_type) |
|
|
|
local str = "" |
|
|
|
if display_type == TRDTS.CENTERED_EVENQ then |
|
|
@ -81,6 +60,14 @@ local function get_top_right_display_text(hex, evenq, centered_evenq, display_ty |
|
|
|
return str |
|
|
|
end |
|
|
|
|
|
|
|
local function get_wave_timer_text() |
|
|
|
if game_state.spawning then |
|
|
|
return string.format("WAVE (%d) OVER: %.2f", game_state.current_wave, game_state.time_until_next_break) |
|
|
|
else |
|
|
|
return string.format("NEXT WAVE (%d): %.2f", game_state.current_wave, game_state.time_until_next_wave) |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
|
-- initialized later, as part of the init of the toolbelt |
|
|
|
local function select_tower_type(tower_type) end |
|
|
|
local function select_toolbelt_button(i) end |
|
|
@ -104,7 +91,64 @@ end |
|
|
|
local function game_pause() |
|
|
|
win.scene("game").paused = true |
|
|
|
|
|
|
|
win.scene:append(main_scene(false, false)) |
|
|
|
local game_scene_options = { |
|
|
|
false, |
|
|
|
{ |
|
|
|
texture = TEXTURES.NEW_GAME_HEX, |
|
|
|
action = function() |
|
|
|
game_init() |
|
|
|
end |
|
|
|
}, |
|
|
|
false, |
|
|
|
{ |
|
|
|
texture = TEXTURES.SAVE_GAME_HEX, |
|
|
|
action = function() |
|
|
|
game_save() |
|
|
|
gui_alert("succesfully saved!") |
|
|
|
end |
|
|
|
}, |
|
|
|
false, |
|
|
|
{ |
|
|
|
texture = TEXTURES.LOAD_GAME_HEX, |
|
|
|
action = function() |
|
|
|
local save = am.load_state("save", "json") |
|
|
|
|
|
|
|
if save then |
|
|
|
game_init(save) |
|
|
|
else |
|
|
|
gui_alert("no saved games") |
|
|
|
end |
|
|
|
end |
|
|
|
}, |
|
|
|
{ |
|
|
|
texture = TEXTURES.MAP_EDITOR_HEX, |
|
|
|
action = function() |
|
|
|
win.scene:remove("game") |
|
|
|
map_editor_init(game_state.map.seed) |
|
|
|
end |
|
|
|
}, |
|
|
|
{ |
|
|
|
texture = TEXTURES.UNPAUSE_HEX, |
|
|
|
action = function() |
|
|
|
|
|
|
|
end |
|
|
|
}, |
|
|
|
{ |
|
|
|
texture = TEXTURES.SETTINGS_HEX, |
|
|
|
action = function() |
|
|
|
gui_alert("not yet :)") |
|
|
|
end |
|
|
|
}, |
|
|
|
{ |
|
|
|
texture = TEXTURES.QUIT_HEX, |
|
|
|
action = function() |
|
|
|
win:close() |
|
|
|
end |
|
|
|
}, |
|
|
|
false |
|
|
|
} |
|
|
|
|
|
|
|
win.scene:append(make_scene_menu(game_scene_options)) |
|
|
|
end |
|
|
|
|
|
|
|
local function game_deserialize(json_string) |
|
|
@ -116,7 +160,7 @@ local function game_deserialize(json_string) |
|
|
|
end |
|
|
|
|
|
|
|
new_game_state.map = random_map(new_game_state.seed) |
|
|
|
new_game_state.world = make_hex_grid_scene(new_game_state.map) |
|
|
|
new_game_state.world = make_hex_grid_scene(new_game_state.map, true) |
|
|
|
new_game_state.seed = nil |
|
|
|
|
|
|
|
for i,t in pairs(new_game_state.towers) do |
|
|
@ -159,7 +203,7 @@ local function game_deserialize(json_string) |
|
|
|
end |
|
|
|
|
|
|
|
local function game_serialize() |
|
|
|
local serialized = table.shallow_copy(state) |
|
|
|
local serialized = table.shallow_copy(game_state) |
|
|
|
serialized.version = version |
|
|
|
|
|
|
|
serialized.seed = game_state.map.seed |
|
|
@ -172,6 +216,7 @@ local function game_serialize() |
|
|
|
-- and the scene graph needs to be re-constituted at load time |
|
|
|
-- |
|
|
|
-- this is dumb and if i forsaw this i would have probably used float arrays instead of vectors |
|
|
|
-- (the scene graph bit makes sense though) |
|
|
|
|
|
|
|
serialized.towers = {} |
|
|
|
for i,t in pairs(game_state.towers) do |
|
|
@ -201,10 +246,16 @@ local function deselect_tile() |
|
|
|
win.scene:remove("tile_select_box") |
|
|
|
end |
|
|
|
|
|
|
|
local function game_pause_menu() |
|
|
|
|
|
|
|
end |
|
|
|
|
|
|
|
local function game_action(scene) |
|
|
|
if game_state.score < 0 then game_end() return true end |
|
|
|
if game_state.score < 0 then |
|
|
|
game_end() |
|
|
|
return true |
|
|
|
end |
|
|
|
|
|
|
|
local perf = am.perf_stats() |
|
|
|
game_state.time = game_state.time + am.delta_time |
|
|
|
game_state.score = game_state.score + am.delta_time |
|
|
|
|
|
|
@ -258,14 +309,14 @@ local function game_action(scene) |
|
|
|
node.color = COLORS.CLARET |
|
|
|
node:action(am.tween(0.1, { color = COLORS.TRANSPARENT })) |
|
|
|
play_sfx(SOUNDS.BIRD2) |
|
|
|
alert("closes the circle") |
|
|
|
gui_alert("closes the circle") |
|
|
|
|
|
|
|
elseif cost > game_state.money then |
|
|
|
local node = win.scene("cursor"):child(2) |
|
|
|
node.color = COLORS.CLARET |
|
|
|
node:action(am.tween(0.1, { color = COLORS.TRANSPARENT })) |
|
|
|
play_sfx(SOUNDS.BIRD2) |
|
|
|
alert("not enough money") |
|
|
|
gui_alert("not enough money") |
|
|
|
|
|
|
|
else |
|
|
|
update_money(-cost) |
|
|
@ -532,7 +583,7 @@ local function make_game_toolbelt() |
|
|
|
-- de-selecting currently selected tower if any |
|
|
|
toolbelt("toolbelt_select_square").hidden = true |
|
|
|
|
|
|
|
win.scene:replace("cursor", make_hex_cursor(0, COLORS.TRANSPARENT):tag"cursor") |
|
|
|
win.scene:replace("cursor", make_hex_cursor_node(0, COLORS.TRANSPARENT):tag"cursor") |
|
|
|
end |
|
|
|
end |
|
|
|
|
|
|
@ -604,14 +655,6 @@ local function game_scene() |
|
|
|
end |
|
|
|
end) |
|
|
|
|
|
|
|
local top_right_display = |
|
|
|
am.translate(win.right - 10, win.top - 15) |
|
|
|
^ am.text("", "right", "top"):tag"top_right_display" |
|
|
|
|
|
|
|
local bottom_right_display = |
|
|
|
am.translate(win.right - 10, win.bottom + win.height * 0.07 + 20) |
|
|
|
^ am.text("", "right", "bottom"):tag"bottom_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))) |
|
|
@ -621,12 +664,12 @@ local function game_scene() |
|
|
|
|
|
|
|
local scene = am.group( |
|
|
|
am.scale(1):tag"world_scale" ^ game_state.world, |
|
|
|
am.translate(HEX_GRID_CENTER):tag"cursor_translate" ^ make_hex_cursor(0, COLORS.TRANSPARENT):tag"cursor", |
|
|
|
am.translate(HEX_GRID_CENTER):tag"cursor_translate" ^ make_hex_cursor_node(0, COLORS.TRANSPARENT):tag"cursor", |
|
|
|
score, |
|
|
|
money, |
|
|
|
wave_timer, |
|
|
|
send_now_button, |
|
|
|
top_right_display, |
|
|
|
make_top_right_display_node(), |
|
|
|
make_game_toolbelt(), |
|
|
|
curtain |
|
|
|
) |
|
|
@ -638,17 +681,45 @@ local function game_scene() |
|
|
|
return scene |
|
|
|
end |
|
|
|
|
|
|
|
-- this is a stupid name, it just returns a scene node group of hexagons in a hexagonal shape centered at 0,0, of size |radius| |
|
|
|
-- |color_f| can be a function that takes a hex and returns a color, or just a color |
|
|
|
-- optionally, |action_f| is a function that operates on the group node every frame |
|
|
|
function make_hex_cursor_node(radius, color_f, action_f) |
|
|
|
local color = type(color_f) == "userdata" and color_f or nil |
|
|
|
local map = hex_spiral_map(vec2(0), radius) |
|
|
|
local group = am.group() |
|
|
|
|
|
|
|
for _,h in pairs(map) do |
|
|
|
local hexagon = am.circle(hex_to_pixel(h, vec2(HEX_SIZE)), HEX_SIZE, color or color_f(h), 6) |
|
|
|
group:append(hexagon) |
|
|
|
end |
|
|
|
|
|
|
|
if action_f then |
|
|
|
group:action(action_f) |
|
|
|
end |
|
|
|
|
|
|
|
return group |
|
|
|
end |
|
|
|
|
|
|
|
function update_score(diff) game_state.score = game_state.score + diff end |
|
|
|
function update_money(diff) game_state.money = game_state.money + diff end |
|
|
|
|
|
|
|
function game_end() |
|
|
|
local hmob = table.highest_index(game_state.mobs) |
|
|
|
local htower = table.highest_index(game_state.towers) |
|
|
|
local hprojectile = table.highest_index(game_state.projectiles) |
|
|
|
gui_alert(string.format( |
|
|
|
"\nmobs spawned: %d\ntowers built: %d\nprojectiles spawned: %d\n", |
|
|
|
hmob, htower, hprojectile |
|
|
|
), COLORS.WHITE, 1000) |
|
|
|
|
|
|
|
game_state = {} |
|
|
|
game = false |
|
|
|
end |
|
|
|
|
|
|
|
function game_save() |
|
|
|
am.save_state("save", game_serialize(), "json") |
|
|
|
alert("succesfully saved!") |
|
|
|
gui_alert("succesfully saved!") |
|
|
|
end |
|
|
|
|
|
|
|
function game_init(saved_state) |
|
|
@ -673,23 +744,3 @@ function game_init(saved_state) |
|
|
|
win.scene:append(game_scene()) |
|
|
|
end |
|
|
|
|
|
|
|
-- this is a stupid name, it just returns a scene node group of hexagons in a hexagonal shape centered at 0,0, of size |radius| |
|
|
|
-- |color_f| can be a function that takes a hex and returns a color, or just a color |
|
|
|
-- optionally, |action_f| is a function that operates on the group node every frame |
|
|
|
function make_hex_cursor(radius, color_f, action_f) |
|
|
|
local color = type(color_f) == "userdata" and color_f or nil |
|
|
|
local map = hex_spiral_map(vec2(0), radius) |
|
|
|
local group = am.group() |
|
|
|
|
|
|
|
for _,h in pairs(map) do |
|
|
|
local hexagon = am.circle(hex_to_pixel(h, vec2(HEX_SIZE)), HEX_SIZE, color or color_f(h), 6) |
|
|
|
group:append(hexagon) |
|
|
|
end |
|
|
|
|
|
|
|
if action_f then |
|
|
|
group:action(action_f) |
|
|
|
end |
|
|
|
|
|
|
|
return group |
|
|
|
end |
|
|
|
|