Browse Source

hack

master
Nicholas Hayashi 4 years ago
parent
commit
9b0b7df866
  1. 2
      color.lua
  2. 5
      src/entity.lua
  3. 11
      src/game.lua
  4. 2
      src/grid.lua
  5. 42
      src/mob.lua
  6. 106
      src/projectile.lua
  7. 117
      src/tower.lua

2
color.lua

@ -12,7 +12,7 @@ COLORS = {
-- non-standard hues -- non-standard hues
WATER = vec4(0.12, 0.25, 0.3, 1), WATER = vec4(0.12, 0.25, 0.3, 1),
GRASS = vec4(0.10, 0.25, 0.10, 1),
GRASS = vec4(0.05, 0.22, 0.11, 1),
DIRT = vec4(0.22, 0.20, 0.10, 1), DIRT = vec4(0.22, 0.20, 0.10, 1),
MOUNTAIN = vec4(0.95, 0.30, 0.20, 1), MOUNTAIN = vec4(0.95, 0.30, 0.20, 1),

5
src/entity.lua

@ -9,6 +9,9 @@ entity structure:
update - function - runs every frame with itself and its index in some array 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
type - enum - sub type - unset if 'basic' entity
props - table - table of properties specific to this entity subtype
... - 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
} }
--]] --]]
@ -31,6 +34,8 @@ function make_basic_entity(hex, node, update, position)
entity.update = update entity.update = update
entity.node = am.translate(entity.position) ^ node entity.node = am.translate(entity.position) ^ node
entity.type = false
entity.props = {}
return entity return entity
end end

11
src/game.lua

