From 01ef6c5785177d558cc861b0a1c94908aa344323 Mon Sep 17 00:00:00 2001
From: Marcin Kurczewski <dash@wind.garden>
Date: Mon, 22 Jul 2024 10:41:46 +0200
Subject: [PATCH] port Room_SetBounds

---
 docs/progress.svg    |  16 ++---
 docs/progress.txt    |   9 ++-
 src/game/room_draw.c | 148 ++++++++++++++++++++++++++++++++++++++++++-
 src/game/room_draw.h |   4 ++
 src/global/funcs.h   |   1 -
 src/global/types.h   |   6 ++
 src/global/vars.h    |   1 +
 src/inject_exec.c    |   1 +
 8 files changed, 175 insertions(+), 11 deletions(-)

diff --git a/docs/progress.svg b/docs/progress.svg
index 7a62a2d2..cc9f2412 100644
--- a/docs/progress.svg
+++ b/docs/progress.svg
@@ -69,10 +69,10 @@
 </g>
 <g transform="translate(0 116)">
 <text x="0" y="7.50">Tomb2.exe progress according to the physical function order:</text>
-<text class="todo" style="font-size: 12px; " x="747" y="9"><tspan text-anchor="end"><tspan class="decompiled">47.87% (583)</tspan> · <tspan class="known">49.67% (605)</tspan> · <tspan class="todo">0% (0)</tspan> · <tspan class="unused">2.46% (30)</tspan></tspan></text>
+<text class="todo" style="font-size: 12px; " x="747" y="9"><tspan text-anchor="end"><tspan class="decompiled">47.95% (584)</tspan> · <tspan class="known">49.59% (604)</tspan> · <tspan class="todo">0% (0)</tspan> · <tspan class="unused">2.46% (30)</tspan></tspan></text>
 <g transform="translate(0 20)">
-<rect width="357.55" height="6" x="0" y="0" class="decompiled"/>
-<rect width="371.05" height="6" x="357.55" y="0" class="known"/>
+<rect width="358.17" height="6" x="0" y="0" class="decompiled"/>
+<rect width="370.43" height="6" x="358.17" y="0" class="known"/>
 <rect width="18.40" height="6" x="728.60" y="0" class="unused"/>
 </g>
 <g transform="translate(0 31)">
@@ -286,7 +286,7 @@
 <rect width="12" height="12" x="105" y="60" class="known"><title>int32_t __cdecl Game_Draw(void);</title></rect>
 <rect width="12" height="12" x="120" y="60" class="known"><title>void __cdecl Room_DrawAllRooms(int16_t current_room);</title></rect>
 <rect width="12" height="12" x="135" y="60" class="decompiled"><title>void __cdecl Room_GetBounds(void);</title></rect>
-<rect width="12" height="12" x="150" y="60" class="known"><title>void __cdecl Room_SetBounds(const int16_t *objptr, int32_t room_num, ROOM_INFO *parent);</title></rect>
+<rect width="12" height="12" x="150" y="60" class="decompiled"><title>void __cdecl Room_SetBounds(const int16_t *objptr, int32_t room_num, ROOM_INFO *parent);</title></rect>
 <rect width="12" height="12" x="165" y="60" class="known"><title>void __cdecl Room_Clip(ROOM_INFO *r);</title></rect>
 <rect width="12" height="12" x="180" y="60" class="known"><title>void __cdecl Room_DrawSingleRoomGeometry(int16_t room_num);</title></rect>
 <rect width="12" height="12" x="195" y="60" class="known"><title>void __cdecl Room_DrawSingleRoomObjects(int16_t room_num);</title></rect>
@@ -1298,10 +1298,10 @@
 </g>
 <g transform="translate(0 546)">
 <text x="0" y="7.50">Tomb2.exe progress according to the function sizes:</text>
-<text class="todo" style="font-size: 12px; " x="747" y="9"><tspan text-anchor="end"><tspan class="decompiled">45.34%</tspan> · <tspan class="known">54.34%</tspan> · <tspan class="todo">0%</tspan> · <tspan class="unused">0.33%</tspan></tspan></text>
+<text class="todo" style="font-size: 12px; " x="747" y="9"><tspan text-anchor="end"><tspan class="decompiled">45.60%</tspan> · <tspan class="known">54.08%</tspan> · <tspan class="todo">0%</tspan> · <tspan class="unused">0.33%</tspan></tspan></text>
 <g transform="translate(0 20)">
