diff --git a/patches/demo_playback_functions.c b/patches/demo_playback_functions.c new file mode 100644 index 0000000..fd6f3bc --- /dev/null +++ b/patches/demo_playback_functions.c @@ -0,0 +1,17 @@ +#include "patches.h" + +extern s32 getGameMode(void); + +// @recomp Check whether the game is currently on a mode that uses demo playback instead of the player's inputs. +bool recomp_in_demo_playback_game_mode() { + switch (getGameMode()) { + case GAME_MODE_6_FILE_PLAYBACK: + case GAME_MODE_7_ATTRACT_DEMO: + case GAME_MODE_8_BOTTLES_BONUS: + case GAME_MODE_A_SNS_PICTURE: + case GAME_MODE_9_BANJO_AND_KAZOOIE: + return TRUE; + default: + return FALSE; + } +} \ No newline at end of file diff --git a/patches/snow_patches.c b/patches/snow_patches.c index 120159a..46cad2a 100644 --- a/patches/snow_patches.c +++ b/patches/snow_patches.c @@ -26,13 +26,16 @@ extern void func_802F8FFC(void); extern void func_8033BD20(BKModelBin **arg0); extern s32 globalTimer_getTime(void); -#define SNOW_PARTICLE_COUNT 256 +#define SNOW_PARTICLE_COUNT_ORIGINAL 100 +#define SNOW_PARTICLE_COUNT_EXPANDED 256 #define SNOW_SKIP_INTERPOLATION_MASK 0x8000 -u16 snowIDArray[SNOW_PARTICLE_COUNT]; -u16 snowIDQueue[SNOW_PARTICLE_COUNT]; +u16 snowIDArray[SNOW_PARTICLE_COUNT_EXPANDED]; +u16 snowIDQueue[SNOW_PARTICLE_COUNT_EXPANDED]; u16 snowIDQueueCount = 0; -struct4Ds snowData[SNOW_PARTICLE_COUNT]; +struct4Ds snowData[SNOW_PARTICLE_COUNT_EXPANDED]; + +extern bool recomp_in_demo_playback_game_mode(); // @recomp Patched to initialize the ID queue for snow particles. RECOMP_PATCH void func_802F9054(void) { @@ -46,7 +49,7 @@ RECOMP_PATCH void func_802F9054(void) { D_80369288 = assetcache_get(0x8a1); //2D_light // @recomp Initialize the snow ID queue. - while (snowIDQueueCount < SNOW_PARTICLE_COUNT) { + while (snowIDQueueCount < SNOW_PARTICLE_COUNT_EXPANDED) { snowIDQueue[snowIDQueueCount] = snowIDQueueCount; snowIDQueueCount++; } @@ -132,8 +135,10 @@ RECOMP_PATCH void func_802F919C(void) { } if (D_80369280->unk18 == 1) { // @recomp Patched to use the count from the macro instead and spawn multiple snow particles at once. - u32 particlesToSpawn = 2; - while (D_80369284 < SNOW_PARTICLE_COUNT && particlesToSpawn > 0) { + // This behavior must be ignored while in demo mode to not alter the RNG, as it'll affect the demo's result. + u32 snowParticleLimit = recomp_in_demo_playback_game_mode() ? SNOW_PARTICLE_COUNT_ORIGINAL : SNOW_PARTICLE_COUNT_EXPANDED; + u32 particlesToSpawn = recomp_in_demo_playback_game_mode() ? 1 : 2; + while (D_80369284 < snowParticleLimit && particlesToSpawn > 0) { sp40 = D_80369280->unk1C + D_80369284; // @recomp Assign a new ID to this particle. Also mark it to skip interpolation. @@ -153,7 +158,10 @@ RECOMP_PATCH void func_802F919C(void) { } // @recomp Use the full 360 degrees instead. - var_f20 = 180.0f; + // This behavior must be ignored while in demo mode to not alter the RNG, as it'll affect the demo's result. + if (!recomp_in_demo_playback_game_mode()) { + var_f20 = 180.0f; + } ml_vec3f_yaw_rotate_copy(sp4C, sp4C, viewport_getYaw() + randf2(-var_f20, var_f20)); sp4C[0] += D_80381040[0]; @@ -184,7 +192,9 @@ RECOMP_PATCH bool func_802F989C(Gfx **gfx, Mtx **mtx, f32 arg2[3]) { && (arg2[1] > -200.0f) && ((-17000.0f < D_80381070[2]) && (D_80381070[2] < 17000.0f)) // @recomp Remove frustum check. - //&& viewport_func_8024DB50(arg2, D_8038108C, aspect_ratio_scale) + // This behavior must be ignored while in demo mode to not alter the RNG, as it'll affect the demo's result. + //&& viewport_func_8024DB50(arg2, D_8038108C) + && (!recomp_in_demo_playback_game_mode() || viewport_func_8024DB50(arg2, D_8038108C)) ) { func_80251B5C(D_80381070[0], D_80381070[1], D_80381070[2]); mlMtxApply(*mtx); diff --git a/patches/transform_ids.h b/patches/transform_ids.h index 7b099ba..1e304ca 100644 --- a/patches/transform_ids.h +++ b/patches/transform_ids.h @@ -102,6 +102,11 @@ #define WEATHER_PARTICLE_ID_COUNT 256 #define WEATHER_PARTICLE_ID_MAX 0x8000 +// Rain Particles: 0x30800000 - 0x31000000 +#define RAIN_PARTICLE_TRANSFORM_ID_START 0x30800000 +#define RAIN_PARTICLE_ID_COUNT 256 +#define RAIN_PARTICLE_ID_MAX 0x8000 + // Markers: 0x10000000 - 0x1FFFFFFF #define BANJO_TRANSFORM_ID_START 0x10000000 #define MARKER_TRANSFORM_ID_START (BANJO_TRANSFORM_ID_START + MARKER_TRANSFORM_ID_COUNT) diff --git a/patches/weather_patches.c b/patches/weather_patches.c index 08c7321..30ef7c6 100644 --- a/patches/weather_patches.c +++ b/patches/weather_patches.c @@ -5,8 +5,13 @@ extern s32 func_8033A170(void); +extern struct6s *D_80381030; +extern struct3s *D_80381034; + u32 weather_particle_spawn_count = 0; +extern bool recomp_in_demo_playback_game_mode(); + u32 get_weather_particle_id(struct5s *p) { u8 *padding = p->pad35; u32 id = (padding[0] << 16) | (padding[1] << 8) | (padding[2] << 0); @@ -48,6 +53,14 @@ RECOMP_PATCH void func_802F87B0(struct6s *this) { else { ml_vec3f_yaw_rotate_copy(sp4C, sp4C, camRot[1] + randf2(-70.0f, 70.0f)); }//L802F88F0 + + // @recomp Force particles to spawn 360 degrees around the player at all times. + // This behavior must be ignored while in demo mode to not alter the RNG, as it'll affect the + // demo's result. + if (!recomp_in_demo_playback_game_mode()) { + ml_vec3f_yaw_rotate_copy(sp4C, sp4C, randf2(0.0f, 360.0f)); + } + sp4C[0] += plyrPos[0]; sp4C[1] += plyrPos[1]; sp4C[2] += plyrPos[2]; @@ -81,6 +94,28 @@ RECOMP_PATCH void func_802F87B0(struct6s *this) { if (weather_particle_spawn_count >= WEATHER_PARTICLE_ID_MAX) { weather_particle_spawn_count = 0; } + + // @recomp Increase the spawn rate of the particles by calling the function recursively again. + // Infinite recursion is prevented by keeping track of the current recursion depth. To control + // the frequency of the spawn rate, an additional counter is used to spawn an extra particle + // every N amount of frames. + // This behavior must be ignored while in demo mode to not alter the RNG, as it'll affect the + // demo's result. + if (!recomp_in_demo_playback_game_mode()) { + const u32 spawn_rate_frequency = 2; + static u32 recursion_depth = 0; + if (recursion_depth == 0) { + static u32 spawn_rate_counter = 0; + spawn_rate_counter++; + + if (spawn_rate_counter == spawn_rate_frequency) { + recursion_depth++; + func_802F87B0(this); + recursion_depth--; + spawn_rate_counter = 0; + } + } + } } // @recomp Patched to tag the weather effect for interpolation. @@ -101,4 +136,147 @@ RECOMP_PATCH void func_802F8A90(struct6s *this, Gfx **gdl, Mtx **mptr, Vtx **vpt iPtr->unk34 = func_8033A170(); } -} \ No newline at end of file +} + +// @recomp Patched to increase capacity of the weather effect vector. +RECOMP_PATCH struct6s *func_802F7C38(void) { + if (D_80381030 == NULL) { + // @recomp Increase capacity of the weather effect vector to accomodate for more particles on screen. + // This behavior must be ignored while in demo mode to not alter the RNG, as it'll affect the + // demo's result. + //D_80381030 = func_802F8BE0(50); //new CCW weather + D_80381030 = func_802F8BE0(recomp_in_demo_playback_game_mode() ? 50 : 250); + } + return D_80381030; +} + +u32 rain_particle_spawn_count = 0; + +u32 get_rain_particle_id(struct4s *p) { + u8 *padding = p->pad19; + u32 id = (padding[0] << 16) | (padding[1] << 8) | (padding[2] << 0); + return id; +} + +void set_rain_particle_id(struct4s *p, u32 id) { + u8 *padding = p->pad19; + padding[0] = (id >> 16) & 0xFF; + padding[1] = (id >> 8) & 0xFF; + padding[2] = (id >> 0) & 0xFF; +} + +// @recomp Patched to assign an interpolation ID to the rain particle when it's created. +RECOMP_PATCH void func_802F7EB0(struct3s *this) { + f32 plyrPos[3]; //sp74 + f32 camNorm[3]; //sp68 + f32 camRot[3]; //sp5C + s32 i; + f32 tmpf; + struct4s *sp50; + f32 sp4C[3]; + + + if (vector_size(this->unk20) >= this->unk24) + return; + + player_getPosition(plyrPos); + viewport_getLookVector(camNorm); + viewport_getRotation_vec3f(camRot); + sp50 = vector_pushBackNew(&this->unk20); + tmpf = randf2(50.0f, 1100.0f); + sp4C[0] = 0.0f; + sp4C[1] = randf2(200.0f, 300.0f); + sp4C[2] = -tmpf; + + if (gu_sqrtf(this->unk10[0] * this->unk10[0] + this->unk10[1] * this->unk10[1] + this->unk10[2] * this->unk10[2]) < 5.0f) { + ml_vec3f_yaw_rotate_copy(sp4C, sp4C, randf2(0.0f, 360.0f)); + } + else { + ml_vec3f_yaw_rotate_copy(sp4C, sp4C, camRot[1] + randf2(-70.0f, 70.0f)); + } + + // @recomp Force particles to spawn 360 degrees around the player at all times. + // This behavior must be ignored while in demo mode to not alter the RNG, as it'll affect the + // demo's result. + if (!recomp_in_demo_playback_game_mode()) { + ml_vec3f_yaw_rotate_copy(sp4C, sp4C, randf2(0.0f, 360.0f)); + } + + sp4C[0] = plyrPos[0] + sp4C[0]; + sp4C[1] = plyrPos[1] + sp4C[1]; + sp4C[2] = plyrPos[2] + sp4C[2]; + if (tmpf < 550.0) + for (i = 0; (i < 0xa) && viewport_isPointPlane_3f(sp4C[0], sp4C[1] - 10.0f, sp4C[2]); i++) { + sp4C[1] += 100.0f; + } + + sp50->unk0[0] = sp4C[0]; + sp50->unk0[1] = sp4C[1]; + sp50->unk0[2] = sp4C[2]; + sp50->unkC[0] = 0.0f; + sp50->unkC[1] = randf2(-1600.0f, -1500.0f); + sp50->unkC[2] = 0.0f; + + // @recomp Set the rain particle's ID based on the particle spawn count and increment it. + set_rain_particle_id(sp50, rain_particle_spawn_count++); + if (rain_particle_spawn_count >= RAIN_PARTICLE_ID_MAX) { + rain_particle_spawn_count = 0; + } + + // @recomp Increase the spawn rate of the particles by calling the function recursively again. + // Infinite recursion is prevented by keeping track of the current recursion depth. To control + // the frequency of the spawn rate, an additional counter is used to spawn an extra particle + // every N amount of frames. + // This behavior must be ignored while in demo mode to not alter the RNG, as it'll affect the + // demo's result. + if (!recomp_in_demo_playback_game_mode()) { + const u32 spawn_rate_frequency = 2; + static u32 recursion_depth = 0; + if (recursion_depth == 0) { + static u32 spawn_rate_counter = 0; + spawn_rate_counter++; + + if (spawn_rate_counter == spawn_rate_frequency) { + recursion_depth++; + func_802F7EB0(this); + recursion_depth--; + spawn_rate_counter = 0; + } + } + } +} + +// @recomp Patched to tag the rain particle for interpolation. +RECOMP_PATCH void func_802F8110(struct3s *this, Gfx **gdl, Mtx **mptr, u32 arg3) { + struct4s *startPtr; //sp4c + struct4s *endPtr; + struct4s *iPtr; + + startPtr = vector_getBegin(this->unk20); + endPtr = vector_getEnd(this->unk20); + for (iPtr = startPtr; iPtr < endPtr; iPtr++) { + modelRender_setDepthMode(MODEL_RENDER_DEPTH_COMPARE); + + // @recomp Set the model transform ID before drawing the rain particle. + cur_drawn_model_transform_id = RAIN_PARTICLE_TRANSFORM_ID_START + RAIN_PARTICLE_ID_COUNT * get_rain_particle_id(iPtr); + + modelRender_draw(gdl, mptr, iPtr, 0, 1.0f, 0, this->unk2C); + + // @recomp Clear the model transform ID after drawing the rain particle. + cur_drawn_model_transform_id = 0; + + iPtr->unk18 = func_8033A170(); + } +} + +// @recomp Patched to increase the capacity of the rain particle vector. +RECOMP_PATCH struct3s *func_802F7C7C(void) { + if (D_80381034 == NULL) { + // @recomp Increase capacity of the rain particle vector to accomodate for more droplets on screen. + // This behavior must be ignored while in demo mode to not alter the RNG, as it'll affect the + // demo's result. + //D_80381034 = func_802F8264(30); //rain + D_80381034 = func_802F8264(recomp_in_demo_playback_game_mode() ? 30 : 300); + } + return D_80381034; +}