From af19ccb833e5f72ca6c027fa4f5a3bf1891cff20 Mon Sep 17 00:00:00 2001 From: Thomas von Dein Date: Sun, 10 Mar 2024 13:05:31 +0100 Subject: [PATCH] shifted from proprietary ascii rawlevel to LDTK (using ldtkgo@master) --- TODO.md | 8 + assets/levels/openquell.ldtk | 686 ++++++++++++++++++++++++++++++++++ assets/loader-levels.go | 169 +++------ assets/loader-sprites.go | 2 +- assets/sprites/block-grey.png | Bin 584 -> 0 bytes assets/sprites/makemap.sh | 2 + assets/sprites/map.png | Bin 0 -> 14324 bytes game/level_scene.go | 7 +- game/levels.go | 62 ++- go.mod | 4 + go.sum | 8 + grid/grid.go | 2 +- systems/collectible_system.go | 4 +- systems/hud_system.go | 10 +- systems/obstacle_system.go | 28 -- 15 files changed, 819 insertions(+), 173 deletions(-) create mode 100644 assets/levels/openquell.ldtk delete mode 100644 assets/sprites/block-grey.png create mode 100644 assets/sprites/makemap.sh create mode 100644 assets/sprites/map.png diff --git a/TODO.md b/TODO.md index 72898aa..88df884 100644 --- a/TODO.md +++ b/TODO.md @@ -22,6 +22,14 @@ slog.Debug("get observer", "minmoves", observer.LevelScore, "file", source.File, "line", source.Line) +- LDTK: + - to each level add these properties: + - level (num) + - name + - description + - background (image name w/o .png) + - min-moves (num, moves required to win) + ## Collider Rework [abandoned: see branch collider-system, fails] - do not use the map anymore for collision detection diff --git a/assets/levels/openquell.ldtk b/assets/levels/openquell.ldtk new file mode 100644 index 0000000..0bfa9a4 --- /dev/null +++ b/assets/levels/openquell.ldtk @@ -0,0 +1,686 @@ +{ + "__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.5.3", + "url": "https://ldtk.io" + }, + "iid": "267e9380-d7b0-11ee-a97e-35bec9c19d52", + "jsonVersion": "1.5.3", + "appBuildId": 473703, + "nextUid": 11, + "identifierStyle": "Capitalize", + "toc": [], + "worldLayout": "Free", + "worldGridWidth": 256, + "worldGridHeight": 256, + "defaultLevelWidth": 256, + "defaultLevelHeight": 256, + "defaultPivotX": 0, + "defaultPivotY": 0, + "defaultGridSize": 32, + "defaultEntityWidth": 32, + "defaultEntityHeight": 32, + "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": "Entities", + "identifier": "Entities", + "type": "Entities", + "uid": 5, + "doc": null, + "uiColor": null, + "gridSize": 32, + "guideGridWid": 0, + "guideGridHei": 0, + "displayOpacity": 1, + "inactiveOpacity": 0.6, + "hideInList": false, + "hideFieldsWhenInactive": true, + "canSelectWhenInactive": true, + "renderInWorldView": true, + "pxOffsetX": 0, + "pxOffsetY": 0, + "parallaxFactorX": 0, + "parallaxFactorY": 0, + "parallaxScaling": true, + "requiredTags": [], + "excludedTags": [], + "autoTilesKilledByOtherLayerUid": null, + "uiFilterTags": [], + "useAsyncRender": false, + "intGridValues": [], + "intGridValuesGroups": [], + "autoRuleGroups": [], + "autoSourceLayerDefUid": null, + "tilesetDefUid": null, + "tilePivotX": 0, + "tilePivotY": 0, + "biomeFieldUid": null + }, + { + "__type": "Tiles", + "identifier": "Tiles", + "type": "Tiles", + "uid": 2, + "doc": null, + "uiColor": null, + "gridSize": 32, + "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": [], + "autoTilesKilledByOtherLayerUid": null, + "uiFilterTags": [], + "useAsyncRender": false, + "intGridValues": [], + "intGridValuesGroups": [], + "autoRuleGroups": [], + "autoSourceLayerDefUid": null, + "tilesetDefUid": 1, + "tilePivotX": 0, + "tilePivotY": 0, + "biomeFieldUid": null + } + ], "entities": [ + { + "identifier": "Player_Primary", + "uid": 3, + "tags": [], + "exportToToc": false, + "allowOutOfBounds": false, + "doc": null, + "width": 32, + "height": 32, + "resizableX": false, + "resizableY": false, + "minWidth": null, + "maxWidth": null, + "minHeight": null, + "maxHeight": null, + "keepAspectRatio": false, + "tileOpacity": 1, + "fillOpacity": 0.08, + "lineOpacity": 0, + "hollow": false, + "color": "#2F3BBE", + "renderMode": "Tile", + "showName": true, + "tilesetId": 1, + "tileRenderMode": "FitInside", + "tileRect": { "tilesetUid": 1, "x": 32, "y": 96, "w": 32, "h": 32 }, + "uiTileRect": null, + "nineSliceBorders": [], + "maxCount": 0, + "limitScope": "PerLevel", + "limitBehavior": "MoveLastOne", + "pivotX": 0, + "pivotY": 0, + "fieldDefs": [] + }, + { + "identifier": "Collectible", + "uid": 4, + "tags": [], + "exportToToc": false, + "allowOutOfBounds": false, + "doc": null, + "width": 32, + "height": 32, + "resizableX": false, + "resizableY": false, + "minWidth": null, + "maxWidth": null, + "minHeight": null, + "maxHeight": null, + "keepAspectRatio": false, + "tileOpacity": 1, + "fillOpacity": 0.08, + "lineOpacity": 0, + "hollow": false, + "color": "#FEAE34", + "renderMode": "Tile", + "showName": true, + "tilesetId": 1, + "tileRenderMode": "FitInside", + "tileRect": { "tilesetUid": 1, "x": 32, "y": 32, "w": 32, "h": 32 }, + "uiTileRect": null, + "nineSliceBorders": [], + "maxCount": 0, + "limitScope": "PerLevel", + "limitBehavior": "MoveLastOne", + "pivotX": 0, + "pivotY": 0, + "fieldDefs": [] + }, + { + "identifier": "Obstacle_West", + "uid": 7, + "tags": [], + "exportToToc": false, + "allowOutOfBounds": false, + "doc": null, + "width": 32, + "height": 32, + "resizableX": false, + "resizableY": false, + "minWidth": null, + "maxWidth": null, + "minHeight": null, + "maxHeight": null, + "keepAspectRatio": false, + "tileOpacity": 1, + "fillOpacity": 0.08, + "lineOpacity": 0, + "hollow": false, + "color": "#BE4A2F", + "renderMode": "Tile", + "showName": true, + "tilesetId": 1, + "tileRenderMode": "FitInside", + "tileRect": { "tilesetUid": 1, "x": 0, "y": 96, "w": 32, "h": 32 }, + "uiTileRect": null, + "nineSliceBorders": [], + "maxCount": 0, + "limitScope": "PerLevel", + "limitBehavior": "MoveLastOne", + "pivotX": 0, + "pivotY": 0, + "fieldDefs": [] + }, + { + "identifier": "Obstacle_East", + "uid": 8, + "tags": [], + "exportToToc": false, + "allowOutOfBounds": false, + "doc": null, + "width": 32, + "height": 32, + "resizableX": false, + "resizableY": false, + "minWidth": null, + "maxWidth": null, + "minHeight": null, + "maxHeight": null, + "keepAspectRatio": false, + "tileOpacity": 1, + "fillOpacity": 0.08, + "lineOpacity": 0, + "hollow": false, + "color": "#D77643", + "renderMode": "Tile", + "showName": true, + "tilesetId": 1, + "tileRenderMode": "FitInside", + "tileRect": { "tilesetUid": 1, "x": 64, "y": 32, "w": 32, "h": 32 }, + "uiTileRect": null, + "nineSliceBorders": [], + "maxCount": 0, + "limitScope": "PerLevel", + "limitBehavior": "MoveLastOne", + "pivotX": 0, + "pivotY": 0, + "fieldDefs": [] + }, + { + "identifier": "Obstacle_North", + "uid": 9, + "tags": [], + "exportToToc": false, + "allowOutOfBounds": false, + "doc": null, + "width": 32, + "height": 32, + "resizableX": false, + "resizableY": false, + "minWidth": null, + "maxWidth": null, + "minHeight": null, + "maxHeight": null, + "keepAspectRatio": false, + "tileOpacity": 1, + "fillOpacity": 0.08, + "lineOpacity": 0, + "hollow": false, + "color": "#F77622", + "renderMode": "Tile", + "showName": true, + "tilesetId": 1, + "tileRenderMode": "FitInside", + "tileRect": { "tilesetUid": 1, "x": 0, "y": 64, "w": 32, "h": 32 }, + "uiTileRect": null, + "nineSliceBorders": [], + "maxCount": 0, + "limitScope": "PerLevel", + "limitBehavior": "MoveLastOne", + "pivotX": 0, + "pivotY": 0, + "fieldDefs": [] + }, + { + "identifier": "Obstacle_South", + "uid": 10, + "tags": [], + "exportToToc": false, + "allowOutOfBounds": false, + "doc": null, + "width": 32, + "height": 32, + "resizableX": false, + "resizableY": false, + "minWidth": null, + "maxWidth": null, + "minHeight": null, + "maxHeight": null, + "keepAspectRatio": false, + "tileOpacity": 1, + "fillOpacity": 0.08, + "lineOpacity": 0, + "hollow": false, + "color": "#E43B44", + "renderMode": "Tile", + "showName": true, + "tilesetId": 1, + "tileRenderMode": "FitInside", + "tileRect": { "tilesetUid": 1, "x": 32, "y": 64, "w": 32, "h": 32 }, + "uiTileRect": null, + "nineSliceBorders": [], + "maxCount": 0, + "limitScope": "PerLevel", + "limitBehavior": "MoveLastOne", + "pivotX": 0, + "pivotY": 0, + "fieldDefs": [] + } + ], "tilesets": [ + { + "__cWid": 3, + "__cHei": 5, + "identifier": "Map2", + "uid": 1, + "relPath": "../sprites/map.png", + "embedAtlas": null, + "pxWid": 96, + "pxHei": 160, + "tileGridSize": 32, + "spacing": 0, + "padding": 0, + "tags": [], + "tagsSourceEnumUid": null, + "enumTags": [], + "customData": [], + "savedSelections": [], + "cachedPixelData": { "opaqueTiles": "110000000000000", "averageColors": "f777f766f766f9533c965c355c355c354b355c35987d687d1b4700000000" } + } + ], "enums": [], "externalEnums": [], "levelFields": [] }, + "levels": [ + { + "identifier": "Level_0", + "iid": "267fa4f0-d7b0-11ee-a97e-d90512e08363", + "uid": 0, + "worldX": -224, + "worldY": 0, + "worldDepth": 0, + "pxWid": 640, + "pxHei": 480, + "__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": "Entities", + "__type": "Entities", + "__cWid": 20, + "__cHei": 15, + "__gridSize": 32, + "__opacity": 1, + "__pxTotalOffsetX": 0, + "__pxTotalOffsetY": 0, + "__tilesetDefUid": null, + "__tilesetRelPath": null, + "iid": "99cd5060-d7b0-11ee-a97e-3f143b461cd1", + "levelId": 0, + "layerDefUid": 5, + "pxOffsetX": 0, + "pxOffsetY": 0, + "visible": true, + "optionalRules": [], + "intGridCsv": [], + "autoLayerTiles": [], + "seed": 1148260, + "overrideTilesetUid": null, + "gridTiles": [], + "entityInstances": [ + { + "__identifier": "Player_Primary", + "__grid": [7,5], + "__pivot": [0,0], + "__tags": [], + "__tile": { "tilesetUid": 1, "x": 32, "y": 96, "w": 32, "h": 32 }, + "__smartColor": "#2F3BBE", + "iid": "c33a2c20-d7b0-11ee-a97e-03409aba0392", + "width": 32, + "height": 32, + "defUid": 3, + "px": [224,160], + "fieldInstances": [], + "__worldX": 0, + "__worldY": 160 + }, + { + "__identifier": "Collectible", + "__grid": [11,7], + "__pivot": [0,0], + "__tags": [], + "__tile": { "tilesetUid": 1, "x": 32, "y": 32, "w": 32, "h": 32 }, + "__smartColor": "#FEAE34", + "iid": "f2b2ff50-d7b0-11ee-98fb-b34c447bc14d", + "width": 32, + "height": 32, + "defUid": 4, + "px": [352,224], + "fieldInstances": [], + "__worldX": 128, + "__worldY": 224 + } + ] + }, + { + "__identifier": "Tiles", + "__type": "Tiles", + "__cWid": 20, + "__cHei": 15, + "__gridSize": 32, + "__opacity": 1, + "__pxTotalOffsetX": 0, + "__pxTotalOffsetY": 0, + "__tilesetDefUid": 1, + "__tilesetRelPath": "../sprites/map.png", + "iid": "3f368c70-d7b0-11ee-a97e-65a98b860ebf", + "levelId": 0, + "layerDefUid": 2, + "pxOffsetX": 0, + "pxOffsetY": 0, + "visible": true, + "optionalRules": [], + "intGridCsv": [], + "autoLayerTiles": [], + "seed": 4985586, + "overrideTilesetUid": null, + "gridTiles": [ + { "px": [192,128], "src": [64,0], "f": 0, "t": 2, "d": [86], "a": 1 }, + { "px": [224,128], "src": [64,0], "f": 0, "t": 2, "d": [87], "a": 1 }, + { "px": [256,128], "src": [64,0], "f": 0, "t": 2, "d": [88], "a": 1 }, + { "px": [288,128], "src": [64,0], "f": 0, "t": 2, "d": [89], "a": 1 }, + { "px": [320,128], "src": [64,0], "f": 0, "t": 2, "d": [90], "a": 1 }, + { "px": [352,128], "src": [64,0], "f": 0, "t": 2, "d": [91], "a": 1 }, + { "px": [384,128], "src": [64,0], "f": 0, "t": 2, "d": [92], "a": 1 }, + { "px": [416,128], "src": [0,0], "f": 0, "t": 0, "d": [93], "a": 1 }, + { "px": [416,128], "src": [64,0], "f": 0, "t": 2, "d": [93], "a": 1 }, + { "px": [192,160], "src": [64,0], "f": 0, "t": 2, "d": [106], "a": 1 }, + { "px": [256,160], "src": [64,0], "f": 0, "t": 2, "d": [108], "a": 1 }, + { "px": [416,160], "src": [64,0], "f": 0, "t": 2, "d": [113], "a": 1 }, + { "px": [192,192], "src": [64,0], "f": 0, "t": 2, "d": [126], "a": 1 }, + { "px": [256,192], "src": [64,0], "f": 0, "t": 2, "d": [128], "a": 1 }, + { "px": [320,192], "src": [64,0], "f": 0, "t": 2, "d": [130], "a": 1 }, + { "px": [352,192], "src": [64,0], "f": 0, "t": 2, "d": [131], "a": 1 }, + { "px": [416,192], "src": [64,0], "f": 0, "t": 2, "d": [133], "a": 1 }, + { "px": [192,224], "src": [64,0], "f": 0, "t": 2, "d": [146], "a": 1 }, + { "px": [256,224], "src": [64,0], "f": 0, "t": 2, "d": [148], "a": 1 }, + { "px": [320,224], "src": [64,0], "f": 0, "t": 2, "d": [150], "a": 1 }, + { "px": [416,224], "src": [64,0], "f": 0, "t": 2, "d": [153], "a": 1 }, + { "px": [192,256], "src": [64,0], "f": 0, "t": 2, "d": [166], "a": 1 }, + { "px": [256,256], "src": [64,0], "f": 0, "t": 2, "d": [168], "a": 1 }, + { "px": [320,256], "src": [64,0], "f": 0, "t": 2, "d": [170], "a": 1 }, + { "px": [416,256], "src": [64,0], "f": 0, "t": 2, "d": [173], "a": 1 }, + { "px": [192,288], "src": [64,0], "f": 0, "t": 2, "d": [186], "a": 1 }, + { "px": [320,288], "src": [64,0], "f": 0, "t": 2, "d": [190], "a": 1 }, + { "px": [416,288], "src": [64,0], "f": 0, "t": 2, "d": [193], "a": 1 }, + { "px": [192,320], "src": [64,0], "f": 0, "t": 2, "d": [206], "a": 1 }, + { "px": [224,320], "src": [64,0], "f": 0, "t": 2, "d": [207], "a": 1 }, + { "px": [256,320], "src": [64,0], "f": 0, "t": 2, "d": [208], "a": 1 }, + { "px": [288,320], "src": [64,0], "f": 0, "t": 2, "d": [209], "a": 1 }, + { "px": [320,320], "src": [64,0], "f": 0, "t": 2, "d": [210], "a": 1 }, + { "px": [352,320], "src": [64,0], "f": 0, "t": 2, "d": [211], "a": 1 }, + { "px": [384,320], "src": [64,0], "f": 0, "t": 2, "d": [212], "a": 1 }, + { "px": [416,320], "src": [64,0], "f": 0, "t": 2, "d": [213], "a": 1 } + ], + "entityInstances": [] + } + ], + "__neighbours": [] + }, + { + "identifier": "_First_Try", + "iid": "024390f0-d7b0-11ee-98fb-1bc6ad7e4172", + "uid": 6, + "worldX": 480, + "worldY": 0, + "worldDepth": 0, + "pxWid": 640, + "pxHei": 480, + "__bgColor": "#696A79", + "bgColor": null, + "useAutoIdentifier": false, + "bgRelPath": "../sprites/background-lila.png", + "bgPos": "Cover", + "bgPivotX": 0.5, + "bgPivotY": 0.5, + "__smartColor": "#ADADB5", + "__bgPos": { "topLeftPx": [0,0], "scale": [1,1], "cropRect": [0,0,640,480] }, + "externalRelPath": null, + "fieldInstances": [], + "layerInstances": [ + { + "__identifier": "Entities", + "__type": "Entities", + "__cWid": 20, + "__cHei": 15, + "__gridSize": 32, + "__opacity": 1, + "__pxTotalOffsetX": 0, + "__pxTotalOffsetY": 0, + "__tilesetDefUid": null, + "__tilesetRelPath": null, + "iid": "0243b800-d7b0-11ee-98fb-f1914bde56c8", + "levelId": 6, + "layerDefUid": 5, + "pxOffsetX": 0, + "pxOffsetY": 0, + "visible": true, + "optionalRules": [], + "intGridCsv": [], + "autoLayerTiles": [], + "seed": 2072933, + "overrideTilesetUid": null, + "gridTiles": [], + "entityInstances": [ + { + "__identifier": "Obstacle_West", + "__grid": [6,8], + "__pivot": [0,0], + "__tags": [], + "__tile": { "tilesetUid": 1, "x": 0, "y": 96, "w": 32, "h": 32 }, + "__smartColor": "#BE4A2F", + "iid": "8aab4e10-d7b0-11ee-98fb-2171f9170515", + "width": 32, + "height": 32, + "defUid": 7, + "px": [192,256], + "fieldInstances": [], + "__worldX": 672, + "__worldY": 256 + }, + { + "__identifier": "Player_Primary", + "__grid": [4,5], + "__pivot": [0,0], + "__tags": [], + "__tile": { "tilesetUid": 1, "x": 32, "y": 96, "w": 32, "h": 32 }, + "__smartColor": "#2F3BBE", + "iid": "8d2c2c90-d7b0-11ee-98fb-d523a27935ba", + "width": 32, + "height": 32, + "defUid": 3, + "px": [128,160], + "fieldInstances": [], + "__worldX": 608, + "__worldY": 160 + }, + { + "__identifier": "Collectible", + "__grid": [4,9], + "__pivot": [0,0], + "__tags": [], + "__tile": { "tilesetUid": 1, "x": 32, "y": 32, "w": 32, "h": 32 }, + "__smartColor": "#FEAE34", + "iid": "a1dfb440-d7b0-11ee-98fb-933f35aeb72b", + "width": 32, + "height": 32, + "defUid": 4, + "px": [128,288], + "fieldInstances": [], + "__worldX": 608, + "__worldY": 288 + }, + { + "__identifier": "Collectible", + "__grid": [14,6], + "__pivot": [0,0], + "__tags": [], + "__tile": { "tilesetUid": 1, "x": 32, "y": 32, "w": 32, "h": 32 }, + "__smartColor": "#FEAE34", + "iid": "f7ec8340-d7b0-11ee-98fb-1fa75c7ce959", + "width": 32, + "height": 32, + "defUid": 4, + "px": [448,192], + "fieldInstances": [], + "__worldX": 928, + "__worldY": 192 + }, + { + "__identifier": "Obstacle_South", + "__grid": [15,6], + "__pivot": [0,0], + "__tags": [], + "__tile": { "tilesetUid": 1, "x": 32, "y": 64, "w": 32, "h": 32 }, + "__smartColor": "#E43B44", + "iid": "45c2d010-d7b0-11ee-98fb-13d8f32ffff0", + "width": 32, + "height": 32, + "defUid": 10, + "px": [480,192], + "fieldInstances": [], + "__worldX": 960, + "__worldY": 192 + } + ] + }, + { + "__identifier": "Tiles", + "__type": "Tiles", + "__cWid": 20, + "__cHei": 15, + "__gridSize": 32, + "__opacity": 1, + "__pxTotalOffsetX": 0, + "__pxTotalOffsetY": 0, + "__tilesetDefUid": 1, + "__tilesetRelPath": "../sprites/map.png", + "iid": "0243b801-d7b0-11ee-98fb-79b1102fb094", + "levelId": 6, + "layerDefUid": 2, + "pxOffsetX": 0, + "pxOffsetY": 0, + "visible": true, + "optionalRules": [], + "intGridCsv": [], + "autoLayerTiles": [], + "seed": 2952425, + "overrideTilesetUid": null, + "gridTiles": [ + { "px": [96,128], "src": [64,0], "f": 0, "t": 2, "d": [83], "a": 1 }, + { "px": [128,128], "src": [64,0], "f": 0, "t": 2, "d": [84], "a": 1 }, + { "px": [160,128], "src": [64,0], "f": 0, "t": 2, "d": [85], "a": 1 }, + { "px": [192,128], "src": [64,0], "f": 0, "t": 2, "d": [86], "a": 1 }, + { "px": [224,128], "src": [64,0], "f": 0, "t": 2, "d": [87], "a": 1 }, + { "px": [384,128], "src": [64,0], "f": 0, "t": 2, "d": [92], "a": 1 }, + { "px": [416,128], "src": [64,0], "f": 0, "t": 2, "d": [93], "a": 1 }, + { "px": [448,128], "src": [64,0], "f": 0, "t": 2, "d": [94], "a": 1 }, + { "px": [480,128], "src": [64,0], "f": 0, "t": 2, "d": [95], "a": 1 }, + { "px": [512,128], "src": [64,0], "f": 0, "t": 2, "d": [96], "a": 1 }, + { "px": [96,160], "src": [64,0], "f": 0, "t": 2, "d": [103], "a": 1 }, + { "px": [224,160], "src": [64,0], "f": 0, "t": 2, "d": [107], "a": 1 }, + { "px": [384,160], "src": [64,0], "f": 0, "t": 2, "d": [112], "a": 1 }, + { "px": [512,160], "src": [64,0], "f": 0, "t": 2, "d": [116], "a": 1 }, + { "px": [96,192], "src": [64,0], "f": 0, "t": 2, "d": [123], "a": 1 }, + { "px": [224,192], "src": [64,0], "f": 0, "t": 2, "d": [127], "a": 1 }, + { "px": [384,192], "src": [64,0], "f": 0, "t": 2, "d": [132], "a": 1 }, + { "px": [512,192], "src": [64,0], "f": 0, "t": 2, "d": [136], "a": 1 }, + { "px": [224,224], "src": [64,0], "f": 0, "t": 2, "d": [147], "a": 1 }, + { "px": [384,224], "src": [64,0], "f": 0, "t": 2, "d": [152], "a": 1 }, + { "px": [224,256], "src": [64,0], "f": 0, "t": 2, "d": [167], "a": 1 }, + { "px": [384,256], "src": [64,0], "f": 0, "t": 2, "d": [172], "a": 1 }, + { "px": [448,256], "src": [64,0], "f": 0, "t": 2, "d": [174], "a": 1 }, + { "px": [224,288], "src": [64,0], "f": 0, "t": 2, "d": [187], "a": 1 }, + { "px": [384,288], "src": [64,0], "f": 0, "t": 2, "d": [192], "a": 1 }, + { "px": [448,288], "src": [64,0], "f": 0, "t": 2, "d": [194], "a": 1 }, + { "px": [96,320], "src": [64,0], "f": 0, "t": 2, "d": [203], "a": 1 }, + { "px": [128,320], "src": [64,0], "f": 0, "t": 2, "d": [204], "a": 1 }, + { "px": [160,320], "src": [64,0], "f": 0, "t": 2, "d": [205], "a": 1 }, + { "px": [192,320], "src": [64,0], "f": 0, "t": 2, "d": [206], "a": 1 }, + { "px": [224,320], "src": [64,0], "f": 0, "t": 2, "d": [207], "a": 1 }, + { "px": [384,320], "src": [64,0], "f": 0, "t": 2, "d": [212], "a": 1 }, + { "px": [416,320], "src": [64,0], "f": 0, "t": 2, "d": [213], "a": 1 }, + { "px": [448,320], "src": [64,0], "f": 0, "t": 2, "d": [214], "a": 1 }, + { "px": [480,320], "src": [64,0], "f": 0, "t": 2, "d": [215], "a": 1 }, + { "px": [512,320], "src": [64,0], "f": 0, "t": 2, "d": [216], "a": 1 } + ], + "entityInstances": [] + } + ], + "__neighbours": [] + } + ], + "worlds": [], + "dummyWorldIid": "267ee1a0-d7b0-11ee-a97e-53f0a359eae1" +} \ No newline at end of file diff --git a/assets/loader-levels.go b/assets/loader-levels.go index 32aabca..3478994 100644 --- a/assets/loader-levels.go +++ b/assets/loader-levels.go @@ -1,23 +1,17 @@ package assets import ( - "bufio" _ "image/png" - "io/fs" "log" - "log/slog" "openquell/config" "openquell/util" - "os" - "path/filepath" - "sort" - "strconv" "strings" "github.com/hajimehoshi/ebiten/v2" + "github.com/solarlune/ldtkgo" ) -var Levels = LoadLevels("levels") +var Project = LoadLDTK("levels") var Tiles = InitTiles() // Tile: contains image, identifier (as used in level data) and @@ -186,44 +180,22 @@ func NewTileHiddenDoor(class []string) *Tile { } // used to map level data bytes to actual tiles -type TileRegistry map[byte]*Tile - -// holds a raw level spec: -// -// Name: the name of the level file w/o the .lvl extension -// Background: an image name used as game background for this level -// Description: text to display on top -// Data: a level spec consisting of chars of the above mapping and spaces, e.g.: -// #### -// # # -// #### -// -// Each level data must be 20 chars wide (= 640 px width) and 15 chars -// high (=480 px height). -type RawLevel struct { - Number int - Name string - Description string - Background *ebiten.Image - Data []byte - MinMoves int -} +type TileRegistry map[string]*Tile func InitTiles() TileRegistry { return TileRegistry{ - ' ': {Id: ' ', Class: "floor", Renderable: false}, - //'#': NewTileBlock("block-grey32"), - '#': NewTileBlock("block-greycolored"), - 'B': NewTileBlock("block-orange-32"), - 'S': NewTilePlayer(Primary), - 's': NewTilePlayer(Secondary), - 'o': NewTileCollectible("collectible-orange"), - '+': NewTileObstacle("obstacle-star", config.All), - '^': NewTileObstacle("obstacle-north", config.North), - 'v': NewTileObstacle("obstacle-south", config.South), - '<': NewTileObstacle("obstacle-west", config.West), - '>': NewTileObstacle("obstacle-east", config.East), - '*': NewTileParticle([]string{ + "floor": {Id: ' ', Class: "floor", Renderable: false}, + "default": NewTileBlock("block-greycolored"), + "solidorange": NewTileBlock("block-orange-32"), + "player-primary": NewTilePlayer(Primary), + "player-secondary": NewTilePlayer(Secondary), + "collectible": NewTileCollectible("collectible-orange"), + "obstacle-star": NewTileObstacle("obstacle-star", config.All), + "obstacle-north": NewTileObstacle("obstacle-north", config.North), + "obstacle-south": NewTileObstacle("obstacle-south", config.South), + "obstacle-west": NewTileObstacle("obstacle-west", config.West), + "obstacle-east": NewTileObstacle("obstacle-east", config.East), + "particle": NewTileParticle([]string{ //"particle-ring-1", "particle-ring-2", "particle-ring-3", @@ -231,91 +203,56 @@ func InitTiles() TileRegistry { "particle-ring-5", "particle-ring-6", }), - 't': NewTileTranswall([]string{"transwall", "block-orange-32"}), - 'W': NewTileHiddenDoor([]string{"block-greycolored", "block-greycolored-damaged"}), + "transient": NewTileTranswall([]string{"transwall", "block-orange-32"}), + "hiddendoor": NewTileHiddenDoor([]string{"block-greycolored", "block-greycolored-damaged"}), } } -// load levels at compile time into ram, creates a slice of raw levels -func LoadLevels(dir string) []RawLevel { - levels := []RawLevel{} - - // we use embed.FS to iterate over all files in ./levels/ - entries, err := assetfs.ReadDir(dir) +// load LDTK project at compile time into ram +func LoadLDTK(dir string) *ldtkgo.Project { + fd, err := assetfs.Open("levels/openquell.ldtk") if err != nil { - log.Fatalf("failed to read level dir %s: %s", dir, err) - } - - sort.Slice(entries, func(i, j int) bool { - return entries[i].Name() < entries[j].Name() - }) - - for idx, levelfile := range entries { - if levelfile.Type().IsRegular() && strings.Contains(levelfile.Name(), ".lvl") { - path := filepath.Join("assets", dir) - level := ParseRawLevel(path, levelfile) - level.Number = idx - - levels = append(levels, level) - - slog.Debug("loaded level", "path", path, "file", levelfile) - } - } - - return levels -} - -func ParseRawLevel(dir string, levelfile fs.DirEntry) RawLevel { - fd, err := os.Open(filepath.Join(dir, levelfile.Name())) - if err != nil { - log.Fatalf("failed to read level file %s: %s", levelfile.Name(), err) + log.Fatalf("failed to open LDTK file levels/openquell.ldtk: %s", err) } defer fd.Close() - name := strings.TrimSuffix(levelfile.Name(), ".lvl") - name = name[3:] - des := "" - background := &ebiten.Image{} - data := []byte{} - minmoves := 0 + fileinfo, err := fd.Stat() + if err != nil { + log.Fatalf("failed to stat() LDTK file levels/openquell.ldtk: %s", err) + } - scanner := bufio.NewScanner(fd) - for scanner.Scan() { - // ignore any whitespace - line := scanner.Text() + filesize := fileinfo.Size() + buffer := make([]byte, filesize) - // ignore empty lines - if len(line) == 0 { - continue + _, err = fd.Read(buffer) + if err != nil { + log.Fatalf("failed to read bytes from LDTK file levels/openquell.ldtk: %s", err) + } + + project, err := ldtkgo.Read(buffer) + if err != nil { + panic(err) + } + + // do some sanity checks + properties := []string{"min-moves", "background", "level", "name", "descrption"} + need := len(properties) + + for idx, level := range project.Levels { + have := 0 + + for _, property := range level.Properties { + if util.Contains(properties, property.Identifier) { + have++ + } } - switch { - case strings.Contains(line, "Background:"): - haveit := strings.Split(line, ": ") - if util.Exists(Assets, haveit[1]) { - background = Assets[haveit[1]] - } - case strings.Contains(line, "Description:"): - haveit := strings.Split(line, ": ") - des = haveit[1] - case strings.Contains(line, "MinMoves:"): - haveit := strings.Split(line, ": ") - minmoves, err = strconv.Atoi(haveit[1]) - if err != nil { - log.Fatal("Failed to convert MinMoves to int: %w", err) - } - default: - // all other non-empty and non-equalsign lines are - // level definition matrix data, merge thes into data - data = append(data, line+"\n"...) + if have != need { + log.Fatalf("level definition for level %d (%s) invalid: %d missing properties\n required: %s", + idx, level.Identifier, need-have, strings.Join(properties, ", "), + ) } } - return RawLevel{ - Name: name, - Data: data, - Background: background, - Description: des, - MinMoves: minmoves, - } + return project } diff --git a/assets/loader-sprites.go b/assets/loader-sprites.go index 7a6fa4c..1bf0fa8 100644 --- a/assets/loader-sprites.go +++ b/assets/loader-sprites.go @@ -16,7 +16,7 @@ import ( // Maps image name to image data type AssetRegistry map[string]*ebiten.Image -//go:embed levels/*.lvl sprites/*.png fonts/*.ttf +//go:embed sprites/*.png fonts/*.ttf levels/*.ldtk var assetfs embed.FS var Assets = LoadImages("sprites") diff --git a/assets/sprites/block-grey.png b/assets/sprites/block-grey.png deleted file mode 100644 index 87614a62facec8e4d23978215682dc3d3a759adb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 584 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 T4aoTK4|viN3gXqGMuGnWMTTZ1 literal 0 HcmV?d00001 diff --git a/game/level_scene.go b/game/level_scene.go index 106eed0..dc5a52e 100644 --- a/game/level_scene.go +++ b/game/level_scene.go @@ -28,10 +28,11 @@ func NewLevelScene(game *Game, startlevel int) Scene { func (scene *LevelScene) GenerateLevels(game *Game) { min := []int{} - for _, level := range assets.Levels { + for _, level := range assets.Project.Levels { level := level - scene.Levels = append(scene.Levels, NewLevel(game, 32, &level)) - min = append(min, level.MinMoves) + scene.Levels = append(scene.Levels, NewLevel(game, 32, level)) + level.PropertyByIdentifier("min-moves") + min = append(min, level.PropertyByIdentifier("min-moves").AsInt()) } scene.Game.Observer.SetupLevelScore(min) diff --git a/game/levels.go b/game/levels.go index ba4e3c0..eb2bf62 100644 --- a/game/levels.go +++ b/game/levels.go @@ -2,18 +2,17 @@ package game import ( "image" - "log" "log/slog" "openquell/assets" "openquell/components" "openquell/grid" "openquell/observers" "openquell/systems" - "openquell/util" "strings" "github.com/hajimehoshi/ebiten/v2" "github.com/mlange-42/arche/ecs" + "github.com/solarlune/ldtkgo" ) type Map map[image.Point]*assets.Tile @@ -31,13 +30,14 @@ type Level struct { Grid *grid.Grid } -func NewLevel(game *Game, cellsize int, plan *assets.RawLevel) *Level { +func NewLevel(game *Game, cellsize int, plan *ldtkgo.Level) *Level { systemlist := []systems.System{} gridcontainer := &grid.GridContainer{} systemlist = append(systemlist, - systems.NewGridSystem(game.World, game.ScreenWidth, game.ScreenHeight, cellsize, plan.Background)) + systems.NewGridSystem(game.World, game.ScreenWidth, game.ScreenHeight, cellsize, + assets.Assets[plan.PropertyByIdentifier("background").AsString()])) systemlist = append(systemlist, systems.NewCollectibleSystem(game.World)) @@ -63,8 +63,8 @@ func NewLevel(game *Game, cellsize int, plan *assets.RawLevel) *Level { World: game.World, Width: game.ScreenWidth, Height: game.ScreenHeight, - Description: plan.Description, - Name: plan.Name, + Description: plan.PropertyByIdentifier("description").AsString(), + Name: plan.Identifier, GridContainer: gridcontainer, Systems: systemlist, } @@ -113,25 +113,51 @@ func (level *Level) SetupGrid(game *Game) { } // parses a RawLevel and generates a mapslice from it, which is being used as grid -func LevelToSlice(game *Game, level *assets.RawLevel, tilesize int) (Map, Map) { +func LevelToSlice(game *Game, level *ldtkgo.Level, tilesize int) (Map, Map) { size := game.ScreenWidth * game.ScreenHeight mapslice := make(Map, size) backupmap := make(Map, size) - for y, line := range strings.Split(string(level.Data), "\n") { - if len(line) != game.ScreenWidth/tilesize && y < game.ScreenHeight/tilesize { - log.Fatalf("line %d doesn't contain %d tiles, but %d", - y, game.ScreenWidth/tilesize, len(line)) - } + for _, layer := range level.Layers { + switch layer.Type { + case ldtkgo.LayerTypeTile: + // load tile from LDTK tile layer, use sprites from associated map. - for x, char := range line { - if !util.Exists(assets.Tiles, byte(char)) { - log.Fatalf("unregistered tile type %c encountered", char) + if tiles := layer.AllTiles(); len(tiles) > 0 { + for _, tileData := range tiles { + // Subimage the Tile from the already loaded map, + // but referenced from LDTK file, that way we + // could use multiple tileset images + tile := assets.Tiles["default"] + tile.Sprite = assets.Assets[strings.TrimSuffix(layer.Tileset.Path, ".png")].SubImage( + image.Rect(tileData.Src[0], + tileData.Src[1], + tileData.Src[0]+layer.GridSize, + tileData.Src[1]+layer.GridSize)).(*ebiten.Image) + + mapslice[image.Point{tileData.Position[0], tileData.Position[1]}] = tile + backupmap[image.Point{tileData.Position[0], tileData.Position[1]}] = tile.Clone() + } } - tile := assets.Tiles[byte(char)] - mapslice[image.Point{x, y}] = tile - backupmap[image.Point{x, y}] = tile.Clone() + case ldtkgo.LayerTypeEntity: + // load mobile tiles (they call them entities) using static map map.png. + tileset := assets.Assets["map"] + + for _, entity := range layer.Entities { + if entity.TileRect != nil { + tile := assets.Tiles[entity.Identifier] + + tileRect := entity.TileRect + tile.Sprite = tileset.SubImage( + image.Rect(tileRect.X, tileRect.Y, + tileRect.X+tileRect.W, + tileRect.Y+tileRect.H)).(*ebiten.Image) + + mapslice[image.Point{entity.Position[0], entity.Position[1]}] = tile + backupmap[image.Point{entity.Position[0], entity.Position[1]}] = tile.Clone() + } + } } } diff --git a/go.mod b/go.mod index ae5ab37..954b741 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,11 @@ require ( github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect + github.com/solarlune/ldtkgo v0.9.4-0.20240310011150-66aa15c2ab56 // indirect github.com/spf13/pflag v1.0.5 // indirect + github.com/tidwall/gjson v1.9.3 // indirect + github.com/tidwall/match v1.1.1 // indirect + github.com/tidwall/pretty v1.2.0 // indirect github.com/tinne26/etxt v0.0.8 // indirect github.com/tlinden/yadu v0.1.3 // indirect golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 // indirect diff --git a/go.sum b/go.sum index 72da15c..c0dff75 100644 --- a/go.sum +++ b/go.sum @@ -253,6 +253,8 @@ github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/solarlune/ldtkgo v0.9.4-0.20240310011150-66aa15c2ab56 h1:QW8w9YQbIlIW053jM2SfBKAFGyd4maoq0AawZsb9rO4= +github.com/solarlune/ldtkgo v0.9.4-0.20240310011150-66aa15c2ab56/go.mod h1:PP4XtlnCSwWo7iexI/NJ3aS3INmiT42o066o6Dx52rs= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -262,6 +264,12 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/tidwall/gjson v1.9.3 h1:hqzS9wAHMO+KVBBkLxYdkEeeFHuqr95GfClRLKlgK0E= +github.com/tidwall/gjson v1.9.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tinne26/etxt v0.0.8 h1:rjb58jkMkapRGLmhBMWnT76E/nMTXC5P1Q956BRZkoc= github.com/tinne26/etxt v0.0.8/go.mod h1:QM/hlNkstsKC39elTFNKAR34xsMb9QoVosf+g9wlYxM= github.com/tlinden/yadu v0.1.2 h1:TYYVnUJwziRJ9YPbIbRf9ikmDw0Q8Ifixm+J/kBQFh8= diff --git a/grid/grid.go b/grid/grid.go index 3dec702..658e451 100644 --- a/grid/grid.go +++ b/grid/grid.go @@ -158,7 +158,7 @@ func (grid *Grid) RemoveTile(point image.Point) { } func (grid *Grid) SetFloorTile(point image.Point) { - grid.Map[point] = assets.Tiles[' '] + grid.Map[point] = assets.Tiles["floor"] } func (grid *Grid) SetSolidTile(tile *assets.Tile, point image.Point) { diff --git a/systems/collectible_system.go b/systems/collectible_system.go index 43b570d..370a133 100644 --- a/systems/collectible_system.go +++ b/systems/collectible_system.go @@ -114,8 +114,8 @@ func (system *CollectibleSystem) AddParticle(position *components.Position) { pos, particle, timer := ptmapper.Get(entity) observer.AddEntity(entity, particleID) - particle.Index = assets.Tiles['*'].Particle - particle.Tiles = assets.Tiles['*'].Tiles + particle.Index = assets.Tiles["particle"].Particle + particle.Tiles = assets.Tiles["particle"].Tiles pos.Update( position.X-(16), // FIXME: use global tilesize! diff --git a/systems/hud_system.go b/systems/hud_system.go index 64c9727..698af9d 100644 --- a/systems/hud_system.go +++ b/systems/hud_system.go @@ -8,16 +8,17 @@ import ( "github.com/hajimehoshi/ebiten/v2" "github.com/mlange-42/arche/ecs" + "github.com/solarlune/ldtkgo" ) type HudSystem struct { World *ecs.World Cellsize int Observer *observers.GameObserver - Plan *assets.RawLevel + Plan *ldtkgo.Level } -func NewHudSystem(world *ecs.World, plan *assets.RawLevel) System { +func NewHudSystem(world *ecs.World, plan *ldtkgo.Level) System { system := &HudSystem{ Observer: observers.GetGameObserver(world), World: world, @@ -45,13 +46,14 @@ func (system *HudSystem) Draw(screen *ebiten.Image) { */ score := fmt.Sprintf("Score: %d", system.Observer.GetScore()) - level := fmt.Sprintf("Level %d %s", system.Plan.Number, system.Plan.Name) + level := fmt.Sprintf("Level %d %s", system.Plan.PropertyByIdentifier("level").AsInt(), + system.Plan.PropertyByIdentifier("name").AsString()) assets.FontRenderer.Renderer.SetSizePx(20) assets.FontRenderer.Renderer.SetTarget(screen) system.Print(score, 515, 22) - system.Print(system.Plan.Description, 10, 470) + system.Print(system.Plan.PropertyByIdentifier("description").AsString(), 10, 470) system.Print(level, 10, 22) } diff --git a/systems/obstacle_system.go b/systems/obstacle_system.go index 7c607a5..d08d2fb 100644 --- a/systems/obstacle_system.go +++ b/systems/obstacle_system.go @@ -2,7 +2,6 @@ package systems import ( "log/slog" - "openquell/assets" "openquell/components" . "openquell/components" . "openquell/config" @@ -146,33 +145,6 @@ func (system *ObstacleSystem) Draw(screen *ebiten.Image) { } } -func (system *ObstacleSystem) AddParticle(position *components.Position) { - observer := observers.GetGameObserver(system.World) - - ptmapper := generic.NewMap3[ - components.Position, - components.Particle, - components.Timer, - ](system.World) - - particleID := ecs.ComponentID[components.Particle](system.World) - - entity := ptmapper.New() - pos, particle, timer := ptmapper.Get(entity) - observer.AddEntity(entity, particleID) - - particle.Index = assets.Tiles['*'].Particle - particle.Tiles = assets.Tiles['*'].Tiles - - pos.Update( - position.X-(16), // FIXME: use global tilesize! - position.Y-(16), - 64, - ) - - timer.Start(PARTICLE_LOOPWAIT) -} - // return true if obstacle weapon points into the direction the player is moving func CheckObstacleSide(playervelocity *Velocity, obsdirection int) bool { movingdirection := playervelocity.InvertDirection()