diff --git a/Makefile b/Makefile index 1980510fa..9aee96953 100644 --- a/Makefile +++ b/Makefile @@ -42,11 +42,10 @@ bin/tmx2lua: bin/love.app/Contents/MacOS/love: mkdir -p bin - $(wget) https://bitbucket.org/kyleconroy/love/downloads/love-sparkle.zip - unzip -q love-sparkle.zip - rm -f love-sparkle.zip + $(wget) https://bitbucket.org/kyleconroy/love/downloads/love-osx-mavericks-fixed.zip + unzip -q love-osx-mavericks-fixed.zip + rm -f love-osx-mavericks-fixed.zip mv love.app bin - cp osx/dsa_pub.pem bin/love.app/Contents/Resources cp osx/Info.plist bin/love.app/Contents /usr/bin/love: diff --git a/osx/Info.plist b/osx/Info.plist index 8a67a798c..04fdf64fd 100644 --- a/osx/Info.plist +++ b/osx/Info.plist @@ -40,9 +40,5 @@ 2006-2012 Hawkthorne Development Team NSPrincipalClass NSApplication - SUFeedURL - http://example.com - SUPublicDSAKeyFile - dsa_pub.pem diff --git a/src/audio/sfx/fire_thrown.ogg b/src/audio/sfx/fire_thrown.ogg index 12b7f89ae..c853dd7ac 100644 Binary files a/src/audio/sfx/fire_thrown.ogg and b/src/audio/sfx/fire_thrown.ogg differ diff --git a/src/character.lua b/src/character.lua index b728021d5..19b766586 100644 --- a/src/character.lua +++ b/src/character.lua @@ -4,172 +4,221 @@ local Timer = require 'vendor/timer' local sound = require 'vendor/TEsound' local utils = require 'utils' -local contents, _ = love.filesystem.read('character_map.json') -local sprite_map = json.decode(contents) +local module = {} -local characters = {} +-- Just for backwards compat +module.name = 'abed' -for i,p in pairs( love.filesystem.enumerate( 'characters' ) ) do - - -- bring in the data from the character file - local contents, _ = love.filesystem.read('characters/' .. p) - local character = json.decode(contents) - - if character.animations then --merge - local base = utils.deepcopy(character.animations) - character.animations = utils.deepcopy(sprite_map) - for k,v in pairs(base) do - character.animations[k] = v - end - else - character.animations = utils.deepcopy(sprite_map) - end - - -- build the character - character.beam = love.graphics.newImage( 'images/characters/' .. character.name .. '/beam.png') - character.beam:setFilter('nearest', 'nearest') - - character.count = 1 - - character.sheets = {} - character.sheets.base = love.graphics.newImage( 'images/characters/' .. character.name .. '/base.png') - character.sheets.base:setFilter('nearest', 'nearest') - - character.mask = love.graphics.newQuad(0, character.offset, 48, 35, character.sheets.base:getWidth(), character.sheets.base:getHeight()) - - character.positions = require( 'positions/' .. character.name ) - - character._grid = anim8.newGrid( 48, 48, character.sheets.base:getWidth(), character.sheets.base:getHeight() ) - character._warp = anim8.newGrid( 36, 300, character.beam:getWidth(), character.beam:getHeight() ) - - for state, _ in pairs( character.animations ) do - local data = character.animations[ state ] - if state == 'warp' then - character.animations[ state ] = anim8.newAnimation(data[1], character._warp(unpack(data[2])), data[3]) - else - if type( data[1] ) == 'string' then - -- positionless - character.animations[ state ] = anim8.newAnimation(data[1], character._grid(unpack(data[2])), data[3]) - else - -- positioned - for i, _ in pairs( data ) do - character.animations[ state ][i] = anim8.newAnimation(data[i][1], character._grid(unpack(data[i][2])), data[i][3]) - end - end - end - end - - character.costumemap = {} - character.categorytocostumes = {} - for _,c in pairs( character.costumes ) do - character.costumemap[ c.sheet ] = c - character.categorytocostumes[ c.category ] = character.categorytocostumes[ c.category ] or {} - table.insert( character.categorytocostumes[ c.category ], c ) - end - - characters[ character.name ] = character -end +local _loaded_character = nil +local _character = 'abed' +local _costume = 'base' local Character = {} Character.__index = Character -Character.characters = characters - -Character.name = 'abed' -Character.costume = 'base' - -Character.warpin = false function Character:reset() - self.state = 'idle' - self.direction = 'right' + self.state = 'idle' + self.direction = 'right' end -function Character:setCharacter( name ) - if character == self.name then return end +-- FIXME: Needed for the taco meat item +-- If a character doesn't have this costume, no effect takes place +function Character:setCostume(costume) + if costume == self.costume then + return + end - if self.characters[name] then - self.name = name - self.costume = 'base' - return - end + -- Stuff for magic +end - error( "Invalid character ( " .. name .. " ) requested!" ) +function Character:sheet() + return self:getSheet(self.costume) end -function Character:setCostume( costume ) - if costume == self.costume then return end +function Character:getSheet(costume) + local path = 'images/characters/' .. self.name .. '/' .. costume .. '.png' - if self:hasCostume(costume) then - self.costume = costume - return - end - - error( "Undefined costume ( " .. costume .. " ) requested for character ( " .. self.name .. " )" ) + if not self.sheets[costume] then + self.sheets[costume] = love.graphics.newImage(path) + self.sheets[costume]:setFilter('nearest', 'nearest') + end + + return self.sheets[costume] end -function Character:hasCostume( costume ) - for _,c in pairs( self:current().costumes ) do - if c.sheet == costume then - return true - end - end - return false +function Character:update(dt) + self:animation():update(dt) end -function Character:current() - return self.characters[self.name] +function Character:animation() + return self.animations[self.state][self.direction] end -function Character:sheet() - return self:getSheet( self.name, self.costume ) +function Character:warpUpdate(dt) + self.animations.warp:update(dt) end -function Character:getSheet(char,costume) - if not self.characters[char].sheets[costume] then - self.characters[char].sheets[costume] = love.graphics.newImage( 'images/characters/' .. char .. '/' .. costume .. '.png') - self.characters[char].sheets[costume]:setFilter('nearest', 'nearest') - end - return self.characters[char].sheets[costume] +function Character:respawn() + self.warpin = true + self.animations.warp:gotoFrame(1) + self.animations.warp:resume() + sound.playSfx( "respawn" ) + Timer.add(0.30, function() self.warpin = false end) end -function Character:updateAnimation(dt) - self:animation():update(dt) +function Character:draw(x, y) + self:animation():draw(self:sheet(), x, y) end -function Character:animation() - return self.characters[self.name].animations[self.state][self.direction] +function Character:getCategory() + return self.costumemap[self.costume].category end -function Character:warpUpdate(dt) - self:current().animations.warp:update(dt) +function Character:getOverworld() + return self.costumemap[self.costume].ow end -function Character:respawn() - self.warpin = true - self:current().animations.warp:gotoFrame(1) - self:current().animations.warp:resume() - sound.playSfx( "respawn" ) - Timer.add(0.30, function() self.warpin = false end) +function module.pick(name, costume) + if not love.filesystem.exists("characters/" .. name .. ".json") then + error("Unknown character " .. name) + end + + if not love.filesystem.exists("images/characters/" .. name .. "/" .. costume .. ".png") then + error("Unknown costume " .. costume .. " for character " .. name) + end + + _character = name + _costume = costume + _loaded_character = nil +end + +function module.load(character) + if not love.filesystem.exists("characters/" .. character .. ".json") then + error("Unknown character " .. character) + end + + local contents, _ = love.filesystem.read('characters/' .. character .. ".json") + return json.decode(contents) end -function Character:draw() +-- Load the current character. Do all the crazy stuff too +function module.current() + if _loaded_character then + return _loaded_character + end + + local beamPath = 'images/characters/' .. _character .. '/beam.png' + local basePath = 'images/characters/' .. _character .. '/base.png' + local characterPath = "characters/" .. _character .. ".json" + + if not love.filesystem.exists(characterPath) then + error("Unknown character " .. _character) + end + + local contents, _ = love.filesystem.read('character_map.json') + local sprite_map = json.decode(contents) + + local contents, _ = love.filesystem.read(characterPath) + + local character = json.decode(contents) + setmetatable(character, Character) + + character.name = _character + character.costume = _costume + character.warpin = false + + if character.animations then --merge + local base = utils.deepcopy(character.animations) + character.animations = utils.deepcopy(sprite_map) + for k,v in pairs(base) do + character.animations[k] = v + end + else + character.animations = utils.deepcopy(sprite_map) + end + + -- build the character + character.beam = love.graphics.newImage(beamPath) + character.beam:setFilter('nearest', 'nearest') + + character.count = 1 + + character.sheets = {} + character.sheets.base = love.graphics.newImage(basePath) + character.sheets.base:setFilter('nearest', 'nearest') + + character.mask = love.graphics.newQuad(0, character.offset, 48, 35, + character.sheets.base:getWidth(), + character.sheets.base:getHeight()) + + character.positions = utils.require('positions/' .. character.name) + + character._grid = anim8.newGrid(48, 48, + character.sheets.base:getWidth(), + character.sheets.base:getHeight()) + + character._warp = anim8.newGrid(36, 300, character.beam:getWidth(), character.beam:getHeight()) + + for state, _ in pairs(character.animations) do + local data = character.animations[state] + if state == 'warp' then + character.animations[state] = anim8.newAnimation(data[1], character._warp(unpack(data[2])), data[3]) + else + if type( data[1] ) == 'string' then + -- positionless + character.animations[state] = anim8.newAnimation(data[1], character._grid(unpack(data[2])), data[3]) + else + -- positioned + for i, _ in pairs( data ) do + character.animations[state][i] = anim8.newAnimation(data[i][1], character._grid(unpack(data[i][2])), data[i][3]) + end + end + end + end + + character.costumemap = {} + character.categorytocostumes = {} + + for _,c in pairs(character.costumes) do + character.costumemap[c.sheet] = c + character.categorytocostumes[c.category] = character.categorytocostumes[c.category] or {} + table.insert(character.categorytocostumes[c.category], c) + end + + _loaded_character = character + return character end -function Character:getCategory() - return self:current().costumemap[ self.costume ].category + +function module.getCostumeImage(character, costume) + local path = "images/characters/" .. character .. "/" .. costume .. ".png" + return love.graphics.newImage(path) end -function Character:getOverworld() - return self:current().costumemap[ self.costume ].ow + +function module.characters() + local list = {} + + for _, filename in pairs(love.filesystem.enumerate('characters')) do + local name, _ = filename:gsub(".json", "") + table.insert(list, name) + end + + return list end -function Character:findRelatedCostume( char ) - --returns the requested character's costume that is most similar to the current character - local costumes = self.characters[ char ].categorytocostumes[ self:getCategory() ] - if costumes then return costumes[math.random(#costumes)].sheet end - return 'base' +--returns the requested character's costume that is most similar to the current character +function module.findRelatedCostume(name, category) + local char = module.load(name) + + for _, costume in pairs(char.costumes) do + if costume.category == category then + return costume.sheet + end + end + + return 'base' end -Character:reset() -return Character +return module diff --git a/src/characterstrip.lua b/src/characterstrip.lua index 93111beeb..0b0616004 100644 --- a/src/characterstrip.lua +++ b/src/characterstrip.lua @@ -3,11 +3,11 @@ -- A single colored strip, on which a character appears for selection. -- Created by tjvezina ---------------------------------------------------------------------- +local window = require 'window' local CharacterStrip = {} CharacterStrip.__index = CharacterStrip -local window = require 'window' local stripSize = 35 -- Thickness of the strip local moveSize = 300 -- Pixels travelled from ratio 0 to 1 diff --git a/src/credits.lua b/src/credits.lua index 23230daa1..c213f4e93 100644 --- a/src/credits.lua +++ b/src/credits.lua @@ -44,6 +44,7 @@ end state.credits = { app.i18n('credits'), '6sutters', + '8bitgentleman', 'a8252359', 'aaronpetykowski', 'academania', diff --git a/src/debugger.lua b/src/debugger.lua index 91d8293b0..85e19f83c 100644 --- a/src/debugger.lua +++ b/src/debugger.lua @@ -115,7 +115,7 @@ function Debugger:draw() end love.graphics.setColor( 255, 255, 255, 255 ) fonts.set('big') - love.graphics.print( math.floor( collectgarbage( 'count' ) / 10 ) / 10 , window.screen_width - 30, window.screen_height - 10,0,0.5,0.5 ) + love.graphics.print(math.floor(collectgarbage('count')), window.screen_width - 30, window.screen_height - 10,0,0.5,0.5 ) fonts.revert() end diff --git a/src/flyin.lua b/src/flyin.lua index ea057ba9e..a4f97f5ce 100644 --- a/src/flyin.lua +++ b/src/flyin.lua @@ -3,62 +3,123 @@ local Gamestate = require 'vendor/gamestate' local window = require 'window' local fonts = require 'fonts' local TunnelParticles = require "tunnelparticles" -local flyin = Gamestate.new() local sound = require 'vendor/TEsound' local Timer = require 'vendor/timer' -local Character = require 'character' +local character = require 'character' local utils = require 'utils' +local flyin = Gamestate.new() + function flyin:init( ) - TunnelParticles:init() + TunnelParticles:init() end function flyin:enter( prev ) - self.flying = {} - self.characterorder = {} - for i,c in pairs(Character.characters) do - if c.name ~= Character.name then - table.insert(self.characterorder, c.name) - end + self.flying = {} + self.images = {} + self.masks = {} + self.characterorder = {} + + self.current = character.current() + + -- Only include greendale seven + for _, name in pairs({"abed", "annie", "jeff", "pierce", "troy", "britta", "shirley"}) do + if name ~= self.current.name then + table.insert(self.characterorder, name) end - self.characterorder = utils.shuffle( self.characterorder, 5 ) - table.insert( self.characterorder, Character.name ) - local time = 0 - for _,name in pairs( self.characterorder ) do - Timer.add(time, function() - table.insert( self.flying, { - n = name, - c = name == Character.name and Character.costume or Character:findRelatedCostume(name), - x = window.width / 2, - y = window.height / 2, - t = math.random( ( math.pi * 2 ) * 10000 ) / 10000, - r = name == Character.name and 0 or ( math.random( 4 ) - 1 ) * ( math.pi / 2 ), - s = 0.1, - show = true - }) - end) - time = time + 0.4 + end + + self.characterorder = utils.shuffle(self.characterorder, 5) + + table.insert(self.characterorder, self.current.name) + + local time = 0 + + for _, name in pairs(self.characterorder) do + local costume_name = self.current.costume + + if name ~= self.current.name then + costume_name = character.findRelatedCostume(name, self.current:getCategory()) end + + Timer.add(time, function() + table.insert(self.flying, { + n = name, + c = costume_name, + x = window.width / 2, + y = window.height / 2, + t = math.random((math.pi * 2) * 10000) / 10000, + r = name == self.current.name and 0 or (math.random(4) - 1) * (math.pi / 2), + s = 0.1, + show = true + }) + end) + time = time + 0.4 + end +end + +function flyin:leave() + self.current = nil + self.flying = {} + self.images = {} + self.masks = {} + self.characterorder = {} + TunnelParticles.leave() +end + +function flyin:drawCharacter(flyer, x, y, r, sx, sy, ox, oy) + local name = flyer.n + local costume = flyer.c + local key = name .. costume + + + -- find costume + -- load image + -- load mask + -- draw + + --local char = self:loadCharacter(name) + --local key = name .. char.costume + + if not self.images[key] then + self.images[key] = character.getCostumeImage(name, costume) + end + + local image = self.images[key] + + if not self.masks[key] then + self.masks[key] = love.graphics.newQuad(11 * 48, 4 * 48, 48, 48, + image:getWidth(), image:getHeight()) + end + + local mask = self.masks[key] + + love.graphics.drawq(image, mask, x, y, r, sx, sy, ox, oy) end + function flyin:draw() - TunnelParticles.draw() - - love.graphics.circle( 'fill', window.width / 2, window.height / 2, 30 ) - - --draw in reverse order, so the older ones get drawn on top of the newer ones - for i = #flyin.flying, 1, -1 do - local v = flyin.flying[i] - if v.show then - love.graphics.setColor( 255, 255, 255, 255 ) - Character.characters[v.n].animations.flyin:draw( Character:getSheet(v.n,v.c), v.x, v.y, v.r - ( v.r % ( math.pi / 2 ) ), math.min(v.s,5), math.min(v.s,5), 22, 32 ) - -- black mask while coming out of 'tunnel' - if v.s <= 1 then - love.graphics.setColor( 0, 0, 0, 255 * ( 1 - v.s ) ) - Character.characters[v.n].animations.flyin:draw( Character:getSheet(v.n,v.c), v.x, v.y, v.r - ( v.r % ( math.pi / 2 ) ), math.min(v.s,5), math.min(v.s,5), 22, 32 ) - end - end + TunnelParticles.draw() + + love.graphics.circle('fill', window.width / 2, window.height / 2, 30) + + -- draw in reverse order, so the older ones get drawn on top of the newer ones + for i = #flyin.flying, 1, -1 do + local v = flyin.flying[i] + if v.show then + love.graphics.setColor(255, 255, 255, 255) + + self:drawCharacter(v, v.x, v.y, v.r - (v.r % (math.pi / 2)), + math.min(v.s, 5), math.min(v.s, 5), 22, 32) + -- black mask while coming out of 'tunnel' + if v.s <= 1 then + love.graphics.setColor(0, 0, 0, 255 * (1 - v.s )) + + self:drawCharacter(v, v.x, v.y, v.r - (v.r % (math.pi / 2)), + math.min(v.s, 5), math.min(v.s, 5), 22, 32) + end end + end end function flyin:startGame(dt) @@ -68,27 +129,27 @@ function flyin:startGame(dt) end function flyin:keypressed(button) - Timer.clear() - self:startGame() + Timer.clear() + self:startGame() end function flyin:update(dt) - TunnelParticles.update(dt) - for k,v in pairs(flyin.flying) do - if v.n ~= Character.name then - v.x = v.x + ( math.cos( v.t ) * dt * v.s * 90 ) - v.y = v.y + ( math.sin( v.t ) * dt * v.s * 90 ) - end - v.s = v.s + dt * 4 - v.r = v.r + dt * 5 - if v.s >= 6 then - v.show = false - end + TunnelParticles.update(dt) + for k,v in pairs(flyin.flying) do + if v.n ~= self.current.name then + v.x = v.x + ( math.cos( v.t ) * dt * v.s * 90 ) + v.y = v.y + ( math.sin( v.t ) * dt * v.s * 90 ) end - if not flyin.flying[ #flyin.flying ].show then - Timer.clear() - self:startGame() + v.s = v.s + dt * 4 + v.r = v.r + dt * 5 + if v.s >= 6 then + v.show = false end + end + if not flyin.flying[#flyin.flying].show then + Timer.clear() + self:startGame() + end end return flyin diff --git a/src/gameover.lua b/src/gameover.lua index 42b4dcf89..d1d6115d0 100644 --- a/src/gameover.lua +++ b/src/gameover.lua @@ -8,59 +8,62 @@ local part = require 'verticalparticles' local character = require 'character' function state:init() - self.text="G A M E O V E R !" - self.text2="-Press any key to continue-" - part.init() - -- The X coordinates of the columns - self.left = 180 - -- The Y coordinate of the top key - self.top = 93 - -- Vertical spacing between keys - self.spacing = 20 + self.text="G A M E O V E R !" + self.text2="-Press any key to continue-" + part.init() + -- The X coordinates of the columns + self.left = 180 + -- The Y coordinate of the top key + self.top = 93 + -- Vertical spacing between keys + self.spacing = 20 end function state:enter(previous) - fonts.set( 'big' ) - sound.playMusic( "you-just-lost" ) + fonts.set('big') + sound.playMusic("you-just-lost") - character.state = 'dead' - character.direction = 'right' - - self.blink=0 - - camera:setPosition(0, 0) - self.previous = previous + self.character = character:current() + self.character.state = 'dead' + self.character.direction = 'right' + + self.blink = 0 + + camera:setPosition(0, 0) + self.previous = previous end function state:leave() - fonts.reset() + self.character = nil + self.previous = nil + fonts.reset() end -function state:keypressed( button ) - Gamestate.switch("splash") +function state:keypressed(button) + Gamestate.switch("splash") end function state:update(dt) - character:animation():update(dt) - part.update(dt) - self.blink = self.blink + dt - if self.blink > 1 then - self.blink = 0 - end + self.character:update(dt) + part.update(dt) + self.blink = self.blink + dt + if self.blink > 1 then + self.blink = 0 + end end function state:draw() - love.graphics.setColor( 0, 0, 0, 255 ) - love.graphics.rectangle( "fill", 0, 0, 528, 336 ) - part.draw() - fonts.set('big') - love.graphics.print( self.text, self.left - 20, self.top ) - character:animation():draw( character:sheet(), self.left + 60, self.top + 20 ) - fonts.set('ariel') - if self.blink < 0.5 then - love.graphics.print( self.text2, self.left + 5, self.top + 80 ) - end - love.graphics.setColor( 255, 255, 255, 255 ) + love.graphics.setColor(0, 0, 0, 255) + love.graphics.rectangle("fill", 0, 0, 528, 336) + part.draw() + fonts.set('big') + love.graphics.print(self.text, self.left - 20, self.top ) + self.character:draw(self.left + 60, self.top + 20) + fonts.set('ariel') + if self.blink < 0.5 then + love.graphics.print(self.text2, self.left + 5, self.top + 80) + end + love.graphics.setColor(255, 255, 255, 255) end return state diff --git a/src/hud.lua b/src/hud.lua index c30245dd8..57ec274cf 100644 --- a/src/hud.lua +++ b/src/hud.lua @@ -1,7 +1,6 @@ local window = require 'window' local camera = require 'camera' local fonts = require 'fonts' -local Character = require 'character' local utils = require 'utils' local HUD = {} @@ -20,10 +19,10 @@ function HUD.new(level) local hud = {} setmetatable(hud, HUD) - local character = level.player.character:current() + local character = level.player.character hud.sheet = level.player.character:sheet() - hud.character_quad = love.graphics.newQuad( 0, character.offset or 5, 48, 48, hud.sheet:getWidth(), hud.sheet:getHeight() ) + hud.character_quad = love.graphics.newQuad(0, character.offset or 5, 48, 48, hud.sheet:getWidth(), hud.sheet:getHeight()) hud.character_stencil = function( x, y ) love.graphics.circle( 'fill', x + 31, y + 31, 21 ) @@ -57,21 +56,23 @@ function HUD:draw( player ) 0, 255 ) - love.graphics.draw( energy, self.x - ( player.max_health - player.health ) * 2.8, self.y ) - love.graphics.setStencil( self.character_stencil, self.x, self.y ) - love.graphics.setColor( 255, 255, 255, 255 ) + + love.graphics.draw(energy, self.x - (player.max_health - player.health) * 5.6, self.y) + love.graphics.setStencil(self.character_stencil, self.x, self.y) + love.graphics.setColor(255, 255, 255, 255) + local currentWeapon = player.inventory:currentWeapon() if currentWeapon and not player.doBasicAttack and not player.currently_held or (player.holdingAmmo and currentWeapon) then local position = {x = self.x + 22, y = self.y + 22} currentWeapon:draw(position, nil,false) else - love.graphics.drawq( self.sheet, self.character_quad, self.x + 7, self.y + 17 ) + love.graphics.drawq(self.sheet, self.character_quad, self.x + 7, self.y + 17) end - love.graphics.setStencil( ) - love.graphics.draw( lens, self.x, self.y) + love.graphics.setStencil() + love.graphics.draw(lens, self.x, self.y) love.graphics.setColor( 0, 0, 0, 255 ) - love.graphics.print( player.money, self.x + 69, self.y + 41,0,0.5,0.5) - love.graphics.print( player.character:current().name, self.x + 60, self.y + 15,0,0.5,0.5) + love.graphics.print(player.money, self.x + 69, self.y + 41,0,0.5,0.5) + love.graphics.print(player.character.name, self.x + 60, self.y + 15,0,0.5,0.5) love.graphics.setColor( 255, 255, 255, 255 ) fonts.revert() diff --git a/src/items/consumables/tacomeat.lua b/src/items/consumables/tacomeat.lua index a06b382f4..6f04e8b85 100644 --- a/src/items/consumables/tacomeat.lua +++ b/src/items/consumables/tacomeat.lua @@ -2,7 +2,7 @@ return{ name = 'tacomeat', type = 'consumable', MAX_ITEMS = 10, - use = function( consumable, player ) + use = function(consumable, player) local Timer = require('vendor/timer') local sound = require('vendor/TEsound') local punchDamage = player.punchDamage @@ -21,9 +21,7 @@ return{ end) end Timer.add(6, function () -- Set costume to zombie and double unarmed player damage. - if player.character:hasCostume('zombie') then - player.character:setCostume('zombie') - end + player.character:setCostume('zombie') player.jumpDamage = player.jumpDamage * 2 player.punchDamage = player.punchDamage * 2 player.slideDamage = player.slideDamage * 2 diff --git a/src/level.lua b/src/level.lua index a46d1384a..e91944a9f 100644 --- a/src/level.lua +++ b/src/level.lua @@ -15,28 +15,17 @@ local HUD = require 'hud' local utils = require 'utils' local music = {} -local node_cache = {} -local tile_cache = {} - local Player = require 'player' local Floorspace = require 'nodes/floorspace' local Floorspaces = require 'floorspaces' local Platform = require 'nodes/platform' +local Sprite = require 'nodes/sprite' local Block = require 'nodes/block' local function limit( x, min, max ) return math.min(math.max(x,min),max) end -local function load_tileset(name) - if tile_cache[name] then - return tile_cache[name] - end - - local tileset = tmx.load(require("maps/" .. name)) - tile_cache[name] = tileset - return tileset -end local function on_collision(dt, shape_a, shape_b, mtv_x, mtv_y) if shape_a.player and shape_b.player then return end @@ -141,6 +130,7 @@ function Level.new(name) local level = {} setmetatable(level, Level) + level.paused = false level.over = false level.state = 'idle' -- TODO: Use state machine level.name = name @@ -153,8 +143,9 @@ function Level.new(name) "Check the documentation for more info." ) - level.map = require("maps/" .. name) - level.tileset = load_tileset(name) + level.node_cache = {} + level.map = utils.require("maps/" .. name) + level.tileset = tmx.load(level.map) level.collider = HC(100, on_collision, collision_stop) level.offset = getCameraOffset(level.map) level.music = getSoundtrack(level.map) @@ -181,13 +172,9 @@ function Level.new(name) for k,v in pairs(level.map.objectgroups.nodes.objects) do local nodePath = 'nodes/' .. v.type - local ok, NodeClass = pcall(require, nodePath) + local ok, NodeClass = level:loadNode(nodePath) - if not ok then - - print("WARNING: Can't load " .. nodePath) - - else + if ok then local node if NodeClass and v.type == 'scenetrigger' then @@ -198,7 +185,7 @@ function Level.new(name) level:addNode(node) elseif NodeClass then v.objectlayer = 'nodes' - node = NodeClass.new( v, level.collider, level) + node = NodeClass.new(v, level.collider, level) node.drawHeight = v.height level:addNode(node) end @@ -214,7 +201,6 @@ function Level.new(name) end end - end if level.map.objectgroups.floorspace then @@ -252,6 +238,23 @@ function Level.new(name) return level end +-- Return the node from the filesystem +function Level:loadNode(path) + if self.node_cache[path] then + return true, self.node_cache[path] + end + + local ok, class = pcall(utils.require, path) + + if not ok then + print("WARNING: Can't load " .. path) + end + + self.node_cache[path] = class + + return true, class +end + function Level:restartLevel() assert(self.name ~= "overworld","level's name cannot be overworld") assert(Gamestate.currentState() ~= Gamestate.get("overworld"),"level cannot be overworld") @@ -270,7 +273,8 @@ function Level:restartLevel() end -function Level:enter( previous, door, position ) +function Level:enter(previous, door, position) + self.paused = false self.respawn = false self.state = 'idle' @@ -310,7 +314,8 @@ function Level:enter( previous, door, position ) self.player:respawn() end if self.doors[ door ].node then - self.doors[ door ].node:show() + -- passing previous will allow the door to check the level against its own + self.doors[ door ].node:show(previous) self.player.freeze = false end end @@ -425,10 +430,6 @@ function Level:quit() end end -function Level:leave() - self.state = 'idle' -end - function Level:exit(levelName, doorName) self.respawn = false if self.state ~= 'idle' then @@ -535,13 +536,34 @@ function Level:floorspaceNodeDraw() end end +-- Called by Gamestate.switch when changing levels function Level:leave() - for i,node in pairs(self.nodes) do - if node.leave then node:leave() end - if node.collide_end then - node:collide_end(self.player) - end + for i,node in pairs(self.nodes) do + if node.leave then node:leave() end + if node.collide_end then + node:collide_end(self.player) end + end + + self.previous = nil + + if not self.paused then + self.player = nil + self.map = nil + self.tileset = nil + self.collider = nil + self.offset = nil + self.music = nil + self.spawn = nil + self.overworldName = nil + self.title = nil + self.environment = nil + self.boundary = nil + self.transition = nil + self.events = nil + self.nodes = {} + self.doors = {} + end end function Level:keyreleased( button ) @@ -588,8 +610,8 @@ function Level:keypressed( button ) end if button == 'START' and not self.player.dead and self.player.health > 0 and not self.player.controlState:is('ignorePause') then - Gamestate.switch('pause', self.player) - return true + Gamestate.stack('pause', self.player) + return true end end @@ -639,14 +661,17 @@ function Level:updatePan(dt) end function Level:addNode(node) - if node.containerLevel then + -- FIXME: This seems like a very bad idea + if node.containerLevel and node.containerLevel.collider then node.containerLevel.collider:remove(node.bb) node.containerLevel:removeNode(node) end + node.containerLevel = self table.insert(self.nodes, node) end + function Level:removeNode(node) node.containerLevel = nil for k,v in pairs(self.nodes) do diff --git a/src/main.lua b/src/main.lua index 682a42260..407a4cd67 100644 --- a/src/main.lua +++ b/src/main.lua @@ -16,7 +16,6 @@ local timer = require 'vendor/timer' local cli = require 'vendor/cliargs' local mixpanel = require 'vendor/mixpanel' - local debugger = require 'debugger' local camera = require 'camera' local fonts = require 'fonts' @@ -43,7 +42,9 @@ function love.load(arg) error("Love 0.8.0 is required") end - table.remove(arg, 1) + -- The Mavericks builds of Love adds too many arguements + arg = utils.cleanarg(arg) + local state, door, position = 'update', nil, nil -- SCIENCE! @@ -105,15 +106,22 @@ function love.load(arg) if args["position"] ~= "" then position = args["position"] end + + + -- Choose character and costume + local char = "abed" + local costume = "base" if args["character"] ~= "" then - character:setCharacter( args["c"] ) + char = args["c"] end if args["costume"] ~= "" then - character:setCostume( args["o"] ) + costume = args["o"] end + character.pick(char, costume) + if args["vol-mute"] == 'all' then sound.disabled = true elseif args["vol-mute"] == 'music' then @@ -127,11 +135,11 @@ function love.load(arg) end if args["d"] then - debugger.set( true, false ) + debugger.set(true, false) end if args["b"] then - debugger.set( true, true ) + debugger.set(true, true) end if args["locale"] ~= "" then @@ -188,6 +196,10 @@ function love.update(dt) tween.update(dt > 0 and dt or 0.001) timer.update(dt) sound.cleanup() + + if debugger.on then + collectgarbage("collect") + end end function love.keyreleased(key) @@ -223,6 +235,7 @@ end function love.draw() if testing then return end + camera:set() Gamestate.draw() fonts.set('arial') @@ -252,6 +265,21 @@ function love.draw() end end +--function love.draw() +--end +-- +--function love.update(dt) +--end +-- +--function love.load() +--end +-- +--function love.keyreleased() +--end +-- +--function love.keypressed() +--end + -- Override the default screenshot functionality so we can disable the fps before taking it local newScreenshot = love.graphics.newScreenshot function love.graphics.newScreenshot() diff --git a/src/maps/black-caverns-2.tmx b/src/maps/black-caverns-2.tmx index c23b11c44..0e82c9378 100644 --- a/src/maps/black-caverns-2.tmx +++ b/src/maps/black-caverns-2.tmx @@ -34,7 +34,7 @@ - eJzt2NEKgjAUBmAhu/U9etTeOoQGQ9Q5bZvm98GBLiKl/Z6503UAAMCv9VE9G98L7cV5AMqY67mPRvdyhF5xD2M2+8yCmuKMAgBALVvPSuatjJbyIh9wP3E/qNkDwnVLXfO1UnPCf/D+Futyc5P6/t552tK6DlFRXstZ/NLznXrmj/w255Zas7X9Yc86y8h1pNY97Bs5+Yj3mhJ9iDpye8HR3iEb17F1reb6Rzf5vOXdVDauK+f9g3uocR515gX4b0PD4vzCu6U8MGc6s5AHplIz8qXZlzMtU+YdAJzZB8eyGDg= + eJzt2FsKgzAQBVCh7a/76FK76yIoBIkm8ZWI58CAH5ZIcx11ug4AADjaO6hP5WuhvjAPwDliPfdV6Vr20CueYcjmu7DgSmFGAQDgKrnfSuatDJbyIh/wPGE/uLIHTOueteZ3pWKm/+A3FutKc5M6f+s8LbWvXKPmLH7p/j4iGzm9g/ak9mzt+bBln2XkPlL73o9Vko/pN13kHNm4j9JesLd3yMZ95O5VrH90s+OwX+xdj/aUvH/wDDn3/B3WAKCevmLRvundUh6Imc8s5IG51Ix8afblm5Y58w4AWvYH1tAYDg== @@ -683,7 +683,7 @@ - + @@ -830,7 +830,7 @@ - + @@ -839,7 +839,7 @@ - + @@ -935,7 +935,6 @@ - diff --git a/src/nodes/activenpcs/blacksmith.lua b/src/nodes/activenpcs/blacksmith.lua index cd9e9ccba..f8e909c69 100644 --- a/src/nodes/activenpcs/blacksmith.lua +++ b/src/nodes/activenpcs/blacksmith.lua @@ -43,10 +43,10 @@ return { player.freeze = false local screenshot = love.graphics.newImage( love.graphics.newScreenshot() ) if result == "Yes" then - Gamestate.switch("shopping", player, screenshot, activenpc.name) + Gamestate.stack("shopping", player, screenshot, activenpc.name) end end player.freeze = true activenpc.prompt = Prompt.new("Would you like to see my wares?",callback, options) end -} \ No newline at end of file +} diff --git a/src/nodes/cauldron.lua b/src/nodes/cauldron.lua index d8d7b1651..538cf6cbd 100644 --- a/src/nodes/cauldron.lua +++ b/src/nodes/cauldron.lua @@ -61,7 +61,7 @@ function Cauldron:keypressed( button, player ) player.freeze = false if result == 'Yes' then local screenshot = love.graphics.newImage(love.graphics.newScreenshot()) - Gamestate.switch('brewing', player, screenshot) + Gamestate.stack('brewing', player, screenshot) end end self.prompt = Prompt.new(message, callback, options) diff --git a/src/nodes/cutscenes/welcome_to_hawkthorne.lua b/src/nodes/cutscenes/welcome_to_hawkthorne.lua index 9cce6d7de..b9a7c4fd3 100644 --- a/src/nodes/cutscenes/welcome_to_hawkthorne.lua +++ b/src/nodes/cutscenes/welcome_to_hawkthorne.lua @@ -6,10 +6,6 @@ local sound = require 'vendor/TEsound' local camera = require 'camera' local dialog = require 'dialog' -local head = love.graphics.newImage('images/cornelius_head.png') -local lightning = love.graphics.newImage('images/lightning.png') -local oval = love.graphics.newImage('images/corn_circles.png') -local sparkle = love.graphics.newImage('images/cornelius_sparkles.png') local Scene = {} Scene.__index = Scene @@ -34,6 +30,11 @@ function Scene.new(node, collider, layer) scene.y = node.y scene.finised = false + scene.head = love.graphics.newImage('images/cornelius_head.png') + scene.lightning = love.graphics.newImage('images/lightning.png') + scene.ovalImg = love.graphics.newImage('images/corn_circles.png') + scene.sparkle = love.graphics.newImage('images/cornelius_sparkles.png') + scene.nodes = nametable(layer) scene.nodes.head.opacity = 0 scene.nodes.lightning.opacity = 0 @@ -58,14 +59,14 @@ function Scene.new(node, collider, layer) sy = 1, } - local g = anim8.newGrid(144, 192, head:getWidth(), head:getHeight()) + local g = anim8.newGrid(144, 192, scene.head:getWidth(), scene.head:getHeight()) scene.talking = anim8.newAnimation('loop', g('1,1', '2,1', '3,1', '2,1', '1,1'), 0.15) - local h = anim8.newGrid(72, 312, lightning:getWidth(), lightning:getHeight()) + local h = anim8.newGrid(72, 312, scene.lightning:getWidth(), scene.lightning:getHeight()) scene.electric = anim8.newAnimation('once', h('1-5,1', '4-5,1'), 0.1) - local j = anim8.newGrid(192, 264, oval:getWidth(), oval:getHeight()) + local j = anim8.newGrid(192, 264, scene.ovalImg:getWidth(), scene.ovalImg:getHeight()) scene.circle = anim8.newAnimation('once', j('1-6,1'), 0.15) scene.pulse = anim8.newAnimation('loop', j('5-6,1'), 0.7) - local s = anim8.newGrid(24, 24, sparkle:getWidth(), sparkle:getHeight()) + local s = anim8.newGrid(24, 24, scene.sparkle:getWidth(), scene.sparkle:getHeight()) for spark in pairs(scene.sparkles) do local anim = anim8.newAnimation('loop', s('1-4,1'), 0.22 + math.random() / 10) @@ -157,19 +158,19 @@ function Scene:draw(player) love.graphics.setColor(255, 255, 255, 255) love.graphics.setColor(255, 255, 255, self.nodes.lightning.opacity) - self.electric:draw(lightning, self.nodes.lightning.x, self.nodes.lightning.y) + self.electric:draw(self.lightning, self.nodes.lightning.x, self.nodes.lightning.y) love.graphics.setColor(255, 255, 255, self.nodes.oval.opacity) - self.oval:draw(oval, self.nodes.oval.x, self.nodes.oval.y) + self.oval:draw(self.ovalImg, self.nodes.oval.x, self.nodes.oval.y) love.graphics.setColor(255, 255, 255, self.nodes.head.opacity) - self.talking:draw(head, self.nodes.head.x, self.nodes.head.y) + self.talking:draw(self.head, self.nodes.head.x, self.nodes.head.y) love.graphics.setColor(255, 255, 255, 255) for i, s in pairs(self.sparkle_animations) do local spark = self.sparkles[i] love.graphics.setColor(255, 255, 255, self.sparkle_opacity) - s:draw(sparkle, self.nodes[spark].x, self.nodes[spark].y) + s:draw(self.sparkle, self.nodes[spark].x, self.nodes[spark].y) love.graphics.setColor(255, 255, 255, 255) end diff --git a/src/nodes/dealer.lua b/src/nodes/dealer.lua index cb070b4c2..1a69d1a77 100644 --- a/src/nodes/dealer.lua +++ b/src/nodes/dealer.lua @@ -47,7 +47,7 @@ function Dealer:keypressed( button, player ) player.freeze = false if result == 'Poker' or result == 'Blackjack' then local screenshot = love.graphics.newImage( love.graphics.newScreenshot() ) - Gamestate.switch(result:lower() .. 'game', player, screenshot) + Gamestate.stack(result:lower() .. 'game', player, screenshot) end end diff --git a/src/nodes/door.lua b/src/nodes/door.lua index cdf404e31..7cce32a65 100644 --- a/src/nodes/door.lua +++ b/src/nodes/door.lua @@ -44,6 +44,7 @@ function Door.new(node, collider) -- generic support for hidden doors if door.hideable then + -- necessary for opening/closing doors with a trigger door.hidden = true door.sprite = love.graphics.newImage('images/' .. node.properties.sprite .. '.png') door.sprite_width = tonumber( node.properties.sprite_width ) @@ -130,17 +131,20 @@ function Door:keypressed( button, player) end -- everything below this is required for hidden doors -function Door:show() - if self.hideable and self.hidden then +function Door:show(previous) + -- level check is to ensure that the player is using a switch and not re-entering a level + if self.hideable and self.hidden and ( not previous or previous.name ~= self.level ) then self.hidden = false sound.playSfx( 'reveal' ) Tween.start( self.movetime, self.position, self.position_shown ) end end -function Door:hide() - if self.hideable and not self.hidden then +function Door:hide(previous) + -- level check is to allow door to close on re-entry or close command + if self.hideable and ( (previous and previous.name == self.level) or not self.hidden ) then self.hidden = true + self.position = utils.deepcopy(self.position_shown) sound.playSfx( 'unreveal' ) Tween.start( self.movetime, self.position, self.position_hidden ) end diff --git a/src/nodes/enemies/hippy.lua b/src/nodes/enemies/hippy.lua index 0d243b59f..b482afd42 100644 --- a/src/nodes/enemies/hippy.lua +++ b/src/nodes/enemies/hippy.lua @@ -33,8 +33,10 @@ return { left = {'loop', {'1-2,1'}, 0.25} } }, - splat = function( enemy ) - enemy.splat = splat:add(enemy.position.x, enemy.position.y, enemy.width, enemy.height) + splat = function(enemy) + local s = splat.new(enemy.position.x, enemy.position.y, enemy.width, enemy.height) + s:add(enemy.position.x, enemy.position.y, enemy.width, enemy.height) + return s end, update = function( dt, enemy, player ) if enemy.position.x > player.position.x then diff --git a/src/nodes/enemies/turkey.lua b/src/nodes/enemies/turkey.lua index eb212fc9e..6a4fe715f 100644 --- a/src/nodes/enemies/turkey.lua +++ b/src/nodes/enemies/turkey.lua @@ -11,7 +11,7 @@ return { jumpkill = true, last_jump = 0, bb_width = 50, - bb_height = 50, + bb_height = 30, bb_offset = {x=4, y=22}, velocity = {x = -20, y = 0}, hp = 8, diff --git a/src/nodes/enemies/turkeyBoss.lua b/src/nodes/enemies/turkeyBoss.lua index 80a29d6e6..d51e9ec2f 100644 --- a/src/nodes/enemies/turkeyBoss.lua +++ b/src/nodes/enemies/turkeyBoss.lua @@ -20,12 +20,12 @@ return { jumpkill = false, player_rebound = 1200, bb_width = 40, - bb_height = 105, + bb_height = 95, bb_offset = { x = -40, y = 10}, attack_width = 40, attack_offset = { x = -40, y = 10}, velocity = {x = 0, y = 1}, - hp = 200, + hp = 100, tokens = 15, hand_x = -40, hand_y = 70, @@ -115,7 +115,7 @@ return { love.graphics.rectangle( 'fill', x + 11, y + 27, 59, 9 ) end love.graphics.setStencil(energy_stencil, x, y) - local max_hp = 200 + local max_hp = 100 local rate = 55/max_hp love.graphics.setColor( math.min(utils.map(enemy.hp, max_hp, max_hp / 2 + 1, 0, 255 ), 255), -- green to yellow @@ -220,9 +220,10 @@ return { Timer.add(0.75, function() enemy.direction = direction == -1 and 'right' or 'left' end) elseif enemy.last_attack > pause and enemy.state ~= 'jump' then - if math.random() > 0.9 and enemy.hp < 80 then + local rand = math.random() + if enemy.hp < 80 and rand > 0.9 then enemy.props.spawn_minion(enemy, direction) - elseif math.random() > 0.6 then + elseif rand > 0.6 then enemy.props.wing_attack(enemy, player, enemy.props.attackDelay) else enemy.props.attackBasketball(enemy) diff --git a/src/nodes/enemy.lua b/src/nodes/enemy.lua index 85e9005f4..fde58731a 100644 --- a/src/nodes/enemy.lua +++ b/src/nodes/enemy.lua @@ -16,6 +16,7 @@ local cheat = require 'cheat' local sound = require 'vendor/TEsound' local token = require 'nodes/token' local game = require 'game' +local utils = require 'utils' local Enemy = {} Enemy.__index = Enemy @@ -33,7 +34,7 @@ function Enemy.new(node, collider, enemytype) enemy.type = type - enemy.props = require( 'nodes/enemies/' .. type ) + enemy.props = utils.require('nodes/enemies/' .. type) local sprite_sheet if node.properties.sheet then sprite_sheet = 'images/enemies/' .. node.properties.sheet .. '.png' @@ -137,7 +138,7 @@ function Enemy:animation() end end -function Enemy:hurt( damage ) +function Enemy:hurt(damage) if self.dead then return end if self.props.die_sound then sound.playSfx( self.props.die_sound ) end @@ -147,7 +148,11 @@ function Enemy:hurt( damage ) if self.hp <= 0 then self.state = 'dying' self:cancel_flash() - if self.props.splat then self.props.splat( self )end + + if self.containerLevel and self.props.splat then + table.insert(self.containerLevel.nodes, 1, self.props.splat(self)) + end + self.collider:setGhost(self.bb) self.collider:setGhost(self.attack_bb) diff --git a/src/nodes/hiddendoortrigger.lua b/src/nodes/hiddendoortrigger.lua index cae3a59cc..23b06a17a 100644 --- a/src/nodes/hiddendoortrigger.lua +++ b/src/nodes/hiddendoortrigger.lua @@ -41,8 +41,8 @@ end function HiddenDoorTrigger:update(dt) end -function HiddenDoorTrigger:enter() - Gamestate.currentState().doors[self.target].node:hide() +function HiddenDoorTrigger:enter(previous) + Gamestate.currentState().doors[self.target].node:hide(previous) end function HiddenDoorTrigger:draw() diff --git a/src/nodes/splat.lua b/src/nodes/splat.lua index 8aca297a4..eb4fbb039 100644 --- a/src/nodes/splat.lua +++ b/src/nodes/splat.lua @@ -12,12 +12,12 @@ local quads = { love.graphics.newQuad(splatterSize.width * 2, 0, splatterSize.width, splatterSize.height, splatters:getWidth(), splatters:getHeight()), } - -Splat.splats = {} - function Splat.new(node) - Splat.node = {x=0, width=0} - return Splat + local splat = {} + setmetatable(splat, Splat) + splat.splats = {} + splat.node = {x=0, width=0} + return splat end function Splat:setup_stencils() @@ -58,7 +58,7 @@ end function Splat:add(x,y,width,height) - table.insert( self.splats, { + table.insert(self.splats, { position = { x = x, y = y @@ -72,11 +72,7 @@ function Splat:add(x,y,width,height) if not self.stencils then self:setup_stencils() - return self - else - return false end - end function Splat:draw() diff --git a/src/nodes/sprite.lua b/src/nodes/sprite.lua index 22a0e2147..c3574ba83 100644 --- a/src/nodes/sprite.lua +++ b/src/nodes/sprite.lua @@ -1,5 +1,4 @@ local anim8 = require 'vendor/anim8' -local Timer = require 'vendor/timer' local utils = require 'utils' local Sprite = {} @@ -9,18 +8,18 @@ Sprite.__index = Sprite local sprite_cache = {} local function load_sprite(name) - if sprite_cache[name] then - return sprite_cache[name] - end - - local image = love.graphics.newImage(name) - image:setFilter('nearest', 'nearest') - sprite_cache[name] = image - return image + if sprite_cache[name] then + return sprite_cache[name] + end + + local image = love.graphics.newImage(name) + image:setFilter('nearest', 'nearest') + sprite_cache[name] = image + return image end -function Sprite.new(node, collider) +function Sprite.new(node, collider, level) local sprite = {} local p = node.properties setmetatable(sprite, Sprite) @@ -28,18 +27,17 @@ function Sprite.new(node, collider) assert(p.sheet, "'sheet' required for sprite node") sprite.sheet = load_sprite(p.sheet) - + sprite.animation = p.animation or false sprite.foreground = p.foreground == 'true' sprite.flip = p.flip == 'true' + sprite.node = node if p.height and p.width then sprite.height = p.height sprite.width = p.width end - - sprite.node = node - + if sprite.animation then sprite.random = p.random == 'true' sprite.speed = p.speed and tonumber(p.speed) or 0.20 @@ -48,7 +46,7 @@ function Sprite.new(node, collider) else sprite.mode = p.mode and p.mode or 'loop' end - + local g = anim8.newGrid(tonumber(p.width), tonumber(p.height), sprite.sheet:getWidth(), sprite.sheet:getHeight()) @@ -58,25 +56,30 @@ function Sprite.new(node, collider) sprite.animation.status = 'stopped' --randomize the play interval local window = p.window and tonumber(p.window) or 5 - local interval = ( math.random( window * 100 ) / 100 ) + ( #sprite.animation.frames * sprite.speed ) - Timer.addPeriodic( interval, function() - sprite.animation:gotoFrame(1) - sprite.animation.status = 'playing' - end) + sprite.interval = (math.random(window * 100) / 100 ) + ( #sprite.animation.frames * sprite.speed) end end + sprite.dt = math.random() sprite.x = node.x sprite.y = node.y - + return sprite end function Sprite:update(dt) - if self.animation then - self.animation:update(dt) - end + self.dt = self.dt + dt + + if self.random and self.dt > self.interval then + self.dt = 0 + self.animation:gotoFrame(1) + self.animation.status = 'playing' + end + + if self.animation then + self.animation:update(dt) + end end function Sprite:draw() diff --git a/src/overworld.lua b/src/overworld.lua index 95058a736..96d10002b 100644 --- a/src/overworld.lua +++ b/src/overworld.lua @@ -6,9 +6,9 @@ local camera = require 'camera' local sound = require 'vendor/TEsound' local utils = require 'utils' local Player = require 'player' -local state = Gamestate.new() -local Character = require 'character' +local character = require 'character' +local state = Gamestate.new() local map = {} map.tileWidth = 12 @@ -18,82 +18,7 @@ map.height = 111 local scale = 2 -local overworld = { - love.graphics.newImage('images/overworld/world_01.png'), - love.graphics.newImage('images/overworld/world_02.png'), - love.graphics.newImage('images/overworld/world_03.png'), - love.graphics.newImage('images/overworld/world_04.png'), - love.graphics.newImage('images/overworld/world_05.png'), - love.graphics.newImage('images/overworld/world_06.png'), - love.graphics.newImage('images/overworld/world_07.png'), - love.graphics.newImage('images/overworld/world_08.png'), -} - -local overlay = { - love.graphics.newImage('images/overworld/world_overlay_01.png'), - love.graphics.newImage('images/overworld/world_overlay_02.png'), - false, - false, - love.graphics.newImage('images/overworld/world_overlay_05.png'), - love.graphics.newImage('images/overworld/world_overlay_06.png'), - false, - false, -} - -local board = love.graphics.newImage('images/overworld/titleboard.png') - - -local charactersprites = love.graphics.newImage( 'images/characters/' .. Character.name .. '/overworld.png') - -local g = anim8.newGrid(36, 36, charactersprites:getWidth(), charactersprites:getHeight()) - ---flags -local flag_image = love.graphics.newImage('images/overworld/flag.png') -local flags = {} - --- free_ride_ferry -local wheelchair = love.graphics.newImage('images/overworld/free_ride_ferry.png') -local wc_x1, wc_x2, wc_y1, wc_y2 = 1685, 1956, 816, 680 -local offset_x, offset_y = math.floor( wheelchair:getHeight() / 2 ) - 10, math.floor( wheelchair:getWidth() / 2 ) - --- animated water -local watersprite = love.graphics.newImage('images/overworld/world_water.png') -local h2o = anim8.newGrid(36, 36, watersprite:getWidth(), watersprite:getHeight()) -local water = anim8.newAnimation('loop', h2o('1-2,1'), 1) - --- cloud puffs -local cloudpuffsprite = love.graphics.newImage('images/overworld/cloud_puff.png') -local spunk_image = anim8.newGrid(100,67, cloudpuffsprite:getWidth(), cloudpuffsprite:getHeight()) --- ( cloud animations will be generated on the fly ) - --- gay sparkles -local sparklesprite = love.graphics.newImage('images/overworld/gay_sparkle.png') -local bling = anim8.newGrid(24, 24, sparklesprite:getWidth(), sparklesprite:getHeight()) -local sparkles = {{1028,456},{1089,442},{1403,440},{1348,591},{1390,633},{1273,698},{1160,657},{1088,702},{1048,665},{1072,604},{1060,552},{1104,548},{1172,555},{1199,727},{1263,735},{1313,505},{1337,459},{1358,429},{1270,617},{1289,571},{1123,505},{1124,472},{1359,709},{1389,555},{1376,677},{1057,624},{1169,710},{1149,592},{1297,639}} -for _,_sp in pairs(sparkles) do - _sp[3] = anim8.newAnimation('loop', bling('1-4,1','1-4,2'), ( math.random(15) / 100 ) + 0.15) - _sp[3]:gotoFrame( math.random( 8 ) ) -end - --- overworld clouds -local cloudquads = { - love.graphics.newQuad( 0, 0, 100, 67, cloudpuffsprite:getWidth(), cloudpuffsprite:getHeight() ), --small - love.graphics.newQuad( 100, 0, 100, 67, cloudpuffsprite:getWidth(), cloudpuffsprite:getHeight() ), --medium - love.graphics.newQuad( 200, 0, 100, 67, cloudpuffsprite:getWidth(), cloudpuffsprite:getHeight() ), --large - love.graphics.newQuad( 300, 0, 200, 67, cloudpuffsprite:getWidth(), cloudpuffsprite:getHeight() ) --x-large -} -local clouds = {} -function insertrandomcloud(nofade) - table.insert( clouds, { - x = math.random( map.width * map.tileWidth ), -- x position - y = math.random( map.height * map.tileHeight ), -- y position - q = math.random( #cloudquads ), -- quad ( cloud size ) - s = ( math.random( 15 ) + 5 ) * ( math.random(2) == 1 and 1 or -1 ), -- speed / direction - o = nofade and 0.8 or 0 -- opacity - } ) -end -for i=0,15 do insertrandomcloud(true) end - +-- FIXME: Put in a JSON file -- overworld state machine state.zones = { greendale= { x=66, y=100, UP=nil, DOWN=nil, RIGHT='forest_2', LEFT=nil, visited = true, name='Greendale', level='studyroom' }, @@ -122,50 +47,155 @@ function state:init() self:reset() end -function state:enter(previous) - - self.previous = previous - - local owd = Character:getOverworld() - - charactersprites = love.graphics.newImage( 'images/characters/' .. Character.name .. '/overworld.png') - - g = anim8.newGrid(36, 36, charactersprites:getWidth(), charactersprites:getHeight()) - - camera:scale(scale, scale) - camera.max.x = map.width * map.tileWidth - (window.width * 2) - - fonts.set( 'big' ) - - sound.playMusic( "overworld" ) - - self.stand = anim8.newAnimation('once', g(owd, 1), 1) - self.walk = anim8.newAnimation('loop', g(owd,2,owd,3), 0.2) - self.facing = 1 - - - local player = Player.factory() - - for _,level in ipairs(player.visitedLevels) do - for _,mapInfo in pairs(self.zones) do - if mapInfo.level == level then - mapInfo.visited = true - table.insert( flags, { - x = mapInfo.x, - y = mapInfo.y - } ) - break - end - end - end - self:reset(player.currentLevel.overworldName) +function state:insertrandomcloud(map, nofade) + table.insert(self.clouds, { + x = math.random(map.width * map.tileWidth), -- x position + y = math.random(map.height * map.tileHeight), -- y position + q = math.random(#self.cloudquads), -- quad ( cloud size ) + s = (math.random(15) + 5) * (math.random(2) == 1 and 1 or -1), -- speed / direction + o = nofade and 0.8 or 0 -- opacity + }) +end +function state:enter(previous) + self.overworld = { + love.graphics.newImage('images/overworld/world_01.png'), + love.graphics.newImage('images/overworld/world_02.png'), + love.graphics.newImage('images/overworld/world_03.png'), + love.graphics.newImage('images/overworld/world_04.png'), + love.graphics.newImage('images/overworld/world_05.png'), + love.graphics.newImage('images/overworld/world_06.png'), + love.graphics.newImage('images/overworld/world_07.png'), + love.graphics.newImage('images/overworld/world_08.png'), + } + + self.overlay = { + love.graphics.newImage('images/overworld/world_overlay_01.png'), + love.graphics.newImage('images/overworld/world_overlay_02.png'), + false, + false, + love.graphics.newImage('images/overworld/world_overlay_05.png'), + love.graphics.newImage('images/overworld/world_overlay_06.png'), + false, + false, + } + + self.board = love.graphics.newImage('images/overworld/titleboard.png') + + local current = character.current() + + self.charactersprites = love.graphics.newImage('images/characters/' .. current.name .. '/overworld.png') + + local g = anim8.newGrid(36, 36, self.charactersprites:getWidth(), self.charactersprites:getHeight()) + + --flags + self.flag_image = love.graphics.newImage('images/overworld/flag.png') + self.flags = {} + + -- free_ride_ferry + self.wheelchair = love.graphics.newImage('images/overworld/free_ride_ferry.png') + self.wc_x1, self.wc_x2, self.wc_y1, self.wc_y2 = 1685, 1956, 816, 680 + self.offset_x, self.offset_y = math.floor(self.wheelchair:getHeight() / 2 ) - 10, math.floor(self.wheelchair:getWidth() / 2 ) + + -- animated water + self.watersprite = love.graphics.newImage('images/overworld/world_water.png') + self.h2o = anim8.newGrid(36, 36, self.watersprite:getWidth(), self.watersprite:getHeight()) + self.water = anim8.newAnimation('loop', self.h2o('1-2,1'), 1) + + -- cloud puffs + self.cloudpuffsprite = love.graphics.newImage('images/overworld/cloud_puff.png') + self.spunk_image = anim8.newGrid(100,67, self.cloudpuffsprite:getWidth(), self.cloudpuffsprite:getHeight()) + -- ( cloud animations will be generated on the fly ) + + -- gay sparkles + self.sparklesprite = love.graphics.newImage('images/overworld/gay_sparkle.png') + self.bling = anim8.newGrid(24, 24, self.sparklesprite:getWidth(), self.sparklesprite:getHeight()) + self.sparkles = { + {1028,456},{1089,442},{1403,440},{1348,591},{1390,633},{1273,698},{1160,657},{1088,702},{1048,665},{1072,604}, + {1060,552},{1104,548},{1172,555},{1199,727},{1263,735},{1313,505},{1337,459},{1358,429},{1270,617},{1289,571}, + {1123,505},{1124,472},{1359,709},{1389,555},{1376,677},{1057,624},{1169,710},{1149,592},{1297,639} + } + + for _,_sp in pairs(self.sparkles) do + _sp[3] = anim8.newAnimation('loop', self.bling('1-4,1','1-4,2'), (math.random(15) / 100) + 0.15) + _sp[3]:gotoFrame(math.random( 8 )) + end + + -- overworld clouds + self.cloudquads = { + love.graphics.newQuad( 0, 0, 100, 67, self.cloudpuffsprite:getWidth(), self.cloudpuffsprite:getHeight() ), --small + love.graphics.newQuad( 100, 0, 100, 67, self.cloudpuffsprite:getWidth(), self.cloudpuffsprite:getHeight() ), --medium + love.graphics.newQuad( 200, 0, 100, 67, self.cloudpuffsprite:getWidth(), self.cloudpuffsprite:getHeight() ), --large + love.graphics.newQuad( 300, 0, 200, 67, self.cloudpuffsprite:getWidth(), self.cloudpuffsprite:getHeight() ) --x-large + } + + self.clouds = {} + + for i=0,15 do + self:insertrandomcloud(map, true) + end + + self.previous = previous + + local owd = current:getOverworld() + local charactersprites = love.graphics.newImage('images/characters/' .. current.name .. '/overworld.png') + + g = anim8.newGrid(36, 36, self.charactersprites:getWidth(), self.charactersprites:getHeight()) + + camera:scale(scale, scale) + camera.max.x = map.width * map.tileWidth - (window.width * 2) + + fonts.set('big') + + sound.playMusic("overworld") + + self.stand = anim8.newAnimation('once', g(owd, 1), 1) + self.walk = anim8.newAnimation('loop', g(owd, 2, owd, 3), 0.2) + self.facing = 1 + + local player = Player.factory() + + for _,level in ipairs(player.visitedLevels) do + for _,mapInfo in pairs(self.zones) do + if mapInfo.level == level then + mapInfo.visited = true + table.insert(self.flags, { + x = mapInfo.x, + y = mapInfo.y + }) + break + end + end + end + + self:reset(player.currentLevel.overworldName) end function state:leave() - camera:scale(window.scale) - fonts.reset() + camera:scale(window.scale) + fonts.reset() + + self.cloudquads = nil + self.clouds = nil + self.watersprite = nil + self.h2o = nil + self.water = nil + + -- cloud puffs + self.cloudpuffsprite = nil + self.spunk_image = nil + + self.sparklesprite = nil + self.bling = nil + self.sparkles = nil + self.overworld = nil + self.overlay = nil + self.board = nil + self.charactersprites = nil + self.flag_image = nil + self.flags = nil + self.spunks = nil end function state:reset(level) @@ -187,20 +217,20 @@ function state:reset(level) end function state:update(dt) - water:update(dt) + self.water:update(dt) - for _,_sp in pairs(sparkles) do + for _,_sp in pairs(self.sparkles) do _sp[3]:update(dt) end - for i,cloud in pairs( clouds ) do + for i,cloud in pairs(self.clouds) do if cloud then cloud.x = cloud.x + ( cloud.s * dt ) / ( cloud.q / 2 ) if cloud.o <= 0.8 then cloud.o = cloud.o + dt end -- fade in --check for out of bounds if cloud.x + 200 < 0 or cloud.x > map.width * map.tileWidth then - clouds[i] = false - insertrandomcloud() + self.clouds[i] = false + self:insertrandomcloud(map) end end end @@ -242,7 +272,7 @@ function state:update(dt) -- release a new spunk local rand = math.random(3) table.insert(self.spunks, { - spunk = anim8.newAnimation('once', spunk_image('1-3,1'), 0.2), + spunk = anim8.newAnimation('once', self.spunk_image('1-3,1'), 0.2), x = self.spunk_x, y = self.spunk_y, dx = ( rand == 3 and self.spunk_dx or ( rand == 2 and 0 or -self.spunk_dx ) ), @@ -254,7 +284,7 @@ function state:update(dt) spunk.x = spunk.x + spunk.dx * dt spunk.y = spunk.y + spunk.dy * dt spunk.spunk:update(dt) - if spunk.y + ( cloudpuffsprite:getHeight() * 2 ) < 0 then + if spunk.y + (self.cloudpuffsprite:getHeight() * 2 ) < 0 then self.spunks[i] = nil end end @@ -338,15 +368,15 @@ end function state:draw() - love.graphics.setBackgroundColor(133, 185, 250) + love.graphics.setBackgroundColor(133, 185, 250) - for x=math.floor( camera.x / 36 ), math.floor( ( camera.x + camera:getWidth() ) / 36 ) do - for y=math.floor( camera.y / 36 ), math.floor( ( camera.y + camera:getHeight() ) / 36 ) do - water:draw(watersprite, x * 36, y * 36 ) + for x=math.floor(camera.x / 36), math.floor((camera.x + camera:getWidth()) / 36) do + for y=math.floor(camera.y / 36), math.floor((camera.y + camera:getHeight()) / 36) do + self.water:draw(self.watersprite, x * 36, y * 36 ) end end - for i, image in ipairs(overworld) do + for i, image in ipairs(self.overworld) do local x = (i - 1) % 4 local y = i > 4 and 1 or 0 love.graphics.draw(image, x * image:getWidth(), y * image:getHeight()) @@ -354,43 +384,44 @@ function state:draw() for _,spunk in pairs(self.spunks) do if spunk then - spunk.spunk:draw( cloudpuffsprite, spunk.x, spunk.y ) + spunk.spunk:draw(self.cloudpuffsprite, spunk.x, spunk.y ) end end - for _,_sp in pairs(sparkles) do - _sp[3]:draw( sparklesprite, _sp[1] - 12, _sp[2] - 12 ) + for _,_sp in pairs(self.sparkles) do + _sp[3]:draw(self.sparklesprite, _sp[1] - 12, _sp[2] - 12 ) end --flags - for _,flag in pairs(flags) do + for _,flag in pairs(self.flags) do if flag then - love.graphics.setColor( 255, 255, 255, 255 ) - love.graphics.draw( flag_image, flag['x']*map.tileWidth+10,flag['y']*map.tileHeight-24 ) + love.graphics.setColor(255, 255, 255, 255) + love.graphics.draw(self.flag_image, flag['x'] * map.tileWidth + 10, flag['y'] * map.tileHeight - 24) end end local face_offset = self.facing == -1 and 36 or 0 + if self.moving then - self.walk:draw(charactersprites, math.floor(self.tx) + face_offset - 7, math.floor(self.ty) - 15,0,self.facing,1) + self.walk:draw(self.charactersprites, math.floor(self.tx) + face_offset - 7, math.floor(self.ty) - 15,0,self.facing,1) else - self.stand:draw(charactersprites, math.floor(self.tx) + face_offset - 7, math.floor(self.ty) - 15,0,self.facing,1) + self.stand:draw(self.charactersprites, math.floor(self.tx) + face_offset - 7, math.floor(self.ty) - 15,0,self.facing,1) end - if ( self.ty == wc_y1 and self.tx > wc_x1 and self.tx <= wc_x2 ) or - ( self.tx == wc_x2 and self.ty > wc_y2 and self.ty <= wc_y1 ) then + if (self.ty == self.wc_y1 and self.tx > self.wc_x1 and self.tx <= self.wc_x2) or + (self.tx == self.wc_x2 and self.ty > self.wc_y2 and self.ty <= self.wc_y1) then -- follow the player - love.graphics.draw( wheelchair, self.tx - offset_x, self.ty - offset_y ) + love.graphics.draw(self.wheelchair, self.tx - self.offset_x, self.ty - self.offset_y) elseif self.zone == self.zones['caverns'] or - ( self.tx == wc_x2 and self.ty <= wc_y2 ) then + ( self.tx == self.wc_x2 and self.ty <= self.wc_y2 ) then -- cavern dock - love.graphics.draw( wheelchair, wc_x2 - offset_x, wc_y2 - offset_y ) + love.graphics.draw(self.wheelchair, self.wc_x2 - self.offset_x, self.wc_y2 - self.offset_y) else -- island dock - love.graphics.draw( wheelchair, wc_x1 - offset_x, wc_y1 - offset_y ) + love.graphics.draw(self.wheelchair, self.wc_x1 - self.offset_x, self.wc_y1 - self.offset_y) end - for i, image in ipairs(overlay) do + for i, image in ipairs(self.overlay) do if image then local x = (i - 1) % 4 local y = i > 4 and 1 or 0 @@ -398,23 +429,23 @@ function state:draw() end end - love.graphics.setColor( 255, 255, 255, 255 ) + love.graphics.setColor(255, 255, 255, 255) - for _,cloud in pairs(clouds) do + for _,cloud in pairs(self.clouds) do if cloud then love.graphics.setColor( 255, 255, 255, cloud.o * 255 ) - love.graphics.drawq( cloudpuffsprite, cloudquads[cloud.q], cloud.x, cloud.y ) + love.graphics.drawq(self.cloudpuffsprite, self.cloudquads[cloud.q], cloud.x, cloud.y ) love.graphics.setColor( 255, 255, 255, 255 ) end end - love.graphics.draw(board, camera.x + window.width - board:getWidth() / 2, - camera.y + window.height + board:getHeight() * 2) + love.graphics.draw(self.board, camera.x + window.width - self.board:getWidth() / 2, + camera.y + window.height + self.board:getHeight() * 2) love.graphics.printf(self:title(), - camera.x + window.width - board:getWidth() / 2, - camera.y + window.height + board:getHeight() * 2.5 - 10, - board:getWidth(), 'center') + camera.x + window.width - self.board:getWidth() / 2, + camera.y + window.height + self.board:getHeight() * 2.5 - 10, + self.board:getWidth(), 'center') end return state diff --git a/src/player.lua b/src/player.lua index 25a4cb751..1bf139836 100644 --- a/src/player.lua +++ b/src/player.lua @@ -18,13 +18,11 @@ local Inventory = require('inventory') local healthbarq = {} -for i=20,0,-1 do - table.insert(healthbarq, love.graphics.newQuad(28 * i, 0, 28, 27, +for i=10,0,-1 do + table.insert(healthbarq, love.graphics.newQuad(28 * i * 2, 0, 28, 27, healthbar:getWidth(), healthbar:getHeight())) end -local health = love.graphics.newImage('images/damage.png') - local Player = {} Player.__index = Player Player.isPlayer = true @@ -35,7 +33,6 @@ Player.jumpFactor = 1 Player.speedFactor = 1 -- single 'character' object that handles all character switching, costumes and animation -Player.character = character local player = nil --- @@ -67,11 +64,12 @@ function Player.new(collider) plyr.height = 48 plyr.bbox_width = 18 plyr.bbox_height = 44 + plyr.character = character.current() --for damage text plyr.healthText = {x=0, y=0} plyr.healthVel = {x=0, y=0} - plyr.max_health = 20 + plyr.max_health = 10 plyr.health = plyr.max_health plyr.jumpDamage = 3 @@ -528,15 +526,15 @@ function Player:update( dt ) if self.wielding or self.attacked then - self.character:animation():update(dt) + self.character:update(dt) elseif self.jumping then self.character.state = self.jump_state - self.character:animation():update(dt) + self.character:update(dt) elseif self.isJumpState(self.character.state) and not self.jumping then self.character.state = self.walk_state - self.character:animation():update(dt) + self.character:update(dt) elseif not self.isJumpState(self.character.state) and self.velocity.x ~= 0 then if crouching and self.crouch_state == 'crouch' then @@ -545,7 +543,7 @@ function Player:update( dt ) self.character.state = self.walk_state end - self.character:animation():update(dt) + self.character:update(dt) elseif not self.isJumpState(self.character.state) and self.velocity.x == 0 then @@ -559,10 +557,10 @@ function Player:update( dt ) self.character.state = self.idle_state end - self.character:animation():update(dt) + self.character:update(dt) else - self.character:animation():update(dt) + self.character:update(dt) end self.healthText.y = self.healthText.y + self.healthVel.y * dt @@ -666,8 +664,8 @@ function Player:draw() end if self.character.warpin then - local y = self.position.y - self.character:current().beam:getHeight() + self.height + 4 - self.character:current().animations.warp:draw(self.character:current().beam, self.position.x + 6, y) + local y = self.position.y - self.character.beam:getHeight() + self.height + 4 + self.character.animations.warp:draw(self.character.beam, self.position.x + 6, y) return end @@ -708,9 +706,9 @@ function Player:draw() self.frame = animation.frames[animation.position] local x,y,w,h = self.frame:getViewport() self.frame = {x/w+1, y/w+1} - if self.character:current().positions then - self.offset_hand_right = self.character:current().positions.hand_right[self.frame[2]][self.frame[1]] - self.offset_hand_left = self.character:current().positions.hand_left[self.frame[2]][self.frame[1]] + if self.character.positions then + self.offset_hand_right = self.character.positions.hand_right[self.frame[2]][self.frame[1]] + self.offset_hand_left = self.character.positions.hand_left[self.frame[2]][self.frame[1]] else self.offset_hand_right = {0,0} self.offset_hand_left = {0,0} @@ -720,8 +718,11 @@ function Player:draw() self.currently_held:draw() end + local health = self.damageTaken * -1 + if self.rebounding and self.damageTaken > 0 then - love.graphics.draw(health, self.healthText.x, self.healthText.y) + love.graphics.setColor( 255, 0, 0, 255 ) + love.graphics.print(health, self.healthText.x, self.healthText.y, 0, 0.7, 0.7) end love.graphics.setColor( 255, 255, 255, 255 ) diff --git a/src/scanning.lua b/src/scanning.lua index c0118c875..a28105808 100644 --- a/src/scanning.lua +++ b/src/scanning.lua @@ -19,19 +19,17 @@ function state:enter(previous) self.previous = previous end - function state:keypressed( button ) - Timer.clear() - if button == "START" then - Gamestate.switch("splash") - return true - else - Gamestate.switch("select") - end + Timer.clear() + if button == "START" then + Gamestate.switch("splash") + return true + else + Gamestate.switch("select") + end end function state:update(dt) - self.backgroundanimate:update(dt) self.namesanimate:update(dt) self.computeranimate:update(dt) @@ -50,15 +48,14 @@ function state:update(dt) self.annieanimation:update(dt) self.troyanimation:update(dt) self.pierceanimation:update(dt) - end function state:draw() --background colour - love.graphics.setColor( 60, 86, 173, 255 ) - love.graphics.rectangle( 'fill', 0, 0, love.graphics:getWidth(), love.graphics:getHeight() ) - love.graphics.setColor( 255, 255, 255, 255 ) + love.graphics.setColor( 60, 86, 173, 255 ) + love.graphics.rectangle( 'fill', 0, 0, love.graphics:getWidth(), love.graphics:getHeight() ) + love.graphics.setColor( 255, 255, 255, 255 ) -- coloured backgrounds local width = window.width @@ -90,7 +87,6 @@ function state:draw() end function state:refresh() - -- sets length of time for animation local rtime = 10 @@ -175,7 +171,43 @@ function state:refresh() -- animation runs for rtime secs Timer.add(rtime, function() Gamestate.switch("select") end) +end + +function state:leave() + self.backgrounds = nil + self.names = nil + self.computer = nil + self.description = nil + self.scanbar = nil + self.scanwords = nil + self.blank = nil + self.isprites = nil + self.iscan = nil + self.jeff = nil + self.britta = nil + self.abed = nil + self.shirley = nil + self.annie = nil + self.troy = nil + self.pierce = nil + + state.backgroundanimate = nil + state.namesanimate = nil + state.computeranimate = nil + state.descriptionanimate = nil + state.scanbaranimate = nil + state.scanwordsanimate = nil + state.blankanimate = nil + state.ispritesanimate = nil + state.iscananimate = nil + state.jeffanimation = nil + state.brittaanimation = nil + state.abedanimation = nil + state.shirleyanimation = nil + state.annieanimation = nil + state.troyanimation = nil + state.pierceanimation = nil end return state diff --git a/src/select.lua b/src/select.lua index fe4c46e09..5d3d40621 100644 --- a/src/select.lua +++ b/src/select.lua @@ -3,214 +3,266 @@ local Level = require 'level' local window = require 'window' local fonts = require 'fonts' local background = require 'selectbackground' -local state = Gamestate.new() local sound = require 'vendor/TEsound' -local Character = require 'character' -local characters = Character.characters +local character = require 'character' local controls = require('inputcontroller').get() -local character_selections = {} -character_selections[1] = {} -- main characters -character_selections[1][0] = {} -- left -character_selections[1][1] = {} -- right -character_selections[1][1][0] = characters['troy'] -character_selections[1][1][1] = characters['shirley'] -character_selections[1][1][2] = characters['pierce'] -character_selections[1][0][0] = characters['jeff'] -character_selections[1][0][1] = characters['britta'] -character_selections[1][0][2] = characters['abed'] -character_selections[1][0][3] = characters['annie'] - -character_selections[2] = {} -- page 2 -character_selections[2][0] = {} -- left -character_selections[2][1] = {} -- right -character_selections[2][1][0] = characters['chang'] -character_selections[2][1][1] = characters['fatneil'] -character_selections[2][1][2] = characters['vicedean'] -character_selections[2][0][0] = characters['dean'] -character_selections[2][0][1] = characters['guzman'] -character_selections[2][0][2] = characters['buddy'] -character_selections[2][0][3] = characters['leonard'] - -character_selections[3] = {} -- page 3 -character_selections[3][0] = {} -- left -character_selections[3][1] = {} -- right -character_selections[3][1][0] = characters['ian'] -character_selections[3][1][1] = characters['rich'] -character_selections[3][1][2] = characters['vicki'] -character_selections[3][0][0] = characters['vaughn'] -character_selections[3][0][1] = characters['garrett'] - -local current_page = 1 -local selections = character_selections[current_page] +local state = Gamestate.new() + +-- The current selected page function state:init() - self.side = 0 -- 0 for left, 1 for right - self.level = 0 -- 0 through 3 for characters + self.side = 0 -- 0 for left, 1 for right + self.level = 0 -- 0 through 3 for characters + self.current_page = 1 - background.init() - self.chartext = "" - self.costtext = "" - self.randomtext = "" + background.init() + self.chartext = "" + self.costtext = "" + self.randomtext = "" end function state:enter(previous) - fonts.set( 'big' ) - self.previous = previous - self.music = sound.playMusic( "opening" ) - background.enter() - background.setSelected( self.side, self.level ) - - self.chartext = "PRESS " .. controls:getKey('JUMP') .. " TO CHOOSE CHARACTER" - self.costtext = "PRESS " .. controls:getKey('ATTACK') .. " or " ..controls:getKey('INTERACT') .. " TO CHANGE COSTUME" - self.randomtext = "PRESS " .. controls:getKey('SELECT') .. " TO GET A RANDOM COSTUME" + self.current_page = 1 + + self.character_selections = {} + self.characters = {} + self.costumes = {} + + self.character_selections[1] = {} -- main characters + self.character_selections[1][0] = {} -- left + self.character_selections[1][1] = {} -- right + self.character_selections[1][1][0] = 'troy' + self.character_selections[1][1][1] = 'shirley' + self.character_selections[1][1][2] = 'pierce' + self.character_selections[1][0][0] = 'jeff' + self.character_selections[1][0][1] = 'britta' + self.character_selections[1][0][2] = 'abed' + self.character_selections[1][0][3] = 'annie' + + self.character_selections[2] = {} -- page 2 + self.character_selections[2][0] = {} -- left + self.character_selections[2][1] = {} -- right + self.character_selections[2][1][0] = 'chang' + self.character_selections[2][1][1] = 'fatneil' + self.character_selections[2][1][2] = 'vicedean' + self.character_selections[2][0][0] = 'dean' + self.character_selections[2][0][1] = 'guzman' + self.character_selections[2][0][2] = 'buddy' + self.character_selections[2][0][3] = 'leonard' + + self.character_selections[3] = {} -- page 3 + self.character_selections[3][0] = {} -- left + self.character_selections[3][1] = {} -- right + self.character_selections[3][1][0] = 'ian' + self.character_selections[3][1][1] = 'rich' + self.character_selections[3][1][2] = 'vicki' + self.character_selections[3][0][0] = 'vaughn' + self.character_selections[3][0][1] = 'garrett' + + self.selections = self.character_selections[self.current_page] + + fonts.set('big') + self.previous = previous + self.music = sound.playMusic("opening") + background.enter() + background.setSelected(self.side, self.level) + + self.chartext = "PRESS " .. controls:getKey('JUMP') .. " TO CHOOSE CHARACTER" + self.costtext = "PRESS " .. controls:getKey('ATTACK') .. " or " ..controls:getKey('INTERACT') .. " TO CHANGE COSTUME" + self.randomtext = "PRESS " .. controls:getKey('SELECT') .. " TO GET A RANDOM COSTUME" end function state:character() - return selections[self.side][self.level] + local name = self.selections[self.side][self.level] + + if not name then + return nil + end + + return self:loadCharacter(name) +end + +function state:loadCharacter(name) + if not self.characters[name] then + self.characters[name] = character.load(name) + self.characters[name].count = 1 + self.characters[name].costume = 'base' + end + + return self.characters[name] end function state:keypressed( button ) - if button == "START" then - Gamestate.switch("splash") - return true - end + if button == "START" then + Gamestate.switch("splash") + return true + end - -- If any input is received while sliding, speed up - if background.slideIn or background.slideOut then - background.speed = 10 - return - end + -- If any input is received while sliding, speed up + if background.slideIn or background.slideOut then + background.speed = 10 + return + end - local level = self.level - local options = 4 + local level = self.level + local options = 4 - if button == 'LEFT' or button == 'RIGHT' then - self.side = (self.side - 1) % 2 - sound.playSfx('click') - elseif button == 'UP' then - level = (self.level - 1) % options - sound.playSfx('click') - elseif button == 'DOWN' then - level = (self.level + 1) % options - sound.playSfx('click') - elseif button == 'ATTACK' then - if self.level == 3 and self.side == 1 then - return - else - local c = self:character() - if c then - c.count = (c.count + 1) - if c.count == (#c.costumes + 1) then - c.count = 1 - end - c.costume = c.costumes[c.count].sheet - sound.playSfx('click') - end - end - return - elseif button == 'INTERACT' then - if self.level == 3 and self.side == 1 then - return - else - local c = self:character() - if c then - c.count = (c.count - 1) - if c.count == 0 then - c.count = #c.costumes - end - c.costume = c.costumes[c.count].sheet - sound.playSfx('click') - end + if button == 'LEFT' or button == 'RIGHT' then + self.side = (self.side - 1) % 2 + sound.playSfx('click') + elseif button == 'UP' then + level = (self.level - 1) % options + sound.playSfx('click') + elseif button == 'DOWN' then + level = (self.level + 1) % options + sound.playSfx('click') + elseif button == 'ATTACK' then + if self.level == 3 and self.side == 1 then + return + else + local c = self:character() + if c then + c.count = (c.count + 1) + if c.count == (#c.costumes + 1) then + c.count = 1 end - return - elseif button == 'SELECT' then - local c = self:character() - if c then - c.count = math.random(#c.costumes) - c.costume = c.costumes[c.count].sheet - sound.playSfx('click') + c.costume = c.costumes[c.count].sheet + sound.playSfx('click') + end + end + return + elseif button == 'INTERACT' then + if self.level == 3 and self.side == 1 then + return + else + local c = self:character() + if c then + c.count = (c.count - 1) + if c.count == 0 then + c.count = #c.costumes end + c.costume = c.costumes[c.count].sheet + sound.playSfx('click') + end end + return + elseif button == 'SELECT' then + local c = self:character() + if c then + c.count = math.random(#c.costumes) + c.costume = c.costumes[c.count].sheet + sound.playSfx('click') + end + end - self.level = level + self.level = level - if ( button == 'JUMP' ) and self.level == 3 and self.side == 1 then - current_page = current_page % #character_selections + 1 - selections = character_selections[current_page] - sound.playSfx('confirm') - elseif button == 'JUMP' then - if self:character() then - -- Tell the background to transition out before changing scenes - background.slideOut = true - end - sound.playSfx('confirm') + if ( button == 'JUMP' ) and self.level == 3 and self.side == 1 then + self.current_page = self.current_page % #self.character_selections + 1 + self.selections = self.character_selections[self.current_page] + sound.playSfx('confirm') + elseif button == 'JUMP' then + if self:character() then + -- Tell the background to transition out before changing scenes + background.slideOut = true end - - background.setSelected( self.side, self.level ) + sound.playSfx('confirm') + end + + background.setSelected(self.side, self.level) end function state:leave() - fonts.reset() + fonts.reset() + background.leave() + + self.character_selections = nil + self.characters = nil + self.costumes = nil + self.selections = nil + self.previous = nil + self.music = nil end function state:update(dt) - -- The background returns 'true' when the slide-out transition is complete - if background.update(dt) then - -- set the selected character and costume - Character:setCharacter( self:character().name ) - Character:setCostume( self:character().costumes[self:character().count].sheet ) - Character.changed = true - - love.graphics.setColor(255, 255, 255, 255) - local level = Gamestate.get('overworld') - level:reset() - Gamestate.switch('flyin') - end + -- The background returns 'true' when the slide-out transition is complete + if background.update(dt) then + -- set the selected character and costume + local currentPick = self:character() + + character.pick(currentPick.name, currentPick.costume) + + -- Probably don't need this anymore + character.changed = true + + love.graphics.setColor(255, 255, 255, 255) + + local level = Gamestate.get('overworld') + level:reset() + + Gamestate.switch('flyin') + end end -function state:draw() - background.draw() +function state:drawCharacter(name, x, y, offset) + local char = self:loadCharacter(name) + local key = name .. char.costume - -- Only draw the details on the screen when the background is up - if not background.slideIn then - local name = "" + if not self.costumes[key] then + self.costumes[key] = character.getCostumeImage(name, char.costume) + end - if self:character() then - name = self:character().costumes[self:character().count].name - end + local image = self.costumes[key] - love.graphics.printf(self.chartext, 0, - window.height - 75, window.width, 'center') - love.graphics.printf(self.costtext, 0, - window.height - 55, window.width, 'center') - love.graphics.printf(self.randomtext, 0, - window.height - 35, window.width, 'center') - - love.graphics.printf(name, 0, - 23, window.width, 'center') - - local x, y = background.getPosition(1, 3) - love.graphics.setColor(255, 255, 255, 200) - love.graphics.print("INSUFFICIENT", x, y + 5, 0, 0.5, 0.5, 12, -6) - love.graphics.print( "FRIENDS" , x, y + 5, 0, 0.5, 0.5, -12, -32) - love.graphics.print( current_page .. ' / ' .. #character_selections, x + 60, y + 15, 0, 0.5, 0.5 ) - love.graphics.setColor(255, 255, 255, 255) + if not char.mask then + char.mask = love.graphics.newQuad(0, char.offset, 48, 35, + image:getWidth(), image:getHeight()) + end + + if offset then + love.graphics.drawq(image, char.mask, x, y, 0, -1, 1) + else + love.graphics.drawq(image, char.mask, x, y) + end +end + + +function state:draw() + background.draw() + + -- Only draw the details on the screen when the background is up + if not background.slideIn then + local name = "" + + if self:character() then + name = self:character().costumes[self:character().count].name end - for i=0,1,1 do - for j=0,3,1 do - local character = selections[i][j] - local x, y = background.getPosition(i, j) - if character then - if i == 0 then - love.graphics.drawq( Character:getSheet(character.name, character.costumes[character.count].sheet ), character.mask , x, y, 0, -1, 1 ) - else - love.graphics.drawq( Character:getSheet(character.name, character.costumes[character.count].sheet ), character.mask , x, y ) - end - end - end + love.graphics.printf(self.chartext, 0, window.height - 75, window.width, 'center') + love.graphics.printf(self.costtext, 0, window.height - 55, window.width, 'center') + love.graphics.printf(self.randomtext, 0, window.height - 35, window.width, 'center') + + love.graphics.printf(name, 0, + 23, window.width, 'center') + + local x, y = background.getPosition(1, 3) + love.graphics.setColor(255, 255, 255, 200) + love.graphics.print("INSUFFICIENT", x, y + 5, 0, 0.5, 0.5, 12, -6) + love.graphics.print( "FRIENDS" , x, y + 5, 0, 0.5, 0.5, -12, -32) + love.graphics.print(self.current_page .. ' / ' .. #self.character_selections, x + 60, y + 15, 0, 0.5, 0.5 ) + love.graphics.setColor(255, 255, 255, 255) + end + + for i=0,1,1 do + for j=0,3,1 do + local character_name = self.selections[i][j] + local x, y = background.getPosition(i, j) + if character_name then + self:drawCharacter(character_name, x, y, i == 0) + end end + end end Gamestate.home = state diff --git a/src/selectbackground.lua b/src/selectbackground.lua index a2d607945..c3a689c43 100644 --- a/src/selectbackground.lua +++ b/src/selectbackground.lua @@ -1,105 +1,110 @@ ----------------------------------------------------------------------- --- selectbackground.lua --- The background details in the character select screen. --- Created by tjvezina ----------------------------------------------------------------------- - local window = require 'window' -local slideTime = 0 -local unknownFriend = nil local VerticalParticles = require "verticalparticles" local CharacterStrip = require "characterstrip" + local selectBackground = {} +local slideTime = 0 +local unknownFriend = nil +local strips = nil + function selectBackground.init() - VerticalParticles:init() - unknownFriend = love.graphics.newImage('images/menu/insufficient_friend.png') end function selectBackground.enter() - selectBackground.speed = 1 - selectBackground.slideIn = false - selectBackground.slideOut = false - slideTime = 0; - - strips = {} - - strips[1] = CharacterStrip.new( 81, 73, 149) -- Jeff - strips[2] = CharacterStrip.new(150, 220, 149) -- Britta - strips[3] = CharacterStrip.new(200, 209, 149) -- Abed - strips[4] = CharacterStrip.new(173, 135, 158) -- Annie - strips[5] = CharacterStrip.new(149, 214, 200) -- Troy - strips[6] = CharacterStrip.new(134, 60, 133) -- Shirley - strips[7] = CharacterStrip.new(171, 98, 109) -- Pierce - strips[8] = CharacterStrip.new( 80, 80, 80) -- Insufficient - - for i,s in pairs(strips) do - s.flip = i > 4 - s.pos = (i-1) % 4 - s.ratio = -(s.pos * 2 + 1) - s.x = window.width / 2 + ( ( 7 + (25+15) * s.pos ) * (s.flip and 1 or -1)) - s.y = 66 + (35+15) * s.pos - end - - if not selectBackground.selected then - selectBackground:setSelected(0,0) - end + VerticalParticles.init() + + unknownFriend = love.graphics.newImage('images/menu/insufficient_friend.png') + + selectBackground.speed = 1 + selectBackground.slideIn = false + selectBackground.slideOut = false + slideTime = 0; + + strips = {} + + strips[1] = CharacterStrip.new( 81, 73, 149) -- Jeff + strips[2] = CharacterStrip.new(150, 220, 149) -- Britta + strips[3] = CharacterStrip.new(200, 209, 149) -- Abed + strips[4] = CharacterStrip.new(173, 135, 158) -- Annie + strips[5] = CharacterStrip.new(149, 214, 200) -- Troy + strips[6] = CharacterStrip.new(134, 60, 133) -- Shirley + strips[7] = CharacterStrip.new(171, 98, 109) -- Pierce + strips[8] = CharacterStrip.new( 80, 80, 80) -- Insufficient + + for i,s in pairs(strips) do + s.flip = i > 4 + s.pos = (i-1) % 4 + s.ratio = -(s.pos * 2 + 1) + s.x = window.width / 2 + ( ( 7 + (25+15) * s.pos ) * (s.flip and 1 or -1)) + s.y = 66 + (35+15) * s.pos + end + + if not selectBackground.selected then + selectBackground:setSelected(0,0) + end +end + +function selectBackground.leave() + unknownFriend = nil + strips = nil + VerticalParticles.leave() end -- Renders the starry background and each strip function selectBackground.draw() - love.graphics.setBackgroundColor(0, 0, 0, 0) + love.graphics.setBackgroundColor(0, 0, 0, 0) - VerticalParticles.draw() + VerticalParticles.draw() - for _,strip in ipairs(strips) do strip:draw() end + for _,strip in ipairs(strips) do strip:draw() end - love.graphics.setColor(255, 255, 255, 255) + love.graphics.setColor(255, 255, 255, 255) - local x, y = strips[8]:getCharacterPos() - love.graphics.draw(unknownFriend, x + 14, y + 10) + local x, y = strips[8]:getCharacterPos() + love.graphics.draw(unknownFriend, x + 14, y + 10) end -- Updates the particle system and each strip function selectBackground.update(dt) - VerticalParticles.update(dt) + VerticalParticles.update(dt) - sliding = selectBackground.slideIn or selectBackground.slideOut + sliding = selectBackground.slideIn or selectBackground.slideOut - if not sliding then selectBackground.speed = 1 end + if not sliding then selectBackground.speed = 1 end - if selectBackground.slideOut then - slideTime = slideTime + (dt * selectBackground.speed) - end + if selectBackground.slideOut then + slideTime = slideTime + (dt * selectBackground.speed) + end - for i,strip in ipairs(strips) do - -- Tell the strips to slide out at the proper time - strip.slideOut = (slideTime*4 > (i-1) % 4) - strip:update( dt * selectBackground.speed, strips[8].ratio == 0 ) - end + for i,strip in ipairs(strips) do + -- Tell the strips to slide out at the proper time + strip.slideOut = (slideTime*4 > (i-1) % 4) + strip:update(dt * selectBackground.speed, strips[8].ratio == 0) + end - -- Set 'slideIn' to false when the last strip is fully on-screen - selectBackground.slideIn = (strips[8].ratio < 0) + -- Set 'slideIn' to false when the last strip is fully on-screen + selectBackground.slideIn = (strips[8].ratio < 0) - -- After a set delay, tell the caller it's safe to swap states - return (slideTime > 1.25) + -- After a set delay, tell the caller it's safe to swap states + return (slideTime > 1.25) end -- Returns the postion the strip's character should be drawing at function selectBackground.getPosition(side, level) - return strips[(side * 4) + (level+1)]:getCharacterPos() + return strips[(side * 4) + (level+1)]:getCharacterPos() end function selectBackground.reset() - slideTime = 0 - selectBackground.slideIn = false - selectBackground.slideOut = false + slideTime = 0 + selectBackground.slideIn = false + selectBackground.slideOut = false end function selectBackground.setSelected(side, level) - for _,strip in pairs(strips) do strip.selected = false end - strips[(level+1) + (side == 1 and 4 or 0)].selected = true - selectBackground.selected = (level+1) + (side == 1 and 4 or 0) + for _,strip in pairs(strips) do strip.selected = false end + strips[(level + 1) + (side == 1 and 4 or 0)].selected = true + selectBackground.selected = (level + 1) + (side == 1 and 4 or 0) end diff --git a/src/splash.lua b/src/splash.lua index 4246d6f98..9af55a829 100644 --- a/src/splash.lua +++ b/src/splash.lua @@ -14,23 +14,7 @@ local menu = require 'menu' function splash:init() - self.cityscape = love.graphics.newImage("images/menu/cityscape.png") - self.logo = love.graphics.newImage("images/menu/logo.png") - self.splash = love.graphics.newImage("images/openingmenu.png") - self.arrow = love.graphics.newImage("images/menu/small_arrow.png") - self.logo_position = {y=-self.logo:getHeight()} - self.logo_position_final = self.logo:getHeight() / 2 + 40 - self.text = "" - tween(4, self.logo_position, { y=self.logo_position_final}) - -- sparkles - self.sparklesprite = love.graphics.newImage('images/cornelius_sparkles.png') - self.bling = anim8.newGrid(24, 24, self.sparklesprite:getWidth(), self.sparklesprite:getHeight()) - self.sparkles = {{55,34},{42,112},{132,139},{271,115},{274,50}} - for _,_sp in pairs(self.sparkles) do - _sp[3] = anim8.newAnimation('loop', self.bling('1-4,1'), 0.22 + math.random() / 10) - _sp[3]:gotoFrame( math.random( 4 ) ) - end self.menu = menu.new({ 'start', 'controls', 'options', 'credits', 'exit' }) @@ -49,6 +33,26 @@ function splash:init() end function splash:enter(a) + self.cityscape = love.graphics.newImage("images/menu/cityscape.png") + self.logo = love.graphics.newImage("images/menu/logo.png") + self.splash = love.graphics.newImage("images/openingmenu.png") + self.arrow = love.graphics.newImage("images/menu/small_arrow.png") + self.logo_position = {y=-self.logo:getHeight()} + self.logo_position_final = self.logo:getHeight() / 2 + 40 + self.text = "" + + tween(4, self.logo_position, { y=self.logo_position_final}) + + -- sparkles + self.sparklesprite = love.graphics.newImage('images/cornelius_sparkles.png') + self.bling = anim8.newGrid(24, 24, self.sparklesprite:getWidth(), self.sparklesprite:getHeight()) + self.sparkles = {{55,34},{42,112},{132,139},{271,115},{274,50}} + + for _,_sp in pairs(self.sparkles) do + _sp[3] = anim8.newAnimation('loop', self.bling('1-4,1'), 0.22 + math.random() / 10) + _sp[3]:gotoFrame( math.random( 4 ) ) + end + fonts.set( 'big' ) self.text = string.format(app.i18n('s_or_s_select_item'), controls:getKey('JUMP'), controls:getKey('ATTACK') ) @@ -70,6 +74,15 @@ function splash:update(dt) end function splash:leave() + self.cityscape = nil + self.logo = nil + self.splash = nil + self.arrow = nil + self.text = nil + self.sparkles = nil + self.sparklesprite = nil + self.bling = nil + fonts.reset() if self.handle then diff --git a/src/test/runner.lua b/src/test/runner.lua index 5f02d67e1..ca2d5977c 100644 --- a/src/test/runner.lua +++ b/src/test/runner.lua @@ -10,6 +10,7 @@ lunatest.suite("test/test_updater") lunatest.suite("test/test_cheat") lunatest.suite("test/test_inventory") lunatest.suite("test/test_inputcontroller") +lunatest.suite("test/test_character") -- Don't change these lines love.audio.setVolume(0) diff --git a/src/test/test_character.lua b/src/test/test_character.lua new file mode 100644 index 000000000..3bd587fd4 --- /dev/null +++ b/src/test/test_character.lua @@ -0,0 +1,68 @@ +local character = require "src/character" + +--Should fail +function test_pick_unknown_character() + assert_error(function() + character.pick('unknown', 'base') + end, "Unknown character should fail") +end + +function test_pick_unknown_costume() + assert_error(function() + character.pick('abed', 'unknown') + end, "Unknown character should fail") +end + +function test_pick_known_combination() + character.pick('abed', 'base') +end + +function test_load_unknown_character() + assert_error(function() + character.load('unknown') + end, "Unknown character should fail") +end + +function test_load_abed() + local abed = character.load('abed') + assert_equal(abed.name, 'abed') +end + +function test_load_abed() + local found = false + for _, name in pairs(character.characters()) do + if name == 'abed' then + found = true + end + end + + assert_true(found, "Couldn't find Abed in characters") +end + +function test_load_current() + local character = character.current() + assert_equal(character.name, 'abed') +end + +function test_load_current() + local character = character.current() + character.state = 'walk' + character.direction = 'left' + + character:reset() + + assert_equal(character.state, 'idle') + assert_equal(character.direction, 'right') +end + +function test_find_unrelated_costume() + local c = character.findRelatedCostume('abed', 'unknown_category') + assert_equal(c, 'base') +end + +function test_find_related_costume() + local c = character.findRelatedCostume('abed', 's1e7') + assert_equal(c, 'batman') +end + + diff --git a/src/test/test_utils.lua b/src/test/test_utils.lua index 39a825bd1..83a1d9853 100644 --- a/src/test/test_utils.lua +++ b/src/test/test_utils.lua @@ -31,3 +31,17 @@ function test_split_string() assert_equal(output[1], "a") assert_equal(output[2], "a") end + +--should remove first and last values if they are the same +function test_remove_duplicate_args() + local output = utils.cleanarg({"src", "--level=forest", "src"}) + assert_equal(output[1], "--level=forest") + assert_equal(#output, 1) +end + +--should remove first value +function test_remove_first_args() + local output = utils.cleanarg({"src", "--level=forest"}) + assert_equal(output[1], "--level=forest") + assert_equal(#output, 1) +end diff --git a/src/tunnelparticles.lua b/src/tunnelparticles.lua index 64aaf29b8..6a2a20e62 100644 --- a/src/tunnelparticles.lua +++ b/src/tunnelparticles.lua @@ -8,42 +8,42 @@ TunnelParticle = {} TunnelParticle.__index = TunnelParticle local window = require 'window' -local maxDistance = math.sqrt( ( window.height / 2 ) ^ 2 + ( window.width / 2 ) ^ 2 ) +local maxDistance = math.sqrt((window.height / 2) ^ 2 + (window.width / 2) ^ 2) function TunnelParticle:new() - new = {} - setmetatable(new, TunnelParticle) + new = {} + setmetatable(new, TunnelParticle) - -- r = radius is the angle - -- d = distance from origin - -- s = speed is constant + -- r = radius is the angle + -- d = distance from origin + -- s = speed is constant - new.startSpeed = math.random( 200, 500 ) - new.radius = math.random( ( math.pi * 2 ) * 10000 ) / 10000 - new.distance = math.random( 30, maxDistance ) - new.speed = new.startSpeed * ( new.distance / maxDistance ) - new.spin = ( ( new.startSpeed - 200 ) / 500 ) + new.startSpeed = math.random(200, 500) + new.radius = math.random((math.pi * 2 ) * 10000) / 10000 + new.distance = math.random(30, maxDistance) + new.speed = new.startSpeed * (new.distance / maxDistance) + new.spin = ((new.startSpeed - 200) / 500) - return new + return new end -- Loop each particle repeatedly over the screen function TunnelParticle:update(dt) - self.speed = self.startSpeed * ( self.distance / maxDistance ) - self.distance = self.distance - dt * self.speed - self.radius = self.radius + self.spin * dt - - if self.distance <= 30 then - self.distance = maxDistance - end + self.speed = self.startSpeed * (self.distance / maxDistance) + self.distance = self.distance - dt * self.speed + self.radius = self.radius + self.spin * dt + + if self.distance <= 30 then + self.distance = maxDistance + end end function TunnelParticle:draw() - love.graphics.setPoint( ( self.startSpeed / 50 ) * ( self.distance / maxDistance ), "rough") - love.graphics.point( - ( window.width / 2 ) + ( math.cos( self.radius ) * self.distance ), - ( window.height / 2 ) + ( math.sin( self.radius ) * self.distance ) - ) + love.graphics.setPoint((self.startSpeed / 50) * (self.distance / maxDistance), "rough") + love.graphics.point( + (window.width / 2) + (math.cos(self.radius) * self.distance), + (window.height / 2) + (math.sin(self.radius) * self.distance) + ) end TunnelParticles = {} @@ -53,18 +53,27 @@ local particles = {} -- Generate the requested number of particles function TunnelParticles.init() - for i = 1,particleCount do - table.insert(particles, TunnelParticle:new()) - end + for i = 1,particleCount do + table.insert(particles, TunnelParticle:new()) + end end function TunnelParticles.update(dt) - for _,particle in ipairs(particles) do particle:update(dt) end + for _, particle in ipairs(particles) do + particle:update(dt) + end end function TunnelParticles.draw() - love.graphics.setColor( 255, 255, 255, 255 ) - for _,particle in ipairs(particles) do particle:draw() end + love.graphics.setColor( 255, 255, 255, 255 ) + for _, particle in ipairs(particles) do + particle:draw() + end +end + +function TunnelParticles.leave() + particles = {} end + return TunnelParticles diff --git a/src/update.lua b/src/update.lua index b8607d343..23cc81b9a 100644 --- a/src/update.lua +++ b/src/update.lua @@ -9,16 +9,16 @@ local window = require 'window' local screen = Gamestate.new() function screen:init() - self.message = "" - self.progress = 0 self.updater = sparkle.newUpdater(app.config.iteration or "0.0.0", app.config.feedurl or "") - self.logo = love.graphics.newImage('images/menu/splash.png') - self.time = 0 end function screen:enter() + self.message = "" + self.progress = 0 + self.time = 0 + self.logo = love.graphics.newImage('images/menu/splash.png') self.bg = sound.playMusic("opening") self.updater:start() end @@ -45,6 +45,7 @@ function screen:update(dt) end function screen:leave() + self.logo = nil love.graphics.setColor(255, 255, 255, 255) end diff --git a/src/utils.lua b/src/utils.lua index 7fc17600b..cc8d50d25 100644 --- a/src/utils.lua +++ b/src/utils.lua @@ -271,4 +271,16 @@ function utils.setMode(width, height, fullscreen, vsync, fsaa) end end +function utils.cleanarg(args) + local first = table.remove(args, 1) + if first == args[#args] then + table.remove(args) + end + return args +end + +function utils.require(path) + return love.filesystem.load(path .. ".lua")() +end + return utils diff --git a/src/vendor/gamestate.lua b/src/vendor/gamestate.lua index a4cb164df..f2bb2c7ce 100644 --- a/src/vendor/gamestate.lua +++ b/src/vendor/gamestate.lua @@ -80,6 +80,13 @@ function GS.switch(to, ...) return current:enter(pre, ...) end +-- Same as GS.switch, but mark the current gamestate as paused +function GS.stack(to, ...) + current.paused = true + return GS.switch(to, ...) +end + + -- holds all defined love callbacks after GS.registerEvents is called -- returns empty function on undefined callback local registry = setmetatable({}, {__index = function() return __NULL__ end}) diff --git a/src/vendor/tmx.lua b/src/vendor/tmx.lua index 94e62bfaf..5523e7da2 100644 --- a/src/vendor/tmx.lua +++ b/src/vendor/tmx.lua @@ -120,7 +120,7 @@ function tmx.load(level) end end - table.sort( map.layers, function(a,b) return a.parallax < b.parallax end ) + table.sort(map.layers, function(a,b) return a.parallax < b.parallax end) return map end diff --git a/src/verticalparticles.lua b/src/verticalparticles.lua index 91b85419a..eb4807bb0 100644 --- a/src/verticalparticles.lua +++ b/src/verticalparticles.lua @@ -1,61 +1,66 @@ ----------------------------------------------------------------------- --- verticalparticles.lua --- Manages the particles for the starry select menu background. --- Created by tjvezina ----------------------------------------------------------------------- +local window = require 'window' -Particle = {} +local Particle = {} Particle.__index = Particle -local window = require 'window' - function Particle:new() - new = {} - setmetatable(new, Particle) + new = {} + setmetatable(new, Particle) - local winWidth = window.width + local winWidth = window.width - new.size = math.random(3) - new.pos = { x = math.random(winWidth), y = math.random(window.height) } + new.size = math.random(3) + new.pos = { x = math.random(winWidth), y = math.random(window.height) } - local ratio = 1.0 - math.cos(math.abs(new.pos.x - winWidth/2) * 2 / winWidth) * 0.6 + local ratio = 1.0 - math.cos(math.abs(new.pos.x - winWidth/2) * 2 / winWidth) * 0.6 - new.speed = 300 * (ratio + math.random()/4) + new.speed = 300 * (ratio + math.random()/4) - return new + return new end -- Loop each particle repeatedly over the screen function Particle:update(dt) - self.pos.y = self.pos.y - (dt * self.speed) + self.pos.y = self.pos.y - (dt * self.speed) - if self.pos.y < 0 then self.pos.y = window.height end + if self.pos.y < 0 then self.pos.y = window.height end end function Particle:draw() - love.graphics.setPoint(self.size, "rough") - love.graphics.point(self.pos.x, self.pos.y) + love.graphics.setPoint(self.size, "rough") + love.graphics.point(self.pos.x, self.pos.y) end -VerticalParticles = {} +local VerticalParticles = {} local particleCount = 100 local particles = {} -- Generate the requested number of particles function VerticalParticles.init() - for i = 1,particleCount do - table.insert(particles, Particle:new()) - end + particles = {} + + for i = 1,particleCount do + table.insert(particles, Particle:new()) + end end function VerticalParticles.update(dt) - for _,particle in ipairs(particles) do particle:update(dt) end + for _,particle in ipairs(particles) do + particle:update(dt) + end end function VerticalParticles.draw() - love.graphics.setColor( 255, 255, 255, 255 ) - for _,particle in ipairs(particles) do particle:draw() end + love.graphics.setColor(255, 255, 255, 255) + for _,particle in ipairs(particles) do + particle:draw() + end end +function VerticalParticles.leave() + particles = {} +end + + return VerticalParticles