diff --git a/.DS_Store b/.DS_Store index f7d03a9..c40f011 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/source/.DS_Store b/source/.DS_Store index 5c2c7a9..cf6b89f 100644 Binary files a/source/.DS_Store and b/source/.DS_Store differ diff --git a/source/assets/.DS_Store b/source/assets/.DS_Store index 1c14d07..0674314 100644 Binary files a/source/assets/.DS_Store and b/source/assets/.DS_Store differ diff --git a/source/assets/audio/bomberTheme.mp3 b/source/assets/audio/bomberTheme.mp3 new file mode 100644 index 0000000..7bed6f0 Binary files /dev/null and b/source/assets/audio/bomberTheme.mp3 differ diff --git a/source/assets/audio/drop.wav b/source/assets/audio/drop.wav new file mode 100644 index 0000000..72fd11f Binary files /dev/null and b/source/assets/audio/drop.wav differ diff --git a/source/assets/audio/drop1.wav b/source/assets/audio/drop1.wav new file mode 100644 index 0000000..594af9b Binary files /dev/null and b/source/assets/audio/drop1.wav differ diff --git a/source/assets/audio/hit1.wav b/source/assets/audio/hit1.wav new file mode 100644 index 0000000..b1a9448 Binary files /dev/null and b/source/assets/audio/hit1.wav differ diff --git a/source/assets/bg_bomber.psd b/source/assets/bg_bomber.psd index 83d3e04..f9ca83e 100644 Binary files a/source/assets/bg_bomber.psd and b/source/assets/bg_bomber.psd differ diff --git a/source/assets/sprites/death.png b/source/assets/sprites/death.png new file mode 100644 index 0000000..edb48ff Binary files /dev/null and b/source/assets/sprites/death.png differ diff --git a/source/assets/sprites/enemy1.png b/source/assets/sprites/enemy1.png new file mode 100644 index 0000000..b229474 Binary files /dev/null and b/source/assets/sprites/enemy1.png differ diff --git a/source/assets/sprites/enemy1_1.png b/source/assets/sprites/enemy1_1.png new file mode 100644 index 0000000..f959ed5 Binary files /dev/null and b/source/assets/sprites/enemy1_1.png differ diff --git a/source/assets/sprites/enemy1_2.png b/source/assets/sprites/enemy1_2.png new file mode 100644 index 0000000..50a0604 Binary files /dev/null and b/source/assets/sprites/enemy1_2.png differ diff --git a/source/assets/sprites/enemy1_3.png b/source/assets/sprites/enemy1_3.png new file mode 100644 index 0000000..92188f8 Binary files /dev/null and b/source/assets/sprites/enemy1_3.png differ diff --git a/source/assets/sprites/enemy1_4.png b/source/assets/sprites/enemy1_4.png new file mode 100644 index 0000000..c65a861 Binary files /dev/null and b/source/assets/sprites/enemy1_4.png differ diff --git a/source/assets/sprites/enemy2.png b/source/assets/sprites/enemy2.png new file mode 100644 index 0000000..83772c2 Binary files /dev/null and b/source/assets/sprites/enemy2.png differ diff --git a/source/assets/sprites/enemy_2.png b/source/assets/sprites/enemy_2.png new file mode 100644 index 0000000..025dfcd Binary files /dev/null and b/source/assets/sprites/enemy_2.png differ diff --git a/source/assets/sprites/noise-table-400-240.png b/source/assets/sprites/noise-table-400-240.png new file mode 100644 index 0000000..e5fa990 Binary files /dev/null and b/source/assets/sprites/noise-table-400-240.png differ diff --git a/source/main.lua b/source/main.lua index 439576b..70cbb07 100644 --- a/source/main.lua +++ b/source/main.lua @@ -24,14 +24,16 @@ ZIndex = { ui = 10, alert = 12, ground = 100, - flash = 101 + flash = 101, + foreground = 102 } CollideGroups = { player = 1, enemy = 2, props = 3, items = 4, - wall = 5 + wall = 5, + granade = 6 } Maps = { @@ -111,7 +113,8 @@ import "scripts/MapCard" import "scripts/bomber/movableCrosshair" import "scripts/bomber/granade" import "scripts/bomber/explosionMark" - +import "scripts/bomber/enemy" +import "scripts/bomber/noiseAnimation" import "scenes/BaseScene" import 'scenes/Assemble' import 'scenes/DroneCardSelector' @@ -159,4 +162,6 @@ playdate.display.setRefreshRate(50) Noble.showFPS = false -Noble.new(BomberScene) + +--Noble.new(BomberScene) +Noble.new(Menu) diff --git a/source/pdxinfo b/source/pdxinfo index 2525648..5063765 100644 --- a/source/pdxinfo +++ b/source/pdxinfo @@ -2,7 +2,7 @@ name=FPV Game author=ut3usw description=This is a FPV Game bundleID=guru.dead.fpv -version=0.2.0 -buildNumber=10 +version=0.2.6 +buildNumber=13 imagePath=assets/launcher/ launchSoundPath=assets/launcher/sound.wav \ No newline at end of file diff --git a/source/scenes/Assemble.lua b/source/scenes/Assemble.lua index d5a3ee6..50f1d9f 100644 --- a/source/scenes/Assemble.lua +++ b/source/scenes/Assemble.lua @@ -159,11 +159,6 @@ This operation is crucial. Execute with precision. Command out.]] -- self.dialogue:setPadding(4) end -function round(number) - local formatted = string.format("%.2f", number) - return formatted -end - local elapsedTime = 0 function scene:update() scene.super.update(self) diff --git a/source/scenes/DroneCardSelector.lua b/source/scenes/DroneCardSelector.lua index 430a0a1..c75d4cd 100644 --- a/source/scenes/DroneCardSelector.lua +++ b/source/scenes/DroneCardSelector.lua @@ -10,7 +10,7 @@ scene.inputHandler = { return end scene.menuConfirmSound:play(1) - mode = Drones[scene.menuIndex].mode + local mode = Drones[scene.menuIndex].mode local soundTable = playdate.sound.playingSources() for i=1, #soundTable do soundTable[i]:stop() diff --git a/source/scenes/Game.lua b/source/scenes/Game.lua index f8f03c3..0b31071 100644 --- a/source/scenes/Game.lua +++ b/source/scenes/Game.lua @@ -4,19 +4,6 @@ local scene = Game local font = Graphics.font.new('assets/fonts/Mini Sans 2X') -local function screenShake(shakeTime, shakeMagnitude) - local shakeTimer = playdate.timer.new(shakeTime, shakeMagnitude, 0) - shakeTimer.updateCallback = function(timer) - local magnitude = math.floor(timer.value) - local shakeX = math.random(-magnitude, magnitude) - local shakeY = math.random(-magnitude, magnitude) - playdate.display.setOffset(shakeX, shakeY) - end - shakeTimer.timerEndedCallback = function() - playdate.display.setOffset(0, 0) - end -end - function scene:drawBackground() local speed = 0.1 if scene.ground ~= nil then @@ -66,8 +53,6 @@ end function scene:start() scene.super.start(self) - playdate.ui.crankIndicator:draw() -- not sure why this is not working - self.optionsMenu:addMenuItem("Main Menu", function() Noble.transition(Menu) end) Noble.showFPS = false end @@ -103,14 +88,13 @@ function scene:enter() end end -function round(number) - local formatted = string.format("%.2f", number) - return formatted -end - function scene:update() scene.super.update(self) + if playdate.isCrankDocked() then + playdate.ui.crankIndicator:draw() + end + if scene.player == nil then return end @@ -169,6 +153,7 @@ function scene:exit() if scene.tank ~= nil then scene.tank:remove() end + scene.helloAudio:stop() scene.telemLostSound:stop() scene.levelAudio:stop() scene.balebaSpawner:remove() diff --git a/source/scenes/MapSelector.lua b/source/scenes/MapSelector.lua index 0ac8f6e..8e379f9 100644 --- a/source/scenes/MapSelector.lua +++ b/source/scenes/MapSelector.lua @@ -42,6 +42,7 @@ end function scene:update() scene.super.update(self) + if not scene.cards then return end elapsedTime = elapsedTime + 1 / playdate.display.getRefreshRate() local dy = 2 * math.sin(20 * elapsedTime) diff --git a/source/scenes/Menu.lua b/source/scenes/Menu.lua index 00e26a4..f7b7b8f 100644 --- a/source/scenes/Menu.lua +++ b/source/scenes/Menu.lua @@ -110,7 +110,7 @@ end function scene:exit() scene.super.exit(self) - -- scene.levelAudio:stop() + scene.levelAudio:stop() self.sequence = Sequence.new():from(self.menuY):to(self.menuYTo, 0.5, Ease.inSine) self.sequence:start() end diff --git a/source/scenes/bomber/BomberScene.lua b/source/scenes/bomber/BomberScene.lua index 484bfb3..d0f0215 100644 --- a/source/scenes/bomber/BomberScene.lua +++ b/source/scenes/bomber/BomberScene.lua @@ -11,6 +11,11 @@ function scene:init() self.bgY = 0 self.scrollSpeed = 0.6 + scene.dropSound = playdate.sound.fileplayer.new("assets/audio/drop1") + scene.themeSound = playdate.sound.fileplayer.new("assets/audio/bomberTheme") + scene.themeSound:setVolume(0.5) + scene.themeSound:play() + scene.progressBar = ProgressBar(50, 210, 50, 5) scene.progressBar:set(0) scene.progressBar:setVisible(false) @@ -19,9 +24,22 @@ function scene:init() scene.grenadeCooldownTimer = nil scene.grenadeCooldownDuration = 100 scene.progressBarMax = 100 + + scene.autoReload = false + scene.reloadProgress = 0 + scene.crankSensitivity = 0.2 scene.availableGrenades = 8 + scene.enemies = {} + + scene.enemySpawnTimer = nil + scene.enemySpawnInterval = 1000 + scene.maxEnemies = 5 + scene.nextEnemyIndex = 1 + scene.minSpawnDelay = 500 + scene.maxSpawnDelay = 3500 + BomberScene.instance = self end @@ -63,15 +81,21 @@ scene.inputHandler = { scene.progressBar:set(0) scene.progressBar:setVisible(true) scene.availableGrenades = scene.availableGrenades - 1 + + scene.dropSound:play() - scene.grenadeCooldownTimer = playdate.timer.new(scene.grenadeCooldownDuration, function() - scene.grenadeCooldown = false - scene.progressBar:setVisible(false) - end) - - scene.grenadeCooldownTimer.updateCallback = function(timer) - local percentage = (scene.grenadeCooldownDuration - timer.timeLeft) / scene.grenadeCooldownDuration * scene.progressBarMax - scene.progressBar:set(percentage) + if scene.autoReload then + scene.grenadeCooldownTimer = playdate.timer.new(scene.grenadeCooldownDuration, function() + scene.grenadeCooldown = false + scene.progressBar:setVisible(false) + end) + + scene.grenadeCooldownTimer.updateCallback = function(timer) + local percentage = (scene.grenadeCooldownDuration - timer.timeLeft) / scene.grenadeCooldownDuration * scene.progressBarMax + scene.progressBar:set(percentage) + end + else + scene.reloadProgress = 0 end end end @@ -81,6 +105,9 @@ function scene:enter() scene.super.enter(self) Noble.Input.setHandler(scene.inputHandler) scene.crosshair = MovableCrosshair(100, 100) + + scene:scheduleNextEnemySpawn() + NoiseAnimation(200, 120) end function scene:start() @@ -91,23 +118,81 @@ end function scene:update() scene.super.update(self) + + if scene.grenadeCooldown and not scene.autoReload and not playdate.isCrankDocked() then + local change = playdate.getCrankChange() + + if change > 0 or change < 0 then + scene.reloadProgress = scene.reloadProgress + (change * scene.crankSensitivity) + if scene.reloadProgress > scene.progressBarMax then + scene.reloadProgress = scene.progressBarMax + + scene.grenadeCooldown = false + scene.progressBar:setVisible(false) + end + + scene.progressBar:set(scene.reloadProgress) + end + end + Noble.Text.draw(scene.availableGrenades .. "x", 10, 210, Noble.Text.ALIGN_LEFT, false, font) if scene.availableGrenades <= 0 then Noble.Text.draw("No grenades left", 200, 110, Noble.Text.ALIGN_CENTER, false, font) scene.crosshair:setVisible(false) - end - - if playdate.isCrankDocked() then + scene.progressBar:setVisible(false) + elseif playdate.isCrankDocked() then Noble.Text.draw("Crank it to reload!", 200, 110, Noble.Text.ALIGN_CENTER, false, font) playdate.ui.crankIndicator:draw() end end --- TODO: to reset grenades spin crank --- TODO: random spawn of enemies +function scene:spawnEnemies() + local activeEnemies = 0 + + for i = 1, #scene.enemies do + if scene.enemies[i] and not scene.enemies[i].removed then + activeEnemies = activeEnemies + 1 + end + end + + if activeEnemies < self.maxEnemies then + scene.enemies[scene.nextEnemyIndex] = Enemy(math.random(30, 370), -20) + scene.nextEnemyIndex = scene.nextEnemyIndex + 1 + end + + scene:scheduleNextEnemySpawn() +end + +function scene:scheduleNextEnemySpawn() + local delay = math.random(scene.minSpawnDelay, scene.maxSpawnDelay) + scene.enemySpawnTimer = playdate.timer.new(delay, function() + scene:spawnEnemies() + end) +end + +function scene:finish() + scene.themeSound:stop() + scene.enemySpawnTimer:remove() + for i = 1, #scene.enemies do + if scene.enemies[i] then + scene.enemies[i]:remove() + end + end + scene.enemies = {} + if scene.progressBar then + scene.progressBar:remove() + end + scene.progressBar = nil + if scene.grenadeCooldownTimer then + scene.grenadeCooldownTimer:remove() + end + scene.grenadeCooldownTimer = nil + scene.crosshair:remove() + scene.crosshair = nil + BomberScene.instance = nil +end + -- TODO: random spawn some decorations --- TODO: add some music --- TODO: add some sound effects -- TODO: add clouds or smoke -- TODO: random disactivate granades \ No newline at end of file diff --git a/source/scripts/bomber/enemy.lua b/source/scripts/bomber/enemy.lua new file mode 100644 index 0000000..7e3c025 --- /dev/null +++ b/source/scripts/bomber/enemy.lua @@ -0,0 +1,94 @@ +Enemy = {} +class('Enemy').extends(NobleSprite) + +function Enemy:init(x,y) + Enemy.super.init(self) + self:moveTo(x, y) + self:setZIndex(4) + self:add(x,y) + self.markImage = Graphics.image.new("assets/sprites/enemy"..math.random(1,2)) -- TODO: make it random + self.deadImage = Graphics.image.new("assets/sprites/enemy1_3") + self.hitSound = playdate.sound.fileplayer.new("assets/audio/hit1") + self:setImage(self.markImage) + self.removed = false + self:setGroups(CollideGroups.enemy) + self:setCollidesWithGroups({ + CollideGroups.granade, + CollideGroups.enemy + }) + self:setCollideRect(-6, -6, 46, 46) + self:setSize(32, 32) + + self.vx = 0 + self.vy = 0 + self.isDying = false + self.friction = 0.95 +end + +function Enemy:update() + if not BomberScene.instance then return end + local speed = 0 + + if self.isDying then + self.vx = self.vx * self.friction + self.vy = self.vy * self.friction + + self:moveBy(self.vx, self.vy + BomberScene.instance.scrollSpeed) + + if math.abs(self.vx) < 0.1 and math.abs(self.vy) < 0.1 then + self.isDying = false + self.removed = true + end + elseif not self.removed then + speed = math.random(0, 7)/10 + self:moveBy(0, BomberScene.instance.scrollSpeed + speed) + else + self:moveBy(0, BomberScene.instance.scrollSpeed) + end + + local actualX, actualY, collisions, numberOfCollisions = self:checkCollisions(self.x, self.y) + + if numberOfCollisions > 0 then + for i, collision in ipairs(collisions) do + if collision.other:getTag() == 154 and collision.other.currentRadius <= 0.05 and not self.isDying then + print("Collision with granade") + self:setImage(self.deadImage) + self.hitSound:play() + self:applyExplosionForce(collision.other.x, collision.other.y) + end + end + end + + if self.y > 240 + 10 then + if not self.removed then + print("Removing enemy") + self:remove() + self:superRemove() + self.removed = true + end + end +end + +function Enemy:applyExplosionForce(explosionX, explosionY) + local dx = self.x - explosionX + local dy = self.y - explosionY + + local dist = math.sqrt(dx*dx + dy*dy) + if dist == 0 then dist = 0.001 end + + dx = dx / dist + dy = dy / dist + + local maxForce = 5 + local maxRadius = 100 + + local force = maxForce * (1 - math.min(dist, maxRadius) / maxRadius) + force = math.max(force, 1) + + self.vx = dx * force + self.vy = dy * force * 0.5 + + self.isDying = true + + self:setRotation(math.random() * 360) +end diff --git a/source/scripts/bomber/explosionMark.lua b/source/scripts/bomber/explosionMark.lua index 94cb0e6..78335d0 100644 --- a/source/scripts/bomber/explosionMark.lua +++ b/source/scripts/bomber/explosionMark.lua @@ -4,7 +4,7 @@ class('ExplosionMark').extends(NobleSprite) function ExplosionMark:init(x, y) ExplosionMark.super.init(self) self.id = math.random(1, 2) - self.markImage = Graphics.image.new("assets/sprites/boomSplash" .. self.id) -- TODO: make it random + self.markImage = Graphics.image.new("assets/sprites/boomSplash" .. self.id) self:setImage(self.markImage) self:moveTo(x, y) self:setZIndex(5) @@ -12,6 +12,7 @@ function ExplosionMark:init(x, y) end function ExplosionMark:update() + if not BomberScene.instance then return end self:moveBy(0, BomberScene.instance.scrollSpeed) if self.y > 240 + 32 then diff --git a/source/scripts/bomber/granade.lua b/source/scripts/bomber/granade.lua index d579b4a..7701939 100644 --- a/source/scripts/bomber/granade.lua +++ b/source/scripts/bomber/granade.lua @@ -1,19 +1,6 @@ Granade = {} class('Granade').extends(NobleSprite) -local function screenShake(shakeTime, shakeMagnitude) - local shakeTimer = playdate.timer.new(shakeTime, shakeMagnitude, 0) - shakeTimer.updateCallback = function(timer) - local magnitude = math.floor(timer.value) - local shakeX = math.random(-magnitude, magnitude) - local shakeY = math.random(-magnitude, magnitude) - playdate.display.setOffset(shakeX, shakeY) - end - shakeTimer.timerEndedCallback = function() - playdate.display.setOffset(0, 0) - end -end - function Granade:init(x, y) Granade.super.init(self) @@ -21,12 +8,12 @@ function Granade:init(x, y) self.currentRadius = self.initialRadius self.shrinkRate = 0.2 - random = math.random(1, 4) + local random = math.random(1, 4) self.boomSound = playdate.sound.fileplayer.new("assets/audio/boom" .. random) self.boomSound:setVolume(0.5) self.isActive = true - -- Variables for random movement + self.randomMovementTimer = 0 self.randomXVelocity = 0 self.randomYVelocity = 0 @@ -35,8 +22,15 @@ function Granade:init(x, y) self.spriteSize = size self:setSize(size, size) self:moveTo(x, y) + self:setZIndex(10) + self:setTag(154) self:setCenter(0.5, 0.5) - + self:setGroups(CollideGroups.granade) + self:setCollidesWithGroups({ + CollideGroups.enemy + }) + self:setCollideRect(0, 0, self:getSize()) + print("Granade init") print(self.x, self.y) self:add(x, y) diff --git a/source/scripts/bomber/movableCrosshair.lua b/source/scripts/bomber/movableCrosshair.lua index 1937647..03c4c77 100644 --- a/source/scripts/bomber/movableCrosshair.lua +++ b/source/scripts/bomber/movableCrosshair.lua @@ -4,28 +4,25 @@ class('MovableCrosshair').extends(playdate.graphics.sprite) function MovableCrosshair:init() MovableCrosshair.super.init(self) - -- Parameters for crosshair self.lineLength = 10 self.gapSize = 3 - -- Parameters for movement self.baseX = 200 self.baseY = 150 self.moveRadius = 2 - self.moveSpeed = 2 + self.moveSpeed = 2.3 self.time = 0 - -- Calculate size based on crosshair dimensions local totalSize = (self.lineLength + self.gapSize) * 2 + 10 self:setSize(totalSize, totalSize) - -- Set the drawing offset to middle of sprite self.drawOffsetX = totalSize / 2 self.drawOffsetY = totalSize / 2 self:add(self.baseX, self.baseY) self:setCenter(0.5, 0.5) self:markDirty() + self:setZIndex(11) end function MovableCrosshair:update() @@ -66,24 +63,24 @@ end function MovableCrosshair:moveUp() if self.baseY > 5 then - self.baseY = self.baseY - 1 + self.baseY = self.baseY - self.moveSpeed end end function MovableCrosshair:moveDown() if self.baseY < 235 then - self.baseY = self.baseY + 1 + self.baseY = self.baseY + self.moveSpeed end end function MovableCrosshair:moveLeft() if self.baseX > 5 then - self.baseX = self.baseX - 1 + self.baseX = self.baseX - self.moveSpeed end end function MovableCrosshair:moveRight() if self.baseX < 395 then - self.baseX = self.baseX + 1 + self.baseX = self.baseX + self.moveSpeed end end \ No newline at end of file diff --git a/source/scripts/bomber/noiseAnimation.lua b/source/scripts/bomber/noiseAnimation.lua new file mode 100644 index 0000000..54c6f44 --- /dev/null +++ b/source/scripts/bomber/noiseAnimation.lua @@ -0,0 +1,36 @@ +NoiseAnimation = {} +class('NoiseAnimation').extends(NobleSprite) + +function NoiseAnimation:init(x, y) + NoiseAnimation.super.init(self, "assets/sprites/noise", true) + self.animation:addState("run", 2, 11) + self.animation:addState("idle", 1, 1) + self.animation.run.frameDuration = 2.5 + self.animation:setState("idle") + self:setZIndex(ZIndex.foreground) + self:setSize(400, 240) + self:add() + self:moveTo(x, y) + + self.state = "idle" + self.idleFrames = 0 +end + +function NoiseAnimation:update() + if self.state == "idle" then + self.idleFrames -= 1 + if self.idleFrames <= 0 then + self.state = "run" + self.animation:setState("run") + end + else + local r = math.random(0) + if r < 0.01 then + self.state = "idle" + self.idleFrames = math.random(30, 100) + self.animation:setState("idle") + else + self.animation:setState("run") + end + end +end diff --git a/source/scripts/pageSprite.lua b/source/scripts/pageSprite.lua index eda7029..493ea16 100644 --- a/source/scripts/pageSprite.lua +++ b/source/scripts/pageSprite.lua @@ -2,7 +2,7 @@ PageSprite = {} class('PageSprite').extends(NobleSprite) function PageSprite:init(x, y) - Baleba.super.init(self, "assets/sprites/pages", true) + PageSprite.super.init(self, "assets/sprites/pages", true) self.animation:addState("1", 1, 1) self.animation:addState("2", 2, 2) self.animation:addState("3", 3, 3) diff --git a/source/scripts/player.lua b/source/scripts/player.lua index 9e2b33f..34b153c 100644 --- a/source/scripts/player.lua +++ b/source/scripts/player.lua @@ -192,9 +192,6 @@ function Player:handleMovementAndCollisions() self:boom() return elseif collisionTag == 154 then -- Baleba - -- if self.debug then TODO: why debug always true? - -- return - -- end self:boom(collisionObject) return elseif collisionTag == 2 then -- Tank