mirror of
https://github.com/bakpakin/tiny-ecs.git
synced 2026-07-22 07:56:52 -06:00
First demo commit.
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
local multisource = require "lib.multisource"
|
||||
|
||||
local assets = {}
|
||||
|
||||
love.graphics.setDefaultFilter("nearest", "nearest")
|
||||
|
||||
assets.img_cat = love.graphics.newImage("assets/cat.png")
|
||||
assets.img_catandcannon = love.graphics.newImage("assets/catandcannon.png")
|
||||
assets.img_gun = love.graphics.newImage("assets/gun.png")
|
||||
assets.img_bullet = love.graphics.newImage("assets/bullet.png")
|
||||
assets.img_explosion = love.graphics.newImage("assets/explosion.png")
|
||||
assets.img_pig = love.graphics.newImage("assets/pig.png")
|
||||
assets.img_spawner = love.graphics.newImage("assets/spawner.png")
|
||||
|
||||
assets.snd_catjump = multisource.new(love.audio.newSource("assets/catjump.wav"))
|
||||
assets.snd_cannon = multisource.new(love.audio.newSource("assets/cannon.wav"))
|
||||
assets.snd_thud = multisource.new(love.audio.newSource("assets/thud.wav"))
|
||||
assets.snd_meow = multisource.new(love.audio.newSource("assets/meow.ogg"))
|
||||
assets.snd_oink = multisource.new(love.audio.newSource("assets/oink.ogg"))
|
||||
assets.snd_yay = multisource.new(love.audio.newSource("assets/yay.wav"))
|
||||
|
||||
assets.snd_music = love.audio.newSource("assets/music.ogg")
|
||||
|
||||
assets.fnt_hud = love.graphics.newFont("assets/font.ttf", 48)
|
||||
assets.fnt_smallhud = love.graphics.newFont("assets/font.ttf", 32)
|
||||
assets.fnt_reallysmallhud = love.graphics.newFont("assets/font.ttf", 24)
|
||||
|
||||
|
||||
return assets
|
||||
@@ -0,0 +1,41 @@
|
||||
local assets = require "src.assets"
|
||||
local Explosion = require "src.entities.Explosion"
|
||||
|
||||
local Bullet = class("Bullet")
|
||||
|
||||
Bullet.speed = 480
|
||||
Bullet.sprite = assets.img_bullet
|
||||
|
||||
function Bullet:init(x, y, direction)
|
||||
self.pos = {x = x, y = y}
|
||||
self.vel = {x = self.speed * math.cos(direction), y = self.speed * math.sin(direction)}
|
||||
self.offset = {x = 4, y = 4}
|
||||
self.hitbox = {w = 4, h = 4}
|
||||
self.bullet = true
|
||||
self.drot = math.random() - 0.5
|
||||
self.rot = direction
|
||||
self.isBullet = true
|
||||
self.isSolid = true
|
||||
self.gravity = 1300
|
||||
self.bg = true
|
||||
end
|
||||
|
||||
function Bullet:explode()
|
||||
world:remove(self)
|
||||
world:add(Explosion(self.pos.x - 32, self.pos.y - 60))
|
||||
assets.snd_thud:play()
|
||||
end
|
||||
|
||||
function Bullet:update(dt)
|
||||
self.rot = self.rot + self.drot * dt * 10
|
||||
end
|
||||
|
||||
function Bullet:onCollision(col)
|
||||
self:explode()
|
||||
if col.other.isEnemy and col.other.gotHit then
|
||||
col.other:gotHit()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return Bullet
|
||||
@@ -0,0 +1,16 @@
|
||||
local assets = require "src.assets"
|
||||
local anim8 = require "lib.anim8"
|
||||
|
||||
local Explosion = class "Explosion"
|
||||
|
||||
Explosion.sprite = assets.img_explosion
|
||||
|
||||
function Explosion:init(x, y)
|
||||
self.pos = {x = x, y = y}
|
||||
self.bg = true
|
||||
local g = anim8.newGrid(64, 64, assets.img_explosion:getWidth(), assets.img_explosion:getHeight())
|
||||
self.animation = anim8.newAnimation(g('1-10', 1), 0.05)
|
||||
self.lifetime = 9 * 0.05
|
||||
end
|
||||
|
||||
return Explosion
|
||||
@@ -0,0 +1,20 @@
|
||||
local assets = require "src.assets"
|
||||
|
||||
local MainHud = class "MainHud"
|
||||
|
||||
function MainHud:drawHud(dt)
|
||||
local n = self.levelState.totalEnemiesToKill - self.levelState.enemiesKilled
|
||||
local d = self.levelState.totalEnemiesToKill
|
||||
love.graphics.setFont(assets.fnt_hud)
|
||||
love.graphics.printf("Wave " .. self.levelState.wave, 20, 20, 300, "left")
|
||||
love.graphics.setFont(assets.fnt_smallhud)
|
||||
love.graphics.printf(n .. "/" .. d .. " Pigs Remaining", 20, 60, 500, "left")
|
||||
love.graphics.printf("Total Pigs Killed: " .. self.levelState.score, love.graphics.getWidth() - 420, 20, 400, "right")
|
||||
end
|
||||
|
||||
function MainHud:init(levelState)
|
||||
self.levelState = levelState
|
||||
self.hudBg = true
|
||||
end
|
||||
|
||||
return MainHud
|
||||
@@ -0,0 +1,59 @@
|
||||
local assets = require "src.assets"
|
||||
local anim8 = require "lib.anim8"
|
||||
local Explosion = require "src.entities.Explosion"
|
||||
local gamestate = require "lib.gamestate"
|
||||
|
||||
local Pig = class "Pig"
|
||||
|
||||
Pig.sprite = assets.img_pig
|
||||
|
||||
function Pig:init(x, y, target)
|
||||
self.pos = {x = x, y = y}
|
||||
self.vel = {x = 0, y = 0}
|
||||
self.gravity = 1300
|
||||
|
||||
self.isAlive = true
|
||||
self.isEnemy = true
|
||||
self.isSolid = true
|
||||
|
||||
self.platforming = {
|
||||
acceleration = 1000,
|
||||
speed = 60,
|
||||
jump = 250,
|
||||
friction = 2000,
|
||||
direction = 'r'
|
||||
}
|
||||
|
||||
self.ai = {
|
||||
|
||||
}
|
||||
|
||||
self.hitbox = {w = 30, h = 21}
|
||||
self.health = 50
|
||||
self.maxHealth = 50
|
||||
|
||||
local g = anim8.newGrid(30, 21, assets.img_pig:getWidth(), assets.img_pig:getHeight())
|
||||
self.animation_stand = anim8.newAnimation(g('1-1', 1), 0.1)
|
||||
self.animation_walk = anim8.newAnimation(g('2-5', 1), 0.1)
|
||||
self.animation = self.animation_stand
|
||||
self.fg = true
|
||||
end
|
||||
|
||||
function Pig:gotHit()
|
||||
if self.isAlive then
|
||||
self.isAlive = nil
|
||||
self.lifetime = 0.25
|
||||
self.fadeTime = 0.25
|
||||
self.alpha = 1
|
||||
self.ai = nil
|
||||
self.platforming.moving = false
|
||||
self.vel.y = -300
|
||||
self.vel.x = 0
|
||||
assets.snd_oink:play()
|
||||
assets.snd_yay:play()
|
||||
world:add(self)
|
||||
gamestate.current().score = gamestate.current().score + 1
|
||||
end
|
||||
end
|
||||
|
||||
return Pig
|
||||
@@ -0,0 +1,82 @@
|
||||
local assets = require "src.assets"
|
||||
local anim8 = require "lib.anim8"
|
||||
local Bullet = require "src.entities.Bullet"
|
||||
local TimerEvent = require "src.entities.TimerEvent"
|
||||
local ScreenSplash = require "src.entities.ScreenSplash"
|
||||
local gamestate = require "lib.gamestate"
|
||||
|
||||
local Player = class("Player")
|
||||
|
||||
function Player:draw(dt)
|
||||
if self.hasGun then
|
||||
local p = self.animation.position
|
||||
local dy = (p ~= 2 and p ~= 3) and 0 or -1
|
||||
local dx = self.platforming.direction == 'l' and 2 or -2
|
||||
love.graphics.draw(assets.img_gun, self.pos.x + 16 + dx, self.pos.y + 10 + dy, self.gunAngle - math.pi / 4)
|
||||
end
|
||||
end
|
||||
|
||||
function Player:onHit()
|
||||
self.isAlive = nil
|
||||
self.lifetime = 0.25
|
||||
self.fadeTime = 0.25
|
||||
self.alpha = 1
|
||||
self.ai = nil
|
||||
self.platforming.moving = false
|
||||
self.vel.y = -300
|
||||
self.vel.x = (math.random() - 0.5) * 400
|
||||
self.controlable = nil
|
||||
assets.snd_meow:play()
|
||||
world:add(self)
|
||||
local n = gamestate.current().score
|
||||
local message = "You Died."
|
||||
if n == 0 then message = "You Failed Pretty Hard."
|
||||
elseif n < 10 then message = "You Killed Some Pigs and They Killed you Back."
|
||||
elseif n < 30 then message = "That's a lot of Bacon."
|
||||
elseif n < 100 then message = "You a crazy Pig Killer."
|
||||
else message = "Pigpocolypse." end
|
||||
|
||||
world:add(TimerEvent(1.2, function() world:add(ScreenSplash(0.5, 0.4, message .. " Press Space to Try Again.", 800)) end))
|
||||
gamestate.current().isSpawning = false
|
||||
gamestate.current().restartOnSpace = true
|
||||
end
|
||||
|
||||
function Player:onCollision(col)
|
||||
if self.isAlive and col.other.isEnemy and col.other.isAlive then
|
||||
self:onHit()
|
||||
end
|
||||
end
|
||||
|
||||
function Player:init(args)
|
||||
self.cameraTrack = {xoffset = 16, yoffset = -35}
|
||||
self.pos = {x = args.x, y = args.y}
|
||||
self.vel = {x = 0, y = 0}
|
||||
self.gravity = 1300
|
||||
self.platforming = {
|
||||
acceleration = 1000,
|
||||
speed = 130,
|
||||
jump = 380,
|
||||
friction = 2000,
|
||||
direction = 'r'
|
||||
}
|
||||
self.isAlive = true
|
||||
self.isPlayer = true
|
||||
self.isSolid = true
|
||||
self.controlable = true
|
||||
self.hitbox = {w = 32, h = 32}
|
||||
self.checkCollisions = true
|
||||
self.sprite = assets.img_catandcannon
|
||||
self.fg = true
|
||||
local g = anim8.newGrid(32, 32, assets.img_cat:getWidth(), assets.img_cat:getHeight())
|
||||
self.animation_stand = anim8.newAnimation(g('1-1', 1), 0.1)
|
||||
self.animation_walk = anim8.newAnimation(g('2-5', 1), 0.1)
|
||||
self.animation = self.animation_stand
|
||||
self.health = 100
|
||||
self.maxHealth = 100
|
||||
self.shotTimer = 0
|
||||
self.shotInterval = 0.45
|
||||
self.gunAngle = 2 * math.pi
|
||||
self.hasGun = true
|
||||
end
|
||||
|
||||
return Player
|
||||
@@ -0,0 +1,30 @@
|
||||
local ScreenSplash = class("Screen Splash")
|
||||
local assets = require "src.assets"
|
||||
|
||||
function ScreenSplash:drawHud()
|
||||
love.graphics.setFont(self.splash.fnt or assets.fnt_hud)
|
||||
local align = self.align
|
||||
local w, h = love.graphics.getWidth() * self.pos.x, love.graphics.getHeight() * self.pos.y
|
||||
local dx, dy = self.offset.x, self.offset.y
|
||||
if align == "center" then
|
||||
dx = dx - self.splash.width / 2
|
||||
end
|
||||
if align == "right" then
|
||||
dx = dx - self.splash.width
|
||||
end
|
||||
love.graphics.printf(self.splash.text, w + dx, h + dy, self.splash.width, self.align)
|
||||
end
|
||||
|
||||
function ScreenSplash:init(x, y, text, width, fnt, align, xo, yo)
|
||||
self.pos = {x = x, y = y}
|
||||
self.offset = {x = xo or 0, y = yo or 0}
|
||||
self.hudBg = true
|
||||
self.align = align or "center"
|
||||
self.splash = {
|
||||
text = text,
|
||||
fnt = fnt,
|
||||
width = width or 400
|
||||
}
|
||||
end
|
||||
|
||||
return ScreenSplash
|
||||
@@ -0,0 +1,27 @@
|
||||
local assets = require "src.assets"
|
||||
local Pig = require "src.entities.Pig"
|
||||
|
||||
local Spawner = class "Spawner"
|
||||
|
||||
Spawner.sprite = assets.img_spawner
|
||||
|
||||
function Spawner:init(args)
|
||||
self.pos = {x = args.x + 32, y = args.y + 32}
|
||||
self.offset = {x = 32, y = 32}
|
||||
self.scale = {x = 1, y = 1}
|
||||
self.rot = 0
|
||||
self.bg = true
|
||||
self.isSpawner = true
|
||||
end
|
||||
|
||||
function Spawner:update(dt)
|
||||
self.rot = self.rot + 2 * dt
|
||||
local s = math.random() * 0.2 + 0.9
|
||||
self.scale.x, self.scale.y = s, s
|
||||
end
|
||||
|
||||
function Spawner:spawn()
|
||||
world:add(Pig(self.pos.x - 15, self.pos.y - 10, nil))
|
||||
end
|
||||
|
||||
return Spawner
|
||||
@@ -0,0 +1,12 @@
|
||||
local TimerEvent = class "TimerEvent"
|
||||
|
||||
function TimerEvent:init(time, fn)
|
||||
self.lifetime = time
|
||||
self.timerCallback = fn
|
||||
end
|
||||
|
||||
function TimerEvent:onLifeover()
|
||||
self.timerCallback()
|
||||
end
|
||||
|
||||
return TimerEvent
|
||||
@@ -0,0 +1,32 @@
|
||||
local gamestate = require "lib.gamestate"
|
||||
local TransitionScreen = class "TransitionScreen"
|
||||
|
||||
TransitionScreen.hudFg = true
|
||||
|
||||
-- mode is true for "toblack" or false for "totransparent"
|
||||
function TransitionScreen:init(mode, newState)
|
||||
self.lifetime = 0.5
|
||||
self.newState = newState
|
||||
if mode then
|
||||
self.alpha = 0
|
||||
self.fadeTime = -0.5
|
||||
else
|
||||
self.alpha = 1
|
||||
self.fadeTime = 0.5
|
||||
end
|
||||
end
|
||||
|
||||
function TransitionScreen:drawHud(dt)
|
||||
local r1, g1, b1, a = love.graphics.getColor()
|
||||
love.graphics.setColor(0, 0, 0, self.alpha * 255)
|
||||
love.graphics.rectangle("fill", 0, 0, love.graphics.getWidth(), love.graphics.getHeight())
|
||||
love.graphics.setColor(r1, g1, b1, a)
|
||||
end
|
||||
|
||||
function TransitionScreen:onLifeover()
|
||||
if self.newState then
|
||||
gamestate.switch(self.newState)
|
||||
end
|
||||
end
|
||||
|
||||
return TransitionScreen
|
||||
@@ -0,0 +1,52 @@
|
||||
local gamestate = require "lib.gamestate"
|
||||
local Level = require "src.states.Level"
|
||||
local TimerEvent = require "src.entities.TimerEvent"
|
||||
local ScreenSplash = require "src.entities.ScreenSplash"
|
||||
local TransitionScreen = require "src.entities.TransitionScreen"
|
||||
local Level = require "src.states.Level"
|
||||
local assets = require "src.assets"
|
||||
local sti = require("lib.sti")
|
||||
local gamera = require("lib.gamera")
|
||||
|
||||
local Intro = class "Intro"
|
||||
|
||||
function Intro:load()
|
||||
|
||||
local tileMap = sti.new("assets/intro")
|
||||
local w, h = tileMap.tilewidth * tileMap.width, tileMap.tileheight * tileMap.height
|
||||
local camera = gamera.new(0, 0, w, h)
|
||||
camera:setPosition(600, 600)
|
||||
camera:setScale(2)
|
||||
|
||||
self.time = 0
|
||||
self.world = tiny.world(
|
||||
require ("src.systems.DrawBackgroundSystem")(140, 205, 255),
|
||||
require ("src.systems.FadeSystem")(),
|
||||
require ("src.systems.LifetimeSystem")(),
|
||||
require ("src.systems.TileMapRenderSystem")(camera, tileMap),
|
||||
require ("src.systems.SpriteSystem")(camera, "bg"),
|
||||
require ("src.systems.SpriteSystem")(camera, "fg"),
|
||||
require ("src.systems.HudSystem")(self, "hudBg"),
|
||||
require ("src.systems.HudSystem")(self, "hudFg"),
|
||||
TransitionScreen(),
|
||||
ScreenSplash(0.5, 0.2, "Cammando Kibbles"),
|
||||
ScreenSplash(0, 0, "Created by bakpakin for Ludum Dare 32", 300, assets.fnt_reallysmallhud, "left", 20, 20),
|
||||
ScreenSplash(0.5, 0.36, "Press Space to Start", 500, assets.fnt_smallhud),
|
||||
ScreenSplash(0.5, 0.45, "Controls:\nMove - WASD\nRotate Cannon - Arrow Keys\nFire - Down\nToggle Fullscreen - \\\nToggle Music - M\nPause - P\nEscape - Quit", 800, assets.fnt_reallysmallhud)
|
||||
)
|
||||
_G.world = self.world
|
||||
_G.camera = camera
|
||||
end
|
||||
|
||||
function Intro:update(dt)
|
||||
self.time = self.time + dt
|
||||
if love.keyboard.isDown(" ") and self.time > 0.55 then
|
||||
world:add(TransitionScreen(true, Level("assets/lvl1")))
|
||||
end
|
||||
end
|
||||
|
||||
function Intro:draw()
|
||||
|
||||
end
|
||||
|
||||
return Intro
|
||||
@@ -0,0 +1,129 @@
|
||||
local sti = require("lib.sti")
|
||||
local bump = require("lib.bump")
|
||||
local gamera = require("lib.gamera")
|
||||
local TimerEvent = require "src.entities.TimerEvent"
|
||||
local ScreenSplash = require "src.entities.ScreenSplash"
|
||||
local TransitionScreen = require "src.entities.TransitionScreen"
|
||||
|
||||
local Level = class "Level"
|
||||
|
||||
local waveTable = {4, 10, 20, 50, 80, 100, 120, 150, 180, 200, 250}
|
||||
local waveSpawnSpeeds = {1, 1, 1.5, 2, 2, 2.2, 3, 3, 4, 5, 7}
|
||||
|
||||
function Level:init(mappath)
|
||||
self.mappath = mappath
|
||||
end
|
||||
|
||||
function Level:nextWave()
|
||||
self.wave = self.wave + 1
|
||||
self.enemiesKilled = 0
|
||||
self.totalEnemiesToKill = waveTable[self.wave] or math.huge
|
||||
self.enemiesSpawned = 0
|
||||
self.spawnInterval = 3 / (waveSpawnSpeeds[self.wave] or 10)
|
||||
self.isSpawning = true
|
||||
if self.wave == #waveTable + 1 then
|
||||
local splash = ScreenSplash(0.5, 0.5, "Kill Them ALL!!")
|
||||
world:add(TimerEvent(1, function() world:add(splash) end),
|
||||
TimerEvent(3, function() world:remove(splash) end))
|
||||
end
|
||||
end
|
||||
|
||||
function Level:load()
|
||||
local tileMap = sti.new(self.mappath)
|
||||
local bumpWorld = bump.newWorld(tileMap.tilewidth * 2)
|
||||
local w, h = tileMap.tilewidth * tileMap.width, tileMap.tileheight * tileMap.height
|
||||
local camera = gamera.new(0, 0, w, h)
|
||||
|
||||
self.wave = 0
|
||||
self:nextWave()
|
||||
self.score = 0
|
||||
|
||||
self.spawnerCount = 0
|
||||
self.spawners = {}
|
||||
|
||||
self.tileMap = tileMap
|
||||
self.bumpWorld = bumpWorld
|
||||
self.camera = camera
|
||||
|
||||
self.aiSystem = require ("src.systems.AISystem")()
|
||||
|
||||
local r, g, b = tileMap.backgroundcolor[1], tileMap.backgroundcolor[2], tileMap.backgroundcolor[3]
|
||||
|
||||
camera:setScale(2)
|
||||
local world = tiny.world(
|
||||
require ("src.systems.DrawBackgroundSystem")(r, g, b),
|
||||
require ("src.systems.UpdateSystem")(),
|
||||
require ("src.systems.PlayerControlSystem")(),
|
||||
self.aiSystem,
|
||||
require ("src.systems.FadeSystem")(),
|
||||
require ("src.systems.PlatformingSystem")(),
|
||||
require ("src.systems.BumpPhysicsSystem")(bumpWorld),
|
||||
require ("src.systems.CameraTrackingSystem")(camera),
|
||||
require ("src.systems.TileMapRenderSystem")(camera, tileMap),
|
||||
require ("src.systems.SpriteSystem")(camera, "bg"),
|
||||
require ("src.systems.SpriteSystem")(camera, "fg"),
|
||||
require ("src.systems.LifetimeSystem")(),
|
||||
require ("src.systems.HudSystem")(self, "hudBg"),
|
||||
require ("src.systems.HudSystem")(self, "hudFg"),
|
||||
require ("src.systems.WaveSystem")(self),
|
||||
require ("src.systems.SpawnSystem")(self),
|
||||
require ("src.entities.MainHud")(self),
|
||||
TransitionScreen()
|
||||
)
|
||||
|
||||
local player = nil
|
||||
|
||||
for lindex, layer in ipairs(tileMap.layers) do
|
||||
if layer.properties.collidable == "true" then
|
||||
-- Entire layer
|
||||
if layer.type == "tilelayer" then
|
||||
local prefix = layer.properties.oneway == "true" and "o(" or "t("
|
||||
for y, tiles in ipairs(layer.data) do
|
||||
for x, tile in pairs(tiles) do
|
||||
bumpWorld:add(
|
||||
prefix..layer.name..", "..x..", "..y..")",
|
||||
x * tileMap.tilewidth + tile.offset.x,
|
||||
y * tileMap.tileheight + tile.offset.y,
|
||||
tile.width,
|
||||
tile.height
|
||||
)
|
||||
end
|
||||
end
|
||||
elseif layer.type == "imagelayer" then
|
||||
bumpWorld:add(
|
||||
layer.name,
|
||||
layer.x or 0,
|
||||
layer.y or 0,
|
||||
layer.width,
|
||||
layer.height
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
if layer.type == "objectgroup" then
|
||||
for _, object in ipairs(layer.objects) do
|
||||
local ctor = require("src.entities." .. object.type)
|
||||
local e = ctor(object)
|
||||
if object.type == "Player" then
|
||||
player = e
|
||||
end
|
||||
world:add(e)
|
||||
end
|
||||
tileMap:removeLayer(lindex)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
self.aiSystem.target = player
|
||||
|
||||
-- add ends to prevent objects from falling off the edge of the world.
|
||||
bumpWorld:add("_leftBlock", -16, 0, 16, h)
|
||||
bumpWorld:add("_rightBlock", w, 0, 16, h)
|
||||
bumpWorld:add("_topBlock", 0, -16, w, 16)
|
||||
|
||||
-- globals
|
||||
_G.camera = camera
|
||||
_G.world = world
|
||||
end
|
||||
|
||||
return Level
|
||||
@@ -0,0 +1,26 @@
|
||||
local AISystem = tiny.processingSystem(class "AISystem")
|
||||
|
||||
function AISystem:init(target)
|
||||
self.target = target
|
||||
end
|
||||
|
||||
AISystem.filter = tiny.requireAll("ai", "pos", "platforming")
|
||||
|
||||
function AISystem:process(e, dt)
|
||||
if not self.target then
|
||||
return
|
||||
end
|
||||
local targetx = self.target.pos.x
|
||||
local pos = e.pos
|
||||
local p = e.platforming
|
||||
p.moving = self.target.isAlive
|
||||
if targetx > pos.x then
|
||||
p.direction = 'r'
|
||||
end
|
||||
if targetx < pos.x then
|
||||
p.direction = 'l'
|
||||
end
|
||||
p.jumping = math.random() < 0.5 * dt
|
||||
end
|
||||
|
||||
return AISystem
|
||||
@@ -0,0 +1,105 @@
|
||||
local BumpPhysicsSystem = tiny.processingSystem(class "BumpPhysicsSystem")
|
||||
|
||||
function BumpPhysicsSystem:init(bumpWorld)
|
||||
self.bumpWorld = bumpWorld
|
||||
end
|
||||
|
||||
BumpPhysicsSystem.filter = tiny.requireAll("pos", "vel", "hitbox")
|
||||
|
||||
local oneWayPrefix = "o"
|
||||
oneWayPrefix = oneWayPrefix:byte(1)
|
||||
local function collisionFilter(e1, e2)
|
||||
if e1.isPlayer then
|
||||
if e2.isBullet then return nil end
|
||||
if e2.isEnemy then return 'cross' end
|
||||
elseif e1.isEnemy then
|
||||
if e2.isBullet then return nil end
|
||||
if e2.isEnemy then return nil end
|
||||
if e2.isPlayer then return 'cross' end
|
||||
elseif e1.isBullet then
|
||||
if e2.isPlayer or e2.isBullet then return nil end
|
||||
end
|
||||
if e1.isSolid then
|
||||
if type(e2) == "string" then -- tile collision
|
||||
if e2:byte(1) == oneWayPrefix then -- one way tile
|
||||
if e1.isBullet then
|
||||
return 'onewayplatformTouch'
|
||||
else
|
||||
return 'onewayplatform'
|
||||
end
|
||||
else
|
||||
return 'slide'
|
||||
end
|
||||
elseif e2.isSolid then
|
||||
return 'slide'
|
||||
elseif e2.isBouncy then
|
||||
return 'bounce'
|
||||
else
|
||||
return 'cross'
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function BumpPhysicsSystem:process(e, dt)
|
||||
local pos = e.pos
|
||||
local vel = e.vel
|
||||
local gravity = e.gravity or 0
|
||||
vel.y = vel.y + gravity * dt
|
||||
local cols, len
|
||||
pos.x, pos.y, cols, len = self.bumpWorld:move(e, pos.x + vel.x * dt, pos.y + vel.y * dt, collisionFilter)
|
||||
e.grounded = false
|
||||
for i = 1, len do
|
||||
local col = cols[i]
|
||||
local collided = true
|
||||
if col.type == "touch" then
|
||||
vel.x, vel.y = 0, 0
|
||||
elseif col.type == "slide" then
|
||||
if col.normal.x == 0 then
|
||||
vel.y = 0
|
||||
if col.normal.y < 0 then
|
||||
e.grounded = true
|
||||
end
|
||||
else
|
||||
vel.x = 0
|
||||
end
|
||||
elseif col.type == "onewayplatform" then
|
||||
if col.didTouch then
|
||||
vel.y = 0
|
||||
e.grounded = true
|
||||
else
|
||||
collided = false
|
||||
end
|
||||
elseif col.type == "onewayplatformTouch" then
|
||||
if col.didTouch then
|
||||
vel.y = 0
|
||||
e.grounded = true
|
||||
else
|
||||
collided = false
|
||||
end
|
||||
elseif col.type == "bounce" then
|
||||
if col.normal.x == 0 then
|
||||
vel.y = -vel.y
|
||||
e.grounded = true
|
||||
else
|
||||
vel.x = -vel.x
|
||||
end
|
||||
end
|
||||
|
||||
if e.onCollision and collided then
|
||||
e:onCollision(col)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function BumpPhysicsSystem:onAdd(e)
|
||||
local pos = e.pos
|
||||
local hitbox = e.hitbox
|
||||
self.bumpWorld:add(e, pos.x, pos.y, hitbox.w, hitbox.h)
|
||||
end
|
||||
|
||||
function BumpPhysicsSystem:onRemove(e)
|
||||
self.bumpWorld:remove(e)
|
||||
end
|
||||
|
||||
return BumpPhysicsSystem
|
||||
@@ -0,0 +1,27 @@
|
||||
local CameraTrackingSystem = tiny.processingSystem(class "CameraTrackingSystem")
|
||||
|
||||
CameraTrackingSystem.filter = tiny.requireAll("cameraTrack", "pos")
|
||||
|
||||
function CameraTrackingSystem:init(camera)
|
||||
self.camera = camera
|
||||
end
|
||||
|
||||
local function round(x)
|
||||
return math.floor(x * 32 + 16) / 32
|
||||
end
|
||||
|
||||
function CameraTrackingSystem:process(e, dt)
|
||||
local xo, yo = e.cameraTrack.xoffset, e.cameraTrack.yoffset
|
||||
local x, y = e.pos.x + xo, e.pos.y + yo
|
||||
local xp, yp = self.camera:getPosition()
|
||||
local lerp = 0.1
|
||||
self.camera:setPosition(round(xp + (x - xp) * lerp), round(yp + (y - yp) * lerp))
|
||||
end
|
||||
|
||||
function CameraTrackingSystem:onAdd(e)
|
||||
local xo, yo = e.cameraTrack.xoffset, e.cameraTrack.yoffset
|
||||
local x, y = e.pos.x + xo, e.pos.y + yo
|
||||
self.camera:setPosition(round(x), round(y))
|
||||
end
|
||||
|
||||
return CameraTrackingSystem
|
||||
@@ -0,0 +1,14 @@
|
||||
local DrawBackgroundSystem = tiny.system(class "DrawBackgroundSystem")
|
||||
|
||||
function DrawBackgroundSystem:init(r, g, b)
|
||||
self.r, self.g, self.b = r, g, b
|
||||
end
|
||||
|
||||
function DrawBackgroundSystem:update(entities, dt)
|
||||
local r1, g1, b1, a = love.graphics.getColor()
|
||||
love.graphics.setColor(self.r, self.g, self.b, 255)
|
||||
love.graphics.rectangle("fill", 0, 0, love.graphics.getWidth(), love.graphics.getHeight())
|
||||
love.graphics.setColor(r1, g1, b1, a)
|
||||
end
|
||||
|
||||
return DrawBackgroundSystem
|
||||
@@ -0,0 +1,9 @@
|
||||
local FadeSystem = tiny.processingSystem(class "FadeSystem")
|
||||
|
||||
FadeSystem.filter = tiny.requireAll("fadeTime", "alpha")
|
||||
|
||||
function FadeSystem:process(e, dt)
|
||||
e.alpha = math.min(1, math.max(0, e.alpha - dt / e.fadeTime))
|
||||
end
|
||||
|
||||
return FadeSystem
|
||||
@@ -0,0 +1,12 @@
|
||||
local HudSystem = tiny.processingSystem(class "HudSystem")
|
||||
|
||||
function HudSystem:init(levelState, layerFlag)
|
||||
self.levelState = levelState
|
||||
self.filter = tiny.requireAll("drawHud", layerFlag)
|
||||
end
|
||||
|
||||
function HudSystem:process(e, dt)
|
||||
e:drawHud(dt)
|
||||
end
|
||||
|
||||
return HudSystem
|
||||
@@ -0,0 +1,15 @@
|
||||
local LifetimeSystem = tiny.processingSystem(class "LifetimeSystem")
|
||||
|
||||
LifetimeSystem.filter = tiny.requireAll("lifetime")
|
||||
|
||||
function LifetimeSystem:process(e, dt)
|
||||
e.lifetime = e.lifetime - dt
|
||||
if e.lifetime <= 0 then
|
||||
if e.onLifeover then
|
||||
e:onLifeover()
|
||||
end
|
||||
world:remove(e)
|
||||
end
|
||||
end
|
||||
|
||||
return LifetimeSystem
|
||||
@@ -0,0 +1,40 @@
|
||||
local assets = require "src.assets"
|
||||
|
||||
local PlatformingSystem = tiny.processingSystem(class "PlatformingSystem")
|
||||
|
||||
PlatformingSystem.filter = tiny.requireAll("pos", "vel", "platforming")
|
||||
|
||||
function PlatformingSystem:process(e, dt)
|
||||
local pos = e.pos
|
||||
local vel = e.vel
|
||||
local platforming = e.platforming
|
||||
local acceleration = platforming.acceleration
|
||||
local friction = platforming.friction
|
||||
local speed = platforming.speed
|
||||
local direction = platforming.direction
|
||||
e.flippedH = direction == 'l'
|
||||
|
||||
if platforming.moving then
|
||||
if direction == 'l' then
|
||||
vel.x = math.max(-speed, vel.x - acceleration * dt)
|
||||
elseif direction == 'r' then
|
||||
vel.x = math.min(speed, vel.x + acceleration * dt)
|
||||
end
|
||||
elseif e.grounded then
|
||||
if vel.x > 0 then
|
||||
vel.x = math.max(0, vel.x - friction * dt)
|
||||
elseif vel.x < 0 then
|
||||
vel.x = math.min(0, vel.x + friction * dt)
|
||||
end
|
||||
end
|
||||
|
||||
if platforming.jumping and e.grounded then
|
||||
vel.y = -platforming.jump
|
||||
e.grounded = false
|
||||
assets.snd_catjump:play()
|
||||
end
|
||||
|
||||
e.animation = platforming.moving and e.animation_walk or e.animation_stand
|
||||
end
|
||||
|
||||
return PlatformingSystem
|
||||
@@ -0,0 +1,50 @@
|
||||
local assets = require "src.assets"
|
||||
local Bullet = require "src.entities.Bullet"
|
||||
|
||||
local PlayerControlSystem = tiny.processingSystem(class "PlayerControlSystem")
|
||||
|
||||
PlayerControlSystem.filter = tiny.requireAll("controlable")
|
||||
|
||||
function PlayerControlSystem:process(e, dt)
|
||||
local vel = e.vel
|
||||
local p = e.platforming
|
||||
local l, r, u = love.keyboard.isDown('a'), love.keyboard.isDown('d'), love.keyboard.isDown('w')
|
||||
local gl, gr = love.keyboard.isDown('left'), love.keyboard.isDown('right')
|
||||
local fire = love.keyboard.isDown('down')
|
||||
|
||||
e.shotTimer = math.max(0, e.shotTimer - dt)
|
||||
|
||||
if l and not r then
|
||||
p.moving = true
|
||||
if p.direction == 'r' then
|
||||
e.gunAngle = math.pi * 3 - e.gunAngle
|
||||
end
|
||||
p.direction = 'l'
|
||||
elseif r and not l then
|
||||
p.moving = true
|
||||
if p.direction == 'l' then
|
||||
e.gunAngle = math.pi * 3 - e.gunAngle
|
||||
end
|
||||
p.direction = 'r'
|
||||
else
|
||||
p.moving = false
|
||||
end
|
||||
|
||||
p.jumping = u
|
||||
|
||||
if gr and not gl then
|
||||
e.gunAngle = math.min(2 * math.pi, e.gunAngle + 8 * dt)
|
||||
elseif gl and not gr then
|
||||
e.gunAngle = math.max(math.pi, e.gunAngle - 8 * dt)
|
||||
end
|
||||
|
||||
if e.hasGun and fire and e.shotTimer == 0 then
|
||||
local dx = e.platforming.direction == 'l' and 2 or -2
|
||||
local bullet = Bullet(e.pos.x + 16 + dx, e.pos.y + 9, e.gunAngle)
|
||||
assets.snd_cannon:play()
|
||||
world:add(bullet)
|
||||
e.shotTimer = e.shotInterval
|
||||
end
|
||||
end
|
||||
|
||||
return PlayerControlSystem
|
||||
@@ -0,0 +1,37 @@
|
||||
local TimerEvent = require "src.entities.TimerEvent"
|
||||
|
||||
local SpawnSystem = tiny.system(class "SpawnSystem")
|
||||
|
||||
SpawnSystem.filter = tiny.requireAll("isSpawner")
|
||||
|
||||
function SpawnSystem:init(levelState)
|
||||
self.levelState = levelState
|
||||
self.time = 0
|
||||
end
|
||||
|
||||
function SpawnSystem:update(entities, dt)
|
||||
self.time = self.time + dt
|
||||
local levelState = self.levelState
|
||||
if levelState.isSpawning and levelState.enemiesSpawned < levelState.totalEnemiesToKill and self.time >= levelState.spawnInterval then
|
||||
local choice = math.ceil(math.random() * levelState.spawnerCount)
|
||||
for spnr in pairs(levelState.spawners) do
|
||||
choice = choice - 1
|
||||
if choice == 0 then
|
||||
spnr:spawn()
|
||||
end
|
||||
end
|
||||
self.time = 0
|
||||
end
|
||||
end
|
||||
|
||||
function SpawnSystem:onAdd(e)
|
||||
self.levelState.spawners[e] = true
|
||||
self.levelState.spawnerCount = self.levelState.spawnerCount + 1
|
||||
end
|
||||
|
||||
function SpawnSystem:onRemove(e)
|
||||
self.levelState.spawners[e] = false
|
||||
self.levelState.spawnerCount = self.levelState.spawnerCount - 1
|
||||
end
|
||||
|
||||
return SpawnSystem
|
||||
@@ -0,0 +1,36 @@
|
||||
local SpriteSystem = tiny.processingSystem(class "SpriteSystem")
|
||||
|
||||
function SpriteSystem:init(camera, layerFlag)
|
||||
self.camera = camera
|
||||
self.filter = tiny.requireAll("sprite", "pos", layerFlag)
|
||||
end
|
||||
|
||||
function SpriteSystem:preProcess(entities, dt)
|
||||
self.camera:apply()
|
||||
end
|
||||
|
||||
function SpriteSystem:postProcess(entities, dt)
|
||||
self.camera:remove()
|
||||
love.graphics.setColor(255, 255, 255, 255)
|
||||
end
|
||||
|
||||
function SpriteSystem:process(e, dt)
|
||||
local an = e.animation
|
||||
local alpha = e.alpha or 1
|
||||
local pos, sprite, scale, rot, offset = e.pos, e.sprite, e.scale, e.rot, e.offset
|
||||
local sx, sy, r, ox, oy = scale and scale.x or 1, scale and scale.y or 1, rot or 0, offset and offset.x or 0, offset and offset.y or 0
|
||||
love.graphics.setColor(255, 255, 255, math.max(0, math.min(1, alpha)) * 255)
|
||||
if an then
|
||||
an.flippedH = e.flippedH or false
|
||||
an.flippedV = e.flippedV or false
|
||||
an:update(dt)
|
||||
an:draw(sprite, pos.x, pos.y, r, sx, sy, ox, oy)
|
||||
else
|
||||
love.graphics.draw(sprite, pos.x, pos.y, r, sx, sy, ox, oy)
|
||||
end
|
||||
if e.draw then
|
||||
e:draw(dt)
|
||||
end
|
||||
end
|
||||
|
||||
return SpriteSystem
|
||||
@@ -0,0 +1,16 @@
|
||||
local TileMapRenderSystem = tiny.system(class "TileMapRenderSystem")
|
||||
|
||||
function TileMapRenderSystem:init(camera, tileMap)
|
||||
self.camera = camera
|
||||
function self.drawFn(l, t, w, h)
|
||||
tileMap:update(dt)
|
||||
tileMap:setDrawRange(-l, -t, w, h)
|
||||
tileMap:draw()
|
||||
end
|
||||
end
|
||||
|
||||
function TileMapRenderSystem:update(entities, dt)
|
||||
self.camera:draw(self.drawFn)
|
||||
end
|
||||
|
||||
return TileMapRenderSystem
|
||||
@@ -0,0 +1,9 @@
|
||||
local UpdateSystem = tiny.processingSystem(class "UpdateSystem")
|
||||
|
||||
UpdateSystem.filter = tiny.requireAll("update")
|
||||
|
||||
function UpdateSystem:process(e, dt)
|
||||
e:update(dt)
|
||||
end
|
||||
|
||||
return UpdateSystem
|
||||
@@ -0,0 +1,27 @@
|
||||
local TimerEvent = require "src.entities.TimerEvent"
|
||||
|
||||
local WaveSystem = tiny.system(class "WaveSystem")
|
||||
|
||||
WaveSystem.filter = tiny.requireOne("isEnemy")
|
||||
|
||||
function WaveSystem:init(levelState)
|
||||
self.levelState = levelState
|
||||
end
|
||||
|
||||
function WaveSystem:onAdd(e)
|
||||
self.levelState.enemiesSpawned = self.levelState.enemiesSpawned + 1
|
||||
end
|
||||
|
||||
function WaveSystem:onRemove(e)
|
||||
local levelState = self.levelState
|
||||
levelState.enemiesKilled = levelState.enemiesKilled + 1
|
||||
if levelState.enemiesKilled >= levelState.totalEnemiesToKill then
|
||||
world:add(TimerEvent(1, function()
|
||||
levelState:nextWave()
|
||||
end))
|
||||
end
|
||||
end
|
||||
|
||||
function WaveSystem:update(entities, dt) end
|
||||
|
||||
return WaveSystem
|
||||
Reference in New Issue
Block a user