mirror of
https://github.com/Kapendev/parin.git
synced 2025-04-25 20:49:57 +03:00
Working on a tile map editor.
This commit is contained in:
parent
c027170fd3
commit
31a96366b3
11 changed files with 291 additions and 7 deletions
|
@ -1,4 +1,3 @@
|
||||||
# Assets
|
# Assets
|
||||||
|
|
||||||
* [Atlas](parin_atlas.png): By [Kapendev](https://kapendev.itch.io), CC0
|
* [Atlas](parin_atlas.png): By [Kapendev](https://kapendev.itch.io), CC0
|
||||||
* [Monogram](parin_monogram.png): By [DATAGOBLIN](https://datagoblin.itch.io/monogram), CC0
|
|
||||||
|
|
1
dub.json
1
dub.json
|
@ -13,6 +13,7 @@
|
||||||
"source"
|
"source"
|
||||||
],
|
],
|
||||||
"subPackages" : [
|
"subPackages" : [
|
||||||
|
"packages/mapy",
|
||||||
"packages/rin",
|
"packages/rin",
|
||||||
"packages/setup",
|
"packages/setup",
|
||||||
"packages/web"
|
"packages/web"
|
||||||
|
|
|
@ -1,7 +1,14 @@
|
||||||
# This file is used by: https://github.com/Kapendev/closed
|
# This file is used by: https://github.com/Kapendev/closed
|
||||||
-I=../source
|
|
||||||
|
[default]
|
||||||
-o=../example
|
-o=../example
|
||||||
# No Joka :(
|
-I=../source
|
||||||
|
-L=-lraylib
|
||||||
|
# Those lines work for me. Use -s=custom to fix any errors.
|
||||||
-I=../../joka/source
|
-I=../../joka/source
|
||||||
-L=-L../../raylib/lib
|
-L=-L../../raylib/lib
|
||||||
|
|
||||||
|
[custom]
|
||||||
|
-o=../example
|
||||||
|
-I=../source
|
||||||
-L=-lraylib
|
-L=-lraylib
|
||||||
|
|
13
packages/mapy/.closed
Normal file
13
packages/mapy/.closed
Normal file
|
@ -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
|
18
packages/mapy/.gitignore
vendored
Normal file
18
packages/mapy/.gitignore
vendored
Normal file
|
@ -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*
|
8
packages/mapy/README.md
Normal file
8
packages/mapy/README.md
Normal file
|
@ -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`
|
13
packages/mapy/dub.json
Normal file
13
packages/mapy/dub.json
Normal file
|
@ -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": ".."}
|
||||||
|
},
|
||||||
|
}
|
218
packages/mapy/source/app.d
Normal file
218
packages/mapy/source/app.d
Normal file
|
@ -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);
|
4
packages/mapy/test.csv
Normal file
4
packages/mapy/test.csv
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
42,-1,-1
|
||||||
|
21,22,23
|
||||||
|
37,38,39
|
||||||
|
53,54,55
|
|
|
@ -1,6 +1,5 @@
|
||||||
# This file is used by: https://github.com/Kapendev/closed
|
# 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
|
-I=../../../joka/source
|
||||||
-L=-L../../../raylib/lib
|
|
||||||
-L=-lraylib
|
|
||||||
|
|
|
@ -294,6 +294,10 @@ struct TileMap {
|
||||||
}
|
}
|
||||||
return Fault.none;
|
return Fault.none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Fault parse(IStr csv) {
|
||||||
|
return parse(csv, tileWidth, tileHeight);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Fault saveTileMap(IStr path, TileMap map) {
|
Fault saveTileMap(IStr path, TileMap map) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue