CrowdControl V2: Effect Overhaul (#2433)

This commit is contained in:
aMannus 2023-03-13 11:37:50 +01:00 committed by GitHub
parent 76e99ffe19
commit 866882e4c6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 2183 additions and 525 deletions

View File

@ -141,6 +141,7 @@ list(FILTER soh__Enhancements EXCLUDE REGEX "soh/Enhancements/gfx.*")
# handle crowd control removals # 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.cs")
list(REMOVE_ITEM soh__Enhancements "soh/Enhancements/crowd-control/soh.ccpak")
if (!BUILD_CROWD_CONTROL) if (!BUILD_CROWD_CONTROL)
list(FILTER soh__Enhancements EXCLUDE REGEX "soh/Enhancements/crowd-control/*") list(FILTER soh__Enhancements EXCLUDE REGEX "soh/Enhancements/crowd-control/*")
endif() endif()

View File

@ -49,8 +49,6 @@ void ResourceMgr_UnpatchGfxByName(const char* path, const char* patchName);
u8 Randomizer_GetSettingValue(RandomizerSettingKey randoSettingKey); 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 // 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` // every item in a group at once. If you are looking for tabs they are rendered manually in ImGui in `DrawCosmeticsEditor`
typedef enum { 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 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 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"); static CosmeticOption& linkGoronTunic = cosmeticOptions.at("Link_GoronTunic");
if (manualChange || CVarGetInteger(linkGoronTunic.rainbowCvar, 0)) { if (manualChange || CVarGetInteger(linkGoronTunic.rainbowCvar, 0)) {
static Color_RGBA8 defaultColor = {linkGoronTunic.defaultColor.x, linkGoronTunic.defaultColor.y, linkGoronTunic.defaultColor.z, linkGoronTunic.defaultColor.w}; 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() { void CosmeticsEditor_RandomizeAll() {
for (auto& [id, cosmeticOption] : cosmeticOptions) { for (auto& [id, cosmeticOption] : cosmeticOptions) {
if (!CVarGetInteger(cosmeticOption.lockedCvar, 0)) { if (!CVarGetInteger(cosmeticOption.lockedCvar, 0) &&
(!cosmeticOption.advancedOption || CVarGetInteger("gCosmetics.AdvancedMode", 0))) {
RandomizeColor(cosmeticOption); RandomizeColor(cosmeticOption);
} }
} }

View File

@ -27,3 +27,4 @@ void InitCosmeticsEditor();//Init the menu itself
ImVec4 GetRandomValue(int MaximumPossible); ImVec4 GetRandomValue(int MaximumPossible);
void CosmeticsEditor_RandomizeAll(); void CosmeticsEditor_RandomizeAll();
void CosmeticsEditor_ResetAll(); void CosmeticsEditor_ResetAll();
void ApplyOrResetCustomGfxPatches(bool manualChange = true);

View File

@ -1,6 +1,7 @@
#ifdef ENABLE_CROWD_CONTROL #ifdef ENABLE_CROWD_CONTROL
#include "CrowdControl.h" #include "CrowdControl.h"
#include "CrowdControlTypes.h"
#include <libultraship/bridge.h> #include <libultraship/bridge.h>
#include <Console.h> #include <Console.h>
#include <ImGuiImpl.h> #include <ImGuiImpl.h>
@ -17,65 +18,6 @@ extern "C" {
extern PlayState* gPlayState; 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() { void CrowdControl::Init() {
SDLNet_Init(); SDLNet_Init();
} }
@ -241,8 +183,6 @@ void CrowdControl::ProcessActiveEffects() {
SPDLOG_TRACE("[CrowdControl] Ending Process thread..."); SPDLOG_TRACE("[CrowdControl] Ending Process thread...");
} }
// MARK: - Helpers
void CrowdControl::EmitMessage(TCPsocket socket, uint32_t eventId, long timeRemaining, EffectResult status) { void CrowdControl::EmitMessage(TCPsocket socket, uint32_t eventId, long timeRemaining, EffectResult status) {
nlohmann::json payload; 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); 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<std::string>();
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) { CrowdControl::EffectResult CrowdControl::ExecuteEffect(Effect* effect) {
GameInteractionEffectQueryResult giResult; GameInteractionEffectQueryResult giResult;
if (effect->category == EFFECT_CAT_SPAWN_ENEMY) { if (effect->category == kEffectCatSpawnEnemy) {
giResult = GameInteractor::RawAction::SpawnEnemyWithOffset(effect->value[0], effect->value[1]); 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 { } else {
giResult = GameInteractor::ApplyEffect(effect->giEffect); 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. /// Checks if effect can be applied -- should not be used to check for spawn enemy effects.
CrowdControl::EffectResult CrowdControl::CanApplyEffect(Effect* effect) { 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); GameInteractionEffectQueryResult giResult = GameInteractor::CanApplyEffect(effect->giEffect);
return TranslateGiEnum(giResult); return TranslateGiEnum(giResult);
@ -486,4 +230,598 @@ CrowdControl::EffectResult CrowdControl::TranslateGiEnum(GameInteractionEffectQu
return result; 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<std::string>();
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 #endif

View File

@ -63,11 +63,10 @@ class CrowdControl {
typedef struct Effect { typedef struct Effect {
uint32_t id; uint32_t id;
uint32_t value[2]; uint32_t spawnParams[2];
std::string category; uint32_t category = 0;
long timeRemaining; long timeRemaining;
GameInteractionEffectBase *giEffect; GameInteractionEffectBase *giEffect;
int32_t paramMultiplier = 1;
// Metadata used while executing (only for timed effects) // Metadata used while executing (only for timed effects)
bool isPaused; bool isPaused;

View File

@ -0,0 +1,310 @@
#pragma once
#include <unordered_map>
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<std::string, CCStringEnumValues> 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 },
};

Binary file not shown.

View File

@ -15,76 +15,193 @@ public class SoH : SimpleTCPPack
public override Game Game { get; } = new Game(90, "Ship of Harkinian", "SoH", "PC", ConnectorType.SimpleTCPConnector); public override Game Game { get; } = new Game(90, "Ship of Harkinian", "SoH", "PC", ConnectorType.SimpleTCPConnector);
private Dictionary<string, string> _enemyType = new Dictionary<string, string>() public override List<Effect> Effects => new List<Effect>
{ {
{"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<Effect> Effects // Spawn Enemies and Objects
{ new Effect("Spawn Enemies/Objects", "spawn_stuff", ItemKind.Folder),
get
{
List<Effect> effects = new List<Effect>
{
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")));
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 //Slider ranges need to be defined
public override List<ItemType> ItemTypes => new List<ItemType> public override List<ItemType> ItemTypes => new List<ItemType>
{ {
new ItemType("Rupees", "rupees999", ItemType.Subtype.Slider, "{\"min\":1,\"max\":999}"), new ItemType("Rupees", "rupees999", ItemType.Subtype.Slider, "{\"min\":1,\"max\":999}"),
new ItemType("Health", "health20", ItemType.Subtype.Slider, "{\"min\":1,\"max\":20}"), 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("Ammo", "ammo30", ItemType.Subtype.Slider, "{\"min\":1,\"max\":30}"),
new ItemType("Knockback Strength", "knockbackstrength", ItemType.Subtype.Slider, "{\"min\":1,\"max\":3}")
}; };
} }

View File

@ -82,21 +82,9 @@ static bool ActorSpawnHandler(std::shared_ptr<Ship::Console> Console, const std:
return CMD_SUCCESS; return CMD_SUCCESS;
} }
static bool GiveDekuShieldHandler(std::shared_ptr<Ship::Console> Console, const std::vector<std::string>&) {
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<Ship::Console> Console, const std::vector<std::string>&) { static bool KillPlayerHandler(std::shared_ptr<Ship::Console> Console, const std::vector<std::string>&) {
GameInteractionEffectBase* effect = new GameInteractionEffect::SetPlayerHealth(); GameInteractionEffectBase* effect = new GameInteractionEffect::SetPlayerHealth();
effect->parameter = 0; effect->parameters[0] = 0;
GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect); GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect);
if (result == GameInteractionEffectQueryResult::Possible) { if (result == GameInteractionEffectQueryResult::Possible) {
SohImGui::GetConsole()->SendInfoMessage("[SOH] You've met with a terrible fate, haven't you?"); SohImGui::GetConsole()->SendInfoMessage("[SOH] You've met with a terrible fate, haven't you?");
@ -127,7 +115,7 @@ static bool SetPlayerHealthHandler(std::shared_ptr<Ship::Console> Console, const
} }
GameInteractionEffectBase* effect = new GameInteractionEffect::SetPlayerHealth(); GameInteractionEffectBase* effect = new GameInteractionEffect::SetPlayerHealth();
effect->parameter = health; effect->parameters[0] = health;
GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect); GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect);
if (result == GameInteractionEffectQueryResult::Possible) { if (result == GameInteractionEffectQueryResult::Possible) {
SohImGui::GetConsole()->SendInfoMessage("[SOH] Player health updated to %d", health); SohImGui::GetConsole()->SendInfoMessage("[SOH] Player health updated to %d", health);
@ -210,48 +198,90 @@ static bool ResetHandler(std::shared_ptr<Ship::Console> Console, std::vector<std
return CMD_SUCCESS; return CMD_SUCCESS;
} }
const static std::map<std::string, uint16_t> ammoItems{ const static std::map<std::string, uint16_t> ammoItems{
{ "sticks", ITEM_STICK }, { "deku_sticks", ITEM_STICK }, { "sticks", ITEM_STICK }, { "nuts", ITEM_NUT },
{ "nuts", ITEM_NUT }, { "deku_nuts", ITEM_NUT }, { "bombs", ITEM_BOMB }, { "seeds", ITEM_SLINGSHOT },
{ "bombs", ITEM_BOMB }, { "arrows", ITEM_BOW }, { "arrows", ITEM_BOW }, { "bombchus", ITEM_BOMBCHU },
{ "bombchus", ITEM_BOMBCHU }, { "chus", ITEM_BOMBCHU }, { "beans", ITEM_BEAN }
{ "beans", ITEM_BEAN },
{ "seeds", ITEM_SLINGSHOT }, { "deku_seeds", ITEM_SLINGSHOT },
{ "magic_beans", ITEM_BEAN },
}; };
static bool AmmoHandler(std::shared_ptr<Ship::Console> Console, const std::vector<std::string>& args) { static bool AddAmmoHandler(std::shared_ptr<Ship::Console> Console, const std::vector<std::string>& args) {
if (args.size() != 3) { if (args.size() != 3) {
SohImGui::GetConsole()->SendErrorMessage("[SOH] Unexpected arguments passed"); SohImGui::GetConsole()->SendErrorMessage("[SOH] Unexpected arguments passed");
return CMD_FAILED; return CMD_FAILED;
} }
int amount;
int count;
try { try {
count = std::stoi(args[2]); amount = std::stoi(args[2]);
} catch (std::invalid_argument const& ex) { } catch (std::invalid_argument const& ex) {
SohImGui::GetConsole()->SendErrorMessage("Ammo count must be an integer"); SohImGui::GetConsole()->SendErrorMessage("Ammo count must be an integer");
return CMD_FAILED; return CMD_FAILED;
} }
if (count < 0) { if (amount < 0) {
SohImGui::GetConsole()->SendErrorMessage("Ammo count must be positive"); SohImGui::GetConsole()->SendErrorMessage("Ammo count must be positive");
return CMD_FAILED; return CMD_FAILED;
} }
const auto& it = ammoItems.find(args[1]); const auto& it = ammoItems.find(args[1]);
if (it == ammoItems.end()) { 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; return CMD_FAILED;
} }
// I dont think you can do OOB with just this GameInteractionEffectBase* effect = new GameInteractionEffect::AddOrTakeAmmo();
AMMO(it->second) = count; effect->parameters[0] = amount;
effect->parameters[1] = it->second;
GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect);
//To use a change by uncomment this if (result == GameInteractionEffectQueryResult::Possible) {
//Inventory_ChangeAmmo(it->second, count); 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<Ship::Console> Console, const std::vector<std::string>& 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<std::string, uint16_t> bottleItems{ const static std::map<std::string, uint16_t> bottleItems{
@ -561,7 +591,7 @@ static bool GiantLinkHandler(std::shared_ptr<Ship::Console> Console, const std::
} }
GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyLinkSize(); GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyLinkSize();
effect->parameter = GI_LINK_SIZE_GIANT; effect->parameters[0] = GI_LINK_SIZE_GIANT;
GameInteractionEffectQueryResult result = GameInteractionEffectQueryResult result =
state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect); state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect);
if (result == GameInteractionEffectQueryResult::Possible) { if (result == GameInteractionEffectQueryResult::Possible) {
@ -589,7 +619,7 @@ static bool MinishLinkHandler(std::shared_ptr<Ship::Console> Console, const std:
} }
GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyLinkSize(); GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyLinkSize();
effect->parameter = GI_LINK_SIZE_MINISH; effect->parameters[0] = GI_LINK_SIZE_MINISH;
GameInteractionEffectQueryResult result = GameInteractionEffectQueryResult result =
state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect); state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect);
if (result == GameInteractionEffectQueryResult::Possible) { if (result == GameInteractionEffectQueryResult::Possible) {
@ -622,7 +652,7 @@ static bool AddHeartContainerHandler(std::shared_ptr<Ship::Console> Console, con
} }
GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyHeartContainers(); GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyHeartContainers();
effect->parameter = hearts; effect->parameters[0] = hearts;
GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect); GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect);
if (result == GameInteractionEffectQueryResult::Possible) { if (result == GameInteractionEffectQueryResult::Possible) {
SohImGui::GetConsole()->SendInfoMessage("[SOH] Added %d heart containers", hearts); SohImGui::GetConsole()->SendInfoMessage("[SOH] Added %d heart containers", hearts);
@ -653,7 +683,7 @@ static bool RemoveHeartContainerHandler(std::shared_ptr<Ship::Console> Console,
} }
GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyHeartContainers(); GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyHeartContainers();
effect->parameter = -hearts; effect->parameters[0] = -hearts;
GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect); GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect);
if (result == GameInteractionEffectQueryResult::Possible) { if (result == GameInteractionEffectQueryResult::Possible) {
SohImGui::GetConsole()->SendInfoMessage("[SOH] Removed %d heart containers", hearts); SohImGui::GetConsole()->SendInfoMessage("[SOH] Removed %d heart containers", hearts);
@ -673,7 +703,7 @@ static bool GravityHandler(std::shared_ptr<Ship::Console> Console, const std::ve
GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyGravity(); GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyGravity();
try { 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) { } catch (std::invalid_argument const& ex) {
SohImGui::GetConsole()->SendErrorMessage("[SOH] Gravity value must be a number."); SohImGui::GetConsole()->SendErrorMessage("[SOH] Gravity value must be a number.");
return CMD_FAILED; return CMD_FAILED;
@ -738,7 +768,7 @@ static bool DefenseModifierHandler(std::shared_ptr<Ship::Console> Console, const
GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyDefenseModifier(); GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyDefenseModifier();
try { try {
effect->parameter = std::stoi(args[1], nullptr, 10); effect->parameters[0] = std::stoi(args[1], nullptr, 10);
} catch (std::invalid_argument const& ex) { } catch (std::invalid_argument const& ex) {
SohImGui::GetConsole()->SendErrorMessage("[SOH] Defense modifier value must be a number."); SohImGui::GetConsole()->SendErrorMessage("[SOH] Defense modifier value must be a number.");
return CMD_FAILED; return CMD_FAILED;
@ -746,7 +776,7 @@ static bool DefenseModifierHandler(std::shared_ptr<Ship::Console> Console, const
GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect); GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect);
if (result == GameInteractionEffectQueryResult::Possible) { 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; return CMD_SUCCESS;
} else { } else {
SohImGui::GetConsole()->SendInfoMessage("[SOH] Command failed: Could not set defense modifier."); SohImGui::GetConsole()->SendInfoMessage("[SOH] Command failed: Could not set defense modifier.");
@ -768,7 +798,7 @@ static bool DamageHandler(std::shared_ptr<Ship::Console> Console, const std::vec
return CMD_FAILED; return CMD_FAILED;
} }
effect->parameter = -value; effect->parameters[0] = -value;
} catch (std::invalid_argument const& ex) { } catch (std::invalid_argument const& ex) {
SohImGui::GetConsole()->SendErrorMessage("[SOH] Damage value must be a number."); SohImGui::GetConsole()->SendErrorMessage("[SOH] Damage value must be a number.");
return CMD_FAILED; return CMD_FAILED;
@ -798,7 +828,7 @@ static bool HealHandler(std::shared_ptr<Ship::Console> Console, const std::vecto
return CMD_FAILED; return CMD_FAILED;
} }
effect->parameter = value; effect->parameters[0] = value;
} catch (std::invalid_argument const& ex) { } catch (std::invalid_argument const& ex) {
SohImGui::GetConsole()->SendErrorMessage("[SOH] Damage value must be a number."); SohImGui::GetConsole()->SendErrorMessage("[SOH] Damage value must be a number.");
return CMD_FAILED; return CMD_FAILED;
@ -939,7 +969,7 @@ static bool PaperLinkHandler(std::shared_ptr<Ship::Console> Console, const std::
} }
GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyLinkSize(); GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyLinkSize();
effect->parameter = GI_LINK_SIZE_PAPER; effect->parameters[0] = GI_LINK_SIZE_PAPER;
GameInteractionEffectQueryResult result = GameInteractionEffectQueryResult result =
state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect); state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect);
@ -1018,7 +1048,7 @@ static bool UpdateRupeesHandler(std::shared_ptr<Ship::Console> Console, const st
GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyRupees(); GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyRupees();
try { try {
effect->parameter = std::stoi(args[1], nullptr, 10); effect->parameters[0] = std::stoi(args[1], nullptr, 10);
} catch (std::invalid_argument const& ex) { } catch (std::invalid_argument const& ex) {
SohImGui::GetConsole()->SendErrorMessage("[SOH] Rupee value must be a number."); SohImGui::GetConsole()->SendErrorMessage("[SOH] Rupee value must be a number.");
return CMD_FAILED; return CMD_FAILED;
@ -1042,7 +1072,7 @@ static bool SpeedModifierHandler(std::shared_ptr<Ship::Console> Console, const s
GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyRunSpeedModifier(); GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyRunSpeedModifier();
try { try {
effect->parameter = std::stoi(args[1], nullptr, 10); effect->parameters[0] = std::stoi(args[1], nullptr, 10);
} catch (std::invalid_argument const& ex) { } catch (std::invalid_argument const& ex) {
SohImGui::GetConsole()->SendErrorMessage("[SOH] Speed modifier value must be a number."); SohImGui::GetConsole()->SendErrorMessage("[SOH] Speed modifier value must be a number.");
return CMD_FAILED; return CMD_FAILED;
@ -1077,11 +1107,11 @@ static bool BootsHandler(std::shared_ptr<Ship::Console> Console, const std::vect
} }
GameInteractionEffectBase* effect = new GameInteractionEffect::ForceEquipBoots(); GameInteractionEffectBase* effect = new GameInteractionEffect::ForceEquipBoots();
effect->parameter = it->second; effect->parameters[0] = it->second;
GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect); GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect);
if (result == GameInteractionEffectQueryResult::Possible) { if (result == GameInteractionEffectQueryResult::Possible) {
SohImGui::GetConsole()->SendInfoMessage("[SOH] Boots updated"); SohImGui::GetConsole()->SendInfoMessage("[SOH] Boots updated.");
return CMD_SUCCESS; return CMD_SUCCESS;
} else { } else {
SohImGui::GetConsole()->SendInfoMessage("[SOH] Command failed: Could not update boots."); SohImGui::GetConsole()->SendInfoMessage("[SOH] Command failed: Could not update boots.");
@ -1089,6 +1119,62 @@ static bool BootsHandler(std::shared_ptr<Ship::Console> Console, const std::vect
} }
} }
const static std::map<std::string, uint16_t> shields {
{ "deku", ITEM_SHIELD_DEKU },
{ "hylian", ITEM_SHIELD_HYLIAN },
{ "mirror", ITEM_SHIELD_MIRROR },
};
static bool GiveShieldHandler(std::shared_ptr<Ship::Console> Console, const std::vector<std::string>& 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<Ship::Console> Console, const std::vector<std::string>& 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<Ship::Console> Console, const std::vector<std::string>& args) { static bool KnockbackHandler(std::shared_ptr<Ship::Console> Console, const std::vector<std::string>& args) {
if (args.size() != 2) { if (args.size() != 2) {
SohImGui::GetConsole()->SendErrorMessage("[SOH] Unexpected arguments passed"); SohImGui::GetConsole()->SendErrorMessage("[SOH] Unexpected arguments passed");
@ -1103,7 +1189,7 @@ static bool KnockbackHandler(std::shared_ptr<Ship::Console> Console, const std::
return CMD_FAILED; return CMD_FAILED;
} }
effect->parameter = value; effect->parameters[0] = value;
} catch (std::invalid_argument const& ex) { } catch (std::invalid_argument const& ex) {
SohImGui::GetConsole()->SendErrorMessage("[SOH] Knockback value must be a number."); SohImGui::GetConsole()->SendErrorMessage("[SOH] Knockback value must be a number.");
return CMD_FAILED; return CMD_FAILED;
@ -1146,8 +1232,7 @@ static bool BurnHandler(std::shared_ptr<Ship::Console> Console, const std::vecto
} }
static bool CuccoStormHandler(std::shared_ptr<Ship::Console> Console, const std::vector<std::string>& args) { static bool CuccoStormHandler(std::shared_ptr<Ship::Console> Console, const std::vector<std::string>& args) {
GameInteractionEffectBase* effect = new GameInteractionEffect::SpawnCuccoStorm(); GameInteractionEffectQueryResult result = GameInteractor::RawAction::SpawnActor(ACTOR_EN_NIW, 0);
GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect);
if (result == GameInteractionEffectQueryResult::Possible) { if (result == GameInteractionEffectQueryResult::Possible) {
SohImGui::GetConsole()->SendInfoMessage("[SOH] Spawned cucco storm"); SohImGui::GetConsole()->SendInfoMessage("[SOH] Spawned cucco storm");
@ -1344,8 +1429,6 @@ void DebugConsole_Init(void) {
{ "Item ID", Ship::ArgumentType::NUMBER } { "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 }, CMD_REGISTER("spawn", { ActorSpawnHandler, "Spawn an actor.", { { "actor_id", Ship::ArgumentType::NUMBER },
{ "data", Ship::ArgumentType::NUMBER }, { "data", Ship::ArgumentType::NUMBER },
{ "x", Ship::ArgumentType::PLAYER_POS, true }, { "x", Ship::ArgumentType::PLAYER_POS, true },
@ -1371,8 +1454,13 @@ void DebugConsole_Init(void) {
{ "varName", Ship::ArgumentType::TEXT } { "varName", Ship::ArgumentType::TEXT }
}}); }});
CMD_REGISTER("ammo", { AmmoHandler, "Changes ammo of an item.", { CMD_REGISTER("addammo", { AddAmmoHandler, "Adds ammo of an item.", {
{ "item", Ship::ArgumentType::TEXT }, { "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 } { "count", Ship::ArgumentType::NUMBER }
}}); }});
@ -1464,7 +1552,15 @@ void DebugConsole_Init(void) {
}}); }});
CMD_REGISTER("boots", { BootsHandler, "Activates boots.", { 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.", { CMD_REGISTER("knockback", { KnockbackHandler, "Knocks Link back.", {

View File

@ -52,8 +52,8 @@ namespace GameInteractionEffect {
if (!GameInteractor::IsSaveLoaded()) { if (!GameInteractor::IsSaveLoaded()) {
return GameInteractionEffectQueryResult::TemporarilyNotPossible; return GameInteractionEffectQueryResult::TemporarilyNotPossible;
} else if ( } else if (
(parameter > 0 && (gSaveContext.healthCapacity + (parameter * 0x10) > 0x140)) || (parameters[0] > 0 && (gSaveContext.healthCapacity + (parameters[0] * 0x10) > 0x140)) ||
(parameter < 0 && (gSaveContext.healthCapacity + (parameter * 0x10) < 0x10)) (parameters[0] < 0 && (gSaveContext.healthCapacity + (parameters[0] * 0x10) < 0x10))
) { ) {
return GameInteractionEffectQueryResult::NotPossible; return GameInteractionEffectQueryResult::NotPossible;
} }
@ -62,7 +62,7 @@ namespace GameInteractionEffect {
} }
void ModifyHeartContainers::_Apply() { void ModifyHeartContainers::_Apply() {
GameInteractor::RawAction::AddOrRemoveHealthContainers(parameter); GameInteractor::RawAction::AddOrRemoveHealthContainers(parameters[0]);
} }
// MARK: - FillMagic // MARK: - FillMagic
@ -98,8 +98,8 @@ namespace GameInteractionEffect {
if (!GameInteractor::IsSaveLoaded()) { if (!GameInteractor::IsSaveLoaded()) {
return GameInteractionEffectQueryResult::TemporarilyNotPossible; return GameInteractionEffectQueryResult::TemporarilyNotPossible;
} else if ( } else if (
(parameter < 0 && gSaveContext.rupees <= 0) || (parameters[0] < 0 && gSaveContext.rupees <= 0) ||
(parameter > 0 && gSaveContext.rupees >= CUR_CAPACITY(UPG_WALLET)) (parameters[0] > 0 && gSaveContext.rupees >= CUR_CAPACITY(UPG_WALLET))
) { ) {
return GameInteractionEffectQueryResult::NotPossible; return GameInteractionEffectQueryResult::NotPossible;
} else { } else {
@ -107,7 +107,7 @@ namespace GameInteractionEffect {
} }
} }
void ModifyRupees::_Apply() { void ModifyRupees::_Apply() {
Rupees_ChangeBy(parameter); Rupees_ChangeBy(parameters[0]);
} }
// MARK: - NoUI // MARK: - NoUI
@ -134,7 +134,7 @@ namespace GameInteractionEffect {
} }
} }
void ModifyGravity::_Apply() { void ModifyGravity::_Apply() {
GameInteractor::State::GravityLevel = (GIGravityLevel)parameter; GameInteractor::State::GravityLevel = (GIGravityLevel)parameters[0];
} }
void ModifyGravity::_Remove() { void ModifyGravity::_Remove() {
GameInteractor::State::GravityLevel = GI_GRAVITY_LEVEL_NORMAL; GameInteractor::State::GravityLevel = GI_GRAVITY_LEVEL_NORMAL;
@ -145,8 +145,8 @@ namespace GameInteractionEffect {
if (!GameInteractor::IsSaveLoaded()) { if (!GameInteractor::IsSaveLoaded()) {
return GameInteractionEffectQueryResult::TemporarilyNotPossible; return GameInteractionEffectQueryResult::TemporarilyNotPossible;
} else if ( } else if (
(parameter > 0 && gSaveContext.health == gSaveContext.healthCapacity) (parameters[0] > 0 && gSaveContext.health == gSaveContext.healthCapacity)
|| (parameter < 0 && (gSaveContext.health + (16 * parameter) <= 0)) || (parameters[0] < 0 && (gSaveContext.health + (16 * parameters[0]) <= 0))
) { ) {
return GameInteractionEffectQueryResult::NotPossible; return GameInteractionEffectQueryResult::NotPossible;
} }
@ -154,7 +154,7 @@ namespace GameInteractionEffect {
return GameInteractionEffectQueryResult::Possible; return GameInteractionEffectQueryResult::Possible;
} }
void ModifyHealth::_Apply() { void ModifyHealth::_Apply() {
GameInteractor::RawAction::HealOrDamagePlayer(parameter); GameInteractor::RawAction::HealOrDamagePlayer(parameters[0]);
} }
// MARK: - SetPlayerHealth // MARK: - SetPlayerHealth
@ -167,7 +167,7 @@ namespace GameInteractionEffect {
} }
} }
void SetPlayerHealth::_Apply() { void SetPlayerHealth::_Apply() {
GameInteractor::RawAction::SetPlayerHealth(parameter); GameInteractor::RawAction::SetPlayerHealth(parameters[0]);
} }
// MARK: - FreezePlayer // MARK: - FreezePlayer
@ -218,7 +218,7 @@ namespace GameInteractionEffect {
} }
} }
void KnockbackPlayer::_Apply() { void KnockbackPlayer::_Apply() {
GameInteractor::RawAction::KnockbackPlayer(parameter); GameInteractor::RawAction::KnockbackPlayer(parameters[0]);
} }
// MARK: - ModifyLinkSize // MARK: - ModifyLinkSize
@ -230,7 +230,7 @@ namespace GameInteractionEffect {
} }
} }
void ModifyLinkSize::_Apply() { void ModifyLinkSize::_Apply() {
GameInteractor::State::LinkSize = (GILinkSize)parameter; GameInteractor::State::LinkSize = (GILinkSize)parameters[0];
} }
void ModifyLinkSize::_Remove() { void ModifyLinkSize::_Remove() {
GameInteractor::State::LinkSize = GI_LINK_SIZE_RESET; GameInteractor::State::LinkSize = GI_LINK_SIZE_RESET;
@ -320,7 +320,7 @@ namespace GameInteractionEffect {
} }
} }
void ForceEquipBoots::_Apply() { void ForceEquipBoots::_Apply() {
GameInteractor::RawAction::ForceEquipBoots(parameter); GameInteractor::RawAction::ForceEquipBoots(parameters[0]);
} }
void ForceEquipBoots::_Remove() { void ForceEquipBoots::_Remove() {
GameInteractor::RawAction::ForceEquipBoots(PLAYER_BOOTS_KOKIRI); GameInteractor::RawAction::ForceEquipBoots(PLAYER_BOOTS_KOKIRI);
@ -335,7 +335,7 @@ namespace GameInteractionEffect {
} }
} }
void ModifyRunSpeedModifier::_Apply() { void ModifyRunSpeedModifier::_Apply() {
GameInteractor::State::RunSpeedModifier = parameter; GameInteractor::State::RunSpeedModifier = parameters[0];
} }
void ModifyRunSpeedModifier::_Remove() { void ModifyRunSpeedModifier::_Remove() {
GameInteractor::State::RunSpeedModifier = 0; GameInteractor::State::RunSpeedModifier = 0;
@ -356,7 +356,7 @@ namespace GameInteractionEffect {
GameInteractor::State::OneHitKOActive = 0; GameInteractor::State::OneHitKOActive = 0;
} }
// MARK: - IncreaseDamageTaken // MARK: - ModifyDefenseModifier
GameInteractionEffectQueryResult ModifyDefenseModifier::CanBeApplied() { GameInteractionEffectQueryResult ModifyDefenseModifier::CanBeApplied() {
if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) {
return GameInteractionEffectQueryResult::TemporarilyNotPossible; return GameInteractionEffectQueryResult::TemporarilyNotPossible;
@ -365,35 +365,229 @@ namespace GameInteractionEffect {
} }
} }
void ModifyDefenseModifier::_Apply() { void ModifyDefenseModifier::_Apply() {
GameInteractor::State::DefenseModifier = parameter; GameInteractor::State::DefenseModifier = parameters[0];
} }
void ModifyDefenseModifier::_Remove() { void ModifyDefenseModifier::_Remove() {
GameInteractor::State::DefenseModifier = 0; GameInteractor::State::DefenseModifier = 0;
} }
// MARK: - GiveDekuShield // MARK: - GiveOrTakeShield
GameInteractionEffectQueryResult GiveDekuShield::CanBeApplied() { GameInteractionEffectQueryResult GiveOrTakeShield::CanBeApplied() {
if (!GameInteractor::IsSaveLoaded()) { if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) {
return GameInteractionEffectQueryResult::TemporarilyNotPossible; 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; return GameInteractionEffectQueryResult::NotPossible;
} else { } else {
return GameInteractionEffectQueryResult::Possible; return GameInteractionEffectQueryResult::Possible;
} }
} }
void GiveDekuShield::_Apply() { void GiveOrTakeShield::_Apply() {
GameInteractor::RawAction::GiveDekuShield(); GameInteractor::RawAction::GiveOrTakeShield(parameters[0]);
} }
// MARK: - SpawnCuccoStorm // MARK: - TeleportPlayer
GameInteractionEffectQueryResult SpawnCuccoStorm::CanBeApplied() { GameInteractionEffectQueryResult TeleportPlayer::CanBeApplied() {
if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) { if (!GameInteractor::IsSaveLoaded() || GameInteractor::IsGameplayPaused()) {
return GameInteractionEffectQueryResult::TemporarilyNotPossible; return GameInteractionEffectQueryResult::TemporarilyNotPossible;
} else { } else {
return GameInteractionEffectQueryResult::Possible; return GameInteractionEffectQueryResult::Possible;
} }
} }
void SpawnCuccoStorm::_Apply() { void TeleportPlayer::_Apply() {
GameInteractor::RawAction::SpawnCuccoStorm(); 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;
} }
} }

