diff --git a/soh/soh/Enhancements/game-interactor/GameInteractionEffect.cpp b/soh/soh/Enhancements/game-interactor/GameInteractionEffect.cpp index 5f7547cc48..4a772a2b49 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractionEffect.cpp +++ b/soh/soh/Enhancements/game-interactor/GameInteractionEffect.cpp @@ -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; diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor.cpp b/soh/soh/Enhancements/game-interactor/GameInteractor.cpp index 315500424f..d8e6abdb66 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor.cpp +++ b/soh/soh/Enhancements/game-interactor/GameInteractor.cpp @@ -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) { diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor.h b/soh/soh/Enhancements/game-interactor/GameInteractor.h index 8d2055fbef..abe3c29974 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor.h @@ -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); diff --git a/soh/soh/Network/CrowdControl/CrowdControl.cpp b/soh/soh/Network/CrowdControl/CrowdControl.cpp index 19ac74f276..12a5a643f8 100644 --- a/soh/soh/Network/CrowdControl/CrowdControl.cpp +++ b/soh/soh/Network/CrowdControl/CrowdControl.cpp @@ -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: