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 }