summaryrefslogtreecommitdiffhomepage
path: root/not
diff options
context:
space:
mode:
Diffstat (limited to 'not')
-rw-r--r--not/Cloud.lua63
-rw-r--r--not/Decoration.lua34
-rw-r--r--not/Effect.lua68
-rw-r--r--not/Hero.lua449
-rw-r--r--not/Platform.lua73
-rw-r--r--not/Sprite.lua81
-rw-r--r--not/World.lua424
7 files changed, 1192 insertions, 0 deletions
diff --git a/not/Cloud.lua b/not/Cloud.lua
new file mode 100644
index 0000000..c12e236
--- /dev/null
+++ b/not/Cloud.lua
@@ -0,0 +1,63 @@
+-- `Cloud`
+-- That white thing moving in the background.
+
+-- WHOLE CODE HAS FLAG OF "need a cleanup"
+
+-- Metatable of `Cloud`
+-- nils initialized in constructor
+Cloud = {
+ x = 0, -- position horizontal
+ y = 0, -- position vertical
+ t = 1, -- type (sprite number)
+ v = 13, -- velocity
+ sprite = nil,
+ quads = {
+ [1] = love.graphics.newQuad( 1, 1, 158,47, 478,49),
+ [2] = love.graphics.newQuad(160, 1, 158,47, 478,49),
+ [3] = love.graphics.newQuad(319, 1, 158,47, 478,49)
+ }
+}
+
+-- Constructor of `Cloud`
+function Cloud:new(x, y, t, v)
+ -- Meta
+ local o = {}
+ setmetatable(o, self)
+ self.__index = self
+ -- Load spritesheet to metatable if not yet loaded
+ if self.sprite == nil then
+ self.sprite = love.graphics.newImage("assets/clouds.png")
+ end
+ -- Init
+ o.x = x or self.x
+ o.y = y or self.y
+ o.t = t or self.t
+ o.v = v or self.v
+ return o
+end
+
+-- Position
+function Cloud:getPosition()
+ return self.x, self.y
+end
+
+-- Update of `Cloud`, returns x for world to delete cloud after reaching right corner
+function Cloud:update(dt)
+ self.x = self.x + self.v*dt
+ return self.x
+end
+
+-- Draw `Cloud`
+function Cloud:draw(offset_x, offset_y, scale)
+ -- locals
+ local offset_x = offset_x or 0
+ local offset_y = offset_y or 0
+ local scale = scale or 1
+ local x, y = self:getPosition()
+ -- pixel grid
+ local draw_x = (math.floor(x) + offset_x) * scale
+ local draw_y = (math.floor(y) + offset_y) * scale
+ -- draw
+ love.graphics.setColor(255,255,255,255)
+ love.graphics.draw(self.sprite, self.quads[self.t], draw_x, draw_y, 0, scale, scale)
+end \ No newline at end of file
diff --git a/not/Decoration.lua b/not/Decoration.lua
new file mode 100644
index 0000000..7f020e1
--- /dev/null
+++ b/not/Decoration.lua
@@ -0,0 +1,34 @@
+require "not.Sprite"
+Decoration = {
+ world = nil,
+ sprite = nil,
+ x = 0,
+ y = 0
+}
+Decoration.__index = Decoration
+setmetatable(Decoration, Sprite)
+function Decoration:new(x, y, sprite)
+ local o = {}
+ setmetatable(o, self)
+ o:setImage(love.graphics.newImage(sprite))
+ o:setPosition(x,y)
+ return o
+end
+function Decoration:setPosition(x, y)
+ self.x, self.y = x, y
+end
+function Decoration:getPosition()
+ return self.x, self.y
+end
+function Decoration:draw(offset_x, offset_y, scale)
+ -- locals
+ local offset_x = offset_x or 0
+ local offset_y = offset_y or 0
+ local scale = scale or 1
+ local x, y = self:getPosition()
+ -- pixel grid
+ local draw_x = (math.floor(x) + offset_x) * scale
+ local draw_y = (math.floor(y) + offset_y) * scale
+ -- draw
+ Sprite.draw(self, draw_x, draw_y, 0, scale, scale)
+end \ No newline at end of file
diff --git a/not/Effect.lua b/not/Effect.lua
new file mode 100644
index 0000000..4e327a1
--- /dev/null
+++ b/not/Effect.lua
@@ -0,0 +1,68 @@
+-- `Effect`
+-- Short animation with graphics that plays in various situation.
+
+-- Metatable of `Effect`
+-- nils initialized in constructor
+Effect = {
+ x = 0,
+ y = 0,
+ delay = 0.06,
+ initial = nil,
+ frame = 1,
+ animation = nil,
+ sprite = nil,
+ quads = require "effects"
+}
+
+-- Construct of `Effect`
+function Effect:new(name, x, y)
+ -- Meta
+ local o = {}
+ setmetatable(o, self)
+ self.__index = self
+ -- Load spritesheet to metatable if not yet loaded
+ if self.sprite == nil then
+ self.sprite = love.graphics.newImage("assets/effects.png")
+ end
+ -- Init
+ o.initial = o.delay
+ o.animation = name
+ o.x = x or self.x
+ o.y = y or self.y
+ return o
+end
+
+-- Position
+function Effect:getPosition()
+ return self.x, self.y
+end
+
+-- Animation and return flag for deletion after completion
+-- returns true if completed and ready to delete
+function Effect:update(dt)
+ self.delay = self.delay - dt
+ if self.delay < 0 then
+ if self.frame < self.quads[self.animation].frames then
+ self.frame = self.frame + 1
+ self.delay = self.delay + self.initial
+ else
+ return true -- delete
+ end
+ end
+ return false
+end
+
+-- Draw me with scale and offsets, senpai
+function Effect:draw(offset_x, offset_y, scale)
+ -- locals
+ local offset_x = offset_x or 0
+ local offset_y = offset_y or 0
+ local scale = scale or 1
+ local x, y = self:getPosition()
+ -- pixel grid
+ local draw_x = (math.floor(x) + offset_x) * scale
+ local draw_y = (math.floor(y) + offset_y) * scale
+ -- draw
+ love.graphics.setColor(255,255,255,255)
+ love.graphics.draw(self.sprite, self.quads[self.animation][self.frame], draw_x, draw_y, 0, scale, scale)
+end \ No newline at end of file
diff --git a/not/Hero.lua b/not/Hero.lua
new file mode 100644
index 0000000..7ddc724
--- /dev/null
+++ b/not/Hero.lua
@@ -0,0 +1,449 @@
+-- `Hero`
+-- Entity controlled by a player. It has a physical body and a sprite. Can play animations and interact with other instances of the same class.
+-- Collision category: [2]
+
+-- WHOLE CODE HAS FLAG OF "need a cleanup"
+require "not.Sprite"
+
+-- Metatable of `Hero`
+-- nils initialized in constructor
+Hero = {
+ -- General and physics
+ name = "empty",
+ body = nil,
+ shape = nil,
+ fixture = nil,
+ sprite = nil,
+ rotate = 0, -- "angle" would sound better
+ facing = 1,
+ max_velocity = 105,
+ world = nil, -- game world
+ -- Combat
+ combo = 0,
+ lives = 3,
+ spawntimer = 2,
+ alive = true,
+ punchcd = 0.25,
+ punchdir = 0, -- a really bad thing
+ -- Movement
+ inAir = true,
+ salto = false,
+ jumpactive = false,
+ jumpdouble = true,
+ jumptimer = 0.16,
+ jumpnumber = 2,
+ -- Keys
+ controlset = nil,
+ -- HUD
+ portrait_sprite = nil,
+ portrait_frame = nil,
+ portrait_sheet = require "nautsicons",
+ portrait_box = love.graphics.newQuad( 0, 15, 32,32, 80,130),
+ -- Sounds
+ sfx = require "sounds",
+ -- Animations table
+ animations = require "animations"
+}
+Hero.__index = Hero
+setmetatable(Hero, Sprite)
+
+-- Constructor of `Hero`
+function Hero:new (game, world, x, y, name)
+ -- Meta
+ local o = {}
+ setmetatable(o, self)
+ -- Physics
+ local group = -1-#game.Nauts
+ o.body = love.physics.newBody(world, x, y, "dynamic")
+ o.shape = love.physics.newRectangleShape(10, 16)
+ o.fixture = love.physics.newFixture(o.body, o.shape, 8)
+ o.fixture:setUserData(o)
+ o.fixture:setCategory(2)
+ o.fixture:setMask(2)
+ o.fixture:setGroupIndex(group)
+ o.body:setFixedRotation(true)
+ -- Misc
+ o.name = name or "empty"
+ o:setImage(newImage("assets/nauts/"..o.name..".png"))
+ o.world = game
+ o.punchcd = 0
+ -- Animation
+ o.current = o.animations.default
+ o:createEffect("respawn")
+ -- Portrait load for first object created
+ if self.portrait_sprite == nil then
+ self.portrait_sprite = love.graphics.newImage("assets/portraits.png")
+ self.portrait_frame = love.graphics.newImage("assets/menu.png")
+ end
+ return o
+end
+
+-- Control set managment
+function Hero:assignControlSet(set)
+ self.controlset = set
+end
+function Hero:getControlSet()
+ return self.controlset
+end
+
+-- Update callback of `Hero`
+function Hero:update(dt)
+ -- hotfix? for destroyed bodies
+ if self.body:isDestroyed() then return end
+ -- locals
+ local x, y = self.body:getLinearVelocity()
+ local isDown = Controller.isDown
+ local controlset = self:getControlSet()
+
+ -- # VERTICAL MOVEMENT
+ -- Jumping
+ if self.jumpactive and self.jumptimer > 0 then
+ self.body:setLinearVelocity(x,-160)
+ self.jumptimer = self.jumptimer - dt
+ end
+
+ -- Salto
+ if self.salto and (self.current == self.animations.walk or self.current == self.animations.default) then
+ self.rotate = (self.rotate + 17 * dt * self.facing) % 360
+ elseif self.rotate ~= 0 then
+ self.rotate = 0
+ end
+
+ -- # HORIZONTAL MOVEMENT
+ -- Walking
+ if isDown(controlset, "left") then
+ self.facing = -1
+ self.body:applyForce(-250, 0)
+ -- Controlled speed limit
+ if x < -self.max_velocity then
+ self.body:applyForce(250, 0)
+ end
+ end
+ if isDown(controlset, "right") then
+ self.facing = 1
+ self.body:applyForce(250, 0)
+ -- Controlled speed limit
+ if x > self.max_velocity then
+ self.body:applyForce(-250, 0)
+ end
+ end
+
+ -- Custom linear damping
+ if not isDown(controlset, "left") and
+ not isDown(controlset, "right")
+ then
+ local face = nil
+ if x < -12 then
+ face = 1
+ elseif x > 12 then
+ face = -1
+ else
+ face = 0
+ end
+ self.body:applyForce(40*face,0)
+ if not self.inAir then
+ self.body:applyForce(80*face,0)
+ end
+ end
+
+ Sprite.update(self, dt)
+
+ -- # DEATH
+ -- We all die in the end.
+ local m = self.world.map
+ if (self.body:getX() < m.center_x - m.width*1.5 or self.body:getX() > m.center_x + m.width*1.5 or
+ self.body:getY() < m.center_y - m.height*1.5 or self.body:getY() > m.center_y + m.height*1.5) and
+ self.alive
+ then
+ self:die()
+ end
+
+ -- respawn
+ if self.spawntimer > 0 then
+ self.spawntimer = self.spawntimer - dt
+ end
+ if self.spawntimer <= 0 and not self.alive and self.lives >= 0 then
+ self:respawn()
+ end
+
+ -- # PUNCH
+ -- Cooldown
+ self.punchcd = self.punchcd - dt
+ if not self.body:isDestroyed() then -- This is weird
+ for _,fixture in pairs(self.body:getFixtureList()) do
+ if fixture:getUserData() ~= self then
+ fixture:setUserData({fixture:getUserData()[1] - dt, fixture:getUserData()[2]})
+ if fixture:getUserData()[1] < 0 then
+ fixture:destroy()
+ end
+ end
+ end
+ end
+
+ -- Stop vertical
+ local c,a = self.current, self.animations
+ if (c == a.attack_up or c == a.attack_down or c == a.attack) and self.frame < c.frames then
+ if self.punchdir == 0 then
+ self.body:setLinearVelocity(0,0)
+ else
+ self.body:setLinearVelocity(38*self.facing,0)
+ end
+ end
+
+ if self.punchcd <= 0 and self.punchdir == 1 then
+ self.punchdir = 0
+ end
+end
+
+-- Controller callbacks
+function Hero:controlpressed(set, action, key)
+ if set ~= self:getControlSet() then return end
+ local isDown = Controller.isDown
+ local controlset = self:getControlSet()
+ -- Jumping
+ if action == "jump" then
+ if self.jumpnumber > 0 then
+ -- General jump logics
+ self.jumpactive = true
+ --self:playSound(6)
+ -- Spawn proper effect
+ if not self.inAir then
+ self:createEffect("jump")
+ else
+ self:createEffect("doublejump")
+ end
+ -- Start salto if last jump
+ if self.jumpnumber == 1 then
+ self.salto = true
+ end
+ -- Animation clear
+ if (self.current == self.animations.attack) or
+ (self.current == self.animations.attack_up) or
+ (self.current == self.animations.attack_down) then
+ self:setAnimation("default")
+ end
+ -- Remove jump
+ self.jumpnumber = self.jumpnumber - 1
+ end
+ end
+
+ -- Walking
+ if (action == "left" or action == "right") and
+ (self.current ~= self.animations.attack) and
+ (self.current ~= self.animations.attack_up) and
+ (self.current ~= self.animations.attack_down) then
+ self:setAnimation("walk")
+ end
+
+ -- Punching
+ if action == "attack" and self.punchcd <= 0 then
+ local f = self.facing
+ self.salto = false
+ if isDown(controlset, "up") then
+ -- Punch up
+ if self.current ~= self.animations.damage then
+ self:setAnimation("attack_up")
+ end
+ self:hit("up")
+ elseif isDown(controlset, "down") then
+ -- Punch down
+ if self.current ~= self.animations.damage then
+ self:setAnimation("attack_down")
+ end
+ self:hit("down")
+ else
+ -- Punch horizontal
+ if self.current ~= self.animations.damage then
+ self:setAnimation("attack")
+ end
+ if f == 1 then
+ self:hit("right")
+ else
+ self:hit("left")
+ end
+ self.punchdir = 1
+ end
+ end
+end
+function Hero:controlreleased(set, action, key)
+ if set ~= self:getControlSet() then return end
+ local isDown = Controller.isDown
+ local controlset = self:getControlSet()
+ -- Jumping
+ if action == "jump" then
+ self.jumpactive = false
+ self.jumptimer = Hero.jumptimer -- take initial from metatable
+ end
+ -- Walking
+ if (action == "left" or action == "right") and not
+ (isDown(controlset, "left") or isDown(controlset, "right")) and
+ self.current == self.animations.walk
+ then
+ self:setAnimation("default")
+ end
+end
+
+-- Draw of `Hero`
+function Hero:draw(offset_x, offset_y, scale, debug)
+ -- draw only alive
+ if not self.alive then return end
+ -- locals
+ local offset_x = offset_x or 0
+ local offset_y = offset_y or 0
+ local scale = scale or 1
+ local debug = debug or false
+ local x, y = self:getPosition()
+ -- pixel grid ; `approx` selected to prevent floating characters on certain conditions
+ local approx = math.floor
+ if (y - math.floor(y)) > 0.5 then approx = math.ceil end
+ local draw_y = (approx(y) + offset_y) * scale
+ local draw_x = (math.floor(x) + offset_x) * scale
+ -- sprite draw
+ Sprite.draw(self, draw_x, draw_y, self.rotate, self.facing*scale, scale, 12, 15)
+ -- debug draw
+ if debug then
+ for _,fixture in pairs(self.body:getFixtureList()) do
+ if fixture:getCategory() == 2 then
+ love.graphics.setColor(137, 255, 0, 120)
+ else
+ love.graphics.setColor(137, 0, 255, 40)
+ end
+ love.graphics.polygon("fill", self.world.camera:translatePoints(self.body:getWorldPoints(fixture:getShape():getPoints())))
+ end
+ for _,contact in pairs(self.body:getContactList()) do
+ love.graphics.setColor(255, 0, 0, 255)
+ love.graphics.setPointSize(scale)
+ love.graphics.points(self.world.camera:translatePoints(contact:getPositions()))
+ end
+ end
+end
+
+-- getPosition
+function Hero:getPosition()
+ return self.body:getPosition()
+end
+
+-- Draw HUD of `Hero`
+-- elevation: 1 bottom, 0 top
+function Hero:drawHUD(x,y,scale,elevation)
+ -- hud displays only if player is alive
+ if self.alive then
+ love.graphics.setColor(255,255,255,255)
+ love.graphics.draw(self.portrait_frame, self.portrait_box, (x)*scale, (y)*scale, 0, scale, scale)
+ love.graphics.draw(self.portrait_sprite, self.portrait_sheet[self.name], (x+2)*scale, (y+3)*scale, 0, scale, scale)
+ local dy = 30 * elevation
+ love.graphics.setFont(Font)
+ love.graphics.print((self.combo*10).."%",(x+2)*scale,(y-3+dy)*scale,0,scale,scale)
+ love.graphics.print(math.max(0, self.lives),(x+24)*scale,(y-3+dy)*scale,0,scale,scale)
+ end
+end
+
+-- Change animation of `Hero`
+-- default, walk, attack, attack_up, attack_down, damage
+function Hero:nextFrame()
+ local isDown = Controller.isDown
+ local controlset = self:getControlSet()
+ if self.current.repeated or not (self.frame == self.current.frames) then
+ self.frame = (self.frame % self.current.frames) + 1
+ elseif isDown(controlset, "right") or isDown(controlset, "left") then
+ -- If nonrepeatable animation is finished and player is walking
+ self:setAnimation("walk")
+ elseif self.current == self.animations.damage then
+ self:setAnimation("default")
+ end
+end
+
+-- Spawn `Effect` relative to `Hero`
+function Hero:createEffect(name)
+ if name == "trail" or name == "hit" then
+ -- 16px effect: -7 -7
+ self.world:createEffect(name, self.body:getX()-8, self.body:getY()-8)
+ elseif name ~= nil then
+ -- 24px effect: -12 -15
+ self.world:createEffect(name, self.body:getX()-12, self.body:getY()-15)
+ end
+end
+
+-- Punch of `Hero`
+-- direction: left, right, up, down
+-- creates temporary fixture for player's body that acts as sensor; fixture is deleted after time set in UserData[1]; deleted by Hero:update(dt)
+function Hero:hit(direction)
+ -- start cooldown
+ self.punchcd = Hero.punchcd -- INITIAL from metatable
+ -- actual punch
+ local fixture
+ if direction == "left" then
+ fixture = love.physics.newFixture(self.body, love.physics.newPolygonShape(-2,-6, -20,-6, -20,6, -2,6), 0)
+ end
+ if direction == "right" then
+ fixture = love.physics.newFixture(self.body, love.physics.newPolygonShape(2,-6, 20,-6, 20,6, 2,6), 0)
+ end
+ if direction == "up" then
+ fixture = love.physics.newFixture(self.body, love.physics.newPolygonShape(-8,-4, -8,-20, 8,-20, 8,-4), 0)
+ end
+ if direction == "down" then
+ fixture = love.physics.newFixture(self.body, love.physics.newPolygonShape(-8,4, -8,20, 8,20, 8,4), 0)
+ end
+ fixture:setSensor(true)
+ fixture:setCategory(3)
+ fixture:setMask(1,3)
+ fixture:setGroupIndex(self.fixture:getGroupIndex())
+ fixture:setUserData({0.08, direction})
+ -- sound
+ self:playSound(4)
+end
+
+-- Taking damage of `Hero` by successful hit test
+-- currently called from World's startContact
+function Hero:damage(direction)
+ local horizontal, vertical = 0, 0
+ if direction == "left" then
+ horizontal = -1
+ end
+ if direction == "right" then
+ horizontal = 1
+ end
+ if direction == "up" then
+ vertical = -1
+ end
+ if direction == "down" then
+ vertical = 1
+ end
+ self:createEffect("hit")
+ local x,y = self.body:getLinearVelocity()
+ self.body:setLinearVelocity(x,0)
+ self.body:applyLinearImpulse((42+10*self.combo)*horizontal, (68+10*self.combo)*vertical + 15)
+ self:setAnimation("damage")
+ self.combo = math.min(27, self.combo + 1)
+ self.punchcd = 0.08 + self.combo*0.006
+ self:playSound(2)
+end
+
+-- DIE
+function Hero:die()
+ self:playSound(1)
+ self.combo = Hero.combo -- INITIAL from metatable
+ self.lives = self.lives - 1
+ self.alive = false
+ self.spawntimer = Hero.spawntimer -- INITIAL from metatable
+ self.body:setActive(false)
+ self.world:onNautKilled(self)
+end
+
+-- And then respawn. Like Jon Snow.
+function Hero:respawn()
+ self.alive = true
+ self.body:setLinearVelocity(0,0)
+ self.body:setPosition(self.world:getSpawnPosition())
+ self.body:setActive(true)
+ self:createEffect("respawn")
+ self:playSound(7)
+end
+
+-- Sounds
+function Hero:playSound(sfx, force)
+ if self.alive or force then
+ local source = love.audio.newSource(self.sfx[sfx])
+ source:play()
+ end
+end
diff --git a/not/Platform.lua b/not/Platform.lua
new file mode 100644
index 0000000..9b7b03c
--- /dev/null
+++ b/not/Platform.lua
@@ -0,0 +1,73 @@
+-- `Platform`
+-- Static platform physical object with a sprite. `Players` can walk on it.
+-- Collision category: [1]
+
+-- WHOLE CODE HAS FLAG OF "need a cleanup"
+require "not.Sprite"
+
+-- Metatable of `Platform`
+-- nils initialized in constructor
+Platform = {
+ body = nil,
+ shape = nil,
+ fixture = nil,
+ world = nil,
+}
+Platform.__index = Platform
+setmetatable(Platform, Sprite)
+
+-- Constructor of `Platform`
+function Platform:new (game, world, x, y, shape, sprite, animations)
+ local o = {}
+ setmetatable(o, self)
+ o.body = love.physics.newBody(world, x, y)
+ -- MULTIPLE SHAPES NEED TO BE REWRITED!
+ o.shape = {}
+ if type(shape[1]) == "number" then
+ local poly = love.physics.newPolygonShape(shape)
+ table.insert(o.shape, poly)
+ o.fixture = love.physics.newFixture(o.body, poly)
+ o.fixture:setCategory(1)
+ o.fixture:setFriction(0.2)
+ else
+ for i,v in pairs(shape) do
+ local poly = love.physics.newPolygonShape(v)
+ table.insert(o.shape, poly)
+ local fixture = love.physics.newFixture(o.body, poly)
+ fixture:setCategory(1)
+ fixture:setFriction(0.2)
+ end
+ end
+ -- END HERE
+ o:setImage(love.graphics.newImage(sprite))
+ o:setAnimationsList(animations)
+ o.world = game
+ return o
+end
+
+-- Position
+function Platform:getPosition()
+ return self.body:getPosition()
+end
+
+-- Draw of `Platform`
+function Platform:draw (offset_x, offset_y, scale, debug)
+ -- locals
+ local offset_x = offset_x or 0
+ local offset_y = offset_y or 0
+ local scale = scale or 1
+ local debug = debug or false
+ local x, y = self:getPosition()
+ -- pixel grid
+ local draw_x = (math.floor(x) + offset_x) * scale
+ local draw_y = (math.floor(y) + offset_y) * scale
+ -- sprite draw
+ Sprite.draw(self, draw_x, draw_y, 0, scale, scale)
+ -- debug draw
+ if debug then
+ love.graphics.setColor(255, 69, 0, 140)
+ for i,v in pairs(self.shape) do
+ love.graphics.polygon("fill", self.world.camera:translatePoints(self.body:getWorldPoints(v:getPoints())))
+ end
+ end
+end \ No newline at end of file
diff --git a/not/Sprite.lua b/not/Sprite.lua
new file mode 100644
index 0000000..1cc46f7
--- /dev/null
+++ b/not/Sprite.lua
@@ -0,0 +1,81 @@
+-- `Sprite`
+-- Abstract class for drawable animated entities.
+
+-- Metatable
+Sprite = {
+ animations--[[table with animations]],
+ current--[[animations.default]],
+ image--[[love.graphics.newImage()]],
+ frame = 1,
+ delay = .1,
+}
+Sprite.__index = Sprite
+
+-- Cleans up reference to image on deletion.
+function Sprite:delete()
+ self.image = nil
+end
+
+-- Sets an Image as a image.
+function Sprite:setImage(image)
+ self.image = image
+end
+-- Returns current image Image.
+function Sprite:getImage()
+ return self.image
+end
+
+-- Sets new animations list.
+function Sprite:setAnimationsList(t)
+ if t then
+ self.animations = t
+ self:setAnimation("default")
+ end
+end
+
+-- Sets current animation by table key.
+function Sprite:setAnimation(animation)
+ self.frame = 1
+ self.delay = Sprite.delay -- INITIAL from metatable
+ self.current = self.animations[animation]
+end
+-- Returns current animation table.
+function Sprite:getAnimation()
+ return self.current
+end
+
+-- Get frame quad for drawing.
+function Sprite:getQuad()
+ if self.animations and self.current then
+ return self.current[self.frame]
+ end
+end
+
+-- Drawing self to LOVE2D buffer.
+-- If there is no Quad, it will draw entire image.
+function Sprite:draw(...)
+ local s, q = self:getImage(), self:getQuad()
+ if s then
+ love.graphics.setColor(255,255,255,255)
+ if q then love.graphics.draw(s, q, ...)
+ else love.graphics.draw(s, ...) end
+ end
+end
+-- Animation updating.
+function Sprite:update(dt)
+ if self.animations and self.current then
+ self.delay = self.delay - dt
+ if self.delay < 0 then
+ self.delay = self.delay + Sprite.delay -- INITIAL from metatable
+ self:nextFrame()
+ end
+ end
+end
+-- Moving to the next frame.
+function Sprite:nextFrame()
+ if self.current.repeated or not (self.frame == self.current.frames) then
+ self.frame = (self.frame % self.current.frames) + 1
+ else
+ self:setAnimation("default")
+ end
+end \ No newline at end of file
diff --git a/not/World.lua b/not/World.lua
new file mode 100644
index 0000000..5afc5bf
--- /dev/null
+++ b/not/World.lua
@@ -0,0 +1,424 @@
+-- `World`
+-- Used to manage physical world and everything inside it: clouds, platforms, nauts, background etc.
+
+-- WHOLE CODE HAS FLAG OF "need a cleanup"
+
+require "not.Platform"
+require "not.Hero"
+require "not.Cloud"
+require "not.Effect"
+require "not.Decoration"
+require "ray"
+
+-- Metatable of `World`
+-- nils initialized in constructor
+World = {
+ -- inside
+ world = nil,
+ Nauts = nil,
+ Platforms = nil,
+ Clouds = nil,
+ Decorations = nil,
+ Effects = nil,
+ Rays = nil,
+ camera = nil,
+ -- cloud generator
+ clouds_delay = 5,
+ -- Map
+ map = nil,
+ background = nil,
+ -- Gameplay status
+ lastNaut = false,
+ -- "WINNER"
+ win_move = 0,
+ -- Music
+ music = nil
+}
+
+-- Constructor of `World` ZA WARUDO!
+function World:new(map, nauts)
+ -- Meta
+ local o = {}
+ setmetatable(o, self)
+ self.__index = self
+ -- Physical world initialization
+ love.physics.setMeter(64)
+ o.world = love.physics.newWorld(0, 9.81*64, true)
+ o.world:setCallbacks(o.beginContact, o.endContact)
+ -- Empty tables for objects
+ local n = {}
+ o.Nauts = n
+ local p = {}
+ o.Platforms = {}
+ local c = {}
+ o.Clouds = c
+ local e = {}
+ o.Effects = e
+ local d = {}
+ o.Decorations = d
+ local r = {}
+ o.Rays = r
+ -- Random init
+ math.randomseed(os.time())
+ -- Map
+ local map = map or "default"
+ o:loadMap(map)
+ -- Nauts
+ o:spawnNauts(nauts)
+ -- Create camera
+ o.camera = Camera:new(o)
+ -- Play music
+ o.music = Music:new(o.map.theme)
+ return o
+end
+
+-- The end of the world
+function World:delete()
+ self.world:destroy()
+ for _,platform in pairs(self.Platforms) do
+ platform:delete()
+ end
+ for _,naut in pairs(self.Nauts) do
+ naut:delete()
+ end
+ self.music:delete()
+ self = nil
+end
+
+-- Load map from file
+function World:loadMap(name)
+ local name = name or "default"
+ name = "maps/" .. name .. ".lua"
+ local map = love.filesystem.load(name)
+ self.map = map()
+ -- Platforms
+ for _,platform in pairs(self.map.platforms) do
+ self:createPlatform(platform.x, platform.y, platform.shape, platform.sprite, platform.animations)
+ end
+ -- Decorations
+ for _,decoration in pairs(self.map.decorations) do
+ self:createDecoration(decoration.x, decoration.y, decoration.sprite)
+ end
+ -- Background
+ self.background = love.graphics.newImage(self.map.background)
+ -- Clouds
+ if self.map.clouds then
+ for i=1,6 do
+ self:randomizeCloud(false)
+ end
+ end
+end
+
+-- Spawn all the nauts for the round
+function World:spawnNauts(nauts)
+ for _,naut in pairs(nauts) do
+ local x,y = self:getSpawnPosition()
+ local spawn = self:createNaut(x, y, naut[1])
+ spawn:assignControlSet(naut[2])
+ end
+end
+
+-- Get respawn location
+function World:getSpawnPosition()
+ local n = math.random(1, #self.map.respawns)
+ return self.map.respawns[n].x, self.map.respawns[n].y
+end
+
+-- Add new platform to the world
+function World:createPlatform(x, y, polygon, sprite, animations)
+ table.insert(self.Platforms, Platform:new(self, self.world, x, y, polygon, sprite, animations))
+end
+
+-- Add new naut to the world
+function World:createNaut(x, y, name)
+ local naut = Hero:new(self, self.world, x, y, name)
+ table.insert(self.Nauts, naut)
+ return naut
+end
+
+-- Add new decoration to the world
+function World:createDecoration(x, y, sprite)
+ table.insert(self.Decorations, Decoration:new(x, y, sprite))
+end
+
+-- Add new cloud to the world
+function World:createCloud(x, y, t, v)
+ table.insert(self.Clouds, Cloud:new(x, y, t, v))
+end
+
+-- Randomize Cloud creation
+function World:randomizeCloud(outside)
+ if outside == nil then
+ outside = true
+ else
+ outside = outside
+ end
+ local x,y,t,v
+ local m = self.map
+ if outside then
+ x = m.center_x-m.width*1.2+math.random(-50,20)
+ else
+ x = math.random(m.center_x-m.width/2,m.center_x+m.width/2)
+ end
+ y = math.random(m.center_y-m.height/2, m.center_y+m.height/2)
+ t = math.random(1,3)
+ v = math.random(8,18)
+ self:createCloud(x, y, t, v)
+end
+
+-- Add an effect behind nauts
+function World:createEffect(name, x, y)
+ table.insert(self.Effects, Effect:new(name, x, y))
+end
+
+-- Add a ray
+function World:createRay(naut)
+ table.insert(self.Rays, Ray:new(naut, self))
+end
+
+-- get Nauts functions
+-- more than -1 lives
+function World:getNautsPlayable()
+ local nauts = {}
+ for _,naut in pairs(self.Nauts) do
+ if naut.lives > -1 then
+ table.insert(nauts, naut)
+ end
+ end
+ return nauts
+end
+-- are alive
+function World:getNautsAlive()
+ local nauts = {}
+ for _,naut in self.Nauts do
+ if naut.alive then
+ table.insert(nauts, naut)
+ end
+ end
+ return nauts
+end
+-- all of them
+function World:getNautsAll()
+ return self.Nauts
+end
+
+-- get Map name
+function World:getMapName()
+ return self.map.name
+end
+
+-- Event: when player is killed
+function World:onNautKilled(naut)
+ self.camera:startShake()
+ self:createRay(naut)
+ local nauts = self:getNautsPlayable()
+ if self.lastNaut then
+ changeScene(Menu:new())
+ elseif #nauts < 2 then
+ self.lastNaut = true
+ naut:playSound(5, true)
+ end
+end
+
+function World:getBounce(f)
+ local f = f or 1
+ return math.sin(self.win_move*f*math.pi)
+end
+
+-- LÖVE2D callbacks
+-- Update ZU WARUDO
+function World:update(dt)
+ -- Physical world
+ self.world:update(dt)
+ -- Camera
+ self.camera:update(dt)
+ -- Engine world: Nauts, Grounds (kek) and Decorations - all Animateds (top kek)
+ for _,naut in pairs(self.Nauts) do
+ naut:update(dt)
+ end
+ for _,platform in pairs(self.Platforms) do
+ platform:update(dt)
+ end
+ for _,decoration in pairs(self.Decorations) do
+ decoration:update(dt)
+ end
+ -- Clouds
+ if self.map.clouds then
+ -- generator
+ local n = table.getn(self.Clouds)
+ self.clouds_delay = self.clouds_delay - dt
+ if self.clouds_delay < 0 and
+ n < 18
+ then
+ self:randomizeCloud()
+ self.clouds_delay = self.clouds_delay + World.clouds_delay -- World.clouds_delay is initial
+ end
+ -- movement
+ for _,cloud in pairs(self.Clouds) do
+ if cloud:update(dt) > 340 then
+ table.remove(self.Clouds, _)
+ end
+ end
+ end
+ -- Effects
+ for _,effect in pairs(self.Effects) do
+ if effect:update(dt) then
+ table.remove(self.Effects, _)
+ end
+ end
+ -- Rays
+ for _,ray in pairs(self.Rays) do
+ if ray:update(dt) then
+ table.remove(self.Rays, _)
+ end
+ end
+ -- Bounce `winner`
+ self.win_move = self.win_move + dt
+ if self.win_move > 2 then
+ self.win_move = self.win_move - 2
+ end
+end
+-- Draw
+function World:draw()
+ -- Camera stuff
+ local offset_x, offset_y = self.camera:getOffsets()
+ local scale = self.camera.scale
+ local scaler = self.camera.scaler
+
+ -- Background
+ love.graphics.draw(self.background, 0, 0, 0, scaler, scaler)
+
+ -- This needs to be reworked!
+ -- Draw clouds
+ for _,cloud in pairs(self.Clouds) do
+ cloud:draw(offset_x, offset_y, scale)
+ end
+
+ -- Draw decorations
+ for _,decoration in pairs(self.Decorations) do
+ decoration:draw(offset_x, offset_y, scale)
+ end
+
+ -- Draw effects
+ for _,effect in pairs(self.Effects) do
+ effect:draw(offset_x,offset_y, scale)
+ end
+
+ -- Draw player
+ for _,naut in pairs(self.Nauts) do
+ naut:draw(offset_x, offset_y, scale, debug)
+ end
+
+ -- Draw ground
+ for _,platform in pairs(self.Platforms) do
+ platform:draw(offset_x, offset_y, scale, debug)
+ end
+
+ -- Draw rays
+ for _,ray in pairs(self.Rays) do
+ ray:draw(offset_x, offset_y, scale)
+ end
+
+ -- draw center
+ if debug then
+ local c = self.camera
+ local w, h = love.graphics.getWidth(), love.graphics.getHeight()
+ -- draw map center
+ love.graphics.setColor(130,130,130)
+ love.graphics.setLineWidth(1)
+ love.graphics.setLineStyle("rough")
+ local cx, cy = c:getPositionScaled()
+ local x1, y1 = c:translatePosition(self.map.center_x, cy)
+ local x2, y2 = c:translatePosition(self.map.center_x, cy+h)
+ love.graphics.line(x1,y1,x2,y2)
+ local x1, y1 = c:translatePosition(cx, self.map.center_y)
+ local x2, y2 = c:translatePosition(cx+w, self.map.center_y)
+ love.graphics.line(x1,y1,x2,y2)
+ -- draw ox, oy
+ love.graphics.setColor(200,200,200)
+ love.graphics.setLineStyle("rough")
+ local cx, cy = c:getPositionScaled()
+ local x1, y1 = c:translatePosition(0, cy)
+ local x2, y2 = c:translatePosition(0, cy+h)
+ love.graphics.line(x1,y1,x2,y2)
+ local x1, y1 = c:translatePosition(cx, 0)
+ local x2, y2 = c:translatePosition(cx+w, 0)
+ love.graphics.line(x1,y1,x2,y2)
+ end
+
+ -- Draw HUDs
+ for _,naut in pairs(self.Nauts) do
+ -- I have no idea where to place them T_T
+ -- let's do: bottom-left, bottom-right, top-left, top-right
+ local w, h = love.graphics.getWidth()/scale, love.graphics.getHeight()/scale
+ local y, e = 1, 1
+ if _ < 3 then y, e = h-33, 0 end
+ naut:drawHUD(1+(_%2)*(w-34), y, scale, e)
+ end
+
+ -- Draw winner
+ if self.lastNaut then
+ local w, h = love.graphics.getWidth()/scale, love.graphics.getHeight()/scale
+ local angle = self:getBounce(2)
+ local dy = self:getBounce()*3
+ love.graphics.setFont(Bold)
+ love.graphics.printf("WINNER",(w/2)*scale,(42+dy)*scale,336,"center",(angle*5)*math.pi/180,scale,scale,168,12)
+ love.graphics.setFont(Font)
+ love.graphics.printf("rofl, now kill yourself", w/2*scale, 18*scale, 160, "center", 0, scale, scale, 80, 3)
+ end
+end
+
+-- Box2D callbacks
+-- beginContact
+function World.beginContact(a, b, coll)
+ if a:getCategory() == 1 then
+ local x,y = coll:getNormal()
+ if y < -0.6 then
+ print(b:getUserData().name .. " is not in air")
+ -- Move them to Hero
+ b:getUserData().inAir = false
+ b:getUserData().jumpnumber = 2
+ b:getUserData().salto = false
+ b:getUserData():createEffect("land")
+ end
+ local vx, vy = b:getUserData().body:getLinearVelocity()
+ if math.abs(x) == 1 or (y < -0.6 and x == 0) then
+ b:getUserData():playSound(3)
+ end
+ end
+ if a:getCategory() == 3 then
+ b:getUserData():damage(a:getUserData()[2])
+ end
+ if b:getCategory() == 3 then
+ a:getUserData():damage(b:getUserData()[2])
+ end
+end
+-- endContact
+function World.endContact(a, b, coll)
+ if a:getCategory() == 1 then
+ print(b:getUserData().name .. " is in air")
+ -- Move them to Hero
+ b:getUserData().inAir = true
+ end
+end
+
+-- Controller callbacks
+function World:controlpressed(set, action, key)
+ if key == "f6" and debug then
+ local map = self:getMapName()
+ local nauts = {}
+ for _,naut in pairs(self:getNautsAll()) do
+ table.insert(nauts, {naut.name, naut:getControlSet()})
+ end
+ local new = World:new(map, nauts)
+ changeScene(new)
+ end
+ for k,naut in pairs(self:getNautsAll()) do
+ naut:controlpressed(set, action, key)
+ end
+end
+function World:controlreleased(set, action, key)
+ for k,naut in pairs(self:getNautsAll()) do
+ naut:controlreleased(set, action, key)
+ end
+end \ No newline at end of file