mirror of
https://github.com/HarbourMasters/Shipwright.git
synced 2024-12-21 15:48:51 -05:00
parent
a6b4e0b7fd
commit
86044a1c50
17
.github/workflows/generate-builds.yml
vendored
17
.github/workflows/generate-builds.yml
vendored
@ -32,17 +32,6 @@ jobs:
|
||||
make -j 10
|
||||
sudo make install
|
||||
sudo cp -av /usr/local/lib/libSDL* /lib/x86_64-linux-gnu/
|
||||
- name: Install latest SDL_net
|
||||
if: ${{ !vars.LINUX_RUNNER }}
|
||||
run: |
|
||||
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
|
||||
wget https://www.libsdl.org/projects/SDL_net/release/SDL2_net-2.2.0.tar.gz
|
||||
tar -xzf SDL2_net-2.2.0.tar.gz
|
||||
cd SDL2_net-2.2.0
|
||||
./configure
|
||||
make -j 10
|
||||
sudo make install
|
||||
sudo cp -av /usr/local/lib/libSDL* /lib/x86_64-linux-gnu/
|
||||
- name: Generate soh.otr
|
||||
run: |
|
||||
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
|
||||
@ -102,7 +91,7 @@ jobs:
|
||||
- name: Build SoH
|
||||
run: |
|
||||
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
|
||||
cmake --no-warn-unused-cli -H. -Bbuild-cmake -GNinja -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64"
|
||||
cmake --no-warn-unused-cli -H. -Bbuild-cmake -GNinja -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -DBUILD_REMOTE_CONTROL=1
|
||||
cmake --build build-cmake --config Release --parallel 10
|
||||
mv soh.otr build-cmake/soh
|
||||
(cd build-cmake && cpack)
|
||||
@ -171,7 +160,7 @@ jobs:
|
||||
- name: Build SoH
|
||||
run: |
|
||||
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
|
||||
cmake --no-warn-unused-cli -H. -Bbuild-cmake -GNinja -DCMAKE_BUILD_TYPE:STRING=Release
|
||||
cmake --no-warn-unused-cli -H. -Bbuild-cmake -GNinja -DCMAKE_BUILD_TYPE:STRING=Release -DBUILD_REMOTE_CONTROL=1
|
||||
cmake --build build-cmake --config Release -j3
|
||||
(cd build-cmake && cpack -G External)
|
||||
|
||||
@ -297,7 +286,7 @@ jobs:
|
||||
VCPKG_ROOT: ${{github.workspace}}/vcpkg
|
||||
run: |
|
||||
set $env:PATH="$env:USERPROFILE/.cargo/bin;$env:PATH"
|
||||
cmake -S . -B build-windows -G Ninja -DCMAKE_MAKE_PROGRAM=ninja -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
|
||||
cmake -S . -B build-windows -G Ninja -DCMAKE_MAKE_PROGRAM=ninja -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DBUILD_REMOTE_CONTROL=1
|
||||
cmake --build build-windows --config Release --parallel 10
|
||||
|
||||
mkdir soh-windows
|
||||
|
2
.github/workflows/macports-deps.txt
vendored
2
.github/workflows/macports-deps.txt
vendored
@ -1 +1 @@
|
||||
libsdl2 +universal libpng +universal glew +universal
|
||||
libsdl2 +universal libsdl2_net +universal libpng +universal glew +universal
|
||||
|
@ -13,12 +13,6 @@ set_property(DIRECTORY ${CMAKE_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT soh)
|
||||
add_compile_options($<$<CXX_COMPILER_ID:MSVC>:/MP>)
|
||||
add_compile_options($<$<CXX_COMPILER_ID:MSVC>:/utf-8>)
|
||||
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Windows|Linux")
|
||||
if(NOT DEFINED BUILD_CROWD_CONTROL)
|
||||
set(BUILD_CROWD_CONTROL ON)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
|
||||
include(CMake/automate-vcpkg.cmake)
|
||||
|
||||
|
@ -154,7 +154,7 @@ list(FILTER soh__Enhancements EXCLUDE REGEX "soh/Enhancements/gfx.*")
|
||||
# handle crowd control removals
|
||||
list(REMOVE_ITEM soh__Enhancements "soh/Enhancements/crowd-control/soh.cs")
|
||||
list(REMOVE_ITEM soh__Enhancements "soh/Enhancements/crowd-control/soh.ccpak")
|
||||
if (!BUILD_CROWD_CONTROL)
|
||||
if (!BUILD_REMOTE_CONTROL)
|
||||
list(FILTER soh__Enhancements EXCLUDE REGEX "soh/Enhancements/crowd-control/*")
|
||||
endif()
|
||||
|
||||
@ -354,7 +354,7 @@ endif()
|
||||
find_package(SDL2)
|
||||
set(SDL2-INCLUDE ${SDL2_INCLUDE_DIRS})
|
||||
|
||||
if (BUILD_CROWD_CONTROL)
|
||||
if (BUILD_REMOTE_CONTROL)
|
||||
find_package(SDL2_net)
|
||||
set(SDL2-NET-INCLUDE ${SDL_NET_INCLUDE_DIRS})
|
||||
endif()
|
||||
@ -408,9 +408,9 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
|
||||
"$<$<CONFIG:Release>:"
|
||||
"NDEBUG"
|
||||
">"
|
||||
"$<$<BOOL:${BUILD_CROWD_CONTROL}>:ENABLE_CROWD_CONTROL>"
|
||||
"$<$<BOOL:${BUILD_REMOTE_CONTROL}>:ENABLE_REMOTE_CONTROL>"
|
||||
"INCLUDE_GAME_PRINTF;"
|
||||
"ENABLE_CROWD_CONTROL;"
|
||||
"ENABLE_REMOTE_CONTROL;"
|
||||
"UNICODE;"
|
||||
"_UNICODE"
|
||||
STORMLIB_NO_AUTO_LINK
|
||||
@ -455,7 +455,7 @@ elseif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU|Clang|AppleClang")
|
||||
"$<$<CONFIG:Release>:"
|
||||
"NDEBUG"
|
||||
">"
|
||||
"$<$<BOOL:${BUILD_CROWD_CONTROL}>:ENABLE_CROWD_CONTROL>"
|
||||
"$<$<BOOL:${BUILD_REMOTE_CONTROL}>:ENABLE_REMOTE_CONTROL>"
|
||||
"SPDLOG_ACTIVE_LEVEL=0;"
|
||||
"_CONSOLE;"
|
||||
"_CRT_SECURE_NO_WARNINGS;"
|
||||
@ -689,7 +689,7 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
|
||||
"glu32;"
|
||||
"SDL2::SDL2;"
|
||||
"SDL2::SDL2main;"
|
||||
"$<$<BOOL:${BUILD_CROWD_CONTROL}>:SDL2_net::SDL2_net-static>"
|
||||
"$<$<BOOL:${BUILD_REMOTE_CONTROL}>:SDL2_net::SDL2_net-static>"
|
||||
"glfw;"
|
||||
"winmm;"
|
||||
"imm32;"
|
||||
@ -742,7 +742,7 @@ else()
|
||||
"ZAPDUtils;"
|
||||
"ZAPDLib;"
|
||||
SDL2::SDL2
|
||||
"$<$<BOOL:${BUILD_CROWD_CONTROL}>:SDL2_net::SDL2_net>"
|
||||
"$<$<BOOL:${BUILD_REMOTE_CONTROL}>:SDL2_net::SDL2_net>"
|
||||
${CMAKE_DL_LIBS}
|
||||
Threads::Threads
|
||||
)
|
||||
|
@ -1,4 +1,4 @@
|
||||
#ifdef ENABLE_CROWD_CONTROL
|
||||
#ifdef ENABLE_REMOTE_CONTROL
|
||||
|
||||
#include "CrowdControl.h"
|
||||
#include "CrowdControlTypes.h"
|
||||
@ -17,25 +17,17 @@ extern "C" {
|
||||
extern PlayState* gPlayState;
|
||||
}
|
||||
|
||||
void CrowdControl::Init() {
|
||||
SDLNet_Init();
|
||||
}
|
||||
|
||||
void CrowdControl::Shutdown() {
|
||||
SDLNet_Quit();
|
||||
}
|
||||
|
||||
void CrowdControl::Enable() {
|
||||
if (isEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (SDLNet_ResolveHost(&ip, "127.0.0.1", 43384) == -1) {
|
||||
SPDLOG_ERROR("[CrowdControl] SDLNet_ResolveHost: {}", SDLNet_GetError());
|
||||
}
|
||||
|
||||
isEnabled = true;
|
||||
ccThreadReceive = std::thread(&CrowdControl::ListenToServer, this);
|
||||
GameInteractor::Instance->EnableRemoteInteractor();
|
||||
GameInteractor::Instance->RegisterRemoteJsonHandler([&](nlohmann::json payload) {
|
||||
HandleRemoteData(payload);
|
||||
});
|
||||
|
||||
ccThreadProcess = std::thread(&CrowdControl::ProcessActiveEffects, this);
|
||||
}
|
||||
|
||||
@ -45,87 +37,42 @@ void CrowdControl::Disable() {
|
||||
}
|
||||
|
||||
isEnabled = false;
|
||||
ccThreadReceive.join();
|
||||
ccThreadProcess.join();
|
||||
GameInteractor::Instance->DisableRemoteInteractor();
|
||||
}
|
||||
|
||||
void CrowdControl::ListenToServer() {
|
||||
while (isEnabled) {
|
||||
while (!connected && isEnabled) {
|
||||
SPDLOG_TRACE("[CrowdControl] Attempting to make connection to server...");
|
||||
tcpsock = SDLNet_TCP_Open(&ip);
|
||||
void CrowdControl::HandleRemoteData(nlohmann::json payload) {
|
||||
Effect* incomingEffect = ParseMessage(payload);
|
||||
if (!incomingEffect) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (tcpsock) {
|
||||
connected = true;
|
||||
SPDLOG_TRACE("[CrowdControl] Connection to server established!");
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
SDLNet_SocketSet socketSet = SDLNet_AllocSocketSet(1);
|
||||
if (tcpsock) {
|
||||
SDLNet_TCP_AddSocket(socketSet, tcpsock);
|
||||
}
|
||||
|
||||
// Listen to socket messages
|
||||
while (connected && tcpsock && isEnabled) {
|
||||
// we check first if socket has data, to not block in the TCP_Recv
|
||||
int socketsReady = SDLNet_CheckSockets(socketSet, 0);
|
||||
|
||||
if (socketsReady == -1) {
|
||||
SPDLOG_ERROR("[CrowdControl] SDLNet_CheckSockets: {}", SDLNet_GetError());
|
||||
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;
|
||||
}
|
||||
|
||||
if (socketsReady == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int len = SDLNet_TCP_Recv(tcpsock, &received, sizeof(received));
|
||||
if (!len || !tcpsock || len == -1) {
|
||||
SPDLOG_ERROR("[CrowdControl] SDLNet_TCP_Recv: {}", SDLNet_GetError());
|
||||
break;
|
||||
}
|
||||
|
||||
Effect* incomingEffect = ParseMessage(received);
|
||||
if (!incomingEffect) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If effect is not a timed effect, execute and return result.
|
||||
if (!incomingEffect->timeRemaining) {
|
||||
EffectResult result = CrowdControl::ExecuteEffect(incomingEffect);
|
||||
EmitMessage(tcpsock, 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(tcpsock, 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(tcpsock, incomingEffect->id, incomingEffect->timeRemaining, result);
|
||||
continue;
|
||||
}
|
||||
|
||||
activeEffectsMutex.lock();
|
||||
activeEffects.push_back(incomingEffect);
|
||||
activeEffectsMutex.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (connected) {
|
||||
SDLNet_TCP_Close(tcpsock);
|
||||
connected = false;
|
||||
SPDLOG_TRACE("[CrowdControl] Ending Listen thread...");
|
||||
activeEffectsMutex.lock();
|
||||
activeEffects.push_back(incomingEffect);
|
||||
activeEffectsMutex.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -147,13 +94,13 @@ void CrowdControl::ProcessActiveEffects() {
|
||||
if (effect->timeRemaining <= 0) {
|
||||
it = activeEffects.erase(std::remove(activeEffects.begin(), activeEffects.end(), effect),
|
||||
activeEffects.end());
|
||||
GameInteractor::RemoveEffect(effect->giEffect);
|
||||
GameInteractor::RemoveEffect(dynamic_cast<RemovableGameInteractionEffect*>(effect->giEffect));
|
||||
delete effect;
|
||||
} else {
|
||||
// If we have a success after previously being paused, tell CC to resume timer.
|
||||
if (effect->isPaused) {
|
||||
effect->isPaused = false;
|
||||
EmitMessage(tcpsock, effect->id, effect->timeRemaining, EffectResult::Resumed);
|
||||
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.
|
||||
@ -161,7 +108,7 @@ void CrowdControl::ProcessActiveEffects() {
|
||||
effect->timeRemaining -= 1000;
|
||||
if (result != effect->lastExecutionResult) {
|
||||
effect->lastExecutionResult = result;
|
||||
EmitMessage(tcpsock, effect->id, effect->timeRemaining, EffectResult::Success);
|
||||
EmitMessage(effect->id, effect->timeRemaining, EffectResult::Success);
|
||||
}
|
||||
}
|
||||
it++;
|
||||
@ -169,7 +116,7 @@ void CrowdControl::ProcessActiveEffects() {
|
||||
} else { // Timed effects only do Success or Retry
|
||||
if (!effect->isPaused && effect->timeRemaining > 0) {
|
||||
effect->isPaused = true;
|
||||
EmitMessage(tcpsock, effect->id, effect->timeRemaining, EffectResult::Paused);
|
||||
EmitMessage(effect->id, effect->timeRemaining, EffectResult::Paused);
|
||||
}
|
||||
it++;
|
||||
}
|
||||
@ -182,7 +129,7 @@ void CrowdControl::ProcessActiveEffects() {
|
||||
SPDLOG_TRACE("[CrowdControl] Ending Process thread...");
|
||||
}
|
||||
|
||||
void CrowdControl::EmitMessage(TCPsocket socket, uint32_t eventId, long timeRemaining, EffectResult status) {
|
||||
void CrowdControl::EmitMessage(uint32_t eventId, long timeRemaining, EffectResult status) {
|
||||
nlohmann::json payload;
|
||||
|
||||
payload["id"] = eventId;
|
||||
@ -190,8 +137,9 @@ void CrowdControl::EmitMessage(TCPsocket socket, uint32_t eventId, long timeRema
|
||||
payload["timeRemaining"] = timeRemaining;
|
||||
payload["status"] = status;
|
||||
|
||||
std::string jsonPayload = payload.dump();
|
||||
SDLNet_TCP_Send(socket, jsonPayload.c_str(), jsonPayload.size() + 1);
|
||||
SPDLOG_INFO("[CrowdControl] Sending payload:\n{}", payload.dump());
|
||||
|
||||
GameInteractor::Instance->TransmitJsonToRemote(payload);
|
||||
}
|
||||
|
||||
CrowdControl::EffectResult CrowdControl::ExecuteEffect(Effect* effect) {
|
||||
@ -229,13 +177,14 @@ CrowdControl::EffectResult CrowdControl::TranslateGiEnum(GameInteractionEffectQu
|
||||
return result;
|
||||
}
|
||||
|
||||
CrowdControl::Effect* CrowdControl::ParseMessage(char payload[512]) {
|
||||
nlohmann::json dataReceived = nlohmann::json::parse(payload, nullptr, false);
|
||||
if (dataReceived.is_discarded()) {
|
||||
SPDLOG_ERROR("Error parsing JSON");
|
||||
CrowdControl::Effect* CrowdControl::ParseMessage(nlohmann::json dataReceived) {
|
||||
if (!dataReceived.contains("id") || !dataReceived.contains("type")) {
|
||||
SPDLOG_ERROR("[CrowdControl] Invalid payload received:\n{}", dataReceived);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SPDLOG_INFO("[CrowdControl] Received payload:\n{}", dataReceived.dump());
|
||||
|
||||
Effect* effect = new Effect();
|
||||
effect->lastExecutionResult = EffectResult::Initiate;
|
||||
effect->id = dataReceived["id"];
|
||||
@ -333,13 +282,13 @@ CrowdControl::Effect* CrowdControl::ParseMessage(char payload[512]) {
|
||||
effect->category = kEffectCatDamageTaken;
|
||||
effect->timeRemaining = 30000;
|
||||
effect->giEffect = new GameInteractionEffect::ModifyDefenseModifier();
|
||||
effect->giEffect->parameters[0] = 2;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = 2;
|
||||
break;
|
||||
case kEffectTakeDoubleDamage:
|
||||
effect->category = kEffectCatDamageTaken;
|
||||
effect->timeRemaining = 30000;
|
||||
effect->giEffect = new GameInteractionEffect::ModifyDefenseModifier();
|
||||
effect->giEffect->parameters[0] = -2;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = -2;
|
||||
break;
|
||||
case kEffectOneHitKo:
|
||||
effect->category = kEffectCatDamageTaken;
|
||||
@ -356,37 +305,37 @@ CrowdControl::Effect* CrowdControl::ParseMessage(char payload[512]) {
|
||||
effect->category = kEffectCatSpeed;
|
||||
effect->timeRemaining = 30000;
|
||||
effect->giEffect = new GameInteractionEffect::ModifyRunSpeedModifier();
|
||||
effect->giEffect->parameters[0] = 2;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = 2;
|
||||
break;
|
||||
case kEffectDecreaseSpeed:
|
||||
effect->category = kEffectCatSpeed;
|
||||
effect->timeRemaining = 30000;
|
||||
effect->giEffect = new GameInteractionEffect::ModifyRunSpeedModifier();
|
||||
effect->giEffect->parameters[0] = -2;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(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;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(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;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(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] = EQUIP_VALUE_BOOTS_IRON;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = EQUIP_VALUE_BOOTS_IRON;
|
||||
break;
|
||||
case kEffectForceHoverBoots:
|
||||
effect->category = kEffectCatBoots;
|
||||
effect->timeRemaining = 30000;
|
||||
effect->giEffect = new GameInteractionEffect::ForceEquipBoots();
|
||||
effect->giEffect->parameters[0] = EQUIP_VALUE_BOOTS_HOVER;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = EQUIP_VALUE_BOOTS_HOVER;
|
||||
break;
|
||||
case kEffectSlipperyFloor:
|
||||
effect->category = kEffectCatSlipperyFloor;
|
||||
@ -412,23 +361,23 @@ CrowdControl::Effect* CrowdControl::ParseMessage(char payload[512]) {
|
||||
// Hurt or Heal Link
|
||||
case kEffectEmptyHeart:
|
||||
effect->giEffect = new GameInteractionEffect::ModifyHealth();
|
||||
effect->giEffect->parameters[0] = receivedParameter * -1;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = receivedParameter * -1;
|
||||
break;
|
||||
case kEffectFillHeart:
|
||||
effect->giEffect = new GameInteractionEffect::ModifyHealth();
|
||||
effect->giEffect->parameters[0] = receivedParameter;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = receivedParameter;
|
||||
break;
|
||||
case kEffectKnockbackLinkWeak:
|
||||
effect->giEffect = new GameInteractionEffect::KnockbackPlayer();
|
||||
effect->giEffect->parameters[0] = 1;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = 1;
|
||||
break;
|
||||
case kEffectKnockbackLinkStrong:
|
||||
effect->giEffect = new GameInteractionEffect::KnockbackPlayer();
|
||||
effect->giEffect->parameters[0] = 3;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = 3;
|
||||
break;
|
||||
case kEffectKnockbackLinkMega:
|
||||
effect->giEffect = new GameInteractionEffect::KnockbackPlayer();
|
||||
effect->giEffect->parameters[0] = 6;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = 6;
|
||||
break;
|
||||
case kEffectBurnLink:
|
||||
effect->giEffect = new GameInteractionEffect::BurnPlayer();
|
||||
@ -441,109 +390,109 @@ CrowdControl::Effect* CrowdControl::ParseMessage(char payload[512]) {
|
||||
break;
|
||||
case kEffectKillLink:
|
||||
effect->giEffect = new GameInteractionEffect::SetPlayerHealth();
|
||||
effect->giEffect->parameters[0] = 0;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = 0;
|
||||
break;
|
||||
|
||||
// Give Items and Consumables
|
||||
case kEffectAddHeartContainer:
|
||||
effect->giEffect = new GameInteractionEffect::ModifyHeartContainers();
|
||||
effect->giEffect->parameters[0] = 1;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = 1;
|
||||
break;
|
||||
case kEffectFillMagic:
|
||||
effect->giEffect = new GameInteractionEffect::FillMagic();
|
||||
break;
|
||||
case kEffectAddRupees:
|
||||
effect->giEffect = new GameInteractionEffect::ModifyRupees();
|
||||
effect->giEffect->parameters[0] = receivedParameter;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = receivedParameter;
|
||||
break;
|
||||
case kEffectGiveDekuShield:
|
||||
effect->giEffect = new GameInteractionEffect::GiveOrTakeShield();
|
||||
effect->giEffect->parameters[0] = ITEM_SHIELD_DEKU;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = ITEM_SHIELD_DEKU;
|
||||
break;
|
||||
case kEffectGiveHylianShield:
|
||||
effect->giEffect = new GameInteractionEffect::GiveOrTakeShield();
|
||||
effect->giEffect->parameters[0] = ITEM_SHIELD_HYLIAN;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = ITEM_SHIELD_HYLIAN;
|
||||
break;
|
||||
case kEffectRefillSticks:
|
||||
effect->giEffect = new GameInteractionEffect::AddOrTakeAmmo();
|
||||
effect->giEffect->parameters[0] = receivedParameter;
|
||||
effect->giEffect->parameters[1] = ITEM_STICK;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = receivedParameter;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[1] = ITEM_STICK;
|
||||
break;
|
||||
case kEffectRefillNuts:
|
||||
effect->giEffect = new GameInteractionEffect::AddOrTakeAmmo();
|
||||
effect->giEffect->parameters[0] = receivedParameter;
|
||||
effect->giEffect->parameters[1] = ITEM_NUT;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = receivedParameter;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[1] = ITEM_NUT;
|
||||
break;
|
||||
case kEffectRefillBombs:
|
||||
effect->giEffect = new GameInteractionEffect::AddOrTakeAmmo();
|
||||
effect->giEffect->parameters[0] = receivedParameter;
|
||||
effect->giEffect->parameters[1] = ITEM_BOMB;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = receivedParameter;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[1] = ITEM_BOMB;
|
||||
break;
|
||||
case kEffectRefillSeeds:
|
||||
effect->giEffect = new GameInteractionEffect::AddOrTakeAmmo();
|
||||
effect->giEffect->parameters[0] = receivedParameter;
|
||||
effect->giEffect->parameters[1] = ITEM_SLINGSHOT;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = receivedParameter;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[1] = ITEM_SLINGSHOT;
|
||||
break;
|
||||
case kEffectRefillArrows:
|
||||
effect->giEffect = new GameInteractionEffect::AddOrTakeAmmo();
|
||||
effect->giEffect->parameters[0] = receivedParameter;
|
||||
effect->giEffect->parameters[1] = ITEM_BOW;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = receivedParameter;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[1] = ITEM_BOW;
|
||||
break;
|
||||
case kEffectRefillBombchus:
|
||||
effect->giEffect = new GameInteractionEffect::AddOrTakeAmmo();
|
||||
effect->giEffect->parameters[0] = receivedParameter;
|
||||
effect->giEffect->parameters[1] = ITEM_BOMBCHU;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = receivedParameter;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[1] = ITEM_BOMBCHU;
|
||||
break;
|
||||
|
||||
// Take Items and Consumables
|
||||
case kEffectRemoveHeartContainer:
|
||||
effect->giEffect = new GameInteractionEffect::ModifyHeartContainers();
|
||||
effect->giEffect->parameters[0] = -1;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(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;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = receivedParameter * -1;
|
||||
break;
|
||||
case kEffectTakeDekuShield:
|
||||
effect->giEffect = new GameInteractionEffect::GiveOrTakeShield();
|
||||
effect->giEffect->parameters[0] = -ITEM_SHIELD_DEKU;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = -ITEM_SHIELD_DEKU;
|
||||
break;
|
||||
case kEffectTakeHylianShield:
|
||||
effect->giEffect = new GameInteractionEffect::GiveOrTakeShield();
|
||||
effect->giEffect->parameters[0] = -ITEM_SHIELD_HYLIAN;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(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;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = receivedParameter * -1;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(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;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = receivedParameter * -1;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(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;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = receivedParameter * -1;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(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;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = receivedParameter * -1;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(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;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = receivedParameter * -1;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(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;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = receivedParameter * -1;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[1] = ITEM_BOMBCHU;
|
||||
break;
|
||||
|
||||
// Link Size Modifiers
|
||||
@ -551,25 +500,25 @@ CrowdControl::Effect* CrowdControl::ParseMessage(char payload[512]) {
|
||||
effect->category = kEffectCatLinkSize;
|
||||
effect->timeRemaining = 30000;
|
||||
effect->giEffect = new GameInteractionEffect::ModifyLinkSize();
|
||||
effect->giEffect->parameters[0] = GI_LINK_SIZE_GIANT;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(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;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(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;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(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;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_LINK_SIZE_SQUISHED;
|
||||
break;
|
||||
case kEffectInvisibleLink:
|
||||
effect->category = kEffectCatLinkSize;
|
||||
@ -585,11 +534,11 @@ CrowdControl::Effect* CrowdControl::ParseMessage(char payload[512]) {
|
||||
break;
|
||||
case kEffectSetTimeToDawn:
|
||||
effect->giEffect = new GameInteractionEffect::SetTimeOfDay();
|
||||
effect->giEffect->parameters[0] = GI_TIMEOFDAY_DAWN;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_TIMEOFDAY_DAWN;
|
||||
break;
|
||||
case kEffectSetTimeToDusk:
|
||||
effect->giEffect = new GameInteractionEffect::SetTimeOfDay();
|
||||
effect->giEffect->parameters[0] = GI_TIMEOFDAY_DUSK;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_TIMEOFDAY_DUSK;
|
||||
break;
|
||||
|
||||
// Visual Effects
|
||||
@ -632,186 +581,186 @@ CrowdControl::Effect* CrowdControl::ParseMessage(char payload[512]) {
|
||||
effect->category = kEffectCatRandomButtons;
|
||||
effect->timeRemaining = 30000;
|
||||
effect->giEffect = new GameInteractionEffect::PressRandomButton();
|
||||
effect->giEffect->parameters[0] = 30;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = 30;
|
||||
break;
|
||||
case kEffectClearCbuttons:
|
||||
effect->giEffect = new GameInteractionEffect::ClearAssignedButtons();
|
||||
effect->giEffect->parameters[0] = GI_BUTTONS_CBUTTONS;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_BUTTONS_CBUTTONS;
|
||||
break;
|
||||
case kEffectClearDpad:
|
||||
effect->giEffect = new GameInteractionEffect::ClearAssignedButtons();
|
||||
effect->giEffect->parameters[0] = GI_BUTTONS_DPAD;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(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;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_TP_DEST_LINKSHOUSE;
|
||||
break;
|
||||
case kEffectTpMinuet:
|
||||
effect->giEffect = new GameInteractionEffect::TeleportPlayer();
|
||||
effect->giEffect->parameters[0] = GI_TP_DEST_MINUET;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_TP_DEST_MINUET;
|
||||
break;
|
||||
case kEffectTpBolero:
|
||||
effect->giEffect = new GameInteractionEffect::TeleportPlayer();
|
||||
effect->giEffect->parameters[0] = GI_TP_DEST_BOLERO;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_TP_DEST_BOLERO;
|
||||
break;
|
||||
case kEffectTpSerenade:
|
||||
effect->giEffect = new GameInteractionEffect::TeleportPlayer();
|
||||
effect->giEffect->parameters[0] = GI_TP_DEST_SERENADE;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_TP_DEST_SERENADE;
|
||||
break;
|
||||
case kEffectTpRequiem:
|
||||
effect->giEffect = new GameInteractionEffect::TeleportPlayer();
|
||||
effect->giEffect->parameters[0] = GI_TP_DEST_REQUIEM;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_TP_DEST_REQUIEM;
|
||||
break;
|
||||
case kEffectTpNocturne:
|
||||
effect->giEffect = new GameInteractionEffect::TeleportPlayer();
|
||||
effect->giEffect->parameters[0] = GI_TP_DEST_NOCTURNE;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_TP_DEST_NOCTURNE;
|
||||
break;
|
||||
case kEffectTpPrelude:
|
||||
effect->giEffect = new GameInteractionEffect::TeleportPlayer();
|
||||
effect->giEffect->parameters[0] = GI_TP_DEST_PRELUDE;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(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;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_COSMETICS_TUNICS;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(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;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_COSMETICS_TUNICS;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(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;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_COSMETICS_TUNICS;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(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;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_COSMETICS_TUNICS;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(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;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_COSMETICS_TUNICS;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(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;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_COSMETICS_TUNICS;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(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;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_COSMETICS_TUNICS;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(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;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_COSMETICS_TUNICS;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(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;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_COSMETICS_TUNICS;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(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;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_COSMETICS_NAVI;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(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;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_COSMETICS_NAVI;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(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;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_COSMETICS_NAVI;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(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;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_COSMETICS_NAVI;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(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;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_COSMETICS_NAVI;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(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;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_COSMETICS_NAVI;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(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;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_COSMETICS_NAVI;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(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;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_COSMETICS_NAVI;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(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;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_COSMETICS_NAVI;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(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;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_COSMETICS_HAIR;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(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;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_COSMETICS_HAIR;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(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;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_COSMETICS_HAIR;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(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;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_COSMETICS_HAIR;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(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;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_COSMETICS_HAIR;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(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;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_COSMETICS_HAIR;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(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;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_COSMETICS_HAIR;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(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;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_COSMETICS_HAIR;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(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;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[0] = GI_COSMETICS_HAIR;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect->giEffect)->parameters[1] = GI_COLOR_BLACK;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -1,4 +1,4 @@
|
||||
#ifdef ENABLE_CROWD_CONTROL
|
||||
#ifdef ENABLE_REMOTE_CONTROL
|
||||
|
||||
#ifndef _CROWDCONTROL_C
|
||||
#define _CROWDCONTROL_C
|
||||
@ -73,33 +73,24 @@ class CrowdControl {
|
||||
EffectResult lastExecutionResult;
|
||||
} Effect;
|
||||
|
||||
std::thread ccThreadReceive;
|
||||
std::thread ccThreadProcess;
|
||||
|
||||
TCPsocket tcpsock;
|
||||
IPaddress ip;
|
||||
|
||||
bool isEnabled;
|
||||
bool connected;
|
||||
|
||||
char received[512];
|
||||
|
||||
std::vector<Effect*> activeEffects;
|
||||
std::mutex activeEffectsMutex;
|
||||
|
||||
void ListenToServer();
|
||||
void HandleRemoteData(nlohmann::json payload);
|
||||
void ProcessActiveEffects();
|
||||
|
||||
void EmitMessage(TCPsocket socket, uint32_t eventId, long timeRemaining, EffectResult status);
|
||||
Effect* ParseMessage(char payload[512]);
|
||||
void EmitMessage(uint32_t eventId, long timeRemaining, EffectResult status);
|
||||
Effect* ParseMessage(nlohmann::json payload);
|
||||
EffectResult ExecuteEffect(Effect* effect);
|
||||
EffectResult CanApplyEffect(Effect *effect);
|
||||
EffectResult TranslateGiEnum(GameInteractionEffectQueryResult giResult);
|
||||
|
||||
public:
|
||||
static CrowdControl* Instance;
|
||||
void Init();
|
||||
void Shutdown();
|
||||
void Enable();
|
||||
void Disable();
|
||||
};
|
||||
|
@ -99,7 +99,7 @@ static bool ActorSpawnHandler(std::shared_ptr<LUS::Console> Console, const std::
|
||||
|
||||
static bool KillPlayerHandler(std::shared_ptr<LUS::Console> Console, const std::vector<std::string>&, std::string* output) {
|
||||
GameInteractionEffectBase* effect = new GameInteractionEffect::SetPlayerHealth();
|
||||
effect->parameters[0] = 0;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0] = 0;
|
||||
GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect);
|
||||
if (result == GameInteractionEffectQueryResult::Possible) {
|
||||
INFO_MESSAGE("[SOH] You've met with a terrible fate, haven't you?");
|
||||
@ -130,7 +130,7 @@ static bool SetPlayerHealthHandler(std::shared_ptr<LUS::Console> Console, const
|
||||
}
|
||||
|
||||
GameInteractionEffectBase* effect = new GameInteractionEffect::SetPlayerHealth();
|
||||
effect->parameters[0] = health;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0] = health;
|
||||
GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect);
|
||||
if (result == GameInteractionEffectQueryResult::Possible) {
|
||||
INFO_MESSAGE("[SOH] Player health updated to %d", health);
|
||||
@ -247,8 +247,8 @@ static bool AddAmmoHandler(std::shared_ptr<LUS::Console> Console, const std::vec
|
||||
}
|
||||
|
||||
GameInteractionEffectBase* effect = new GameInteractionEffect::AddOrTakeAmmo();
|
||||
effect->parameters[0] = amount;
|
||||
effect->parameters[1] = it->second;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0] = amount;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[1] = it->second;
|
||||
GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect);
|
||||
|
||||
if (result == GameInteractionEffectQueryResult::Possible) {
|
||||
@ -287,8 +287,8 @@ static bool TakeAmmoHandler(std::shared_ptr<LUS::Console> Console, const std::ve
|
||||
}
|
||||
|
||||
GameInteractionEffectBase* effect = new GameInteractionEffect::AddOrTakeAmmo();
|
||||
effect->parameters[0] = -amount;
|
||||
effect->parameters[1] = it->second;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0] = -amount;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[1] = it->second;
|
||||
GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect);
|
||||
|
||||
if (result == GameInteractionEffectQueryResult::Possible) {
|
||||
@ -577,7 +577,7 @@ static bool InvisibleHandler(std::shared_ptr<LUS::Console> Console, const std::v
|
||||
return 1;
|
||||
}
|
||||
|
||||
GameInteractionEffectBase* effect = new GameInteractionEffect::InvisibleLink();
|
||||
RemovableGameInteractionEffect* effect = new GameInteractionEffect::InvisibleLink();
|
||||
GameInteractionEffectQueryResult result =
|
||||
state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect);
|
||||
if (result == GameInteractionEffectQueryResult::Possible) {
|
||||
@ -604,8 +604,8 @@ static bool GiantLinkHandler(std::shared_ptr<LUS::Console> Console, const std::v
|
||||
return 1;
|
||||
}
|
||||
|
||||
GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyLinkSize();
|
||||
effect->parameters[0] = GI_LINK_SIZE_GIANT;
|
||||
RemovableGameInteractionEffect* effect = new GameInteractionEffect::ModifyLinkSize();
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0] = GI_LINK_SIZE_GIANT;
|
||||
GameInteractionEffectQueryResult result =
|
||||
state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect);
|
||||
if (result == GameInteractionEffectQueryResult::Possible) {
|
||||
@ -632,8 +632,8 @@ static bool MinishLinkHandler(std::shared_ptr<LUS::Console> Console, const std::
|
||||
return 1;
|
||||
}
|
||||
|
||||
GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyLinkSize();
|
||||
effect->parameters[0] = GI_LINK_SIZE_MINISH;
|
||||
RemovableGameInteractionEffect* effect = new GameInteractionEffect::ModifyLinkSize();
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0] = GI_LINK_SIZE_MINISH;
|
||||
GameInteractionEffectQueryResult result =
|
||||
state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect);
|
||||
if (result == GameInteractionEffectQueryResult::Possible) {
|
||||
@ -666,7 +666,7 @@ static bool AddHeartContainerHandler(std::shared_ptr<LUS::Console> Console, cons
|
||||
}
|
||||
|
||||
GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyHeartContainers();
|
||||
effect->parameters[0] = hearts;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0] = hearts;
|
||||
GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect);
|
||||
if (result == GameInteractionEffectQueryResult::Possible) {
|
||||
INFO_MESSAGE("[SOH] Added %d heart containers", hearts);
|
||||
@ -697,7 +697,7 @@ static bool RemoveHeartContainerHandler(std::shared_ptr<LUS::Console> Console, c
|
||||
}
|
||||
|
||||
GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyHeartContainers();
|
||||
effect->parameters[0] = -hearts;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0] = -hearts;
|
||||
GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect);
|
||||
if (result == GameInteractionEffectQueryResult::Possible) {
|
||||
INFO_MESSAGE("[SOH] Removed %d heart containers", hearts);
|
||||
@ -717,7 +717,7 @@ static bool GravityHandler(std::shared_ptr<LUS::Console> Console, const std::vec
|
||||
GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyGravity();
|
||||
|
||||
try {
|
||||
effect->parameters[0] = LUS::Math::clamp(std::stoi(args[1], nullptr, 10), GI_GRAVITY_LEVEL_LIGHT, GI_GRAVITY_LEVEL_HEAVY);
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0] = LUS::Math::clamp(std::stoi(args[1], nullptr, 10), GI_GRAVITY_LEVEL_LIGHT, GI_GRAVITY_LEVEL_HEAVY);
|
||||
} catch (std::invalid_argument const& ex) {
|
||||
ERROR_MESSAGE("[SOH] Gravity value must be a number.");
|
||||
return 1;
|
||||
@ -747,7 +747,7 @@ static bool NoUIHandler(std::shared_ptr<LUS::Console> Console, const std::vector
|
||||
return 1;
|
||||
}
|
||||
|
||||
GameInteractionEffectBase* effect = new GameInteractionEffect::NoUI();
|
||||
RemovableGameInteractionEffect* effect = new GameInteractionEffect::NoUI();
|
||||
GameInteractionEffectQueryResult result =
|
||||
state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect);
|
||||
|
||||
@ -782,7 +782,7 @@ static bool DefenseModifierHandler(std::shared_ptr<LUS::Console> Console, const
|
||||
GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyDefenseModifier();
|
||||
|
||||
try {
|
||||
effect->parameters[0] = std::stoi(args[1], nullptr, 10);
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0] = std::stoi(args[1], nullptr, 10);
|
||||
} catch (std::invalid_argument const& ex) {
|
||||
ERROR_MESSAGE("[SOH] Defense modifier value must be a number.");
|
||||
return 1;
|
||||
@ -790,7 +790,7 @@ static bool DefenseModifierHandler(std::shared_ptr<LUS::Console> Console, const
|
||||
|
||||
GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect);
|
||||
if (result == GameInteractionEffectQueryResult::Possible) {
|
||||
INFO_MESSAGE("[SOH] Defense modifier set to %d", effect->parameters[0]);
|
||||
INFO_MESSAGE("[SOH] Defense modifier set to %d", dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0]);
|
||||
return 0;
|
||||
} else {
|
||||
INFO_MESSAGE("[SOH] Command failed: Could not set defense modifier.");
|
||||
@ -812,7 +812,7 @@ static bool DamageHandler(std::shared_ptr<LUS::Console> Console, const std::vect
|
||||
return 1;
|
||||
}
|
||||
|
||||
effect->parameters[0] = -value;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0] = -value;
|
||||
} catch (std::invalid_argument const& ex) {
|
||||
ERROR_MESSAGE("[SOH] Damage value must be a number.");
|
||||
return 1;
|
||||
@ -842,7 +842,7 @@ static bool HealHandler(std::shared_ptr<LUS::Console> Console, const std::vector
|
||||
return 1;
|
||||
}
|
||||
|
||||
effect->parameters[0] = value;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0] = value;
|
||||
} catch (std::invalid_argument const& ex) {
|
||||
ERROR_MESSAGE("[SOH] Damage value must be a number.");
|
||||
return 1;
|
||||
@ -898,7 +898,7 @@ static bool NoZHandler(std::shared_ptr<LUS::Console> Console, const std::vector<
|
||||
return 1;
|
||||
}
|
||||
|
||||
GameInteractionEffectBase* effect = new GameInteractionEffect::DisableZTargeting();
|
||||
RemovableGameInteractionEffect* effect = new GameInteractionEffect::DisableZTargeting();
|
||||
GameInteractionEffectQueryResult result =
|
||||
state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect);
|
||||
|
||||
@ -926,7 +926,7 @@ static bool OneHitKOHandler(std::shared_ptr<LUS::Console> Console, const std::ve
|
||||
return 1;
|
||||
}
|
||||
|
||||
GameInteractionEffectBase* effect = new GameInteractionEffect::OneHitKO();
|
||||
RemovableGameInteractionEffect* effect = new GameInteractionEffect::OneHitKO();
|
||||
GameInteractionEffectQueryResult result =
|
||||
state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect);
|
||||
|
||||
@ -954,7 +954,7 @@ static bool PacifistHandler(std::shared_ptr<LUS::Console> Console, const std::ve
|
||||
return 1;
|
||||
}
|
||||
|
||||
GameInteractionEffectBase* effect = new GameInteractionEffect::PacifistMode();
|
||||
RemovableGameInteractionEffect* effect = new GameInteractionEffect::PacifistMode();
|
||||
GameInteractionEffectQueryResult result =
|
||||
state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect);
|
||||
|
||||
@ -982,8 +982,8 @@ static bool PaperLinkHandler(std::shared_ptr<LUS::Console> Console, const std::v
|
||||
return 1;
|
||||
}
|
||||
|
||||
GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyLinkSize();
|
||||
effect->parameters[0] = GI_LINK_SIZE_PAPER;
|
||||
RemovableGameInteractionEffect* effect = new GameInteractionEffect::ModifyLinkSize();
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0] = GI_LINK_SIZE_PAPER;
|
||||
GameInteractionEffectQueryResult result =
|
||||
state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect);
|
||||
|
||||
@ -1011,7 +1011,7 @@ static bool RainstormHandler(std::shared_ptr<LUS::Console> Console, const std::v
|
||||
return 1;
|
||||
}
|
||||
|
||||
GameInteractionEffectBase* effect = new GameInteractionEffect::WeatherRainstorm();
|
||||
RemovableGameInteractionEffect* effect = new GameInteractionEffect::WeatherRainstorm();
|
||||
GameInteractionEffectQueryResult result =
|
||||
state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect);
|
||||
|
||||
@ -1039,7 +1039,7 @@ static bool ReverseControlsHandler(std::shared_ptr<LUS::Console> Console, const
|
||||
return 1;
|
||||
}
|
||||
|
||||
GameInteractionEffectBase* effect = new GameInteractionEffect::ReverseControls();
|
||||
RemovableGameInteractionEffect* effect = new GameInteractionEffect::ReverseControls();
|
||||
GameInteractionEffectQueryResult result =
|
||||
state ? GameInteractor::ApplyEffect(effect) : GameInteractor::RemoveEffect(effect);
|
||||
|
||||
@ -1062,7 +1062,7 @@ static bool UpdateRupeesHandler(std::shared_ptr<LUS::Console> Console, const std
|
||||
GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyRupees();
|
||||
|
||||
try {
|
||||
effect->parameters[0] = std::stoi(args[1], nullptr, 10);
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0] = std::stoi(args[1], nullptr, 10);
|
||||
} catch (std::invalid_argument const& ex) {
|
||||
ERROR_MESSAGE("[SOH] Rupee value must be a number.");
|
||||
return 1;
|
||||
@ -1086,7 +1086,7 @@ static bool SpeedModifierHandler(std::shared_ptr<LUS::Console> Console, const st
|
||||
GameInteractionEffectBase* effect = new GameInteractionEffect::ModifyRunSpeedModifier();
|
||||
|
||||
try {
|
||||
effect->parameters[0] = std::stoi(args[1], nullptr, 10);
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0] = std::stoi(args[1], nullptr, 10);
|
||||
} catch (std::invalid_argument const& ex) {
|
||||
ERROR_MESSAGE("[SOH] Speed modifier value must be a number.");
|
||||
return 1;
|
||||
@ -1121,7 +1121,7 @@ static bool BootsHandler(std::shared_ptr<LUS::Console> Console, const std::vecto
|
||||
}
|
||||
|
||||
GameInteractionEffectBase* effect = new GameInteractionEffect::ForceEquipBoots();
|
||||
effect->parameters[0] = it->second;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0] = it->second;
|
||||
GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect);
|
||||
|
||||
if (result == GameInteractionEffectQueryResult::Possible) {
|
||||
@ -1152,7 +1152,7 @@ static bool GiveShieldHandler(std::shared_ptr<LUS::Console> Console, const std::
|
||||
}
|
||||
|
||||
GameInteractionEffectBase* effect = new GameInteractionEffect::GiveOrTakeShield();
|
||||
effect->parameters[0] = it->second;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0] = it->second;
|
||||
GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect);
|
||||
|
||||
if (result == GameInteractionEffectQueryResult::Possible) {
|
||||
@ -1177,7 +1177,7 @@ static bool TakeShieldHandler(std::shared_ptr<LUS::Console> Console, const std::
|
||||
}
|
||||
|
||||
GameInteractionEffectBase* effect = new GameInteractionEffect::GiveOrTakeShield();
|
||||
effect->parameters[0] = it->second * -1;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0] = it->second * -1;
|
||||
GameInteractionEffectQueryResult result = GameInteractor::ApplyEffect(effect);
|
||||
|
||||
if (result == GameInteractionEffectQueryResult::Possible) {
|
||||
@ -1203,7 +1203,7 @@ static bool KnockbackHandler(std::shared_ptr<LUS::Console> Console, const std::v
|
||||
return 1;
|
||||
}
|
||||
|
||||
effect->parameters[0] = value;
|
||||
dynamic_cast<ParameterizedGameInteractionEffect*>(effect)->parameters[0] = value;
|
||||
} catch (std::invalid_argument const& ex) {
|
||||
ERROR_MESSAGE("[SOH] Knockback value must be a number.");
|
||||
return 1;
|
||||
|
@ -31,11 +31,11 @@ GameInteractionEffectQueryResult GameInteractionEffectBase::Apply() {
|
||||
}
|
||||
|
||||
/// For most effects, CanBeRemoved is the same as CanBeApplied. When its not: please override `CanBeRemoved`.
|
||||
GameInteractionEffectQueryResult GameInteractionEffectBase::CanBeRemoved() {
|
||||
GameInteractionEffectQueryResult RemovableGameInteractionEffect::CanBeRemoved() {
|
||||
return CanBeApplied();
|
||||
}
|
||||
|
||||
GameInteractionEffectQueryResult GameInteractionEffectBase::Remove() {
|
||||
GameInteractionEffectQueryResult RemovableGameInteractionEffect::Remove() {
|
||||
GameInteractionEffectQueryResult result = CanBeRemoved();
|
||||
if (result != GameInteractionEffectQueryResult::Possible) {
|
||||
return result;
|
||||
|
@ -15,38 +15,46 @@ enum GameInteractionEffectQueryResult {
|
||||
class GameInteractionEffectBase {
|
||||
public:
|
||||
virtual GameInteractionEffectQueryResult CanBeApplied() = 0;
|
||||
virtual GameInteractionEffectQueryResult CanBeRemoved();
|
||||
GameInteractionEffectQueryResult Apply();
|
||||
GameInteractionEffectQueryResult Remove();
|
||||
int32_t parameters[3];
|
||||
|
||||
protected:
|
||||
protected:
|
||||
virtual void _Apply() = 0;
|
||||
};
|
||||
|
||||
class RemovableGameInteractionEffect: public GameInteractionEffectBase {
|
||||
public:
|
||||
virtual GameInteractionEffectQueryResult CanBeRemoved();
|
||||
GameInteractionEffectQueryResult Remove();
|
||||
protected:
|
||||
virtual void _Remove() {};
|
||||
};
|
||||
|
||||
class ParameterizedGameInteractionEffect {
|
||||
public:
|
||||
int32_t parameters[3];
|
||||
};
|
||||
|
||||
namespace GameInteractionEffect {
|
||||
class SetSceneFlag: public GameInteractionEffectBase {
|
||||
class SetSceneFlag: public GameInteractionEffectBase, public ParameterizedGameInteractionEffect {
|
||||
GameInteractionEffectQueryResult CanBeApplied() override;
|
||||
void _Apply() override;
|
||||
};
|
||||
|
||||
class UnsetSceneFlag: public GameInteractionEffectBase {
|
||||
class UnsetSceneFlag: public GameInteractionEffectBase, public ParameterizedGameInteractionEffect {
|
||||
GameInteractionEffectQueryResult CanBeApplied() override;
|
||||
void _Apply() override;
|
||||
};
|
||||
|
||||
class SetFlag: public GameInteractionEffectBase {
|
||||
class SetFlag: public GameInteractionEffectBase, public ParameterizedGameInteractionEffect {
|
||||
GameInteractionEffectQueryResult CanBeApplied() override;
|
||||
void _Apply() override;
|
||||
};
|
||||
|
||||
class UnsetFlag: public GameInteractionEffectBase {
|
||||
class UnsetFlag: public GameInteractionEffectBase, public ParameterizedGameInteractionEffect {
|
||||
GameInteractionEffectQueryResult CanBeApplied() override;
|
||||
void _Apply() override;
|
||||
};
|
||||
|
||||
class ModifyHeartContainers: public GameInteractionEffectBase {
|
||||
class ModifyHeartContainers: public GameInteractionEffectBase, public ParameterizedGameInteractionEffect {
|
||||
GameInteractionEffectQueryResult CanBeApplied() override;
|
||||
void _Apply() override;
|
||||
};
|
||||
@ -61,29 +69,29 @@ namespace GameInteractionEffect {
|
||||
void _Apply() override;
|
||||
};
|
||||
|
||||
class ModifyRupees: public GameInteractionEffectBase {
|
||||
class ModifyRupees: public GameInteractionEffectBase, public ParameterizedGameInteractionEffect {
|
||||
GameInteractionEffectQueryResult CanBeApplied() override;
|
||||
void _Apply() override;
|
||||
};
|
||||
|
||||
class NoUI: public GameInteractionEffectBase {
|
||||
class NoUI: public RemovableGameInteractionEffect {
|
||||
GameInteractionEffectQueryResult CanBeApplied() override;
|
||||
void _Apply() override;
|
||||
void _Remove() override;
|
||||
};
|
||||
|
||||
class ModifyGravity: public GameInteractionEffectBase {
|
||||
class ModifyGravity: public RemovableGameInteractionEffect, public ParameterizedGameInteractionEffect {
|
||||
GameInteractionEffectQueryResult CanBeApplied() override;
|
||||
void _Apply() override;
|
||||
void _Remove() override;
|
||||
};
|
||||
|
||||
class ModifyHealth: public GameInteractionEffectBase {
|
||||
class ModifyHealth: public GameInteractionEffectBase, public ParameterizedGameInteractionEffect {
|
||||
GameInteractionEffectQueryResult CanBeApplied() override;
|
||||
void _Apply() override;
|
||||
};
|
||||
|
||||
class SetPlayerHealth: public GameInteractionEffectBase {
|
||||
class SetPlayerHealth: public GameInteractionEffectBase, public ParameterizedGameInteractionEffect {
|
||||
GameInteractionEffectQueryResult CanBeApplied() override;
|
||||
void _Apply() override;
|
||||
};
|
||||
@ -103,98 +111,98 @@ namespace GameInteractionEffect {
|
||||
void _Apply() override;
|
||||
};
|
||||
|
||||
class KnockbackPlayer: public GameInteractionEffectBase {
|
||||
class KnockbackPlayer: public GameInteractionEffectBase, public ParameterizedGameInteractionEffect {
|
||||
GameInteractionEffectQueryResult CanBeApplied() override;
|
||||
void _Apply() override;
|
||||
};
|
||||
|
||||
class ModifyLinkSize: public GameInteractionEffectBase {
|
||||
class ModifyLinkSize: public RemovableGameInteractionEffect, public ParameterizedGameInteractionEffect {
|
||||
GameInteractionEffectQueryResult CanBeApplied() override;
|
||||
void _Apply() override;
|
||||
void _Remove() override;
|
||||
};
|
||||
|
||||
class InvisibleLink : public GameInteractionEffectBase {
|
||||
class InvisibleLink : public RemovableGameInteractionEffect, public ParameterizedGameInteractionEffect {
|
||||
GameInteractionEffectQueryResult CanBeApplied() override;
|
||||
void _Apply() override;
|
||||
void _Remove() override;
|
||||
};
|
||||
|
||||
class PacifistMode : public GameInteractionEffectBase {
|
||||
class PacifistMode : public RemovableGameInteractionEffect {
|
||||
GameInteractionEffectQueryResult CanBeApplied() override;
|
||||
void _Apply() override;
|
||||
void _Remove() override;
|
||||
};
|
||||
|
||||
class DisableZTargeting: public GameInteractionEffectBase {
|
||||
class DisableZTargeting: public RemovableGameInteractionEffect, public ParameterizedGameInteractionEffect {
|
||||
GameInteractionEffectQueryResult CanBeApplied() override;
|
||||
void _Apply() override;
|
||||
void _Remove() override;
|
||||
};
|
||||
|
||||
class WeatherRainstorm: public GameInteractionEffectBase {
|
||||
class WeatherRainstorm: public RemovableGameInteractionEffect {
|
||||
GameInteractionEffectQueryResult CanBeApplied() override;
|
||||
void _Apply() override;
|
||||
void _Remove() override;
|
||||
};
|
||||
|
||||
class ReverseControls: public GameInteractionEffectBase {
|
||||
class ReverseControls: public RemovableGameInteractionEffect {
|
||||
GameInteractionEffectQueryResult CanBeApplied() override;
|
||||
void _Apply() override;
|
||||
void _Remove() override;
|
||||
};
|
||||
|
||||
class ForceEquipBoots: public GameInteractionEffectBase {
|
||||
class ForceEquipBoots: public RemovableGameInteractionEffect, public ParameterizedGameInteractionEffect {
|
||||
GameInteractionEffectQueryResult CanBeApplied() override;
|
||||
void _Apply() override;
|
||||
void _Remove() override;
|
||||
};
|
||||
|
||||
class ModifyRunSpeedModifier: public GameInteractionEffectBase {
|
||||
class ModifyRunSpeedModifier: public RemovableGameInteractionEffect, public ParameterizedGameInteractionEffect {
|
||||
GameInteractionEffectQueryResult CanBeApplied() override;
|
||||
void _Apply() override;
|
||||
void _Remove() override;
|
||||
};
|
||||
|
||||
class OneHitKO : public GameInteractionEffectBase {
|
||||
class OneHitKO : public RemovableGameInteractionEffect {
|
||||
GameInteractionEffectQueryResult CanBeApplied() override;
|
||||
void _Apply() override;
|
||||
void _Remove() override;
|
||||
};
|
||||
|
||||
class ModifyDefenseModifier: public GameInteractionEffectBase {
|
||||
class ModifyDefenseModifier: public RemovableGameInteractionEffect, public ParameterizedGameInteractionEffect {
|
||||
GameInteractionEffectQueryResult CanBeApplied() override;
|
||||
void _Apply() override;
|
||||
void _Remove() override;
|
||||
};
|
||||
|
||||
class GiveOrTakeShield: public GameInteractionEffectBase {
|
||||
class GiveOrTakeShield: public GameInteractionEffectBase, public ParameterizedGameInteractionEffect {
|
||||
GameInteractionEffectQueryResult CanBeApplied() override;
|
||||
void _Apply() override;
|
||||
};
|
||||
|
||||
class TeleportPlayer: public GameInteractionEffectBase {
|
||||
class TeleportPlayer: public GameInteractionEffectBase, public ParameterizedGameInteractionEffect {
|
||||
GameInteractionEffectQueryResult CanBeApplied() override;
|
||||
void _Apply() override;
|
||||
};
|
||||
|
||||
class ClearAssignedButtons: public GameInteractionEffectBase {
|
||||
class ClearAssignedButtons: public GameInteractionEffectBase, public ParameterizedGameInteractionEffect {
|
||||
GameInteractionEffectQueryResult CanBeApplied() override;
|
||||
void _Apply() override;
|
||||
};
|
||||
|
||||
class SetTimeOfDay: public GameInteractionEffectBase {
|
||||
class SetTimeOfDay: public GameInteractionEffectBase, public ParameterizedGameInteractionEffect {
|
||||
GameInteractionEffectQueryResult CanBeApplied() override;
|
||||
void _Apply() override;
|
||||
};
|
||||
|
||||
class SetCollisionViewer: public GameInteractionEffectBase {
|
||||
class SetCollisionViewer: public RemovableGameInteractionEffect {
|
||||
GameInteractionEffectQueryResult CanBeApplied() override;
|
||||
void _Apply() override;
|
||||
void _Remove() override;
|
||||
};
|
||||
|
||||
class SetCosmeticsColor: public GameInteractionEffectBase {
|
||||
class SetCosmeticsColor: public GameInteractionEffectBase, public ParameterizedGameInteractionEffect {
|
||||
GameInteractionEffectQueryResult CanBeApplied() override;
|
||||
void _Apply() override;
|
||||
};
|
||||
@ -204,52 +212,52 @@ namespace GameInteractionEffect {
|
||||
void _Apply() override;
|
||||
};
|
||||
|
||||
class PressButton: public GameInteractionEffectBase {
|
||||
class PressButton: public GameInteractionEffectBase, public ParameterizedGameInteractionEffect {
|
||||
GameInteractionEffectQueryResult CanBeApplied() override;
|
||||
void _Apply() override;
|
||||
};
|
||||
|
||||
class PressRandomButton: public GameInteractionEffectBase {
|
||||
class PressRandomButton: public GameInteractionEffectBase, public ParameterizedGameInteractionEffect {
|
||||
GameInteractionEffectQueryResult CanBeApplied() override;
|
||||
void _Apply() override;
|
||||
};
|
||||
|
||||
class AddOrTakeAmmo: public GameInteractionEffectBase {
|
||||
class AddOrTakeAmmo: public GameInteractionEffectBase, public ParameterizedGameInteractionEffect {
|
||||
GameInteractionEffectQueryResult CanBeApplied() override;
|
||||
void _Apply() override;
|
||||
};
|
||||
|
||||
class RandomBombFuseTimer: public GameInteractionEffectBase {
|
||||
class RandomBombFuseTimer: public RemovableGameInteractionEffect {
|
||||
GameInteractionEffectQueryResult CanBeApplied() override;
|
||||
void _Apply() override;
|
||||
void _Remove() override;
|
||||
};
|
||||
|
||||
class DisableLedgeGrabs: public GameInteractionEffectBase {
|
||||
class DisableLedgeGrabs: public RemovableGameInteractionEffect {
|
||||
GameInteractionEffectQueryResult CanBeApplied() override;
|
||||
void _Apply() override;
|
||||
void _Remove() override;
|
||||
};
|
||||
|
||||
class RandomWind: public GameInteractionEffectBase {
|
||||
class RandomWind: public RemovableGameInteractionEffect {
|
||||
GameInteractionEffectQueryResult CanBeApplied() override;
|
||||
void _Apply() override;
|
||||
void _Remove() override;
|
||||
};
|
||||
|
||||
class RandomBonks: public GameInteractionEffectBase {
|
||||
class RandomBonks: public RemovableGameInteractionEffect {
|
||||
GameInteractionEffectQueryResult CanBeApplied() override;
|
||||
void _Apply() override;
|
||||
void _Remove() override;
|
||||
};
|
||||
|
||||
class PlayerInvincibility: public GameInteractionEffectBase {
|
||||
class PlayerInvincibility: public RemovableGameInteractionEffect {
|
||||
GameInteractionEffectQueryResult CanBeApplied() override;
|
||||
void _Apply() override;
|
||||
void _Remove() override;
|
||||
};
|
||||
|
||||
class SlipperyFloor: public GameInteractionEffectBase {
|
||||
class SlipperyFloor: public RemovableGameInteractionEffect {
|
||||
GameInteractionEffectQueryResult CanBeApplied() override;
|
||||
void _Apply() override;
|
||||
void _Remove() override;
|
||||
|
@ -31,7 +31,7 @@ GameInteractionEffectQueryResult GameInteractor::ApplyEffect(GameInteractionEffe
|
||||
return effect->Apply();
|
||||
}
|
||||
|
||||
GameInteractionEffectQueryResult GameInteractor::RemoveEffect(GameInteractionEffectBase* effect) {
|
||||
GameInteractionEffectQueryResult GameInteractor::RemoveEffect(RemovableGameInteractionEffect* effect) {
|
||||
return effect->Remove();
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,11 @@
|
||||
#include "soh/Enhancements/item-tables/ItemTableTypes.h"
|
||||
#include <z64.h>
|
||||
|
||||
typedef enum {
|
||||
GI_SCHEME_SAIL,
|
||||
GI_SCHEME_CROWD_CONTROL,
|
||||
} GIScheme;
|
||||
|
||||
typedef enum {
|
||||
/* 0x00 */ GI_LINK_SIZE_NORMAL,
|
||||
/* 0x01 */ GI_LINK_SIZE_GIANT,
|
||||
@ -92,10 +97,15 @@ void GameInteractor_SetTriforceHuntCreditsWarpActive(uint8_t state);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
#ifdef ENABLE_REMOTE_CONTROL
|
||||
#include <SDL2/SDL_net.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
#endif
|
||||
|
||||
#define DEFINE_HOOK(name, type) \
|
||||
struct name { \
|
||||
typedef std::function<type> fn; \
|
||||
@ -132,10 +142,24 @@ public:
|
||||
static void SetPacifistMode(bool active);
|
||||
};
|
||||
|
||||
#ifdef ENABLE_REMOTE_CONTROL
|
||||
bool isRemoteInteractorEnabled;
|
||||
bool isRemoteInteractorConnected;
|
||||
|
||||
void EnableRemoteInteractor();
|
||||
void DisableRemoteInteractor();
|
||||
void RegisterRemoteDataHandler(std::function<void(char payload[512])> method);
|
||||
void RegisterRemoteJsonHandler(std::function<void(nlohmann::json)> method);
|
||||
void RegisterRemoteConnectedHandler(std::function<void()> method);
|
||||
void RegisterRemoteDisconnectedHandler(std::function<void()> method);
|
||||
void TransmitDataToRemote(const char* payload);
|
||||
void TransmitJsonToRemote(nlohmann::json packet);
|
||||
#endif
|
||||
|
||||
// Effects
|
||||
static GameInteractionEffectQueryResult CanApplyEffect(GameInteractionEffectBase* effect);
|
||||
static GameInteractionEffectQueryResult ApplyEffect(GameInteractionEffectBase* effect);
|
||||
static GameInteractionEffectQueryResult RemoveEffect(GameInteractionEffectBase* effect);
|
||||
static GameInteractionEffectQueryResult RemoveEffect(RemovableGameInteractionEffect* effect);
|
||||
|
||||
// Game Hooks
|
||||
template <typename H> struct RegisteredGameHooks { inline static std::vector<typename H::fn> functions; };
|
||||
@ -238,6 +262,21 @@ public:
|
||||
static GameInteractionEffectQueryResult SpawnEnemyWithOffset(uint32_t enemyId, int32_t enemyParams);
|
||||
static GameInteractionEffectQueryResult SpawnActor(uint32_t actorId, int32_t actorParams);
|
||||
};
|
||||
|
||||
private:
|
||||
#ifdef ENABLE_REMOTE_CONTROL
|
||||
IPaddress remoteIP;
|
||||
TCPsocket remoteSocket;
|
||||
std::thread remoteThreadReceive;
|
||||
std::function<void(char payload[512])> remoteDataHandler;
|
||||
std::function<void(nlohmann::json)> remoteJsonHandler;
|
||||
std::function<void()> remoteConnectedHandler;
|
||||
std::function<void()> remoteDisconnectedHandler;
|
||||
|
||||
void ReceiveFromServer();
|
||||
void HandleRemoteData(char payload[512]);
|
||||
void HandleRemoteJson(std::string payload);
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif /* __cplusplus */
|
||||
|
182
soh/soh/Enhancements/game-interactor/GameInteractor_Remote.cpp
Normal file
182
soh/soh/Enhancements/game-interactor/GameInteractor_Remote.cpp
Normal file
@ -0,0 +1,182 @@
|
||||
#ifdef ENABLE_REMOTE_CONTROL
|
||||
|
||||
#include "GameInteractor.h"
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <ImGui/imgui.h>
|
||||
#include <ImGui/imgui_internal.h>
|
||||
#include <unordered_map>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <libultraship/libultraship.h>
|
||||
|
||||
// MARK: - Remote
|
||||
|
||||
void GameInteractor::EnableRemoteInteractor() {
|
||||
if (isRemoteInteractorEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (SDLNet_ResolveHost(&remoteIP, CVarGetString("gRemote.IP", "127.0.0.1"), CVarGetInteger("gRemote.Port", 43384)) == -1) {
|
||||
SPDLOG_ERROR("[GameInteractor] SDLNet_ResolveHost: {}", SDLNet_GetError());
|
||||
}
|
||||
|
||||
isRemoteInteractorEnabled = true;
|
||||
|
||||
// First check if there is a thread running, if so, join it
|
||||
if (remoteThreadReceive.joinable()) {
|
||||
remoteThreadReceive.join();
|
||||
}
|
||||
|
||||
remoteThreadReceive = std::thread(&GameInteractor::ReceiveFromServer, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Raw data handler
|
||||
*
|
||||
* If you are developing a new remote, you should probably use the json methods instead. This
|
||||
* method requires you to parse the data and ensure packets are complete manually, we cannot
|
||||
* gaurentee that the data will be complete, or that it will only contain one packet with this
|
||||
*/
|
||||
void GameInteractor::RegisterRemoteDataHandler(std::function<void(char payload[512])> method) {
|
||||
remoteDataHandler = method;
|
||||
}
|
||||
|
||||
/**
|
||||
* Json handler
|
||||
*
|
||||
* This method will be called when a complete json packet is received. All json packets must
|
||||
* be delimited by a null terminator (\0).
|
||||
*/
|
||||
void GameInteractor::RegisterRemoteJsonHandler(std::function<void(nlohmann::json)> method) {
|
||||
remoteJsonHandler = method;
|
||||
}
|
||||
|
||||
void GameInteractor::RegisterRemoteConnectedHandler(std::function<void()> method) {
|
||||
remoteConnectedHandler = method;
|
||||
}
|
||||
|
||||
void GameInteractor::RegisterRemoteDisconnectedHandler(std::function<void()> method) {
|
||||
remoteDisconnectedHandler = method;
|
||||
}
|
||||
|
||||
void GameInteractor::DisableRemoteInteractor() {
|
||||
if (!isRemoteInteractorEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
isRemoteInteractorEnabled = false;
|
||||
remoteThreadReceive.join();
|
||||
remoteDataHandler = nullptr;
|
||||
remoteJsonHandler = nullptr;
|
||||
remoteConnectedHandler = nullptr;
|
||||
remoteDisconnectedHandler = nullptr;
|
||||
}
|
||||
|
||||
void GameInteractor::TransmitDataToRemote(const char* payload) {
|
||||
SDLNet_TCP_Send(remoteSocket, payload, strlen(payload) + 1);
|
||||
}
|
||||
|
||||
// Appends a newline character to the end of the json payload and sends it to the remote
|
||||
void GameInteractor::TransmitJsonToRemote(nlohmann::json payload) {
|
||||
TransmitDataToRemote(payload.dump().c_str());
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
std::string receivedData;
|
||||
|
||||
void GameInteractor::ReceiveFromServer() {
|
||||
while (isRemoteInteractorEnabled) {
|
||||
while (!isRemoteInteractorConnected && isRemoteInteractorEnabled) {
|
||||
SPDLOG_TRACE("[GameInteractor] Attempting to make connection to server...");
|
||||
remoteSocket = SDLNet_TCP_Open(&remoteIP);
|
||||
|
||||
if (remoteSocket) {
|
||||
isRemoteInteractorConnected = true;
|
||||
SPDLOG_INFO("[GameInteractor] Connection to server established!");
|
||||
|
||||
if (remoteConnectedHandler) {
|
||||
remoteConnectedHandler();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SDLNet_SocketSet socketSet = SDLNet_AllocSocketSet(1);
|
||||
if (remoteSocket) {
|
||||
SDLNet_TCP_AddSocket(socketSet, remoteSocket);
|
||||
}
|
||||
|
||||
// Listen to socket messages
|
||||
while (isRemoteInteractorConnected && remoteSocket && isRemoteInteractorEnabled) {
|
||||
// we check first if socket has data, to not block in the TCP_Recv
|
||||
int socketsReady = SDLNet_CheckSockets(socketSet, 0);
|
||||
|
||||
if (socketsReady == -1) {
|
||||
SPDLOG_ERROR("[GameInteractor] SDLNet_CheckSockets: {}", SDLNet_GetError());
|
||||
break;
|
||||
}
|
||||
|
||||
if (socketsReady == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
char remoteDataReceived[512];
|
||||
memset(remoteDataReceived, 0, sizeof(remoteDataReceived));
|
||||
int len = SDLNet_TCP_Recv(remoteSocket, &remoteDataReceived, sizeof(remoteDataReceived));
|
||||
if (!len || !remoteSocket || len == -1) {
|
||||
SPDLOG_ERROR("[GameInteractor] SDLNet_TCP_Recv: {}", SDLNet_GetError());
|
||||
break;
|
||||
}
|
||||
|
||||
HandleRemoteData(remoteDataReceived);
|
||||
|
||||
receivedData.append(remoteDataReceived, len);
|
||||
|
||||
// Proess all complete packets
|
||||
size_t delimiterPos = receivedData.find('\0');
|
||||
while (delimiterPos != std::string::npos) {
|
||||
// Extract the complete packet until the delimiter
|
||||
std::string packet = receivedData.substr(0, delimiterPos);
|
||||
// Remove the packet (including the delimiter) from the received data
|
||||
receivedData.erase(0, delimiterPos + 1);
|
||||
HandleRemoteJson(packet);
|
||||
// Find the next delimiter
|
||||
delimiterPos = receivedData.find('\0');
|
||||
}
|
||||
}
|
||||
|
||||
if (isRemoteInteractorConnected) {
|
||||
SDLNet_TCP_Close(remoteSocket);
|
||||
isRemoteInteractorConnected = false;
|
||||
if (remoteDisconnectedHandler) {
|
||||
remoteDisconnectedHandler();
|
||||
}
|
||||
SPDLOG_INFO("[GameInteractor] Ending receiving thread...");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GameInteractor::HandleRemoteData(char payload[512]) {
|
||||
if (remoteDataHandler) {
|
||||
remoteDataHandler(payload);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void GameInteractor::HandleRemoteJson(std::string payload) {
|
||||
nlohmann::json jsonPayload;
|
||||
try {
|
||||
jsonPayload = nlohmann::json::parse(payload);
|
||||
} catch (const std::exception& e) {
|
||||
SPDLOG_ERROR("[GameInteractor] Failed to parse json: \n{}\n{}\n", payload, e.what());
|
||||
return;
|
||||
}
|
||||
|
||||
if (remoteJsonHandler) {
|
||||
remoteJsonHandler(jsonPayload);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
471
soh/soh/Enhancements/game-interactor/GameInteractor_Sail.cpp
Normal file
471
soh/soh/Enhancements/game-interactor/GameInteractor_Sail.cpp
Normal file
@ -0,0 +1,471 @@
|
||||
#ifdef ENABLE_REMOTE_CONTROL
|
||||
|
||||
#include "GameInteractor_Sail.h"
|
||||
#include <libultraship/bridge.h>
|
||||
#include <libultraship/libultraship.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
template <class DstType, class SrcType>
|
||||
bool IsType(const SrcType* src) {
|
||||
return dynamic_cast<const DstType*>(src) != nullptr;
|
||||
}
|
||||
|
||||
void GameInteractorSail::Enable() {
|
||||
if (isEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
isEnabled = true;
|
||||
GameInteractor::Instance->EnableRemoteInteractor();
|
||||
GameInteractor::Instance->RegisterRemoteJsonHandler([&](nlohmann::json payload) {
|
||||
HandleRemoteJson(payload);
|
||||
});
|
||||
GameInteractor::Instance->RegisterRemoteConnectedHandler([&]() {
|
||||
RegisterHooks();
|
||||
});
|
||||
}
|
||||
|
||||
void GameInteractorSail::Disable() {
|
||||
if (!isEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
isEnabled = false;
|
||||
GameInteractor::Instance->DisableRemoteInteractor();
|
||||
}
|
||||
|
||||
void GameInteractorSail::HandleRemoteJson(nlohmann::json payload) {
|
||||
SPDLOG_INFO("[GameInteractorSail] Received payload: \n{}", payload.dump());
|
||||
|
||||
nlohmann::json responsePayload;
|
||||
responsePayload["type"] = "result";
|
||||
responsePayload["status"] = "failure";
|
||||
|
||||
try {
|
||||
if (!payload.contains("id")) {
|
||||
SPDLOG_ERROR("[GameInteractorSail] Received payload without ID");
|
||||
GameInteractor::Instance->TransmitJsonToRemote(responsePayload);
|
||||
return;
|
||||
}
|
||||
|
||||
responsePayload["id"] = payload["id"];
|
||||
|
||||
if (!payload.contains("type")) {
|
||||
SPDLOG_ERROR("[GameInteractorSail] Received payload without type");
|
||||
GameInteractor::Instance->TransmitJsonToRemote(responsePayload);
|
||||
return;
|
||||
}
|
||||
|
||||
std::string payloadType = payload["type"].get<std::string>();
|
||||
|
||||
if (payloadType == "command") {
|
||||
if (!payload.contains("command")) {
|
||||
SPDLOG_ERROR("[GameInteractorSail] Received command payload without command");
|
||||
GameInteractor::Instance->TransmitJsonToRemote(responsePayload);
|
||||
return;
|
||||
}
|
||||
|
||||
std::string command = payload["command"].get<std::string>();
|
||||
std::reinterpret_pointer_cast<LUS::ConsoleWindow>(LUS::Context::GetInstance()->GetWindow()->GetGui()->GetGuiWindow("Console"))->Dispatch(command);
|
||||
responsePayload["status"] = "success";
|
||||
GameInteractor::Instance->TransmitJsonToRemote(responsePayload);
|
||||
return;
|
||||
} else if (payloadType == "effect") {
|
||||
if (!payload.contains("effect") || !payload["effect"].contains("type")) {
|
||||
SPDLOG_ERROR("[GameInteractorSail] Received effect payload without effect type");
|
||||
GameInteractor::Instance->TransmitJsonToRemote(responsePayload);
|
||||
return;
|
||||
}
|
||||
|
||||
std::string effectType = payload["effect"]["type"].get<std::string>();
|
||||
|
||||
// Special case for "command" effect, so we can also run commands from the `simple_twitch_sail` script
|
||||
if (effectType == "command") {
|
||||
if (!payload["effect"].contains("command")) {
|
||||
SPDLOG_ERROR("[GameInteractorSail] Received command effect payload without command");
|
||||
GameInteractor::Instance->TransmitJsonToRemote(responsePayload);
|
||||
return;
|
||||
}
|
||||
|
||||
std::string command = payload["effect"]["command"].get<std::string>();
|
||||
std::reinterpret_pointer_cast<LUS::ConsoleWindow>(LUS::Context::GetInstance()->GetWindow()->GetGui()->GetGuiWindow("Console"))->Dispatch(command);
|
||||
responsePayload["status"] = "success";
|
||||
GameInteractor::Instance->TransmitJsonToRemote(responsePayload);
|
||||
return;
|
||||
}
|
||||
|
||||
if (effectType != "apply" && effectType != "remove") {
|
||||
SPDLOG_ERROR("[GameInteractorSail] Received effect payload with unknown effect type: {}", effectType);
|
||||
GameInteractor::Instance->TransmitJsonToRemote(responsePayload);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!GameInteractor::IsSaveLoaded()) {
|
||||
responsePayload["status"] = "try_again";
|
||||
GameInteractor::Instance->TransmitJsonToRemote(responsePayload);
|
||||
return;
|
||||
}
|
||||
|
||||
GameInteractionEffectBase* giEffect = EffectFromJson(payload["effect"]);
|
||||
if (giEffect) {
|
||||
GameInteractionEffectQueryResult result;
|
||||
if (effectType == "remove") {
|
||||
if (IsType<RemovableGameInteractionEffect>(giEffect)) {
|
||||
result = dynamic_cast<RemovableGameInteractionEffect*>(giEffect)->Remove();
|
||||
} else {
|
||||
result = GameInteractionEffectQueryResult::NotPossible;
|
||||
}
|
||||
} else {
|
||||
result = giEffect->Apply();
|
||||
}
|
||||
|
||||
if (result == GameInteractionEffectQueryResult::Possible) {
|
||||
responsePayload["status"] = "success";
|
||||
} else if (result == GameInteractionEffectQueryResult::TemporarilyNotPossible) {
|
||||
responsePayload["status"] = "try_again";
|
||||
}
|
||||
GameInteractor::Instance->TransmitJsonToRemote(responsePayload);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
SPDLOG_ERROR("[GameInteractorSail] Unknown payload type: {}", payloadType);
|
||||
GameInteractor::Instance->TransmitJsonToRemote(responsePayload);
|
||||
return;
|
||||
}
|
||||
|
||||
// If we get here, something went wrong, send the failure response
|
||||
SPDLOG_ERROR("[GameInteractorSail] Failed to handle remote JSON, sending failure response");
|
||||
GameInteractor::Instance->TransmitJsonToRemote(responsePayload);
|
||||
} catch (const std::exception& e) {
|
||||
SPDLOG_ERROR("[GameInteractorSail] Exception handling remote JSON: {}", e.what());
|
||||
} catch (...) {
|
||||
SPDLOG_ERROR("[GameInteractorSail] Unknown exception handling remote JSON");
|
||||
}
|
||||
}
|
||||
|
||||
GameInteractionEffectBase* GameInteractorSail::EffectFromJson(nlohmann::json payload) {
|
||||
if (!payload.contains("name")) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string name = payload["name"].get<std::string>();
|
||||
|
||||
if (name == "SetSceneFlag") {
|
||||
auto effect = new GameInteractionEffect::SetSceneFlag();
|
||||
if (payload.contains("parameters")) {
|
||||
effect->parameters[0] = payload["parameters"][0].get<int32_t>();
|
||||
effect->parameters[1] = payload["parameters"][1].get<int32_t>();
|
||||
effect->parameters[2] = payload["parameters"][2].get<int32_t>();
|
||||
}
|
||||
return effect;
|
||||
} else if (name == "UnsetSceneFlag") {
|
||||
auto effect = new GameInteractionEffect::UnsetSceneFlag();
|
||||
if (payload.contains("parameters")) {
|
||||
effect->parameters[0] = payload["parameters"][0].get<int32_t>();
|
||||
effect->parameters[1] = payload["parameters"][1].get<int32_t>();
|
||||
effect->parameters[2] = payload["parameters"][2].get<int32_t>();
|
||||
}
|
||||
return effect;
|
||||
} else if (name == "SetFlag") {
|
||||
auto effect = new GameInteractionEffect::SetFlag();
|
||||
if (payload.contains("parameters")) {
|
||||
effect->parameters[0] = payload["parameters"][0].get<int32_t>();
|
||||
effect->parameters[1] = payload["parameters"][1].get<int32_t>();
|
||||
}
|
||||
return effect;
|
||||
} else if (name == "UnsetFlag") {
|
||||
auto effect = new GameInteractionEffect::UnsetFlag();
|
||||
if (payload.contains("parameters")) {
|
||||
effect->parameters[0] = payload["parameters"][0].get<int32_t>();
|
||||
effect->parameters[1] = payload["parameters"][1].get<int32_t>();
|
||||
}
|
||||
return effect;
|
||||
} else if (name == "ModifyHeartContainers") {
|
||||
auto effect = new GameInteractionEffect::ModifyHeartContainers();
|
||||
if (payload.contains("parameters")) {
|
||||
effect->parameters[0] = payload["parameters"][0].get<int32_t>();
|
||||
}
|
||||
return effect;
|
||||
} else if (name == "FillMagic") {
|
||||
return new GameInteractionEffect::FillMagic();
|
||||
} else if (name == "EmptyMagic") {
|
||||
return new GameInteractionEffect::EmptyMagic();
|
||||
} else if (name == "ModifyRupees") {
|
||||
auto effect = new GameInteractionEffect::ModifyRupees();
|
||||
if (payload.contains("parameters")) {
|
||||
effect->parameters[0] = payload["parameters"][0].get<int32_t>();
|
||||
}
|
||||
return effect;
|
||||
} else if (name == "NoUI") {
|
||||
return new GameInteractionEffect::NoUI();
|
||||
} else if (name == "ModifyGravity") {
|
||||
auto effect = new GameInteractionEffect::ModifyGravity();
|
||||
if (payload.contains("parameters")) {
|
||||
effect->parameters[0] = payload["parameters"][0].get<int32_t>();
|
||||
}
|
||||
return effect;
|
||||
} else if (name == "ModifyHealth") {
|
||||
auto effect = new GameInteractionEffect::ModifyHealth();
|
||||
if (payload.contains("parameters")) {
|
||||
effect->parameters[0] = payload["parameters"][0].get<int32_t>();
|
||||
}
|
||||
return effect;
|
||||
} else if (name == "SetPlayerHealth") {
|
||||
auto effect = new GameInteractionEffect::SetPlayerHealth();
|
||||
if (payload.contains("parameters")) {
|
||||
effect->parameters[0] = payload["parameters"][0].get<int32_t>();
|
||||
}
|
||||
return effect;
|
||||
} else if (name == "FreezePlayer") {
|
||||
return new GameInteractionEffect::FreezePlayer();
|
||||
} else if (name == "BurnPlayer") {
|
||||
return new GameInteractionEffect::BurnPlayer();
|
||||
} else if (name == "ElectrocutePlayer") {
|
||||
return new GameInteractionEffect::ElectrocutePlayer();
|
||||
} else if (name == "KnockbackPlayer") {
|
||||
auto effect = new GameInteractionEffect::KnockbackPlayer();
|
||||
if (payload.contains("parameters")) {
|
||||
effect->parameters[0] = payload["parameters"][0].get<int32_t>();
|
||||
}
|
||||
return effect;
|
||||
} else if (name == "ModifyLinkSize") {
|
||||
auto effect = new GameInteractionEffect::ModifyLinkSize();
|
||||
if (payload.contains("parameters")) {
|
||||
effect->parameters[0] = payload["parameters"][0].get<int32_t>();
|
||||
}
|
||||
return effect;
|
||||
} else if (name == "InvisibleLink") {
|
||||
return new GameInteractionEffect::InvisibleLink();
|
||||
} else if (name == "PacifistMode") {
|
||||
return new GameInteractionEffect::PacifistMode();
|
||||
} else if (name == "DisableZTargeting") {
|
||||
return new GameInteractionEffect::DisableZTargeting();
|
||||
} else if (name == "WeatherRainstorm") {
|
||||
return new GameInteractionEffect::WeatherRainstorm();
|
||||
} else if (name == "ReverseControls") {
|
||||
return new GameInteractionEffect::ReverseControls();
|
||||
} else if (name == "ForceEquipBoots") {
|
||||
auto effect = new GameInteractionEffect::ForceEquipBoots();
|
||||
if (payload.contains("parameters")) {
|
||||
effect->parameters[0] = payload["parameters"][0].get<int32_t>();
|
||||
}
|
||||
return effect;
|
||||
} else if (name == "ModifyRunSpeedModifier") {
|
||||
auto effect = new GameInteractionEffect::ModifyRunSpeedModifier();
|
||||
if (payload.contains("parameters")) {
|
||||
effect->parameters[0] = payload["parameters"][0].get<int32_t>();
|
||||
}
|
||||
return effect;
|
||||
} else if (name == "OneHitKO") {
|
||||
return new GameInteractionEffect::OneHitKO();
|
||||
} else if (name == "ModifyDefenseModifier") {
|
||||
auto effect = new GameInteractionEffect::ModifyDefenseModifier();
|
||||
if (payload.contains("parameters")) {
|
||||
effect->parameters[0] = payload["parameters"][0].get<int32_t>();
|
||||
}
|
||||
return effect;
|
||||
} else if (name == "GiveOrTakeShield") {
|
||||
auto effect = new GameInteractionEffect::GiveOrTakeShield();
|
||||
if (payload.contains("parameters")) {
|
||||
effect->parameters[0] = payload["parameters"][0].get<int32_t>();
|
||||
}
|
||||
return effect;
|
||||
} else if (name == "TeleportPlayer") {
|
||||
auto effect = new GameInteractionEffect::TeleportPlayer();
|
||||
if (payload.contains("parameters")) {
|
||||
effect->parameters[0] = payload["parameters"][0].get<int32_t>();
|
||||
}
|
||||
return effect;
|
||||
} else if (name == "ClearAssignedButtons") {
|
||||
auto effect = new GameInteractionEffect::ClearAssignedButtons();
|
||||
if (payload.contains("parameters")) {
|
||||
effect->parameters[0] = payload["parameters"][0].get<int32_t>();
|
||||
}
|
||||
return effect;
|
||||
} else if (name == "SetTimeOfDay") {
|
||||
auto effect = new GameInteractionEffect::SetTimeOfDay();
|
||||
if (payload.contains("parameters")) {
|
||||
effect->parameters[0] = payload["parameters"][0].get<int32_t>();
|
||||
}
|
||||
return effect;
|
||||
} else if (name == "SetCollisionViewer") {
|
||||
return new GameInteractionEffect::SetCollisionViewer();
|
||||
} else if (name == "SetCosmeticsColor") {
|
||||
auto effect = new GameInteractionEffect::SetCosmeticsColor();
|
||||
if (payload.contains("parameters")) {
|
||||
effect->parameters[0] = payload["parameters"][0].get<int32_t>();
|
||||
effect->parameters[1] = payload["parameters"][1].get<int32_t>();
|
||||
}
|
||||
return effect;
|
||||
} else if (name == "RandomizeCosmetics") {
|
||||
return new GameInteractionEffect::RandomizeCosmetics();
|
||||
} else if (name == "PressButton") {
|
||||
auto effect = new GameInteractionEffect::PressButton();
|
||||
if (payload.contains("parameters")) {
|
||||
effect->parameters[0] = payload["parameters"][0].get<int32_t>();
|
||||
}
|
||||
return effect;
|
||||
} else if (name == "PressRandomButton") {
|
||||
auto effect = new GameInteractionEffect::PressRandomButton();
|
||||
if (payload.contains("parameters")) {
|
||||
effect->parameters[0] = payload["parameters"][0].get<int32_t>();
|
||||
}
|
||||
return effect;
|
||||
} else if (name == "AddOrTakeAmmo") {
|
||||
auto effect = new GameInteractionEffect::AddOrTakeAmmo();
|
||||
if (payload.contains("parameters")) {
|
||||
effect->parameters[0] = payload["parameters"][0].get<int32_t>();
|
||||
effect->parameters[1] = payload["parameters"][1].get<int32_t>();
|
||||
}
|
||||
return effect;
|
||||
} else if (name == "RandomBombFuseTimer") {
|
||||
return new GameInteractionEffect::RandomBombFuseTimer();
|
||||
} else if (name == "DisableLedgeGrabs") {
|
||||
return new GameInteractionEffect::DisableLedgeGrabs();
|
||||
} else if (name == "RandomWind") {
|
||||
return new GameInteractionEffect::RandomWind();
|
||||
} else if (name == "RandomBonks") {
|
||||
return new GameInteractionEffect::RandomBonks();
|
||||
} else if (name == "PlayerInvincibility") {
|
||||
return new GameInteractionEffect::PlayerInvincibility();
|
||||
} else if (name == "SlipperyFloor") {
|
||||
return new GameInteractionEffect::SlipperyFloor();
|
||||
} else {
|
||||
SPDLOG_INFO("[GameInteractorSail] Unknown effect name: {}", name);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Workaround until we have a way to unregister hooks
|
||||
static bool hasRegisteredHooks = false;
|
||||
|
||||
void GameInteractorSail::RegisterHooks() {
|
||||
if (hasRegisteredHooks) {
|
||||
return;
|
||||
}
|
||||
hasRegisteredHooks = true;
|
||||
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnTransitionEnd>([](int32_t sceneNum) {
|
||||
if (!GameInteractor::Instance->isRemoteInteractorConnected || !GameInteractor::IsSaveLoaded()) return;
|
||||
|
||||
nlohmann::json payload;
|
||||
payload["id"] = std::rand();
|
||||
payload["type"] = "hook";
|
||||
payload["hook"]["type"] = "OnTransitionEnd";
|
||||
payload["hook"]["sceneNum"] = sceneNum;
|
||||
|
||||
GameInteractor::Instance->TransmitJsonToRemote(payload);
|
||||
});
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnLoadGame>([](int32_t fileNum) {
|
||||
if (!GameInteractor::Instance->isRemoteInteractorConnected || !GameInteractor::IsSaveLoaded()) return;
|
||||
|
||||
nlohmann::json payload;
|
||||
payload["id"] = std::rand();
|
||||
payload["type"] = "hook";
|
||||
payload["hook"]["type"] = "OnLoadGame";
|
||||
payload["hook"]["fileNum"] = fileNum;
|
||||
|
||||
GameInteractor::Instance->TransmitJsonToRemote(payload);
|
||||
});
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnExitGame>([](int32_t fileNum) {
|
||||
if (!GameInteractor::Instance->isRemoteInteractorConnected || !GameInteractor::IsSaveLoaded()) return;
|
||||
|
||||
nlohmann::json payload;
|
||||
payload["id"] = std::rand();
|
||||
payload["type"] = "hook";
|
||||
payload["hook"]["type"] = "OnExitGame";
|
||||
payload["hook"]["fileNum"] = fileNum;
|
||||
|
||||
GameInteractor::Instance->TransmitJsonToRemote(payload);
|
||||
});
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnItemReceive>([](GetItemEntry itemEntry) {
|
||||
if (!GameInteractor::Instance->isRemoteInteractorConnected || !GameInteractor::IsSaveLoaded()) return;
|
||||
|
||||
nlohmann::json payload;
|
||||
payload["id"] = std::rand();
|
||||
payload["type"] = "hook";
|
||||
payload["hook"]["type"] = "OnItemReceive";
|
||||
payload["hook"]["tableId"] = itemEntry.tableId;
|
||||
payload["hook"]["getItemId"] = itemEntry.getItemId;
|
||||
|
||||
GameInteractor::Instance->TransmitJsonToRemote(payload);
|
||||
});
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnEnemyDefeat>([](void* refActor) {
|
||||
if (!GameInteractor::Instance->isRemoteInteractorConnected || !GameInteractor::IsSaveLoaded()) return;
|
||||
|
||||
Actor* actor = (Actor*)refActor;
|
||||
nlohmann::json payload;
|
||||
payload["id"] = std::rand();
|
||||
payload["type"] = "hook";
|
||||
payload["hook"]["type"] = "OnEnemyDefeat";
|
||||
payload["hook"]["actorId"] = actor->id;
|
||||
payload["hook"]["params"] = actor->params;
|
||||
|
||||
GameInteractor::Instance->TransmitJsonToRemote(payload);
|
||||
});
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnActorInit>([](void* refActor) {
|
||||
if (!GameInteractor::Instance->isRemoteInteractorConnected || !GameInteractor::IsSaveLoaded()) return;
|
||||
|
||||
Actor* actor = (Actor*)refActor;
|
||||
nlohmann::json payload;
|
||||
payload["id"] = std::rand();
|
||||
payload["type"] = "hook";
|
||||
payload["hook"]["type"] = "OnActorInit";
|
||||
payload["hook"]["actorId"] = actor->id;
|
||||
payload["hook"]["params"] = actor->params;
|
||||
|
||||
GameInteractor::Instance->TransmitJsonToRemote(payload);
|
||||
});
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnFlagSet>([](int16_t flagType, int16_t flag) {
|
||||
if (!GameInteractor::Instance->isRemoteInteractorConnected || !GameInteractor::IsSaveLoaded()) return;
|
||||
|
||||
nlohmann::json payload;
|
||||
payload["id"] = std::rand();
|
||||
payload["type"] = "hook";
|
||||
payload["hook"]["type"] = "OnFlagSet";
|
||||
payload["hook"]["flagType"] = flagType;
|
||||
payload["hook"]["flag"] = flag;
|
||||
|
||||
GameInteractor::Instance->TransmitJsonToRemote(payload);
|
||||
});
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnFlagUnset>([](int16_t flagType, int16_t flag) {
|
||||
if (!GameInteractor::Instance->isRemoteInteractorConnected || !GameInteractor::IsSaveLoaded()) return;
|
||||
|
||||
nlohmann::json payload;
|
||||
payload["id"] = std::rand();
|
||||
payload["type"] = "hook";
|
||||
payload["hook"]["type"] = "OnFlagUnset";
|
||||
payload["hook"]["flagType"] = flagType;
|
||||
payload["hook"]["flag"] = flag;
|
||||
|
||||
GameInteractor::Instance->TransmitJsonToRemote(payload);
|
||||
});
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSceneFlagSet>([](int16_t sceneNum, int16_t flagType, int16_t flag) {
|
||||
if (!GameInteractor::Instance->isRemoteInteractorConnected || !GameInteractor::IsSaveLoaded()) return;
|
||||
|
||||
nlohmann::json payload;
|
||||
payload["id"] = std::rand();
|
||||
payload["type"] = "hook";
|
||||
payload["hook"]["type"] = "OnSceneFlagSet";
|
||||
payload["hook"]["flagType"] = flagType;
|
||||
payload["hook"]["flag"] = flag;
|
||||
payload["hook"]["sceneNum"] = sceneNum;
|
||||
|
||||
GameInteractor::Instance->TransmitJsonToRemote(payload);
|
||||
});
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSceneFlagUnset>([](int16_t sceneNum, int16_t flagType, int16_t flag) {
|
||||
if (!GameInteractor::Instance->isRemoteInteractorConnected || !GameInteractor::IsSaveLoaded()) return;
|
||||
|
||||
nlohmann::json payload;
|
||||
payload["id"] = std::rand();
|
||||
payload["type"] = "hook";
|
||||
payload["hook"]["type"] = "OnSceneFlagUnset";
|
||||
payload["hook"]["flagType"] = flagType;
|
||||
payload["hook"]["flag"] = flag;
|
||||
payload["hook"]["sceneNum"] = sceneNum;
|
||||
|
||||
GameInteractor::Instance->TransmitJsonToRemote(payload);
|
||||
});
|
||||
}
|
||||
|
||||
#endif
|
29
soh/soh/Enhancements/game-interactor/GameInteractor_Sail.h
Normal file
29
soh/soh/Enhancements/game-interactor/GameInteractor_Sail.h
Normal file
@ -0,0 +1,29 @@
|
||||
#ifdef ENABLE_REMOTE_CONTROL
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include <SDL2/SDL_net.h>
|
||||
#include <cstdint>
|
||||
#include <thread>
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
#include <future>
|
||||
|
||||
#include "./GameInteractor.h"
|
||||
|
||||
class GameInteractorSail {
|
||||
private:
|
||||
bool isEnabled;
|
||||
|
||||
void HandleRemoteJson(nlohmann::json payload);
|
||||
GameInteractionEffectBase* EffectFromJson(nlohmann::json payload);
|
||||
void RegisterHooks();
|
||||
public:
|
||||
static GameInteractorSail* Instance;
|
||||
void Enable();
|
||||
void Disable();
|
||||
};
|
||||
#endif
|
||||
#endif
|
@ -81,9 +81,11 @@
|
||||
#include "SohGui.hpp"
|
||||
#include "ActorDB.h"
|
||||
|
||||
#ifdef ENABLE_CROWD_CONTROL
|
||||
#ifdef ENABLE_REMOTE_CONTROL
|
||||
#include "Enhancements/crowd-control/CrowdControl.h"
|
||||
#include "Enhancements/game-interactor/GameInteractor_Sail.h"
|
||||
CrowdControl* CrowdControl::Instance;
|
||||
GameInteractorSail* GameInteractorSail::Instance;
|
||||
#endif
|
||||
|
||||
#include "Enhancements/mods.h"
|
||||
@ -1097,7 +1099,12 @@ extern "C" void InitOTR() {
|
||||
SpeechSynthesizer::Instance = new SAPISpeechSynthesizer();
|
||||
SpeechSynthesizer::Instance->Init();
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef ENABLE_REMOTE_CONTROL
|
||||
CrowdControl::Instance = new CrowdControl();
|
||||
GameInteractorSail::Instance = new GameInteractorSail();
|
||||
#endif
|
||||
|
||||
clearMtx = (uintptr_t)&gMtxClear;
|
||||
OTRMessage_Init();
|
||||
OTRAudio_Init();
|
||||
@ -1117,13 +1124,17 @@ extern "C" void InitOTR() {
|
||||
}
|
||||
|
||||
srand(now);
|
||||
#ifdef ENABLE_CROWD_CONTROL
|
||||
CrowdControl::Instance = new CrowdControl();
|
||||
CrowdControl::Instance->Init();
|
||||
if (CVarGetInteger("gCrowdControl", 0)) {
|
||||
CrowdControl::Instance->Enable();
|
||||
} else {
|
||||
CrowdControl::Instance->Disable();
|
||||
#ifdef ENABLE_REMOTE_CONTROL
|
||||
SDLNet_Init();
|
||||
if (CVarGetInteger("gRemote.Enabled", 0)) {
|
||||
switch (CVarGetInteger("gRemote.Scheme", GI_SCHEME_SAIL)) {
|
||||
case GI_SCHEME_SAIL:
|
||||
GameInteractorSail::Instance->Enable();
|
||||
break;
|
||||
case GI_SCHEME_CROWD_CONTROL:
|
||||
CrowdControl::Instance->Enable();
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -1140,9 +1151,18 @@ extern "C" void SaveManager_ThreadPoolWait() {
|
||||
extern "C" void DeinitOTR() {
|
||||
SaveManager_ThreadPoolWait();
|
||||
OTRAudio_Exit();
|
||||
#ifdef ENABLE_CROWD_CONTROL
|
||||
CrowdControl::Instance->Disable();
|
||||
CrowdControl::Instance->Shutdown();
|
||||
#ifdef ENABLE_REMOTE_CONTROL
|
||||
if (CVarGetInteger("gRemote.Enabled", 0)) {
|
||||
switch (CVarGetInteger("gRemote.Scheme", GI_SCHEME_SAIL)) {
|
||||
case GI_SCHEME_SAIL:
|
||||
GameInteractorSail::Instance->Disable();
|
||||
break;
|
||||
case GI_SCHEME_CROWD_CONTROL:
|
||||
CrowdControl::Instance->Disable();
|
||||
break;
|
||||
}
|
||||
}
|
||||
SDLNet_Quit();
|
||||
#endif
|
||||
|
||||
// Destroying gui here because we have shared ptrs to LUS objects which output to SPDLOG which is destroyed before these shared ptrs.
|
||||
|
@ -31,8 +31,9 @@
|
||||
#include "soh/resource/type/Skeleton.h"
|
||||
#include "libultraship/libultraship.h"
|
||||
|
||||
#ifdef ENABLE_CROWD_CONTROL
|
||||
#ifdef ENABLE_REMOTE_CONTROL
|
||||
#include "Enhancements/crowd-control/CrowdControl.h"
|
||||
#include "Enhancements/game-interactor/GameInteractor_Sail.h"
|
||||
#endif
|
||||
|
||||
#include "Enhancements/game-interactor/GameInteractor.h"
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include "SohMenuBar.h"
|
||||
#include "ImGui/imgui.h"
|
||||
#include "regex"
|
||||
#include "public/bridge/consolevariablebridge.h"
|
||||
#include <libultraship/libultraship.h>
|
||||
#include "UIWidgets.hpp"
|
||||
@ -10,8 +11,9 @@
|
||||
#include "soh/Enhancements/presets.h"
|
||||
#include "soh/Enhancements/mods.h"
|
||||
#include "Enhancements/cosmetics/authenticGfxPatches.h"
|
||||
#ifdef ENABLE_CROWD_CONTROL
|
||||
#ifdef ENABLE_REMOTE_CONTROL
|
||||
#include "Enhancements/crowd-control/CrowdControl.h"
|
||||
#include "Enhancements/game-interactor/GameInteractor_Sail.h"
|
||||
#endif
|
||||
|
||||
|
||||
@ -1116,17 +1118,6 @@ void DrawEnhancementsMenu() {
|
||||
UIWidgets::Spacer(0);
|
||||
|
||||
if (ImGui::BeginMenu("Extra Modes")) {
|
||||
#ifdef ENABLE_CROWD_CONTROL
|
||||
if (UIWidgets::PaddedEnhancementCheckbox("Crowd Control", "gCrowdControl", false, false)) {
|
||||
if (CVarGetInteger("gCrowdControl", 0)) {
|
||||
CrowdControl::Instance->Enable();
|
||||
} else {
|
||||
CrowdControl::Instance->Disable();
|
||||
}
|
||||
}
|
||||
UIWidgets::Tooltip("Will attempt to connect to the Crowd Control server. Check out crowdcontrol.live for more information.");
|
||||
#endif
|
||||
|
||||
UIWidgets::PaddedText("Mirrored World", true, false);
|
||||
if (UIWidgets::EnhancementCombobox("gMirroredWorldMode", mirroredWorldModes, MIRRORED_WORLD_OFF) && gPlayState != NULL) {
|
||||
UpdateMirrorModeState(gPlayState->sceneNum);
|
||||
@ -1520,6 +1511,135 @@ void DrawDeveloperToolsMenu() {
|
||||
}
|
||||
}
|
||||
|
||||
bool isStringEmpty(std::string str) {
|
||||
// Remove spaces at the beginning of the string
|
||||
std::string::size_type start = str.find_first_not_of(' ');
|
||||
// Remove spaces at the end of the string
|
||||
std::string::size_type end = str.find_last_not_of(' ');
|
||||
|
||||
// Check if the string is empty after stripping spaces
|
||||
if (start == std::string::npos || end == std::string::npos)
|
||||
return true; // The string is empty
|
||||
else
|
||||
return false; // The string is not empty
|
||||
}
|
||||
|
||||
#ifdef ENABLE_REMOTE_CONTROL
|
||||
void DrawRemoteControlMenu() {
|
||||
if (ImGui::BeginMenu("Network")) {
|
||||
static std::string ip = CVarGetString("gRemote.IP", "127.0.0.1");
|
||||
static uint16_t port = CVarGetInteger("gRemote.Port", 43384);
|
||||
bool isFormValid = !isStringEmpty(CVarGetString("gRemote.IP", "127.0.0.1")) && port > 1024 && port < 65535;
|
||||
|
||||
const char* remoteOptions[2] = { "Sail", "Crowd Control"};
|
||||
|
||||
ImGui::BeginDisabled(GameInteractor::Instance->isRemoteInteractorEnabled);
|
||||
ImGui::Text("Remote Interaction Scheme");
|
||||
if (UIWidgets::EnhancementCombobox("gRemote.Scheme", remoteOptions, GI_SCHEME_SAIL)) {
|
||||
switch (CVarGetInteger("gRemote.Scheme", GI_SCHEME_SAIL)) {
|
||||
case GI_SCHEME_SAIL:
|
||||
case GI_SCHEME_CROWD_CONTROL:
|
||||
CVarSetString("gRemote.IP", "127.0.0.1");
|
||||
CVarSetInteger("gRemote.Port", 43384);
|
||||
ip = "127.0.0.1";
|
||||
port = 43384;
|
||||
break;
|
||||
}
|
||||
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
|
||||
}
|
||||
switch (CVarGetInteger("gRemote.Scheme", GI_SCHEME_SAIL)) {
|
||||
case GI_SCHEME_SAIL:
|
||||
UIWidgets::InsertHelpHoverText(
|
||||
"Sail is a networking protocol designed to facilitate remote "
|
||||
"control of the Ship of Harkinian client. It is intended to "
|
||||
"be utilized alongside a Sail server, for which we provide a "
|
||||
"few straightforward implementations on our GitHub. The current "
|
||||
"implementations available allow integration with Twitch chat "
|
||||
"and SAMMI Bot, feel free to contribute your own!\n"
|
||||
"\n"
|
||||
"Click the question mark to copy the link to the Sail Github "
|
||||
"page to your clipboard."
|
||||
);
|
||||
if (ImGui::IsItemClicked()) {
|
||||
ImGui::SetClipboardText("https://github.com/HarbourMasters/sail");
|
||||
}
|
||||
break;
|
||||
case GI_SCHEME_CROWD_CONTROL:
|
||||
UIWidgets::InsertHelpHoverText(
|
||||
"Crowd Control is a platform that allows viewers to interact "
|
||||
"with a streamer's game in real time.\n"
|
||||
"\n"
|
||||
"Click the question mark to copy the link to the Crowd Control "
|
||||
"website to your clipboard."
|
||||
);
|
||||
if (ImGui::IsItemClicked()) {
|
||||
ImGui::SetClipboardText("https://crowdcontrol.live");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
ImGui::Text("Remote IP & Port");
|
||||
if (ImGui::InputText("##gRemote.IP", (char*)ip.c_str(), ip.capacity() + 1)) {
|
||||
CVarSetString("gRemote.IP", ip.c_str());
|
||||
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::PushItemWidth(ImGui::GetFontSize() * 5);
|
||||
if (ImGui::InputScalar("##gRemote.Port", ImGuiDataType_U16, &port)) {
|
||||
CVarSetInteger("gRemote.Port", port);
|
||||
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
|
||||
}
|
||||
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::Spacing();
|
||||
|
||||
ImGui::BeginDisabled(!isFormValid);
|
||||
const char* buttonLabel = GameInteractor::Instance->isRemoteInteractorEnabled ? "Disable" : "Enable";
|
||||
if (ImGui::Button(buttonLabel, ImVec2(-1.0f, 0.0f))) {
|
||||
if (GameInteractor::Instance->isRemoteInteractorEnabled) {
|
||||
CVarSetInteger("gRemote.Enabled", 0);
|
||||
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
|
||||
switch (CVarGetInteger("gRemote.Scheme", GI_SCHEME_SAIL)) {
|
||||
case GI_SCHEME_SAIL:
|
||||
GameInteractorSail::Instance->Disable();
|
||||
break;
|
||||
case GI_SCHEME_CROWD_CONTROL:
|
||||
CrowdControl::Instance->Disable();
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
CVarSetInteger("gRemote.Enabled", 1);
|
||||
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
|
||||
switch (CVarGetInteger("gRemote.Scheme", GI_SCHEME_SAIL)) {
|
||||
case GI_SCHEME_SAIL:
|
||||
GameInteractorSail::Instance->Enable();
|
||||
break;
|
||||
case GI_SCHEME_CROWD_CONTROL:
|
||||
CrowdControl::Instance->Enable();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
|
||||
if (GameInteractor::Instance->isRemoteInteractorEnabled) {
|
||||
ImGui::Spacing();
|
||||
if (GameInteractor::Instance->isRemoteInteractorConnected) {
|
||||
ImGui::Text("Connected");
|
||||
} else {
|
||||
ImGui::Text("Connecting...");
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Dummy(ImVec2(0.0f, 0.0f));
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
extern std::shared_ptr<RandomizerSettingsWindow> mRandomizerSettingsWindow;
|
||||
extern std::shared_ptr<ItemTrackerWindow> mItemTrackerWindow;
|
||||
extern std::shared_ptr<ItemTrackerSettingsWindow> mItemTrackerSettingsWindow;
|
||||
@ -1658,6 +1778,12 @@ void SohMenuBar::DrawElement() {
|
||||
|
||||
ImGui::SetCursorPosY(0.0f);
|
||||
|
||||
#ifdef ENABLE_REMOTE_CONTROL
|
||||
DrawRemoteControlMenu();
|
||||
|
||||
ImGui::SetCursorPosY(0.0f);
|
||||
#endif
|
||||
|
||||
DrawRandomizerMenu();
|
||||
|
||||
ImGui::PopStyleVar(1);
|
||||
|
Loading…
Reference in New Issue
Block a user