Browse Source

i dont remember

master
Nicholas Hayashi 4 years ago
parent
commit
06524a2e20
  1. 24
      TODO.md
  2. BIN
      res/bagel.jpg
  3. 0
      res/tower_radar.png
  4. BIN
      res/tower_radar_icon.png
  5. 10
      src/extra.lua
  6. 38
      src/game.lua
  7. 9
      src/grid.lua
  8. 29
      src/mob.lua
  9. 219
      src/tower.lua
  10. 49
      texture.lua

24
TODO.md

@ -0,0 +1,24 @@
- fix lighthouse pathing recusrive bug thing
- make more towers
- make/fix radar tower
- do waves
- make mob difficulty scale
towers:
1 - wall
2 - howitzer
3 -
4 - redeye
q -
w - moat
e -
r - radar
a -
s - lighthouse
d - not a tower, deselects current selection
f -

BIN
res/bagel.jpg

After

Width: 820  |  Height: 820  |  Size: 165 KiB

0
res/satelite.png → res/tower_radar.png

Before

Width: 300  |  Height: 300  |  Size: 30 KiB

After

Width: 300  |  Height: 300  |  Size: 30 KiB

BIN
res/tower_radar_icon.png

After

Width: 300  |  Height: 300  |  Size: 30 KiB

10
src/extra.lua

@ -1,4 +1,14 @@
-- @TODO make it work with functions that return multiple values
-- right now it discards returned values beyond the first
function fprofile(f, ...)
local t1 = am.current_time()
local result = f(...)
log("%f", am.current_time() - t1)
return result
end
function booltostring(bool)
return bool and "true" or "false"
end

38
src/game.lua

@ -77,7 +77,7 @@ local function can_do_build(hex, tile, tower_type)
end
if not tower_is_buildable_on(hex, tile, tower_type) then
local node = WIN.scene("cursor"):child(1)
local node = WIN.scene("cursor"):child(1):child(2)
node.color = COLORS.CLARET
node:action(am.tween(0.1, { color = COLORS.TRANSPARENT }))
@ -150,6 +150,7 @@ local function game_action(scene)
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)
end
end
@ -230,7 +231,7 @@ 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_base_cost(tower_type)
local base_cost = get_tower_cost(tower_type)
if not (name or placement_rules or short_desc or base_cost) then
return am.group():tag"tower_tooltip_text"
@ -245,7 +246,7 @@ local function make_game_toolbelt()
}):tag"tower_tooltip_text"
end
local toolbelt = am.group{
get_tower_tooltip_text_node(state.selected_tower_type),
am.group():tag"tower_tooltip_text",
am.rect(WIN.left, WIN.bottom, WIN.right, WIN.bottom + toolbelt_height, COLORS.TRANSPARENT)
}:tag"toolbelt"
@ -265,19 +266,23 @@ local function make_game_toolbelt()
tower_select_square.hidden = true
toolbelt:append(tower_select_square)
local keys = { '1', '2', '3', '4', 'q', 'w', 'e', 'r', 'a', 's', 'd', 'f' }
-- order of this array is the order of towers on the toolbelt.
local tower_type_values = {
TOWER_TYPE.REDEYE,
TOWER_TYPE.LIGHTHOUSE,
TOWER_TYPE.WALL,
TOWER_TYPE.MOAT
TOWER_TYPE.HOWITZER,
TOWER_TYPE.REDEYE,
TOWER_TYPE.MOAT,
TOWER_TYPE.RADAR,
TOWER_TYPE.LIGHTHOUSE
}
local keys = { '1', '2', '3', '4', 'q', 'w', 'e', 'r', 'a', 's', 'd', 'f' }
for i = 1, #keys do
local icon_texture = get_tower_icon_texture(tower_type_values[i])
toolbelt:append(
toolbelt_button(
size,
half_size,
get_tower_icon_texture(tower_type_values[i]),
icon_texture,
padding,
i,
offset,
@ -289,7 +294,7 @@ local function make_game_toolbelt()
select_tower_type = function(tower_type)
state.selected_tower_type = tower_type
if TOWER_SPECS[state.selected_tower_type] then
if get_tower_spec(tower_type) then
WIN.scene:replace("tower_tooltip_text", get_tower_tooltip_text_node(tower_type))
local new_position = vec2((size + padding) * tower_type, size/2) + offset
@ -314,19 +319,25 @@ local function make_game_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)
-- |action_f| should be an action that operates on the group node or nil
function make_hex_cursor(position, 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()
for _,h in pairs(map) do
group:append(am.circle(hex_to_pixel(h), HEX_SIZE, color or color_f(h), 6))
local hexagon = am.circle(hex_to_pixel(h), HEX_SIZE, color or color_f(h), 6)
group:append(hexagon)
end
if action_f then
group:action(action_f)
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 top_right_display = am.translate(WIN.right - 10, WIN.top - 20) ^ am.text("", "right", "top"):tag"top_right_display"
@ -354,6 +365,7 @@ end
function game_init()
state = get_initial_game_state()
build_tower(HEX_GRID_CENTER, TOWER_TYPE.RADAR)
WIN.scene:remove"game"
WIN.scene:append(game_scene())

9
src/grid.lua

@ -17,7 +17,7 @@ HEX_GRID_DIMENSIONS = vec2(HEX_GRID_WIDTH, HEX_GRID_HEIGHT)
-- leaving y == 0 makes this the center in hex coordinates
HEX_GRID_CENTER = vec2(math.floor(HEX_GRID_WIDTH/2)
, 0)
-- math.floor(HEX_GRID_HEIGHT/2))
-- , math.floor(HEX_GRID_HEIGHT/2))
HEX_GRID_MINIMUM_ELEVATION = -1
HEX_GRID_MAXIMUM_ELEVATION = 1
@ -76,7 +76,7 @@ end
function making_hex_unwalkable_breaks_flow_field(hex, tile)
local original_elevation = tile.elevation
-- making the tile's elevation very large *should* make it unwalkable
tile.elevation = 10
tile.elevation = 999
local flow_field = generate_flow_field(state.map, HEX_GRID_CENTER)
local result = not hex_map_get(flow_field, 0, 0)
@ -121,8 +121,6 @@ function generate_and_apply_flow_field(map, start, world)
^ am.text(string.format("%.1f", flow.priority * 10)))
else
map[i][j].priority = nil
log('fire')
-- should fire exactly once
end
end
end
@ -200,9 +198,6 @@ function random_map(seed)
generate_and_apply_flow_field(map, HEX_GRID_CENTER, world)
world:append(am.translate(hex_to_pixel(HEX_GRID_CENTER))
^ pack_texture_into_sprite(TEXTURES.SATELLITE, HEX_PIXEL_WIDTH, HEX_PIXEL_HEIGHT))
return map, am.translate(WORLDSPACE_COORDINATE_OFFSET) ^ world
end

