summaryrefslogtreecommitdiff
path: root/kon
diff options
context:
space:
mode:
authorKonAki <konaki@anokonaki.org>2026-10-10 13:39:48 +0800
committerKonAki <konaki@anokonaki.org>2026-10-10 13:39:48 +0800
commit1064801706b24e12229996765daf43fafe651bf0 (patch)
tree8a0f84b973179fd495e831e254b1cb0ade053436 /kon
parentee3a22ce2987790cec4b47db0b9cc1edf018ff19 (diff)
tilemap move that slides along walls
Diffstat (limited to 'kon')
-rw-r--r--kon/kontile.c70
-rw-r--r--kon/kontile.h12
2 files changed, 82 insertions, 0 deletions
diff --git a/kon/kontile.c b/kon/kontile.c
index b133656..add9ba9 100644
--- a/kon/kontile.c
+++ b/kon/kontile.c
@@ -26,6 +26,8 @@
#include "kontile.h"
+#include <math.h>
+
static int kon_floorDiv_(int a, int b) {
int q = a / b;
return (a % b != 0 && (a < 0) != (b < 0)) ? q - 1 : q;
@@ -99,3 +101,71 @@ void kon_drawTilemap(kon_framebuffer_t *fb, const kon_tilemap_t *map, int camera
}
}
}
+
+/*** moving ***/
+
+static int kon_tileSolid_(const kon_tilemap_t *map, int column, int row, const uint8_t *solid, int solidCount) {
+ uint16_t id = kon_tilemapAt(map, column, row);
+
+ return id < solidCount && solid[id];
+}
+
+/* tiles a box covers, an edge exactly on a tile border does not touch the next tile */
+static void kon_tileSpan_(const kon_tilemap_t *map, kon_box2_t box, int *c0, int *r0, int *c1, int *r1) {
+ *c0 = (int)floorf(box.x / (float)map->tileWidth);
+ *r0 = (int)floorf(box.y / (float)map->tileHeight);
+ *c1 = (int)ceilf((box.x + box.w) / (float)map->tileWidth) - 1;
+ *r1 = (int)ceilf((box.y + box.h) / (float)map->tileHeight) - 1;
+}
+
+/* moves along x or y by at most one tile at a time. returns 1 if it ran into a tile */
+static int kon_tileStep_(const kon_tilemap_t *map, kon_box2_t *box, int vertical, float amount, const uint8_t *solid, int solidCount) {
+ float tile = (float)(vertical ? map->tileHeight : map->tileWidth);
+ int c0, r0, c1, r1, c, r;
+
+ while (amount != 0.0f) {
+ float step = amount > tile ? tile : (amount < -tile ? -tile : amount);
+ kon_box2_t next = *box;
+
+ if (vertical) next.y += step;
+ else next.x += step;
+ amount -= step;
+
+ kon_tileSpan_(map, next, &c0, &r0, &c1, &r1);
+
+ /* the line of tiles the box is moving into */
+ {
+ int line = vertical ? (step > 0.0f ? r1 : r0) : (step > 0.0f ? c1 : c0);
+ int hit = 0, b0, br0, b1, br1;
+
+ if (vertical) {
+ for (c = c0; c <= c1 && !hit; c++) hit = kon_tileSolid_(map, c, line, solid, solidCount);
+ } else {
+ for (r = r0; r <= r1 && !hit; r++) hit = kon_tileSolid_(map, line, r, solid, solidCount);
+ }
+
+ /* a line the box was already in means it started inside a tile, let it move */
+ kon_tileSpan_(map, *box, &b0, &br0, &b1, &br1);
+ if (hit && (vertical ? (line >= br0 && line <= br1) : (line >= b0 && line <= b1))) hit = 0;
+
+ if (hit) {
+ if (vertical) next.y = step > 0.0f ? (float)(line * map->tileHeight) - next.h : (float)((line + 1) * map->tileHeight);
+ else next.x = step > 0.0f ? (float)(line * map->tileWidth) - next.w : (float)((line + 1) * map->tileWidth);
+ *box = next;
+ return 1;
+ }
+ }
+ *box = next;
+ }
+ return 0;
+}
+
+int kon_tilemapMove(const kon_tilemap_t *map, kon_box2_t *box, float dx, float dy, const uint8_t *solid, int solidCount) {
+ int flags = 0;
+
+ if (!map || !box || !solid || solidCount <= 0 || map->tileWidth <= 0 || map->tileHeight <= 0 || box->w <= 0.0f || box->h <= 0.0f) return 0;
+
+ if (kon_tileStep_(map, box, 0, dx, solid, solidCount)) flags |= dx > 0.0f ? KON_HIT_RIGHT : KON_HIT_LEFT;
+ if (kon_tileStep_(map, box, 1, dy, solid, solidCount)) flags |= dy > 0.0f ? KON_HIT_DOWN : KON_HIT_UP;
+ return flags;
+}
diff --git a/kon/kontile.h b/kon/kontile.h
index f671da9..00119b1 100644
--- a/kon/kontile.h
+++ b/kon/kontile.h
@@ -27,6 +27,7 @@
#define KONTILE_H
#include "konsofren.h"
+#include "koncollide.h"
/*** definition ***/
@@ -52,6 +53,17 @@ int kon_tilemapPixelToTile(const kon_tilemap_t *map, int x, int y, int *column,
and everything outside the map counts as. returns 1 if any tile touching the box x, y, width, height (in map pixels) is solid */
int kon_tilemapBoxHits(const kon_tilemap_t *map, int x, int y, int width, int height, const uint8_t *solid, int solidCount);
+/* which sides of the box ran into something in kon_tilemapMove */
+#define KON_HIT_LEFT 1
+#define KON_HIT_RIGHT 2
+#define KON_HIT_UP 4
+#define KON_HIT_DOWN 8
+
+/* moves the box by dx, dy (in map pixels) and stops it against solid tiles, one axis after the other so it slides along walls.
+ solid works like in kon_tilemapBoxHits. returns the KON_HIT_ flags of what stopped it, down is the way y grows.
+ the box can start inside a solid tile, it is left alone on that axis. fast moves do not tunnel through tiles */
+int kon_tilemapMove(const kon_tilemap_t *map, kon_box2_t *box, float dx, float dy, const uint8_t *solid, int solidCount);
+
/* cameraX, cameraY is the map pixel shown at the top left of fb */
void kon_drawTilemap(kon_framebuffer_t *fb, const kon_tilemap_t *map, int cameraX, int cameraY);