diff --git a/README.md b/README.md index 590eb5a2..1bf60109 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ All versions are supported, and the US 1.0 version (SHA1 = 0cb115d8716dbbc2922fd As of May 30, 2025, this is our current score: -    Decomp progress: 88.84% +    Decomp progress: 89.02%     Documentation progress: 58.62% @@ -121,16 +121,16 @@ As of May 30, 2025, this is our current score: ``` =================================================================== ADVENTURE ONE (ASM -> C Decompilation) - -------------- 88.84% Complete (89.81% NON_MATCHING) -------------- - # Decompiled functions: 1905 - # GLOBAL_ASM remaining: 46 - # NON_MATCHING functions: 5 + -------------- 89.02% Complete (90.35% NON_MATCHING) -------------- + # Decompiled functions: 1906 + # GLOBAL_ASM remaining: 45 + # NON_MATCHING functions: 6 # NON_EQUIVALENT WIP functions: 16 --------------------------- Game Status --------------------------- Balloons: 41/47, Keys: 4/4, Trophies: 4/5 T.T. Amulets: 4/4, Wizpig Amulets: 4/4 ------------------------------------------------------------------- - We are racing in Spaceport Alpha. (Lap 2/3) + We are racing in Spaceport Alpha. (Lap 3/3) =================================================================== ADVENTURE TWO (Cleanup & Documentation) ------------------------- 58.62% Complete ------------------------- diff --git a/include/structs.h b/include/structs.h index 15f1a60f..ae638fce 100644 --- a/include/structs.h +++ b/include/structs.h @@ -431,11 +431,8 @@ typedef struct LevelHeader { /* 0x0C */ u8 unkC[10]; /* 0x16 */ u8 unk16[10]; /* 0x20 */ s8 *AILevelTable; - /* 0x24 */ u8 pad24[6]; - /* 0x2A */ u8 unk2A; - /* 0x2B */ u8 pad2B[9]; - + /* 0x2A */ s8 unk2A[10]; /* 0x34 */ s16 geometry; /* 0x36 */ s16 collectables; // Objects such as bananas, balloons, etc. /* 0x38 */ s16 skybox; @@ -698,7 +695,10 @@ typedef struct LevelModelSegment { /* 0x0C */ TriangleBatchInfo *batches; /* 0x10 */ s16 *unk10; /* 0x14 */ CollisionNode *unk14; +union { /* 0x18 */ f32 *unk18; +/* 0x18 */ Vec4f *unk18_vec4f; // Used for objects, not levels. +}; /* 0x1C */ s16 numberOfVertices; /* 0x1E */ s16 numberOfTriangles; /* 0x20 */ s16 numberOfBatches; diff --git a/src/hasm/math_util.c b/src/hasm/math_util.c index 9c63e3f0..eafd64a8 100644 --- a/src/hasm/math_util.c +++ b/src/hasm/math_util.c @@ -8,14 +8,14 @@ #include "string.h" #include "PR/os_internal_reg.h" -extern s32 gIntDisFlag; +extern u8 gIntDisFlag; extern s32 gCurrentRNGSeed; // Official Name: rngSeed extern s32 gPrevRNGSeed; extern s16 gSineTable[]; extern s16 gArcTanTable[]; /** - * Most files below are handwritten assembly. Because of this, matching C code is impossible. + * All of the functions below are handwritten assembly. Because of this, matching C code is impossible. * Nonmatching is not, so functionally equivalent C code can be here to replace these handwritten functions in * nonmatching builds. Variables cannot be declared here because of the way they're aligned, so they have to stay in an * assembly file. @@ -29,7 +29,8 @@ extern s16 gArcTanTable[]; * from being interrupted by others, letting you safely * work with delicate areas in memory. Kind of like a mutex. * Returns what the interrupt mask wask before. - * Official Name: disableInterrupts */ + * Official Name: disableInterrupts + */ u32 interrupts_disable(void) { if (gIntDisFlag) { return __osDisableInt(); @@ -44,7 +45,8 @@ GLOBAL_ASM("asm/math_util/disable_interrupts.s") * Set the interrupt mask to whichever flags were given. * Required after zeroing them out, otherwise system * operation won't work as normal. - * Official Name: enableInterrupts */ + * Official Name: enableInterrupts + */ void interrupts_enable(u32 flags) { if (gIntDisFlag) { __osRestoreInt(flags); @@ -55,8 +57,11 @@ GLOBAL_ASM("asm/math_util/enable_interrupts.s") #endif #ifdef NON_MATCHING -/* Official Name: setIntDisFlag */ -void set_gIntDisFlag(s8 setting) { +/** + * Sets the global interrupt disable flag to allow enabling or disabling hardware interrupts for debugging. + * Official Name: setIntDisFlag + */ +void set_gIntDisFlag(u8 setting) { gIntDisFlag = setting; } #else @@ -64,19 +69,22 @@ GLOBAL_ASM("asm/math_util/set_gIntDisFlag.s") #endif #ifdef NON_MATCHING -/* Official Name: getIntDisFlag */ -s8 get_gIntDisFlag(void) { +/** + * Gets the global interrupt disable flag, which indicates whether hardware interrupts are enabled or disabled. + * Official Name: getIntDisFlag + */ +u8 get_gIntDisFlag(void) { return gIntDisFlag; } #else GLOBAL_ASM("asm/math_util/get_gIntDisFlag.s") #endif +#ifdef NON_MATCHING /**  * Converts a Mtx (fixed-point matrix with split integer and fractional parts)  * into a 4×4 matrix of 32-bit signed integers, where each element is in 16.16 fixed-point format.  */ -#ifdef NON_MATCHING UNUSED void mtx_to_mtxs(Mtx *m, MtxS *mi) { s32 i, j; s32 ei, ef; @@ -98,11 +106,11 @@ UNUSED void mtx_to_mtxs(Mtx *m, MtxS *mi) { GLOBAL_ASM("asm/math_util/mtx_to_mtxs.s") #endif +#ifdef NON_MATCHING /**  * Converts a 4×4 matrix of 32-bit floating-point values into a 4×4 matrix  * of 32-bit signed fixed-point values in 16.16 format. */ -#ifdef NON_MATCHING void mtxf_to_mtxs(MtxF *mf, MtxS *mi) { s32 i, j; @@ -119,8 +127,8 @@ GLOBAL_ASM("asm/math_util/mtxf_to_mtxs.s") #ifdef NON_MATCHING /** * Transforms a 3D vector using a 4×4 transformation matrix. + * Official name: mathMtxXFMF */ -/* Official name: mathMtxXFMF */ void mtxf_transform_point(MtxF *mf, float x, float y, float z, float *ox, float *oy, float *oz) { *ox = (*mf)[0][0] * x + (*mf)[1][0] * y + (*mf)[2][0] * z + (*mf)[3][0]; *oy = (*mf)[0][1] * x + (*mf)[1][1] * y + (*mf)[2][1] * z + (*mf)[3][1]; @@ -136,8 +144,8 @@ GLOBAL_ASM("asm/math_util/mtxf_transform_point.s") * This function multiplies the input vector by the upper-left 3×3 portion of the matrix mf, * ignoring the translation component. It is used for transforming directions, such as normals, * rather than points. + * Official name: mathMtxFastXFMF */ -/* Official name: mathMtxFastXFMF */ void mtxf_transform_dir(MtxF *mf, Vec3f *in, Vec3f *out) { out->f[0] = (in->f[0] * mf[0][0]) + (in->f[1] * mf[1][0]) + (in->f[2] * mf[2][0]); out->f[1] = (in->f[0] * mf[0][1]) + (in->f[1] * mf[1][1]) + (in->f[2] * mf[2][1]); @@ -150,8 +158,8 @@ GLOBAL_ASM("asm/math_util/mtxf_transform_dir.s") #ifdef NON_MATCHING /** * Multiplies two 4×4 matrices. + * Official name: mathMtxCatF */ -/* Official name: mathMtxCatF */ void mtxf_mul(MtxF *mat1, MtxF *mat2, MtxF *output) { s32 i, j, k; @@ -176,8 +184,8 @@ GLOBAL_ASM("asm/math_util/mtxf_mul.s") #ifdef NON_MATCHING /** * Converts a floating-point 4×4 matrix to a Mtx fixed-point matrix. + * Official name: mathMtxF2L */ -/* Official name: mathMtxF2L */ void mtxf_to_mtx(MtxF *mf, Mtx *m) { s32 i, j; s32 e1, e2; @@ -186,13 +194,14 @@ void mtxf_to_mtx(MtxF *mf, Mtx *m) { ai = &m->m[0][0]; af = &m->m[2][0]; - for (i = 0; i < 4; i++) + for (i = 0; i < 4; i++) { for (j = 0; j < 4; j += 2) { e1 = FTOFIX32((*mf)[i][j]); e2 = FTOFIX32((*mf)[i][j + 1]); *ai++ = (e1 & 0xFFFF0000) | ((e2 >> 16) & 0xFFFF); *af++ = ((e1 << 16) & 0xFFFF0000) | (e2 & 0xFFFF); } + } } } #else @@ -227,8 +236,8 @@ s32 get_rng_seed(void) { #ifdef NON_MATCHING /** * Generates a random integer within the inclusive range [min, max]. + * Official Name: mathRnd */ -/* Official Name: mathRnd */ s32 rand_range(s32 min, s32 max) { s64 temp = gCurrentRNGSeed; @@ -249,9 +258,8 @@ GLOBAL_ASM("asm/math_util/rng.s")  *  * The normal vector must be normalized and represented in 3.13 fixed-point format (signed 16-bit,  * with 13 fractional bits). - * + * Official name: fastShortReflection */ -/* Official name: fastShortReflection */ void vec3s_reflect(Vec3s *vec, Vec3s *n) { s32 proj_x2 = (vec->x * n->x + vec->y * n->y + vec->z * n->z) >> 12; @@ -391,8 +399,8 @@ GLOBAL_ASM("asm/math_util/mtxf_scale_y.s") * Modifies the matrix by translating its position along the local Y axis. * If this is a model matrix, the operation is equivalent to moving the model  * along its local Y axis in model space. + * Official name: mathTransY */ -/* Official name: mathTransY */ void mtxf_translate_y(MtxF *input, f32 offset) { (*input)[3][0] += (*input)[1][0] * offset; (*input)[3][1] += (*input)[1][1] * offset; @@ -416,8 +424,9 @@ GLOBAL_ASM("asm/math_util/mtxf_translate_y.s") * 2. Rotate Y (negative yaw) * 3. Rotate X (negative pitch) * 4. Rotate Z (negative roll) + * + * Official Name: mathRpyXyzMtx */ -/* Official Name: mathRpyXyzMtx */ void mtxf_from_inverse_transform(MtxF *mtx, ObjectTransform *trans) { f32 yRotSine; f32 yRotCosine; @@ -540,8 +549,8 @@ GLOBAL_ASM("asm/math_util/vec3s_rotate_rpy.s") /** * Rotates the given vector according to the specified rotation angles. * The result is written back into the same vector. + * Official Name: mathOneFloatRPY */ -/* Official Name: mathOneFloatRPY */ void vec3f_rotate(Vec3s *rotation, Vec3f *vec) { f32 sine; f32 cosine; @@ -584,8 +593,8 @@ GLOBAL_ASM("asm/math_util/vec3f_rotate.s") * Unlike the standard roll-pitch-yaw (Z-X-Y) order, this applies the angles in yaw-pitch-roll (Y-X-Z) order. * To fully reverse the effect of vec3f_rotate, the input angles must also be negated. * The result is written back into the same vector. + * Official Name: mathOneFloatYPR */ -/* Official Name: mathOneFloatYPR */ void vec3f_rotate_ypr(Vec3s *rotation, Vec3f *vec) { f32 sine; f32 cosine; @@ -629,8 +638,8 @@ GLOBAL_ASM("asm/math_util/vec3f_rotate_ypr.s") * The roll angle is also ignored, as it has no effect on directional vectors. * This is typically used to compute a direction vector from pitch and yaw angles. * The result is written back into the same vector. + * Official Name: mathOneFloatPY */ -/* Official Name: mathOneFloatPY */ void vec3f_rotate_py(Vec3s *rotation, Vec3f *vec) { f32 sinX; f32 cosX; @@ -657,8 +666,8 @@ GLOBAL_ASM("asm/math_util/vec3f_rotate_py.s") /** * Determines whether a point lies inside a triangle, projected onto the XZ plane. * Points lying exactly on the triangle's edges are not considered inside. + * Official Name: mathXZInTri */ -/* Official Name: mathXZInTri */ s32 tri2d_xz_contains_point(s32 x, s32 z, Vec3s *pointA, Vec3s *pointB, Vec3s *pointC) { s32 aX, aZ, bX, bZ, cX, cZ; s32 var_a1; @@ -684,8 +693,8 @@ GLOBAL_ASM("asm/math_util/tri2d_xz_contains_point.s") #ifdef NON_MATCHING /** * Creates a translation matrix that moves points by the specified (x, y, z) offset. + * Official Name: mathTranslateMtx */ -/* Official Name: mathTranslateMtx */ void mtxf_from_translation(MtxF *mtx, f32 x, f32 y, f32 z) { s32 i, j; @@ -710,8 +719,8 @@ GLOBAL_ASM("asm/math_util/mtxf_from_translation.s") #ifdef NON_MATCHING /** * Creates a scaling matrix with the specified scale factors along the X, Y, and Z axes. + * Official Name: mathScaleMtx */ -/* Official Name: mathScaleMtx */ void mtxf_from_scale(MtxF *mtx, f32 scaleX, f32 scaleY, f32 scaleZ) { s32 i, j; diff --git a/src/math_util.h b/src/math_util.h index 5abe606b..7fd21cee 100644 --- a/src/math_util.h +++ b/src/math_util.h @@ -49,17 +49,17 @@ void mtxf_transform_dir(MtxF *mf, Vec3f *in, Vec3f *out); void mtxf_mul(MtxF *mat1, MtxF *mat2, MtxF *output); void mtxf_to_mtx(MtxF *input, Mtx *output); void vec3s_reflect(Vec3s *vec, Vec3s *n); -void mtxs_transform_dir(MtxS *input, Vec3s *output); +void mtxs_transform_dir(MtxS *mi, Vec3s *vec); void mtxf_from_transform(MtxF *mtx, ObjectTransform *trans); void mtxf_scale_y(MtxF *input, f32 scale); void mtxf_translate_y(MtxF *input, f32 offset); void mtxf_from_inverse_transform(MtxF *mtx, ObjectTransform *trans); void mtxf_billboard(MtxF *mtx, s32 angle, f32 scale, f32 scaleY); -void vec3s_rotate_rpy(RPYAngles *rotation, Vec3s *vec3Arg); +void vec3s_rotate_rpy(RPYAngles *rotation, Vec3s *vec); void vec3f_rotate(Vec3s *rotation, Vec3f *vec); void vec3f_rotate_ypr(Vec3s *rotation, Vec3f *vec); void vec3f_rotate_py(Vec3s *rotation, Vec3f *vec); -s32 tri2d_xz_contains_point(s32 x, s32 z, Vec3s *vec3A, Vec3s *vec3B, Vec3s *vec3C); +s32 tri2d_xz_contains_point(s32 x, s32 z, Vec3s *pointA, Vec3s *pointB, Vec3s *pointC); void mtxf_from_translation(MtxF *mtx, f32 x, f32 y, f32 z); void mtxf_from_scale(MtxF *mtx, f32 scaleX, f32 scaleY, f32 scaleZ); s32 atan2s(s32 xDelta, s32 zDelta); diff --git a/src/menu.h b/src/menu.h index ab1b1607..404bec8d 100644 --- a/src/menu.h +++ b/src/menu.h @@ -679,7 +679,7 @@ s32 menu_title_screen_loop(s32 updateRate); s32 menu_magic_codes_loop(s32 updateRate); s32 menu_credits_loop(s32 updateRate); void func_8007FFEC(s32 arg0); -void set_gIntDisFlag(s8 setting); +void set_gIntDisFlag(u8 setting); void init_save_data(void); void func_80080580(Gfx **dList, s32 startX, s32 startY, s32 width, s32 height, s32 borderWidth, s32 borderHeight, s32 colour, TextureHeader *tex); diff --git a/src/objects.c b/src/objects.c index 2a63b6f2..aea7f15f 100644 --- a/src/objects.c +++ b/src/objects.c @@ -1737,7 +1737,9 @@ UNUSED s32 func_8000E558(Object *arg0) { return TRUE; } +// https://decomp.me/scratch/QHVHG #pragma GLOBAL_ASM("asm/nonmatchings/objects/func_8000E5EC.s") +// https://decomp.me/scratch/btRnW #pragma GLOBAL_ASM("asm/nonmatchings/objects/func_8000E79C.s") UNUSED u8 *func_8000E898(u8 *arg0, s32 arg1) { @@ -2425,6 +2427,7 @@ void gParticlePtrList_flush(void) { gFreeListCount = 0; } +// https://decomp.me/scratch/bJM0P #pragma GLOBAL_ASM("asm/nonmatchings/objects/func_800101AC.s") void obj_update(s32 updateRate) { @@ -3749,6 +3752,7 @@ void render_racer_magnet(Gfx **dList, Mtx **mtx, Vertex **vtxList, Object *obj) } } +// https://decomp.me/scratch/0fEHd #pragma GLOBAL_ASM("asm/nonmatchings/objects/func_80014090.s") /** @@ -4487,6 +4491,7 @@ void func_8001709C(Object *obj) { obj5C->unk100 = NULL; } +// https://decomp.me/scratch/qYswd #pragma GLOBAL_ASM("asm/nonmatchings/objects/func_80017248.s") unk800179D0 *func_8001790C(u32 *arg0, u32 *arg1) { @@ -4709,6 +4714,7 @@ void func_80017E98(void) { } } +// https://decomp.me/scratch/xQbet #pragma GLOBAL_ASM("asm/nonmatchings/objects/func_800185E4.s") /** @@ -4727,6 +4733,7 @@ Object *find_taj_object(void) { return NULL; } +// https://decomp.me/scratch/MTL0u #pragma GLOBAL_ASM("asm/nonmatchings/objects/func_80018CE0.s") // Rocket Path @@ -4788,6 +4795,7 @@ s32 func_8001955C(Object *obj, s32 checkpoint, u8 arg2, s32 arg3, s32 arg4, f32 return TRUE; } +// https://decomp.me/scratch/QupaR #pragma GLOBAL_ASM("asm/nonmatchings/objects/func_80019808.s") /** @@ -5739,6 +5747,7 @@ s32 ainode_find_nearest(f32 diffX, f32 diffY, f32 diffZ, s32 useElevation) { return result; } +// https://decomp.me/scratch/cfVAM #pragma GLOBAL_ASM("asm/nonmatchings/objects/func_8001C6C4.s") s32 ainode_find_next(s32 nodeId, s32 arg1, s32 direction) { @@ -6486,6 +6495,7 @@ void func_8001E89C(void) { } } +// https://decomp.me/scratch/G2ZOW #pragma GLOBAL_ASM("asm/nonmatchings/objects/func_8001E93C.s") void func_8001EE74(void) { diff --git a/src/racer.c b/src/racer.c index 6550a313..f086b27b 100644 --- a/src/racer.c +++ b/src/racer.c @@ -167,7 +167,7 @@ s16 gGhostMapID; // Previous MapId? s8 gRacerWaveCount; s8 D_8011D5AF; WaterProperties **gRacerCurrentWave; -s32 D_8011D5B4; +s8 D_8011D5B4[4]; s16 D_8011D5B8; /******************************/ @@ -656,7 +656,376 @@ s32 roll_percent_chance(s32 chance) { return rand_range(0, 99) < chance; } +#ifdef NON_MATCHING +#define FAKEMATCH +// Handles the opponent A.I. for battle & banana challenges. +void func_8004447C(Object *aiRacerObj, Object_Racer *aiRacer, s32 updateRate) { + Object *sp74; + Object_64 *tempRacer; + LevelObjectEntry *sp6C; + s32 var_v1; + f32 xDiff; // sp64 + f32 zDiff; // sp60 + f32 dist; // sp5C + s32 temp; // sp58 + s32 someBool; + s16 i; // sp52 + s16 index; // sp50 + s16 sp4E; + s16 sp4C; + s16 sp4A; + s16 sp48; + s16 sp46; + LevelHeader *levelHeader; + s8 raceType; + s8 *sp38; + Object *tempRacerObj; + +#ifdef FAKEMATCH + if ((!tempRacerObj->unk64) && (!tempRacerObj->unk64)) {} // Fake +#endif + + gCurrentButtonsPressed = 0; + gCurrentButtonsReleased = 0; + gCurrentRacerInput = 0; + gCurrentStickX = 0; + gCurrentStickY = 0; + get_racer_objects(&temp); // temp = Number of racers + if (temp != 4) { + return; + } + + levelHeader = get_current_level_header(); + raceType = levelHeader->race_type; + sp38 = levelHeader->unk2A; + if (aiRacer->unk1CD == 0) { + temp = ainode_find_nearest(aiRacerObj->segment.trans.x_position, aiRacerObj->segment.trans.y_position, + aiRacerObj->segment.trans.z_position, 0); + if (temp != 0xFF) { + aiRacer->unk154 = ainode_get(temp); + aiRacer->unk1CD = 1; + aiRacer->unk1CE = 0xFF; + } + } + sp74 = aiRacer->unk154; + if (sp74 != NULL) { + sp6C = sp74->segment.level_entry; + xDiff = sp74->segment.trans.x_position - aiRacerObj->segment.trans.x_position; + zDiff = sp74->segment.trans.z_position - aiRacerObj->segment.trans.z_position; + dist = sqrtf((xDiff * xDiff) + (zDiff * zDiff)); + if (dist > 0.0) { + temp = ((arctan2_f(xDiff, zDiff)) - 0x8000) & 0xFFFF; + var_v1 = temp - (aiRacer->steerVisualRotation & 0xFFFF); + if (var_v1 > 0x8000) { + var_v1 -= 0xFFFF; + } + if (var_v1 < -0x8000) { + var_v1 += 0xFFFF; + } + gCurrentStickX = -var_v1 >> 4; + } + } + if (gRaceStartTimer != 0) { + aiRacer->unk1C6 = 0; + } + if ((aiRacer->unk1CD == 2) || (aiRacer->unk1CD == 4) || (aiRacer->unk1CD == 5)) { + if (aiRacer->unk1C6 > 0) { + aiRacer->unk1C6 -= updateRate; + } else { + aiRacer->unk1C6 = 0; + } + } + aiRacer->elevation = obj_elevation(aiRacerObj->segment.trans.y_position); + switch (aiRacer->unk1CD) { + case 1: + rand_range(0, 9); + if (aiRacer->balloon_quantity == 0) { + if (roll_percent_chance(sp38[3]) != 0) { + aiRacer->unk1CD = 3; + } else if ((roll_percent_chance(sp38[6]) != 0) && (raceType == RACETYPE_CHALLENGE_BANANAS) && + (aiRacer->bananas >= 2)) { + aiRacer->unk1CD = 7; + } else if ((roll_percent_chance(sp38[5]) != 0) && (D_8011D58C[aiRacer->racerIndex] != 0)) { + aiRacer->unk1C6 = 1200; + aiRacer->eggHudCounter = D_8011D58C[aiRacer->racerIndex] - 1; + aiRacer->unk1CD = 5; + } else { + aiRacer->unk1CD = 2; + aiRacer->unk1C6 = 300; + } + } else if ((roll_percent_chance(sp38[6]) != 0) && (raceType == RACETYPE_CHALLENGE_BANANAS) && + (aiRacer->bananas >= 2)) { + aiRacer->unk1CD = 7; + } else if (roll_percent_chance(sp38[0]) != 0) { + if (roll_percent_chance(50) != 0) { + sp4C = 1; + } else { + sp4C = 0; + } + if (roll_percent_chance(sp38[1]) != 0) { + sp46 = 0; + } else { + sp46 = 1; + } + sp4A = -1; + sp48 = sp46 << 4; + for (i = 0; i < 4; i++) { + if (sp4C != 0) { + index = 3 - i; + } else { + index = i; + } + if ((D_8011D58C[index] == 0) && (index != aiRacer->racerIndex)) { + tempRacerObj = get_racer_object(index); + tempRacer = tempRacerObj->unk64; + if (sp46 == 0) { + if (sp48 < tempRacer->racer.bananas) { +#ifdef FAKEMATCH + if (tempRacerObj->segment.trans.y_position) {} // Fake +#endif + sp48 = tempRacer->racer.bananas; + sp4A = index; + } + } else { + if ((tempRacer->racer.bananas > 0) && (tempRacer->racer.bananas < sp48)) { + sp48 = tempRacer->racer.bananas; + sp4A = index; + } + } + } + } + if (roll_percent_chance(sp38[2]) != 0) { + if (cam_get_viewport_layout() == 0) { + tempRacerObj = get_racer_object(index); + tempRacer = tempRacerObj->unk64; + if (tempRacer->racer.bananas > 0) { + sp4A = 0; + } + } + } + if (sp4A >= 0) { + aiRacer->eggHudCounter = (s8) sp4A; + D_8011D58C[sp4A] = aiRacer->racerIndex + 1; + aiRacer->unk1CD = 4; + aiRacer->unk1C6 = 0x4B0; + } else { + aiRacer->unk1CD = 2; + aiRacer->unk1C6 = 0x4B0; + } + } else { + aiRacer->unk1CD = 2; + aiRacer->unk1C6 = 0x12C; + } + break; + case 2: + case 3: + case 4: + case 5: + case 7: + switch (aiRacer->unk1CD) { + case 2: + if (aiRacer->unk1C6 == 0) { + aiRacer->unk1CD = 1; + } + break; + case 3: + if (aiRacer->balloon_type != 0) { + aiRacer->unk1CD = 2; + aiRacer->unk1C6 = 180; + } + break; + case 4: + if ((aiRacer->balloon_quantity == 0) || (aiRacer->unk1C6 == 0)) { + aiRacer->unk1CD = 1; + D_8011D58C[aiRacer->eggHudCounter] = 0; + } + break; + case 5: + if ((D_8011D58C[aiRacer->racerIndex] == 0) || (aiRacer->unk1C6 == 0)) { + aiRacer->unk1CD = 1; + } + break; + case 7: + if (aiRacer->bananas == 0) { + aiRacer->unk1CD = 1; + } + break; + } + if (aiRacer->unk1CE != 0xFF) { + sp4E = aiRacer->elevation; + tempRacerObj = + ainode_get(aiRacer->unk1CE); // I'm assuming this is a Ai Node (Take with a grain of salt!) + raceType = tempRacerObj->segment.level_entry->aiNode.elevation; + someBool = FALSE; + if (sp6C->aiNode.elevation < raceType) { + if ((raceType < sp4E) || (sp4E < sp6C->aiNode.elevation)) { + someBool = TRUE; + } + } else { +#ifdef FAKEMATCH + if (!(&sp74->segment)) {} // Fake +#endif + if ((sp6C->aiNode.elevation < sp4E) || (sp4E < raceType)) { + someBool = TRUE; + } + } + if (someBool) { + if (aiRacer->unk1CD == 4) { + D_8011D58C[aiRacer->eggHudCounter] = 0; + } + aiRacer->unk1CD = 6; + } + } + if ((gCurrentStickX > -30) && (gCurrentStickX < 30)) { + if (aiRacer->velocity > -10.0) { + gCurrentRacerInput = A_BUTTON; + } + } else { + if (aiRacer->velocity > -4.0) { + gCurrentRacerInput = (A_BUTTON | B_BUTTON); + } else { + gCurrentRacerInput = B_BUTTON; + } + if (aiRacer->velocity > -1.0) { + gCurrentRacerInput = A_BUTTON; + } + } + if (aiRacer->nodeCurrent != NULL) { + xDiff = aiRacer->nodeCurrent->segment.trans.x_position - sp74->segment.trans.x_position; + zDiff = aiRacer->nodeCurrent->segment.trans.z_position - sp74->segment.trans.z_position; + if (sqrtf((xDiff * xDiff) + (zDiff * zDiff)) > 0.0) { + temp = (arctan2_f(xDiff, zDiff) - 0x8000) & 0xFFFF; + var_v1 = temp - (aiRacer->steerVisualRotation & 0xFFFF); + if (var_v1 > 0x8000) { + var_v1 -= 0xFFFF; + } + if (var_v1 < -0x8000) { + var_v1 += 0xFFFF; + } + if ((var_v1 > 0x1500) || (var_v1 < -0x1500)) { + if (aiRacer->velocity > -4.0) { + gCurrentRacerInput = (A_BUTTON | B_BUTTON); + } else { + gCurrentRacerInput = B_BUTTON; + } + } + } + } + if (dist < 300.0) { + if (aiRacer->nodeCurrent == NULL) { + switch (aiRacer->unk1CD) { + case 3: + temp = func_8001CD28(sp6C->animation.x_rotation, 1, aiRacer->unk1CE, aiRacer->racerIndex); + break; + case 4: + tempRacerObj = get_racer_object(aiRacer->eggHudCounter); + tempRacer = tempRacerObj->unk64; +#ifdef FAKEMATCH + if (tempRacerObj->unk64->racer.playerIndex == -1) { +#else + if (tempRacer->racer.playerIndex == -1) { +#endif + temp = func_8001CD28( + sp6C->animation.x_rotation, + tempRacer->racer.unk154->segment.level_entry->animation.x_rotation | 0x100, + aiRacer->unk1CE, aiRacer->racerIndex); + } else { + temp = ainode_find_nearest(tempRacerObj->segment.trans.x_position, + tempRacerObj->segment.trans.y_position, + tempRacerObj->segment.trans.z_position, 0); + temp = func_8001CD28(sp6C->animation.x_rotation, temp | 0x100, aiRacer->unk1CE, + aiRacer->racerIndex); + } + break; + case 5: + temp = func_8001CD28(sp6C->animation.x_rotation, 1, aiRacer->unk1CE, aiRacer->racerIndex); + break; + case 7: + temp = func_8001CD28(sp6C->animation.x_rotation, aiRacer->racerIndex + 4, aiRacer->unk1CE, + aiRacer->racerIndex); + break; + default: +#ifdef FAKEMATCH + temp = ainode_find_next(sp6C->animation.x_rotation, aiRacer->unk1CE, + aiRacer->racerIndex & 0xFFFFFFFFFFFFFFFFu); // Fake +#else + temp = ainode_find_next(sp6C->animation.x_rotation, aiRacer->unk1CE, aiRacer->racerIndex); +#endif + break; + } + + if (temp != 0xFF) { + aiRacer->nodeCurrent = ainode_get(temp); + } else { + if (aiRacer->unk1CE != 0xFF) { + aiRacer->nodeCurrent = ainode_get(aiRacer->unk1CE); + } else { + aiRacer->nodeCurrent = NULL; + } + } + } + } + if (dist < 50.0 || (dist > 320.0 && aiRacer->nodeCurrent != NULL)) { + aiRacer->unk1CE = sp6C->animation.x_rotation; + aiRacer->unk154 = aiRacer->nodeCurrent; + if (aiRacer->nodeCurrent == NULL) { + aiRacer->unk1CD = 0; + } + aiRacer->nodeCurrent = NULL; + } + break; + case 6: + temp = ainode_find_nearest(aiRacerObj->segment.trans.x_position, aiRacerObj->segment.trans.y_position, + aiRacerObj->segment.trans.z_position, 2); + if (temp != 0xFF) { + aiRacer->unk154 = ainode_get(temp); + aiRacer->unk1CD = 1; + aiRacer->unk1CE = 0xFF; + } + aiRacer->unk1C9 = 1; + break; + } + for (i = 0; i < 4; i++) { + if (i != aiRacer->racerIndex) { + tempRacerObj = get_racer_object(i); +#ifdef FAKEMATCH + if (tempRacerObj->unk64->racer.playerIndex != -1) { + D_8011D5B4[i] = tempRacerObj->unk64->racer.elevation; + } +#else + tempRacer = tempRacerObj->unk64; + if (tempRacer->racer.playerIndex != -1) { + D_8011D5B4[i] = tempRacer->racer.elevation; + } +#endif + if (D_8011D5B4[aiRacer->racerIndex] == D_8011D5B4[i]) { + xDiff = aiRacerObj->segment.trans.x_position - tempRacerObj->segment.trans.x_position; + zDiff = aiRacerObj->segment.trans.z_position - tempRacerObj->segment.trans.z_position; + if (sqrtf((xDiff * xDiff) + (zDiff * zDiff)) < 800.0) { + temp = arctan2_f(xDiff, zDiff); + temp -= (aiRacerObj->segment.trans.rotation.y_rotation & 0xFFFF); + if (temp > 0x8000) { + temp = -0xFFFF; + } + if (temp < -0x8000) { + temp = 0xFFFF; + } + if (aiRacer->balloon_level == 1) { + var_v1 = 0x1000; + } else { + var_v1 = 0x800; + } + if ((-var_v1 < temp) && (temp < var_v1)) { + gCurrentButtonsReleased |= Z_TRIG; + } + } + } + } + } +} +#else #pragma GLOBAL_ASM("asm/nonmatchings/racer/func_8004447C.s") +#endif void func_80045128(Object **racerObjs) { Object_Racer *obj; @@ -674,6 +1043,7 @@ void func_80045128(Object **racerObjs) { } } +// https://decomp.me/scratch/HSAE4 #pragma GLOBAL_ASM("asm/nonmatchings/racer/func_800452A0.s") #ifdef NON_EQUIVALENT @@ -1857,6 +2227,7 @@ f32 rotate_racer_in_water(Object *obj, Object_Racer *racer, Vec3f *pos, s8 arg3, return velocity; } +// https://decomp.me/scratch/ZIUaE #pragma GLOBAL_ASM("asm/nonmatchings/racer/func_80049794.s") /** @@ -2158,6 +2529,7 @@ void update_camera_plane(f32 updateRate, Object *obj, Object_Racer *racer) { #pragma GLOBAL_ASM("asm/nonmatchings/racer/update_camera_plane.s") #endif +// https://decomp.me/scratch/yfGqf #pragma GLOBAL_ASM("asm/nonmatchings/racer/func_8004CC20.s") /** @@ -6706,9 +7078,9 @@ s16 timetrial_ghost_full(void) { #ifdef NON_EQUIVALENT // timetrial_ghost_read s32 set_ghost_position_and_rotation(Object *obj) { - f32 vectorX[3]; - f32 vectorY[3]; - f32 vectorZ[3]; + f32 vectorX[4]; + f32 vectorY[4]; + f32 vectorZ[4]; GhostNode *nextGhostNode; GhostNode *ghostData; GhostNode *curGhostNode; @@ -6720,7 +7092,7 @@ s32 set_ghost_position_and_rotation(Object *obj) { s32 ghostDataIndex; s32 nodeIndex; s32 rotDiff; - s32 rot; + s16 rot; s32 i; ghostDataIndex = (gCurrentGhostIndex + 1) & 1; @@ -6745,24 +7117,26 @@ s32 set_ghost_position_and_rotation(Object *obj) { nodeIndex = commonUnk0s32 - 1; curGhostNode = &ghostData[nodeIndex]; +#define DOUBLE(x) ((x) + (x)) // This whole loop is a bit of a mystery still... The i < 4 is a complete guess... - for (i = 0; i < 3; i++) { + for (i = 0; i < 4; i++) { if (nodeIndex == -1) { - vectorX[i] = ((curGhostNode + 1)->x * 2) - (curGhostNode + 2)->x; - vectorY[i] = ((curGhostNode + 1)->y * 2) - (curGhostNode + 2)->y; - vectorZ[i] = ((curGhostNode + 1)->z * 2) - (curGhostNode + 2)->z; + vectorX[i] = DOUBLE(ghostData[nodeIndex + 1].x) - ghostData[nodeIndex + 2].x; + vectorY[i] = DOUBLE(ghostData[nodeIndex + 1].y) - ghostData[nodeIndex + 2].y; + vectorZ[i] = DOUBLE(ghostData[nodeIndex + 1].z) - ghostData[nodeIndex + 2].z; } else if (nodeIndex >= ghostNodeCount) { - vectorX[i] = (curGhostNode->x * 2) - (curGhostNode - 1)->x; - vectorY[i] = (curGhostNode->y * 2) - (curGhostNode - 1)->y; - vectorZ[i] = (curGhostNode->z * 2) - (curGhostNode - 1)->z; + vectorX[i] = DOUBLE(ghostData[nodeIndex].x) - ghostData[nodeIndex - 1].x; + vectorY[i] = DOUBLE(ghostData[nodeIndex].y) - ghostData[nodeIndex - 1].y; + vectorZ[i] = DOUBLE(ghostData[nodeIndex].z) - ghostData[nodeIndex - 1].z; } else { - vectorX[i] = curGhostNode->x; - vectorY[i] = curGhostNode->y; - vectorZ[i] = curGhostNode->z; + vectorX[i] = ghostData[nodeIndex].x; + vectorY[i] = ghostData[nodeIndex].y; + vectorZ[i] = ghostData[nodeIndex].z; } nodeIndex++; curGhostNode++; } +#undef DOUBLE catmullX = commonUnk0f32 - commonUnk0s32; obj->segment.trans.x_position = catmull_rom_interpolation(vectorX, 0, catmullX); @@ -6770,10 +7144,13 @@ s32 set_ghost_position_and_rotation(Object *obj) { obj->segment.trans.z_position = catmull_rom_interpolation(vectorZ, 0, catmullX); curGhostNode = &ghostData[commonUnk0s32]; + nextGhostNode = &ghostData[commonUnk0s32 + 1]; + + // It seems important to have a reference to the next ghost node before the first usage of the current one. + if (nextGhostNode) {} // Y Rotation rot = curGhostNode->yRotation; - nextGhostNode = curGhostNode + 1; rotDiff = nextGhostNode->yRotation - (rot & 0xFFFF); if (rotDiff > 0x8000) { rotDiff -= 0xFFFF; @@ -6781,11 +7158,10 @@ s32 set_ghost_position_and_rotation(Object *obj) { if (rotDiff < -0x8000) { rotDiff += 0xFFFF; } - obj->segment.trans.rotation.y_rotation = rot + (s32) (rotDiff * catmullX); + obj->segment.trans.rotation.y_rotation = rot + (s32) (rotDiff * (commonUnk0f32 - commonUnk0s32)); // X Rotation rot = curGhostNode->xRotation; - nextGhostNode = curGhostNode + 1; rotDiff = nextGhostNode->xRotation - (rot & 0xFFFF); if (rotDiff > 0x8000) { rotDiff -= 0xFFFF; @@ -6793,11 +7169,10 @@ s32 set_ghost_position_and_rotation(Object *obj) { if (rotDiff < -0x8000) { rotDiff += 0xFFFF; } - obj->segment.trans.rotation.x_rotation = rot + (s32) (rotDiff * catmullX); + obj->segment.trans.rotation.x_rotation = rot + (s32) (rotDiff * (commonUnk0f32 - commonUnk0s32)); // Z Rotation rot = curGhostNode->zRotation; - nextGhostNode = curGhostNode + 1; rotDiff = nextGhostNode->zRotation - (rot & 0xFFFF); if (rotDiff > 0x8000) { rotDiff -= 0xFFFF; @@ -6805,7 +7180,9 @@ s32 set_ghost_position_and_rotation(Object *obj) { if (rotDiff < -0x8000) { rotDiff += 0xFFFF; } - obj->segment.trans.rotation.z_rotation = rot + (s32) (rotDiff * catmullX); + obj->segment.trans.rotation.z_rotation = rot + (s32) (rotDiff * (commonUnk0f32 - commonUnk0s32)); + + if ((catmullX)) {} obj->particleEmittersEnabled = OBJ_EMIT_NONE; obj->segment.object.segmentID = get_level_segment_index_from_position( diff --git a/src/racer.h b/src/racer.h index fc92c734..77591de0 100644 --- a/src/racer.h +++ b/src/racer.h @@ -228,7 +228,7 @@ s32 func_80017248(void*, s32, s32*, Vec3f*, f32*, f32*, s8* surface); void func_80059208(Object* obj, Object_Racer* racer, s32 updateRate); /* extern */ void func_80049794(s32 updateRate, f32 updateRateF, Object* obj, Object_Racer* racer); /* extern */ void func_8004CC20(s32 updateRate, f32 updateRateF, Object* obj, Object_Racer* racer); /* extern */ -void func_8004447C(Object *obj, Object_Racer *racer, s32 updateRate); +void func_8004447C(Object *aiRacerObj, Object_Racer *aiRacer, s32 updateRate); void func_800452A0(Object *obj, Object_Racer *racer, s32 updateRate); void func_80045C48(Object *obj, Object_Racer *racer, s32 updateRate); void racer_activate_magnet(Object *obj, Object_Racer *racer, s32 updateRate); diff --git a/src/tracks.c b/src/tracks.c index 58eaf7a2..739087b8 100644 --- a/src/tracks.c +++ b/src/tracks.c @@ -2762,8 +2762,7 @@ s32 func_8002BAB0(s32 levelSegmentIndex, f32 xIn, f32 zIn, f32 *yOut) { yOutCount = 0; for (batchNum = 0; batchNum < currentSegment->numberOfBatches; batchNum++) { - do { - } while (0); + if (1) {} currentBatch = ¤tSegment->batches[batchNum]; currentFaceOffset = currentBatch->facesOffset; nextFaceOffset = currentBatch[1].facesOffset; @@ -2783,6 +2782,7 @@ s32 func_8002BAB0(s32 levelSegmentIndex, f32 xIn, f32 zIn, f32 *yOut) { temp_ra_1 = ((((XInInt - vert2X) * (vert3Z - vert2Z)) - ((vert3X - vert2X) * (ZInInt - vert2Z))) >= 0); temp_ra_2 = ((((XInInt - vert1X) * (vert2Z - vert1Z)) - ((vert2X - vert1X) * (ZInInt - vert1Z))) >= 0); temp_ra_3 = ((((XInInt - vert1X) * (vert3Z - vert1Z)) - ((vert3X - vert1X) * (ZInInt - vert1Z))) >= 0); + var_v0 = faceNum; // fake? if (temp_ra_1 == temp_ra_2 && temp_ra_2 != temp_ra_3) { temp = currentSegment->unk14[faceNum].triangleIndex; temp_v1_4 = (f32 *) ¤tSegment->unk18[temp * 4]; @@ -2790,6 +2790,11 @@ s32 func_8002BAB0(s32 levelSegmentIndex, f32 xIn, f32 zIn, f32 *yOut) { tempVec4f.y = temp_v1_4[1]; tempVec4f.z = temp_v1_4[2]; tempVec4f.w = temp_v1_4[3]; + // temp = currentSegment->unk14[faceNum].triangleIndex; + // tempVec4f.x = currentSegment->unk18_vec4f[temp].x; + // tempVec4f.y = currentSegment->unk18_vec4f[temp].y; + // tempVec4f.z = currentSegment->unk18_vec4f[temp].z; + // tempVec4f.w = currentSegment->unk18_vec4f[temp].w; if (tempVec4f.y != 0.0) { yOut[yOutCount] = -(((tempVec4f.x * xIn) + (tempVec4f.z * zIn) + tempVec4f.w) / tempVec4f.y); yOutCount++; @@ -3591,9 +3596,9 @@ void func_8002DE30(Object *obj) { } if (maxYPos >= sp90 && sp94 >= minYPos) { if (tri2d_xz_contains_point(obj->segment.trans.x_position, obj->segment.trans.z_position, - &vertices[triangle->verticesArray[1]].x, - &vertices[triangle->verticesArray[2]].x, - &vertices[triangle->verticesArray[3]].x)) { + (Vec3s *) &vertices[triangle->verticesArray[1]], + (Vec3s *) &vertices[triangle->verticesArray[2]], + (Vec3s *) &vertices[triangle->verticesArray[3]])) { foundResult = TRUE; obj->shading->unk0 += (((1.0f - D_800DC884[batchFlags]) - obj->shading->unk0) * 0.2); } @@ -3948,7 +3953,98 @@ void func_8002F2AC(void) { #pragma GLOBAL_ASM("asm/nonmatchings/tracks/func_8002F2AC.s") #endif -#pragma GLOBAL_ASM("asm/nonmatchings/tracks/func_8002F440.s") +void func_8002F440(void) { + s32 spAC; + s32 var_t0; + Triangle *tri; + Vertex *vert; + s32 alpha; + s16 sp90[6]; + s32 var_s2; + s16 sp80[6]; + f32 temp_f18; + f32 yRotCos; + f32 yRotSin; + f32 zDiff; + f32 someY; + f32 scale; + f32 xDiff; + s32 i; + UNUSED s32 pad; + + alpha = 255; + yRotSin = sins_f(gNewShadowObj->segment.trans.rotation.y_rotation); + yRotCos = coss_f(gNewShadowObj->segment.trans.rotation.y_rotation); + temp_f18 = gNewShadowTexture->width * 16; + scale = ((gNewShadowTexture->width * 16) / gNewShadowWidth); + scale *= 1.4142f; + if (D_8011D0F0 > 0.0f) { + someY = gNewShadowObj->segment.trans.y_position - D_8011D0D0; + if (D_8011D0F0 < someY) { + alpha = 255; + alpha -= (s32) ((255 * (someY - D_8011D0F0)) / D_8011D0F4); + if (alpha < 0) { + alpha = 0; + } + } + if (someY > 0.0f) { + scale *= 1.0f + (0.005f * someY); + } + } + alpha *= gShadowOpacity; + var_s2 = 25; + for (spAC = 0; spAC < D_8011C230; spAC++) { + if ((D_8011C238[spAC].unk0 + var_s2) >= 24) { + gCurrShadowHeapData[gShadowTail].texture = gNewShadowTexture; + gCurrShadowHeapData[gShadowTail].triCount = gNewShadowTriCount; + gCurrShadowHeapData[gShadowTail].vtxCount = gNewShadowVtxCount; + gShadowTail++; + var_s2 = 0; + } + var_t0 = D_8011C238[spAC].unk1; + + for (i = 0; i < D_8011C238[spAC].unk0; i++) { + vert = &gCurrShadowVerts[gNewShadowVtxCount]; + gNewShadowVtxCount++; + if (var_t0 & 1) { + vert->x = D_8011B330[D_8011C238[spAC].unk2[i]].x; + xDiff = D_8011B330[D_8011C238[spAC].unk2[i]].x - gNewShadowObj->segment.trans.x_position; + vert->y = (D_8011B330[D_8011C238[spAC].unk2[i]].y + D_8011D0C8); + vert->z = D_8011B330[D_8011C238[spAC].unk2[i]].z; + zDiff = D_8011B330[D_8011C238[spAC].unk2[i]].z - gNewShadowObj->segment.trans.z_position; + } else { + vert->x = D_8011B120[D_8011C238[spAC].unk2[i]].x; + xDiff = D_8011B120[D_8011C238[spAC].unk2[i]].x - gNewShadowObj->segment.trans.x_position; + vert->y = (D_8011B120[D_8011C238[spAC].unk2[i]].y + D_8011D0C8); + vert->z = D_8011B120[D_8011C238[spAC].unk2[i]].z; + zDiff = D_8011B120[D_8011C238[spAC].unk2[i]].z - gNewShadowObj->segment.trans.z_position; + } + var_t0 >>= 1; + vert->r = 255; + vert->g = 255; + vert->b = 255; + vert->a = alpha; + sp90[i] = ((((xDiff * yRotCos) - (zDiff * yRotSin)) * scale) + temp_f18); + sp80[i] = ((((zDiff * yRotCos) + (xDiff * yRotSin)) * scale) + temp_f18); + } + + for (i = 1; i < (D_8011C238[spAC].unk0 - 1); i++) { + tri = &gCurrShadowTris[gNewShadowTriCount]; + gNewShadowTriCount++; + tri->flags = 0x40; + tri->vi0 = var_s2 + i; + tri->vi1 = var_s2 + i + 1; + tri->vi2 = var_s2; + tri->uv0.u = sp90[i]; + tri->uv0.v = sp80[i]; + tri->uv1.u = sp90[i + 1]; + tri->uv1.v = sp80[i + 1]; + tri->uv2.u = sp90[0]; + tri->uv2.v = sp80[0]; + } + var_s2 += D_8011C238[spAC].unk0; + } +} // Transition points between different lighting levels, used by certain objects f32 func_8002FA64(void) { diff --git a/src/vehicle_bubbler.c b/src/vehicle_bubbler.c index 7c2b0216..88a89740 100644 --- a/src/vehicle_bubbler.c +++ b/src/vehicle_bubbler.c @@ -84,7 +84,7 @@ void update_bubbler(s32 updateRate, f32 updateRateF, Object *obj, Object_Racer * } gBubblerStartBoost = TRUE; *startTimer = 0; - *input |= 0x8000; + *input |= A_BUTTON; } else { gBubblerStartBoost = FALSE; }