Skip to content

Commit

Permalink
Fix a possible softlock after Gyorg's inhale attack
Browse files Browse the repository at this point in the history
This forces the FreezeLink flag to be cleared after an inhale attack
has ended because a very rare bug causes Link to be frozen after
the attack, even though a state handler change is already supposed
to clear the flag.
  • Loading branch information
leoetlino committed Apr 24, 2020
1 parent 81ff580 commit fe301d4
Showing 1 changed file with 28 additions and 2 deletions.
30 changes: 28 additions & 2 deletions source/rst/fixes/boss.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -317,12 +317,31 @@ extern "C" RST_HOOK int rst_GetGyorgCollisionResponse(game::act::BossGyorg* gyor
return util::Contains(std::array{stunned, stunned_2, stunned_attacked}, calc_fn) ? 2 : 1;
}

struct GyorgFixState {
std::optional<u32> frames_since_inhale_attack_end;
};

void FixGyorg() {
static std::optional<GyorgFixState> state{};
auto* gctx = GetContext().gctx;
auto* gyorg =
gctx->FindActorWithId<game::act::BossGyorg>(game::act::Id::BossGyorg, game::act::Type::Boss);
if (!gyorg)
if (!gyorg) {
state.reset();
return;
}

if (!state) {
util::Print("%s: initialising state", __func__);
state.emplace();
}

if (state->frames_since_inhale_attack_end.has_value())
++*state->frames_since_inhale_attack_end;

const auto gyorg_eating_link = (decltype(gyorg->gyorg_calc))util::GetAddr(0x557900);
if (gyorg->gyorg_calc == gyorg_eating_link)
state->frames_since_inhale_attack_end = 0;

// Disable the first stun cutscene, which is known to be buggy.
gyorg->field_F24 |= 1;
Expand All @@ -334,12 +353,19 @@ void FixGyorg() {
// switches to the frozen state (0x20d96c), even though he's supposed to be in the "held" state.
// If this situation is detected, clear the FreezeLink flag.
const auto player = gctx->GetPlayerActor();
const auto gyorg_eating_link = (decltype(gyorg->gyorg_calc))util::GetAddr(0x557900);
const auto link_handle_frozen = (decltype(player->state_handler_fn))util::GetAddr(0x20D96C);
if (gyorg->gyorg_calc == gyorg_eating_link && player->state_handler_fn == link_handle_frozen &&
player->flags1.TestAndClear(game::act::Player::Flag1::FreezeLink)) {
util::Print("%s: clearing FreezeLink flag", __func__);
}

// Hacky workaround for a very rare bug that causes Link to be frozen *after* being spit out of
// Gyorg, despite the fact that a state handler change is supposed to clear the FreezeLink flag...
// This forces the flag to be cleared after an inhale attack has ended.
if (state->frames_since_inhale_attack_end && *state->frames_since_inhale_attack_end <= 30 &&
player->flags1.TestAndClear(game::act::Player::Flag1::FreezeLink)) {
util::Print("%s: clearing FreezeLink flag after inhale attack", __func__);
}
}

struct TwinmoldFixState {
Expand Down

0 comments on commit fe301d4

Please sign in to comment.