@ -84,7 +84,7 @@ function select_toolbelt_button(i)
end end
function do_day_night_cycle() function do_day_night_cycle()
local tstep = (math.sin(state.time / 100) + 1) / state.perf.avg_fps
local tstep = (math.sin(state.time / 100) + 1) * am.delta_time
state.world"negative_mask".color = vec4(tstep){a=1} state.world"negative_mask".color = vec4(tstep){a=1}
end end
@ -92,7 +92,10 @@ local function game_pause()
WIN.scene"game".paused = true WIN.scene"game".paused = true
WIN.scene"game":append(am.group{ WIN.scene"game":append(am.group{
am.rect(WIN.left, WIN.bottom, WIN.right, WIN.top, COLORS.TRANSPARENT), am.rect(WIN.left, WIN.bottom, WIN.right, WIN.top, COLORS.TRANSPARENT),
am.scale(3) ^ am.text("Paused.\nEscape to Resume\nf4 to start a new game", COLORS.BLACK)
am.scale(3)
^ am.text(string.format(
"Paused.\nSeed: %d\nEscape to Resume\nf4 to start a new game", state.map.seed
), COLORS.BLACK)
} }
:tag"pause_menu") :tag"pause_menu")
WIN.scene:action(function() WIN.scene:action(function()
@ -286,6 +289,7 @@ local function make_game_toolbelt()
toolbelt:append(tower_select_square) toolbelt:append(tower_select_square)
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', '-', '=' }
-- order of this array is the order of towers on the toolbelt. -- order of this array is the order of towers on the toolbelt.
local tower_type_values = { local tower_type_values = {
TOWER_TYPE.WALL, TOWER_TYPE.WALL,
@ -392,6 +396,9 @@ function game_init()
state = get_initial_game_state() state = get_initial_game_state()
build_tower(HEX_GRID_CENTER, TOWER_TYPE.RADAR) 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
WIN.scene:remove("game") WIN.scene:remove("game")
WIN.scene:append(game_scene()) WIN.scene:append(game_scene())
end end

2
src/grid.lua

@ -174,7 +174,7 @@ function random_map(seed)
noise = noise * d^0.125 -- arbitrary, seems to work good noise = noise * d^0.125 -- arbitrary, seems to work good
end end
-- light shading on edge cells @TODO replace this with a skylight, that can move
-- light shading on edge cells
local mask = vec4(0, 0, 0, math.max(((evenq.x - HEX_GRID_WIDTH/2) / HEX_GRID_WIDTH) ^ 2 local mask = vec4(0, 0, 0, math.max(((evenq.x - HEX_GRID_WIDTH/2) / HEX_GRID_WIDTH) ^ 2
, ((-evenq.y - HEX_GRID_HEIGHT/2) / HEX_GRID_HEIGHT) ^ 2)) , ((-evenq.y - HEX_GRID_HEIGHT/2) / HEX_GRID_HEIGHT) ^ 2))
local color = color_at(noise) - mask local color = color_at(noise) - mask

42
src/mob.lua

@ -2,9 +2,26 @@
MOBS = {} MOBS = {}
MOB_TYPE = {
BEEPER = 1,
}
MAX_MOB_SIZE = hex_height(HEX_SIZE, ORIENTATION.FLAT) / 2 MAX_MOB_SIZE = hex_height(HEX_SIZE, ORIENTATION.FLAT) / 2
MOB_SIZE = MAX_MOB_SIZE MOB_SIZE = MAX_MOB_SIZE
MOB_SPECS = {
[MOB_TYPE.BEEPER] = {
health = 10,
speed = 10,
bounty = 5,
hurtbox_radius = MOB_SIZE/2
}
}
function get_mob_spec(mob_type)
return MOB_SPECS[mob_type]
end
function mobs_on_hex(hex) function mobs_on_hex(hex)
local t = {} local t = {}
for mob_index,mob in pairs(MOBS) do for mob_index,mob in pairs(MOBS) do
@ -15,8 +32,8 @@ function mobs_on_hex(hex)
return t return t
end end
-- @NOTE returns i,v in the table
function mob_on_hex(hex) function mob_on_hex(hex)
-- table.find returns i,v in the table
return table.find(MOBS, function(mob) return table.find(MOBS, function(mob)
return mob and mob.hex == hex return mob and mob.hex == hex
end) end)
@ -134,7 +151,7 @@ local function update_mob(mob, mob_index)
end end
if mob.frame_target and mob.frame_target == last_frame_hex then if mob.frame_target and mob.frame_target == last_frame_hex then
--log('backpedaling')
log('backpedaling')
end end
-- do movement -- do movement
@ -143,9 +160,7 @@ local function update_mob(mob, mob_index)
-- or between when we last calculated this target and now -- or between when we last calculated this target and now
-- check for that now -- check for that now
if mob_can_pass_through(mob, mob.frame_target) then 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
local rate = 4 * mob.speed * am.delta_time
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
@ -171,21 +186,24 @@ local function make_and_register_mob(mob_type)
update_mob update_mob
) )
--mob.path = get_mob_path(mob, HEX_MAP, mob.hex, HEX_GRID_CENTER)
mob.health = 10
mob.speed = 10
mob.bounty = 5
mob.hurtbox_radius = MOB_SIZE/2
mob.type = mob_type
local spec = get_mob_spec(mob_type)
mob.health = spec.health
mob.speed = spec.speed
mob.bounty = spec.bounty
mob.hurtbox_radius = spec.hurtbox_radius
register_entity(MOBS, mob) register_entity(MOBS, mob)
return mob
end end
local SPAWN_CHANCE = 45
local SPAWN_CHANCE = 25
function do_mob_spawning() function do_mob_spawning()
--if WIN:key_pressed"space" then --if WIN:key_pressed"space" then
if math.random(SPAWN_CHANCE) == 1 then if math.random(SPAWN_CHANCE) == 1 then
--if #MOBS < 1 then --if #MOBS < 1 then
make_and_register_mob()
make_and_register_mob(MOB_TYPE.BEEPER)
end end
end end

106
src/projectile.lua

@ -2,7 +2,77 @@
PROJECTILES = {} PROJECTILES = {}
local function update_projectile(projectile, projectile_index)
PROJECTILE_TYPE = {
SHELL = 1,
LASER = 2,
}
PROJECTILE_SPECS = {
[PROJECTILE_TYPE.SHELL] = {
velocity = 13,
damage = 20,
hitbox_radius = 3
},
[PROJECTILE_TYPE.LASER] = {
velocity = 25,
damage = 5,
hitbox_radius = 10
},
}
function get_projectile_velocity(projectile_type)
return PROJECTILE_SPECS[projectile_type].velocity
end
function get_projectile_damage(projectile_type)
return PROJECTILE_SPECS[projectile_type].damage
end
function get_projectile_hitbox_radius(projectile_type)
return PROJECTILE_SPECS[projectile_type].hitbox_radius
end
function get_projectile_spec(projectile_type)
return PROJECTILE_SPECS[projectile_type]
end
local function update_projectile_shell(projectile, projectile_index)
projectile.position = projectile.position + projectile.vector * projectile.velocity
projectile.node.position2d = projectile.position
projectile.hex = pixel_to_hex(projectile.position)
projectile.props.z = projectile.props.z - 0.6 * am.delta_time
if projectile.props.z <= 0 then
log('exploded cuz we hit da grund')
delete_entity(PROJECTILES, projectile_index)
return true
end
-- check if we hit something
-- get a list of hexes that could have something we could hit on them
-- right now, it's just the hex we're on and all of its neighbours.
-- this is done to avoid having to check every mob on screen, though maybe it's not necessary.
local do_explode = false
local search_hexes = spiral_map(projectile.hex, 1)
for _,hex in pairs(search_hexes) do
for mob_index,mob in pairs(mobs_on_hex(hex)) do
if mob and circles_intersect(mob.position
, projectile.position
, mob.hurtbox_radius
, projectile.hitbox_radius) then
do_explode = true
break
end
end
end
if do_explode then
log('exploded cuz we hit a boi')
delete_entity(PROJECTILES, projectile_index)
return true
end
end
local function update_projectile_laser(projectile, projectile_index)
projectile.position = projectile.position + projectile.vector * projectile.velocity projectile.position = projectile.position + projectile.vector * projectile.velocity
projectile.node.position2d = projectile.position projectile.node.position2d = projectile.position
projectile.hex = pixel_to_hex(projectile.position) projectile.hex = pixel_to_hex(projectile.position)
@ -60,16 +130,38 @@ local function update_projectile(projectile, projectile_index)
vplay_sfx(SOUNDS.HIT1, 0.5) vplay_sfx(SOUNDS.HIT1, 0.5)
end end
function make_and_register_projectile(hex, vector, velocity, damage, hitbox_radius)
function make_projectile_node(projectile_type, vector)
if projectile_type == PROJECTILE_TYPE.LASER then
return am.line(vector, vector*get_projectile_hitbox_radius(projectile_type), 3, COLORS.CLARET)
elseif projectile_type == PROJECTILE_TYPE.SHELL then
return am.circle(vec2(0), 3, COLORS.VERY_DARK_GRAY)
end
end
function get_projectile_update_function(projectile_type)
if projectile_type == PROJECTILE_TYPE.LASER then
return update_projectile_laser
elseif projectile_type == PROJECTILE_TYPE.SHELL then
return update_projectile_shell
end
end
function make_and_register_projectile(hex, projectile_type, vector)
local projectile = make_basic_entity(hex local projectile = make_basic_entity(hex
, am.line(vector, vector*hitbox_radius, 3, COLORS.CLARET)
, update_projectile)
, make_projectile_node(projectile_type, vector)
, get_projectile_update_function(projectile_type))
projectile.type = projectile_type
projectile.vector = vector projectile.vector = vector
projectile.velocity = velocity
projectile.damage = damage
projectile.hitbox_radius = hitbox_radius
local spec = get_projectile_spec(projectile_type)
projectile.velocity = spec.velocity
projectile.damage = spec.damage
projectile.hitbox_radius = spec.hitbox_radius
register_entity(PROJECTILES, projectile) register_entity(PROJECTILES, projectile)
return projectile
end end
function delete_all_projectiles() function delete_all_projectiles()

117
src/tower.lua

@ -15,7 +15,7 @@ TOWER_TYPE = {
LIGHTHOUSE = 6 LIGHTHOUSE = 6
} }
local TOWER_SPECS = {
TOWER_SPECS = {
[TOWER_TYPE.WALL] = { [TOWER_TYPE.WALL] = {
name = "Wall", name = "Wall",
placement_rules_text = "Place on grass or dirt", placement_rules_text = "Place on grass or dirt",
@ -24,17 +24,17 @@ local TOWER_SPECS = {
icon_texture = TEXTURES.TOWER_WALL_ICON, icon_texture = TEXTURES.TOWER_WALL_ICON,
cost = 10, cost = 10,
range = 0, range = 0,
props = {}
fire_rate = 2,
}, },
[TOWER_TYPE.HOWITZER] = { [TOWER_TYPE.HOWITZER] = {
name = "HOWITZER", name = "HOWITZER",
placement_rules_text = "Place on non-Water", placement_rules_text = "Place on non-Water",
short_description = "Fires artillery. Range and cost increase with elevation of terrain underneath.", short_description = "Fires artillery. Range and cost increase with elevation of terrain underneath.",
texture = TEXTURES.TOWER_SHELL,
icon_texture = TEXTURES.TOWER_SHELL_ICON,
texture = TEXTURES.TOWER_HOWITZER,
icon_texture = TEXTURES.TOWER_HOWITZER_ICON,
cost = 20, cost = 20,
range = 10, range = 10,
props = {}
fire_rate = 4,
}, },
[TOWER_TYPE.REDEYE] = { [TOWER_TYPE.REDEYE] = {
name = "Redeye", name = "Redeye",
@ -44,7 +44,7 @@ local TOWER_SPECS = {
icon_texture = TEXTURES.TOWER_REDEYE_ICON, icon_texture = TEXTURES.TOWER_REDEYE_ICON,
cost = 20, cost = 20,
range = 12, range = 12,
props = {}
fire_rate = 1,
}, },
[TOWER_TYPE.MOAT] = { [TOWER_TYPE.MOAT] = {
name = "Moat", name = "Moat",
@ -54,7 +54,7 @@ local TOWER_SPECS = {
icon_texture = TEXTURES.TOWER_MOAT_ICON, icon_texture = TEXTURES.TOWER_MOAT_ICON,
cost = 10, cost = 10,
range = 0, range = 0,
props = {}
fire_rate = 2,
}, },
[TOWER_TYPE.RADAR] = { [TOWER_TYPE.RADAR] = {
name = "Radar", name = "Radar",
@ -64,7 +64,7 @@ local TOWER_SPECS = {
icon_texture = TEXTURES.TOWER_RADAR_ICON, icon_texture = TEXTURES.TOWER_RADAR_ICON,
cost = 20, cost = 20,
range = 0, range = 0,
props = {}
fire_rate = 1,
}, },
[TOWER_TYPE.LIGHTHOUSE] = { [TOWER_TYPE.LIGHTHOUSE] = {
name = "Lighthouse", name = "Lighthouse",
@ -74,7 +74,7 @@ local TOWER_SPECS = {
icon_texture = TEXTURES.TOWER_LIGHTHOUSE_ICON, icon_texture = TEXTURES.TOWER_LIGHTHOUSE_ICON,
cost = 20, cost = 20,
range = 8, range = 8,
props = {}
fire_rate = 1,
}, },
} }
@ -102,6 +102,9 @@ end
function get_tower_range(tower_type) function get_tower_range(tower_type)
return TOWER_SPECS[tower_type].range return TOWER_SPECS[tower_type].range
end end
function get_tower_fire_rate(tower_type)
return TOWER_SPECS[tower_type].fire_rate
end
local function make_tower_sprite(tower_type) local function make_tower_sprite(tower_type)
return pack_texture_into_sprite(get_tower_texture(tower_type), HEX_PIXEL_WIDTH, HEX_PIXEL_HEIGHT) return pack_texture_into_sprite(get_tower_texture(tower_type), HEX_PIXEL_WIDTH, HEX_PIXEL_HEIGHT)
@ -143,6 +146,9 @@ local function make_tower_node(tower_type)
if tower_type == TOWER_TYPE.REDEYE then if tower_type == TOWER_TYPE.REDEYE then
return make_tower_sprite(tower_type) return make_tower_sprite(tower_type)
elseif tower_type == TOWER_TYPE.HOWITZER then
return make_tower_sprite(tower_type)
elseif tower_type == TOWER_TYPE.LIGHTHOUSE then elseif tower_type == TOWER_TYPE.LIGHTHOUSE then
return am.group( return am.group(
make_tower_sprite(tower_type), make_tower_sprite(tower_type),
@ -165,7 +171,8 @@ local function make_tower_node(tower_type)
end_color_var = vec4(0.1), end_color_var = vec4(0.1),
emission_rate = 4, emission_rate = 4,
start_particles = 4, start_particles = 4,
max_particles = 200
max_particles = 200,
warmup_time = 5
} }
) )
elseif tower_type == TOWER_TYPE.WALL then elseif tower_type == TOWER_TYPE.WALL then
@ -183,6 +190,9 @@ local function get_tower_update_function(tower_type)
if tower_type == TOWER_TYPE.REDEYE then if tower_type == TOWER_TYPE.REDEYE then
return update_tower_redeye return update_tower_redeye
elseif tower_type == TOWER_TYPE.HOWITZER then
return update_tower_howitzer
elseif tower_type == TOWER_TYPE.LIGHTHOUSE then elseif tower_type == TOWER_TYPE.LIGHTHOUSE then
return update_tower_lighthouse return update_tower_lighthouse
end end
@ -213,7 +223,19 @@ function tower_type_is_buildable_on(hex, tile, tower_type)
local blocked = mobs_blocking or towers_blocking local blocked = mobs_blocking or towers_blocking
if tower_type == TOWER_TYPE.REDEYE then
if tower_type == TOWER_TYPE.HOWITZER then
if not mobs_blocking and towers_blocking then
blocked = false
for _,tower in pairs(TOWERS) do
if tower.type ~= TOWER_TYPE.WALL then
blocked = true
break
end
end
end
return not blocked and tile.elevation >= -0.5
elseif tower_type == TOWER_TYPE.REDEYE then
if not mobs_blocking and towers_blocking then if not mobs_blocking and towers_blocking then
blocked = false blocked = false
for _,tower in pairs(TOWERS) do for _,tower in pairs(TOWERS) do
@ -249,7 +271,7 @@ function update_tower_redeye(tower, tower_index)
if not tower.target_index then if not tower.target_index then
for index,mob in pairs(MOBS) do for index,mob in pairs(MOBS) do
if mob then if mob then
local d = math.distance(mob.position, tower.position) / (HEX_SIZE * 2)
local d = math.distance(mob.hex, tower.hex)
if d <= tower.range then if d <= tower.range then
tower.target_index = index tower.target_index = index
break break
@ -260,15 +282,13 @@ function update_tower_redeye(tower, tower_index)
if MOBS[tower.target_index] == false then if MOBS[tower.target_index] == false then
tower.target_index = false tower.target_index = false
elseif (state.time - tower.last_shot_time) > 1 then
elseif (state.time - tower.last_shot_time) > tower.fire_rate then
local mob = MOBS[tower.target_index] local mob = MOBS[tower.target_index]
make_and_register_projectile( make_and_register_projectile(
tower.hex, tower.hex,
math.normalize(hex_to_pixel(mob.hex) - tower.position),
15,
5,
10
PROJECTILE_TYPE.LASER,
math.normalize(mob.position - tower.position)
) )
tower.last_shot_time = state.time tower.last_shot_time = state.time
@ -277,6 +297,38 @@ function update_tower_redeye(tower, tower_index)
end end
end end
function update_tower_howitzer(tower, tower_index)
if not tower.target_index then
for index,mob in pairs(MOBS) do
if mob then
local d = math.distance(mob.hex, tower.hex)
if d <= tower.range then
tower.target_index = index
break
end
end
end
else
if MOBS[tower.target_index] == false then
tower.target_index = false
elseif (state.time - tower.last_shot_time) > tower.fire_rate then
local mob = MOBS[tower.target_index]
local projectile = make_and_register_projectile(
tower.hex,
PROJECTILE_TYPE.SHELL,
math.normalize(mob.position - tower.position)
)
projectile.props.z = tower.props.z
tower.last_shot_time = state.time
play_sfx(SOUNDS.EXPLOSION2)
end
end
end
function update_tower_lighthouse(tower, tower_index) function update_tower_lighthouse(tower, tower_index)
-- check if there's a mob on a hex in our perimeter -- check if there's a mob on a hex in our perimeter
for _,h in pairs(tower.perimeter) do for _,h in pairs(tower.perimeter) do
@ -293,6 +345,7 @@ function update_tower_lighthouse(tower, tower_index)
if made_it then if made_it then
m.path = path m.path = path
--[[
local area = spiral_map(tower.hex, tower.range) local area = spiral_map(tower.hex, tower.range)
for _,h in pairs(area) do for _,h in pairs(area) do
local node = state.map[h.x][h.y].node"circle" local node = state.map[h.x][h.y].node"circle"
@ -305,6 +358,7 @@ function update_tower_lighthouse(tower, tower_index)
am.tween(node, 0.3, { color = initial_color }) am.tween(node, 0.3, { color = initial_color })
}) })
end end
]]
end end
end end
end end
@ -319,30 +373,39 @@ function make_and_register_tower(hex, tower_type)
) )
tower.type = tower_type tower.type = tower_type
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
for k,v in pairs(TOWER_SPECS[tower_type].props) do
tower[k] = v
end
local spec = get_tower_spec(tower_type)
tower.cost = spec.cost
tower.range = spec.range
tower.fire_rate = spec.fire_rate
tower.last_shot_time = -spec.fire_rate
if tower_type == TOWER_TYPE.REDEYE then if tower_type == TOWER_TYPE.REDEYE then
local tile = state.map.get(hex.x, hex.y)
tile.elevation = tile.elevation + 0.6
elseif tower_type == TOWER_TYPE.HOWITZER then
local tile = state.map.get(hex.x, hex.y)
tile.elevation = tile.elevation + 0.6
tower.props.z = tile.elevation
elseif tower_type == TOWER_TYPE.LIGHTHOUSE then elseif tower_type == TOWER_TYPE.LIGHTHOUSE then
tower.perimeter = ring_map(tower.hex, tower.range) tower.perimeter = ring_map(tower.hex, tower.range)
local tile = state.map.get(hex.x, hex.y) local tile = state.map.get(hex.x, hex.y)
tile.elevation = tile.elevation + 0.5
tile.elevation = tile.elevation + 0.6
elseif tower_type == TOWER_TYPE.WALL then elseif tower_type == TOWER_TYPE.WALL then
state.map.get(hex.x, hex.y).elevation = 0.5
state.map.get(hex.x, hex.y).elevation = 0.6
elseif tower_type == TOWER_TYPE.MOAT then elseif tower_type == TOWER_TYPE.MOAT then
state.map.get(hex.x, hex.y).elevation = -0.49
state.map.get(hex.x, hex.y).elevation = -0.6
elseif tower_type == TOWER_TYPE.RADAR then elseif tower_type == TOWER_TYPE.RADAR then
local tile = state.map.get(hex.x, hex.y)
tile.elevation = tile.elevation + 0.6
end end
register_entity(TOWERS, tower)
return register_entity(TOWERS, tower)
end end
function build_tower(hex, tower_type) function build_tower(hex, tower_type)

Loading…
Cancel
Save