diff --git a/assets/README.md b/assets/README.md index f7b10c8..fa1d6ce 100644 --- a/assets/README.md +++ b/assets/README.md @@ -1,4 +1,3 @@ # Assets * [Atlas](parin_atlas.png): By [Kapendev](https://kapendev.itch.io), CC0 -* [Monogram](parin_monogram.png): By [DATAGOBLIN](https://datagoblin.itch.io/monogram), CC0 diff --git a/dub.json b/dub.json index 27d347a..921ba3e 100644 --- a/dub.json +++ b/dub.json @@ -13,6 +13,7 @@ "source" ], "subPackages" : [ + "packages/mapy", "packages/rin", "packages/setup", "packages/web" diff --git a/examples/.closed b/examples/.closed index b9f054a..8c07e4d 100644 --- a/examples/.closed +++ b/examples/.closed @@ -1,7 +1,14 @@ # This file is used by: https://github.com/Kapendev/closed --I=../source + +[default] -o=../example -# No Joka :( +-I=../source +-L=-lraylib +# Those lines work for me. Use -s=custom to fix any errors. -I=../../joka/source -L=-L../../raylib/lib + +[custom] +-o=../example +-I=../source -L=-lraylib diff --git a/packages/mapy/.closed b/packages/mapy/.closed new file mode 100644 index 0000000..539d571 --- /dev/null +++ b/packages/mapy/.closed @@ -0,0 +1,13 @@ +# This file is used by: https://github.com/Kapendev/closed + +[default] +-I=../../source +-L=-lraylib +-R=../../assets/parin_atlas.png +# Those lines work for me. Use -s=custom to fix any errors. +-I=../../../joka/source +-L=-L../../../raylib/lib + +[custom] +-I=../../source +-L=-lraylib diff --git a/packages/mapy/.gitignore b/packages/mapy/.gitignore new file mode 100644 index 0000000..23d6a45 --- /dev/null +++ b/packages/mapy/.gitignore @@ -0,0 +1,18 @@ +.dub +dub.s* +docs.json +__dummy.html +docs/ +/mapy +mapy.so +mapy.dylib +mapy.dll +mapy.a +mapy.lib +mapy-test-* +*.exe +*.pdb +*.o +*.obj +*.lst +parin* diff --git a/packages/mapy/README.md b/packages/mapy/README.md new file mode 100644 index 0000000..b392103 --- /dev/null +++ b/packages/mapy/README.md @@ -0,0 +1,8 @@ +# Mapy + +A tile map editor for Parin. + +## Usage + +* With DUB: `dub run parin:mapy` +* Without DUB: `dmd -Ijoka_path -Iparin_path -i -run source/app.d` diff --git a/packages/mapy/dub.json b/packages/mapy/dub.json new file mode 100644 index 0000000..63a4c79 --- /dev/null +++ b/packages/mapy/dub.json @@ -0,0 +1,13 @@ +{ + "authors": [ + "Alexandros F. G. Kapretsos" + ], + "copyright": "Copyright © 2024, Alexandros F. G. Kapretsos", + "description": "A tile map editor for Parin.", + "license": "MIT", + "name": "mapy", + "dependencies": { + "joka": "*", + "parin": {"path": ".."} + }, +} diff --git a/packages/mapy/source/app.d b/packages/mapy/source/app.d new file mode 100644 index 0000000..33182c2 --- /dev/null +++ b/packages/mapy/source/app.d @@ -0,0 +1,218 @@ +/// A tile map editor for Parin. + +import parin; + +AppState appState; + +enum defaultCameraMoveSpeed = 300; +enum defaultCameraZoomSpeed = 60; +enum defaultCameraSlowdown = 0.07f; + +enum panelColor1 = toRgb(0xd7d6d6); +enum panelColor2 = toRgb(0xb0afaf); +enum panelColor3 = toRgb(0x4a4b4c); +enum panelColor4 = toRgb(0x262626); + +enum canvasColor = toRgb(0x484d51); +enum mapAreaColor = toRgb(0x2d2f34); +enum mapAreaOutlineColor = toRgb(0x161a1f); + +enum mouseAreaColor = toRgb(0x5ca4cf); + +enum AppMode { + edit, + select, +} + +struct AppCamera { + Camera data; + Vec2 targetPosition; + float targetScale = 1.0f; + + void update(Vec2 moveDelta, float scaleDelta, float dt) { + targetPosition += moveDelta.normalize() * Vec2(dt * defaultCameraMoveSpeed * (Keyboard.shift.isDown + 1) * (1.0f / min(targetScale, 1.0f))); + targetScale = max(targetScale + (scaleDelta * dt * defaultCameraZoomSpeed), 0.25f); + data.followPositionWithSlowdown(targetPosition, defaultCameraSlowdown); + data.followScaleWithSlowdown(targetScale, defaultCameraSlowdown); + } + + void attach() { + data.attach(); + } + + void detach() { + data.detach(); + } +} + +struct AppState { + TextureId atlas; + Viewport atlasViewport; + FontId font; + TileMap map; + AppCamera camera; + IStr mapFile; + IStr atlasFile; + AppMode mode; +} + +void drawText(IStr text, Vec2 position, DrawOptions options = DrawOptions()) { + auto font = appState.font.isValid ? appState.font.get() : engineFont; + if (font == engineFont) options.scale = Vec2(2); + parin.drawText(font, text, position, options); +} + +void ready() { + setBackgroundColor(canvasColor); + setIsUsingAssetsPath(false); + appState.camera.data = Camera(0, 0, true); + appState.map = TileMap(256, 256, 0, 0); + // Parse args. + foreach (arg; envArgs[1 .. $]) { + if (0) { + } else if (arg.endsWith(".ttf")) { + appState.font = loadFont(arg, 24, 0, 24); + appState.font.setFilter(Filter.linear); + } else if (arg.endsWith(".png")) { + appState.atlas = loadTexture(arg); + if (appState.atlas.isValid) appState.atlasFile = arg; + } else if (arg.endsWith(".csv")) { + appState.mapFile = arg; + } else { + if (appState.map.tileWidth) { + appState.map.tileHeight = cast(int) arg.toSigned().getOr(16); + } else { + appState.map.tileWidth = cast(int) arg.toSigned().getOr(16); + appState.map.tileHeight = appState.map.tileWidth; + } + } + } + if (appState.map.tileWidth == 0) { + appState.map.tileWidth = 16; + appState.map.tileHeight = 16; + } + auto value = loadTempText(appState.mapFile); + if (value.isSome) { + appState.map.parse(value.get()); + appState.map.resizeSoft(appState.map.hardColCount, appState.map.hardRowCount); + } else { + appState.mapFile = ""; + } +} + +bool update(float dt) { +// drawTileMap(appState.atlas, appState.map, appState.camera); + if (Keyboard.f11.isPressed) toggleIsFullscreen(); + if (Keyboard.n1.isPressed) appState.mode = cast(AppMode) !appState.mode; + appState.camera.update(wasd, deltaWheel, dt); + + auto panelHeight = 48; + auto windowCenter = windowSize * Vec2(0.5f); + auto appMouse = mouse; + auto canvasMouse = appMouse; + with (AppMode) final switch (appState.mode) { + case edit: + if (0) { + } else if (canvasMouse.y <= panelHeight) { + canvasMouse.y = -100000; + } else if (canvasMouse.y >= windowHeight - panelHeight) { + canvasMouse.y = 100000; + } + break; + case select: + if (0) { + } else if (canvasMouse.y <= panelHeight) { + canvasMouse.y = -100000; + } else if (canvasMouse.y >= windowHeight - panelHeight) { + canvasMouse.y = 100000; + } + break; + } + + auto worldMouse = canvasMouse.toWorldPoint(appState.camera.data); + auto gridMouse = floor(worldMouse / appState.map.tileSize).toIVec(); + auto gridMouseIndex = appState.map.softColCount * gridMouse.y + gridMouse.x; + auto worldGridMouse = gridMouse.toVec() * appState.map.tileSize; + auto isGridMouseInMap = appState.map.has(gridMouse); + + with (AppMode) final switch (appState.mode) { + case edit: + if (appState.atlas.isValid && isGridMouseInMap) { + if (0) { + } else if (Mouse.left.isDown) { + appState.map[gridMouse] = 0; + } else if (Mouse.right.isDown) { + appState.map[gridMouse] = -1; + } + } + break; + case select: + break; + } + + appState.camera.attach(); + drawRect(Rect(appState.map.size).addAll(4), mapAreaColor); + if (appState.camera.targetScale >= 1.0f) drawHollowRect(Rect(appState.map.size).addAll(4), 1, mapAreaOutlineColor); + drawTileMap(appState.atlas, appState.map, appState.camera.data); + if (appState.mode == AppMode.edit) drawHollowRect(Rect(worldGridMouse, appState.map.tileSize), 1, mouseAreaColor); + appState.camera.detach(); + + auto tempArea = Rect(windowSize); + auto topPanelArea = tempArea.subTop(panelHeight); + auto canvasArea = tempArea.subTop(tempArea.size.y - panelHeight); + auto bottomPanelArea = tempArea.subTop(panelHeight); + drawRect(topPanelArea, panelColor1); + drawRect(bottomPanelArea, panelColor1); + if (appState.camera.targetScale <= 0.5f) drawRect(Rect(windowCenter, 11, 11).centerArea, panelColor1); + + auto textOptions = DrawOptions(panelColor4); + textOptions.hook = Hook.center; + + tempArea = topPanelArea; + tempArea.subLeftRight(16); + tempArea.subTopBottom(8); + drawRect(tempArea.subLeft(48 - 16), panelColor2); // Menu + tempArea.subLeft(6); + drawRect(tempArea.subLeft(48 - 16), panelColor3); // Pencil + tempArea.subLeft(6); + drawRect(tempArea.subLeft(48 - 16), panelColor2); // Eraser + tempArea.subLeft(6); + drawRect(tempArea.subLeft(48 - 16), panelColor2); // Set + tempArea.subLeft(16); + auto mapButtonArea = tempArea.subLeft(200); + drawRect(mapButtonArea, panelColor2); // Map 1 + drawText(appState.mapFile.length ? appState.mapFile.pathBaseNameNoExt : "Empty", mapButtonArea.centerPoint.floor(), textOptions); + + tempArea = bottomPanelArea; + tempArea.subLeftRight(16); + tempArea.subTopBottom(8); + textOptions.hook = Hook.left; + drawText( + "({},{})({})".format( + isGridMouseInMap ? gridMouse.x : 0, + isGridMouseInMap ? gridMouse.y : 0, + isGridMouseInMap ? gridMouseIndex : 0, + ), + tempArea.leftPoint.floor(), + textOptions, + ); + textOptions.hook = Hook.center; + drawText(appState.atlasFile.length ? appState.atlasFile.pathBaseNameNoExt : "Empty", tempArea.centerPoint.floor(), textOptions); + textOptions.hook = Hook.right; + drawText("{}x{}".format(appState.map.tileWidth, appState.map.tileHeight), tempArea.rightPoint.floor(), textOptions); + + if (appState.mode == AppMode.select) { + auto selectArea = canvasArea; + selectArea.subTopBottom(panelHeight * 0.50f); + selectArea.subLeftRight(windowWidth * 0.17f); + auto atlasArea = selectArea; + atlasArea.subAll(8); + drawRect(selectArea, panelColor1); + drawTextureArea(appState.atlas, Rect(appState.atlas.size), atlasArea.position); + } + return false; +} + +void finish() { } + +mixin runGame!(ready, update, finish, 666, 666); diff --git a/packages/mapy/test.csv b/packages/mapy/test.csv new file mode 100644 index 0000000..820a6c6 --- /dev/null +++ b/packages/mapy/test.csv @@ -0,0 +1,4 @@ +42,-1,-1 +21,22,23 +37,38,39 +53,54,55 diff --git a/packages/rin/.closed b/packages/rin/.closed index 5c5b05b..57be39b 100644 --- a/packages/rin/.closed +++ b/packages/rin/.closed @@ -1,6 +1,5 @@ # This file is used by: https://github.com/Kapendev/closed --I=../../source -# HAHAHA IT FORCES YOU TO LINK WITH RAYLIB. Also no Joka :( + +-D=../../source/parin/story.d +# This line works for me. Use -s=custom to fix any errors. -I=../../../joka/source --L=-L../../../raylib/lib --L=-lraylib diff --git a/source/parin/map.d b/source/parin/map.d index 3417791..37ea46b 100644 --- a/source/parin/map.d +++ b/source/parin/map.d @@ -294,6 +294,10 @@ struct TileMap { } return Fault.none; } + + Fault parse(IStr csv) { + return parse(csv, tileWidth, tileHeight); + } } Fault saveTileMap(IStr path, TileMap map) {