Browse Source

fix some stuff

master
Nicholas Hayashi 4 years ago
parent
commit
696a800ce7
  1. 30
      main.lua
  2. BIN
      res/loadgamehex.png
  3. BIN
      res/mapeditorhex.png
  4. BIN
      res/newgamehex.png
  5. BIN
      res/quithex.png
  6. BIN
      res/savegamehex.png
  7. BIN
      res/settingshex.png
  8. BIN
      res/unpausehex.png
  9. 187
      src/game.lua
  10. 2
      src/grid.lua
  11. 2
      src/mob.lua
  12. 2
      src/projectile.lua
  13. 8
      src/tower.lua

30
main.lua

@ -116,6 +116,8 @@ function main_action(self)
else else
--win:close() --win:close()
end end
elseif win:key_pressed("f4") then
win:close()
end end
if self"hex_backdrop" then if self"hex_backdrop" then
self"hex_backdrop""rotate".angle = math.wrapf(self"hex_backdrop""rotate".angle - 0.005 * am.delta_time, math.pi*2) self"hex_backdrop""rotate".angle = math.wrapf(self"hex_backdrop""rotate".angle - 0.005 * am.delta_time, math.pi*2)
@ -175,17 +177,14 @@ function make_main_scene_toolbelt()
false false
} }
local spacing = 150
-- calculate the dimensions of the whole grid -- calculate the dimensions of the whole grid
local spacing = 150
local grid_width = 6 local grid_width = 6
local grid_height = 2 local grid_height = 2
local hhs = hex_horizontal_spacing(spacing) local hhs = hex_horizontal_spacing(spacing)
local hvs = hex_vertical_spacing(spacing) local hvs = hex_vertical_spacing(spacing)
local grid_pixel_width = grid_width * hhs local grid_pixel_width = grid_width * hhs
local grid_pixel_height = grid_height * hvs local grid_pixel_height = grid_height * hvs
-- @TODO the vertical offset should be different depending on if this is the main menu or the pause menu
-- perhaps the map that makes the grid of hexes should be different as well
local pixel_offset = vec2(-grid_pixel_width/2, win.bottom + hex_height(spacing)/2 + 20) local pixel_offset = vec2(-grid_pixel_width/2, win.bottom + hex_height(spacing)/2 + 20)
local map = hex_rectangular_map(grid_width, grid_height, HEX_ORIENTATION.POINTY) local map = hex_rectangular_map(grid_width, grid_height, HEX_ORIENTATION.POINTY)
@ -271,10 +270,27 @@ function main_scene(do_backdrop, do_logo)
) )
if do_logo then if do_logo then
group:append(
am.translate(0, win.top - 20 - TEXTURES.LOGO.height/2)
local position = vec2(0, win.top - 20 - TEXTURES.LOGO.height/2)
local logo =
am.translate(position)
^ pack_texture_into_sprite(TEXTURES.LOGO, TEXTURES.LOGO.width, TEXTURES.LOGO.height) ^ pack_texture_into_sprite(TEXTURES.LOGO, TEXTURES.LOGO.width, TEXTURES.LOGO.height)
)
local selected = false
logo:action(function(self)
local mouse = win:mouse_position()
if math.distance(mouse, position) < TEXTURES.LOGO.height/2 then
selected = true
self"sprite".color = vec4(1)
if win:mouse_pressed("left") then
end
else
selected = false
self"sprite".color = vec4(0.95)
end
end)
group:append(logo)
end end
group:append(make_main_scene_toolbelt()) group:append(make_main_scene_toolbelt())

BIN
res/loadgamehex.png

Before

Width: 282  |  Height: 290  |  Size: 51 KiB

After

Width: 282  |  Height: 290  |  Size: 50 KiB

BIN
res/mapeditorhex.png

Before

Width: 283  |  Height: 290  |  Size: 59 KiB

After

Width: 283  |  Height: 290  |  Size: 58 KiB

BIN
res/newgamehex.png

Before

Width: 282  |  Height: 290  |  Size: 60 KiB

After

Width: 282  |  Height: 290  |  Size: 59 KiB

BIN
res/quithex.png

Before

Width: 282  |  Height: 290  |  Size: 56 KiB

After

Width: 282  |  Height: 290  |  Size: 57 KiB

BIN
res/savegamehex.png

Before

Width: 283  |  Height: 290  |  Size: 59 KiB

After

Width: 283  |  Height: 290  |  Size: 58 KiB

BIN
res/settingshex.png

Before

Width: 282  |  Height: 290  |  Size: 58 KiB

After

Width: 282  |  Height: 290  |  Size: 57 KiB

BIN
res/unpausehex.png

Before

Width: 282  |  Height: 290  |  Size: 60 KiB

After

Width: 282  |  Height: 290  |  Size: 59 KiB

187
src/game.lua

@ -3,14 +3,6 @@
game = false -- flag to tell if there is a game running game = false -- flag to tell if there is a game running
state = {} state = {}
function game_end()
state = {}
game = false
-- @TODO
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 -- top right display types
local TRDTS = { local TRDTS = {
@ -25,9 +17,9 @@ local TRDTS = {
} }
local function get_initial_game_state(seed) local function get_initial_game_state(seed)
local STARTING_MONEY = 200
local STARTING_MONEY = 50
local map, world = random_map()
local map, world = random_map(seed)
return { return {
map = map, -- map of hex coords map[x][y] to a 'tile' map = map, -- map of hex coords map[x][y] to a 'tile'
@ -95,14 +87,14 @@ local function select_tower_type(tower_type) end
local function select_toolbelt_button(i) end local function select_toolbelt_button(i) end
local function get_wave_time(current_wave) local function get_wave_time(current_wave)
return 90
return 45
end end
local function get_break_time(current_wave) local function get_break_time(current_wave)
return 15
return 20
end end
function do_day_night_cycle()
local function do_day_night_cycle()
local tstep = (math.sin(state.time * am.delta_time) + 1) / 100 local tstep = (math.sin(state.time * am.delta_time) + 1) / 100
--state.world"negative_mask".color = vec4(tstep){a=1} --state.world"negative_mask".color = vec4(tstep){a=1}
end end
@ -166,8 +158,12 @@ local function game_serialize()
serialized.map = nil -- we re-generate the entire map from the seed on de-serialize serialized.map = nil -- we re-generate the entire map from the seed on de-serialize
-- in order to serialize the game state, we have to convert all relevant userdata into -- in order to serialize the game state, we have to convert all relevant userdata into
-- something else. this practically only means vectors need to become arrays of floats.
-- this is dumb and if i forsaw this i would have probably used float arrays the whole time.
-- something else.
--
-- this practically means vectors need to become arrays of floats,
-- 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
serialized.towers = {} serialized.towers = {}
for i,t in pairs(state.towers) do for i,t in pairs(state.towers) do
@ -194,11 +190,6 @@ local function game_serialize()
return am.to_json(serialized) return am.to_json(serialized)
end end
function game_save()
am.save_state("save", game_serialize(), "json")
log("succesfully saved!")
end
local function game_action(scene) local function game_action(scene)
if state.score < 0 then game_end() return true end if state.score < 0 then game_end() return true end
@ -226,7 +217,7 @@ local function game_action(scene)
state.spawning = true state.spawning = true
-- calculate spawn chance for next wave -- calculate spawn chance for next wave
state.spawn_chance = math.log(state.current_wave) + 0.002
state.spawn_chance = math.log(state.current_wave)/2 + 0.002
state.time_until_next_break = get_wave_time(state.current_wave) state.time_until_next_break = get_wave_time(state.current_wave)
end end
@ -242,7 +233,6 @@ local function game_action(scene)
local interactable = evenq_is_in_interactable_region(evenq{ y = -evenq.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 buildable = tower_type_is_buildable_on(hex, tile, state.selected_tower_type)
local firable = false
if win:mouse_pressed"left" then if win:mouse_pressed"left" then
if interactable then if interactable then
@ -274,6 +264,7 @@ local function game_action(scene)
end end
elseif not state.selected_tower_type then elseif not state.selected_tower_type then
-- interactable tile, but no tower type selected -- interactable tile, but no tower type selected
local towers = towers_on_hex(hex)
end end
end end
@ -323,16 +314,23 @@ local function game_action(scene)
do_mob_spawning(state.spawn_chance) do_mob_spawning(state.spawn_chance)
do_day_night_cycle() do_day_night_cycle()
if interactable then
win.scene("cursor").hidden = false
-- update the cursor
if not interactable then
win.scene("cursor").hidden = true
else
if state.selected_tower_type then
if buildable then if buildable then
win.scene("cursor_translate").position2d = rounded_mouse
win.scene("cursor").hidden = false
else else
win.scene("cursor").hidden = true win.scene("cursor").hidden = true
end end
else else
win.scene("cursor").hidden = true
-- if we don't have a tower selected, but the tile is interactable, then show the 'select' cursor
win.scene("cursor").hidden = false
end
win.scene("cursor_translate").position2d = rounded_mouse
end end
win.scene("score").text = string.format("SCORE: %.2f", state.score) win.scene("score").text = string.format("SCORE: %.2f", state.score)
@ -343,8 +341,9 @@ end
local function make_game_toolbelt() local function make_game_toolbelt()
local function toolbelt_button(size, half_size, tower_texture, padding, i, offset, key_name) local function toolbelt_button(size, half_size, tower_texture, padding, i, offset, key_name)
local button = am.translate(vec2(size + padding, 0) * i + offset)
^ am.group{
local button =
am.translate(vec2(size + padding, 0) * i + offset)
^ am.group(
am.translate(0, half_size) am.translate(0, half_size)
^ pack_texture_into_sprite(TEXTURES.BUTTON1, size, size), ^ pack_texture_into_sprite(TEXTURES.BUTTON1, size, size),
@ -352,12 +351,12 @@ local function make_game_toolbelt()
^ pack_texture_into_sprite(tower_texture, size, size), ^ pack_texture_into_sprite(tower_texture, size, size),
am.translate(vec2(half_size)) am.translate(vec2(half_size))
^ am.group{
^ am.group(
pack_texture_into_sprite(TEXTURES.BUTTON1, half_size, half_size), pack_texture_into_sprite(TEXTURES.BUTTON1, half_size, half_size),
am.scale(2) am.scale(2)
^ am.text(key_name, COLORS.BLACK) ^ am.text(key_name, COLORS.BLACK)
}
}
)
)
local x1 = (size + padding) * i + offset.x - half_size local x1 = (size + padding) * i + offset.x - half_size
local y1 = offset.y local y1 = offset.y
@ -366,9 +365,13 @@ local function make_game_toolbelt()
local rect = { x1 = x1, y1 = y1, x2 = x2, y2 = y2 } local rect = { x1 = x1, y1 = y1, x2 = x2, y2 = y2 }
button:action(function(self) button:action(function(self)
if win:mouse_pressed"left" and point_in_rect(win:mouse_position(), rect) then
if point_in_rect(win:mouse_position(), rect) then
win.scene:replace("tower_tooltip_text", get_tower_tooltip_text_node(tower_type))
if win:mouse_pressed"left" then
select_toolbelt_button(i) select_toolbelt_button(i)
end end
end
end) end)
return button return button
@ -387,7 +390,8 @@ local function make_game_toolbelt()
local color = COLORS.WHITE local color = COLORS.WHITE
return (am.translate(win.left + 10, win.bottom + toolbelt_height + 20) return (am.translate(win.left + 10, win.bottom + toolbelt_height + 20)
^ am.group{
^ am.scale(1)
^ am.group(
am.translate(0, 60) am.translate(0, 60)
^ am.text(name, color, "left"):tag"tower_name", ^ am.text(name, color, "left"):tag"tower_name",
@ -399,7 +403,7 @@ local function make_game_toolbelt()
am.translate(0, 0) am.translate(0, 0)
^ am.text(string.format("cost: %d", cost), color, "left"):tag"tower_cost" ^ am.text(string.format("cost: %d", cost), color, "left"):tag"tower_cost"
}
)
) )
:tag"tower_tooltip_text" :tag"tower_tooltip_text"
end end
@ -424,9 +428,6 @@ local function make_game_toolbelt()
local keys = { '1', '2', '3', '4', 'q', 'w', 'e', 'r', 'a', 's', 'd', 'f' } local keys = { '1', '2', '3', '4', 'q', 'w', 'e', 'r', 'a', 's', 'd', 'f' }
--local keys = { '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=' } --local keys = { '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=' }
local TOOLBELT_OPTION = {
SELECT = 11
}
local toolbelt_options = { local toolbelt_options = {
TOWER_TYPE.WALL, TOWER_TYPE.WALL,
TOWER_TYPE.HOWITZER, TOWER_TYPE.HOWITZER,
@ -434,29 +435,11 @@ local function make_game_toolbelt()
TOWER_TYPE.MOAT, TOWER_TYPE.MOAT,
TOWER_TYPE.RADAR, TOWER_TYPE.RADAR,
TOWER_TYPE.LIGHTHOUSE, TOWER_TYPE.LIGHTHOUSE,
-- reserved for tower types
false,
false,
false,
false,
TOOLBELT_OPTION.SELECT,
false
} }
local tower_type_count = table.count(TOWER_TYPE) local tower_type_count = table.count(TOWER_TYPE)
local function get_toolbelt_icon_texture(i) local function get_toolbelt_icon_texture(i)
if i <= tower_type_count then if i <= tower_type_count then
return get_tower_icon_texture(toolbelt_options[i]) return get_tower_icon_texture(toolbelt_options[i])
else
local toolbelt_option = TOOLBELT_OPTION[i - tower_type_count]
if toolbelt_option then
if toolbelt_option == TOOLBELT_OPTION.SELECT then
return TEXTURES.SELECT_BOX_ICON
end
end
end end
end end
for i,v in pairs(toolbelt_options) do for i,v in pairs(toolbelt_options) do
@ -473,6 +456,9 @@ local function make_game_toolbelt()
) )
end end
local a = win.left + #toolbelt_options * (size + padding)
log(a)
local settings_button_position = vec2(win.right - half_size - 20, win.bottom + half_size + padding/3) local settings_button_position = vec2(win.right - half_size - 20, win.bottom + half_size + padding/3)
local settings_button_rect = { local settings_button_rect = {
x1 = settings_button_position.x - size/2, x1 = settings_button_position.x - size/2,
@ -517,9 +503,8 @@ local function make_game_toolbelt()
else else
-- de-selecting currently selected tower if any -- de-selecting currently selected tower if any
toolbelt("tower_select_square").hidden = true toolbelt("tower_select_square").hidden = true
log('hi2')
win.scene:replace("cursor", make_hex_cursor(0, COLORS.TRANSPARENT))
win.scene:replace("cursor", make_hex_cursor(0, COLORS.TRANSPARENT):tag"cursor")
end end
end end
@ -531,49 +516,33 @@ local function make_game_toolbelt()
else else
select_tower_type(nil) select_tower_type(nil)
if i == 11 then
log('hi')
end
end end
end end
return toolbelt return toolbelt
end 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:tag"cursor"
end
function game_scene()
local score = am.translate(win.left + 10, win.top - 20)
local function game_scene()
local score =
am.translate(win.left + 10, win.top - 20)
^ am.text("", "left"):tag"score" ^ am.text("", "left"):tag"score"
local money = am.translate(win.left + 10, win.top - 40)
local money =
am.translate(win.left + 10, win.top - 40)
^ am.text("", "left"):tag"money" ^ am.text("", "left"):tag"money"
local wave_timer = am.translate(0, win.top - 20)
local wave_timer =
am.translate(0, win.top - 20)
^ am.text(get_wave_timer_text()):tag"wave_timer" ^ am.text(get_wave_timer_text()):tag"wave_timer"
local top_right_display = am.translate(win.right - 10, win.top - 20)
local top_right_display =
am.translate(win.right - 10, win.top - 20)
^ am.text("", "right", "top"):tag"top_right_display" ^ 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) local curtain = am.rect(win.left, win.bottom, win.right, win.top, COLORS.TRUE_BLACK)
curtain:action(coroutine.create(function() curtain:action(coroutine.create(function()
am.wait(am.tween(curtain, 3, { color = vec4(0) }, am.ease.out(am.ease.hyperbola))) am.wait(am.tween(curtain, 3, { color = vec4(0) }, am.ease.out(am.ease.hyperbola)))
@ -581,25 +550,43 @@ function game_scene()
return true return true
end)) end))
local scene = am.group{
local scene = am.group(
am.scale(1):tag"world_scale" ^ state.world, am.scale(1):tag"world_scale" ^ state.world,
am.translate(HEX_GRID_CENTER):tag"cursor_translate" ^ make_hex_cursor(0, COLORS.TRANSPARENT),
am.translate(HEX_GRID_CENTER):tag"cursor_translate" ^ make_hex_cursor(0, COLORS.TRANSPARENT):tag"cursor",
score, score,
money, money,
wave_timer, wave_timer,
top_right_display, top_right_display,
make_game_toolbelt(), make_game_toolbelt(),
curtain,
}:tag"game"
curtain
)
:tag"game"
scene:action(game_action) scene:action(game_action)
return scene return scene
end end
function update_score(diff) state.score = state.score + diff end
function update_money(diff) state.money = state.money + diff end
function game_end()
state = {}
game = false
-- @TODO anything
end
function game_save()
am.save_state("save", game_serialize(), "json")
alert("succesfully saved!")
end
function game_init(saved_state) function game_init(saved_state)
if saved_state then if saved_state then
state = game_deserialize(saved_state) state = game_deserialize(saved_state)
-- @HACK fixes a bug where loading game state with a tower type selected, but you don't have a built tower cursor node, so hovering a buildable tile throws an error
select_tower_type(nil)
else else
state = get_initial_game_state() state = get_initial_game_state()
local home_tower = build_tower(HEX_GRID_CENTER, TOWER_TYPE.RADAR) local home_tower = build_tower(HEX_GRID_CENTER, TOWER_TYPE.RADAR)
@ -614,3 +601,23 @@ function game_init(saved_state)
win.scene:append(game_scene()) win.scene:append(game_scene())
end 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

2
src/grid.lua

@ -6,6 +6,7 @@ do
-- the size of the grid should basically always be constant (i think), -- the size of the grid should basically always be constant (i think),
-- but different aspect ratios complicate this in an annoying way -- but different aspect ratios complicate this in an annoying way
-- grid width should be ~== height * 3 / 2
HEX_GRID_WIDTH = 33 + padding HEX_GRID_WIDTH = 33 + padding
HEX_GRID_HEIGHT = 23 + padding HEX_GRID_HEIGHT = 23 + padding
@ -215,6 +216,7 @@ function make_hex_grid_scene(map)
end end
apply_flow_field(map, generate_flow_field(map, HEX_GRID_CENTER), world) apply_flow_field(map, generate_flow_field(map, HEX_GRID_CENTER), world)
return am.translate(WORLDSPACE_COORDINATE_OFFSET) ^ world return am.translate(WORLDSPACE_COORDINATE_OFFSET) ^ world
end end

2
src/mob.lua

@ -14,7 +14,7 @@ MOB_SPECS = {
[MOB_TYPE.BEEPER] = { [MOB_TYPE.BEEPER] = {
health = 30, health = 30,
speed = 8, speed = 8,
bounty = 15,
bounty = 5,
hurtbox_radius = MOB_SIZE/2 hurtbox_radius = MOB_SIZE/2
}, },
[MOB_TYPE.SPOODER] = { [MOB_TYPE.SPOODER] = {

2
src/projectile.lua

@ -227,7 +227,7 @@ end
function projectile_deserialize(json_string) function projectile_deserialize(json_string)
local projectile = entity_basic_json_parse(json_string) local projectile = entity_basic_json_parse(json_string)
projectile.vector = vec2(projectile.vector[0], projectile.vector[1])
projectile.vector = vec2(projectile.vector[1], projectile.vector[2])
projectile.update = get_projectile_update_function(projectile.type) projectile.update = get_projectile_update_function(projectile.type)
projectile.node = am.translate(projectile.position) projectile.node = am.translate(projectile.position)

8
src/tower.lua

@ -35,7 +35,7 @@ TOWER_SPECS = {
texture = TEXTURES.TOWER_HOWITZER, texture = TEXTURES.TOWER_HOWITZER,
icon_texture = TEXTURES.TOWER_HOWITZER_ICON, icon_texture = TEXTURES.TOWER_HOWITZER_ICON,
cost = 20, cost = 20,
range = 10,
range = 6,
fire_rate = 4, fire_rate = 4,
size = 0, size = 0,
height = 1, height = 1,
@ -47,7 +47,7 @@ TOWER_SPECS = {
texture = TEXTURES.TOWER_REDEYE, texture = TEXTURES.TOWER_REDEYE,
icon_texture = TEXTURES.TOWER_REDEYE_ICON, icon_texture = TEXTURES.TOWER_REDEYE_ICON,
cost = 20, cost = 20,
range = 12,
range = 8,
fire_rate = 1, fire_rate = 1,
size = 0, size = 0,
height = 1, height = 1,
@ -83,7 +83,7 @@ TOWER_SPECS = {
texture = TEXTURES.TOWER_LIGHTHOUSE, texture = TEXTURES.TOWER_LIGHTHOUSE,
icon_texture = TEXTURES.TOWER_LIGHTHOUSE_ICON, icon_texture = TEXTURES.TOWER_LIGHTHOUSE_ICON,
cost = 20, cost = 20,
range = 8,
range = 7,
fire_rate = 1, fire_rate = 1,
size = 0, size = 0,
height = 1, height = 1,
@ -220,7 +220,7 @@ end
function tower_serialize(tower) function tower_serialize(tower)
local serialized = entity_basic_devectored_copy(tower) local serialized = entity_basic_devectored_copy(tower)
for i,h in pairs(serialized.hexes) do
for i,h in pairs(tower.hexes) do
serialized.hexes[i] = { h.x, h.y } serialized.hexes[i] = { h.x, h.y }
end end

Loading…
Cancel
Save