Implement GameInteractor & move CrowdControl and console commands to it (#2358)

* Begin GameInteractor

* Basic skeleton of PoC

* WIP

* First 2 CC effects transitioned to GameInteractor

* Prepare GameInteractor classes for CrowdControl

* More effects & replace chaosEffects with GameInteractor

* CC connection fixes & all CC effects (enemy spawns still borked)

* First couple of build error fixes

* Fix build

* Proper enemy spawning

* Clean up old CC code

* Extract link size/invisibility into GameInteractor

* Small fix/cleanup

* Suggestions for PR

* Address PR comment

* Addressed more comments & small adjustments

* Fix crash when spawning enemies

* Remove Remove()

* Move checks into Apply() and move CC and some console commands to it

* Use inheritance to abstract check on application

* Rename prefix Actions with RawAction

* Make Remove return a Result

* Fix issue with compilation

* debugconsole -> GameInteractionEffects progress

* Add State in GI

* Unify some Effects

* Port more debug console items

* Remove state modifyiers from raw actions

* Port over last raw action / state in console

* Adjust some types

* Consolidate link size modifier effect

* Adjust more types

* Define category strings in CC

* Clean up remaining non defined strings

* Fix bug in timed effects

* Rename old pack

* CC fixes

* Translate GI enum function

* Console cleanup/fixes/consistency

Co-authored-by: David Chavez <david@dcvz.io>
This commit is contained in:
aMannus 2023-01-26 00:13:33 +01:00 committed by GitHub
parent ec4cee787c
commit 170a9103f9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 1686 additions and 598 deletions

View File

@ -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}

View File

@ -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);

View File

@ -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<std::string>();
auto effectName = dataReceived["code"].get<std::string>();
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

View File

@ -17,6 +17,8 @@
#include <chrono>
#include <future>
#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;

View File

@ -8,10 +8,10 @@
#include <string>
#include "soh/OTRGlobals.h"
#include <soh/Enhancements/item-tables/ItemTableManager.h>
#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 <Utils/StringHelper.h>
@ -30,24 +30,9 @@ extern PlayState* gPlayState;
}
#include <libultraship/bridge.h>
#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<Ship::Console> Console, const std::vector<std::string>& 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<Ship::Console> Console, const std:
}
static bool GiveDekuShieldHandler(std::shared_ptr<Ship::Console> Console, const std::vector<std::string>&) {
// 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<Ship::Console> Console, const std::vector<std::string>&) {
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<Ship::Console> Console, const std::vector<std::string>& args) {
@ -120,7 +112,6 @@ static bool SetPlayerHealthHandler(std::shared_ptr<Ship::Console> 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<Ship::Console> 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<Ship::Console> Console, const std::vector<std::string>&) {
@ -168,7 +165,7 @@ static bool RupeeHandler(std::shared_ptr<Ship::Console> 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<Ship::Console> 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<Ship::Console> 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<Ship::Console> Console, const std::vector<std::string>& args) {
@ -546,21 +550,27 @@ static bool GiantLinkHandler(std::shared_ptr<Ship::Console> 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<Ship::Console> Console, const std::vector<std::string>& args) {
@ -568,37 +578,89 @@ static bool MinishLinkHandler(std::shared_ptr<Ship::Console> 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<Ship::Console> Console, const std::vector<std::string>& 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<Ship::Console> Console, const std::vector<std::string>& 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<Ship::Console> Console, const std::vector<std::string>& args) {
@ -607,11 +669,21 @@ static bool GravityHandler(std::shared_ptr<Ship::Console> 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<Ship::Console> 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<Ship::Console> Console, const std::vector<std::string>& 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<Ship::Console> Console, const std::vector<std::string>& args) {
@ -641,14 +734,23 @@ static bool DefenseModifierHandler(std::shared_ptr<Ship::Console> 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<Ship::Console> Console, const std::vector<std::string>& args) {
@ -656,6 +758,7 @@ static bool DamageHandler(std::shared_ptr<Ship::Console> 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<Ship::Console> 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<Ship::Console> Console, const std::vector<std::string>& args) {
@ -682,6 +788,7 @@ static bool HealHandler(std::shared_ptr<Ship::Console> 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<Ship::Console> 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<Ship::Console> Console, const std::vector<std::string>& 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<Ship::Console> Console, const std::vector<std::string>& 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<Ship::Console> Console, const std::vector<std::string>& args) {
@ -713,14 +844,27 @@ static bool NoZHandler(std::shared_ptr<Ship::Console> 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<Ship::Console> Console, const std::vector<std::string>& args) {
@ -728,14 +872,27 @@ static bool OneHitKOHandler(std::shared_ptr<Ship::Console> 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<Ship::Console> Console, const std::vector<std::string>& args) {
@ -743,17 +900,27 @@ static bool PacifistHandler(std::shared_ptr<Ship::Console> 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<Ship::Console> Console, const std::vector<std::string>& args) {
@ -761,20 +928,28 @@ static bool PaperLinkHandler(std::shared_ptr<Ship::Console> 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<Ship::Console> Console, const std::vector<std::string>& args) {
@ -782,40 +957,25 @@ static bool RainstormHandler(std::shared_ptr<Ship::Console> 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<Ship::Console> 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<Ship::Console> Console, const std::vector<std::string>& args) {
@ -840,15 +1014,23 @@ static bool UpdateRupeesHandler(std::shared_ptr<Ship::Console> 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<Ship::Console> Console, const std::vector<std::string>& args) {
@ -856,14 +1038,23 @@ static bool SpeedModifierHandler(std::shared_ptr<Ship::Console> 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<std::string, uint16_t> boots {
@ -884,12 +1075,17 @@ static bool BootsHandler(std::shared_ptr<Ship::Console> 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<Ship::Console> Console, const std::vector<std::string>& args) {
@ -897,6 +1093,7 @@ static bool KnockbackHandler(std::shared_ptr<Ship::Console> 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<Ship::Console> 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<Ship::Console> Console, const std::vector<std::string>& 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<Ship::Console> Console, const std::vector<std::string>& 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<Ship::Console> Console, const std::vector<std::string>& 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<Ship::Console> Console, const std::vector<std::string>& args) {

View File

@ -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);

View File

@ -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 <libultraship/bridge.h>
extern "C" {
#include <z64.h>
#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();
}
}

View File

@ -0,0 +1,162 @@
#pragma once
#ifndef GameInteractionEffect_h
#define GameInteractionEffect_h
#include <stdint.h>
#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 */

View File

@ -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 <libultraship/bridge.h>
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();
}

View File

@ -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 */

View File

@ -0,0 +1,199 @@
#include "GameInteractor.h"
#include <libultraship/bridge.h>
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;
}

View File

@ -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;
}

View File

@ -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<std::string> 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();

View File

@ -1,7 +1,7 @@
#include "global.h"
#include "vt.h"
#include "soh/Enhancements/debugconsole.h"
#include "soh/Enhancements/game-interactor/GameInteractor.h"
//#include <string.h>
@ -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 {

View File

@ -13,7 +13,7 @@
#include <assert.h>
#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;
}

View File

@ -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;
}

View File

@ -22,7 +22,7 @@
#include "textures/icon_item_24_static/icon_item_24_static.h"
#include <soh/Enhancements/custom-message/CustomMessageTypes.h>
#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;
}
}