Skip to content

Commit

Permalink
Support for loading ldtk files
Browse files Browse the repository at this point in the history
Both at runtime and compile time (as a macro)
  • Loading branch information
therabidbanana committed Oct 9, 2023
1 parent ffb8011 commit 2eae734
Show file tree
Hide file tree
Showing 11 changed files with 321 additions and 24 deletions.
6 changes: 4 additions & 2 deletions Makefile
Expand Up @@ -2,7 +2,8 @@ compile: source/**/*.fnl
./support/build.sh

build: compile
pdc source test.pdx
pdc -k source test.pdx
cp source/*.ldtk test.pdx/

launch: build
playdate test.pdx
Expand All @@ -14,7 +15,8 @@ win-compile: source/**/*.fnl
powershell.exe "./support/build.ps1"

win-build: win-compile
powershell.exe "pdc source test.pdx"
powershell.exe "pdc -k source test.pdx"
powershell.exe "cp source/*.ldtk test.pdx/"

win-launch: win-build
powershell.exe "playdate test.pdx"
Expand Down
53 changes: 38 additions & 15 deletions source/game/scenes/title.fnl
@@ -1,16 +1,39 @@
(import-macros {: inspect} :source.lib.macros)
(let [{:player player-ent} (require :source.game.entities.core)
pd playdate
gfx pd.graphics]
{:enter! (fn scene-enter! [$]
(let [player (player-ent.new! 20 20)]
(player:add)))
:exit! (fn scene-exit! [$])
:tick! (fn scene-tick! [$]
;; (listview:drawInRect 180 20 200 200)
(gfx.sprite.performOnAllSprites (fn react-each [ent]
(if (?. ent :react!) (ent:react!)))))
:draw! (fn scene-tick! [$]
;; (listview:drawInRect 180 20 200 200)
)})
(import-macros {: defns : inspect} :source.lib.macros)
(import-macros {: deflevel} :source.lib.ldtk-macros)


(deflevel :Level_0
[{:player player-ent} (require :source.game.entities.core)
;; ldtk (require :source.lib.ldtk-loader)
{: prepare-level} (require :source.lib.level)
pd playdate
gfx pd.graphics]

(fn enter! [$]
(let [player (player-ent.new! 20 20)
;; loaded (prepare-level (ldtk.load-level {:level 0}))
loaded (prepare-level Level_0)
layer (?. loaded :layers 1)
bg (gfx.sprite.new)
]
(bg:setTilemap layer.tilemap)
(bg:setCenter 0 0)
(bg:moveTo 0 0)
(bg:setZIndex -100)
(tset $ :layer layer)
(player:add)
(bg:add)
;; (printTable (ldtk.load-level 0))
)
)

(fn exit! [$])

(fn tick! [$]
(gfx.sprite.performOnAllSprites (fn react-each [ent]
(if (?. ent :react!) (ent:react!)))))
(fn draw! [$]
;; ($.layer.tilemap:draw 0 0)
)
)

