Physics chain rollback checkpoint

master
a327ex 2021-02-18 15:28:40 -03:00
parent 28e24736f3
commit 015afe0459
5 changed files with 92 additions and 27 deletions

View File

@ -8,9 +8,14 @@ function Arena:init(name)
self.effects = Group()
self.ui = Group():no_camera()
self.main:disable_collision_between('player', 'player')
self.main:disable_collision_between('player', 'enemy')
self.main:disable_collision_between('enemy', 'enemy')
self.main:enable_trigger_between('player', 'enemy')
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.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_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(self.x2, -40, gw + 40, gh + 40), color = bg[-1]}
@ -18,7 +23,7 @@ function Arena:init(name)
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:add_follower(Unit{group = self.main, player = true, color = red[0]})
-- self.player:add_follower(Unit{group = self.main, player = true, character = 'seeker'})
end
@ -32,6 +37,10 @@ function Arena:update(dt)
self.main:update(dt*slow_amount)
self.effects:update(dt*slow_amount)
self.ui:update(dt*slow_amount)
if input.k.pressed then
self:spawn_enemy()
end
end
@ -40,3 +49,12 @@ function Arena:draw()
self.effects:draw()
self.ui:draw()
end
function Arena:spawn_enemy()
local p = table.random(self.spawn_points)
local o = table.random(self.spawn_offsets)
local leader = Unit{group = self.main, x = p.x + o.x, y = p.y + o.y, enemy = true, leader = true, character = 'seeker'}
leader:add_follower(Unit{group = self.main, x = p.x + o.x, y = p.y + o.y, enemy = true, character = 'seeker'})
leader:add_follower(Unit{group = self.main, x = p.x + o.x, y = p.y + o.y, enemy = true, character = 'seeker'})
end

View File

@ -452,7 +452,7 @@ end
-- Applies a continuous amount of force to the object
-- self:apply_force(100*math.cos(angle), 100*math.sin(angle))
function Physics:apply_force(fx, fy, x, y)
if self.body then self.body:applyForce(fx, fy, x, y) end
if self.body then self.body:applyForce(fx, fy, x or self.x, y or self.y) end
return self
end

View File

@ -28,11 +28,11 @@ end
-- then you need to destroy everything that needs to be destroyed in an on_exit function and then recreate it again in the on_enter function.
--
-- You'd add a state to the game like this:
-- state.add(MyState'level_1')
-- main:add(MyState'level_1')
-- You'd move to that state like so:
-- state.go_to'level_1'
-- state.go_to automatically calls on_exit for the currently active state and on_enter for the new one.
-- You can access the currently active state with state.current.
-- main:go_to'level_1'
-- main:go_to automatically calls on_exit for the currently active state and on_enter for the new one.
-- You can access the currently active state with main.current.
State = Object:extend()
function State:init_state(name)
self.name = name or random:uid()

View File

@ -5,6 +5,7 @@
-- self:set_as_steerable(100, 1000)
function Physics:set_as_steerable(max_v, max_f, max_turn_rate, turn_multiplier)
self.steerable = true
self.steering_enabled = true
self.heading = Vector()
self.side = Vector()
self.mass = 1
@ -29,14 +30,14 @@ end
function Physics:steering_update(dt)
if self.steerable then
if self.steerable and self.steering_enabled then
local steering_force = self:calculate_steering_force(dt):div(self.mass)
self:apply_force(steering_force.x, steering_force.y)
local vx, vy = self:get_velocity()
local v = Vector(vx, vy):truncate(self.max_v)
self:set_velocity(v.x, v.y)
if v:length_squared() > 0.00001 then
self.heading = self.v:clone():normalize()
self.heading = v:clone():normalize()
self.side = self.heading:perpendicular()
end
end
@ -80,7 +81,8 @@ function Physics:seek_point(x, y, deceleration, weight)
local v = d/((deceleration or 1)*0.08)
v = math.min(v, self.max_v)
local dvx, dvy = v*tx/d, v*ty/d
self.seek_f:set((dvx - self.v.x)*self.turn_multiplier*(weight or 1), (dvy - self.v.y)*self.turn_multiplier*(weight or 1))
local vx, vy = self:get_velocity()
self.seek_f:set((dvx - vx)*self.turn_multiplier*(weight or 1), (dvy - vy)*self.turn_multiplier*(weight or 1))
else self.seek_f:set(0, 0) end
end

View File

