Browse Source

biggggg refactor, tower cursor silhouettes

master
Nicholas Hayashi 4 years ago
parent
commit
c7c4ff52c2
  1. 244
      main.lua
  2. 6
      src/entity.lua
  3. 257
      src/game.lua
  4. 93
      src/gui.lua
  5. 2
      src/hexyz.lua
  6. 10
      src/mob.lua
  7. 45
      src/tower.lua

244
main.lua

@ -5,17 +5,20 @@ math.random()
math.random() 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 -- assets and/or trivial code
require "color" require "color"
@ -26,6 +29,7 @@ require "src/entity"
require "src/extra" require "src/extra"
require "src/geometry" require "src/geometry"
require "src/hexyz" require "src/hexyz"
require "src/game"
require "src/grid" require "src/grid"
require "src/gui" require "src/gui"
require "src/mob" require "src/mob"
@ -33,212 +37,62 @@ require "src/projectile"
require "src/tower" 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 -- global audio settings
MUSIC_VOLUME = 0.1 MUSIC_VOLUME = 0.1
SFX_VOLUME = 0.1 SFX_VOLUME = 0.1
-- game stuff
SELECTED_TOWER_TYPE = 1
MODES = { MAIN, GAME }
CURRENT_MODE = MODES.MAIN
-- top right display types -- top right display types
local TRDTS = { 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 end
do_day_night_cycle()
return str
end 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 end
return group
end 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 end
WIN.scene = am.group(game_scene())
game_init()
noglobals() noglobals()

6
src/entity.lua

@ -6,7 +6,7 @@ entity structure:
TOB - number - time of birth, const TOB - number - time of birth, const
hex - vec2 - current occupied hex, if any hex - vec2 - current occupied hex, if any
position - vec2 - current pixel position of it's translate (forced parent) node 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 node - node - scene graph node
... - any - a bunch of other shit depending on what entity type it is ... - 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) function make_basic_entity(hex, node, update, position)
local entity = {} 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 -- 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 -- 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) function register_entity(t, entity)
table.insert(t, entity) table.insert(t, entity)
WORLD:append(entity.node)
state.world:append(entity.node)
end end
-- |t| is the source table, probably MOBS, TOWERS, or PROJECTILES -- |t| is the source table, probably MOBS, TOWERS, or PROJECTILES

257
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

93
src/gui.lua

@ -37,97 +37,4 @@ function do_gui_updates()
end end
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

2
src/hexyz.lua

@ -236,7 +236,7 @@ end
-- Returns Ordered Spiral Hexagonal Map of |radius| Rings from |center| -- Returns Ordered Spiral Hexagonal Map of |radius| Rings from |center|
function spiral_map(center, radius) function spiral_map(center, radius)
local map = {center}
local map = { center }
for i = 1, radius do for i = 1, radius do
table.append(map, ring_map(center, i)) table.append(map, ring_map(center, i))

10
src/mob.lua

@ -108,16 +108,16 @@ local function update_mob(mob, mob_index)
end end
else else
-- use the map's flow field - gotta find the the best neighbour -- 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 if #neighbours > 0 then
local first_neighbour = neighbours[1] 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_hex = first_neighbour
local lowest_cost = tile.priority or 0 local lowest_cost = tile.priority or 0
for _,n in pairs(neighbours) do 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 local current_cost = tile.priority
if current_cost and current_cost < lowest_cost then 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 if mob.frame_target then
-- this is supposed to achieve frame rate independence, but i have no idea if it actually does -- 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 -- 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.position = mob.position + math.normalize(hex_to_pixel(mob.frame_target) - mob.position) * rate
mob.node.position2d = mob.position mob.node.position2d = mob.position
@ -160,7 +160,7 @@ end
local function make_and_register_mob(mob_type) local function make_and_register_mob(mob_type)
local mob = make_basic_entity( local mob = make_basic_entity(
get_spawn_hex(), 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 update_mob
) )

45
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 return TOWER_SPECS[tower_type] and TOWER_SPECS[tower_type].base_cost
end 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 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
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 end
local function make_tower_node(tower_type) local function make_tower_node(tower_type)
@ -98,6 +96,21 @@ local function make_tower_node(tower_type)
end end
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) function towers_on_hex(hex)
local t = {} local t = {}
for tower_index,tower in pairs(TOWERS) do 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.last_shot_time = tower.TOB
tower.target_index = false 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 elseif tower_type == TOWER_TYPE.LIGHTHOUSE then
tower.range = 5 tower.range = 5
@ -268,14 +281,14 @@ function make_and_register_tower(hex, tower_type)
need_to_regen_flow_field = false need_to_regen_flow_field = false
elseif tower_type == TOWER_TYPE.WALL then 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 elseif tower_type == TOWER_TYPE.MOAT then
HEX_MAP[hex.x][hex.y].elevation = -1
state.map[hex.x][hex.y].elevation = -1
end end
if need_to_regen_flow_field then 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 end
register_entity(TOWERS, tower) register_entity(TOWERS, tower)

Loading…
Cancel
Save