View File

@ -18,8 +18,9 @@ public:
virtual GameInteractionEffectQueryResult CanBeRemoved(); virtual GameInteractionEffectQueryResult CanBeRemoved();
GameInteractionEffectQueryResult Apply(); GameInteractionEffectQueryResult Apply();
GameInteractionEffectQueryResult Remove(); GameInteractionEffectQueryResult Remove();
int32_t parameter; int32_t parameters[2];
protected:
protected:
virtual void _Apply() = 0; virtual void _Apply() = 0;
virtual void _Remove() {}; virtual void _Remove() {};
}; };
@ -147,15 +148,92 @@ namespace GameInteractionEffect {
void _Remove() override; void _Remove() override;
}; };
class GiveDekuShield: public GameInteractionEffectBase { class GiveOrTakeShield: public GameInteractionEffectBase {
GameInteractionEffectQueryResult CanBeApplied() override; GameInteractionEffectQueryResult CanBeApplied() override;
void _Apply() override; void _Apply() override;
}; };
class SpawnCuccoStorm: public GameInteractionEffectBase { class TeleportPlayer: public GameInteractionEffectBase {
GameInteractionEffectQueryResult CanBeApplied() override; GameInteractionEffectQueryResult CanBeApplied() override;
void _Apply() 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 */ #endif /* __cplusplus */

View File

@ -50,6 +50,48 @@ bool GameInteractor::IsGameplayPaused() {
return (Player_InBlockingCsMode(gPlayState, player) || gPlayState->pauseCtx.state != 0 || gPlayState->msgCtx.msgMode != 0) ? true : false; 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(); 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;
}
}

View File

@ -11,6 +11,7 @@ typedef enum {
/* 0x01 */ GI_LINK_SIZE_GIANT, /* 0x01 */ GI_LINK_SIZE_GIANT,
/* 0x02 */ GI_LINK_SIZE_MINISH, /* 0x02 */ GI_LINK_SIZE_MINISH,
/* 0x03 */ GI_LINK_SIZE_PAPER, /* 0x03 */ GI_LINK_SIZE_PAPER,
/* 0x03 */ GI_LINK_SIZE_SQUISHED,
/* 0x04 */ GI_LINK_SIZE_RESET /* 0x04 */ GI_LINK_SIZE_RESET
} GILinkSize; } GILinkSize;
@ -20,6 +21,46 @@ typedef enum {
/* 0x02 */ GI_GRAVITY_LEVEL_HEAVY, /* 0x02 */ GI_GRAVITY_LEVEL_HEAVY,
} GIGravityLevel; } 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 #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
@ -34,6 +75,13 @@ uint8_t GameInteractor_ReverseControlsActive();
int32_t GameInteractor_DefenseModifier(); int32_t GameInteractor_DefenseModifier();
int32_t GameInteractor_RunSpeedModifier(); int32_t GameInteractor_RunSpeedModifier();
GIGravityLevel GameInteractor_GravityLevel(); 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 #ifdef __cplusplus
} }
#endif #endif
@ -66,6 +114,13 @@ public:
static int32_t DefenseModifier; static int32_t DefenseModifier;
static int32_t RunSpeedModifier; static int32_t RunSpeedModifier;
static GIGravityLevel GravityLevel; 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); static void SetPacifistMode(bool active);
}; };
@ -116,7 +171,8 @@ public:
// Helpers // Helpers
static bool IsSaveLoaded(); static bool IsSaveLoaded();
static bool IsGameplayPaused(); static bool IsGameplayPaused();
static bool CanSpawnEnemy(); static bool CanSpawnActor();
static bool CanAddOrTakeAmmo(int16_t amount, int16_t item);
class RawAction { class RawAction {
public: public:
@ -131,11 +187,22 @@ public:
static void BurnPlayer(); static void BurnPlayer();
static void ElectrocutePlayer(); static void ElectrocutePlayer();
static void KnockbackPlayer(float strength); static void KnockbackPlayer(float strength);
static void GiveDekuShield(); static void GiveOrTakeShield(int32_t shield);
static void SpawnCuccoStorm();
static void ForceInterfaceUpdate(); 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 SpawnEnemyWithOffset(uint32_t enemyId, int32_t enemyParams);
static GameInteractionEffectQueryResult SpawnActor(uint32_t actorId, int32_t actorParams);
}; };
}; };