-<rect width="338.66" height="6" x="0" y="0" class="decompiled"/>
-<rect width="405.90" height="6" x="338.66" y="0" class="known"/>
+<rect width="340.62" height="6" x="0" y="0" class="decompiled"/>
+<rect width="403.94" height="6" x="340.62" y="0" class="known"/>
 <rect width="2.44" height="6" x="744.56" y="0" class="unused"/>
 </g>
 <g transform="translate(0 31)">
@@ -1385,7 +1385,7 @@
 <rect width="24.49" height="24.32" x="267.09" y="83.15" class="known"><title>void __cdecl Diver_Control(int16_t item_num);</title></rect>
 <rect width="24.49" height="24.26" x="267.09" y="110.47" class="known"><title>void __cdecl Dog_Control(int16_t item_num);</title></rect>
 <rect width="24.49" height="24.23" x="267.09" y="137.73" class="known"><title>int32_t __cdecl Effect_ExplodingDeath(int16_t item_num, int32_t mesh_bits, int16_t damage);</title></rect>
-<rect width="24.49" height="24.02" x="267.09" y="164.97" class="known"><title>void __cdecl Room_SetBounds(const int16_t *objptr, int32_t room_num, ROOM_INFO *parent);</title></rect>
+<rect width="24.49" height="24.02" x="267.09" y="164.97" class="decompiled"><title>void __cdecl Room_SetBounds(const int16_t *objptr, int32_t room_num, ROOM_INFO *parent);</title></rect>
 <rect width="24.49" height="23.99" x="267.09" y="191.99" class="known"><title>void __cdecl AnimateShotgun(int32_t weapon_type);</title></rect>
 <rect width="24.49" height="23.81" x="267.09" y="218.98" class="known"><title>void __cdecl Object_DrawAnimatingItem(ITEM_INFO *item);</title></rect>
 <rect width="24.49" height="23.69" x="267.09" y="245.79" class="decompiled"><title>void __cdecl WinVidStart(void);</title></rect>
diff --git a/docs/progress.txt b/docs/progress.txt
index eb5d6567..11fde05e 100644
--- a/docs/progress.txt
+++ b/docs/progress.txt
@@ -2606,6 +2606,12 @@ typedef struct __unaligned {
     } shift, rot;
 } OBJECT_BOUNDS;
 
