diff options
Diffstat (limited to 'not')
-rw-r--r-- | not/Button.lua | 29 | ||||
-rw-r--r-- | not/Camera.lua | 14 | ||||
-rw-r--r-- | not/Cloud.lua | 30 | ||||
-rw-r--r-- | not/Decoration.lua | 29 | ||||
-rw-r--r-- | not/Effect.lua | 32 | ||||
-rw-r--r-- | not/Element.lua | 16 | ||||
-rw-r--r-- | not/Entity.lua | 17 | ||||
-rw-r--r-- | not/Header.lua | 20 | ||||
-rw-r--r-- | not/Hero.lua | 239 | ||||
-rw-r--r-- | not/Menu.lua | 114 | ||||
-rw-r--r-- | not/MenuBackground.lua | 49 | ||||
-rw-r--r-- | not/Music.lua | 25 | ||||
-rw-r--r-- | not/MusicPlayer.lua | 51 | ||||
-rw-r--r-- | not/Object.lua | 3 | ||||
-rw-r--r-- | not/PhysicalBody.lua | 34 | ||||
-rw-r--r-- | not/Platform.lua | 29 | ||||
-rw-r--r-- | not/Player.lua | 49 | ||||
-rw-r--r-- | not/Ray.lua | 40 | ||||
-rw-r--r-- | not/Scene.lua | 38 | ||||
-rw-r--r-- | not/SceneManager.lua | 62 | ||||
-rw-r--r-- | not/Selector.lua | 50 | ||||
-rw-r--r-- | not/Settings.lua | 90 | ||||
-rw-r--r-- | not/Sprite.lua | 72 | ||||
-rw-r--r-- | not/World.lua | 153 |
24 files changed, 698 insertions, 587 deletions
diff --git a/not/Button.lua b/not/Button.lua index 91aca45..a2f7a19 100644 --- a/not/Button.lua +++ b/not/Button.lua @@ -1,27 +1,18 @@ +require "not.Element" + --- `Button` -- Menu element that can be activated by user. -Button = { - parent = --[[not.Menu]]nil, - x = 0, - y = 0, - text = "", - focused = false, - sprite, - quads, - delay = 2, - parent, -} +Button = Element:extends() --- `Button` is a child of `Element`. -require "not.Element" -Button.__index = Button -setmetatable(Button, Element) +Button.text = "" +Button.focused = false +Button.sprite = --[[]]nil +Button.quads = --[[]]nil +Button.delay = 2 function Button:new (parent) - local o = setmetatable({}, self) - o.parent = parent - o.sprite, o.quads = parent:getSheet() - return o + Button.__super.new(self, parent) + self.sprite, self.quads = parent:getSheet() end function Button:setText (text) diff --git a/not/Camera.lua b/not/Camera.lua index 63489f3..aa4df5b 100644 --- a/not/Camera.lua +++ b/not/Camera.lua @@ -5,8 +5,6 @@ Camera = { y = 0, dest_x = 0, dest_y = 0, - scale = getScale(), - scaler = getRealScale(), shake = 0, timer = 0, delay = 0, @@ -45,7 +43,7 @@ function Camera:getPosition () end function Camera:getPositionScaled () - return self.x*self.scale, self.y*self.scale + return self.x*getScale(), self.y*getScale() end -- Destination @@ -63,7 +61,7 @@ end function Camera:translatePosition (x, y) local x = x or 0 local y = y or 0 - return (x-self.x)*self.scale, (y-self.y)*self.scale + return (x-self.x)*getScale(), (y-self.y)*getScale() end function Camera:translatePoints(...) @@ -72,9 +70,9 @@ function Camera:translatePoints(...) local x,y = self:getOffsets() for k,v in pairs(a) do if k%2 == 1 then - table.insert(r, (v + x) * self.scale) + table.insert(r, (v + x) * getScale()) else - table.insert(r, (v + y) * self.scale) + table.insert(r, (v + y) * getScale()) end end return r @@ -120,8 +118,8 @@ function Camera:follow () sum_y = naut_y + sum_y end end - local x = sum_x / i - love.graphics.getWidth()/self.scale/2 - local y = sum_y / i - love.graphics.getHeight()/self.scale/2 + 4*self.scale -- hotfix + local x = sum_x / i - love.graphics.getWidth()/getScale()/2 + local y = sum_y / i - love.graphics.getHeight()/getScale()/2 + 4*getScale() -- hotfix return x,y end diff --git a/not/Cloud.lua b/not/Cloud.lua index 3bc5377..25169c0 100644 --- a/not/Cloud.lua +++ b/not/Cloud.lua @@ -1,10 +1,11 @@ +require "not.Decoration" + --- `Cloud` -- That white thing moving in the background. -- TODO: extends variables names to be readable. -Cloud = { - t = 1, -- type (sprite number) - v = 13 -- velocity -} +Cloud = Decoration:extends() +Cloud.t = 1 -- type (sprite number) +Cloud.v = 13 -- velocity -- TODO: allow maps to use other quads and sprites for clouds -- TODO: you know this isn't right, don't you? @@ -26,25 +27,12 @@ local animations = { } } --- `Cloud` is a child of `Decoration`. -require "not.Decoration" -Cloud.__index = Cloud -setmetatable(Cloud, Decoration) - -- Constructor of `Cloud`. -function Cloud:new (x, y, t, v) - local o = setmetatable({}, self) - o:init(x, y, t, v) - -- Load spritesheet statically. +function Cloud:new (x, y, t, v, world) if self:getImage() == nil then self:setImage(Sprite.newImage("assets/clouds.png")) end - return o -end - --- Initializer of `Cloud`. -function Cloud:init (x, y, t, v) - Decoration.init(self, x, y, nil) + Cloud.__super.new(self, x, y, world, nil) self:setAnimationsList(animations) self:setVelocity(v) self:setType(t) @@ -65,7 +53,9 @@ end -- Update of `Cloud`, returns x for world to delete cloud after reaching right corner. function Cloud:update (dt) - Decoration.update(self, dt) + Cloud.__super.update(self, dt) self.x = self.x + self.v*dt return self.x end + +return Cloud diff --git a/not/Decoration.lua b/not/Decoration.lua index 9dc2bdd..97524f5 100644 --- a/not/Decoration.lua +++ b/not/Decoration.lua @@ -1,26 +1,15 @@ +require "not.Entity" + --- `Decoration` -- Positioned sprite used to decorate maps with additional graphics. -Decoration = { - world = --[[not.World]]nil, - x = 0, - y = 0 -} +Decoration = Entity:extends() --- `Decoration` is a child of `Sprite`. -require "not.Sprite" -Decoration.__index = Decoration -setmetatable(Decoration, Sprite) +Decoration.x = 0 +Decoration.y = 0 -- Constructor of `Decoration`. -function Decoration:new (x, y, imagePath) - local o = setmetatable({}, self) - o:init(x, y, imagePath) - return o -end - --- Initializer of `Decoration`. -function Decoration:init (x, y, imagePath) - Sprite.init(self, imagePath) +function Decoration:new (x, y, world, imagePath) + Decoration.__super.new(self, world, imagePath) self:setPosition(x, y) end @@ -30,4 +19,6 @@ function Decoration:getPosition () end function Decoration:setPosition (x, y) self.x, self.y = x, y -end
\ No newline at end of file +end + +return Decoration diff --git a/not/Effect.lua b/not/Effect.lua index dd7570a..6c0dad0 100644 --- a/not/Effect.lua +++ b/not/Effect.lua @@ -1,29 +1,15 @@ --- `Effect` -- Short animation with graphics that plays in various situation. --- TODO: animation is currently slower than it used to be, check if it is ok; if not then make it possible to change it to 0.06 delay. -Effect = { - finished = false, -} - --- `Effect` is a child of `Decoration`. -require "not.Decoration" -Effect.__index = Effect -setmetatable(Effect, Decoration) +Effect = require "not.Decoration":extends() -- Constructor of `Effect`. -function Effect:new (name, x, y) - local o = setmetatable({}, self) - o:init(name, x, y) - -- Load spritesheet statically. - if self:getImage() == nil then - self:setImage(Sprite.newImage("assets/effects.png")) +function Effect:new (name, x, y, world) + -- TODO: Load spritesheet statically. Put it to load or somewhere else within non-existent resource manager. + if Effect:getImage() == nil then + Effect:setImage(Sprite.newImage("assets/effects.png")) end - return o -end - --- Initializer of `Effect`. -function Effect:init (name, x, y) - Decoration.init(self, x, y, nil) + Effect.__super.new(self, x, y, world, nil) + self.finished = false self:setAnimationsList(require("config.animations.effects")) self:setAnimation(name) end @@ -31,7 +17,7 @@ end -- Update of `Effect`. -- Returns true if animation is finished and effect is ready to be deleted. function Effect:update (dt) - Decoration.update(self, dt) + Effect.__super.update(self, dt) return self.finished end @@ -44,3 +30,5 @@ function Effect:goToNextFrame () self.finished = true end end + +return Effect diff --git a/not/Element.lua b/not/Element.lua index e6d91da..24576e6 100644 --- a/not/Element.lua +++ b/not/Element.lua @@ -1,17 +1,15 @@ +require "not.Object" + --- `Element` -- Empty element used inside `Menu`. -Element = { - parent = --[[not.Menu]]nil, - x = 0, - y = 0 -} +Element = Object:extends() -Element.__index = Element +Element.parent = --[[not.Menu]]nil +Element.x = 0 +Element.y = 0 function Element:new (parent) - local o = setmetatable({}, self) - o.parent = parent - return o + self.parent = parent end function Element:delete () end -- deletes Element diff --git a/not/Entity.lua b/not/Entity.lua new file mode 100644 index 0000000..0e21e48 --- /dev/null +++ b/not/Entity.lua @@ -0,0 +1,17 @@ +require "not.Sprite" + +--- `Entity` +-- Basic, visible object to be used within World instance. Can be anything. Represented as `Sprite`. +-- We still need to keep old, global way of using modules but there are `returns` on the end at least. +-- It probably would be nice to move `World` dependency out of it but for now it should stay this way. Later `World` instance could be just passed to methods that need it. +Entity = Sprite:extends() + +Entity.world =--[[not.World]]nil + +-- Simple constructor for `Entity`. +function Entity:new (world, imagePath) + Entity.__super.new(self, imagePath) + self.world = world +end + +return Entity diff --git a/not/Header.lua b/not/Header.lua index a563ab2..8b2ec0d 100644 --- a/not/Header.lua +++ b/not/Header.lua @@ -1,22 +1,14 @@ +require "not.Element" + --- `Header` -- Swinging title. -Header = { - parent = --[[not.Menu]]nil, - x = 0, - y = 0, - text = "", - bounce = 2, -} +Header = Element:extends() --- `Header` is a child of `Element`. -require "not.Element" -Header.__index = Header -setmetatable(Header, Element) +Header.text = "" +Header.bounce = 2 function Header:new (parent) - local o = setmetatable({}, self) - o.parent = parent - return o + Header.__super.new(self, parent) end function Header:setText (text) diff --git a/not/Hero.lua b/not/Hero.lua index feb61da..039aeb8 100644 --- a/not/Hero.lua +++ b/not/Hero.lua @@ -1,82 +1,81 @@ --- `Hero` -- Hero (often referred to as: "naut") entity that exists in a game world. -- Collision category: [2] -Hero = { - -- General and physics - name = "empty", - angle = 0, - facing = 1, - max_velocity = 105, - world = --[[not.World]]nil, - group = nil, - -- Combat - combo = 0, - lives = 3, - spawntimer = 2, - isAlive = true, - punchCooldown = 0.25, - punchdir = 0, -- a really bad thing - -- Movement - inAir = true, - salto = false, - isJumping = false, - isWalking = false, - jumpTimer = 0.16, - jumpCounter = 2, - -- Statics - portrait_sprite = nil, - portrait_frame = nil, - portrait_sheet = getNautsIconsList(), - portrait_box = love.graphics.newQuad( 0, 15, 32,32, 80,130), - sfx = require "config.sounds", -} +Hero = require "not.PhysicalBody":extends() --- `Hero` is a child of `PhysicalBody`. -require "not.PhysicalBody" -Hero.__index = Hero -setmetatable(Hero, PhysicalBody) +-- Few are left... +Hero.jumpTimer = 0.16 +Hero.jumpCounter = 2 +Hero.sfx = require "config.sounds" + +Hero.QUAD_PORTRAITS = getNautsIconsList() +Hero.QUAD_FRAME = love.graphics.newQuad(0, 15, 32,32, 80,130) +Hero.IMAGE_PORTRAITS = nil +Hero.IMAGE_FRAME = nil +Hero.MAX_VELOCITY = 105 +Hero.RESPAWN_TIME = 2 +Hero.PUNCH_COOLDOWN = 0.25 +Hero.PUNCH_FIXTURE_LIFETIME = 0.08 +Hero.PUNCH_LEFT = {-2,-6, -20,-6, -20,6, -2,6} +Hero.PUNCH_RIGHT = {2,-6, 20,-6, 20,6, 2,6} +Hero.PUNCH_UP = {-8,-4, -8,-20, 8,-20, 8,-4} +Hero.PUNCH_DOWN = {-8,4, -8,20, 8,20, 8,4} -- Constructor of `Hero`. -function Hero:new (game, world, x, y, name) - local o = setmetatable({}, self) - o:init(name, game, x, y) - -- Load portraits statically. - if self.portrait_sprite == nil then - self.portrait_sprite = love.graphics.newImage("assets/portraits.png") - self.portrait_frame = love.graphics.newImage("assets/menu.png") +function Hero:new (name, x, y, world) + local imagePath = string.format("assets/nauts/%s.png", name) + Hero.load() + Hero.__super.new(self, x, y, world, imagePath) + -- Physics + self.group = -1-#world.Nauts + self:setBodyType("dynamic") + self:setBodyFixedRotation(true) + self:newFixture() + -- General + self.world = world + self.name = name + self.angle = 0 + self.facing = 1 + -- Status + self.combo = 0 + self.lives = 3 + self.inAir = true + self.salto = false + self.smoke = false + self.isAlive = true + self.isWalking = false + self.isJumping = false + self.spawntimer = 2 + self.punchCooldown = 0 + self:setAnimationsList(require("config.animations.hero")) + -- Post-creation + self:createEffect("respawn") +end + +-- TODO: This is temporarily called by constructor. +function Hero.load () + if Hero.IMAGE_PORTRAITS == nil then + Hero.IMAGE_PORTRAITS = love.graphics.newImage("assets/portraits.png") + Hero.IMAGE_FRAME = love.graphics.newImage("assets/menu.png") end - return o end --- Initializer of `Hero`. -function Hero:init (name, world, x, y) - -- Find imagePath based on hero name. - local fileName = name or Hero.name -- INITIAL from metatable - local imagePath = string.format("assets/nauts/%s.png", fileName) - -- `PhysicalBody` initialization. - PhysicalBody.init(self, world, x, y, imagePath) - self:setBodyType("dynamic") - self:setBodyFixedRotation(true) - self.group = -1-#world.Nauts - -- Main fixture initialization. +--- Creates hero's fixture and adds it to physical body. +function Hero:newFixture () local fixture = self:addFixture({-5,-8, 5,-8, 5,8, -5,8}, 8) fixture:setUserData(self) fixture:setCategory(2) fixture:setMask(2) fixture:setGroupIndex(self.group) - -- Actual `Hero` initialization. - self.world = world - self.punchCooldown = 0 - self.name = name - self:setAnimationsList(require("config.animations.hero")) - self:createEffect("respawn") end -- Update callback of `Hero` function Hero:update (dt) - PhysicalBody.update(self, dt) - if self.body:isDestroyed() then return end - + Hero.__super.update(self, dt) + if self.body:isDestroyed() then + return + end + self:dampVelocity(dt) -- Salto if self.salto and (self.current == self.animations.walk or self.current == self.animations.default) then self.angle = (self.angle + 17 * dt * self.facing) % 360 @@ -84,23 +83,6 @@ function Hero:update (dt) self.angle = 0 end - -- Custom linear damping. - if not self.isWalking then - local face = nil - local x, y = self:getLinearVelocity() - if x < -12 then - face = 1 - elseif x > 12 then - face = -1 - else - face = 0 - end - self:applyForce(40*face,0) - if not self.inAir then - self:applyForce(80*face,0) - end - end - -- Could you please die? -- TODO: World/Map function for testing if Point is inside playable area. local m = self.world.map @@ -120,6 +102,13 @@ function Hero:update (dt) self:respawn() end + -- Trail spawner + -- TODO: lower the frequency of spawning - currently it is each frame. + if self.smoke and self.inAir then + local dx, dy = love.math.random(-5, 5), love.math.random(-5, 5) + self:createEffect("trail", dx, dy) + end + -- # PUNCH -- Cooldown self.punchCooldown = self.punchCooldown - dt @@ -135,17 +124,33 @@ function Hero:update (dt) 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:setLinearVelocity(0,0) - else - self:setLinearVelocity(38*self.facing,0) + local currentAnimation = self:getAnimation() + if self.frame < currentAnimation.frames then + if currentAnimation == self.animations.attack_up or currentAnimation == self.animations.attack_down then + self:setLinearVelocity(0, 0) + end + if currentAnimation == self.animations.attack then + self:setLinearVelocity(38*self.facing, 0) end end +end - if self.punchCooldown <= 0 and self.punchdir == 1 then - self.punchdir = 0 +--- Damps linear velocity every frame by applying minor force to body. +function Hero:dampVelocity (dt) + if not self.isWalking then + local face + local x, y = self:getLinearVelocity() + if x < -12 then + face = 1 + elseif x > 12 then + face = -1 + else + face = 0 + end + self:applyForce(40*face,0) + if not self.inAir then + self:applyForce(80*face,0) + end end end @@ -163,7 +168,13 @@ end -- Draw of `Hero` function Hero:draw (offset_x, offset_y, scale, debug) if not self.isAlive then return end - PhysicalBody.draw(self, offset_x, offset_y, scale, debug) + Hero.__super.draw(self, offset_x, offset_y, scale, debug) +end + +function Hero:drawTag (offset_x, offset_y, scale) + local x,y = self:getPosition() + love.graphics.setFont(Font) + love.graphics.printf(string.format("Player %d", math.abs(self.group)), (math.floor(x)+offset_x)*scale, (math.floor(y)+offset_y-26)*scale,100,'center',0,scale,scale,50,0) end -- Draw HUD of `Hero` @@ -172,8 +183,8 @@ function Hero:drawHUD (x,y,scale,elevation) -- hud displays only if player is alive if self.isAlive 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) + love.graphics.draw(self.IMAGE_FRAME, self.QUAD_FRAME, (x)*scale, (y)*scale, 0, scale, scale) + love.graphics.draw(self.IMAGE_PORTRAITS, self.QUAD_PORTRAITS[self.name], (x+2)*scale, (y+3)*scale, 0, scale, scale) local dy = 30 * elevation love.graphics.setFont(Font) love.graphics.print((self.combo).."%",(x+2)*scale,(y-3+dy)*scale,0,scale,scale) @@ -194,35 +205,44 @@ function Hero:goToNextFrame () 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) +function Hero:createEffect (name, dx, dy) + local x, y = self.body:getX()-12, self.body:getY()-15 + if dx then + x = x + dx end + if dy then + y = y + dy + end + self.world:createEffect(name, x, y) +end + +-- Called by World when Hero starts contact with Platform (lands). +function Hero:land () + self.inAir = false + self.jumpCounter = 2 + self.salto = false + self.smoke = false + self:createEffect("land") end -- Creates temporary fixture for hero's body that acts as sensor. -- direction: ("left", "right", "up", "down") -- Sensor fixture is deleted after time set in UserData[1]; deleted by `not.Hero.update`. --- TODO: Magic numbers present in `not.Hero.punch`. function Hero:punch (direction) - self.punchCooldown = Hero.punchCooldown -- INITIAL from metatable + self.punchCooldown = Hero.PUNCH_COOLDOWN -- Choose shape based on punch direction. local shape - if direction == "left" then shape = {-2,-6, -20,-6, -20,6, -2,6} end - if direction == "right" then shape = {2,-6, 20,-6, 20,6, 2,6} end - if direction == "up" then shape = {-8,-4, -8,-20, 8,-20, 8,-4} end - if direction == "down" then shape = {-8,4, -8,20, 8,20, 8,4} end + if direction == "left" then shape = Hero.PUNCH_LEFT end + if direction == "right" then shape = Hero.PUNCH_RIGHT end + if direction == "up" then shape = Hero.PUNCH_UP end + if direction == "down" then shape = Hero.PUNCH_DOWN end -- Create and set sensor fixture. local fixture = self:addFixture(shape, 0) fixture:setSensor(true) fixture:setCategory(3) - fixture:setMask(1,3) + fixture:setMask(1) fixture:setGroupIndex(self.group) - fixture:setUserData({0.08, direction}) + fixture:setUserData({Hero.PUNCH_FIXTURE_LIFETIME, direction}) self:playSound(4) end @@ -251,15 +271,18 @@ function Hero:damage (direction) self.combo = math.min(999, self.combo + 10) self.punchCooldown = 0.08 + self.combo*0.0006 self:playSound(2) + if self.combo > 80 then + self.smoke = true + end end -- DIE function Hero:die () self:playSound(1) - self.combo = Hero.combo -- INITIAL from metatable + self.combo = 0 self.lives = self.lives - 1 self.isAlive = false - self.spawntimer = Hero.spawntimer -- INITIAL from metatable + self.spawntimer = Hero.RESPAWN_TIME self:setBodyActive(false) self.world:onNautKilled(self) end @@ -267,6 +290,8 @@ end -- And then respawn. Like Jon Snow. function Hero:respawn () self.isAlive = true + self.salto = false + self.smoke = false self:setLinearVelocity(0,0) self:setPosition(self.world:getSpawnPosition()) -- TODO: I'm not convinced about getting new position like this. self:setBodyActive(true) @@ -282,3 +307,5 @@ function Hero:playSound (sfx, force) source:play() end end + +return Hero diff --git a/not/Menu.lua b/not/Menu.lua index 8ef1861..f6da354 100644 --- a/not/Menu.lua +++ b/not/Menu.lua @@ -1,72 +1,49 @@ --- `Menu` -- It creates single screen of a menu -- I do know that model I used here and in `World` loading configuration files is not flawless but I did not want to rewrite `World`s one but wanted to keep things similar at least in project scope. -Menu = { - scale = getScale(), - elements = --[[{not.Element}]]nil, - active = 1, - music = --[[not.Music]]nil, - sprite = --[[love.graphics.newImage]]nil, - background = --[[love.graphics.newImage]]nil, - asteroids = --[[love.graphics.newImage]]nil, - stars = --[[love.graphics.newImage]]nil, - asteroids_bounce = 0, - stars_frame = 1, - stars_delay = 0.8, - allowMove = true, - quads = { -- TODO: Could be moved to config file or perhaps QuadManager to manage all quads for animations etc. - button = { - normal = love.graphics.newQuad(0, 0, 58,15, 80,130), - active = love.graphics.newQuad(0, 0, 58,15, 80,130) - }, - portrait = { - normal = love.graphics.newQuad( 0, 15, 32,32, 80,130), - active = love.graphics.newQuad(32, 15, 32,32, 80,130) - }, - panorama = { - normal = love.graphics.newQuad(0,47, 80,42, 80,130), - active = love.graphics.newQuad(0,88, 80,42, 80,130) - }, - arrow_l = love.graphics.newQuad(68, 0, 6, 6, 80,130), - arrow_r = love.graphics.newQuad(74, 0, 6, 6, 80,130), - stars = { - love.graphics.newQuad( 0, 0, 320, 200, 640,200), - love.graphics.newQuad(320, 0, 320, 200, 640,200) - }, - } -} - -Menu.__index = Menu +Menu = require "not.Scene":extends() -require "not.Music" +Menu.elements = --[[{not.Element}]]nil +Menu.active = 1 +Menu.music = --[[not.Music]]nil +Menu.sprite = --[[love.graphics.newImage]]nil +Menu.allowMove = true +Menu.quads = { -- TODO: Could be moved to config file or perhaps QuadManager to manage all quads for animations etc. + button = { + normal = love.graphics.newQuad(0, 0, 58,15, 80,130), + active = love.graphics.newQuad(0, 0, 58,15, 80,130) + }, + portrait = { + normal = love.graphics.newQuad( 0, 15, 32,32, 80,130), + active = love.graphics.newQuad(32, 15, 32,32, 80,130) + }, + panorama = { + normal = love.graphics.newQuad(0,47, 80,42, 80,130), + active = love.graphics.newQuad(0,88, 80,42, 80,130) + }, + arrow_l = love.graphics.newQuad(68, 0, 6, 6, 80,130), + arrow_r = love.graphics.newQuad(74, 0, 6, 6, 80,130), +} function Menu:new (name) - local o = setmetatable({}, self) -- Load statically. - if self.sprite == nil then - self.sprite = love.graphics.newImage("assets/menu.png") - self.background = love.graphics.newImage("assets/backgrounds/menu.png") - self.asteroids = love.graphics.newImage("assets/asteroids.png") - self.stars = love.graphics.newImage("assets/stars.png") + if Menu.sprite == nil then + Menu.sprite = love.graphics.newImage("assets/menu.png") end - o:init(name) - return o -end - -function Menu:init (name) - self.music = Music:new("menu.ogg") + self.elements = {} self:open(name) end -function Menu:delete () - self.music:delete() -end +function Menu:delete () end function Menu:open (name) local name = name or "main" self.active = Menu.active --Menu.active is initial - self.elements = love.filesystem.load(string.format("config/menus/%s.lua", name))(self) - self.elements[self.active]:focus() + self.elements = love.filesystem.load(string.format("config/menus/%s.lua", name))(self, self.elements[1]) + -- Common with `next` method. + if not self.elements[self.active]:focus() then + self:next() + end end -- Return reference to quads table and menu sprite @@ -94,29 +71,26 @@ function Menu:previous () end end +-- @Override +function Menu:isInputDisabled () + if self.inputBreakTimer then + return self.inputDisabled or self.inputBreakTimer > 0 + end + return self.inputDisabled +end + -- LÖVE2D callbacks function Menu:update (dt) for _,element in pairs(self.elements) do element:update(dt) end - self.asteroids_bounce = self.asteroids_bounce + dt*0.1 - if self.asteroids_bounce > 2 then self.asteroids_bounce = self.asteroids_bounce - 2 end - self.stars_delay = self.stars_delay - dt - if self.stars_delay < 0 then - self.stars_delay = self.stars_delay + Menu.stars_delay --Menu.stars_delay is initial - if self.stars_frame == 2 then - self.stars_frame = 1 - else - self.stars_frame = 2 - end + if self.inputBreakTimer and self.inputBreakTimer > 0 then + self.inputBreakTimer = self.inputBreakTimer - dt end end function Menu:draw () - local scale = self.scale + local scale = getScale() local scaler = getRealScale() - love.graphics.draw(self.background, 0, 0, 0, scaler, scaler) - love.graphics.draw(self.stars, self.quads.stars[self.stars_frame], 0, 0, 0, scaler, scaler) - love.graphics.draw(self.asteroids, 0, math.floor(64+math.sin(self.asteroids_bounce*math.pi)*4)*scaler, 0, scaler, scaler) love.graphics.setFont(Font) for _,element in pairs(self.elements) do element:draw(scale) @@ -141,4 +115,6 @@ function Menu:controlreleased (set, action, key) for _,element in pairs(self.elements) do element:controlreleased(set, action, key) end -end
\ No newline at end of file +end + +return Menu diff --git a/not/MenuBackground.lua b/not/MenuBackground.lua new file mode 100644 index 0000000..83b409c --- /dev/null +++ b/not/MenuBackground.lua @@ -0,0 +1,49 @@ +--- `MenuBackground` +-- Represented as space background with blinking stars and moving asteroids. +-- It might be too specific, but whatever. It is still better than hardcoded background in `Menu` class. +MenuBackground = require "not.Element":extends() + +MenuBackground.BASE_STARS_DELAY = .8 +MenuBackground.QUAD_STARS = { + love.graphics.newQuad( 0, 0, 320, 200, 640,200), + love.graphics.newQuad(320, 0, 320, 200, 640,200) +} + +function MenuBackground:new (parent) + MenuBackground.__super.new(parent) + self.starsFrame = 1 + self.starsDelay = self.BASE_STARS_DELAY + self.asteroidsBounce = 0 + -- Load statically. + if MenuBackground.IMAGE_BACKGROUND == nil then + MenuBackground.IMAGE_BACKGROUND = love.graphics.newImage("assets/backgrounds/menu.png") + MenuBackground.IMAGE_ASTEROIDS = love.graphics.newImage("assets/asteroids.png") + MenuBackground.IMAGE_STARS = love.graphics.newImage("assets/stars.png") + end +end + +function MenuBackground:update (dt) + self.asteroidsBounce = self.asteroidsBounce + dt*0.1 + if self.asteroidsBounce > 2 then + self.asteroidsBounce = self.asteroidsBounce - 2 + end + self.starsDelay = self.starsDelay - dt + if self.starsDelay < 0 then + self.starsDelay = self.starsDelay + self.BASE_STARS_DELAY + if self.starsFrame == 2 then + self.starsFrame = 1 + else + self.starsFrame = 2 + end + end +end + +function MenuBackground:draw () + local scale = self.scale + local scaler = getRealScale() + love.graphics.draw(self.IMAGE_BACKGROUND, 0, 0, 0, scaler, scaler) + love.graphics.draw(self.IMAGE_STARS, self.QUAD_STARS[self.starsFrame], 0, 0, 0, scaler, scaler) + love.graphics.draw(self.IMAGE_ASTEROIDS, 0, math.floor(64+math.sin(self.asteroidsBounce*math.pi)*4)*scaler, 0, scaler, scaler) +end + +return MenuBackground diff --git a/not/Music.lua b/not/Music.lua deleted file mode 100644 index ee930f4..0000000 --- a/not/Music.lua +++ /dev/null @@ -1,25 +0,0 @@ ---- `Music` --- Simple music player object that plays and loops selected track in single Scene. -Music = { - source = --[[love.audio.newSource]]nil -} - -Music.__index = Music - -function Music:new (trackName) - local o = setmetatable({}, self) - o:init(trackName) - return o -end - --- TODO: trackName should be passed without file extension. -function Music:init (trackName) - self.source = love.audio.newSource("assets/music/" .. trackName) - self.source:setLooping(true) - self.source:setVolume(.7) - self.source:play() -end - -function Music:delete () - self.source:stop() -end
\ No newline at end of file diff --git a/not/MusicPlayer.lua b/not/MusicPlayer.lua new file mode 100644 index 0000000..4634ed9 --- /dev/null +++ b/not/MusicPlayer.lua @@ -0,0 +1,51 @@ +require "not.Object" + +--- `MusicPlayer` +-- Simple music player object that plays and loops selected track. +MusicPlayer = Object:extends() + +function MusicPlayer:new (trackName) + self.tracks = {} + if trackName then + self:setTrack(trackName) + self:play() + end +end + +function MusicPlayer:delete () + self.tracks = nil + self:stop() +end + +function MusicPlayer:setTrack (trackName) + if self.source then + self.source:stop() + end + if self.tracks[trackName] then + self.source = self.tracks[trackName] + else + local source = love.audio.newSource("assets/music/" .. trackName) + source:setLooping(true) + source:setVolume(.7) + self.source = source + self.tracks[trackName] = source + end +end + +function MusicPlayer:getCurrentTrack () + for key,track in pairs(self.tracks) do + if self.tracks[key] == self.source then + return key + end + end +end + +function MusicPlayer:play () + self.source:play() +end + +function MusicPlayer:stop () + self.source:stop() +end + +return MusicPlayer diff --git a/not/Object.lua b/not/Object.lua new file mode 100644 index 0000000..30b91b5 --- /dev/null +++ b/not/Object.lua @@ -0,0 +1,3 @@ +-- Wrapping library to game's hierarchy in a shameless way. +Object = require "lib.object.Object" +return Object diff --git a/not/PhysicalBody.lua b/not/PhysicalBody.lua index e9625fa..804c706 100644 --- a/not/PhysicalBody.lua +++ b/not/PhysicalBody.lua @@ -1,26 +1,17 @@ +require "not.Entity" + --- `PhysicalBody` -- Abstract class for drawable entity existing in `not.World`. -PhysicalBody = { - body =--[[love.physics.newBody]]nil, -} - --- `PhysicalBody` is a child of `Sprite`. -require "not.Sprite" -PhysicalBody.__index = PhysicalBody -setmetatable(PhysicalBody, Sprite) +PhysicalBody = Entity:extends() ---[[ Constructor of `PhysicalBody`. -function PhysicalBody:new (world, x, y, imagePath) - local o = setmetatable({}, self) - o:init(world, x, y, imagePath) - return o -end -]] +PhysicalBody.body =--[[love.physics.newBody]]nil --- Initializer of `PhysicalBody`. -function PhysicalBody:init (world, x, y, imagePath) - Sprite.init(self, imagePath) +-- Constructor of `PhysicalBody`. +-- `world` and `imagePath` are passed to parent's constructor (`Entity`). +function PhysicalBody:new (x, y, world, imagePath) + PhysicalBody.__super.new(self, world, imagePath) self.body = love.physics.newBody(world.world, x, y) + self.body:setUserData(self) end -- Add new fixture to body. @@ -68,12 +59,12 @@ end -- Update of `PhysicalBody`. function PhysicalBody:update (dt) - Sprite.update(self, dt) + PhysicalBody.__super.update(self, dt) end -- Draw of `PhysicalBody`. function PhysicalBody:draw (offset_x, offset_y, scale, debug) - Sprite.draw(self, offset_x, offset_y, scale) + PhysicalBody.__super.draw(self, offset_x, offset_y, scale) if debug then for _,fixture in pairs(self.body:getFixtureList()) do local category = fixture:getCategory() @@ -86,8 +77,9 @@ function PhysicalBody:draw (offset_x, offset_y, scale, debug) if category == 3 then love.graphics.setColor(137, 0, 255, 40) end - -- TODO: `world` is not a member of `PhysicalBody` or its instance normally. love.graphics.polygon("fill", self.world.camera:translatePoints(self.body:getWorldPoints(fixture:getShape():getPoints()))) end end end + +return PhysicalBody diff --git a/not/Platform.lua b/not/Platform.lua index 3748c47..a4b3a59 100644 --- a/not/Platform.lua +++ b/not/Platform.lua @@ -1,29 +1,14 @@ +require "not.PhysicalBody" + --- `Platform` -- Static platform physical object with a sprite. `Players` can walk on it. -- Collision category: [1] --- TODO: reformat code to follow new code patterns --- TODO: comment uncovered code parts -Platform = { - world = --[[not.World]]nil, -} - --- `Platform` is a child of `PhysicalBody`. -require "not.PhysicalBody" -Platform.__index = Platform -setmetatable(Platform, PhysicalBody) +Platform = PhysicalBody:extends() -- Constructor of `Platform` -function Platform:new (animations, shape, game, x, y, sprite) - local o = setmetatable({}, self) - o:init(animations, shape, game, x, y, sprite) - return o -end - --- Initializer of `Platform`. -function Platform:init (animations, shape, world, x, y, imagePath) - PhysicalBody.init(self, world, x, y, imagePath) +function Platform:new (animations, shape, x, y, world, imagePath) + Platform.__super.new(self, x, y, world, imagePath) self:setAnimationsList(animations) - self.world = world -- Create table of shapes if single shape is passed. if type(shape[1]) == "number" then shape = {shape} @@ -34,4 +19,6 @@ function Platform:init (animations, shape, world, x, y, imagePath) fixture:setCategory(1) fixture:setFriction(0.2) end -end
\ No newline at end of file +end + +return Platform diff --git a/not/Player.lua b/not/Player.lua index 2a4b2e6..b0dac75 100644 --- a/not/Player.lua +++ b/not/Player.lua @@ -1,31 +1,15 @@ +require "not.Hero" + --- `Player` -- Special `not.Hero` controllable by a player. -Player = { - -- TODO: move functions and properties related to controls from `not.Hero`. - controllerSet = --[[Controller.sets.*]]nil, -} +-- TODO: move functions and properties related to controls from `not.Hero`. +Player = Hero:extends() --- `Player` is a child of `Hero`. -require "not.Hero" -Player.__index = Player -setmetatable(Player, Hero) +Player.controllerSet =--[[Controller.sets.*]]nil -- Constructor of `Player`. -function Player:new (name, game, x, y) - local o = setmetatable({}, self) - o:init(name, game, x, y) - -- Load portraits statically to `not.Hero`. - -- TODO: this is heresy, put it into `load` method or something similar. - if Hero.portrait_sprite == nil then - Hero.portrait_sprite = love.graphics.newImage("assets/portraits.png") - Hero.portrait_frame = love.graphics.newImage("assets/menu.png") - end - return o -end - --- Initializer of `Player`. -function Player:init (...) - Hero.init(self, ...) +function Player:new (name, x, y, world) + Player.__super.new(self, name, x, y, world) end -- Controller set manipulation. @@ -43,7 +27,7 @@ end -- Update of `Player`. function Player:update (dt) - Hero.update(self, dt) -- TODO: It would be probably a good idea to add return to update functions to terminate if something goes badly in parent's update. + Player.__super.update(self, dt) -- TODO: It would be probably a good idea to add return to update functions to terminate if something goes badly in parent's update. if self.body:isDestroyed() then return end local x, y = self:getLinearVelocity() -- Jumping. @@ -57,7 +41,7 @@ function Player:update (dt) self.facing = -1 self:applyForce(-250, 0) -- Controlled speed limit - if x < -self.max_velocity then + if x < -self.MAX_VELOCITY then self:applyForce(250, 0) end end @@ -65,7 +49,7 @@ function Player:update (dt) self.facing = 1 self:applyForce(250, 0) -- Controlled speed limit - if x > self.max_velocity then + if x > self.MAX_VELOCITY then self:applyForce(-250, 0) end end @@ -74,6 +58,7 @@ end -- Controller callbacks. function Player:controlpressed (set, action, key) if set ~= self:getControllerSet() then return end + self.smoke = false -- TODO: temporary -- Jumping if action == "jump" then if self.jumpCounter > 0 then @@ -137,7 +122,6 @@ function Player:controlpressed (set, action, key) else self:punch("left") end - self.punchdir = 1 end end end @@ -150,10 +134,13 @@ function Player:controlreleased (set, action, key) end -- Walking if (action == "left" or action == "right") then - self.isWalking = false - if not (self:isControlDown("left") or self:isControlDown("right")) and - self.current == self.animations.walk then - self:setAnimation("default") + if not (self:isControlDown("left") or self:isControlDown("right")) then + self.isWalking = false + if self.current == self.animations.walk then + self:setAnimation("default") + end end end end + +return Player diff --git a/not/Ray.lua b/not/Ray.lua index bbe11c1..16a9bee 100644 --- a/not/Ray.lua +++ b/not/Ray.lua @@ -1,36 +1,32 @@ --- `Ray` +require "not.Object" + +--- `Ray` -- That awesome effect that blinks when player dies! +Ray = Object:extends() --- WHOLE CODE HAS FLAG OF "need a cleanup" +Ray.naut =--[[not.Hero]]nil +Ray.world =--[[not.World]]nil +Ray.canvas =--[[love.graphics.newCanvas]]nil +Ray.delay = 0.3 -Ray = { - naut = nil, - world = nil, - canvas = nil, - delay = 0.3 -} -function Ray:new(naut, world) - -- Meta - local o = {} - setmetatable(o, self) - self.__index = self - -- Init - o.naut = naut - o.world = world +function Ray:new (naut, world) + self.naut = naut + self.world = world -- Cavas, this is temporary, I believe. - local scale = o.world.camera.scale + local scale = getScale() local w, h = love.graphics.getWidth(), love.graphics.getHeight() - o.canvas = love.graphics.newCanvas(w/scale, h/scale) - return o + self.canvas = love.graphics.newCanvas(w/scale, h/scale) end -function Ray:update(dt) + +function Ray:update (dt) self.delay = self.delay - dt if self.delay < 0 then return true -- delete end return false end -function Ray:draw(offset_x, offset_y, scale) + +function Ray:draw (offset_x, offset_y, scale) love.graphics.setCanvas(self.canvas) love.graphics.clear() love.graphics.setColor(255, 247, 228, 247) @@ -50,3 +46,5 @@ function Ray:draw(offset_x, offset_y, scale) -- draw on screen love.graphics.draw(self.canvas, 0, 0, 0, scale, scale) end + +return Ray diff --git a/not/Scene.lua b/not/Scene.lua new file mode 100644 index 0000000..f0e2e34 --- /dev/null +++ b/not/Scene.lua @@ -0,0 +1,38 @@ +--- `Scene` +Scene = require "not.Object":extends() + +function Scene:new () + self.sleeping = false + self.hidden = false + self.inputDisabled = false +end + +function Scene:delete () end +function Scene:update (dt) end +function Scene:draw () end +function Scene:controlpressed (set, action, key) end +function Scene:controlreleased (set, action, key) end + +-- Following setters and getters are a little bit too much, I think. But they do follow general coding directions. +function Scene:setSleeping (sleeping) + self.sleeping = sleeping +end +function Scene:isSleeping () + return self.sleeping +end + +function Scene:setHidden (hidden) + self.hidden = hidden +end +function Scene:isHidden () + return self.hidden +end + +function Scene:setInputDisabled (inputDisabled) + self.inputDisabled = inputDisabled +end +function Scene:isInputDisabled () + return self.inputDisabled +end + +return Scene diff --git a/not/SceneManager.lua b/not/SceneManager.lua new file mode 100644 index 0000000..c076448 --- /dev/null +++ b/not/SceneManager.lua @@ -0,0 +1,62 @@ +--- `SceneManager` +-- Used for changing single active scene. +-- TODO: Extend functionality for more than one active scene (eg. overlay menu). +SceneManager = require "not.Object":extends() + +function SceneManager:new () + self.scenes = {} +end + +-- This function should be removed when multiple scenes will be handled properly by SceneManager and other things. +function SceneManager:changeScene (scene) + table.remove(self.scenes, #self.scenes) + return self:addScene(scene) +end + +function SceneManager:addScene (scene) + table.insert(self.scenes, scene) + return scene +end + +-- Not nice, not nice. +function SceneManager:removeTopScene () + table.remove(self.scenes, #self.scenes) +end + +function SceneManager:getAllScenes () + return self.scenes +end + +function SceneManager:update (dt) + for _,scene in pairs(self:getAllScenes()) do + if not scene:isSleeping() then + scene:update(dt) + end + end +end + +function SceneManager:draw () + for _,scene in pairs(self:getAllScenes()) do + if not scene:isHidden() then + scene:draw() + end + end +end + +function SceneManager:controlpressed (set, action, key) + for _,scene in pairs(self:getAllScenes()) do + if not scene:isInputDisabled() then + scene:controlpressed(set, action, key) + end + end +end + +function SceneManager:controlreleased (set, action, key) + for _,scene in pairs(self:getAllScenes()) do + if not scene:isInputDisabled() then + scene:controlreleased(set, action, key) + end + end +end + +return SceneManager diff --git a/not/Selector.lua b/not/Selector.lua index 8e03457..ef78778 100644 --- a/not/Selector.lua +++ b/not/Selector.lua @@ -1,3 +1,5 @@ +require "not.Element" + --- `Selector` -- Used in Menu for selecting various things from list. Works for each Controller set or globally. --[[ @@ -12,39 +14,29 @@ selector:new(menu) :set("global", false) -- true: single selector; false: selector for each controller set present :init() ]] -Selector = { - parent = --[[not.Menu]]nil, - x = 0, - y = 0, - width = 0, - height = 0, - margin = 0, - focused = false, - global = false, - delay = 2, - first = false, - list, - sets, - locks, - selections, - shape = "portrait", - sprite, - quads, - icons_i, - icons_q -} +Selector = Element:extends() --- `Selector` is a child of `Element`. -require "not.Element" -Selector.__index = Selector -setmetatable(Selector, Element) +Selector.width = 0 +Selector.height = 0 +Selector.margin = 0 +Selector.focused = false +Selector.global = false +Selector.delay = 2 +Selector.first = false +Selector.list = --[[]]nil +Selector.sets = --[[]]nil +Selector.locks = --[[]]nil +Selector.selections = --[[]]nil +Selector.shape = "portrait" +Selector.sprite = --[[]]nil +Selector.quads = --[[]]nil +Selector.icons_i = --[[]]nil +Selector.icons_q = --[[]]nil -- Constructor function Selector:new (parent) - local o = setmetatable({}, self) - o.parent = parent - o.sprite, o.quads = parent:getSheet() - return o + Selector.__super.new(self, parent) + self.sprite, self.quads = parent:getSheet() end -- Size of single block diff --git a/not/Settings.lua b/not/Settings.lua index e3316f9..ca429eb 100644 --- a/not/Settings.lua +++ b/not/Settings.lua @@ -4,26 +4,39 @@ Settings = { current = {} } -function Settings.load() +-- Converts from old settings format to the one after `02aba07e03465205b45c41df7aec6894d4e89909`. +local function convertToNew (old) + return {sets = old, display = "fullscreen"} +end + +local function filePrepare () + if not love.filesystem.exists("settings") then + local def = love.filesystem.newFile("settings.default") + local new = love.filesystem.newFile("settings") + new:open("w") def:open("r") + new:write(def:read()) + new:close() def:close() + end +end + +local function fileLoad () + local getSettings = love.filesystem.load("settings") + local settings = getSettings() + if not settings.sets then + settings = convertToNew(settings) + end + Settings.current = settings +end + +local function controllerLoad () if Controller then - if not love.filesystem.exists("settings") then - local def = love.filesystem.newFile("settings.default") - local new = love.filesystem.newFile("settings") - new:open("w") def:open("r") - new:write(def:read()) - new:close() def:close() - end - local getSettings = love.filesystem.load("settings") - Settings.current = getSettings() Controller.reset() - local joysticksList = love.joystick.getJoysticks() -- local list for editing - for _,set in pairs(Settings.current) do + local joysticksList = love.joystick.getJoysticks() + for _,set in pairs(Settings.current.sets) do local isJoystick = set[7] local joystick if isJoystick then - -- take and remove first joystick from list - joystick = joysticksList[1] - table.remove(joysticksList, 1) + joystick = table.remove(joysticksList, 1) end if not isJoystick or joystick then Controller.registerSet(set[1], set[2], set[3], set[4], set[5], set[6], joystick) @@ -32,12 +45,37 @@ function Settings.load() end end -function Settings.save() +local function displayLoad () + local width, height, flags = love.window.getMode() + if Settings.current.display == "fullscreen" then + if not flags.fullscreen then + love.window.setFullscreen(true, "desktop") + end + else + local scale = tonumber(Settings.current.display) or 1 + local expectedWidth, expectedHeight = 320 * scale, 180 * scale + if flags.fullscreen then + love.window.setFullscreen(false) + end + if width ~= expectedWidth or height ~= expectedHeight then + love.window.setMode(expectedWidth, expectedHeight) + end + end +end + +function Settings.load () + filePrepare() + fileLoad() + controllerLoad() + displayLoad() +end + +function Settings.save () local new = love.filesystem.newFile("settings") - local sets = Settings.current - local string = "return {\n" + local sets = Settings.current.sets + local string = "return {\n\tsets = {\n" for i,set in pairs(sets) do - string = string .. "\t{" + string = string .. "\t\t{" for j,word in pairs(set) do if j ~= 7 then string = string .. "\"" .. word .. "\", " @@ -51,22 +89,26 @@ function Settings.save() end string = string .. "},\n" end - string = string .. "}" + string = string .. "\t},\n" + string = string .. "\tdisplay = \"" .. Settings.current.display .. "\",\n" + string = string .. "}\n" new:open("w") new:write(string) new:close() end -function Settings.change(n, left, right, up, down, attack, jump, joystick) +function Settings.change (n, left, right, up, down, attack, jump, joystick) local bool if joystick then bool = true else bool = false end - -- Save current settings - Settings.current[n] = {left, right, up, down, attack, jump, bool} + Settings.current.sets[n] = {left, right, up, down, attack, jump, bool} + Settings.reload() +end + +function Settings.reload () Settings.save() - -- Load settings Settings.load() end diff --git a/not/Sprite.lua b/not/Sprite.lua index 25d85f1..3951e6e 100644 --- a/not/Sprite.lua +++ b/not/Sprite.lua @@ -1,34 +1,27 @@ +require "not.Object" + --- `Sprite` -- Abstract class for drawable animated entities. -Sprite = { - animations =--[[table with animations]]nil, - current =--[[animations.default]]nil, - image =--[[love.graphics.newImage]]nil, - frame = 1, - delay = .1, -} -Sprite.__index = Sprite - ---[[ Constructor of `Sprite`. +Sprite = Object:extends() + +Sprite.animations =--[[table with animations]]nil +Sprite.current =--[[animations.default]]nil +Sprite.image =--[[love.graphics.newImage]]nil +Sprite.frame = 1 +Sprite.delay = .1 + +-- Constructor of `Sprite`. function Sprite:new (imagePath) - local o = setmetatable({}, self) - o:init(imagePath) - return o + if type(imagePath) == "string" then + self:setImage(Sprite.newImage(imagePath)) + end end -]] -- Cleans up reference to image on deletion. function Sprite:delete () self.image = nil end --- Initializes new Sprite instance. -function Sprite:init (imagePath) - if type(imagePath) == "string" then - self:setImage(Sprite.newImage(imagePath)) - end -end - -- Creates new Image object from path. Key-colours two shades of green. Static. function Sprite.newImage (path) local imagedata = love.image.newImageData(path) @@ -64,7 +57,7 @@ end -- Sets current animation by table key. function Sprite:setAnimation (animation) self.frame = 1 - self.delay = Sprite.delay -- INITIAL from metatable + self.delay = Sprite.delay -- INITIAL from prototype self.current = self.animations[animation] end -- Returns current animation table. @@ -79,26 +72,15 @@ function Sprite:getQuad () end end --- TODO: Following five methods are stupid, do something about them! --- Sprite can't be moved by itself. Positioning should be handled by children's methods. -function Sprite:getPosition () - return 0,0 -end --- Sprite can't be rotated by itself. Rotation should be handled by children's methods. -function Sprite:getAngle () - return 0 -end --- Sprite can't be mirrored by itself. Mirroring should be handled by children's methods. -function Sprite:getHorizontalMirror () - return 1 -end -function Sprite:getVerticalMirror () - return 1 -end --- Sprite can't be offset by itself. Offsetting should be handled by children's methods. -function Sprite:getOffset () - return 0,0 -end +-- Sprite's position. Can be overriden to add functionality. +function Sprite:getPosition () return 0,0 end +-- Sprite's angle. Can be overriden to add functionality. +function Sprite:getAngle () return 0 end +-- Sprite's horizontal and vertical mirrors. Can be overriden to add functionality. +function Sprite:getHorizontalMirror () return 1 end +function Sprite:getVerticalMirror () return 1 end +-- Sprite's drawing offset from position. Can be overriden to add functionality. +function Sprite:getOffset () return 0,0 end -- Drawing self to LOVE2D buffer. -- If there is no Quad, it will draw entire image. It won't draw anything if there is no image. @@ -136,7 +118,7 @@ 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.delay = self.delay + Sprite.delay -- INITIAL from prototype self:goToNextFrame() end end @@ -149,4 +131,6 @@ function Sprite:goToNextFrame () else self:setAnimation("default") end -end
\ No newline at end of file +end + +return Sprite diff --git a/not/World.lua b/not/World.lua index bbceec4..c73a3da 100644 --- a/not/World.lua +++ b/not/World.lua @@ -1,29 +1,21 @@ --- `World` -- Used to manage physical world and everything inside it: clouds, platforms, nauts, background etc. -- TODO: Possibly move common parts of `World` and `Menu` to abstract class `Scene`. -World = { - world = --[[love.physics.newWorld]]nil, - Nauts = --[[{not.Hero}]]nil, - Platforms = --[[{not.Platform}]]nil, - Clouds = --[[{not.Cloud}]]nil, - Decorations = --[[{not.Decoration}]]nil, - Effects = --[[{not.Effect}]]nil, - Rays = --[[{not.Ray}]]nil, - camera = --[[not.Camera]]nil, - -- cloud generator - clouds_delay = 5, - -- Map - map = nil, - background = nil, - -- Gameplay status - lastNaut = false, - -- "WINNER" - win_move = 0, - -- Music - music = nil -} +World = require "not.Scene":extends() -World.__index = World +World.world =--[[love.physics.newWorld]]nil +World.Nauts =--[[{not.Hero}]]nil +World.Platforms =--[[{not.Platform}]]nil +World.Clouds =--[[{not.Cloud}]]nil +World.Decorations =--[[{not.Decoration}]]nil +World.Effects =--[[{not.Effect}]]nil +World.Rays =--[[{not.Ray}]]nil +World.camera =--[[not.Camera]]nil +World.music =--[[not.Music]]nil +World.clouds_delay = 5 +World.map =--[[config.maps.*]]nil +World.background =--[[image?]]nil +World.lastNaut = false require "not.Platform" require "not.Player" @@ -31,17 +23,9 @@ require "not.Cloud" require "not.Effect" require "not.Decoration" require "not.Ray" -require "not.Music" -- Constructor of `World` ZA WARUDO! function World:new (map, nauts) - local o = setmetatable({}, self) - o:init(map, nauts) - return o -end - --- Init za warudo -function World:init (map, nauts) -- Box2D physical world. love.physics.setMeter(64) self.world = love.physics.newWorld(0, 9.81*64, true) @@ -53,14 +37,13 @@ function World:init (map, nauts) self.Effects = {} self.Decorations = {} self.Rays = {} - -- Random init; TODO: use LOVE2D's random. - math.randomseed(os.time()) -- Map and misc. local map = map or "default" self:loadMap(map) self:spawnNauts(nauts) self.camera = Camera:new(self) - self.music = Music:new(self.map.theme) + musicPlayer:setTrack(self.map.theme) + musicPlayer:play() end -- The end of the world @@ -71,7 +54,6 @@ function World:delete () for _,naut in pairs(self.Nauts) do naut:delete() end - self.music:delete() self.world:destroy() end @@ -110,20 +92,20 @@ end -- Get respawn location function World:getSpawnPosition () - local n = math.random(1, #self.map.respawns) + local n = love.math.random(1, #self.map.respawns) return self.map.respawns[n].x, self.map.respawns[n].y end -- Add new platform to the world -- TODO: it would be nice if function parameters would be same as `not.Platform.new`. function World:createPlatform (x, y, polygon, sprite, animations) - table.insert(self.Platforms, Platform:new(animations, polygon, self, x, y, sprite)) + table.insert(self.Platforms, Platform(animations, polygon, x, y, self, sprite)) end -- Add new naut to the world -- TODO: separate two methods for `not.Hero` and `not.Player`. function World:createNaut (x, y, name) - local naut = Player:new(name, self, x, y) + local naut = Player(name, x, y, self) table.insert(self.Nauts, naut) return naut end @@ -131,14 +113,14 @@ end -- Add new decoration to the world -- TODO: `not.World.create*` functions often have different naming for parameters. It is not ground-breaking but it makes reading code harder for no good reason. function World:createDecoration (x, y, sprite) - table.insert(self.Decorations, Decoration:new(x, y, sprite)) + table.insert(self.Decorations, Decoration(x, y, self, sprite)) end -- Add new cloud to the world -- TODO: extend variables names to provide better readability. -- TODO: follow new parameters in `not.Cloud.new` based on `not.Cloud.init`. function World:createCloud (x, y, t, v) - table.insert(self.Clouds, Cloud:new(x, y, t, v)) + table.insert(self.Clouds, Cloud(x, y, t, v, self)) end -- Randomize Cloud creation @@ -151,13 +133,13 @@ function World:randomizeCloud (outside) local x,y,t,v local m = self.map if outside then - x = m.center_x-m.width*1.2+math.random(-50,20) + x = m.center_x-m.width*1.2+love.math.random(-50,20) else - x = math.random(m.center_x-m.width/2,m.center_x+m.width/2) + x = love.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) + y = love.math.random(m.center_y-m.height/2, m.center_y+m.height/2) + t = love.math.random(1,3) + v = love.math.random(8,18) self:createCloud(x, y, t, v) end @@ -165,12 +147,12 @@ end -- TODO: follow new parameters in `not.Effect.new` based on `not.Effect.init`. -- TODO: along with `createRay` move this nearer reast of `create*` methods for readability. function World:createEffect (name, x, y) - table.insert(self.Effects, Effect:new(name, x, y)) + table.insert(self.Effects, Effect(name, x, y, self)) end -- Add a ray function World:createRay (naut) - table.insert(self.Rays, Ray:new(naut, self)) + table.insert(self.Rays, Ray(naut, self)) end -- get Nauts functions @@ -210,18 +192,15 @@ function World:onNautKilled (naut) self:createRay(naut) local nauts = self:getNautsPlayable() if self.lastNaut then - changeScene(Menu:new()) + sceneManager:removeTopScene() + sceneManager:changeScene(Menu()) elseif #nauts < 2 then self.lastNaut = true naut:playSound(5, true) + sceneManager:addScene(Menu("win")) 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) @@ -267,18 +246,13 @@ function World:update (dt) 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 + local scale = getScale() + local scaler = getRealScale() -- Background love.graphics.draw(self.background, 0, 0, 0, scaler, scaler) @@ -341,6 +315,10 @@ function World:draw () love.graphics.line(x1,y1,x2,y2) end + for _,naut in pairs(self.Nauts) do + naut:drawTag(offset_x, offset_y, scale) + end + -- Draw HUDs for _,naut in pairs(self.Nauts) do -- I have no idea where to place them T_T @@ -350,31 +328,20 @@ function World:draw () 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 +-- TODO: Rather than here, these contacts should be in `Hero` (most likely). +-- TODO: Explode these into more functions.\ +-- TODO: Stop using magical numbers: +-- [1] -> Platform +-- [2] -> Hero +-- [3] -> Punch sensor function World.beginContact (a, b, coll) if a:getCategory() == 1 then local x,y = coll:getNormal() if y < -0.6 then - -- TODO: move landing to `not.Hero` - -- Move them to Hero - b:getUserData().inAir = false - b:getUserData().jumpCounter = 2 - b:getUserData().salto = false - b:getUserData():createEffect("land") + b:getUserData():land() end local vx, vy = b:getUserData().body:getLinearVelocity() if math.abs(x) == 1 or (y < -0.6 and x == 0) then @@ -382,16 +349,27 @@ function World.beginContact (a, b, coll) end end if a:getCategory() == 3 then - b:getUserData():damage(a:getUserData()[2]) + if b:getCategory() == 2 then + b:getUserData():damage(a:getUserData()[2]) + end + if b:getCategory() == 3 then + a:getBody():getUserData():damage(b:getUserData()[2]) + b:getBody():getUserData():damage(a:getUserData()[2]) + local x1,y1 = b:getBody():getUserData():getPosition() + local x2,y2 = a:getBody():getUserData():getPosition() + local x = (x2 - x1) / 2 + x1 - 12 + local y = (y2 - y1) / 2 + y1 - 15 + a:getBody():getUserData().world:createEffect("clash", x, y) + end end if b:getCategory() == 3 then - a:getUserData():damage(b:getUserData()[2]) + if a:getCategory() == 2 then + a:getUserData():damage(b:getUserData()[2]) + end end end --- endContact function World.endContact (a, b, coll) if a:getCategory() == 1 then - -- Move them to Hero b:getUserData().inAir = true end end @@ -403,10 +381,15 @@ function World:controlpressed (set, action, key) local map = self:getMapName() local nauts = {} for _,naut in pairs(self:getNautsAll()) do - table.insert(nauts, {naut.name, naut:getControlSet()}) + table.insert(nauts, {naut.name, naut:getControllerSet()}) end - local new = World:new(map, nauts) - changeScene(new) + local new = World(map, nauts) + sceneManager:changeScene(new) + end + if key == "escape" then + sceneManager:addScene(Menu("pause")) + self:setInputDisabled(true) + self:setSleeping(true) end for k,naut in pairs(self:getNautsAll()) do naut:controlpressed(set, action, key) @@ -416,4 +399,4 @@ 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 +end |