init
This commit is contained in:
parent
4b45c8fba1
commit
347403ad4d
16
.vscode/launch.json
vendored
Normal file
16
.vscode/launch.json
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"configurations": [
|
||||||
|
//run love in current dir
|
||||||
|
{
|
||||||
|
"name": "love",
|
||||||
|
"type": "f5anything",
|
||||||
|
"request": "launch",
|
||||||
|
"command": "love --console .",
|
||||||
|
"stopAtEntry": false,
|
||||||
|
"cwd": "${workspaceRoot}",
|
||||||
|
"env": {
|
||||||
|
"PATH": "${env:PATH}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
BIN
assets/images/backgrounds/bg0.png
Normal file
BIN
assets/images/backgrounds/bg0.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 48 KiB |
BIN
assets/images/spritesheets/player.gif
Normal file
BIN
assets/images/spritesheets/player.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 94 KiB |
519
assets/images/spritesheets/player.json
Normal file
519
assets/images/spritesheets/player.json
Normal file
@ -0,0 +1,519 @@
|
|||||||
|
{ "frames": [
|
||||||
|
{
|
||||||
|
"filename": "Sprite-0002 0.aseprite",
|
||||||
|
"frame": { "x": 140, "y": 0, "w": 140, "h": 160 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 10, "y": 0, "w": 140, "h": 160 },
|
||||||
|
"sourceSize": { "w": 160, "h": 160 },
|
||||||
|
"duration": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "Sprite-0002 1.aseprite",
|
||||||
|
"frame": { "x": 0, "y": 0, "w": 140, "h": 160 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 10, "y": 0, "w": 140, "h": 160 },
|
||||||
|
"sourceSize": { "w": 160, "h": 160 },
|
||||||
|
"duration": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "Sprite-0002 2.aseprite",
|
||||||
|
"frame": { "x": 140, "y": 320, "w": 140, "h": 160 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 10, "y": 0, "w": 140, "h": 160 },
|
||||||
|
"sourceSize": { "w": 160, "h": 160 },
|
||||||
|
"duration": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "Sprite-0002 3.aseprite",
|
||||||
|
"frame": { "x": 0, "y": 320, "w": 140, "h": 160 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 10, "y": 0, "w": 140, "h": 160 },
|
||||||
|
"sourceSize": { "w": 160, "h": 160 },
|
||||||
|
"duration": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "Sprite-0002 4.aseprite",
|
||||||
|
"frame": { "x": 840, "y": 160, "w": 140, "h": 160 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 10, "y": 0, "w": 140, "h": 160 },
|
||||||
|
"sourceSize": { "w": 160, "h": 160 },
|
||||||
|
"duration": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "Sprite-0002 5.aseprite",
|
||||||
|
"frame": { "x": 700, "y": 160, "w": 140, "h": 160 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 10, "y": 0, "w": 140, "h": 160 },
|
||||||
|
"sourceSize": { "w": 160, "h": 160 },
|
||||||
|
"duration": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "Sprite-0002 6.aseprite",
|
||||||
|
"frame": { "x": 560, "y": 160, "w": 140, "h": 160 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 10, "y": 0, "w": 140, "h": 160 },
|
||||||
|
"sourceSize": { "w": 160, "h": 160 },
|
||||||
|
"duration": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "Sprite-0002 7.aseprite",
|
||||||
|
"frame": { "x": 420, "y": 160, "w": 140, "h": 160 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 10, "y": 0, "w": 140, "h": 160 },
|
||||||
|
"sourceSize": { "w": 160, "h": 160 },
|
||||||
|
"duration": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "Sprite-0002 8.aseprite",
|
||||||
|
"frame": { "x": 280, "y": 160, "w": 140, "h": 160 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 10, "y": 0, "w": 140, "h": 160 },
|
||||||
|
"sourceSize": { "w": 160, "h": 160 },
|
||||||
|
"duration": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "Sprite-0002 9.aseprite",
|
||||||
|
"frame": { "x": 140, "y": 160, "w": 140, "h": 160 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 10, "y": 0, "w": 140, "h": 160 },
|
||||||
|
"sourceSize": { "w": 160, "h": 160 },
|
||||||
|
"duration": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "Sprite-0002 10.aseprite",
|
||||||
|
"frame": { "x": 280, "y": 320, "w": 140, "h": 150 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 10, "y": 0, "w": 140, "h": 150 },
|
||||||
|
"sourceSize": { "w": 160, "h": 160 },
|
||||||
|
"duration": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "Sprite-0002 11.aseprite",
|
||||||
|
"frame": { "x": 420, "y": 320, "w": 140, "h": 150 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 10, "y": 0, "w": 140, "h": 150 },
|
||||||
|
"sourceSize": { "w": 160, "h": 160 },
|
||||||
|
"duration": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "Sprite-0002 12.aseprite",
|
||||||
|
"frame": { "x": 0, "y": 620, "w": 140, "h": 140 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 10, "y": 0, "w": 140, "h": 140 },
|
||||||
|
"sourceSize": { "w": 160, "h": 160 },
|
||||||
|
"duration": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "Sprite-0002 13.aseprite",
|
||||||
|
"frame": { "x": 840, "y": 610, "w": 140, "h": 140 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 10, "y": 0, "w": 140, "h": 140 },
|
||||||
|
"sourceSize": { "w": 160, "h": 160 },
|
||||||
|
"duration": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "Sprite-0002 14.aseprite",
|
||||||
|
"frame": { "x": 700, "y": 610, "w": 140, "h": 140 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 10, "y": 0, "w": 140, "h": 140 },
|
||||||
|
"sourceSize": { "w": 160, "h": 160 },
|
||||||
|
"duration": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "Sprite-0002 15.aseprite",
|
||||||
|
"frame": { "x": 560, "y": 610, "w": 140, "h": 140 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 10, "y": 0, "w": 140, "h": 140 },
|
||||||
|
"sourceSize": { "w": 160, "h": 160 },
|
||||||
|
"duration": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "Sprite-0002 16.aseprite",
|
||||||
|
"frame": { "x": 420, "y": 610, "w": 140, "h": 140 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 10, "y": 0, "w": 140, "h": 140 },
|
||||||
|
"sourceSize": { "w": 160, "h": 160 },
|
||||||
|
"duration": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "Sprite-0002 17.aseprite",
|
||||||
|
"frame": { "x": 420, "y": 610, "w": 140, "h": 140 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 10, "y": 0, "w": 140, "h": 140 },
|
||||||
|
"sourceSize": { "w": 160, "h": 160 },
|
||||||
|
"duration": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "Sprite-0002 18.aseprite",
|
||||||
|
"frame": { "x": 280, "y": 610, "w": 140, "h": 140 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 10, "y": 0, "w": 140, "h": 140 },
|
||||||
|
"sourceSize": { "w": 160, "h": 160 },
|
||||||
|
"duration": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "Sprite-0002 19.aseprite",
|
||||||
|
"frame": { "x": 140, "y": 480, "w": 140, "h": 140 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 10, "y": 0, "w": 140, "h": 140 },
|
||||||
|
"sourceSize": { "w": 160, "h": 160 },
|
||||||
|
"duration": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "Sprite-0002 20.aseprite",
|
||||||
|
"frame": { "x": 0, "y": 480, "w": 140, "h": 140 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 10, "y": 0, "w": 140, "h": 140 },
|
||||||
|
"sourceSize": { "w": 160, "h": 160 },
|
||||||
|
"duration": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "Sprite-0002 21.aseprite",
|
||||||
|
"frame": { "x": 840, "y": 470, "w": 140, "h": 140 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 10, "y": 0, "w": 140, "h": 140 },
|
||||||
|
"sourceSize": { "w": 160, "h": 160 },
|
||||||
|
"duration": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "Sprite-0002 22.aseprite",
|
||||||
|
"frame": { "x": 700, "y": 470, "w": 140, "h": 140 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 10, "y": 0, "w": 140, "h": 140 },
|
||||||
|
"sourceSize": { "w": 160, "h": 160 },
|
||||||
|
"duration": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "Sprite-0002 23.aseprite",
|
||||||
|
"frame": { "x": 560, "y": 470, "w": 140, "h": 140 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 10, "y": 0, "w": 140, "h": 140 },
|
||||||
|
"sourceSize": { "w": 160, "h": 160 },
|
||||||
|
"duration": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "Sprite-0002 24.aseprite",
|
||||||
|
"frame": { "x": 140, "y": 620, "w": 140, "h": 140 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 10, "y": 0, "w": 140, "h": 140 },
|
||||||
|
"sourceSize": { "w": 160, "h": 160 },
|
||||||
|
"duration": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "Sprite-0002 25.aseprite",
|
||||||
|
"frame": { "x": 420, "y": 470, "w": 140, "h": 140 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 10, "y": 0, "w": 140, "h": 140 },
|
||||||
|
"sourceSize": { "w": 160, "h": 160 },
|
||||||
|
"duration": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "Sprite-0002 26.aseprite",
|
||||||
|
"frame": { "x": 840, "y": 320, "w": 140, "h": 150 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 10, "y": 0, "w": 140, "h": 150 },
|
||||||
|
"sourceSize": { "w": 160, "h": 160 },
|
||||||
|
"duration": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "Sprite-0002 27.aseprite",
|
||||||
|
"frame": { "x": 700, "y": 320, "w": 140, "h": 150 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 10, "y": 0, "w": 140, "h": 150 },
|
||||||
|
"sourceSize": { "w": 160, "h": 160 },
|
||||||
|
"duration": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "Sprite-0002 28.aseprite",
|
||||||
|
"frame": { "x": 280, "y": 160, "w": 140, "h": 160 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 10, "y": 0, "w": 140, "h": 160 },
|
||||||
|
"sourceSize": { "w": 160, "h": 160 },
|
||||||
|
"duration": 500
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "Sprite-0002 29.aseprite",
|
||||||
|
"frame": { "x": 280, "y": 0, "w": 140, "h": 160 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 10, "y": 0, "w": 140, "h": 160 },
|
||||||
|
"sourceSize": { "w": 160, "h": 160 },
|
||||||
|
"duration": 500
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "Sprite-0002 30.aseprite",
|
||||||
|
"frame": { "x": 420, "y": 0, "w": 140, "h": 160 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 10, "y": 0, "w": 140, "h": 160 },
|
||||||
|
"sourceSize": { "w": 160, "h": 160 },
|
||||||
|
"duration": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "Sprite-0002 31.aseprite",
|
||||||
|
"frame": { "x": 560, "y": 0, "w": 140, "h": 160 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 10, "y": 0, "w": 140, "h": 160 },
|
||||||
|
"sourceSize": { "w": 160, "h": 160 },
|
||||||
|
"duration": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "Sprite-0002 32.aseprite",
|
||||||
|
"frame": { "x": 700, "y": 0, "w": 140, "h": 160 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 10, "y": 0, "w": 140, "h": 160 },
|
||||||
|
"sourceSize": { "w": 160, "h": 160 },
|
||||||
|
"duration": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "Sprite-0002 33.aseprite",
|
||||||
|
"frame": { "x": 420, "y": 0, "w": 140, "h": 160 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 10, "y": 0, "w": 140, "h": 160 },
|
||||||
|
"sourceSize": { "w": 160, "h": 160 },
|
||||||
|
"duration": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "Sprite-0002 34.aseprite",
|
||||||
|
"frame": { "x": 420, "y": 0, "w": 140, "h": 160 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 10, "y": 0, "w": 140, "h": 160 },
|
||||||
|
"sourceSize": { "w": 160, "h": 160 },
|
||||||
|
"duration": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "Sprite-0002 35.aseprite",
|
||||||
|
"frame": { "x": 840, "y": 0, "w": 140, "h": 160 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 10, "y": 0, "w": 140, "h": 160 },
|
||||||
|
"sourceSize": { "w": 160, "h": 160 },
|
||||||
|
"duration": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "Sprite-0002 36.aseprite",
|
||||||
|
"frame": { "x": 0, "y": 160, "w": 140, "h": 160 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 10, "y": 0, "w": 140, "h": 160 },
|
||||||
|
"sourceSize": { "w": 160, "h": 160 },
|
||||||
|
"duration": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "Sprite-0002 37.aseprite",
|
||||||
|
"frame": { "x": 420, "y": 0, "w": 140, "h": 160 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 10, "y": 0, "w": 140, "h": 160 },
|
||||||
|
"sourceSize": { "w": 160, "h": 160 },
|
||||||
|
"duration": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "Sprite-0002 38.aseprite",
|
||||||
|
"frame": { "x": 280, "y": 0, "w": 140, "h": 160 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 10, "y": 0, "w": 140, "h": 160 },
|
||||||
|
"sourceSize": { "w": 160, "h": 160 },
|
||||||
|
"duration": 65535
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "Sprite-0002 39.aseprite",
|
||||||
|
"frame": { "x": 280, "y": 0, "w": 140, "h": 160 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 10, "y": 0, "w": 140, "h": 160 },
|
||||||
|
"sourceSize": { "w": 160, "h": 160 },
|
||||||
|
"duration": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "Sprite-0002 40.aseprite",
|
||||||
|
"frame": { "x": 560, "y": 320, "w": 140, "h": 150 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 10, "y": 10, "w": 140, "h": 150 },
|
||||||
|
"sourceSize": { "w": 160, "h": 160 },
|
||||||
|
"duration": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "Sprite-0002 41.aseprite",
|
||||||
|
"frame": { "x": 280, "y": 470, "w": 140, "h": 140 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 10, "y": 20, "w": 140, "h": 140 },
|
||||||
|
"sourceSize": { "w": 160, "h": 160 },
|
||||||
|
"duration": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "Sprite-0002 42.aseprite",
|
||||||
|
"frame": { "x": 280, "y": 750, "w": 140, "h": 130 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 10, "y": 30, "w": 140, "h": 130 },
|
||||||
|
"sourceSize": { "w": 160, "h": 160 },
|
||||||
|
"duration": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "Sprite-0002 43.aseprite",
|
||||||
|
"frame": { "x": 420, "y": 750, "w": 140, "h": 120 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 10, "y": 40, "w": 140, "h": 120 },
|
||||||
|
"sourceSize": { "w": 160, "h": 160 },
|
||||||
|
"duration": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "Sprite-0002 44.aseprite",
|
||||||
|
"frame": { "x": 560, "y": 750, "w": 140, "h": 110 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 10, "y": 50, "w": 140, "h": 110 },
|
||||||
|
"sourceSize": { "w": 160, "h": 160 },
|
||||||
|
"duration": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "Sprite-0002 45.aseprite",
|
||||||
|
"frame": { "x": 700, "y": 750, "w": 140, "h": 100 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 10, "y": 60, "w": 140, "h": 100 },
|
||||||
|
"sourceSize": { "w": 160, "h": 160 },
|
||||||
|
"duration": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "Sprite-0002 46.aseprite",
|
||||||
|
"frame": { "x": 840, "y": 750, "w": 140, "h": 90 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 10, "y": 70, "w": 140, "h": 90 },
|
||||||
|
"sourceSize": { "w": 160, "h": 160 },
|
||||||
|
"duration": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "Sprite-0002 47.aseprite",
|
||||||
|
"frame": { "x": 0, "y": 760, "w": 140, "h": 80 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 10, "y": 80, "w": 140, "h": 80 },
|
||||||
|
"sourceSize": { "w": 160, "h": 160 },
|
||||||
|
"duration": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "Sprite-0002 48.aseprite",
|
||||||
|
"frame": { "x": 140, "y": 760, "w": 140, "h": 70 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 10, "y": 90, "w": 140, "h": 70 },
|
||||||
|
"sourceSize": { "w": 160, "h": 160 },
|
||||||
|
"duration": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "Sprite-0002 49.aseprite",
|
||||||
|
"frame": { "x": 140, "y": 830, "w": 120, "h": 60 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 20, "y": 100, "w": 120, "h": 60 },
|
||||||
|
"sourceSize": { "w": 160, "h": 160 },
|
||||||
|
"duration": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "Sprite-0002 50.aseprite",
|
||||||
|
"frame": { "x": 0, "y": 840, "w": 100, "h": 50 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 30, "y": 110, "w": 100, "h": 50 },
|
||||||
|
"sourceSize": { "w": 160, "h": 160 },
|
||||||
|
"duration": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "Sprite-0002 51.aseprite",
|
||||||
|
"frame": { "x": 840, "y": 840, "w": 100, "h": 40 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 30, "y": 120, "w": 100, "h": 40 },
|
||||||
|
"sourceSize": { "w": 160, "h": 160 },
|
||||||
|
"duration": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "Sprite-0002 52.aseprite",
|
||||||
|
"frame": { "x": 700, "y": 850, "w": 60, "h": 30 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 50, "y": 130, "w": 60, "h": 30 },
|
||||||
|
"sourceSize": { "w": 160, "h": 160 },
|
||||||
|
"duration": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "Sprite-0002 53.aseprite",
|
||||||
|
"frame": { "x": 760, "y": 850, "w": 60, "h": 20 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 50, "y": 140, "w": 60, "h": 20 },
|
||||||
|
"sourceSize": { "w": 160, "h": 160 },
|
||||||
|
"duration": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "Sprite-0002 54.aseprite",
|
||||||
|
"frame": { "x": 560, "y": 860, "w": 60, "h": 10 },
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"spriteSourceSize": { "x": 50, "y": 150, "w": 60, "h": 10 },
|
||||||
|
"sourceSize": { "w": 160, "h": 160 },
|
||||||
|
"duration": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"meta": {
|
||||||
|
"app": "http://www.aseprite.org/",
|
||||||
|
"version": "1.x-dev",
|
||||||
|
"image": "player.png",
|
||||||
|
"format": "RGBA8888",
|
||||||
|
"size": { "w": 980, "h": 890 },
|
||||||
|
"scale": "1",
|
||||||
|
"frameTags": [
|
||||||
|
{ "name": "Jump", "from": 0, "to": 27, "direction": "forward" },
|
||||||
|
{ "name": "Idle", "from": 28, "to": 29, "direction": "forward" },
|
||||||
|
{ "name": "Walk_left", "from": 30, "to": 33, "direction": "forward" },
|
||||||
|
{ "name": "Walk_right", "from": 34, "to": 37, "direction": "forward" },
|
||||||
|
{ "name": "Default", "from": 38, "to": 38, "direction": "forward" },
|
||||||
|
{ "name": "Die", "from": 39, "to": 54, "direction": "forward" }
|
||||||
|
],
|
||||||
|
"layers": [
|
||||||
|
{ "name": "Layer 1", "opacity": 255, "blendMode": "normal" }
|
||||||
|
],
|
||||||
|
"slices": [
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
BIN
assets/images/spritesheets/player.png
Normal file
BIN
assets/images/spritesheets/player.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
BIN
assets/sounds/effects/coin.ogg
Normal file
BIN
assets/sounds/effects/coin.ogg
Normal file
Binary file not shown.
BIN
assets/sounds/effects/die.ogg
Normal file
BIN
assets/sounds/effects/die.ogg
Normal file
Binary file not shown.
BIN
assets/sounds/effects/explosion.ogg
Normal file
BIN
assets/sounds/effects/explosion.ogg
Normal file
Binary file not shown.
BIN
assets/sounds/effects/jump.ogg
Normal file
BIN
assets/sounds/effects/jump.ogg
Normal file
Binary file not shown.
BIN
assets/sounds/effects/powerup.ogg
Normal file
BIN
assets/sounds/effects/powerup.ogg
Normal file
Binary file not shown.
BIN
assets/sounds/effects/revive.ogg
Normal file
BIN
assets/sounds/effects/revive.ogg
Normal file
Binary file not shown.
BIN
assets/sounds/effects/select.ogg
Normal file
BIN
assets/sounds/effects/select.ogg
Normal file
Binary file not shown.
BIN
assets/sounds/effects/shoot.ogg
Normal file
BIN
assets/sounds/effects/shoot.ogg
Normal file
Binary file not shown.
BIN
assets/sounds/music/track0.ogg
Normal file
BIN
assets/sounds/music/track0.ogg
Normal file
Binary file not shown.
BIN
assets/sounds/music/track1.ogg
Normal file
BIN
assets/sounds/music/track1.ogg
Normal file
Binary file not shown.
BIN
assets/sounds/music/track10.ogg
Normal file
BIN
assets/sounds/music/track10.ogg
Normal file
Binary file not shown.
BIN
assets/sounds/music/track11.ogg
Normal file
BIN
assets/sounds/music/track11.ogg
Normal file
Binary file not shown.
BIN
assets/sounds/music/track2.ogg
Normal file
BIN
assets/sounds/music/track2.ogg
Normal file
Binary file not shown.
BIN
assets/sounds/music/track3.ogg
Normal file
BIN
assets/sounds/music/track3.ogg
Normal file
Binary file not shown.
BIN
assets/sounds/music/track4.ogg
Normal file
BIN
assets/sounds/music/track4.ogg
Normal file
Binary file not shown.
BIN
assets/sounds/music/track5.ogg
Normal file
BIN
assets/sounds/music/track5.ogg
Normal file
Binary file not shown.
BIN
assets/sounds/music/track6.ogg
Normal file
BIN
assets/sounds/music/track6.ogg
Normal file
Binary file not shown.
BIN
assets/sounds/music/track7.ogg
Normal file
BIN
assets/sounds/music/track7.ogg
Normal file
Binary file not shown.
BIN
assets/sounds/music/track8.ogg
Normal file
BIN
assets/sounds/music/track8.ogg
Normal file
Binary file not shown.
BIN
assets/sounds/music/track9.ogg
Normal file
BIN
assets/sounds/music/track9.ogg
Normal file
Binary file not shown.
51
conf.lua
Normal file
51
conf.lua
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
function love.conf(t)
|
||||||
|
t.identity = nil -- The name of the save directory (string)
|
||||||
|
t.appendidentity = false -- Search files in source directory before save directory (boolean)
|
||||||
|
t.version = "11.4" -- The LÖVE version this game was made for (string)
|
||||||
|
t.console = true -- Attach a console (boolean, Windows only)
|
||||||
|
t.accelerometerjoystick = true -- Enable the accelerometer on iOS and Android by exposing it as a Joystick (boolean)
|
||||||
|
t.externalstorage = false -- True to save files (and read from the save directory) in external storage on Android (boolean)
|
||||||
|
t.gammacorrect = true -- Enable gamma-correct rendering, when supported by the system (boolean)
|
||||||
|
|
||||||
|
t.audio.mic = false -- Request and use microphone capabilities in Android (boolean)
|
||||||
|
t.audio.mixwithsystem = true -- Keep background music playing when opening LOVE (boolean, iOS and Android only)
|
||||||
|
|
||||||
|
t.window.title = "Weeeeeeeee" -- The window title (string)
|
||||||
|
t.window.icon = nil -- Filepath to an image to use as the window's icon (string)
|
||||||
|
t.window.width = 1024 -- The window width (number)
|
||||||
|
t.window.height = 768 -- The window height (number)
|
||||||
|
t.window.borderless = false -- Remove all border visuals from the window (boolean)
|
||||||
|
t.window.resizable = false -- Let the window be user-resizable (boolean)
|
||||||
|
t.window.minwidth = 1 -- Minimum window width if the window is resizable (number)
|
||||||
|
t.window.minheight = 1 -- Minimum window height if the window is resizable (number)
|
||||||
|
t.window.fullscreen = false -- Enable fullscreen (boolean)
|
||||||
|
t.window.fullscreentype = "desktop" -- Choose between "desktop" fullscreen or "exclusive" fullscreen mode (string)
|
||||||
|
t.window.vsync = 1 -- Vertical sync mode (number)
|
||||||
|
t.window.msaa = 0 -- The number of samples to use with multi-sampled antialiasing (number)
|
||||||
|
t.window.depth = nil -- The number of bits per sample in the depth buffer
|
||||||
|
t.window.stencil = nil -- The number of bits per sample in the stencil buffer
|
||||||
|
t.window.display = 1 -- Index of the monitor to show the window in (number)
|
||||||
|
t.window.highdpi = false -- Enable high-dpi mode for the window on a Retina display (boolean)
|
||||||
|
t.window.usedpiscale = true -- Enable automatic DPI scaling when highdpi is set to true as well (boolean)
|
||||||
|
t.window.x = nil -- The x-coordinate of the window's position in the specified display (number)
|
||||||
|
t.window.y = nil -- The y-coordinate of the window's position in the specified display (number)
|
||||||
|
|
||||||
|
t.modules.audio = true -- Enable the audio module (boolean)
|
||||||
|
t.modules.data = true -- Enable the data module (boolean)
|
||||||
|
t.modules.event = true -- Enable the event module (boolean)
|
||||||
|
t.modules.font = true -- Enable the font module (boolean)
|
||||||
|
t.modules.graphics = true -- Enable the graphics module (boolean)
|
||||||
|
t.modules.image = true -- Enable the image module (boolean)
|
||||||
|
t.modules.joystick = true -- Enable the joystick module (boolean)
|
||||||
|
t.modules.keyboard = true -- Enable the keyboard module (boolean)
|
||||||
|
t.modules.math = true -- Enable the math module (boolean)
|
||||||
|
t.modules.mouse = true -- Enable the mouse module (boolean)
|
||||||
|
t.modules.physics = true -- Enable the physics module (boolean)
|
||||||
|
t.modules.sound = true -- Enable the sound module (boolean)
|
||||||
|
t.modules.system = true -- Enable the system module (boolean)
|
||||||
|
t.modules.thread = true -- Enable the thread module (boolean)
|
||||||
|
t.modules.timer = true -- Enable the timer module (boolean), Disabling it will result 0 delta time in love.update
|
||||||
|
t.modules.touch = true -- Enable the touch module (boolean)
|
||||||
|
t.modules.video = true -- Enable the video module (boolean)
|
||||||
|
t.modules.window = true -- Enable the window module (boolean)
|
||||||
|
end
|
106
libs/cron.lua
Normal file
106
libs/cron.lua
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
local cron = {
|
||||||
|
__VERSION = 'cron.lua 2.0.0',
|
||||||
|
__DESCRIPTION = 'Time-related functions for lua',
|
||||||
|
__URL = 'https://github.com/kikito/cron.lua',
|
||||||
|
__LICENSE = [[
|
||||||
|
Copyright (c) 2011 Enrique García Cota
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included
|
||||||
|
in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
]]
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Private functions
|
||||||
|
|
||||||
|
local function isCallable(callback)
|
||||||
|
local tc = type(callback)
|
||||||
|
if tc == 'function' then return true end
|
||||||
|
if tc == 'table' then
|
||||||
|
local mt = getmetatable(callback)
|
||||||
|
return type(mt) == 'table' and type(mt.__call) == 'function'
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local function checkPositiveInteger(name, value)
|
||||||
|
if type(value) ~= "number" or value < 0 then
|
||||||
|
error(name .. " must be a positive number")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local Clock = {}
|
||||||
|
local Clock_mt = {__index = Clock}
|
||||||
|
|
||||||
|
local function newClock(time, callback, update, ...)
|
||||||
|
checkPositiveInteger('time', time)
|
||||||
|
assert(isCallable(callback), "callback must be a function")
|
||||||
|
|
||||||
|
return setmetatable({
|
||||||
|
time = time,
|
||||||
|
callback = callback,
|
||||||
|
args = {...},
|
||||||
|
running = 0,
|
||||||
|
update = update
|
||||||
|
}, Clock_mt)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function updateAfterClock(self, dt) -- returns true if expired
|
||||||
|
checkPositiveInteger('dt', dt)
|
||||||
|
|
||||||
|
if self.running >= self.time then return true end
|
||||||
|
|
||||||
|
self.running = self.running + dt
|
||||||
|
|
||||||
|
if self.running >= self.time then
|
||||||
|
self.callback(unpack(self.args))
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local function updateEveryClock(self, dt)
|
||||||
|
checkPositiveInteger('dt', dt)
|
||||||
|
|
||||||
|
self.running = self.running + dt
|
||||||
|
|
||||||
|
while self.running >= self.time do
|
||||||
|
self.callback(unpack(self.args))
|
||||||
|
self.running = self.running - self.time
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function Clock:reset(running)
|
||||||
|
running = running or 0
|
||||||
|
checkPositiveInteger('running', running)
|
||||||
|
|
||||||
|
self.running = running
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function cron.after(time, callback, ...)
|
||||||
|
return newClock(time, callback, updateAfterClock, ...)
|
||||||
|
end
|
||||||
|
|
||||||
|
function cron.every(time, callback, ...)
|
||||||
|
return newClock(time, callback, updateEveryClock, ...)
|
||||||
|
end
|
||||||
|
|
||||||
|
return cron
|
||||||
|
|
400
libs/json.lua
Normal file
400
libs/json.lua
Normal file
@ -0,0 +1,400 @@
|
|||||||
|
--
|
||||||
|
-- json.lua
|
||||||
|
--
|
||||||
|
-- Copyright (c) 2018 rxi
|
||||||
|
--
|
||||||
|
-- Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
-- this software and associated documentation files (the "Software"), to deal in
|
||||||
|
-- the Software without restriction, including without limitation the rights to
|
||||||
|
-- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
-- of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
|
-- so, subject to the following conditions:
|
||||||
|
--
|
||||||
|
-- The above copyright notice and this permission notice shall be included in all
|
||||||
|
-- copies or substantial portions of the Software.
|
||||||
|
--
|
||||||
|
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
-- SOFTWARE.
|
||||||
|
--
|
||||||
|
|
||||||
|
local json = { _version = "0.1.1" }
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
-- Encode
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
local encode
|
||||||
|
|
||||||
|
local escape_char_map = {
|
||||||
|
[ "\\" ] = "\\\\",
|
||||||
|
[ "\"" ] = "\\\"",
|
||||||
|
[ "\b" ] = "\\b",
|
||||||
|
[ "\f" ] = "\\f",
|
||||||
|
[ "\n" ] = "\\n",
|
||||||
|
[ "\r" ] = "\\r",
|
||||||
|
[ "\t" ] = "\\t",
|
||||||
|
}
|
||||||
|
|
||||||
|
local escape_char_map_inv = { [ "\\/" ] = "/" }
|
||||||
|
for k, v in pairs(escape_char_map) do
|
||||||
|
escape_char_map_inv[v] = k
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function escape_char(c)
|
||||||
|
return escape_char_map[c] or string.format("\\u%04x", c:byte())
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function encode_nil(val)
|
||||||
|
return "null"
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function encode_table(val, stack)
|
||||||
|
local res = {}
|
||||||
|
stack = stack or {}
|
||||||
|
|
||||||
|
-- Circular reference?
|
||||||
|
if stack[val] then error("circular reference") end
|
||||||
|
|
||||||
|
stack[val] = true
|
||||||
|
|
||||||
|
if val[1] ~= nil or next(val) == nil then
|
||||||
|
-- Treat as array -- check keys are valid and it is not sparse
|
||||||
|
local n = 0
|
||||||
|
for k in pairs(val) do
|
||||||
|
if type(k) ~= "number" then
|
||||||
|
error("invalid table: mixed or invalid key types")
|
||||||
|
end
|
||||||
|
n = n + 1
|
||||||
|
end
|
||||||
|
if n ~= #val then
|
||||||
|
error("invalid table: sparse array")
|
||||||
|
end
|
||||||
|
-- Encode
|
||||||
|
for i, v in ipairs(val) do
|
||||||
|
table.insert(res, encode(v, stack))
|
||||||
|
end
|
||||||
|
stack[val] = nil
|
||||||
|
return "[" .. table.concat(res, ",") .. "]"
|
||||||
|
|
||||||
|
else
|
||||||
|
-- Treat as an object
|
||||||
|
for k, v in pairs(val) do
|
||||||
|
if type(k) ~= "string" then
|
||||||
|
error("invalid table: mixed or invalid key types")
|
||||||
|
end
|
||||||
|
table.insert(res, encode(k, stack) .. ":" .. encode(v, stack))
|
||||||
|
end
|
||||||
|
stack[val] = nil
|
||||||
|
return "{" .. table.concat(res, ",") .. "}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function encode_string(val)
|
||||||
|
return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"'
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function encode_number(val)
|
||||||
|
-- Check for NaN, -inf and inf
|
||||||
|
if val ~= val or val <= -math.huge or val >= math.huge then
|
||||||
|
error("unexpected number value '" .. tostring(val) .. "'")
|
||||||
|
end
|
||||||
|
return string.format("%.14g", val)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local type_func_map = {
|
||||||
|
[ "nil" ] = encode_nil,
|
||||||
|
[ "table" ] = encode_table,
|
||||||
|
[ "string" ] = encode_string,
|
||||||
|
[ "number" ] = encode_number,
|
||||||
|
[ "boolean" ] = tostring,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
encode = function(val, stack)
|
||||||
|
local t = type(val)
|
||||||
|
local f = type_func_map[t]
|
||||||
|
if f then
|
||||||
|
return f(val, stack)
|
||||||
|
end
|
||||||
|
error("unexpected type '" .. t .. "'")
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function json.encode(val)
|
||||||
|
return ( encode(val) )
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
-- Decode
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
local parse
|
||||||
|
|
||||||
|
local function create_set(...)
|
||||||
|
local res = {}
|
||||||
|
for i = 1, select("#", ...) do
|
||||||
|
res[ select(i, ...) ] = true
|
||||||
|
end
|
||||||
|
return res
|
||||||
|
end
|
||||||
|
|
||||||
|
local space_chars = create_set(" ", "\t", "\r", "\n")
|
||||||
|
local delim_chars = create_set(" ", "\t", "\r", "\n", "]", "}", ",")
|
||||||
|
local escape_chars = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u")
|
||||||
|
local literals = create_set("true", "false", "null")
|
||||||
|
|
||||||
|
local literal_map = {
|
||||||
|
[ "true" ] = true,
|
||||||
|
[ "false" ] = false,
|
||||||
|
[ "null" ] = nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
local function next_char(str, idx, set, negate)
|
||||||
|
for i = idx, #str do
|
||||||
|
if set[str:sub(i, i)] ~= negate then
|
||||||
|
return i
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return #str + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function decode_error(str, idx, msg)
|
||||||
|
local line_count = 1
|
||||||
|
local col_count = 1
|
||||||
|
for i = 1, idx - 1 do
|
||||||
|
col_count = col_count + 1
|
||||||
|
if str:sub(i, i) == "\n" then
|
||||||
|
line_count = line_count + 1
|
||||||
|
col_count = 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
error( string.format("%s at line %d col %d", msg, line_count, col_count) )
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function codepoint_to_utf8(n)
|
||||||
|
-- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa
|
||||||
|
local f = math.floor
|
||||||
|
if n <= 0x7f then
|
||||||
|
return string.char(n)
|
||||||
|
elseif n <= 0x7ff then
|
||||||
|
return string.char(f(n / 64) + 192, n % 64 + 128)
|
||||||
|
elseif n <= 0xffff then
|
||||||
|
return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128)
|
||||||
|
elseif n <= 0x10ffff then
|
||||||
|
return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128,
|
||||||
|
f(n % 4096 / 64) + 128, n % 64 + 128)
|
||||||
|
end
|
||||||
|
error( string.format("invalid unicode codepoint '%x'", n) )
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function parse_unicode_escape(s)
|
||||||
|
local n1 = tonumber( s:sub(3, 6), 16 )
|
||||||
|
local n2 = tonumber( s:sub(9, 12), 16 )
|
||||||
|
-- Surrogate pair?
|
||||||
|
if n2 then
|
||||||
|
return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000)
|
||||||
|
else
|
||||||
|
return codepoint_to_utf8(n1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function parse_string(str, i)
|
||||||
|
local has_unicode_escape = false
|
||||||
|
local has_surrogate_escape = false
|
||||||
|
local has_escape = false
|
||||||
|
local last
|
||||||
|
for j = i + 1, #str do
|
||||||
|
local x = str:byte(j)
|
||||||
|
|
||||||
|
if x < 32 then
|
||||||
|
decode_error(str, j, "control character in string")
|
||||||
|
end
|
||||||
|
|
||||||
|
if last == 92 then -- "\\" (escape char)
|
||||||
|
if x == 117 then -- "u" (unicode escape sequence)
|
||||||
|
local hex = str:sub(j + 1, j + 5)
|
||||||
|
if not hex:find("%x%x%x%x") then
|
||||||
|
decode_error(str, j, "invalid unicode escape in string")
|
||||||
|
end
|
||||||
|
if hex:find("^[dD][89aAbB]") then
|
||||||
|
has_surrogate_escape = true
|
||||||
|
else
|
||||||
|
has_unicode_escape = true
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local c = string.char(x)
|
||||||
|
if not escape_chars[c] then
|
||||||
|
decode_error(str, j, "invalid escape char '" .. c .. "' in string")
|
||||||
|
end
|
||||||
|
has_escape = true
|
||||||
|
end
|
||||||
|
last = nil
|
||||||
|
|
||||||
|
elseif x == 34 then -- '"' (end of string)
|
||||||
|
local s = str:sub(i + 1, j - 1)
|
||||||
|
if has_surrogate_escape then
|
||||||
|
s = s:gsub("\\u[dD][89aAbB]..\\u....", parse_unicode_escape)
|
||||||
|
end
|
||||||
|
if has_unicode_escape then
|
||||||
|
s = s:gsub("\\u....", parse_unicode_escape)
|
||||||
|
end
|
||||||
|
if has_escape then
|
||||||
|
s = s:gsub("\\.", escape_char_map_inv)
|
||||||
|
end
|
||||||
|
return s, j + 1
|
||||||
|
|
||||||
|
else
|
||||||
|
last = x
|
||||||
|
end
|
||||||
|
end
|
||||||
|
decode_error(str, i, "expected closing quote for string")
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function parse_number(str, i)
|
||||||
|
local x = next_char(str, i, delim_chars)
|
||||||
|
local s = str:sub(i, x - 1)
|
||||||
|
local n = tonumber(s)
|
||||||
|
if not n then
|
||||||
|
decode_error(str, i, "invalid number '" .. s .. "'")
|
||||||
|
end
|
||||||
|
return n, x
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function parse_literal(str, i)
|
||||||
|
local x = next_char(str, i, delim_chars)
|
||||||
|
local word = str:sub(i, x - 1)
|
||||||
|
if not literals[word] then
|
||||||
|
decode_error(str, i, "invalid literal '" .. word .. "'")
|
||||||
|
end
|
||||||
|
return literal_map[word], x
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function parse_array(str, i)
|
||||||
|
local res = {}
|
||||||
|
local n = 1
|
||||||
|
i = i + 1
|
||||||
|
while 1 do
|
||||||
|
local x
|
||||||
|
i = next_char(str, i, space_chars, true)
|
||||||
|
-- Empty / end of array?
|
||||||
|
if str:sub(i, i) == "]" then
|
||||||
|
i = i + 1
|
||||||
|
break
|
||||||
|
end
|
||||||
|
-- Read token
|
||||||
|
x, i = parse(str, i)
|
||||||
|
res[n] = x
|
||||||
|
n = n + 1
|
||||||
|
-- Next token
|
||||||
|
i = next_char(str, i, space_chars, true)
|
||||||
|
local chr = str:sub(i, i)
|
||||||
|
i = i + 1
|
||||||
|
if chr == "]" then break end
|
||||||
|
if chr ~= "," then decode_error(str, i, "expected ']' or ','") end
|
||||||
|
end
|
||||||
|
return res, i
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function parse_object(str, i)
|
||||||
|
local res = {}
|
||||||
|
i = i + 1
|
||||||
|
while 1 do
|
||||||
|
local key, val
|
||||||
|
i = next_char(str, i, space_chars, true)
|
||||||
|
-- Empty / end of object?
|
||||||
|
if str:sub(i, i) == "}" then
|
||||||
|
i = i + 1
|
||||||
|
break
|
||||||
|
end
|
||||||
|
-- Read key
|
||||||
|
if str:sub(i, i) ~= '"' then
|
||||||
|
decode_error(str, i, "expected string for key")
|
||||||
|
end
|
||||||
|
key, i = parse(str, i)
|
||||||
|
-- Read ':' delimiter
|
||||||
|
i = next_char(str, i, space_chars, true)
|
||||||
|
if str:sub(i, i) ~= ":" then
|
||||||
|
decode_error(str, i, "expected ':' after key")
|
||||||
|
end
|
||||||
|
i = next_char(str, i + 1, space_chars, true)
|
||||||
|
-- Read value
|
||||||
|
val, i = parse(str, i)
|
||||||
|
-- Set
|
||||||
|
res[key] = val
|
||||||
|
-- Next token
|
||||||
|
i = next_char(str, i, space_chars, true)
|
||||||
|
local chr = str:sub(i, i)
|
||||||
|
i = i + 1
|
||||||
|
if chr == "}" then break end
|
||||||
|
if chr ~= "," then decode_error(str, i, "expected '}' or ','") end
|
||||||
|
end
|
||||||
|
return res, i
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local char_func_map = {
|
||||||
|
[ '"' ] = parse_string,
|
||||||
|
[ "0" ] = parse_number,
|
||||||
|
[ "1" ] = parse_number,
|
||||||
|
[ "2" ] = parse_number,
|
||||||
|
[ "3" ] = parse_number,
|
||||||
|
[ "4" ] = parse_number,
|
||||||
|
[ "5" ] = parse_number,
|
||||||
|
[ "6" ] = parse_number,
|
||||||
|
[ "7" ] = parse_number,
|
||||||
|
[ "8" ] = parse_number,
|
||||||
|
[ "9" ] = parse_number,
|
||||||
|
[ "-" ] = parse_number,
|
||||||
|
[ "t" ] = parse_literal,
|
||||||
|
[ "f" ] = parse_literal,
|
||||||
|
[ "n" ] = parse_literal,
|
||||||
|
[ "[" ] = parse_array,
|
||||||
|
[ "{" ] = parse_object,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
parse = function(str, idx)
|
||||||
|
local chr = str:sub(idx, idx)
|
||||||
|
local f = char_func_map[chr]
|
||||||
|
if f then
|
||||||
|
return f(str, idx)
|
||||||
|
end
|
||||||
|
decode_error(str, idx, "unexpected character '" .. chr .. "'")
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function json.decode(str)
|
||||||
|
if type(str) ~= "string" then
|
||||||
|
error("expected argument of type string, got " .. type(str))
|
||||||
|
end
|
||||||
|
local res, idx = parse(str, next_char(str, 1, space_chars, true))
|
||||||
|
idx = next_char(str, idx, space_chars, true)
|
||||||
|
if idx <= #str then
|
||||||
|
decode_error(str, idx, "trailing garbage")
|
||||||
|
end
|
||||||
|
return res
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
return json
|
351
libs/peachy.lua
Normal file
351
libs/peachy.lua
Normal file
@ -0,0 +1,351 @@
|
|||||||
|
--- A parser/renderer for Aseprite animations in LÖVE.
|
||||||
|
-- @classmod peachy
|
||||||
|
|
||||||
|
local peachy = {
|
||||||
|
_VERSION = "1.0.0-alpha",
|
||||||
|
_DESCRIPTION = "A parser/renderer for Aseprite animations in LÖVE.",
|
||||||
|
_URL = "https://github.com/josh-perry/peachy",
|
||||||
|
_LICENSE = [[
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2018 Josh Perry
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
]]
|
||||||
|
}
|
||||||
|
|
||||||
|
local PATH = select('1', ...):match(".+%.") or ""
|
||||||
|
local json = require(PATH.."libs/json")
|
||||||
|
local cron = require(PATH.."libs/cron")
|
||||||
|
|
||||||
|
peachy.__index = peachy
|
||||||
|
|
||||||
|
--- Creates a new Peachy animation object.
|
||||||
|
--
|
||||||
|
-- If imageData isn't specified then Peachy will attempt to load it using the
|
||||||
|
-- filename from the JSON data.
|
||||||
|
--
|
||||||
|
-- If no initial tag is set then the object will be paused (i.e. not displayed) with no tag.
|
||||||
|
-- The animation will start playing immediately once created.
|
||||||
|
--
|
||||||
|
-- @usage
|
||||||
|
-- -- Load the image ourselves and set animation tag to "Spin".
|
||||||
|
-- -- Will start playing immediately.
|
||||||
|
-- spinner = peachy.new("spinner.json", love.graphics.newImage("spinner.png"), "Spin")
|
||||||
|
--
|
||||||
|
-- @tparam string dataFile a path to an Aseprite JSON file. It is also possible to pass a predecoded table,
|
||||||
|
-- which is useful for performance when creating large amounts of the same animation.
|
||||||
|
-- @tparam Image imageData a LÖVE image to animate.
|
||||||
|
-- @tparam string initialTag the name of the animation tag to use initially.
|
||||||
|
-- @return the new Peachy object.
|
||||||
|
function peachy.new(dataFile, imageData, initialTag)
|
||||||
|
assert(dataFile ~= nil, "No JSON data!")
|
||||||
|
|
||||||
|
local self = setmetatable({}, peachy)
|
||||||
|
|
||||||
|
--store the path to the passed json file
|
||||||
|
self.json_path = dataFile
|
||||||
|
|
||||||
|
-- check if datafile is a lua table (i.e. pre decoded)
|
||||||
|
if type(dataFile) == 'table' then
|
||||||
|
self._jsonData = dataFile
|
||||||
|
else
|
||||||
|
-- Read the data
|
||||||
|
self._jsonData = json.decode(love.filesystem.read(dataFile))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Load the image
|
||||||
|
self.image = imageData or love.graphics.newImage(self._jsonData.meta.image)
|
||||||
|
|
||||||
|
self:_checkImageSize()
|
||||||
|
|
||||||
|
self:_initializeFrames()
|
||||||
|
self:_initializeTags()
|
||||||
|
|
||||||
|
self.paused = true
|
||||||
|
|
||||||
|
self.tag = nil
|
||||||
|
self.tagName = nil
|
||||||
|
self.direction = nil
|
||||||
|
|
||||||
|
if initialTag then
|
||||||
|
self:setTag(initialTag)
|
||||||
|
self.paused = false
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Switch to a different animation tag.
|
||||||
|
-- In the case that we're attempting to switch to the animation currently playing,
|
||||||
|
-- nothing will happen.
|
||||||
|
--
|
||||||
|
-- @tparam string tag the animation tag name to switch to.
|
||||||
|
function peachy:setTag(tag)
|
||||||
|
assert(tag, "No animation tag specified!")
|
||||||
|
assert(self.frameTags[tag], "Tag "..tag.." not found in frametags!")
|
||||||
|
|
||||||
|
if self.tag == self.frameTags[tag] then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
self.tagName = tag
|
||||||
|
self.tag = self.frameTags[self.tagName]
|
||||||
|
self.frameIndex = nil
|
||||||
|
self.direction = self.tag.direction
|
||||||
|
|
||||||
|
if self.direction == "pingpong" then
|
||||||
|
self.direction = "forward"
|
||||||
|
end
|
||||||
|
|
||||||
|
self:nextFrame()
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Jump to a particular frame index (1-based indexes) in the current animation.
|
||||||
|
--
|
||||||
|
-- Errors if the frame is outside the tag's frame range.
|
||||||
|
--
|
||||||
|
-- @usage
|
||||||
|
-- -- Go to the 4th frame
|
||||||
|
-- sound:setFrame(4)
|
||||||
|
--
|
||||||
|
-- @tparam number frame the frame index to jump to.
|
||||||
|
function peachy:setFrame(frame)
|
||||||
|
if frame < 1 or frame > #self.tag.frames then
|
||||||
|
error("Frame "..frame.." is out of range of tag '"..self.tagName.."' (1.."..#self.tag.frames..")")
|
||||||
|
end
|
||||||
|
|
||||||
|
self.frameIndex = frame
|
||||||
|
|
||||||
|
self.frame = self.tag.frames[self.frameIndex]
|
||||||
|
self.frameTimer = cron.after(self.frame.duration, self.nextFrame, self)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get the current frame of the current animation
|
||||||
|
-- @usage
|
||||||
|
-- Get the 2nd frame
|
||||||
|
-- local f = sound:getFrame()
|
||||||
|
--
|
||||||
|
function peachy:getFrame()
|
||||||
|
return self.frameIndex
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get the json path passed in the object
|
||||||
|
-- @usage
|
||||||
|
-- Get the (string) JSON path
|
||||||
|
-- local str_json = obj:getJSON()
|
||||||
|
--
|
||||||
|
function peachy:getJSON()
|
||||||
|
return self.json_path
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Draw the animation's current frame in a specified location.
|
||||||
|
-- @tparam number x the x position.
|
||||||
|
-- @tparam number y the y position.
|
||||||
|
-- @tparam number rot the rotation to draw at.
|
||||||
|
-- @tparam number sx the x scaling.
|
||||||
|
-- @tparam number sy the y scaling.
|
||||||
|
-- @tparam number ox the origin offset x.
|
||||||
|
-- @tparam number oy the origin offset y.
|
||||||
|
function peachy:draw(x, y, rot, sx, sy, ox, oy)
|
||||||
|
if not self.frame then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
love.graphics.draw(self.image, self.frame.quad, x, y, rot or 0, sx or 1, sy or 1, ox or 0, oy or 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Update the animation.
|
||||||
|
-- @tparam number dt frame delta. Should be called from love.update and given the dt.
|
||||||
|
function peachy:update(dt)
|
||||||
|
assert(dt, "No dt passed into update!")
|
||||||
|
|
||||||
|
if self.paused then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- If we're trying to play an animation and it's nil or hasn't been set up
|
||||||
|
-- properly then error
|
||||||
|
assert(self.tag, "No animation tag has been set!")
|
||||||
|
assert(self.frameTimer, "Frame timer hasn't been initialized!")
|
||||||
|
|
||||||
|
-- Update timer in milliseconds since that's how Aseprite stores durations
|
||||||
|
self.frameTimer:update(dt * 1000)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Move to the next frame.
|
||||||
|
-- Internal: unless you want to skip frames, this generally will not ever
|
||||||
|
-- need to be called manually.
|
||||||
|
function peachy:nextFrame()
|
||||||
|
local forward = self.direction == "forward"
|
||||||
|
|
||||||
|
if forward then
|
||||||
|
self.frameIndex = (self.frameIndex or 0) + 1
|
||||||
|
else
|
||||||
|
self.frameIndex = (self.frameIndex or #self.tag.frames + 1) - 1
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Looping
|
||||||
|
if forward and self.frameIndex > #self.tag.frames then
|
||||||
|
if self.tag.direction == "pingpong" then
|
||||||
|
self:_pingpongBounce()
|
||||||
|
else
|
||||||
|
self.frameIndex = 1
|
||||||
|
end
|
||||||
|
self:call_onLoop()
|
||||||
|
elseif not forward and self.frameIndex < 1 then
|
||||||
|
if self.tag.direction == "pingpong" then
|
||||||
|
self:_pingpongBounce()
|
||||||
|
else
|
||||||
|
self.frameIndex = #self.tag.frames
|
||||||
|
self:call_onLoop()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Get next frame
|
||||||
|
self.frame = self.tag.frames[self.frameIndex]
|
||||||
|
|
||||||
|
self.frameTimer = cron.after(self.frame.duration, self.nextFrame, self)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Check for callbacks
|
||||||
|
function peachy:call_onLoop()
|
||||||
|
if self.callback_onLoop then self.callback_onLoop(unpack(self.args_onLoop)) end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Pauses the animation.
|
||||||
|
function peachy:pause()
|
||||||
|
self.paused = true
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Unpauses the animation.
|
||||||
|
function peachy:play()
|
||||||
|
self.paused = false
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Stops the animation (pause it then return to first frame or last if specified)
|
||||||
|
function peachy:stop(onLast)
|
||||||
|
local index = 1
|
||||||
|
self.paused = true
|
||||||
|
if onLast then index = #self.tag.frames end
|
||||||
|
self:setFrame(index)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Adds a callback function that will be called when the animation loops
|
||||||
|
function peachy:onLoop(fn, ...)
|
||||||
|
self.callback_onLoop = fn
|
||||||
|
self.args_onLoop = {...}
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Toggle between playing/paused.
|
||||||
|
function peachy:togglePlay()
|
||||||
|
if self.paused then
|
||||||
|
self:play()
|
||||||
|
else
|
||||||
|
self:pause()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Provides width stored in the metadata of a current frame
|
||||||
|
function peachy:getWidth()
|
||||||
|
return self._jsonData.frames[self.frameIndex].frame.w
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Provides height stored in the metadata of a current frame
|
||||||
|
function peachy:getHeight()
|
||||||
|
return self._jsonData.frames[self.frameIndex].frame.h
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Provides dimensions stored in the metadata of a current frame
|
||||||
|
function peachy:getDimensions()
|
||||||
|
return self:getWidth(), self:getHeight()
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Internal: handles the ping-pong animation type.
|
||||||
|
--
|
||||||
|
-- Should only be called when we actually want to bounce.
|
||||||
|
-- Swaps the direction.
|
||||||
|
function peachy:_pingpongBounce()
|
||||||
|
-- We need to increment/decrement frame index by 2 because
|
||||||
|
-- at this point we've already gone to the next frame
|
||||||
|
if self.direction == "forward" then
|
||||||
|
self.direction = "reverse"
|
||||||
|
self.frameIndex = self.frameIndex - 2
|
||||||
|
else
|
||||||
|
self.direction = "forward"
|
||||||
|
self.frameIndex = self.frameIndex + 2
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Internal: loads all of the frames
|
||||||
|
--
|
||||||
|
-- Loads quads and frame duration data from the JSON.
|
||||||
|
--
|
||||||
|
-- Called from peachy.new
|
||||||
|
function peachy:_initializeFrames()
|
||||||
|
assert(self._jsonData ~= nil, "No JSON data!")
|
||||||
|
assert(self._jsonData.meta ~= nil, "No metadata in JSON!")
|
||||||
|
assert(self._jsonData.frames ~= nil, "No frame data in JSON!")
|
||||||
|
|
||||||
|
-- Initialize all the quads
|
||||||
|
self.frames = {}
|
||||||
|
for _, frameData in ipairs(self._jsonData.frames) do
|
||||||
|
local frame = {}
|
||||||
|
|
||||||
|
local fd = frameData.frame
|
||||||
|
frame.quad = love.graphics.newQuad(fd.x, fd.y, fd.w, fd.h, self._jsonData.meta.size.w, self._jsonData.meta.size.h)
|
||||||
|
frame.duration = frameData.duration
|
||||||
|
|
||||||
|
table.insert(self.frames, frame)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Internal: loads all of the animation tags
|
||||||
|
--
|
||||||
|
-- Called from peachy.new
|
||||||
|
function peachy:_initializeTags()
|
||||||
|
assert(self._jsonData ~= nil, "No JSON data!")
|
||||||
|
assert(self._jsonData.meta ~= nil, "No metadata in JSON!")
|
||||||
|
assert(self._jsonData.meta.frameTags ~= nil, "No frame tags in JSON! Make sure you exported them in Aseprite!")
|
||||||
|
|
||||||
|
self.frameTags = {}
|
||||||
|
|
||||||
|
for _, frameTag in ipairs(self._jsonData.meta.frameTags) do
|
||||||
|
local ft = {}
|
||||||
|
ft.direction = frameTag.direction
|
||||||
|
ft.frames = {}
|
||||||
|
|
||||||
|
for frame = frameTag.from + 1, frameTag.to + 1 do
|
||||||
|
table.insert(ft.frames, self.frames[frame])
|
||||||
|
end
|
||||||
|
|
||||||
|
self.frameTags[frameTag.name] = ft
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Internal: checks that the loaded image size matches the metadata
|
||||||
|
--
|
||||||
|
-- Called from peachy.new
|
||||||
|
function peachy:_checkImageSize()
|
||||||
|
local imageWidth, imageHeight = self._jsonData.meta.size.w, self._jsonData.meta.size.h
|
||||||
|
assert(imageWidth == self.image:getWidth(), "Image width metadata doesn't match actual width of file")
|
||||||
|
assert(imageHeight == self.image:getHeight(), "Image height metadata doesn't match actual height of file")
|
||||||
|
end
|
||||||
|
|
||||||
|
return peachy
|
38
main.lua
Normal file
38
main.lua
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
--load peachy
|
||||||
|
peachy = require("libs/peachy")
|
||||||
|
|
||||||
|
--load player
|
||||||
|
player = require("modules/player")
|
||||||
|
|
||||||
|
--load content_loader
|
||||||
|
local content_loader = require("modules/content_loader")
|
||||||
|
spritesheets = content_loader.spritesheets
|
||||||
|
sound_effects = content_loader.sound_effects
|
||||||
|
backgrounds = content_loader.backgrounds
|
||||||
|
music = content_loader.music
|
||||||
|
|
||||||
|
--load globals
|
||||||
|
local globals = require("modules/globals")
|
||||||
|
local game = globals.game
|
||||||
|
|
||||||
|
--load draw_callback
|
||||||
|
draw_mod = require("modules/draw_callback")
|
||||||
|
local draw_callback = draw_mod.draw
|
||||||
|
local drawerinit = draw_mod.drawerinit
|
||||||
|
|
||||||
|
--load update_callback
|
||||||
|
update_mod = require("modules/update_callback")
|
||||||
|
local update_callback = update_mod.update
|
||||||
|
local updateinit = update_mod.updateinit
|
||||||
|
|
||||||
|
drawerinit(spritesheets, player, game)
|
||||||
|
updateinit(game)
|
||||||
|
player:init(sound_effects, spritesheets, game)
|
||||||
|
|
||||||
|
function love.draw()
|
||||||
|
draw_callback()
|
||||||
|
end
|
||||||
|
|
||||||
|
function love.update(dt)
|
||||||
|
update_callback(dt)
|
||||||
|
end
|
64
modules/content_loader.lua
Normal file
64
modules/content_loader.lua
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
--import media
|
||||||
|
--import spritesheets
|
||||||
|
local spritesheets = {}
|
||||||
|
--load jsons
|
||||||
|
--load player jumping
|
||||||
|
spritesheets["player_jump"] = peachy.new("assets/images/spritesheets/player.json", love.graphics.newImage("assets/images/spritesheets/player.png"), "Jump")
|
||||||
|
--load player walking left
|
||||||
|
spritesheets["player_walk_left"] = peachy.new("assets/images/spritesheets/player.json", love.graphics.newImage("assets/images/spritesheets/player.png"), "Walk_left")
|
||||||
|
--load player walking right
|
||||||
|
spritesheets["player_walk_right"] = peachy.new("assets/images/spritesheets/player.json", love.graphics.newImage("assets/images/spritesheets/player.png"), "Walk_right")
|
||||||
|
--load player idle
|
||||||
|
spritesheets["player_idle"] = peachy.new("assets/images/spritesheets/player.json", love.graphics.newImage("assets/images/spritesheets/player.png"), "Idle")
|
||||||
|
--load player dying
|
||||||
|
spritesheets["player_die"] = peachy.new("assets/images/spritesheets/player.json", love.graphics.newImage("assets/images/spritesheets/player.png"), "Die")
|
||||||
|
--load player DEFAULT
|
||||||
|
spritesheets["player_default"] = peachy.new("assets/images/spritesheets/player.json", love.graphics.newImage("assets/images/spritesheets/player.png"), "Default")
|
||||||
|
|
||||||
|
--load backgrounds
|
||||||
|
local backgrounds = {}
|
||||||
|
for i, file in ipairs(love.filesystem.getDirectoryItems("assets/images/backgrounds")) do
|
||||||
|
if file:find(".png") then
|
||||||
|
--load backgrounds
|
||||||
|
print(i)
|
||||||
|
backgrounds[i] = love.graphics.newImage("assets/images/backgrounds/"..file)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
--load sound effects
|
||||||
|
local sound_effects = {}
|
||||||
|
for i, file in ipairs(love.filesystem.getDirectoryItems("assets/sounds/effects")) do
|
||||||
|
if file:find(".ogg") then
|
||||||
|
--load sound effects
|
||||||
|
local keyname = file:gsub(".ogg", "")
|
||||||
|
sound = love.audio.newSource("assets/sounds/effects/"..file, "static")
|
||||||
|
sound_effects[keyname] = {}
|
||||||
|
sound_effects[keyname].volume = 1
|
||||||
|
sound_effects[keyname].timer = love.timer.getTime()
|
||||||
|
sound_effects[keyname].first_play = true
|
||||||
|
sound_effects[keyname].sound = sound
|
||||||
|
sound_effects[keyname].duration = sound:getDuration()
|
||||||
|
sound_effects[keyname].timer_min = sound:getDuration() * 2
|
||||||
|
sound_effects[keyname].play = function(self)
|
||||||
|
if (love.timer.getTime() - self.timer) >= self.timer_min or self.first_play then
|
||||||
|
self.timer = love.timer.getTime()
|
||||||
|
self.first_play = false
|
||||||
|
self.sound:setVolume(self.volume)
|
||||||
|
self.sound:play()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
--load music
|
||||||
|
local music = {}
|
||||||
|
for i, file in ipairs(love.filesystem.getDirectoryItems("assets/sounds/music")) do
|
||||||
|
if file:find(".ogg") then
|
||||||
|
--load music
|
||||||
|
music[file:sub(1, -5)] = love.audio.newSource("assets/sounds/music/"..file, "stream")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return {
|
||||||
|
spritesheets = spritesheets,
|
||||||
|
backgrounds = backgrounds,
|
||||||
|
sound_effects = sound_effects,
|
||||||
|
music = music
|
||||||
|
}
|
33
modules/draw_callback.lua
Normal file
33
modules/draw_callback.lua
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
local spritesheets = {}
|
||||||
|
local player = {}
|
||||||
|
local game = {}
|
||||||
|
|
||||||
|
function drawerinit(spritesheetst, playert, gamex)
|
||||||
|
spritesheets = spritesheetst
|
||||||
|
player = playert
|
||||||
|
game = gamex
|
||||||
|
end
|
||||||
|
|
||||||
|
local function draw()
|
||||||
|
--draw background
|
||||||
|
love.graphics.draw(backgrounds[game.level], 0, 0)
|
||||||
|
--draw player_animation
|
||||||
|
player.animation:draw(player.x, player.y)
|
||||||
|
if player.state == "idle" then
|
||||||
|
spritesheets["player_idle"]:draw(player.x, player.y)
|
||||||
|
elseif player.state == "walk_left" then
|
||||||
|
spritesheets["player_walk_left"]:draw(player.x, player.y)
|
||||||
|
elseif player.state == "walk_right" then
|
||||||
|
spritesheets["player_walk_right"]:draw(player.x, player.y)
|
||||||
|
elseif player.state == "jump" then
|
||||||
|
spritesheets["player_jump"]:draw(player.x, player.y)
|
||||||
|
elseif player.state == "die" then
|
||||||
|
spritesheets["player_die"]:draw(player.x, player.y)
|
||||||
|
else
|
||||||
|
spritesheets["player_default"]:draw(player.x, player.y)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return {
|
||||||
|
drawerinit = drawerinit,
|
||||||
|
draw = draw
|
||||||
|
}
|
13
modules/globals.lua
Normal file
13
modules/globals.lua
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
local game = {}
|
||||||
|
game.debug = true
|
||||||
|
game.width = 1024
|
||||||
|
game.height = 768
|
||||||
|
game.title = "Weeeeeeeee"
|
||||||
|
game.version = "0.0.1"
|
||||||
|
game.fullscreen = false
|
||||||
|
game.level = 1
|
||||||
|
game.die_animation_elapsed = false
|
||||||
|
|
||||||
|
return {
|
||||||
|
game = game
|
||||||
|
}
|
80
modules/player.lua
Normal file
80
modules/player.lua
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
local player = {}
|
||||||
|
player.x = 0
|
||||||
|
player.y = 500
|
||||||
|
player.width = 160
|
||||||
|
player.height = 160
|
||||||
|
player.speed = 200
|
||||||
|
player.jump_height = 500
|
||||||
|
player.state = "idle"
|
||||||
|
player.direction = "right"
|
||||||
|
player.alive = true
|
||||||
|
player.init = function(self, sound_effects, spritesheets, game)
|
||||||
|
self.sound_effects = sound_effects
|
||||||
|
self.spritesheets = spritesheets
|
||||||
|
self.game = game
|
||||||
|
player.animation = spritesheets["player_idle"]
|
||||||
|
end
|
||||||
|
|
||||||
|
player.walk_right = function(self, dt)
|
||||||
|
if self.x < self.game.width - self.width and self.alive then
|
||||||
|
self.x = self.x + self.speed * dt
|
||||||
|
self.state = "walk_right"
|
||||||
|
self.direction = "right"
|
||||||
|
self.animation = self.spritesheets["player_walk_right"]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
player.walk_left = function(self, dt)
|
||||||
|
if self.x > 0 and self.alive then
|
||||||
|
self.x = self.x - self.speed * dt
|
||||||
|
self.state = "walk_left"
|
||||||
|
self.direction = "left"
|
||||||
|
self.animation = self.spritesheets["player_walk_left"]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
player.jump = function(self, dt)
|
||||||
|
if self.y > 0 and self.alive then
|
||||||
|
self.y = self.y - self.jump_height * dt
|
||||||
|
self.state = "jump"
|
||||||
|
self.animation = self.spritesheets["player_jump"]
|
||||||
|
self.sound_effects["jump"]:play()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
player.down = function(self, dt)
|
||||||
|
if self.y < self.game.height - self.height and self.alive then
|
||||||
|
self.y = self.y + self.jump_height * dt
|
||||||
|
self.state = "jump"
|
||||||
|
self.animation = self.spritesheets["player_jump"]
|
||||||
|
self.sound_effects["jump"]:play()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
player.die = function(self, dt)
|
||||||
|
if self.alive then
|
||||||
|
self.alive = false
|
||||||
|
self.state = "die"
|
||||||
|
self.animation = self.spritesheets["player_die"]
|
||||||
|
self.sound_effects["die"]:play()
|
||||||
|
die_animation_elapsed = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
player.revive = function(self, dt)
|
||||||
|
if not self.alive then
|
||||||
|
self.alive = true
|
||||||
|
self.state = "idle"
|
||||||
|
self.animation = self.spritesheets["player_default"]
|
||||||
|
self.sound_effects["revive"]:play()
|
||||||
|
die_animation_elapsed = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
player.idle = function(self, dt)
|
||||||
|
if self.alive then
|
||||||
|
self.state = "idle"
|
||||||
|
self.animation = self.spritesheets["player_idle"]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
player.default = function(self, dt)
|
||||||
|
if self.alive then
|
||||||
|
self.state = "default"
|
||||||
|
self.animation = self.spritesheets["player_default"]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return player
|
54
modules/update_callback.lua
Normal file
54
modules/update_callback.lua
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
local game = {}
|
||||||
|
|
||||||
|
local function updateinit(gamex)
|
||||||
|
game = gamex
|
||||||
|
end
|
||||||
|
|
||||||
|
local function update(dt)
|
||||||
|
--update player_animation
|
||||||
|
if spritesheets["player_die"]:getFrame() ~= 16 and not game.die_animation_elapsed then
|
||||||
|
spritesheets["player_die"]:update(dt)
|
||||||
|
else
|
||||||
|
game.die_animation_elapsed = true
|
||||||
|
end
|
||||||
|
if player.alive then
|
||||||
|
spritesheets["player_jump"]:update(dt)
|
||||||
|
spritesheets["player_walk_left"]:update(dt)
|
||||||
|
spritesheets["player_walk_right"]:update(dt)
|
||||||
|
spritesheets["player_idle"]:update(dt)
|
||||||
|
spritesheets["player_default"]:update(dt)
|
||||||
|
end
|
||||||
|
|
||||||
|
--get keyboard
|
||||||
|
local key = love.keyboard.isDown
|
||||||
|
--move player
|
||||||
|
if key("d") then
|
||||||
|
player:walk_right(dt)
|
||||||
|
elseif key("a") then
|
||||||
|
player:walk_left(dt)
|
||||||
|
else
|
||||||
|
player:idle(dt)
|
||||||
|
end
|
||||||
|
--jump player
|
||||||
|
if key("w") then
|
||||||
|
player:jump(dt)
|
||||||
|
end
|
||||||
|
if game.debug then
|
||||||
|
if key("s") then
|
||||||
|
player:down(dt)
|
||||||
|
end
|
||||||
|
if key("f") then
|
||||||
|
--die
|
||||||
|
player:die(dt)
|
||||||
|
game.die_animation_elapsed = false
|
||||||
|
elseif key("g") then
|
||||||
|
--revive player
|
||||||
|
player:revive(dt)
|
||||||
|
game.die_animation_elapsed = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return {
|
||||||
|
update = update,
|
||||||
|
updateinit = updateinit
|
||||||
|
}
|
97
test.tldr
Normal file
97
test.tldr
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
{
|
||||||
|
"document": {
|
||||||
|
"id": "doc",
|
||||||
|
"name": "New Document",
|
||||||
|
"version": 15.3,
|
||||||
|
"pages": {
|
||||||
|
"page": {
|
||||||
|
"id": "page",
|
||||||
|
"name": "Page 1",
|
||||||
|
"childIndex": 1,
|
||||||
|
"shapes": {
|
||||||
|
"feba193a-4518-4953-0fd0-31a3c7550237": {
|
||||||
|
"id": "feba193a-4518-4953-0fd0-31a3c7550237",
|
||||||
|
"type": "draw",
|
||||||
|
"name": "Draw",
|
||||||
|
"parentId": "page",
|
||||||
|
"childIndex": 1,
|
||||||
|
"point": [
|
||||||
|
786,
|
||||||
|
360.5
|
||||||
|
],
|
||||||
|
"rotation": 0,
|
||||||
|
"style": {
|
||||||
|
"color": "black",
|
||||||
|
"size": "small",
|
||||||
|
"isFilled": false,
|
||||||
|
"dash": "draw",
|
||||||
|
"scale": 1
|
||||||
|
},
|
||||||
|
"points": [
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0.5
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0.5
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"isComplete": true
|
||||||
|
},
|
||||||
|
"c3e4d223-d03e-4cd0-278b-53b9ae1164f0": {
|
||||||
|
"id": "c3e4d223-d03e-4cd0-278b-53b9ae1164f0",
|
||||||
|
"type": "draw",
|
||||||
|
"name": "Draw",
|
||||||
|
"parentId": "page",
|
||||||
|
"childIndex": 2,
|
||||||
|
"point": [
|
||||||
|
2315.63,
|
||||||
|
1240.9
|
||||||
|
],
|
||||||
|
"rotation": 0,
|
||||||
|
"style": {
|
||||||
|
"color": "black",
|
||||||
|
"size": "small",
|
||||||
|
"isFilled": false,
|
||||||
|
"dash": "draw",
|
||||||
|
"scale": 1
|
||||||
|
},
|
||||||
|
"points": [
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0.5
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0.5
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"isComplete": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"bindings": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pageStates": {
|
||||||
|
"page": {
|
||||||
|
"id": "page",
|
||||||
|
"selectedIds": [],
|
||||||
|
"camera": {
|
||||||
|
"point": [
|
||||||
|
0,
|
||||||
|
-79.5
|
||||||
|
],
|
||||||
|
"zoom": 1
|
||||||
|
},
|
||||||
|
"editingId": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"assets": {}
|
||||||
|
},
|
||||||
|
"assets": {}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user