diff --git a/autogen/convert_structs.py b/autogen/convert_structs.py
index 3a88390ec..657f23d74 100644
--- a/autogen/convert_structs.py
+++ b/autogen/convert_structs.py
@@ -35,6 +35,7 @@ in_files = [
"src/game/player_palette.h",
"src/engine/graph_node.h",
"include/PR/gbi.h",
+ "src/game/object_list_processor.h",
]
out_filename_c = 'src/pc/lua/smlua_cobject_autogen.c'
diff --git a/autogen/lua_definitions/constants.lua b/autogen/lua_definitions/constants.lua
index 8e160c5d4..adde37875 100644
--- a/autogen/lua_definitions/constants.lua
+++ b/autogen/lua_definitions/constants.lua
@@ -6668,7 +6668,10 @@ TIME_STOP_MARIO_OPENED_DOOR = (1 << 5)
TIME_STOP_ACTIVE = (1 << 6)
--- @type integer
-OBJECT_POOL_CAPACITY = 1200
+OBJECT_POOL_CAPACITY = 0xFFFFFFFF
+
+--- @type integer
+OBJECT_POOL_NODE_CAPACITY = 256
OBJ_LIST_PLAYER = 0 --- @type ObjectList
OBJ_LIST_EXT = 1 --- @type ObjectList
diff --git a/autogen/lua_definitions/functions.lua b/autogen/lua_definitions/functions.lua
index 746a8f4c4..0778f6e73 100644
--- a/autogen/lua_definitions/functions.lua
+++ b/autogen/lua_definitions/functions.lua
@@ -12124,6 +12124,12 @@ function obj_anim_skip_interpolation(o)
-- ...
end
+--- @return integer
+--- Gets the amount of objects in this area
+function obj_get_count()
+ -- ...
+end
+
--- Resets every modified dialog back to vanilla
function smlua_text_utils_reset_all()
-- ...
diff --git a/autogen/lua_definitions/structs.lua b/autogen/lua_definitions/structs.lua
index 5441e7424..5728a924e 100644
--- a/autogen/lua_definitions/structs.lua
+++ b/autogen/lua_definitions/structs.lua
@@ -1282,6 +1282,11 @@
--- @field public overridePaletteIndex integer
--- @field public overridePaletteIndexLp integer
+--- @class NumTimesCalled
+--- @field public floor integer
+--- @field public ceil integer
+--- @field public wall integer
+
--- @class Object
--- @field public header ObjectNode
--- @field public prevObj Object
@@ -2062,6 +2067,12 @@
--- @field public gfx GraphNodeObject
--- @field public next ObjectNode
--- @field public prev ObjectNode
+--- @field public pool ObjectPoolNode
+
+--- @class ObjectPoolNode
+--- @field public pool Object[]
+--- @field public freeList ObjectNode
+--- @field public next ObjectPoolNode
--- @class ObjectWarpNode
--- @field public node WarpNode
diff --git a/docs/lua/constants.md b/docs/lua/constants.md
index 0c92edac9..d10c012f7 100644
--- a/docs/lua/constants.md
+++ b/docs/lua/constants.md
@@ -2907,6 +2907,7 @@
- TIME_STOP_MARIO_OPENED_DOOR
- TIME_STOP_ACTIVE
- OBJECT_POOL_CAPACITY
+- OBJECT_POOL_NODE_CAPACITY
### [enum ObjectList](#ObjectList)
| Identifier | Value |
diff --git a/docs/lua/functions-7.md b/docs/lua/functions-7.md
index 207edbb6f..d85dadc6f 100644
--- a/docs/lua/functions-7.md
+++ b/docs/lua/functions-7.md
@@ -3622,6 +3622,27 @@ Skips animation interpolation for a frame
+## [obj_get_count](#obj_get_count)
+
+### Description
+Gets the amount of objects in this area
+
+### Lua Example
+`local integerValue = obj_get_count()`
+
+### Parameters
+- None
+
+### Returns
+- `integer`
+
+### C Prototype
+`u32 obj_get_count(void);`
+
+[:arrow_up_small:](#)
+
+
+
---
# functions from smlua_text_utils.h
diff --git a/docs/lua/functions.md b/docs/lua/functions.md
index 4076375df..5357a0d72 100644
--- a/docs/lua/functions.md
+++ b/docs/lua/functions.md
@@ -2143,6 +2143,7 @@
- [set_whirlpools](functions-7.md#set_whirlpools)
- [obj_skip_interpolation](functions-7.md#obj_skip_interpolation)
- [obj_anim_skip_interpolation](functions-7.md#obj_anim_skip_interpolation)
+ - [obj_get_count](functions-7.md#obj_get_count)
diff --git a/docs/lua/structs.md b/docs/lua/structs.md
index 79c6d19ff..4e229277e 100644
--- a/docs/lua/structs.md
+++ b/docs/lua/structs.md
@@ -66,9 +66,11 @@
- [ModFsFile](#ModFsFile)
- [NametagsSettings](#NametagsSettings)
- [NetworkPlayer](#NetworkPlayer)
+- [NumTimesCalled](#NumTimesCalled)
- [Object](#Object)
- [ObjectHitbox](#ObjectHitbox)
- [ObjectNode](#ObjectNode)
+- [ObjectPoolNode](#ObjectPoolNode)
- [ObjectWarpNode](#ObjectWarpNode)
- [Painting](#Painting)
- [PaintingValues](#PaintingValues)
@@ -1869,6 +1871,18 @@
+## [NumTimesCalled](#NumTimesCalled)
+
+| Field | Type | Access |
+| ----- | ---- | ------ |
+| floor | `integer` | |
+| ceil | `integer` | |
+| wall | `integer` | |
+
+[:arrow_up_small:](#)
+
+
+
## [Object](#Object)
| Field | Type | Access |
@@ -2674,6 +2688,19 @@
| gfx | [GraphNodeObject](structs.md#GraphNodeObject) | read-only |
| next | [ObjectNode](structs.md#ObjectNode) | read-only |
| prev | [ObjectNode](structs.md#ObjectNode) | read-only |
+| pool | [ObjectPoolNode](structs.md#ObjectPoolNode) | |
+
+[:arrow_up_small:](#)
+
+
+
+## [ObjectPoolNode](#ObjectPoolNode)
+
+| Field | Type | Access |
+| ----- | ---- | ------ |
+| pool | `Array` <`Object`> | read-only |
+| freeList | [ObjectNode](structs.md#ObjectNode) | read-only |
+| next | [ObjectPoolNode](structs.md#ObjectPoolNode) | |
[:arrow_up_small:](#)
diff --git a/include/types.h b/include/types.h
index ea64ff19b..6335f6b57 100644
--- a/include/types.h
+++ b/include/types.h
@@ -232,6 +232,7 @@ struct ObjectNode
struct GraphNodeObject gfx;
struct ObjectNode *next;
struct ObjectNode *prev;
+ struct ObjectPoolNode* pool;
};
// NOTE: Since ObjectNode is the first member of Object, it is difficult to determine
diff --git a/src/engine/surface_load.c b/src/engine/surface_load.c
index 345a73202..58d0b573f 100644
--- a/src/engine/surface_load.c
+++ b/src/engine/surface_load.c
@@ -644,11 +644,11 @@ void clear_dynamic_surfaces(void) {
clear_spatial_partition(&gDynamicSurfacePartition[0][0]);
- for (u16 i = 0; i < OBJECT_POOL_CAPACITY; i++) {
- struct Object *obj = &gObjectPool[i];
+ traverse_object_pools(
+ struct Object *obj = &node->pool[i];
obj->firstSurface = 0;
obj->numSurfaces = 0;
- }
+ )
}
}
diff --git a/src/game/behaviors/bubble.inc.c b/src/game/behaviors/bubble.inc.c
index 626aefd0a..550859d8f 100644
--- a/src/game/behaviors/bubble.inc.c
+++ b/src/game/behaviors/bubble.inc.c
@@ -12,14 +12,16 @@ void bhv_object_bubble_loop(void) {
f32 bubbleY = o->oPosY;
if (bubbleY > waterY) {
- if (gFreeObjectList.next) {
- bubbleSplash = spawn_object_at_origin(o, 0, MODEL_SMALL_WATER_SPLASH, bhvBubbleSplash);
- if (bubbleSplash != NULL) {
- bubbleSplash->oPosX = o->oPosX;
- bubbleSplash->oPosY = bubbleY + 5.0f;
- bubbleSplash->oPosZ = o->oPosZ;
+ traverse_object_pools(
+ if (node != NULL && node->freeList.next != NULL) {
+ bubbleSplash = spawn_object_at_origin(o, 0, MODEL_SMALL_WATER_SPLASH, bhvBubbleSplash);
+ if (bubbleSplash != NULL) {
+ bubbleSplash->oPosX = o->oPosX;
+ bubbleSplash->oPosY = bubbleY + 5.0f;
+ bubbleSplash->oPosZ = o->oPosZ;
+ }
}
- }
+ )
o->activeFlags = ACTIVE_FLAG_DEACTIVATED;
}
diff --git a/src/game/behaviors/tree_particles.inc.c b/src/game/behaviors/tree_particles.inc.c
index aecdb1a45..3628e2c0a 100644
--- a/src/game/behaviors/tree_particles.inc.c
+++ b/src/game/behaviors/tree_particles.inc.c
@@ -16,8 +16,8 @@ void bhv_tree_snow_or_leaf_loop(void) {
obj_mark_for_deletion(o);
if (o->oTimer > 100)
obj_mark_for_deletion(o);
- if (gPrevFrameObjectCount > (OBJECT_POOL_CAPACITY * 212 / 240))
- obj_mark_for_deletion(o);
+ //if (gPrevFrameObjectCount > (OBJECT_POOL_CAPACITY * 212 / 240))
+ // obj_mark_for_deletion(o);
o->oFaceAnglePitch += o->oAngleVelPitch;
o->oFaceAngleRoll += o->oAngleVelRoll;
o->oVelY += -3.0f;
diff --git a/src/game/behaviors/water_objs.inc.c b/src/game/behaviors/water_objs.inc.c
index e829ebf20..336919845 100644
--- a/src/game/behaviors/water_objs.inc.c
+++ b/src/game/behaviors/water_objs.inc.c
@@ -63,8 +63,11 @@ void bhv_small_water_wave_loop(void) {
if (o->oPosY > sp1C) {
o->activeFlags = ACTIVE_FLAG_DEACTIVATED;
o->oPosY += 5.0f;
- if (gFreeObjectList.next != NULL)
- spawn_object(o, MODEL_SMALL_WATER_SPLASH, bhvObjectWaterSplash);
+ traverse_object_pools(
+ if (node != NULL && node->freeList.next != NULL) {
+ spawn_object(o, MODEL_SMALL_WATER_SPLASH, bhvObjectWaterSplash);
+ }
+ )
}
if (o->oInteractStatus & INT_STATUS_INTERACTED)
obj_mark_for_deletion(o);
diff --git a/src/game/object_helpers.c b/src/game/object_helpers.c
index f0bf4d162..364657d76 100644
--- a/src/game/object_helpers.c
+++ b/src/game/object_helpers.c
@@ -716,15 +716,17 @@ struct Object *try_to_spawn_object(s16 offsetY, f32 scale, struct Object *parent
const BehaviorScript *behavior) {
struct Object *obj;
- if (gFreeObjectList.next != NULL) {
+ traverse_object_pools(
+ if (node == NULL || node->freeList.next == NULL) { return NULL; }
+
obj = spawn_object(parent, model, behavior);
if (obj == NULL) { return NULL; }
obj->oPosY += offsetY;
obj_scale(obj, scale);
return obj;
- } else {
- return NULL;
- }
+ )
+
+ return NULL;
}
struct Object *spawn_object_with_scale(struct Object *parent, s32 model, const BehaviorScript *behavior, f32 scale) {
@@ -2547,16 +2549,16 @@ void cur_obj_spawn_particles(struct SpawnParticlesInfo *info) {
s32 numParticles = info->count;
// If there are a lot of objects already, limit the number of particles
- if (gPrevFrameObjectCount > (OBJECT_POOL_CAPACITY * 150 / 240) && numParticles > 10) {
+ /*if (gPrevFrameObjectCount > (OBJECT_POOL_CAPACITY * 150 / 240) && numParticles > 10) {
numParticles = 10;
- }
+ }*/
// We're close to running out of object slots, so don't spawn particles at
// all
- if (gPrevFrameObjectCount > (OBJECT_POOL_CAPACITY * 210 / 240)) {
+ /*if (gPrevFrameObjectCount > (OBJECT_POOL_CAPACITY * 210 / 240)) {
numParticles = 0;
- }
+ }*/
for (i = 0; i < numParticles; i++) {
scale = random_float() * (info->sizeRange * 0.1f) + info->sizeBase * 0.1f;
diff --git a/src/game/object_list_processor.c b/src/game/object_list_processor.c
index 1752ae7b4..de095c329 100644
--- a/src/game/object_list_processor.c
+++ b/src/game/object_list_processor.c
@@ -71,7 +71,7 @@ u32 gTimeStopState;
/**
* The pool that objects are allocated from.
*/
-struct Object gObjectPool[OBJECT_POOL_CAPACITY];
+struct ObjectPoolNode gObjectPool = { 0 };
/**
* A special object whose purpose is to act as a parent for macro objects.
@@ -87,8 +87,9 @@ struct ObjectNode *gObjectLists;
/**
* A singly linked list of available slots in the object pool.
+ * Now stored in gObjectPool.
*/
-struct ObjectNode gFreeObjectList;
+//struct ObjectNode gFreeObjectList;
/**
* The object representing Mario.
@@ -568,11 +569,20 @@ void spawn_objects_from_info(UNUSED s32 unused, struct SpawnInfo *spawnInfo) {
}
}
+void free_pool_nodes(struct ObjectPoolNode* node) {
+ if (node->next == NULL) {
+ free(node);
+ return;
+ }
+
+ free_pool_nodes(node->next);
+ free(node);
+}
+
/**
* Clear objects, dynamic surfaces, and some miscellaneous level data used by objects.
*/
void clear_objects(void) {
- s32 i;
sync_objects_clear();
gTHIWaterDrained = 0;
gTimeStopState = 0;
@@ -586,19 +596,24 @@ void clear_objects(void) {
gMarioStates[i].currentRoom = 0;
}
- for (i = 0; i < 60; i++) {
+ for (s32 i = 0; i < 60; i++) {
gDoorAdjacentRooms[i][0] = 0;
gDoorAdjacentRooms[i][1] = 0;
}
debug_unknown_level_select_check();
- init_free_object_list();
+ if (gObjectPool.next != NULL) {
+ free_pool_nodes(gObjectPool.next);
+ gObjectPool.next = NULL;
+ }
+
+ init_free_object_list(&gObjectPool);
clear_object_lists(gObjectListArray);
- for (i = 0; i < OBJECT_POOL_CAPACITY; i++) {
- gObjectPool[i].activeFlags = ACTIVE_FLAG_DEACTIVATED;
- geo_reset_object_node(&gObjectPool[i].header.gfx);
+ for (u32 i = 0; i < OBJECT_POOL_NODE_CAPACITY; i++) {
+ gObjectPool.pool[i].activeFlags = ACTIVE_FLAG_DEACTIVATED;
+ geo_reset_object_node(&gObjectPool.pool[i].header.gfx);
}
gObjectLists = gObjectListArray;
@@ -607,13 +622,26 @@ void clear_objects(void) {
geo_clear_interp_data();
}
+void reinit_objects(struct ObjectPoolNode* node) {
+ init_free_object_list(node);
+
+ for (u32 i = 0; i < OBJECT_POOL_NODE_CAPACITY; i++) {
+ node->pool[i].activeFlags = ACTIVE_FLAG_DEACTIVATED;
+ geo_reset_object_node(&node->pool[i].header.gfx);
+ }
+
+ clear_dynamic_surfaces();
+ geo_clear_interp_data();
+}
+
/**
* Update spawner and surface objects.
*/
void update_terrain_objects(void) {
gObjectCounter = update_objects_in_list(&gObjectLists[OBJ_LIST_SPAWNER]);
//! This was meant to be +=
- gObjectCounter = update_objects_in_list(&gObjectLists[OBJ_LIST_SURFACE]);
+ // Since this counter does not affect gameplay at all, fix the bug
+ gObjectCounter += update_objects_in_list(&gObjectLists[OBJ_LIST_SURFACE]);
}
/**
diff --git a/src/game/object_list_processor.h b/src/game/object_list_processor.h
index 98cb25a95..07f33ee23 100644
--- a/src/game/object_list_processor.h
+++ b/src/game/object_list_processor.h
@@ -22,8 +22,15 @@
/**
* The maximum number of objects that can be loaded at once.
+ * Kept for compatibility, but is unused.
*/
-#define OBJECT_POOL_CAPACITY 1200
+#define OBJECT_POOL_CAPACITY 0xFFFFFFFF
+/**
+ * The maximum number of objects that can be in a single pool node.
+ * A higher number might be better for less fragmentation.
+ * ! Can't seem to go above 256 without crashing in geo_reset_object_node()...
+ */
+#define OBJECT_POOL_NODE_CAPACITY 256
/**
* Every object is categorized into an object list, which controls the order
@@ -58,6 +65,18 @@ enum ObjectList
NUM_OBJ_LISTS
};
+struct ObjectPoolNode {
+ struct Object pool[OBJECT_POOL_NODE_CAPACITY];
+ struct ObjectNode freeList;
+ struct ObjectPoolNode* next;
+};
+
+#define traverse_object_pools(statements) \
+for (struct ObjectPoolNode* node = &gObjectPool; node != NULL; node = node->next) { \
+ for (u32 i = 0; i < OBJECT_POOL_NODE_CAPACITY; i++) { \
+ statements \
+ } \
+} \
extern struct ObjectNode gObjectListArray[];
@@ -79,10 +98,10 @@ extern s16 gDebugInfo[][8];
extern s16 gDebugInfoOverwrite[][8];
extern u32 gTimeStopState;
-extern struct Object gObjectPool[];
+extern struct ObjectPoolNode gObjectPool;
extern struct Object gMacroObjectDefaultParent;
extern struct ObjectNode *gObjectLists;
-extern struct ObjectNode gFreeObjectList;
+//extern struct ObjectNode gFreeObjectList;
extern struct Object *gMarioObject;
extern struct Object *gMarioObjects[];
@@ -118,6 +137,7 @@ void set_object_respawn_info_bits(struct Object *obj, u8 bits);
void unload_objects_from_area(UNUSED s32 unused, s32 areaIndex);
void spawn_objects_from_info(UNUSED s32 unused, struct SpawnInfo *spawnInfo);
void clear_objects(void);
+void reinit_objects(struct ObjectPoolNode* node);
void update_objects(UNUSED s32 unused);
diff --git a/src/game/spawn_object.c b/src/game/spawn_object.c
index 9972d5535..99e43b2e8 100644
--- a/src/game/spawn_object.c
+++ b/src/game/spawn_object.c
@@ -81,17 +81,22 @@ struct LinkedList *unused_try_allocate(struct LinkedList *destList,
* to the end of destList (doubly linked). Return the object, or NULL if
* freeList is empty.
*/
-struct Object *try_allocate_object(struct ObjectNode *destList, struct ObjectNode *freeList) {
+struct Object* try_allocate_object(struct ObjectNode* destList, struct ObjectPoolNode* node) {
struct ObjectNode *nextObj = NULL;
- if (destList == NULL || freeList == NULL) {
- fprintf(stderr, "FATAL ERROR: Failed to try and allocate a object because either the destList %p or freeList %p was NULL!\n", destList, freeList);
+ if (node == NULL) {
+ fprintf(stderr, "FATAL ERROR: Failed to try and allocate an object because the pool %p was NULL!\n", node);
return NULL;
}
- if ((nextObj = freeList->next) != NULL) {
+ if (destList == NULL) {
+ fprintf(stderr, "FATAL ERROR: Failed to try and allocate an object because the destList %p was NULL!\n", destList);
+ return NULL;
+ }
+
+ if ((nextObj = node->freeList.next) != NULL) {
// Remove from free list
- freeList->next = nextObj->next;
+ node->freeList.next = nextObj->next;
// Insert at end of destination list
nextObj->prev = destList->prev;
@@ -103,7 +108,13 @@ struct Object *try_allocate_object(struct ObjectNode *destList, struct ObjectNod
}
destList->prev = nextObj;
} else {
- return NULL;
+ if (node->next != NULL) {
+ return try_allocate_object(destList, node->next);
+ }
+
+ node->next = (struct ObjectPoolNode*)calloc(1, sizeof(struct ObjectPoolNode));
+ reinit_objects(node->next);
+ return try_allocate_object(destList, node->next);
}
geo_remove_child(&nextObj->gfx.node);
@@ -138,35 +149,40 @@ void unused_deallocate(struct LinkedList *freeList, struct LinkedList *node) {
* Remove the given object from the object list that it's currently in, and
* insert it at the beginning of the free list (singly linked).
*/
-static void deallocate_object(struct ObjectNode *freeList, struct ObjectNode *obj) {
- if (!obj || !freeList) { return; }
+static void deallocate_object(struct ObjectNode *obj) {
+ if (obj == NULL) { return; }
// Remove from object list
if (obj->next) { obj->next->prev = obj->prev; }
if (obj->prev) { obj->prev->next = obj->next; }
// Insert at beginning of free list
- obj->next = freeList->next;
- freeList->next = obj;
+ for (struct ObjectPoolNode* node = &gObjectPool; node != NULL; node = node->next) {
+ if (node->freeList.next == NULL) { continue; }
+ obj->next = node->freeList.next;
+ node->freeList.next = obj;
+ }
}
/**
* Add every object in the pool to the free object list.
*/
-void init_free_object_list(void) {
- s32 poolLength = OBJECT_POOL_CAPACITY;
+void init_free_object_list(struct ObjectPoolNode* node) {
+ if (node == NULL) { return; }
// Add the first object in the pool to the free list
- struct Object *obj = &gObjectPool[0];
- gFreeObjectList.next = (struct ObjectNode *) obj;
+ struct Object *obj = &node->pool[0];
+ node->freeList.next = (struct ObjectNode*)obj;
// Link each object in the pool to the following object
- for (s32 i = 0; i < poolLength - 1; i++) {
+ for (s32 i = 0; i < OBJECT_POOL_NODE_CAPACITY - 1; i++) {
obj->header.next = &(obj + 1)->header;
obj++;
}
// End the list
obj->header.next = NULL;
+ // Attach pool
+ obj->header.pool = node;
}
/**
@@ -247,7 +263,7 @@ void unload_object(struct Object *obj) {
smlua_call_event_hooks(HOOK_ON_OBJECT_UNLOAD, obj);
- deallocate_object(&gFreeObjectList, &obj->header);
+ deallocate_object(&obj->header);
}
/**
@@ -257,7 +273,7 @@ void unload_object(struct Object *obj) {
*/
struct Object *allocate_object(struct ObjectNode *objList) {
if (!objList) { return NULL; }
- struct Object *obj = try_allocate_object(objList, &gFreeObjectList);
+ struct Object *obj = try_allocate_object(objList, &gObjectPool);
// The object list is full if the newly created pointer is NULL.
// If this happens, we first attempt to unload unimportant objects
@@ -273,7 +289,7 @@ struct Object *allocate_object(struct ObjectNode *objList) {
} else {
// If an unimportant object does exist, unload it and take its slot.
unload_object(unimportantObj);
- obj = try_allocate_object(objList, &gFreeObjectList);
+ obj = try_allocate_object(objList, &gObjectPool);
if (gCurrentObject == obj) {
//! Uh oh, the unimportant object was in the middle of
// updating! This could cause some interesting logic errors,
diff --git a/src/game/spawn_object.h b/src/game/spawn_object.h
index acb9cd038..145609332 100644
--- a/src/game/spawn_object.h
+++ b/src/game/spawn_object.h
@@ -3,7 +3,7 @@
#include "types.h"
-void init_free_object_list(void);
+void init_free_object_list(struct ObjectPoolNode* node);
void clear_object_lists(struct ObjectNode *objLists);
void unload_object(struct Object *obj);
struct Object *create_object(const BehaviorScript *bhvScript);
diff --git a/src/pc/lua/smlua_cobject_autogen.c b/src/pc/lua/smlua_cobject_autogen.c
index 35a3ce52d..fa625c65a 100644
--- a/src/pc/lua/smlua_cobject_autogen.c
+++ b/src/pc/lua/smlua_cobject_autogen.c
@@ -29,6 +29,7 @@
#include "src/game/player_palette.h"
#include "src/engine/graph_node.h"
#include "include/PR/gbi.h"
+#include "src/game/object_list_processor.h"
#include "include/object_fields.h"
@@ -1580,6 +1581,13 @@ static struct LuaObjectField sNetworkPlayerFields[LUA_NETWORK_PLAYER_FIELD_COUNT
{ "type", LVT_U8, offsetof(struct NetworkPlayer, type), true, LOT_NONE, 1, sizeof(u8) },
};
+#define LUA_NUM_TIMES_CALLED_FIELD_COUNT 3
+static struct LuaObjectField sNumTimesCalledFields[LUA_NUM_TIMES_CALLED_FIELD_COUNT] = {
+ { "ceil", LVT_S16, offsetof(struct NumTimesCalled, ceil), false, LOT_NONE, 1, sizeof(s16) },
+ { "floor", LVT_S16, offsetof(struct NumTimesCalled, floor), false, LOT_NONE, 1, sizeof(s16) },
+ { "wall", LVT_S16, offsetof(struct NumTimesCalled, wall), false, LOT_NONE, 1, sizeof(s16) },
+};
+
#define LUA_OBJECT_FIELD_COUNT 763
static struct LuaObjectField sObjectFields[LUA_OBJECT_FIELD_COUNT] = {
{ "activeFlags", LVT_S16, offsetof(struct Object, activeFlags), false, LOT_NONE, 1, sizeof(s16) },
@@ -2374,11 +2382,19 @@ static struct LuaObjectField sObjectHitboxFields[LUA_OBJECT_HITBOX_FIELD_COUNT]
{ "radius", LVT_S16, offsetof(struct ObjectHitbox, radius), false, LOT_NONE, 1, sizeof(s16) },
};
-#define LUA_OBJECT_NODE_FIELD_COUNT 3
+#define LUA_OBJECT_NODE_FIELD_COUNT 4
static struct LuaObjectField sObjectNodeFields[LUA_OBJECT_NODE_FIELD_COUNT] = {
- { "gfx", LVT_COBJECT, offsetof(struct ObjectNode, gfx), true, LOT_GRAPHNODEOBJECT, 1, sizeof(struct GraphNodeObject) },
- { "next", LVT_COBJECT_P, offsetof(struct ObjectNode, next), true, LOT_OBJECTNODE, 1, sizeof(struct ObjectNode*) },
- { "prev", LVT_COBJECT_P, offsetof(struct ObjectNode, prev), true, LOT_OBJECTNODE, 1, sizeof(struct ObjectNode*) },
+ { "gfx", LVT_COBJECT, offsetof(struct ObjectNode, gfx), true, LOT_GRAPHNODEOBJECT, 1, sizeof(struct GraphNodeObject) },
+ { "next", LVT_COBJECT_P, offsetof(struct ObjectNode, next), true, LOT_OBJECTNODE, 1, sizeof(struct ObjectNode*) },
+ { "pool", LVT_COBJECT_P, offsetof(struct ObjectNode, pool), false, LOT_OBJECTPOOLNODE, 1, sizeof(struct ObjectPoolNode*) },
+ { "prev", LVT_COBJECT_P, offsetof(struct ObjectNode, prev), true, LOT_OBJECTNODE, 1, sizeof(struct ObjectNode*) },
+};
+
+#define LUA_OBJECT_POOL_NODE_FIELD_COUNT 3
+static struct LuaObjectField sObjectPoolNodeFields[LUA_OBJECT_POOL_NODE_FIELD_COUNT] = {
+ { "freeList", LVT_COBJECT, offsetof(struct ObjectPoolNode, freeList), true, LOT_OBJECTNODE, 1, sizeof(struct ObjectNode) },
+ { "next", LVT_COBJECT_P, offsetof(struct ObjectPoolNode, next), false, LOT_OBJECTPOOLNODE, 1, sizeof(struct ObjectPoolNode*) },
+ { "pool", LVT_COBJECT, offsetof(struct ObjectPoolNode, pool), true, LOT_OBJECT, OBJECT_POOL_NODE_CAPACITY, sizeof(struct Object) },
};
#define LUA_OBJECT_WARP_NODE_FIELD_COUNT 3
@@ -2736,9 +2752,11 @@ struct LuaObjectTable sLuaObjectAutogenTable[LOT_AUTOGEN_MAX - LOT_AUTOGEN_MIN]
{ LOT_MODFSFILE, sModFsFileFields, LUA_MOD_FS_FILE_FIELD_COUNT },
{ LOT_NAMETAGSSETTINGS, sNametagsSettingsFields, LUA_NAMETAGS_SETTINGS_FIELD_COUNT },
{ LOT_NETWORKPLAYER, sNetworkPlayerFields, LUA_NETWORK_PLAYER_FIELD_COUNT },
+ { LOT_NUMTIMESCALLED, sNumTimesCalledFields, LUA_NUM_TIMES_CALLED_FIELD_COUNT },
{ LOT_OBJECT, sObjectFields, LUA_OBJECT_FIELD_COUNT },
{ LOT_OBJECTHITBOX, sObjectHitboxFields, LUA_OBJECT_HITBOX_FIELD_COUNT },
{ LOT_OBJECTNODE, sObjectNodeFields, LUA_OBJECT_NODE_FIELD_COUNT },
+ { LOT_OBJECTPOOLNODE, sObjectPoolNodeFields, LUA_OBJECT_POOL_NODE_FIELD_COUNT },
{ LOT_OBJECTWARPNODE, sObjectWarpNodeFields, LUA_OBJECT_WARP_NODE_FIELD_COUNT },
{ LOT_PAINTING, sPaintingFields, LUA_PAINTING_FIELD_COUNT },
{ LOT_PAINTINGVALUES, sPaintingValuesFields, LUA_PAINTING_VALUES_FIELD_COUNT },
@@ -2841,9 +2859,11 @@ const char *sLuaLotNames[] = {
[LOT_MODFSFILE] = "ModFsFile",
[LOT_NAMETAGSSETTINGS] = "NametagsSettings",
[LOT_NETWORKPLAYER] = "NetworkPlayer",
+ [LOT_NUMTIMESCALLED] = "NumTimesCalled",
[LOT_OBJECT] = "Object",
[LOT_OBJECTHITBOX] = "ObjectHitbox",
[LOT_OBJECTNODE] = "ObjectNode",
+ [LOT_OBJECTPOOLNODE] = "ObjectPoolNode",
[LOT_OBJECTWARPNODE] = "ObjectWarpNode",
[LOT_PAINTING] = "Painting",
[LOT_PAINTINGVALUES] = "PaintingValues",
diff --git a/src/pc/lua/smlua_cobject_autogen.h b/src/pc/lua/smlua_cobject_autogen.h
index d034ce9f7..11ea10ab6 100644
--- a/src/pc/lua/smlua_cobject_autogen.h
+++ b/src/pc/lua/smlua_cobject_autogen.h
@@ -86,9 +86,11 @@ enum LuaObjectAutogenType {
LOT_MODFSFILE,
LOT_NAMETAGSSETTINGS,
LOT_NETWORKPLAYER,
+ LOT_NUMTIMESCALLED,
LOT_OBJECT,
LOT_OBJECTHITBOX,
LOT_OBJECTNODE,
+ LOT_OBJECTPOOLNODE,
LOT_OBJECTWARPNODE,
LOT_PAINTING,
LOT_PAINTINGVALUES,
diff --git a/src/pc/lua/smlua_constants_autogen.c b/src/pc/lua/smlua_constants_autogen.c
index d7ca5a882..4179649f3 100644
--- a/src/pc/lua/smlua_constants_autogen.c
+++ b/src/pc/lua/smlua_constants_autogen.c
@@ -2976,7 +2976,8 @@ char gSmluaConstants[] = ""
"TIME_STOP_ALL_OBJECTS=(1 << 4)\n"
"TIME_STOP_MARIO_OPENED_DOOR=(1 << 5)\n"
"TIME_STOP_ACTIVE=(1 << 6)\n"
-"OBJECT_POOL_CAPACITY=1200\n"
+"OBJECT_POOL_CAPACITY=0xFFFFFFFF\n"
+"OBJECT_POOL_NODE_CAPACITY=256\n"
"OBJ_LIST_PLAYER=0\n"
"OBJ_LIST_EXT=1\n"
"OBJ_LIST_DESTRUCTIVE=2\n"
diff --git a/src/pc/lua/smlua_functions_autogen.c b/src/pc/lua/smlua_functions_autogen.c
index e227a54aa..54e57063a 100644
--- a/src/pc/lua/smlua_functions_autogen.c
+++ b/src/pc/lua/smlua_functions_autogen.c
@@ -35485,6 +35485,21 @@ int smlua_func_obj_anim_skip_interpolation(lua_State* L) {
return 1;
}
+int smlua_func_obj_get_count(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", "obj_get_count", 0, top);
+ return 0;
+ }
+
+
+ lua_pushinteger(L, obj_get_count());
+
+ return 1;
+}
+
////////////////////////
// smlua_text_utils.h //
////////////////////////
@@ -38753,6 +38768,7 @@ void smlua_bind_functions_autogen(void) {
smlua_bind_function(L, "set_whirlpools", smlua_func_set_whirlpools);
smlua_bind_function(L, "obj_skip_interpolation", smlua_func_obj_skip_interpolation);
smlua_bind_function(L, "obj_anim_skip_interpolation", smlua_func_obj_anim_skip_interpolation);
+ smlua_bind_function(L, "obj_get_count", smlua_func_obj_get_count);
// smlua_text_utils.h
smlua_bind_function(L, "smlua_text_utils_reset_all", smlua_func_smlua_text_utils_reset_all);
diff --git a/src/pc/lua/utils/smlua_obj_utils.c b/src/pc/lua/utils/smlua_obj_utils.c
index aa71c422d..e945c8e31 100644
--- a/src/pc/lua/utils/smlua_obj_utils.c
+++ b/src/pc/lua/utils/smlua_obj_utils.c
@@ -530,6 +530,10 @@ void obj_anim_skip_interpolation(struct Object *o) {
if (o) { o->header.gfx.animInfo.prevAnimFrameTimestamp = 0; }
}
+u32 obj_get_count(void) {
+ return gObjectCounter;
+}
+
#ifdef DEVELOPMENT
void obj_randomize(struct Object* o) {
if (!o) { return; }
diff --git a/src/pc/lua/utils/smlua_obj_utils.h b/src/pc/lua/utils/smlua_obj_utils.h
index 518e3b193..4017fdf1d 100644
--- a/src/pc/lua/utils/smlua_obj_utils.h
+++ b/src/pc/lua/utils/smlua_obj_utils.h
@@ -160,4 +160,7 @@ void obj_skip_interpolation(struct Object *o);
/* |description|Skips animation interpolation for a frame|descriptionEnd| */
void obj_anim_skip_interpolation(struct Object *o);
+/* |description|Gets the amount of objects in this area|descriptionEnd| */
+u32 obj_get_count(void);
+
#endif
diff --git a/src/pc/network/packets/packet_level_macro.c b/src/pc/network/packets/packet_level_macro.c
index 3cae16a7a..be7e71897 100644
--- a/src/pc/network/packets/packet_level_macro.c
+++ b/src/pc/network/packets/packet_level_macro.c
@@ -18,10 +18,10 @@
// TODO: move to common utility location
static struct Object* get_object_matching_respawn_info(s16* respawnInfo) {
- for (s32 i = 0; i < OBJECT_POOL_CAPACITY; i++) {
- struct Object* o = &gObjectPool[i];
+ traverse_object_pools(
+ struct Object* o = &node->pool[i];
if (o->respawnInfo == respawnInfo) { return o; }
- }
+ )
return NULL;
}
@@ -196,19 +196,19 @@ void network_receive_level_macro(struct Packet* p) {
o->oCoinUnkF4 = (o->oBehParams >> 8) & 0xFF;
u8 childIndex = 0;
- for (s32 i = 0; i < OBJECT_POOL_CAPACITY; i++) {
- struct Object* o2 = &gObjectPool[i];
+ traverse_object_pools(
+ struct Object* o2 = &node->pool[i];
if (o2->parentObj != o) { continue; }
if (o2 == o) { continue; }
if (o2->behavior != smlua_override_behavior(bhvCoinFormationSpawn) && o2->behavior != smlua_override_behavior(bhvYellowCoin)) { continue; }
if (o->oCoinUnkF4 & (1 << childIndex++)) {
obj_mark_for_deletion(o2);
}
- }
+ )
LOG_INFO("rx macro special: coin formation");
} else if (behavior == bhvGoombaTripletSpawner) {
- for (s32 i = 0; i < OBJECT_POOL_CAPACITY; i++) {
- struct Object* o2 = &gObjectPool[i];
+ traverse_object_pools(
+ struct Object* o2 = &node->pool[i];
if (o2->parentObj != o) { continue; }
if (o2 == o) { continue; }
if (o2->behavior != smlua_override_behavior(bhvGoomba)) { continue; }
@@ -222,7 +222,7 @@ void network_receive_level_macro(struct Packet* p) {
obj_mark_for_deletion(o2);
gCurrentObject = prevObject;
}
- }
+ )
LOG_INFO("rx macro special: goomba triplet");
} else {
o->oBehParams = (((*respawnInfo) & 0x00FF) << 16) + ((*respawnInfo) & 0xFF00);
diff --git a/src/pc/network/packets/packet_level_spawn_info.c b/src/pc/network/packets/packet_level_spawn_info.c
index 816be414b..b6603ded8 100644
--- a/src/pc/network/packets/packet_level_spawn_info.c
+++ b/src/pc/network/packets/packet_level_spawn_info.c
@@ -16,10 +16,10 @@
// TODO: move to common utility location
static struct Object* get_object_matching_respawn_info(u32* respawnInfo) {
- for (s32 i = 0; i < OBJECT_POOL_CAPACITY; i++) {
- struct Object* o = &gObjectPool[i];
+ traverse_object_pools(
+ struct Object* o = &node->pool[i];
if (o->respawnInfo == respawnInfo) { return o; }
- }
+ )
return NULL;
}