#include "Trophy.h" #include "assets/common_data.h" #include "assets/data_segment2.h" #include "port/Game.h" extern "C" { #include "main.h" #include "defines.h" #include "update_objects.h" #include "code_80057C60.h" #include "assets/ceremony_data.h" #include "podium_ceremony_actors.h" #include "engine/particles/StarEmitter.h" #include "math_util.h" #include "math_util_2.h" #include "data/some_data.h" #include "engine/Matrix.h" #include "render_objects.h" #include "code_80086E70.h" #include "menu_items.h" } OTrophy::OTrophy(const FVector& pos, TrophyType trophy, Behaviour bhv) { Name = "Trophy"; _trophy = trophy; _spawnPos = pos; _spawnPos.y += 16.0f; // Adjust the height so the trophy sits on the surface when positioned to 0,0,0 _bhv = bhv; find_unused_obj_index(&_objectIndex); // If you're trying to place multiple trophies in the podium ceremony // This will make the camera look at the last trophy spawned. // Thus this will need to be changed if that's not desired. gTrophyIndex = _objectIndex; if (bhv == OTrophy::Behaviour::PODIUM_CEREMONY) { _toggleVisibility = &D_801658CE; } else { int8_t toggle = 1; _toggleVisibility = &toggle; _isMod = true; } // This allows spawning for mods if (*_toggleVisibility == true) { init_object(_objectIndex, 0); } switch (trophy) { case TrophyType::GOLD: gObjectList[_objectIndex].model = (Gfx*)gold_trophy_dl10; break; case TrophyType::SILVER: gObjectList[_objectIndex].model = (Gfx*)gold_trophy_dl12; break; case TrophyType::BRONZE: gObjectList[_objectIndex].model = (Gfx*)gold_trophy_dl14; break; case TrophyType::GOLD_150: gObjectList[_objectIndex].model = (Gfx*)gold_trophy_dl11; break; case TrophyType::SILVER_150: gObjectList[_objectIndex].model = (Gfx*)gold_trophy_dl13; break; case TrophyType::BRONZE_150: gObjectList[_objectIndex].model = (Gfx*)gold_trophy_dl15; break; } // Set defaults for modded behaviours if (_bhv != OTrophy::Behaviour::PODIUM_CEREMONY) { gObjectList[_objectIndex].sizeScaling = 0.025f; gObjectList[_objectIndex].unk_084[1] = 0x0200; object_next_state(_objectIndex); func_80086E70(_objectIndex); } switch(_bhv) { case OTrophy::Behaviour::GO_FISH: gObjectList[_objectIndex].sizeScaling = 0.010f; break; } Object *object = &gObjectList[_objectIndex]; object->origin_pos[0] = _spawnPos.x; object->origin_pos[1] = _spawnPos.y; object->origin_pos[2] = _spawnPos.z; object->pos.x = _spawnPos.x; object->pos.y = _spawnPos.y; object->pos.z = _spawnPos.z; _emitter = reinterpret_cast(gWorldInstance.AddEmitter(new StarEmitter())); } void OTrophy::Tick() { // func_80086D80 s32 objectIndex = _objectIndex; s32 var_s0; // Fallback for podium ceremony where the trophy is not spawned until it is needed if ((*_toggleVisibility == true) && (D_801658DC == 0) && (_isMod == false)) { init_object(objectIndex, 0); D_801658DC = 1; } switch(_bhv) { case OTrophy::Behaviour::PODIUM_CEREMONY: if (gObjectList[objectIndex].state != 0 && (*_toggleVisibility == true)) { OTrophy::func_80086C14(objectIndex); OTrophy::func_80086940(objectIndex); if (D_801658F4 != 0) { if (D_8016559C == 0) { OTrophy::func_80086C6C(objectIndex); } } else { for (var_s0 = 0; var_s0 < 2; var_s0++) { OTrophy::func_80086C6C(objectIndex); } } } break; case OTrophy::Behaviour::STATIONARY: if (gObjectList[objectIndex].state != 0) { gObjectList[objectIndex].sizeScaling = 0.025f; set_obj_origin_pos(objectIndex, _spawnPos.x, _spawnPos.y + 16.0, _spawnPos.z); set_obj_origin_offset(objectIndex, 0.0f, 0.0f, 0.0f); set_obj_direction_angle(objectIndex, 0U, 0U, 0U); gObjectList[objectIndex].unk_084[1] = 0x0200; object_next_state(objectIndex); func_80086E70(objectIndex); OTrophy::func_80086940(objectIndex); } break; case OTrophy::Behaviour::ROTATE: if (gObjectList[objectIndex].state != 0) { gObjectList[objectIndex].direction_angle[0] += 0x400; gObjectList[objectIndex].direction_angle[1] -= 0x200; } break; case OTrophy::Behaviour::ROTATE2: if (gObjectList[objectIndex].state != 0) { gObjectList[objectIndex].direction_angle[0] += 0x400; gObjectList[objectIndex].direction_angle[1] = 0xE800; gObjectList[objectIndex].direction_angle[2] = 0xDA00; } break; case OTrophy::Behaviour::GO_FISH: if (gObjectList[objectIndex].state != 0) { // Get the player's yaw Player *player = &gPlayers[0]; float yaw = (player->rotation.y + 0x4000) * (M_PI / 32768.0f); // Convert degrees to radians // Calculate forward direction based on yaw (same as before) float lookAtX = player->pos.x + cos(yaw); float lookAtZ = player->pos.z + sin(yaw); float forwardX = lookAtX - player->pos.x; float forwardY = 0; // Optional: Ignore height changes float forwardZ = lookAtZ - player->pos.z; // Normalize the forward vector float length = sqrtf(forwardX * forwardX + forwardZ * forwardZ); if (length != 0) { forwardX /= length; forwardZ /= length; } float distance = 30.0f; // Calculate the object's position in front of the player (same as before) gObjectList[objectIndex].pos.x = player->pos.x + forwardX * distance; gObjectList[objectIndex].pos.y = player->pos.y + 8.0f; // Optional height offset gObjectList[objectIndex].pos.z = player->pos.z + forwardZ * distance; // Apply more sensitive random movement based on player velocity to simulate floating behavior float velocityFactor = 0.1f; // Increased factor for more sensitivity gObjectList[objectIndex].pos.x += player->velocity.x * velocityFactor; gObjectList[objectIndex].pos.y += player->velocity.y * velocityFactor; // Optional: Add vertical movement gObjectList[objectIndex].pos.z += player->velocity.z * velocityFactor; // Increase oscillation for more dynamic movement (sine wave effect) float oscillationSpeed = 4.0f; // Increased speed for quicker oscillations float oscillationAmplitude = 0.4f; // Increased amplitude for more noticeable movement gObjectList[objectIndex].pos.y += oscillationAmplitude * sinf(oscillationSpeed * gGlobalTimer); // Vertical oscillation based on time // Now use smooth interpolation (lerp) to gradually move the trophy towards its target position float lerpFactor = 0.25f; // Increased to make the trophy follow the player more quickly gObjectList[objectIndex].pos.x = _oldPos[0] + lerpFactor * (gObjectList[objectIndex].pos.x - _oldPos[0]); gObjectList[objectIndex].pos.y = _oldPos[1] + lerpFactor * (gObjectList[objectIndex].pos.y - _oldPos[1]); gObjectList[objectIndex].pos.z = _oldPos[2] + lerpFactor * (gObjectList[objectIndex].pos.z - _oldPos[2]); // Save the current position for the next frame _oldPos[0] = gObjectList[objectIndex].pos.x; _oldPos[1] = gObjectList[objectIndex].pos.y; _oldPos[2] = gObjectList[objectIndex].pos.z; gObjectList[objectIndex].direction_angle[0] += 0x400; gObjectList[objectIndex].direction_angle[1] -= 0x200; } break; } } void OTrophy::Draw(s32 cameraId) { s32 listIndex = _objectIndex; Mat4 someMatrix1; Mat4 someMatrix2; Object* object; if (*_toggleVisibility == true) { object = &gObjectList[listIndex]; if (object->state >= 2) { gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(&gGfxPool->mtxPersp[0]), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION); gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(&gGfxPool->mtxLookAt[0]), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); mtxf_set_matrix_transformation(someMatrix1, object->pos, object->direction_angle, object->sizeScaling); //convert_to_fixed_point_matrix(&gGfxPool->mtxHud[gMatrixHudCount], someMatrix1); //gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(&gGfxPool->mtxHud[gMatrixHudCount++]), // G_MTX_NOPUSH | G_MTX_MUL | G_MTX_MODELVIEW); AddHudMatrix(someMatrix1, G_MTX_NOPUSH | G_MTX_MUL | G_MTX_MODELVIEW); gSPDisplayList(gDisplayListHead++, (Gfx*)D_0D0077A0); gSPDisplayList(gDisplayListHead++, object->model); gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(&gGfxPool->mtxLookAt[0]), G_MTX_NOPUSH | G_MTX_MUL | G_MTX_PROJECTION); mtxf_identity(someMatrix2); render_set_position(someMatrix2, 0); } switch (_bhv) { case GO_FISH: size_t numTrophies = 0; func_80057A50(40, 0, (char*) "Trophies Collected: ", (s16) numTrophies); break; } } } void OTrophy::func_80086700(s32 objectIndex) { gObjectList[objectIndex].sizeScaling = 0.005f; set_obj_origin_pos(objectIndex, gObjectList[indexObjectList2[0]].pos.x, gObjectList[indexObjectList2[0]].pos.y + 16.0, gObjectList[indexObjectList2[0]].pos.z); set_obj_origin_offset(objectIndex, 0.0f, 0.0f, 0.0f); set_obj_direction_angle(objectIndex, 0U, 0U, 0U); gObjectList[objectIndex].unk_084[1] = 0x0200; object_next_state(objectIndex); func_80086E70(objectIndex); } void OTrophy::func_80086940(s32 objectIndex) { Object* object; object = &gObjectList[objectIndex]; switch (object->unk_0AE) { case 0: break; case 1: func_80086FD4(objectIndex); break; case 2: f32_step_towards(&object->sizeScaling, 0.025f, 0.001f); func_80087C48(objectIndex, 6.0f, 0.1f, 0x000000C8); if ((f64) object->velocity.y <= 0.0) { func_8008701C(objectIndex, 3); } break; case 3: func_800871AC(objectIndex, 0x00000064); break; case 4: D_801658D6 = 1; object->velocity.y = -0.4f; func_80086FD4(objectIndex); // object->origin_pos[1] = 90.0f; object->offset[1] = 60.0f; // switch (D_802874D8.unk1D) { // case 1: // object->origin_pos[0] -= 3.0; // object->origin_pos[2] += 15.0; // break; // case 2: // object->origin_pos[0] -= 2.0; // object->origin_pos[2] -= 15.0; // break; // } break; case 5: if ((f64) object->offset[1] <= 8.0) { f32_step_towards(&object->velocity.y, -0.1f, -0.01f); } object_add_velocity_offset_y(objectIndex); if ((f64) object->offset[1] <= 0.0) { func_80086FD4(objectIndex); } break; case 6: if (func_800871AC(objectIndex, 0x00000041) != 0) { D_801658F4 = 1; } break; case 7: if (func_800871AC(objectIndex, 0x00000064) != 0) { func_8009265C(); func_80086F60(objectIndex); } break; } if (D_801658D6 != 0) { object->direction_angle[0] += 0x400; object->direction_angle[1] = 0xE800; object->direction_angle[2] = 0xDA00; } else { object->direction_angle[0] += 0x400; object->direction_angle[1] -= 0x200; } object_calculate_new_pos_offset(objectIndex); } void OTrophy::func_80086C14(s32 objectIndex) { switch (gObjectList[objectIndex].state) { /* irregular */ case 1: OTrophy::func_80086700(objectIndex); break; case 0: case 2: break; } } void OTrophy::func_80086C6C(s32 objectIndex) { Vec3f sp24; sp24[0] = (gObjectList[objectIndex].pos.x - 5.0f) + random_int(0x000AU); sp24[2] = (gObjectList[objectIndex].pos.z - 5.0f) + random_int(0x000AU); if (D_801658F4 != 0) { sp24[1] = gObjectList[objectIndex].pos.y + 14.0; } else { sp24[1] = gObjectList[objectIndex].pos.y - 2.0; } if (_emitter != nullptr) { _emitter->Emit(sp24, D_801658F4); } }