Nicholas Hayashi
4 years ago
14 changed files with 492 additions and 248 deletions
-
35NOTES.md
-
18color.lua
-
27main.lua
-
BINres/moat1.png
-
BINres/wall_closed.png
-
118src/entity.lua
-
3src/extra.lua
-
13src/geometry.lua
-
39src/grid.lua
-
107src/hexyz.lua
-
161src/mob.lua
-
92src/projectile.lua
-
126src/tower.lua
-
1texture.lua
@ -0,0 +1,35 @@ |
|||
|
|||
|
|||
todoooos & notes |
|||
|
|||
@TODO test optimizing pathfinding via breadth first search/djikstra |
|||
|
|||
i think i want more or less no such thing as 'impassable' terrain |
|||
|
|||
all mobs always can always traverse everything, though they initially will give the impression that certain tiles are impassable by prefering certain tiles. |
|||
|
|||
the illusion is likely to be broken when you attempt to fully wall-off an area, and mobs begin deciding to climb over mountains or swim through lakes. this will come at great cost to them, but they will be capable of it |
|||
|
|||
MAP RESOURCES |
|||
- spawn diamonds or special floating resources that give you bonuses for building on, whether it's score, money, or boosting the effectiveness of the tower you place on top, etc. |
|||
- killing certain mobs may cause these resources to spawn on the hex they died on |
|||
|
|||
|
|||
towers: |
|||
0 - wall |
|||
some fraction of the height of the tallest mountain |
|||
makes mob pathing more difficult |
|||
|
|||
upgrades: |
|||
- +height - making the tower taller makes it more difficult/costly for mobs to climb over it |
|||
- spikes - mobs take damage when climbing |
|||
|
|||
1 - moat |
|||
some fraction of the depth of the deepest lake |
|||
makes mob pathing more difficult |
|||
|
|||
upgrades: |
|||
- +depth - making the moat deeper makes it more difficult/costly for mobs to swim through it |
|||
- alligators - mobs take damage while swimming |
|||
|
|||
|
After Width: 137 | Height: 137 | Size: 1.8 KiB |
Before Width: 138 | Height: 138 | Size: 2.0 KiB After Width: 100 | Height: 88 | Size: 1.9 KiB |
@ -1,77 +1,81 @@ |
|||
|
|||
|
|||
ENTITY_TYPE = { |
|||
ENTITY = 0, |
|||
MOB = 1, |
|||
TOWER = 2, |
|||
PROJECTILE = 3 |
|||
} |
|||
MOBS = {} |
|||
TOWERS = {} |
|||
PROJECTILES = {} |
|||
|
|||
ENTITIES = {} |
|||
-- entity structure: |
|||
-- { |
|||
-- TOB - number - time of birth, const |
|||
-- hex - vec2 - current occupied hex, if any |
|||
-- 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 |
|||
-- node - node - scene graph node |
|||
-- } |
|||
-- |
|||
-- mob(entity) structure: |
|||
-- { |
|||
-- path - 2d table - map of hexes to other hexes, forms a path |
|||
-- speed - number - multiplier on distance travelled per frame, up to the update function to use correctly |
|||
-- bounty - number - score bonus you get when this mob is killed |
|||
-- hurtbox_radius - number - |
|||
-- } |
|||
-- |
|||
-- tower(entity) structure: |
|||
-- { |
|||
-- -- @NOTE these should probably be wrapped in a 'weapon' struct or something, so towers can have multiple weapons |
|||
-- range - number - distance it can shoot |
|||
-- last_shot_time - number - timestamp (seconds) of last time it shot |
|||
-- target_index - number - index of entity it is currently shooting |
|||
-- } |
|||
-- |
|||
-- bullet/projectile structure |
|||
-- { |
|||
-- vector - vec2 - normalized vector of the current direction of this projectile |
|||
-- velocity - number - multplier on distance travelled per frame |
|||
-- damage - number - guess |
|||
-- hitbox_radius - number - hitboxes are circles |
|||
-- } |
|||
-- |
|||
function make_and_register_entity(type_, hex, node, update) |
|||
--[[ |
|||
entity structure: |
|||
{ |
|||
TOB - number - time of birth, const |
|||
hex - vec2 - current occupied hex, if any |
|||
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 |
|||
node - node - scene graph node |
|||
} |
|||
--]] |
|||
function make_basic_entity(hex, node, update, position) |
|||
local entity = {} |
|||
|
|||
entity.type = type_ |
|||
entity.TOB = TIME |
|||
entity.hex = hex |
|||
entity.position = hex_to_pixel(hex) |
|||
entity.update = update or function() log("unimplemented update function!") end |
|||
|
|||
-- 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 |
|||
-- pixel position instead |
|||
if position then |
|||
entity.position = position |
|||
entity.hex = pixel_to_hex(entity.position) |
|||
else |
|||
entity.hex = hex |
|||
entity.position = hex_to_pixel(hex) |
|||
end |
|||
|
|||
entity.update = update |
|||
entity.node = am.translate(entity.position) ^ node |
|||
|
|||
table.insert(ENTITIES, entity) |
|||
WORLD:append(entity.node) |
|||
return entity |
|||
end |
|||
|
|||
function delete_all_entities() |
|||
for index,entity in pairs(ENTITIES) do |
|||
delete_entity(index) |
|||
end |
|||
function register_entity(t, entity) |
|||
table.insert(t, entity) |
|||
WORLD:append(entity.node) |
|||
end |
|||
|
|||
-- |t| is the source table, probably MOBS, TOWERS, or PROJECTILES |
|||
function delete_entity(t, index) |
|||
if not t then log("splat!") end |
|||
|
|||
ENTITIES = {} |
|||
WORLD:remove(t[index].node) |
|||
t[index] = false -- leave empty indexes so other entities can learn that this entity was deleted |
|||
end |
|||
|
|||
function delete_entity(index) |
|||
WORLD:remove(ENTITIES[index].node) |
|||
ENTITIES[index] = nil -- leave empty indexes so other entities can learn that this entity was deleted |
|||
function delete_all_entities() |
|||
for mob_index,mob in pairs(MOBS) do |
|||
delete_entity(MOBS, mob_index) |
|||
end |
|||
for tower_index,tower in pairs(TOWERS) do |
|||
delete_entity(TOWERS, tower_index) |
|||
end |
|||
for projectile_index,projectile in pairs(PROJECTILES) do |
|||
delete_entity(PROJECTILES, projectile_index) |
|||
end |
|||
end |
|||
|
|||
function do_entity_updates() |
|||
for index,entity in pairs(ENTITIES) do |
|||
entity.update(entity, index) |
|||
for mob_index,mob in pairs(MOBS) do |
|||
if mob and mob.update then |
|||
mob.update(mob, mob_index) |
|||
end |
|||
end |
|||
for tower_index,tower in pairs(TOWERS) do |
|||
if tower and tower.update then |
|||
tower.update(tower, tower_index) |
|||
end |
|||
end |
|||
for projectile_index,projectile in pairs(PROJECTILES) do |
|||
if projectile and projectile.update then |
|||
projectile.update(projectile, projectile_index) |
|||
end |
|||
end |
|||
end |
|||
|
@ -1,45 +1,81 @@ |
|||
|
|||
|
|||
function make_and_register_projectile(hex, vector, velocity, damage, hitbox_radius) |
|||
local projectile = make_and_register_entity( |
|||
-- type |
|||
ENTITY_TYPE.PROJECTILE, |
|||
--[[ |
|||
bullet/projectile(entity) structure |
|||
{ |
|||
vector - vec2 - normalized vector of the current direction of this projectile |
|||
velocity - number - multplier on distance travelled per frame |
|||
damage - number - guess |
|||
hitbox_radius - number - hitboxes are circles |
|||
} |
|||
--]] |
|||
|
|||
hex, |
|||
function projectile_update(projectile, projectile_index) |
|||
projectile.position = projectile.position + projectile.vector * projectile.velocity |
|||
projectile.node.position2d = projectile.position |
|||
projectile.hex = pixel_to_hex(projectile.position) |
|||
|
|||
-- node |
|||
am.circle(vec2(0), hitbox_radius - 1, COLORS.CLARET), |
|||
-- check if we're out of bounds |
|||
if not point_in_rect(projectile.position + WORLDSPACE_COORDINATE_OFFSET, { |
|||
x1 = WIN.left, |
|||
y1 = WIN.bottom, |
|||
x2 = WIN.right, |
|||
y2 = WIN.top |
|||
}) then |
|||
delete_entity(PROJECTILES, projectile_index) |
|||
return true |
|||
end |
|||
|
|||
-- update function |
|||
function(_projectile, _projectile_index) |
|||
_projectile.position = _projectile.position + vector * velocity |
|||
_projectile.node.position2d = _projectile.position |
|||
_projectile.hex = pixel_to_hex(_projectile.position) |
|||
-- check if we hit something |
|||
-- get a list of hexes that could have something we could hit on them |
|||
local search_hexes = spiral_map(projectile.hex, 1) |
|||
local hit_mob_count = 0 |
|||
local hit_mobs = {} |
|||
for _,hex in pairs(search_hexes) do |
|||
|
|||
local mob_index,mob = mob_on_hex(_projectile.hex) |
|||
-- check if there's a mob on the hex |
|||
for mob_index,mob in pairs(mobs_on_hex(hex)) do |
|||
if mob and circles_intersect(mob.position |
|||
, _projectile.position |
|||
, projectile.position |
|||
, mob.hurtbox_radius |
|||
, _projectile.hitbox_radius) then |
|||
|
|||
do_hit_mob(mob, _projectile.damage, mob_index) |
|||
delete_entity(_projectile_index) |
|||
WORLD:action(vplay_sound(SOUNDS.HIT1)) |
|||
|
|||
elseif not point_in_rect(_projectile.position + WORLDSPACE_COORDINATE_OFFSET, { |
|||
x1 = WIN.left, |
|||
y1 = WIN.bottom, |
|||
x2 = WIN.right, |
|||
y2 = WIN.top |
|||
}) then |
|||
delete_entity(_projectile_index) |
|||
, projectile.hitbox_radius) then |
|||
table.insert(hit_mobs, mob_index, mob) |
|||
hit_mob_count = hit_mob_count + 1 |
|||
end |
|||
end |
|||
) |
|||
end |
|||
|
|||
-- we didn't hit anyone |
|||
if hit_mob_count == 0 then return end |
|||
|
|||
-- we could have hit multiple, (optionally) find the closest |
|||
local closest_mob_index, closest_mob = next(hit_mobs, nil) |
|||
local closest_d = math.distance(closest_mob.position, projectile.position) |
|||
for _mob_index,mob in pairs(hit_mobs) do |
|||
local d = math.distance(mob.position, projectile.position) |
|||
if d < closest_d then |
|||
closest_mob_index = _mob_index |
|||
closest_mob = mob |
|||
closest_d = d |
|||
end |
|||
end |
|||
|
|||
-- hit the mob, delete ourselves, affect the world |
|||
do_hit_mob(closest_mob, projectile.damage, closest_mob_index) |
|||
delete_entity(PROJECTILES, projectile_index) |
|||
WORLD:action(vplay_sound(SOUNDS.HIT1)) |
|||
end |
|||
|
|||
function make_and_register_projectile(hex, vector, velocity, damage, hitbox_radius) |
|||
local projectile = make_basic_entity(hex |
|||
, am.line(vector, vector*hitbox_radius, 3, COLORS.CLARET) |
|||
, projectile_update) |
|||
|
|||
projectile.vector = vector |
|||
projectile.velocity = velocity |
|||
projectile.damage = damage |
|||
projectile.hitbox_radius = hitbox_radius |
|||
|
|||
register_entity(PROJECTILES, projectile) |
|||
end |
|||
|
Write
Preview
Loading…
Cancel
Save
Reference in new issue