View File

@ -1,5 +1,8 @@
#include "GameInteractor.h" #include "GameInteractor.h"
#include <libultraship/bridge.h> #include <libultraship/bridge.h>
#include "soh/Enhancements/cosmetics/CosmeticsEditor.h"
#include "soh/Enhancements/randomizer/3drando/random.hpp"
#include <math.h>
extern "C" { extern "C" {
#include "variables.h" #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_Niw/z_en_niw.h"
#include "overlays/actors/ovl_En_Bom/z_en_bom.h"
void GameInteractor::RawAction::AddOrRemoveHealthContainers(int16_t amount) { void GameInteractor::RawAction::AddOrRemoveHealthContainers(int16_t amount) {
gSaveContext.healthCapacity += amount * 0x10; 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); func_8002F71C(gPlayState, &player->actor, strength * 5, player->actor.world.rot.y + 0x8000, strength * 5);
} }
void GameInteractor::RawAction::GiveDekuShield() { void GameInteractor::RawAction::GiveOrTakeShield(int32_t shield) {
// Give Deku Shield to the player, and automatically equip it when they're child and have no shield currently equiped. // When taking a shield, make sure it is unequipped as well.
Player* player = GET_PLAYER(gPlayState); // When giving a shield and the player isn't wearing one yet,
Item_Give(gPlayState, ITEM_SHIELD_DEKU); // equip said shield when the player's current age can equip it.
if (LINK_IS_CHILD && player->currentShield == PLAYER_SHIELD_NONE) { // 'shield' being a negative number means taking that shield.
player->currentShield = PLAYER_SHIELD_DEKU;
Inventory_ChangeEquipment(EQUIP_SHIELD, PLAYER_SHIELD_DEKU);
}
}
void GameInteractor::RawAction::SpawnCuccoStorm() {
Player* player = GET_PLAYER(gPlayState); Player* player = GET_PLAYER(gPlayState);
EnNiw* cucco = (EnNiw*)Actor_Spawn(&gPlayState->actorCtx, gPlayState, ACTOR_EN_NIW, player->actor.world.pos.x, int8_t shieldToCheck = PLAYER_SHIELD_NONE;
player->actor.world.pos.y + 2200, player->actor.world.pos.z, 0, 0, 0, 0, 0);
cucco->actionFunc = func_80AB70A0_nocutscene; 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() { void GameInteractor::RawAction::ForceInterfaceUpdate() {
@ -142,58 +178,348 @@ void GameInteractor::RawAction::ForceInterfaceUpdate() {
Interface_Update(gPlayState); 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) { 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; return GameInteractionEffectQueryResult::TemporarilyNotPossible;
} }
Player* player = GET_PLAYER(gPlayState); Player* player = GET_PLAYER(gPlayState);
float posXOffset = 0; if (actorId == ACTOR_EN_NIW) {
float posYOffset = 0; // Spawn Cucco and make it angry
float posZOffset = 0; 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 (enemyId == ACTOR_EN_WALLMAS) { if (cucco == NULL) {
return GameInteractionEffectQueryResult::TemporarilyNotPossible;
} else if (enemyId == ACTOR_EN_CLEAR_TAG) {
// Don't allow Arwings in certain areas because they cause issues.
// Locations: King dodongo room, Morpha room, Twinrova room, Ganondorf room, Fishing pond, Ganon's room
// TODO: Swap this to disabling the option in CC options menu instead.
if (gPlayState->sceneNum == SCENE_DDAN_BOSS || gPlayState->sceneNum == SCENE_MIZUSIN_BS ||
gPlayState->sceneNum == SCENE_JYASINBOSS || gPlayState->sceneNum == SCENE_GANON_BOSS ||
gPlayState->sceneNum == SCENE_TURIBORI || gPlayState->sceneNum == SCENE_GANON_DEMO) {
return GameInteractionEffectQueryResult::NotPossible;
} }
posYOffset = 100;
} else if (enemyId == ACTOR_EN_TORCH2) {
posXOffset = 75;
posYOffset = 50;
} else if (enemyId == ACTOR_EN_TEST) {
posXOffset = 75;
posYOffset = 50;
} else if (enemyId == ACTOR_EN_WF) {
posXOffset = 75;
posYOffset = 50;
} else if (enemyId == ACTOR_EN_FZ) {
posXOffset = 75;
posYOffset = 50;
} else if (enemyId == ACTOR_EN_FIREFLY) {
posXOffset = 75;
posYOffset = 50;
} else if (enemyId == ACTOR_EN_TITE) {
posXOffset = 75;
posYOffset = 50;
} else if (enemyId == ACTOR_EN_RR) {
posXOffset = 75;
posYOffset = 50;
}
if (Actor_Spawn(&gPlayState->actorCtx, gPlayState, enemyId, player->actor.world.pos.x + posXOffset, cucco->actionFunc = func_80AB70A0_nocutscene;
player->actor.world.pos.y + posYOffset, player->actor.world.pos.z + posZOffset, 0, 0, 0,
enemyParams, 0) != NULL) {
return GameInteractionEffectQueryResult::Possible; 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; return GameInteractionEffectQueryResult::TemporarilyNotPossible;
} }

View File

@ -12,6 +12,13 @@ bool GameInteractor::State::ReverseControlsActive = 0;
int32_t GameInteractor::State::DefenseModifier = 0; int32_t GameInteractor::State::DefenseModifier = 0;
int32_t GameInteractor::State::RunSpeedModifier = 0; int32_t GameInteractor::State::RunSpeedModifier = 0;
GIGravityLevel GameInteractor::State::GravityLevel = GI_GRAVITY_LEVEL_NORMAL; 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) { void GameInteractor::State::SetPacifistMode(bool active) {
PacifistModeActive = active; PacifistModeActive = active;
@ -79,3 +86,38 @@ int32_t GameInteractor_RunSpeedModifier() {
GIGravityLevel GameInteractor_GravityLevel() { GIGravityLevel GameInteractor_GravityLevel() {
return GameInteractor::State::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;
}

View File

@ -233,6 +233,12 @@ void PadMgr_ProcessInputs(PadMgr* padMgr) {
input->cur.button &= ~(BTN_Z); input->cur.button &= ~(BTN_Z);
} }
uint32_t emulatedButtons = GameInteractor_GetEmulatedButtons();
if (emulatedButtons) {
input->cur.button |= emulatedButtons;
GameInteractor_SetEmulatedButtons(0);
}
if (GameInteractor_ReverseControlsActive()) { if (GameInteractor_ReverseControlsActive()) {
if (input->cur.stick_x == -128) { if (input->cur.stick_x == -128) {
input->cur.stick_x = 127; input->cur.stick_x = 127;

View File

@ -7,6 +7,7 @@
#include "z_en_bom.h" #include "z_en_bom.h"
#include "overlays/effects/ovl_Effect_Ss_Dead_Sound/z_eff_ss_dead_sound.h" #include "overlays/effects/ovl_Effect_Ss_Dead_Sound/z_eff_ss_dead_sound.h"
#include "objects/gameplay_keep/gameplay_keep.h" #include "objects/gameplay_keep/gameplay_keep.h"
#include "soh/Enhancements/game-interactor/GameInteractor.h"
#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) #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.mass = 200;
thisx->colChkInfo.cylRadius = 5; thisx->colChkInfo.cylRadius = 5;
thisx->colChkInfo.cylHeight = 10; 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; this->flashSpeedScale = 7;
Collider_InitCylinder(play, &this->bombCollider); Collider_InitCylinder(play, &this->bombCollider);
Collider_InitJntSph(play, &this->explosionCollider); Collider_InitJntSph(play, &this->explosionCollider);
@ -244,7 +254,8 @@ void EnBom_Update(Actor* thisx, PlayState* play2) {
this->timer--; 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); Audio_PlayActorSound2(thisx, NA_SE_PL_TAKE_OUT_SHIELD);
Actor_SetScale(thisx, 0.01f); Actor_SetScale(thisx, 0.01f);
} }

View File

@ -230,12 +230,14 @@ void func_80A74398(Actor* thisx, PlayState* play) {
Effect_Add(play, &this->blureIdx, EFFECT_BLURE1, 0, 0, &blureInit); Effect_Add(play, &this->blureIdx, EFFECT_BLURE1, 0, 0, &blureInit);
func_80A74714(this); func_80A74714(this);
uint8_t enemyRandoCCActive = CVarGetInteger("gRandomizedEnemies", 0) || CVarGetInteger("gCrowdControl", 0);
if (this->switchFlags != 0xFF) { if (this->switchFlags != 0xFF) {
// In vanilla gameplay, Iron Knuckles are despawned based on specific flags in specific scenarios. // 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. // 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)) || if ((Flags_GetSwitch(play, this->switchFlags) && !enemyRandoCCActive) ||
(thisx->params != 0 && Flags_GetClear(play, play->roomCtx.curRoom.num) && CVarGetInteger("gRandomizedEnemies", 0))) { (thisx->params != 0 && Flags_GetClear(play, play->roomCtx.curRoom.num) && enemyRandoCCActive)) {
Actor_Kill(thisx); Actor_Kill(thisx);
} }
} else if (thisx->params != 0 && Flags_GetClear(play, play->roomCtx.curRoom.num)) { } 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) { if (this->unk_2F9 == 0) {
Item_DropCollectibleRandom(play, &this->actor, &this->actor.world.pos, 0xB0); Item_DropCollectibleRandom(play, &this->actor, &this->actor.world.pos, 0xB0);
// Don't set flag when Iron Knuckle is spawned by Enemy Rando. // Don't set flag when Enemy Rando or CrowdControl are on.
// Instead Iron Knuckles rely on the "clear room" flag when Enemy Rando is on. // Instead Iron Knuckles rely on the "clear room" flag.
if (this->switchFlags != 0xFF && !CVarGetInteger("gRandomizedEnemies",0)) { if (this->switchFlags != 0xFF && !CVarGetInteger("gRandomizedEnemies", 0) && !CVarGetInteger("gCrowdControl", 0)) {
Flags_SetSwitch(play, this->switchFlags); Flags_SetSwitch(play, this->switchFlags);
} }
Actor_Kill(&this->actor); Actor_Kill(&this->actor);
@ -1481,8 +1483,8 @@ void EnIk_Init(Actor* thisx, PlayState* play) {
func_80A780D0(this, play); func_80A780D0(this, play);
} }
// Immediately trigger Iron Knuckle for enemy randomizer // Immediately trigger Iron Knuckle for Enemy Rando and Crowd Control
if (CVarGetInteger("gRandomizedEnemies", 0) && (thisx->params == 2 || thisx->params == 3)) { if ((CVarGetInteger("gRandomizedEnemies", 0) || CVarGetInteger("gCrowdControl", 0)) && (thisx->params == 2 || thisx->params == 3)) {
this->skelAnime.playSpeed = 1.0f; this->skelAnime.playSpeed = 1.0f;
} }
} }

View File

@ -251,8 +251,8 @@ void func_80AE2744(EnRd* this, PlayState* play) {
// Add a height check to redeads/gibdos freeze when Enemy Randomizer is on. // 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 // 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. // vertical rooms (like the first room in Deku Tree), making these rooms nearly unplayable.
s8 enemyRando = CVarGetInteger("gRandomizedEnemies", 0); s8 enemyRandoCCActive = CVarGetInteger("gRandomizedEnemies", 0) || CVarGetInteger("gCrowdControl", 0);
if (!enemyRando || (enemyRando && this->actor.yDistToPlayer <= 100.0f && this->actor.yDistToPlayer >= -100.0f)) { if (!enemyRandoCCActive || (enemyRandoCCActive && this->actor.yDistToPlayer <= 100.0f && this->actor.yDistToPlayer >= -100.0f)) {
if ((this->actor.params != 2) && (this->unk_305 == 0)) { if ((this->actor.params != 2) && (this->unk_305 == 0)) {
func_80AE37BC(this); func_80AE37BC(this);
} else { } else {
@ -663,8 +663,9 @@ void func_80AE3C98(EnRd* this, PlayState* play) {
if (SkelAnime_Update(&this->skelAnime)) { if (SkelAnime_Update(&this->skelAnime)) {
if (this->unk_30C == 0) { 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. // 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); Flags_SetSwitch(play, this->unk_312 & 0x7F);
} }
if (this->unk_314 != 0) { if (this->unk_314 != 0) {

View File

@ -8559,7 +8559,8 @@ void func_8084411C(Player* this, PlayState* play) {
func_80843E14(this, NA_SE_VO_LI_FALL_L); 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)) { !(this->stateFlags1 & (PLAYER_STATE1_11 | PLAYER_STATE1_27)) && (this->linearVelocity > 0.0f)) {
if ((this->wallHeight >= 150.0f) && (this->unk_84B[this->unk_846] == 0)) { if ((this->wallHeight >= 150.0f) && (this->unk_84B[this->unk_846] == 0)) {
func_8083EC18(this, play, D_808535F0); func_8083EC18(this, play, D_808535F0);
@ -8655,8 +8656,10 @@ void func_80844708(Player* this, PlayState* play) {
func_8083A060(this, play); func_8083A060(this, play);
} }
} else { } else {
f32 rand = Rand_ZeroOne();
uint8_t randomBonk = (rand <= .05) && GameInteractor_GetRandomBonksActive();
if (this->linearVelocity >= 7.0f) { 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) && ((this->cylinder.base.ocFlags1 & OC1_HIT) &&
(cylinderOc = this->cylinder.base.oc, (cylinderOc = this->cylinder.base.oc,
((cylinderOc->id == ACTOR_EN_WOOD02) && ((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->skelAnime.moveFlags & 0x80)) {
if (((this->actor.bgCheckFlags & 1) && (D_808535E4 == 5) && (this->currentBoots != PLAYER_BOOTS_IRON)) || 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)))) { !(this->stateFlags1 & (PLAYER_STATE1_27 | PLAYER_STATE1_29)))) {
f32 sp70 = this->linearVelocity; f32 sp70 = this->linearVelocity;
s16 sp6E = this->currentYaw; s16 sp6E = this->currentYaw;
@ -11003,42 +11006,67 @@ void Player_Update(Actor* thisx, PlayState* play) {
MREG(54) = this->actor.world.pos.z; MREG(54) = this->actor.world.pos.z;
MREG(55) = this->actor.world.rot.y; MREG(55) = this->actor.world.rot.y;
switch (GameInteractor_GetLinkSize()) { // Make Link normal size when going through doors and crawlspaces and when climbing ladders.
case GI_LINK_SIZE_RESET: // Otherwise Link can glitch out, being in unloaded rooms or falling OoB.
this->actor.scale.x = 0.01f; if (this->stateFlags1 & PLAYER_STATE1_21 || this->stateFlags1 & PLAYER_STATE1_29 ||
this->actor.scale.y = 0.01f; this->stateFlags2 & PLAYER_STATE2_CRAWLING) {
this->actor.scale.z = 0.01f; this->actor.scale.x = 0.01f;
GameInteractor_SetLinkSize(GI_LINK_SIZE_NORMAL); this->actor.scale.y = 0.01f;
break; this->actor.scale.z = 0.01f;
case GI_LINK_SIZE_GIANT: } else {
this->actor.scale.x = 0.02f; switch (GameInteractor_GetLinkSize()) {
this->actor.scale.y = 0.02f; case GI_LINK_SIZE_RESET:
this->actor.scale.z = 0.02f; this->actor.scale.x = 0.01f;
break; this->actor.scale.y = 0.01f;
case GI_LINK_SIZE_MINISH: this->actor.scale.z = 0.01f;
this->actor.scale.x = 0.001f; GameInteractor_SetLinkSize(GI_LINK_SIZE_NORMAL);
this->actor.scale.y = 0.001f; break;
this->actor.scale.z = 0.001f; case GI_LINK_SIZE_GIANT:
break; this->actor.scale.x = 0.02f;
case GI_LINK_SIZE_PAPER: this->actor.scale.y = 0.02f;
this->actor.scale.x = 0.001f; this->actor.scale.z = 0.02f;
this->actor.scale.y = 0.01f; break;
this->actor.scale.z = 0.01f; case GI_LINK_SIZE_MINISH:
break; this->actor.scale.x = 0.001f;
case GI_LINK_SIZE_NORMAL: this->actor.scale.y = 0.001f;
default: this->actor.scale.z = 0.001f;
break; 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()) { // Don't apply gravity when Link is in water, otherwise
case GI_GRAVITY_LEVEL_HEAVY: // it makes him sink instead of float.
this->actor.gravity = -4.0f; if (!(this->stateFlags1 & PLAYER_STATE1_27)) {
break; switch (GameInteractor_GravityLevel()) {
case GI_GRAVITY_LEVEL_LIGHT: case GI_GRAVITY_LEVEL_HEAVY:
this->actor.gravity = -0.3f; this->actor.gravity = -4.0f;
break; break;
default: case GI_GRAVITY_LEVEL_LIGHT:
break; 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(); GameInteractor_ExecuteOnPlayerUpdate();