Initial commit.

This commit is contained in:
Anna Rose 2023-09-28 15:02:46 -04:00
commit 1d824dcc10
21 changed files with 962 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*.pdx

4
Makefile Normal file
View File

@ -0,0 +1,4 @@
all: build
build:
make -C src/

3
license.md Normal file
View File

@ -0,0 +1,3 @@
Copyright 2023 Anna Rose Wiggins.
All rights reserved.

7
readme.md Normal file
View File

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

10
src/Makefile Normal file
View File

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

81
src/ball.lua Normal file
View File

@ -0,0 +1,81 @@
import "CoreLibs/graphics"
import "CoreLibs/timer"
import "pixie"
local gfx <const> = 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

12
src/brick.lua Normal file
View File

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

14
src/constants.lua Normal file
View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

103
src/fonts/Mini Mono 2X.fnt Normal file
View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

103
src/fonts/Mini Mono.fnt Normal file
View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

121
src/fonts/font-Bitmore.fnt Normal file
View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

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

BIN
src/images/brick.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 485 B

165
src/main.lua Normal file
View File

@ -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 <const> = playdate.graphics
local brickFreq <const> = 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()

5
src/pdxinfo Normal file
View File

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

32
src/pixie.lua Normal file
View File

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

43
src/textbox.lua Normal file
View File

@ -0,0 +1,43 @@
import "CoreLibs/graphics"
import "CoreLibs/object"
import "pixie"
local gfx <const> = 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