sm64

A Super Mario 64 decompilation
Log | Files | Refs | README | LICENSE

spawn_object.c (10703B)


      1 #include <PR/ultratypes.h>
      2 
      3 #include "audio/external.h"
      4 #include "engine/geo_layout.h"
      5 #include "engine/graph_node.h"
      6 #include "engine/math_util.h"
      7 #include "engine/surface_collision.h"
      8 #include "level_table.h"
      9 #include "object_constants.h"
     10 #include "object_fields.h"
     11 #include "object_helpers.h"
     12 #include "object_list_processor.h"
     13 #include "spawn_object.h"
     14 #include "types.h"
     15 
     16 /**
     17  * An unused linked list struct that seems to have been replaced by ObjectNode.
     18  */
     19 struct LinkedList {
     20     struct LinkedList *next;
     21     struct LinkedList *prev;
     22 };
     23 
     24 /**
     25  * Clear the doubly linked usedList. Singly link each item in the pool into
     26  * a list, and return this list in pFreeList.
     27  * Appears to have been replaced by init_free_object_list.
     28  */
     29 void unused_init_free_list(struct LinkedList *usedList, struct LinkedList **pFreeList,
     30                            struct LinkedList *pool, s32 itemSize, s32 poolLength) {
     31     s32 i;
     32     struct LinkedList *node = pool;
     33 
     34     usedList->next = usedList;
     35     usedList->prev = usedList;
     36 
     37     *pFreeList = pool;
     38 
     39     for (i = 0; i < poolLength - 1; i++) {
     40         // Add next node to free list
     41         node = (struct LinkedList *) ((u8 *) node + itemSize);
     42         pool->next = node;
     43         pool = node;
     44     }
     45 
     46     // End the list
     47     pool->next = NULL;
     48 }
     49 
     50 /**
     51  * Attempt to allocate a node from freeList (singly linked) and append it
     52  * to the end of destList (doubly linked). Return the object, or NULL if
     53  * freeList is empty.
     54  * Appears to have been replaced by try_allocate_object.
     55  */
     56 struct LinkedList *unused_try_allocate(struct LinkedList *destList,
     57                                        struct LinkedList *freeList) {
     58     struct LinkedList *node = freeList->next;
     59 
     60     if (node != NULL) {
     61         // Remove from free list
     62         freeList->next = node->next;
     63 
     64         // Insert at the end of destination list
     65         node->prev = destList->prev;
     66         node->next = destList;
     67         destList->prev->next = node;
     68         destList->prev = node;
     69     }
     70 
     71     return node;
     72 }
     73 
     74 /**
     75  * Attempt to allocate an object from freeList (singly linked) and append it
     76  * to the end of destList (doubly linked). Return the object, or NULL if
     77  * freeList is empty.
     78  */
     79 struct Object *try_allocate_object(struct ObjectNode *destList, struct ObjectNode *freeList) {
     80     struct ObjectNode *nextObj;
     81 
     82     if ((nextObj = freeList->next) != NULL) {
     83         // Remove from free list
     84         freeList->next = nextObj->next;
     85 
     86         // Insert at end of destination list
     87         nextObj->prev = destList->prev;
     88         nextObj->next = destList;
     89         destList->prev->next = nextObj;
     90         destList->prev = nextObj;
     91     } else {
     92         return NULL;
     93     }
     94 
     95     geo_remove_child(&nextObj->gfx.node);
     96     geo_add_child(&gObjParentGraphNode, &nextObj->gfx.node);
     97 
     98     return (struct Object *) nextObj;
     99 }
    100 
    101 /**
    102  * Remove the node from the doubly linked list it's in, and place it in the
    103  * singly linked freeList.
    104  * This function seems to have been replaced by deallocate_object.
    105  */
    106 void unused_deallocate(struct LinkedList *freeList, struct LinkedList *node) {
    107     // Remove from doubly linked list
    108     node->next->prev = node->prev;
    109     node->prev->next = node->next;
    110 
    111     // Insert at beginning of singly linked list
    112     node->next = freeList->next;
    113     freeList->next = node;
    114 }
    115 /**
    116  * Remove the given object from the object list that it's currently in, and
    117  * insert it at the beginning of the free list (singly linked).
    118  */
    119 static void deallocate_object(struct ObjectNode *freeList, struct ObjectNode *obj) {
    120     // Remove from object list
    121     obj->next->prev = obj->prev;
    122     obj->prev->next = obj->next;
    123 
    124     // Insert at beginning of free list
    125     obj->next = freeList->next;
    126     freeList->next = obj;
    127 }
    128 
    129 /**
    130  * Add every object in the pool to the free object list.
    131  */
    132 void init_free_object_list(void) {
    133     s32 i;
    134     s32 poolLength = OBJECT_POOL_CAPACITY;
    135 
    136     // Add the first object in the pool to the free list
    137     struct Object *obj = &gObjectPool[0];
    138     gFreeObjectList.next = (struct ObjectNode *) obj;
    139 
    140     // Link each object in the pool to the following object
    141     for (i = 0; i < poolLength - 1; i++) {
    142         obj->header.next = &(obj + 1)->header;
    143         obj++;
    144     }
    145 
    146     // End the list
    147     obj->header.next = NULL;
    148 }
    149 
    150 /**
    151  * Clear each object list, without adding the objects back to the free list.
    152  */
    153 void clear_object_lists(struct ObjectNode *objLists) {
    154     s32 i;
    155 
    156     for (i = 0; i < NUM_OBJ_LISTS; i++) {
    157         objLists[i].next = &objLists[i];
    158         objLists[i].prev = &objLists[i];
    159     }
    160 }
    161 
    162 /**
    163  * This function looks broken, but it appears to attempt to delete the leaf
    164  * graph nodes under obj and obj's siblings.
    165  */
    166 UNUSED static void unused_delete_leaf_nodes(struct Object *obj) {
    167     struct Object *children;
    168     struct Object *sibling;
    169     struct Object *obj0 = obj;
    170 
    171     if ((children = (struct Object *) obj->header.gfx.node.children) != NULL) {
    172         unused_delete_leaf_nodes(children);
    173     } else {
    174         // No children
    175         mark_obj_for_deletion(obj);
    176     }
    177 
    178     // Probably meant to be !=
    179     while ((sibling = (struct Object *) obj->header.gfx.node.next) == obj0) {
    180         unused_delete_leaf_nodes(sibling);
    181         obj = (struct Object *) sibling->header.gfx.node.next;
    182     }
    183 }
    184 
    185 /**
    186  * Free the given object.
    187  */
    188 void unload_object(struct Object *obj) {
    189     obj->activeFlags = ACTIVE_FLAG_DEACTIVATED;
    190     obj->prevObj = NULL;
    191 
    192     obj->header.gfx.throwMatrix = NULL;
    193     stop_sounds_from_source(obj->header.gfx.cameraToObject);
    194     geo_remove_child(&obj->header.gfx.node);
    195     geo_add_child(&gObjParentGraphNode, &obj->header.gfx.node);
    196 
    197     obj->header.gfx.node.flags &= ~GRAPH_RENDER_BILLBOARD;
    198     obj->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE;
    199 
    200     deallocate_object(&gFreeObjectList, &obj->header);
    201 }
    202 
    203 /**
    204  * Attempt to allocate a new object slot into the given object list, freeing
    205  * an unimportant object if necessary. If this is not possible, hang using an
    206  * infinite loop.
    207  */
    208 struct Object *allocate_object(struct ObjectNode *objList) {
    209     s32 i;
    210     struct Object *obj = try_allocate_object(objList, &gFreeObjectList);
    211 
    212     // The object list is full if the newly created pointer is NULL.
    213     // If this happens, we first attempt to unload unimportant objects
    214     // in order to finish allocating the object.
    215     if (obj == NULL) {
    216         // Look for an unimportant object to kick out.
    217         struct Object *unimportantObj = find_unimportant_object();
    218 
    219         // If no unimportant object exists, then the object pool is exhausted.
    220         if (unimportantObj == NULL) {
    221             // We've met with a terrible fate.
    222             while (TRUE) {
    223             }
    224         } else {
    225             // If an unimportant object does exist, unload it and take its slot.
    226             unload_object(unimportantObj);
    227             obj = try_allocate_object(objList, &gFreeObjectList);
    228             if (gCurrentObject == obj) {
    229                 //! Uh oh, the unimportant object was in the middle of
    230                 //  updating! This could cause some interesting logic errors,
    231                 //  but I don't know of any unimportant objects that spawn
    232                 //  other objects.
    233             }
    234         }
    235     }
    236 
    237     // Initialize object fields
    238 
    239     obj->activeFlags = ACTIVE_FLAG_ACTIVE | ACTIVE_FLAG_UNK8;
    240     obj->parentObj = obj;
    241     obj->prevObj = NULL;
    242     obj->collidedObjInteractTypes = 0;
    243     obj->numCollidedObjs = 0;
    244 
    245 #if IS_64_BIT
    246     for (i = 0; i < 0x50; i++) {
    247         obj->rawData.asS32[i] = 0;
    248         obj->ptrData.asVoidPtr[i] = NULL;
    249     }
    250 #else
    251     // -O2 needs everything until = on the same line
    252     for (i = 0; i < 0x50; i++) obj->rawData.asS32[i] = 0;
    253 #endif
    254 
    255     obj->unused1 = 0;
    256     obj->bhvStackIndex = 0;
    257     obj->bhvDelayTimer = 0;
    258 
    259     obj->hitboxRadius = 50.0f;
    260     obj->hitboxHeight = 100.0f;
    261     obj->hurtboxRadius = 0.0f;
    262     obj->hurtboxHeight = 0.0f;
    263     obj->hitboxDownOffset = 0.0f;
    264     obj->unused2 = 0;
    265 
    266     obj->platform = NULL;
    267     obj->collisionData = NULL;
    268     obj->oIntangibleTimer = -1;
    269     obj->oDamageOrCoinValue = 0;
    270     obj->oHealth = 2048;
    271 
    272     obj->oCollisionDistance = 1000.0f;
    273     if (gCurrLevelNum == LEVEL_TTC) {
    274         obj->oDrawingDistance = 2000.0f;
    275     } else {
    276         obj->oDrawingDistance = 4000.0f;
    277     }
    278 
    279     mtxf_identity(obj->transform);
    280 
    281     obj->respawnInfoType = RESPAWN_INFO_TYPE_NULL;
    282     obj->respawnInfo = NULL;
    283 
    284     obj->oDistanceToMario = 19000.0f;
    285     obj->oRoom = -1;
    286 
    287     obj->header.gfx.node.flags &= ~GRAPH_RENDER_INVISIBLE;
    288     obj->header.gfx.pos[0] = -10000.0f;
    289     obj->header.gfx.pos[1] = -10000.0f;
    290     obj->header.gfx.pos[2] = -10000.0f;
    291     obj->header.gfx.throwMatrix = NULL;
    292 
    293     return obj;
    294 }
    295 
    296 /**
    297  * If the object is close to being on the floor, move it to be exactly on the floor.
    298  */
    299 static void snap_object_to_floor(struct Object *obj) {
    300     struct Surface *surface;
    301 
    302     obj->oFloorHeight = find_floor(obj->oPosX, obj->oPosY, obj->oPosZ, &surface);
    303 
    304     if (obj->oFloorHeight + 2.0f > obj->oPosY && obj->oPosY > obj->oFloorHeight - 10.0f) {
    305         obj->oPosY = obj->oFloorHeight;
    306         obj->oMoveFlags |= OBJ_MOVE_ON_GROUND;
    307     }
    308 }
    309 
    310 /**
    311  * Spawn an object at the origin with the behavior script at virtual address bhvScript.
    312  */
    313 struct Object *create_object(const BehaviorScript *bhvScript) {
    314     s32 objListIndex;
    315     struct Object *obj;
    316     struct ObjectNode *objList;
    317     const BehaviorScript *behavior = bhvScript;
    318 
    319     // If the first behavior script command is "begin <object list>", then
    320     // extract the object list from it
    321     if ((bhvScript[0] >> 24) == 0) {
    322         objListIndex = (bhvScript[0] >> 16) & 0xFFFF;
    323     } else {
    324         objListIndex = OBJ_LIST_DEFAULT;
    325     }
    326 
    327     objList = &gObjectLists[objListIndex];
    328     obj = allocate_object(objList);
    329 
    330     obj->curBhvCommand = bhvScript;
    331     obj->behavior = behavior;
    332 
    333     if (objListIndex == OBJ_LIST_UNIMPORTANT) {
    334         obj->activeFlags |= ACTIVE_FLAG_UNIMPORTANT;
    335     }
    336 
    337     //! They intended to snap certain objects to the floor when they spawn.
    338     //  However, at this point the object's position is the origin. So this will
    339     //  place the object at the floor beneath the origin. Typically this
    340     //  doesn't matter since the caller of this function sets oPosX/Y/Z
    341     //  themselves.
    342     switch (objListIndex) {
    343         case OBJ_LIST_GENACTOR:
    344         case OBJ_LIST_PUSHABLE:
    345         case OBJ_LIST_POLELIKE:
    346             snap_object_to_floor(obj);
    347             break;
    348         default:
    349             break;
    350     }
    351 
    352     return obj;
    353 }
    354 
    355 /**
    356  * Mark an object to be unloaded at the end of the frame.
    357  */
    358 void mark_obj_for_deletion(struct Object *obj) {
    359     //! Same issue as obj_mark_for_deletion
    360     obj->activeFlags = ACTIVE_FLAG_DEACTIVATED;
    361 }