This commit is contained in:
2026-02-23 20:52:54 +01:00
parent fae2abf94e
commit 9eb426021e
32 changed files with 268 additions and 78 deletions

BIN
source/.DS_Store vendored

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 369 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 313 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 379 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 396 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 343 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 440 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 854 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

View File

@@ -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)

View File

@@ -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

View File

@@ -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)

View File

@@ -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()

View File

@@ -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()

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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