+typedef struct __unaligned {
+    int32_t xv;
+    int32_t yv;
+    int32_t zv;
+} DOOR_VBUF;
+
 typedef enum {
     TO_OBJECT      = 0,
     TO_CAMERA      = 1,
@@ -2883,7 +2889,7 @@ typedef enum {
 0x00418990  0x0037  -       int32_t __cdecl Game_Draw(void);
 0x004189D0  0x02B0  -       void __cdecl Room_DrawAllRooms(int16_t current_room);
 0x00418C80  0x01C6  +       void __cdecl Room_GetBounds(void);
-0x00418E50  0x037F  -       void __cdecl Room_SetBounds(const int16_t *objptr, int32_t room_num, ROOM_INFO *parent);
+0x00418E50  0x037F  +       void __cdecl Room_SetBounds(const int16_t *objptr, int32_t room_num, ROOM_INFO *parent);
 0x004191D0  0x03D2  -       void __cdecl Room_Clip(ROOM_INFO *r);
 0x004195B0  0x00B4  -       void __cdecl Room_DrawSingleRoomGeometry(int16_t room_num);
 0x00419670  0x0218  -       void __cdecl Room_DrawSingleRoomObjects(int16_t room_num);
@@ -4338,3 +4344,4 @@ typedef enum {
 0x005261AC  -       int32_t g_OutsideTop;
 0x00525B00  -       int32_t g_OutsideBottom;
 0x00525900  -       int32_t g_BoundRooms[MAX_BOUND_ROOMS];
+0x005258C0  -       DOOR_VBUF g_DoorVBuf[4];
diff --git a/src/game/room_draw.c b/src/game/room_draw.c
index b8cdbe0c..6404be2f 100644
--- a/src/game/room_draw.c
+++ b/src/game/room_draw.c
@@ -2,7 +2,6 @@
 
 #include "game/matrix.h"
 #include "global/funcs.h"
-#include "global/types.h"
 #include "global/vars.h"
 
 void __cdecl Room_GetBounds(void)
@@ -76,3 +75,150 @@ void __cdecl Room_GetBounds(void)
         Matrix_Pop();
     }
 }
+
+void __cdecl Room_SetBounds(
+    const int16_t *obj_ptr, int32_t room_num, const ROOM_INFO *parent)
+{
+    ROOM_INFO *const r = &g_Rooms[room_num];
+    const DOOR_INFO *const door = (const DOOR_INFO *)(obj_ptr - 1);
+
+    // clang-format off
+    if (r->bound_left <= parent->test_left &&
+        r->bound_right >= parent->test_right &&
+        r->bound_top <= parent->test_top &&
+        r->bound_bottom >= parent->test_bottom
+   ) {
+        return;
+    }
+    // clang-format on
+
+    const MATRIX *const m = g_MatrixPtr;
+    int32_t left = parent->test_right;
+    int32_t right = parent->test_left;
+    int32_t bottom = parent->test_top;
+    int32_t top = parent->test_bottom;
+
+    DOOR_VBUF door_vbuf[4];
+    int32_t z_behind = 0;
+    int32_t z_too_far = 0;
+
+    for (int32_t i = 0; i < 4; i++) {
+        DOOR_VBUF *const dvbuf = &door_vbuf[i];
+        const XYZ_16 *const dvtx = &door->vertex[i];
+        const int32_t xv =
+            m->_03 + dvtx->x * m->_00 + m->_01 * dvtx->y + m->_02 * dvtx->z;
+        const int32_t yv =
+            m->_13 + m->_12 * dvtx->z + m->_11 * dvtx->y + m->_10 * dvtx->x;
+        const int32_t zv =
+            m->_23 + m->_21 * dvtx->y + m->_22 * dvtx->z + m->_20 * dvtx->x;
+        dvbuf->xv = xv;
+        dvbuf->yv = yv;
+        dvbuf->zv = zv;
+
+        if (zv <= 0) {
+            z_behind++;
+            continue;
+        }
+
+        if (zv > g_PhdFarZ) {
+            z_too_far++;
+        }
+
+        int32_t xs;
+        int32_t ys;
+        const int32_t zp = zv / g_PhdPersp;
+        if (zp) {
+            xs = xv / zp + g_PhdWinCenterX;
+            ys = yv / zp + g_PhdWinCenterY;
+        } else {
+            xs = xv < 0 ? g_PhdWinLeft : g_PhdWinRight;
+            ys = yv < 0 ? g_PhdWinTop : g_PhdWinBottom;
+        }
+
+        if (xs - 1 < left) {
+            left = xs - 1;
+        }
+        if (xs + 1 > right) {
+            right = xs + 1;
+        }
+        if (ys - 1 < top) {
+            top = ys - 1;
+        }
+        if (ys + 1 > bottom) {
+            bottom = ys + 1;
+        }
+    }
+
+    if (z_behind == 4 || z_too_far == 4) {
+        return;
+    }
+
+    if (z_behind > 0) {
+        const DOOR_VBUF *dest = &door_vbuf[0];
+        const DOOR_VBUF *last = &door_vbuf[3];
+
+        for (int32_t i = 0; i < 4; i++, last = dest++) {
+            if ((dest->zv < 0) == (last->zv < 0)) {
+                continue;
+            }
+
+            if (dest->xv < 0 && last->xv < 0) {
+                left = 0;
+            } else if (dest->xv > 0 && last->xv > 0) {
+                right = g_PhdWinMaxX;
+            } else {
+                left = 0;
+                right = g_PhdWinMaxX;
+            }
+
+            if (dest->yv < 0 && last->yv < 0) {
+                top = 0;
+            } else if (dest->yv > 0 && last->yv > 0) {
+                bottom = g_PhdWinMaxY;
+            } else {
+                top = 0;
+                bottom = g_PhdWinMaxY;
+            }
+        }
+    }
+
+    if (left < parent->test_left) {
+        left = parent->test_left;
+    }
+    if (right > parent->test_right) {
+        right = parent->test_right;
+    }
+    if (top < parent->test_top) {
+        top = parent->test_top;
+    }
+    if (bottom > parent->test_bottom) {
+        bottom = parent->test_bottom;
+    }
+
+    if (left >= right || top >= bottom) {
+        return;
+    }
+
+    if (r->bound_active & 2) {
+        if (left < r->test_left) {
+            r->test_left = left;
+        }
+        if (top < r->test_top) {
+            r->test_top = top;
+        }
+        if (right > r->test_right) {
+            r->test_right = right;
+        }
+        if (bottom > r->test_bottom) {
+            r->test_bottom = bottom;
+        }
+    } else {
+        g_BoundRooms[g_BoundEnd++ % MAX_BOUND_ROOMS] = room_num;
+        r->bound_active |= 2;
+        r->bound_active += (int16_t)(g_MidSort << 8);
+        r->test_left = left;
+        r->test_right = right;
+        r->test_top = top;
+        r->test_bottom = bottom;
+    }
+}
diff --git a/src/game/room_draw.h b/src/game/room_draw.h
index 42977df2..b69c8ffe 100644
--- a/src/game/room_draw.h
+++ b/src/game/room_draw.h
@@ -1,3 +1,7 @@
 #pragma once
 
+#include "global/types.h"
+
 void __cdecl Room_GetBounds(void);
+void __cdecl Room_SetBounds(
+    const int16_t *obj_ptr, int32_t room_num, const ROOM_INFO *parent);
diff --git a/src/global/funcs.h b/src/global/funcs.h
index 3bf40a5a..cf295419 100644
--- a/src/global/funcs.h
+++ b/src/global/funcs.h
@@ -31,7 +31,6 @@
 #define Game_DrawCinematic ((int32_t __cdecl (*)(void))0x00418950)
 #define Game_Draw ((int32_t __cdecl (*)(void))0x00418990)
 #define Room_DrawAllRooms ((void __cdecl (*)(int16_t current_room))0x004189D0)
-#define Room_SetBounds ((void __cdecl (*)(const int16_t *objptr, int32_t room_num, ROOM_INFO *parent))0x00418E50)
 #define Room_Clip ((void __cdecl (*)(ROOM_INFO *r))0x004191D0)
 #define Room_DrawSingleRoomGeometry ((void __cdecl (*)(int16_t room_num))0x004195B0)
 #define Room_DrawSingleRoomObjects ((void __cdecl (*)(int16_t room_num))0x00419670)
diff --git a/src/global/types.h b/src/global/types.h
index 11ddb7d9..b099f152 100644
--- a/src/global/types.h
+++ b/src/global/types.h
@@ -2622,6 +2622,12 @@ typedef struct __unaligned {
     } shift, rot;
 } OBJECT_BOUNDS;
 
