diff --git a/Makefile b/Makefile index 09cf843..95ce9b4 100644 --- a/Makefile +++ b/Makefile @@ -19,6 +19,9 @@ build: buildwasm: env GOOS=js GOARCH=wasm go build -o openquell.wasm $(LDFLAGS) . +buildwindows: + GOOS=windows GOARCH=amd64 go build $(LDFLAGS) -o openquell.exe + zipwasm: zip -r openquell-$(SHORTVERSION).zip index.html openquell.wasm wasm_exec.js diff --git a/TODO.md b/TODO.md index 834e3cc..b9559db 100644 --- a/TODO.md +++ b/TODO.md @@ -31,6 +31,8 @@ - Remove Sprite from Tile{}, not used anymore +- Implement pair_system using switch+door, relation is already setup, see grid:189 + ## 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 index d5484d1..fdb99a6 100644 --- a/assets/levels/openquell.ldtk +++ b/assets/levels/openquell.ldtk @@ -11,7 +11,7 @@ "iid": "267e9380-d7b0-11ee-a97e-35bec9c19d52", "jsonVersion": "1.5.3", "appBuildId": 473703, - "nextUid": 64, + "nextUid": 70, "identifierStyle": "Capitalize", "toc": [], "worldLayout": "Free", @@ -427,6 +427,152 @@ "pivotY": 0, "fieldDefs": [] }, + { + "identifier": "Switch", + "uid": 66, + "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": "#63C74D", + "renderMode": "Tile", + "showName": true, + "tilesetId": 57, + "tileRenderMode": "FitInside", + "tileRect": { "tilesetUid": 57, "x": 96, "y": 160, "w": 32, "h": 32 }, + "uiTileRect": null, + "nineSliceBorders": [], + "maxCount": 0, + "limitScope": "PerLevel", + "limitBehavior": "MoveLastOne", + "pivotX": 0, + "pivotY": 0, + "fieldDefs": [ + { + "identifier": "Entity_ref", + "doc": null, + "__type": "EntityRef", + "uid": 69, + "type": "F_EntityRef", + "isArray": false, + "canBeNull": true, + "arrayMinLength": null, + "arrayMaxLength": null, + "editorDisplayMode": "RefLinkBetweenCenters", + "editorDisplayScale": 1, + "editorDisplayPos": "Above", + "editorLinkStyle": "CurvedArrow", + "editorDisplayColor": null, + "editorAlwaysShow": false, + "editorShowInWorld": true, + "editorCutLongValues": true, + "editorTextSuffix": null, + "editorTextPrefix": null, + "useForSmartColor": false, + "exportToToc": false, + "searchable": false, + "min": null, + "max": null, + "regex": null, + "acceptFileTypes": null, + "defaultOverride": null, + "textLanguageMode": null, + "symmetricalRef": false, + "autoChainRef": true, + "allowOutOfLevelRef": true, + "allowedRefs": "OnlySpecificEntity", + "allowedRefsEntityUid": 65, + "allowedRefTags": [], + "tilesetUid": null + } + ] + }, + { + "identifier": "Door", + "uid": 65, + "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": "#B1EAAA", + "renderMode": "Tile", + "showName": true, + "tilesetId": 57, + "tileRenderMode": "FitInside", + "tileRect": { "tilesetUid": 57, "x": 64, "y": 160, "w": 32, "h": 32 }, + "uiTileRect": null, + "nineSliceBorders": [], + "maxCount": 0, + "limitScope": "PerLevel", + "limitBehavior": "MoveLastOne", + "pivotX": 0, + "pivotY": 0, + "fieldDefs": [ + { + "identifier": "Entity_ref", + "doc": null, + "__type": "EntityRef", + "uid": 68, + "type": "F_EntityRef", + "isArray": false, + "canBeNull": true, + "arrayMinLength": null, + "arrayMaxLength": null, + "editorDisplayMode": "RefLinkBetweenCenters", + "editorDisplayScale": 1, + "editorDisplayPos": "Above", + "editorLinkStyle": "CurvedArrow", + "editorDisplayColor": null, + "editorAlwaysShow": false, + "editorShowInWorld": true, + "editorCutLongValues": true, + "editorTextSuffix": null, + "editorTextPrefix": null, + "useForSmartColor": false, + "exportToToc": false, + "searchable": false, + "min": null, + "max": null, + "regex": null, + "acceptFileTypes": null, + "defaultOverride": null, + "textLanguageMode": null, + "symmetricalRef": false, + "autoChainRef": true, + "allowOutOfLevelRef": true, + "allowedRefs": "OnlySpecificEntity", + "allowedRefsEntityUid": 66, + "allowedRefTags": [], + "tilesetUid": null + } + ] + }, { "identifier": "HiddenDoor", "uid": 23, @@ -1223,11 +1369,11 @@ "seed": 4985586, "overrideTilesetUid": null, "gridTiles": [ - { "px": [192,128], "src": [128,0], "f": 0, "t": 4, "d": [86], "a": 1 }, + { "px": [192,128], "src": [96,0], "f": 0, "t": 3, "d": [86], "a": 1 }, { "px": [224,128], "src": [64,0], "f": 0, "t": 2, "d": [87], "a": 1 }, { "px": [256,128], "src": [0,0], "f": 0, "t": 0, "d": [88], "a": 1 }, { "px": [288,128], "src": [64,0], "f": 0, "t": 2, "d": [89], "a": 1 }, - { "px": [320,128], "src": [128,0], "f": 0, "t": 4, "d": [90], "a": 1 }, + { "px": [320,128], "src": [96,0], "f": 0, "t": 3, "d": [90], "a": 1 }, { "px": [352,128], "src": [96,0], "f": 0, "t": 3, "d": [91], "a": 1 }, { "px": [384,128], "src": [0,0], "f": 0, "t": 0, "d": [92], "a": 1 }, { "px": [416,128], "src": [0,0], "f": 0, "t": 0, "d": [93], "a": 1 }, @@ -1252,12 +1398,12 @@ { "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": [128,0], "f": 0, "t": 4, "d": [207], "a": 1 }, + { "px": [224,320], "src": [96,0], "f": 0, "t": 3, "d": [207], "a": 1 }, { "px": [256,320], "src": [64,0], "f": 0, "t": 2, "d": [208], "a": 1 }, { "px": [288,320], "src": [0,0], "f": 0, "t": 0, "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": [128,0], "f": 0, "t": 4, "d": [212], "a": 1 }, + { "px": [384,320], "src": [96,0], "f": 0, "t": 3, "d": [212], "a": 1 }, { "px": [416,320], "src": [64,0], "f": 0, "t": 2, "d": [213], "a": 1 } ], "entityInstances": [] @@ -1399,10 +1545,10 @@ { "px": [384,128], "src": [64,224], "f": 0, "t": 58, "d": [92], "a": 1 }, { "px": [416,128], "src": [192,192], "f": 0, "t": 54, "d": [93], "a": 1 }, { "px": [448,128], "src": [192,192], "f": 0, "t": 54, "d": [94], "a": 1 }, - { "px": [480,128], "src": [0,224], "f": 0, "t": 56, "d": [95], "a": 1 }, + { "px": [480,128], "src": [128,192], "f": 0, "t": 52, "d": [95], "a": 1 }, { "px": [512,128], "src": [192,192], "f": 0, "t": 54, "d": [96], "a": 1 }, { "px": [96,160], "src": [192,192], "f": 0, "t": 54, "d": [103], "a": 1 }, - { "px": [224,160], "src": [0,224], "f": 0, "t": 56, "d": [107], "a": 1 }, + { "px": [224,160], "src": [128,192], "f": 0, "t": 52, "d": [107], "a": 1 }, { "px": [384,160], "src": [192,192], "f": 0, "t": 54, "d": [112], "a": 1 }, { "px": [512,160], "src": [192,192], "f": 0, "t": 54, "d": [116], "a": 1 }, { "px": [96,192], "src": [192,192], "f": 0, "t": 54, "d": [123], "a": 1 }, @@ -1410,9 +1556,9 @@ { "px": [384,192], "src": [192,192], "f": 0, "t": 54, "d": [132], "a": 1 }, { "px": [512,192], "src": [192,192], "f": 0, "t": 54, "d": [136], "a": 1 }, { "px": [224,224], "src": [192,192], "f": 0, "t": 54, "d": [147], "a": 1 }, - { "px": [384,224], "src": [0,224], "f": 0, "t": 56, "d": [152], "a": 1 }, + { "px": [384,224], "src": [128,192], "f": 0, "t": 52, "d": [152], "a": 1 }, { "px": [96,256], "src": [224,192], "f": 0, "t": 55, "d": [163], "a": 1 }, - { "px": [128,256], "src": [0,224], "f": 0, "t": 56, "d": [164], "a": 1 }, + { "px": [128,256], "src": [128,192], "f": 0, "t": 52, "d": [164], "a": 1 }, { "px": [160,256], "src": [192,192], "f": 0, "t": 54, "d": [165], "a": 1 }, { "px": [192,256], "src": [192,192], "f": 0, "t": 54, "d": [166], "a": 1 }, { "px": [224,256], "src": [192,192], "f": 0, "t": 54, "d": [167], "a": 1 }, @@ -1423,10 +1569,10 @@ { "px": [128,320], "src": [192,192], "f": 0, "t": 54, "d": [204], "a": 1 }, { "px": [160,320], "src": [64,224], "f": 0, "t": 58, "d": [205], "a": 1 }, { "px": [192,320], "src": [192,192], "f": 0, "t": 54, "d": [206], "a": 1 }, - { "px": [224,320], "src": [0,224], "f": 0, "t": 56, "d": [207], "a": 1 }, + { "px": [224,320], "src": [128,192], "f": 0, "t": 52, "d": [207], "a": 1 }, { "px": [384,320], "src": [192,192], "f": 0, "t": 54, "d": [212], "a": 1 }, { "px": [416,320], "src": [192,192], "f": 0, "t": 54, "d": [213], "a": 1 }, - { "px": [448,320], "src": [0,224], "f": 0, "t": 56, "d": [214], "a": 1 }, + { "px": [448,320], "src": [128,192], "f": 0, "t": 52, "d": [214], "a": 1 }, { "px": [480,320], "src": [224,192], "f": 0, "t": 55, "d": [215], "a": 1 }, { "px": [512,320], "src": [192,192], "f": 0, "t": 54, "d": [216], "a": 1 } ], @@ -2154,14 +2300,14 @@ { "px": [288,128], "src": [32,0], "f": 0, "t": 1, "d": [89], "a": 1 }, { "px": [320,128], "src": [64,0], "f": 0, "t": 2, "d": [90], "a": 1 }, { "px": [352,128], "src": [0,0], "f": 0, "t": 0, "d": [91], "a": 1 }, - { "px": [384,128], "src": [128,0], "f": 0, "t": 4, "d": [92], "a": 1 }, + { "px": [384,128], "src": [96,0], "f": 0, "t": 3, "d": [92], "a": 1 }, { "px": [192,160], "src": [64,0], "f": 0, "t": 2, "d": [106], "a": 1 }, { "px": [416,160], "src": [32,0], "f": 0, "t": 1, "d": [113], "a": 1 }, { "px": [160,192], "src": [64,0], "f": 0, "t": 2, "d": [125], "a": 1 }, { "px": [192,192], "src": [96,0], "f": 0, "t": 3, "d": [126], "a": 1 }, - { "px": [416,192], "src": [128,0], "f": 0, "t": 4, "d": [133], "a": 1 }, + { "px": [416,192], "src": [96,0], "f": 0, "t": 3, "d": [133], "a": 1 }, { "px": [448,192], "src": [0,0], "f": 0, "t": 0, "d": [134], "a": 1 }, - { "px": [128,224], "src": [128,0], "f": 0, "t": 4, "d": [144], "a": 1 }, + { "px": [128,224], "src": [96,0], "f": 0, "t": 3, "d": [144], "a": 1 }, { "px": [160,224], "src": [0,0], "f": 0, "t": 0, "d": [145], "a": 1 }, { "px": [192,224], "src": [32,0], "f": 0, "t": 1, "d": [146], "a": 1 }, { "px": [416,224], "src": [0,0], "f": 0, "t": 0, "d": [153], "a": 1 }, @@ -2181,7 +2327,7 @@ { "px": [384,320], "src": [64,0], "f": 0, "t": 2, "d": [212], "a": 1 }, { "px": [256,352], "src": [64,0], "f": 0, "t": 2, "d": [228], "a": 1 }, { "px": [288,352], "src": [0,0], "f": 0, "t": 0, "d": [229], "a": 1 }, - { "px": [320,352], "src": [128,0], "f": 0, "t": 4, "d": [230], "a": 1 }, + { "px": [320,352], "src": [96,0], "f": 0, "t": 3, "d": [230], "a": 1 }, { "px": [352,352], "src": [32,0], "f": 0, "t": 1, "d": [231], "a": 1 }, { "px": [288,384], "src": [64,0], "f": 0, "t": 2, "d": [249], "a": 1 }, { "px": [320,384], "src": [0,0], "f": 0, "t": 0, "d": [250], "a": 1 } @@ -2447,7 +2593,7 @@ "gridTiles": [ { "px": [160,64], "src": [224,160], "f": 0, "t": 47, "d": [45], "a": 1 }, { "px": [288,64], "src": [224,160], "f": 0, "t": 47, "d": [49], "a": 1 }, - { "px": [128,96], "src": [96,192], "f": 0, "t": 51, "d": [64], "a": 1 }, + { "px": [128,96], "src": [224,160], "f": 0, "t": 47, "d": [64], "a": 1 }, { "px": [256,96], "src": [224,160], "f": 0, "t": 47, "d": [68], "a": 1 }, { "px": [96,128], "src": [224,160], "f": 0, "t": 47, "d": [83], "a": 1 }, { "px": [224,128], "src": [224,160], "f": 0, "t": 47, "d": [87], "a": 1 }, @@ -2458,7 +2604,7 @@ { "px": [32,256], "src": [224,160], "f": 0, "t": 47, "d": [161], "a": 1 }, { "px": [64,288], "src": [224,160], "f": 0, "t": 47, "d": [182], "a": 1 }, { "px": [256,320], "src": [224,160], "f": 0, "t": 47, "d": [208], "a": 1 }, - { "px": [288,352], "src": [128,192], "f": 0, "t": 52, "d": [229], "a": 1 }, + { "px": [288,352], "src": [224,160], "f": 0, "t": 47, "d": [229], "a": 1 }, { "px": [320,384], "src": [224,160], "f": 0, "t": 47, "d": [250], "a": 1 }, { "px": [352,416], "src": [224,160], "f": 0, "t": 47, "d": [271], "a": 1 } ], @@ -2692,19 +2838,19 @@ { "px": [128,96], "src": [64,0], "f": 0, "t": 2, "d": [64], "a": 1 }, { "px": [192,96], "src": [64,0], "f": 0, "t": 2, "d": [66], "a": 1 }, { "px": [224,96], "src": [64,0], "f": 0, "t": 2, "d": [67], "a": 1 }, - { "px": [256,96], "src": [128,0], "f": 0, "t": 4, "d": [68], "a": 1 }, + { "px": [256,96], "src": [96,0], "f": 0, "t": 3, "d": [68], "a": 1 }, { "px": [288,96], "src": [64,0], "f": 0, "t": 2, "d": [69], "a": 1 }, { "px": [320,96], "src": [96,0], "f": 0, "t": 3, "d": [70], "a": 1 }, { "px": [352,96], "src": [64,0], "f": 0, "t": 2, "d": [71], "a": 1 }, { "px": [384,96], "src": [64,0], "f": 0, "t": 2, "d": [72], "a": 1 }, - { "px": [416,96], "src": [128,0], "f": 0, "t": 4, "d": [73], "a": 1 }, + { "px": [416,96], "src": [96,0], "f": 0, "t": 3, "d": [73], "a": 1 }, { "px": [448,96], "src": [96,0], "f": 0, "t": 3, "d": [74], "a": 1 }, { "px": [480,96], "src": [64,0], "f": 0, "t": 2, "d": [75], "a": 1 }, { "px": [128,128], "src": [64,0], "f": 0, "t": 2, "d": [84], "a": 1 }, { "px": [480,128], "src": [64,0], "f": 0, "t": 2, "d": [95], "a": 1 }, - { "px": [128,160], "src": [128,0], "f": 0, "t": 4, "d": [104], "a": 1 }, + { "px": [128,160], "src": [96,0], "f": 0, "t": 3, "d": [104], "a": 1 }, { "px": [352,160], "src": [96,0], "f": 0, "t": 3, "d": [111], "a": 1 }, - { "px": [480,160], "src": [128,0], "f": 0, "t": 4, "d": [115], "a": 1 }, + { "px": [480,160], "src": [96,0], "f": 0, "t": 3, "d": [115], "a": 1 }, { "px": [256,192], "src": [64,0], "f": 0, "t": 2, "d": [128], "a": 1 }, { "px": [480,192], "src": [64,0], "f": 0, "t": 2, "d": [135], "a": 1 }, { "px": [128,224], "src": [64,0], "f": 0, "t": 2, "d": [144], "a": 1 }, @@ -2713,11 +2859,11 @@ { "px": [224,224], "src": [64,0], "f": 0, "t": 2, "d": [147], "a": 1 }, { "px": [256,224], "src": [64,0], "f": 0, "t": 2, "d": [148], "a": 1 }, { "px": [288,224], "src": [64,0], "f": 0, "t": 2, "d": [149], "a": 1 }, - { "px": [320,224], "src": [128,0], "f": 0, "t": 4, "d": [150], "a": 1 }, + { "px": [320,224], "src": [96,0], "f": 0, "t": 3, "d": [150], "a": 1 }, { "px": [352,224], "src": [64,0], "f": 0, "t": 2, "d": [151], "a": 1 }, { "px": [480,224], "src": [64,0], "f": 0, "t": 2, "d": [155], "a": 1 }, - { "px": [128,256], "src": [128,0], "f": 0, "t": 4, "d": [164], "a": 1 }, - { "px": [480,256], "src": [128,0], "f": 0, "t": 4, "d": [175], "a": 1 }, + { "px": [128,256], "src": [96,0], "f": 0, "t": 3, "d": [164], "a": 1 }, + { "px": [480,256], "src": [96,0], "f": 0, "t": 3, "d": [175], "a": 1 }, { "px": [128,288], "src": [64,0], "f": 0, "t": 2, "d": [184], "a": 1 }, { "px": [192,288], "src": [64,0], "f": 0, "t": 2, "d": [186], "a": 1 }, { "px": [480,288], "src": [64,0], "f": 0, "t": 2, "d": [195], "a": 1 }, @@ -2726,11 +2872,11 @@ { "px": [480,320], "src": [64,0], "f": 0, "t": 2, "d": [215], "a": 1 }, { "px": [128,352], "src": [64,0], "f": 0, "t": 2, "d": [224], "a": 1 }, { "px": [192,352], "src": [64,0], "f": 0, "t": 2, "d": [226], "a": 1 }, - { "px": [224,352], "src": [128,0], "f": 0, "t": 4, "d": [227], "a": 1 }, + { "px": [224,352], "src": [96,0], "f": 0, "t": 3, "d": [227], "a": 1 }, { "px": [256,352], "src": [64,0], "f": 0, "t": 2, "d": [228], "a": 1 }, - { "px": [288,352], "src": [128,0], "f": 0, "t": 4, "d": [229], "a": 1 }, + { "px": [288,352], "src": [96,0], "f": 0, "t": 3, "d": [229], "a": 1 }, { "px": [320,352], "src": [64,0], "f": 0, "t": 2, "d": [230], "a": 1 }, - { "px": [352,352], "src": [128,0], "f": 0, "t": 4, "d": [231], "a": 1 }, + { "px": [352,352], "src": [96,0], "f": 0, "t": 3, "d": [231], "a": 1 }, { "px": [384,352], "src": [64,0], "f": 0, "t": 2, "d": [232], "a": 1 }, { "px": [416,352], "src": [96,0], "f": 0, "t": 3, "d": [233], "a": 1 }, { "px": [448,352], "src": [64,0], "f": 0, "t": 2, "d": [234], "a": 1 }, @@ -5621,7 +5767,7 @@ "__bgPos": { "topLeftPx": [0,0], "scale": [1,1], "cropRect": [0,0,640,480] }, "externalRelPath": null, "fieldInstances": [ - { "__identifier": "level", "__type": "Int", "__value": 12, "__tile": null, "defUid": 11, "realEditorValues": [{ "id": "V_Int", "params": [12] }] }, + { "__identifier": "level", "__type": "Int", "__value": 20, "__tile": null, "defUid": 11, "realEditorValues": [{ "id": "V_Int", "params": [20] }] }, { "__identifier": "description", "__type": "String", "__value": "Take care of each other", "__tile": null, "defUid": 12, "realEditorValues": [{ "id": "V_String", "params": ["Take care of each other"] @@ -5828,6 +5974,237 @@ } ], "__neighbours": [] + }, + { + "identifier": "Machines", + "iid": "50621a80-d7b0-11ee-968e-a98c2d35fbdb", + "uid": 64, + "worldX": -224, + "worldY": 2752, + "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": [ + { "__identifier": "level", "__type": "Int", "__value": 21, "__tile": null, "defUid": 11, "realEditorValues": [{ "id": "V_Int", "params": [21] }] }, + { "__identifier": "description", "__type": "String", "__value": "Use the switch to open the door", "__tile": null, "defUid": 12, "realEditorValues": [{ + "id": "V_String", + "params": ["Use the switch to open the door"] + }] }, + { "__identifier": "background", "__type": "String", "__value": "background-lila", "__tile": null, "defUid": 13, "realEditorValues": [] }, + { "__identifier": "minmoves", "__type": "Int", "__value": 7, "__tile": null, "defUid": 14, "realEditorValues": [] } + ], + "layerInstances": [ + { + "__identifier": "Entities", + "__type": "Entities", + "__cWid": 20, + "__cHei": 15, + "__gridSize": 32, + "__opacity": 1, + "__pxTotalOffsetX": 0, + "__pxTotalOffsetY": 0, + "__tilesetDefUid": null, + "__tilesetRelPath": null, + "iid": "50624190-d7b0-11ee-968e-3dbcbdc42f40", + "levelId": 64, + "layerDefUid": 5, + "pxOffsetX": 0, + "pxOffsetY": 0, + "visible": true, + "optionalRules": [], + "intGridCsv": [], + "autoLayerTiles": [], + "seed": 4917353, + "overrideTilesetUid": null, + "gridTiles": [], + "entityInstances": [ + { + "__identifier": "PlayerSecondary", + "__grid": [9,5], + "__pivot": [0,0], + "__tags": [], + "__tile": { "tilesetUid": 57, "x": 64, "y": 0, "w": 32, "h": 32 }, + "__smartColor": "#272DC5", + "iid": "758dd4b0-d7b0-11ee-9add-dbfe89124029", + "width": 32, + "height": 32, + "defUid": 36, + "px": [288,160], + "fieldInstances": [], + "__worldX": 64, + "__worldY": 2912 + }, + { + "__identifier": "PlayerPrimary", + "__grid": [14,9], + "__pivot": [0,0], + "__tags": [], + "__tile": { "tilesetUid": 57, "x": 32, "y": 0, "w": 32, "h": 32 }, + "__smartColor": "#2F3BBE", + "iid": "78ccac00-d7b0-11ee-9add-f91552f3c021", + "width": 32, + "height": 32, + "defUid": 3, + "px": [448,288], + "fieldInstances": [], + "__worldX": 224, + "__worldY": 3040 + }, + { + "__identifier": "Switch", + "__grid": [11,5], + "__pivot": [0,0], + "__tags": [], + "__tile": { "tilesetUid": 57, "x": 96, "y": 160, "w": 32, "h": 32 }, + "__smartColor": "#63C74D", + "iid": "7d975370-d7b0-11ee-9add-971995295000", + "width": 32, + "height": 32, + "defUid": 66, + "px": [352,160], + "fieldInstances": [{ "__identifier": "Entity_ref", "__type": "EntityRef", "__value": { + "entityIid": "81e502b0-d7b0-11ee-9add-6193c766edeb", + "layerIid": "50624190-d7b0-11ee-968e-3dbcbdc42f40", + "levelIid": "50621a80-d7b0-11ee-968e-a98c2d35fbdb", + "worldIid": "267ee1a0-d7b0-11ee-a97e-53f0a359eae1" + }, "__tile": null, "defUid": 69, "realEditorValues": [{ + "id": "V_String", + "params": ["81e502b0-d7b0-11ee-9add-6193c766edeb"] + }] }], + "__worldX": 128, + "__worldY": 2912 + }, + { + "__identifier": "Door", + "__grid": [7,7], + "__pivot": [0,0], + "__tags": [], + "__tile": { "tilesetUid": 57, "x": 64, "y": 160, "w": 32, "h": 32 }, + "__smartColor": "#B1EAAA", + "iid": "81e502b0-d7b0-11ee-9add-6193c766edeb", + "width": 32, + "height": 32, + "defUid": 65, + "px": [224,224], + "fieldInstances": [{ "__identifier": "Entity_ref", "__type": "EntityRef", "__value": null, "__tile": null, "defUid": 68, "realEditorValues": [] }], + "__worldX": 0, + "__worldY": 2976 + }, + { + "__identifier": "ObstacleNorth", + "__grid": [8,6], + "__pivot": [0,0], + "__tags": [], + "__tile": { "tilesetUid": 57, "x": 64, "y": 32, "w": 32, "h": 32 }, + "__smartColor": "#F77622", + "iid": "9b781960-d7b0-11ee-9add-e1d2013b29e2", + "width": 32, + "height": 32, + "defUid": 9, + "px": [256,192], + "fieldInstances": [], + "__worldX": 32, + "__worldY": 2944 + }, + { + "__identifier": "Collectible", + "__grid": [5,5], + "__pivot": [0,0], + "__tags": [], + "__tile": { "tilesetUid": 57, "x": 0, "y": 64, "w": 32, "h": 32 }, + "__smartColor": "#FEAE34", + "iid": "a056be00-d7b0-11ee-9add-01e4d4e8f475", + "width": 32, + "height": 32, + "defUid": 4, + "px": [160,160], + "fieldInstances": [], + "__worldX": -64, + "__worldY": 2912 + } + ] + }, + { + "__identifier": "Tiles", + "__type": "Tiles", + "__cWid": 20, + "__cHei": 15, + "__gridSize": 32, + "__opacity": 1, + "__pxTotalOffsetX": 0, + "__pxTotalOffsetY": 0, + "__tilesetDefUid": 50, + "__tilesetRelPath": "../sprites/primarymap.png", + "iid": "50624191-d7b0-11ee-968e-b9865cb1b5c1", + "levelId": 64, + "layerDefUid": 2, + "pxOffsetX": 0, + "pxOffsetY": 0, + "visible": true, + "optionalRules": [], + "intGridCsv": [], + "autoLayerTiles": [], + "seed": 6697541, + "overrideTilesetUid": null, + "gridTiles": [ + { "px": [128,128], "src": [224,352], "f": 0, "t": 95, "d": [84], "a": 1 }, + { "px": [160,128], "src": [224,352], "f": 0, "t": 95, "d": [85], "a": 1 }, + { "px": [192,128], "src": [224,352], "f": 0, "t": 95, "d": [86], "a": 1 }, + { "px": [224,128], "src": [224,352], "f": 0, "t": 95, "d": [87], "a": 1 }, + { "px": [256,128], "src": [224,352], "f": 0, "t": 95, "d": [88], "a": 1 }, + { "px": [288,128], "src": [224,352], "f": 0, "t": 95, "d": [89], "a": 1 }, + { "px": [320,128], "src": [224,352], "f": 0, "t": 95, "d": [90], "a": 1 }, + { "px": [352,128], "src": [224,352], "f": 0, "t": 95, "d": [91], "a": 1 }, + { "px": [384,128], "src": [224,352], "f": 0, "t": 95, "d": [92], "a": 1 }, + { "px": [416,128], "src": [224,352], "f": 0, "t": 95, "d": [93], "a": 1 }, + { "px": [448,128], "src": [224,352], "f": 0, "t": 95, "d": [94], "a": 1 }, + { "px": [480,128], "src": [224,352], "f": 0, "t": 95, "d": [95], "a": 1 }, + { "px": [128,160], "src": [224,352], "f": 0, "t": 95, "d": [104], "a": 1 }, + { "px": [224,160], "src": [224,352], "f": 0, "t": 95, "d": [107], "a": 1 }, + { "px": [320,160], "src": [224,352], "f": 0, "t": 95, "d": [110], "a": 1 }, + { "px": [480,160], "src": [224,352], "f": 0, "t": 95, "d": [115], "a": 1 }, + { "px": [128,192], "src": [224,352], "f": 0, "t": 95, "d": [124], "a": 1 }, + { "px": [224,192], "src": [224,352], "f": 0, "t": 95, "d": [127], "a": 1 }, + { "px": [320,192], "src": [224,352], "f": 0, "t": 95, "d": [130], "a": 1 }, + { "px": [480,192], "src": [224,352], "f": 0, "t": 95, "d": [135], "a": 1 }, + { "px": [128,224], "src": [224,352], "f": 0, "t": 95, "d": [144], "a": 1 }, + { "px": [320,224], "src": [224,352], "f": 0, "t": 95, "d": [150], "a": 1 }, + { "px": [480,224], "src": [224,352], "f": 0, "t": 95, "d": [155], "a": 1 }, + { "px": [128,256], "src": [224,352], "f": 0, "t": 95, "d": [164], "a": 1 }, + { "px": [224,256], "src": [224,352], "f": 0, "t": 95, "d": [167], "a": 1 }, + { "px": [320,256], "src": [224,352], "f": 0, "t": 95, "d": [170], "a": 1 }, + { "px": [480,256], "src": [224,352], "f": 0, "t": 95, "d": [175], "a": 1 }, + { "px": [128,288], "src": [224,352], "f": 0, "t": 95, "d": [184], "a": 1 }, + { "px": [224,288], "src": [224,352], "f": 0, "t": 95, "d": [187], "a": 1 }, + { "px": [320,288], "src": [224,352], "f": 0, "t": 95, "d": [190], "a": 1 }, + { "px": [480,288], "src": [224,352], "f": 0, "t": 95, "d": [195], "a": 1 }, + { "px": [128,320], "src": [224,352], "f": 0, "t": 95, "d": [204], "a": 1 }, + { "px": [160,320], "src": [224,352], "f": 0, "t": 95, "d": [205], "a": 1 }, + { "px": [192,320], "src": [224,352], "f": 0, "t": 95, "d": [206], "a": 1 }, + { "px": [224,320], "src": [224,352], "f": 0, "t": 95, "d": [207], "a": 1 }, + { "px": [256,320], "src": [224,352], "f": 0, "t": 95, "d": [208], "a": 1 }, + { "px": [288,320], "src": [224,352], "f": 0, "t": 95, "d": [209], "a": 1 }, + { "px": [320,320], "src": [224,352], "f": 0, "t": 95, "d": [210], "a": 1 }, + { "px": [352,320], "src": [224,352], "f": 0, "t": 95, "d": [211], "a": 1 }, + { "px": [384,320], "src": [224,352], "f": 0, "t": 95, "d": [212], "a": 1 }, + { "px": [416,320], "src": [224,352], "f": 0, "t": 95, "d": [213], "a": 1 }, + { "px": [448,320], "src": [224,352], "f": 0, "t": 95, "d": [214], "a": 1 }, + { "px": [480,320], "src": [224,352], "f": 0, "t": 95, "d": [215], "a": 1 } + ], + "entityInstances": [] + } + ], + "__neighbours": [] } ], "worlds": [], diff --git a/assets/loader-levels.go b/assets/loader-levels.go index 163cfbe..e44b982 100644 --- a/assets/loader-levels.go +++ b/assets/loader-levels.go @@ -17,7 +17,7 @@ var Tiles = InitTiles() // Tile: contains image, identifier (as used in level data) and // additional properties type Tile struct { - Id byte + Id, Ref string Sprite *ebiten.Image Class string Solid bool // wall brick @@ -35,11 +35,15 @@ type Tile struct { Direction int // obstacle business end shows into this direction Shader *ebiten.Shader Alpha *ebiten.Image + Bond bool // denotes an entity which can have a relation to another + Door bool // a door, can be manipulated by a switch + Switch bool // opens|closes a door } func (tile *Tile) Clone() *Tile { newtile := &Tile{ Id: tile.Id, + Ref: tile.Ref, Sprite: tile.Sprite, Class: tile.Class, Solid: tile.Solid, @@ -57,6 +61,9 @@ func (tile *Tile) Clone() *Tile { Direction: tile.Direction, Alpha: tile.Alpha, Shader: tile.Shader, + Bond: tile.Bond, + Door: tile.Door, + Switch: tile.Switch, } return newtile @@ -67,9 +74,18 @@ const ( Secondary bool = false ) +func GetSprites(class []string) []*ebiten.Image { + sprites := []*ebiten.Image{} + + for _, sprite := range class { + sprites = append(sprites, Assets[sprite]) + } + + return sprites +} + func NewTilePlayer(isprimary bool) *Tile { tile := &Tile{ - Id: 'S', Class: "sphere", Renderable: true, Player: true, @@ -82,7 +98,6 @@ func NewTilePlayer(isprimary bool) *Tile { tile.Sprite = Assets["sphere-blue"] case Secondary: tile.Sprite = Assets["sphere-blue-secondary"] - tile.Id = 's' } // primary sprite is always the first one @@ -92,7 +107,6 @@ func NewTilePlayer(isprimary bool) *Tile { func NewTileBlock(class string) *Tile { return &Tile{ - Id: '#', Sprite: Assets[class], Class: class, Solid: true, @@ -102,7 +116,6 @@ func NewTileBlock(class string) *Tile { func NewTileCollectible(class string) *Tile { return &Tile{ - Id: 'o', Sprite: Assets[class], Class: class, Solid: false, @@ -113,7 +126,6 @@ func NewTileCollectible(class string) *Tile { func NewTileObstacle(class string, direction int) *Tile { return &Tile{ - Id: '+', Sprite: Assets[class], Class: class, Solid: false, @@ -125,14 +137,9 @@ func NewTileObstacle(class string, direction int) *Tile { } func NewTileParticle(class []string) *Tile { - sprites := []*ebiten.Image{} - - for _, sprite := range class { - sprites = append(sprites, Assets[sprite]) - } + sprites := GetSprites(class) return &Tile{ - Id: '*', Class: "particle", Solid: false, Renderable: false, @@ -142,29 +149,21 @@ func NewTileParticle(class []string) *Tile { } func NewTileTranswall(class []string) *Tile { - sprites := []*ebiten.Image{} - names := []string{} - - for _, sprite := range class { - sprites = append(sprites, Assets[sprite]) - names = append(names, sprite) - } + sprites := GetSprites(class) return &Tile{ - Id: 't', Class: "transwall", Solid: false, Renderable: true, Transient: true, Tiles: sprites, Sprite: sprites[0], // initially use the first - TileNames: names, + TileNames: class, } } func NewTileHiddenDoor(class, alpha string) *Tile { return &Tile{ - Id: 'W', Class: "hiddendoor", Solid: false, Renderable: true, @@ -175,12 +174,40 @@ func NewTileHiddenDoor(class, alpha string) *Tile { } } +func NewTileSwitch(class []string) *Tile { + sprites := GetSprites(class) + + return &Tile{ + Class: "switch", + Solid: false, + Renderable: true, + Bond: true, + Switch: true, + Tiles: sprites, + Sprite: sprites[0], // initially use the first + } +} + +func NewTileDoor(class []string) *Tile { + sprites := GetSprites(class) + + return &Tile{ + Class: "door", + Solid: false, + Renderable: true, + Bond: true, + Door: true, + Tiles: sprites, + Sprite: sprites[0], // initially use the first + } +} + // used to map level data bytes to actual tiles type TileRegistry map[string]*Tile func InitTiles() TileRegistry { return TileRegistry{ - "floor": {Id: ' ', Class: "floor", Renderable: false}, + "floor": {Class: "floor", Renderable: false}, "default": NewTileBlock("block-greycolored"), "solidorange": NewTileBlock("block-orange-32"), "PlayerPrimary": NewTilePlayer(Primary), @@ -213,6 +240,8 @@ func InitTiles() TileRegistry { "HiddenDoor11": NewTileHiddenDoor("block-greycolored", "damage"), "HiddenDoor12": NewTileHiddenDoor("block-greycolored", "damage"), "HiddenDoor13": NewTileHiddenDoor("block-greycolored", "damage"), + "Switch": NewTileSwitch([]string{"switch-open.png", "switch-closed.png"}), + "Door": NewTileDoor([]string{"door-open.png", "door-closed.png"}), } } diff --git a/assets/loader-shaders.go b/assets/loader-shaders.go index ea786fe..1281263 100644 --- a/assets/loader-shaders.go +++ b/assets/loader-shaders.go @@ -4,7 +4,7 @@ import ( "bytes" "log" "log/slog" - "path/filepath" + "path" "strings" "github.com/hajimehoshi/ebiten/v2" @@ -23,7 +23,7 @@ func LoadShaders(dir string) ShaderRegistry { } for _, file := range entries { - path := filepath.Join(dir, file.Name()) + path := path.Join(dir, file.Name()) fd, err := assetfs.Open(path) if err != nil { log.Fatalf("failed to open shader file %s: %s", file.Name(), err) diff --git a/assets/loader-sprites.go b/assets/loader-sprites.go index 90735a3..933484c 100644 --- a/assets/loader-sprites.go +++ b/assets/loader-sprites.go @@ -6,7 +6,7 @@ import ( _ "image/png" "log" "log/slog" - "path/filepath" + "path" "strings" "github.com/hajimehoshi/ebiten/v2" @@ -30,7 +30,7 @@ func LoadImages(dir string) AssetRegistry { } for _, imagefile := range entries { - path := filepath.Join(dir, imagefile.Name()) + path := path.Join(dir, imagefile.Name()) fd, err := assetfs.Open(path) if err != nil { log.Fatalf("failed to open image file %s: %s", imagefile.Name(), err) diff --git a/assets/sprites/entitymap.png b/assets/sprites/entitymap.png index 25ce9b7..aa7aa95 100644 Binary files a/assets/sprites/entitymap.png and b/assets/sprites/entitymap.png differ diff --git a/components/pairs.go b/components/pairs.go index 0659a59..266fe82 100644 --- a/components/pairs.go +++ b/components/pairs.go @@ -9,6 +9,7 @@ import ( // manipulates the other type Bond struct { ecs.Relation + Id, Ref string } // A door has a relation to a switch using the Bond component. The @@ -19,6 +20,7 @@ type Door struct { IsOpen bool OpenSprite *ebiten.Image CloseSprite *ebiten.Image + Id string } func (door *Door) Open() *ebiten.Image { @@ -35,4 +37,5 @@ type Switch struct { IsOpen bool OpenSprite *ebiten.Image CloseSprite *ebiten.Image + Ref string // the IId of the door } diff --git a/config/static.go b/config/static.go index 9f6d12d..de1dc20 100644 --- a/config/static.go +++ b/config/static.go @@ -16,7 +16,7 @@ const ( const PLAYERSPEED int = 5 const PARTICLE_LOOPWAIT time.Duration = 250 * time.Millisecond const LEVEL_END_WAIT time.Duration = 500 * time.Millisecond -const version string = "1.2.1" +const version string = "1.2.2" const MenuRectX int = 600 const MenuRectY int = 0 diff --git a/game/levels.go b/game/levels.go index 0dd1068..8369b42 100644 --- a/game/levels.go +++ b/game/levels.go @@ -164,11 +164,20 @@ func LevelToSlice(game *Game, level *ldtkgo.Level, tilesize int) (Map, Map) { case ldtkgo.LayerTypeEntity: // load mobile tiles (they call them entities) using static map map.png. - tileset := assets.Assets["primarymap"] + tileset := assets.Assets["entitymap"] for _, entity := range layer.Entities { if entity.TileRect != nil { tile := assets.Tiles[entity.Identifier] + tile.Id = entity.IID + + ref := entity.PropertyByIdentifier("Entity_ref") + if ref != nil { + if ref.Value != nil { + refid := ref.Value.(map[string]interface{}) + tile.Ref = refid["entityIid"].(string) + } + } slog.Debug("LOAD TILE", "tile", entity.TileRect) diff --git a/grid/grid.go b/grid/grid.go index 8cf6a81..5ba4e58 100644 --- a/grid/grid.go +++ b/grid/grid.go @@ -8,6 +8,8 @@ import ( "openquell/config" "openquell/observers" + "log/slog" + "github.com/mlange-42/arche/ecs" "github.com/mlange-42/arche/generic" ) @@ -59,23 +61,41 @@ func NewGrid(world *ecs.World, components.Renderable, components.Transient](world) - doormapper := generic.NewMap3[ + destructiblemapper := generic.NewMap3[ components.Position, components.Renderable, components.Destroyable](world) + switchmapper := generic.NewMap4[ + components.Position, + components.Renderable, + components.Bond, + components.Switch](world) + + doormapper := generic.NewMap4[ + components.Position, + components.Renderable, + components.Bond, + components.Door](world) + var pos *components.Position var vel *components.Velocity var render *components.Renderable var transient *components.Transient var player *components.Player var destroyable *components.Destroyable + var switcher *components.Switch + var door *components.Door + var bond *components.Bond playerID := ecs.ComponentID[components.Player](world) obstacleID := ecs.ComponentID[components.Obstacle](world) observer := observers.GetGameObserver(world) + switches := []ecs.Entity{} + doors := []ecs.Entity{} + for point, tile := range mapslice { switch tile.Renderable { case true: @@ -83,6 +103,7 @@ func NewGrid(world *ecs.World, case tile.Solid: entity := solidmapper.New() pos, render, _, _ = solidmapper.Get(entity) + case tile.Player: entity := playermapper.New() pos, vel, render, player = playermapper.Get(entity) @@ -91,9 +112,11 @@ func NewGrid(world *ecs.World, player.IsPrimary = tile.IsPrimary player.Sprites = tile.Tiles player.LoopPos = &components.Position{Cellsize: tilesize} + case tile.Collectible: entity := colmapper.New() pos, render, _ = colmapper.Get(entity) + case tile.Obstacle: entity := obsmapper.New() pos, vel, render, _ = obsmapper.Get(entity) @@ -101,17 +124,36 @@ func NewGrid(world *ecs.World, vel.PointingAt = tile.Direction vel.Speed = config.PLAYERSPEED observer.AddEntity(entity, obstacleID) + case tile.Transient: entity := transmapper.New() pos, render, transient = transmapper.Get(entity) transient.Sprites = tile.TileNames + case tile.Destroyable: - entity := doormapper.New() - pos, render, destroyable = doormapper.Get(entity) + entity := destructiblemapper.New() + pos, render, destroyable = destructiblemapper.Get(entity) destroyable.Sprites = tile.Tiles render.DamageImage = tile.Alpha render.Shader = tile.Shader render.Damaged = 0 + + case tile.Switch: + entity := switchmapper.New() + pos, render, _, switcher = switchmapper.Get(entity) + switcher.OpenSprite = tile.Tiles[0] + switcher.CloseSprite = tile.Tiles[0] + switcher.Ref = tile.Ref + switches = append(switches, entity) + + case tile.Door: + entity := doormapper.New() + pos, render, _, door = doormapper.Get(entity) + door.OpenSprite = tile.Tiles[0] + door.CloseSprite = tile.Tiles[0] + door.Id = tile.Id + doors = append(doors, entity) + default: log.Fatalln("unsupported tile type encountered") } @@ -134,6 +176,22 @@ func NewGrid(world *ecs.World, pos.Update(point.X*tilesize, point.Y*tilesize, tilesize) } + for _, switchentity := range switches { + _, _, bond, switcher = switchmapper.Get(switchentity) + + if switcher.Ref != "" { + for _, doorentity := range doors { + _, _, _, door = doormapper.Get(doorentity) + if door.Id == switcher.Ref { + bond.Ref = switcher.Ref + relID := ecs.ComponentID[components.Bond](world) + world.Relations().Set(doorentity, relID, switchentity) + slog.Debug("setup switch/door reference") + } + } + } + } + return &Grid{ Size: len(mapslice), Tilesize: tilesize, diff --git a/src/entitymap.xcf b/src/entitymap.xcf index 2297b3c..71f4f68 100644 Binary files a/src/entitymap.xcf and b/src/entitymap.xcf differ