Ivan: New Farore’s Wind (#6735)

This commit is contained in:
Shishu the Dragon
2026-06-13 16:41:50 +12:00
committed by GitHub
parent eb4142835e
commit 8a1547a6e5
4 changed files with 132 additions and 26 deletions
-1
View File
@@ -951,7 +951,6 @@ typedef struct Player {
// #region SOH [Enhancements]
// Upstream TODO: Rename this to make it more obvious it is apart of an enhancement
/* */ u8 boomerangQuickRecall; // Has the player pressed the boomerang button while it's in the air still?
/* */ u8 ivanFloating;
/* */ u8 ivanDamageMultiplier;
// #endregion
} Player; // size = 0xA94
@@ -13,6 +13,7 @@
#include <overlays/actors/ovl_Obj_Switch/z_obj_switch.h>
#include "soh/OTRGlobals.h"
#include "soh/ResourceManagerHelpers.h"
#include <assets/objects/object_efc_tw/object_efc_tw.h>
#define FLAGS \
(ACTOR_FLAG_UPDATE_CULLING_DISABLED | ACTOR_FLAG_DRAW_CULLING_DISABLED | ACTOR_FLAG_HOOKSHOT_PULLS_PLAYER | \
@@ -70,7 +71,6 @@ void EnPartner_Init(Actor* thisx, PlayState* play) {
this->canMove = 1;
this->shouldDraw = 1;
this->hookshotTarget = NULL;
GET_PLAYER(play)->ivanFloating = 0;
this->innerColor.r = 255.0f;
this->innerColor.g = 255.0f;
@@ -118,9 +118,13 @@ void EnPartner_Destroy(Actor* thisx, PlayState* play) {
this->hookshotTarget = NULL;
}
if (this->windEffect != NULL) {
Actor_Kill(this->windEffect);
this->windEffect = NULL;
}
Player* player = GET_PLAYER(play);
if (player) {
player->ivanFloating = 0;
player->ivanDamageMultiplier = 1;
}
@@ -360,6 +364,123 @@ void UseDekuStick(Actor* thisx, PlayState* play, u8 started) {
}
}
// #region IvanWindEffect
// Custom DemoEffect based on the effect that beams down on timeblocks when playing Song of Time
extern void DemoEffect_TimewarpShrink(f32 size);
void IvanWindEffect_UpdateShrink(DemoEffect* this, PlayState* play) {
this->timeWarp.shrinkTimer += 20;
if (this->timeWarp.shrinkTimer <= 100) {
f32 shrinkProgress = (100 - this->timeWarp.shrinkTimer) * 0.010f;
DemoEffect_TimewarpShrink(shrinkProgress);
} else {
DemoEffect_TimewarpShrink(1.0f); // reset shared resource before killing
Actor_Kill(&this->actor);
}
}
void IvanWindEffect_SetupFadeOut(DemoEffect* this) {
this->updateFunc = IvanWindEffect_UpdateShrink;
this->timeWarp.shrinkTimer = 0;
}
void IvanWindEffect_UpdateIdle(DemoEffect* this, PlayState* play) {
SkelCurve_Update(play, &this->skelCurve);
}
void IvanWindEffect_Init(DemoEffect* this, PlayState* play) {
SkelCurve_Init(play, &this->skelCurve, &gTimeWarpSkel, &gTimeWarpAnim);
SkelCurve_SetAnim(&this->skelCurve, &gTimeWarpAnim, 1.0f, 59.0f, 1.0f, 8.0f);
SkelCurve_Update(play, &this->skelCurve);
this->updateFunc = IvanWindEffect_UpdateIdle;
Actor_SetScale(&this->actor, 0.10f);
DemoEffect_TimewarpShrink(1.0f);
}
DemoEffect* IvanWindEffect_Spawn(EnPartner* ivan, PlayState* play) {
PosRot spawn = {
ivan->actor.world.pos,
{ DEGF_TO_BINANG(90.0f), ivan->actor.world.rot.y, 0 },
};
DemoEffect* windEffect =
Actor_Spawn(&play->actorCtx, play, ACTOR_DEMO_EFFECT, spawn.pos.x, spawn.pos.y, spawn.pos.z, spawn.rot.x,
spawn.rot.y, spawn.rot.z, DEMO_EFFECT_TIMEWARP_TIMEBLOCK_LARGE);
windEffect->envXluColor[1] = 100;
windEffect->envXluColor[2] = 0;
windEffect->initUpdateFunc = IvanWindEffect_Init;
return windEffect;
}
// #endregion
void EndFaroresWind(EnPartner* this, PlayState* play) {
IvanWindEffect_SetupFadeOut(this->windEffect);
this->windEffect = NULL;
gSaveContext.magicState = MAGIC_STATE_RESET;
this->itemTimer = 5;
this->usedItem = 0xFF;
}
void UseFaroresWind(EnPartner* this, PlayState* play, u8 started) {
Player* player = GET_PLAYER(play);
if (started == 1) {
if (gSaveContext.magic <= 0 || gSaveContext.magicState != MAGIC_STATE_IDLE) {
Sfx_PlaySfxCentered(NA_SE_SY_ERROR);
this->usedItem = 0xFF;
return;
}
this->windEffect = IvanWindEffect_Spawn(this, play);
this->magicTimer = 0;
}
if (started == 1 || started == 2) {
func_8002F974(&this->actor, NA_SE_EV_WIND_TRAP - SFX_FLAG);
this->windEffect->actor.world.pos.x = this->actor.world.pos.x;
this->windEffect->actor.world.pos.y = this->actor.world.pos.y;
this->windEffect->actor.world.pos.z = this->actor.world.pos.z;
this->windEffect->actor.shape.rot.y = this->actor.world.rot.y;
// based on BgHakaTrap_FanBlade_UpdateFanRotation
Vec3f playerRel;
Actor_WorldToActorCoords(&this->actor, &playerRel, &player->actor.world.pos);
if ((fabsf(playerRel.x) < 70.0f) && (fabsf(playerRel.y) < 100.0f) && (playerRel.z < 400.0f) &&
(playerRel.z > 0) && (player->currentBoots != PLAYER_BOOTS_IRON)) {
float factor = (1.0f - (playerRel.z / 400.0f));
factor = sqrtf(factor);
player->pushedSpeed = factor * 10.0f;
player->pushedYaw = this->actor.shape.rot.y;
}
gSaveContext.magicState = MAGIC_STATE_METER_FLASH_1;
this->magicTimer--;
if (this->magicTimer <= 0) {
if (gSaveContext.magic <= 0) {
gSaveContext.magic = 0;
EndFaroresWind(this, play);
return;
}
gSaveContext.magic--; // Note: after the `if` statement so that the last tick of magic
this->magicTimer = 20; // gives the full count of frames
}
}
if (started == 0) {
EndFaroresWind(this, play);
return;
}
}
void UseNuts(Actor* thisx, PlayState* play, u8 started) {
EnPartner* this = (EnPartner*)thisx;
@@ -486,9 +607,6 @@ void UseSpell(Actor* thisx, PlayState* play, u8 started, u8 spellType) {
case 1:
GET_PLAYER(play)->ivanDamageMultiplier = 1;
break;
case 3:
GET_PLAYER(play)->ivanFloating = 0;
break;
}
this->usedSpell = 0;
@@ -506,10 +624,6 @@ void UseSpell(Actor* thisx, PlayState* play, u8 started, u8 spellType) {
case 2: // Nayru's
GET_PLAYER(play)->invincibilityTimer = -10;
break;
case 3: // Farore's
GET_PLAYER(play)->hoverBootsTimer = 10;
GET_PLAYER(play)->ivanFloating = 1;
break;
}
gSaveContext.magicState = MAGIC_STATE_METER_FLASH_1;
@@ -577,7 +691,7 @@ void UseItem(uint8_t usedItem, u8 started, Actor* thisx, PlayState* play) {
UseSpell(this, play, started, 2);
break;
case ITEM_FARORES_WIND:
UseSpell(this, play, started, 3);
UseFaroresWind(this, play, started);
break;
case ITEM_HAMMER:
UseHammer(this, play, started);
@@ -4,6 +4,7 @@
#include <libultraship/libultra.h>
#include "global.h"
#include <overlays/actors/ovl_En_Boom/z_en_boom.h>
#include <overlays/actors/ovl_Demo_Effect/z_demo_effect.h>
struct EnPartner;
@@ -43,6 +44,7 @@ typedef struct EnPartner {
EnBoom* boomerangActor;
Actor* hookshotTarget;
DemoEffect* windEffect;
} EnPartner;
#ifdef __cplusplus
@@ -56,4 +58,4 @@ void EnPartner_Draw(Actor* thisx, PlayState* play);
}
#endif
#endif
#endif
@@ -8070,11 +8070,7 @@ void func_8084029C(Player* this, f32 arg1) {
arg1 = 7.25f;
}
if ((this->currentBoots == PLAYER_BOOTS_HOVER ||
(CVarGetInteger(CVAR_ENHANCEMENT("IvanCoopModeEnabled"), 0) && this->ivanFloating)) &&
!(this->actor.bgCheckFlags & 1) &&
(this->hoverBootsTimer != 0 ||
(CVarGetInteger(CVAR_ENHANCEMENT("IvanCoopModeEnabled"), 0) && this->ivanFloating))) {
if ((this->currentBoots == PLAYER_BOOTS_HOVER) && !(this->actor.bgCheckFlags & 1) && (this->hoverBootsTimer != 0)) {
func_8002F8F0(&this->actor, NA_SE_PL_HOBBERBOOTS_LV - SFX_FLAG);
} else if (func_8084021C(this->unk_868, arg1, 29.0f, 10.0f) || func_8084021C(this->unk_868, arg1, 29.0f, 24.0f)) {
Player_PlaySteppingSfx(this, this->linearVelocity);
@@ -11123,17 +11119,14 @@ void Player_UpdateInterface(PlayState* play, Player* this) {
s32 Player_UpdateHoverBoots(Player* this) {
s32 canHoverOnGround;
if ((this->currentBoots == PLAYER_BOOTS_HOVER ||
(CVarGetInteger(CVAR_ENHANCEMENT("IvanCoopModeEnabled"), 0) && this->ivanFloating)) &&
(this->hoverBootsTimer != 0)) {
if ((this->currentBoots == PLAYER_BOOTS_HOVER) && (this->hoverBootsTimer != 0)) {
this->hoverBootsTimer--;
} else {
this->hoverBootsTimer = 0;
}
canHoverOnGround =
(this->currentBoots == PLAYER_BOOTS_HOVER ||
(CVarGetInteger(CVAR_ENHANCEMENT("IvanCoopModeEnabled"), 0) && this->ivanFloating)) &&
(this->currentBoots == PLAYER_BOOTS_HOVER) &&
((this->actor.yDistToWater >= 0.0f) || (func_80838144(sFloorType) >= 0) || func_8083816C(sFloorType));
if (canHoverOnGround && (this->actor.bgCheckFlags & 1) && (this->hoverBootsTimer != 0)) {
@@ -12439,10 +12432,8 @@ void Player_DrawGameplay(PlayState* play, Player* this, s32 lod, Gfx* cullDList,
Matrix_Pop();
}
if ((this->currentBoots == PLAYER_BOOTS_HOVER ||
(CVarGetInteger(CVAR_ENHANCEMENT("IvanCoopModeEnabled"), 0) && this->ivanFloating)) &&
!(this->actor.bgCheckFlags & 1) && !(this->stateFlags1 & PLAYER_STATE1_ON_HORSE) &&
(this->hoverBootsTimer != 0)) {
if ((this->currentBoots == PLAYER_BOOTS_HOVER) && !(this->actor.bgCheckFlags & 1) &&
!(this->stateFlags1 & PLAYER_STATE1_ON_HORSE) && (this->hoverBootsTimer != 0)) {
s32 sp5C;
s32 hoverBootsTimer = this->hoverBootsTimer;