187 changes: 187 additions & 0 deletions source/levels.ldtk
@@ -0,0 +1,187 @@
{
"__header__": {
"fileType": "LDtk Project JSON",
"app": "LDtk",
"doc": "https://ldtk.io/json",
"schema": "https://ldtk.io/files/JSON_SCHEMA.json",
"appAuthor": "Sebastien 'deepnight' Benard",
"appVersion": "1.4.1",
"url": "https://ldtk.io"
},
"iid": "7e489f20-6280-11ee-94e3-4b3019ff8156",
"jsonVersion": "1.4.1",
"appBuildId": 471015,
"nextUid": 3,
"identifierStyle": "Capitalize",
"toc": [],
"worldLayout": "Free",
"worldGridWidth": 256,
"worldGridHeight": 256,
"defaultLevelWidth": 256,
"defaultLevelHeight": 256,
"defaultPivotX": 0,
"defaultPivotY": 0,
"defaultGridSize": 16,
"defaultEntityWidth": 16,
"defaultEntityHeight": 16,
"bgColor": "#40465B",
"defaultLevelBgColor": "#696A79",
"minifyJson": false,
"externalLevels": false,
"exportTiled": false,
"simplifiedExport": false,
"imageExportMode": "None",
"exportLevelBg": true,
"pngFilePattern": null,
"backupOnSave": false,
"backupLimit": 10,
"backupRelPath": null,
"levelNamePattern": "Level_%idx",
"tutorialDesc": null,
"customCommands": [],
"flags": [],
"defs": { "layers": [
{
"__type": "Tiles",
"identifier": "Tiles",
"type": "Tiles",
"uid": 2,
"doc": null,
"uiColor": null,
"gridSize": 16,
"guideGridWid": 0,
"guideGridHei": 0,
"displayOpacity": 1,
"inactiveOpacity": 1,
"hideInList": false,
"hideFieldsWhenInactive": false,
"canSelectWhenInactive": true,
"renderInWorldView": true,
"pxOffsetX": 0,
"pxOffsetY": 0,
"parallaxFactorX": 0,
"parallaxFactorY": 0,
"parallaxScaling": true,
"requiredTags": [],
"excludedTags": [],
"intGridValues": [],
"intGridValuesGroups": [],
"autoRuleGroups": [],
"autoSourceLayerDefUid": null,
"tilesetDefUid": 1,
"tilePivotX": 0,
"tilePivotY": 0
}
], "entities": [], "tilesets": [
{
"__cWid": 8,
"__cHei": 8,
"identifier": "Tiles_table_16_16",
"uid": 1,
"relPath": "assets/images/tiles-table-16-16.png",
"embedAtlas": null,
"pxWid": 128,
"pxHei": 128,
"tileGridSize": 16,
"spacing": 0,
"padding": 0,
"tags": [],
"tagsSourceEnumUid": null,
"enumTags": [],
"customData": [],
"savedSelections": [],
"cachedPixelData": {
"opaqueTiles": "1111111111111111111111111111111111111111111111111111111111111111",
"averageColors": "fabcfabcf000f000f000f000f000f000f344f000f000f000f000f000f000f000fcccf000f000f000f000f000f000f000f000f000f000f000f000f000f000f000f000f000f000f000f000f000f000f000f000f000f000f000f000f000f000f000f000f000f000f000f000f000f000f000f000f000f000f000f000f000f000f000"
}
}
], "enums": [], "externalEnums": [], "levelFields": [] },
"levels": [
{
"identifier": "Level_0",
"iid": "7e4a9af0-6280-11ee-94e3-adb9dce3e871",
"uid": 0,
"worldX": 0,
"worldY": 0,
"worldDepth": 0,
"pxWid": 400,
"pxHei": 240,
"__bgColor": "#696A79",
"bgColor": null,
"useAutoIdentifier": true,
"bgRelPath": null,
"bgPos": null,
"bgPivotX": 0.5,
"bgPivotY": 0.5,
"__smartColor": "#ADADB5",
"__bgPos": null,
"externalRelPath": null,
"fieldInstances": [],
"layerInstances": [
{
"__identifier": "Tiles",
"__type": "Tiles",
"__cWid": 25,
"__cHei": 15,
"__gridSize": 16,
"__opacity": 1,
"__pxTotalOffsetX": 0,
"__pxTotalOffsetY": 0,
"__tilesetDefUid": 1,
"__tilesetRelPath": "assets/images/tiles-table-16-16.png",
"iid": "9f770970-6280-11ee-94e3-413d0dd073fa",
"levelId": 0,
"layerDefUid": 2,
"pxOffsetX": 0,
"pxOffsetY": 0,
"visible": true,
"optionalRules": [],
"intGridCsv": [],
"autoLayerTiles": [],
"seed": 1668546,
"overrideTilesetUid": null,
"gridTiles": [
{ "px": [128,32], "src": [0,32], "f": 0, "t": 16, "d": [58], "a": 1 },
{ "px": [224,80], "src": [0,32], "f": 0, "t": 16, "d": [139], "a": 1 },
{ "px": [160,96], "src": [0,32], "f": 0, "t": 16, "d": [160], "a": 1 },
{ "px": [176,96], "src": [0,32], "f": 0, "t": 16, "d": [161], "a": 1 },
{ "px": [192,96], "src": [0,32], "f": 0, "t": 16, "d": [162], "a": 1 },
{ "px": [208,96], "src": [0,32], "f": 0, "t": 16, "d": [163], "a": 1 },
{ "px": [0,112], "src": [0,32], "f": 0, "t": 16, "d": [175], "a": 1 },
{ "px": [16,112], "src": [0,32], "f": 0, "t": 16, "d": [176], "a": 1 },
{ "px": [32,112], "src": [0,32], "f": 0, "t": 16, "d": [177], "a": 1 },
{ "px": [48,112], "src": [0,32], "f": 0, "t": 16, "d": [178], "a": 1 },
{ "px": [64,112], "src": [0,32], "f": 0, "t": 16, "d": [179], "a": 1 },
{ "px": [80,112], "src": [0,32], "f": 0, "t": 16, "d": [180], "a": 1 },
{ "px": [96,112], "src": [0,32], "f": 0, "t": 16, "d": [181], "a": 1 },
{ "px": [112,112], "src": [0,32], "f": 0, "t": 16, "d": [182], "a": 1 },
{ "px": [128,112], "src": [0,32], "f": 0, "t": 16, "d": [183], "a": 1 },
{ "px": [144,112], "src": [0,32], "f": 0, "t": 16, "d": [184], "a": 1 },
{ "px": [160,112], "src": [0,32], "f": 0, "t": 16, "d": [185], "a": 1 },
{ "px": [176,112], "src": [0,32], "f": 0, "t": 16, "d": [186], "a": 1 },
{ "px": [192,112], "src": [0,32], "f": 0, "t": 16, "d": [187], "a": 1 },
{ "px": [208,112], "src": [0,32], "f": 0, "t": 16, "d": [188], "a": 1 },
{ "px": [224,112], "src": [0,32], "f": 0, "t": 16, "d": [189], "a": 1 },
{ "px": [240,112], "src": [0,32], "f": 0, "t": 16, "d": [190], "a": 1 },
{ "px": [256,112], "src": [0,32], "f": 0, "t": 16, "d": [191], "a": 1 },
{ "px": [272,112], "src": [0,32], "f": 0, "t": 16, "d": [192], "a": 1 },
{ "px": [288,112], "src": [0,32], "f": 0, "t": 16, "d": [193], "a": 1 },
{ "px": [304,112], "src": [0,32], "f": 0, "t": 16, "d": [194], "a": 1 },
{ "px": [320,112], "src": [0,32], "f": 0, "t": 16, "d": [195], "a": 1 },
{ "px": [336,112], "src": [0,32], "f": 0, "t": 16, "d": [196], "a": 1 },
{ "px": [352,112], "src": [0,32], "f": 0, "t": 16, "d": [197], "a": 1 },
{ "px": [368,112], "src": [0,32], "f": 0, "t": 16, "d": [198], "a": 1 },
{ "px": [384,112], "src": [0,32], "f": 0, "t": 16, "d": [199], "a": 1 },
{ "px": [160,176], "src": [16,0], "f": 0, "t": 1, "d": [285], "a": 1 },
{ "px": [48,208], "src": [0,32], "f": 0, "t": 16, "d": [328], "a": 1 },
{ "px": [224,208], "src": [0,32], "f": 0, "t": 16, "d": [339], "a": 1 }
],
"entityInstances": []
}
],
"__neighbours": []
}
],
"worlds": [],
"dummyWorldIid": "7e493b60-6280-11ee-94e3-294f98677a3c"
}
20 changes: 20 additions & 0 deletions source/lib/ldtk-loader.fnl
@@ -0,0 +1,20 @@
(import-macros { : pd/import : defns : inspect} :source.lib.macros)

;; This dynamically loads levels created via ldtk
;;
;; The macro approach is more complicated but compiles to lua datastructures at
;; compile
(defns :source.lib.ldtk-loader
[gfx playdate.graphics
ldtk (require :source.lib.ldtk)]

(fn load-world [{ : filename}]
(let [filename (or filename "levels.ldtk")]
(json.decodeFile filename)))

(fn load-level [{ : filename : level &as args}]
(let [world (load-world args)
level-data (ldtk.find-level world level)]
(ldtk.parse-level level-data)))
)

24 changes: 24 additions & 0 deletions source/lib/ldtk-macros.fnl
@@ -0,0 +1,24 @@
(global LEVELS-FILE "source/levels.ldtk")
(local ldtk (require :source.lib.ldtk))

(fn defns [ns-name bindings & forms]
(let [names (icollect [_ [t name & def] (ipairs forms)]
(if (= t (sym :local)) name
(= t (sym :fn)) name))
map (collect [_ name (ipairs names)]
(values (tostring name) name))]

`(let ,bindings
,forms
,map)))

(fn deflevel [levelname bindings & forms]
(let [json (require :lunajson)
file (with-open [f (io.open LEVELS-FILE)] (f:read "*all"))
data (json.decode file)
leveldata (ldtk.parse-level (ldtk.find-level data levelname))]
(table.insert bindings (sym levelname))
(table.insert bindings leveldata)
(defns levelname bindings (table.unpack forms))))

{: deflevel}
22 changes: 22 additions & 0 deletions source/lib/ldtk.fnl
@@ -0,0 +1,22 @@
(fn find-level [world name]
(?.
(icollect [_ { : identifier &as level} (ipairs (. world :levels))]
(if (or (= name identifier) (= (.. "Level_" name) identifier))
level))
1))

(fn parse-layer [{:__tilesetRelPath imagetable :gridTiles tiles :__gridSize grid-size}
{:w map-width :h map-height}]
(let [(_ _ tile-w tile-h) (string.find imagetable ".+%-table%-(%d+)%-(%d+)%..+")
tile-w (tonumber tile-w)
tile-h (tonumber tile-h)
tiles (icollect [_ {:px [x y] : t} (ipairs tiles)]
{:x (+ (// x tile-w) 1) :y (+ (// y tile-h) 1) :tile (+ t 1)})]
{: imagetable : tiles : tile-w : tile-h :w (// map-width tile-w) :h (// map-height tile-h)}))

(fn parse-level [{: layerInstances :pxWid w :pxHei h}]
(let [layers (icollect [_ layer (ipairs layerInstances)]
(parse-layer layer {: w : h}))]
{: layers : w : h}))

{: parse-level : find-level}
18 changes: 18 additions & 0 deletions source/lib/level.fnl
@@ -0,0 +1,18 @@
(import-macros {: inspect : defns : pd/import} :source.lib.macros)

(defns :source.lib.level
[gfx playdate.graphics]

(fn prepare-layer [{: imagetable : tiles : w : h}]
(let [tileset (gfx.imagetable.new imagetable)
tilemap (gfx.tilemap.new)
_ (tilemap:setImageTable tileset)]
(tilemap:setSize w h)
(each [_ {: x : y : tile} (ipairs tiles)]
(tilemap:setTileAtPosition x y tile))
{: tilemap}))

(fn prepare-level [{: layers : w : h}]
(let [layers (icollect [_ l (ipairs layers)] (prepare-layer l))]
{: layers : w : h}))
)
7 changes: 4 additions & 3 deletions source/lib/macros.fnl
Expand Up @@ -13,16 +13,17 @@
(fn pd/import [lib]
`(lua ,(.. "import \"" lib "\"")))

(fn defns [ns-name arr & forms]
(fn defns [ns-name bindings & forms]
(let [names (icollect [_ [t name & def] (ipairs forms)]
(if (= t (sym :local)) name
(= t (sym :fn)) name))
map (collect [_ name (ipairs names)]
(values (tostring name) name))]

`(let ,arr
`(let ,bindings
,forms
,map)))

{: inspect : pd/import : defns}

{: inspect : pd/import : defns }

2 changes: 1 addition & 1 deletion source/pdxinfo
Expand Up @@ -4,5 +4,5 @@ description=Fennel Test Game
bundleID=com.davidhaslem.fennel-test
version=1.0
buildNumber=1
imagePath=system
imagePath=assets
launchSoundPath=
4 changes: 2 additions & 2 deletions support/build.ps1
@@ -1,2 +1,2 @@
$content = fennel -c --require-as-include "./source/main.fnl"
[IO.File]::WriteAllLines("./source/main.lua", $content)
$content = fennel -c --require-as-include --no-compiler-sandbox "./source/main.fnl"
[IO.File]::WriteAllLines("./source/main.lua", $content)
2 changes: 1 addition & 1 deletion support/build.sh
@@ -1 +1 @@
fennel -c --require-as-include source/main.fnl > source.main.lua
fennel -c --require-as-include --no-compiler-sandbox source/main.fnl > source.main.lua

0 comments on commit 2eae734

Please sign in to comment.