mirror of
https://github.com/coop-deluxe/sm64coopdx.git
synced 2024-10-20 04:02:39 +00:00
687 lines
20 KiB
C
687 lines
20 KiB
C
#include <ultra64.h>
|
|
|
|
#include "sm64.h"
|
|
#include "behavior_data.h"
|
|
#include "engine/behavior_script.h"
|
|
#include "area.h"
|
|
#include "camera.h"
|
|
#include "engine/graph_node.h"
|
|
#include "interaction.h"
|
|
#include "debug.h"
|
|
#include "profiler.h"
|
|
#include "spawn_object.h"
|
|
#include "engine/surface_collision.h"
|
|
#include "memory.h"
|
|
#include "level_update.h"
|
|
#include "object_collision.h"
|
|
#include "object_helpers.h"
|
|
#include "platform_displacement.h"
|
|
#include "engine/surface_load.h"
|
|
#include "object_list_processor.h"
|
|
#include "mario.h"
|
|
|
|
|
|
/**
|
|
* Flags controlling what debug info is displayed.
|
|
*/
|
|
s32 gDebugInfoFlags;
|
|
|
|
/**
|
|
* The number of times per frame find_floor found no floor beneath an
|
|
* object, and therefore either returned a dynamic floor or NULL.
|
|
*/
|
|
s32 gNumFindFloorMisses;
|
|
|
|
UNUSED s32 unused_8033BEF8;
|
|
|
|
/**
|
|
* An unused debug counter with the label "WALL".
|
|
*/
|
|
s32 gUnknownWallCount;
|
|
|
|
/**
|
|
* Roughly the number of objects that have been processed this frame so far.
|
|
* A bug in update_terrain_objects makes this count inaccurate.
|
|
*/
|
|
u32 gObjectCounter;
|
|
|
|
/**
|
|
* The number of times find_floor, find_ceil, and find_wall_collisions have been called respectively.
|
|
*/
|
|
struct NumTimesCalled gNumCalls;
|
|
|
|
/**
|
|
* An array of debug controls that could be used to tweak in-game parameters.
|
|
* The only used rows are [4] and [5] (effectinfo and enemyinfo).
|
|
*/
|
|
s16 gDebugInfo[16][8];
|
|
s16 gDebugInfoOverwrite[16][8];
|
|
|
|
/**
|
|
* A set of flags to control which objects are updated on a given frame.
|
|
* This is used during dialog and cutscenes to freeze most objects in place.
|
|
*/
|
|
u32 gTimeStopState;
|
|
|
|
/**
|
|
* The pool that objects are allocated from.
|
|
*/
|
|
struct Object gObjectPool[OBJECT_POOL_CAPACITY];
|
|
|
|
/**
|
|
* A special object whose purpose is to act as a parent for macro objects.
|
|
*/
|
|
struct Object gMacroObjectDefaultParent;
|
|
|
|
/**
|
|
* A pointer to gObjectListArray.
|
|
* Given an object list index idx, gObjectLists[idx] is the head of a doubly
|
|
* linked list of all currently spawned objects in the list.
|
|
*/
|
|
struct ObjectNode *gObjectLists;
|
|
|
|
/**
|
|
* A singly linked list of available slots in the object pool.
|
|
*/
|
|
struct ObjectNode gFreeObjectList;
|
|
|
|
/**
|
|
* The object representing mario.
|
|
*/
|
|
struct Object *gMarioObject;
|
|
|
|
/**
|
|
* An object variable that may have been used to represent the planned
|
|
* second player. This is speculation, based on its position and its usage in
|
|
* shadow.c.
|
|
*/
|
|
struct Object *gLuigiObject;
|
|
|
|
/**
|
|
* The object whose behavior script is currently being updated.
|
|
* This object is used frequently in object behavior code, and so is often
|
|
* aliased as "o".
|
|
*/
|
|
struct Object *gCurrentObject;
|
|
|
|
/**
|
|
* The next object behavior command to be executed.
|
|
*/
|
|
const BehaviorScript *gBehCommand;
|
|
|
|
/**
|
|
* The number of objects that were processed last frame, which may miss some
|
|
* objects that were spawned last frame and all objects that were spawned this
|
|
* frame. It also includes objects that were unloaded last frame.
|
|
* Besides this, a bug in update_terrain_objects makes this count inaccurate.
|
|
*/
|
|
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;
|
|
|
|
/**
|
|
* A pool used by chain chomp and wiggler to allocate their body parts.
|
|
*/
|
|
struct MemoryPool *gObjectMemoryPool;
|
|
|
|
|
|
s16 gCheckingSurfaceCollisionsForCamera;
|
|
s16 gFindFloorIncludeSurfaceIntangible;
|
|
s16 *gEnvironmentRegions;
|
|
s32 gEnvironmentLevels[20];
|
|
s8 gDoorAdjacentRooms[60][2];
|
|
s16 gMarioCurrentRoom;
|
|
s16 D_8035FEE2;
|
|
s16 D_8035FEE4;
|
|
s16 gTHIWaterDrained;
|
|
s16 gTTCSpeedSetting;
|
|
s16 gMarioShotFromCannon;
|
|
s16 gCCMEnteredSlide;
|
|
s16 gNumRoomedObjectsInMarioRoom;
|
|
s16 gNumRoomedObjectsNotInMarioRoom;
|
|
s16 gWDWWaterLevelChanging;
|
|
s16 gMarioOnMerryGoRound;
|
|
|
|
/**
|
|
* Nodes used to represent the doubly linked object lists.
|
|
*/
|
|
struct ObjectNode gObjectListArray[16];
|
|
|
|
/**
|
|
* The order that object lists are processed in a frame.
|
|
*/
|
|
s8 sObjectListUpdateOrder[] = { OBJ_LIST_SPAWNER,
|
|
OBJ_LIST_SURFACE,
|
|
OBJ_LIST_POLELIKE,
|
|
OBJ_LIST_PLAYER,
|
|
OBJ_LIST_PUSHABLE,
|
|
OBJ_LIST_GENACTOR,
|
|
OBJ_LIST_DESTRUCTIVE,
|
|
OBJ_LIST_LEVEL,
|
|
OBJ_LIST_DEFAULT,
|
|
OBJ_LIST_UNIMPORTANT,
|
|
-1 };
|
|
|
|
/**
|
|
* Info needed to spawn particles and keep track of which have been spawned for
|
|
* an object.
|
|
*/
|
|
struct ParticleProperties {
|
|
u32 particleFlag;
|
|
u32 activeParticleFlag;
|
|
u8 model;
|
|
const BehaviorScript *behavior;
|
|
};
|
|
|
|
/**
|
|
* A table mapping particle flags to various properties use when spawning a particle.
|
|
*/
|
|
struct ParticleProperties sParticleTypes[] = {
|
|
{ PARTICLE_DUST, ACTIVE_PARTICLE_0, MODEL_MIST, bhvMarioDustGenerator },
|
|
{ PARTICLE_1, ACTIVE_PARTICLE_18, MODEL_NONE, bhvWallTinyStarParticleSpawn },
|
|
{ PARTICLE_4, ACTIVE_PARTICLE_4, MODEL_NONE, bhvPoundTinyStarParticleSpawn },
|
|
{ PARTICLE_SPARKLES, ACTIVE_PARTICLE_3, MODEL_SPARKLES, bhvSpecialTripleJumpSparkles },
|
|
{ PARTICLE_5, ACTIVE_PARTICLE_5, MODEL_BUBBLE, bhvBubbleMario },
|
|
{ PARTICLE_6, ACTIVE_PARTICLE_6, MODEL_WATER_SPLASH, bhvWaterSplash },
|
|
{ PARTICLE_7, ACTIVE_PARTICLE_7, MODEL_WATER_WAVES_SURF, bhvSurfaceWaves },
|
|
{ PARTICLE_9, ACTIVE_PARTICLE_9, MODEL_WHITE_PARTICLE_SMALL, bhvWaterWaves },
|
|
{ PARTICLE_10, ACTIVE_PARTICLE_10, MODEL_WATER_WAVES, bhvWaveTrailOnSurface },
|
|
{ PARTICLE_11, ACTIVE_PARTICLE_11, MODEL_RED_FLAME, bhvFlameMario },
|
|
{ PARTICLE_8, ACTIVE_PARTICLE_8, MODEL_NONE, bhvWavesGenerator },
|
|
{ PARTICLE_12, ACTIVE_PARTICLE_12, MODEL_NONE, bhvSurfaceWaveShrinking },
|
|
{ PARTICLE_LEAVES, ACTIVE_PARTICLE_13, MODEL_NONE, bhvSnowLeafParticleSpawn },
|
|
{ PARTICLE_14, ACTIVE_PARTICLE_16, MODEL_NONE, bhvGroundSnow },
|
|
{ PARTICLE_17, ACTIVE_PARTICLE_17, MODEL_NONE, bhvWaterMistSpawn },
|
|
{ PARTICLE_15, ACTIVE_PARTICLE_14, MODEL_NONE, bhvGroundSand },
|
|
{ PARTICLE_16, ACTIVE_PARTICLE_15, MODEL_NONE, bhvPoundWhitePuffs },
|
|
{ PARTICLE_18, ACTIVE_PARTICLE_19, MODEL_NONE, bhvPunchTinyTriangleSpawn },
|
|
{ 0, 0, MODEL_NONE, NULL },
|
|
};
|
|
|
|
/**
|
|
* Copy position, velocity, and angle variables from MarioState to the mario
|
|
* object.
|
|
*/
|
|
void copy_mario_state_to_object(void) {
|
|
s32 i = 0;
|
|
// L is real
|
|
if (gCurrentObject != gMarioObject) {
|
|
i += 1;
|
|
}
|
|
|
|
gCurrentObject->oVelX = gMarioStates[i].vel[0];
|
|
gCurrentObject->oVelY = gMarioStates[i].vel[1];
|
|
gCurrentObject->oVelZ = gMarioStates[i].vel[2];
|
|
|
|
gCurrentObject->oPosX = gMarioStates[i].pos[0];
|
|
gCurrentObject->oPosY = gMarioStates[i].pos[1];
|
|
gCurrentObject->oPosZ = gMarioStates[i].pos[2];
|
|
|
|
gCurrentObject->oMoveAnglePitch = gCurrentObject->header.gfx.angle[0];
|
|
gCurrentObject->oMoveAngleYaw = gCurrentObject->header.gfx.angle[1];
|
|
gCurrentObject->oMoveAngleRoll = gCurrentObject->header.gfx.angle[2];
|
|
|
|
gCurrentObject->oFaceAnglePitch = gCurrentObject->header.gfx.angle[0];
|
|
gCurrentObject->oFaceAngleYaw = gCurrentObject->header.gfx.angle[1];
|
|
gCurrentObject->oFaceAngleRoll = gCurrentObject->header.gfx.angle[2];
|
|
|
|
gCurrentObject->oAngleVelPitch = gMarioStates[i].angleVel[0];
|
|
gCurrentObject->oAngleVelYaw = gMarioStates[i].angleVel[1];
|
|
gCurrentObject->oAngleVelRoll = gMarioStates[i].angleVel[2];
|
|
}
|
|
|
|
/**
|
|
* Spawn a particle at gCurrentObject's location.
|
|
*/
|
|
void spawn_particle(u32 activeParticleFlag, s16 model, const BehaviorScript *behavior) {
|
|
if (!(gCurrentObject->oActiveParticleFlags & activeParticleFlag)) {
|
|
struct Object *particle;
|
|
gCurrentObject->oActiveParticleFlags |= activeParticleFlag;
|
|
particle = spawn_object_at_origin(gCurrentObject, 0, model, behavior);
|
|
copy_object_pos_and_angle(particle, gCurrentObject);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Mario's primary behavior update function.
|
|
*/
|
|
void bhv_mario_update(void) {
|
|
u32 particleFlags = 0;
|
|
s32 i;
|
|
|
|
particleFlags = execute_mario_action(gCurrentObject);
|
|
gCurrentObject->oMarioParticleFlags = particleFlags;
|
|
|
|
// Mario code updates MarioState's versions of position etc, so we need
|
|
// to sync it with the mario object
|
|
copy_mario_state_to_object();
|
|
|
|
i = 0;
|
|
while (sParticleTypes[i].particleFlag != 0) {
|
|
if (particleFlags & sParticleTypes[i].particleFlag) {
|
|
spawn_particle(sParticleTypes[i].activeParticleFlag, sParticleTypes[i].model,
|
|
sParticleTypes[i].behavior);
|
|
}
|
|
|
|
i++;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update every object that occurs after firstObj in the given object list,
|
|
* including firstObj itself. Return the number of objects that were updated.
|
|
*/
|
|
s32 update_objects_starting_at(struct ObjectNode *objList, struct ObjectNode *firstObj) {
|
|
s32 count = 0;
|
|
|
|
while (objList != firstObj) {
|
|
gCurrentObject = (struct Object *) firstObj;
|
|
|
|
gCurrentObject->header.gfx.node.flags |= GRAPH_RENDER_HAS_ANIMATION;
|
|
cur_object_exec_behavior();
|
|
|
|
firstObj = firstObj->next;
|
|
count += 1;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
/**
|
|
* Update objects in objList starting with firstObj while time stop is active.
|
|
* This means that only certain select objects will be updated, such as mario,
|
|
* doors, unimportant objects, and the object that initiated time stop.
|
|
* The exact set of objects that are updated depends on which flags are set
|
|
* in gTimeStopState.
|
|
* Return the total number of objects in the list (including those that weren't
|
|
* updated)
|
|
*/
|
|
s32 update_objects_during_time_stop(struct ObjectNode *objList, struct ObjectNode *firstObj) {
|
|
s32 count = 0;
|
|
s32 unfrozen;
|
|
|
|
while (objList != firstObj) {
|
|
gCurrentObject = (struct Object *) firstObj;
|
|
|
|
unfrozen = FALSE;
|
|
|
|
// Selectively unfreeze certain objects
|
|
if (!(gTimeStopState & TIME_STOP_ALL_OBJECTS)) {
|
|
if (gCurrentObject == gMarioObject && !(gTimeStopState & TIME_STOP_MARIO_AND_DOORS)) {
|
|
unfrozen = TRUE;
|
|
}
|
|
|
|
if ((gCurrentObject->oInteractType & (INTERACT_DOOR | INTERACT_WARP_DOOR))
|
|
&& !(gTimeStopState & TIME_STOP_MARIO_AND_DOORS)) {
|
|
unfrozen = TRUE;
|
|
}
|
|
|
|
if (gCurrentObject->activeFlags
|
|
& (ACTIVE_FLAG_UNIMPORTANT | ACTIVE_FLAG_INITIATED_TIME_STOP)) {
|
|
unfrozen = TRUE;
|
|
}
|
|
}
|
|
|
|
// Only update if unfrozen
|
|
if (unfrozen) {
|
|
gCurrentObject->header.gfx.node.flags |= GRAPH_RENDER_HAS_ANIMATION;
|
|
cur_object_exec_behavior();
|
|
} else {
|
|
gCurrentObject->header.gfx.node.flags &= ~GRAPH_RENDER_HAS_ANIMATION;
|
|
}
|
|
|
|
firstObj = firstObj->next;
|
|
count++;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
/**
|
|
* Update every object in the given list. Return the total number of objects in
|
|
* the list.
|
|
*/
|
|
s32 update_objects_in_list(struct ObjectNode *objList) {
|
|
s32 count;
|
|
struct ObjectNode *firstObj = objList->next;
|
|
|
|
if (!(gTimeStopState & TIME_STOP_ACTIVE)) {
|
|
count = update_objects_starting_at(objList, firstObj);
|
|
} else {
|
|
count = update_objects_during_time_stop(objList, firstObj);
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
/**
|
|
* Unload any objects in the list that have been deactivated.
|
|
*/
|
|
s32 unload_deactivated_objects_in_list(struct ObjectNode *objList) {
|
|
struct ObjectNode *obj = objList->next;
|
|
|
|
while (objList != obj) {
|
|
gCurrentObject = (struct Object *) obj;
|
|
|
|
obj = obj->next;
|
|
|
|
if ((gCurrentObject->activeFlags & ACTIVE_FLAG_ACTIVE) != ACTIVE_FLAG_ACTIVE) {
|
|
// Prevent object from respawning after exiting and re-entering the
|
|
// area
|
|
if (!(gCurrentObject->oFlags & OBJ_FLAG_PERSISTENT_RESPAWN)) {
|
|
set_object_respawn_info_bits(gCurrentObject, RESPAWN_INFO_DONT_RESPAWN);
|
|
}
|
|
|
|
unload_object(gCurrentObject);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* OR the object's respawn info with bits << 8. If bits = 0xFF, this prevents
|
|
* the object from respawning after leaving and re-entering the area.
|
|
* For macro objects, respawnInfo points to the 16 bit entry in the macro object
|
|
* list. For other objects, it points to the 32 bit behaviorArg in the
|
|
* SpawnInfo.
|
|
*/
|
|
void set_object_respawn_info_bits(struct Object *obj, u8 bits) {
|
|
u32 *info32;
|
|
u16 *info16;
|
|
|
|
switch (obj->respawnInfoType) {
|
|
case RESPAWN_INFO_TYPE_32:
|
|
info32 = (u32 *) obj->respawnInfo;
|
|
*info32 |= bits << 8;
|
|
break;
|
|
|
|
case RESPAWN_INFO_TYPE_16:
|
|
info16 = (u16 *) obj->respawnInfo;
|
|
*info16 |= bits << 8;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Unload all objects whose activeAreaIndex is areaIndex.
|
|
*/
|
|
void unload_objects_from_area(UNUSED s32 unused, s32 areaIndex) {
|
|
struct Object *obj;
|
|
struct ObjectNode *node;
|
|
struct ObjectNode *list;
|
|
s32 i;
|
|
gObjectLists = gObjectListArray;
|
|
|
|
for (i = 0; i < NUM_OBJ_LISTS; i++) {
|
|
list = gObjectLists + i;
|
|
node = list->next;
|
|
|
|
while (node != list) {
|
|
obj = (struct Object *) node;
|
|
node = node->next;
|
|
|
|
if (obj->header.gfx.unk19 == areaIndex) {
|
|
unload_object(obj);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Spawn objects given a list of SpawnInfos. Called when loading an area.
|
|
*/
|
|
void spawn_objects_from_info(UNUSED s32 unused, struct SpawnInfo *spawnInfo) {
|
|
gObjectLists = gObjectListArray;
|
|
gTimeStopState = 0;
|
|
|
|
gWDWWaterLevelChanging = FALSE;
|
|
gMarioOnMerryGoRound = 0;
|
|
|
|
//! (Spawning Displacement) On the Japanese version, mario's platform object
|
|
// isn't cleared when transitioning between areas. This can cause mario to
|
|
// receive displacement after spawning.
|
|
#ifndef VERSION_JP
|
|
clear_mario_platform();
|
|
#endif
|
|
|
|
if (gCurrAreaIndex == 2) {
|
|
gCCMEnteredSlide |= 1;
|
|
}
|
|
|
|
while (spawnInfo != NULL) {
|
|
struct Object *object;
|
|
UNUSED s32 unused;
|
|
const BehaviorScript *script;
|
|
UNUSED s16 arg16 = (s16)(spawnInfo->behaviorArg & 0xFFFF);
|
|
|
|
script = segmented_to_virtual(spawnInfo->behaviorScript);
|
|
|
|
// If the object was previously killed/collected, don't respawn it
|
|
if ((spawnInfo->behaviorArg & (RESPAWN_INFO_DONT_RESPAWN << 8))
|
|
!= (RESPAWN_INFO_DONT_RESPAWN << 8)) {
|
|
object = create_object(script);
|
|
|
|
// Behavior parameters are often treated as four separate bytes, but
|
|
// are stored as an s32.
|
|
object->oBehParams = spawnInfo->behaviorArg;
|
|
// The second byte of the behavior parameters is copied over to a special field
|
|
// as it is the most frequently used by objects.
|
|
object->oBehParams2ndByte = ((spawnInfo->behaviorArg) >> 16) & 0xFF;
|
|
|
|
object->behavior = script;
|
|
object->unused1 = 0;
|
|
|
|
// Record death/collection in the SpawnInfo
|
|
object->respawnInfoType = RESPAWN_INFO_TYPE_32;
|
|
object->respawnInfo = &spawnInfo->behaviorArg;
|
|
|
|
if (spawnInfo->behaviorArg & 0x01) {
|
|
gMarioObject = object;
|
|
geo_make_first_child(&object->header.gfx.node);
|
|
}
|
|
|
|
geo_obj_init_spawninfo(&object->header.gfx, spawnInfo);
|
|
|
|
object->oPosX = spawnInfo->startPos[0];
|
|
object->oPosY = spawnInfo->startPos[1];
|
|
object->oPosZ = spawnInfo->startPos[2];
|
|
|
|
object->oFaceAnglePitch = spawnInfo->startAngle[0];
|
|
object->oFaceAngleYaw = spawnInfo->startAngle[1];
|
|
object->oFaceAngleRoll = spawnInfo->startAngle[2];
|
|
|
|
object->oMoveAnglePitch = spawnInfo->startAngle[0];
|
|
object->oMoveAngleYaw = spawnInfo->startAngle[1];
|
|
object->oMoveAngleRoll = spawnInfo->startAngle[2];
|
|
}
|
|
|
|
spawnInfo = spawnInfo->next;
|
|
}
|
|
}
|
|
|
|
void stub_8029CA50() {
|
|
}
|
|
|
|
/**
|
|
* Clear objects, dynamic surfaces, and some miscellaneous level data used by
|
|
* objects.
|
|
*/
|
|
void clear_objects(void) {
|
|
s32 i;
|
|
|
|
gTHIWaterDrained = 0;
|
|
gTimeStopState = 0;
|
|
gMarioObject = NULL;
|
|
gMarioCurrentRoom = 0;
|
|
|
|
for (i = 0; i < 60; i++) {
|
|
gDoorAdjacentRooms[i][0] = 0;
|
|
gDoorAdjacentRooms[i][1] = 0;
|
|
}
|
|
|
|
debug_unknown_level_select_check();
|
|
|
|
init_free_object_list();
|
|
clear_object_lists(gObjectListArray);
|
|
|
|
stub_80385BF0();
|
|
stub_8029CA50();
|
|
|
|
for (i = 0; i < OBJECT_POOL_CAPACITY; i++) {
|
|
gObjectPool[i].activeFlags = ACTIVE_FLAGS_DEACTIVATED;
|
|
geo_reset_object_node(&gObjectPool[i].header.gfx);
|
|
}
|
|
|
|
gObjectMemoryPool = mem_pool_init(0x800, MEMORY_POOL_LEFT);
|
|
gObjectLists = gObjectListArray;
|
|
|
|
clear_dynamic_surfaces();
|
|
}
|
|
|
|
/**
|
|
* 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]);
|
|
}
|
|
|
|
/**
|
|
* Update all other object lists besides spawner and surface objects, using
|
|
* the order specified by sObjectListUpdateOrder.
|
|
*/
|
|
void update_non_terrain_objects(void) {
|
|
UNUSED s32 unused;
|
|
s32 listIndex;
|
|
|
|
s32 i = 2;
|
|
while ((listIndex = sObjectListUpdateOrder[i]) != -1) {
|
|
gObjectCounter += update_objects_in_list(&gObjectLists[listIndex]);
|
|
i += 1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Unload deactivated objects in any object list.
|
|
*/
|
|
void unload_deactivated_objects(void) {
|
|
UNUSED s32 unused;
|
|
s32 listIndex;
|
|
|
|
s32 i = 0;
|
|
while ((listIndex = sObjectListUpdateOrder[i]) != -1) {
|
|
unload_deactivated_objects_in_list(&gObjectLists[listIndex]);
|
|
i += 1;
|
|
}
|
|
|
|
// TIME_STOP_UNKNOWN_0 was most likely intended to be used to track whether
|
|
// any objects had been deactivated
|
|
gTimeStopState &= ~TIME_STOP_UNKNOWN_0;
|
|
}
|
|
|
|
/**
|
|
* Unused profiling function.
|
|
*/
|
|
static u16 unused_get_elapsed_time(u64 *cycleCounts, s32 index) {
|
|
u16 time;
|
|
f64 cycles;
|
|
|
|
cycles = cycleCounts[index] - cycleCounts[index - 1];
|
|
if (cycles < 0) {
|
|
cycles = 0;
|
|
}
|
|
|
|
time = (u16)(((u64) cycles * 1000000 / osClockRate) / 16667.0 * 1000.0);
|
|
if (time > 999) {
|
|
time = 999;
|
|
}
|
|
|
|
return time;
|
|
}
|
|
|
|
/**
|
|
* Update all objects. This includes script execution, object collision detection,
|
|
* and object surface management.
|
|
*/
|
|
void update_objects(UNUSED s32 unused) {
|
|
s64 cycleCounts[30];
|
|
|
|
cycleCounts[0] = get_current_clock();
|
|
|
|
gTimeStopState &= ~TIME_STOP_MARIO_OPENED_DOOR;
|
|
|
|
gNumRoomedObjectsInMarioRoom = 0;
|
|
gNumRoomedObjectsNotInMarioRoom = 0;
|
|
gCheckingSurfaceCollisionsForCamera = FALSE;
|
|
|
|
reset_debug_objectinfo();
|
|
stub_802CA5D0();
|
|
|
|
gObjectLists = gObjectListArray;
|
|
|
|
// If time stop is not active, unload object surfaces
|
|
cycleCounts[1] = get_clock_difference(cycleCounts[0]);
|
|
clear_dynamic_surfaces();
|
|
|
|
// Update spawners and objects with surfaces
|
|
cycleCounts[2] = get_clock_difference(cycleCounts[0]);
|
|
update_terrain_objects();
|
|
|
|
// If mario was touching a moving platform at the end of last frame, apply
|
|
// displacement now
|
|
//! If the platform object unloaded and a different object took its place,
|
|
// displacement could be applied incorrectly
|
|
apply_mario_platform_displacement();
|
|
|
|
// Detect which objects are intersecting
|
|
cycleCounts[3] = get_clock_difference(cycleCounts[0]);
|
|
detect_object_collisions();
|
|
|
|
// Update all other objects that haven't been updated yet
|
|
cycleCounts[4] = get_clock_difference(cycleCounts[0]);
|
|
update_non_terrain_objects();
|
|
|
|
// Unload any objects that have been deactivated
|
|
cycleCounts[5] = get_clock_difference(cycleCounts[0]);
|
|
unload_deactivated_objects();
|
|
|
|
// Check if mario is on a platform object and save this object
|
|
cycleCounts[6] = get_clock_difference(cycleCounts[0]);
|
|
update_mario_platform();
|
|
|
|
cycleCounts[7] = get_clock_difference(cycleCounts[0]);
|
|
|
|
cycleCounts[0] = 0;
|
|
try_print_debug_mario_object_info();
|
|
|
|
// If time stop was enabled this frame, activate it now so that it will
|
|
// take effect next frame
|
|
if (gTimeStopState & TIME_STOP_ENABLED) {
|
|
gTimeStopState |= TIME_STOP_ACTIVE;
|
|
} else {
|
|
gTimeStopState &= ~TIME_STOP_ACTIVE;
|
|
}
|
|
|
|
gPrevFrameObjectCount = gObjectCounter;
|
|
}
|