diff --git a/soh/CMakeLists.txt b/soh/CMakeLists.txt index 8475ebe0e..577c0c33c 100644 --- a/soh/CMakeLists.txt +++ b/soh/CMakeLists.txt @@ -141,6 +141,7 @@ list(FILTER soh__Enhancements EXCLUDE REGEX "soh/Enhancements/gfx.*") # handle crowd control removals list(REMOVE_ITEM soh__Enhancements "soh/Enhancements/crowd-control/soh.cs") +list(REMOVE_ITEM soh__Enhancements "soh/Enhancements/crowd-control/soh.ccpak") if (!BUILD_CROWD_CONTROL) list(FILTER soh__Enhancements EXCLUDE REGEX "soh/Enhancements/crowd-control/*") endif() diff --git a/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp b/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp index aef57c531..56ee6bb5a 100644 --- a/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp +++ b/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp @@ -49,8 +49,6 @@ void ResourceMgr_UnpatchGfxByName(const char* path, const char* patchName); u8 Randomizer_GetSettingValue(RandomizerSettingKey randoSettingKey); } -void ApplyOrResetCustomGfxPatches(bool rainbowTick); - // Not to be confused with tabs, groups are 1:1 with the boxes shown in the UI, grouping them allows us to reset/randomize // every item in a group at once. If you are looking for tabs they are rendered manually in ImGui in `DrawCosmeticsEditor` typedef enum { @@ -425,7 +423,7 @@ void CosmeticsUpdateTick(bool& open) { 4. GFX Command Index: Index of the GFX command you want to replace, the instructions on finding this are in the giant comment block above the cosmeticOptions map 5. GFX Command: The GFX command you want to insert */ -void ApplyOrResetCustomGfxPatches(bool manualChange = true) { +void ApplyOrResetCustomGfxPatches(bool manualChange) { static CosmeticOption& linkGoronTunic = cosmeticOptions.at("Link_GoronTunic"); if (manualChange || CVarGetInteger(linkGoronTunic.rainbowCvar, 0)) { static Color_RGBA8 defaultColor = {linkGoronTunic.defaultColor.x, linkGoronTunic.defaultColor.y, linkGoronTunic.defaultColor.z, linkGoronTunic.defaultColor.w}; @@ -1822,7 +1820,8 @@ void InitCosmeticsEditor() { void CosmeticsEditor_RandomizeAll() { for (auto& [id, cosmeticOption] : cosmeticOptions) { - if (!CVarGetInteger(cosmeticOption.lockedCvar, 0)) { + if (!CVarGetInteger(cosmeticOption.lockedCvar, 0) && + (!cosmeticOption.advancedOption || CVarGetInteger("gCosmetics.AdvancedMode", 0))) { RandomizeColor(cosmeticOption); } } diff --git a/soh/soh/Enhancements/cosmetics/CosmeticsEditor.h b/soh/soh/Enhancements/cosmetics/CosmeticsEditor.h index 12a190c17..475e93d7e 100644 --- a/soh/soh/Enhancements/cosmetics/CosmeticsEditor.h +++ b/soh/soh/Enhancements/cosmetics/CosmeticsEditor.h @@ -27,3 +27,4 @@ void InitCosmeticsEditor();//Init the menu itself ImVec4 GetRandomValue(int MaximumPossible); void CosmeticsEditor_RandomizeAll(); void CosmeticsEditor_ResetAll(); +void ApplyOrResetCustomGfxPatches(bool manualChange = true); diff --git a/soh/soh/Enhancements/crowd-control/CrowdControl.cpp b/soh/soh/Enhancements/crowd-control/CrowdControl.cpp index fd2aef94e..ad3b13818 100644 --- a/soh/soh/Enhancements/crowd-control/CrowdControl.cpp +++ b/soh/soh/Enhancements/crowd-control/CrowdControl.cpp @@ -1,6 +1,7 @@ #ifdef ENABLE_CROWD_CONTROL #include "CrowdControl.h" +#include "CrowdControlTypes.h" #include #include #include @@ -17,65 +18,6 @@ extern "C" { extern PlayState* gPlayState; } -#define EFFECT_HIGH_GRAVITY "high_gravity" -#define EFFECT_LOW_GRAVITY "low_gravity" -#define EFFECT_DAMAGE_MULTIPLIER "damage_multiplier" -#define EFFECT_DEFENSE_MULTIPLIER "defense_multiplier" -#define EFFECT_GIANT_LINK "giant_link" -#define EFFECT_MINISH_LINK "minish_link" -#define EFFECT_INVISIBLE_LINK "invisible" -#define EFFECT_PAPER_LINK "paper_link" -#define EFFECT_FREEZE "freeze" -#define EFFECT_DAMAGE "damage" -#define EFFECT_HEAL "heal" -#define EFFECT_KNOCKBACK "knockback" -#define EFFECT_ELECTROCUTE "electrocute" -#define EFFECT_BURN "burn" -#define EFFECT_KILL "kill" -#define EFFECT_HOVER_BOOTS "hover_boots" -#define EFFECT_IRON_BOOTS "iron_boots" -#define EFFECT_ADD_HEART_CONTAINER "add_heart_container" -#define EFFECT_REMOVE_HEART_CONTAINER "remove_heart_container" -#define EFFECT_NO_UI "no_ui" -#define EFFECT_FILL_MAGIC "fill_magic" -#define EFFECT_EMPTY_MAGIC "empty_magic" -#define EFFECT_OHKO "ohko" -#define EFFECT_PACIFIST "pacifist" -#define EFFECT_RAINSTORM "rainstorm" -#define EFFECT_REVERSE_CONTROLS "reverse" -#define EFFECT_ADD_RUPEES "add_rupees" -#define EFFECT_REMOVE_RUPEES "remove_rupees" -#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" -#define EFFECT_SPAWN_DARK_LINK "spawn_darklink" -#define EFFECT_SPAWN_STALFOS "spawn_stalfos" -#define EFFECT_SPAWN_WOLFOS "spawn_wolfos" -#define EFFECT_SPAWN_FREEZARD "spawn_freezard" -#define EFFECT_SPAWN_KEESE "spawn_keese" -#define EFFECT_SPAWN_ICE_KEESE "spawn_icekeese" -#define EFFECT_SPAWN_FIRE_KEESE "spawn_firekeese" -#define EFFECT_SPAWN_TEKTITE "spawn_tektite" -#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(); } @@ -241,8 +183,6 @@ void CrowdControl::ProcessActiveEffects() { SPDLOG_TRACE("[CrowdControl] Ending Process thread..."); } -// MARK: - Helpers - void CrowdControl::EmitMessage(TCPsocket socket, uint32_t eventId, long timeRemaining, EffectResult status) { nlohmann::json payload; @@ -255,208 +195,12 @@ void CrowdControl::EmitMessage(TCPsocket socket, uint32_t eventId, long timeRema SDLNet_TCP_Send(socket, jsonPayload.c_str(), jsonPayload.size() + 1); } -CrowdControl::Effect* CrowdControl::ParseMessage(char payload[512]) { - nlohmann::json dataReceived = nlohmann::json::parse(payload, nullptr, false); - if (dataReceived.is_discarded()) { - SPDLOG_ERROR("Error parsing JSON"); - return nullptr; - } - - Effect* effect = new Effect(); - effect->lastExecutionResult = EffectResult::Initiate; - effect->id = dataReceived["id"]; - auto parameters = dataReceived["parameters"]; - auto effectName = dataReceived["code"].get(); - - 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; - effect->giEffect = new GameInteractionEffect::NoUI(); - } else if (effectName == EFFECT_HIGH_GRAVITY) { - effect->category = EFFECT_CAT_GRAVITY; - effect->timeRemaining = 30000; - 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; - effect->giEffect = new GameInteractionEffect::PacifistMode(); - } else if (effectName == EFFECT_NO_Z_TARGETING) { - effect->category = EFFECT_CAT_NO_Z; - effect->timeRemaining = 30000; - 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; - effect->giEffect = new GameInteractionEffect::ReverseControls(); - } else if (effectName == EFFECT_IRON_BOOTS) { - effect->category = EFFECT_CAT_BOOTS; - effect->timeRemaining = 30000; - 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; - 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(Effect* effect) { GameInteractionEffectQueryResult giResult; - if (effect->category == EFFECT_CAT_SPAWN_ENEMY) { - giResult = GameInteractor::RawAction::SpawnEnemyWithOffset(effect->value[0], effect->value[1]); + if (effect->category == kEffectCatSpawnEnemy) { + giResult = GameInteractor::RawAction::SpawnEnemyWithOffset(effect->spawnParams[0], effect->spawnParams[1]); + } else if (effect->category == kEffectCatSpawnActor) { + giResult = GameInteractor::RawAction::SpawnActor(effect->spawnParams[0], effect->spawnParams[1]); } else { giResult = GameInteractor::ApplyEffect(effect->giEffect); } @@ -466,7 +210,7 @@ CrowdControl::EffectResult CrowdControl::ExecuteEffect(Effect* effect) { /// Checks if effect can be applied -- should not be used to check for spawn enemy effects. CrowdControl::EffectResult CrowdControl::CanApplyEffect(Effect* effect) { - assert(effect->category != EFFECT_CAT_SPAWN_ENEMY); + assert(effect->category != kEffectCatSpawnEnemy || effect->category != kEffectCatSpawnActor); GameInteractionEffectQueryResult giResult = GameInteractor::CanApplyEffect(effect->giEffect); return TranslateGiEnum(giResult); @@ -486,4 +230,598 @@ CrowdControl::EffectResult CrowdControl::TranslateGiEnum(GameInteractionEffectQu return result; } +CrowdControl::Effect* CrowdControl::ParseMessage(char payload[512]) { + nlohmann::json dataReceived = nlohmann::json::parse(payload, nullptr, false); + if (dataReceived.is_discarded()) { + SPDLOG_ERROR("Error parsing JSON"); + return nullptr; + } + + Effect* effect = new Effect(); + effect->lastExecutionResult = EffectResult::Initiate; + effect->id = dataReceived["id"]; + auto parameters = dataReceived["parameters"]; + uint32_t receivedParameter = 0; + auto effectName = dataReceived["code"].get(); + + if (parameters.size() > 0) { + receivedParameter = dataReceived["parameters"][0]; + } + + // Assign GameInteractionEffect + values to CC effect. + // Categories are mostly used for checking for conflicting timed effects. + switch (effectStringToEnum[effectName]) { + + // Spawn Enemies and Objects + case kEffectSpawnCuccoStorm: + effect->spawnParams[0] = ACTOR_EN_NIW; + effect->category = kEffectCatSpawnActor; + break; + case kEffectSpawnLitBomb: + effect->spawnParams[0] = ACTOR_EN_BOM; + effect->category = kEffectCatSpawnActor; + break; + case kEffectSpawnExplosion: + effect->spawnParams[0] = ACTOR_EN_BOM; + effect->spawnParams[1] = 1; + effect->category = kEffectCatSpawnActor; + break; + case kEffectSpawnArwing: + effect->spawnParams[0] = ACTOR_EN_CLEAR_TAG; + // Parameter for no cutscene Arwing + effect->spawnParams[1] = 1; + effect->category = kEffectCatSpawnEnemy; + break; + case kEffectSpawnDarklink: + effect->spawnParams[0] = ACTOR_EN_TORCH2; + effect->category = kEffectCatSpawnEnemy; + break; + case kEffectSpawnIronKnuckle: + effect->spawnParams[0] = ACTOR_EN_IK; + // Parameter for black standing Iron Knuckle + effect->spawnParams[1] = 2; + effect->category = kEffectCatSpawnEnemy; + break; + case kEffectSpawnStalfos: + effect->spawnParams[0] = ACTOR_EN_TEST; + // Parameter for gravity-obeying Stalfos + effect->spawnParams[1] = 2; + effect->category = kEffectCatSpawnEnemy; + break; + case kEffectSpawnFreezard: + effect->spawnParams[0] = ACTOR_EN_FZ; + effect->category = kEffectCatSpawnEnemy; + break; + case kEffectSpawnLikeLike: + effect->spawnParams[0] = ACTOR_EN_RR; + effect->category = kEffectCatSpawnEnemy; + break; + case kEffectSpawnGibdo: + effect->spawnParams[0] = ACTOR_EN_RD; + // Parameter for Gibdo + effect->spawnParams[1] = 32766; + effect->category = kEffectCatSpawnEnemy; + break; + case kEffectSpawnKeese: + effect->spawnParams[0] = ACTOR_EN_FIREFLY; + // Parameter for normal keese + effect->spawnParams[1] = 2; + effect->category = kEffectCatSpawnEnemy; + break; + case kEffectSpawnIceKeese: + effect->spawnParams[0] = ACTOR_EN_FIREFLY; + // Parameter for ice keese + effect->spawnParams[1] = 4; + effect->category = kEffectCatSpawnEnemy; + break; + case kEffectSpawnFireKeese: + effect->spawnParams[0] = ACTOR_EN_FIREFLY; + // Parameter for fire keese + effect->spawnParams[1] = 1; + effect->category = kEffectCatSpawnEnemy; + break; + case kEffectSpawnWolfos: + effect->spawnParams[0] = ACTOR_EN_WF; + effect->category = kEffectCatSpawnEnemy; + break; + case kEffectSpawnWallmaster: + effect->spawnParams[0] = ACTOR_EN_WALLMAS; + effect->category = kEffectCatSpawnEnemy; + break; + + // Link Modifiers + case kEffectTakeHalfDamage: + effect->category = kEffectCatDamageTaken; + effect->timeRemaining = 30000; + effect->giEffect = new GameInteractionEffect::ModifyDefenseModifier(); + effect->giEffect->parameters[0] = 2; + break; + case kEffectTakeDoubleDamage: + effect->category = kEffectCatDamageTaken; + effect->timeRemaining = 30000; + effect->giEffect = new GameInteractionEffect::ModifyDefenseModifier(); + effect->giEffect->parameters[0] = -2; + break; + case kEffectOneHitKo: + effect->category = kEffectCatOhko; + effect->timeRemaining = 30000; + effect->giEffect = new GameInteractionEffect::OneHitKO(); + break; + case kEffectInvincibility: + effect->category = kEffectCatInvincible; + effect->timeRemaining = 15000; + effect->giEffect = new GameInteractionEffect::PlayerInvincibility(); + break; + break; + case kEffectIncreaseSpeed: + effect->category = kEffectCatSpeed; + effect->timeRemaining = 30000; + effect->giEffect = new GameInteractionEffect::ModifyRunSpeedModifier(); + effect->giEffect->parameters[0] = 2; + break; + case kEffectDecreaseSpeed: + effect->category = kEffectCatSpeed; + effect->timeRemaining = 30000; + effect->giEffect = new GameInteractionEffect::ModifyRunSpeedModifier(); + effect->giEffect->parameters[0] = -2; + break; + case kEffectLowGravity: + effect->category = kEffectCatGravity; + effect->timeRemaining = 30000; + effect->giEffect = new GameInteractionEffect::ModifyGravity(); + effect->giEffect->parameters[0] = GI_GRAVITY_LEVEL_LIGHT; + break; + case kEffectHighGravity: + effect->category = kEffectCatGravity; + effect->timeRemaining = 30000; + effect->giEffect = new GameInteractionEffect::ModifyGravity(); + effect->giEffect->parameters[0] = GI_GRAVITY_LEVEL_HEAVY; + break; + case kEffectForceIronBoots: + effect->category = kEffectCatBoots; + effect->timeRemaining = 30000; + effect->giEffect = new GameInteractionEffect::ForceEquipBoots(); + effect->giEffect->parameters[0] = PLAYER_BOOTS_IRON; + break; + case kEffectForceHoverBoots: + effect->category = kEffectCatBoots; + effect->timeRemaining = 30000; + effect->giEffect = new GameInteractionEffect::ForceEquipBoots(); + effect->giEffect->parameters[0] = PLAYER_BOOTS_HOVER; + break; + case kEffectSlipperyFloor: + effect->category = kEffectCatSlipperyFloor; + effect->timeRemaining = 30000; + effect->giEffect = new GameInteractionEffect::SlipperyFloor(); + break; + case kEffectNoLedgeGrabs: + effect->category = kEffectCatNoLedgeGrabs; + effect->timeRemaining = 30000; + effect->giEffect = new GameInteractionEffect::DisableLedgeGrabs(); + break; + case kEffectRandomWind: + effect->category = kEffectCatRandomWind; + effect->timeRemaining = 30000; + effect->giEffect = new GameInteractionEffect::RandomWind(); + break; + case kEffectRandomBonks: + effect->category = kEffectCatRandomBonks; + effect->timeRemaining = 60000; + effect->giEffect = new GameInteractionEffect::RandomBonks(); + break; + + // Hurt or Heal Link + case kEffectEmptyHeart: + effect->giEffect = new GameInteractionEffect::ModifyHealth(); + effect->giEffect->parameters[0] = receivedParameter * -1; + break; + case kEffectFillHeart: + effect->giEffect = new GameInteractionEffect::ModifyHealth(); + break; + case kEffectKnockbackLinkWeak: + effect->giEffect = new GameInteractionEffect::KnockbackPlayer(); + effect->giEffect->parameters[0] = 1; + break; + case kEffectKnockbackLinkStrong: + effect->giEffect = new GameInteractionEffect::KnockbackPlayer(); + effect->giEffect->parameters[0] = 3; + break; + case kEffectKnockbackLinkMega: + effect->giEffect = new GameInteractionEffect::KnockbackPlayer(); + effect->giEffect->parameters[0] = 6; + break; + case kEffectBurnLink: + effect->giEffect = new GameInteractionEffect::BurnPlayer(); + break; + case kEffectFreezeLink: + effect->giEffect = new GameInteractionEffect::FreezePlayer(); + break; + case kEffectElectrocuteLink: + effect->giEffect = new GameInteractionEffect::ElectrocutePlayer(); + break; + case kEffectKillLink: + effect->giEffect = new GameInteractionEffect::SetPlayerHealth(); + effect->giEffect->parameters[0] = 0; + break; + + // Give Items and Consumables + case kEffectAddHeartContainer: + effect->giEffect = new GameInteractionEffect::ModifyHeartContainers(); + effect->giEffect->parameters[0] = 1; + break; + case kEffectFillMagic: + effect->giEffect = new GameInteractionEffect::FillMagic(); + break; + case kEffectAddRupees: + effect->giEffect = new GameInteractionEffect::ModifyRupees(); + break; + case kEffectGiveDekuShield: + effect->giEffect = new GameInteractionEffect::GiveOrTakeShield(); + effect->giEffect->parameters[0] = ITEM_SHIELD_DEKU; + break; + case kEffectGiveHylianShield: + effect->giEffect = new GameInteractionEffect::GiveOrTakeShield(); + effect->giEffect->parameters[0] = ITEM_SHIELD_HYLIAN; + break; + case kEffectRefillSticks: + effect->giEffect = new GameInteractionEffect::AddOrTakeAmmo(); + effect->giEffect->parameters[1] = ITEM_STICK; + break; + case kEffectRefillNuts: + effect->giEffect = new GameInteractionEffect::AddOrTakeAmmo(); + effect->giEffect->parameters[1] = ITEM_NUT; + break; + case kEffectRefillBombs: + effect->giEffect = new GameInteractionEffect::AddOrTakeAmmo(); + effect->giEffect->parameters[1] = ITEM_BOMB; + break; + case kEffectRefillSeeds: + effect->giEffect = new GameInteractionEffect::AddOrTakeAmmo(); + effect->giEffect->parameters[1] = ITEM_SLINGSHOT; + break; + case kEffectRefillArrows: + effect->giEffect = new GameInteractionEffect::AddOrTakeAmmo(); + effect->giEffect->parameters[1] = ITEM_BOW; + break; + case kEffectRefillBombchus: + effect->giEffect = new GameInteractionEffect::AddOrTakeAmmo(); + effect->giEffect->parameters[1] = ITEM_BOMBCHU; + break; + + // Take Items and Consumables + case kEffectRemoveHeartContainer: + effect->giEffect = new GameInteractionEffect::ModifyHeartContainers(); + effect->giEffect->parameters[0] = -1; + break; + case kEffectEmptyMagic: + effect->giEffect = new GameInteractionEffect::EmptyMagic(); + break; + case kEffectRemoveRupees: + effect->giEffect = new GameInteractionEffect::ModifyRupees(); + effect->giEffect->parameters[0] = receivedParameter * -1; + break; + case kEffectTakeDekuShield: + effect->giEffect = new GameInteractionEffect::GiveOrTakeShield(); + effect->giEffect->parameters[0] = -ITEM_SHIELD_DEKU; + break; + case kEffectTakeHylianShield: + effect->giEffect = new GameInteractionEffect::GiveOrTakeShield(); + effect->giEffect->parameters[0] = -ITEM_SHIELD_HYLIAN; + break; + case kEffectTakeSticks: + effect->giEffect = new GameInteractionEffect::AddOrTakeAmmo(); + effect->giEffect->parameters[0] = receivedParameter * -1; + effect->giEffect->parameters[1] = ITEM_STICK; + break; + case kEffectTakeNuts: + effect->giEffect = new GameInteractionEffect::AddOrTakeAmmo(); + effect->giEffect->parameters[0] = receivedParameter * -1; + effect->giEffect->parameters[1] = ITEM_NUT; + break; + case kEffectTakeBombs: + effect->giEffect = new GameInteractionEffect::AddOrTakeAmmo(); + effect->giEffect->parameters[0] = receivedParameter * -1; + effect->giEffect->parameters[1] = ITEM_BOMB; + break; + case kEffectTakeSeeds: + effect->giEffect = new GameInteractionEffect::AddOrTakeAmmo(); + effect->giEffect->parameters[0] = receivedParameter * -1; + effect->giEffect->parameters[1] = ITEM_SLINGSHOT; + break; + case kEffectTakeArrows: + effect->giEffect = new GameInteractionEffect::AddOrTakeAmmo(); + effect->giEffect->parameters[0] = receivedParameter * -1; + effect->giEffect->parameters[1] = ITEM_BOW; + break; + case kEffectTakeBombchus: + effect->giEffect = new GameInteractionEffect::AddOrTakeAmmo(); + effect->giEffect->parameters[0] = receivedParameter * -1; + effect->giEffect->parameters[1] = ITEM_BOMBCHU; + break; + + // Link Size Modifiers + case kEffectGiantLink: + effect->category = kEffectCatLinkSize; + effect->timeRemaining = 30000; + effect->giEffect = new GameInteractionEffect::ModifyLinkSize(); + effect->giEffect->parameters[0] = GI_LINK_SIZE_GIANT; + break; + case kEffectMinishLink: + effect->category = kEffectCatLinkSize; + effect->timeRemaining = 30000; + effect->giEffect = new GameInteractionEffect::ModifyLinkSize(); + effect->giEffect->parameters[0] = GI_LINK_SIZE_MINISH; + break; + case kEffectPaperLink: + effect->category = kEffectCatLinkSize; + effect->timeRemaining = 30000; + effect->giEffect = new GameInteractionEffect::ModifyLinkSize(); + effect->giEffect->parameters[0] = GI_LINK_SIZE_PAPER; + break; + case kEffectSquishedLink: + effect->category = kEffectCatLinkSize; + effect->timeRemaining = 30000; + effect->giEffect = new GameInteractionEffect::ModifyLinkSize(); + effect->giEffect->parameters[0] = GI_LINK_SIZE_SQUISHED; + break; + case kEffectInvisibleLink: + effect->category = kEffectCatLinkSize; + effect->timeRemaining = 30000; + effect->giEffect = new GameInteractionEffect::InvisibleLink(); + break; + + // Generic Effects + case kEffectRandomBombTimer: + effect->category = kEffectCatRandomBombFuseTimer; + effect->timeRemaining = 60000; + effect->giEffect = new GameInteractionEffect::RandomBombFuseTimer(); + break; + case kEffectSetTimeToDawn: + effect->giEffect = new GameInteractionEffect::SetTimeOfDay(); + effect->giEffect->parameters[0] = GI_TIMEOFDAY_DAWN; + break; + case kEffectSetTimeToDusk: + effect->giEffect = new GameInteractionEffect::SetTimeOfDay(); + effect->giEffect->parameters[0] = GI_TIMEOFDAY_DUSK; + break; + + // Visual Effects + case kEffectNoUi: + effect->category = kEffectCatUi; + effect->timeRemaining = 60000; + effect->giEffect = new GameInteractionEffect::NoUI(); + break; + case kEffectRainstorm: + effect->category = kEffectCatWeather; + effect->timeRemaining = 30000; + effect->giEffect = new GameInteractionEffect::WeatherRainstorm(); + break; + case kEffectDebugMode: + effect->category = kEffectCatDebugMode; + effect->timeRemaining = 30000; + effect->giEffect = new GameInteractionEffect::SetCollisionViewer(); + break; + case kEffectRandomCosmetics: + effect->giEffect = new GameInteractionEffect::RandomizeCosmetics(); + break; + + // Controls + case kEffectNoZButton: + effect->category = kEffectCatNoZ; + effect->timeRemaining = 30000; + effect->giEffect = new GameInteractionEffect::DisableZTargeting(); + break; + case kEffectReverseControls: + effect->category = kEffectCatReverseControls; + effect->timeRemaining = 60000; + effect->giEffect = new GameInteractionEffect::ReverseControls(); + break; + case kEffectPacifistMode: + effect->category = kEffectCatPacifist; + effect->timeRemaining = 15000; + effect->giEffect = new GameInteractionEffect::PacifistMode(); + break; + case kEffectPressRandomButtons: + effect->category = kEffectCatRandomButtons; + effect->timeRemaining = 30000; + effect->giEffect = new GameInteractionEffect::PressRandomButton(); + effect->giEffect->parameters[0] = 30; + break; + case kEffectClearCbuttons: + effect->giEffect = new GameInteractionEffect::ClearAssignedButtons(); + effect->giEffect->parameters[0] = GI_BUTTONS_CBUTTONS; + break; + case kEffectClearDpad: + effect->giEffect = new GameInteractionEffect::ClearAssignedButtons(); + effect->giEffect->parameters[0] = GI_BUTTONS_DPAD; + break; + + // Teleport Player + case kEffectTpLinksHouse: + effect->giEffect = new GameInteractionEffect::TeleportPlayer(); + effect->giEffect->parameters[0] = GI_TP_DEST_LINKSHOUSE; + break; + case kEffectTpMinuet: + effect->giEffect = new GameInteractionEffect::TeleportPlayer(); + effect->giEffect->parameters[0] = GI_TP_DEST_MINUET; + break; + case kEffectTpBolero: + effect->giEffect = new GameInteractionEffect::TeleportPlayer(); + effect->giEffect->parameters[0] = GI_TP_DEST_BOLERO; + break; + case kEffectTpSerenade: + effect->giEffect = new GameInteractionEffect::TeleportPlayer(); + effect->giEffect->parameters[0] = GI_TP_DEST_SERENADE; + break; + case kEffectTpRequiem: + effect->giEffect = new GameInteractionEffect::TeleportPlayer(); + effect->giEffect->parameters[0] = GI_TP_DEST_REQUIEM; + break; + case kEffectTpNocturne: + effect->giEffect = new GameInteractionEffect::TeleportPlayer(); + effect->giEffect->parameters[0] = GI_TP_DEST_NOCTURNE; + break; + case kEffectTpPrelude: + effect->giEffect = new GameInteractionEffect::TeleportPlayer(); + effect->giEffect->parameters[0] = GI_TP_DEST_PRELUDE; + break; + + // Tunic Color (Bidding War) + case kEffectTunicRed: + effect->giEffect = new GameInteractionEffect::SetCosmeticsColor(); + effect->giEffect->parameters[0] = GI_COSMETICS_TUNICS; + effect->giEffect->parameters[1] = GI_COLOR_RED; + break; + case kEffectTunicGreen: + effect->giEffect = new GameInteractionEffect::SetCosmeticsColor(); + effect->giEffect->parameters[0] = GI_COSMETICS_TUNICS; + effect->giEffect->parameters[1] = GI_COLOR_GREEN; + break; + case kEffectTunicBlue: + effect->giEffect = new GameInteractionEffect::SetCosmeticsColor(); + effect->giEffect->parameters[0] = GI_COSMETICS_TUNICS; + effect->giEffect->parameters[1] = GI_COLOR_BLUE; + break; + case kEffectTunicOrange: + effect->giEffect = new GameInteractionEffect::SetCosmeticsColor(); + effect->giEffect->parameters[0] = GI_COSMETICS_TUNICS; + effect->giEffect->parameters[1] = GI_COLOR_ORANGE; + break; + case kEffectTunicYellow: + effect->giEffect = new GameInteractionEffect::SetCosmeticsColor(); + effect->giEffect->parameters[0] = GI_COSMETICS_TUNICS; + effect->giEffect->parameters[1] = GI_COLOR_YELLOW; + break; + case kEffectTunicPurple: + effect->giEffect = new GameInteractionEffect::SetCosmeticsColor(); + effect->giEffect->parameters[0] = GI_COSMETICS_TUNICS; + effect->giEffect->parameters[1] = GI_COLOR_PURPLE; + break; + case kEffectTunicPink: + effect->giEffect = new GameInteractionEffect::SetCosmeticsColor(); + effect->giEffect->parameters[0] = GI_COSMETICS_TUNICS; + effect->giEffect->parameters[1] = GI_COLOR_PINK; + break; + case kEffectTunicBrown: + effect->giEffect = new GameInteractionEffect::SetCosmeticsColor(); + effect->giEffect->parameters[0] = GI_COSMETICS_TUNICS; + effect->giEffect->parameters[1] = GI_COLOR_BROWN; + break; + case kEffectTunicBlack: + effect->giEffect = new GameInteractionEffect::SetCosmeticsColor(); + effect->giEffect->parameters[0] = GI_COSMETICS_TUNICS; + effect->giEffect->parameters[1] = GI_COLOR_BLACK; + break; + + // Navi Color (Bidding War) + case kEffectNaviRed: + effect->giEffect = new GameInteractionEffect::SetCosmeticsColor(); + effect->giEffect->parameters[0] = GI_COSMETICS_NAVI; + effect->giEffect->parameters[1] = GI_COLOR_RED; + break; + case kEffectNaviGreen: + effect->giEffect = new GameInteractionEffect::SetCosmeticsColor(); + effect->giEffect->parameters[0] = GI_COSMETICS_NAVI; + effect->giEffect->parameters[1] = GI_COLOR_GREEN; + break; + case kEffectNaviBlue: + effect->giEffect = new GameInteractionEffect::SetCosmeticsColor(); + effect->giEffect->parameters[0] = GI_COSMETICS_NAVI; + effect->giEffect->parameters[1] = GI_COLOR_BLUE; + break; + case kEffectNaviOrange: + effect->giEffect = new GameInteractionEffect::SetCosmeticsColor(); + effect->giEffect->parameters[0] = GI_COSMETICS_NAVI; + effect->giEffect->parameters[1] = GI_COLOR_ORANGE; + break; + case kEffectNaviYellow: + effect->giEffect = new GameInteractionEffect::SetCosmeticsColor(); + effect->giEffect->parameters[0] = GI_COSMETICS_NAVI; + effect->giEffect->parameters[1] = GI_COLOR_YELLOW; + break; + case kEffectNaviPurple: + effect->giEffect = new GameInteractionEffect::SetCosmeticsColor(); + effect->giEffect->parameters[0] = GI_COSMETICS_NAVI; + effect->giEffect->parameters[1] = GI_COLOR_PURPLE; + break; + case kEffectNaviPink: + effect->giEffect = new GameInteractionEffect::SetCosmeticsColor(); + effect->giEffect->parameters[0] = GI_COSMETICS_NAVI; + effect->giEffect->parameters[1] = GI_COLOR_PINK; + break; + case kEffectNaviBrown: + effect->giEffect = new GameInteractionEffect::SetCosmeticsColor(); + effect->giEffect->parameters[0] = GI_COSMETICS_NAVI; + effect->giEffect->parameters[1] = GI_COLOR_BROWN; + break; + case kEffectNaviBlack: + effect->giEffect = new GameInteractionEffect::SetCosmeticsColor(); + effect->giEffect->parameters[0] = GI_COSMETICS_NAVI; + effect->giEffect->parameters[1] = GI_COLOR_BLACK; + break; + + // Link's Hair Color (Bidding War) + case kEffectHairRed: + effect->giEffect = new GameInteractionEffect::SetCosmeticsColor(); + effect->giEffect->parameters[0] = GI_COSMETICS_HAIR; + effect->giEffect->parameters[1] = GI_COLOR_RED; + break; + case kEffectHairGreen: + effect->giEffect = new GameInteractionEffect::SetCosmeticsColor(); + effect->giEffect->parameters[0] = GI_COSMETICS_HAIR; + effect->giEffect->parameters[1] = GI_COLOR_GREEN; + break; + case kEffectHairBlue: + effect->giEffect = new GameInteractionEffect::SetCosmeticsColor(); + effect->giEffect->parameters[0] = GI_COSMETICS_HAIR; + effect->giEffect->parameters[1] = GI_COLOR_BLUE; + break; + case kEffectHairOrange: + effect->giEffect = new GameInteractionEffect::SetCosmeticsColor(); + effect->giEffect->parameters[0] = GI_COSMETICS_HAIR; + effect->giEffect->parameters[1] = GI_COLOR_ORANGE; + break; + case kEffectHairYellow: + effect->giEffect = new GameInteractionEffect::SetCosmeticsColor(); + effect->giEffect->parameters[0] = GI_COSMETICS_HAIR; + effect->giEffect->parameters[1] = GI_COLOR_YELLOW; + break; + case kEffectHairPurple: + effect->giEffect = new GameInteractionEffect::SetCosmeticsColor(); + effect->giEffect->parameters[0] = GI_COSMETICS_HAIR; + effect->giEffect->parameters[1] = GI_COLOR_PURPLE; + break; + case kEffectHairPink: + effect->giEffect = new GameInteractionEffect::SetCosmeticsColor(); + effect->giEffect->parameters[0] = GI_COSMETICS_HAIR; + effect->giEffect->parameters[1] = GI_COLOR_PINK; + break; + case kEffectHairBrown: + effect->giEffect = new GameInteractionEffect::SetCosmeticsColor(); + effect->giEffect->parameters[0] = GI_COSMETICS_HAIR; + effect->giEffect->parameters[1] = GI_COLOR_BROWN; + break; + case kEffectHairBlack: + effect->giEffect = new GameInteractionEffect::SetCosmeticsColor(); + effect->giEffect->parameters[0] = GI_COSMETICS_HAIR; + effect->giEffect->parameters[1] = GI_COLOR_BLACK; + break; + + default: + break; + } + + // 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->parameters[0]) { + effect->giEffect->parameters[0] = receivedParameter; + } + } + + return effect; +} + #endif diff --git a/soh/soh/Enhancements/crowd-control/CrowdControl.h b/soh/soh/Enhancements/crowd-control/CrowdControl.h index 51b0adba6..672e6d361 100644 --- a/soh/soh/Enhancements/crowd-control/CrowdControl.h +++ b/soh/soh/Enhancements/crowd-control/CrowdControl.h @@ -63,11 +63,10 @@ class CrowdControl { typedef struct Effect { uint32_t id; - uint32_t value[2]; - std::string category; + uint32_t spawnParams[2]; + uint32_t category = 0; long timeRemaining; GameInteractionEffectBase *giEffect; - int32_t paramMultiplier = 1; // Metadata used while executing (only for timed effects) bool isPaused; diff --git a/soh/soh/Enhancements/crowd-control/CrowdControlTypes.h b/soh/soh/Enhancements/crowd-control/CrowdControlTypes.h new file mode 100644 index 000000000..86ddeba09 --- /dev/null +++ b/soh/soh/Enhancements/crowd-control/CrowdControlTypes.h @@ -0,0 +1,310 @@ +#pragma once + +#include + +enum CCStringEnumValues { + + // Spawn Enemies and Objects + kEffectSpawnCuccoStorm, + kEffectSpawnLitBomb, + kEffectSpawnExplosion, + kEffectSpawnArwing, + kEffectSpawnDarklink, + kEffectSpawnIronKnuckle, + kEffectSpawnStalfos, + kEffectSpawnFreezard, + kEffectSpawnLikeLike, + kEffectSpawnGibdo, + kEffectSpawnKeese, + kEffectSpawnIceKeese, + kEffectSpawnFireKeese, + kEffectSpawnWolfos, + kEffectSpawnWallmaster, + + // Link Modifiers + kEffectTakeHalfDamage, + kEffectTakeDoubleDamage, + kEffectOneHitKo, + kEffectInvincibility, + kEffectIncreaseSpeed, + kEffectDecreaseSpeed, + kEffectLowGravity, + kEffectHighGravity, + kEffectForceIronBoots, + kEffectForceHoverBoots, + kEffectSlipperyFloor, + kEffectNoLedgeGrabs, + kEffectRandomWind, + kEffectRandomBonks, + + // Hurt or Heal Link + kEffectEmptyHeart, + kEffectFillHeart, + kEffectKnockbackLinkWeak, + kEffectKnockbackLinkStrong, + kEffectKnockbackLinkMega, + kEffectBurnLink, + kEffectFreezeLink, + kEffectElectrocuteLink, + kEffectKillLink, + + // Give Items and Consumables + kEffectAddHeartContainer, + kEffectFillMagic, + kEffectAddRupees, + kEffectGiveDekuShield, + kEffectGiveHylianShield, + kEffectRefillSticks, + kEffectRefillNuts, + kEffectRefillBombs, + kEffectRefillSeeds, + kEffectRefillArrows, + kEffectRefillBombchus, + + // Take Items and Consumables + kEffectRemoveHeartContainer, + kEffectEmptyMagic, + kEffectRemoveRupees, + kEffectTakeDekuShield, + kEffectTakeHylianShield, + kEffectTakeSticks, + kEffectTakeNuts, + kEffectTakeBombs, + kEffectTakeSeeds, + kEffectTakeArrows, + kEffectTakeBombchus, + + // Link Size Modifiers + kEffectGiantLink, + kEffectMinishLink, + kEffectPaperLink, + kEffectSquishedLink, + kEffectInvisibleLink, + + // Generic kEffects + kEffectRandomBombTimer, + kEffectSetTimeToDawn, + kEffectSetTimeToDusk, + + // Visual kEffects + kEffectNoUi, + kEffectRainstorm, + kEffectDebugMode, + kEffectRandomCosmetics, + + // Controls + kEffectNoZButton, + kEffectReverseControls, + kEffectPacifistMode, + kEffectPressRandomButtons, + kEffectClearCbuttons, + kEffectClearDpad, + + // Teleport Player + kEffectTpLinksHouse, + kEffectTpMinuet, + kEffectTpBolero, + kEffectTpSerenade, + kEffectTpRequiem, + kEffectTpNocturne, + kEffectTpPrelude, + + // Tunic Color (Bidding War) + kEffectTunicRed, + kEffectTunicGreen, + kEffectTunicBlue, + kEffectTunicOrange, + kEffectTunicYellow, + kEffectTunicPurple, + kEffectTunicPink, + kEffectTunicBrown, + kEffectTunicBlack, + + // Navi Color (Bidding War) + kEffectNaviRed, + kEffectNaviGreen, + kEffectNaviBlue, + kEffectNaviOrange, + kEffectNaviYellow, + kEffectNaviPurple, + kEffectNaviPink, + kEffectNaviBrown, + kEffectNaviBlack, + + // Link's Hair Color (Bidding War) + kEffectHairRed, + kEffectHairGreen, + kEffectHairBlue, + kEffectHairOrange, + kEffectHairYellow, + kEffectHairPurple, + kEffectHairPink, + kEffectHairBrown, + kEffectHairBlack, +}; + +enum CCCatEnumValues { + kEffectCatNone, + kEffectCatGravity, + kEffectCatLinkSize, + kEffectCatRandomBombFuseTimer, + kEffectCatPacifist, + kEffectCatNoZ, + kEffectCatRandomButtons, + kEffectCatUi, + kEffectCatWeather, + kEffectCatDebugMode, + kEffectCatNoLedgeGrabs, + kEffectCatRandomWind, + kEffectCatRandomBonks, + kEffectCatReverseControls, + kEffectCatBoots, + kEffectCatSlipperyFloor, + kEffectCatSpeed, + kEffectCatDamageTaken, + kEffectCatOhko, + kEffectCatInvincible, + kEffectCatSpawnEnemy, + kEffectCatSpawnActor +}; + +std::unordered_map effectStringToEnum = { + + // Spawn Enemies and Objects + { "spawn_cucco_storm", kEffectSpawnCuccoStorm }, + { "spawn_lit_bomb", kEffectSpawnLitBomb }, + { "spawn_explosion", kEffectSpawnExplosion }, + { "spawn_arwing", kEffectSpawnArwing }, + { "spawn_darklink", kEffectSpawnDarklink }, + { "spawn_iron_knuckle", kEffectSpawnIronKnuckle }, + { "spawn_stalfos", kEffectSpawnStalfos }, + { "spawn_freezard", kEffectSpawnFreezard }, + { "spawn_like_like", kEffectSpawnLikeLike }, + { "spawn_gibdo", kEffectSpawnGibdo }, + { "spawn_keese", kEffectSpawnKeese }, + { "spawn_ice_keese", kEffectSpawnIceKeese }, + { "spawn_fire_keese", kEffectSpawnFireKeese }, + { "spawn_wolfos", kEffectSpawnWolfos }, + { "spawn_wallmaster", kEffectSpawnWallmaster }, + + // Link Modifiers + { "take_half_damage", kEffectTakeHalfDamage }, + { "take_double_damage", kEffectTakeDoubleDamage }, + { "one_hit_ko", kEffectOneHitKo }, + { "invincibility", kEffectInvincibility }, + { "increase_speed", kEffectIncreaseSpeed }, + { "decrease_speed", kEffectDecreaseSpeed }, + { "low_gravity", kEffectLowGravity }, + { "high_gravity", kEffectHighGravity }, + { "force_iron_boots", kEffectForceIronBoots }, + { "force_hover_boots", kEffectForceHoverBoots }, + { "slippery_floor", kEffectSlipperyFloor }, + { "no_ledge_grabs", kEffectNoLedgeGrabs }, + { "random_wind", kEffectRandomWind }, + { "random_bonks", kEffectRandomBonks }, + + // Hurt or Heal Link + { "empty_heart", kEffectEmptyHeart }, + { "fill_heart", kEffectFillHeart }, + { "knockback_link_weak", kEffectKnockbackLinkWeak }, + { "knockback_link_strong", kEffectKnockbackLinkStrong }, + { "knockback_link_mega", kEffectKnockbackLinkMega }, + { "burn_link", kEffectBurnLink }, + { "freeze_link", kEffectFreezeLink }, + { "electrocute_link", kEffectElectrocuteLink }, + { "kill_link", kEffectKillLink }, + + // Give Items and Consumables + { "add_heart_container", kEffectAddHeartContainer }, + { "fill_magic", kEffectFillMagic }, + { "add_rupees", kEffectAddRupees }, + { "give_deku_shield", kEffectGiveDekuShield }, + { "give_hylian_shield", kEffectGiveHylianShield }, + { "refill_sticks", kEffectRefillSticks }, + { "refill_nuts", kEffectRefillNuts }, + { "refill_bombs", kEffectRefillBombs }, + { "refill_seeds", kEffectRefillSeeds }, + { "refill_arrows", kEffectRefillArrows }, + { "refill_bombchus", kEffectRefillBombchus }, + + // Take Items and Consumables + { "remove_heart_container", kEffectRemoveHeartContainer }, + { "empty_magic", kEffectEmptyMagic }, + { "remove_rupees", kEffectRemoveRupees }, + { "take_deku_shield", kEffectTakeDekuShield }, + { "take_hylian_shield", kEffectTakeHylianShield }, + { "take_sticks", kEffectTakeSticks }, + { "take_nuts", kEffectTakeNuts }, + { "take_bombs", kEffectTakeBombs }, + { "take_seeds", kEffectTakeSeeds }, + { "take_arrows", kEffectTakeArrows }, + { "take_bombchus", kEffectTakeBombchus }, + + // Link Size Modifiers + { "giant_link", kEffectGiantLink }, + { "minish_link", kEffectMinishLink }, + { "paper_link", kEffectPaperLink }, + { "squished_link", kEffectSquishedLink }, + { "invisible_link", kEffectInvisibleLink }, + + // Generic kEffects + { "random_bomb_timer", kEffectRandomBombTimer }, + { "set_time_to_dawn", kEffectSetTimeToDawn }, + { "set_time_to_dusk", kEffectSetTimeToDusk }, + + // Visual kEffects + { "no_ui", kEffectNoUi }, + { "rainstorm", kEffectRainstorm }, + { "debug_mode", kEffectDebugMode }, + { "random_cosmetics", kEffectRandomCosmetics }, + + // Controls + { "no_z_button", kEffectNoZButton }, + { "reverse_controls", kEffectReverseControls }, + { "pacifist_mode", kEffectPacifistMode }, + { "press_random_buttons", kEffectPressRandomButtons }, + { "clear_cbuttons", kEffectClearCbuttons }, + { "clear_dpad", kEffectClearDpad }, + + // Teleport Player + { "tp_links_house", kEffectTpLinksHouse }, + { "tp_minuet", kEffectTpMinuet }, + { "tp_bolero", kEffectTpBolero }, + { "tp_serenade", kEffectTpSerenade }, + { "tp_requiem", kEffectTpRequiem }, + { "tp_nocturne", kEffectTpNocturne }, + { "tp_prelude", kEffectTpPrelude }, + + // Tunic Color (Bidding War) + { "tunic_red", kEffectTunicRed }, + { "tunic_green", kEffectTunicGreen }, + { "tunic_blue", kEffectTunicBlue }, + { "tunic_orange", kEffectTunicOrange }, + { "tunic_yellow", kEffectTunicYellow }, + { "tunic_purple", kEffectTunicPurple }, + { "tunic_pink", kEffectTunicPink }, + { "tunic_brown", kEffectTunicBrown }, + { "tunic_black", kEffectTunicBlack }, + + // Navi Color (Bidding War) + { "navi_red", kEffectNaviRed }, + { "navi_green", kEffectNaviGreen }, + { "navi_blue", kEffectNaviBlue }, + { "navi_orange", kEffectNaviOrange }, + { "navi_yellow", kEffectNaviYellow }, + { "navi_purple", kEffectNaviPurple }, + { "navi_pink", kEffectNaviPink }, + { "navi_brown", kEffectNaviBrown }, + { "navi_black", kEffectNaviBlack }, + + // Link's Hair Color (Bidding War) + { "hair_red", kEffectHairRed }, + { "hair_green", kEffectHairGreen }, + { "hair_blue", kEffectHairBlue }, + { "hair_orange", kEffectHairOrange }, + { "hair_yellow", kEffectHairYellow }, + { "hair_purple", kEffectHairPurple }, + { "hair_pink", kEffectHairPink }, + { "hair_brown", kEffectHairBrown }, + { "hair_black", kEffectHairBlack }, +}; diff --git a/soh/soh/Enhancements/crowd-control/soh.ccpak b/soh/soh/Enhancements/crowd-control/soh.ccpak new file mode 100644 index 000000000..a4af39582 Binary files /dev/null and b/soh/soh/Enhancements/crowd-control/soh.ccpak differ diff --git a/soh/soh/Enhancements/crowd-control/soh.cs b/soh/soh/Enhancements/crowd-control/soh.cs index e8649fcde..fe0b6f55f 100644 --- a/soh/soh/Enhancements/crowd-control/soh.cs +++ b/soh/soh/Enhancements/crowd-control/soh.cs @@ -15,76 +15,193 @@ public class SoH : SimpleTCPPack public override Game Game { get; } = new Game(90, "Ship of Harkinian", "SoH", "PC", ConnectorType.SimpleTCPConnector); - private Dictionary _enemyType = new Dictionary() + public override List Effects => new List { - {"wallmaster", ("Wallmaster")}, - {"arwing", ("Arwing")}, - {"darklink", ("Dark Link")}, - {"stalfos", ("Stalfos")}, - {"wolfos", ("Wolfos")}, - {"freezard", ("Freezard")}, - {"keese", ("Keese")}, - {"icekeese", ("Ice Keese")}, - {"firekeese", ("Fire Keese")}, - {"tektite", ("Tektite")}, - {"likelike", ("Like-Like")} - }; - public override List Effects - { - get - { - List effects = new List - { - new Effect("Add Heart Container", "add_heart_container"), - new Effect("Remove Heart Container", "remove_heart_container"), - new Effect("Damage Multiplier", "damage_multiplier", new[] { "damdefmulti" }) { Duration = 30 }, - new Effect("Defense Multiplier", "defense_multiplier", new[] { "damdefmulti" }) { Duration = 30 }, - new Effect("Freeze Link", "freeze"), - new Effect("Giant Lonk", "giant_link") { Duration = 30 }, - new Effect("Empty Heart", "damage", new[] { "health20" }), - new Effect("Fill Heart", "heal", new[] { "health20" }), - new Effect("Kill Player", "kill"), - new Effect("High Gravity", "high_gravity") { Duration = 30 }, - new Effect("Hover Boots", "hover_boots") { Duration = 30 }, - new Effect("Invisible Link", "invisible") { Duration = 30 }, - new Effect("No UI", "no_ui") { Duration = 60 }, - new Effect("Low Gravity", "low_gravity") { Duration = 30 }, - new Effect("Fill Magic", "fill_magic"), - new Effect("Empty Magic", "empty_magic"), - new Effect("Minish Link", "minish_link") { Duration = 30 }, - new Effect("No Z Button", "no_z") { Duration = 30 }, - new Effect("One-Hit KO", "ohko") { Duration = 30 }, - new Effect("Pacifist", "pacifist") { Duration = 15 }, - new Effect("Paper Link", "paper_link") { Duration = 30 }, - new Effect("Rainstorm", "rainstorm") { Duration = 30 }, - new Effect("Reverse Controls", "reverse") { Duration = 60 }, - new Effect("Give Rupees", "add_rupees", new[] { "rupees999" }), - new Effect("Take Rupees", "remove_rupees", new[] { "rupees999" }), - new Effect("Increase Speed", "increase_speed") { Duration = 30 }, - new Effect("Decrease Speed", "decrease_speed") { Duration = 30 }, - new Effect("Cucco Swarm", "cucco_storm"), - new Effect("Knockback Link", "knockback", new[] { "knockbackstrength" }), - new Effect("Burn Link", "burn"), - new Effect("Electrocute Link", "electrocute"), - new Effect("Iron Boots", "iron_boots") { Duration = 30 }, - new Effect("Give Deku Shield", "give_dekushield"), - new Effect("Spawn Enemy", "spawn_enemy", ItemKind.Folder), - }; - - effects.AddRange(_enemyType.Select(t => new Effect($"Spawn {t.Value}", $"spawn_{t.Key}", "spawn_enemy"))); + // Spawn Enemies and Objects + new Effect("Spawn Enemies/Objects", "spawn_stuff", ItemKind.Folder), - return effects; - } - } + new Effect ("Cucco Storm", "spawn_cucco_storm", "spawn_stuff"), + new Effect ("Lit Bomb", "spawn_lit_bomb", "spawn_stuff"), + new Effect ("Explosion", "spawn_explosion", "spawn_stuff"), + new Effect ("Arwing", "spawn_arwing", "spawn_stuff"), + new Effect ("Dark Link", "spawn_darklink", "spawn_stuff"), + new Effect ("Iron Knuckle", "spawn_iron_knuckle", "spawn_stuff"), + new Effect ("Stalfos", "spawn_stalfos", "spawn_stuff"), + new Effect ("Freezard", "spawn_freezard", "spawn_stuff"), + new Effect ("Like-Like", "spawn_like_like", "spawn_stuff"), + new Effect ("Gibdo", "spawn_gibdo", "spawn_stuff"), + new Effect ("Flock of Keese (5x)", "spawn_keese", "spawn_stuff"), + new Effect ("Ice Keese", "spawn_ice_keese", "spawn_stuff"), + new Effect ("Fire Keese", "spawn_fire_keese", "spawn_stuff"), + new Effect ("Wolfos", "spawn_wolfos", "spawn_stuff"), + new Effect ("Wallmaster", "spawn_wallmaster", "spawn_stuff"), + + + // Link Modifiers + new Effect("Link Modifiers", "link_modifiers", ItemKind.Folder), + + new Effect("Take Half Damage", "take_half_damage", "link_modifiers") { Duration = 30 }, + new Effect("Take Double Damage", "take_double_damage", "link_modifiers") { Duration = 30 }, + new Effect("One-Hit KO", "one_hit_ko", "link_modifiers") { Duration = 30 }, + new Effect("Invincibility", "invincibility", "link_modifiers") { Duration = 15 }, + new Effect("Increase Speed", "increase_speed", "link_modifiers") { Duration = 30 }, + new Effect("Decrease Speed", "decrease_speed", "link_modifiers") { Duration = 30 }, + new Effect("Low Gravity", "low_gravity", "link_modifiers") { Duration = 30 }, + new Effect("High Gravity", "high_gravity", "link_modifiers") { Duration = 30 }, + new Effect("Force Iron Boots", "force_iron_boots", "link_modifiers") { Duration = 30 }, + new Effect("Force Hover Boots", "force_hover_boots", "link_modifiers") { Duration = 30 }, + new Effect("Slippery Floor", "slippery_floor", "link_modifiers") { Duration = 30 }, + new Effect("Disable Ledge Grabs", "no_ledge_grabs", "link_modifiers") { Duration = 30 }, + new Effect("Random Wind", "random_wind", "link_modifiers") { Duration = 30 }, + new Effect("Random Bonks When Rolling", "random_bonks", "link_modifiers") { Duration = 60 }, + + + // Hurt or Heal Link + new Effect("Hurt/Heal Link", "hurtheal_link", ItemKind.Folder), + + new Effect("Empty Heart", "empty_heart", new[] { "health20" }, "hurtheal_link"), + new Effect("Fill Heart", "fill_heart", new[] { "health20" }, "hurtheal_link"), + new Effect("Knockback Link (Weak)", "knockback_link_weak", "hurtheal_link"), + new Effect("Knockback Link (Strong)", "knockback_link_strong", "hurtheal_link"), + new Effect("Knockback Link (Mega)", "knockback_link_mega", "hurtheal_link"), + new Effect("Burn Link", "burn_link", "hurtheal_link"), + new Effect("Freeze Link", "freeze_link", "hurtheal_link"), + new Effect("Electrocute Link", "electrocute_link", "hurtheal_link"), + new Effect("Kill Link", "kill_link", "hurtheal_link"), + + + // Give Items and Consumables + new Effect("Give Items/Consumables", "give_item", ItemKind.Folder), + + new Effect("Add Heart Container", "add_heart_container", "give_item"), + new Effect("Fill Magic", "fill_magic", "give_item"), + new Effect("Give Rupees", "add_rupees", new[] { "rupees999" }, "give_item"), + new Effect("Give Deku Shield", "give_deku_shield", "give_item"), + new Effect("Give Hylian Shield", "give_hylian_shield", "give_item"), + new Effect("Refill Deku Sticks", "refill_sticks", new[] { "ammo30" }, "give_item"), + new Effect("Refill Deku Nuts", "refill_nuts", new[] { "ammo30" }, "give_item"), + new Effect("Refill Bombs", "refill_bombs", new[] { "ammo30" }, "give_item"), + new Effect("Refill Slingshot Seeds", "refill_seeds", new[] { "ammo30" }, "give_item"), + new Effect("Refill Arrows", "refill_arrows", new[] { "ammo30" }, "give_item"), + new Effect("Refill Bombchus", "refill_bombchus", new[] { "ammo30" }, "give_item"), + + + // Take Items and Consumables + new Effect("Take Items/Consumables", "take_item", ItemKind.Folder), + + new Effect("Remove Heart Container", "remove_heart_container", "take_item"), + new Effect("Empty Magic", "empty_magic", "take_item"), + new Effect("Take Rupees", "remove_rupees", new[] { "rupees999" }, "take_item"), + new Effect("Take Deku Shield", "take_deku_shield", "take_item"), + new Effect("Take Hylian Shield", "take_hylian_shield", "take_item"), + new Effect("Take Deku Sticks", "take_sticks", new[] { "ammo30" }, "take_item"), + new Effect("Take Deku Nuts", "take_nuts", new[] { "ammo30" }, "take_item"), + new Effect("Take Bombs", "take_bombs", new[] { "ammo30" }, "take_item"), + new Effect("Take Slingshot Seeds", "take_seeds", new[] { "ammo30" }, "take_item"), + new Effect("Take Arrows", "take_arrows", new[] { "ammo30" }, "take_item"), + new Effect("Take Bombchus", "take_bombchus", new[] { "ammo30" }, "take_item"), + + + // Link Size Modifiers + new Effect("Change Link's Size", "link_size", ItemKind.Folder), + + new Effect("Giant Lonk", "giant_link", "link_size") { Duration = 30 }, + new Effect("Minish Link", "minish_link", "link_size") { Duration = 30 }, + new Effect("Paper Link", "paper_link", "link_size") { Duration = 30 }, + new Effect("Squished Link", "squished_link", "link_size") { Duration = 30 }, + new Effect("Invisible Link", "invisible_link", "link_size") { Duration = 30 }, + + + // Generic Effects + new Effect("Generic Effects", "generic_effects", ItemKind.Folder), + + new Effect("Random Bomb Fuse Timer", "random_bomb_timer", "generic_effects") { Duration = 60 }, + new Effect("Set Time to Dawn", "set_time_to_dawn", "generic_effects"), + new Effect("Set Time to Dusk", "set_time_to_dusk", "generic_effects"), + + + // Visual Effects + new Effect("Visual Effects", "visual_effects", ItemKind.Folder), + + new Effect("No UI", "no_ui", "visual_effects") { Duration = 60 }, + new Effect("Rainstorm", "rainstorm", "visual_effects") { Duration = 30 }, + new Effect("Debug Mode", "debug_mode", "visual_effects") { Duration = 30 }, + new Effect("Randomize Cosmetics", "random_cosmetics", "visual_effects") { Duration = 30 }, + + + // Controls + new Effect("Controls", "controls", ItemKind.Folder), + + new Effect("No Z Button", "no_z_button", "controls") { Duration = 30 }, + new Effect("Reverse Controls", "reverse_controls", "controls") { Duration = 60 }, + new Effect("Pacifist Mode", "pacifist_mode", "controls") { Duration = 15 }, + new Effect("Press Random Buttons", "press_random_buttons", "controls") { Duration = 30 }, + new Effect("Clear C-Buttons", "clear_cbuttons", "controls"), + new Effect("Clear D-pad", "clear_dpad", "controls"), + + + // Teleport Player + new Effect("Teleport Player", "teleport", ItemKind.Folder), + + new Effect("Link's House", "tp_links_house", "teleport"), + new Effect("Minuet Destination", "tp_minuet", "teleport"), + new Effect("Bolero Destination", "tp_bolero", "teleport"), + new Effect("Serenade Destination", "tp_serenade", "teleport"), + new Effect("Requiem Destination", "tp_requiem", "teleport"), + new Effect("Nocturne Destination", "tp_nocturne", "teleport"), + new Effect("Prelude Destination", "tp_prelude", "teleport"), + + + // Tunic Color (Bidding War) + new Effect("Tunic Color", "color_tunic", ItemKind.BidWar), + + new Effect("Red", "tunic_red", ItemKind.BidWarValue, "color_tunic"), + new Effect("Green", "tunic_green", ItemKind.BidWarValue, "color_tunic"), + new Effect("Blue", "tunic_blue", ItemKind.BidWarValue, "color_tunic"), + new Effect("Orange", "tunic_orange", ItemKind.BidWarValue, "color_tunic"), + new Effect("Yellow", "tunic_yellow", ItemKind.BidWarValue, "color_tunic"), + new Effect("Purple", "tunic_purple", ItemKind.BidWarValue, "color_tunic"), + new Effect("Pink", "tunic_pink", ItemKind.BidWarValue, "color_tunic"), + new Effect("Brown", "tunic_brown", ItemKind.BidWarValue, "color_tunic"), + new Effect("Black", "tunic_black", ItemKind.BidWarValue, "color_tunic"), + + + // Navi Color (Bidding War) + new Effect("Navi Color", "color_navi", ItemKind.BidWar), + + new Effect("Red", "navi_red", ItemKind.BidWarValue, "color_navi"), + new Effect("Green", "navi_green", ItemKind.BidWarValue, "color_navi"), + new Effect("Blue", "navi_blue", ItemKind.BidWarValue, "color_navi"), + new Effect("Orange", "navi_orange", ItemKind.BidWarValue, "color_navi"), + new Effect("Yellow", "navi_yellow", ItemKind.BidWarValue, "color_navi"), + new Effect("Purple", "navi_purple", ItemKind.BidWarValue, "color_navi"), + new Effect("Pink", "navi_pink", ItemKind.BidWarValue, "color_navi"), + new Effect("Brown", "navi_brown", ItemKind.BidWarValue, "color_navi"), + new Effect("Black", "navi_black", ItemKind.BidWarValue, "color_navi"), + + + // Link's Hair Color (Bidding War) + new Effect("Link's Hair Color", "color_hair", ItemKind.BidWar), + + new Effect("Red", "hair_red", ItemKind.BidWarValue, "color_hair"), + new Effect("Green", "hair_green", ItemKind.BidWarValue, "color_hair"), + new Effect("Blue", "hair_blue", ItemKind.BidWarValue, "color_hair"), + new Effect("Orange", "hair_orange", ItemKind.BidWarValue, "color_hair"), + new Effect("Yellow", "hair_yellow", ItemKind.BidWarValue, "color_hair"), + new Effect("Purple", "hair_purple", ItemKind.BidWarValue, "color_hair"), + new Effect("Pink", "hair_pink", ItemKind.BidWarValue, "color_hair"), + new Effect("Brown", "hair_brown", ItemKind.BidWarValue, "color_hair"), + new Effect("Black", "hair_black", ItemKind.BidWarValue, "color_hair"), + + }; //Slider ranges need to be defined public override List ItemTypes => new List { new ItemType("Rupees", "rupees999", ItemType.Subtype.Slider, "{\"min\":1,\"max\":999}"), - new ItemType("Health", "health20", ItemType.Subtype.Slider, "{\"min\":1,\"max\":20}"), - new ItemType("Damage/Defense Multiplier", "damdefmulti", ItemType.Subtype.Slider, "{\"min\":1,\"max\":10}"), - new ItemType("Knockback Strength", "knockbackstrength", ItemType.Subtype.Slider, "{\"min\":1,\"max\":3}") + new ItemType("Health", "health20", ItemType.Subtype.Slider, "{\"min\":1,\"max\":20}"), + new ItemType("Ammo", "ammo30", ItemType.Subtype.Slider, "{\"min\":1,\"max\":30}"), }; } diff --git a/soh/soh/Enhancements/debugconsole.cpp b/soh/soh/Enhancements/debugconsole.cpp index 2c81b6405..b26c3ab97 100644 --- a/soh/soh/Enhancements/debugconsole.cpp +++ b/soh/soh/Enhancements/debugconsole.cpp @@ -82,21 +82,9 @@ static bool ActorSpawnHandler(std::shared_ptr Console, const std: return CMD_SUCCESS; } -static bool GiveDekuShieldHandler(std::shared_ptr Console, const std::vector&) { - 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; - } -} - static bool KillPlayerHandler(std::shared_ptr Console, const std::vector&) { GameInteractionEffectBase* effect = new GameInteractionEffect::SetPlayerHealth(); - effect->parameter = 0; + effect->parameters[0] = 0; GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect); if (result == GameInteractionEffectQueryResult::Possible) { SohImGui::GetConsole()->SendInfoMessage("[SOH] You've met with a terrible fate, haven't you?"); @@ -127,7 +115,7 @@ static bool SetPlayerHealthHandler(std::shared_ptr Console, const } GameInteractionEffectBase* effect = new GameInteractionEffect::SetPlayerHealth(); - effect->parameter = health; + effect->parameters[0] = health; GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect); if (result == GameInteractionEffectQueryResult::Possible) { SohImGui::GetConsole()->SendInfoMessage("[SOH] Player health updated to %d", health); @@ -210,48 +198,90 @@ static bool ResetHandler(std::shared_ptr Console, std::vector ammoItems{ - { "sticks", ITEM_STICK }, { "deku_sticks", ITEM_STICK }, - { "nuts", ITEM_NUT }, { "deku_nuts", ITEM_NUT }, - { "bombs", ITEM_BOMB }, { "arrows", ITEM_BOW }, - { "bombchus", ITEM_BOMBCHU }, { "chus", ITEM_BOMBCHU }, - { "beans", ITEM_BEAN }, - { "seeds", ITEM_SLINGSHOT }, { "deku_seeds", ITEM_SLINGSHOT }, - { "magic_beans", ITEM_BEAN }, +const static std::map ammoItems{ + { "sticks", ITEM_STICK }, { "nuts", ITEM_NUT }, + { "bombs", ITEM_BOMB }, { "seeds", ITEM_SLINGSHOT }, + { "arrows", ITEM_BOW }, { "bombchus", ITEM_BOMBCHU }, + { "beans", ITEM_BEAN } }; -static bool AmmoHandler(std::shared_ptr Console, const std::vector& args) { +static bool AddAmmoHandler(std::shared_ptr Console, const std::vector& args) { if (args.size() != 3) { SohImGui::GetConsole()->SendErrorMessage("[SOH] Unexpected arguments passed"); return CMD_FAILED; } - - int count; + int amount; try { - count = std::stoi(args[2]); + amount = std::stoi(args[2]); } catch (std::invalid_argument const& ex) { SohImGui::GetConsole()->SendErrorMessage("Ammo count must be an integer"); return CMD_FAILED; } - if (count < 0) { + if (amount < 0) { SohImGui::GetConsole()->SendErrorMessage("Ammo count must be positive"); return CMD_FAILED; } const auto& it = ammoItems.find(args[1]); - if (it == ammoItems.end()) { - SohImGui::GetConsole()->SendErrorMessage("Invalid item passed"); + SohImGui::GetConsole()->SendErrorMessage("Invalid ammo type. Options are 'sticks', 'nuts', 'bombs', 'seeds', 'arrows', 'bombchus' and 'beans'"); return CMD_FAILED; } - // I dont think you can do OOB with just this - AMMO(it->second) = count; + GameInteractionEffectBase* effect = new GameInteractionEffect::AddOrTakeAmmo(); + effect->parameters[0] = amount; + effect->parameters[1] = it->second; + GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect); - //To use a change by uncomment this - //Inventory_ChangeAmmo(it->second, count); + if (result == GameInteractionEffectQueryResult::Possible) { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Added ammo."); + return CMD_SUCCESS; + } else { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Command failed: Could not add ammo."); + return CMD_FAILED; + } +} + +static bool TakeAmmoHandler(std::shared_ptr Console, const std::vector& args) { + if (args.size() != 3) { + SohImGui::GetConsole()->SendErrorMessage("[SOH] Unexpected arguments passed"); + return CMD_FAILED; + } + int amount; + + try { + amount = std::stoi(args[2]); + } catch (std::invalid_argument const& ex) { + SohImGui::GetConsole()->SendErrorMessage("Ammo count must be an integer"); + return CMD_FAILED; + } + + if (amount < 0) { + SohImGui::GetConsole()->SendErrorMessage("Ammo count must be positive"); + return CMD_FAILED; + } + + const auto& it = ammoItems.find(args[1]); + if (it == ammoItems.end()) { + SohImGui::GetConsole()->SendErrorMessage( + "Invalid ammo type. Options are 'sticks', 'nuts', 'bombs', 'seeds', 'arrows', 'bombchus' and 'beans'"); + return CMD_FAILED; + } + + GameInteractionEffectBase* effect = new GameInteractionEffect::AddOrTakeAmmo(); + effect->parameters[0] = -amount; + effect->parameters[1] = it->second; + GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect); + + if (result == GameInteractionEffectQueryResult::Possible) { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Took ammo."); + return CMD_SUCCESS; + } else { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Command failed: Could not take ammo."); + return CMD_FAILED; + } } const static std::map bottleItems{ @@ -561,7 +591,7 @@ static bool GiantLinkHandler(std::shared_ptr Console, const std:: } GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyLinkSize(); - effect->parameter = GI_LINK_SIZE_GIANT; + effect->parameters[0] = GI_LINK_SIZE_GIANT; GameInteractionEffectQueryResult result = state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect); if (result == GameInteractionEffectQueryResult::Possible) { @@ -589,7 +619,7 @@ static bool MinishLinkHandler(std::shared_ptr Console, const std: } GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyLinkSize(); - effect->parameter = GI_LINK_SIZE_MINISH; + effect->parameters[0] = GI_LINK_SIZE_MINISH; GameInteractionEffectQueryResult result = state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect); if (result == GameInteractionEffectQueryResult::Possible) { @@ -622,7 +652,7 @@ static bool AddHeartContainerHandler(std::shared_ptr Console, con } GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyHeartContainers(); - effect->parameter = hearts; + effect->parameters[0] = hearts; GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect); if (result == GameInteractionEffectQueryResult::Possible) { SohImGui::GetConsole()->SendInfoMessage("[SOH] Added %d heart containers", hearts); @@ -653,7 +683,7 @@ static bool RemoveHeartContainerHandler(std::shared_ptr Console, } GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyHeartContainers(); - effect->parameter = -hearts; + effect->parameters[0] = -hearts; GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect); if (result == GameInteractionEffectQueryResult::Possible) { SohImGui::GetConsole()->SendInfoMessage("[SOH] Removed %d heart containers", hearts); @@ -673,7 +703,7 @@ static bool GravityHandler(std::shared_ptr Console, const std::ve GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyGravity(); try { - effect->parameter = Ship::Math::clamp(std::stoi(args[1], nullptr, 10), GI_GRAVITY_LEVEL_LIGHT, GI_GRAVITY_LEVEL_HEAVY); + effect->parameters[0] = 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] Gravity value must be a number."); return CMD_FAILED; @@ -738,7 +768,7 @@ static bool DefenseModifierHandler(std::shared_ptr Console, const GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyDefenseModifier(); try { - effect->parameter = std::stoi(args[1], nullptr, 10); + effect->parameters[0] = 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; @@ -746,7 +776,7 @@ static bool DefenseModifierHandler(std::shared_ptr Console, const GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect); if (result == GameInteractionEffectQueryResult::Possible) { - SohImGui::GetConsole()->SendInfoMessage("[SOH] Defense modifier set to %d", effect->parameter); + SohImGui::GetConsole()->SendInfoMessage("[SOH] Defense modifier set to %d", effect->parameters[0]); return CMD_SUCCESS; } else { SohImGui::GetConsole()->SendInfoMessage("[SOH] Command failed: Could not set defense modifier."); @@ -768,7 +798,7 @@ static bool DamageHandler(std::shared_ptr Console, const std::vec return CMD_FAILED; } - effect->parameter = -value; + effect->parameters[0] = -value; } catch (std::invalid_argument const& ex) { SohImGui::GetConsole()->SendErrorMessage("[SOH] Damage value must be a number."); return CMD_FAILED; @@ -798,7 +828,7 @@ static bool HealHandler(std::shared_ptr Console, const std::vecto return CMD_FAILED; } - effect->parameter = value; + effect->parameters[0] = value; } catch (std::invalid_argument const& ex) { SohImGui::GetConsole()->SendErrorMessage("[SOH] Damage value must be a number."); return CMD_FAILED; @@ -939,7 +969,7 @@ static bool PaperLinkHandler(std::shared_ptr Console, const std:: } GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyLinkSize(); - effect->parameter = GI_LINK_SIZE_PAPER; + effect->parameters[0] = GI_LINK_SIZE_PAPER; GameInteractionEffectQueryResult result = state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect); @@ -1018,7 +1048,7 @@ static bool UpdateRupeesHandler(std::shared_ptr Console, const st GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyRupees(); try { - effect->parameter = std::stoi(args[1], nullptr, 10); + effect->parameters[0] = 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; @@ -1042,7 +1072,7 @@ static bool SpeedModifierHandler(std::shared_ptr Console, const s GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyRunSpeedModifier(); try { - effect->parameter = std::stoi(args[1], nullptr, 10); + effect->parameters[0] = 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; @@ -1077,11 +1107,11 @@ static bool BootsHandler(std::shared_ptr Console, const std::vect } GameInteractionEffectBase* effect = new GameInteractionEffect::ForceEquipBoots(); - effect->parameter = it->second; + effect->parameters[0] = it->second; GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect); if (result == GameInteractionEffectQueryResult::Possible) { - SohImGui::GetConsole()->SendInfoMessage("[SOH] Boots updated"); + SohImGui::GetConsole()->SendInfoMessage("[SOH] Boots updated."); return CMD_SUCCESS; } else { SohImGui::GetConsole()->SendInfoMessage("[SOH] Command failed: Could not update boots."); @@ -1089,6 +1119,62 @@ static bool BootsHandler(std::shared_ptr Console, const std::vect } } +const static std::map shields { + { "deku", ITEM_SHIELD_DEKU }, + { "hylian", ITEM_SHIELD_HYLIAN }, + { "mirror", ITEM_SHIELD_MIRROR }, +}; + +static bool GiveShieldHandler(std::shared_ptr Console, const std::vector& args) { + if (args.size() != 2) { + SohImGui::GetConsole()->SendErrorMessage("[SOH] Unexpected arguments passed"); + return CMD_FAILED; + } + + const auto& it = shields.find(args[1]); + if (it == shields.end()) { + SohImGui::GetConsole()->SendErrorMessage("Invalid shield type. Options are 'deku', 'hylian' and 'mirror'"); + return CMD_FAILED; + } + + GameInteractionEffectBase* effect = new GameInteractionEffect::GiveOrTakeShield(); + effect->parameters[0] = it->second; + GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect); + + if (result == GameInteractionEffectQueryResult::Possible) { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Gave shield."); + return CMD_SUCCESS; + } else { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Command failed: Could not give shield."); + return CMD_FAILED; + } +} + +static bool TakeShieldHandler(std::shared_ptr Console, const std::vector& args) { + if (args.size() != 2) { + SohImGui::GetConsole()->SendErrorMessage("[SOH] Unexpected arguments passed"); + return CMD_FAILED; + } + + const auto& it = shields.find(args[1]); + if (it == shields.end()) { + SohImGui::GetConsole()->SendErrorMessage("Invalid shield type. Options are 'deku', 'hylian' and 'mirror'"); + return CMD_FAILED; + } + + GameInteractionEffectBase* effect = new GameInteractionEffect::GiveOrTakeShield(); + effect->parameters[0] = it->second * -1; + GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect); + + if (result == GameInteractionEffectQueryResult::Possible) { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Took shield."); + return CMD_SUCCESS; + } else { + SohImGui::GetConsole()->SendInfoMessage("[SOH] Command failed: Could not take shield."); + return CMD_FAILED; + } +} + static bool KnockbackHandler(std::shared_ptr Console, const std::vector& args) { if (args.size() != 2) { SohImGui::GetConsole()->SendErrorMessage("[SOH] Unexpected arguments passed"); @@ -1103,7 +1189,7 @@ static bool KnockbackHandler(std::shared_ptr Console, const std:: return CMD_FAILED; } - effect->parameter = value; + effect->parameters[0] = value; } catch (std::invalid_argument const& ex) { SohImGui::GetConsole()->SendErrorMessage("[SOH] Knockback value must be a number."); return CMD_FAILED; @@ -1146,8 +1232,7 @@ static bool BurnHandler(std::shared_ptr Console, const std::vecto } static bool CuccoStormHandler(std::shared_ptr Console, const std::vector& args) { - GameInteractionEffectBase* effect = new GameInteractionEffect::SpawnCuccoStorm(); - GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect); + GameInteractionEffectQueryResult result = GameInteractor::RawAction::SpawnActor(ACTOR_EN_NIW, 0); if (result == GameInteractionEffectQueryResult::Possible) { SohImGui::GetConsole()->SendInfoMessage("[SOH] Spawned cucco storm"); @@ -1344,8 +1429,6 @@ void DebugConsole_Init(void) { { "Item ID", Ship::ArgumentType::NUMBER } }}); - CMD_REGISTER("givedekushield", { GiveDekuShieldHandler, "Gives a deku shield and equips it when Link is a child with no shield equiped." }); - CMD_REGISTER("spawn", { ActorSpawnHandler, "Spawn an actor.", { { "actor_id", Ship::ArgumentType::NUMBER }, { "data", Ship::ArgumentType::NUMBER }, { "x", Ship::ArgumentType::PLAYER_POS, true }, @@ -1371,8 +1454,13 @@ void DebugConsole_Init(void) { { "varName", Ship::ArgumentType::TEXT } }}); - CMD_REGISTER("ammo", { AmmoHandler, "Changes ammo of an item.", { - { "item", Ship::ArgumentType::TEXT }, + CMD_REGISTER("addammo", { AddAmmoHandler, "Adds ammo of an item.", { + { "sticks|nuts|bombs|seeds|arrows|bombchus|beans", Ship::ArgumentType::TEXT }, + { "count", Ship::ArgumentType::NUMBER } + }}); + + CMD_REGISTER("takeammo", { TakeAmmoHandler, "Removes ammo of an item.", { + { "sticks|nuts|bombs|seeds|arrows|bombchus|beans", Ship::ArgumentType::TEXT }, { "count", Ship::ArgumentType::NUMBER } }}); @@ -1464,7 +1552,15 @@ void DebugConsole_Init(void) { }}); CMD_REGISTER("boots", { BootsHandler, "Activates boots.", { - { "type", Ship::ArgumentType::TEXT }, + { "kokiri|iron|hover", Ship::ArgumentType::TEXT }, + }}); + + CMD_REGISTER("giveshield", { GiveShieldHandler, "Gives a shield and equips it when Link is the right age for it.", { + { "deku|hylian|mirror", Ship::ArgumentType::TEXT }, + }}); + + CMD_REGISTER("takeshield", { TakeShieldHandler, "Takes a shield and unequips it if Link is wearing it.", { + { "deku|hylian|mirror", Ship::ArgumentType::TEXT }, }}); CMD_REGISTER("knockback", { KnockbackHandler, "Knocks Link back.", { diff --git a/soh/soh/Enhancements/game-interactor/GameInteractionEffect.cpp b/soh/soh/Enhancements/game-interactor/GameInteractionEffect.cpp index c27b207f0..d0fda0db8 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractionEffect.cpp +++ b/soh/soh/Enhancements/game-interactor/GameInteractionEffect.cpp @@ -52,8 +52,8 @@ namespace GameInteractionEffect { if (!GameInteractor::IsSaveLoaded()) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else if ( - (parameter > 0 && (gSaveContext.healthCapacity + (parameter * 0x10) > 0x140)) || - (parameter < 0 && (gSaveContext.healthCapacity + (parameter * 0x10) < 0x10)) + (parameters[0] > 0 && (gSaveContext.healthCapacity + (parameters[0] * 0x10) > 0x140)) || + (parameters[0] < 0 && (gSaveContext.healthCapacity + (parameters[0] * 0x10) < 0x10)) ) { return GameInteractionEffectQueryResult::NotPossible; } @@ -62,7 +62,7 @@ namespace GameInteractionEffect { } void ModifyHeartContainers::_Apply() { - GameInteractor::RawAction::AddOrRemoveHealthContainers(parameter); + GameInteractor::RawAction::AddOrRemoveHealthContainers(parameters[0]); } // MARK: - FillMagic @@ -98,8 +98,8 @@ namespace GameInteractionEffect { if (!GameInteractor::IsSaveLoaded()) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else if ( - (parameter < 0 && gSaveContext.rupees <= 0) || - (parameter > 0 && gSaveContext.rupees >= CUR_CAPACITY(UPG_WALLET)) + (parameters[0] < 0 && gSaveContext.rupees <= 0) || + (parameters[0] > 0 && gSaveContext.rupees >= CUR_CAPACITY(UPG_WALLET)) ) { return GameInteractionEffectQueryResult::NotPossible; } else { @@ -107,7 +107,7 @@ namespace GameInteractionEffect { } } void ModifyRupees::_Apply() { - Rupees_ChangeBy(parameter); + Rupees_ChangeBy(parameters[0]); } // MARK: - NoUI @@ -134,7 +134,7 @@ namespace GameInteractionEffect { } } void ModifyGravity::_Apply() { - GameInteractor::State::GravityLevel = (GIGravityLevel)parameter; + GameInteractor::State::GravityLevel = (GIGravityLevel)parameters[0]; } void ModifyGravity::_Remove() { GameInteractor::State::GravityLevel = GI_GRAVITY_LEVEL_NORMAL; @@ -145,8 +145,8 @@ namespace GameInteractionEffect { if (!GameInteractor::IsSaveLoaded()) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else if ( - (parameter > 0 && gSaveContext.health == gSaveContext.healthCapacity) - || (parameter < 0 && (gSaveContext.health + (16 * parameter) <= 0)) + (parameters[0] > 0 && gSaveContext.health == gSaveContext.healthCapacity) + || (parameters[0] < 0 && (gSaveContext.health + (16 * parameters[0]) <= 0)) ) { return GameInteractionEffectQueryResult::NotPossible; } @@ -154,7 +154,7 @@ namespace GameInteractionEffect { return GameInteractionEffectQueryResult::Possible; } void ModifyHealth::_Apply() { - GameInteractor::RawAction::HealOrDamagePlayer(parameter); + GameInteractor::RawAction::HealOrDamagePlayer(parameters[0]); } // MARK: - SetPlayerHealth @@ -167,7 +167,7 @@ namespace GameInteractionEffect { } } void SetPlayerHealth::_Apply() { - GameInteractor::RawAction::SetPlayerHealth(parameter); + GameInteractor::RawAction::SetPlayerHealth(parameters[0]); } // MARK: - FreezePlayer @@ -218,7 +218,7 @@ namespace GameInteractionEffect { } } void KnockbackPlayer::_Apply() { - GameInteractor::RawAction::KnockbackPlayer(parameter); + GameInteractor::RawAction::KnockbackPlayer(parameters[0]); } // MARK: - ModifyLinkSize @@ -230,7 +230,7 @@ namespace GameInteractionEffect { } } void ModifyLinkSize::_Apply() { - GameInteractor::State::LinkSize = (GILinkSize)parameter; + GameInteractor::State::LinkSize = (GILinkSize)parameters[0]; } void ModifyLinkSize::_Remove() { GameInteractor::State::LinkSize = GI_LINK_SIZE_RESET; @@ -320,7 +320,7 @@ namespace GameInteractionEffect { } } void ForceEquipBoots::_Apply() { - GameInteractor::RawAction::ForceEquipBoots(parameter); + GameInteractor::RawAction::ForceEquipBoots(parameters[0]); } void ForceEquipBoots::_Remove() { GameInteractor::RawAction::ForceEquipBoots(PLAYER_BOOTS_KOKIRI); @@ -335,7 +335,7 @@ namespace GameInteractionEffect { } } void ModifyRunSpeedModifier::_Apply() { - GameInteractor::State::RunSpeedModifier = parameter; + GameInteractor::State::RunSpeedModifier = parameters[0]; } void ModifyRunSpeedModifier::_Remove() { GameInteractor::State::RunSpeedModifier = 0; @@ -356,7 +356,7 @@ namespace GameInteractionEffect { GameInteractor::State::OneHitKOActive = 0; } - // MARK: - IncreaseDamageTaken + // MARK: - ModifyDefenseModifier GameInteractionEffectQueryResult ModifyDefenseModifier::CanBeApplied() { if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; @@ -365,35 +365,229 @@ namespace GameInteractionEffect { } } void ModifyDefenseModifier::_Apply() { - GameInteractor::State::DefenseModifier = parameter; + GameInteractor::State::DefenseModifier = parameters[0]; } void ModifyDefenseModifier::_Remove() { GameInteractor::State::DefenseModifier = 0; } - // MARK: - GiveDekuShield - GameInteractionEffectQueryResult GiveDekuShield::CanBeApplied() { - if (!GameInteractor::IsSaveLoaded()) { + // MARK: - GiveOrTakeShield + GameInteractionEffectQueryResult GiveOrTakeShield::CanBeApplied() { + if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; - } else if ((Item_CheckObtainability(ITEM_SHIELD_DEKU) != ITEM_NONE)) { + } else if ((parameters[0] > 0 && ((gBitFlags[parameters[0] - ITEM_SHIELD_DEKU] << gEquipShifts[EQUIP_SHIELD]) & + gSaveContext.inventory.equipment)) || + (parameters[0] < 0 && !((gBitFlags[(parameters[0] * -1) - ITEM_SHIELD_DEKU] << gEquipShifts[EQUIP_SHIELD]) & + gSaveContext.inventory.equipment))) { return GameInteractionEffectQueryResult::NotPossible; } else { return GameInteractionEffectQueryResult::Possible; } } - void GiveDekuShield::_Apply() { - GameInteractor::RawAction::GiveDekuShield(); + void GiveOrTakeShield::_Apply() { + GameInteractor::RawAction::GiveOrTakeShield(parameters[0]); } - // MARK: - SpawnCuccoStorm - GameInteractionEffectQueryResult SpawnCuccoStorm::CanBeApplied() { + // MARK: - TeleportPlayer + GameInteractionEffectQueryResult TeleportPlayer::CanBeApplied() { if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { return GameInteractionEffectQueryResult::TemporarilyNotPossible; } else { return GameInteractionEffectQueryResult::Possible; } } - void SpawnCuccoStorm::_Apply() { - GameInteractor::RawAction::SpawnCuccoStorm(); + void TeleportPlayer::_Apply() { + GameInteractor::RawAction::TeleportPlayer(parameters[0]); + } + + // MARK: - ClearAssignedButtons + GameInteractionEffectQueryResult ClearAssignedButtons::CanBeApplied() { + if (!GameInteractor::IsSaveLoaded()) { + return GameInteractionEffectQueryResult::TemporarilyNotPossible; + } else { + return GameInteractionEffectQueryResult::Possible; + } + } + void ClearAssignedButtons::_Apply() { + GameInteractor::RawAction::ClearAssignedButtons(parameters[0]); + } + + // MARK: - SetTimeOfDay + GameInteractionEffectQueryResult SetTimeOfDay::CanBeApplied() { + if (!GameInteractor::IsSaveLoaded()) { + return GameInteractionEffectQueryResult::TemporarilyNotPossible; + } else { + return GameInteractionEffectQueryResult::Possible; + } + } + void SetTimeOfDay::_Apply() { + GameInteractor::RawAction::SetTimeOfDay(parameters[0]); + } + + // MARK: - SetCollisionViewer + GameInteractionEffectQueryResult SetCollisionViewer::CanBeApplied() { + if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + return GameInteractionEffectQueryResult::TemporarilyNotPossible; + } else { + return GameInteractionEffectQueryResult::Possible; + } + } + void SetCollisionViewer::_Apply() { + GameInteractor::RawAction::SetCollisionViewer(true); + } + void SetCollisionViewer::_Remove() { + GameInteractor::RawAction::SetCollisionViewer(false); + } + + // MARK: - SetCosmeticsColor + GameInteractionEffectQueryResult SetCosmeticsColor::CanBeApplied() { + if (!GameInteractor::IsSaveLoaded()) { + return GameInteractionEffectQueryResult::TemporarilyNotPossible; + } else { + return GameInteractionEffectQueryResult::Possible; + } + } + void SetCosmeticsColor::_Apply() { + GameInteractor::RawAction::SetCosmeticsColor(parameters[0], parameters[1]); + } + + // MARK: - RandomizeCosmetics + GameInteractionEffectQueryResult RandomizeCosmetics::CanBeApplied() { + if (!GameInteractor::IsSaveLoaded()) { + return GameInteractionEffectQueryResult::TemporarilyNotPossible; + } else { + return GameInteractionEffectQueryResult::Possible; + } + } + void RandomizeCosmetics::_Apply() { + GameInteractor::RawAction::RandomizeCosmeticsColors(true); + } + + // MARK: - PressButton + GameInteractionEffectQueryResult PressButton::CanBeApplied() { + if (!GameInteractor::IsSaveLoaded()) { + return GameInteractionEffectQueryResult::TemporarilyNotPossible; + } else { + return GameInteractionEffectQueryResult::Possible; + } + } + void PressButton::_Apply() { + GameInteractor::RawAction::EmulateButtonPress(parameters[0]); + } + + // MARK: - PressRandomButton + GameInteractionEffectQueryResult PressRandomButton::CanBeApplied() { + if (!GameInteractor::IsSaveLoaded()) { + return GameInteractionEffectQueryResult::TemporarilyNotPossible; + } else { + return GameInteractionEffectQueryResult::Possible; + } + } + void PressRandomButton::_Apply() { + GameInteractor::RawAction::EmulateRandomButtonPress(parameters[0]); + } + + // MARK: - AddOrTakeAmmo + GameInteractionEffectQueryResult AddOrTakeAmmo::CanBeApplied() { + if (!GameInteractor::IsSaveLoaded()) { + return GameInteractionEffectQueryResult::TemporarilyNotPossible; + } else if (!GameInteractor::CanAddOrTakeAmmo(parameters[0], parameters[1])) { + return GameInteractionEffectQueryResult::NotPossible; + } else { + return GameInteractionEffectQueryResult::Possible; + } + } + void AddOrTakeAmmo::_Apply() { + GameInteractor::RawAction::AddOrTakeAmmo(parameters[0], parameters[1]); + } + + // MARK: - RandomBombFuseTimer + GameInteractionEffectQueryResult RandomBombFuseTimer::CanBeApplied() { + if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + return GameInteractionEffectQueryResult::TemporarilyNotPossible; + } else { + return GameInteractionEffectQueryResult::Possible; + } + } + void RandomBombFuseTimer::_Apply() { + GameInteractor::State::RandomBombFuseTimerActive = 1; + } + void RandomBombFuseTimer::_Remove() { + GameInteractor::State::RandomBombFuseTimerActive = 0; + } + + // MARK: - DisableLedgeGrabs + GameInteractionEffectQueryResult DisableLedgeGrabs::CanBeApplied() { + if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + return GameInteractionEffectQueryResult::TemporarilyNotPossible; + } else { + return GameInteractionEffectQueryResult::Possible; + } + } + void DisableLedgeGrabs::_Apply() { + GameInteractor::State::DisableLedgeGrabsActive = 1; + } + void DisableLedgeGrabs::_Remove() { + GameInteractor::State::DisableLedgeGrabsActive = 0; + } + + // MARK: - RandomWind + GameInteractionEffectQueryResult RandomWind::CanBeApplied() { + if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + return GameInteractionEffectQueryResult::TemporarilyNotPossible; + } else { + return GameInteractionEffectQueryResult::Possible; + } + } + void RandomWind::_Apply() { + GameInteractor::RawAction::SetRandomWind(true); + } + void RandomWind::_Remove() { + GameInteractor::RawAction::SetRandomWind(false); + } + + // MARK: - RandomBonks + GameInteractionEffectQueryResult RandomBonks::CanBeApplied() { + if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + return GameInteractionEffectQueryResult::TemporarilyNotPossible; + } else { + return GameInteractionEffectQueryResult::Possible; + } + } + void RandomBonks::_Apply() { + GameInteractor::State::RandomBonksActive = 1; + } + void RandomBonks::_Remove() { + GameInteractor::State::RandomBonksActive = 0; + } + + // MARK: - PlayerInvincibility + GameInteractionEffectQueryResult PlayerInvincibility::CanBeApplied() { + if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + return GameInteractionEffectQueryResult::TemporarilyNotPossible; + } else { + return GameInteractionEffectQueryResult::Possible; + } + } + void PlayerInvincibility::_Apply() { + GameInteractor::RawAction::SetPlayerInvincibility(true); + } + void PlayerInvincibility::_Remove() { + GameInteractor::RawAction::SetPlayerInvincibility(false); + } + + // MARK: - SlipperyFloor + GameInteractionEffectQueryResult SlipperyFloor::CanBeApplied() { + if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { + return GameInteractionEffectQueryResult::TemporarilyNotPossible; + } else { + return GameInteractionEffectQueryResult::Possible; + } + } + void SlipperyFloor::_Apply() { + GameInteractor::State::SlipperyFloorActive = 1; + } + void SlipperyFloor::_Remove() { + GameInteractor::State::SlipperyFloorActive = 0; } } diff --git a/soh/soh/Enhancements/game-interactor/GameInteractionEffect.h b/soh/soh/Enhancements/game-interactor/GameInteractionEffect.h index 7f0ed9ee4..ab8cfb610 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractionEffect.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractionEffect.h @@ -18,8 +18,9 @@ public: virtual GameInteractionEffectQueryResult CanBeRemoved(); GameInteractionEffectQueryResult Apply(); GameInteractionEffectQueryResult Remove(); - int32_t parameter; -protected: + int32_t parameters[2]; + + protected: virtual void _Apply() = 0; virtual void _Remove() {}; }; @@ -147,15 +148,92 @@ namespace GameInteractionEffect { void _Remove() override; }; - class GiveDekuShield: public GameInteractionEffectBase { + class GiveOrTakeShield: public GameInteractionEffectBase { GameInteractionEffectQueryResult CanBeApplied() override; void _Apply() override; }; - class SpawnCuccoStorm: public GameInteractionEffectBase { + class TeleportPlayer: public GameInteractionEffectBase { GameInteractionEffectQueryResult CanBeApplied() override; void _Apply() override; }; + + class ClearAssignedButtons: public GameInteractionEffectBase { + GameInteractionEffectQueryResult CanBeApplied() override; + void _Apply() override; + }; + + class SetTimeOfDay: public GameInteractionEffectBase { + GameInteractionEffectQueryResult CanBeApplied() override; + void _Apply() override; + }; + + class SetCollisionViewer: public GameInteractionEffectBase { + GameInteractionEffectQueryResult CanBeApplied() override; + void _Apply() override; + void _Remove() override; + }; + + class SetCosmeticsColor: public GameInteractionEffectBase { + GameInteractionEffectQueryResult CanBeApplied() override; + void _Apply() override; + }; + + class RandomizeCosmetics: public GameInteractionEffectBase { + GameInteractionEffectQueryResult CanBeApplied() override; + void _Apply() override; + }; + + class PressButton: public GameInteractionEffectBase { + GameInteractionEffectQueryResult CanBeApplied() override; + void _Apply() override; + }; + + class PressRandomButton: public GameInteractionEffectBase { + GameInteractionEffectQueryResult CanBeApplied() override; + void _Apply() override; + }; + + class AddOrTakeAmmo: public GameInteractionEffectBase { + GameInteractionEffectQueryResult CanBeApplied() override; + void _Apply() override; + }; + + class RandomBombFuseTimer: public GameInteractionEffectBase { + GameInteractionEffectQueryResult CanBeApplied() override; + void _Apply() override; + void _Remove() override; + }; + + class DisableLedgeGrabs: public GameInteractionEffectBase { + GameInteractionEffectQueryResult CanBeApplied() override; + void _Apply() override; + void _Remove() override; + }; + + class RandomWind: public GameInteractionEffectBase { + GameInteractionEffectQueryResult CanBeApplied() override; + void _Apply() override; + void _Remove() override; + }; + + class RandomBonks: public GameInteractionEffectBase { + GameInteractionEffectQueryResult CanBeApplied() override; + void _Apply() override; + void _Remove() override; + }; + + class PlayerInvincibility: public GameInteractionEffectBase { + GameInteractionEffectQueryResult CanBeApplied() override; + void _Apply() override; + void _Remove() override; + }; + + class SlipperyFloor: public GameInteractionEffectBase { + GameInteractionEffectQueryResult CanBeApplied() override; + void _Apply() override; + void _Remove() override; + }; } #endif /* __cplusplus */ diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor.cpp b/soh/soh/Enhancements/game-interactor/GameInteractor.cpp index 44f9877e1..77d3c168c 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor.cpp +++ b/soh/soh/Enhancements/game-interactor/GameInteractor.cpp @@ -50,6 +50,48 @@ bool GameInteractor::IsGameplayPaused() { return (Player_InBlockingCsMode(gPlayState, player) || gPlayState->pauseCtx.state != 0 || gPlayState->msgCtx.msgMode != 0) ? true : false; } -bool GameInteractor::CanSpawnEnemy() { +bool GameInteractor::CanSpawnActor() { return GameInteractor::IsSaveLoaded() && !GameInteractor::IsGameplayPaused(); } + +bool GameInteractor::CanAddOrTakeAmmo(int16_t amount, int16_t item) { + int16_t upgradeToCheck = 0; + + switch (item) { + case ITEM_STICK: + upgradeToCheck = UPG_STICKS; + break; + case ITEM_NUT: + upgradeToCheck = UPG_NUTS; + break; + case ITEM_BOW: + upgradeToCheck = UPG_QUIVER; + break; + case ITEM_SLINGSHOT: + upgradeToCheck = UPG_BULLET_BAG; + break; + case ITEM_BOMB: + upgradeToCheck = UPG_BOMB_BAG; + break; + default: + break; + } + + if (amount < 0 && AMMO(item) == 0) { + return false; + } + + if (item != ITEM_BOMBCHU && item != ITEM_BEAN) { + if ((CUR_CAPACITY(upgradeToCheck) == 0) || (amount > 0 && AMMO(item) == CUR_CAPACITY(upgradeToCheck))) { + return false; + } + return true; + } else { + // Seperate checks for beans and bombchus because they don't have capacity upgrades + if (INV_CONTENT(item) != item || + (amount > 0 && ((item == ITEM_BOMBCHU && AMMO(item) == 50) || (item == ITEM_BEAN && AMMO(item) == 10)))) { + return false; + } + return true; + } +} diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor.h b/soh/soh/Enhancements/game-interactor/GameInteractor.h index 927e59f23..3da93146e 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor.h @@ -11,6 +11,7 @@ typedef enum { /* 0x01 */ GI_LINK_SIZE_GIANT, /* 0x02 */ GI_LINK_SIZE_MINISH, /* 0x03 */ GI_LINK_SIZE_PAPER, + /* 0x03 */ GI_LINK_SIZE_SQUISHED, /* 0x04 */ GI_LINK_SIZE_RESET } GILinkSize; @@ -20,6 +21,46 @@ typedef enum { /* 0x02 */ GI_GRAVITY_LEVEL_HEAVY, } GIGravityLevel; +typedef enum { + /* 0x00 */ GI_BUTTONS_CBUTTONS, + /* 0x01 */ GI_BUTTONS_DPAD, +} GIButtonSet; + +typedef enum { + /* */ GI_TIMEOFDAY_DAWN = 32768, + /* */ GI_TIMEOFDAY_NOON = 49152, + /* */ GI_TIMEOFDAY_DUSK = 0, + /* */ GI_TIMEOFDAY_MIDNIGHT = 16384, +} GITimeOfDay; + +typedef enum { + /* 0x00 */ GI_COSMETICS_TUNICS, + /* 0x01 */ GI_COSMETICS_NAVI, + /* 0x02 */ GI_COSMETICS_HAIR, +} GICosmeticCategories; + +typedef enum { + /* 0x00 */ GI_COLOR_RED, + /* 0x01 */ GI_COLOR_GREEN, + /* 0x02 */ GI_COLOR_BLUE, + /* 0x03 */ GI_COLOR_ORANGE, + /* 0x04 */ GI_COLOR_YELLOW, + /* 0x05 */ GI_COLOR_PURPLE, + /* 0x06 */ GI_COLOR_PINK, + /* 0x07 */ GI_COLOR_BROWN, + /* 0x08 */ GI_COLOR_BLACK, +} GIColors; + +typedef enum { + /* */ GI_TP_DEST_LINKSHOUSE = 187, + /* */ GI_TP_DEST_MINUET = 1536, + /* */ GI_TP_DEST_BOLERO = 1270, + /* */ GI_TP_DEST_SERENADE = 1540, + /* */ GI_TP_DEST_REQUIEM = 497, + /* */ GI_TP_DEST_NOCTURNE = 1384, + /* */ GI_TP_DEST_PRELUDE = 1524, +} GITeleportDestinations; + #ifdef __cplusplus extern "C" { #endif @@ -34,6 +75,13 @@ uint8_t GameInteractor_ReverseControlsActive(); int32_t GameInteractor_DefenseModifier(); int32_t GameInteractor_RunSpeedModifier(); GIGravityLevel GameInteractor_GravityLevel(); +uint32_t GameInteractor_GetEmulatedButtons(); +void GameInteractor_SetEmulatedButtons(uint32_t buttons); +uint8_t GameInteractor_GetRandomBombFuseTimerActive(); +uint8_t GameInteractor_GetDisableLedgeGrabsActive(); +uint8_t GameInteractor_GetRandomWindActive(); +uint8_t GameInteractor_GetRandomBonksActive(); +uint8_t GameInteractor_GetSlipperyFloorActive(); #ifdef __cplusplus } #endif @@ -66,6 +114,13 @@ public: static int32_t DefenseModifier; static int32_t RunSpeedModifier; static GIGravityLevel GravityLevel; + static uint32_t EmulatedButtons; + static uint8_t RandomBombFuseTimerActive; + static uint8_t DisableLedgeGrabsActive; + static uint8_t RandomWindActive; + static uint8_t RandomWindSecondsSinceLastDirectionChange; + static uint8_t RandomBonksActive; + static uint8_t SlipperyFloorActive; static void SetPacifistMode(bool active); }; @@ -116,7 +171,8 @@ public: // Helpers static bool IsSaveLoaded(); static bool IsGameplayPaused(); - static bool CanSpawnEnemy(); + static bool CanSpawnActor(); + static bool CanAddOrTakeAmmo(int16_t amount, int16_t item); class RawAction { public: @@ -131,11 +187,22 @@ public: static void BurnPlayer(); static void ElectrocutePlayer(); static void KnockbackPlayer(float strength); - static void GiveDekuShield(); - static void SpawnCuccoStorm(); + static void GiveOrTakeShield(int32_t shield); static void ForceInterfaceUpdate(); + static void TeleportPlayer(int32_t nextEntrance); + static void ClearAssignedButtons(uint8_t buttonSet); + static void SetTimeOfDay(uint32_t time); + static void SetCollisionViewer(bool active); + static void SetCosmeticsColor(uint8_t cosmeticCategory, uint8_t colorValue); + static void RandomizeCosmeticsColors(bool excludeBiddingWarColors); + static void EmulateButtonPress(int32_t button); + static void AddOrTakeAmmo(int16_t amount, int16_t item); + static void EmulateRandomButtonPress(uint32_t chancePercentage = 100); + static void SetRandomWind(bool active); + static void SetPlayerInvincibility(bool active); static GameInteractionEffectQueryResult SpawnEnemyWithOffset(uint32_t enemyId, int32_t enemyParams); + static GameInteractionEffectQueryResult SpawnActor(uint32_t actorId, int32_t actorParams); }; }; diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_RawAction.cpp b/soh/soh/Enhancements/game-interactor/GameInteractor_RawAction.cpp index 2409347ea..20cc19464 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_RawAction.cpp +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_RawAction.cpp @@ -1,5 +1,8 @@ #include "GameInteractor.h" #include +#include "soh/Enhancements/cosmetics/CosmeticsEditor.h" +#include "soh/Enhancements/randomizer/3drando/random.hpp" +#include extern "C" { #include "variables.h" @@ -9,6 +12,7 @@ extern PlayState* gPlayState; } #include "overlays/actors/ovl_En_Niw/z_en_niw.h" +#include "overlays/actors/ovl_En_Bom/z_en_bom.h" void GameInteractor::RawAction::AddOrRemoveHealthContainers(int16_t amount) { gSaveContext.healthCapacity += amount * 0x10; @@ -120,21 +124,53 @@ void GameInteractor::RawAction::KnockbackPlayer(float strength) { 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::GiveOrTakeShield(int32_t shield) { + // When taking a shield, make sure it is unequipped as well. + // When giving a shield and the player isn't wearing one yet, + // equip said shield when the player's current age can equip it. + // 'shield' being a negative number means taking that shield. -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; + int8_t shieldToCheck = PLAYER_SHIELD_NONE; + + if (shield < 0) { + shield = shield * -1; + + switch (shield) { + case ITEM_SHIELD_DEKU: + shieldToCheck = PLAYER_SHIELD_DEKU; + break; + case ITEM_SHIELD_HYLIAN: + shieldToCheck = PLAYER_SHIELD_HYLIAN; + break; + case ITEM_SHIELD_MIRROR: + shieldToCheck = PLAYER_SHIELD_MIRROR; + break; + default: + break; + } + + gSaveContext.inventory.equipment &= ~(gBitFlags[shield - ITEM_SHIELD_DEKU] << gEquipShifts[EQUIP_SHIELD]); + + if (player->currentShield == shieldToCheck) { + player->currentShield = PLAYER_SHIELD_NONE; + Inventory_ChangeEquipment(EQUIP_SHIELD, PLAYER_SHIELD_NONE); + } + } else { + Item_Give(gPlayState, shield); + if (player->currentShield == PLAYER_SHIELD_NONE) { + if (LINK_IS_CHILD && shield == ITEM_SHIELD_DEKU) { + player->currentShield = PLAYER_SHIELD_DEKU; + Inventory_ChangeEquipment(EQUIP_SHIELD, PLAYER_SHIELD_DEKU); + } else if (LINK_IS_ADULT && shield == ITEM_SHIELD_MIRROR) { + player->currentShield = PLAYER_SHIELD_MIRROR; + Inventory_ChangeEquipment(EQUIP_SHIELD, PLAYER_SHIELD_MIRROR); + } else if (shield == ITEM_SHIELD_HYLIAN) { + player->currentShield = PLAYER_SHIELD_HYLIAN; + Inventory_ChangeEquipment(EQUIP_SHIELD, PLAYER_SHIELD_HYLIAN); + } + } + } } void GameInteractor::RawAction::ForceInterfaceUpdate() { @@ -142,58 +178,348 @@ void GameInteractor::RawAction::ForceInterfaceUpdate() { Interface_Update(gPlayState); } +void GameInteractor::RawAction::TeleportPlayer(int32_t nextEntrance) { + Audio_PlaySoundGeneral(NA_SE_EN_GANON_LAUGH, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + gPlayState->nextEntranceIndex = nextEntrance; + gPlayState->sceneLoadFlag = 0x14; + gPlayState->fadeTransition = 2; + gSaveContext.nextTransitionType = 2; +} + +void GameInteractor::RawAction::ClearAssignedButtons(uint8_t buttonSet) { + switch (buttonSet) { + case GI_BUTTONS_CBUTTONS: + gSaveContext.equips.buttonItems[1] = gSaveContext.equips.buttonItems[2] = + gSaveContext.equips.buttonItems[3] = ITEM_NONE; + break; + case GI_BUTTONS_DPAD: + gSaveContext.equips.buttonItems[4] = gSaveContext.equips.buttonItems[5] = + gSaveContext.equips.buttonItems[6] = gSaveContext.equips.buttonItems[7] = ITEM_NONE; + break; + } +} + +void GameInteractor::RawAction::SetTimeOfDay(uint32_t time) { + if (time > 0xFFFF) { + gSaveContext.dayTime = 0xFFFF; + } else { + gSaveContext.dayTime = time; + } +} + +void GameInteractor::RawAction::SetCollisionViewer(bool active) { + CVarSetInteger("gColViewerEnabled", active); + CVarSetInteger("gColViewerDecal", active); + + if (active) { + CVarSetInteger("gColViewerScene", 2); + CVarSetInteger("gColViewerBgActors", 2); + CVarSetInteger("gColViewerColCheck", 2); + CVarSetInteger("gColViewerWaterbox", 2); + } else { + CVarSetInteger("gColViewerScene", 0); + CVarSetInteger("gColViewerBgActors", 0); + CVarSetInteger("gColViewerColCheck", 0); + CVarSetInteger("gColViewerWaterbox", 0); + } +} + +void GameInteractor::RawAction::SetCosmeticsColor(uint8_t cosmeticCategory, uint8_t colorValue) { + Color_RGBA8 newColor; + newColor.r = 255; + newColor.g = 255; + newColor.b = 255; + newColor.a = 255; + + switch (colorValue) { + case GI_COLOR_RED: + newColor.r = 200; + newColor.g = 30; + newColor.b = 30; + break; + case GI_COLOR_GREEN: + newColor.r = 50; + newColor.g = 200; + newColor.b = 50; + break; + case GI_COLOR_BLUE: + newColor.r = 50; + newColor.g = 50; + newColor.b = 200; + break; + case GI_COLOR_ORANGE: + newColor.r = 200; + newColor.g = 120; + newColor.b = 0; + break; + case GI_COLOR_YELLOW: + newColor.r = 234; + newColor.g = 240; + newColor.b = 33; + break; + case GI_COLOR_PURPLE: + newColor.r = 144; + newColor.g = 13; + newColor.b = 178; + break; + case GI_COLOR_PINK: + newColor.r = 215; + newColor.g = 93; + newColor.b = 246; + break; + case GI_COLOR_BROWN: + newColor.r = 108; + newColor.g = 72; + newColor.b = 15; + break; + case GI_COLOR_BLACK: + newColor.r = 0; + newColor.g = 0; + newColor.b = 0; + break; + default: + break; + } + + switch (cosmeticCategory) { + case GI_COSMETICS_TUNICS: + CVarSetColor("gCosmetics.Link_KokiriTunic.Value", newColor); + CVarSetInteger("gCosmetics.Link_KokiriTunic.Changed", 1); + CVarSetColor("gCosmetics.Link_GoronTunic.Value", newColor); + CVarSetInteger("gCosmetics.Link_GoronTunic.Changed", 1); + CVarSetColor("gCosmetics.Link_ZoraTunic.Value", newColor); + CVarSetInteger("gCosmetics.Link_ZoraTunic.Changed", 1); + break; + case GI_COSMETICS_NAVI: + CVarSetColor("gCosmetics.Navi_EnemyPrimary.Value", newColor); + CVarSetInteger("gCosmetics.Navi_EnemyPrimary.Changed", 1); + CVarSetColor("gCosmetics.Navi_EnemySecondary.Value", newColor); + CVarSetInteger("gCosmetics.Navi_EnemySecondary.Changed", 1); + CVarSetColor("gCosmetics.Navi_IdlePrimary.Value", newColor); + CVarSetInteger("gCosmetics.Navi_IdlePrimary.Changed", 1); + CVarSetColor("gCosmetics.Navi_IdleSecondary.Value", newColor); + CVarSetInteger("gCosmetics.Navi_IdleSecondary.Changed", 1); + CVarSetColor("gCosmetics.Navi_NPCPrimary.Value", newColor); + CVarSetInteger("gCosmetics.Navi_NPCPrimary.Changed", 1); + CVarSetColor("gCosmetics.Navi_NPCSecondary.Value", newColor); + CVarSetInteger("gCosmetics.Navi_NPCSecondary.Changed", 1); + CVarSetColor("gCosmetics.Navi_PropsPrimary.Value", newColor); + CVarSetInteger("gCosmetics.Navi_PropsPrimary.Changed", 1); + CVarSetColor("gCosmetics.Navi_PropsSecondary.Value", newColor); + CVarSetInteger("gCosmetics.Navi_PropsSecondary.Changed", 1); + break; + case GI_COSMETICS_HAIR: + CVarSetColor("gCosmetics.Link_Hair.Value", newColor); + CVarSetInteger("gCosmetics.Link_Hair.Changed", 1); + break; + } + + SohImGui::RequestCvarSaveOnNextTick(); + ApplyOrResetCustomGfxPatches(); +} + +void GameInteractor::RawAction::RandomizeCosmeticsColors(bool excludeBiddingWarColors) { + const char* cvarsToLock[12] = { + "gCosmetics.Link_KokiriTunic.Locked", + "gCosmetics.Link_GoronTunic.Locked", + "gCosmetics.Link_ZoraTunic.Locked", + "gCosmetics.Navi_EnemyPrimary.Locked", + "gCosmetics.Navi_EnemySecondary.Locked", + "gCosmetics.Navi_IdlePrimary.Locked", + "gCosmetics.Navi_IdleSecondary.Locked", + "gCosmetics.Navi_NPCPrimary.Locked", + "gCosmetics.Navi_NPCSecondary.Locked", + "gCosmetics.Navi_PropsPrimary.Locked", + "gCosmetics.Navi_PropsSecondary.Locked", + "gCosmetics.Link_Hair.Locked" + }; + + if (excludeBiddingWarColors) { + for (uint8_t i = 0; i < 12; i++) { + CVarSetInteger(cvarsToLock[i], 1); + } + } + + CosmeticsEditor_RandomizeAll(); + + if (excludeBiddingWarColors) { + for (uint8_t i = 0; i < 12; i++) { + CVarSetInteger(cvarsToLock[i], 0); + } + } +} + +void GameInteractor::RawAction::EmulateButtonPress(int32_t button) { + GameInteractor::State::EmulatedButtons |= button; +} + +void GameInteractor::RawAction::EmulateRandomButtonPress(uint32_t chancePercentage) { + uint32_t emulatedButton; + uint32_t randomNumber = rand(); + uint32_t possibleButtons[14] = { BTN_CRIGHT, BTN_CLEFT, BTN_CDOWN, BTN_CUP, BTN_R, BTN_L, BTN_DRIGHT, + BTN_DLEFT, BTN_DDOWN, BTN_DUP, BTN_START, BTN_Z, BTN_B, BTN_A }; + + emulatedButton = possibleButtons[randomNumber % 14]; + + if (randomNumber % 100 < chancePercentage) { + GameInteractor::State::EmulatedButtons |= emulatedButton; + } +} + +void GameInteractor::RawAction::AddOrTakeAmmo(int16_t amount, int16_t item) { + Inventory_ChangeAmmo(item, amount); +} + +void GameInteractor::RawAction::SetRandomWind(bool active) { + Player* player = GET_PLAYER(gPlayState); + if (active) { + GameInteractor::State::RandomWindActive = 1; + if (GameInteractor::State::RandomWindSecondsSinceLastDirectionChange == 0) { + player->windDirection = (rand() % 49152) - 32767; + GameInteractor::State::RandomWindSecondsSinceLastDirectionChange = 5; + } else { + GameInteractor::State::RandomWindSecondsSinceLastDirectionChange--; + } + } else { + GameInteractor::State::RandomWindActive = 0; + GameInteractor::State::RandomWindSecondsSinceLastDirectionChange = 0; + player->windSpeed = 0.0f; + player->windDirection = 0.0f; + } +} + +void GameInteractor::RawAction::SetPlayerInvincibility(bool active) { + Player* player = GET_PLAYER(gPlayState); + if (active) { + player->invincibilityTimer = 1000; + } else { + player->invincibilityTimer = 0; + } +} + GameInteractionEffectQueryResult GameInteractor::RawAction::SpawnEnemyWithOffset(uint32_t enemyId, int32_t enemyParams) { - if (!GameInteractor::CanSpawnEnemy()) { + if (!GameInteractor::CanSpawnActor()) { + return GameInteractionEffectQueryResult::TemporarilyNotPossible; + } + + int16_t sceneNum = gPlayState->sceneNum; + int16_t roomNum = gPlayState->roomCtx.curRoom.num; + Player* player = GET_PLAYER(gPlayState); + + // Disallow enemy spawns in the painting Poe rooms in Forest Temple. + // Killing a spawned enemy before the Poe can softlock the rooms entirely. + if (sceneNum == SCENE_BMORI1 && (roomNum == 12 || roomNum == 13 || roomNum == 16)) { + return GameInteractionEffectQueryResult::NotPossible; + } + + // Only one Dark Link may exist at a time. + if (enemyId == ACTOR_EN_TORCH2) { + Actor* nearbyEnTest = Actor_FindNearby(gPlayState, &player->actor, ACTOR_EN_TORCH2, ACTORCAT_ENEMY, 8000.0f); + if (nearbyEnTest != NULL) { + return GameInteractionEffectQueryResult::TemporarilyNotPossible; + } + } + + 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 (sceneNum == SCENE_DDAN_BOSS || sceneNum == SCENE_MIZUSIN_BS || sceneNum == SCENE_JYASINBOSS || + sceneNum == SCENE_GANON_BOSS || sceneNum == SCENE_TURIBORI || sceneNum == SCENE_GANON_DEMO) { + return GameInteractionEffectQueryResult::NotPossible; + } + } + + // Generate point in random angle with a radius. + float angle = Random(0, 2 * M_PI); + float radius = 150; + float posXOffset = radius * cos(angle); + float posZOffset = radius * sin(angle); + + // Raycast to the ground from randomly generated point. + CollisionPoly poly; + Vec3f pos; + f32 raycastResult; + + pos.x = player->actor.world.pos.x + posXOffset; + pos.y = player->actor.world.pos.y + 50; + pos.z = player->actor.world.pos.z + posZOffset; + raycastResult = BgCheck_AnyRaycastFloor1(&gPlayState->colCtx, &poly, &pos); + + // If ground is found below actor, move actor to that height. + // If not it's likely out of bounds, so make it temporarily not possible and try again later. + if (raycastResult > BGCHECK_Y_MIN) { + pos.y = raycastResult; + } else { + return GameInteractionEffectQueryResult::TemporarilyNotPossible; + } + + // Offset flying enemies off the ground + if (enemyId == ACTOR_EN_CLEAR_TAG || enemyId == ACTOR_EN_FIREFLY) { + pos.y += 100; + } + + if (enemyId == ACTOR_EN_FIREFLY && enemyParams == 2) { + // Spawn Flock of Keese (5x) + for (uint8_t i = 0; i < 5; i++) { + pos.x += 10; + pos.y += 10; + pos.z += 10; + if (Actor_Spawn(&gPlayState->actorCtx, gPlayState, enemyId, pos.x, pos.y, pos.z, 0, 0, 0, enemyParams, 0) == NULL) { + return GameInteractionEffectQueryResult::TemporarilyNotPossible; + } + } + return GameInteractionEffectQueryResult::Possible; + } else { + if (Actor_Spawn(&gPlayState->actorCtx, gPlayState, enemyId, pos.x, pos.y, pos.z, 0, 0, 0, enemyParams, 0) != NULL) { + return GameInteractionEffectQueryResult::Possible; + } + } + + return GameInteractionEffectQueryResult::TemporarilyNotPossible; +} + +GameInteractionEffectQueryResult GameInteractor::RawAction::SpawnActor(uint32_t actorId, int32_t actorParams) { + + if (!GameInteractor::CanSpawnActor()) { 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; + if (actorId == ACTOR_EN_NIW) { + // Spawn Cucco and make it angry + EnNiw* cucco = (EnNiw*)Actor_Spawn(&gPlayState->actorCtx, gPlayState, actorId, player->actor.world.pos.x, + player->actor.world.pos.y + 2200, player->actor.world.pos.z, 0, 0, 0, actorParams, 0); + if (cucco == NULL) { + return GameInteractionEffectQueryResult::TemporarilyNotPossible; } - 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) { + cucco->actionFunc = func_80AB70A0_nocutscene; return GameInteractionEffectQueryResult::Possible; + } else if (actorId == ACTOR_EN_BOM) { + // Spawn a bomb, make it explode instantly when params is set to 1 to emulate spawning an explosion + EnBom* bomb = (EnBom*)Actor_Spawn(&gPlayState->actorCtx, gPlayState, ACTOR_EN_BOM, player->actor.world.pos.x, + player->actor.world.pos.y + 30, player->actor.world.pos.z, 0, 0, 0, BOMB_BODY, true); + + if (bomb == NULL) { + return GameInteractionEffectQueryResult::TemporarilyNotPossible; + } + + // Make bomb explode immediately + if (actorParams == 1) { + bomb->timer = 2; + } + return GameInteractionEffectQueryResult::Possible; + } else { + // Generic spawn an actor at Link's position + if (Actor_Spawn(&gPlayState->actorCtx, gPlayState, actorId, player->actor.world.pos.x, + player->actor.world.pos.y, player->actor.world.pos.z, 0, 0, 0, actorParams, 0) != NULL) { + return GameInteractionEffectQueryResult::Possible; + } } return GameInteractionEffectQueryResult::TemporarilyNotPossible; + } diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_State.cpp b/soh/soh/Enhancements/game-interactor/GameInteractor_State.cpp index 23222bb4b..00fdd5858 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_State.cpp +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_State.cpp @@ -12,6 +12,13 @@ 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; +uint32_t GameInteractor::State::EmulatedButtons = 0; +uint8_t GameInteractor::State::RandomBombFuseTimerActive = 0; +uint8_t GameInteractor::State::DisableLedgeGrabsActive = 0; +uint8_t GameInteractor::State::RandomWindActive = 0; +uint8_t GameInteractor::State::RandomWindSecondsSinceLastDirectionChange = 0; +uint8_t GameInteractor::State::RandomBonksActive = 0; +uint8_t GameInteractor::State::SlipperyFloorActive = 0; void GameInteractor::State::SetPacifistMode(bool active) { PacifistModeActive = active; @@ -79,3 +86,38 @@ int32_t GameInteractor_RunSpeedModifier() { GIGravityLevel GameInteractor_GravityLevel() { return GameInteractor::State::GravityLevel; } + +// MARK: - GameInteractor::State::GetEmulatedButtons +uint32_t GameInteractor_GetEmulatedButtons() { + return GameInteractor::State::EmulatedButtons; +} + +// MARK: - GameInteractor::State::SetEmulatedButtons +void GameInteractor_SetEmulatedButtons(uint32_t buttons) { + GameInteractor::State::EmulatedButtons = buttons; +} + +// MARK: - GameInteractor::State::GetRandomBombFuseTimerActive +uint8_t GameInteractor_GetRandomBombFuseTimerActive() { + return GameInteractor::State::RandomBombFuseTimerActive; +} + +// MARK: - GameInteractor::State::GetDisableLedgeGrabsActive +uint8_t GameInteractor_GetDisableLedgeGrabsActive() { + return GameInteractor::State::DisableLedgeGrabsActive; +} + +// MARK: - GameInteractor::State::GetRandomWindActive +uint8_t GameInteractor_GetRandomWindActive() { + return GameInteractor::State::RandomWindActive; +} + +// MARK: - GameInteractor::State::GetRandomBonksActive +uint8_t GameInteractor_GetRandomBonksActive() { + return GameInteractor::State::RandomBonksActive; +} + +// MARK: - GameInteractor::State::GetSlipperyFloorActive +uint8_t GameInteractor_GetSlipperyFloorActive() { + return GameInteractor::State::SlipperyFloorActive; +} diff --git a/soh/src/code/padmgr.c b/soh/src/code/padmgr.c index 6521ff3aa..cfc0c05b5 100644 --- a/soh/src/code/padmgr.c +++ b/soh/src/code/padmgr.c @@ -233,6 +233,12 @@ void PadMgr_ProcessInputs(PadMgr* padMgr) { input->cur.button &= ~(BTN_Z); } + uint32_t emulatedButtons = GameInteractor_GetEmulatedButtons(); + if (emulatedButtons) { + input->cur.button |= emulatedButtons; + GameInteractor_SetEmulatedButtons(0); + } + if (GameInteractor_ReverseControlsActive()) { if (input->cur.stick_x == -128) { input->cur.stick_x = 127; diff --git a/soh/src/overlays/actors/ovl_En_Bom/z_en_bom.c b/soh/src/overlays/actors/ovl_En_Bom/z_en_bom.c index 3174cc3e7..5f77b4ece 100644 --- a/soh/src/overlays/actors/ovl_En_Bom/z_en_bom.c +++ b/soh/src/overlays/actors/ovl_En_Bom/z_en_bom.c @@ -7,6 +7,7 @@ #include "z_en_bom.h" #include "overlays/effects/ovl_Effect_Ss_Dead_Sound/z_eff_ss_dead_sound.h" #include "objects/gameplay_keep/gameplay_keep.h" +#include "soh/Enhancements/game-interactor/GameInteractor.h" #define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) @@ -96,7 +97,16 @@ void EnBom_Init(Actor* thisx, PlayState* play) { thisx->colChkInfo.mass = 200; thisx->colChkInfo.cylRadius = 5; thisx->colChkInfo.cylHeight = 10; - this->timer = 70; + if (!GameInteractor_GetRandomBombFuseTimerActive()) { + this->timer = 70; + } else { + // Set random fuse timer with a minimum of 10. Do the sound and scale immediately, + // otherwise the bomb is invisible until the timer hits the "normal" amount. + uint32_t randomTimer = (rand() % 150) + 10; + this->timer = randomTimer; + Audio_PlayActorSound2(thisx, NA_SE_PL_TAKE_OUT_SHIELD); + Actor_SetScale(thisx, 0.01f); + } this->flashSpeedScale = 7; Collider_InitCylinder(play, &this->bombCollider); Collider_InitJntSph(play, &this->explosionCollider); @@ -244,7 +254,8 @@ void EnBom_Update(Actor* thisx, PlayState* play2) { this->timer--; } - if (this->timer == 67) { + // With random bomb fuse timer, sound effect and scaling is already done on init. + if (this->timer == 67 && !GameInteractor_GetRandomBombFuseTimerActive()) { Audio_PlayActorSound2(thisx, NA_SE_PL_TAKE_OUT_SHIELD); Actor_SetScale(thisx, 0.01f); } diff --git a/soh/src/overlays/actors/ovl_En_Ik/z_en_ik.c b/soh/src/overlays/actors/ovl_En_Ik/z_en_ik.c index 82242f7ec..17825746d 100644 --- a/soh/src/overlays/actors/ovl_En_Ik/z_en_ik.c +++ b/soh/src/overlays/actors/ovl_En_Ik/z_en_ik.c @@ -230,12 +230,14 @@ void func_80A74398(Actor* thisx, PlayState* play) { Effect_Add(play, &this->blureIdx, EFFECT_BLURE1, 0, 0, &blureInit); func_80A74714(this); + uint8_t enemyRandoCCActive = CVarGetInteger("gRandomizedEnemies", 0) || CVarGetInteger("gCrowdControl", 0); + if (this->switchFlags != 0xFF) { // In vanilla gameplay, Iron Knuckles are despawned based on specific flags in specific scenarios. - // In Enemy Randomizer, this made the Iron Knuckles despawn when the same flag was set by other objects. + // In Enemy Randomizer and Crowd Control, this made the Iron Knuckles despawn when the same flag was set by other objects. // Instead, rely on the "Clear enemy room" flag when in Enemy Randomizer for Iron Knuckles that aren't Nabooru. - if ((Flags_GetSwitch(play, this->switchFlags) && !CVarGetInteger("gRandomizedEnemies", 0)) || - (thisx->params != 0 && Flags_GetClear(play, play->roomCtx.curRoom.num) && CVarGetInteger("gRandomizedEnemies", 0))) { + if ((Flags_GetSwitch(play, this->switchFlags) && !enemyRandoCCActive) || + (thisx->params != 0 && Flags_GetClear(play, play->roomCtx.curRoom.num) && enemyRandoCCActive)) { Actor_Kill(thisx); } } else if (thisx->params != 0 && Flags_GetClear(play, play->roomCtx.curRoom.num)) { @@ -656,9 +658,9 @@ void func_80A75A38(EnIk* this, PlayState* play) { } if (this->unk_2F9 == 0) { Item_DropCollectibleRandom(play, &this->actor, &this->actor.world.pos, 0xB0); - // Don't set flag when Iron Knuckle is spawned by Enemy Rando. - // Instead Iron Knuckles rely on the "clear room" flag when Enemy Rando is on. - if (this->switchFlags != 0xFF && !CVarGetInteger("gRandomizedEnemies",0)) { + // Don't set flag when Enemy Rando or CrowdControl are on. + // Instead Iron Knuckles rely on the "clear room" flag. + if (this->switchFlags != 0xFF && !CVarGetInteger("gRandomizedEnemies", 0) && !CVarGetInteger("gCrowdControl", 0)) { Flags_SetSwitch(play, this->switchFlags); } Actor_Kill(&this->actor); @@ -1481,8 +1483,8 @@ void EnIk_Init(Actor* thisx, PlayState* play) { func_80A780D0(this, play); } - // Immediately trigger Iron Knuckle for enemy randomizer - if (CVarGetInteger("gRandomizedEnemies", 0) && (thisx->params == 2 || thisx->params == 3)) { + // Immediately trigger Iron Knuckle for Enemy Rando and Crowd Control + if ((CVarGetInteger("gRandomizedEnemies", 0) || CVarGetInteger("gCrowdControl", 0)) && (thisx->params == 2 || thisx->params == 3)) { this->skelAnime.playSpeed = 1.0f; } } diff --git a/soh/src/overlays/actors/ovl_En_Rd/z_en_rd.c b/soh/src/overlays/actors/ovl_En_Rd/z_en_rd.c index 5ebf0a697..2bf2429fd 100644 --- a/soh/src/overlays/actors/ovl_En_Rd/z_en_rd.c +++ b/soh/src/overlays/actors/ovl_En_Rd/z_en_rd.c @@ -251,8 +251,8 @@ void func_80AE2744(EnRd* this, PlayState* play) { // Add a height check to redeads/gibdos freeze when Enemy Randomizer is on. // Without the height check, redeads/gibdos can freeze the player from insane distances in // vertical rooms (like the first room in Deku Tree), making these rooms nearly unplayable. - s8 enemyRando = CVarGetInteger("gRandomizedEnemies", 0); - if (!enemyRando || (enemyRando && this->actor.yDistToPlayer <= 100.0f && this->actor.yDistToPlayer >= -100.0f)) { + s8 enemyRandoCCActive = CVarGetInteger("gRandomizedEnemies", 0) || CVarGetInteger("gCrowdControl", 0); + if (!enemyRandoCCActive || (enemyRandoCCActive && this->actor.yDistToPlayer <= 100.0f && this->actor.yDistToPlayer >= -100.0f)) { if ((this->actor.params != 2) && (this->unk_305 == 0)) { func_80AE37BC(this); } else { @@ -663,8 +663,9 @@ void func_80AE3C98(EnRd* this, PlayState* play) { if (SkelAnime_Update(&this->skelAnime)) { if (this->unk_30C == 0) { + s8 enemyRandoCCActive = CVarGetInteger("gRandomizedEnemies", 0) || CVarGetInteger("gCrowdControl", 0); // Don't set this flag in Enemy Rando as it can overlap with other objects using the same flag. - if (!Flags_GetSwitch(play, this->unk_312 & 0x7F) && !CVarGetInteger("gRandomizedEnemies", 0)) { + if (!Flags_GetSwitch(play, this->unk_312 & 0x7F) && !enemyRandoCCActive) { Flags_SetSwitch(play, this->unk_312 & 0x7F); } if (this->unk_314 != 0) { diff --git a/soh/src/overlays/actors/ovl_player_actor/z_player.c b/soh/src/overlays/actors/ovl_player_actor/z_player.c index 7c56b7f6c..8b94847dd 100644 --- a/soh/src/overlays/actors/ovl_player_actor/z_player.c +++ b/soh/src/overlays/actors/ovl_player_actor/z_player.c @@ -8559,7 +8559,8 @@ void func_8084411C(Player* this, PlayState* play) { func_80843E14(this, NA_SE_VO_LI_FALL_L); } - if ((this->actor.bgCheckFlags & 0x200) && !(this->stateFlags2 & PLAYER_STATE2_19) && + if (!GameInteractor_GetDisableLedgeGrabsActive() && (this->actor.bgCheckFlags & 0x200) && + !(this->stateFlags2 & PLAYER_STATE2_19) && !(this->stateFlags1 & (PLAYER_STATE1_11 | PLAYER_STATE1_27)) && (this->linearVelocity > 0.0f)) { if ((this->wallHeight >= 150.0f) && (this->unk_84B[this->unk_846] == 0)) { func_8083EC18(this, play, D_808535F0); @@ -8655,8 +8656,10 @@ void func_80844708(Player* this, PlayState* play) { func_8083A060(this, play); } } else { + f32 rand = Rand_ZeroOne(); + uint8_t randomBonk = (rand <= .05) && GameInteractor_GetRandomBonksActive(); if (this->linearVelocity >= 7.0f) { - if (((this->actor.bgCheckFlags & 0x200) && (D_8085360C < 0x2000)) || + if (randomBonk || ((this->actor.bgCheckFlags & 0x200) && (D_8085360C < 0x2000)) || ((this->cylinder.base.ocFlags1 & OC1_HIT) && (cylinderOc = this->cylinder.base.oc, ((cylinderOc->id == ACTOR_EN_WOOD02) && @@ -10665,7 +10668,7 @@ void Player_UpdateCommon(Player* this, PlayState* play, Input* input) { if (!(this->skelAnime.moveFlags & 0x80)) { if (((this->actor.bgCheckFlags & 1) && (D_808535E4 == 5) && (this->currentBoots != PLAYER_BOOTS_IRON)) || - ((this->currentBoots == PLAYER_BOOTS_HOVER) && + ((this->currentBoots == PLAYER_BOOTS_HOVER || GameInteractor_GetSlipperyFloorActive()) && !(this->stateFlags1 & (PLAYER_STATE1_27 | PLAYER_STATE1_29)))) { f32 sp70 = this->linearVelocity; s16 sp6E = this->currentYaw; @@ -11003,42 +11006,67 @@ void Player_Update(Actor* thisx, PlayState* play) { MREG(54) = this->actor.world.pos.z; MREG(55) = this->actor.world.rot.y; - switch (GameInteractor_GetLinkSize()) { - case GI_LINK_SIZE_RESET: - this->actor.scale.x = 0.01f; - this->actor.scale.y = 0.01f; - this->actor.scale.z = 0.01f; - GameInteractor_SetLinkSize(GI_LINK_SIZE_NORMAL); - break; - 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: - break; + // Make Link normal size when going through doors and crawlspaces and when climbing ladders. + // Otherwise Link can glitch out, being in unloaded rooms or falling OoB. + if (this->stateFlags1 & PLAYER_STATE1_21 || this->stateFlags1 & PLAYER_STATE1_29 || + this->stateFlags2 & PLAYER_STATE2_CRAWLING) { + this->actor.scale.x = 0.01f; + this->actor.scale.y = 0.01f; + this->actor.scale.z = 0.01f; + } else { + switch (GameInteractor_GetLinkSize()) { + case GI_LINK_SIZE_RESET: + this->actor.scale.x = 0.01f; + this->actor.scale.y = 0.01f; + this->actor.scale.z = 0.01f; + GameInteractor_SetLinkSize(GI_LINK_SIZE_NORMAL); + break; + 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_SQUISHED: + this->actor.scale.x = 0.015f; + this->actor.scale.y = 0.001f; + this->actor.scale.z = 0.015f; + break; + case GI_LINK_SIZE_NORMAL: + default: + break; + } } - 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; + // Don't apply gravity when Link is in water, otherwise + // it makes him sink instead of float. + if (!(this->stateFlags1 & PLAYER_STATE1_27)) { + 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; + } + } + + if (GameInteractor_GetRandomWindActive()) { + Player* player = GET_PLAYER(play); + player->windSpeed = 3.0f; + // Play fan sound (too annoying) + //func_8002F974(&player->actor, NA_SE_EV_WIND_TRAP - SFX_FLAG); } GameInteractor_ExecuteOnPlayerUpdate();