29
src/mob.lua

@ -24,7 +24,7 @@ end
-- check if a the tile at |hex| is passable by |mob|
function mob_can_pass_through(mob, hex)
local tile = HEX_MAP.get(hex.x, hex.y)
local tile = state.map.get(hex.x, hex.y)
return tile and tile_is_medium_elevation(tile)
end
@ -84,7 +84,7 @@ local function update_mob(mob, mob_index)
-- figure out movement
if last_frame_hex ~= mob.hex or not mob.frame_target then
local frame_target, tile = nil, nil
local frame_target, tile = false, false
if mob.path then
--log('A*')
-- we have an explicitly stored target
@ -92,8 +92,8 @@ local function update_mob(mob, mob_index)
if not path_entry then
-- we should be just about to reach the target, delete the path.
mob.path = nil
mob.frame_target = nil
mob.path = false
mob.frame_target = false
return
end
@ -104,7 +104,7 @@ local function update_mob(mob, mob_index)
if last_frame_hex ~= mob.hex and not mob_can_pass_through(mob, mob.frame_target) then
log('recalc')
mob.path = get_mob_path(mob, HEX_MAP, mob.hex, HEX_GRID_CENTER)
mob.frame_target = nil
mob.frame_target = false
end
else
-- use the map's flow field - gotta find the the best neighbour
@ -139,12 +139,19 @@ local function update_mob(mob, mob_index)
-- do movement
if mob.frame_target then
-- 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
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.node.position2d = mob.position
-- it's totally possible that the target we have was invalidated by a tower placed this frame,
-- or between when we last calculated this target and now
-- check for that now
if mob_can_pass_through(mob, mob.frame_target) then
-- 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
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.node.position2d = mob.position
else
mob.frame_target = false
end
else
log('no target')
end

219
src/tower.lua

