summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKonAki <konaki@anokonaki.org>2026-10-15 14:57:24 +0800
committerKonAki <konaki@anokonaki.org>2026-10-15 14:57:24 +0800
commita971a6688ebdfc5fb4a21f2795546fd1cae8cc92 (patch)
treeef3ca3d414f041645b6493eaafc0ceb522d8653e
parenteef87380a41357bc3bb52149ec33f1787ebd2b40 (diff)
add rmtile: rpg maker tile geometry
-rw-r--r--kon/konrmtile.c216
-rw-r--r--kon/konrmtile.h67
2 files changed, 283 insertions, 0 deletions
diff --git a/kon/konrmtile.c b/kon/konrmtile.c
new file mode 100644
index 0000000..48a0eb3
--- /dev/null
+++ b/kon/konrmtile.c
@@ -0,0 +1,216 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * konrmtile.c
+ *
+ * Copyright (c) 2026 KonAki
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "konrmtile.h"
+
+#define TILE_B 0
+#define TILE_C 256
+#define TILE_D 512
+#define TILE_E 768
+#define TILE_A5 1536
+#define TILE_A1 2048
+#define TILE_A2 2816
+#define TILE_A3 4352
+#define TILE_A4 5888
+#define TILE_MAX 8192
+
+/*** the shapes ***/
+
+/* the four quarters of each of the 48 shapes of an autotile that is a floor or a water edge. every entry is the column and row
+ in the picture of the autotile's 4 by 6 quarters, for the top left, top right, bottom left and bottom right of the tile.
+ the order is the one the editor writes into the tile id */
+static const signed char floorTable[48][4][2] = {
+ {{2,4},{1,4},{2,3},{1,3}}, {{2,0},{1,4},{2,3},{1,3}}, {{2,4},{3,0},{2,3},{1,3}}, {{2,0},{3,0},{2,3},{1,3}},
+ {{2,4},{1,4},{2,3},{3,1}}, {{2,0},{1,4},{2,3},{3,1}}, {{2,4},{3,0},{2,3},{3,1}}, {{2,0},{3,0},{2,3},{3,1}},
+ {{2,4},{1,4},{2,1},{1,3}}, {{2,0},{1,4},{2,1},{1,3}}, {{2,4},{3,0},{2,1},{1,3}}, {{2,0},{3,0},{2,1},{1,3}},
+ {{2,4},{1,4},{2,1},{3,1}}, {{2,0},{1,4},{2,1},{3,1}}, {{2,4},{3,0},{2,1},{3,1}}, {{2,0},{3,0},{2,1},{3,1}},
+ {{0,4},{1,4},{0,3},{1,3}}, {{0,4},{3,0},{0,3},{1,3}}, {{0,4},{1,4},{0,3},{3,1}}, {{0,4},{3,0},{0,3},{3,1}},
+ {{2,2},{1,2},{2,3},{1,3}}, {{2,2},{1,2},{2,3},{3,1}}, {{2,2},{1,2},{2,1},{1,3}}, {{2,2},{1,2},{2,1},{3,1}},
+ {{2,4},{3,4},{2,3},{3,3}}, {{2,4},{3,4},{2,1},{3,3}}, {{2,0},{3,4},{2,3},{3,3}}, {{2,0},{3,4},{2,1},{3,3}},
+ {{2,4},{1,4},{2,5},{1,5}}, {{2,0},{1,4},{2,5},{1,5}}, {{2,4},{3,0},{2,5},{1,5}}, {{2,0},{3,0},{2,5},{1,5}},
+ {{0,4},{3,4},{0,3},{3,3}}, {{2,2},{1,2},{2,5},{1,5}}, {{0,2},{1,2},{0,3},{1,3}}, {{0,2},{1,2},{0,3},{3,1}},
+ {{2,2},{3,2},{2,3},{3,3}}, {{2,2},{3,2},{2,1},{3,3}}, {{2,4},{3,4},{2,5},{3,5}}, {{2,0},{3,4},{2,5},{3,5}},
+ {{0,4},{1,4},{0,5},{1,5}}, {{0,4},{3,0},{0,5},{1,5}}, {{0,2},{3,2},{0,3},{3,3}}, {{0,2},{1,2},{0,5},{1,5}},
+ {{0,4},{3,4},{0,5},{3,5}}, {{2,2},{3,2},{2,5},{3,5}}, {{0,2},{3,2},{0,5},{3,5}}, {{0,0},{1,0},{0,1},{1,1}}
+};
+
+/* walls have four on or off edges, the shape is those as bits: 1 left, 2 up, 4 right, 8 down. a set bit means that side is the border */
+static void wallQuarter(int shape, int corner, signed char out[2])
+{
+ int left = shape & 1, up = shape >> 1 & 1, right = shape >> 2 & 1, down = shape >> 3 & 1;
+
+ switch (corner) {
+ case 0: out[0] = left ? 0 : 2; out[1] = up ? 0 : 2; break;
+ case 1: out[0] = right ? 3 : 1; out[1] = up ? 0 : 2; break;
+ case 2: out[0] = left ? 0 : 2; out[1] = down ? 3 : 1; break;
+ default: out[0] = right ? 3 : 1; out[1] = down ? 3 : 1; break;
+ }
+}
+
+/* a waterfall only has the left and right: 1 left, 2 right, and it is always the top two rows of its picture */
+static void waterfallQuarter(int shape, int corner, signed char out[2])
+{
+ int left = shape & 1, right = shape >> 1 & 1;
+
+ out[0] = corner % 2 == 0 ? (left ? 0 : 2) : (right ? 3 : 1);
+ out[1] = corner < 2 ? 0 : 1;
+}
+
+/*** tiles ***/
+
+int kon_rmTileIsAutotile(int tileId)
+{
+ return tileId >= TILE_A1 && tileId < TILE_MAX;
+}
+
+int kon_rmTileIsAnimated(int tileId)
+{
+ int kind;
+
+ if (tileId < TILE_A1 || tileId >= TILE_A2) return 0;
+ kind = (tileId - TILE_A1) / 48;
+ return kind < 16;
+}
+
+int kon_rmTileIsHigher(int flags)
+{
+ return (flags & 0x10) != 0;
+}
+
+int kon_rmSheetSize(int sheet, int *columns, int *rows)
+{
+ int c, r;
+
+ switch (sheet) {
+ case 0: case 1: c = 32; r = 24; break;
+ case 2: c = 32; r = 16; break;
+ case 3: c = 32; r = 30; break;
+ case 4: c = 16; r = 32; break;
+ case 5: case 6: case 7: case 8: c = 32; r = 32; break;
+ default: return 0;
+ }
+ if (columns) *columns = c;
+ if (rows) *rows = r;
+ return 1;
+}
+
+static int plainTile(int tileId, kon_rmQuad_t quads[4])
+{
+ int sheet, column, row;
+
+ if (tileId >= TILE_A5 && tileId < TILE_A1) {
+ if (tileId >= TILE_A5 + 128) return 0;
+ sheet = 4;
+ column = tileId % 8;
+ row = (tileId / 8) % 16;
+ } else if (tileId < TILE_A5) {
+ if (tileId >= TILE_E + 256) return 0;
+ sheet = 5 + tileId / 256;
+ column = (tileId / 128 % 2) * 8 + tileId % 8;
+ row = (tileId % 256 / 8) % 16;
+ } else {
+ return 0;
+ }
+ quads[0].sheet = sheet;
+ quads[0].sx = column * 2;
+ quads[0].sy = row * 2;
+ quads[0].dx = quads[0].dy = 0;
+ quads[0].w = quads[0].h = 2;
+ return 1;
+}
+
+int kon_rmTileQuads(int tileId, int frame, kon_rmQuad_t quads[4])
+{
+ int kind, shape, tx, ty, bx = 0, by = 0, sheet = 0, i;
+ int table = 0; /* 0 floor, 1 wall, 2 waterfall */
+ int surface;
+
+ if (!quads || tileId <= 0 || tileId >= TILE_MAX) return 0;
+ if (!kon_rmTileIsAutotile(tileId)) return plainTile(tileId, quads);
+ if (frame < 0) frame = 0;
+
+ kind = (tileId - TILE_A1) / 48;
+ shape = (tileId - TILE_A1) % 48;
+ tx = kind % 8;
+ ty = kind / 8;
+ surface = (int[]){0, 1, 2, 1}[frame % 4];
+
+ if (tileId < TILE_A2) {
+ sheet = 0;
+ if (kind == 0) { bx = surface * 2; by = 0; }
+ else if (kind == 1) { bx = surface * 2; by = 3; }
+ else if (kind == 2) { bx = 6; by = 0; }
+ else if (kind == 3) { bx = 6; by = 3; }
+ else {
+ bx = tx / 4 * 8;
+ by = ty * 6 + tx / 2 % 2 * 3;
+ if (kind % 2 == 0) {
+ bx += surface * 2;
+ } else {
+ bx += 6;
+ table = 2;
+ by += frame % 3;
+ }
+ }
+ } else if (tileId < TILE_A3) {
+ sheet = 1;
+ bx = tx * 2;
+ by = (ty - 2) * 3;
+ } else if (tileId < TILE_A4) {
+ sheet = 2;
+ bx = tx * 2;
+ by = (ty - 6) * 2;
+ table = 1;
+ } else {
+ sheet = 3;
+ bx = tx * 2;
+ by = ((ty - 10) * 5 + (ty % 2 == 1 ? 1 : 0)) / 2;
+ if (ty % 2 == 1) table = 1;
+ }
+
+ if (table == 0 && shape >= 48) return 0;
+ if (table == 1 && shape >= 16) return 0;
+ if (table == 2 && shape >= 4) return 0;
+
+ for (i = 0; i < 4; i++) {
+ signed char q[2];
+
+ if (table == 0) {
+ q[0] = floorTable[shape][i][0];
+ q[1] = floorTable[shape][i][1];
+ } else if (table == 1) {
+ wallQuarter(shape, i, q);
+ } else {
+ waterfallQuarter(shape, i, q);
+ }
+ quads[i].sheet = sheet;
+ quads[i].sx = bx * 2 + q[0];
+ quads[i].sy = by * 2 + q[1];
+ quads[i].dx = i % 2;
+ quads[i].dy = i / 2;
+ quads[i].w = quads[i].h = 1;
+ }
+ return 4;
+}
diff --git a/kon/konrmtile.h b/kon/konrmtile.h
new file mode 100644
index 0000000..bae2ada
--- /dev/null
+++ b/kon/konrmtile.h
@@ -0,0 +1,67 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * konrmtile.h
+ *
+ * Copyright (c) 2026 KonAki
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef KONRMTILE_H
+#define KONRMTILE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*** definition ***/
+
+/* where the pieces of one RPG Maker map tile come from in the tileset pictures, with nothing drawn. a tile is one whole piece
+ from a picture, or for an autotile four quarters that fit together with their neighbours. everything is counted in half tiles,
+ so for a picture made of 48 pixel tiles a half tile is 24 pixels. mzmap writes the tileset's picture names in order:
+ sheet 0 is A1, 1 A2, 2 A3, 3 A4, 4 A5, 5 B, 6 C, 7 D and 8 E */
+
+#define KON_RM_SHEETS 9
+
+typedef struct kon_rmQuad {
+ int sheet;
+ int sx, sy; /* where the piece starts in the picture */
+ int dx, dy; /* where it goes inside the tile, 0 or 1 for a quarter */
+ int w, h; /* 2 and 2 for a whole tile, 1 and 1 for a quarter */
+} kon_rmQuad_t;
+
+/* fills quads (room for 4) and returns how many there are: 0 for an empty tile or one that is not drawn, 1 for a whole tile,
+ 4 for an autotile. frame is the animation count, water and waterfalls move with it and it is 0 for a still picture */
+int kon_rmTileQuads(int tileId, int frame, kon_rmQuad_t quads[4]);
+
+/* how many half tiles wide and tall a picture of that sheet has to be to hold every piece of it, for checking a loaded picture.
+ returns 0 for a sheet number that does not exist */
+int kon_rmSheetSize(int sheet, int *columns, int *rows);
+
+/* 1 for the tiles that are drawn above the characters: the ones marked with a star, flags is the tile's flags from its tileset */
+int kon_rmTileIsHigher(int flags);
+
+/* 1 for a tile that is an autotile, 1 for one that moves */
+int kon_rmTileIsAutotile(int tileId);
+int kon_rmTileIsAnimated(int tileId);
+
+#ifdef __cplusplus
+}
+#endif
+#endif