@ -7,8 +7,17 @@ function Unit:init(args)
if self.character == 'vagrant' then
self.color = fg[0]
self.visual_shape = 'triangle'
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()
@ -49,8 +58,34 @@ function Unit:update(dt)
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 d = math.ceil(self.v*0.1)*self.follower_index
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)
@ -62,12 +97,31 @@ function Unit:update(dt)
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()
@ -154,35 +208,29 @@ function Unit:calculate_stats()
for _, class in ipairs(self.classes) do
if class == 'warrior' then self.class_hp_m = self.class_hp_m*1.4
elseif class == 'ranger' then self.class_hp_m = self.class_hp_m*1
elseif class == 'mage' then self.class_hp_m = self.class_hp_m*0.6
elseif class == 'healer' then self.class_hp_m = self.class_hp_m*1.1
elseif class == 'cycler' then self.class_hp_m = self.class_hp_m*0.9 end
elseif class == 'cycler' then self.class_hp_m = self.class_hp_m*0.9
elseif class == 'seeker' then self.class_hp_m = self.class_hp_m*0.5 end
end
self.max_hp = (self.base_hp + self.class_hp_a + self.buff_hp_a)*self.class_hp_m*self.buff_hp_m
for _, class in ipairs(self.classes) do
if class == 'warrior' then self.class_dmg_m = self.class_dmg_m*1.1
elseif class == 'ranger' then self.class_dmg_m = self.class_dmg_m*1.2
elseif class == 'mage' then self.class_dmg_m = self.class_dmg_m*1.4
elseif class == 'healer' then self.class_dmg_m = self.class_dmg_m*1
elseif class == 'cycler' then self.class_dmg_m = self.class_dmg_m*1 end
elseif class == 'mage' then self.class_dmg_m = self.class_dmg_m*1.4 end
end
self.dmg = (self.base_dmg + self.class_dmg_a + self.buff_dmg_a)*self.class_dmg_m*self.buff_dmg_m
for _, class in ipairs(self.classes) do
if class == 'warrior' then self.class_aspd_m = self.class_aspd_m*0.9
elseif class == 'ranger' then self.class_aspd_m = self.class_aspd_m*1.4
elseif class == 'mage' then self.class_aspd_m = self.class_aspd_m*1
elseif class == 'healer' then self.class_aspd_m = self.class_aspd_m*0.5
elseif class == 'cycler' then self.class_aspd_m = self.class_aspd_m*1 end
elseif class == 'healer' then self.class_aspd_m = self.class_aspd_m*0.5 end
end
self.aspd = 1/((self.base_aspd + self.class_aspd_a + self.buff_aspd_a)*self.class_aspd_m*self.buff_aspd_m)
for _, class in ipairs(self.classes) do
if class == 'warrior' then self.class_cycle_m = self.class_cycle_m*1
elseif class == 'ranger' then self.class_cycle_m = self.class_cycle_m*1
elseif class == 'mage' then self.class_cycle_m = self.class_cycle_m*1.25
if class == 'mage' then self.class_cycle_m = self.class_cycle_m*1.25
elseif class == 'healer' then self.class_cycle_m = self.class_cycle_m*1.1
elseif class == 'cycler' then self.class_cycle_m = self.class_cycle_m*1.5 end
end
@ -192,17 +240,14 @@ function Unit:calculate_stats()
if class == 'warrior' then self.class_def_m = self.class_def_m*1.25
elseif class == 'ranger' then self.class_def_m = self.class_def_m*1.1
elseif class == 'mage' then self.class_def_m = self.class_def_m*1.5
elseif class == 'healer' then self.class_def_m = self.class_def_m*1.2
elseif class == 'cycler' then self.class_def_m = self.class_def_m*1 end
elseif class == 'healer' then self.class_def_m = self.class_def_m*1.2 end
end
self.def = (self.base_def + self.class_def_a + self.buff_def_a)*self.class_def_m*self.buff_def_m
for _, class in ipairs(self.classes) do
if class == 'warrior' then self.class_mvspd_m = self.class_mvspd_m*0.9
elseif class == 'ranger' then self.class_mvspd_m = self.class_mvspd_m*1.2
elseif class == 'mage' then self.class_mvspd_m = self.class_mvspd_m*1
elseif class == 'healer' then self.class_mvspd_m = self.class_mvspd_m*1
elseif class == 'cycler' then self.class_mvspd_m = self.class_mvspd_m*1 end
elseif class == 'seeker' then self.class_mvspd_m = self.class_mvspd_m*0.3 end
end
self.v = (self.base_mvspd + self.class_mvspd_a + self.buff_mvspd_a)*self.class_mvspd_m*self.buff_mvspd_m
end