Fix Crowd Control Wolfos despawning when scene switch 0 is set (#6591)

Crowd Control passed actor params 0 for EN_WF, so EnWf_Init treated
switchFlag 0 and killed the actor when scene switch 0 was set. Match
vanilla EnEncount1 Wolfos spawns: (0xFF << 8) | 0x00

Also prevent crowd control effects from triggering when link is not in controllable state
This commit is contained in:
Matt Jakubowski
2026-06-05 01:18:26 -05:00
committed by GitHub
parent 90823fad5a
commit 8a0315a2df
4 changed files with 52 additions and 3 deletions
@@ -259,9 +259,12 @@ void ElectrocutePlayer::_Apply() {
// MARK: - KnockbackPlayer
GameInteractionEffectQueryResult KnockbackPlayer::CanBeApplied() {
if (!GameInteractor::IsPlayerInControl()) {
return GameInteractionEffectQueryResult::TemporarilyNotPossible;
}
Player* player = GET_PLAYER(gPlayState);
if (!GameInteractor::IsSaveLoaded(true) || GameInteractor::IsGameplayPaused() ||
player->stateFlags2 & PLAYER_STATE2_CRAWLING) {
if (player->stateFlags2 & PLAYER_STATE2_CRAWLING) {
return GameInteractionEffectQueryResult::TemporarilyNotPossible;
} else {
return GameInteractionEffectQueryResult::Possible;
@@ -51,15 +51,49 @@ bool GameInteractor::IsSaveLoaded(bool allowDbgSave) {
}
bool GameInteractor::IsGameplayPaused() {
if (gPlayState == NULL) {
return true;
}
Player* player = GET_PLAYER(gPlayState);
if (player == NULL) {
return true;
}
return (Player_InBlockingCsMode(gPlayState, player) || gPlayState->pauseCtx.state != 0 ||
gPlayState->msgCtx.msgMode != 0)
? true
: false;
}
bool GameInteractor::IsPlayerInControl() {
if (gPlayState == NULL) {
return false;
}
Player* player = GET_PLAYER(gPlayState);
if (player == NULL) {
return false;
}
if (gSaveContext.gameMode != GAMEMODE_NORMAL) {
return false;
}
if (!((gSaveContext.fileNum >= 0 && gSaveContext.fileNum <= 2) || gSaveContext.fileNum == 0xFF)) {
return false;
}
if (Player_InBlockingCsMode(gPlayState, player) || gPlayState->pauseCtx.state != 0 ||
gPlayState->msgCtx.msgMode != 0 || player->unk_6AD == 4) {
return false;
}
return true;
}
bool GameInteractor::CanSpawnActor() {
return GameInteractor::IsSaveLoaded() && !GameInteractor::IsGameplayPaused();
return GameInteractor::IsPlayerInControl();
}
bool GameInteractor::CanAddOrTakeAmmo(int16_t amount, int16_t item) {
@@ -530,6 +530,7 @@ class GameInteractor {
// Helpers
static bool IsSaveLoaded(bool allowDbgSave = false);
static bool IsGameplayPaused();
static bool IsPlayerInControl();
static bool CanSpawnActor();
static bool CanAddOrTakeAmmo(int16_t amount, int16_t item);
@@ -132,6 +132,10 @@ void CrowdControl::EmitMessage(uint32_t eventId, long timeRemaining, EffectResul
}
CrowdControl::EffectResult CrowdControl::ExecuteEffect(Effect* effect) {
if (!GameInteractor::IsPlayerInControl()) {
return EffectResult::Retry;
}
GameInteractionEffectQueryResult giResult;
if (effect->category == kEffectCatSpawnEnemy) {
giResult = GameInteractor::RawAction::SpawnEnemyWithOffset(effect->spawnParams[0], effect->spawnParams[1],
@@ -149,6 +153,10 @@ CrowdControl::EffectResult CrowdControl::ExecuteEffect(Effect* effect) {
/// Checks if effect can be applied -- should not be used to check for spawn enemy effects.
CrowdControl::EffectResult CrowdControl::CanApplyEffect(Effect* effect) {
assert(effect->category != kEffectCatSpawnEnemy || effect->category != kEffectCatSpawnActor);
if (!GameInteractor::IsPlayerInControl()) {
return EffectResult::Retry;
}
GameInteractionEffectQueryResult giResult = GameInteractor::CanApplyEffect(*effect->giEffect.get());
return TranslateGiEnum(giResult);
@@ -268,6 +276,9 @@ CrowdControl::Effect* CrowdControl::ParseMessage(nlohmann::json dataReceived) {
break;
case kEffectSpawnWolfos:
effect->spawnParams[0] = ACTOR_EN_WF;
// Match EnEncount1 wolfos spawner (0xFF00): high byte must be 0xFF so EnWf_Init does not treat
// switchFlag 0; Flags_GetSwitch(play, 0) is true in many scenes and would instantly kill the actor.
effect->spawnParams[1] = (0xFF << 8) | 0x00; // normal Wolfos; high byte 0xFF = no switch (vanilla encount)
effect->category = kEffectCatSpawnEnemy;
break;
case kEffectSpawnWallmaster: