Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions soh/soh/SohGui/SohMenuEnhancements.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1635,6 +1635,9 @@ void SohMenu::AddMenuEnhancements() {
AddWidget(path, "Dogs Follow You Everywhere", WIDGET_CVAR_CHECKBOX)
.CVar(CVAR_ENHANCEMENT("DogFollowsEverywhere"))
.Options(CheckboxOptions().Tooltip("Allows dogs to follow you anywhere you go, even if you leave the Market."));
AddWidget(path, "Richard's Rage", WIDGET_CVAR_CHECKBOX)
.CVar(CVAR_ENHANCEMENT("RichardsRage"))
.Options(CheckboxOptions().Tooltip("Allows Link to carry and throw dogs around like Ruto."));
AddWidget(path, "Rupee Dash Mode", WIDGET_CVAR_CHECKBOX)
.CVar(CVAR_ENHANCEMENT("RupeeDash"))
.Options(CheckboxOptions().Tooltip("Rupees reduce over time, Link suffers damage when the count hits 0."));
Expand Down
115 changes: 110 additions & 5 deletions soh/src/overlays/actors/ovl_En_Dog/z_en_dog.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ void EnDog_RunAway(EnDog* this, PlayState* play);
void EnDog_FaceLink(EnDog* this, PlayState* play);
void EnDog_Wait(EnDog* this, PlayState* play);

// #region SOH [Enhancement] - Richard's Rage
void EnDog_Carried(EnDog* this, PlayState* play);
void EnDog_Thrown(EnDog* this, PlayState* play);
// #endregion

const ActorInit En_Dog_InitVars = {
ACTOR_EN_DOG,
ACTORCAT_NPC,
Expand Down Expand Up @@ -478,17 +483,117 @@ void EnDog_Wait(EnDog* this, PlayState* play) {
}
}

// #region SOH [Enhancement] - Richard's Rage
void EnDog_Carried(EnDog* this, PlayState* play) {
// Link dropped or threw the Richard.
if (Actor_HasNoParent(&this->actor, play)) {
this->actor.gravity = -1.0f;
this->actor.minVelocityY = -10.0f;
this->actor.bgCheckFlags = 0; // Clear stale flags from before carry

// Enable AT collider so the thrown Richard can hit enemies.
this->collider.base.atFlags = AT_ON | AT_TYPE_PLAYER;
this->collider.info.toucher.dmgFlags = DMG_HAMMER;
this->collider.info.toucher.damage = 4;
this->collider.info.toucherFlags = TOUCH_ON;

// Clear carry flag -- Richard is no longer held.
gSaveContext.dogParams &= ~0x4000;

this->nextBehavior = DOG_BARK;
this->actionFunc = EnDog_Thrown;
return;
}

// Signal carry state for scene transitions.
gSaveContext.dogParams |= 0x4000;

// While carried, just sit still.
this->nextBehavior = DOG_SIT;
this->actor.speedXZ = 0.0f;
this->actor.shape.shadowAlpha = 0;
}

void EnDog_Thrown(EnDog* this, PlayState* play) {
// Megaton Hammer impact effects when the Richard hits something mid-flight.
if (this->collider.base.atFlags & AT_HIT) {
this->collider.base.atFlags &= ~AT_HIT;

const s16 quakeIndex = Quake_Add(Play_GetCamera(play, 0), 3);
Quake_SetSpeed(quakeIndex, 27767);
Quake_SetQuakeValues(quakeIndex, 7, 0, 0, 0);
Quake_SetCountdown(quakeIndex, 20);

play->actorCtx.unk_02 = 4;
func_800AA000(0, 255, 20, 150); // Screen shake
Audio_PlayActorSound2(&this->actor, NA_SE_IT_HAMMER_HIT);
}

// Landed on the ground.
if (this->actor.bgCheckFlags & 1) {
// Disable AT collider.
this->collider.base.atFlags = AT_NONE;
this->collider.info.toucherFlags = TOUCH_NONE;

this->actor.speedXZ = 0.0f;
this->actor.velocity.y = 0.0f;
this->nextBehavior = DOG_BARK;
this->actor.shape.shadowAlpha = 255;

this->actionFunc = EnDog_FollowPlayer;
Audio_PlayActorSound2(&this->actor, NA_SE_EV_SMALL_DOG_BARK);
}
}

void EnDog_Update(Actor* thisx, PlayState* play) {
EnDog* this = (EnDog*)thisx;
s32 pad;

// #region SOH [Enhancement] - Richard's Rage
// Offer carry and detect pickup.
if (CVarGetInteger(CVAR_ENHANCEMENT("RichardsRage"), 0)) {
if (Actor_HasParent(&this->actor, play)) {
// Link just picked up the Richard. Set dogParams so it respawns in the next scene.
if (gSaveContext.dogParams == 0) {
gSaveContext.dogParams = this->actor.params & 0x7FFF;
}

this->nextBehavior = DOG_SIT;
this->actor.speedXZ = 0.0f;
this->actionFunc = EnDog_Carried;
} else if (this->actionFunc != EnDog_Carried && this->actionFunc != EnDog_Thrown) {
// Offer carry when not already held or airborne.
Actor_OfferCarry(&this->actor, play);
}
}
// #endregion

EnDog_PlayAnimAndSFX(this);
SkelAnime_Update(&this->skelAnime);
Actor_UpdateBgCheckInfo(play, &this->actor, this->collider.dim.radius, this->collider.dim.height * 0.5f, 0.0f, 5);
Actor_MoveXZGravity(&this->actor);

// #region SOH [Enhancement] - Richard's Rage
// Skip physics and collision while the Richard is being carried -- Link controls the Richard's position.
// Without this, the Richard's OC collider pushes Link around and movement code fights the carry position.
if (this->actionFunc != EnDog_Carried) {
Actor_UpdateBgCheckInfo(play, &this->actor, this->collider.dim.radius,
this->collider.dim.height * 0.5f, 0.0f, 5);
Actor_MoveXZGravity(&this->actor);
}
// #endregion

this->actionFunc(this, play);
Collider_UpdateCylinder(&this->actor, &this->collider);
CollisionCheck_SetOC(play, &play->colChkCtx, &this->collider.base);

// #region SOH [Enhancement] - Richard's Rage
if (this->actionFunc != EnDog_Carried) {
Collider_UpdateCylinder(&this->actor, &this->collider);
CollisionCheck_SetOC(play, &play->colChkCtx, &this->collider.base);
}

// Register AT collider while the Richard is airborne so it can hit enemies.
if (this->actionFunc == EnDog_Thrown) {
CollisionCheck_SetAT(play, &play->colChkCtx, &this->collider.base);
}
// #endregion
}

s32 EnDog_OverrideLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) {
Expand Down Expand Up @@ -520,4 +625,4 @@ void EnDog_Draw(Actor* thisx, PlayState* play) {
SkelAnime_DrawSkeletonOpa(play, &this->skelAnime, EnDog_OverrideLimbDraw, EnDog_PostLimbDraw, this);

CLOSE_DISPS(play->state.gfxCtx);
}
}
54 changes: 46 additions & 8 deletions soh/src/overlays/actors/ovl_player_actor/z_player.c
Original file line number Diff line number Diff line change
Expand Up @@ -3161,6 +3161,38 @@ s32 func_80835644(PlayState* play, Player* this, Actor* arg2) {
return 0;
}

