diff --git a/soh/CMakeLists.txt b/soh/CMakeLists.txt index 6069c5d0e..98997bc76 100644 --- a/soh/CMakeLists.txt +++ b/soh/CMakeLists.txt @@ -258,6 +258,12 @@ set(Header_Files__soh__Enhancements__item_tables source_group("Header Files\\soh\\Enhancements\\item-tables" FILES ${Header_Files__soh__Enhancements__item_tables}) +set(Header_Files__soh__Enhancements__game_interactor + "soh/Enhancements/game-interactor/GameInteractor.h" + "soh/Enhancements/game-interactor/GameInteractionEffect.h" +) +source_group("Header Files\\soh\\Enhancements\\game-interactor" FILES ${Header_Files__soh__Enhancements__game_interactor}) + if (BUILD_CROWD_CONTROL) set(Header_Files__soh__Enhancements__crowd_control "soh/Enhancements/crowd-control/CrowdControl.h" @@ -603,6 +609,14 @@ set(Source_Files__soh__Enhancements__item_tables source_group("Source Files\\soh\\Enhancements\\item-tables" FILES ${Source_Files__soh__Enhancements__item_tables}) +set(Source_Files__soh__Enhancements__game_interactor + "soh/Enhancements/game-interactor/GameInteractor.cpp" + "soh/Enhancements/game-interactor/GameInteractor_RawAction.cpp" + "soh/Enhancements/game-interactor/GameInteractor_State.cpp" + "soh/Enhancements/game-interactor/GameInteractionEffect.cpp" +) +source_group("Source Files\\soh\\Enhancements\\game-interactor" FILES ${Source_Files__soh__Enhancements__game_interactor}) + if (BUILD_CROWD_CONTROL) set(Source_Files__soh__Enhancements__crowd_control "soh/Enhancements/crowd-control/CrowdControl.cpp" @@ -1821,6 +1835,7 @@ set(ALL_FILES ${Header_Files__soh__Enhancements__randomizer__3drando} ${Header_Files__soh__Enhancements__item_tables} ${Header_Files__soh__Enhancements__custom_message} + ${Header_Files__soh__Enhancements__game_interactor} ${Header_Files__soh__Enhancements__crowd_control} ${Source_Files__soh} ${Source_Files__soh__Enhancements} @@ -1834,6 +1849,7 @@ set(ALL_FILES ${Source_Files__soh__Enhancements__randomizer__3drando__location_access} ${Source_Files__soh__Enhancements__item_tables} ${Source_Files__soh__Enhancements__custom_message} + ${Source_Files__soh__Enhancements__game_interactor} ${Source_Files__soh__Enhancements__crowd_control} ${Source_Files__src__boot} ${Source_Files__src__buffers} diff --git a/soh/include/functions.h b/soh/include/functions.h index 70def4ac2..1befbf8aa 100644 --- a/soh/include/functions.h +++ b/soh/include/functions.h @@ -1079,8 +1079,6 @@ void Interface_SetDoAction(PlayState* play, u16 action); void Interface_SetNaviCall(PlayState* play, u16 naviCallState); void Interface_LoadActionLabelB(PlayState* play, u16 action); s32 Health_ChangeBy(PlayState* play, s16 healthChange); -void Health_GiveHearts(s16 hearts); -void Health_RemoveHearts(s16 hearts); void Rupees_ChangeBy(s16 rupeeChange); void Inventory_ChangeAmmo(s16 item, s16 ammoChange); void Magic_Fill(PlayState* play); diff --git a/soh/soh/Enhancements/crowd-control/CrowdControl.cpp b/soh/soh/Enhancements/crowd-control/CrowdControl.cpp index 48af4c34e..fd2aef94e 100644 --- a/soh/soh/Enhancements/crowd-control/CrowdControl.cpp +++ b/soh/soh/Enhancements/crowd-control/CrowdControl.cpp @@ -17,10 +17,6 @@ extern "C" { extern PlayState* gPlayState; } -#include "../debugconsole.h" - -#define CMD_EXECUTE SohImGui::GetConsole()->Dispatch - #define EFFECT_HIGH_GRAVITY "high_gravity" #define EFFECT_LOW_GRAVITY "low_gravity" #define EFFECT_DAMAGE_MULTIPLIER "damage_multiplier" @@ -52,6 +48,7 @@ extern PlayState* gPlayState; #define EFFECT_INCREASE_SPEED "increase_speed" #define EFFECT_DECREASE_SPEED "decrease_speed" #define EFFECT_NO_Z_TARGETING "no_z" +#define EFFECT_GIVE_DEKU_SHIELD "give_dekushield" #define EFFECT_SPAWN_WALLMASTER "spawn_wallmaster" #define EFFECT_SPAWN_ARWING "spawn_arwing" @@ -66,6 +63,18 @@ extern PlayState* gPlayState; #define EFFECT_SPAWN_LIKE_LIKE "spawn_likelike" #define EFFECT_SPAWN_CUCCO_STORM "cucco_storm" +#define EFFECT_CAT_UI "ui" +#define EFFECT_CAT_GRAVITY "gravity" +#define EFFECT_CAT_LINK_SIZE "link_size" +#define EFFECT_CAT_PACIFIST "pacifist" +#define EFFECT_CAT_NO_Z "no_z" +#define EFFECT_CAT_WEATHER "weather" +#define EFFECT_CAT_REVERSE_CONTROLS "reverse_controls" +#define EFFECT_CAT_BOOTS "boots" +#define EFFECT_CAT_SPEED "speed" +#define EFFECT_CAT_DAMAGE_TAKEN "damage_taken" +#define EFFECT_CAT_SPAWN_ENEMY "spawn_enemy" +#define EFFECT_CAT_NONE "none" void CrowdControl::Init() { SDLNet_Init(); @@ -142,34 +151,29 @@ void CrowdControl::ListenToServer() { continue; } - // If effect is a one off run, let's execute + // If effect is not a timed effect, execute and return result. if (!incomingEffect->timeRemaining) { - EffectResult result = - ExecuteEffect(incomingEffect->type.c_str(), incomingEffect->value, false); + EffectResult result = CrowdControl::ExecuteEffect(incomingEffect); EmitMessage(tcpsock, incomingEffect->id, incomingEffect->timeRemaining, result); } else { - // check if a conflicting event is already active + // If another timed effect is already active that conflicts with the incoming effect. bool isConflictingEffectActive = false; - for (Effect* pack : activeEffects) { - if (pack != incomingEffect && pack->category == incomingEffect->category && - pack->id < incomingEffect->id) { + for (Effect* effect : activeEffects) { + if (effect != incomingEffect && effect->category == incomingEffect->category && effect->id < incomingEffect->id) { isConflictingEffectActive = true; - EmitMessage(tcpsock, incomingEffect->id, incomingEffect->timeRemaining, - EffectResult::Retry); - + EmitMessage(tcpsock, incomingEffect->id, incomingEffect->timeRemaining, EffectResult::Retry); break; } } - // check if effect can be executed - EffectResult result = - ExecuteEffect(incomingEffect->type.c_str(), incomingEffect->value, true); - if (result == EffectResult::Retry || result == EffectResult::Failure) { - EmitMessage(tcpsock, incomingEffect->id, incomingEffect->timeRemaining, result); - continue; - } - if (!isConflictingEffectActive) { + // Check if effect can be applied, if it can't, let CC know. + EffectResult result = CrowdControl::CanApplyEffect(incomingEffect); + if (result == EffectResult::Retry || result == EffectResult::Failure) { + EmitMessage(tcpsock, incomingEffect->id, incomingEffect->timeRemaining, result); + continue; + } + activeEffectsMutex.lock(); activeEffects.push_back(incomingEffect); activeEffectsMutex.unlock(); @@ -187,27 +191,31 @@ void CrowdControl::ListenToServer() { void CrowdControl::ProcessActiveEffects() { while (isEnabled) { - // we only want to send events when status changes, on start we send Success, - // if it fails at some point, we send Pause, and when it starts to succeed again we send Success. + // We only want to send events when status changes, on start we send Success. + // If it fails at some point, we send Pause, and when it starts to succeed again we send Success. + // CC uses this to pause the timer on the overlay. activeEffectsMutex.lock(); auto it = activeEffects.begin(); while (it != activeEffects.end()) { Effect *effect = *it; - EffectResult result = ExecuteEffect(effect->type.c_str(), effect->value, false); + EffectResult result = CrowdControl::ExecuteEffect(effect); + if (result == EffectResult::Success) { - // If time remaining has reached 0, we have finished the effect + // If time remaining has reached 0, we have finished the effect. if (effect->timeRemaining <= 0) { it = activeEffects.erase(std::remove(activeEffects.begin(), activeEffects.end(), effect), activeEffects.end()); - RemoveEffect(effect->type.c_str()); - + GameInteractor::RemoveEffect(effect->giEffect); delete effect; } else { - // If we have a success after previously being paused, fire Resume event + // If we have a success after previously being paused, tell CC to resume timer. if (effect->isPaused) { effect->isPaused = false; EmitMessage(tcpsock, effect->id, effect->timeRemaining, EffectResult::Resumed); + // If not paused before, subtract time from the timer and send a Success event if + // the result is different from the last time this was ran. + // Timed events are put on a thread that runs once per second. } else { effect->timeRemaining -= 1000; if (result != effect->lastExecutionResult) { @@ -215,7 +223,6 @@ void CrowdControl::ProcessActiveEffects() { EmitMessage(tcpsock, effect->id, effect->timeRemaining, EffectResult::Success); } } - it++; } } else { // Timed effects only do Success or Retry @@ -223,7 +230,6 @@ void CrowdControl::ProcessActiveEffects() { effect->isPaused = true; EmitMessage(tcpsock, effect->id, effect->timeRemaining, EffectResult::Paused); } - it++; } } @@ -260,328 +266,224 @@ CrowdControl::Effect* CrowdControl::ParseMessage(char payload[512]) { effect->lastExecutionResult = EffectResult::Initiate; effect->id = dataReceived["id"]; auto parameters = dataReceived["parameters"]; - if (parameters.size() > 0) { - effect->value = dataReceived["parameters"][0]; - } - effect->type = dataReceived["code"].get(); + auto effectName = dataReceived["code"].get(); - if (effect->type == EFFECT_HIGH_GRAVITY || effect->type == EFFECT_LOW_GRAVITY) { - effect->category = "gravity"; - effect->timeRemaining = 30000; - } else if (effect->type == EFFECT_DAMAGE_MULTIPLIER || effect->type == EFFECT_DEFENSE_MULTIPLIER) { - effect->category = "defense"; - effect->timeRemaining = 30000; - } else if (effect->type == EFFECT_GIANT_LINK || effect->type == EFFECT_MINISH_LINK || - effect->type == EFFECT_INVISIBLE_LINK || effect->type == EFFECT_PAPER_LINK) { - effect->category = "link_size"; - effect->timeRemaining = 30000; - } else if (effect->type == EFFECT_FREEZE || effect->type == EFFECT_DAMAGE || effect->type == EFFECT_HEAL || - effect->type == EFFECT_KNOCKBACK || effect->type == EFFECT_ELECTROCUTE || - effect->type == EFFECT_BURN || effect->type == EFFECT_KILL) { - effect->category = "link_damage"; - } else if (effect->type == EFFECT_HOVER_BOOTS || effect->type == EFFECT_IRON_BOOTS) { - effect->category = "boots"; - effect->timeRemaining = 30000; - } else if (effect->type == EFFECT_ADD_HEART_CONTAINER || effect->type == EFFECT_REMOVE_HEART_CONTAINER) { - effect->category = "heart_container"; - } else if (effect->type == EFFECT_NO_UI) { - effect->category = "ui"; + if (parameters.size() > 0) { + effect->value[0] = dataReceived["parameters"][0]; + } + + // Assign GameInteractionEffect + values to CC effect. + // Categories are mostly used for checking for conflicting timed effects. + if (effectName == EFFECT_ADD_HEART_CONTAINER) { + effect->giEffect = new GameInteractionEffect::ModifyHeartContainers(); + effect->giEffect->parameter = 1; + } else if (effectName == EFFECT_REMOVE_HEART_CONTAINER) { + effect->giEffect = new GameInteractionEffect::ModifyHeartContainers(); + effect->giEffect->parameter = -1; + } else if (effectName == EFFECT_FILL_MAGIC) { + effect->giEffect = new GameInteractionEffect::FillMagic(); + } else if (effectName == EFFECT_EMPTY_MAGIC) { + effect->giEffect = new GameInteractionEffect::EmptyMagic(); + } else if (effectName == EFFECT_ADD_RUPEES) { + effect->giEffect = new GameInteractionEffect::ModifyRupees(); + } else if (effectName == EFFECT_REMOVE_RUPEES) { + effect->giEffect = new GameInteractionEffect::ModifyRupees(); + effect->paramMultiplier = -1; + } else if (effectName == EFFECT_NO_UI) { + effect->category = EFFECT_CAT_UI; effect->timeRemaining = 60000; - } else if (effect->type == EFFECT_FILL_MAGIC || effect->type == EFFECT_EMPTY_MAGIC) { - effect->category = "magic"; - } else if (effect->type == EFFECT_OHKO) { - effect->category = "ohko"; + effect->giEffect = new GameInteractionEffect::NoUI(); + } else if (effectName == EFFECT_HIGH_GRAVITY) { + effect->category = EFFECT_CAT_GRAVITY; effect->timeRemaining = 30000; - } else if (effect->type == EFFECT_PACIFIST) { - effect->category = "pacifist"; + effect->giEffect = new GameInteractionEffect::ModifyGravity(); + effect->giEffect->parameter = GI_GRAVITY_LEVEL_HEAVY; + } else if (effectName == EFFECT_LOW_GRAVITY) { + effect->category = EFFECT_CAT_GRAVITY; + effect->timeRemaining = 30000; + effect->giEffect = new GameInteractionEffect::ModifyGravity(); + effect->giEffect->parameter = GI_GRAVITY_LEVEL_LIGHT; + } else if (effectName == EFFECT_KILL) { + effect->giEffect = new GameInteractionEffect::SetPlayerHealth(); + effect->value[0] = 0; + } else if (effectName == EFFECT_FREEZE) { + effect->giEffect = new GameInteractionEffect::FreezePlayer(); + } else if (effectName == EFFECT_BURN) { + effect->giEffect = new GameInteractionEffect::BurnPlayer(); + } else if (effectName == EFFECT_ELECTROCUTE) { + effect->giEffect = new GameInteractionEffect::ElectrocutePlayer(); + } else if (effectName == EFFECT_KNOCKBACK) { + effect->giEffect = new GameInteractionEffect::KnockbackPlayer(); + } else if (effectName == EFFECT_HEAL) { + effect->giEffect = new GameInteractionEffect::ModifyHealth(); + } else if (effectName == EFFECT_DAMAGE) { + effect->giEffect = new GameInteractionEffect::ModifyHealth(); + effect->paramMultiplier = -1; + } else if (effectName == EFFECT_GIANT_LINK) { + effect->category = EFFECT_CAT_LINK_SIZE; + effect->timeRemaining = 30000; + effect->giEffect = new GameInteractionEffect::ModifyLinkSize(); + effect->giEffect->parameter = GI_LINK_SIZE_GIANT; + } else if (effectName == EFFECT_MINISH_LINK) { + effect->category = EFFECT_CAT_LINK_SIZE; + effect->timeRemaining = 30000; + effect->giEffect = new GameInteractionEffect::ModifyLinkSize(); + effect->giEffect->parameter = GI_LINK_SIZE_MINISH; + } else if (effectName == EFFECT_PAPER_LINK) { + effect->category = EFFECT_CAT_LINK_SIZE; + effect->timeRemaining = 30000; + effect->giEffect = new GameInteractionEffect::ModifyLinkSize(); + effect->giEffect->parameter = GI_LINK_SIZE_PAPER; + } else if (effectName == EFFECT_INVISIBLE_LINK) { + effect->category = EFFECT_CAT_LINK_SIZE; + effect->timeRemaining = 30000; + effect->giEffect = new GameInteractionEffect::InvisibleLink(); + } else if (effectName == EFFECT_PACIFIST) { + effect->category = EFFECT_CAT_PACIFIST; effect->timeRemaining = 15000; - } else if (effect->type == EFFECT_RAINSTORM) { - effect->category = "weather"; + effect->giEffect = new GameInteractionEffect::PacifistMode(); + } else if (effectName == EFFECT_NO_Z_TARGETING) { + effect->category = EFFECT_CAT_NO_Z; effect->timeRemaining = 30000; - } else if (effect->type == EFFECT_REVERSE_CONTROLS) { - effect->category = "controls"; + effect->giEffect = new GameInteractionEffect::DisableZTargeting(); + } else if (effectName == EFFECT_RAINSTORM) { + effect->category = EFFECT_CAT_WEATHER; + effect->timeRemaining = 30000; + effect->giEffect = new GameInteractionEffect::WeatherRainstorm(); + } else if (effectName == EFFECT_REVERSE_CONTROLS) { + effect->category = EFFECT_CAT_REVERSE_CONTROLS; effect->timeRemaining = 60000; - } else if (effect->type == EFFECT_ADD_RUPEES || effect->type == EFFECT_REMOVE_RUPEES) { - effect->category = "rupees"; - } else if (effect->type == EFFECT_INCREASE_SPEED || effect->type == EFFECT_DECREASE_SPEED) { - effect->category = "speed"; + effect->giEffect = new GameInteractionEffect::ReverseControls(); + } else if (effectName == EFFECT_IRON_BOOTS) { + effect->category = EFFECT_CAT_BOOTS; effect->timeRemaining = 30000; - } else if (effect->type == EFFECT_NO_Z_TARGETING) { - effect->category = "no_z"; + effect->giEffect = new GameInteractionEffect::ForceEquipBoots(); + effect->giEffect->parameter = PLAYER_BOOTS_IRON; + } else if (effectName == EFFECT_HOVER_BOOTS) { + effect->category = EFFECT_CAT_BOOTS; effect->timeRemaining = 30000; - } else if (effect->type == EFFECT_SPAWN_WALLMASTER || effect->type == EFFECT_SPAWN_ARWING || - effect->type == EFFECT_SPAWN_DARK_LINK || effect->type == EFFECT_SPAWN_STALFOS || - effect->type == EFFECT_SPAWN_WOLFOS || effect->type == EFFECT_SPAWN_FREEZARD || - effect->type == EFFECT_SPAWN_KEESE || effect->type == EFFECT_SPAWN_ICE_KEESE || - effect->type == EFFECT_SPAWN_FIRE_KEESE || effect->type == EFFECT_SPAWN_TEKTITE || - effect->type == EFFECT_SPAWN_LIKE_LIKE || effect->type == EFFECT_SPAWN_CUCCO_STORM) { - effect->category = "spawn"; - } else { - effect->category = "none"; - effect->timeRemaining = 0; + effect->giEffect = new GameInteractionEffect::ForceEquipBoots(); + effect->giEffect->parameter = PLAYER_BOOTS_HOVER;; + } else if (effectName == EFFECT_GIVE_DEKU_SHIELD) { + effect->giEffect = new GameInteractionEffect::GiveDekuShield(); + } else if (effectName == EFFECT_INCREASE_SPEED) { + effect->category = EFFECT_CAT_SPEED; + effect->timeRemaining = 30000; + effect->giEffect = new GameInteractionEffect::ModifyRunSpeedModifier(); + effect->giEffect->parameter = 2; + } else if (effectName == EFFECT_DECREASE_SPEED) { + effect->category = EFFECT_CAT_SPEED; + effect->timeRemaining = 30000; + effect->giEffect = new GameInteractionEffect::ModifyRunSpeedModifier(); + effect->giEffect->parameter = -2; + } else if (effectName == EFFECT_OHKO) { + effect->category = EFFECT_CAT_DAMAGE_TAKEN; + effect->timeRemaining = 30000; + effect->giEffect = new GameInteractionEffect::OneHitKO(); + } else if (effectName == EFFECT_DAMAGE_MULTIPLIER) { + effect->category = EFFECT_CAT_DAMAGE_TAKEN; + effect->timeRemaining = 30000; + effect->giEffect = new GameInteractionEffect::ModifyDefenseModifier(); + effect->paramMultiplier = -1; + } else if (effectName == EFFECT_DEFENSE_MULTIPLIER) { + effect->category = EFFECT_CAT_DAMAGE_TAKEN; + effect->timeRemaining = 30000; + effect->giEffect = new GameInteractionEffect::ModifyDefenseModifier(); + } else if (effectName == EFFECT_SPAWN_CUCCO_STORM) { + effect->giEffect = new GameInteractionEffect::SpawnCuccoStorm(); + } else if (effectName == EFFECT_SPAWN_WALLMASTER) { + effect->value[0] = ACTOR_EN_WALLMAS; + effect->category = EFFECT_CAT_SPAWN_ENEMY; + } else if (effectName == EFFECT_SPAWN_ARWING) { + effect->value[0] = ACTOR_EN_CLEAR_TAG; + // Parameter for no cutscene Arwing + effect->value[1] = 1; + effect->category = EFFECT_CAT_SPAWN_ENEMY; + } else if (effectName == EFFECT_SPAWN_DARK_LINK) { + effect->value[0] = ACTOR_EN_TORCH2; + effect->category = EFFECT_CAT_SPAWN_ENEMY; + } else if (effectName == EFFECT_SPAWN_STALFOS) { + effect->value[0] = ACTOR_EN_TEST; + // Parameter for gravity-obeying Stalfos + effect->value[1] = 2; + effect->category = EFFECT_CAT_SPAWN_ENEMY; + } else if (effectName == EFFECT_SPAWN_WOLFOS) { + effect->value[0] = ACTOR_EN_WF; + effect->category = EFFECT_CAT_SPAWN_ENEMY; + } else if (effectName == EFFECT_SPAWN_FREEZARD) { + effect->value[0] = ACTOR_EN_FZ; + effect->category = EFFECT_CAT_SPAWN_ENEMY; + } else if (effectName == EFFECT_SPAWN_KEESE) { + effect->value[0] = ACTOR_EN_FIREFLY; + // Parameter for normal keese + effect->value[1] = 2; + effect->category = EFFECT_CAT_SPAWN_ENEMY; + } else if (effectName == EFFECT_SPAWN_ICE_KEESE) { + effect->value[0] = ACTOR_EN_FIREFLY; + // Parameter for ice keese + effect->value[1] = 4; + effect->category = EFFECT_CAT_SPAWN_ENEMY; + } else if (effectName == EFFECT_SPAWN_FIRE_KEESE) { + effect->value[0] = ACTOR_EN_FIREFLY; + // Parameter for fire keese + effect->value[1] = 1; + effect->category = EFFECT_CAT_SPAWN_ENEMY; + } else if (effectName == EFFECT_SPAWN_TEKTITE) { + effect->value[0] = ACTOR_EN_TITE; + effect->category = EFFECT_CAT_SPAWN_ENEMY; + } else if (effectName == EFFECT_SPAWN_LIKE_LIKE) { + effect->value[0] = ACTOR_EN_RR; + effect->category = EFFECT_CAT_SPAWN_ENEMY; + } + + // If no value is specifically set, default to using whatever CC sends us. + // Values are used for various things depending on the effect, but they + // usually represent the "amount" of an effect. Amount of hearts healed, + // strength of knockback, etc. + if (effect->giEffect != NULL) { + if (!effect->giEffect->parameter && effect->value[0]) { + effect->giEffect->parameter = effect->value[0] * effect->paramMultiplier; + } + } + + if (effect->category == "") { + effect->category = EFFECT_CAT_NONE; } return effect; } -CrowdControl::EffectResult CrowdControl::ExecuteEffect(std::string effectId, uint32_t value, bool dryRun) { - // Don't execute effect and don't advance timer when the player is not in a proper loaded savefile - // and when they're busy dying. - if (gPlayState == NULL || gPlayState->gameOverCtx.state > 0 || gSaveContext.fileNum < 0 || gSaveContext.fileNum > 2) { - return EffectResult::Retry; +CrowdControl::EffectResult CrowdControl::ExecuteEffect(Effect* effect) { + GameInteractionEffectQueryResult giResult; + if (effect->category == EFFECT_CAT_SPAWN_ENEMY) { + giResult = GameInteractor::RawAction::SpawnEnemyWithOffset(effect->value[0], effect->value[1]); + } else { + giResult = GameInteractor::ApplyEffect(effect->giEffect); } - Player* player = GET_PLAYER(gPlayState); - - if (player != NULL) { - if (effectId == EFFECT_ADD_HEART_CONTAINER) { - if (gSaveContext.healthCapacity >= 0x140) { - return EffectResult::Failure; - } - - if (dryRun == 0) CMD_EXECUTE(EFFECT_ADD_HEART_CONTAINER); - return EffectResult::Success; - } else if (effectId == EFFECT_REMOVE_HEART_CONTAINER) { - if ((gSaveContext.healthCapacity - 0x10) <= 0) { - return EffectResult::Failure; - } - - if (dryRun == 0) CMD_EXECUTE(EFFECT_REMOVE_HEART_CONTAINER); - return EffectResult::Success; - } else if (effectId == EFFECT_FILL_MAGIC) { - if (!gSaveContext.isMagicAcquired) { - return EffectResult::Failure; - } - - if (gSaveContext.magic >= (gSaveContext.isDoubleMagicAcquired + 1) + 0x30) { - return EffectResult::Failure; - } - - if (dryRun == 0) CMD_EXECUTE(EFFECT_FILL_MAGIC); - return EffectResult::Success; - } else if (effectId == EFFECT_EMPTY_MAGIC) { - if (!gSaveContext.isMagicAcquired || gSaveContext.magic <= 0) { - return EffectResult::Failure; - } - - if (dryRun == 0) CMD_EXECUTE(EFFECT_EMPTY_MAGIC); - return EffectResult::Success; - } else if (effectId == EFFECT_ADD_RUPEES) { - if (dryRun == 0) CMD_EXECUTE(fmt::format("update_rupees {}", value)); - return EffectResult::Success; - } else if (effectId == EFFECT_REMOVE_RUPEES) { - if (gSaveContext.rupees - value < 0) { - return EffectResult::Failure; - } - - if (dryRun == 0) CMD_EXECUTE(fmt::format("update_rupees -{}", value)); - return EffectResult::Success; - } - } - - if (player != NULL && !Player_InBlockingCsMode(gPlayState, player) && gPlayState->pauseCtx.state == 0 - && gPlayState->msgCtx.msgMode == 0) { - if (effectId == EFFECT_HIGH_GRAVITY) { - if (dryRun == 0) CMD_EXECUTE("gravity 2"); - return EffectResult::Success; - } else if (effectId == EFFECT_LOW_GRAVITY) { - if (dryRun == 0) CMD_EXECUTE("gravity 0"); - return EffectResult::Success; - } else if (effectId == EFFECT_KILL - || effectId == EFFECT_FREEZE - || effectId == EFFECT_BURN - || effectId == EFFECT_ELECTROCUTE - || effectId == EFFECT_SPAWN_CUCCO_STORM - ) { - if (PlayerGrounded(player)) { - if (dryRun == 0) CMD_EXECUTE(fmt::format("{}", effectId)); - return EffectResult::Success; - } - return EffectResult::Failure; - } else if (effectId == EFFECT_HEAL - || effectId == EFFECT_KNOCKBACK - ) { - if (dryRun == 0) CMD_EXECUTE(fmt::format("{} {}", effectId, value)); - return EffectResult::Success; - } else if (effectId == EFFECT_GIANT_LINK - || effectId == EFFECT_MINISH_LINK - || effectId == EFFECT_NO_UI - || effectId == EFFECT_INVISIBLE_LINK - || effectId == EFFECT_PAPER_LINK - || effectId == EFFECT_NO_Z_TARGETING - || effectId == EFFECT_OHKO - || effectId == EFFECT_PACIFIST - || effectId == EFFECT_RAINSTORM - ) { - if (dryRun == 0) CMD_EXECUTE(fmt::format("{} 1", effectId)); - return EffectResult::Success; - } else if (effectId == EFFECT_REVERSE_CONTROLS) { - if (dryRun == 0) CMD_EXECUTE("reverse_controls 1"); - return EffectResult::Success; - } else if (effectId == EFFECT_IRON_BOOTS) { - if (dryRun == 0) CMD_EXECUTE("boots iron"); - return EffectResult::Success; - } else if (effectId == EFFECT_HOVER_BOOTS) { - if (dryRun == 0) CMD_EXECUTE("boots hover"); - return EffectResult::Success; - } else if (effectId == "give_dekushield") { - if (dryRun == 0) CMD_EXECUTE("givedekushield"); - return EffectResult::Success; - } else if (effectId == EFFECT_SPAWN_WALLMASTER - || effectId == EFFECT_SPAWN_ARWING - || effectId == EFFECT_SPAWN_DARK_LINK - || effectId == EFFECT_SPAWN_STALFOS - || effectId == EFFECT_SPAWN_WOLFOS - || effectId == EFFECT_SPAWN_FREEZARD - || effectId == EFFECT_SPAWN_KEESE - || effectId == EFFECT_SPAWN_ICE_KEESE - || effectId == EFFECT_SPAWN_FIRE_KEESE - || effectId == EFFECT_SPAWN_TEKTITE - || effectId == EFFECT_SPAWN_LIKE_LIKE - ) { - if (dryRun == 0) { - if (CrowdControl::SpawnEnemy(effectId)) { - return EffectResult::Success; - } else { - return EffectResult::Failure; - } - } - return EffectResult::Success; - } else if (effectId == EFFECT_INCREASE_SPEED) { - if (dryRun == 0) CMD_EXECUTE("speed_modifier 2"); - return EffectResult::Success; - } else if (effectId == EFFECT_DECREASE_SPEED) { - if (dryRun == 0) CMD_EXECUTE("speed_modifier -2"); - return EffectResult::Success; - } else if (effectId == EFFECT_DAMAGE_MULTIPLIER) { - if (dryRun == 0) CMD_EXECUTE(fmt::format("defense_modifier -{}", value)); - return EffectResult::Success; - } else if (effectId == EFFECT_DEFENSE_MULTIPLIER) { - if (dryRun == 0) CMD_EXECUTE(fmt::format("defense_modifier {}", value)); - return EffectResult::Success; - } else if (effectId == EFFECT_DAMAGE) { - if ((gSaveContext.health - (16 * value)) <= 0) { - return EffectResult::Failure; - } - - if (dryRun == 0) CMD_EXECUTE(fmt::format("{} {}", effectId, value)); - return EffectResult::Success; - } - } - - return EffectResult::Retry; + return TranslateGiEnum(giResult); } -bool CrowdControl::SpawnEnemy(std::string effectId) { - Player* player = GET_PLAYER(gPlayState); - - int enemyId = 0; - int enemyParams = 0; - float posXOffset = 0; - float posYOffset = 0; - float posZOffset = 0; - - if (effectId == EFFECT_SPAWN_WALLMASTER) { - enemyId = 17; - } else if (effectId == EFFECT_SPAWN_ARWING) { - // Don't allow Arwings in certain areas because they cause issues. - // Locations: King dodongo room, Morpha room, Twinrova room, Ganondorf room, Fishing pond, Ganon's room - // TODO: Swap this to disabling the option in CC options menu instead. - if (gPlayState->sceneNum == SCENE_DDAN_BOSS || gPlayState->sceneNum == SCENE_MIZUSIN_BS || - gPlayState->sceneNum == SCENE_JYASINBOSS || gPlayState->sceneNum == SCENE_GANON_BOSS || - gPlayState->sceneNum == SCENE_TURIBORI || gPlayState->sceneNum == SCENE_GANON_DEMO) { - return 0; - } - enemyId = 315; - enemyParams = 1; - posYOffset = 100; - } else if (effectId == EFFECT_SPAWN_DARK_LINK) { - enemyId = 51; - posXOffset = 75; - posYOffset = 50; - } else if (effectId == EFFECT_SPAWN_STALFOS) { - enemyId = 2; - enemyParams = 2; - posXOffset = 75; - posYOffset = 50; - } else if (effectId == EFFECT_SPAWN_WOLFOS) { - enemyId = 431; - posXOffset = 75; - posYOffset = 50; - } else if (effectId == EFFECT_SPAWN_FREEZARD) { - enemyId = 289; - posXOffset = 75; - posYOffset = 50; - } else if (effectId == EFFECT_SPAWN_KEESE) { - enemyId = 19; - enemyParams = 2; - posXOffset = 75; - posYOffset = 50; - } else if (effectId == EFFECT_SPAWN_ICE_KEESE) { - enemyId = 19; - enemyParams = 4; - posXOffset = 75; - posYOffset = 50; - } else if (effectId == EFFECT_SPAWN_FIRE_KEESE) { - enemyId = 19; - enemyParams = 1; - posXOffset = 75; - posYOffset = 50; - } else if (effectId == EFFECT_SPAWN_TEKTITE) { - enemyId = 27; - posXOffset = 75; - posYOffset = 50; - } else if (effectId == EFFECT_SPAWN_LIKE_LIKE) { - enemyId = 221; - posXOffset = 75; - posYOffset = 50; - } - - return Actor_Spawn(&gPlayState->actorCtx, gPlayState, enemyId, player->actor.world.pos.x + posXOffset, - player->actor.world.pos.y + posYOffset, player->actor.world.pos.z + posZOffset, 0, 0, 0, enemyParams, 0); +/// 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 != EFFECT_CAT_SPAWN_ENEMY); + GameInteractionEffectQueryResult giResult = GameInteractor::CanApplyEffect(effect->giEffect); + return TranslateGiEnum(giResult); } -void CrowdControl::RemoveEffect(std::string effectId) { - if (gPlayState == NULL) { - return; +CrowdControl::EffectResult CrowdControl::TranslateGiEnum(GameInteractionEffectQueryResult giResult) { + // Translate GameInteractor result into CC's own enums. + EffectResult result; + if (giResult == GameInteractionEffectQueryResult::Possible) { + result = EffectResult::Success; + } else if (giResult == GameInteractionEffectQueryResult::TemporarilyNotPossible) { + result = EffectResult::Retry; + } else { + result = EffectResult::Failure; } - Player* player = GET_PLAYER(gPlayState); - - if (player != NULL) { - if (effectId == EFFECT_GIANT_LINK - || effectId == EFFECT_MINISH_LINK - || effectId == EFFECT_NO_UI - || effectId == EFFECT_INVISIBLE_LINK - || effectId == EFFECT_PAPER_LINK - || effectId == EFFECT_NO_Z_TARGETING - || effectId == EFFECT_OHKO - || effectId == EFFECT_PACIFIST - || effectId == EFFECT_RAINSTORM - ) { - CMD_EXECUTE(fmt::format("{} 0", effectId)); - return; - } else if (effectId == EFFECT_IRON_BOOTS || effectId == EFFECT_HOVER_BOOTS) { - CMD_EXECUTE("boots kokiri"); - return; - } else if (effectId == EFFECT_HIGH_GRAVITY || effectId == EFFECT_LOW_GRAVITY) { - CMD_EXECUTE("gravity 1"); - return; - } else if (effectId == EFFECT_REVERSE_CONTROLS) { - CMD_EXECUTE("reverse_controls 0"); - return; - } else if (effectId == EFFECT_INCREASE_SPEED - || effectId == EFFECT_DECREASE_SPEED - ) { - CMD_EXECUTE("speed_modifier 0"); - return; - } else if (effectId == EFFECT_DAMAGE_MULTIPLIER - || effectId == EFFECT_DEFENSE_MULTIPLIER - ) { - CMD_EXECUTE("defense_modifier 0"); - return; - } - } + return result; } + #endif diff --git a/soh/soh/Enhancements/crowd-control/CrowdControl.h b/soh/soh/Enhancements/crowd-control/CrowdControl.h index 6b1dfd4ee..51b0adba6 100644 --- a/soh/soh/Enhancements/crowd-control/CrowdControl.h +++ b/soh/soh/Enhancements/crowd-control/CrowdControl.h @@ -17,6 +17,8 @@ #include #include +#include "../game-interactor/GameInteractor.h" + class CrowdControl { private: enum EffectResult { @@ -61,10 +63,11 @@ class CrowdControl { typedef struct Effect { uint32_t id; - std::string type; - uint32_t value; + uint32_t value[2]; std::string category; long timeRemaining; + GameInteractionEffectBase *giEffect; + int32_t paramMultiplier = 1; // Metadata used while executing (only for timed effects) bool isPaused; @@ -88,12 +91,11 @@ class CrowdControl { void ListenToServer(); void ProcessActiveEffects(); - void EmitMessage(TCPsocket socket, uint32_t eventId, long timeRemaining, - CrowdControl::EffectResult status); + void EmitMessage(TCPsocket socket, uint32_t eventId, long timeRemaining, EffectResult status); Effect* ParseMessage(char payload[512]); - EffectResult ExecuteEffect(std::string effectId, uint32_t value, bool dryRun); - void RemoveEffect(std::string effectId); - bool SpawnEnemy(std::string effectId); + EffectResult ExecuteEffect(Effect* effect); + EffectResult CanApplyEffect(Effect *effect); + EffectResult TranslateGiEnum(GameInteractionEffectQueryResult giResult); public: static CrowdControl* Instance; diff --git a/soh/soh/Enhancements/debugconsole.cpp b/soh/soh/Enhancements/debugconsole.cpp index 40cd841d7..f3d461657 100644 --- a/soh/soh/Enhancements/debugconsole.cpp +++ b/soh/soh/Enhancements/debugconsole.cpp @@ -8,10 +8,10 @@ #include #include "soh/OTRGlobals.h" #include +#include "soh/Enhancements/game-interactor/GameInteractor.h" #include "soh/Enhancements/cosmetics/CosmeticsEditor.h" #include "soh/Enhancements/sfx-editor/SfxEditor.h" - #define Path _Path #define PATH_HACK #include @@ -30,24 +30,9 @@ extern PlayState* gPlayState; } #include -#include "overlays/actors/ovl_En_Niw/z_en_niw.h" #define CMD_REGISTER SohImGui::GetConsole()->AddCommand -uint32_t chaosEffectNoUI; -uint32_t chaosEffectGiantLink; -uint32_t chaosEffectMinishLink; -uint32_t chaosEffectPaperLink; -uint32_t chaosEffectResetLinkScale; -uint32_t chaosEffectInvisibleLink; -uint32_t chaosEffectOneHitKO; -uint32_t chaosEffectPacifistMode; -int32_t chaosEffectDefenseModifier; -uint32_t chaosEffectNoZ; -uint32_t chaosEffectReverseControls; -uint32_t chaosEffectGravityLevel = GRAVITY_LEVEL_NORMAL; -int32_t chaosEffectSpeedModifier; - static bool ActorSpawnHandler(std::shared_ptr Console, const std::vector& args) { if ((args.size() != 9) && (args.size() != 3) && (args.size() != 6)) { SohImGui::GetConsole()->SendErrorMessage("Not enough arguments passed to actorspawn"); @@ -98,21 +83,28 @@ static bool ActorSpawnHandler(std::shared_ptr Console, const std: } static bool GiveDekuShieldHandler(std::shared_ptr Console, const std::vector&) { - // Give Deku Shield to the player, and automatically equip it when they're child and have no shield currently equiped. - Player* player = GET_PLAYER(gPlayState); - Item_Give(gPlayState, ITEM_SHIELD_DEKU); - if (LINK_IS_CHILD && player->currentShield == PLAYER_SHIELD_NONE) { - player->currentShield = PLAYER_SHIELD_DEKU; - Inventory_ChangeEquipment(EQUIP_SHIELD, PLAYER_SHIELD_DEKU); + GameInteractionEffectBase* effect = new GameInteractionEffect::GiveDekuShield(); + GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect); + if (result == GameInteractionEffectQueryResult::Possible) { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Gave Deku Shield."); + return CMD_SUCCESS; + } else { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Command failed: Could not give Deku Shield."); + return CMD_FAILED; } - SohImGui::GetConsole()->SendInfoMessage("[SOH] Gave Deku Shield"); - return CMD_SUCCESS; } static bool KillPlayerHandler(std::shared_ptr Console, const std::vector&) { - gSaveContext.health = 0; - SohImGui::GetConsole()->SendInfoMessage("[SOH] You've met with a terrible fate, haven't you?"); - return CMD_SUCCESS; + GameInteractionEffectBase* effect = new GameInteractionEffect::SetPlayerHealth(); + effect->parameter = 0; + GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect); + if (result == GameInteractionEffectQueryResult::Possible) { + SohImGui::GetConsole()->SendInfoMessage("[SOH] You've met with a terrible fate, haven't you?"); + return CMD_SUCCESS; + } else { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Command failed: Could not kill player."); + return CMD_FAILED; + } } static bool SetPlayerHealthHandler(std::shared_ptr Console, const std::vector& args) { @@ -120,7 +112,6 @@ static bool SetPlayerHealthHandler(std::shared_ptr Console, const SohImGui::GetConsole()->SendErrorMessage("[SOH] Unexpected arguments passed"); return CMD_FAILED; } - int health; try { @@ -132,13 +123,19 @@ static bool SetPlayerHealthHandler(std::shared_ptr Console, const if (health < 0) { SohImGui::GetConsole()->SendErrorMessage("[SOH] Health value must be a positive integer"); - return CMD_SUCCESS; + return CMD_FAILED; } - gSaveContext.health = health * 0x10; - - SohImGui::GetConsole()->SendInfoMessage("[SOH] Player health updated to %d", health); - return CMD_SUCCESS; + GameInteractionEffectBase* effect = new GameInteractionEffect::SetPlayerHealth(); + effect->parameter = health; + GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect); + if (result == GameInteractionEffectQueryResult::Possible) { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Player health updated to %d", health); + return CMD_SUCCESS; + } else { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Command failed: Could not set player health."); + return CMD_FAILED; + } } static bool LoadSceneHandler(std::shared_ptr Console, const std::vector&) { @@ -168,7 +165,7 @@ static bool RupeeHandler(std::shared_ptr Console, const std::vect return CMD_FAILED; } - gSaveContext.rupees = rupeeAmount; + gSaveContext.rupees = rupeeAmount; SohImGui::GetConsole()->SendInfoMessage("Set rupee count to %u", rupeeAmount); return CMD_SUCCESS; @@ -501,7 +498,7 @@ static bool StateSlotSelectHandler(std::shared_ptr Console, const SohImGui::GetConsole()->SendErrorMessage("[SOH] Unexpected arguments passed"); return CMD_FAILED; } - int slot; + uint8_t slot; try { slot = std::stoi(args[1], nullptr, 10); @@ -526,19 +523,26 @@ static bool InvisibleHandler(std::shared_ptr Console, const std:: SohImGui::GetConsole()->SendErrorMessage("[SOH] Unexpected arguments passed"); return CMD_FAILED; } + uint8_t state; try { - chaosEffectInvisibleLink = std::stoi(args[1], nullptr, 10) == 0 ? 0 : 1; - if (!chaosEffectInvisibleLink) { - Player* player = GET_PLAYER(gPlayState); - player->actor.shape.shadowDraw = ActorShadow_DrawFeet; - } - - return CMD_SUCCESS; + state = std::stoi(args[1], nullptr, 10) == 0 ? 0 : 1; } catch (std::invalid_argument const& ex) { SohImGui::GetConsole()->SendErrorMessage("[SOH] Invisible value must be a number."); return CMD_FAILED; } + + GameInteractionEffectBase* effect = new GameInteractionEffect::InvisibleLink(); + GameInteractionEffectQueryResult result = + state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect); + if (result == GameInteractionEffectQueryResult::Possible) { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Invisible Link %s", state ? "enabled" : "disabled"); + return CMD_SUCCESS; + } else { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Command failed: Could not %s Invisible Link.", + state ? "enable" : "disable"); + return CMD_FAILED; + } } static bool GiantLinkHandler(std::shared_ptr Console, const std::vector& args) { @@ -546,21 +550,27 @@ static bool GiantLinkHandler(std::shared_ptr Console, const std:: SohImGui::GetConsole()->SendErrorMessage("[SOH] Unexpected arguments passed"); return CMD_FAILED; } + uint8_t state; try { - chaosEffectGiantLink = std::stoi(args[1], nullptr, 10) == 0 ? 0 : 1; - if (chaosEffectGiantLink) { - chaosEffectPaperLink = 0; - chaosEffectMinishLink = 0; - } else { - chaosEffectResetLinkScale = 1; - } - - return CMD_SUCCESS; + state = std::stoi(args[1], nullptr, 10) == 0 ? 0 : 1; } catch (std::invalid_argument const& ex) { SohImGui::GetConsole()->SendErrorMessage("[SOH] Giant value must be a number."); return CMD_FAILED; } + + GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyLinkSize(); + effect->parameter = GI_LINK_SIZE_GIANT; + GameInteractionEffectQueryResult result = + state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect); + if (result == GameInteractionEffectQueryResult::Possible) { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Giant Link %s", state ? "enabled" : "disabled"); + return CMD_SUCCESS; + } else { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Command failed: Could not %s Giant Link.", + state ? "enable" : "disable"); + return CMD_FAILED; + } } static bool MinishLinkHandler(std::shared_ptr Console, const std::vector& args) { @@ -568,37 +578,89 @@ static bool MinishLinkHandler(std::shared_ptr Console, const std: SohImGui::GetConsole()->SendErrorMessage("[SOH] Unexpected arguments passed"); return CMD_FAILED; } + uint8_t state; try { - chaosEffectMinishLink = std::stoi(args[1], nullptr, 10) == 0 ? 0 : 1; - if (chaosEffectMinishLink) { - chaosEffectPaperLink = 0; - chaosEffectGiantLink = 0; - } else { - chaosEffectResetLinkScale = 1; - } - - return CMD_SUCCESS; + state = std::stoi(args[1], nullptr, 10) == 0 ? 0 : 1; } catch (std::invalid_argument const& ex) { SohImGui::GetConsole()->SendErrorMessage("[SOH] Minish value must be a number."); return CMD_FAILED; } + + GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyLinkSize(); + effect->parameter = GI_LINK_SIZE_MINISH; + GameInteractionEffectQueryResult result = + state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect); + if (result == GameInteractionEffectQueryResult::Possible) { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Minish Link %s", state ? "enabled" : "disabled"); + return CMD_SUCCESS; + } else { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Command failed: Could not %s Minish Link.", + state ? "enable" : "disable"); + return CMD_FAILED; + } } static bool AddHeartContainerHandler(std::shared_ptr Console, const std::vector& args) { - if (gSaveContext.healthCapacity >= 0x140) + if (args.size() != 2) { + SohImGui::GetConsole()->SendErrorMessage("[SOH] Unexpected arguments passed"); return CMD_FAILED; + } + int hearts; - Health_GiveHearts(1); - return CMD_SUCCESS; + try { + hearts = std::stoi(args[1]); + } catch (std::invalid_argument const& ex) { + SohImGui::GetConsole()->SendErrorMessage("[SOH] Hearts value must be an integer."); + return CMD_FAILED; + } + + if (hearts < 0) { + SohImGui::GetConsole()->SendErrorMessage("[SOH] Hearts value must be a positive integer"); + return CMD_FAILED; + } + + GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyHeartContainers(); + effect->parameter = hearts; + GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect); + if (result == GameInteractionEffectQueryResult::Possible) { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Added %d heart containers", hearts); + return CMD_SUCCESS; + } else { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Command failed: Could not add heart containers."); + return CMD_FAILED; + } } static bool RemoveHeartContainerHandler(std::shared_ptr Console, const std::vector& args) { - if ((gSaveContext.healthCapacity - 0x10) < 3) + if (args.size() != 2) { + SohImGui::GetConsole()->SendErrorMessage("[SOH] Unexpected arguments passed"); return CMD_FAILED; + } + int hearts; - Health_RemoveHearts(1); - return CMD_SUCCESS; + try { + hearts = std::stoi(args[1]); + } catch (std::invalid_argument const& ex) { + SohImGui::GetConsole()->SendErrorMessage("[SOH] Hearts value must be an integer."); + return CMD_FAILED; + } + + if (hearts < 0) { + SohImGui::GetConsole()->SendErrorMessage("[SOH] Hearts value must be a positive integer"); + return CMD_FAILED; + } + + GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyHeartContainers(); + effect->parameter = -hearts; + GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect); + if (result == GameInteractionEffectQueryResult::Possible) { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Removed %d heart containers", hearts); + return CMD_SUCCESS; + } else { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Command failed: Could not remove heart containers."); + return CMD_FAILED; + } } static bool GravityHandler(std::shared_ptr Console, const std::vector& args) { @@ -607,11 +669,21 @@ static bool GravityHandler(std::shared_ptr Console, const std::ve return CMD_FAILED; } + GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyGravity(); + try { - chaosEffectGravityLevel = Ship::Math::clamp(std::stoi(args[1], nullptr, 10), GRAVITY_LEVEL_LIGHT, GRAVITY_LEVEL_HEAVY); - return CMD_SUCCESS; + effect->parameter = Ship::Math::clamp(std::stoi(args[1], nullptr, 10), GI_GRAVITY_LEVEL_LIGHT, GI_GRAVITY_LEVEL_HEAVY); } catch (std::invalid_argument const& ex) { - SohImGui::GetConsole()->SendErrorMessage("[SOH] Minish value must be a number."); + SohImGui::GetConsole()->SendErrorMessage("[SOH] Gravity value must be a number."); + return CMD_FAILED; + } + + GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect); + if (result == GameInteractionEffectQueryResult::Possible) { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Updated gravity."); + return CMD_SUCCESS; + } else { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Command failed: Could not update gravity."); return CMD_FAILED; } } @@ -621,19 +693,40 @@ static bool NoUIHandler(std::shared_ptr Console, const std::vecto SohImGui::GetConsole()->SendErrorMessage("[SOH] Unexpected arguments passed"); return CMD_FAILED; } + uint8_t state; try { - chaosEffectNoUI = std::stoi(args[1], nullptr, 10) == 0 ? 0 : 1; - return CMD_SUCCESS; + state = std::stoi(args[1], nullptr, 10) == 0 ? 0 : 1; } catch (std::invalid_argument const& ex) { SohImGui::GetConsole()->SendErrorMessage("[SOH] No UI value must be a number."); return CMD_FAILED; } + + GameInteractionEffectBase* effect = new GameInteractionEffect::NoUI(); + GameInteractionEffectQueryResult result = + state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect); + + if (result == GameInteractionEffectQueryResult::Possible) { + SohImGui::GetConsole()->SendInfoMessage("[SOH] No UI %s", state ? "enabled" : "disabled"); + return CMD_SUCCESS; + } else { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Command failed: Could not %s No UI.", + state ? "enable" : "disable"); + return CMD_FAILED; + } } static bool FreezeHandler(std::shared_ptr Console, const std::vector& args) { - gSaveContext.pendingIceTrapCount++; - return CMD_SUCCESS; + GameInteractionEffectBase* effect = new GameInteractionEffect::FreezePlayer(); + GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect); + + if (result == GameInteractionEffectQueryResult::Possible) { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Player frozen"); + return CMD_SUCCESS; + } else { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Command failed: Could not freeze player."); + return CMD_FAILED; + } } static bool DefenseModifierHandler(std::shared_ptr Console, const std::vector& args) { @@ -641,14 +734,23 @@ static bool DefenseModifierHandler(std::shared_ptr Console, const SohImGui::GetConsole()->SendErrorMessage("[SOH] Unexpected arguments passed"); return CMD_FAILED; } + GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyDefenseModifier(); try { - chaosEffectDefenseModifier = std::stoi(args[1], nullptr, 10); - return CMD_SUCCESS; + effect->parameter = std::stoi(args[1], nullptr, 10); } catch (std::invalid_argument const& ex) { SohImGui::GetConsole()->SendErrorMessage("[SOH] Defense modifier value must be a number."); return CMD_FAILED; } + + GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect); + if (result == GameInteractionEffectQueryResult::Possible) { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Defense modifier set to %d", effect->parameter); + return CMD_SUCCESS; + } else { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Command failed: Could not set defense modifier."); + return CMD_FAILED; + } } static bool DamageHandler(std::shared_ptr Console, const std::vector& args) { @@ -656,6 +758,7 @@ static bool DamageHandler(std::shared_ptr Console, const std::vec SohImGui::GetConsole()->SendErrorMessage("[SOH] Unexpected arguments passed"); return CMD_FAILED; } + GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyHealth(); try { int value = std::stoi(args[1], nullptr, 10); @@ -664,17 +767,20 @@ static bool DamageHandler(std::shared_ptr Console, const std::vec return CMD_FAILED; } - Player* player = GET_PLAYER(gPlayState); - - Health_ChangeBy(gPlayState, -value * 0x10); - func_80837C0C(gPlayState, player, 0, 0, 0, 0, 0); - player->invincibilityTimer = 28; - - return CMD_SUCCESS; + effect->parameter = -value; } catch (std::invalid_argument const& ex) { SohImGui::GetConsole()->SendErrorMessage("[SOH] Damage value must be a number."); return CMD_FAILED; } + + GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect); + if (result == GameInteractionEffectQueryResult::Possible) { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Player damaged"); + return CMD_SUCCESS; + } else { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Command failed: Could not damage player."); + return CMD_FAILED; + } } static bool HealHandler(std::shared_ptr Console, const std::vector& args) { @@ -682,6 +788,7 @@ static bool HealHandler(std::shared_ptr Console, const std::vecto SohImGui::GetConsole()->SendErrorMessage("[SOH] Unexpected arguments passed"); return CMD_FAILED; } + GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyHealth(); try { int value = std::stoi(args[1], nullptr, 10); @@ -690,22 +797,46 @@ static bool HealHandler(std::shared_ptr Console, const std::vecto return CMD_FAILED; } - Health_ChangeBy(gPlayState, value * 0x10); - return CMD_SUCCESS; + effect->parameter = value; } catch (std::invalid_argument const& ex) { - SohImGui::GetConsole()->SendErrorMessage("[SOH] Heal value must be a number."); + SohImGui::GetConsole()->SendErrorMessage("[SOH] Damage value must be a number."); + return CMD_FAILED; + } + + GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect); + if (result == GameInteractionEffectQueryResult::Possible) { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Player healed"); + return CMD_SUCCESS; + } else { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Command failed: Could not heal player."); return CMD_FAILED; } } static bool FillMagicHandler(std::shared_ptr Console, const std::vector& args) { - Magic_Fill(gPlayState); - return CMD_SUCCESS; + GameInteractionEffectBase* effect = new GameInteractionEffect::FillMagic(); + GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect); + + if (result == GameInteractionEffectQueryResult::Possible) { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Magic filled"); + return CMD_SUCCESS; + } else { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Command failed: Could not fill magic."); + return CMD_FAILED; + } } static bool EmptyMagicHandler(std::shared_ptr Console, const std::vector& args) { - gSaveContext.magic = 0; - return CMD_SUCCESS; + GameInteractionEffectBase* effect = new GameInteractionEffect::EmptyMagic(); + GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect); + + if (result == GameInteractionEffectQueryResult::Possible) { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Magic emptied"); + return CMD_SUCCESS; + } else { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Command failed: Could not empty magic."); + return CMD_FAILED; + } } static bool NoZHandler(std::shared_ptr Console, const std::vector& args) { @@ -713,14 +844,27 @@ static bool NoZHandler(std::shared_ptr Console, const std::vector SohImGui::GetConsole()->SendErrorMessage("[SOH] Unexpected arguments passed"); return CMD_FAILED; } + uint8_t state; try { - chaosEffectNoZ = std::stoi(args[1], nullptr, 10) == 0 ? 0 : 1; - return CMD_SUCCESS; + state = std::stoi(args[1], nullptr, 10) == 0 ? 0 : 1; } catch (std::invalid_argument const& ex) { SohImGui::GetConsole()->SendErrorMessage("[SOH] NoZ value must be a number."); return CMD_FAILED; } + + GameInteractionEffectBase* effect = new GameInteractionEffect::DisableZTargeting(); + GameInteractionEffectQueryResult result = + state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect); + + if (result == GameInteractionEffectQueryResult::Possible) { + SohImGui::GetConsole()->SendInfoMessage("[SOH] NoZ " + std::string(state ? "enabled" : "disabled")); + return CMD_SUCCESS; + } else { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Command failed: Could not " + + std::string(state ? "enable" : "disable") + " NoZ."); + return CMD_FAILED; + } } static bool OneHitKOHandler(std::shared_ptr Console, const std::vector& args) { @@ -728,14 +872,27 @@ static bool OneHitKOHandler(std::shared_ptr Console, const std::v SohImGui::GetConsole()->SendErrorMessage("[SOH] Unexpected arguments passed"); return CMD_FAILED; } + uint8_t state; try { - chaosEffectOneHitKO = std::stoi(args[1], nullptr, 10) == 0 ? 0 : 1; - return CMD_SUCCESS; + state = std::stoi(args[1], nullptr, 10) == 0 ? 0 : 1; } catch (std::invalid_argument const& ex) { SohImGui::GetConsole()->SendErrorMessage("[SOH] One-hit KO value must be a number."); return CMD_FAILED; } + + GameInteractionEffectBase* effect = new GameInteractionEffect::OneHitKO(); + GameInteractionEffectQueryResult result = + state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect); + + if (result == GameInteractionEffectQueryResult::Possible) { + SohImGui::GetConsole()->SendInfoMessage("[SOH] One-hit KO " + std::string(state ? "enabled" : "disabled")); + return CMD_SUCCESS; + } else { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Command failed: Could not " + + std::string(state ? "enable" : "disable") + " One-hit KO."); + return CMD_FAILED; + } } static bool PacifistHandler(std::shared_ptr Console, const std::vector& args) { @@ -743,17 +900,27 @@ static bool PacifistHandler(std::shared_ptr Console, const std::v SohImGui::GetConsole()->SendErrorMessage("[SOH] Unexpected arguments passed"); return CMD_FAILED; } + uint8_t state; try { - chaosEffectPacifistMode = std::stoi(args[1], nullptr, 10) == 0 ? 0 : 1; - // Force interface to update to make the buttons transparent - gSaveContext.unk_13E8 = 50; - Interface_Update(gPlayState); - return CMD_SUCCESS; + state = std::stoi(args[1], nullptr, 10) == 0 ? 0 : 1; } catch (std::invalid_argument const& ex) { SohImGui::GetConsole()->SendErrorMessage("[SOH] Pacifist value must be a number."); return CMD_FAILED; } + + GameInteractionEffectBase* effect = new GameInteractionEffect::PacifistMode(); + GameInteractionEffectQueryResult result = + state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect); + + if (result == GameInteractionEffectQueryResult::Possible) { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Pacifist " + std::string(state ? "enabled" : "disabled")); + return CMD_SUCCESS; + } else { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Command failed: Could not " + + std::string(state ? "enable" : "disable") + " Pacifist."); + return CMD_FAILED; + } } static bool PaperLinkHandler(std::shared_ptr Console, const std::vector& args) { @@ -761,20 +928,28 @@ static bool PaperLinkHandler(std::shared_ptr Console, const std:: SohImGui::GetConsole()->SendErrorMessage("[SOH] Unexpected arguments passed"); return CMD_FAILED; } + uint8_t state; try { - chaosEffectPaperLink = std::stoi(args[1], nullptr, 10) == 0 ? 0 : 1; - if (chaosEffectPaperLink) { - chaosEffectMinishLink = 0; - chaosEffectGiantLink = 0; - } else { - chaosEffectResetLinkScale = 1; - } - return CMD_SUCCESS; + state = std::stoi(args[1], nullptr, 10) == 0 ? 0 : 1; } catch (std::invalid_argument const& ex) { SohImGui::GetConsole()->SendErrorMessage("[SOH] Paper Link value must be a number."); return CMD_FAILED; } + + GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyLinkSize(); + effect->parameter = GI_LINK_SIZE_PAPER; + GameInteractionEffectQueryResult result = + state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect); + + if (result == GameInteractionEffectQueryResult::Possible) { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Paper Link " + std::string(state ? "enabled" : "disabled")); + return CMD_SUCCESS; + } else { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Command failed: Could not " + + std::string(state ? "enable" : "disable") + " Paper Link."); + return CMD_FAILED; + } } static bool RainstormHandler(std::shared_ptr Console, const std::vector& args) { @@ -782,40 +957,25 @@ static bool RainstormHandler(std::shared_ptr Console, const std:: SohImGui::GetConsole()->SendErrorMessage("[SOH] Unexpected arguments passed"); return CMD_FAILED; } + uint8_t state; try { - uint32_t rainstorm = std::stoi(args[1], nullptr, 10) == 0 ? 0 : 1; - if (rainstorm) { - gPlayState->envCtx.unk_F2[0] = 20; // rain intensity target - gPlayState->envCtx.gloomySkyMode = 1; // start gloomy sky - if ((gWeatherMode != 0) || gPlayState->envCtx.unk_17 != 0) { - gPlayState->envCtx.unk_DE = 1; - } - gPlayState->envCtx.lightningMode = LIGHTNING_MODE_ON; - Environment_PlayStormNatureAmbience(gPlayState); - } else { - gPlayState->envCtx.unk_F2[0] = 0; - if (gPlayState->csCtx.state == CS_STATE_IDLE) { - Environment_StopStormNatureAmbience(gPlayState); - } else if (func_800FA0B4(SEQ_PLAYER_BGM_MAIN) == NA_BGM_NATURE_AMBIENCE) { - Audio_SetNatureAmbienceChannelIO(NATURE_CHANNEL_LIGHTNING, CHANNEL_IO_PORT_1, 0); - Audio_SetNatureAmbienceChannelIO(NATURE_CHANNEL_RAIN, CHANNEL_IO_PORT_1, 0); - } - osSyncPrintf("\n\n\nE_wether_flg=[%d]", gWeatherMode); - osSyncPrintf("\nrain_evt_trg=[%d]\n\n", gPlayState->envCtx.gloomySkyMode); - if (gWeatherMode == 0 && (gPlayState->envCtx.gloomySkyMode == 1)) { - gPlayState->envCtx.gloomySkyMode = 2; // end gloomy sky - } else { - gPlayState->envCtx.gloomySkyMode = 0; - gPlayState->envCtx.unk_DE = 0; - } - gPlayState->envCtx.lightningMode = LIGHTNING_MODE_LAST; - } - - - return CMD_SUCCESS; + state = std::stoi(args[1], nullptr, 10) == 0 ? 0 : 1; } catch (std::invalid_argument const& ex) { - SohImGui::GetConsole()->SendErrorMessage("[SOH] rainstorm value must be a number."); + SohImGui::GetConsole()->SendErrorMessage("[SOH] Rainstorm value must be a number."); + return CMD_FAILED; + } + + GameInteractionEffectBase* effect = new GameInteractionEffect::WeatherRainstorm(); + GameInteractionEffectQueryResult result = + state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect); + + if (result == GameInteractionEffectQueryResult::Possible) { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Rainstorm " + std::string(state ? "enabled" : "disabled")); + return CMD_SUCCESS; + } else { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Command failed: Could not " + + std::string(state ? "enable" : "disable") + " Rainstorm."); return CMD_FAILED; } } @@ -825,14 +985,28 @@ static bool ReverseControlsHandler(std::shared_ptr Console, const SohImGui::GetConsole()->SendErrorMessage("[SOH] Unexpected arguments passed"); return CMD_FAILED; } + uint8_t state; try { - chaosEffectReverseControls = std::stoi(args[1], nullptr, 10) == 0 ? 0 : 1; - return CMD_SUCCESS; + state = std::stoi(args[1], nullptr, 10) == 0 ? 0 : 1; } catch (std::invalid_argument const& ex) { SohImGui::GetConsole()->SendErrorMessage("[SOH] Reverse controls value must be a number."); return CMD_FAILED; } + + GameInteractionEffectBase* effect = new GameInteractionEffect::ReverseControls(); + GameInteractionEffectQueryResult result = + state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect); + + if (result == GameInteractionEffectQueryResult::Possible) { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Reverse controls " + + std::string(state ? "enabled" : "disabled")); + return CMD_SUCCESS; + } else { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Command failed: Could not " + + std::string(state ? "enable" : "disable") + " Reverse controls."); + return CMD_FAILED; + } } static bool UpdateRupeesHandler(std::shared_ptr Console, const std::vector& args) { @@ -840,15 +1014,23 @@ static bool UpdateRupeesHandler(std::shared_ptr Console, const st SohImGui::GetConsole()->SendErrorMessage("[SOH] Unexpected arguments passed"); return CMD_FAILED; } + GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyRupees(); try { - int value = std::stoi(args[1], nullptr, 10); - Rupees_ChangeBy(value); - return CMD_SUCCESS; + effect->parameter = std::stoi(args[1], nullptr, 10); } catch (std::invalid_argument const& ex) { SohImGui::GetConsole()->SendErrorMessage("[SOH] Rupee value must be a number."); return CMD_FAILED; } + + GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect); + if (result == GameInteractionEffectQueryResult::Possible) { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Rupees updated"); + return CMD_SUCCESS; + } else { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Command failed: Could not update rupees."); + return CMD_FAILED; + } } static bool SpeedModifierHandler(std::shared_ptr Console, const std::vector& args) { @@ -856,14 +1038,23 @@ static bool SpeedModifierHandler(std::shared_ptr Console, const s SohImGui::GetConsole()->SendErrorMessage("[SOH] Unexpected arguments passed"); return CMD_FAILED; } + GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyRunSpeedModifier(); try { - chaosEffectSpeedModifier = std::stoi(args[1], nullptr, 10); - return CMD_SUCCESS; + effect->parameter = std::stoi(args[1], nullptr, 10); } catch (std::invalid_argument const& ex) { SohImGui::GetConsole()->SendErrorMessage("[SOH] Speed modifier value must be a number."); return CMD_FAILED; } + + GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect); + if (result == GameInteractionEffectQueryResult::Possible) { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Speed modifier updated"); + return CMD_SUCCESS; + } else { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Command failed: Could not update speed modifier."); + return CMD_FAILED; + } } const static std::map boots { @@ -884,12 +1075,17 @@ static bool BootsHandler(std::shared_ptr Console, const std::vect return CMD_FAILED; } - Player* player = GET_PLAYER(gPlayState); - player->currentBoots = it->second; - Inventory_ChangeEquipment(EQUIP_BOOTS, it->second + 1); - Player_SetBootData(gPlayState, player); + GameInteractionEffectBase* effect = new GameInteractionEffect::ForceEquipBoots(); + effect->parameter = it->second; + GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect); - return CMD_SUCCESS; + if (result == GameInteractionEffectQueryResult::Possible) { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Boots updated"); + return CMD_SUCCESS; + } else { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Command failed: Could not update boots."); + return CMD_FAILED; + } } static bool KnockbackHandler(std::shared_ptr Console, const std::vector& args) { @@ -897,6 +1093,7 @@ static bool KnockbackHandler(std::shared_ptr Console, const std:: SohImGui::GetConsole()->SendErrorMessage("[SOH] Unexpected arguments passed"); return CMD_FAILED; } + GameInteractionEffectBase* effect = new GameInteractionEffect::KnockbackPlayer(); try { int value = std::stoi(args[1], nullptr, 10); @@ -905,45 +1102,59 @@ static bool KnockbackHandler(std::shared_ptr Console, const std:: return CMD_FAILED; } - Player* player = GET_PLAYER(gPlayState); - func_8002F71C(gPlayState, &player->actor, value * 5, player->actor.world.rot.y + 0x8000, value * 5); - - return CMD_SUCCESS; + effect->parameter = value; } catch (std::invalid_argument const& ex) { SohImGui::GetConsole()->SendErrorMessage("[SOH] Knockback value must be a number."); return CMD_FAILED; } + + GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect); + if (result == GameInteractionEffectQueryResult::Possible) { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Knockback applied"); + return CMD_SUCCESS; + } else { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Command failed: Could not apply knockback."); + return CMD_FAILED; + } } static bool ElectrocuteHandler(std::shared_ptr Console, const std::vector& args) { - Player* player = GET_PLAYER(gPlayState); - if (PlayerGrounded(player)) { - func_80837C0C(gPlayState, player, 4, 0, 0, 0, 0); - return CMD_SUCCESS; - } + GameInteractionEffectBase* effect = new GameInteractionEffect::ElectrocutePlayer(); + GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect); - return CMD_FAILED; + if (result == GameInteractionEffectQueryResult::Possible) { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Electrocuted player"); + return CMD_SUCCESS; + } else { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Command failed: Could not electrocute player."); + return CMD_FAILED; + } } static bool BurnHandler(std::shared_ptr Console, const std::vector& args) { - Player* player = GET_PLAYER(gPlayState); - if (PlayerGrounded(player)) { - for (int i = 0; i < 18; i++) { - player->flameTimers[i] = Rand_S16Offset(0, 200); - } - player->isBurning = true; - func_80837C0C(gPlayState, player, 0, 0, 0, 0, 0); + GameInteractionEffectBase* effect = new GameInteractionEffect::BurnPlayer(); + GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect); + + if (result == GameInteractionEffectQueryResult::Possible) { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Burned player"); + return CMD_SUCCESS; + } else { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Command failed: Could not burn player."); return CMD_FAILED; } - return CMD_SUCCESS; } static bool CuccoStormHandler(std::shared_ptr Console, const std::vector& args) { - Player* player = GET_PLAYER(gPlayState); - EnNiw* cucco = (EnNiw*)Actor_Spawn(&gPlayState->actorCtx, gPlayState, ACTOR_EN_NIW, player->actor.world.pos.x, - player->actor.world.pos.y + 2200, player->actor.world.pos.z, 0, 0, 0, 0, 0); - cucco->actionFunc = func_80AB70A0_nocutscene; - return CMD_SUCCESS; + GameInteractionEffectBase* effect = new GameInteractionEffect::SpawnCuccoStorm(); + GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect); + + if (result == GameInteractionEffectQueryResult::Possible) { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Spawned cucco storm"); + return CMD_SUCCESS; + } else { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Command failed: Could not spawn cucco storm."); + return CMD_FAILED; + } } static bool CosmeticsHandler(std::shared_ptr Console, const std::vector& args) { diff --git a/soh/soh/Enhancements/debugconsole.h b/soh/soh/Enhancements/debugconsole.h index 40c56bf6d..148dadcbc 100644 --- a/soh/soh/Enhancements/debugconsole.h +++ b/soh/soh/Enhancements/debugconsole.h @@ -2,30 +2,4 @@ #include "stdint.h" -#define GRAVITY_LEVEL_NORMAL 1.0f -#define GRAVITY_LEVEL_LIGHT 0.0f -#define GRAVITY_LEVEL_HEAVY 2.0f - -#ifdef __cplusplus -extern "C" { -#endif -// bools are exported as uint32_t for compatibility with C code -extern uint32_t chaosEffectNoUI; -extern uint32_t chaosEffectGiantLink; -extern uint32_t chaosEffectMinishLink; -extern uint32_t chaosEffectPaperLink; -extern uint32_t chaosEffectResetLinkScale; -extern uint32_t chaosEffectInvisibleLink; -extern uint32_t chaosEffectOneHitKO; -extern uint32_t chaosEffectPacifistMode; -extern int32_t chaosEffectDefenseModifier; -extern uint32_t chaosEffectNoZ; -extern uint32_t chaosEffectReverseControls; - -extern uint32_t chaosEffectGravityLevel; -extern int32_t chaosEffectSpeedModifier; -#ifdef __cplusplus -} -#endif - void DebugConsole_Init(void); diff --git a/soh/soh/Enhancements/game-interactor/GameInteractionEffect.cpp b/soh/soh/Enhancements/game-interactor/GameInteractionEffect.cpp new file mode 100644 index 000000000..b1edfe229 --- /dev/null +++ b/soh/soh/Enhancements/game-interactor/GameInteractionEffect.cpp @@ -0,0 +1,399 @@ +/* +GameInteractionEffects is used in conjunction with GameInteractor. + +It's intended to be used in places that want to interact with the game +while having checks built-in for if said effect is able to be executed. + +Effects that can last for a certain amount of time (timed effects) +have functions to both enable and disable said effect. +*/ + +#include "GameInteractionEffect.h" +#include "GameInteractor.h" +#include + +extern "C" { +#include +#include "variables.h" +#include "functions.h" +#include "macros.h" +extern PlayState* gPlayState; +} + +GameInteractionEffectQueryResult GameInteractionEffectBase::Apply() { + GameInteractionEffectQueryResult result = CanBeApplied(); + if (result != GameInteractionEffectQueryResult::Possible) { + return result; + } + + _Apply(); + return result; +} + +/// For most effects, CanBeRemoved is the same as CanBeApplied. When its not: please override `CanBeRemoved`. +GameInteractionEffectQueryResult GameInteractionEffectBase::CanBeRemoved() { + return CanBeApplied(); +} + +GameInteractionEffectQueryResult GameInteractionEffectBase::Remove() { + GameInteractionEffectQueryResult result = CanBeRemoved(); + if (result != GameInteractionEffectQueryResult::Possible) { + return result; + } + + _Remove(); + return result; +} + +namespace GameInteractionEffect { + + // MARK: - ModifyHeartContainers + GameInteractionEffectQueryResult ModifyHeartContainers::CanBeApplied() { + if (!GameInteractor::IsSaveLoaded()) { + return GameInteractionEffectQueryResult::TemporarilyNotPossible; + } else if ( + (parameter > 0 && (gSaveContext.healthCapacity + (parameter * 0x10) > 0x140)) || + (parameter < 0 && (gSaveContext.healthCapacity + (parameter * 0x10) < 0x10)) + ) { + return GameInteractionEffectQueryResult::NotPossible; + } + + return GameInteractionEffectQueryResult::Possible; + } + + void ModifyHeartContainers::_Apply() { + GameInteractor::RawAction::AddOrRemoveHealthContainers(parameter); + } + + // MARK: - FillMagic + GameInteractionEffectQueryResult FillMagic::CanBeApplied() { + if (!GameInteractor::IsSaveLoaded()) { + return GameInteractionEffectQueryResult::TemporarilyNotPossible; + } else if (!gSaveContext.isMagicAcquired || gSaveContext.magic >= ((gSaveContext.isDoubleMagicAcquired + 1) * 48)) { + return GameInteractionEffectQueryResult::NotPossible; + } else { + return GameInteractionEffectQueryResult::Possible; + } + } + void FillMagic::_Apply() { + GameInteractor::RawAction::AddOrRemoveMagic(96); + } + + // MARK: - EmptyMagic + GameInteractionEffectQueryResult EmptyMagic::CanBeApplied() { + if (!GameInteractor::IsSaveLoaded()) { + return GameInteractionEffectQueryResult::TemporarilyNotPossible; + } else if (!gSaveContext.isMagicAcquired || gSaveContext.magic <= 0) { + return GameInteractionEffectQueryResult::NotPossible; + } else { + return GameInteractionEffectQueryResult::Possible; + } + } + void EmptyMagic::_Apply() { + GameInteractor::RawAction::AddOrRemoveMagic(-96); + } + + // MARK: - ModifyRupees + GameInteractionEffectQueryResult ModifyRupees::CanBeApplied() { + if (!GameInteractor::IsSaveLoaded()) { + return GameInteractionEffectQueryResult::TemporarilyNotPossible; + } else if ( + (parameter < 0 && gSaveContext.rupees <= 0) || + (parameter > 0 && gSaveContext.rupees >= CUR_CAPACITY(UPG_WALLET)) + ) { + return GameInteractionEffectQueryResult::NotPossible; + } else { + return GameInteractionEffectQueryResult::Possible; + } + } + void ModifyRupees::_Apply() { + Rupees_ChangeBy(parameter); + } + + // MARK: - NoUI + GameInteractionEffectQueryResult NoUI::CanBeApplied() { + if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + return GameInteractionEffectQueryResult::TemporarilyNotPossible; + } else { + return GameInteractionEffectQueryResult::Possible; + } + } + void NoUI::_Apply() { + GameInteractor::State::NoUIActive = 1; + } + void NoUI::_Remove() { + GameInteractor::State::NoUIActive = 0; + } + + // MARK: - ModifyGravity + GameInteractionEffectQueryResult ModifyGravity::CanBeApplied() { + if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + return GameInteractionEffectQueryResult::TemporarilyNotPossible; + } else { + return GameInteractionEffectQueryResult::Possible; + } + } + void ModifyGravity::_Apply() { + GameInteractor::State::GravityLevel = (GIGravityLevel)parameter; + } + void ModifyGravity::_Remove() { + GameInteractor::State::GravityLevel = GI_GRAVITY_LEVEL_NORMAL; + } + + // MARK: - ModifyHealth + GameInteractionEffectQueryResult ModifyHealth::CanBeApplied() { + if (!GameInteractor::IsSaveLoaded()) { + return GameInteractionEffectQueryResult::TemporarilyNotPossible; + } else if ( + (parameter > 0 && gSaveContext.health == gSaveContext.healthCapacity) + || (parameter < 0 && (gSaveContext.health + (16 * parameter) <= 0)) + ) { + return GameInteractionEffectQueryResult::NotPossible; + } + + return GameInteractionEffectQueryResult::Possible; + } + void ModifyHealth::_Apply() { + GameInteractor::RawAction::HealOrDamagePlayer(parameter); + } + + // MARK: - SetPlayerHealth + GameInteractionEffectQueryResult SetPlayerHealth::CanBeApplied() { + Player* player = GET_PLAYER(gPlayState); + if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + return GameInteractionEffectQueryResult::TemporarilyNotPossible; + } else { + return GameInteractionEffectQueryResult::Possible; + } + } + void SetPlayerHealth::_Apply() { + GameInteractor::RawAction::SetPlayerHealth(parameter); + } + + // MARK: - FreezePlayer + GameInteractionEffectQueryResult FreezePlayer::CanBeApplied() { + Player* player = GET_PLAYER(gPlayState); + if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + return GameInteractionEffectQueryResult::TemporarilyNotPossible; + } else { + return GameInteractionEffectQueryResult::Possible; + } + } + void FreezePlayer::_Apply() { + GameInteractor::RawAction::FreezePlayer(); + } + + // MARK: - BurnPlayer + GameInteractionEffectQueryResult BurnPlayer::CanBeApplied() { + Player* player = GET_PLAYER(gPlayState); + if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused() || !PlayerGrounded(player)) { + return GameInteractionEffectQueryResult::TemporarilyNotPossible; + } else { + return GameInteractionEffectQueryResult::Possible; + } + } + void BurnPlayer::_Apply() { + GameInteractor::RawAction::BurnPlayer(); + } + + // MARK: - ElectrocutePlayer + GameInteractionEffectQueryResult ElectrocutePlayer::CanBeApplied() { + Player* player = GET_PLAYER(gPlayState); + if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused() || !PlayerGrounded(player)) { + return GameInteractionEffectQueryResult::TemporarilyNotPossible; + } else { + return GameInteractionEffectQueryResult::Possible; + } + } + void ElectrocutePlayer::_Apply() { + GameInteractor::RawAction::ElectrocutePlayer(); + } + + // MARK: - KnockbackPlayer + GameInteractionEffectQueryResult KnockbackPlayer::CanBeApplied() { + if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + return GameInteractionEffectQueryResult::TemporarilyNotPossible; + } else { + return GameInteractionEffectQueryResult::Possible; + } + } + void KnockbackPlayer::_Apply() { + GameInteractor::RawAction::KnockbackPlayer(parameter); + } + + // MARK: - ModifyLinkSize + GameInteractionEffectQueryResult ModifyLinkSize::CanBeApplied() { + if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + return GameInteractionEffectQueryResult::TemporarilyNotPossible; + } else { + return GameInteractionEffectQueryResult::Possible; + } + } + void ModifyLinkSize::_Apply() { + GameInteractor::State::LinkSize = (GILinkSize)parameter; + } + void ModifyLinkSize::_Remove() { + GameInteractor::State::LinkSize = GI_LINK_SIZE_NORMAL; + } + + // MARK: - InvisibleLink + GameInteractionEffectQueryResult InvisibleLink::CanBeApplied() { + if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + return GameInteractionEffectQueryResult::TemporarilyNotPossible; + } else { + return GameInteractionEffectQueryResult::Possible; + } + } + void InvisibleLink::_Apply() { + GameInteractor::RawAction::SetLinkInvisibility(true); + } + void InvisibleLink::_Remove() { + GameInteractor::RawAction::SetLinkInvisibility(false); + } + + // MARK: - PacifistMode + GameInteractionEffectQueryResult PacifistMode::CanBeApplied() { + if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + return GameInteractionEffectQueryResult::TemporarilyNotPossible; + } else { + return GameInteractionEffectQueryResult::Possible; + } + } + void PacifistMode::_Apply() { + GameInteractor::State::SetPacifistMode(true); + } + void PacifistMode::_Remove() { + GameInteractor::State::SetPacifistMode(false); + } + + // MARK: - DisableZTargeting + GameInteractionEffectQueryResult DisableZTargeting::CanBeApplied() { + if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + return GameInteractionEffectQueryResult::TemporarilyNotPossible; + } else { + return GameInteractionEffectQueryResult::Possible; + } + } + void DisableZTargeting::_Apply() { + GameInteractor::State::DisableZTargetingActive = 1; + } + void DisableZTargeting::_Remove() { + GameInteractor::State::DisableZTargetingActive = 0; + } + + // MARK: - WeatherRainstorm + GameInteractionEffectQueryResult WeatherRainstorm::CanBeApplied() { + if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + return GameInteractionEffectQueryResult::TemporarilyNotPossible; + } else { + return GameInteractionEffectQueryResult::Possible; + } + } + void WeatherRainstorm::_Apply() { + GameInteractor::RawAction::SetWeatherStorm(true); + } + void WeatherRainstorm::_Remove() { + GameInteractor::RawAction::SetWeatherStorm(false); + } + + // MARK: - ReverseControls + GameInteractionEffectQueryResult ReverseControls::CanBeApplied() { + if (!GameInteractor::IsSaveLoaded()) { + return GameInteractionEffectQueryResult::TemporarilyNotPossible; + } else { + return GameInteractionEffectQueryResult::Possible; + } + } + void ReverseControls::_Apply() { + GameInteractor::State::ReverseControlsActive = 1; + } + void ReverseControls::_Remove() { + GameInteractor::State::ReverseControlsActive = 0; + } + + // MARK: - ForceEquipBoots + GameInteractionEffectQueryResult ForceEquipBoots::CanBeApplied() { + if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + return GameInteractionEffectQueryResult::TemporarilyNotPossible; + } else { + return GameInteractionEffectQueryResult::Possible; + } + } + void ForceEquipBoots::_Apply() { + GameInteractor::RawAction::ForceEquipBoots(parameter); + } + void ForceEquipBoots::_Remove() { + GameInteractor::RawAction::ForceEquipBoots(PLAYER_BOOTS_KOKIRI); + } + + // MARK: - ModifyRunSpeedModifier + GameInteractionEffectQueryResult ModifyRunSpeedModifier::CanBeApplied() { + if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + return GameInteractionEffectQueryResult::TemporarilyNotPossible; + } else { + return GameInteractionEffectQueryResult::Possible; + } + } + void ModifyRunSpeedModifier::_Apply() { + GameInteractor::State::RunSpeedModifier = parameter; + } + void ModifyRunSpeedModifier::_Remove() { + GameInteractor::State::RunSpeedModifier = 0; + } + + // MARK: - OneHitKO + GameInteractionEffectQueryResult OneHitKO::CanBeApplied() { + if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + return GameInteractionEffectQueryResult::TemporarilyNotPossible; + } else { + return GameInteractionEffectQueryResult::Possible; + } + } + void OneHitKO::_Apply() { + GameInteractor::State::OneHitKOActive = 1; + } + void OneHitKO::_Remove() { + GameInteractor::State::OneHitKOActive = 0; + } + + // MARK: - IncreaseDamageTaken + GameInteractionEffectQueryResult ModifyDefenseModifier::CanBeApplied() { + if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + return GameInteractionEffectQueryResult::TemporarilyNotPossible; + } else { + return GameInteractionEffectQueryResult::Possible; + } + } + void ModifyDefenseModifier::_Apply() { + GameInteractor::State::DefenseModifier = parameter; + } + void ModifyDefenseModifier::_Remove() { + GameInteractor::State::DefenseModifier = 0; + } + + // MARK: - GiveDekuShield + GameInteractionEffectQueryResult GiveDekuShield::CanBeApplied() { + if (!GameInteractor::IsSaveLoaded()) { + return GameInteractionEffectQueryResult::TemporarilyNotPossible; + } else if ((Item_CheckObtainability(ITEM_SHIELD_DEKU) != ITEM_NONE)) { + return GameInteractionEffectQueryResult::NotPossible; + } else { + return GameInteractionEffectQueryResult::Possible; + } + } + void GiveDekuShield::_Apply() { + GameInteractor::RawAction::GiveDekuShield(); + } + + // MARK: - SpawnCuccoStorm + GameInteractionEffectQueryResult SpawnCuccoStorm::CanBeApplied() { + if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + return GameInteractionEffectQueryResult::TemporarilyNotPossible; + } else { + return GameInteractionEffectQueryResult::Possible; + } + } + void SpawnCuccoStorm::_Apply() { + GameInteractor::RawAction::SpawnCuccoStorm(); + } +} diff --git a/soh/soh/Enhancements/game-interactor/GameInteractionEffect.h b/soh/soh/Enhancements/game-interactor/GameInteractionEffect.h new file mode 100644 index 000000000..7f0ed9ee4 --- /dev/null +++ b/soh/soh/Enhancements/game-interactor/GameInteractionEffect.h @@ -0,0 +1,162 @@ +#pragma once + +#ifndef GameInteractionEffect_h +#define GameInteractionEffect_h + +#include + +#ifdef __cplusplus +enum GameInteractionEffectQueryResult { + Possible = 0x00, + TemporarilyNotPossible = 0x01, + NotPossible = 0xFF +}; + +class GameInteractionEffectBase { +public: + virtual GameInteractionEffectQueryResult CanBeApplied() = 0; + virtual GameInteractionEffectQueryResult CanBeRemoved(); + GameInteractionEffectQueryResult Apply(); + GameInteractionEffectQueryResult Remove(); + int32_t parameter; +protected: + virtual void _Apply() = 0; + virtual void _Remove() {}; +}; + +namespace GameInteractionEffect { + class ModifyHeartContainers: public GameInteractionEffectBase { + GameInteractionEffectQueryResult CanBeApplied() override; + void _Apply() override; + }; + + class FillMagic: public GameInteractionEffectBase { + GameInteractionEffectQueryResult CanBeApplied() override; + void _Apply() override; + }; + + class EmptyMagic: public GameInteractionEffectBase { + GameInteractionEffectQueryResult CanBeApplied() override; + void _Apply() override; + }; + + class ModifyRupees: public GameInteractionEffectBase { + GameInteractionEffectQueryResult CanBeApplied() override; + void _Apply() override; + }; + + class NoUI: public GameInteractionEffectBase { + GameInteractionEffectQueryResult CanBeApplied() override; + void _Apply() override; + void _Remove() override; + }; + + class ModifyGravity: public GameInteractionEffectBase { + GameInteractionEffectQueryResult CanBeApplied() override; + void _Apply() override; + void _Remove() override; + }; + + class ModifyHealth: public GameInteractionEffectBase { + GameInteractionEffectQueryResult CanBeApplied() override; + void _Apply() override; + }; + + class SetPlayerHealth: public GameInteractionEffectBase { + GameInteractionEffectQueryResult CanBeApplied() override; + void _Apply() override; + }; + + class FreezePlayer: public GameInteractionEffectBase { + GameInteractionEffectQueryResult CanBeApplied() override; + void _Apply() override; + }; + + class BurnPlayer: public GameInteractionEffectBase { + GameInteractionEffectQueryResult CanBeApplied() override; + void _Apply() override; + }; + + class ElectrocutePlayer: public GameInteractionEffectBase { + GameInteractionEffectQueryResult CanBeApplied() override; + void _Apply() override; + }; + + class KnockbackPlayer: public GameInteractionEffectBase { + GameInteractionEffectQueryResult CanBeApplied() override; + void _Apply() override; + }; + + class ModifyLinkSize: public GameInteractionEffectBase { + GameInteractionEffectQueryResult CanBeApplied() override; + void _Apply() override; + void _Remove() override; + }; + + class InvisibleLink : public GameInteractionEffectBase { + GameInteractionEffectQueryResult CanBeApplied() override; + void _Apply() override; + void _Remove() override; + }; + + class PacifistMode : public GameInteractionEffectBase { + GameInteractionEffectQueryResult CanBeApplied() override; + void _Apply() override; + void _Remove() override; + }; + + class DisableZTargeting: public GameInteractionEffectBase { + GameInteractionEffectQueryResult CanBeApplied() override; + void _Apply() override; + void _Remove() override; + }; + + class WeatherRainstorm: public GameInteractionEffectBase { + GameInteractionEffectQueryResult CanBeApplied() override; + void _Apply() override; + void _Remove() override; + }; + + class ReverseControls: public GameInteractionEffectBase { + GameInteractionEffectQueryResult CanBeApplied() override; + void _Apply() override; + void _Remove() override; + }; + + class ForceEquipBoots: public GameInteractionEffectBase { + GameInteractionEffectQueryResult CanBeApplied() override; + void _Apply() override; + void _Remove() override; + }; + + class ModifyRunSpeedModifier: public GameInteractionEffectBase { + GameInteractionEffectQueryResult CanBeApplied() override; + void _Apply() override; + void _Remove() override; + }; + + class OneHitKO : public GameInteractionEffectBase { + GameInteractionEffectQueryResult CanBeApplied() override; + void _Apply() override; + void _Remove() override; + }; + + class ModifyDefenseModifier: public GameInteractionEffectBase { + GameInteractionEffectQueryResult CanBeApplied() override; + void _Apply() override; + void _Remove() override; + }; + + class GiveDekuShield: public GameInteractionEffectBase { + GameInteractionEffectQueryResult CanBeApplied() override; + void _Apply() override; + }; + + class SpawnCuccoStorm: public GameInteractionEffectBase { + GameInteractionEffectQueryResult CanBeApplied() override; + void _Apply() override; + }; +} + +#endif /* __cplusplus */ +#endif /* GameInteractionEffect_h */ diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor.cpp b/soh/soh/Enhancements/game-interactor/GameInteractor.cpp new file mode 100644 index 000000000..44f9877e1 --- /dev/null +++ b/soh/soh/Enhancements/game-interactor/GameInteractor.cpp @@ -0,0 +1,55 @@ +/* +GameInteractor is meant to be used for interacting with the game (yup...). +It exposes functions that directly modify, add or remove game related elements. + +GameInteractionEffects.cpp is used when code that needs these +functions also need a check wether a command can be run or not. + +If these checks need to happen wherever GameInteractor functions are needed, the +GameInteractor functions can be called directly. +*/ + +#include "GameInteractor.h" +#include + +extern "C" { +#include "variables.h" +#include "macros.h" +#include "functions.h" +extern PlayState* gPlayState; +} + +#include "overlays/actors/ovl_En_Niw/z_en_niw.h" + +// MARK: - Effects + +GameInteractionEffectQueryResult GameInteractor::CanApplyEffect(GameInteractionEffectBase* effect) { + return effect->CanBeApplied(); +} + +GameInteractionEffectQueryResult GameInteractor::ApplyEffect(GameInteractionEffectBase* effect) { + return effect->Apply(); +} + +GameInteractionEffectQueryResult GameInteractor::RemoveEffect(GameInteractionEffectBase* effect) { + return effect->Remove(); +} + +// MARK: - Helpers + +bool GameInteractor::IsSaveLoaded() { + Player* player; + if (gPlayState != NULL) { + player = GET_PLAYER(gPlayState); + } + return (gPlayState == NULL || player == NULL || gSaveContext.fileNum < 0 || gSaveContext.fileNum > 2) ? false : true; +} + +bool GameInteractor::IsGameplayPaused() { + Player* player = GET_PLAYER(gPlayState); + return (Player_InBlockingCsMode(gPlayState, player) || gPlayState->pauseCtx.state != 0 || gPlayState->msgCtx.msgMode != 0) ? true : false; +} + +bool GameInteractor::CanSpawnEnemy() { + return GameInteractor::IsSaveLoaded() && !GameInteractor::IsGameplayPaused(); +} diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor.h b/soh/soh/Enhancements/game-interactor/GameInteractor.h new file mode 100644 index 000000000..fb36793b7 --- /dev/null +++ b/soh/soh/Enhancements/game-interactor/GameInteractor.h @@ -0,0 +1,94 @@ +#pragma once + +#ifndef GameInteractor_h +#define GameInteractor_h + +#include "GameInteractionEffect.h" + +typedef enum { + /* 0x00 */ GI_LINK_SIZE_NORMAL, + /* 0x01 */ GI_LINK_SIZE_GIANT, + /* 0x02 */ GI_LINK_SIZE_MINISH, + /* 0x03 */ GI_LINK_SIZE_PAPER, +} GILinkSize; + +typedef enum { + /* 0x00 */ GI_GRAVITY_LEVEL_LIGHT, + /* 0x01 */ GI_GRAVITY_LEVEL_NORMAL, + /* 0x02 */ GI_GRAVITY_LEVEL_HEAVY, +} GIGravityLevel; + +#ifdef __cplusplus +extern "C" { +#endif +uint8_t GameInteractor_NoUIActive(); +GILinkSize GameInteractor_LinkSize(); +uint8_t GameInteractor_InvisibleLinkActive(); +uint8_t GameInteractor_ResetLinkScale(); +uint8_t GameInteractor_OneHitKOActive(); +uint8_t GameInteractor_PacifistModeActive(); +uint8_t GameInteractor_DisableZTargetingActive(); +uint8_t GameInteractor_ReverseControlsActive(); +int32_t GameInteractor_DefenseModifier(); +int32_t GameInteractor_RunSpeedModifier(); +GIGravityLevel GameInteractor_GravityLevel(); +#ifdef __cplusplus +} +#endif + + +#ifdef __cplusplus +class GameInteractor { +public: + static GameInteractor* Instance; + + // Gsme State + class State { + public: + static bool NoUIActive; + static GILinkSize LinkSize; + static bool InvisibleLinkActive; + static bool OneHitKOActive; + static bool PacifistModeActive; + static bool DisableZTargetingActive; + static bool ReverseControlsActive; + static int32_t DefenseModifier; + static int32_t RunSpeedModifier; + static GIGravityLevel GravityLevel; + + static void SetPacifistMode(bool active); + }; + + // Effects + static GameInteractionEffectQueryResult CanApplyEffect(GameInteractionEffectBase* effect); + static GameInteractionEffectQueryResult ApplyEffect(GameInteractionEffectBase* effect); + static GameInteractionEffectQueryResult RemoveEffect(GameInteractionEffectBase* effect); + + // Helpers + static bool IsSaveLoaded(); + static bool IsGameplayPaused(); + static bool CanSpawnEnemy(); + + class RawAction { + public: + static void AddOrRemoveHealthContainers(int16_t amount); + static void AddOrRemoveMagic(int8_t amount); + static void HealOrDamagePlayer(int16_t hearts); + static void SetPlayerHealth(int16_t hearts); + static void SetLinkInvisibility(bool active); + static void SetWeatherStorm(bool active); + static void ForceEquipBoots(int8_t boots); + static void FreezePlayer(); + static void BurnPlayer(); + static void ElectrocutePlayer(); + static void KnockbackPlayer(float strength); + static void GiveDekuShield(); + static void SpawnCuccoStorm(); + static void ForceInterfaceUpdate(); + + static GameInteractionEffectQueryResult SpawnEnemyWithOffset(uint32_t enemyId, int32_t enemyParams); + }; +}; + +#endif /* __cplusplus */ +#endif /* GameInteractor_h */ diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_RawAction.cpp b/soh/soh/Enhancements/game-interactor/GameInteractor_RawAction.cpp new file mode 100644 index 000000000..2409347ea --- /dev/null +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_RawAction.cpp @@ -0,0 +1,199 @@ +#include "GameInteractor.h" +#include + +extern "C" { +#include "variables.h" +#include "macros.h" +#include "functions.h" +extern PlayState* gPlayState; +} + +#include "overlays/actors/ovl_En_Niw/z_en_niw.h" + +void GameInteractor::RawAction::AddOrRemoveHealthContainers(int16_t amount) { + gSaveContext.healthCapacity += amount * 0x10; +} + +void GameInteractor::RawAction::AddOrRemoveMagic(int8_t amount) { + // Full single magic = 48 + // Full double magic = 96 + int8_t currentMagicCapacity = (gSaveContext.isDoubleMagicAcquired + 1) * 48; + + if (gSaveContext.isMagicAcquired) { + gSaveContext.prevMagicState = gSaveContext.magicState; + if (amount > 0) { + if (gSaveContext.magic + amount > currentMagicCapacity) { + gSaveContext.magicFillTarget = currentMagicCapacity; + } else { + gSaveContext.magicFillTarget = gSaveContext.magic + amount; + } + gSaveContext.magicState = MAGIC_STATE_FILL; + } else if (amount < 0) { + if (gSaveContext.magic + amount < 0) { + gSaveContext.magic = 0; + } else { + gSaveContext.magic += amount; + } + } + } +} + +void GameInteractor::RawAction::HealOrDamagePlayer(int16_t hearts) { + if (hearts > 0) { + Health_ChangeBy(gPlayState, hearts * 0x10); + } else if (hearts < 0) { + Player* player = GET_PLAYER(gPlayState); + Health_ChangeBy(gPlayState, hearts * 0x10); + func_80837C0C(gPlayState, player, 0, 0, 0, 0, 0); + player->invincibilityTimer = 28; + } +} + +void GameInteractor::RawAction::SetPlayerHealth(int16_t hearts) { + gSaveContext.health = hearts * 0x10; +} + +void GameInteractor::RawAction::SetLinkInvisibility(bool active) { + GameInteractor::State::InvisibleLinkActive = active; + if (!active) { + Player* player = GET_PLAYER(gPlayState); + player->actor.shape.shadowDraw = ActorShadow_DrawFeet; + } +} + +void GameInteractor::RawAction::SetWeatherStorm(bool active) { + if (active) { + gPlayState->envCtx.unk_F2[0] = 20; // rain intensity target + gPlayState->envCtx.gloomySkyMode = 1; // start gloomy sky + if ((gWeatherMode != 0) || gPlayState->envCtx.unk_17 != 0) { + gPlayState->envCtx.unk_DE = 1; + } + gPlayState->envCtx.lightningMode = LIGHTNING_MODE_ON; + Environment_PlayStormNatureAmbience(gPlayState); + } else { + gPlayState->envCtx.unk_F2[0] = 0; + if (gPlayState->csCtx.state == CS_STATE_IDLE) { + Environment_StopStormNatureAmbience(gPlayState); + } else if (func_800FA0B4(SEQ_PLAYER_BGM_MAIN) == NA_BGM_NATURE_AMBIENCE) { + Audio_SetNatureAmbienceChannelIO(NATURE_CHANNEL_LIGHTNING, CHANNEL_IO_PORT_1, 0); + Audio_SetNatureAmbienceChannelIO(NATURE_CHANNEL_RAIN, CHANNEL_IO_PORT_1, 0); + } + osSyncPrintf("\n\n\nE_wether_flg=[%d]", gWeatherMode); + osSyncPrintf("\nrain_evt_trg=[%d]\n\n", gPlayState->envCtx.gloomySkyMode); + if (gWeatherMode == 0 && (gPlayState->envCtx.gloomySkyMode == 1)) { + gPlayState->envCtx.gloomySkyMode = 2; // end gloomy sky + } else { + gPlayState->envCtx.gloomySkyMode = 0; + gPlayState->envCtx.unk_DE = 0; + } + gPlayState->envCtx.lightningMode = LIGHTNING_MODE_LAST; + } +} + +void GameInteractor::RawAction::ForceEquipBoots(int8_t boots) { + Player* player = GET_PLAYER(gPlayState); + player->currentBoots = boots; + Inventory_ChangeEquipment(EQUIP_BOOTS, boots + 1); + Player_SetBootData(gPlayState, player); +} + +void GameInteractor::RawAction::FreezePlayer() { + gSaveContext.pendingIceTrapCount++; +} + +void GameInteractor::RawAction::BurnPlayer() { + Player* player = GET_PLAYER(gPlayState); + for (int i = 0; i < 18; i++) { + player->flameTimers[i] = Rand_S16Offset(0, 200); + } + player->isBurning = true; + func_80837C0C(gPlayState, player, 0, 0, 0, 0, 0); +} + +void GameInteractor::RawAction::ElectrocutePlayer() { + Player* player = GET_PLAYER(gPlayState); + func_80837C0C(gPlayState, player, 4, 0, 0, 0, 0); +} + +void GameInteractor::RawAction::KnockbackPlayer(float strength) { + Player* player = GET_PLAYER(gPlayState); + func_8002F71C(gPlayState, &player->actor, strength * 5, player->actor.world.rot.y + 0x8000, strength * 5); +} + +void GameInteractor::RawAction::GiveDekuShield() { + // Give Deku Shield to the player, and automatically equip it when they're child and have no shield currently equiped. + Player* player = GET_PLAYER(gPlayState); + Item_Give(gPlayState, ITEM_SHIELD_DEKU); + if (LINK_IS_CHILD && player->currentShield == PLAYER_SHIELD_NONE) { + player->currentShield = PLAYER_SHIELD_DEKU; + Inventory_ChangeEquipment(EQUIP_SHIELD, PLAYER_SHIELD_DEKU); + } +} + +void GameInteractor::RawAction::SpawnCuccoStorm() { + Player* player = GET_PLAYER(gPlayState); + EnNiw* cucco = (EnNiw*)Actor_Spawn(&gPlayState->actorCtx, gPlayState, ACTOR_EN_NIW, player->actor.world.pos.x, + player->actor.world.pos.y + 2200, player->actor.world.pos.z, 0, 0, 0, 0, 0); + cucco->actionFunc = func_80AB70A0_nocutscene; +} + +void GameInteractor::RawAction::ForceInterfaceUpdate() { + gSaveContext.unk_13E8 = 50; + Interface_Update(gPlayState); +} + +GameInteractionEffectQueryResult GameInteractor::RawAction::SpawnEnemyWithOffset(uint32_t enemyId, int32_t enemyParams) { + + if (!GameInteractor::CanSpawnEnemy()) { + return GameInteractionEffectQueryResult::TemporarilyNotPossible; + } + + Player* player = GET_PLAYER(gPlayState); + + float posXOffset = 0; + float posYOffset = 0; + float posZOffset = 0; + + if (enemyId == ACTOR_EN_WALLMAS) { + + } else if (enemyId == ACTOR_EN_CLEAR_TAG) { + // Don't allow Arwings in certain areas because they cause issues. + // Locations: King dodongo room, Morpha room, Twinrova room, Ganondorf room, Fishing pond, Ganon's room + // TODO: Swap this to disabling the option in CC options menu instead. + if (gPlayState->sceneNum == SCENE_DDAN_BOSS || gPlayState->sceneNum == SCENE_MIZUSIN_BS || + gPlayState->sceneNum == SCENE_JYASINBOSS || gPlayState->sceneNum == SCENE_GANON_BOSS || + gPlayState->sceneNum == SCENE_TURIBORI || gPlayState->sceneNum == SCENE_GANON_DEMO) { + return GameInteractionEffectQueryResult::NotPossible; + } + posYOffset = 100; + } else if (enemyId == ACTOR_EN_TORCH2) { + posXOffset = 75; + posYOffset = 50; + } else if (enemyId == ACTOR_EN_TEST) { + posXOffset = 75; + posYOffset = 50; + } else if (enemyId == ACTOR_EN_WF) { + posXOffset = 75; + posYOffset = 50; + } else if (enemyId == ACTOR_EN_FZ) { + posXOffset = 75; + posYOffset = 50; + } else if (enemyId == ACTOR_EN_FIREFLY) { + posXOffset = 75; + posYOffset = 50; + } else if (enemyId == ACTOR_EN_TITE) { + posXOffset = 75; + posYOffset = 50; + } else if (enemyId == ACTOR_EN_RR) { + posXOffset = 75; + posYOffset = 50; + } + + if (Actor_Spawn(&gPlayState->actorCtx, gPlayState, enemyId, player->actor.world.pos.x + posXOffset, + player->actor.world.pos.y + posYOffset, player->actor.world.pos.z + posZOffset, 0, 0, 0, + enemyParams, 0) != NULL) { + return GameInteractionEffectQueryResult::Possible; + } + + return GameInteractionEffectQueryResult::TemporarilyNotPossible; +} diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_State.cpp b/soh/soh/Enhancements/game-interactor/GameInteractor_State.cpp new file mode 100644 index 000000000..4846080b0 --- /dev/null +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_State.cpp @@ -0,0 +1,76 @@ +#include "GameInteractor.h" + +// MARK: - State Definitions + +bool GameInteractor::State::NoUIActive = 0; +GILinkSize GameInteractor::State::LinkSize = GI_LINK_SIZE_NORMAL; +bool GameInteractor::State::InvisibleLinkActive = 0; +bool GameInteractor::State::OneHitKOActive = 0; +bool GameInteractor::State::PacifistModeActive = 0; +bool GameInteractor::State::DisableZTargetingActive = 0; +bool GameInteractor::State::ReverseControlsActive = 0; +int32_t GameInteractor::State::DefenseModifier = 0; +int32_t GameInteractor::State::RunSpeedModifier = 0; +GIGravityLevel GameInteractor::State::GravityLevel = GI_GRAVITY_LEVEL_NORMAL; + +void GameInteractor::State::SetPacifistMode(bool active) { + PacifistModeActive = active; + // Force interface update to update the button's transparency. + GameInteractor::RawAction::ForceInterfaceUpdate(); +} + +// MARK: C - Bridge + +// MARK: - GameInteractor::State::NoUIActive +uint8_t GameInteractor_NoUIActive() { + return GameInteractor::State::NoUIActive; +} + +// MARK: - GameInteractor::State::LinkSize +GILinkSize GameInteractor_LinkSize() { + return GameInteractor::State::LinkSize; +} + +// MARK: - GameInteractor::State::InvisibleLinkActive +uint8_t GameInteractor_InvisibleLinkActive() { + return GameInteractor::State::InvisibleLinkActive; +} + +void GameInteractor_SetInvisibleLinkActive(uint8_t active) { + GameInteractor::State::InvisibleLinkActive = active; +} + +// MARK: - GameInteractor::State::OneHitKOActive +uint8_t GameInteractor_OneHitKOActive() { + return GameInteractor::State::OneHitKOActive; +} + +// MARK: - GameInteractor::State::PacifistModeActive +uint8_t GameInteractor_PacifistModeActive() { + return GameInteractor::State::PacifistModeActive; +} + +// MARK: - GameInteractor::State::DisableZTargetingActive +uint8_t GameInteractor_DisableZTargetingActive() { + return GameInteractor::State::DisableZTargetingActive; +} + +// MARK: - GameInteractor::State::DisableCameraRotationActive +uint8_t GameInteractor_ReverseControlsActive() { + return GameInteractor::State::ReverseControlsActive; +} + +// MARK: - GameInteractor::State::DisableCameraRotationActive +int32_t GameInteractor_DefenseModifier() { + return GameInteractor::State::DefenseModifier; +} + +// MARK: - GameInteractor::State::DisableCameraRotationActive +int32_t GameInteractor_RunSpeedModifier() { + return GameInteractor::State::RunSpeedModifier; +} + +// MARK: - GameInteractor::State::DisableCameraRotationActive +GIGravityLevel GameInteractor_GravityLevel() { + return GameInteractor::State::GravityLevel; +} diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index a34c69917..86192ae2f 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -71,6 +71,7 @@ CrowdControl* CrowdControl::Instance; #endif +#include "Enhancements/game-interactor/GameInteractor.h" #include "libultraship/libultraship.h" // Resource Types/Factories @@ -104,6 +105,7 @@ OTRGlobals* OTRGlobals::Instance; SaveManager* SaveManager::Instance; CustomMessageManager* CustomMessageManager::Instance; ItemTableManager* ItemTableManager::Instance; +GameInteractor* GameInteractor::Instance; OTRGlobals::OTRGlobals() { std::vector OTRFiles; @@ -471,6 +473,7 @@ extern "C" void InitOTR() { SaveManager::Instance = new SaveManager(); CustomMessageManager::Instance = new CustomMessageManager(); ItemTableManager::Instance = new ItemTableManager(); + GameInteractor::Instance = new GameInteractor(); clearMtx = (uintptr_t)&gMtxClear; OTRMessage_Init(); diff --git a/soh/src/code/padmgr.c b/soh/src/code/padmgr.c index c79bbfe16..cb56cb806 100644 --- a/soh/src/code/padmgr.c +++ b/soh/src/code/padmgr.c @@ -1,7 +1,7 @@ #include "global.h" #include "vt.h" -#include "soh/Enhancements/debugconsole.h" +#include "soh/Enhancements/game-interactor/GameInteractor.h" //#include @@ -229,11 +229,11 @@ void PadMgr_ProcessInputs(PadMgr* padMgr) { case 0: input->cur = *padnow1; - if (chaosEffectNoZ) { + if (GameInteractor_DisableZTargetingActive()) { input->cur.button &= ~(BTN_Z); } - if (chaosEffectReverseControls) { + if (GameInteractor_ReverseControlsActive()) { if (input->cur.stick_x == -128) { input->cur.stick_x = 127; } else { diff --git a/soh/src/code/z_parameter.c b/soh/src/code/z_parameter.c index c7ec3b46f..167716ccd 100644 --- a/soh/src/code/z_parameter.c +++ b/soh/src/code/z_parameter.c @@ -13,7 +13,7 @@ #include #endif -#include "soh/Enhancements/debugconsole.h" +#include "soh/Enhancements/game-interactor/GameInteractor.h" static uint16_t _doActionTexWidth, _doActionTexHeight = -1; @@ -939,7 +939,7 @@ void func_80083108(PlayState* play) { Interface_ChangeAlpha(50); } } else if (msgCtx->msgMode == MSGMODE_NONE) { - if (chaosEffectPacifistMode) { + if (GameInteractor_PacifistModeActive()) { gSaveContext.buttonStatus[0] = gSaveContext.buttonStatus[1] = gSaveContext.buttonStatus[2] = gSaveContext.buttonStatus[3] = gSaveContext.buttonStatus[5] = gSaveContext.buttonStatus[6] = gSaveContext.buttonStatus[7] = gSaveContext.buttonStatus[8] = BTN_DISABLED; @@ -3074,7 +3074,7 @@ s32 Health_ChangeBy(PlayState* play, s16 healthChange) { } // If one-hit ko mode is on, any damage kills you and you cannot gain health. - if (chaosEffectOneHitKO) { + if (GameInteractor_OneHitKOActive()) { if (healthChange < 0) { gSaveContext.health = 0; } @@ -3091,11 +3091,12 @@ s32 Health_ChangeBy(PlayState* play, s16 healthChange) { } // clang-format on - if (chaosEffectDefenseModifier != 0 && healthChange < 0) { - if (chaosEffectDefenseModifier > 0) { - healthChange /= chaosEffectDefenseModifier; + int32_t giDefenseModifier = GameInteractor_DefenseModifier(); + if (giDefenseModifier != 0 && healthChange < 0) { + if (giDefenseModifier > 0) { + healthChange /= giDefenseModifier; } else { - healthChange *= abs(chaosEffectDefenseModifier); + healthChange *= abs(giDefenseModifier); } } @@ -3129,14 +3130,6 @@ s32 Health_ChangeBy(PlayState* play, s16 healthChange) { } } -void Health_GiveHearts(s16 hearts) { - gSaveContext.healthCapacity += hearts * 0x10; -} - -void Health_RemoveHearts(s16 hearts) { - gSaveContext.healthCapacity -= hearts * 0x10; -} - void Rupees_ChangeBy(s16 rupeeChange) { gSaveContext.rupeeAccumulator += rupeeChange; @@ -4968,7 +4961,7 @@ void Interface_Draw(PlayState* play) { s16 svar6; bool fullUi = !CVarGetInteger("gMinimalUI", 0) || !R_MINIMAP_DISABLED || play->pauseCtx.state != 0; - if (chaosEffectNoUI) { + if (GameInteractor_NoUIActive()) { return; } diff --git a/soh/src/code/z_player_lib.c b/soh/src/code/z_player_lib.c index 6569c2a5c..da7dff3e5 100644 --- a/soh/src/code/z_player_lib.c +++ b/soh/src/code/z_player_lib.c @@ -6,7 +6,7 @@ #include "objects/object_triforce_spot/object_triforce_spot.h" #include "overlays/actors/ovl_Demo_Effect/z_demo_effect.h" -#include "soh/Enhancements/debugconsole.h" +#include "soh/Enhancements/game-interactor/GameInteractor.h" typedef struct { /* 0x00 */ u8 flag; @@ -1050,7 +1050,7 @@ s32 func_80090014(PlayState* play, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s } } - if (chaosEffectInvisibleLink) { + if (GameInteractor_InvisibleLinkActive()) { this->actor.shape.shadowDraw = NULL; *dList = NULL; } diff --git a/soh/src/overlays/actors/ovl_player_actor/z_player.c b/soh/src/overlays/actors/ovl_player_actor/z_player.c index 0aa4a65bf..135dc8eff 100644 --- a/soh/src/overlays/actors/ovl_player_actor/z_player.c +++ b/soh/src/overlays/actors/ovl_player_actor/z_player.c @@ -22,7 +22,7 @@ #include "textures/icon_item_24_static/icon_item_24_static.h" #include #include "soh/Enhancements/item-tables/ItemTableTypes.h" -#include "soh/Enhancements/debugconsole.h" +#include "soh/Enhancements/game-interactor/GameInteractor.h" #include "soh/Enhancements/randomizer/randomizer_entrance.h" typedef enum { @@ -6080,11 +6080,12 @@ void func_8083DFE0(Player* this, f32* arg1, s16* arg2) { if (this->swordState == 0) { float maxSpeed = R_RUN_SPEED_LIMIT / 100.0f; - if (chaosEffectSpeedModifier != 0) { - if (chaosEffectSpeedModifier > 0) { - maxSpeed *= chaosEffectSpeedModifier; + int32_t giSpeedModifier = GameInteractor_RunSpeedModifier(); + if (giSpeedModifier != 0) { + if (giSpeedModifier > 0) { + maxSpeed *= giSpeedModifier; } else { - maxSpeed /= abs(chaosEffectSpeedModifier); + maxSpeed /= abs(giSpeedModifier); } } @@ -7717,11 +7718,12 @@ void func_80842180(Player* this, PlayState* play) { func_80837268(this, &sp2C, &sp2A, 0.018f, play); if (!func_8083C484(this, &sp2C, &sp2A)) { - if (chaosEffectSpeedModifier != 0) { - if (chaosEffectSpeedModifier > 0) { - sp2C *= chaosEffectSpeedModifier; + int32_t giSpeedModifier = GameInteractor_RunSpeedModifier(); + if (giSpeedModifier != 0) { + if (giSpeedModifier > 0) { + sp2C *= giSpeedModifier; } else { - sp2C /= abs(chaosEffectSpeedModifier); + sp2C /= abs(giSpeedModifier); } } @@ -10993,37 +10995,39 @@ void Player_Update(Actor* thisx, PlayState* play) { MREG(54) = this->actor.world.pos.z; MREG(55) = this->actor.world.rot.y; - if (chaosEffectGiantLink) { - this->actor.scale.x = 0.02f; - this->actor.scale.y = 0.02f; - this->actor.scale.z = 0.02f; + switch (GameInteractor_LinkSize()) { + case GI_LINK_SIZE_GIANT: + this->actor.scale.x = 0.02f; + this->actor.scale.y = 0.02f; + this->actor.scale.z = 0.02f; + break; + case GI_LINK_SIZE_MINISH: + this->actor.scale.x = 0.001f; + this->actor.scale.y = 0.001f; + this->actor.scale.z = 0.001f; + break; + case GI_LINK_SIZE_PAPER: + this->actor.scale.x = 0.001f; + this->actor.scale.y = 0.01f; + this->actor.scale.z = 0.01f; + break; + case GI_LINK_SIZE_NORMAL: + default: + this->actor.scale.x = 0.01f; + this->actor.scale.y = 0.01f; + this->actor.scale.z = 0.01f; + break; } - if (chaosEffectMinishLink) { - this->actor.scale.x = 0.001f; - this->actor.scale.y = 0.001f; - this->actor.scale.z = 0.001f; - } - - if (chaosEffectPaperLink) { - this->actor.scale.x = 0.001f; - this->actor.scale.y = 0.01f; - this->actor.scale.z = 0.01f; - } - - if (chaosEffectResetLinkScale) { - this->actor.scale.x = 0.01f; - this->actor.scale.y = 0.01f; - this->actor.scale.z = 0.01f; - chaosEffectResetLinkScale = 0; - } - - if (chaosEffectGravityLevel == GRAVITY_LEVEL_HEAVY) { - this->actor.gravity = -4.0f; - } - - if (chaosEffectGravityLevel == GRAVITY_LEVEL_LIGHT) { - this->actor.gravity = -0.3f; + switch (GameInteractor_GravityLevel()) { + case GI_GRAVITY_LEVEL_HEAVY: + this->actor.gravity = -4.0f; + break; + case GI_GRAVITY_LEVEL_LIGHT: + this->actor.gravity = -0.3f; + break; + default: + break; } }