initial commit
This commit is contained in:
commit
85225f7c2d
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
builds/*
|
6
.vscode/extensions.json
vendored
Normal file
6
.vscode/extensions.json
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"sumneko.lua",
|
||||
"jep-a.lua-plus"
|
||||
]
|
||||
}
|
8
.vscode/settings.json
vendored
Normal file
8
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"Lua.runtime.version": "Lua 5.4",
|
||||
"Lua.diagnostics.disable": ["undefined-global", "lowercase-global"],
|
||||
"Lua.diagnostics.globals": ["playdate", "import"],
|
||||
"Lua.runtime.nonstandardSymbol": ["+=", "-=", "*=", "/=", "//=", "%=", "<<=", ">>=", "&=", "|=", "^="],
|
||||
"Lua.workspace.library": ["$PLAYDATE_SDK_PATH/CoreLibs"],
|
||||
"Lua.workspace.preloadFileSize": 1000,
|
||||
}
|
103
.vscode/tasks.json
vendored
Normal file
103
.vscode/tasks.json
vendored
Normal file
@ -0,0 +1,103 @@
|
||||
{
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "Invoke Build and Run script",
|
||||
"type": "shell",
|
||||
"command": "&",
|
||||
"windows": {
|
||||
"args": [
|
||||
"${workspaceFolder}\\Build and Run (Simulator).ps1",
|
||||
"-build",
|
||||
"'${workspaceFolder}\\builds'",
|
||||
"-source",
|
||||
"'${workspaceFolder}\\source'",
|
||||
"-name",
|
||||
"'${workspaceFolderBasename}'"
|
||||
],
|
||||
"options": {
|
||||
"shell": {
|
||||
"executable": "powershell.exe"
|
||||
}
|
||||
}
|
||||
},
|
||||
"linux": {
|
||||
"command": "${workspaceFolder}/build_and_run.sh",
|
||||
"args": [
|
||||
"build"
|
||||
]
|
||||
},
|
||||
"presentation": {
|
||||
"showReuseMessage": false,
|
||||
"reveal": "always",
|
||||
"panel": "shared"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "Invoke Run script",
|
||||
"type": "shell",
|
||||
"command": "&",
|
||||
"windows": {
|
||||
"args": [
|
||||
"${workspaceFolder}\\Build and Run (Simulator).ps1",
|
||||
"-build",
|
||||
"'${workspaceFolder}\\builds'",
|
||||
"-source",
|
||||
"'${workspaceFolder}\\source'",
|
||||
"-name",
|
||||
"'${workspaceFolderBasename}'",
|
||||
"-dontbuild"
|
||||
],
|
||||
"options": {
|
||||
"shell": {
|
||||
"executable": "powershell.exe"
|
||||
}
|
||||
}
|
||||
},
|
||||
"linux": {
|
||||
"command": "${workspaceFolder}/build_and_run.sh",
|
||||
"args": [
|
||||
"run"
|
||||
]
|
||||
},
|
||||
"presentation": {
|
||||
"showReuseMessage": false,
|
||||
"reveal": "always",
|
||||
"panel": "shared"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "Build and Run (Simulator)",
|
||||
"dependsOn": ["Invoke Build and Run script"],
|
||||
"dependsOrder": "sequence",
|
||||
"presentation": {
|
||||
"showReuseMessage": false,
|
||||
"reveal": "always",
|
||||
"panel": "shared"
|
||||
},
|
||||
"problemMatcher": [],
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "Run (Simulator)",
|
||||
"dependsOn": ["Invoke Run script"],
|
||||
"dependsOrder": "sequence",
|
||||
"presentation": {
|
||||
"showReuseMessage": false,
|
||||
"reveal": "always",
|
||||
"panel": "shared"
|
||||
},
|
||||
"problemMatcher": [],
|
||||
"group": {
|
||||
"kind": "test",
|
||||
"isDefault": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
145
build_and_run.sh
Executable file
145
build_and_run.sh
Executable file
@ -0,0 +1,145 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
export PLAYDATE_SDK_PATH="/home/ut3usw/PlaydateSDK-2.5.0"
|
||||
|
||||
# Check for color by variable and tput command
|
||||
if [[ -z $NOCOLOR && -n $(command -v tput) ]]; then
|
||||
RED=$(tput setaf 1)
|
||||
CYN=$(tput setaf 6)
|
||||
YEL=$(tput setaf 3)
|
||||
RST=$(tput sgr0)
|
||||
fi
|
||||
|
||||
function display_help() {
|
||||
printf "%s\n\n" "${0} build|run|-h|--help|"
|
||||
printf "%-16s\n" "build: Builds the project and runs the Simulator"
|
||||
printf "%-16s\n" "run : Skips building the project and runs the Simulator"
|
||||
printf "\n"
|
||||
printf "%s\n\n" "Set NOCOLOR=1 to disable terminal coloring"
|
||||
exit 0
|
||||
}
|
||||
|
||||
# We don't need fancy flags/operators for two commands
|
||||
case $1 in
|
||||
"build")
|
||||
BUILD=1
|
||||
;;
|
||||
"run")
|
||||
BUILD=0
|
||||
;;
|
||||
*)
|
||||
display_help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# Set some paths
|
||||
BUILD_DIR="./builds"
|
||||
SOURCE_DIR="./source"
|
||||
PDX_PATH="${BUILD_DIR}/$(basename $(pwd)).pdx"
|
||||
|
||||
# Logging functions
|
||||
function log() {
|
||||
printf "%s\n" "${CYN}>> $1${RST}"
|
||||
}
|
||||
function log_warn() {
|
||||
printf "%s\n" "${YEL}>! $1${RST}"
|
||||
}
|
||||
function log_err() {
|
||||
printf "%s\n >> %s\n" "${RED}!! ERROR !!" "$1${RST}"
|
||||
}
|
||||
|
||||
function check_pdxinfo() {
|
||||
if [[ -f ./source/pdxinfo ]]; then
|
||||
if grep "com.organization.package" ./source/pdxinfo 2>&1 >/dev/null; then
|
||||
log_warn "PDXINFO NOTICE:"
|
||||
log_warn "Don't forget to change your unique project info in 'source/pdxinfo': 'bundleID', 'name', 'author', 'description'."
|
||||
log_warn "It's critical to change your game bundleID, so there will be no collisions with other games, installed via sideload."
|
||||
log_warn "Read more about pdxinfo here: https://sdk.play.date/Inside%20Playdate.html#pdxinfo"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
function chk_err() {
|
||||
# Check for errors in last process and bail if needed
|
||||
if [[ $? > 0 ]]; then
|
||||
log_err "There was an issue with the previous command; exiting!"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
function check_close_sim() {
|
||||
# Check if we have 'pidof'
|
||||
PIDOF=$(command -v pidof2)
|
||||
|
||||
# Prefer 'pidof'; use ps if not
|
||||
if [[ -n $PIDOF ]]; then
|
||||
SIMPID=$($PIDOF "PlaydateSimulator")
|
||||
if [[ -n $SIMPID ]]; then
|
||||
log "Found existing Simulator, closing..."
|
||||
kill -9 $SIMPID
|
||||
chk_err
|
||||
fi
|
||||
else
|
||||
SIMPID=$(ps aux | grep PlaydateSimulator | grep -v grep | awk '{print $2}')
|
||||
if [[ -n $SIMPID ]]; then
|
||||
log "Found existing Simulator, closing..."
|
||||
kill -9 $SIMPID
|
||||
chk_err
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# Create build dir
|
||||
function make_build_dir() {
|
||||
if [[ ! -d "${BUILD_DIR}" ]]; then
|
||||
log "Creating build directory..."
|
||||
mkdir -p "${BUILD_DIR}"
|
||||
chk_err
|
||||
fi
|
||||
}
|
||||
|
||||
# Clean build dir
|
||||
function clean_build_dir() {
|
||||
if [[ -d "${BUILD_DIR}" ]]; then
|
||||
log "Cleaning build directory..."
|
||||
rm -rfv "${BUILD_DIR}/*"
|
||||
chk_err
|
||||
fi
|
||||
}
|
||||
|
||||
# Compile the PDX
|
||||
function build_pdx() {
|
||||
if [[ $BUILD == 1 ]]; then
|
||||
log "Building PDX with 'pdc'..."
|
||||
$PLAYDATE_SDK_PATH/bin/pdc -sdkpath "${PLAYDATE_SDK_PATH}" "${SOURCE_DIR}" "${PDX_PATH}"
|
||||
chk_err
|
||||
fi
|
||||
}
|
||||
|
||||
# Run the PDX with Simulator
|
||||
function run_pdx() {
|
||||
if [[ -d "${PDX_PATH}" ]]; then
|
||||
log "Running PDX with Simulator..."
|
||||
$PLAYDATE_SDK_PATH/bin/PlaydateSimulator "${PDX_PATH}"
|
||||
else
|
||||
log_err "PDX doesn't exist! Please 'build' the project first!"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
#### MAIN SCRIPT ####
|
||||
if [[ $BUILD == 1 ]]; then
|
||||
log "Attempting a build and run of PDX..."
|
||||
make_build_dir
|
||||
clean_build_dir
|
||||
check_pdxinfo
|
||||
build_pdx
|
||||
check_close_sim
|
||||
run_pdx
|
||||
else
|
||||
log "Attempting to run PDX: ${PDX_PATH}..."
|
||||
check_close_sim
|
||||
run_pdx
|
||||
fi
|
||||
|
BIN
source/audio/hello.wav
Normal file
BIN
source/audio/hello.wav
Normal file
Binary file not shown.
BIN
source/audio/quad.wav
Normal file
BIN
source/audio/quad.wav
Normal file
Binary file not shown.
BIN
source/audio/telemko.wav
Normal file
BIN
source/audio/telemko.wav
Normal file
Binary file not shown.
BIN
source/audio/ukr.wav
Normal file
BIN
source/audio/ukr.wav
Normal file
Binary file not shown.
BIN
source/audio/war.wav
Normal file
BIN
source/audio/war.wav
Normal file
Binary file not shown.
15
source/backgroundSprite.lua
Normal file
15
source/backgroundSprite.lua
Normal file
@ -0,0 +1,15 @@
|
||||
local pd <const> = playdate
|
||||
local gfx <const> = playdate.graphics
|
||||
|
||||
class("BackGround").extends()
|
||||
|
||||
function BackGround:init(x, y)
|
||||
gfx.sprite.setBackgroundDrawingCallback(
|
||||
function (x, y, width, height)
|
||||
local groundImage = playdate.graphics.image.new("sprites/bg")
|
||||
gfx.setClipRect(x, y, width, height)
|
||||
groundImage:draw(0,0)
|
||||
gfx.clearClipRect()
|
||||
end
|
||||
)
|
||||
end
|
BIN
source/font/Mini Sans 2X-table-18-20.png
Normal file
BIN
source/font/Mini Sans 2X-table-18-20.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.4 KiB |
265
source/font/Mini Sans 2X.fnt
Normal file
265
source/font/Mini Sans 2X.fnt
Normal file
@ -0,0 +1,265 @@
|
||||
--metrics={"baseline":0,"xHeight":0,"capHeight":0,"pairs":{"Ta":[-4,1],"Tc":[-4,1],"Te":[-4,1],"To":[-4,1],"Tm":[-4,1],"Tn":[-4,1],"Tr":[-4,1],"Tu":[-4,1],"Tv":[-4,1],"Tw":[-4,1],"fB":[2,1],"fD":[2,1],"fE":[2,1],"fF":[2,1],"fH":[2,1],"fI":[2,1],"fK":[2,1],"fL":[2,1],"fM":[2,1],"fN":[2,1],"fP":[2,1],"fR":[2,1],"fW":[2,1],"fb":[2,1],"fh":[2,1],"fk":[2,1],"fC":[2,1],"fG":[2,1],"fO":[2,1],"fQ":[2,1],"fU":[2,1],"fl":[2,1],"lB":[1,1],"lD":[1,1],"lE":[1,1],"lF":[1,1],"lH":[1,1],"lI":[1,1],"lK":[1,1],"lL":[1,1],"lM":[1,1],"lN":[1,1],"lP":[1,1],"lR":[1,1],"lW":[1,1],"lb":[1,1],"lh":[1,1],"lk":[1,1],"lC":[1,1],"lG":[1,1],"lO":[1,1],"lQ":[1,1],"lU":[0,1,1,1],"ll":[0,1,1,1],"la":[0,1,1,1],"lc":[0,1,1,1],"le":[0,1,1,1],"lo":[0,1,1,1],"lm":[1,1],"ln":[1,1],"lr":[1,1],"lu":[0,1,1,1],"lv":[0,1,1,1],"lw":[0,1,1,1],"Fa":[-2,1],"Fc":[-2,1],"Fe":[-2,1],"Fo":[-2,1],"Fm":[-2,1],"Fn":[-2,1],"Fr":[-2,1],"Fu":[-2,1],"Fv":[-2,1],"Fw":[-2,1],"aT":[-4,2],"uT":[-4,2],"gT":[-4,2],"yT":[-4,2],"mT":[-4,2],"nT":[-4,2],"vT":[-4,2],"wT":[-4,2],"Td":[-4,0],"Tf":[-2,0],"Tg":[-4,0],"Tp":[-4,0],"Tq":[-4,0],"Ts":[-4,0],"Tt":[-2,0],"Tx":[-4,0],"Ty":[-4,0],"Tz":[-4,0],"TJ":[-4,0],"T<>":[-4,0],"bT":[-4,0],"cT":[-4,0],"eT":[-4,0],"fA":[1,0,2,0],"fT":[2,0],"fi":[2,0],"fj":[2,0],"fY":[2,0],"fJ":[-2,0],"fS":[2,0],"fV":[2,0],"fX":[2,0],"fZ":[2,0],"hT":[-4,0],"kT":[-4,0],"lA":[1,0],"lT":[-1,0],"ld":[0,0,1,0],"lf":[-1,0],"lg":[0,0,1,0],"li":[1,0],"lj":[1,0],"lp":[1,0],"lq":[0,0,1,0],"ls":[1,0],"lt":[-1,0],"lx":[1,0],"ly":[0,0,1,0],"lz":[1,0],"lY":[-1,0],"lJ":[1,0],"lS":[1,0],"lV":[-1,0],"lX":[1,0],"lZ":[1,0],"l<>":[-1,0],"oT":[-4,0],"pT":[-4,0],"qT":[-4,0],"rT":[-4,0],"rX":[-2,0],"rZ":[-2,0],"sT":[-4,0],"tT":[-2,0],"xT":[-4,0],"zT":[-4,0],"C<>":[-2,0],"Ef":[-2,0],"Et":[-2,0],"E<>":[-2,0],"Fd":[-2,0],"Ff":[-2,0],"Fg":[-2,0],"Fp":[-2,0],"Fq":[-2,0],"Fs":[-2,0],"Ft":[-2,0],"Fx":[-2,0],"Fy":[-2,0],"Fz":[-2,0],"FJ":[-2,0],"F<>":[-2,0],"YJ":[-2,0],"Kf":[-2,0],"Kt":[-2,0],"K<>":[-4,0],"LT":[-4,0],"Lf":[-2,0],"Lt":[-2,0],"LY":[-4,0],"LV":[-4,0],"L<>":[-4,0],"PJ":[-2,0],"X<>":[-2,0],"Z<>":[-2,0],"<22>T":[-4,0],"<22>X":[-2,0],"<22>Z":[-2,0]},"left":["BDEFHIKLMNPRWbhk","CGOQ","Ul","aceo","mnr","uvw"],"right":["AQ","DO","HIMNWd","JU","au","gy","mn","vw"]}
|
||||
tracking=2
|
||||
|
||||
0 12
|
||||
1 12
|
||||
2 12
|
||||
3 12
|
||||
4 12
|
||||
5 12
|
||||
6 12
|
||||
7 12
|
||||
8 12
|
||||
9 12
|
||||
space 8
|
||||
. 4
|
||||
A 12
|
||||
B 12
|
||||
T 12
|
||||
a 12
|
||||
b 12
|
||||
c 12
|
||||
d 12
|
||||
e 12
|
||||
f 8
|
||||
g 12
|
||||
h 12
|
||||
i 4
|
||||
j 4
|
||||
k 12
|
||||
l 5
|
||||
m 16
|
||||
n 12
|
||||
o 12
|
||||
p 12
|
||||
q 12
|
||||
r 12
|
||||
s 12
|
||||
t 8
|
||||
u 12
|
||||
v 12
|
||||
w 16
|
||||
x 12
|
||||
y 12
|
||||
z 12
|
||||
, 4
|
||||
C 12
|
||||
D 12
|
||||
E 12
|
||||
F 12
|
||||
G 12
|
||||
O 12
|
||||
H 12
|
||||
I 4
|
||||
! 4
|
||||
" 10
|
||||
' 4
|
||||
Y 12
|
||||
{ 8
|
||||
| 4
|
||||
} 8
|
||||
J 12
|
||||
K 14
|
||||
L 12
|
||||
M 14
|
||||
N 14
|
||||
P 12
|
||||
Q 12
|
||||
R 12
|
||||
S 12
|
||||
U 12
|
||||
V 14
|
||||
W 14
|
||||
X 12
|
||||
Z 12
|
||||
/ 12
|
||||
\ 12
|
||||
[ 6
|
||||
] 6
|
||||
: 4
|
||||
; 4
|
||||
^ 12
|
||||
_ 10
|
||||
` 8
|
||||
~ 16
|
||||
¥ 12
|
||||
… 16
|
||||
™ 14
|
||||
‼ 10
|
||||
© 18
|
||||
® 18
|
||||
<EFBFBD> 16
|
||||
@ 18
|
||||
# 18
|
||||
$ 12
|
||||
% 14
|
||||
& 12
|
||||
( 6
|
||||
) 6
|
||||
* 12
|
||||
+ 12
|
||||
- 10
|
||||
= 10
|
||||
? 12
|
||||
< 10
|
||||
> 10
|
||||
|
||||
Ta -4
|
||||
Tc -4
|
||||
Te -4
|
||||
To -4
|
||||
Tm -4
|
||||
Tn -4
|
||||
Tr -4
|
||||
Tu -4
|
||||
Tv -4
|
||||
Tw -4
|
||||
fB 2
|
||||
fD 2
|
||||
fE 2
|
||||
fF 2
|
||||
fH 2
|
||||
fI 2
|
||||
fK 2
|
||||
fL 2
|
||||
fM 2
|
||||
fN 2
|
||||
fP 2
|
||||
fR 2
|
||||
fW 2
|
||||
fb 2
|
||||
fh 2
|
||||
fk 2
|
||||
fC 2
|
||||
fG 2
|
||||
fO 2
|
||||
fQ 2
|
||||
fU 2
|
||||
fl 2
|
||||
lB 1
|
||||
lD 1
|
||||
lE 1
|
||||
lF 1
|
||||
lH 1
|
||||
lI 1
|
||||
lK 1
|
||||
lL 1
|
||||
lM 1
|
||||
lN 1
|
||||
lP 1
|
||||
lR 1
|
||||
lW 1
|
||||
lb 1
|
||||
lh 1
|
||||
lk 1
|
||||
lC 1
|
||||
lG 1
|
||||
lO 1
|
||||
lQ 1
|
||||
lm 1
|
||||
ln 1
|
||||
lr 1
|
||||
Fa -2
|
||||
Fc -2
|
||||
Fe -2
|
||||
Fo -2
|
||||
Fm -2
|
||||
Fn -2
|
||||
Fr -2
|
||||
Fu -2
|
||||
Fv -2
|
||||
Fw -2
|
||||
aT -4
|
||||
uT -4
|
||||
gT -4
|
||||
yT -4
|
||||
mT -4
|
||||
nT -4
|
||||
vT -4
|
||||
wT -4
|
||||
Td -4
|
||||
Tf -2
|
||||
Tg -4
|
||||
Tp -4
|
||||
Tq -4
|
||||
Ts -4
|
||||
Tt -2
|
||||
Tx -4
|
||||
Ty -4
|
||||
Tz -4
|
||||
TJ -4
|
||||
T<EFBFBD> -4
|
||||
bT -4
|
||||
cT -4
|
||||
eT -4
|
||||
fA 1
|
||||
fT 2
|
||||
fi 2
|
||||
fj 2
|
||||
fY 2
|
||||
fJ -2
|
||||
fS 2
|
||||
fV 2
|
||||
fX 2
|
||||
fZ 2
|
||||
hT -4
|
||||
kT -4
|
||||
lA 1
|
||||
lT -1
|
||||
lf -1
|
||||
li 1
|
||||
lj 1
|
||||
lp 1
|
||||
ls 1
|
||||
lt -1
|
||||
lx 1
|
||||
lz 1
|
||||
lY -1
|
||||
lJ 1
|
||||
lS 1
|
||||
lV -1
|
||||
lX 1
|
||||
lZ 1
|
||||
l<EFBFBD> -1
|
||||
oT -4
|
||||
pT -4
|
||||
qT -4
|
||||
rT -4
|
||||
rX -2
|
||||
rZ -2
|
||||
sT -4
|
||||
tT -2
|
||||
xT -4
|
||||
zT -4
|
||||
C<EFBFBD> -2
|
||||
Ef -2
|
||||
Et -2
|
||||
E<EFBFBD> -2
|
||||
Fd -2
|
||||
Ff -2
|
||||
Fg -2
|
||||
Fp -2
|
||||
Fq -2
|
||||
Fs -2
|
||||
Ft -2
|
||||
Fx -2
|
||||
Fy -2
|
||||
Fz -2
|
||||
FJ -2
|
||||
F<EFBFBD> -2
|
||||
YJ -2
|
||||
Kf -2
|
||||
Kt -2
|
||||
K<EFBFBD> -4
|
||||
LT -4
|
||||
Lf -2
|
||||
Lt -2
|
||||
LY -4
|
||||
LV -4
|
||||
L<EFBFBD> -4
|
||||
PJ -2
|
||||
X<EFBFBD> -2
|
||||
Z<EFBFBD> -2
|
||||
<EFBFBD>T -4
|
||||
<EFBFBD>X -2
|
||||
<EFBFBD>Z -2
|
31
source/ground.lua
Normal file
31
source/ground.lua
Normal file
@ -0,0 +1,31 @@
|
||||
class("Ground").extends(playdate.graphics.sprite)
|
||||
|
||||
function Ground:init(x, y, player)
|
||||
local groundImage = playdate.graphics.image.new("sprites/groundFin")
|
||||
Ground.super.init(self, groundImage)
|
||||
self:moveTo(x, y)
|
||||
self:setZIndex(100)
|
||||
self:setTag(3)
|
||||
self:setCollideRect(0, 28, 800, 10)
|
||||
|
||||
Ground.moveSpeed = 2
|
||||
Ground.player = player
|
||||
end
|
||||
|
||||
function Ground:setMoveSpeed(speed)
|
||||
Ground.moveSpeed = speed
|
||||
end
|
||||
|
||||
function Ground:update()
|
||||
if Ground.player.isMovingRight() == false then
|
||||
Ground.moveSpeed = 0.2
|
||||
else
|
||||
Ground.moveSpeed = 1
|
||||
end
|
||||
|
||||
if self.x <= 0 then
|
||||
self:moveWithCollisions(400, self.y)
|
||||
end
|
||||
|
||||
self:moveWithCollisions(self.x-Ground.moveSpeed, self.y)
|
||||
end
|
68
source/level.lua
Normal file
68
source/level.lua
Normal file
@ -0,0 +1,68 @@
|
||||
local pd <const> = playdate
|
||||
local gfx <const> = playdate.graphics
|
||||
|
||||
import "player"
|
||||
import "ground"
|
||||
import "backgroundSprite"
|
||||
|
||||
|
||||
class("Level").extends()
|
||||
|
||||
|
||||
-- This function relies on the use of timers, so the timer core library
|
||||
-- must be imported, and updateTimers() must be called in the update loop
|
||||
local function screenShake(shakeTime, shakeMagnitude)
|
||||
-- Creating a value timer that goes from shakeMagnitude to 0, over
|
||||
-- the course of 'shakeTime' milliseconds
|
||||
local shakeTimer = playdate.timer.new(shakeTime, shakeMagnitude, 0)
|
||||
-- Every frame when the timer is active, we shake the screen
|
||||
shakeTimer.updateCallback = function(timer)
|
||||
-- Using the timer value, so the shaking magnitude
|
||||
-- gradually decreases over time
|
||||
local magnitude = math.floor(timer.value)
|
||||
local shakeX = math.random(-magnitude, magnitude)
|
||||
local shakeY = math.random(-magnitude, magnitude)
|
||||
playdate.display.setOffset(shakeX, shakeY)
|
||||
end
|
||||
-- Resetting the display offset at the end of the screen shake
|
||||
shakeTimer.timerEndedCallback = function()
|
||||
playdate.display.setOffset(0, 0)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function Level:init()
|
||||
player = Player(30, 30)
|
||||
player:add()
|
||||
|
||||
ground = Ground(400, 225, player)
|
||||
ground:add()
|
||||
|
||||
BackGround()
|
||||
|
||||
local fp = playdate.sound.fileplayer.new( "audio/war" )
|
||||
local hello = playdate.sound.fileplayer.new( "audio/hello" )
|
||||
|
||||
hello:play(1)
|
||||
fp:setVolume(0.7)
|
||||
fp:play(0)
|
||||
|
||||
Level.telemLostSound = playdate.sound.fileplayer.new( "audio/telemko" )
|
||||
Level.telemLostSoundPlayed = false
|
||||
end
|
||||
|
||||
function Level:update()
|
||||
if player.isDead() then
|
||||
if not Level.telemLostSoundPlayed then
|
||||
Level.telemLostSound:play(1)
|
||||
Level.telemLostSoundPlayed = true
|
||||
screenShake(500, 5)
|
||||
end
|
||||
gfx.drawText("Telemetry Lost", 100, 110)
|
||||
end
|
||||
|
||||
local bat = player.getBat()
|
||||
bat = math.floor(bat/100)
|
||||
gfx.drawText("Bat: " .. bat, 10, 215)
|
||||
-- gfx.drawText("Dis: " .. player.dischargeRate, 100, 215)
|
||||
end
|
512
source/lib/AnimatedSprite.lua
Normal file
512
source/lib/AnimatedSprite.lua
Normal file
@ -0,0 +1,512 @@
|
||||
-----------------------------------------------
|
||||
--- Sprite class extension with support of ---
|
||||
--- imagetables and finite state machine, ---
|
||||
--- with json configuration and autoplay. ---
|
||||
--- By @Whitebrim git.brim.ml ---
|
||||
-----------------------------------------------
|
||||
|
||||
-- You can find examples and docs at https://github.com/Whitebrim/AnimatedSprite/wiki
|
||||
-- Comments use EmmyLua style
|
||||
|
||||
import 'CoreLibs/object'
|
||||
import 'CoreLibs/sprites'
|
||||
local gfx <const> = playdate.graphics
|
||||
local function emptyFunc()end
|
||||
|
||||
class("AnimatedSprite").extends(gfx.sprite)
|
||||
|
||||
---@param imagetable table|string actual imagetable or path
|
||||
---@param states? table If provided, calls `setStates(states)` after initialisation
|
||||
---@param animate? boolean If `True`, then the animation of default state will start after initialisation. Default: `False`
|
||||
function AnimatedSprite.new(imagetable, states, animate)
|
||||
return AnimatedSprite(imagetable, states, animate)
|
||||
end
|
||||
|
||||
function AnimatedSprite:init(imagetable, states, animate)
|
||||
AnimatedSprite.super.init(self)
|
||||
|
||||
---@type table
|
||||
if (type(imagetable) == "string") then
|
||||
imagetable = gfx.imagetable.new(imagetable)
|
||||
end
|
||||
self.imagetable = imagetable
|
||||
assert(self.imagetable, "Imagetable is nil. Check if it was loaded correctly.")
|
||||
|
||||
self:add()
|
||||
|
||||
self.globalFlip = gfx.kImageUnflipped
|
||||
self.defaultState = "default"
|
||||
self.states = {
|
||||
default = {
|
||||
name = "default",
|
||||
---@type integer|string
|
||||
firstFrameIndex = 1,
|
||||
framesCount = #self.imagetable,
|
||||
animationStartingFrame = 1,
|
||||
tickStep = 1,
|
||||
frameStep = 1,
|
||||
reverse = false,
|
||||
---@type boolean|integer
|
||||
loop = true,
|
||||
yoyo = false,
|
||||
flip = gfx.kImageUnflipped,
|
||||
xScale = 1,
|
||||
yScale = 1,
|
||||
nextAnimation = nil,
|
||||
|
||||
onFrameChangedEvent = emptyFunc,
|
||||
onStateChangedEvent = emptyFunc,
|
||||
onLoopFinishedEvent = emptyFunc,
|
||||
onAnimationEndEvent = emptyFunc
|
||||
}
|
||||
}
|
||||
|
||||
self._enabled = false
|
||||
self._currentFrame = 0 -- purposely
|
||||
self._ticks = 1
|
||||
self._previousTicks = 1
|
||||
self._loopsFinished = 0
|
||||
self._currentYoyoDirection = true
|
||||
|
||||
if (states) then
|
||||
self:setStates(states)
|
||||
end
|
||||
|
||||
if (animate) then
|
||||
self:playAnimation()
|
||||
end
|
||||
end
|
||||
|
||||
local function drawFrame(self)
|
||||
local state = self.states[self.currentState]
|
||||
self:setImage(self._image, state.flip ~ self.globalFlip, state.xScale, state.yScale)
|
||||
end
|
||||
|
||||
local function setImage(self)
|
||||
local frames = self.states[self.currentState].frames
|
||||
if (frames) then
|
||||
self._image = self.imagetable[frames[self._currentFrame]]
|
||||
else
|
||||
self._image = self.imagetable[self._currentFrame]
|
||||
end
|
||||
end
|
||||
|
||||
---Start/resume the animation
|
||||
---If `currentState` is nil then `defaultState` will be choosen as current
|
||||
function AnimatedSprite:playAnimation()
|
||||
|
||||
local state = self.states[self.currentState]
|
||||
|
||||
if (type(self.currentState) == 'nil') then
|
||||
self.currentState = self.defaultState
|
||||
state = self.states[self.currentState]
|
||||
self._currentFrame = state.animationStartingFrame + state.firstFrameIndex - 1
|
||||
end
|
||||
|
||||
if (self._currentFrame == 0) then
|
||||
self._currentFrame = state.animationStartingFrame + state.firstFrameIndex - 1
|
||||
end
|
||||
|
||||
self._enabled = true
|
||||
self._previousTicks = self._ticks
|
||||
setImage(self)
|
||||
drawFrame(self)
|
||||
if (state.framesCount == 1) then
|
||||
self._loopsFinished += 1
|
||||
state.onFrameChangedEvent(self)
|
||||
state.onLoopFinishedEvent(self)
|
||||
else
|
||||
state.onFrameChangedEvent(self)
|
||||
end
|
||||
end
|
||||
|
||||
---Stop the animation without resetting
|
||||
function AnimatedSprite:pauseAnimation()
|
||||
self._enabled = false
|
||||
end
|
||||
|
||||
---Play the animation without resetting
|
||||
function AnimatedSprite:resumeAnimation()
|
||||
self._enabled = true
|
||||
end
|
||||
|
||||
---Play/Pause animation based on current state
|
||||
function AnimatedSprite:toggleAnimation()
|
||||
if (self._enabled) then
|
||||
self:pauseAnimation()
|
||||
else
|
||||
self:resumeAnimation()
|
||||
end
|
||||
end
|
||||
|
||||
---Stop and reset the animation
|
||||
---After calling `playAnimation` the animation will start from `defaultState`
|
||||
function AnimatedSprite:stopAnimation()
|
||||
self:pauseAnimation()
|
||||
self.currentState = nil
|
||||
self._currentFrame = 0 -- purposely
|
||||
self._ticks = 1
|
||||
self._previousTicks = self._ticks
|
||||
self._loopsFinished = 0
|
||||
self._currentYoyoDirection = true
|
||||
end
|
||||
|
||||
local function addState(self, params)
|
||||
assert(params.name, "The animation state is unnamed!")
|
||||
if (self.defaultState == "default") then
|
||||
self.defaultState = params.name -- Init first added state as default
|
||||
end
|
||||
|
||||
self.states[params.name] = {}
|
||||
local state = self.states[params.name]
|
||||
setmetatable(state, {__index = self.states.default})
|
||||
|
||||
params = params or {}
|
||||
|
||||
state.name = params.name
|
||||
if (params.frames ~= nil) then
|
||||
state["frames"] = params.frames -- Custom animation for non-sequential frames from the imagetable
|
||||
params.firstFrameIndex = 1
|
||||
params.framesCount = #params.frames
|
||||
end
|
||||
if (type(params.firstFrameIndex) == "string") then
|
||||
local thatState = self.states[params.firstFrameIndex]
|
||||
state["firstFrameIndex"] = thatState.firstFrameIndex + thatState.framesCount
|
||||
else
|
||||
state["firstFrameIndex"] = params.firstFrameIndex -- index in the imagetable for the firstFrame
|
||||
end
|
||||
state["framesCount"] = params.framesCount and params.framesCount or (self.states.default.framesCount - state.firstFrameIndex + 1) -- This state frames count
|
||||
state["nextAnimation"] = params.nextAnimation -- Animation to switch to after this finishes
|
||||
if (params.nextAnimation == nil) then
|
||||
state["loop"] = params.loop -- You can put in number of loops or true for endless loop
|
||||
else
|
||||
state["loop"] = params.loop or false
|
||||
end
|
||||
state["reverse"] = params.reverse -- You can reverse animation sequence
|
||||
state["animationStartingFrame"] = params.animationStartingFrame or (state.reverse and state.framesCount or 1) -- Frame to start the animation from
|
||||
state["tickStep"] = params.tickStep -- Speed of animation (2 = every second frame)
|
||||
state["frameStep"] = params.frameStep -- Number of images to skip on next frame
|
||||
state["yoyo"] = params.yoyo -- Ping-pong animation (from 1 to n to 1 to n)
|
||||
state["flip"] = params.flip -- You can set up flip mode, read Playdate SDK Docs for more info
|
||||
state["xScale"] = params.xScale -- Optional scale for horizontal axis
|
||||
state["yScale"] = params.yScale -- Optional scale for vertical axis
|
||||
|
||||
state["onFrameChangedEvent"] = params.onFrameChangedEvent -- Event that will be raised when animation moves to the next frame
|
||||
state["onStateChangedEvent"] = params.onStateChangedEvent -- Event that will be raised when animation state changes
|
||||
state["onLoopFinishedEvent"] = params.onLoopFinishedEvent -- Event that will be raised when animation changes to the final frame
|
||||
state["onAnimationEndEvent"] = params.onAnimationEndEvent -- Event that will be raised after animation in this state ends
|
||||
|
||||
return state
|
||||
end
|
||||
|
||||
---Parse `json` file with animation configuration
|
||||
---@param path string Path to the file
|
||||
---@return table config You can use it in `setStates(states)`
|
||||
function AnimatedSprite.loadStates(path)
|
||||
return assert(json.decodeFile(path), "Requested JSON parse failed. Path: " .. path)
|
||||
end
|
||||
|
||||
---Get imagetable's frame index that is currently displayed
|
||||
---@return integer index Current frame index
|
||||
function AnimatedSprite:getCurrentFrameIndex()
|
||||
if (self.currentState and self.states[self.currentState].frames) then
|
||||
return self.states[self.currentState].frames[self._currentFrame]
|
||||
else
|
||||
return self._currentFrame
|
||||
end
|
||||
end
|
||||
|
||||
---Get the current frame's local index in the state
|
||||
---I.e. 1, 2, 3, N, where N = number of frames in this state
|
||||
---Also works if `frames` property was provided
|
||||
---@return integer index Current frame local index
|
||||
function AnimatedSprite:getCurrentFrameLocalIndex()
|
||||
return self.currentFrame - self.states[self.currentState].firstFrameIndex + 1
|
||||
end
|
||||
|
||||
---Get reference to the current state
|
||||
---@return table state Reference to the current state
|
||||
function AnimatedSprite:getCurrentState()
|
||||
return self.states[self.currentState]
|
||||
end
|
||||
|
||||
---Get reference to the current states
|
||||
---@return table states Reference to the current states
|
||||
function AnimatedSprite:getLocalStates()
|
||||
return self.states
|
||||
end
|
||||
|
||||
---Get copy of the states
|
||||
---@return table states Deepcopy of the current states
|
||||
function AnimatedSprite:copyLocalStates()
|
||||
return table.deepcopy(self.states)
|
||||
end
|
||||
|
||||
---Add all states from the `states` to the current state machine (overwrites values in case of conflicts)
|
||||
---@param states table State machine state list, you can get one by calling `loadStates`
|
||||
---@param animate? boolean If `True`, then the animation of default/current state will start immediately after. Default: `False`
|
||||
---@param defaultState? string If provided, changes default state
|
||||
function AnimatedSprite:setStates(states, animate, defaultState)
|
||||
local statesCount = #states
|
||||
|
||||
local function proceedState(state)
|
||||
if (state.name ~= "default") then
|
||||
addState(self, state)
|
||||
else
|
||||
local default = self.states.default
|
||||
for key, value in pairs(state) do
|
||||
default[key] = value
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if (statesCount == 0) then
|
||||
proceedState(states)
|
||||
if (defaultState) then
|
||||
self.defaultState = defaultState
|
||||
end
|
||||
if (animate) then
|
||||
self:playAnimation()
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
for i = 1, statesCount do
|
||||
proceedState(states[i])
|
||||
end
|
||||
if (defaultState) then
|
||||
self.defaultState = defaultState
|
||||
end
|
||||
if (animate) then
|
||||
self:playAnimation()
|
||||
end
|
||||
end
|
||||
|
||||
---Add new state to the state machine
|
||||
---@param name string Name of the state, should be unique, used as id
|
||||
---@param startFrame? integer Index of the first frame in the imagetable (starts from 1). Default: `1` (from states.default)
|
||||
---@param endFrame? integer Index of the last frame in the imagetable. Default: last frame (from states.default)
|
||||
---@param params? table See examples
|
||||
---@param animate? boolean If `True`, then the animation of this state will start immediately after. Default: `False`
|
||||
function AnimatedSprite:addState(name, startFrame, endFrame, params, animate)
|
||||
params = params or {}
|
||||
params.firstFrameIndex = startFrame or 1
|
||||
params.framesCount = endFrame and (endFrame - params.firstFrameIndex + 1) or nil
|
||||
params.name = name
|
||||
|
||||
addState(self, params)
|
||||
|
||||
if (animate) then
|
||||
self.currentState = name
|
||||
self:playAnimation()
|
||||
end
|
||||
|
||||
return {
|
||||
asDefault = function ()
|
||||
self.defaultState = name
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
---Change current state to an existing state
|
||||
---@param name string New state name
|
||||
---@param play? boolean If new animation should be played right away. Default: `True`
|
||||
function AnimatedSprite:changeState(name, play)
|
||||
if (name == self.currentState) then
|
||||
return
|
||||
end
|
||||
play = type(play) == "nil" and true or play
|
||||
local state = self.states[name]
|
||||
assert (state, "There's no state named \""..name.."\".")
|
||||
self.currentState = name
|
||||
self._currentFrame = 0 -- purposely
|
||||
self._loopsFinished = 0
|
||||
self._currentYoyoDirection = true
|
||||
state.onStateChangedEvent(self)
|
||||
if (play) then
|
||||
self:playAnimation()
|
||||
end
|
||||
end
|
||||
|
||||
---Change current state to an existing state and start from selected frame
|
||||
---If new state is the same as current state, nothing will change
|
||||
---@param name string New state name
|
||||
---@param frameIndex integer Local frame index of this state. Indexing starts from 1. Default: `1`
|
||||
---@param play? boolean If new animation should be played right away. Default: `True`
|
||||
function AnimatedSprite:changeStateAndSelectFrame(name, frameIndex, play)
|
||||
if (name == self.currentState) then
|
||||
return
|
||||
end
|
||||
play = type(play) == "nil" and true or play
|
||||
frameIndex = frameIndex or 1
|
||||
local state = self.states[name]
|
||||
assert (state, "There's no state named \""..name.."\".")
|
||||
self.currentState = name
|
||||
self._currentFrame = state.firstFrameIndex + frameIndex - 1
|
||||
self._loopsFinished = 0
|
||||
self._currentYoyoDirection = true
|
||||
state.onStateChangedEvent(self)
|
||||
if (play) then
|
||||
self:playAnimation()
|
||||
end
|
||||
end
|
||||
|
||||
---Force animation state machine to switch to the next state
|
||||
---@param instant? boolean If `False` change will be performed after the final frame of this loop iteration. Default: `True`
|
||||
---@param state? string Name of the state to change to. If not provided, animator will try to change to the next animation, else stop the animation
|
||||
function AnimatedSprite:forceNextAnimation(instant, state)
|
||||
instant = type(instant) == "nil" and true or instant
|
||||
local currentState = self.states[self.currentState]
|
||||
self.forcedState = state
|
||||
|
||||
if (instant) then
|
||||
self.forcedSwitchOnLoop = nil
|
||||
currentState.onAnimationEndEvent(self)
|
||||
if (currentState.name == self.currentState) then -- If state was not changed during the event then proceed
|
||||
if (type(self.forcedState) == "string") then
|
||||
self:changeState(self.forcedState)
|
||||
self.forcedState = nil
|
||||
elseif (currentState.nextAnimation) then
|
||||
self:changeState(currentState.nextAnimation)
|
||||
else
|
||||
self:stopAnimation()
|
||||
end
|
||||
end
|
||||
else
|
||||
self.forcedSwitchOnLoop = self._loopsFinished + 1
|
||||
end
|
||||
end
|
||||
|
||||
---Set default state
|
||||
---@param name string Name of an existing state
|
||||
function AnimatedSprite:setDefaultState(name)
|
||||
assert (self.states[name], "State name is nil.")
|
||||
self.defaultState = name
|
||||
end
|
||||
|
||||
---Print all states from this state machine to the console for debug purposes
|
||||
function AnimatedSprite:printAllStates()
|
||||
printTable(self.states)
|
||||
end
|
||||
|
||||
---Procees the animation to the next step without redrawing the sprite
|
||||
local function processAnimation(self)
|
||||
local state = self.states[self.currentState]
|
||||
|
||||
local function changeFrame(value)
|
||||
value += state.firstFrameIndex
|
||||
self._currentFrame = value
|
||||
state.onFrameChangedEvent(self)
|
||||
end
|
||||
|
||||
local reverse = state.reverse
|
||||
local frame = self._currentFrame - state.firstFrameIndex
|
||||
local framesCount = state.framesCount
|
||||
local frameStep = state.frameStep
|
||||
|
||||
if (self._currentFrame == 0) then -- true only after changing state
|
||||
self._currentFrame = state.animationStartingFrame + state.firstFrameIndex - 1
|
||||
if (framesCount == 1) then
|
||||
self._loopsFinished += 1
|
||||
state.onFrameChangedEvent(self)
|
||||
state.onLoopFinishedEvent(self)
|
||||
return
|
||||
else
|
||||
state.onFrameChangedEvent(self)
|
||||
end
|
||||
setImage(self)
|
||||
return
|
||||
end
|
||||
|
||||
if (framesCount == 1) then -- if this state is only 1 frame long
|
||||
self._loopsFinished += 1
|
||||
state.onFrameChangedEvent(self)
|
||||
state.onLoopFinishedEvent(self)
|
||||
return
|
||||
end
|
||||
|
||||
if (state.yoyo) then
|
||||
if (reverse ~= self._currentYoyoDirection) then
|
||||
if (frame + frameStep + 1 < framesCount) then
|
||||
changeFrame(frame + frameStep)
|
||||
else
|
||||
if (frame ~= framesCount - 1) then
|
||||
self._loopsFinished += 1
|
||||
changeFrame(2 * framesCount - frame - frameStep - 2)
|
||||
state.onLoopFinishedEvent(self)
|
||||
else
|
||||
changeFrame(2 * framesCount - frame - frameStep - 2)
|
||||
end
|
||||
self._currentYoyoDirection = not self._currentYoyoDirection
|
||||
end
|
||||
else
|
||||
if (frame - frameStep > 0) then
|
||||
changeFrame(frame - frameStep)
|
||||
else
|
||||
if (frame ~= 0) then
|
||||
self._loopsFinished += 1
|
||||
changeFrame(frameStep - frame)
|
||||
state.onLoopFinishedEvent(self)
|
||||
else
|
||||
changeFrame(frameStep - frame)
|
||||
end
|
||||
self._currentYoyoDirection = not self._currentYoyoDirection
|
||||
end
|
||||
end
|
||||
else
|
||||
if (reverse) then
|
||||
if (frame - frameStep > 0) then
|
||||
changeFrame(frame - frameStep)
|
||||
else
|
||||
if (frame ~= 0) then
|
||||
self._loopsFinished += 1
|
||||
changeFrame((frame - frameStep) % framesCount)
|
||||
state.onLoopFinishedEvent(self)
|
||||
else
|
||||
changeFrame((frame - frameStep) % framesCount)
|
||||
end
|
||||
end
|
||||
else
|
||||
if (frame + frameStep + 1 < framesCount) then
|
||||
changeFrame(frame + frameStep)
|
||||
else
|
||||
if (frame ~= framesCount - 1) then
|
||||
self._loopsFinished += 1
|
||||
changeFrame((frame + frameStep) % framesCount)
|
||||
state.onLoopFinishedEvent(self)
|
||||
else
|
||||
changeFrame((frame + frameStep) % framesCount)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
setImage(self)
|
||||
end
|
||||
|
||||
---Called by default in the `:update()` function.
|
||||
---Must be called once per frame if you overwrite `:update()`.
|
||||
---Invoke manually to move the animation to the next frame.
|
||||
function AnimatedSprite:updateAnimation()
|
||||
if (self._enabled) then
|
||||
self._ticks += 1
|
||||
if ((self._ticks - self._previousTicks) >= self.states[self.currentState].tickStep) then
|
||||
local state = self.states[self.currentState]
|
||||
local loop = state.loop
|
||||
local loopsFinished = self._loopsFinished
|
||||
if (type(loop) == "number" and loop <= loopsFinished or
|
||||
type(loop) == "boolean" and not loop and loopsFinished >= 1 or
|
||||
self.forcedSwitchOnLoop == loopsFinished) then
|
||||
self:forceNextAnimation(true)
|
||||
return
|
||||
end
|
||||
processAnimation(self)
|
||||
drawFrame(self)
|
||||
self._previousTicks += state.tickStep
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function AnimatedSprite:update()
|
||||
self:updateAnimation()
|
||||
end
|
55
source/main.lua
Normal file
55
source/main.lua
Normal file
@ -0,0 +1,55 @@
|
||||
-- CoreLibs
|
||||
import "CoreLibs/object"
|
||||
import "CoreLibs/graphics"
|
||||
import "CoreLibs/sprites"
|
||||
import "CoreLibs/timer"
|
||||
import "CoreLibs/crank"
|
||||
|
||||
local pd <const> = playdate
|
||||
local gfx <const> = pd.graphics
|
||||
|
||||
TAGS = {
|
||||
Pickup = 1,
|
||||
Player = 2,
|
||||
Hazard = 3
|
||||
}
|
||||
|
||||
Z_INDEXES = {
|
||||
Ground = 100,
|
||||
Pickup = 50,
|
||||
Player = 20
|
||||
}
|
||||
|
||||
local font = gfx.font.new('font/Mini Sans 2X')
|
||||
|
||||
gfx.setFont(font)
|
||||
|
||||
-- Libraries
|
||||
import "lib/AnimatedSprite"
|
||||
playdate.display.setRefreshRate(50)
|
||||
|
||||
-- Game
|
||||
import "level"
|
||||
|
||||
level = nil
|
||||
|
||||
local function initialize()
|
||||
-- Make it different, every time!
|
||||
math.randomseed(playdate.getSecondsSinceEpoch())
|
||||
|
||||
-- Init all the things!
|
||||
level = Level()
|
||||
playdate.resetElapsedTime()
|
||||
end
|
||||
|
||||
initialize()
|
||||
|
||||
function pd.update()
|
||||
gfx.sprite.update()
|
||||
pd.timer.updateTimers()
|
||||
pd.drawFPS(10,0)
|
||||
|
||||
if level then
|
||||
level.update()
|
||||
end
|
||||
end
|
0
source/menu.lua
Normal file
0
source/menu.lua
Normal file
6
source/pdxinfo
Normal file
6
source/pdxinfo
Normal file
@ -0,0 +1,6 @@
|
||||
name=FPVRun
|
||||
author=ut3usw
|
||||
description=My first game
|
||||
bundleID=com.ut3usw.fpv
|
||||
version=0.0.1
|
||||
buildNumber=1
|
189
source/player.lua
Normal file
189
source/player.lua
Normal file
@ -0,0 +1,189 @@
|
||||
local pd <const> = playdate
|
||||
local gfx <const> = playdate.graphics
|
||||
|
||||
class('Player').extends(AnimatedSprite)
|
||||
|
||||
function Player:init(x, y, gameManager)
|
||||
self.gameManager = gameManager
|
||||
|
||||
local playerImageTable = gfx.imagetable.new("sprites/player-table-48-48")
|
||||
Player.super.init(self, playerImageTable)
|
||||
|
||||
self:addState("run", 8,14, {tickStep = 2})
|
||||
self:addState("up", 1,7, {tickStep = 6})
|
||||
self:addState("down", 1,7, {tickStep = 6})
|
||||
self:addState("boom", 15,21, {tickStep = 10, loop = false})
|
||||
|
||||
self:setDefaultState("down")
|
||||
|
||||
self:playAnimation()
|
||||
|
||||
-- Sprite properties
|
||||
self:moveTo(x, y)
|
||||
self:setZIndex(10)
|
||||
self:setCollideRect(3, 19, 63, 33)
|
||||
|
||||
self:setTag(1)
|
||||
|
||||
self:changeState("down")
|
||||
|
||||
-- Physics properties
|
||||
self.xVelocity = 0
|
||||
self.yVelocity = 0
|
||||
self.maxXSpeed = 2
|
||||
self.maxYSpeed = 5
|
||||
|
||||
self.fallSpeed = 0.4
|
||||
|
||||
-- Player State
|
||||
self.touchingGround = false
|
||||
self.touchingCeiling = false
|
||||
self.touchingWall = false
|
||||
Player.dead = false
|
||||
|
||||
Player.bat = 10000
|
||||
|
||||
Player.dischargeRate = 1
|
||||
|
||||
self.cantDown = false
|
||||
|
||||
Player.moveRight = false
|
||||
end
|
||||
|
||||
function Player:handleGroundInput(state)
|
||||
if Player.bat <= 0 or Player.dead then
|
||||
return
|
||||
end
|
||||
|
||||
local change, acceleratedChange = playdate.getCrankChange()
|
||||
|
||||
if pd.buttonJustReleased(pd.kButtonLeft) or pd.buttonJustReleased(pd.kButtonRight) then
|
||||
self.cantDown = false
|
||||
end
|
||||
|
||||
if pd.buttonIsPressed(pd.kButtonLeft) then
|
||||
self:changeToRunState("left")
|
||||
self.cantDown = true
|
||||
Player.moveRight = false
|
||||
elseif pd.buttonIsPressed(pd.kButtonRight) then
|
||||
self:changeToRunState("right")
|
||||
self.cantDown = true
|
||||
Player.moveRight = true
|
||||
end
|
||||
|
||||
if change ~= 0 then
|
||||
local force = 0.01
|
||||
if change > 0 then
|
||||
self:changeState("up")
|
||||
else
|
||||
self:changeState("down")
|
||||
force = 0.05
|
||||
end
|
||||
self.yVelocity = self.yVelocity - acceleratedChange * force
|
||||
elseif self.cantDown == false then
|
||||
self.cantDown = false
|
||||
self:changeToDownState()
|
||||
else
|
||||
self.yVelocity = self.fallSpeed
|
||||
end
|
||||
end
|
||||
|
||||
function Player:changeToDownState()
|
||||
Player.moveRight = false
|
||||
self.yVelocity = self.fallSpeed
|
||||
self.xVelocity = 0
|
||||
self:changeState("down")
|
||||
end
|
||||
|
||||
function Player:changeToRunState(direction)
|
||||
if direction == "left" then
|
||||
self.xVelocity = -self.maxXSpeed
|
||||
self.globalFlip = 1
|
||||
self:changeState("run")
|
||||
elseif direction == "right" then
|
||||
self.xVelocity = self.maxXSpeed
|
||||
self.globalFlip = 0
|
||||
self:changeState("run")
|
||||
end
|
||||
end
|
||||
|
||||
function Player:handleMovementAndCollisions()
|
||||
local _, _, collisions, length = self:moveWithCollisions(self.x + self.xVelocity, self.y + self.yVelocity)
|
||||
|
||||
self.touchingGround = false
|
||||
self.touchingCeiling = false
|
||||
self.touchingWall = false
|
||||
|
||||
for i=1,length do
|
||||
local collision = collisions[i]
|
||||
local collisionType = collision.type
|
||||
local collisionObject = collision.other
|
||||
local collisionTag = collisionObject:getTag()
|
||||
if collisionType == gfx.sprite.kCollisionTypeSlide then
|
||||
if collision.normal.y == -1 then
|
||||
self.touchingGround = true
|
||||
elseif collision.normal.y == 1 then
|
||||
self.touchingCeiling = true
|
||||
end
|
||||
|
||||
if collision.normal.x ~= 0 then
|
||||
self.touchingWall = true
|
||||
end
|
||||
end
|
||||
|
||||
if collisionTag == 3 then
|
||||
Player.dead = true
|
||||
self:changeState("boom")
|
||||
-- elseif collisionTag == TAGS.Pickup then
|
||||
-- collisionObject:pickUp(self)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function Player:isMovingRight()
|
||||
return Player.moveRight
|
||||
end
|
||||
|
||||
function Player:isDead()
|
||||
return Player.dead
|
||||
end
|
||||
|
||||
function Player:getBat()
|
||||
return Player.bat
|
||||
end
|
||||
|
||||
function Player:handleDischarge(state)
|
||||
if Player.dead then
|
||||
return
|
||||
end
|
||||
if Player.bat <= 0 then
|
||||
Player.bat = 0
|
||||
self.fallSpeed = 2
|
||||
self:changeToDownState()
|
||||
end
|
||||
|
||||
if state == "run" then
|
||||
Player.dischargeRate = 5
|
||||
elseif state == "up" then
|
||||
Player.dischargeRate = 10
|
||||
else
|
||||
Player.dischargeRate = 1
|
||||
end
|
||||
|
||||
Player.bat = Player.bat - Player.dischargeRate
|
||||
end
|
||||
|
||||
function Player:update()
|
||||
self:updateAnimation()
|
||||
if Player.dead then
|
||||
return
|
||||
end
|
||||
|
||||
local state = self:getCurrentState()["name"]
|
||||
|
||||
self:handleDischarge(state)
|
||||
self:handleGroundInput(state)
|
||||
|
||||
self:handleMovementAndCollisions()
|
||||
end
|
BIN
source/sprites/bg.png
Normal file
BIN
source/sprites/bg.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.7 KiB |
BIN
source/sprites/groundDbg.png
Normal file
BIN
source/sprites/groundDbg.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.1 KiB |
BIN
source/sprites/groundFin.png
Normal file
BIN
source/sprites/groundFin.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.6 KiB |
BIN
source/sprites/player-table-64-64.png
Normal file
BIN
source/sprites/player-table-64-64.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.6 KiB |
Loading…
Reference in New Issue
Block a user