diff --git a/patches/effect_transform_tagging.c b/patches/effect_transform_tagging.c index a9c34e1..f62deb0 100644 --- a/patches/effect_transform_tagging.c +++ b/patches/effect_transform_tagging.c @@ -2,6 +2,7 @@ #include "transform_ids.h" #include "overlays/actors/ovl_En_Test7/z_en_test7.h" #include "overlays/actors/ovl_Object_Kankyo/z_object_kankyo.h" +#include "z64effect.h" // Decomp renames, TODO update decomp and remove these #define gSoaringWarpCsWindCapsuleTexAnim gameplay_keep_Matanimheader_0815D0 @@ -325,3 +326,142 @@ void func_808DD3C8(Actor* thisx, PlayState* play2) { CLOSE_DISPS(play->state.gfxCtx); } + +#define SPARK_COUNT 3 +#define BLURE_COUNT 25 +#define SHIELD_PARTICLE_COUNT 3 +#define TIRE_MARK_COUNT 15 + +#define TOTAL_EFFECT_COUNT SPARK_COUNT + BLURE_COUNT + SHIELD_PARTICLE_COUNT + TIRE_MARK_COUNT + +typedef struct EffectStatus { + /* 0x0 */ u8 active; + /* 0x1 */ u8 unk1; + /* 0x2 */ u8 unk2; +} EffectStatus; // size = 0x3 + +typedef struct EffectContext { + /* 0x0000 */ struct PlayState* play; + struct { + EffectStatus status; + EffectSpark effect; + } /* 0x0004 */ sparks[SPARK_COUNT]; + struct { + EffectStatus status; + EffectBlure effect; + } /* 0x0E5C */ blures[BLURE_COUNT]; + struct { + EffectStatus status; + EffectShieldParticle effect; + } /* 0x388C */ shieldParticles[SHIELD_PARTICLE_COUNT]; + struct { + EffectStatus status; + EffectTireMark effect; + } /* 0x3DF0 */ tireMarks[TIRE_MARK_COUNT]; +} EffectContext; // size = 0x98E0 + +typedef struct EffectInfo { + /* 0x00 */ u32 size; + /* 0x04 */ void (*init)(void* effect, void* initParams); + /* 0x08 */ void (*destroy)(void* effect); + /* 0x0C */ s32 (*update)(void* effect); + /* 0x10 */ void (*draw)(void* effect, struct GraphicsContext* gfxCtx); +} EffectInfo; // size = 0x14 + +extern EffectContext sEffectContext; +extern EffectInfo sEffectInfoTable[]; + +static inline void tag_interpolate_effect(GraphicsContext* gfxCtx, u32 id) { + OPEN_DISPS(gfxCtx); + + gEXMatrixGroupDecomposed(POLY_OPA_DISP++, id, G_EX_PUSH, G_MTX_MODELVIEW, + G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_INTERPOLATE, + G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_INTERPOLATE, G_EX_ORDER_LINEAR); + + gEXMatrixGroupDecomposed(POLY_XLU_DISP++, id + EFFECT_TRANSFORM_ID_COUNT, G_EX_PUSH, G_MTX_MODELVIEW, + G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_INTERPOLATE, + G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_INTERPOLATE, G_EX_ORDER_LINEAR); + + CLOSE_DISPS(); +} + +static inline void tag_skip_effect(GraphicsContext* gfxCtx, u32 id) { + OPEN_DISPS(gfxCtx); + + gEXMatrixGroupSimple(POLY_OPA_DISP++, id, G_EX_PUSH, G_MTX_MODELVIEW, + G_EX_COMPONENT_SKIP, G_EX_COMPONENT_SKIP, G_EX_COMPONENT_SKIP, + G_EX_COMPONENT_SKIP, G_EX_COMPONENT_INTERPOLATE, G_EX_ORDER_LINEAR); + + gEXMatrixGroupSimple(POLY_XLU_DISP++, id + EFFECT_TRANSFORM_ID_COUNT, G_EX_PUSH, G_MTX_MODELVIEW, + G_EX_COMPONENT_SKIP, G_EX_COMPONENT_SKIP, G_EX_COMPONENT_SKIP, + G_EX_COMPONENT_SKIP, G_EX_COMPONENT_INTERPOLATE, G_EX_ORDER_LINEAR); + + CLOSE_DISPS(); +} + +static inline void pop_effect_tag(GraphicsContext* gfxCtx) { + OPEN_DISPS(gfxCtx); + + gEXPopMatrixGroup(POLY_OPA_DISP++); + gEXPopMatrixGroup(POLY_XLU_DISP++); + + CLOSE_DISPS(); +} + +// @recomp Patched to tag effects. +void Effect_DrawAll(GraphicsContext* gfxCtx) { + s32 i; + + + for (i = 0; i < SPARK_COUNT; i++) { + if (!sEffectContext.sparks[i].status.active) { + continue; + } + // @recomp Tag transform. + tag_interpolate_effect(gfxCtx, EFFECT_SPARK_TRANSFORM_ID_START + i); + + sEffectInfoTable[EFFECT_SPARK].draw(&sEffectContext.sparks[i].effect, gfxCtx); + + // @recomp Pop tag. + pop_effect_tag(gfxCtx); + } + + for (i = 0; i < BLURE_COUNT; i++) { + if (!sEffectContext.blures[i].status.active) { + continue; + } + // @recomp Tag transform to skip interpolation as this effect doesn't work well with it. + tag_skip_effect(gfxCtx, EFFECT_BLURE_TRANSFORM_ID_START + i); + + sEffectInfoTable[EFFECT_BLURE1].draw(&sEffectContext.blures[i].effect, gfxCtx); + + // @recomp Pop tag. + pop_effect_tag(gfxCtx); + } + + for (i = 0; i < SHIELD_PARTICLE_COUNT; i++) { + if (!sEffectContext.shieldParticles[i].status.active) { + continue; + } + // @recomp Tag transform. + tag_interpolate_effect(gfxCtx, EFFECT_SHIELD_PARTICLE_TRANSFORM_ID_START + i); + + sEffectInfoTable[EFFECT_SHIELD_PARTICLE].draw(&sEffectContext.shieldParticles[i].effect, gfxCtx); + + // @recomp Pop tag. + pop_effect_tag(gfxCtx); + } + + for (i = 0; i < TIRE_MARK_COUNT; i++) { + if (!sEffectContext.tireMarks[i].status.active) { + continue; + } + // @recomp Tag transform to skip interpolation as this effect doesn't work well with it. + tag_skip_effect(gfxCtx, EFFECT_TIRE_MARK_TRANSFORM_ID_START + i); + + sEffectInfoTable[EFFECT_TIRE_MARK].draw(&sEffectContext.tireMarks[i].effect, gfxCtx); + + // @recomp Pop tag. + pop_effect_tag(gfxCtx); + } +} diff --git a/patches/input.c b/patches/input.c index e7c6029..4b4a07f 100644 --- a/patches/input.c +++ b/patches/input.c @@ -282,6 +282,118 @@ ItemId Player_GetItemOnButton(PlayState* play, Player* player, EquipSlot slot) { return C_BTN_ITEM(EQUIP_SLOT_C_RIGHT); } +typedef struct struct_8085D910 { + /* 0x0 */ u8 unk_0; + /* 0x1 */ u8 unk_1; + /* 0x2 */ u8 unk_2; + /* 0x3 */ u8 unk_3; +} struct_8085D910; // size = 0x4 + +u16 D_8085D908[] = { + WEEKEVENTREG_30_80, // PLAYER_FORM_FIERCE_DEITY + WEEKEVENTREG_30_20, // PLAYER_FORM_GORON + WEEKEVENTREG_30_40, // PLAYER_FORM_ZORA + WEEKEVENTREG_30_10, // PLAYER_FORM_DEKU +}; +struct_8085D910 D_8085D910[] = { + { 0x10, 0xA, 0x3B, 0x3F }, + { 9, 0x32, 0xA, 0xD }, +}; + +bool func_808323C0(Player *this, s16 csId); +void func_80855218(PlayState *play, Player *this, struct_8085D910 **arg2); +void func_808550D0(PlayState *play, Player *this, f32 arg2, f32 arg3, s32 arg4); + +void Player_Action_86(Player *this, PlayState *play) { + struct_8085D910 *sp4C = D_8085D910; + s32 sp48 = false; + + func_808323C0(this, play->playerCsIds[PLAYER_CS_ID_MASK_TRANSFORMATION]); + sPlayerControlInput = play->state.input; + + Camera_ChangeMode(GET_ACTIVE_CAM(play), + (this->transformation == PLAYER_FORM_HUMAN) ? CAM_MODE_NORMAL : CAM_MODE_JUMP); + this->stateFlags2 |= PLAYER_STATE2_40; + this->actor.shape.rot.y = Camera_GetCamDirYaw(GET_ACTIVE_CAM(play)) + 0x8000; + + func_80855218(play, this, &sp4C); + + if (this->av1.actionVar1 == 0x14) { + Play_EnableMotionBlurPriority(100); + } + + if (R_PLAY_FILL_SCREEN_ON != 0) { + R_PLAY_FILL_SCREEN_ALPHA += R_PLAY_FILL_SCREEN_ON; + if (R_PLAY_FILL_SCREEN_ALPHA > 255) { + R_PLAY_FILL_SCREEN_ALPHA = 255; + this->actor.update = func_8012301C; + this->actor.draw = NULL; + this->av1.actionVar1 = 0; + Play_DisableMotionBlurPriority(); + SET_WEEKEVENTREG(D_8085D908[GET_PLAYER_FORM]); + } + } + else if ((this->av1.actionVar1++ > ((this->transformation == PLAYER_FORM_HUMAN) ? 0x53 : 0x37)) || + ((this->av1.actionVar1 >= 5) && + (sp48 = + ((this->transformation != PLAYER_FORM_HUMAN) || CHECK_WEEKEVENTREG(D_8085D908[GET_PLAYER_FORM])) && + // @recomp Patched to also check for d-pad buttons for skipping the transformation cutscene. + CHECK_BTN_ANY(sPlayerControlInput->press.button, + BTN_CRIGHT | BTN_CLEFT | BTN_CDOWN | BTN_CUP | BTN_B | BTN_A | BTN_DRIGHT | BTN_DLEFT | BTN_DDOWN | BTN_DUP)))) { + R_PLAY_FILL_SCREEN_ON = 45; + R_PLAY_FILL_SCREEN_R = 220; + R_PLAY_FILL_SCREEN_G = 220; + R_PLAY_FILL_SCREEN_B = 220; + R_PLAY_FILL_SCREEN_ALPHA = 0; + + if (sp48) { + if (CutsceneManager_GetCurrentCsId() == this->csId) { + func_800E0348(Play_GetCamera(play, CutsceneManager_GetCurrentSubCamId(this->csId))); + } + + if (this->transformation == PLAYER_FORM_HUMAN) { + AudioSfx_StopById(NA_SE_PL_TRANSFORM_VOICE); + AudioSfx_StopById(NA_SE_IT_TRANSFORM_MASK_BROKEN); + } + else { + AudioSfx_StopById(NA_SE_PL_FACE_CHANGE); + } + } + + Player_PlaySfx(this, NA_SE_SY_TRANSFORM_MASK_FLASH); + } + + if (this->av1.actionVar1 >= sp4C->unk_0) { + if (this->av1.actionVar1 < sp4C->unk_2) { + Math_StepToF(&this->unk_B10[4], 1.0f, sp4C->unk_1 / 100.0f); + } + else if (this->av1.actionVar1 < sp4C->unk_3) { + if (this->av1.actionVar1 == sp4C->unk_2) { + Lib_PlaySfx_2(NA_SE_EV_LIGHTNING_HARD); + } + + Math_StepToF(&this->unk_B10[4], 2.0f, 0.5f); + } + else { + Math_StepToF(&this->unk_B10[4], 3.0f, 0.2f); + } + } + + if (this->av1.actionVar1 >= 0x10) { + if (this->av1.actionVar1 < 0x40) { + Math_StepToF(&this->unk_B10[5], 1.0f, 0.2f); + } + else if (this->av1.actionVar1 < 0x37) { + Math_StepToF(&this->unk_B10[5], 2.0f, 1.0f); + } + else { + Math_StepToF(&this->unk_B10[5], 3.0f, 0.55f); + } + } + + func_808550D0(play, this, this->unk_B10[4], this->unk_B10[5], (this->transformation == PLAYER_FORM_HUMAN) ? 0 : 1); +} + extern s16 sPictoState; extern s16 sPictoPhotoBeingTaken; extern void* gWorkBuffer; diff --git a/patches/item_transform_tagging.c b/patches/item_transform_tagging.c index d6aa655..d520eb3 100644 --- a/patches/item_transform_tagging.c +++ b/patches/item_transform_tagging.c @@ -157,27 +157,29 @@ void Player_DrawGameplay(PlayState* play, Player* this, s32 lod, Gfx* cullDList, // @recomp Force the closest LOD lod = 0; - // @recomp Patch object_link_child_DL_017818 (the DL for the bowstring) with a transform tag. - gSegments[0x0C] = OS_K0_TO_PHYSICAL(cullDList); - Gfx* dl_virtual_address = (Gfx*)Lib_SegmentedToVirtual(object_link_child_DL_017818); + // @recomp If the player is a human, patch object_link_child_DL_017818 (the DL for the bowstring) with a transform tag. + if (this->transformation == PLAYER_FORM_HUMAN) { + gSegments[0x0C] = OS_K0_TO_PHYSICAL(cullDList); + Gfx* dl_virtual_address = (Gfx*)Lib_SegmentedToVirtual(object_link_child_DL_017818); - // Check if the commands have already been overwritten. - if ((dl_virtual_address[0].words.w0 >> 24) != G_DL) { - // Copy the first command before overwriting. - bowstring_start_hook_dl[0] = dl_virtual_address[0]; - // Overwrite the first command with a branch. - gSPBranchList(dl_virtual_address, OS_K0_TO_PHYSICAL(bowstring_start_hook_dl)); - Gfx* enddl_command = dl_virtual_address; - while ((enddl_command->words.w0 >> 24) != G_ENDDL) { - enddl_command++; + // Check if the commands have already been overwritten. + if ((dl_virtual_address[0].words.w0 >> 24) != G_DL) { + // Copy the first command before overwriting. + bowstring_start_hook_dl[0] = dl_virtual_address[0]; + // Overwrite the first command with a branch. + gSPBranchList(dl_virtual_address, OS_K0_TO_PHYSICAL(bowstring_start_hook_dl)); + Gfx* enddl_command = dl_virtual_address; + while ((enddl_command->words.w0 >> 24) != G_ENDDL) { + enddl_command++; + } + // Overwrite the last command with a branch. + gSPBranchList(enddl_command, bowstring_end_hook_dl); + // Write the transform tag command. Use simple interpolation to avoid issues from decomposition failure due to a scale of zero. + gEXMatrixGroupSimple(&bowstring_start_hook_dl[1], BOWSTRING_TRANSFORM_ID, G_EX_PUSH, G_MTX_MODELVIEW, + G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_INTERPOLATE, G_EX_ORDER_LINEAR); + // Write the pop group command. + gEXPopMatrixGroup(&bowstring_end_hook_dl[0]); } - // Overwrite the last command with a branch. - gSPBranchList(enddl_command, bowstring_end_hook_dl); - // Write the transform tag command. Use simple interpolation to avoid issues from decomposition failure due to a scale of zero. - gEXMatrixGroupSimple(&bowstring_start_hook_dl[1], BOWSTRING_TRANSFORM_ID, G_EX_PUSH, G_MTX_MODELVIEW, - G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_INTERPOLATE, G_EX_COMPONENT_INTERPOLATE, G_EX_ORDER_LINEAR); - // Write the pop group command. - gEXPopMatrixGroup(&bowstring_end_hook_dl[0]); } // @recomp Manually relocate Player_PostLimbDrawGameplay. diff --git a/patches/transform_ids.h b/patches/transform_ids.h index 96bd8a5..f6ab6d3 100644 --- a/patches/transform_ids.h +++ b/patches/transform_ids.h @@ -18,6 +18,12 @@ #define STAR_TRANSFORM_ID_START 0x1000U +#define EFFECT_TRANSFORM_ID_COUNT 0x800U +#define EFFECT_SPARK_TRANSFORM_ID_START 0x2000U +#define EFFECT_BLURE_TRANSFORM_ID_START (EFFECT_SPARK_TRANSFORM_ID_START + 2 * EFFECT_TRANSFORM_ID_COUNT) +#define EFFECT_SHIELD_PARTICLE_TRANSFORM_ID_START (EFFECT_BLURE_TRANSFORM_ID_START + 2 * EFFECT_TRANSFORM_ID_COUNT) +#define EFFECT_TIRE_MARK_TRANSFORM_ID_START (EFFECT_SHIELD_PARTICLE_TRANSFORM_ID_START + 2 * EFFECT_TRANSFORM_ID_COUNT) + #define ACTOR_TRANSFORM_LIMB_COUNT 128 #define ACTOR_TRANSFORM_ID_COUNT (ACTOR_TRANSFORM_LIMB_COUNT * 2) // One ID for each limb and another for each post-draw #define ACTOR_TRANSFORM_ID_START 0x1000000U