From 9a934f844380573905ff8ebdb4b3deff07166d86 Mon Sep 17 00:00:00 2001
From: Cooliokid956 <68075390+Cooliokid956@users.noreply.github.com>
Date: Tue, 21 Oct 2025 12:04:05 -0500
Subject: [PATCH] Static Object Collision / Check for freed CObject (#924)
* Static Object Collision
* no soc surface pool; bnot
* Isaac review
---------
Co-authored-by: PeachyPeachSM64 <72323920+PeachyPeachSM64@users.noreply.github.com>
---
autogen/convert_structs.py | 1 +
autogen/lua_definitions/constants.lua | 3 +
autogen/lua_definitions/functions.lua | 21 ++++
autogen/lua_definitions/structs.lua | 4 +
docs/lua/constants.md | 1 +
docs/lua/functions-7.md | 69 ++++++++++
docs/lua/functions.md | 3 +
docs/lua/structs.md | 12 ++
include/surface_terrains.h | 1 +
src/engine/surface_collision.c | 10 +-
src/engine/surface_collision.h | 6 +
src/engine/surface_load.c | 174 +++++++++++++++++++++-----
src/engine/surface_load.h | 16 +++
src/game/memory.c | 26 ++++
src/game/memory.h | 1 +
src/game/object_list_processor.c | 21 ----
src/game/object_list_processor.h | 5 -
src/pc/lua/smlua_cobject.c | 8 ++
src/pc/lua/smlua_cobject_autogen.c | 8 ++
src/pc/lua/smlua_cobject_autogen.h | 1 +
src/pc/lua/smlua_constants_autogen.c | 1 +
src/pc/lua/smlua_functions_autogen.c | 56 +++++++++
src/pc/lua/smlua_utils.h | 1 +
23 files changed, 388 insertions(+), 61 deletions(-)
diff --git a/autogen/convert_structs.py b/autogen/convert_structs.py
index 9216d915e..a15eb03da 100644
--- a/autogen/convert_structs.py
+++ b/autogen/convert_structs.py
@@ -142,6 +142,7 @@ override_field_immutable = {
"DialogEntry": [ "unused", "linesPerBox", "leftOffset", "width", "str", "text", "replaced"],
"ModFsFile": [ "*" ],
"ModFs": [ "*" ],
+ "StaticObjectCollision": [ "*" ],
}
override_field_version_excludes = {
diff --git a/autogen/lua_definitions/constants.lua b/autogen/lua_definitions/constants.lua
index 601038e72..91e53ffeb 100644
--- a/autogen/lua_definitions/constants.lua
+++ b/autogen/lua_definitions/constants.lua
@@ -10956,6 +10956,9 @@ SURFACE_FLAG_DYNAMIC = (1 << 0)
--- @type integer
SURFACE_FLAG_NO_CAM_COLLISION = (1 << 1)
+--- @type integer
+SURFACE_FLAG_INTANGIBLE = (1 << 2)
+
--- @type integer
SURFACE_FLAG_X_PROJECTION = (1 << 3)
diff --git a/autogen/lua_definitions/functions.lua b/autogen/lua_definitions/functions.lua
index 673c1a31f..7ce58f6e8 100644
--- a/autogen/lua_definitions/functions.lua
+++ b/autogen/lua_definitions/functions.lua
@@ -12295,6 +12295,27 @@ function load_object_collision_model()
-- ...
end
+--- @return StaticObjectCollision
+--- Loads the object's collision data into static collision. You may run this only once to capture the object's collision at that frame.
+function load_static_object_collision()
+ -- ...
+end
+
+--- @param col StaticObjectCollision
+--- @param tangible boolean
+--- Toggles a collection of static object surfaces
+function toggle_static_object_collision(col, tangible)
+ -- ...
+end
+
+--- @param col StaticObjectCollision
+--- @param index integer
+--- @return Surface
+--- Gets a surface corresponding to `index` from the static object collision
+function get_static_object_surface(col, index)
+ -- ...
+end
+
--- @param o Object
--- @param index integer
--- @return Surface
diff --git a/autogen/lua_definitions/structs.lua b/autogen/lua_definitions/structs.lua
index 76a1c2a0f..614411477 100644
--- a/autogen/lua_definitions/structs.lua
+++ b/autogen/lua_definitions/structs.lua
@@ -2337,6 +2337,10 @@
--- @field public dialog5 integer
--- @field public dialog6 integer
+--- @class StaticObjectCollision
+--- @field public index integer
+--- @field public length integer
+
--- @class Surface
--- @field public type integer
--- @field public flags integer
diff --git a/docs/lua/constants.md b/docs/lua/constants.md
index 6f7403334..2a9b5aecf 100644
--- a/docs/lua/constants.md
+++ b/docs/lua/constants.md
@@ -4620,6 +4620,7 @@
- SURFACE_CLASS_NOT_SLIPPERY
- SURFACE_FLAG_DYNAMIC
- SURFACE_FLAG_NO_CAM_COLLISION
+- SURFACE_FLAG_INTANGIBLE
- SURFACE_FLAG_X_PROJECTION
- HAZARD_TYPE_LAVA_FLOOR
- HAZARD_TYPE_LAVA_WALL
diff --git a/docs/lua/functions-7.md b/docs/lua/functions-7.md
index c1c05249a..3c6be9bb4 100644
--- a/docs/lua/functions-7.md
+++ b/docs/lua/functions-7.md
@@ -32,6 +32,75 @@ Loads the object's collision data into dynamic collision. You must run this ever
+## [load_static_object_collision](#load_static_object_collision)
+
+### Description
+Loads the object's collision data into static collision. You may run this only once to capture the object's collision at that frame.
+
+### Lua Example
+`local StaticObjectCollisionValue = load_static_object_collision()`
+
+### Parameters
+- None
+
+### Returns
+[StaticObjectCollision](structs.md#StaticObjectCollision)
+
+### C Prototype
+`struct StaticObjectCollision *load_static_object_collision();`
+
+[:arrow_up_small:](#)
+
+
+
+## [toggle_static_object_collision](#toggle_static_object_collision)
+
+### Description
+Toggles a collection of static object surfaces
+
+### Lua Example
+`toggle_static_object_collision(col, tangible)`
+
+### Parameters
+| Field | Type |
+| ----- | ---- |
+| col | [StaticObjectCollision](structs.md#StaticObjectCollision) |
+| tangible | `boolean` |
+
+### Returns
+- None
+
+### C Prototype
+`void toggle_static_object_collision(struct StaticObjectCollision *col, bool tangible);`
+
+[:arrow_up_small:](#)
+
+
+
+## [get_static_object_surface](#get_static_object_surface)
+
+### Description
+Gets a surface corresponding to `index` from the static object collision
+
+### Lua Example
+`local SurfaceValue = get_static_object_surface(col, index)`
+
+### Parameters
+| Field | Type |
+| ----- | ---- |
+| col | [StaticObjectCollision](structs.md#StaticObjectCollision) |
+| index | `integer` |
+
+### Returns
+[Surface](structs.md#Surface)
+
+### C Prototype
+`struct Surface *get_static_object_surface(struct StaticObjectCollision *col, u32 index);`
+
+[:arrow_up_small:](#)
+
+
+
## [obj_get_surface_from_index](#obj_get_surface_from_index)
### Description
diff --git a/docs/lua/functions.md b/docs/lua/functions.md
index 4ec201ce2..1783c3bae 100644
--- a/docs/lua/functions.md
+++ b/docs/lua/functions.md
@@ -2189,6 +2189,9 @@
- surface_load.h
- [load_object_collision_model](functions-7.md#load_object_collision_model)
+ - [load_static_object_collision](functions-7.md#load_static_object_collision)
+ - [toggle_static_object_collision](functions-7.md#toggle_static_object_collision)
+ - [get_static_object_surface](functions-7.md#get_static_object_surface)
- [obj_get_surface_from_index](functions-7.md#obj_get_surface_from_index)
- [surface_has_force](functions-7.md#surface_has_force)
diff --git a/docs/lua/structs.md b/docs/lua/structs.md
index 6ba8fff59..0083c2a6c 100644
--- a/docs/lua/structs.md
+++ b/docs/lua/structs.md
@@ -105,6 +105,7 @@
- [SpawnParticlesInfo](#SpawnParticlesInfo)
- [StarPositions](#StarPositions)
- [StarsNeededForDialog](#StarsNeededForDialog)
+- [StaticObjectCollision](#StaticObjectCollision)
- [Surface](#Surface)
- [TextureInfo](#TextureInfo)
- [TransitionInfo](#TransitionInfo)
@@ -3210,6 +3211,17 @@
+## [StaticObjectCollision](#StaticObjectCollision)
+
+| Field | Type | Access |
+| ----- | ---- | ------ |
+| index | `integer` | read-only |
+| length | `integer` | read-only |
+
+[:arrow_up_small:](#)
+
+
+
## [Surface](#Surface)
| Field | Type | Access |
diff --git a/include/surface_terrains.h b/include/surface_terrains.h
index 091ed64d4..c43924a5f 100644
--- a/include/surface_terrains.h
+++ b/include/surface_terrains.h
@@ -163,6 +163,7 @@
#define SURFACE_FLAG_DYNAMIC (1 << 0)
#define SURFACE_FLAG_NO_CAM_COLLISION (1 << 1)
+#define SURFACE_FLAG_INTANGIBLE (1 << 2)
#define SURFACE_FLAG_X_PROJECTION (1 << 3)
#define HAZARD_TYPE_LAVA_FLOOR 1
diff --git a/src/engine/surface_collision.c b/src/engine/surface_collision.c
index 96ef6cfcb..1e34afd7f 100644
--- a/src/engine/surface_collision.c
+++ b/src/engine/surface_collision.c
@@ -137,9 +137,8 @@ static s32 find_wall_collisions_from_list(struct SurfaceNode *surfaceNode,
surfaceNode = surfaceNode->next;
// Exclude a large number of walls immediately to optimize.
- if (y < surf->lowerY || y > surf->upperY) {
- continue;
- }
+ if (y < surf->lowerY || y > surf->upperY) { continue; }
+ if (surf->flags & SURFACE_FLAG_INTANGIBLE) { continue; }
if (gLevelValues.fixCollisionBugs && gLevelValues.fixCollisionBugsRoundedCorners && !gFindWallDirectionAirborne) {
// Check AABB to exclude walls before doing expensive triangle check
@@ -399,6 +398,8 @@ static struct Surface *find_ceil_from_list(struct SurfaceNode *surfaceNode, s32
surf = surfaceNode->surface;
surfaceNode = surfaceNode->next;
+ if (surf->flags & SURFACE_FLAG_INTANGIBLE) { continue; }
+
x1 = surf->vertex1[0];
z1 = surf->vertex1[2];
z2 = surf->vertex2[2];
@@ -622,7 +623,8 @@ static struct Surface *find_floor_from_list(struct SurfaceNode *surfaceNode, s32
if (surf == NULL) { break; }
surfaceNode = surfaceNode->next;
interpolate = gInterpolatingSurfaces;
-
+
+ if (surf->flags & SURFACE_FLAG_INTANGIBLE) { continue; }
if (gCheckingSurfaceCollisionsForObject != NULL) {
if (surf->object != gCheckingSurfaceCollisionsForObject) {
continue;
diff --git a/src/engine/surface_collision.h b/src/engine/surface_collision.h
index d954b847b..ff5257a57 100644
--- a/src/engine/surface_collision.h
+++ b/src/engine/surface_collision.h
@@ -38,6 +38,12 @@ struct FloorGeometry
f32 originOffset;
};
+struct StaticObjectCollision
+{
+ u32 index;
+ u16 length;
+};
+
extern Vec3f gFindWallDirection;
extern u8 gFindWallDirectionActive;
extern u8 gFindWallDirectionAirborne;
diff --git a/src/engine/surface_load.c b/src/engine/surface_load.c
index f74eb8110..e5296490e 100644
--- a/src/engine/surface_load.c
+++ b/src/engine/surface_load.c
@@ -28,12 +28,48 @@
SpatialPartitionCell gStaticSurfacePartition[NUM_CELLS][NUM_CELLS];
SpatialPartitionCell gDynamicSurfacePartition[NUM_CELLS][NUM_CELLS];
+/**
+ * The total number of surface nodes allocated (a node is allocated for each
+ * spatial partition cell that a surface intersects).
+ */
+s32 gSurfaceNodesAllocated;
+
+/**
+ * The total number of surfaces allocated.
+ */
+s32 gSurfacesAllocated;
+
+/**
+ * The number of nodes that have been created for static surfaces.
+ */
+s32 gNumStaticSurfaceNodes;
+
+/**
+ * The number of static surfaces in the pool.
+ */
+s32 gNumStaticSurfaces;
+
+/**
+ * The number of nodes that have been created for static object collision surfaces.
+ */
+s32 gNumSOCSurfaceNodes;
+
+/**
+ * The number of static object collision surfaces in the pool.
+ */
+s32 gNumSOCSurfaces;
+
/**
* Pools of data to contain either surface nodes or surfaces.
*/
static struct GrowingArray *sSurfaceNodePool = NULL;
static struct GrowingArray *sSurfacePool = NULL;
+/**
+ * Pool of data for static object collisions.
+ */
+static struct GrowingArray *sSOCPool = NULL;
+
/**
* Allocate the part of the surface node pool to contain a surface node.
*/
@@ -51,6 +87,10 @@ static struct Surface *alloc_surface(void) {
return growing_array_alloc(sSurfacePool, sizeof(struct Surface));
}
+static struct StaticObjectCollision *alloc_static_object_collision(void) {
+ return growing_array_alloc(sSOCPool, sizeof(struct StaticObjectCollision));
+}
+
/**
* Iterates through the entire partition, clearing the surfaces.
*/
@@ -71,6 +111,7 @@ static void clear_spatial_partition(SpatialPartitionCell *cells) {
*/
static void clear_static_surfaces(void) {
clear_spatial_partition(&gStaticSurfacePartition[0][0]);
+ sSOCPool = growing_array_init(sSOCPool, 0x100, malloc, smlua_free_soc);
}
/**
@@ -479,6 +520,8 @@ void alloc_surface_pools(void) {
gSurfacesAllocated = 0;
gNumStaticSurfaceNodes = 0;
gNumStaticSurfaces = 0;
+ gNumSOCSurfaceNodes = 0;
+ gNumSOCSurfaces = 0;
gCCMEnteredSlide = 0;
reset_red_coins_collected();
@@ -586,6 +629,8 @@ void load_area_terrain(s16 index, s16 *data, s8 *surfaceRooms, s16 *macroObjects
gNumStaticSurfaceNodes = gSurfaceNodesAllocated;
gNumStaticSurfaces = gSurfacesAllocated;
+ gNumSOCSurfaceNodes = 0;
+ gNumSOCSurfaces = 0;
}
/**
@@ -593,8 +638,8 @@ void load_area_terrain(s16 index, s16 *data, s8 *surfaceRooms, s16 *macroObjects
*/
void clear_dynamic_surfaces(void) {
if (!(gTimeStopState & TIME_STOP_ACTIVE)) {
- gSurfacesAllocated = gNumStaticSurfaces;
- gSurfaceNodesAllocated = gNumStaticSurfaceNodes;
+ gSurfacesAllocated = gNumStaticSurfaces + gNumSOCSurfaces;
+ gSurfaceNodesAllocated = gNumStaticSurfaceNodes + gNumSOCSurfaceNodes;
clear_spatial_partition(&gDynamicSurfacePartition[0][0]);
@@ -650,7 +695,7 @@ void transform_object_vertices(s16 **data, s16 *vertexData) {
/**
* Load in the surfaces for the gCurrentObject. This includes setting the flags, exertion, and room.
*/
-void load_object_surfaces(s16** data, s16* vertexData) {
+void load_object_surfaces(s16** data, s16* vertexData, bool isSOC) {
if (!gCurrentObject) { return; }
s32 surfaceType;
s32 i;
@@ -682,15 +727,16 @@ void load_object_surfaces(s16** data, s16* vertexData) {
struct Surface* surface = read_surface_data(vertexData, data);
if (surface != NULL) {
-
- // Set index of first surface
- if (gCurrentObject->firstSurface == 0) {
- gCurrentObject->firstSurface = gSurfacesAllocated - 1;
+ if (!isSOC) {
+ // Set index of first surface
+ if (gCurrentObject->firstSurface == 0) {
+ gCurrentObject->firstSurface = gSurfacesAllocated - 1;
+ }
+
+ // Increase surface count
+ gCurrentObject->numSurfaces++;
}
- // Increase surface count
- gCurrentObject->numSurfaces++;
-
surface->object = gCurrentObject;
surface->type = surfaceType;
@@ -702,7 +748,7 @@ void load_object_surfaces(s16** data, s16* vertexData) {
surface->flags |= flags;
surface->room = (s8)room;
- add_surface(surface, TRUE);
+ add_surface(surface, !isSOC);
}
if (hasForce) {
@@ -716,9 +762,12 @@ void load_object_surfaces(s16** data, s16* vertexData) {
/**
* Transform an object's vertices, reload them, and render the object.
*/
-void load_object_collision_model(void) {
+static void load_object_collision_model_internal(bool isSOC) {
+ static bool sIsLoadingCollision = false;
+
if (!gCurrentObject) { return; }
if (gCurrentObject->collisionData == NULL) { return; }
+ if (sIsLoadingCollision) { return; }
s32 numVertices = 64;
if (gCurrentObject->collisionData[0] == COL_INIT()) {
@@ -736,6 +785,9 @@ void load_object_collision_model(void) {
static s32 sVertexDataCount = 0;
static s16* sVertexData = NULL;
+ // start loading collision
+ sIsLoadingCollision = true;
+
// allocate vertex data
if (numVertices > sVertexDataCount || sVertexData == NULL) {
if (sVertexData) { free(sVertexData); }
@@ -746,39 +798,99 @@ void load_object_collision_model(void) {
}
s16* collisionData = gCurrentObject->collisionData;
- f32 tangibleDist = gCurrentObject->oCollisionDistance;
u8 anyPlayerInTangibleRange = FALSE;
- for (s32 i = 0; i < MAX_PLAYERS; i++) {
- f32 dist = dist_between_objects(gCurrentObject, gMarioStates[i].marioObj);
- if (dist < tangibleDist) { anyPlayerInTangibleRange = TRUE; }
+ if (!isSOC) {
+ f32 tangibleDist = gCurrentObject->oCollisionDistance;
+
+ for (s32 i = 0; i < MAX_PLAYERS; i++) {
+ f32 dist = dist_between_objects(gCurrentObject, gMarioStates[i].marioObj);
+ if (dist < tangibleDist) { anyPlayerInTangibleRange = TRUE; }
+ }
+
+ // If the object collision is supposed to be loaded more than the
+ // drawing distance of 4000, extend the drawing range.
+ if (gCurrentObject->oCollisionDistance > 4000.0f) {
+ gCurrentObject->oDrawingDistance = gCurrentObject->oCollisionDistance;
+ }
}
- // If the object collision is supposed to be loaded more than the
- // drawing distance of 4000, extend the drawing range.
- if (gCurrentObject->oCollisionDistance > 4000.0f) {
- gCurrentObject->oDrawingDistance = gCurrentObject->oCollisionDistance;
- }
-
- // Update if no Time Stop, in range, and in the current room.
- if (!(gTimeStopState & TIME_STOP_ACTIVE)
+ // Update if no Time Stop, in range, and in the current room. (or if static)
+ if (isSOC ||
+ (!(gTimeStopState & TIME_STOP_ACTIVE)
&& (anyPlayerInTangibleRange)
- && !(gCurrentObject->activeFlags & ACTIVE_FLAG_IN_DIFFERENT_ROOM)) {
+ && !(gCurrentObject->activeFlags & ACTIVE_FLAG_IN_DIFFERENT_ROOM))
+ ) {
collisionData++;
transform_object_vertices(&collisionData, sVertexData);
// TERRAIN_LOAD_CONTINUE acts as an "end" to the terrain data.
while (*collisionData != TERRAIN_LOAD_CONTINUE) {
- load_object_surfaces(&collisionData, sVertexData);
+ load_object_surfaces(&collisionData, sVertexData, isSOC);
}
}
- f32 marioDist = dist_between_objects(gCurrentObject, gMarioStates[0].marioObj);
- if (marioDist < gCurrentObject->oDrawingDistance * draw_distance_scalar()) {
- gCurrentObject->header.gfx.node.flags |= GRAPH_RENDER_ACTIVE;
- } else {
- gCurrentObject->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE;
+ if (!isSOC) {
+ f32 marioDist = dist_between_objects(gCurrentObject, gMarioStates[0].marioObj);
+ if (marioDist < gCurrentObject->oDrawingDistance * draw_distance_scalar()) {
+ gCurrentObject->header.gfx.node.flags |= GRAPH_RENDER_ACTIVE;
+ } else {
+ gCurrentObject->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE;
+ }
}
+
+ // stop loading collision
+ sIsLoadingCollision = false;
+}
+
+void load_object_collision_model(void) {
+ load_object_collision_model_internal(false);
+}
+
+struct StaticObjectCollision *load_static_object_collision() {
+ struct StaticObjectCollision *col;
+ u32 lastSurfaceIndex = gSurfacesAllocated;
+ u32 lastSurfaceNodeIndex = gSurfaceNodesAllocated;
+ u32 lastSOCSurfaceIndex = gNumStaticSurfaces + gNumSOCSurfaces;
+ u32 lastSOCSurfaceNodeIndex = gNumStaticSurfaceNodes + gNumSOCSurfaceNodes;
+
+ load_object_collision_model_internal(true);
+
+ // Reorder surfaces and nodes and update SOC variables
+ u32 addedSurfaces = gSurfacesAllocated - lastSurfaceIndex;
+ u32 addedSurfaceNodes = gSurfaceNodesAllocated - lastSurfaceNodeIndex;
+ if (addedSurfaces > 0) {
+ growing_array_move(sSurfacePool, lastSurfaceIndex, lastSOCSurfaceIndex, addedSurfaces);
+ gNumSOCSurfaces += addedSurfaces;
+ }
+ if (addedSurfaceNodes > 0) {
+ growing_array_move(sSurfaceNodePool, lastSurfaceNodeIndex, lastSOCSurfaceNodeIndex, addedSurfaceNodes);
+ gNumSOCSurfaceNodes += addedSurfaceNodes;
+ }
+
+ col = alloc_static_object_collision();
+ col->index = lastSOCSurfaceIndex;
+ col->length = addedSurfaces;
+
+ return col;
+}
+
+void toggle_static_object_collision(struct StaticObjectCollision *col, bool tangible) {
+ for (s32 i = 0; i < col->length; i++) {
+ struct Surface *surf = sSurfacePool->buffer[col->index + i];
+ if (tangible) {
+ surf->flags &= ~SURFACE_FLAG_INTANGIBLE;
+ } else {
+ surf->flags |= SURFACE_FLAG_INTANGIBLE;
+ }
+ }
+}
+
+struct Surface *get_static_object_surface(struct StaticObjectCollision *col, u32 index) {
+ if (!col) { return NULL; }
+ if (index >= col->length) { return NULL; }
+ struct Surface *surf = sSurfacePool->buffer[col->index + index];
+ return surf;
}
struct Surface *obj_get_surface_from_index(struct Object *o, u32 index) {
diff --git a/src/engine/surface_load.h b/src/engine/surface_load.h
index 262958423..d0f2db1ad 100644
--- a/src/engine/surface_load.h
+++ b/src/engine/surface_load.h
@@ -27,6 +27,13 @@ typedef struct SurfaceNode SpatialPartitionCell[3];
extern SpatialPartitionCell gStaticSurfacePartition[NUM_CELLS][NUM_CELLS];
extern SpatialPartitionCell gDynamicSurfacePartition[NUM_CELLS][NUM_CELLS];
+extern s32 gSurfaceNodesAllocated;
+extern s32 gSurfacesAllocated;
+extern s32 gNumStaticSurfaceNodes;
+extern s32 gNumStaticSurfaces;
+extern s32 gNumSOCSurfaceNodes;
+extern s32 gNumSOCSurfaces;
+
void alloc_surface_pools(void);
u32 get_area_terrain_size(s16 *data);
@@ -38,6 +45,15 @@ Loads the object's collision data into dynamic collision.
You must run this every frame in your object's behavior loop for it to have collision
|descriptionEnd| */
void load_object_collision_model(void);
+/* |description|
+Loads the object's collision data into static collision.
+You may run this only once to capture the object's collision at that frame.
+|descriptionEnd| */
+struct StaticObjectCollision *load_static_object_collision();
+/* |description|Toggles a collection of static object surfaces|descriptionEnd| */
+void toggle_static_object_collision(struct StaticObjectCollision *col, bool tangible);
+/* |description|Gets a surface corresponding to `index` from the static object collision|descriptionEnd| */
+struct Surface *get_static_object_surface(struct StaticObjectCollision *col, u32 index);
/* |description|Gets a surface corresponding to `index` from the surface pool buffer|descriptionEnd| */
struct Surface *obj_get_surface_from_index(struct Object *o, u32 index);
/* |description|Checks if a surface has force|descriptionEnd| */
diff --git a/src/game/memory.c b/src/game/memory.c
index 6018a6e33..feca24fd1 100644
--- a/src/game/memory.c
+++ b/src/game/memory.c
@@ -220,6 +220,32 @@ void *growing_array_alloc(struct GrowingArray *array, u32 size) {
return NULL;
}
+void growing_array_move(struct GrowingArray *array, u32 from, u32 to, u32 count) {
+ if (array && array->buffer && count > 0 &&
+ (to < from || to > from + count) &&
+ (from + count) <= array->count && to <= array->count) {
+
+ void **temp = malloc(sizeof(void *) * count);
+ if (!temp) { return; }
+
+ // Copy elements to move to temporary buffer
+ memcpy(temp, array->buffer + from, sizeof(void *) * count);
+
+ // Remove copied elements from the array
+ memmove(array->buffer + from, array->buffer + (from + count), sizeof(void *) * (array->count - (from + count)));
+
+ // Make place for the copied elements
+ // If moving left to right, account for the removed elements
+ if (to > from) { to -= count; }
+ memmove(array->buffer + (to + count), array->buffer + to, sizeof(void *) * (array->count - (to + count)));
+
+ // Insert copied elements
+ memcpy(array->buffer + to, temp, sizeof(void *) * count);
+
+ free(temp);
+ }
+}
+
void growing_array_free(struct GrowingArray **array) {
if (*array) {
for (u32 i = 0; i != (*array)->capacity; ++i) {
diff --git a/src/game/memory.h b/src/game/memory.h
index dba8f7535..d8f469814 100644
--- a/src/game/memory.h
+++ b/src/game/memory.h
@@ -77,6 +77,7 @@ void growing_pool_free_pool(struct GrowingPool *pool);
struct GrowingArray *growing_array_init(struct GrowingArray *array, u32 capacity, GrowingArrayAllocFunc alloc, GrowingArrayFreeFunc free);
void *growing_array_alloc(struct GrowingArray *array, u32 size);
+void growing_array_move(struct GrowingArray *array, u32 from, u32 to, u32 count);
void growing_array_free(struct GrowingArray **array);
void growing_array_debug_print(struct GrowingArray *array, const char *name, s32 x, s32 y);
diff --git a/src/game/object_list_processor.c b/src/game/object_list_processor.c
index 3e901bdc7..1752ae7b4 100644
--- a/src/game/object_list_processor.c
+++ b/src/game/object_list_processor.c
@@ -122,27 +122,6 @@ const BehaviorScript *gCurBhvCommand;
*/
s16 gPrevFrameObjectCount;
-/**
- * The total number of surface nodes allocated (a node is allocated for each
- * spatial partition cell that a surface intersects).
- */
-s32 gSurfaceNodesAllocated;
-
-/**
- * The total number of surfaces allocated.
- */
-s32 gSurfacesAllocated;
-
-/**
- * The number of nodes that have been created for surfaces.
- */
-s32 gNumStaticSurfaceNodes;
-
-/**
- * The number of surfaces in the pool.
- */
-s32 gNumStaticSurfaces;
-
struct Object* gCheckingSurfaceCollisionsForObject = NULL;
s16 gCheckingSurfaceCollisionsForCamera;
s16 gFindFloorIncludeSurfaceIntangible;
diff --git a/src/game/object_list_processor.h b/src/game/object_list_processor.h
index 24a1acf27..98cb25a95 100644
--- a/src/game/object_list_processor.h
+++ b/src/game/object_list_processor.h
@@ -91,11 +91,6 @@ extern struct Object *gCurrentObject;
extern const BehaviorScript *gCurBhvCommand;
extern s16 gPrevFrameObjectCount;
-extern s32 gSurfaceNodesAllocated;
-extern s32 gSurfacesAllocated;
-extern s32 gNumStaticSurfaceNodes;
-extern s32 gNumStaticSurfaces;
-
extern struct Object* gCheckingSurfaceCollisionsForObject;
extern s16 gCheckingSurfaceCollisionsForCamera;
extern s16 gFindFloorIncludeSurfaceIntangible;
diff --git a/src/pc/lua/smlua_cobject.c b/src/pc/lua/smlua_cobject.c
index 9b8d0b401..bc8ca1cca 100644
--- a/src/pc/lua/smlua_cobject.c
+++ b/src/pc/lua/smlua_cobject.c
@@ -650,6 +650,12 @@ int smlua__eq(lua_State *L) {
return 1;
}
+int smlua__bnot(lua_State *L) {
+ const CObject *a = lua_touserdata(L, 1);
+ lua_pushboolean(L, !a || a->freed);
+ return 1;
+}
+
static int smlua_cpointer_get(lua_State* L) {
const CPointer *cptr = lua_touserdata(L, 1);
const char *key = lua_tostring(L, 2);
@@ -690,6 +696,7 @@ void smlua_cobject_init_globals(void) {
{ "__index", smlua__get_field },
{ "__newindex", smlua__set_field },
{ "__eq", smlua__eq },
+ { "__bnot", smlua__bnot },
{ "__metatable", NULL },
{ NULL, NULL }
};
@@ -700,6 +707,7 @@ void smlua_cobject_init_globals(void) {
{ "__index", smlua_cpointer_get },
{ "__newindex", smlua_cpointer_set },
{ "__eq", smlua__eq },
+ { "__bnot", smlua__bnot },
{ "__metatable", NULL },
{ NULL, NULL }
};
diff --git a/src/pc/lua/smlua_cobject_autogen.c b/src/pc/lua/smlua_cobject_autogen.c
index 2bd02f925..ca833cc68 100644
--- a/src/pc/lua/smlua_cobject_autogen.c
+++ b/src/pc/lua/smlua_cobject_autogen.c
@@ -2742,6 +2742,12 @@ static struct LuaObjectField sStarsNeededForDialogFields[LUA_STARS_NEEDED_FOR_DI
{ "dialog6", LVT_U16, offsetof(struct StarsNeededForDialog, dialog6), false, LOT_NONE, 1, sizeof(u16) },
};
+#define LUA_STATIC_OBJECT_COLLISION_FIELD_COUNT 2
+static struct LuaObjectField sStaticObjectCollisionFields[LUA_STATIC_OBJECT_COLLISION_FIELD_COUNT] = {
+ { "index", LVT_U32, offsetof(struct StaticObjectCollision, index), true, LOT_NONE, 1, sizeof(u32) },
+ { "length", LVT_U16, offsetof(struct StaticObjectCollision, length), true, LOT_NONE, 1, sizeof(u16) },
+};
+
#define LUA_SURFACE_FIELD_COUNT 16
static struct LuaObjectField sSurfaceFields[LUA_SURFACE_FIELD_COUNT] = {
{ "flags", LVT_S8, offsetof(struct Surface, flags), false, LOT_NONE, 1, sizeof(s8) },
@@ -2980,6 +2986,7 @@ struct LuaObjectTable sLuaObjectAutogenTable[LOT_AUTOGEN_MAX - LOT_AUTOGEN_MIN]
{ LOT_SPAWNPARTICLESINFO, sSpawnParticlesInfoFields, LUA_SPAWN_PARTICLES_INFO_FIELD_COUNT },
{ LOT_STARPOSITIONS, sStarPositionsFields, LUA_STAR_POSITIONS_FIELD_COUNT },
{ LOT_STARSNEEDEDFORDIALOG, sStarsNeededForDialogFields, LUA_STARS_NEEDED_FOR_DIALOG_FIELD_COUNT },
+ { LOT_STATICOBJECTCOLLISION, sStaticObjectCollisionFields, LUA_STATIC_OBJECT_COLLISION_FIELD_COUNT },
{ LOT_SURFACE, sSurfaceFields, LUA_SURFACE_FIELD_COUNT },
{ LOT_TEXTUREINFO, sTextureInfoFields, LUA_TEXTURE_INFO_FIELD_COUNT },
{ LOT_TRANSITIONINFO, sTransitionInfoFields, LUA_TRANSITION_INFO_FIELD_COUNT },
@@ -3110,6 +3117,7 @@ const char *sLuaLotNames[] = {
[LOT_SPAWNPARTICLESINFO] = "SpawnParticlesInfo",
[LOT_STARPOSITIONS] = "StarPositions",
[LOT_STARSNEEDEDFORDIALOG] = "StarsNeededForDialog",
+ [LOT_STATICOBJECTCOLLISION] = "StaticObjectCollision",
[LOT_SURFACE] = "Surface",
[LOT_TEXTUREINFO] = "TextureInfo",
[LOT_TRANSITIONINFO] = "TransitionInfo",
diff --git a/src/pc/lua/smlua_cobject_autogen.h b/src/pc/lua/smlua_cobject_autogen.h
index b5ec35d19..a79378a98 100644
--- a/src/pc/lua/smlua_cobject_autogen.h
+++ b/src/pc/lua/smlua_cobject_autogen.h
@@ -123,6 +123,7 @@ enum LuaObjectAutogenType {
LOT_SPAWNPARTICLESINFO,
LOT_STARPOSITIONS,
LOT_STARSNEEDEDFORDIALOG,
+ LOT_STATICOBJECTCOLLISION,
LOT_SURFACE,
LOT_TEXTUREINFO,
LOT_TRANSITIONINFO,
diff --git a/src/pc/lua/smlua_constants_autogen.c b/src/pc/lua/smlua_constants_autogen.c
index 6966f02fe..e916e2a1b 100644
--- a/src/pc/lua/smlua_constants_autogen.c
+++ b/src/pc/lua/smlua_constants_autogen.c
@@ -4543,6 +4543,7 @@ char gSmluaConstants[] = ""
"SURFACE_CLASS_NOT_SLIPPERY=0x0015\n"
"SURFACE_FLAG_DYNAMIC=(1 << 0)\n"
"SURFACE_FLAG_NO_CAM_COLLISION=(1 << 1)\n"
+"SURFACE_FLAG_INTANGIBLE=(1 << 2)\n"
"SURFACE_FLAG_X_PROJECTION=(1 << 3)\n"
"HAZARD_TYPE_LAVA_FLOOR=1\n"
"HAZARD_TYPE_LAVA_WALL=2\n"
diff --git a/src/pc/lua/smlua_functions_autogen.c b/src/pc/lua/smlua_functions_autogen.c
index c28245176..50125e3ef 100644
--- a/src/pc/lua/smlua_functions_autogen.c
+++ b/src/pc/lua/smlua_functions_autogen.c
@@ -36485,6 +36485,59 @@ int smlua_func_load_object_collision_model(UNUSED lua_State* L) {
return 1;
}
+int smlua_func_load_static_object_collision(UNUSED lua_State* L) {
+ if (L == NULL) { return 0; }
+
+ int top = lua_gettop(L);
+ if (top != 0) {
+ LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "load_static_object_collision", 0, top);
+ return 0;
+ }
+
+
+ smlua_push_object(L, LOT_STATICOBJECTCOLLISION, load_static_object_collision(), NULL);
+
+ return 1;
+}
+
+int smlua_func_toggle_static_object_collision(lua_State* L) {
+ if (L == NULL) { return 0; }
+
+ int top = lua_gettop(L);
+ if (top != 2) {
+ LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "toggle_static_object_collision", 2, top);
+ return 0;
+ }
+
+ struct StaticObjectCollision* col = (struct StaticObjectCollision*)smlua_to_cobject(L, 1, LOT_STATICOBJECTCOLLISION);
+ if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 1, "toggle_static_object_collision"); return 0; }
+ bool tangible = smlua_to_boolean(L, 2);
+ if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 2, "toggle_static_object_collision"); return 0; }
+
+ toggle_static_object_collision(col, tangible);
+
+ return 1;
+}
+
+int smlua_func_get_static_object_surface(lua_State* L) {
+ if (L == NULL) { return 0; }
+
+ int top = lua_gettop(L);
+ if (top != 2) {
+ LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "get_static_object_surface", 2, top);
+ return 0;
+ }
+
+ struct StaticObjectCollision* col = (struct StaticObjectCollision*)smlua_to_cobject(L, 1, LOT_STATICOBJECTCOLLISION);
+ if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 1, "get_static_object_surface"); return 0; }
+ u32 index = smlua_to_integer(L, 2);
+ if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 2, "get_static_object_surface"); return 0; }
+
+ smlua_push_object(L, LOT_SURFACE, get_static_object_surface(col, index), NULL);
+
+ return 1;
+}
+
int smlua_func_obj_get_surface_from_index(lua_State* L) {
if (L == NULL) { return 0; }
@@ -38657,6 +38710,9 @@ void smlua_bind_functions_autogen(void) {
// surface_load.h
smlua_bind_function(L, "load_object_collision_model", smlua_func_load_object_collision_model);
+ smlua_bind_function(L, "load_static_object_collision", smlua_func_load_static_object_collision);
+ smlua_bind_function(L, "toggle_static_object_collision", smlua_func_toggle_static_object_collision);
+ smlua_bind_function(L, "get_static_object_surface", smlua_func_get_static_object_surface);
smlua_bind_function(L, "obj_get_surface_from_index", smlua_func_obj_get_surface_from_index);
smlua_bind_function(L, "surface_has_force", smlua_func_surface_has_force);
diff --git a/src/pc/lua/smlua_utils.h b/src/pc/lua/smlua_utils.h
index 25a76c81b..a855b0a02 100644
--- a/src/pc/lua/smlua_utils.h
+++ b/src/pc/lua/smlua_utils.h
@@ -69,5 +69,6 @@ void smlua_free(void *ptr, u16 lot);
static inline void smlua_free_##name(void *ptr) { smlua_free(ptr, lot); }
smlua_free_lot(surface, LOT_SURFACE);
+smlua_free_lot(soc, LOT_STATICOBJECTCOLLISION);
#endif
\ No newline at end of file