+typedef struct __unaligned {
+    int32_t xv;
+    int32_t yv;
+    int32_t zv;
+} DOOR_VBUF;
+
 typedef enum {
     TO_OBJECT      = 0,
     TO_CAMERA      = 1,
diff --git a/src/global/vars.h b/src/global/vars.h
index 04911d9e..c56e60aa 100644
--- a/src/global/vars.h
+++ b/src/global/vars.h
@@ -321,6 +321,7 @@ extern const char *g_TR2XVersion;
 #define g_Outside (*(int32_t*)0x005252B4)
 #define g_DrawRoomsCount (*(int32_t*)0x005252B8)
 #define g_IMMatrixStack (*(MATRIX(*)[256])0x005252C0)
+#define g_DoorVBuf (*(DOOR_VBUF(*)[4])0x005258C0)
 #define g_IMFrac (*(int32_t*)0x005258F0)
 #define g_Anims (*(ANIM_STRUCT **)0x005258F4)
 #define g_BoundRooms (*(int32_t(*)[MAX_BOUND_ROOMS])0x00525900)
diff --git a/src/inject_exec.c b/src/inject_exec.c
index 6eaf0236..849f151b 100644
--- a/src/inject_exec.c
+++ b/src/inject_exec.c
@@ -252,6 +252,7 @@ static void Inject_Room(const bool enable)
     INJECT(enable, 0x00416700, Room_RemoveFlipItems);
     INJECT(enable, 0x004167A0, Room_AddFlipItems);
     INJECT(enable, 0x00418C80, Room_GetBounds);
+    INJECT(enable, 0x00418E50, Room_SetBounds);
 }
 
 static void Inject_Matrix(const bool enable)