shifted from proprietary ascii rawlevel to LDTK (using ldtkgo@master)
This commit is contained in:
parent
a2ed7782b2
commit
af19ccb833
8
TODO.md
8
TODO.md
@ -22,6 +22,14 @@
|
|||||||
slog.Debug("get observer", "minmoves", observer.LevelScore,
|
slog.Debug("get observer", "minmoves", observer.LevelScore,
|
||||||
"file", source.File, "line", source.Line)
|
"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]
|
## Collider Rework [abandoned: see branch collider-system, fails]
|
||||||
|
|
||||||
- do not use the map anymore for collision detection
|
- do not use the map anymore for collision detection
|
||||||
|
|||||||
686
assets/levels/openquell.ldtk
Normal file
686
assets/levels/openquell.ldtk
Normal file
@ -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"
|
||||||
|
}
|
||||||
@ -1,23 +1,17 @@
|
|||||||
package assets
|
package assets
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
_ "image/png"
|
_ "image/png"
|
||||||
"io/fs"
|
|
||||||
"log"
|
"log"
|
||||||
"log/slog"
|
|
||||||
"openquell/config"
|
"openquell/config"
|
||||||
"openquell/util"
|
"openquell/util"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"sort"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/hajimehoshi/ebiten/v2"
|
"github.com/hajimehoshi/ebiten/v2"
|
||||||
|
"github.com/solarlune/ldtkgo"
|
||||||
)
|
)
|
||||||
|
|
||||||
var Levels = LoadLevels("levels")
|
var Project = LoadLDTK("levels")
|
||||||
var Tiles = InitTiles()
|
var Tiles = InitTiles()
|
||||||
|
|
||||||
// Tile: contains image, identifier (as used in level data) and
|
// 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
|
// used to map level data bytes to actual tiles
|
||||||
type TileRegistry map[byte]*Tile
|
type TileRegistry map[string]*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
|
|
||||||
}
|
|
||||||
|
|
||||||
func InitTiles() TileRegistry {
|
func InitTiles() TileRegistry {
|
||||||
return TileRegistry{
|
return TileRegistry{
|
||||||
' ': {Id: ' ', Class: "floor", Renderable: false},
|
"floor": {Id: ' ', Class: "floor", Renderable: false},
|
||||||
//'#': NewTileBlock("block-grey32"),
|
"default": NewTileBlock("block-greycolored"),
|
||||||
'#': NewTileBlock("block-greycolored"),
|
"solidorange": NewTileBlock("block-orange-32"),
|
||||||
'B': NewTileBlock("block-orange-32"),
|
"player-primary": NewTilePlayer(Primary),
|
||||||
'S': NewTilePlayer(Primary),
|
"player-secondary": NewTilePlayer(Secondary),
|
||||||
's': NewTilePlayer(Secondary),
|
"collectible": NewTileCollectible("collectible-orange"),
|
||||||
'o': NewTileCollectible("collectible-orange"),
|
"obstacle-star": NewTileObstacle("obstacle-star", config.All),
|
||||||
'+': NewTileObstacle("obstacle-star", config.All),
|
"obstacle-north": NewTileObstacle("obstacle-north", config.North),
|
||||||
'^': NewTileObstacle("obstacle-north", config.North),
|
"obstacle-south": NewTileObstacle("obstacle-south", config.South),
|
||||||
'v': NewTileObstacle("obstacle-south", config.South),
|
"obstacle-west": NewTileObstacle("obstacle-west", config.West),
|
||||||
'<': NewTileObstacle("obstacle-west", config.West),
|
"obstacle-east": NewTileObstacle("obstacle-east", config.East),
|
||||||
'>': NewTileObstacle("obstacle-east", config.East),
|
"particle": NewTileParticle([]string{
|
||||||
'*': NewTileParticle([]string{
|
|
||||||
//"particle-ring-1",
|
//"particle-ring-1",
|
||||||
"particle-ring-2",
|
"particle-ring-2",
|
||||||
"particle-ring-3",
|
"particle-ring-3",
|
||||||
@ -231,91 +203,56 @@ func InitTiles() TileRegistry {
|
|||||||
"particle-ring-5",
|
"particle-ring-5",
|
||||||
"particle-ring-6",
|
"particle-ring-6",
|
||||||
}),
|
}),
|
||||||
't': NewTileTranswall([]string{"transwall", "block-orange-32"}),
|
"transient": NewTileTranswall([]string{"transwall", "block-orange-32"}),
|
||||||
'W': NewTileHiddenDoor([]string{"block-greycolored", "block-greycolored-damaged"}),
|
"hiddendoor": NewTileHiddenDoor([]string{"block-greycolored", "block-greycolored-damaged"}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// load levels at compile time into ram, creates a slice of raw levels
|
// load LDTK project at compile time into ram
|
||||||
func LoadLevels(dir string) []RawLevel {
|
func LoadLDTK(dir string) *ldtkgo.Project {
|
||||||
levels := []RawLevel{}
|
fd, err := assetfs.Open("levels/openquell.ldtk")
|
||||||
|
|
||||||
// we use embed.FS to iterate over all files in ./levels/
|
|
||||||
entries, err := assetfs.ReadDir(dir)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("failed to read level dir %s: %s", dir, err)
|
log.Fatalf("failed to open LDTK file levels/openquell.ldtk: %s", 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)
|
|
||||||
}
|
}
|
||||||
defer fd.Close()
|
defer fd.Close()
|
||||||
|
|
||||||
name := strings.TrimSuffix(levelfile.Name(), ".lvl")
|
fileinfo, err := fd.Stat()
|
||||||
name = name[3:]
|
if err != nil {
|
||||||
des := ""
|
log.Fatalf("failed to stat() LDTK file levels/openquell.ldtk: %s", err)
|
||||||
background := &ebiten.Image{}
|
}
|
||||||
data := []byte{}
|
|
||||||
minmoves := 0
|
|
||||||
|
|
||||||
scanner := bufio.NewScanner(fd)
|
filesize := fileinfo.Size()
|
||||||
for scanner.Scan() {
|
buffer := make([]byte, filesize)
|
||||||
// ignore any whitespace
|
|
||||||
line := scanner.Text()
|
|
||||||
|
|
||||||
// ignore empty lines
|
_, err = fd.Read(buffer)
|
||||||
if len(line) == 0 {
|
if err != nil {
|
||||||
continue
|
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 {
|
if have != need {
|
||||||
case strings.Contains(line, "Background:"):
|
log.Fatalf("level definition for level %d (%s) invalid: %d missing properties\n required: %s",
|
||||||
haveit := strings.Split(line, ": ")
|
idx, level.Identifier, need-have, strings.Join(properties, ", "),
|
||||||
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"...)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return RawLevel{
|
return project
|
||||||
Name: name,
|
|
||||||
Data: data,
|
|
||||||
Background: background,
|
|
||||||
Description: des,
|
|
||||||
MinMoves: minmoves,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,7 +16,7 @@ import (
|
|||||||
// Maps image name to image data
|
// Maps image name to image data
|
||||||
type AssetRegistry map[string]*ebiten.Image
|
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 assetfs embed.FS
|
||||||
|
|
||||||
var Assets = LoadImages("sprites")
|
var Assets = LoadImages("sprites")
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 584 B |
2
assets/sprites/makemap.sh
Normal file
2
assets/sprites/makemap.sh
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
montage -tile 4x0 -geometry +0+0 block* collectible-orange.png obstacle-* sphere-blue* transwall.png map.png && okular map.png
|
||||||
BIN
assets/sprites/map.png
Normal file
BIN
assets/sprites/map.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
@ -28,10 +28,11 @@ func NewLevelScene(game *Game, startlevel int) Scene {
|
|||||||
|
|
||||||
func (scene *LevelScene) GenerateLevels(game *Game) {
|
func (scene *LevelScene) GenerateLevels(game *Game) {
|
||||||
min := []int{}
|
min := []int{}
|
||||||
for _, level := range assets.Levels {
|
for _, level := range assets.Project.Levels {
|
||||||
level := level
|
level := level
|
||||||
scene.Levels = append(scene.Levels, NewLevel(game, 32, &level))
|
scene.Levels = append(scene.Levels, NewLevel(game, 32, level))
|
||||||
min = append(min, level.MinMoves)
|
level.PropertyByIdentifier("min-moves")
|
||||||
|
min = append(min, level.PropertyByIdentifier("min-moves").AsInt())
|
||||||
}
|
}
|
||||||
|
|
||||||
scene.Game.Observer.SetupLevelScore(min)
|
scene.Game.Observer.SetupLevelScore(min)
|
||||||
|
|||||||
@ -2,18 +2,17 @@ package game
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"image"
|
"image"
|
||||||
"log"
|
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"openquell/assets"
|
"openquell/assets"
|
||||||
"openquell/components"
|
"openquell/components"
|
||||||
"openquell/grid"
|
"openquell/grid"
|
||||||
"openquell/observers"
|
"openquell/observers"
|
||||||
"openquell/systems"
|
"openquell/systems"
|
||||||
"openquell/util"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/hajimehoshi/ebiten/v2"
|
"github.com/hajimehoshi/ebiten/v2"
|
||||||
"github.com/mlange-42/arche/ecs"
|
"github.com/mlange-42/arche/ecs"
|
||||||
|
"github.com/solarlune/ldtkgo"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Map map[image.Point]*assets.Tile
|
type Map map[image.Point]*assets.Tile
|
||||||
@ -31,13 +30,14 @@ type Level struct {
|
|||||||
Grid *grid.Grid
|
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{}
|
systemlist := []systems.System{}
|
||||||
|
|
||||||
gridcontainer := &grid.GridContainer{}
|
gridcontainer := &grid.GridContainer{}
|
||||||
|
|
||||||
systemlist = append(systemlist,
|
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))
|
systemlist = append(systemlist, systems.NewCollectibleSystem(game.World))
|
||||||
|
|
||||||
@ -63,8 +63,8 @@ func NewLevel(game *Game, cellsize int, plan *assets.RawLevel) *Level {
|
|||||||
World: game.World,
|
World: game.World,
|
||||||
Width: game.ScreenWidth,
|
Width: game.ScreenWidth,
|
||||||
Height: game.ScreenHeight,
|
Height: game.ScreenHeight,
|
||||||
Description: plan.Description,
|
Description: plan.PropertyByIdentifier("description").AsString(),
|
||||||
Name: plan.Name,
|
Name: plan.Identifier,
|
||||||
GridContainer: gridcontainer,
|
GridContainer: gridcontainer,
|
||||||
Systems: systemlist,
|
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
|
// 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
|
size := game.ScreenWidth * game.ScreenHeight
|
||||||
mapslice := make(Map, size)
|
mapslice := make(Map, size)
|
||||||
backupmap := make(Map, size)
|
backupmap := make(Map, size)
|
||||||
|
|
||||||
for y, line := range strings.Split(string(level.Data), "\n") {
|
for _, layer := range level.Layers {
|
||||||
if len(line) != game.ScreenWidth/tilesize && y < game.ScreenHeight/tilesize {
|
switch layer.Type {
|
||||||
log.Fatalf("line %d doesn't contain %d tiles, but %d",
|
case ldtkgo.LayerTypeTile:
|
||||||
y, game.ScreenWidth/tilesize, len(line))
|
// load tile from LDTK tile layer, use sprites from associated map.
|
||||||
}
|
|
||||||
|
|
||||||
for x, char := range line {
|
if tiles := layer.AllTiles(); len(tiles) > 0 {
|
||||||
if !util.Exists(assets.Tiles, byte(char)) {
|
for _, tileData := range tiles {
|
||||||
log.Fatalf("unregistered tile type %c encountered", char)
|
// 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)]
|
case ldtkgo.LayerTypeEntity:
|
||||||
mapslice[image.Point{x, y}] = tile
|
// load mobile tiles (they call them entities) using static map map.png.
|
||||||
backupmap[image.Point{x, y}] = tile.Clone()
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
4
go.mod
4
go.mod
@ -22,7 +22,11 @@ require (
|
|||||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
github.com/mitchellh/reflectwalk v1.0.2 // 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/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/tinne26/etxt v0.0.8 // indirect
|
||||||
github.com/tlinden/yadu v0.1.3 // indirect
|
github.com/tlinden/yadu v0.1.3 // indirect
|
||||||
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 // indirect
|
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 // indirect
|
||||||
|
|||||||
8
go.sum
8
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.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
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/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 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
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.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.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
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 h1:rjb58jkMkapRGLmhBMWnT76E/nMTXC5P1Q956BRZkoc=
|
||||||
github.com/tinne26/etxt v0.0.8/go.mod h1:QM/hlNkstsKC39elTFNKAR34xsMb9QoVosf+g9wlYxM=
|
github.com/tinne26/etxt v0.0.8/go.mod h1:QM/hlNkstsKC39elTFNKAR34xsMb9QoVosf+g9wlYxM=
|
||||||
github.com/tlinden/yadu v0.1.2 h1:TYYVnUJwziRJ9YPbIbRf9ikmDw0Q8Ifixm+J/kBQFh8=
|
github.com/tlinden/yadu v0.1.2 h1:TYYVnUJwziRJ9YPbIbRf9ikmDw0Q8Ifixm+J/kBQFh8=
|
||||||
|
|||||||
@ -158,7 +158,7 @@ func (grid *Grid) RemoveTile(point image.Point) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (grid *Grid) SetFloorTile(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) {
|
func (grid *Grid) SetSolidTile(tile *assets.Tile, point image.Point) {
|
||||||
|
|||||||
@ -114,8 +114,8 @@ func (system *CollectibleSystem) AddParticle(position *components.Position) {
|
|||||||
pos, particle, timer := ptmapper.Get(entity)
|
pos, particle, timer := ptmapper.Get(entity)
|
||||||
observer.AddEntity(entity, particleID)
|
observer.AddEntity(entity, particleID)
|
||||||
|
|
||||||
particle.Index = assets.Tiles['*'].Particle
|
particle.Index = assets.Tiles["particle"].Particle
|
||||||
particle.Tiles = assets.Tiles['*'].Tiles
|
particle.Tiles = assets.Tiles["particle"].Tiles
|
||||||
|
|
||||||
pos.Update(
|
pos.Update(
|
||||||
position.X-(16), // FIXME: use global tilesize!
|
position.X-(16), // FIXME: use global tilesize!
|
||||||
|
|||||||
@ -8,16 +8,17 @@ import (
|
|||||||
|
|
||||||
"github.com/hajimehoshi/ebiten/v2"
|
"github.com/hajimehoshi/ebiten/v2"
|
||||||
"github.com/mlange-42/arche/ecs"
|
"github.com/mlange-42/arche/ecs"
|
||||||
|
"github.com/solarlune/ldtkgo"
|
||||||
)
|
)
|
||||||
|
|
||||||
type HudSystem struct {
|
type HudSystem struct {
|
||||||
World *ecs.World
|
World *ecs.World
|
||||||
Cellsize int
|
Cellsize int
|
||||||
Observer *observers.GameObserver
|
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{
|
system := &HudSystem{
|
||||||
Observer: observers.GetGameObserver(world),
|
Observer: observers.GetGameObserver(world),
|
||||||
World: world,
|
World: world,
|
||||||
@ -45,13 +46,14 @@ func (system *HudSystem) Draw(screen *ebiten.Image) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
score := fmt.Sprintf("Score: %d", system.Observer.GetScore())
|
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.SetSizePx(20)
|
||||||
assets.FontRenderer.Renderer.SetTarget(screen)
|
assets.FontRenderer.Renderer.SetTarget(screen)
|
||||||
|
|
||||||
system.Print(score, 515, 22)
|
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)
|
system.Print(level, 10, 22)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,6 @@ package systems
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"openquell/assets"
|
|
||||||
"openquell/components"
|
"openquell/components"
|
||||||
. "openquell/components"
|
. "openquell/components"
|
||||||
. "openquell/config"
|
. "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
|
// return true if obstacle weapon points into the direction the player is moving
|
||||||
func CheckObstacleSide(playervelocity *Velocity, obsdirection int) bool {
|
func CheckObstacleSide(playervelocity *Velocity, obsdirection int) bool {
|
||||||
movingdirection := playervelocity.InvertDirection()
|
movingdirection := playervelocity.InvertDirection()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user