commit 1d824dcc1098df518ad95e5070fc1acf6ddf5814 Author: annabunches Date: Thu Sep 28 15:02:46 2023 -0400 Initial commit. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e00b594 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.pdx diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..cf18b5c --- /dev/null +++ b/Makefile @@ -0,0 +1,4 @@ +all: build + +build: + make -C src/ diff --git a/license.md b/license.md new file mode 100644 index 0000000..cea10d7 --- /dev/null +++ b/license.md @@ -0,0 +1,3 @@ +Copyright 2023 Anna Rose Wiggins. + +All rights reserved. diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..d55d65e --- /dev/null +++ b/readme.md @@ -0,0 +1,7 @@ +## March of the Sinister Bricks + +This is a simple game for the (Playdate)[https://play.date] handheld console. In it, you use the crank to control a paddle, and try to break the bricks marching toward you from the right. + +This is, of course, a completely novel idea and is dissimilar from any other game ever made. + +It was written as part of a series of games to learn the Playdate SDK. diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..e20ce2a --- /dev/null +++ b/src/Makefile @@ -0,0 +1,10 @@ +PLAYDATE_SDK_PATH := ~/playdate +GAME := SinisterBricks + +all: build + +build: clean + PLAYDATE_SDK_PATH=$(PLAYDATE_SDK_PATH) $(PLAYDATE_SDK_PATH)/bin/pdc -k main.lua ../$(GAME).pdx + +clean: + rm -rf ../$(GAME).pdx diff --git a/src/ball.lua b/src/ball.lua new file mode 100644 index 0000000..2f9bb74 --- /dev/null +++ b/src/ball.lua @@ -0,0 +1,81 @@ +import "CoreLibs/graphics" +import "CoreLibs/timer" +import "pixie" + +local gfx = playdate.graphics + +class("Ball", { + direction = {x=5, y=5}, +}).extends(Pixie) + +function Ball:init(width, height, color, x, y) + local img = gfx.image.new(width, height, color) + Ball.super.init(self, img, x, y) + self.collisionResponse = gfx.sprite.kCollisionTypeBounce + + self:levelUpTimer() + + return self +end + +function Ball:levelUpTimer() + playdate.timer.performAfterDelay(5000, + function(ball) + if ball.direction.x > 0 then + ball.direction.x += 1 + else + ball.direction.x -= 1 + end + if ball.direction.y > 0 then + ball.direction.y += 1 + else + ball.direction.y -= 1 + end + + ball:levelUpTimer() + end, + self) +end + +function Ball:move() + local collisions + collisions = select(3, self:moveWithCollisions(self.x + self.direction.x, self.y + self.direction.y)) + + for i=1, #collisions, 1 do + self:changeDirection(collisions[i].normal) + end + + return collisions +end + +function Ball:changeDirection(normal) + local dx,dy = normal:unpack() + if dx ~= 0 then + self.direction.x *= -1 + end + + if dy ~= 0 then + self.direction.y *= -1 + end +end + +function Ball:setDirection(x, y) + x = x or self.direction.x + y = y or self.direction.y + self.direction.x = x + self.direction.y = y +end + +function Ball:deflect() + self:setDirection(1) +end + +function Ball:_normalizePosition() + local changeVector = Ball.super._normalizePosition(self) + if changeVector[1] ~= 0 then + self.direction.x = changeVector[1] + end + if changeVector[2] ~= 0 then + self.direction.y = changeVector[2] + end +end diff --git a/src/brick.lua b/src/brick.lua new file mode 100644 index 0000000..f366597 --- /dev/null +++ b/src/brick.lua @@ -0,0 +1,12 @@ +import "CoreLibs/graphics" + +import "pixie" + +class("Brick").extends(Pixie) + +function Brick:init(x, y) + local img = playdate.graphics.image.new("images/brick.png") + Brick.super.init(self, img, x, y) + return self +end + diff --git a/src/constants.lua b/src/constants.lua new file mode 100644 index 0000000..bbcd826 --- /dev/null +++ b/src/constants.lua @@ -0,0 +1,14 @@ +-- Important constants used by multiple files. + +-- The playable screen limits; this allows us to set aside regions of the screen for +-- UI elements. Pixies will not venture beyond these bounds. +-- This must be coordinated with background images / drawing functions. +constants = { + screenTop = 20, + screenBottom = 240, + screenLeft = 0, + screenRight = 400, +} + +constants.screenHeight = constants.screenBottom - constants.screenTop +constants.screenWidth = constants.screenRight - constants.screenLeft diff --git a/src/fonts/Mini Mono 2X-table-16-20.png b/src/fonts/Mini Mono 2X-table-16-20.png new file mode 100644 index 0000000..8b9444a Binary files /dev/null and b/src/fonts/Mini Mono 2X-table-16-20.png differ diff --git a/src/fonts/Mini Mono 2X.fnt b/src/fonts/Mini Mono 2X.fnt new file mode 100644 index 0000000..13e03db --- /dev/null +++ b/src/fonts/Mini Mono 2X.fnt @@ -0,0 +1,103 @@ + +space 16 +A 16 +B 16 +C 16 +D 16 +E 16 +F 16 +G 16 +H 16 +I 16 +J 16 +K 16 +L 16 +M 16 +N 16 +O 16 +P 16 +Q 16 +R 16 +S 16 +T 16 +U 16 +V 16 +W 16 +X 16 +Y 16 +Z 16 +[ 16 +\ 16 +] 16 +^ 16 +_ 16 +` 16 +a 16 +b 16 +c 16 +d 16 +e 16 +f 16 +g 16 +h 16 +i 16 +j 16 +k 16 +l 16 +m 16 +n 16 +o 16 +p 16 +q 16 +r 16 +s 16 +t 16 +u 16 +v 16 +w 16 +x 16 +y 16 +z 16 +{ 16 +| 16 +} 16 +~ 16 +¥ 16 +… 16 +™ 16 +‼ 16 +© 16 +® 16 +� 16 +! 16 +" 16 +# 16 +$ 16 +% 16 +& 16 +' 16 +( 16 +) 16 +* 16 ++ 16 +, 16 +- 16 +. 16 +/ 16 +0 16 +1 16 +2 16 +3 16 +4 16 +5 16 +6 16 +7 16 +8 16 +9 16 +: 16 +; 16 +< 16 += 16 +> 16 +? 16 +@ 16 \ No newline at end of file diff --git a/src/fonts/Mini Mono-table-8-10.png b/src/fonts/Mini Mono-table-8-10.png new file mode 100644 index 0000000..2598eba Binary files /dev/null and b/src/fonts/Mini Mono-table-8-10.png differ diff --git a/src/fonts/Mini Mono.fnt b/src/fonts/Mini Mono.fnt new file mode 100644 index 0000000..9e0a620 --- /dev/null +++ b/src/fonts/Mini Mono.fnt @@ -0,0 +1,103 @@ + +space 8 +A 8 +B 8 +C 8 +D 8 +E 8 +F 8 +G 8 +H 8 +I 8 +J 8 +K 8 +L 8 +M 8 +N 8 +O 8 +P 8 +Q 8 +R 8 +S 8 +T 8 +U 8 +V 8 +W 8 +X 8 +Y 8 +Z 8 +[ 8 +\ 8 +] 8 +^ 8 +_ 8 +` 8 +a 8 +b 8 +c 8 +d 8 +e 8 +f 8 +g 8 +h 8 +i 8 +j 8 +k 8 +l 8 +m 8 +n 8 +o 8 +p 8 +q 8 +r 8 +s 8 +t 8 +u 8 +v 8 +w 8 +x 8 +y 8 +z 8 +{ 8 +| 8 +} 8 +~ 8 +¥ 8 +… 8 +™ 8 +‼ 8 +© 8 +® 8 +� 8 +! 8 +" 8 +# 8 +$ 8 +% 8 +& 8 +' 8 +( 8 +) 8 +* 8 ++ 8 +, 8 +- 8 +. 8 +/ 8 +0 8 +1 8 +2 8 +3 8 +4 8 +5 8 +6 8 +7 8 +8 8 +9 8 +: 8 +; 8 +< 8 += 8 +> 8 +? 8 +@ 8 \ No newline at end of file diff --git a/src/fonts/font-Bitmore-table-7-11.png b/src/fonts/font-Bitmore-table-7-11.png new file mode 100644 index 0000000..2f57799 Binary files /dev/null and b/src/fonts/font-Bitmore-table-7-11.png differ diff --git a/src/fonts/font-Bitmore.fnt b/src/fonts/font-Bitmore.fnt new file mode 100644 index 0000000..c0fefb7 --- /dev/null +++ b/src/fonts/font-Bitmore.fnt @@ -0,0 +1,121 @@ +space 2 +! 2 +" 4 +# 6 +$ 5 +% 7 +& 6 +' 2 +( 2 +) 2 +* 3 ++ 5 +, 2 +- 4 +. 2 +/ 4 +0 5 +1 2 +2 5 +3 5 +4 5 +5 5 +6 5 +7 5 +8 5 +9 5 +: 2 +; 3 +< 4 += 5 +> 4 +? 6 +@ 7 +A 5 +B 5 +C 5 +D 5 +E 5 +F 5 +G 5 +H 5 +I 3 +J 5 +K 5 +L 4 +M 7 +N 7 +O 5 +P 5 +Q 6 +R 5 +S 5 +T 5 +U 5 +V 7 +W 7 +X 7 +Y 5 +Z 5 +[ 2 +\ 4 +] 2 +^ 6 +_ 5 +` 2 +a 6 +b 5 +c 5 +d 5 +e 5 +f 5 +g 5 +h 5 +i 2 +j 4 +k 5 +l 2 +m 7 +n 5 +o 5 +p 5 +q 5 +r 5 +s 5 +t 4 +u 5 +v 5 +w 5 +x 5 +y 5 +z 5 +{ 4 +| 2 +} 4 +~ 4 +ag -1 +at -1 +av -1 +ay -1 +ff -2 +fi -1 +fr -1 +ft -1 +fu -2 +le -1 +is -1 +it -1 +iv -1 +li -1 +ll -1 +iz -1 +ly -1 +rc -1 +re -1 +ri -1 +ro -1 +tf -1 +rt -1 +ry -1 +vi -1 +tt -1 \ No newline at end of file diff --git a/src/fonts/font-Cuberick-Bold-table-12-15.png b/src/fonts/font-Cuberick-Bold-table-12-15.png new file mode 100644 index 0000000..69cc81b Binary files /dev/null and b/src/fonts/font-Cuberick-Bold-table-12-15.png differ diff --git a/src/fonts/font-Cuberick-Bold.fnt b/src/fonts/font-Cuberick-Bold.fnt new file mode 100644 index 0000000..686a3c7 --- /dev/null +++ b/src/fonts/font-Cuberick-Bold.fnt @@ -0,0 +1,258 @@ +tracking=1 +space 6 +! 2 +" 7 +# 9 +$ 8 +% 8 +& 8 +' 3 +( 3 +) 3 +* 6 ++ 6 +, 3 +- 4 +. 2 +/ 6 +0 8 +1 6 +2 7 +3 7 +4 8 +5 7 +6 7 +7 7 +8 7 +9 7 +: 2 +; 2 +< 7 += 7 +> 7 +? 6 +@ 10 +A 8 +B 8 +C 8 +D 8 +E 6 +F 6 +G 8 +H 8 +I 2 +J 6 +K 7 +L 6 +M 12 +N 9 +O 8 +P 8 +Q 9 +R 8 +S 8 +T 8 +U 8 +V 9 +W 12 +X 8 +Y 8 +Z 8 +[ 3 +\ 6 +] 3 +^ 6 +_ 7 +` 3 +a 6 +b 6 +c 6 +d 6 +e 6 +f 5 +g 6 +h 6 +i 2 +j 3 +k 7 +l 2 +m 10 +n 6 +o 6 +p 6 +q 6 +r 5 +s 6 +t 6 +u 6 +v 7 +w 10 +x 7 +y 7 +z 6 +{ 5 +| 2 +} 5 +~ 7 +™ 9 +… 12 +¥ 8 +‼ 5 +© 10 +® 10 +� 10 + +va -1 +Wm -1 +Ok 0 +Eu 0 +Fo 0 +Pe -1 +W; -1 +Ol 0 +Ev 0 +Vu -1 +He 0 +Yd -1 +Fr 0 +Ye -1 +Nu 0 +Wr -1 +Ft -1 +Fu 0 +Ic 0 +Wt -1 +Id 0 +J, 0 +Wu -1 +Xo 0 +Yi -1 +Ac 0 +J. 0 +B, 0 +Ad 0 +Po -1 +Fy -1 +Ae 0 +Ja 0 +Rd -1 +S, -1 +Y: -1 +B. 0 +Wy -1 +Re -1 +Ho 0 +Ag 0 +Gu 0 +Y; -1 +S. -1 +Bb 0 +Xu 0 +Je 0 +Yp -1 +C, 0 +T, -1 +C. 0 +Xy -1 +Io 0 +Ca 0 +Hu 0 +T. -1 +Yu -1 +Ta -1 +Iq 0 +Ao 0 +Ke 0 +Bi 0 +Yv -1 +Ap 0 +D, 0 +Si -1 +Hy 0 +Aq 0 +Ro -1 +Tc -1 +Bk 0 +U, -1 +It 0 +D. 0 +Te -1 +Jo 0 +Da 0 +Bl 0 +U. -1 +At -1 +Ua -1 +Au 0 +Rt -1 +Av -1 +Ru -1 +Ma 0 +Aw -1 +Ti -1 +Sp -1 +V, -1 +Br 0 +Ko 0 +Ay -1 +Mc 0 +Ju 0 +Md 0 +T: -1 +N, 0 +V. -1 +Va -1 +Ug -1 +Bu 0 +Me 0 +T; -1 +N. 0 +F, -2 +To -1 +Na 0 +Su -1 +W, -1 +Cr 0 +F. -2 +Ve -1 +Ku 0 +By -1 +Fa -1 +Tr -1 +W. -1 +O, -1 +Ts -1 +Ne 0 +Um -1 +Un -1 +O. -1 +Tu -1 +Oa 0 +Fe -1 +Vi -1 +Up -1 +Ob 0 +Wd -1 +Tw -1 +Mo 0 +Lu 0 +Ni 0 +V: -1 +Ty -1 +Us -1 +Fi 0 +V; -1 +Xa -1 +Vo -1 +Ly -1 +Pa -1 +Wi -1 +Oh 0 +Y, -1 +F: -1 +Xe 0 +No 0 +F; -1 +Vr -1 +W: -1 +Y. -1 \ No newline at end of file diff --git a/src/images/brick.png b/src/images/brick.png new file mode 100644 index 0000000..53f4513 Binary files /dev/null and b/src/images/brick.png differ diff --git a/src/main.lua b/src/main.lua new file mode 100644 index 0000000..ca66920 --- /dev/null +++ b/src/main.lua @@ -0,0 +1,165 @@ +-- Collision mask reference: +import "CoreLibs/object" +import "CoreLibs/graphics" +import "CoreLibs/sprites" +import "CoreLibs/timer" +import "CoreLibs/crank" + +import "pixie" +import "ball" +import "brick" +import "textbox" + +local gfx = playdate.graphics + +local brickFreq = 0.7 + +local paddle = nil +local deadWall = nil +local ball = nil + +local startTime = nil +local endTime = nil +local timeWidget = nil +local speedWidget = nil + +function setup() + playdate.resetElapsedTime() + math.randomseed(playdate.getSecondsSinceEpoch()) + + paddle = Pixie.new(6, 50, gfx.kColorWhite, 6, 120) + paddle:add() + ball = Ball(6, 6, gfx.kColorWhite, 12, 120) + ball:add() + + addWallColliders() + setupBricks() + + timeWidget = TextBox(100, 20, 350, 10, 5, kTextAlignment.center) + updateTimeWidget() + timeWidget:add() + + speedWidget = TextBox(100, 20, 50, 10, 5, kTextAlignment.center) + updateSpeedWidget() + speedWidget:add() + + local backgroundImage = gfx.image.new(400, 220, gfx.kColorBlack) + gfx.sprite.setBackgroundDrawingCallback( + function(x, y, width, height) + backgroundImage:draw(0, 20) + end + ) + + startTime = playdate.getElapsedTime() +end + +function addWallColliders() + -- set up border boundaries + -- top + gfx.sprite.addEmptyCollisionSprite(constants.screenLeft, constants.screenTop, + constants.screenWidth, 1) + -- right + gfx.sprite.addEmptyCollisionSprite(constants.screenRight, constants.screenTop, + 1, constants.screenHeight) + -- bottom + gfx.sprite.addEmptyCollisionSprite(constants.screenLeft, constants.screenBottom, + constants.screenWidth, 1) + + -- left + deadWall = Pixie.new(1, constants.screenHeight, gfx.kColorClear, + 0, constants.screenHeight / 2 + constants.screenTop) + deadWall:add() +end + +function setupBricks() + for i=1,12 do + addBrickRow() + advanceBricks() + end +end + +function addBrickRow() + for i=1,10 do + if math.random() < brickFreq then + local brick = Brick(constants.screenRight - 5, + constants.screenTop + (22 * (i-1)) + 11) + brick:add() + end + end +end + +function advanceBricks() + -- move all the bricks over + local sprites = gfx.sprite.getAllSprites() + for i=1, #sprites, 1 do + local brick = sprites[i] + if brick:isa(Brick) then + brick:moveBy(-10, 0) + end + end +end + +function playdate.update() + gfx.clear() + + if endTime then + if playdate.buttonJustPressed(playdate.kButtonA) then + gfx.sprite.removeAll() + playdate.file.run("main.pdz") + end + else + -- Handle player movement + movePaddle(playdate.getCrankTicks(80)) + + -- Handle ball movement + collision + moveBall() + + -- UI Updates + updateTimeWidget() + updateSpeedWidget() + + playdate.timer.updateTimers() + end + + gfx.sprite.update() +end + +function updateTimeWidget() + timeWidget:setText(string.format("time: %.2f", playdate.getElapsedTime())) +end + +function updateSpeedWidget() + speedWidget:setText(string.format("speed: " .. math.abs(ball.direction.x) - 4)) +end + +function movePaddle(ticks) + if ticks == 0 then return end + paddle:moveWithCollisions(paddle.x, paddle.y + ticks*-5) +end + +function moveBall() + local collisions = ball:move() + for i = 1, #collisions, 1 do + local obj = collisions[i].other + if obj:isa(Brick) then + obj:remove() + end + if obj == deadWall then + endGame() + end + end +end + +function endGame() + endTime = playdate.getElapsedTime() + + local textBox = TextBox(275, 40, 200, 120, 5, kTextAlignment.center) + textBox:setText( + "Game Over!\nYou survived for " .. + string.format("%.2f", endTime) .. + " seconds.\n Press 'A' to play again." + ) + textBox:add() +end + +setup() diff --git a/src/pdxinfo b/src/pdxinfo new file mode 100644 index 0000000..5a8f321 --- /dev/null +++ b/src/pdxinfo @@ -0,0 +1,5 @@ +name=March of the Sinister Bricks +author=Anna Rose Wiggins +description=You think they're essential masonry supplies but I think they're sinister. +bundleID=net.annabunches.sinister-bricks +version=0.1 diff --git a/src/pixie.lua b/src/pixie.lua new file mode 100644 index 0000000..c7d9b7a --- /dev/null +++ b/src/pixie.lua @@ -0,0 +1,32 @@ +-- A pixie is like a sprite, but better +-- built-in collider, support for setting position at creation +-- ideal for subclassing sprites that don't need a custom +-- draw function + +import "CoreLibs/object" +import "CoreLibs/graphics" +import "CoreLibs/sprites" + +import "constants" + +local gfx = playdate.graphics + +class("Pixie").extends(gfx.sprite) + +-- automatically initialize pixie with a simple rectangle. +-- great for simple games or for debugging +function Pixie.new(width, height, color, x, y) + local img = gfx.image.new(width, height, color) + local pixie = Pixie(img, x, y) + return pixie +end + +function Pixie:init(img, x, y) + x = x or 0 + y = y or 0 + + Pixie.super.init(self, img) + self:setCollideRect(0, 0, self:getSize()) + self:moveTo(x, y) + return self +end diff --git a/src/textbox.lua b/src/textbox.lua new file mode 100644 index 0000000..caed2e3 --- /dev/null +++ b/src/textbox.lua @@ -0,0 +1,43 @@ +import "CoreLibs/graphics" +import "CoreLibs/object" +import "pixie" + +local gfx = playdate.graphics + +local defaultFont = gfx.font.new("fonts/Mini Mono") + +class("TextBox").extends(gfx.sprite) + +function TextBox:init(w, h, x, y, vert, align, font) + TextBox.super.init(self) + + self:setSize(w, h) + self:moveTo(x, y) + self:setZIndex(999) + self.align = align or kTextAlignment.left + self.font = font or defaultFont + self.vert = vert + self.text = "" +end + +function TextBox:setText(text) + self.text = text +end + +function TextBox:draw(x, y, w, h) + gfx.pushContext() + + gfx.setColor(gfx.kColorWhite) + gfx.fillRect(x, y, w, h) + gfx.setColor(gfx.kColorBlack) + gfx.drawRoundRect(x, y, w, h, 2) + + gfx.setFont(self.font) + gfx.drawTextInRect( + self.text, + x, y+self.vert, w, h, nil, nil, self.align + ) + + gfx.popContext() +end +