// #region SOH [Enhancement] - Richard's Rage
// Returns true if the held actor should be treated like Ruto for carry interactions (door opening, fall sounds,
// action text).
static s32 Player_HeldActorAllowsInteraction(Player* this) {
if (this->heldActor == NULL) {
return false;
}

if (this->heldActor->id == ACTOR_EN_RU1) {
return true;
}

if (this->heldActor->id == ACTOR_EN_DOG && CVarGetInteger(CVAR_ENHANCEMENT("RichardsRage"), 0)) {
return true;
}

return false;
}

static void Player_HeldActorFallSound(Player* this) {
if (this->heldActor == NULL) {
return;
}

if (this->heldActor->id == ACTOR_EN_RU1) {
Audio_PlayActorSound2(this->heldActor, NA_SE_VO_RT_FALL);
} else if (this->heldActor->id == ACTOR_EN_DOG && CVarGetInteger(CVAR_ENHANCEMENT("RichardsRage"), 0)) {
Audio_PlayActorSound2(this->heldActor, NA_SE_EV_SMALL_DOG_BARK);
}
}
// #endregion

void func_80835688(Player* this, PlayState* play) {
if (!func_80835644(play, this, this->heldActor)) {
Player_SetUpperActionFunc(this, Player_UpperAction_CarryActor);
Expand Down Expand Up @@ -5333,8 +5365,7 @@ s32 Player_ActionHandler_1(Player* this, PlayState* play) {
Vec3f checkPos;

if ((this->doorType != PLAYER_DOORTYPE_NONE) &&
(!(this->stateFlags1 & PLAYER_STATE1_CARRYING_ACTOR) ||
((this->heldActor != NULL) && (this->heldActor->id == ACTOR_EN_RU1)))) {
(!(this->stateFlags1 & PLAYER_STATE1_CARRYING_ACTOR) || Player_HeldActorAllowsInteraction(this))) {
if ((CHECK_BTN_ALL(sControlInput->press.button, BTN_A) || (Player_Action_8084F9A0 == this->actionFunc)) &&
GameInteractor_Should(VB_BE_ABLE_TO_OPEN_DOORS, true)) {
doorActor = this->doorActor;
Expand Down Expand Up @@ -9574,10 +9605,7 @@ void Player_Action_80843CEC(Player* this, PlayState* play) {

void func_80843E14(Player* this, u16 sfxId) {
Player_PlayVoiceSfx(this, sfxId);

if ((this->heldActor != NULL) && (this->heldActor->id == ACTOR_EN_RU1)) {
Audio_PlayActorSound2(this->heldActor, NA_SE_VO_RT_FALL);
}
Player_HeldActorFallSound(this);
}

static FallImpactInfo D_80854600[] = {
Expand Down Expand Up @@ -11016,8 +11044,7 @@ void Player_UpdateInterface(PlayState* play, Player* this) {
}
} else if ((Player_Action_8084E3C4 != this->actionFunc) && !(this->stateFlags2 & PLAYER_STATE2_CRAWLING)) {
if ((this->doorType != PLAYER_DOORTYPE_NONE) &&
(!(this->stateFlags1 & PLAYER_STATE1_CARRYING_ACTOR) ||
((heldActor != NULL) && (heldActor->id == ACTOR_EN_RU1)))) {
(!(this->stateFlags1 & PLAYER_STATE1_CARRYING_ACTOR) || Player_HeldActorAllowsInteraction(this))) {
doAction = DO_ACTION_OPEN;
} else if ((!(this->stateFlags1 & PLAYER_STATE1_CARRYING_ACTOR) || (heldActor == NULL)) &&
(interactRangeActor != NULL) &&
Expand Down Expand Up @@ -12288,6 +12315,17 @@ void Player_Update(Actor* thisx, PlayState* play) {
if (dog != NULL) {
// Room -1 allows actor to cross between rooms, similar to Navi
dog->room = CVarGetInteger(CVAR_ENHANCEMENT("DogFollowsEverywhere"), 0) ? -1 : 0;

// #region SOH [Enhancement] - Richard's Rage
if (CVarGetInteger(CVAR_ENHANCEMENT("RichardsRage"), 1) && gSaveContext.dogParams & 0x4000) {
this->heldActor = dog;
this->interactRangeActor = dog;
dog->parent = &this->actor;
this->actor.child = dog;
this->stateFlags1 |= PLAYER_STATE1_CARRYING_ACTOR;
func_80835688(this, play); // Upper body action (carry pose)
gSaveContext.dogParams &= ~0x4000;
}
}
}
}
Expand Down