mirror of
https://github.com/HarbourMasters/Shipwright
synced 2026-06-14 22:29:42 -04:00
677 lines
32 KiB
C++
677 lines
32 KiB
C++
#include "CrowdControl.h"
|
|
#include "CrowdControlTypes.h"
|
|
#include <libultraship/bridge.h>
|
|
#include <libultraship/libultraship.h>
|
|
#include <nlohmann/json.hpp>
|
|
#include <spdlog/spdlog.h>
|
|
#include <spdlog/fmt/fmt.h>
|
|
#include "soh/ShipInit.hpp"
|
|
|
|
extern "C" {
|
|
#include <z64.h>
|
|
#include "variables.h"
|
|
#include "functions.h"
|
|
#include "macros.h"
|
|
extern PlayState* gPlayState;
|
|
}
|
|
|
|
void CrowdControl::Enable() {
|
|
Network::Enable(CVarGetString(CVAR_REMOTE_CROWD_CONTROL("Host"), "127.0.0.1"),
|
|
CVarGetInteger(CVAR_REMOTE_CROWD_CONTROL("Port"), 43384));
|
|
}
|
|
|
|
void CrowdControl::OnConnected() {
|
|
ccThreadProcess = std::thread(&CrowdControl::ProcessActiveEffects, this);
|
|
}
|
|
|
|
void CrowdControl::OnDisconnected() {
|
|
ccThreadProcess.join();
|
|
}
|
|
|
|
void CrowdControl::OnIncomingJson(nlohmann::json payload) {
|
|
Effect* incomingEffect = ParseMessage(payload);
|
|
if (!incomingEffect) {
|
|
return;
|
|
}
|
|
|
|
// If effect is not a timed effect, execute and return result.
|
|
if (!incomingEffect->timeRemaining) {
|
|
EffectResult result = CrowdControl::ExecuteEffect(incomingEffect);
|
|
EmitMessage(incomingEffect->id, incomingEffect->timeRemaining, result);
|
|
} else {
|
|
// If another timed effect is already active that conflicts with the incoming effect.
|
|
bool isConflictingEffectActive = false;
|
|
for (Effect* effect : activeEffects) {
|
|
if (effect != incomingEffect && effect->category == incomingEffect->category &&
|
|
effect->id < incomingEffect->id) {
|
|
isConflictingEffectActive = true;
|
|
EmitMessage(incomingEffect->id, incomingEffect->timeRemaining, EffectResult::Retry);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!isConflictingEffectActive) {
|
|
// Check if effect can be applied, if it can't, let CC know.
|
|
EffectResult result = CrowdControl::CanApplyEffect(incomingEffect);
|
|
if (result == EffectResult::Retry || result == EffectResult::Failure) {
|
|
EmitMessage(incomingEffect->id, incomingEffect->timeRemaining, result);
|
|
return;
|
|
}
|
|
|
|
activeEffectsMutex.lock();
|
|
activeEffects.push_back(incomingEffect);
|
|
activeEffectsMutex.unlock();
|
|
}
|
|
}
|
|
}
|
|
|
|
void CrowdControl::ProcessActiveEffects() {
|
|
while (isEnabled) {
|
|
// We only want to send events when status changes, on start we send Success.
|
|
// If it fails at some point, we send Pause, and when it starts to succeed again we send Success.
|
|
// CC uses this to pause the timer on the overlay.
|
|
activeEffectsMutex.lock();
|
|
auto it = activeEffects.begin();
|
|
|
|
while (it != activeEffects.end()) {
|
|
Effect* effect = *it;
|
|
EffectResult result = CrowdControl::ExecuteEffect(effect);
|
|
|
|
if (result == EffectResult::Success) {
|
|
// If time remaining has reached 0, we have finished the effect.
|
|
if (effect->timeRemaining <= 0) {
|
|
it = activeEffects.erase(std::remove(activeEffects.begin(), activeEffects.end(), effect),
|
|
activeEffects.end());
|
|
GameInteractor::RemoveEffect(
|
|
*dynamic_cast<RemovableGameInteractionEffect*>(effect->giEffect.get()));
|
|
delete effect;
|
|
} else {
|
|
// If we have a success after previously being paused, tell CC to resume timer.
|
|
if (effect->isPaused) {
|
|
effect->isPaused = false;
|
|
EmitMessage(effect->id, effect->timeRemaining, EffectResult::Resumed);
|
|
// If not paused before, subtract time from the timer and send a Success event if
|
|
// the result is different from the last time this was ran.
|
|
// Timed events are put on a thread that runs once per second.
|
|
} else {
|
|
effect->timeRemaining -= 1000;
|
|
if (result != effect->lastExecutionResult) {
|
|
effect->lastExecutionResult = result;
|
|
EmitMessage(effect->id, effect->timeRemaining, EffectResult::Success);
|
|
}
|
|
}
|
|
it++;
|
|
}
|
|
} else { // Timed effects only do Success or Retry
|
|
if (!effect->isPaused && effect->timeRemaining > 0) {
|
|
effect->isPaused = true;
|
|
EmitMessage(effect->id, effect->timeRemaining, EffectResult::Paused);
|
|
}
|
|
it++;
|
|
}
|
|
}
|
|
|
|
activeEffectsMutex.unlock();
|
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
|
}
|
|
|
|
SPDLOG_TRACE("[CrowdControl] Ending Process thread...");
|
|
}
|
|
|
|
void CrowdControl::EmitMessage(uint32_t eventId, long timeRemaining, EffectResult status) {
|
|
nlohmann::json payload;
|
|
|
|
payload["id"] = eventId;
|
|
payload["type"] = 0;
|
|
payload["timeRemaining"] = timeRemaining;
|
|
payload["status"] = status;
|
|
|
|
SPDLOG_INFO("[CrowdControl] Sending payload:\n{}", payload.dump());
|
|
|
|
SendJsonToRemote(payload);
|
|
}
|
|
|
|
CrowdControl::EffectResult CrowdControl::ExecuteEffect(Effect* effect) {
|
|
GameInteractionEffectQueryResult giResult;
|
|
if (effect->category == kEffectCatSpawnEnemy) {
|
|
giResult = GameInteractor::RawAction::SpawnEnemyWithOffset(effect->spawnParams[0], effect->spawnParams[1],
|
|
effect->viewerName);
|
|
} else if (effect->category == kEffectCatSpawnActor) {
|
|
giResult =
|
|
GameInteractor::RawAction::SpawnActor(effect->spawnParams[0], effect->spawnParams[1], effect->viewerName);
|
|
} else {
|
|
giResult = GameInteractor::ApplyEffect(*effect->giEffect.get());
|
|
}
|
|
|
|
return TranslateGiEnum(giResult);
|
|
}
|
|
|
|
/// Checks if effect can be applied -- should not be used to check for spawn enemy effects.
|
|
CrowdControl::EffectResult CrowdControl::CanApplyEffect(Effect* effect) {
|
|
assert(effect->category != kEffectCatSpawnEnemy || effect->category != kEffectCatSpawnActor);
|
|
GameInteractionEffectQueryResult giResult = GameInteractor::CanApplyEffect(*effect->giEffect.get());
|
|
|
|
return TranslateGiEnum(giResult);
|
|
}
|
|
|
|
CrowdControl::EffectResult CrowdControl::TranslateGiEnum(GameInteractionEffectQueryResult giResult) {
|
|
// Translate GameInteractor result into CC's own enums.
|
|
EffectResult result;
|
|
if (giResult == GameInteractionEffectQueryResult::Possible) {
|
|
result = EffectResult::Success;
|
|
} else if (giResult == GameInteractionEffectQueryResult::TemporarilyNotPossible) {
|
|
result = EffectResult::Retry;
|
|
} else {
|
|
result = EffectResult::Failure;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
CrowdControl::Effect* CrowdControl::ParseMessage(nlohmann::json dataReceived) {
|
|
if (!dataReceived.contains("id") || !dataReceived.contains("type")) {
|
|
SPDLOG_ERROR("[CrowdControl] Invalid payload received:\n{}", dataReceived.dump());
|
|
return nullptr;
|
|
}
|
|
|
|
SPDLOG_INFO("[CrowdControl] Received payload:\n{}", dataReceived.dump());
|
|
|
|
if (!dataReceived.contains("code")) {
|
|
// This seems to happen when the CC session ends
|
|
SPDLOG_ERROR("[CrowdControl] Payload does not contain code, ignoring.");
|
|
return nullptr;
|
|
}
|
|
|
|
Effect* effect = new Effect();
|
|
effect->lastExecutionResult = EffectResult::Initiate;
|
|
effect->id = dataReceived["id"];
|
|
effect->viewerName = dataReceived["viewer"];
|
|
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 = std::make_unique<GameInteractionEffect::ModifyDefenseModifier>();
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[0] = 2;
|
|
break;
|
|
case kEffectTakeDoubleDamage:
|
|
effect->category = kEffectCatDamageTaken;
|
|
effect->timeRemaining = 30000;
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::ModifyDefenseModifier>();
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[0] = -2;
|
|
break;
|
|
case kEffectOneHitKo:
|
|
effect->category = kEffectCatDamageTaken;
|
|
effect->timeRemaining = 30000;
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::OneHitKO>();
|
|
break;
|
|
case kEffectInvincibility:
|
|
effect->category = kEffectCatDamageTaken;
|
|
effect->timeRemaining = 15000;
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::PlayerInvincibility>();
|
|
break;
|
|
break;
|
|
case kEffectIncreaseSpeed:
|
|
effect->category = kEffectCatSpeed;
|
|
effect->timeRemaining = 30000;
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::ModifyMovementSpeedMultiplier>();
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[0] = 2;
|
|
break;
|
|
case kEffectDecreaseSpeed:
|
|
effect->category = kEffectCatSpeed;
|
|
effect->timeRemaining = 30000;
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::ModifyMovementSpeedMultiplier>();
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[0] = -2;
|
|
break;
|
|
case kEffectLowGravity:
|
|
effect->category = kEffectCatGravity;
|
|
effect->timeRemaining = 30000;
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::ModifyGravity>();
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[0] =
|
|
GI_GRAVITY_LEVEL_LIGHT;
|
|
break;
|
|
case kEffectHighGravity:
|
|
effect->category = kEffectCatGravity;
|
|
effect->timeRemaining = 30000;
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::ModifyGravity>();
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[0] =
|
|
GI_GRAVITY_LEVEL_HEAVY;
|
|
break;
|
|
case kEffectForceIronBoots:
|
|
effect->category = kEffectCatBoots;
|
|
effect->timeRemaining = 30000;
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::ForceEquipBoots>();
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[0] =
|
|
EQUIP_VALUE_BOOTS_IRON;
|
|
break;
|
|
case kEffectForceHoverBoots:
|
|
effect->category = kEffectCatBoots;
|
|
effect->timeRemaining = 30000;
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::ForceEquipBoots>();
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[0] =
|
|
EQUIP_VALUE_BOOTS_HOVER;
|
|
break;
|
|
case kEffectSlipperyFloor:
|
|
effect->category = kEffectCatSlipperyFloor;
|
|
effect->timeRemaining = 30000;
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::SlipperyFloor>();
|
|
break;
|
|
case kEffectNoLedgeGrabs:
|
|
effect->category = kEffectCatNoLedgeGrabs;
|
|
effect->timeRemaining = 30000;
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::DisableLedgeGrabs>();
|
|
break;
|
|
case kEffectRandomWind:
|
|
effect->category = kEffectCatRandomWind;
|
|
effect->timeRemaining = 30000;
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::RandomWind>();
|
|
break;
|
|
case kEffectRandomBonks:
|
|
effect->category = kEffectCatRandomBonks;
|
|
effect->timeRemaining = 60000;
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::RandomBonks>();
|
|
break;
|
|
|
|
// Hurt or Heal Link
|
|
case kEffectEmptyHeart:
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::ModifyHealth>();
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[0] =
|
|
receivedParameter * -1;
|
|
break;
|
|
case kEffectFillHeart:
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::ModifyHealth>();
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[0] =
|
|
receivedParameter;
|
|
break;
|
|
case kEffectKnockbackLinkWeak:
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::KnockbackPlayer>();
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[0] = 1;
|
|
break;
|
|
case kEffectKnockbackLinkStrong:
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::KnockbackPlayer>();
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[0] = 3;
|
|
break;
|
|
case kEffectKnockbackLinkMega:
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::KnockbackPlayer>();
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[0] = 6;
|
|
break;
|
|
case kEffectBurnLink:
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::BurnPlayer>();
|
|
break;
|
|
case kEffectFreezeLink:
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::FreezePlayer>();
|
|
break;
|
|
case kEffectElectrocuteLink:
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::ElectrocutePlayer>();
|
|
break;
|
|
case kEffectKillLink:
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::SetPlayerHealth>();
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[0] = 0;
|
|
break;
|
|
|
|
// Give Items and Consumables
|
|
case kEffectAddHeartContainer:
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::ModifyHeartContainers>();
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[0] = 1;
|
|
break;
|
|
case kEffectFillMagic:
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::FillMagic>();
|
|
break;
|
|
case kEffectAddRupees:
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::ModifyRupees>();
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[0] =
|
|
receivedParameter;
|
|
break;
|
|
case kEffectGiveDekuShield:
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::GiveOrTakeShield>();
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[0] = ITEM_SHIELD_DEKU;
|
|
break;
|
|
case kEffectGiveHylianShield:
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::GiveOrTakeShield>();
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[0] =
|
|
ITEM_SHIELD_HYLIAN;
|
|
break;
|
|
case kEffectRefillSticks:
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::AddOrTakeAmmo>();
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[0] =
|
|
receivedParameter;
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[1] = ITEM_STICK;
|
|
break;
|
|
case kEffectRefillNuts:
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::AddOrTakeAmmo>();
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[0] =
|
|
receivedParameter;
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[1] = ITEM_NUT;
|
|
break;
|
|
case kEffectRefillBombs:
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::AddOrTakeAmmo>();
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[0] =
|
|
receivedParameter;
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[1] = ITEM_BOMB;
|
|
break;
|
|
case kEffectRefillSeeds:
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::AddOrTakeAmmo>();
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[0] =
|
|
receivedParameter;
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[1] = ITEM_SLINGSHOT;
|
|
break;
|
|
case kEffectRefillArrows:
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::AddOrTakeAmmo>();
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[0] =
|
|
receivedParameter;
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[1] = ITEM_BOW;
|
|
break;
|
|
case kEffectRefillBombchus:
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::AddOrTakeAmmo>();
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[0] =
|
|
receivedParameter;
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[1] = ITEM_BOMBCHU;
|
|
break;
|
|
|
|
// Take Items and Consumables
|
|
case kEffectRemoveHeartContainer:
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::ModifyHeartContainers>();
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[0] = -1;
|
|
break;
|
|
case kEffectEmptyMagic:
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::EmptyMagic>();
|
|
break;
|
|
case kEffectRemoveRupees:
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::ModifyRupees>();
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[0] =
|
|
receivedParameter * -1;
|
|
break;
|
|
case kEffectTakeDekuShield:
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::GiveOrTakeShield>();
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[0] =
|
|
-ITEM_SHIELD_DEKU;
|
|
break;
|
|
case kEffectTakeHylianShield:
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::GiveOrTakeShield>();
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[0] =
|
|
-ITEM_SHIELD_HYLIAN;
|
|
break;
|
|
case kEffectTakeSticks:
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::AddOrTakeAmmo>();
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[0] =
|
|
receivedParameter * -1;
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[1] = ITEM_STICK;
|
|
break;
|
|
case kEffectTakeNuts:
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::AddOrTakeAmmo>();
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[0] =
|
|
receivedParameter * -1;
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[1] = ITEM_NUT;
|
|
break;
|
|
case kEffectTakeBombs:
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::AddOrTakeAmmo>();
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[0] =
|
|
receivedParameter * -1;
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[1] = ITEM_BOMB;
|
|
break;
|
|
case kEffectTakeSeeds:
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::AddOrTakeAmmo>();
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[0] =
|
|
receivedParameter * -1;
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[1] = ITEM_SLINGSHOT;
|
|
break;
|
|
case kEffectTakeArrows:
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::AddOrTakeAmmo>();
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[0] =
|
|
receivedParameter * -1;
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[1] = ITEM_BOW;
|
|
break;
|
|
case kEffectTakeBombchus:
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::AddOrTakeAmmo>();
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[0] =
|
|
receivedParameter * -1;
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[1] = ITEM_BOMBCHU;
|
|
break;
|
|
|
|
// Link Size Modifiers
|
|
case kEffectGiantLink:
|
|
effect->category = kEffectCatLinkSize;
|
|
effect->timeRemaining = 30000;
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::ModifyLinkSize>();
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[0] =
|
|
GI_LINK_SIZE_GIANT;
|
|
break;
|
|
case kEffectMinishLink:
|
|
effect->category = kEffectCatLinkSize;
|
|
effect->timeRemaining = 30000;
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::ModifyLinkSize>();
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[0] =
|
|
GI_LINK_SIZE_MINISH;
|
|
break;
|
|
case kEffectPaperLink:
|
|
effect->category = kEffectCatLinkSize;
|
|
effect->timeRemaining = 30000;
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::ModifyLinkSize>();
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[0] =
|
|
GI_LINK_SIZE_PAPER;
|
|
break;
|
|
case kEffectSquishedLink:
|
|
effect->category = kEffectCatLinkSize;
|
|
effect->timeRemaining = 30000;
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::ModifyLinkSize>();
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[0] =
|
|
GI_LINK_SIZE_SQUISHED;
|
|
break;
|
|
case kEffectInvisibleLink:
|
|
effect->category = kEffectCatLinkSize;
|
|
effect->timeRemaining = 30000;
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::InvisibleLink>();
|
|
break;
|
|
|
|
// Generic Effects
|
|
case kEffectRandomBombTimer:
|
|
effect->category = kEffectCatRandomBombFuseTimer;
|
|
effect->timeRemaining = 60000;
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::RandomBombFuseTimer>();
|
|
break;
|
|
case kEffectSetTimeToDawn:
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::SetTimeOfDay>();
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[0] =
|
|
GI_TIMEOFDAY_DAWN;
|
|
break;
|
|
case kEffectSetTimeToDusk:
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::SetTimeOfDay>();
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[0] =
|
|
GI_TIMEOFDAY_DUSK;
|
|
break;
|
|
|
|
// Visual Effects
|
|
case kEffectNoUi:
|
|
effect->category = kEffectCatUi;
|
|
effect->timeRemaining = 60000;
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::NoUI>();
|
|
break;
|
|
case kEffectRainstorm:
|
|
effect->category = kEffectCatWeather;
|
|
effect->timeRemaining = 30000;
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::WeatherRainstorm>();
|
|
break;
|
|
case kEffectDebugMode:
|
|
effect->category = kEffectCatDebugMode;
|
|
effect->timeRemaining = 30000;
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::SetCollisionViewer>();
|
|
break;
|
|
case kEffectRandomCosmetics:
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::RandomizeCosmetics>();
|
|
break;
|
|
|
|
// Controls
|
|
case kEffectNoZButton:
|
|
effect->category = kEffectCatNoZ;
|
|
effect->timeRemaining = 30000;
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::DisableZTargeting>();
|
|
break;
|
|
case kEffectReverseControls:
|
|
effect->category = kEffectCatReverseControls;
|
|
effect->timeRemaining = 60000;
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::ReverseControls>();
|
|
break;
|
|
case kEffectPacifistMode:
|
|
effect->category = kEffectCatPacifist;
|
|
effect->timeRemaining = 15000;
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::PacifistMode>();
|
|
break;
|
|
case kEffectPressRandomButtons:
|
|
effect->category = kEffectCatRandomButtons;
|
|
effect->timeRemaining = 30000;
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::PressRandomButton>();
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[0] = 30;
|
|
break;
|
|
case kEffectClearCbuttons:
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::ClearAssignedButtons>();
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[0] =
|
|
GI_BUTTONS_CBUTTONS;
|
|
break;
|
|
case kEffectClearDpad:
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::ClearAssignedButtons>();
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[0] = GI_BUTTONS_DPAD;
|
|
break;
|
|
|
|
// Teleport Player
|
|
case kEffectTpLinksHouse:
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::TeleportPlayer>();
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[0] =
|
|
ENTR_LINKS_HOUSE_CHILD_SPAWN;
|
|
break;
|
|
case kEffectTpMinuet:
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::TeleportPlayer>();
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[0] =
|
|
ENTR_SACRED_FOREST_MEADOW_WARP_PAD;
|
|
break;
|
|
case kEffectTpBolero:
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::TeleportPlayer>();
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[0] =
|
|
ENTR_DEATH_MOUNTAIN_CRATER_WARP_PAD;
|
|
break;
|
|
case kEffectTpSerenade:
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::TeleportPlayer>();
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[0] =
|
|
ENTR_LAKE_HYLIA_WARP_PAD;
|
|
break;
|
|
case kEffectTpRequiem:
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::TeleportPlayer>();
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[0] =
|
|
ENTR_DESERT_COLOSSUS_WARP_PAD;
|
|
break;
|
|
case kEffectTpNocturne:
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::TeleportPlayer>();
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[0] =
|
|
ENTR_GRAVEYARD_WARP_PAD;
|
|
break;
|
|
case kEffectTpPrelude:
|
|
effect->giEffect = std::make_unique<GameInteractionEffect::TeleportPlayer>();
|
|
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect.get())->parameters[0] =
|
|
ENTR_TEMPLE_OF_TIME_WARP_PAD;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return effect;
|
|
}
|
|
|
|
void RegisterCrowdControlHooks() {
|
|
COND_VB_SHOULD(VB_SHOULD_LOAD_BG_IMAGE, CVarGetInteger(CVAR_REMOTE_CROWD_CONTROL("Enabled"), 0), {
|
|
int32_t* camId = va_arg(args, int*);
|
|
if (*camId == -1) {
|
|
*should = false;
|
|
}
|
|
});
|
|
}
|
|
|
|
static RegisterShipInitFunc initFunc(RegisterCrowdControlHooks, { CVAR_REMOTE_CROWD_CONTROL("Enabled") });
|