@ -3,36 +3,48 @@
TOWERS = {}
TOWER_TYPE = {
REDEYE = 1,
LIGHTHOUSE = 2,
WALL = 3,
WALL = 1,
HOWITZER = 2,
-- = 3,
REDEYE = 3,
-- = 5,
MOAT = 4,
-- = 7,
RADAR = 5,
-- = 9,
LIGHTHOUSE = 6
}
TOWER_SPECS = {
[TOWER_TYPE.REDEYE] = {
name = "Redeye",
placement_rules_text = "Place on mountains or on Walls",
short_description = "Long range laser tower",
texture = TEXTURES.TOWER_REDEYE,
icon_texture = TEXTURES.TOWER_REDEYE_ICON,
base_cost = 25,
},
[TOWER_TYPE.LIGHTHOUSE] = {
name = "Lighthouse",
placement_rules_text = "Place next to - but not on - water or moats",
short_description = "Attracts and distracts mobs",
texture = TEXTURES.TOWER_LIGHTHOUSE,
icon_texture = TEXTURES.TOWER_LIGHTHOUSE_ICON,
base_cost = 25
},
local TOWER_SPECS = {
[TOWER_TYPE.WALL] = {
name = "Wall",
placement_rules_text = "Place on grass or dirt",
short_description = "Restricts movement",
texture = TEXTURES.TOWER_WALL,
icon_texture = TEXTURES.TOWER_WALL_ICON,
base_cost = 5,
cost = 10,
range = 0,
props = {}
},
[TOWER_TYPE.HOWITZER] = {
name = "HOWITZER",
placement_rules_text = "Place on non-Water",
short_description = "Fires artillery. Range and cost increase with elevation of terrain underneath.",
texture = TEXTURES.TOWER_SHELL,
icon_texture = TEXTURES.TOWER_SHELL_ICON,
cost = 20,
range = 10,
props = {}
},
[TOWER_TYPE.REDEYE] = {
name = "Redeye",
placement_rules_text = "Place on Mountains or on Walls",
short_description = "Long-range, single-target laser tower",
texture = TEXTURES.TOWER_REDEYE,
icon_texture = TEXTURES.TOWER_REDEYE_ICON,
cost = 20,
range = 12,
props = {}
},
[TOWER_TYPE.MOAT] = {
name = "Moat",
@ -40,27 +52,55 @@ TOWER_SPECS = {
short_description = "Restricts movement",
texture = TEXTURES.TOWER_MOAT,
icon_texture = TEXTURES.TOWER_MOAT_ICON,
base_cost = 5,
}
cost = 10,
range = 0,
props = {}
},
[TOWER_TYPE.RADAR] = {
name = "Radar",
placement_rules_text = "Place on any non-water",
short_description = "Provides information about incoming waves.",
texture = TEXTURES.TOWER_RADAR,
icon_texture = TEXTURES.TOWER_RADAR_ICON,
cost = 20,
range = 0,
props = {}
},
[TOWER_TYPE.LIGHTHOUSE] = {
name = "Lighthouse",
placement_rules_text = "Place next to - but not on - Water or Moats",
short_description = "Attracts nearby mobs; temporarily redirects their path",
texture = TEXTURES.TOWER_LIGHTHOUSE,
icon_texture = TEXTURES.TOWER_LIGHTHOUSE_ICON,
cost = 20,
range = 8,
props = {}
},
}
function get_tower_spec(tower_type)
return TOWER_SPECS[tower_type]
end
function get_tower_name(tower_type)
return TOWER_SPECS[tower_type] and TOWER_SPECS[tower_type].name
return TOWER_SPECS[tower_type].name
end
function get_tower_placement_rules_text(tower_type)
return TOWER_SPECS[tower_type] and TOWER_SPECS[tower_type].placement_rules_text
return TOWER_SPECS[tower_type].placement_rules_text
end
function get_tower_short_description(tower_type)
return TOWER_SPECS[tower_type] and TOWER_SPECS[tower_type].short_description
return TOWER_SPECS[tower_type].short_description
end
function get_tower_texture(tower_type)
return TOWER_SPECS[tower_type] and TOWER_SPECS[tower_type].texture
return TOWER_SPECS[tower_type].texture
end
function get_tower_icon_texture(tower_type)
return TOWER_SPECS[tower_type] and TOWER_SPECS[tower_type].icon_texture
end
function get_tower_base_cost(tower_type)
return TOWER_SPECS[tower_type] and TOWER_SPECS[tower_type].base_cost
function get_tower_cost(tower_type)
return TOWER_SPECS[tower_type].cost
end
function get_tower_range(tower_type)
return TOWER_SPECS[tower_type].range
end
local function make_tower_sprite(tower_type)
@ -70,8 +110,28 @@ end
do
local tower_cursors = {}
for _,i in pairs(TOWER_TYPE) do
tower_cursors[i] = make_tower_sprite(i)
tower_cursors[i].color = COLORS.TRANSPARENT
local tower_sprite = make_tower_sprite(i)
tower_sprite.color = COLORS.TRANSPARENT
local coroutine_ = coroutine.create(function(node)
local flash_on = {}
local flash_off = {}
while true do
for _,n in node:child_pairs() do
table.insert(flash_on, am.tween(n, 1, { color = vec4(0.4) }))
table.insert(flash_off, am.tween(n, 1, { color = vec4(0) }))
end
am.wait(am.parallel(flash_on))
am.wait(am.parallel(flash_off))
flash_on = {}
flash_off = {}
end
end)
tower_cursors[i] = am.group{
make_hex_cursor(vec2(0), get_tower_range(i), vec4(0), coroutine_),
tower_sprite
}
end
function get_tower_cursor(tower_type)
@ -85,18 +145,42 @@ local function make_tower_node(tower_type)
elseif tower_type == TOWER_TYPE.LIGHTHOUSE then
return am.group(
make_tower_sprite(tower_type)
make_tower_sprite(tower_type),
am.particles2d{
source_pos = vec2(0, 12),
source_pos_var = vec2(2),
start_size = 1,
start_size_var = 1,
end_size = 1,
end_size_var = 1,
angle = 0,
angle_var = math.pi,
speed = 1,
speed_var = 2,
life = 10,
life_var = 1,
start_color = COLORS.WHITE,
start_color_var = vec4(0.1, 0.1, 0.1, 1),
end_color = COLORS.SUNRAY,
end_color_var = vec4(0.1),
emission_rate = 4,
start_particles = 4,
max_particles = 200
}
)
elseif tower_type == TOWER_TYPE.WALL then
return am.circle(vec2(0), HEX_SIZE, COLORS.VERY_DARK_GRAY, 6)
elseif tower_type == TOWER_TYPE.MOAT then
return am.circle(vec2(0), HEX_SIZE, (COLORS.WATER){a=1}, 6)
elseif tower_type == TOWER_TYPE.RADAR then
return make_tower_sprite(tower_type)
end
end
function can_afford_tower(money, tower_type)
local cost = get_tower_base_cost(tower_type)
local cost = get_tower_cost(tower_type)
return (money - cost) >= 0
end
@ -109,7 +193,6 @@ local function get_tower_update_function(tower_type)
end
end
function towers_on_hex(hex)
local t = {}
for tower_index,tower in pairs(TOWERS) do
@ -120,16 +203,11 @@ function towers_on_hex(hex)
return t
end
function tower_on_hex(hex)
return table.find(TOWERS, function(tower)
return tower.hex == hex
end)
return table.find(TOWERS, function(tower) return tower.hex == hex end)
end
function tower_is_buildable_on(hex, tile, tower_type)
if hex == HEX_GRID_CENTER then return false end
local blocking_towers = towers_on_hex(hex)
local blocking_mobs = mobs_on_hex(hex)
@ -209,18 +287,22 @@ function update_tower_lighthouse(tower, tower_index)
for _,m in pairs(mobs) do
if not m.path then
local path, made_it = Astar(HEX_MAP, tower.hex, m.hex, grid_heuristic, grid_cost)
-- @TODO only attract the mob if its frame target (direction vector)
-- is within some angle range...? if the mob is heading directly away from the tower, then
-- the lighthouse shouldn't do much
local path, made_it = Astar(state.map, tower.hex, m.hex, grid_heuristic, grid_cost)
if made_it then
m.path = path
local area = spiral_map(tower.hex, tower.range)
for _,h in pairs(area) do
local node = HEX_MAP[h.x][h.y].node"circle"
local node = state.map[h.x][h.y].node"circle"
local initial_color = node.color
local d = math.distance(h, tower.hex)
local target_color = COLORS.SUNRAY{ a = 1/(d/tower.range) }
local target_color = COLORS.SUNRAY{ a = 1/(d/tower.range) + 0.9 }
node:late_action(am.series{
am.tween(node, 0.3, { color = target_color }),
am.tween(node, 0.3, { color = initial_color })
@ -239,62 +321,29 @@ function make_and_register_tower(hex, tower_type)
get_tower_update_function(tower_type)
)
local need_to_regen_flow_field = true
tower.type = tower_type
if tower_type == TOWER_TYPE.REDEYE then
tower.range = 7
tower.last_shot_time = tower.TOB
tower.target_index = false
tower.cost = get_tower_cost(tower_type)
tower.range = get_tower_range(tower_type)
tower.last_shot_time = tower.TOB -- a tower has never shot if its TOB == its last_shot_time
state.map[hex.x][hex.y].elevation = 2
for k,v in pairs(TOWER_SPECS[tower_type].props) do
tower[k] = v
end
if tower_type == TOWER_TYPE.REDEYE then
elseif tower_type == TOWER_TYPE.LIGHTHOUSE then
tower.range = 5
tower.perimeter = ring_map(tower.hex, tower.range)
--[[
tower.node:append(
am.particles2d{
source_pos = vec2(0, 12),
source_pos_var = vec2(2),
start_size = 1,
start_size_var = 1,
end_size = 1,
end_size_var = 1,
angle = 0,
angle_var = math.pi,
speed = 1,
speed_var = 2,
life = 10,
life_var = 1,
start_color = COLORS.WHITE,
start_color_var = vec4(0.1, 0.1, 0.1, 1),
end_color = COLORS.SUNRAY,
end_color_var = vec4(0.1),
emission_rate = 4,
start_particles = 4,
max_particles = 200
}
)
]]
-- @HACK
need_to_regen_flow_field = false
elseif tower_type == TOWER_TYPE.WALL then
state.map[hex.x][hex.y].elevation = 1
elseif tower_type == TOWER_TYPE.MOAT then
state.map[hex.x][hex.y].elevation = -1
end
if need_to_regen_flow_field then
generate_and_apply_flow_field(state.map, HEX_GRID_CENTER, state.world)
elseif tower_type == TOWER_TYPE.RADAR then
end
generate_and_apply_flow_field(state.map, HEX_GRID_CENTER, state.world)
register_entity(TOWERS, tower)
end
function build_tower(hex, tower_type)
update_money(-get_tower_base_cost(tower_type))
make_and_register_tower(hex, tower_type)
vplay_sfx(SOUNDS.EXPLOSION4)
end

49
texture.lua

@ -1,24 +1,37 @@
local function load_texture(filepath)
local status, texture = pcall(am.texture2d, filepath)
if status then
return texture
else
return am.texture2d("res/bagel.jpg")
end
end
TEXTURES = {
LOGO = am.texture2d("res/logo.png"),
BUTTON1 = am.texture2d("res/button1.png"),
WIDER_BUTTON1 = am.texture2d("res/wider_button1.png"),
TAB_ICON = am.texture2d("res/tab_icon.png"),
SATELLITE = am.texture2d("res/satelite.png"),
TOWER_REDEYE = am.texture2d("res/tower_redeye.png"),
TOWER_LIGHTHOUSE = am.texture2d("res/tower_lighthouse.png"),
TOWER_WALL = am.texture2d("res/tower_wall.png"),
TOWER_MOAT = am.texture2d("res/tower_moat.png"),
TOWER_REDEYE_ICON = am.texture2d("res/tower_redeye_icon.png"),
TOWER_LIGHTHOUSE_ICON = am.texture2d("res/tower_lighthouse_icon.png"),
TOWER_WALL_ICON = am.texture2d("res/tower_wall_icon.png"),
TOWER_MOAT_ICON = am.texture2d("res/tower_moat_icon.png"),
MOB_BEEPER = am.texture2d("res/mob_beeper.png"),
LOGO = load_texture("res/logo.png"),
BUTTON1 = load_texture("res/button1.png"),
WIDER_BUTTON1 = load_texture("res/wider_button1.png"),
TAB_ICON = load_texture("res/tab_icon.png"),
-- tower stuff
TOWER_WALL = load_texture("res/tower_wall.png"),
TOWER_WALL_ICON = load_texture("res/tower_wall_icon.png"),
TOWER_HOWITZER = load_texture("res/tower_howitzer.png"),
TOWER_HOWITZER_ICON = load_texture("res/tower_howitzer_icon.png"),
TOWER_REDEYE = load_texture("res/tower_redeye.png"),
TOWER_REDEYE_ICON = load_texture("res/tower_redeye_icon.png"),
TOWER_MOAT = load_texture("res/tower_moat.png"),
TOWER_MOAT_ICON = load_texture("res/tower_moat_icon.png"),
TOWER_RADAR = load_texture("res/tower_radar.png"),
TOWER_RADAR_ICON = load_texture("res/tower_radar_icon.png"),
TOWER_LIGHTHOUSE = load_texture("res/tower_lighthouse.png"),
TOWER_LIGHTHOUSE_ICON = load_texture("res/tower_lighthouse_icon.png"),
-- mob stuff
MOB_BEEPER = load_texture("res/mob_beeper.png"),
}
function pack_texture_into_sprite(texture, width, height)

Loading…
Cancel
Save