/* 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; }