Day 2
parent
015afe0459
commit
30b10c0bd4
37
arena.lua
37
arena.lua
|
@ -8,13 +8,18 @@ function Arena:init(name)
|
||||||
self.effects = Group()
|
self.effects = Group()
|
||||||
self.ui = Group():no_camera()
|
self.ui = Group():no_camera()
|
||||||
self.main:disable_collision_between('player', 'player')
|
self.main:disable_collision_between('player', 'player')
|
||||||
self.main:disable_collision_between('player', 'enemy')
|
|
||||||
self.main:disable_collision_between('enemy', 'enemy')
|
self.enemies = {Seeker}
|
||||||
self.main:enable_trigger_between('player', 'enemy')
|
|
||||||
|
|
||||||
self.x1, self.y1 = gw/2 - 0.8*gw/2, gh/2 - 0.8*gh/2
|
self.x1, self.y1 = gw/2 - 0.8*gw/2, gh/2 - 0.8*gh/2
|
||||||
self.x2, self.y2 = gw/2 + 0.8*gw/2, gh/2 + 0.8*gh/2
|
self.x2, self.y2 = gw/2 + 0.8*gw/2, gh/2 + 0.8*gh/2
|
||||||
self.spawn_points = {{x = self.x1 + 32, y = self.y1 + 32}, {x = self.x1 + 32, y = self.y2 - 32}, {x = self.x2 - 32, y = self.y1 + 32}, {x = self.x2 - 32, y = self.y2 - 32}, {x = gw/2, y = gh/2}}
|
self.spawn_points = {
|
||||||
|
{x = self.x1 + 32, y = self.y1 + 32, r = math.pi/4},
|
||||||
|
{x = self.x1 + 32, y = self.y2 - 32, r = -math.pi/4},
|
||||||
|
{x = self.x2 - 32, y = self.y1 + 32, r = 3*math.pi/4},
|
||||||
|
{x = self.x2 - 32, y = self.y2 - 32, r = -3*math.pi/4},
|
||||||
|
{x = gw/2, y = gh/2, r = random:float(0, 2*math.pi)}
|
||||||
|
}
|
||||||
self.spawn_offsets = {{x = -12, y = -12}, {x = 12, y = -12}, {x = 12, y = 12}, {x = -12, y = 12}, {x = 0, y = 0}}
|
self.spawn_offsets = {{x = -12, y = -12}, {x = 12, y = -12}, {x = 12, y = 12}, {x = -12, y = 12}, {x = 0, y = 0}}
|
||||||
|
|
||||||
Wall{group = self.main, vertices = math.to_rectangle_vertices(-40, -40, self.x1, gh + 40), color = bg[-1]}
|
Wall{group = self.main, vertices = math.to_rectangle_vertices(-40, -40, self.x1, gh + 40), color = bg[-1]}
|
||||||
|
@ -22,8 +27,13 @@ function Arena:init(name)
|
||||||
Wall{group = self.main, vertices = math.to_rectangle_vertices(self.x1, -40, self.x2, self.y1), color = bg[-1]}
|
Wall{group = self.main, vertices = math.to_rectangle_vertices(self.x1, -40, self.x2, self.y1), color = bg[-1]}
|
||||||
Wall{group = self.main, vertices = math.to_rectangle_vertices(self.x1, self.y2, self.x2, gh + 40), color = bg[-1]}
|
Wall{group = self.main, vertices = math.to_rectangle_vertices(self.x1, self.y2, self.x2, gh + 40), color = bg[-1]}
|
||||||
|
|
||||||
self.player = Unit{group = self.main, x = gw/2, y = gh/2, player = true, leader = true, character = 'vagrant'}
|
self.player = Player{group = self.main, x = gw/2, y = gh/2, leader = true, character = 'vagrant'}
|
||||||
-- self.player:add_follower(Unit{group = self.main, player = true, character = 'seeker'})
|
self.player:add_follower(Player{group = self.main, character = 'vagrant'})
|
||||||
|
self.player:add_follower(Player{group = self.main, character = 'vagrant'})
|
||||||
|
self.player:add_follower(Player{group = self.main, character = 'vagrant'})
|
||||||
|
self.player:add_follower(Player{group = self.main, character = 'vagrant'})
|
||||||
|
self.player:add_follower(Player{group = self.main, character = 'vagrant'})
|
||||||
|
self.player:add_follower(Player{group = self.main, character = 'vagrant'})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@ -39,7 +49,7 @@ function Arena:update(dt)
|
||||||
self.ui:update(dt*slow_amount)
|
self.ui:update(dt*slow_amount)
|
||||||
|
|
||||||
if input.k.pressed then
|
if input.k.pressed then
|
||||||
self:spawn_enemy()
|
self:spawn_enemy(4)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -51,10 +61,13 @@ function Arena:draw()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function Arena:spawn_enemy()
|
function Arena:spawn_enemy(n)
|
||||||
|
n = n or 1
|
||||||
local p = table.random(self.spawn_points)
|
local p = table.random(self.spawn_points)
|
||||||
local o = table.random(self.spawn_offsets)
|
for i = 1, n do
|
||||||
local leader = Unit{group = self.main, x = p.x + o.x, y = p.y + o.y, enemy = true, leader = true, character = 'seeker'}
|
self.t:after((i-1)*0.1, function()
|
||||||
leader:add_follower(Unit{group = self.main, x = p.x + o.x, y = p.y + o.y, enemy = true, character = 'seeker'})
|
local o = table.random(self.spawn_offsets)
|
||||||
leader:add_follower(Unit{group = self.main, x = p.x + o.x, y = p.y + o.y, enemy = true, character = 'seeker'})
|
SpawnEffect{group = self.effects, x = p.x + o.x, y = p.y + o.y, action = function(x, y) Seeker{group = self.main, x = x, y = y, character = 'seeker'} end}
|
||||||
|
end)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
15
devlog.md
15
devlog.md
|
@ -28,3 +28,18 @@ Ideaguyed the basics of the game's classes and mechanics, and implemented basic
|
||||||
* Cycle speed: stacks additively, starts at 2 and capped at minimum 0.5s or +300%
|
* Cycle speed: stacks additively, starts at 2 and capped at minimum 0.5s or +300%
|
||||||
|
|
||||||
Perhaps I'm overengineering it already with the stats but I wanna see where this goes. From SHOOTRX it seems like figuring out stats earlier is better than later, and these seem like they have enough flexibility.
|
Perhaps I'm overengineering it already with the stats but I wanna see where this goes. From SHOOTRX it seems like figuring out stats earlier is better than later, and these seem like they have enough flexibility.
|
||||||
|
|
||||||
|
# Day 2 - 18/02/21
|
||||||
|
|
||||||
|
Went through like 3 small refactors of how I was laying out Unit, Player and Enemy classes and how I wanted enemies to behave.
|
||||||
|
Settled on just copying enemy behavior 100% from SHOOTRX, which is likely the more correct decision since it saves a lot of time.
|
||||||
|
|
||||||
|
Right now basic player and enemy movement works, as well as melee collisions between player and enemy. To do for tomorrow:
|
||||||
|
|
||||||
|
* HP bar should be drawn on top of all player units
|
||||||
|
* Projectiles
|
||||||
|
* Areas
|
||||||
|
* Stats: attack speed, damage, cycle
|
||||||
|
* One or a few of the characters
|
||||||
|
* Port over enemy spawn logic from SHOOTRX
|
||||||
|
* Sounds
|
||||||
|
|
|
@ -143,6 +143,18 @@ function Physics:set_as_triangle(w, h, body_type, tag)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Physics:connect(other, direction)
|
||||||
|
if not self.joints then self.joints = {} end
|
||||||
|
local d = Vector(0, 0)
|
||||||
|
if direction == 'right' then d:set(1, 0)
|
||||||
|
elseif direction == 'left' then d:set(-1, 0)
|
||||||
|
elseif direction == 'up' then d:set(0, -1)
|
||||||
|
elseif direction == 'down' then d:set(0, 1) end
|
||||||
|
self.joints[direction] = love.physics.newRevoluteJoint(self.body, other.body, self.x + 0.5*d.x*self.shape.w, self.y + 0.5*d.y*self.shape.h)
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
-- Automatically called by the group instance this game object belongs to whenever it dies.
|
-- Automatically called by the group instance this game object belongs to whenever it dies.
|
||||||
function Physics:destroy()
|
function Physics:destroy()
|
||||||
if self.body then
|
if self.body then
|
||||||
|
|
387
objects.lua
387
objects.lua
|
@ -1,169 +1,62 @@
|
||||||
Unit = Object:extend()
|
Unit = Object:extend()
|
||||||
Unit:implement(GameObject)
|
function Unit:init_unit()
|
||||||
Unit:implement(Physics)
|
|
||||||
function Unit:init(args)
|
|
||||||
self:init_game_object(args)
|
|
||||||
self:set_as_rectangle(9, 9, 'dynamic', (self.player and 'player') or (self.enemy and 'enemy'))
|
|
||||||
|
|
||||||
if self.character == 'vagrant' then
|
|
||||||
self.color = fg[0]
|
|
||||||
self.visual_shape = 'rectangle'
|
|
||||||
self.classes = {'ranger', 'warrior', 'mage'}
|
|
||||||
elseif self.character == 'seeker' then
|
|
||||||
self.color = red[0]
|
|
||||||
self.visual_shape = 'capsule'
|
|
||||||
self.classes = {'seeker'}
|
|
||||||
if self.enemy then
|
|
||||||
self.enemy_behavior = 'seek'
|
|
||||||
self:calculate_stats()
|
|
||||||
self:set_as_steerable(self.v)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
self:calculate_stats()
|
|
||||||
|
|
||||||
self.r = 0
|
|
||||||
self.hfx:add('hit', 1)
|
self.hfx:add('hit', 1)
|
||||||
self.hp = self.max_hp
|
|
||||||
|
|
||||||
if self.leader then
|
|
||||||
self.previous_positions = {}
|
|
||||||
self.followers = {}
|
|
||||||
self.t:every(0.01, function()
|
|
||||||
table.insert(self.previous_positions, 1, {x = self.x, y = self.y, r = self.r})
|
|
||||||
if #self.previous_positions > 256 then self.previous_positions[257] = nil end
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function Unit:update(dt)
|
function Unit:draw_hp()
|
||||||
self:update_game_object(dt)
|
|
||||||
self:calculate_stats()
|
|
||||||
|
|
||||||
if self.player and self.leader then
|
|
||||||
if input.move_left.down then self.r = self.r - 1.66*math.pi*dt end
|
|
||||||
if input.move_right.down then self.r = self.r + 1.66*math.pi*dt end
|
|
||||||
self:set_velocity(self.v*math.cos(self.r), self.v*math.sin(self.r))
|
|
||||||
|
|
||||||
local vx, vy = self:get_velocity()
|
|
||||||
local hd = math.remap(math.abs(self.x - gw/2), 0, 192, 1, 0)
|
|
||||||
local vd = math.remap(math.abs(self.y - gh/2), 0, 108, 1, 0)
|
|
||||||
camera.x = camera.x + math.remap(vx, -100, 100, -24*hd, 24*hd)*dt
|
|
||||||
camera.y = camera.y + math.remap(vy, -100, 100, -8*vd, 8*vd)*dt
|
|
||||||
if input.move_right.down then camera.r = math.lerp_angle_dt(0.01, dt, camera.r, math.pi/256)
|
|
||||||
elseif input.move_left.down then camera.r = math.lerp_angle_dt(0.01, dt, camera.r, -math.pi/256)
|
|
||||||
elseif input.move_down.down then camera.r = math.lerp_angle_dt(0.01, dt, camera.r, math.pi/256)
|
|
||||||
elseif input.move_up.down then camera.r = math.lerp_angle_dt(0.01, dt, camera.r, -math.pi/256)
|
|
||||||
else camera.r = math.lerp_angle_dt(0.005, dt, camera.r, 0) end
|
|
||||||
end
|
|
||||||
|
|
||||||
if not self.player and self.leader then
|
|
||||||
local player = main.current.player
|
|
||||||
if self.enemy_behavior == 'seek' then
|
|
||||||
if self.being_pushed then
|
|
||||||
local v = math.length(self:get_velocity())
|
|
||||||
if v < 25 then
|
|
||||||
self.being_pushed = false
|
|
||||||
self.steering_enabled = true
|
|
||||||
self:set_damping(0)
|
|
||||||
self:set_angular_damping(0)
|
|
||||||
end
|
|
||||||
self.r = self:get_angle()
|
|
||||||
else
|
|
||||||
self:seek_point(player.x, player.y)
|
|
||||||
self:wander(25, 100, 20)
|
|
||||||
self:rotate_towards_velocity(0.5)
|
|
||||||
self.r = self:get_angle()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if not self.leader then
|
|
||||||
local ds
|
|
||||||
if self.parent.v >= 80 and self.parent.v <= 90 then ds = 8 end
|
|
||||||
if self.parent.v >= 20 and self.parent.v <= 30 then ds = 12 end
|
|
||||||
if not ds then error('undefined unit distance for parent velocity: ' .. self.parent.v) end
|
|
||||||
|
|
||||||
local d = ds*self.follower_index
|
|
||||||
local p = self.parent.previous_positions[d]
|
|
||||||
if p then
|
|
||||||
self:set_position(p.x, p.y)
|
|
||||||
self.r = p.r
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
self:set_angle(self.r)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function Unit:on_trigger_enter(other)
|
|
||||||
if other:is(Unit) and other.enemy then
|
|
||||||
other:push(math.length(self:get_velocity())/3.5, self:angle_to_object(other))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function Unit:push(f, r)
|
|
||||||
self.being_pushed = true
|
|
||||||
self.steering_enabled = false
|
|
||||||
self:apply_impulse(f*math.cos(r), f*math.sin(r))
|
|
||||||
self:apply_angular_impulse(random:float(-12*math.pi, 12*math.pi))
|
|
||||||
self:set_damping(1.5)
|
|
||||||
self:set_angular_damping(1.5)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function Unit:draw()
|
|
||||||
graphics.push(self.x, self.y, self.r, self.hfx.hit.x, self.hfx.hit.x)
|
|
||||||
if self.visual_shape == 'triangle' then
|
|
||||||
graphics.triangle(self.x, self.y, self.shape.w, self.shape.h, self.hfx.hit.f and fg[0] or self.color)
|
|
||||||
elseif self.visual_shape == 'rectangle' then
|
|
||||||
graphics.rectangle(self.x, self.y, self.shape.w, self.shape.h, 2, 2, self.hfx.hit.f and fg[0] or self.color)
|
|
||||||
elseif self.visual_shape == 'capsule' then
|
|
||||||
graphics.rectangle(self.x, self.y, self.shape.w, 0.525*self.shape.h, 2, 2, self.hfx.hit.f and fg[0] or self.color)
|
|
||||||
end
|
|
||||||
graphics.pop()
|
|
||||||
|
|
||||||
graphics.push(self.x, self.y, 0, self.hfx.hit.x, self.hfx.hit.x)
|
graphics.push(self.x, self.y, 0, self.hfx.hit.x, self.hfx.hit.x)
|
||||||
if self.show_hp then
|
if self.show_hp_bar then
|
||||||
graphics.line(self.x - 0.5*self.shape.w, self.y - self.shape.h, self.x + 0.5*self.shape.w, self.y - self.shape.h, bg[-3], 2)
|
graphics.line(self.x - 0.5*self.shape.w, self.y - self.shape.h, self.x + 0.5*self.shape.w, self.y - self.shape.h, bg[-3], 2)
|
||||||
local n = math.remap(self.hp, 0, self.max_hp, 0, 1)
|
local n = math.remap(self.hp, 0, self.max_hp, 0, 1)
|
||||||
graphics.line(self.x - 0.5*self.shape.w, self.y - self.shape.h, self.x - 0.5*self.shape.w + n*self.shape.w, self.y - self.shape.h, self.hfx.hit.f and fg[0] or ((self.player and green[0]) or (self.enemy and red[0])), 2)
|
graphics.line(self.x - 0.5*self.shape.w, self.y - self.shape.h, self.x - 0.5*self.shape.w + n*self.shape.w, self.y - self.shape.h,
|
||||||
|
self.hfx.hit.f and fg[0] or ((self:is(Player) and green[0]) or (table.any(main.current.enemies, function(v) return self:is(v) end) and red[0])), 2)
|
||||||
end
|
end
|
||||||
graphics.pop()
|
graphics.pop()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function Unit:on_collision_enter(other, contact)
|
function Unit:bounce(nx, ny)
|
||||||
if other:is(Wall) and self.leader then
|
local vx, vy = self:get_velocity()
|
||||||
self.hfx:use('hit', 0.5, 200, 10, 0.1)
|
if nx == 0 then
|
||||||
camera:spring_shake(2, math.pi - self.r)
|
self:set_velocity(vx, -vy)
|
||||||
|
self.r = 2*math.pi - self.r
|
||||||
for i, unit in ipairs(self.followers) do
|
end
|
||||||
self.t:after((i-1)*self.v*0.00185, function()
|
if ny == 0 then
|
||||||
unit.hfx:use('hit', 0.25, 200, 10, 0.1)
|
self:set_velocity(-vx, vy)
|
||||||
end)
|
self.r = math.pi - self.r
|
||||||
end
|
|
||||||
|
|
||||||
local nx, ny = contact:getNormal()
|
|
||||||
local vx, vy = self:get_velocity()
|
|
||||||
if nx == 0 then
|
|
||||||
self:set_velocity(vx, -vy)
|
|
||||||
self.r = 2*math.pi - self.r
|
|
||||||
end
|
|
||||||
if ny == 0 then
|
|
||||||
self:set_velocity(-vx, vy)
|
|
||||||
self.r = math.pi - self.r
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function Unit:add_follower(unit)
|
function Unit:show_hp(n)
|
||||||
table.insert(self.followers, unit)
|
self.show_hp_bar = true
|
||||||
unit.parent = self
|
self.t:after(n or 2, function() self.show_hp_bar = false end, 'show_hp_bar')
|
||||||
unit.follower_index = #self.followers
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Unit:hit(damage)
|
||||||
|
if self.dead then return end
|
||||||
|
|
||||||
|
self.hfx:use('hit', 0.25, 200, 10)
|
||||||
|
self:show_hp()
|
||||||
|
|
||||||
|
local actual_damage = self:calculate_damage(damage)
|
||||||
|
self.hp = self.hp - actual_damage
|
||||||
|
if self:is(Player) then
|
||||||
|
if actual_damage >= 20 then
|
||||||
|
camera:shake(2, 1)
|
||||||
|
slow(0.25, 1)
|
||||||
|
else
|
||||||
|
camera:shake(2, 0.5)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.hp <= 0 then
|
||||||
|
self.dead = true
|
||||||
|
for i = 1, random:int(4, 6) do HitParticle{group = main.current.effects, x = self.x, y = self.y, color = self.color} end
|
||||||
|
HitCircle{group = main.current.effects, x = self.x, y = self.y, rs = 12}:scale_down(0.3):change_color(0.5, self.color)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@ -174,7 +67,7 @@ function Unit:calculate_damage(dmg)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function Unit:calculate_stats()
|
function Unit:calculate_stats(first_run)
|
||||||
self.base_hp = 100
|
self.base_hp = 100
|
||||||
self.base_dmg = 10
|
self.base_dmg = 10
|
||||||
self.base_aspd = 1
|
self.base_aspd = 1
|
||||||
|
@ -214,6 +107,7 @@ function Unit:calculate_stats()
|
||||||
elseif class == 'seeker' then self.class_hp_m = self.class_hp_m*0.5 end
|
elseif class == 'seeker' then self.class_hp_m = self.class_hp_m*0.5 end
|
||||||
end
|
end
|
||||||
self.max_hp = (self.base_hp + self.class_hp_a + self.buff_hp_a)*self.class_hp_m*self.buff_hp_m
|
self.max_hp = (self.base_hp + self.class_hp_a + self.buff_hp_a)*self.class_hp_m*self.buff_hp_m
|
||||||
|
if first_run then self.hp = self.max_hp end
|
||||||
|
|
||||||
for _, class in ipairs(self.classes) do
|
for _, class in ipairs(self.classes) do
|
||||||
if class == 'warrior' then self.class_dmg_m = self.class_dmg_m*1.1
|
if class == 'warrior' then self.class_dmg_m = self.class_dmg_m*1.1
|
||||||
|
@ -251,3 +145,196 @@ function Unit:calculate_stats()
|
||||||
end
|
end
|
||||||
self.v = (self.base_mvspd + self.class_mvspd_a + self.buff_mvspd_a)*self.class_mvspd_m*self.buff_mvspd_m
|
self.v = (self.base_mvspd + self.class_mvspd_a + self.buff_mvspd_a)*self.class_mvspd_m*self.buff_mvspd_m
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Player = Object:extend()
|
||||||
|
Player:implement(GameObject)
|
||||||
|
Player:implement(Physics)
|
||||||
|
Player:implement(Unit)
|
||||||
|
function Player:init(args)
|
||||||
|
self:init_game_object(args)
|
||||||
|
self:init_unit()
|
||||||
|
|
||||||
|
if self.character == 'vagrant' then
|
||||||
|
self.color = blue[0]
|
||||||
|
self:set_as_rectangle(9, 9, 'dynamic', 'player')
|
||||||
|
self.visual_shape = 'rectangle'
|
||||||
|
self.classes = {'ranger', 'warrior', 'mage'}
|
||||||
|
end
|
||||||
|
self:calculate_stats(true)
|
||||||
|
|
||||||
|
if self.leader then
|
||||||
|
self.previous_positions = {}
|
||||||
|
self.followers = {}
|
||||||
|
self.t:every(0.01, function()
|
||||||
|
table.insert(self.previous_positions, 1, {x = self.x, y = self.y, r = self.r})
|
||||||
|
if #self.previous_positions > 256 then self.previous_positions[257] = nil end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Player:update(dt)
|
||||||
|
self:update_game_object(dt)
|
||||||
|
self:calculate_stats()
|
||||||
|
|
||||||
|
if self.leader then
|
||||||
|
if input.move_left.down then self.r = self.r - 1.66*math.pi*dt end
|
||||||
|
if input.move_right.down then self.r = self.r + 1.66*math.pi*dt end
|
||||||
|
self:set_velocity(self.v*math.cos(self.r), self.v*math.sin(self.r))
|
||||||
|
|
||||||
|
local vx, vy = self:get_velocity()
|
||||||
|
local hd = math.remap(math.abs(self.x - gw/2), 0, 192, 1, 0)
|
||||||
|
local vd = math.remap(math.abs(self.y - gh/2), 0, 108, 1, 0)
|
||||||
|
camera.x = camera.x + math.remap(vx, -100, 100, -24*hd, 24*hd)*dt
|
||||||
|
camera.y = camera.y + math.remap(vy, -100, 100, -8*vd, 8*vd)*dt
|
||||||
|
if input.move_right.down then camera.r = math.lerp_angle_dt(0.01, dt, camera.r, math.pi/256)
|
||||||
|
elseif input.move_left.down then camera.r = math.lerp_angle_dt(0.01, dt, camera.r, -math.pi/256)
|
||||||
|
elseif input.move_down.down then camera.r = math.lerp_angle_dt(0.01, dt, camera.r, math.pi/256)
|
||||||
|
elseif input.move_up.down then camera.r = math.lerp_angle_dt(0.01, dt, camera.r, -math.pi/256)
|
||||||
|
else camera.r = math.lerp_angle_dt(0.005, dt, camera.r, 0) end
|
||||||
|
|
||||||
|
self:set_angle(self.r)
|
||||||
|
|
||||||
|
else
|
||||||
|
local target_distance = 10.6*self.follower_index
|
||||||
|
local distance_sum = 0
|
||||||
|
local p
|
||||||
|
local previous = self.parent
|
||||||
|
for i, point in ipairs(self.parent.previous_positions) do
|
||||||
|
local distance_to_previous = math.distance(previous.x, previous.y, point.x, point.y)
|
||||||
|
distance_sum = distance_sum + distance_to_previous
|
||||||
|
if distance_sum >= target_distance then
|
||||||
|
p = point
|
||||||
|
break
|
||||||
|
end
|
||||||
|
previous = point
|
||||||
|
end
|
||||||
|
|
||||||
|
if p then
|
||||||
|
self:set_position(p.x, p.y)
|
||||||
|
self.r = p.r
|
||||||
|
if not self.following then
|
||||||
|
for i = 1, random:int(3, 4) do HitParticle{group = main.current.effects, x = self.x, y = self.y, color = self.color} end
|
||||||
|
HitCircle{group = main.current.effects, x = self.x, y = self.y, rs = 10, color = fg[0]}:scale_down(0.3):change_color(0.5, self.color)
|
||||||
|
self.following = true
|
||||||
|
end
|
||||||
|
else
|
||||||
|
self.r = self:get_angle()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Player:draw()
|
||||||
|
graphics.push(self.x, self.y, self.r, self.hfx.hit.x, self.hfx.hit.x)
|
||||||
|
if self.visual_shape == 'rectangle' then
|
||||||
|
graphics.rectangle(self.x, self.y, self.shape.w, self.shape.h, 3, 3, self.hfx.hit.f and fg[0] or self.color)
|
||||||
|
end
|
||||||
|
graphics.pop()
|
||||||
|
self:draw_hp()
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Player:on_collision_enter(other, contact)
|
||||||
|
local x, y = contact:getPositions()
|
||||||
|
|
||||||
|
if other:is(Wall) then
|
||||||
|
self.hfx:use('hit', 0.5, 200, 10, 0.1)
|
||||||
|
camera:spring_shake(2, math.pi - self.r)
|
||||||
|
self:bounce(contact:getNormal())
|
||||||
|
|
||||||
|
elseif table.any(main.current.enemies, function(v) return other:is(v) end) then
|
||||||
|
other:push(random:float(25, 35), self:angle_to_object(other))
|
||||||
|
other:hit(20)
|
||||||
|
self:hit(20)
|
||||||
|
HitCircle{group = main.current.effects, x = x, y = y, rs = 6, color = fg[0], duration = 0.1}
|
||||||
|
for i = 1, 2 do HitParticle{group = main.current.effects, x = x, y = y, color = self.color} end
|
||||||
|
for i = 1, 2 do HitParticle{group = main.current.effects, x = x, y = y, color = other.color} end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Player:add_follower(unit)
|
||||||
|
table.insert(self.followers, unit)
|
||||||
|
unit.parent = self
|
||||||
|
unit.follower_index = #self.followers
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
Seeker = Object:extend()
|
||||||
|
Seeker:implement(GameObject)
|
||||||
|
Seeker:implement(Physics)
|
||||||
|
Seeker:implement(Unit)
|
||||||
|
function Seeker:init(args)
|
||||||
|
self:init_game_object(args)
|
||||||
|
self:init_unit()
|
||||||
|
self:set_as_rectangle(14, 6, 'dynamic', 'enemy')
|
||||||
|
self:set_restitution(0.5)
|
||||||
|
|
||||||
|
self.color = red[0]
|
||||||
|
self.classes = {'seeker'}
|
||||||
|
self:calculate_stats(true)
|
||||||
|
self:set_as_steerable(self.v, 2000, 4*math.pi, 4)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Seeker:update(dt)
|
||||||
|
self:update_game_object(dt)
|
||||||
|
self:calculate_stats()
|
||||||
|
|
||||||
|
if self.being_pushed then
|
||||||
|
local v = math.length(self:get_velocity())
|
||||||
|
if v < 25 then
|
||||||
|
self.being_pushed = false
|
||||||
|
self.steering_enabled = true
|
||||||
|
self:set_damping(0)
|
||||||
|
self:set_angular_damping(0)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local player = main.current.player
|
||||||
|
self:seek_point(player.x, player.y)
|
||||||
|
self:wander(50, 100, 20)
|
||||||
|
self:separate(16, main.current.enemies)
|
||||||
|
self:rotate_towards_velocity(0.5)
|
||||||
|
end
|
||||||
|
self.r = self:get_angle()
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Seeker:draw()
|
||||||
|
graphics.push(self.x, self.y, self.r, self.hfx.hit.x, self.hfx.hit.x)
|
||||||
|
graphics.rectangle(self.x, self.y, self.shape.w, self.shape.h, 3, 3, self.hfx.hit.f and fg[0] or self.color)
|
||||||
|
graphics.pop()
|
||||||
|
self:draw_hp()
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Seeker:on_collision_enter(other, contact)
|
||||||
|
local x, y = contact:getPositions()
|
||||||
|
|
||||||
|
if other:is(Wall) then
|
||||||
|
self.hfx:use('hit', 0.15, 200, 10, 0.1)
|
||||||
|
self:bounce(contact:getNormal())
|
||||||
|
|
||||||
|
elseif table.any(main.current.enemies, function(v) return other:is(v) end) then
|
||||||
|
if self.being_pushed and math.length(self:get_velocity()) > 60 then
|
||||||
|
other:hit(5)
|
||||||
|
self:hit(10)
|
||||||
|
other:push(random:float(10, 15), other:angle_to_object(self))
|
||||||
|
HitCircle{group = main.current.effects, x = x, y = y, rs = 6, color = fg[0], duration = 0.1}
|
||||||
|
for i = 1, 2 do HitParticle{group = main.current.effects, x = x, y = y, color = self.color} end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Seeker:push(f, r)
|
||||||
|
self.being_pushed = true
|
||||||
|
self.steering_enabled = false
|
||||||
|
self:apply_impulse(f*math.cos(r), f*math.sin(r))
|
||||||
|
self:apply_angular_impulse(random:table{random:float(-12*math.pi, -4*math.pi), random:float(4*math.pi, 12*math.pi)})
|
||||||
|
self:set_damping(1.5)
|
||||||
|
self:set_angular_damping(1.5)
|
||||||
|
end
|
||||||
|
|
39
shared.lua
39
shared.lua
|
@ -291,6 +291,35 @@ end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
SpawnEffect = Object:extend()
|
||||||
|
SpawnEffect:implement(GameObject)
|
||||||
|
function SpawnEffect:init(args)
|
||||||
|
self:init_game_object(args)
|
||||||
|
self.target_color = self.color or red[0]
|
||||||
|
self.color = fg[0]
|
||||||
|
self.rs = 0
|
||||||
|
self.t:tween(0.1, self, {rs = 6}, math.cubic_in_out, function()
|
||||||
|
if self.action then self.action(self.x, self.y) end
|
||||||
|
self.spring:pull(1)
|
||||||
|
for i = 1, random:int(6, 8) do HitParticle{group = main.current.effects, x = self.x, y = self.y, color = self.target_color, duration = random:float(0.3, 0.5), w = random:float(5, 8), v = random:float(150, 200)} end
|
||||||
|
self.t:tween(0.25, self, {rs = 0}, math.linear, function() self.dead = true end)
|
||||||
|
self.t:after(0.15, function() self.color = self.target_color end)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function SpawnEffect:update(dt)
|
||||||
|
self:update_game_object(dt)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function SpawnEffect:draw()
|
||||||
|
graphics.circle(self.x, self.y, random:float(0.9, 1.1)*self.rs*self.spring.x, self.color)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
HoverCrosshair = Object:extend()
|
HoverCrosshair = Object:extend()
|
||||||
HoverCrosshair:implement(GameObject)
|
HoverCrosshair:implement(GameObject)
|
||||||
function HoverCrosshair:init(args)
|
function HoverCrosshair:init(args)
|
||||||
|
@ -489,7 +518,7 @@ end
|
||||||
function flash(duration, color)
|
function flash(duration, color)
|
||||||
flashing = true
|
flashing = true
|
||||||
flash_color = color or fg[0]
|
flash_color = color or fg[0]
|
||||||
t:after(duration, function() flashing = false end, 'flash')
|
trigger:after(duration, function() flashing = false end, 'flash')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@ -498,7 +527,7 @@ function slow(amount, duration, tween_method)
|
||||||
duration = duration or 0.5
|
duration = duration or 0.5
|
||||||
tween_method = tween_method or math.cubic_in_out
|
tween_method = tween_method or math.cubic_in_out
|
||||||
slow_amount = amount
|
slow_amount = amount
|
||||||
t:tween(duration, _G, {slow_amount = 1}, tween_method, function() slow_amount = 1 end, 'slow')
|
trigger:tween(duration, _G, {slow_amount = 1}, tween_method, function() slow_amount = 1 end, 'slow')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@ -510,7 +539,7 @@ function HitCircle:init(args)
|
||||||
self:init_game_object(args)
|
self:init_game_object(args)
|
||||||
self.rs = self.rs or 8
|
self.rs = self.rs or 8
|
||||||
self.duration = self.duration or 0.05
|
self.duration = self.duration or 0.05
|
||||||
self.color = self.color or white
|
self.color = self.color or fg[0]
|
||||||
self.t:after(self.duration, function() self.dead = true end, 'die')
|
self.t:after(self.duration, function() self.dead = true end, 'die')
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
@ -552,7 +581,7 @@ function HitParticle:init(args)
|
||||||
self.duration = self.duration or random:float(0.2, 0.6)
|
self.duration = self.duration or random:float(0.2, 0.6)
|
||||||
self.w = self.w or random:float(3.5, 7)
|
self.w = self.w or random:float(3.5, 7)
|
||||||
self.h = self.h or self.w/2
|
self.h = self.h or self.w/2
|
||||||
self.color = self.color or white
|
self.color = self.color or fg[0]
|
||||||
self.t:tween(self.duration, self, {w = 2, h = 2, v = 0}, math.cubic_in_out, function() self.dead = true end)
|
self.t:tween(self.duration, self, {w = 2, h = 2, v = 0}, math.cubic_in_out, function() self.dead = true end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -585,7 +614,7 @@ AnimationEffect:implement(GameObject)
|
||||||
function AnimationEffect:init(args)
|
function AnimationEffect:init(args)
|
||||||
self:init_game_object(args)
|
self:init_game_object(args)
|
||||||
self.animation = Animation(self.delay, self.frames, 'once', {[0] = function() self.dead = true end})
|
self.animation = Animation(self.delay, self.frames, 'once', {[0] = function() self.dead = true end})
|
||||||
self.color = self.color or white
|
self.color = self.color or fg[0]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue