diff --git a/Makefile b/Makefile index c8f5e89..d53f941 100644 --- a/Makefile +++ b/Makefile @@ -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 @@ -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" diff --git a/source/game/scenes/title.fnl b/source/game/scenes/title.fnl index e44d816..bb2bce7 100644 --- a/source/game/scenes/title.fnl +++ b/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) + ) + ) diff --git a/source/levels.ldtk b/source/levels.ldtk new file mode 100644 index 0000000..094a570 --- /dev/null +++ b/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" +} \ No newline at end of file diff --git a/source/lib/ldtk-loader.fnl b/source/lib/ldtk-loader.fnl new file mode 100644 index 0000000..7db0c00 --- /dev/null +++ b/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))) + ) + diff --git a/source/lib/ldtk-macros.fnl b/source/lib/ldtk-macros.fnl new file mode 100644 index 0000000..dc43982 --- /dev/null +++ b/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} diff --git a/source/lib/ldtk.fnl b/source/lib/ldtk.fnl new file mode 100644 index 0000000..1312126 --- /dev/null +++ b/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} diff --git a/source/lib/level.fnl b/source/lib/level.fnl new file mode 100644 index 0000000..0682526 --- /dev/null +++ b/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})) + ) diff --git a/source/lib/macros.fnl b/source/lib/macros.fnl index 481dd8a..767e3f5 100644 --- a/source/lib/macros.fnl +++ b/source/lib/macros.fnl @@ -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 } diff --git a/source/pdxinfo b/source/pdxinfo index df65134..7f60084 100644 --- a/source/pdxinfo +++ b/source/pdxinfo @@ -4,5 +4,5 @@ description=Fennel Test Game bundleID=com.davidhaslem.fennel-test version=1.0 buildNumber=1 -imagePath=system +imagePath=assets launchSoundPath= diff --git a/support/build.ps1 b/support/build.ps1 index ee31129..28ed4dd 100644 --- a/support/build.ps1 +++ b/support/build.ps1 @@ -1,2 +1,2 @@ -$content = fennel -c --require-as-include "./source/main.fnl" -[IO.File]::WriteAllLines("./source/main.lua", $content) \ No newline at end of file +$content = fennel -c --require-as-include --no-compiler-sandbox "./source/main.fnl" +[IO.File]::WriteAllLines("./source/main.lua", $content) diff --git a/support/build.sh b/support/build.sh index 7366214..fd6e319 100644 --- a/support/build.sh +++ b/support/build.sh @@ -1 +1 @@ -fennel -c --require-as-include source/main.fnl > source.main.lua \ No newline at end of file +fennel -c --require-as-include --no-compiler-sandbox source/main.fnl > source.main.lua