242 lines
8.5 KiB
Lua
242 lines
8.5 KiB
Lua
|
|
||
|
local DEFAULT_PLATFORM_COLOR = {255,255,255}
|
||
|
|
||
|
local GRAVITY_CONSTANT = 700
|
||
|
local AIR_RES_CONSTANT = 0.5
|
||
|
local DEFAULT_PLATFORM_RES = 0.7
|
||
|
local VERY_SMALL_NUM = 0.01
|
||
|
local PLAYER_MOVE_SPEED = 20
|
||
|
local PUSH_RESISTANCE = math.huge -- Should be >=2, set to math.huge to disable pushing
|
||
|
local BOUNCE_CONSTANT_X = 0
|
||
|
local BOUNCE_CONSTANT_Y = 0
|
||
|
|
||
|
local function platform_below_platform (platform, system)
|
||
|
local tmp
|
||
|
tmp, platform.collided_on_y_axis = platform.collided_on_y_axis, false
|
||
|
return tmp
|
||
|
end
|
||
|
|
||
|
--------------------------------------------------------------------------------
|
||
|
|
||
|
local BulletController
|
||
|
local PlayerController = {}
|
||
|
PlayerController.__index = PlayerController
|
||
|
|
||
|
function PlayerController.new (target, controls)
|
||
|
assert(controls.jump and controls.left and controls.right )
|
||
|
local self = setmetatable({}, PlayerController)
|
||
|
self.target = target
|
||
|
self.controls = controls
|
||
|
self.last_dir = 0
|
||
|
return self
|
||
|
end
|
||
|
|
||
|
function PlayerController:update (dt, system)
|
||
|
local not_jumping = platform_below_platform(self.target, system)
|
||
|
if love.keyboard.isDown( self.controls.jump ) then
|
||
|
self.target.dy = self.target.dy + (not_jumping and -400 or (-400 * dt))
|
||
|
end
|
||
|
if love.keyboard.isDown( self.controls.left ) then
|
||
|
self.target.dx = self.target.dx - PLAYER_MOVE_SPEED
|
||
|
self.last_dir = -1
|
||
|
end
|
||
|
if love.keyboard.isDown( self.controls.right ) then
|
||
|
self.target.dx = self.target.dx + PLAYER_MOVE_SPEED
|
||
|
self.last_dir = 1
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function PlayerController:canCollide (platform, collided_with)
|
||
|
return platform ~= collided_with
|
||
|
and (not collided_with.controller or collided_with.controller.__index ~= BulletController)
|
||
|
end
|
||
|
|
||
|
function PlayerController:onCollide (platform, collided_with, system)
|
||
|
end
|
||
|
|
||
|
--------------------------------------------------------------------------------
|
||
|
|
||
|
BulletController = {}
|
||
|
BulletController.__index = BulletController
|
||
|
|
||
|
function BulletController.new (target, owner)
|
||
|
local self = setmetatable({}, BulletController)
|
||
|
self.target = target
|
||
|
self.owner = owner
|
||
|
self.owner_im = 0.5
|
||
|
return self
|
||
|
end
|
||
|
|
||
|
function BulletController:update (dt, system)
|
||
|
self.target.x = (self.target.x + self.target.w + (WINDOW_WIDTH + self.target.w)) % (WINDOW_WIDTH + self.target.w) - self.target.w
|
||
|
self.owner_im = self.owner_im - dt
|
||
|
end
|
||
|
|
||
|
function BulletController:canCollide (platform, collided_with)
|
||
|
return collided_with ~= platform and (collided_with ~= self.owner or self.owner_im <= 0) and (collided_with.controller and collided_with.controller.__index ~= BulletController)
|
||
|
end
|
||
|
|
||
|
function BulletController:onCollide (platform, collided_with, system)
|
||
|
local dir = platform.dx / math.abs(platform.dx)
|
||
|
local force = dir * ((platform.w / (40*0.2) )^1.5) * (40*0.2)
|
||
|
local ndx, ndy = collided_with.dx + force * 15, collided_with.dy + -platform.h * 12
|
||
|
|
||
|
if ndx == ndx and ndy == ndy then -- Prevent nan values
|
||
|
collided_with.dx, collided_with.dy = ndx, ndy
|
||
|
system:removeController(self)
|
||
|
system:removePlatform(self.target)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
--------------------------------------------------------------------------------
|
||
|
|
||
|
local Platform = {}
|
||
|
Platform.__index = Platform
|
||
|
|
||
|
function Platform.new (t)
|
||
|
local self = setmetatable(t, Platform)
|
||
|
assert(self.x and self.y and self.w and self.y)
|
||
|
self.color = self.color or DEFAULT_PLATFORM_COLOR
|
||
|
self.resistance = DEFAULT_PLATFORM_RES
|
||
|
return self
|
||
|
end
|
||
|
|
||
|
--------------------------------------------------------------------------------
|
||
|
|
||
|
local PlatformSystem = {}
|
||
|
PlatformSystem.__index = PlatformSystem
|
||
|
|
||
|
function PlatformSystem.new ()
|
||
|
local self = setmetatable({}, PlatformSystem)
|
||
|
self.platforms = {}
|
||
|
self.controllers = {}
|
||
|
return self
|
||
|
end
|
||
|
|
||
|
function PlatformSystem:addPlatform (x, y, w, h)
|
||
|
local platform = Platform.new {
|
||
|
x = x, y = y, w = w, h = h
|
||
|
}
|
||
|
self.platforms[platform] = true
|
||
|
return platform
|
||
|
end
|
||
|
|
||
|
function PlatformSystem:removeController( controller )
|
||
|
self.controllers[controller] = nil
|
||
|
end
|
||
|
|
||
|
function PlatformSystem:removePlatform( platform )
|
||
|
self.platforms[platform] = nil
|
||
|
end
|
||
|
|
||
|
function PlatformSystem:addPlayer ( x, y, w, h, controls )
|
||
|
local platform = Platform.new {
|
||
|
x = x, y = y, w = w, h = h,
|
||
|
movable = true, dx = 0, dy = 0
|
||
|
}
|
||
|
platform.controller = PlayerController.new(platform, controls)
|
||
|
self.platforms[platform] = true
|
||
|
self.controllers[platform.controller] = true
|
||
|
return platform
|
||
|
end
|
||
|
|
||
|
function PlatformSystem:addBullet (x, y, w, h, dx, owner)
|
||
|
local platform = Platform.new {
|
||
|
x = x, y = y, w = w, h = h,
|
||
|
movable = true, dx = dx, dy = 0, constant = true
|
||
|
}
|
||
|
platform.controller = BulletController.new(platform, owner)
|
||
|
self.platforms[platform] = true
|
||
|
self.controllers[platform.controller] = true
|
||
|
return platform
|
||
|
end
|
||
|
|
||
|
function PlatformSystem:isPointOccupied (x, y, who_is_asking)
|
||
|
for platform, _ in pairs(self.platforms) do
|
||
|
if platform.x <= x and x <= platform.x + platform.w and platform.y <= y and y <= platform.y + platform.h and who_is_asking.controller:canCollide(who_is_asking, platform) then
|
||
|
return platform
|
||
|
end
|
||
|
end
|
||
|
return nil
|
||
|
end
|
||
|
|
||
|
local function is_movement_on_collision_course (platform, nx, ny, platforms, func)
|
||
|
for other in pairs(platforms) do
|
||
|
if func(platform, nx, ny, other) then
|
||
|
return other
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
local function check_move_x (platform, nx, ny, other)
|
||
|
return platform ~= other and nx < other.x + other.w and other.x < nx + platform.w and other.y < platform.y + platform.h and platform.y < other.y + other.h and platform.controller:canCollide(platform, other)
|
||
|
end
|
||
|
|
||
|
local function check_move_y (platform, nx, ny, other)
|
||
|
return platform ~= other and ny < other.y + other.h and other.y < ny + platform.h and other.x < platform.x + platform.w and platform.x < other.x + other.w and platform.controller:canCollide(platform, other)
|
||
|
end
|
||
|
|
||
|
function PlatformSystem:updatePlatform (platform, dt)
|
||
|
local ndx, ndy = platform.dx * (platform.constant and 1 or (1 - AIR_RES_CONSTANT) ^ dt), platform.dy + (platform.constant and 0 or (GRAVITY_CONSTANT * dt))
|
||
|
local nx, ny = platform.x + ndx * dt, platform.y + ndy * dt
|
||
|
|
||
|
-- Moving down
|
||
|
do
|
||
|
local collided_with = is_movement_on_collision_course(platform, nx, ny, self.platforms, check_move_y)
|
||
|
if not collided_with then
|
||
|
platform.y, platform.dy = ny, ndy
|
||
|
else
|
||
|
platform.collided_on_y_axis = collided_with and (ndy >= 0)
|
||
|
platform.controller:onCollide(platform, collided_with, self)
|
||
|
ndx = ndx * (platform.constant and 1 or (1 - collided_with.resistance) ^ dt)
|
||
|
platform.y = collided_with.y + ( (ndy >= 0) and -platform.h or collided_with.h)
|
||
|
platform.dy = -platform.dy * BOUNCE_CONSTANT_Y
|
||
|
end
|
||
|
end
|
||
|
|
||
|
-- Moving on x axis
|
||
|
do
|
||
|
local collided_with = is_movement_on_collision_course(platform, nx, ny, self.platforms, check_move_x)
|
||
|
if not collided_with then
|
||
|
platform.x, platform.dx = nx, ndx
|
||
|
else
|
||
|
platform.controller:onCollide(platform, collided_with, self)
|
||
|
platform.x = collided_with.x + ( (ndx >= 0) and -platform.w or collided_with.w)
|
||
|
if platform.movable and collided_with.movable and self.platforms[platform] and PUSH_RESISTANCE ~= math.huge then
|
||
|
platform.dx, collided_with.dx = (platform.dx + collided_with.dx)/PUSH_RESISTANCE, (platform.dx + collided_with.dx)/PUSH_RESISTANCE
|
||
|
elseif BOUNCE_CONSTANT_X == 0 then
|
||
|
platform.dx = 0
|
||
|
end
|
||
|
platform.dx = -platform.dx * BOUNCE_CONSTANT_X
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function PlatformSystem:update (dt)
|
||
|
-- Update movable platforms
|
||
|
for platform, _ in pairs(self.platforms) do
|
||
|
if platform.movable then
|
||
|
self:updatePlatform(platform, dt)
|
||
|
end
|
||
|
end
|
||
|
-- Update controllers
|
||
|
for controller, _ in pairs(self.controllers) do
|
||
|
controller:update(dt, self)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function PlatformSystem:draw (dt, offset_x, offset_y)
|
||
|
local offset_x, offset_y = offset_x or 0, offset_y or 0
|
||
|
for platform, _ in pairs(self.platforms) do
|
||
|
love.graphics.setColor(platform.color)
|
||
|
love.graphics.rectangle('fill', platform.x - offset_y, platform.y - offset_x, platform.w, platform.h)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
--------------------------------------------------------------------------------
|
||
|
|
||
|
return {
|
||
|
PlatformSystem = PlatformSystem,
|
||
|
Player = Player,
|
||
|
}
|