diff --git a/include/object.h b/include/object.h index e40e671120..786c5a8cc1 100644 --- a/include/object.h +++ b/include/object.h @@ -41,7 +41,7 @@ void Object_InitContext(struct PlayState* play, ObjectContext* objectCtx); void Object_UpdateEntries(ObjectContext* objectCtx); s32 Object_GetSlot(ObjectContext* objectCtx, s16 objectId); s32 Object_IsLoaded(ObjectContext* objectCtx, s32 slot); -void func_800981B8(ObjectContext* objectCtx); +void Object_ReloadAll(ObjectContext* objectCtx); extern u32 gObjectTableSize; extern RomFile gObjectTable[OBJECT_ID_MAX]; diff --git a/src/code/z_scene.c b/src/code/z_scene.c index 38323e6c7b..96fe9b9170 100644 --- a/src/code/z_scene.c +++ b/src/code/z_scene.c @@ -20,18 +20,18 @@ SceneCmdHandlerFunc sSceneCmdHandlers[SCENE_CMD_ID_MAX]; RomFile sNaviQuestHintFiles[]; /** - * Spawn an object file of a specified ID that will persist through room changes. + * Loads an object file of a specified ID that will persist through room changes. * * This waits for the file to be fully loaded, the data is available when the function returns. * * @return The new object slot corresponding to the requested object ID. * - * @note This function is not meant to be called externally to spawn object files on the fly. - * When an object is spawned with this function, all objects that come before it in the entry list will be treated as + * @note This function is not meant to be called externally to load object files on the fly. + * When an object is loaded with this function, all objects that come before it in the entry list will be treated as * persistent, which will likely cause either the amount of free slots or object space memory to run out. * This function is only meant to be called internally on scene load, before the object list from any room is processed. */ -s32 Object_SpawnPersistent(ObjectContext* objectCtx, s16 objectId) { +s32 Object_LoadPersistent(ObjectContext* objectCtx, s16 objectId) { u32 size; objectCtx->slots[objectCtx->numEntries].id = objectId; @@ -109,10 +109,14 @@ void Object_InitContext(PlayState* play, ObjectContext* objectCtx) { GAME_STATE_ALLOC(&play->state, spaceSize, "../z_scene.c", 219); objectCtx->spaceEnd = (void*)((uintptr_t)objectCtx->spaceStart + spaceSize); - objectCtx->mainKeepSlot = Object_SpawnPersistent(objectCtx, OBJECT_GAMEPLAY_KEEP); + objectCtx->mainKeepSlot = Object_LoadPersistent(objectCtx, OBJECT_GAMEPLAY_KEEP); gSegments[4] = OS_K0_TO_PHYSICAL(objectCtx->slots[objectCtx->mainKeepSlot].segment); } +/** + * Run every frame. Loads non-persistent objects on scene init/room change, whose data have + * been added to the object slot array by `func_800982FC`. Unloaded status is marked by negative object id. + */ void Object_UpdateEntries(ObjectContext* objectCtx) { s32 i; ObjectEntry* entry = &objectCtx->slots[0]; @@ -120,7 +124,7 @@ void Object_UpdateEntries(ObjectContext* objectCtx) { u32 size; for (i = 0; i < objectCtx->numEntries; i++) { - if (entry->id < 0) { + if (entry->id < 0) { // Not yet loaded object if (entry->dmaRequest.vromAddr == 0) { osCreateMesgQueue(&entry->loadQueue, &entry->loadMsg, 1); objectFile = &gObjectTable[-entry->id]; @@ -131,17 +135,22 @@ void Object_UpdateEntries(ObjectContext* objectCtx) { DMA_REQUEST_ASYNC(&entry->dmaRequest, entry->segment, objectFile->vromStart, size, 0, &entry->loadQueue, NULL, "../z_scene.c", 266); } else if (osRecvMesg(&entry->loadQueue, NULL, OS_MESG_NOBLOCK) == 0) { - entry->id = -entry->id; + entry->id = -entry->id; // Object now loaded } } entry++; } } +/** + * Get object slot for a given object id. + * @return object slot for given id, or -1 if not in any slot + */ s32 Object_GetSlot(ObjectContext* objectCtx, s16 objectId) { s32 i; for (i = 0; i < objectCtx->numEntries; i++) { + // ABS is necessary because object might not yet be loaded if (ABS(objectCtx->slots[i].id) == objectId) { return i; } @@ -150,6 +159,10 @@ s32 Object_GetSlot(ObjectContext* objectCtx, s16 objectId) { return -1; } +/** + * Check if the object in a given slot is loaded. + * @return true if loaded, else false + */ s32 Object_IsLoaded(ObjectContext* objectCtx, s32 slot) { if (objectCtx->slots[slot].id > 0) { return true; @@ -158,7 +171,12 @@ s32 Object_IsLoaded(ObjectContext* objectCtx, s32 slot) { } } -void func_800981B8(ObjectContext* objectCtx) { +/** + * Run when exiting the pause menu (either to continue play or due to game over). + * Sync DMA loads all objects for current room. + * (Collision header data is restored by `func_800418D0`) + */ +void Object_ReloadAll(ObjectContext* objectCtx) { s32 i; s32 id; u32 size; @@ -173,6 +191,14 @@ void func_800981B8(ObjectContext* objectCtx) { } } +/** + * Prepares a given slot to be filled with data of given object id. Called by `Scene_CommandObjectList` + * for every non-persistent object to be added. The data is used by `Object_UpdateEntries` to load the + * objects. + * @note There is an assert to ensure that the next object slot address is within the object memory space, + * but in non-debug builds this is not included, and no other bounds check is done. + * @return start address of the next object slot's memory in object space + */ void* func_800982FC(ObjectContext* objectCtx, s32 slot, s16 objectId) { ObjectEntry* entry = &objectCtx->slots[slot]; RomFile* objectFile = &gObjectTable[objectId]; @@ -195,6 +221,10 @@ void* func_800982FC(ObjectContext* objectCtx, s32 slot, s16 objectId) { return nextPtr; } +/** + * Executes all the scene commands for the current scene/room upon loading. The commands to be run + * for a particular scene/room is found in the scene/room file's data. + */ s32 Scene_ExecuteCommands(PlayState* play, SceneCmd* sceneCmd) { while (true) { u32 cmdCode = sceneCmd->base.code; @@ -230,7 +260,7 @@ BAD_RETURN(s32) Scene_CommandPlayerEntryList(PlayState* play, SceneCmd* cmd) { linkObjectId = gLinkObjectIds[((void)0, gSaveContext.save.linkAge)]; gActorOverlayTable[playerEntry->id].profile->objectId = linkObjectId; - Object_SpawnPersistent(&play->objectCtx, linkObjectId); + Object_LoadPersistent(&play->objectCtx, linkObjectId); } BAD_RETURN(s32) Scene_CommandActorEntryList(PlayState* play, SceneCmd* cmd) { @@ -265,7 +295,7 @@ BAD_RETURN(s32) Scene_CommandSpawnList(PlayState* play, SceneCmd* cmd) { BAD_RETURN(s32) Scene_CommandSpecialFiles(PlayState* play, SceneCmd* cmd) { if (cmd->specialFiles.keepObjectId != OBJECT_INVALID) { - play->objectCtx.subKeepSlot = Object_SpawnPersistent(&play->objectCtx, cmd->specialFiles.keepObjectId); + play->objectCtx.subKeepSlot = Object_LoadPersistent(&play->objectCtx, cmd->specialFiles.keepObjectId); gSegments[5] = OS_K0_TO_PHYSICAL(play->objectCtx.slots[play->objectCtx.subKeepSlot].segment); } @@ -300,6 +330,10 @@ BAD_RETURN(s32) Scene_CommandObjectList(PlayState* play, SceneCmd* cmd) { entries = play->objectCtx.slots; entry = &play->objectCtx.slots[i]; + // On room load (room change/scene init), go through all current loaded non-persistent + // object slots and new objects to be loaded in those slots. + // If a slot's object id is not the same for both rooms, invalidate that object slot + // and every following slot. Also kill any actors dependent on those objects. while (i < play->objectCtx.numEntries) { if (entry->id != *objectListEntry) { @@ -325,6 +359,7 @@ BAD_RETURN(s32) Scene_CommandObjectList(PlayState* play, SceneCmd* cmd) { "scene_info->object_bank.num <= OBJECT_EXCHANGE_BANK_MAX", "../z_scene.c", 705); while (k < cmd->objectList.length) { + // Add object from new object list at `nextPtr` (next start address in object memory space) nextPtr = func_800982FC(&play->objectCtx, i, *objectListEntry); if (i < (ARRAY_COUNT(play->objectCtx.slots) - 1)) { entries[i + 1].segment = nextPtr; diff --git a/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope.c b/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope.c index 6d4985e758..75cbb25b84 100644 --- a/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope.c +++ b/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope.c @@ -4643,7 +4643,7 @@ void KaleidoScope_Update(PlayState* play) { pauseCtx->state = PAUSE_STATE_OFF; R_UPDATE_RATE = 3; R_PAUSE_BG_PRERENDER_STATE = PAUSE_BG_PRERENDER_OFF; - func_800981B8(&play->objectCtx); + Object_ReloadAll(&play->objectCtx); func_800418D0(&play->colCtx, play); if (pauseCtx->promptChoice == 0) { Play_TriggerRespawn(play); @@ -4704,7 +4704,7 @@ void KaleidoScope_Update(PlayState* play) { R_UPDATE_RATE = 3; R_PAUSE_BG_PRERENDER_STATE = PAUSE_BG_PRERENDER_OFF; - func_800981B8(&play->objectCtx); + Object_ReloadAll(&play->objectCtx); func_800418D0(&play->colCtx, play); switch (play->sceneId) {