Merge branch 'rando-next' into trade_quest_shuffle

This commit is contained in:
lilDavid 2022-08-09 20:40:00 -05:00 committed by GitHub
commit aef77a0913
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
98 changed files with 1960 additions and 816 deletions

3
.gitignore vendored
View File

@ -441,4 +441,5 @@ compile_commands.json
CTestTestfile.cmake
_deps
*/extract_assets_cmake*
/build*
/build*
build.c

View File

@ -2,9 +2,27 @@
## Windows
1. Requires Visual Studio 2022 Community Edition && `python3, cmake, git` (can be installed via chocolatey or manually)
2. Clone the Ship of Harkinian repository
3. Place one or more [compatible](#compatible-roms) roms in the `OTRExporter` directory with namings of your choice
Requires:
* At least 8GB of RAM (machines with 4GB have seen complier failures)
* Visual Studio 2022 Community Edition with the C++ feature set
* One of the Windows SDKs that comes with Visual Studio, for example the current Windows 10 version 10.0.19041.0
* The `MSVC v142 - VS 2019 C++ build tools` component of Visual Studio
* Python 3 (can be installed manually or as part of Visual Studio)
* Git (can be installed manually or as part of Visual Studio)
* Cmake (can be installed via chocolatey or manually)
During installation, check the "Desktop development with C++" feature set:
![image](https://user-images.githubusercontent.com/30329717/183511274-d11aceea-7900-46ec-acb6-3f2cc110021a.png)
Doing so should also check one of the Windows SDKs by default. Then, in the installation details in the right-hand column, make sure you also check the v142 toolset.
You can also find the v142 toolset by searching through the individual components tab:
![image](https://user-images.githubusercontent.com/30329717/183521169-ead6a73b-a1bf-4e99-aab8-441746d8f08e.png)
While you're there, you can also install Python 3 and Git if needed.
1. Clone the Ship of Harkinian repository
2. Place one or more [compatible](#compatible-roms) roms in the `OTRExporter` directory with namings of your choice
_Note: Instructions assume using powershell_
```powershell
@ -30,8 +48,10 @@ With the cmake build system you have two options for working on the project:
#### Visual Studio
To develop using Visual Studio you only need to use cmake to generate the solution file:
```powershell
# Generates Ship.sln at the root directory
& 'C:\Program Files\CMake\bin\cmake' -S . -G "Visual Studio 17 2022" -T v142 -A x64
# Generates Ship.sln at `build/x64` for Visual Studio 2022
& 'C:\Program Files\CMake\bin\cmake' -S . -B "build/x64" -G "Visual Studio 17 2022" -T v142 -A x64
# or for Visual Studio 2019
& 'C:\Program Files\CMake\bin\cmake' -S . -B "build/x64" -G "Visual Studio 16 2019" -T v142 -A x64
```
#### Visual Studio Code or another editor
@ -53,7 +73,7 @@ cd "build/x64"
```
## Linux
1. Requires `gcc, x11, curl, python3, sdl2, libpng, glew, ninja, cmake`
Requires `gcc >= 10, x11, curl, python3, sdl2 >= 2.0.22, libpng, glew >= 2.2, ninja, cmake, lld`
**Important: For maximum performance make sure you have ninja build tools installed!**
@ -91,7 +111,7 @@ cpack -G External (creates appimage)
```
## macOS
1. Requires Xcode (or xcode-tools) && `sdl2, libpng, glew, ninja, cmake` (can be installed via homebrew, macports, etc)
Requires Xcode (or xcode-tools) && `sdl2, libpng, glew, ninja, cmake` (can be installed via homebrew, macports, etc)
**Important: For maximum performance make sure you have ninja build tools installed!**

5
CMake/genscript.cmake Normal file
View File

@ -0,0 +1,5 @@
file(READ ${CMAKE_CURRENT_SOURCE_DIR}/extract_assets.py filedata)
string(REGEX REPLACE "zapd_exe = .*exec_cmd =" "zapd_exe = \"${program}\"\n exec_cmd =" filedata "${filedata}")
file(WRITE "${CMAKE_CURRENT_SOURCE_DIR}/extract_assets_cmake2.py" "${filedata}")
file(CHMOD "${CMAKE_CURRENT_SOURCE_DIR}/extract_assets_cmake2.py" PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_WRITE GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)

View File

@ -20,6 +20,9 @@ add_compile_options($<$<CXX_COMPILER_ID:MSVC>:/MP>)
if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
include(cmake/automate-vcpkg.cmake)
set(VCPKG_TRIPLET x64-windows-static)
set(VCPKG_TARGET_TRIPLET x64-windows-static)
vcpkg_bootstrap()
vcpkg_install_packages(zlib bzip2 libpng)
endif()
@ -42,11 +45,16 @@ endif()
################################################################################
# Global configuration types
################################################################################
set(CMAKE_CONFIGURATION_TYPES
"Debug"
"Release"
CACHE STRING "" FORCE
)
if (CMAKE_SYSTEM_NAME STREQUAL "NintendoSwitch")
set(CMAKE_C_FLAGS_DEBUG "-O3 -ffast-math")
set(CMAKE_CXX_FLAGS_DEBUG "-O3 -ffast-math")
set(CMAKE_C_FLAGS_RELEASE "-O3 -ffast-math -DNDEBUG")
set(CMAKE_CXX_FLAGS_RELEASE "-O3 -ffast-math -DNDEBUG")
else()
set(CMAKE_C_FLAGS_RELEASE "-O2 -DNDEBUG")
set(CMAKE_CXX_FLAGS_RELEASE "-O2 -DNDEBUG")
set(CMAKE_OBJCXX_FLAGS_RELEASE "-O2 -DNDEBUG")
endif()
if(NOT CMAKE_BUILD_TYPE )
set(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Choose the type of build." FORCE)
@ -126,24 +134,19 @@ if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
INSTALL(PROGRAMS "${CMAKE_SOURCE_DIR}/scripts/linux/appimage/soh.sh" DESTINATION . COMPONENT appimage)
endif()
file(READ ${CMAKE_CURRENT_SOURCE_DIR}/OTRExporter/extract_assets.py filedata)
string(REGEX REPLACE "../ZAPDTR/ZAPD.out" "${CMAKE_BINARY_DIR}/ZAPD/ZAPD.out" filedata "${filedata}")
string(REGEX REPLACE "x64" "..\\\\\\\\x64" filedata "${filedata}")
string(REGEX REPLACE "Release" "${CMAKE_BUILD_TYPE}" filedata "${filedata}")
file(WRITE "${CMAKE_CURRENT_SOURCE_DIR}/OTRExporter/extract_assets_cmake2.py" "${filedata}")
file(CHMOD "${CMAKE_CURRENT_SOURCE_DIR}/OTRExporter/extract_assets_cmake2.py" PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_WRITE GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
find_package(Python3 COMPONENTS Interpreter)
add_custom_target(
ExtractAssets
COMMAND ${CMAKE_COMMAND} -Dprogram=$<TARGET_FILE:ZAPD> -P ${CMAKE_SOURCE_DIR}/CMake/genscript.cmake
COMMAND ${CMAKE_COMMAND} -E rm -f oot.otr
COMMAND ${Python_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/OTRExporter/extract_assets_cmake2.py
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/OTRExporter/extract_assets_cmake2.py
COMMAND ${CMAKE_COMMAND} -E copy oot.otr ${CMAKE_SOURCE_DIR}
COMMAND ${CMAKE_COMMAND} -E copy oot.otr ${CMAKE_BINARY_DIR}/soh
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/OTRExporter
COMMENT "Running asset extraction..."
DEPENDS ZAPD
BYPRODUCTS oot.otr ${CMAKE_SOURCE_DIR}/oot.otr ${CMAKE_CURRENT_SOURCE_DIR}/OTRExporter/extract_assets_cmake2.py
)
if(CMAKE_SYSTEM_NAME MATCHES "Linux")

View File

@ -46,7 +46,8 @@ RUN curl -sLO https://libsdl.org/release/SDL2-${SDL2VER}.tar.gz && \
cd SDL2-${SDL2VER} && \
./configure --build=x86_64-linux-gnu && \
make -j$(nproc) && make install && \
rm ../SDL2-${SDL2VER}.tar.gz
rm ../SDL2-${SDL2VER}.tar.gz && \
cp -av /usr/local/lib/libSDL* /lib/x86_64-linux-gnu/
RUN \
ln -sf /proc/self/mounts /etc/mtab && \

68
Jenkinsfile vendored
View File

@ -8,12 +8,35 @@ pipeline {
}
stages {
stage('Generate Assets') {
options {
timeout(time: 10)
}
agent {
label "SoH-Mac-Builders"
}
steps {
checkout([
$class: 'GitSCM',
branches: scm.branches,
doGenerateSubmoduleConfigurations: scm.doGenerateSubmoduleConfigurations,
extensions: scm.extensions,
userRemoteConfigs: scm.userRemoteConfigs
])
catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') {
sh '''
cp ../../ZELOOTD.z64 OTRExporter/baserom_non_mq.z64
cmake --no-warn-unused-cli -H. -Bbuild-cmake -GNinja -DCMAKE_BUILD_TYPE:STRING=Release
cmake --build build-cmake --target ExtractAssets --config Release
'''
stash includes: 'soh/assets/**/*', name: 'assets'
}
}
}
stage('Build SoH') {
parallel {
stage ('Build Windows') {
options {
timeout(time: 20)
}
environment {
PLATFORM='x64'
PYTHON='C:\\Users\\jenkins\\AppData\\Local\\Programs\\Python\\Python310\\python.exe'
@ -34,12 +57,9 @@ pipeline {
])
catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') {
bat """
xcopy "..\\..\\ZELOOTD.z64" "OTRExporter\\"
unstash 'assets'
bat """
"${env.CMAKE}" -S . -B "build\\${env.PLATFORM}" -G "Visual Studio 17 2022" -T ${env.TOOLSET} -A ${env.PLATFORM} -D Python_EXECUTABLE=${env.PYTHON} -D CMAKE_BUILD_TYPE:STRING=Release
"${env.CMAKE}" --build ".\\build\\${env.PLATFORM}" --target ExtractAssets --config Release
"${env.CMAKE}" --build ".\\build\\${env.PLATFORM}" --config Release
cd ".\\build\\${env.PLATFORM}"
"${env.CPACK}" -G ZIP
@ -47,8 +67,8 @@ pipeline {
move "_packages\\*.zip" "soh.zip"
"""
archiveArtifacts artifacts: 'soh.zip', followSymlinks: false, onlyIfSuccessful: true
}
archiveArtifacts artifacts: 'soh.zip', followSymlinks: false, onlyIfSuccessful: true
}
post {
always {
@ -57,9 +77,6 @@ pipeline {
}
}
stage ('Build Linux') {
options {
timeout(time: 20)
}
agent {
label "SoH-Linux-Builders"
}
@ -72,9 +89,9 @@ pipeline {
userRemoteConfigs: scm.userRemoteConfigs
])
catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') {
unstash 'assets'
sh '''
cp ../../ZELOOTD.z64 OTRExporter/baserom_non_mq.z64
if docker ps -aq --filter "name=sohcont" | grep -q .; then docker rm -f sohcont; fi
docker build . -t soh
docker run --name sohcont -dit --rm -v $(pwd):/soh soh /bin/bash
docker exec sohcont scripts/linux/appimage/build.sh
@ -86,11 +103,12 @@ pipeline {
'''
}
sh 'sudo docker container stop sohcont'
archiveArtifacts artifacts: 'soh-linux.7z', followSymlinks: false, onlyIfSuccessful: true
}
post {
always {
sh 'sudo docker container stop sohcont'
sh 'docker images --quiet --filter=dangling=true | xargs --no-run-if-empty docker rmi' // Clean dangling docker images
step([$class: 'WsCleanup']) // Clean workspace
}
}
@ -108,11 +126,9 @@ pipeline {
userRemoteConfigs: scm.userRemoteConfigs
])
catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') {
unstash 'assets'
sh '''
cp ../../ZELOOTD.z64 OTRExporter/baserom_non_mq.z64
cmake --no-warn-unused-cli -H. -Bbuild-cmake -GNinja -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64"
cmake --build build-cmake --target ExtractAssets --
cmake --build build-cmake --config Release --
(cd build-cmake && cpack)
@ -131,9 +147,6 @@ pipeline {
}
}
stage ('Build Switch') {
options {
timeout(time: 20)
}
agent {
label "SoH-Linux-Builders"
}
@ -146,12 +159,12 @@ pipeline {
userRemoteConfigs: scm.userRemoteConfigs
])
catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') {
sh '''
cp ../../ZELOOTD.z64 OTRExporter/baserom_non_mq.z64
unstash 'assets'
sh '''
if docker ps -aq --filter "name=sohswitchcont" | grep -q .; then docker rm -f sohswitchcont; fi
docker build . -t sohswitch
docker run --name sohcont -dit --rm -v $(pwd):/soh sohswitch /bin/bash
docker exec sohcont scripts/switch/build.sh
docker run --name sohswitchcont -dit --rm -v $(pwd):/soh sohswitch /bin/bash
docker exec sohswitchcont scripts/switch/build.sh
mv build-switch/soh/*.nro soh.nro
mv README.md readme.txt
@ -160,11 +173,12 @@ pipeline {
'''
}
sh 'sudo docker container stop sohcont'
archiveArtifacts artifacts: 'soh-switch.7z', followSymlinks: false, onlyIfSuccessful: true
}
post {
always {
sh 'sudo docker container stop sohswitchcont'
sh 'docker images --quiet --filter=dangling=true | xargs --no-run-if-empty docker rmi' // Clean dangling docker images
step([$class: 'WsCleanup']) // Clean workspace
}
}

View File

@ -348,11 +348,14 @@ std::string ZResource::GetSourceOutputHeader([[maybe_unused]] const std::string&
str += StringHelper::Sprintf("#define d%s \"__OTR__%s/%s\"", name.c_str(), outName.c_str(), nameStr.c_str());
if (nameSet && nameSet->find(name) == nameSet->end()) {
str += StringHelper::Sprintf(R"(
#ifdef _WIN32
str += StringHelper::Sprintf("\nstatic const __declspec(align(2)) char %s[] = d%s;", name.c_str(), name.c_str());
static const __declspec(align(2)) char %s[] = d%s;
#else
str += StringHelper::Sprintf("\nstatic const char %s[] __attribute__((aligned (2))) = d%s;", name.c_str(), name.c_str());
static const char %s[] __attribute__((aligned (2))) = d%s;
#endif
)", name.c_str(), name.c_str(), name.c_str(), name.c_str());
if (nameSet) {
nameSet->insert(name);
}

View File

@ -456,6 +456,7 @@ if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU|Clang|AppleClang")
"NDEBUG"
">"
"ENABLE_OPENGL;"
"SPDLOG_ACTIVE_LEVEL=0;"
)
endif()
################################################################################

View File

@ -100,11 +100,15 @@ namespace Ship {
}
void Console::Draw() {
if (!this->opened) {
CVar_SetS32("gConsoleEnabled", 0);
return;
}
bool input_focus = false;
if (!this->opened) return;
ImGui::SetNextWindowSize(ImVec2(520, 600), ImGuiCond_FirstUseEver);
ImGui::Begin("Console", nullptr, ImGuiWindowFlags_NoFocusOnAppearing);
ImGui::Begin("Console", &this->opened, ImGuiWindowFlags_NoFocusOnAppearing);
const ImVec2 pos = ImGui::GetWindowPos();
const ImVec2 size = ImGui::GetWindowSize();
// SohImGui::ShowCursor(ImGui::IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows | ImGuiHoveredFlags_RectOnly), SohImGui::Dialogues::dConsole);

View File

@ -6,6 +6,7 @@
#include "KeyboardController.h"
#include "SDLController.h"
#include <Utils/StringHelper.h>
#include "Cvar.h"
namespace Ship {
uint8_t* controllerBits;
@ -51,18 +52,31 @@ namespace Ship {
*controllerBits |= (backend->Connected()) << slot;
}
void ControlDeck::WriteToPad(OSContPad* pad) const {
for (size_t i = 0; i < virtualDevices.size(); i++) {
const std::shared_ptr<Controller> backend = physicalDevices[virtualDevices[i]];
if (backend->GetGuid() == "Auto") {
for (const auto& device : physicalDevices) {
device->Read(&pad[i], i);
}
continue;
}
backend->Read(&pad[i], i);
}
}
void ControlDeck::WriteToPad(OSContPad* pad) const {
#ifdef __SWITCH__
bool shouldBlockGameInput = CVar_GetS32("gOpenMenuBar", 0);
#else
bool shouldBlockGameInput = CVar_GetS32("gOpenMenuBar", 0) && CVar_GetS32("gControlNav", 0);
#endif
for (size_t i = 0; i < virtualDevices.size(); i++) {
const std::shared_ptr<Controller> backend = physicalDevices[virtualDevices[i]];
if (backend->GetGuid() == "Auto") {
for (const auto& device : physicalDevices) {
if(shouldBlockGameInput && device->GetGuid() != "Keyboard") {
continue;
}
device->Read(&pad[i], i);
}
continue;
}
if(shouldBlockGameInput && backend->GetGuid() != "Keyboard") {
continue;
}
backend->Read(&pad[i], i);
}
}
#define NESTED(key, ...) StringHelper::Sprintf("Controllers.%s.Slot_%d." key, device->GetGuid().c_str(), slot, __VA_ARGS__)

File diff suppressed because it is too large Load Diff

View File

@ -86,6 +86,12 @@ namespace SohImGui {
void EnhancementColor(const char* text, const char* cvarName, ImVec4 ColorRGBA, ImVec4 default_colors, bool allow_rainbow = true, bool has_alpha=false, bool TitleSameLine=false);
void EnhancementCombo(const std::string& name, const char* cvarName, const std::vector<std::string>& items, int defaultValue = 0);
void applyEnhancementPresets(void);
void applyEnhancementPresetDefault(void);
void applyEnhancementPresetVanillaPlus(void);
void applyEnhancementPresetEnhanced(void);
void applyEnhancementPresetRandomizer(void);
void DrawMainMenuAndCalculateGameSize(void);
void DrawFramebufferAndGameInput(void);
@ -106,6 +112,12 @@ namespace SohImGui {
void EndGroupPanel(float minHeight = 0.0f);
std::string BreakTooltip(const char* text, int lineLength = 60);
std::string BreakTooltip(const std::string& text, int lineLength = 60);
void InsertPadding(float extraVerticalPadding = 0.0f);
void PaddedSeparator(bool padTop = true, bool padBottom = true, float extraVerticalTopPadding = 0.0f, float extraVerticalBottomPadding = 0.0f);
void PaddedEnhancementSliderInt(const char* text, const char* id, const char* cvarName, int min, int max, const char* format, int defaultValue = 0, bool PlusMinusButton = false, bool padTop = true, bool padBottom = true);
void PaddedEnhancementCheckbox(const char* text, const char* cvarName, bool padTop = true, bool padBottom = true);
void PaddedText(const char* text, bool padTop = true, bool padBottom = true);
std::string GetWindowButtonText(const char* text, bool menuOpen);
}
#endif
#endif

View File

@ -239,8 +239,8 @@ static LRESULT CALLBACK gfx_dxgi_wnd_proc(HWND h_wnd, UINT message, WPARAM w_par
dxgi.current_height = (uint32_t)(l_param >> 16);
break;
case WM_DESTROY:
Ship::ExecuteHooks<Ship::ExitGame>();
exit(0);
PostQuitMessage(0);
break;
case WM_PAINT:
if (dxgi.in_paint) {
dxgi.recursive_paint_detected = true;
@ -378,6 +378,8 @@ static void gfx_dxgi_main_loop(void (*run_one_game_iter)(void)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
Ship::ExecuteHooks<Ship::ExitGame>();
}
static void gfx_dxgi_get_dimensions(uint32_t *width, uint32_t *height) {

View File

@ -166,10 +166,12 @@ static void gfx_sdl_init(const char *game_name, bool start_in_fullscreen, uint32
#ifdef __SWITCH__
// For Switch we need to set the window width before creating the window
Ship::Switch::GetDisplaySize(&window_width, &window_height);
width = window_width;
height = window_height;
#endif
wnd = SDL_CreateWindow(title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
window_width, window_height, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
width, height, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
#ifndef __SWITCH__
SDL_GL_GetDrawableSize(wnd, &window_width, &window_height);
@ -238,6 +240,8 @@ static void gfx_sdl_main_loop(void (*run_one_game_iter)(void)) {
Ship::Switch::Exit();
#endif
Ship::ExecuteHooks<Ship::ExitGame>();
SDL_Quit();
}
static void gfx_sdl_get_dimensions(uint32_t *width, uint32_t *height) {
@ -307,9 +311,8 @@ static void gfx_sdl_handle_events(void) {
CVar_Save();
break;
case SDL_QUIT:
Ship::ExecuteHooks<Ship::ExitGame>();
SDL_Quit(); // bandaid fix for linux window closing issue
exit(0);
is_running = false;
break;
}
}
}

View File

@ -82,19 +82,14 @@ namespace Ship {
break;
}
//Lock.lock();
std::shared_ptr<File> ToLoad = FileLoadQueue.front();
FileLoadQueue.pop();
//Lock.unlock();
OTR->LoadFile(ToLoad->path, true, ToLoad);
//Lock.lock();
if (!ToLoad->bHasLoadError)
FileCache[ToLoad->path] = ToLoad->bIsLoaded && !ToLoad->bHasLoadError ? ToLoad : nullptr;
//Lock.unlock();
SPDLOG_DEBUG("Loaded File {} on ResourceMgr thread", ToLoad->path);
ToLoad->FileLoadNotifier.notify_all();
@ -117,10 +112,8 @@ namespace Ship {
}
std::shared_ptr<ResourcePromise> ToLoad = nullptr;
//ResLock.lock();
ToLoad = ResourceLoadQueue.front();
ResourceLoadQueue.pop();
//ResLock.unlock();
// Wait for the underlying File to complete loading
{
@ -148,9 +141,6 @@ namespace Ship {
SPDLOG_DEBUG("Loaded Resource {} on ResourceMgr thread", ToLoad->file->path);
// Disabled for now because it can cause random crashes
//FileCache[Res->File->path] = nullptr;
//FileCache.erase(FileCache.find(Res->File->path));
Res->file = nullptr;
}
else {
@ -159,9 +149,6 @@ namespace Ship {
SPDLOG_ERROR("Resource load FAILED {} on ResourceMgr thread", ToLoad->file->path);
}
//ResLock.lock();
//ResLock.unlock();
}
}
else

View File

@ -19,7 +19,7 @@
#include <string>
#include <chrono>
#include "Console.h"
#include "Cvar.h"
#include "ImGuiImpl.h"
#include <iostream>
@ -67,10 +67,9 @@ extern "C" {
pad->gyro_x = 0;
pad->gyro_y = 0;
if (!CVar_GetS32("gOpenMenuBar", 0)) {
Ship::GlobalCtx2::GetInstance()->GetWindow()->GetControlDeck()->WriteToPad(pad);
}
if (SohImGui::controller->Opened) return;
Ship::GlobalCtx2::GetInstance()->GetWindow()->GetControlDeck()->WriteToPad(pad);
Ship::ExecuteHooks<Ship::ControllerRead>(pad);
}

View File

@ -1,7 +1,6 @@
#!/bin/bash
cmake --no-warn-unused-cli -H. -Bbuild-cmake -GNinja -DCMAKE_BUILD_TYPE:STRING=Release
cmake --build build-cmake --target ExtractAssets --
cmake --build build-cmake --config Release --
(cd build-cmake && cpack -G External)

View File

@ -1,7 +1,4 @@
#!/bin/bash
cmake --no-warn-unused-cli -H. -Bbuild-linux -GNinja
cmake --build build-linux --target ExtractAssets
cmake -H. -Bbuild-switch -GNinja -DCMAKE_TOOLCHAIN_FILE=/opt/devkitpro/cmake/Switch.cmake
cmake --build build-switch --target soh_nro

View File

@ -216,6 +216,13 @@ set(Header_Files__soh__Enhancements__randomizer__3drando
)
source_group("Header Files\\soh\\Enhancements\\randomizer\\3drando" FILES ${Header_Files__soh__Enhancements__randomizer__3drando})
set(Header_Files__soh__Enhancements__custom_message
"soh/Enhancements/custom-message/CustomMessageTypes.h"
"soh/Enhancements/custom-message/CustomMessageManager.h"
)
source_group("Header Files\\soh\\Enhancements\\custom-message" FILES ${Header_Files__soh__Enhancements__custom_message})
set(Source_Files__soh
"soh/GbiWrap.cpp"
"soh/OTRAudio.h"
@ -328,6 +335,12 @@ set(Source_Files__soh__Enhancements__randomizer__3drando__location_access
)
source_group("Source Files\\soh\\Enhancements\\randomizer\\3drando\\location_access" FILES ${Source_Files__soh__Enhancements__randomizer__3drando__location_access})
set(Source_Files__soh__Enhancements__custom_message
"soh/Enhancements/custom-message/CustomMessageManager.cpp"
)
source_group("Source Files\\soh\\Enhancements\\custom-message" FILES ${Source_Files__soh__Enhancements__custom_message})
set(Source_Files__src__boot
"src/boot/assert.c"
"src/boot/boot_main.c"
@ -1536,6 +1549,7 @@ set(ALL_FILES
${Header_Files__soh__Enhancements__debugger}
${Header_Files__soh__Enhancements__randomizer}
${Header_Files__soh__Enhancements__randomizer__3drando}
${Header_Files__soh__Enhancements__custom_message}
${Source_Files__soh}
${Source_Files__soh__Enhancements}
${Source_Files__soh__Enhancements__cosmetics}
@ -1544,6 +1558,7 @@ set(ALL_FILES
${Source_Files__soh__Enhancements__randomizer__3drando}
${Source_Files__soh__Enhancements__randomizer__3drando__hint_list}
${Source_Files__soh__Enhancements__randomizer__3drando__location_access}
${Source_Files__soh__Enhancements__custom_message}
${Source_Files__src__boot}
${Source_Files__src__buffers}
${Source_Files__src__code}
@ -1561,7 +1576,7 @@ set(ALL_FILES
################################################################################
# Target
################################################################################
add_executable(${PROJECT_NAME} ${ALL_FILES})
add_executable(${PROJECT_NAME} ${ALL_FILES} ${CMAKE_CURRENT_SOURCE_DIR}/Resource.rc)
if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
use_props(${PROJECT_NAME} "${CMAKE_CONFIGURATION_TYPES}" "${DEFAULT_CXX_PROPS}")
@ -1807,7 +1822,6 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang|AppleClang")
$<$<COMPILE_LANGUAGE:CXX>:-fpermissive>
$<$<COMPILE_LANGUAGE:CXX>:-Wno-deprecated-enum-enum-conversion>
-pthread
-O3 -ffast-math
)
target_link_options(${PROJECT_NAME} PRIVATE
@ -1981,13 +1995,16 @@ endif()
if(CMAKE_SYSTEM_NAME MATCHES "NintendoSwitch")
set_target_properties(soh PROPERTIES
APP_TITLE "Ship of Harkirian"
APP_AUTHOR "Ship"
APP_VERSION "3.0.0"
ICON "icon.jpg")
nx_create_nro(soh)
nx_generate_nacp(Ship.nacp
NAME "Ship of Harkinian"
AUTHOR "Harbour Masters"
VERSION "3.1.0"
)
nx_create_nro(soh
NACP Ship.nacp
ICON ${CMAKE_CURRENT_SOURCE_DIR}/icon.jpg
)
INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/soh.nro DESTINATION . COMPONENT ship)

View File

@ -25,7 +25,7 @@ typedef struct {
/* 0x0C */ Vec3f position;
/* 0x18 */ Vec3s unkVelocity;
/* 0x1E */ Vec3s unkPosition;
/* 0x24 */ s32 epoch;
/* 0x24 */ u32 epoch;
} EffectSparkElement; // size = 0x28
typedef struct {
@ -118,7 +118,7 @@ typedef struct {
/* 0x10 */ f32 startX;
/* 0x14 */ s16 yaw;
/* 0x16 */ s16 pitch;
/* 0x18 */ s32 epoch;
/* 0x18 */ u32 epoch;
} EffectShieldParticleElement; // size = 0x1C
typedef struct {

View File

@ -374,6 +374,7 @@ typedef enum {
FLAG_SCENE_TREASURE,
FLAG_SCENE_CLEAR,
FLAG_SCENE_COLLECTIBLE,
FLAG_COW_MILKED
} FlagType;
typedef struct {
@ -637,6 +638,7 @@ typedef struct Player {
/* 0x0A88 */ Vec3f unk_A88; // previous body part 0 position
/* 0x0A89 */ bool pendingIceTrap;
/* 0x0A95 */ PendingFlag pendingFlag;
} Player; // size = 0xAA1
/* 0x0AA1 */ u8 boomerangQuickRecall; // Has the player pressed the boomerang button while it's in the air still?
} Player; // size = 0xAA2
#endif

View File

@ -1124,7 +1124,7 @@ void DrawCosmeticsEditor(bool& open) {
return;
}
ImGui::SetNextWindowSize(ImVec2(465, 430), ImGuiCond_FirstUseEver);
if (!ImGui::Begin("Cosmetics Editor", &open, ImGuiWindowFlags_NoFocusOnAppearing)) {
if (!ImGui::Begin("Cosmetics Editor", &open)) {
ImGui::End();
return;
}
@ -1162,7 +1162,7 @@ void InitCosmeticsEditor() {
//This allow to hide a window without disturbing the player nor adding things in menu
//LoadRainbowColor() will this way run in background once it's window is activated
//ImGui::SetNextItemWidth(0.0f);
SohImGui::AddWindow("Cosmetics", "Rainbowfunction", LoadRainbowColor, true, true);
SohImGui::AddWindow("Enhancements", "Rainbowfunction", LoadRainbowColor, true, true);
//Draw the bar in the menu.
SohImGui::AddWindow("Cosmetics", "Cosmetics Editor", DrawCosmeticsEditor);
}
SohImGui::AddWindow("Enhancements", "Cosmetics Editor", DrawCosmeticsEditor);
}

View File

@ -0,0 +1,150 @@
#include "CustomMessageManager.h"
#include <algorithm>
using namespace std::literals::string_literals;
CustomMessageManager::CustomMessageManager() {
this->textBoxSpecialCharacters = { { "À", 0x80 }, { "î", 0x81 }, { "Â", 0x82 }, { "Ä", 0x83 }, { "Ç", 0x84 },
{ "È", 0x85 }, { "É", 0x86 }, { "Ê", 0x87 }, { "Ë", 0x88 }, { "Ï", 0x89 },
{ "Ô", 0x8A }, { "Ö", 0x8B }, { "Ù", 0x8C }, { "Û", 0x8D }, { "Ü", 0x8E },
{ "ß", 0x8F }, { "à", 0x90 }, { "á", 0x91 }, { "â", 0x92 }, { "ä", 0x93 },
{ "ç", 0x94 }, { "è", 0x95 }, { "é", 0x96 }, { "ê", 0x97 }, { "ë", 0x98 },
{ "ï", 0x99 }, { "ô", 0x9A }, { "ö", 0x9B }, { "ù", 0x9C }, { "û", 0x9D },
{ "ü", 0x9E } };
this->colors = { { "w", QM_WHITE }, { "r", QM_RED }, { "g", QM_GREEN }, { "b", QM_BLUE },
{ "c", QM_LBLUE }, { "p", QM_PINK }, { "y", QM_YELLOW }, { "B", QM_BLACK } };
}
CustomMessageManager::~CustomMessageManager() {
this->textBoxSpecialCharacters.clear();
this->colors.clear();
this->messageTables.clear();
}
void CustomMessageManager::ReplaceSpecialCharacters(std::string& string) {
// add special characters
for (auto specialCharacterPair : this->textBoxSpecialCharacters) {
size_t start_pos = 0;
std::string textBoxSpecialCharacterString = ""s;
textBoxSpecialCharacterString += specialCharacterPair.second;
while ((start_pos = string.find(specialCharacterPair.first, 0)) != std::string::npos) {
string.replace(start_pos, specialCharacterPair.first.length(), textBoxSpecialCharacterString);
start_pos += textBoxSpecialCharacterString.length();
}
}
}
void CustomMessageManager::ReplaceColors(std::string& string) {
for (auto colorPair : colors) {
std::string textToReplace = "%";
textToReplace += colorPair.first;
size_t start_pos = 0;
while ((start_pos = string.find(textToReplace)) != std::string::npos) {
string.replace(start_pos, textToReplace.length(), COLOR(colorPair.second));
start_pos += textToReplace.length();
}
}
}
void CustomMessageManager::FormatCustomMessage(std::string& message, ItemID iid) {
message.insert(0, ITEM_OBTAINED(iid));
size_t start_pos = 0;
std::replace(message.begin(), message.end(), '&', NEWLINE()[0]);
while ((start_pos = message.find('^', start_pos)) != std::string::npos) {
message.replace(start_pos, 1, WAIT_FOR_INPUT() + ITEM_OBTAINED(iid));
start_pos += 3;
}
std::replace(message.begin(), message.end(), '@', PLAYER_NAME()[0]);
ReplaceSpecialCharacters(message);
ReplaceColors(message);
message += MESSAGE_END();
}
void CustomMessageManager::FormatCustomMessage(std::string& message) {
size_t start_pos = 0;
std::replace(message.begin(), message.end(), '&', NEWLINE()[0]);
std::replace(message.begin(), message.end(), '^', WAIT_FOR_INPUT()[0]);
std::replace(message.begin(), message.end(), '@', PLAYER_NAME()[0]);
ReplaceSpecialCharacters(message);
ReplaceColors(message);
message += MESSAGE_END();
}
bool CustomMessageManager::InsertCustomMessage(std::string tableID, uint16_t textID, CustomMessageEntry messages) {
auto foundMessageTable = messageTables.find(tableID);
if (foundMessageTable == messageTables.end()) {
return false;
}
auto& messageTable = foundMessageTable->second;
auto messageInsertResult = messageTable.emplace(textID, messages);
return messageInsertResult.second;
}
bool CustomMessageManager::CreateGetItemMessage(std::string tableID, GetItemID giid, ItemID iid, CustomMessageEntry messageEntry) {
FormatCustomMessage(messageEntry.english, iid);
FormatCustomMessage(messageEntry.german, iid);
FormatCustomMessage(messageEntry.french, iid);
const uint16_t textID = giid;
return InsertCustomMessage(tableID, textID, messageEntry);
}
bool CustomMessageManager::CreateMessage(std::string tableID, uint16_t textID, CustomMessageEntry messageEntry) {
FormatCustomMessage(messageEntry.english);
FormatCustomMessage(messageEntry.german);
FormatCustomMessage(messageEntry.french);
return InsertCustomMessage(tableID, textID, messageEntry);
}
CustomMessageEntry CustomMessageManager::RetrieveMessage(std::string tableID, uint16_t textID) {
std::unordered_map<std::string, CustomMessageTable>::const_iterator foundMessageTable = messageTables.find(tableID);
if (foundMessageTable == messageTables.end()) {
return NULL_CUSTOM_MESSAGE;
}
CustomMessageTable messageTable = foundMessageTable->second;
std::unordered_map<uint16_t, CustomMessageEntry>::const_iterator foundMessage = messageTable.find(textID);
if (foundMessage == messageTable.end()) {
return NULL_CUSTOM_MESSAGE;
}
CustomMessageEntry message = foundMessage->second;
return message;
}
bool CustomMessageManager::ClearMessageTable(std::string tableID) {
auto foundMessageTable = messageTables.find(tableID);
if (foundMessageTable == messageTables.end()) {
return false;
}
auto& messageTable = foundMessageTable->second;
messageTable.clear();
}
bool CustomMessageManager::AddCustomMessageTable(std::string tableID) {
CustomMessageTable newMessageTable;
return messageTables.emplace(tableID, newMessageTable).second;
}
std::string CustomMessageManager::MESSAGE_END() {
return "\x02"s;
}
std::string CustomMessageManager::ITEM_OBTAINED(uint8_t x) {
return "\x13"s + char(x);
}
std::string CustomMessageManager::NEWLINE() {
return "\x01"s;
}
std::string CustomMessageManager::COLOR(uint8_t x) {
return "\x05"s + char(x);
}
std::string CustomMessageManager::WAIT_FOR_INPUT() {
return "\x04"s;
}
std::string CustomMessageManager::PLAYER_NAME() {
return "\x0F"s;
}

View File

@ -0,0 +1,133 @@
#pragma once
#include <string>
#include <unordered_map>
#include "../../../include/z64item.h"
#undef MESSAGE_END
#define QM_WHITE 0x00
#define QM_RED 0x41
#define QM_GREEN 0x42
#define QM_BLUE 0x43
#define QM_LBLUE 0x44
#define QM_PINK 0x45
#define QM_YELLOW 0x46
#define QM_BLACK 0x47
#ifndef MESSAGE_DATA_STATIC_H
typedef enum {
/* 0 */ TEXTBOX_TYPE_BLACK,
/* 1 */ TEXTBOX_TYPE_WOODEN,
/* 2 */ TEXTBOX_TYPE_BLUE,
/* 3 */ TEXTBOX_TYPE_OCARINA,
/* 4 */ TEXTBOX_TYPE_NONE_BOTTOM,
/* 5 */ TEXTBOX_TYPE_NONE_NO_SHADOW,
/* 11 */ TEXTBOX_TYPE_CREDITS = 11
} TextBoxType;
typedef enum {
/* 0 */ TEXTBOX_BG_CROSS
} TextBoxBackground;
typedef enum {
/* 0 */ TEXTBOX_POS_VARIABLE,
/* 1 */ TEXTBOX_POS_TOP,
/* 2 */ TEXTBOX_POS_MIDDLE,
/* 3 */ TEXTBOX_POS_BOTTOM
} TextBoxPosition;
#endif
typedef struct {
TextBoxType textBoxType;
TextBoxPosition textBoxPos;
std::string english;
std::string german;
std::string french;
} CustomMessageEntry;
// Message Entry without the text type and position, useful for when
// you need an array of these to loop over for registration
// that will all have the same textbox type and position.
typedef struct {
std::string english;
std::string german;
std::string french;
} CustomMessageMinimal;
#define NULL_CUSTOM_MESSAGE \
{ (TextBoxType)(-1), (TextBoxPosition)(-1), "", "", "" }
typedef std::unordered_map<uint16_t, CustomMessageEntry> CustomMessageTable;
class CustomMessageManager {
private:
std::unordered_map<std::string, char> textBoxSpecialCharacters;
std::unordered_map<std::string, char> colors;
std::unordered_map<std::string, CustomMessageTable> messageTables;
void ReplaceSpecialCharacters(std::string &string);
void ReplaceColors(std::string& string);
bool InsertCustomMessage(std::string tableID, uint16_t textID, CustomMessageEntry messages);
std::string MESSAGE_END();
std::string ITEM_OBTAINED(uint8_t x);
std::string NEWLINE();
std::string COLOR(uint8_t x);
std::string WAIT_FOR_INPUT();
std::string PLAYER_NAME();
public:
static CustomMessageManager* Instance;
CustomMessageManager();
~CustomMessageManager();
/*
Formats the provided Custom Message Entry and inserts it into the table with the provided tableID,
with the provided giid (getItemID) as its key. This function also inserts the icon corresponding to
the provided iid (itemID) at the beginning of each page of the textbox.
*/
bool CreateGetItemMessage(std::string tableID, GetItemID giid, ItemID iid, CustomMessageEntry messages);
/*
Formats the provided Custom Message Entry and inserts it into the table with the provided tableID,
with the provided textID as its key.
*/
bool CreateMessage(std::string tableID, uint16_t textID, CustomMessageEntry messages);
/*
Retrieves a message from the table with id tableID with the provided textID.
Returns a NULL_CUSTOM_MESSAGE if the message or table does not exist.
*/
CustomMessageEntry RetrieveMessage(std::string tableID, uint16_t textID);
/*
Empties out the message table identified by tableID.
Returns true if successful and false if not (for instance
if a table with the provided tableID does not exist).
*/
bool ClearMessageTable(std::string tableID);
/*
Creates an empty CustomMessageTable accessible at the provided
tableID, returns true if creation was successful and false
if not.
*/
bool AddCustomMessageTable(std::string tableID);
/*
Replaces special characters and certain symbols with control codes
& for newline, ^ for wait-for-input, and @ for the player name,
as well as %<letter> for colors (i.e. %r for red and %w for white).
*/
void FormatCustomMessage(std::string& message, ItemID iid);
/*
Replaces special characters and certain symbols with control codes
& for newline, ^ for wait-for-input, and @ for the player name,
as well as %<letter> for colors (i.e. %r for red and %w for white).
*/
void FormatCustomMessage(std::string& message);
};

View File

@ -0,0 +1,33 @@
#pragma once
typedef enum {
TEXT_GS_NO_FREEZE = 0xB4,
TEXT_GS_FREEZE = 0xB5,
TEXT_RANDOMIZER_CUSTOM_ITEM = 0xF8,
TEXT_SCRUB_POH = 0x10A2,
TEXT_SCRUB_STICK_UPGRADE = 0x10DC,
TEXT_SCRUB_NUT_UPGRADE = 0x10DD,
TEXT_RANDOMIZER_GOSSIP_STONE_HINTS = 0x2053,
TEXT_ALTAR_CHILD = 0x7040,
TEXT_ALTAR_ADULT = 0x7088,
TEXT_GANONDORF = 0x70CC,
TEXT_GANONDORF_NOHINT = 0x70CD
} TextIDs;
#ifdef __cplusplus
typedef struct {
GetItemID giid;
ItemID iid;
std::string english;
std::string german;
std::string french;
} GetItemMessage;
#define GIMESSAGE(giid, iid, english, german, french) \
{ giid, iid, english, german, french }
#define GIMESSAGE_UNTRANSLATED(giid, iid, message) \
{ giid, iid, message, message, message }
#endif

View File

@ -165,6 +165,11 @@ void LocationTable_Init() {
//Zoras River
locationTable[ZR_OPEN_GROTTO_CHEST] = ItemLocation::Chest (0x3E, 0x09, "ZR Open Grotto Chest", ZR_OPEN_GROTTO_CHEST, RED_RUPEE, {Category::cZorasRiver, Category::cGrotto,}, SpoilerCollectionCheckGroup::GROUP_ZORAS_RIVER);
locationTable[ZR_MAGIC_BEAN_SALESMAN] = ItemLocation::Base (0x54, 0x16, "ZR Magic Bean Salesman", ZR_MAGIC_BEAN_SALESMAN, MAGIC_BEAN, {Category::cZorasRiver,}, SpoilerCollectionCheck::MagicBeans(0x54, 0x01), SpoilerCollectionCheckGroup::GROUP_ZORAS_RIVER);
locationTable[ZR_FROGS_ZELDAS_LULLABY] = ItemLocation::Base (0x54, 0x3E, "ZR Frogs Zelda's Lullaby", ZR_FROGS_ZELDAS_LULLABY, PURPLE_RUPEE, {Category::cZorasRiver,}, SpoilerCollectionCheck::EventChkInf(0xD6), SpoilerCollectionCheckGroup::GROUP_ZORAS_RIVER);
locationTable[ZR_FROGS_EPONAS_SONG] = ItemLocation::Base (0x54, 0x3E, "ZR Frogs Epona's Song", ZR_FROGS_EPONAS_SONG, PURPLE_RUPEE, {Category::cZorasRiver,}, SpoilerCollectionCheck::EventChkInf(0xD6), SpoilerCollectionCheckGroup::GROUP_ZORAS_RIVER);
locationTable[ZR_FROGS_SARIAS_SONG] = ItemLocation::Base (0x54, 0x3E, "ZR Frogs Saria's Song", ZR_FROGS_SARIAS_SONG, PURPLE_RUPEE, {Category::cZorasRiver,}, SpoilerCollectionCheck::EventChkInf(0xD6), SpoilerCollectionCheckGroup::GROUP_ZORAS_RIVER);
locationTable[ZR_FROGS_SUNS_SONG] = ItemLocation::Base (0x54, 0x3E, "ZR Frogs Sun's Song", ZR_FROGS_SUNS_SONG, PURPLE_RUPEE, {Category::cZorasRiver,}, SpoilerCollectionCheck::EventChkInf(0xD6), SpoilerCollectionCheckGroup::GROUP_ZORAS_RIVER);
locationTable[ZR_FROGS_SONG_OF_TIME] = ItemLocation::Base (0x54, 0x3E, "ZR Frogs Song of Time", ZR_FROGS_SONG_OF_TIME, PURPLE_RUPEE, {Category::cZorasRiver,}, SpoilerCollectionCheck::EventChkInf(0xD6), SpoilerCollectionCheckGroup::GROUP_ZORAS_RIVER);
locationTable[ZR_FROGS_IN_THE_RAIN] = ItemLocation::Base (0x54, 0x3E, "ZR Frogs in the Rain", ZR_FROGS_IN_THE_RAIN, PIECE_OF_HEART, {Category::cZorasRiver,}, SpoilerCollectionCheck::EventChkInf(0xD6), SpoilerCollectionCheckGroup::GROUP_ZORAS_RIVER);
locationTable[ZR_FROGS_OCARINA_GAME] = ItemLocation::Base (0x54, 0x76, "ZR Frogs Ocarina Game", ZR_FROGS_OCARINA_GAME, PIECE_OF_HEART, {Category::cZorasRiver, Category::cMinigame,}, SpoilerCollectionCheck::EventChkInf(0xD0), SpoilerCollectionCheckGroup::GROUP_ZORAS_RIVER);
locationTable[ZR_NEAR_OPEN_GROTTO_FREESTANDING_POH] = ItemLocation::Collectable(0x54, 0x04, "ZR Near Open Grotto Freestanding PoH", ZR_NEAR_OPEN_GROTTO_FREESTANDING_POH, PIECE_OF_HEART, {Category::cZorasRiver,}, SpoilerCollectionCheckGroup::GROUP_ZORAS_RIVER);
@ -1302,6 +1307,11 @@ std::vector<uint32_t> overworldLocations = {
//Zoras River
ZR_OPEN_GROTTO_CHEST,
ZR_MAGIC_BEAN_SALESMAN,
ZR_FROGS_ZELDAS_LULLABY,
ZR_FROGS_EPONAS_SONG,
ZR_FROGS_SARIAS_SONG,
ZR_FROGS_SUNS_SONG,
ZR_FROGS_SONG_OF_TIME,
ZR_FROGS_IN_THE_RAIN,
ZR_FROGS_OCARINA_GAME,
ZR_NEAR_OPEN_GROTTO_FREESTANDING_POH,

View File

@ -729,6 +729,16 @@ void GenerateItemPool() {
PlaceItemInLocation(WASTELAND_BOMBCHU_SALESMAN, BOMBCHU_10, false, true);
}
if (ShuffleFrogSongRupees) {
AddItemToMainPool(PURPLE_RUPEE, 5);
} else {
PlaceItemInLocation(ZR_FROGS_ZELDAS_LULLABY, PURPLE_RUPEE, false, true);
PlaceItemInLocation(ZR_FROGS_EPONAS_SONG, PURPLE_RUPEE, false, true);
PlaceItemInLocation(ZR_FROGS_SARIAS_SONG, PURPLE_RUPEE, false, true);
PlaceItemInLocation(ZR_FROGS_SUNS_SONG, PURPLE_RUPEE, false, true);
PlaceItemInLocation(ZR_FROGS_SONG_OF_TIME, PURPLE_RUPEE, false, true);
}
if (ShuffleAdultTradeQuest) {
AddItemToMainPool(POCKET_EGG);
AddItemToMainPool(COJIRO);

View File

@ -472,6 +472,11 @@ typedef enum {
//ZORA'S_RIVER
ZR_MAGIC_BEAN_SALESMAN,
ZR_OPEN_GROTTO_CHEST,
ZR_FROGS_ZELDAS_LULLABY,
ZR_FROGS_EPONAS_SONG,
ZR_FROGS_SARIAS_SONG,
ZR_FROGS_SUNS_SONG,
ZR_FROGS_SONG_OF_TIME,
ZR_FROGS_IN_THE_RAIN,
ZR_FROGS_OCARINA_GAME,
ZR_NEAR_OPEN_GROTTO_FREESTANDING_POH,

View File

@ -31,6 +31,21 @@ void AreaTable_Init_ZorasDomain() {
LocationAccess(ZR_FROGS_IN_THE_RAIN, {[]{return IsChild && CanPlay(SongOfStorms);},
/*Glitched*/[]{return (CanDoGlitch(GlitchType::OutdoorBombOI, GlitchDifficulty::ADVANCED) || ((Bugs || Fish) && CanShield && CanDoGlitch(GlitchType::QPA, GlitchDifficulty::ADVANCED)) ||
((Bugs || Fish) && CanShield && HasBombchus && CanDoGlitch(GlitchType::ActionSwap, GlitchDifficulty::ADVANCED))) && IsChild && SongOfStorms;}}),
LocationAccess(ZR_FROGS_ZELDAS_LULLABY, {[]{return IsChild && CanPlay(ZeldasLullaby);},
/*Glitched*/[]{return (CanDoGlitch(GlitchType::OutdoorBombOI, GlitchDifficulty::ADVANCED) || ((Bugs || Fish) && CanShield && CanDoGlitch(GlitchType::QPA, GlitchDifficulty::ADVANCED)) ||
((Bugs || Fish) && CanShield && HasBombchus && CanDoGlitch(GlitchType::ActionSwap, GlitchDifficulty::ADVANCED))) && IsChild && ZeldasLullaby;}}),
LocationAccess(ZR_FROGS_EPONAS_SONG, {[]{return IsChild && CanPlay(EponasSong);},
/*Glitched*/[]{return (CanDoGlitch(GlitchType::OutdoorBombOI, GlitchDifficulty::ADVANCED) || ((Bugs || Fish) && CanShield && CanDoGlitch(GlitchType::QPA, GlitchDifficulty::ADVANCED)) ||
((Bugs || Fish) && CanShield && HasBombchus && CanDoGlitch(GlitchType::ActionSwap, GlitchDifficulty::ADVANCED))) && IsChild && EponasSong;}}),
LocationAccess(ZR_FROGS_SARIAS_SONG, {[]{return IsChild && CanPlay(SariasSong);},
/*Glitched*/[]{return (CanDoGlitch(GlitchType::OutdoorBombOI, GlitchDifficulty::ADVANCED) || ((Bugs || Fish) && CanShield && CanDoGlitch(GlitchType::QPA, GlitchDifficulty::ADVANCED)) ||
((Bugs || Fish) && CanShield && HasBombchus && CanDoGlitch(GlitchType::ActionSwap, GlitchDifficulty::ADVANCED))) && IsChild && SariasSong;}}),
LocationAccess(ZR_FROGS_SUNS_SONG, {[]{return IsChild && CanPlay(SunsSong);},
/*Glitched*/[]{return (CanDoGlitch(GlitchType::OutdoorBombOI, GlitchDifficulty::ADVANCED) || ((Bugs || Fish) && CanShield && CanDoGlitch(GlitchType::QPA, GlitchDifficulty::ADVANCED)) ||
((Bugs || Fish) && CanShield && HasBombchus && CanDoGlitch(GlitchType::ActionSwap, GlitchDifficulty::ADVANCED))) && IsChild && SunsSong;}}),
LocationAccess(ZR_FROGS_SONG_OF_TIME, {[]{return IsChild && CanPlay(SongOfTime);},
/*Glitched*/[]{return (CanDoGlitch(GlitchType::OutdoorBombOI, GlitchDifficulty::ADVANCED) || ((Bugs || Fish) && CanShield && CanDoGlitch(GlitchType::QPA, GlitchDifficulty::ADVANCED)) ||
((Bugs || Fish) && CanShield && HasBombchus && CanDoGlitch(GlitchType::ActionSwap, GlitchDifficulty::ADVANCED))) && IsChild && SongOfTime;}}),
LocationAccess(ZR_NEAR_OPEN_GROTTO_FREESTANDING_POH, {[]{return IsChild || CanUse(HOVER_BOOTS) || (IsAdult && LogicZoraRiverLower);}}),
LocationAccess(ZR_NEAR_DOMAIN_FREESTANDING_POH, {[]{return IsChild || CanUse(HOVER_BOOTS) || (IsAdult && LogicZoraRiverUpper);}}),
LocationAccess(ZR_GS_LADDER, {[]{return IsChild && AtNight && CanChildAttack && CanGetNightTimeGS;}}),

View File

@ -385,6 +385,13 @@ string_view merchantsHintsDesc = "These hints will make Medigoron and the
"The Clearer Hints setting will affect how they\n" //
"refer to the item."; //
/*------------------------------ //
| SHUFFLE FROG SONG RUPEES | //
------------------------------*/ //
string_view frogSongRupeesDesc = "Enabling this adds 5 Purple Rupees to the item\n" //
"pool and shuffles the rewards from playing Zelda's\n"
"Lullaby, Epona's Song, Saria's Song, Sun's Song,\n"
"and Song of Time to the frogs in Zora's River.\n";//
/*------------------------------ //
| SHUFFLE ADULT TRADE | //
------------------------------*/ //
string_view adultTradeDesc = "Enabling this adds all of the adult trade quest\n"//

View File

@ -126,6 +126,8 @@ extern string_view magicBeansDesc;
extern string_view merchantsDesc;
extern string_view merchantsHintsDesc;
extern string_view frogSongRupeesDesc;
extern string_view adultTradeDesc;
extern string_view chestMinigameDesc;

View File

@ -166,6 +166,7 @@ namespace Settings {
Option ShuffleGerudoToken = Option::Bool("Shuffle Gerudo Card", {"Off", "On"}, {gerudoTokenDesc});
Option ShuffleMagicBeans = Option::Bool("Shuffle Magic Beans", {"Off", "On"}, {magicBeansDesc});
Option ShuffleMerchants = Option::U8 ("Shuffle Merchants", {"Off", "On (No Hints)", "On (With Hints)"}, {merchantsDesc, merchantsHintsDesc});
Option ShuffleFrogSongRupees = Option::Bool("Shuffle Frog Song Rupees",{"Off", "On"}, {frogSongRupeesDesc});
Option ShuffleAdultTradeQuest = Option::Bool("Shuffle Adult Trade", {"Off", "On"}, {adultTradeDesc});
Option ShuffleChestMinigame = Option::U8 ("Shuffle Chest Minigame", {"Off", "On (Separate)", "On (Pack)"}, {chestMinigameDesc});
std::vector<Option *> shuffleOptions = {
@ -183,6 +184,7 @@ namespace Settings {
&ShuffleGerudoToken,
&ShuffleMagicBeans,
&ShuffleMerchants,
&ShuffleFrogSongRupees,
&ShuffleAdultTradeQuest,
&ShuffleChestMinigame,
};
@ -1263,6 +1265,7 @@ namespace Settings {
ctx.shuffleGerudoToken = (ShuffleGerudoToken) ? 1 : 0;
ctx.shuffleMagicBeans = (ShuffleMagicBeans) ? 1 : 0;
ctx.shuffleMerchants = ShuffleMerchants.Value<uint8_t>();
ctx.shuffleFrogSongRupees= (ShuffleFrogSongRupees) ? 1 : 0;
ctx.shuffleAdultTradeQuest = (ShuffleAdultTradeQuest) ? 1 : 0;
ctx.shuffleChestMinigame = ShuffleChestMinigame.Value<uint8_t>();
@ -1724,6 +1727,21 @@ namespace Settings {
IncludeAndHide({WASTELAND_BOMBCHU_SALESMAN});
}
//Force include frog song rupees if they're not shuffled
if (ShuffleFrogSongRupees) {
Unhide({ZR_FROGS_ZELDAS_LULLABY});
Unhide({ZR_FROGS_EPONAS_SONG});
Unhide({ZR_FROGS_SARIAS_SONG});
Unhide({ZR_FROGS_SUNS_SONG});
Unhide({ZR_FROGS_SONG_OF_TIME});
} else {
IncludeAndHide({ZR_FROGS_ZELDAS_LULLABY});
IncludeAndHide({ZR_FROGS_EPONAS_SONG});
IncludeAndHide({ZR_FROGS_SARIAS_SONG});
IncludeAndHide({ZR_FROGS_SUNS_SONG});
IncludeAndHide({ZR_FROGS_SONG_OF_TIME});
}
//Force include adult trade quest if Shuffle Adult Trade Quest is off
std::vector<uint32_t> adultTradeLocations = {KAK_TRADE_POCKET_CUCCO, LW_TRADE_COJIRO, KAK_TRADE_ODD_MUSHROOM, LW_TRADE_ODD_POTION, GV_TRADE_SAW, DMT_TRADE_BROKEN_SWORD, ZD_TRADE_PRESCRIPTION, LH_TRADE_FROG, DMT_TRADE_EYEDROPS};
if (ShuffleAdultTradeQuest) {
@ -2261,6 +2279,7 @@ namespace Settings {
&ShuffleCows,
&ShuffleMagicBeans,
&ShuffleMerchants,
&ShuffleFrogSongRupees,
&ShuffleAdultTradeQuest,
&GossipStoneHints,
};
@ -2516,6 +2535,7 @@ namespace Settings {
SkipChildStealth.SetSelectedIndex(cvarSettings[RSK_SKIP_CHILD_STEALTH]);
ShuffleGerudoToken.SetSelectedIndex(cvarSettings[RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD]);
ShuffleFrogSongRupees.SetSelectedIndex(cvarSettings[RSK_SHUFFLE_FROG_SONG_RUPEES]);
ShuffleAdultTradeQuest.SetSelectedIndex(cvarSettings[RSK_SHUFFLE_ADULT_TRADE]);

View File

@ -403,6 +403,7 @@ typedef struct {
uint8_t shuffleGerudoToken;
uint8_t shuffleMagicBeans;
uint8_t shuffleMerchants;
uint8_t shuffleFrogSongRupees;
uint8_t shuffleAdultTradeQuest;
uint8_t shuffleChestMinigame;
@ -904,6 +905,7 @@ void UpdateSettings(std::unordered_map<RandomizerSettingKey, uint8_t> cvarSettin
extern Option ShuffleGerudoToken;
extern Option ShuffleMagicBeans;
extern Option ShuffleMerchants;
extern Option ShuffleFrogSongRupees;
extern Option ShuffleAdultTradeQuest;
extern Option ShuffleChestMinigame;

View File

@ -14,13 +14,20 @@
#include "3drando/rando_main.hpp"
#include <soh/Enhancements/debugger/ImGuiHelpers.h>
#include "Lib/ImGui/imgui_internal.h"
#include <soh/Enhancements/custom-message/CustomMessageManager.h>
#include <soh/Enhancements/custom-message/CustomMessageTypes.h>
using json = nlohmann::json;
using namespace std::literals::string_literals;
std::unordered_map<uint8_t, Sprite> gSeedTextures;
u8 generated;
const std::string Randomizer::getItemMessageTableID = "Randomizer";
const std::string Randomizer::hintMessageTableID = "RandomizerHints";
const std::string Randomizer::scrubMessageTableID = "RandomizerScrubs";
Randomizer::Randomizer() {
Sprite bowSprite = { dgFairyBowIconTex, 32, 32, G_IM_FMT_RGBA, G_IM_SIZ_32b, 0 };
gSeedTextures[0] = bowSprite;
@ -183,6 +190,11 @@ std::unordered_map<std::string, RandomizerCheck> SpoilerfileCheckNameToEnum = {
{ "DMC Deku Scrub Grotto Center", RC_DMC_DEKU_SCRUB_GROTTO_CENTER },
{ "ZR Open Grotto Chest", RC_ZR_OPEN_GROTTO_CHEST },
{ "ZR Magic Bean Salesman", RC_ZR_MAGIC_BEAN_SALESMAN },
{ "ZR Frogs Zelda's Lullaby", RC_ZR_FROGS_ZELDAS_LULLABY },
{ "ZR Frogs Epona's Song", RC_ZR_FROGS_EPONAS_SONG },
{ "ZR Frogs Saria's Song", RC_ZR_FROGS_SARIAS_SONG },
{ "ZR Frogs Sun's Song", RC_ZR_FROGS_SUNS_SONG },
{ "ZR Frogs Song of Time", RC_ZR_FROGS_SONG_OF_TIME },
{ "ZR Frogs in the Rain", RC_ZR_FROGS_IN_THE_RAIN },
{ "ZR Frogs Ocarina Game", RC_ZR_FROGS_OCARINA_GAME },
{ "ZR Near Open Grotto Freestanding PoH", RC_ZR_NEAR_OPEN_GROTTO_FREESTANDING_POH },
@ -1394,6 +1406,7 @@ std::unordered_map<std::string, RandomizerSettingKey> SpoilerfileSettingNameToEn
{ "Open Settings:Random Ganon's Trials", RSK_RANDOM_TRIALS },
{ "Open Settings:Trial Count", RSK_TRIAL_COUNT },
{ "Shuffle Settings:Shuffle Cows", RSK_SHUFFLE_COWS },
{ "Shuffle Settings:Tokensanity", RSK_SHUFFLE_TOKENS },
{ "Shuffle Settings:Shuffle Adult Trade", RSK_SHUFFLE_ADULT_TRADE },
{ "Start with Deku Shield", RSK_STARTING_DEKU_SHIELD },
{ "Start with Kokiri Sword", RSK_STARTING_KOKIRI_SWORD },
@ -1401,8 +1414,8 @@ std::unordered_map<std::string, RandomizerSettingKey> SpoilerfileSettingNameToEn
{ "Shuffle Dungeon Items:Start with Maps/Compasses", RSK_STARTING_MAPS_COMPASSES },
{ "Shuffle Dungeon Items:Ganon's Boss Key", RSK_GANONS_BOSS_KEY },
{ "Misc Settings:Gossip Stone Hints", RSK_GOSSIP_STONE_HINTS },
{ "Misc Settings:Hint Clarity", RSK_HINT_CLARITY},
{ "Misc Settings:Hint Distribution", RSK_HINT_DISTRIBUTION},
{ "Misc Settings:Hint Clarity", RSK_HINT_CLARITY },
{ "Misc Settings:Hint Distribution", RSK_HINT_DISTRIBUTION },
{ "Skip Child Zelda", RSK_SKIP_CHILD_ZELDA },
{ "Start with Consumables", RSK_STARTING_CONSUMABLES },
{ "Start with Max Rupees", RSK_FULL_WALLETS },
@ -1478,6 +1491,26 @@ void Randomizer::LoadHintLocations(const char* spoilerFileName) {
ParseHintLocationsFile(spoilerFileName);
}
CustomMessageManager::Instance->ClearMessageTable(Randomizer::hintMessageTableID);
CustomMessageManager::Instance->AddCustomMessageTable(Randomizer::hintMessageTableID);
CustomMessageManager::Instance->CreateMessage(
Randomizer::hintMessageTableID, TEXT_ALTAR_CHILD,
{ TEXTBOX_TYPE_BLUE, TEXTBOX_POS_BOTTOM, gSaveContext.childAltarText,
gSaveContext.childAltarText, gSaveContext.childAltarText });
CustomMessageManager::Instance->CreateMessage(
Randomizer::hintMessageTableID, TEXT_ALTAR_ADULT,
{ TEXTBOX_TYPE_BLUE, TEXTBOX_POS_BOTTOM, gSaveContext.adultAltarText,
gSaveContext.adultAltarText, gSaveContext.adultAltarText });
CustomMessageManager::Instance->CreateMessage(
Randomizer::hintMessageTableID, TEXT_GANONDORF,
{ TEXTBOX_TYPE_BLACK, TEXTBOX_POS_BOTTOM, gSaveContext.ganonHintText,
gSaveContext.ganonHintText, gSaveContext.ganonHintText });
CustomMessageManager::Instance->CreateMessage(
Randomizer::hintMessageTableID, TEXT_GANONDORF_NOHINT,
{ TEXTBOX_TYPE_BLACK, TEXTBOX_POS_BOTTOM, gSaveContext.ganonText,
gSaveContext.ganonText, gSaveContext.ganonText });
this->childAltarText = gSaveContext.childAltarText;
this->adultAltarText = gSaveContext.adultAltarText;
this->ganonHintText = gSaveContext.ganonHintText;
@ -1486,6 +1519,8 @@ void Randomizer::LoadHintLocations(const char* spoilerFileName) {
for (auto hintLocation : gSaveContext.hintLocations) {
if(hintLocation.check == RC_LINKS_POCKET) break;
this->hintLocations[hintLocation.check] = hintLocation.hintText;
CustomMessageManager::Instance->CreateMessage(
Randomizer::hintMessageTableID, hintLocation.check, { TEXTBOX_TYPE_BLUE, TEXTBOX_POS_BOTTOM, hintLocation.hintText, hintLocation.hintText, hintLocation.hintText });
}
}
@ -1699,6 +1734,18 @@ void Randomizer::ParseRandomizerSettingsFile(const char* spoilerFileName) {
} else if (it.value() == "Skip") {
gSaveContext.randoSettings[index].value = 1;
}
break;
case RSK_SHUFFLE_TOKENS:
if (it.value() == "Off") {
gSaveContext.randoSettings[index].value = 0;
} else if (it.value() == "Dungeons") {
gSaveContext.randoSettings[index].value = 1;
} else if (it.value() == "Overworld") {
gSaveContext.randoSettings[index].value = 2;
} else if (it.value() == "All Tokens") {
gSaveContext.randoSettings[index].value = 3;
}
break;
}
index++;
}
@ -1799,57 +1846,8 @@ std::string AltarIconString(char iconChar) {
std::string FormatJsonHintText(std::string jsonHint) {
std::string formattedHintMessage = jsonHint;
char newLine = 0x01;
char playerName = 0x0F;
char nextBox = 0x04;
std::replace(formattedHintMessage.begin(), formattedHintMessage.end(), '&', newLine);
std::replace(formattedHintMessage.begin(), formattedHintMessage.end(), '^', nextBox);
std::replace(formattedHintMessage.begin(), formattedHintMessage.end(), '@', playerName);
std::unordered_map<std::string, char> textBoxSpecialCharacters = {
{"À", 0x80 },
{"î", 0x81 },
{"Â", 0x82 },
{"Ä", 0x83 },
{"Ç", 0x84 },
{"È", 0x85 },
{"É", 0x86 },
{"Ê", 0x87 },
{"Ë", 0x88 },
{"Ï", 0x89 },
{"Ô", 0x8A },
{"Ö", 0x8B },
{"Ù", 0x8C },
{"Û", 0x8D },
{"Ü", 0x8E },
{"ß", 0x8F },
{"à", 0x90 },
{"á", 0x91 },
{"â", 0x92 },
{"ä", 0x93 },
{"ç", 0x94 },
{"è", 0x95 },
{"é", 0x96 },
{"ê", 0x97 },
{"ë", 0x98 },
{"ï", 0x99 },
{"ô", 0x9A },
{"ö", 0x9B },
{"ù", 0x9C },
{"û", 0x9D },
{"ü", 0x9E }
};
// add special characters
for (auto specialCharacterPair : textBoxSpecialCharacters) {
size_t start_pos = 0;
std::string textBoxSpecialCharacterString = "";
textBoxSpecialCharacterString += specialCharacterPair.second;
while((start_pos = formattedHintMessage.find(specialCharacterPair.first, start_pos)) != std::string::npos) {
formattedHintMessage.replace(start_pos, specialCharacterPair.first.length(), textBoxSpecialCharacterString);
start_pos += textBoxSpecialCharacterString.length();
}
}
CustomMessageManager::Instance->FormatCustomMessage(formattedHintMessage);
// add icons to altar text
for (char iconChar : {'0', '1', '2', '3', '4', '5', '6', '7', '8', 'o', 'c', 'i', 'l', 'b', 'L', 'k'}) {
@ -2410,10 +2408,6 @@ std::string Randomizer::GetGanonHintText() const {
return ganonHintText;
}
std::string Randomizer::GetHintFromCheck(RandomizerCheck check) {
return this->hintLocations[check];
}
u8 Randomizer::GetRandoSettingValue(RandomizerSettingKey randoSettingKey) {
return this->randoSettings[randoSettingKey];
}
@ -3447,9 +3441,11 @@ void GenerateRandomizerImgui() {
// if we skip child zelda, we start with zelda's letter, and malon starts
// at the ranch, so we should *not* shuffle the weird egg
cvarSettings[RSK_SHUFFLE_WEIRD_EGG] = ((CVar_GetS32("gRandomizeSkipChildZelda", 0) == 0) && CVar_GetS32("gRandomizeShuffleWeirdEgg", 0));
cvarSettings[RSK_SHUFFLE_WEIRD_EGG] = ((CVar_GetS32("gRandomizeSkipChildZelda", 0) == 0) &&
CVar_GetS32("gRandomizeShuffleWeirdEgg", 0));
cvarSettings[RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD] = CVar_GetS32("gRandomizeShuffleGerudoToken", 0);
cvarSettings[RSK_SHUFFLE_FROG_SONG_RUPEES] = CVar_GetS32("gRandomizeShuffleFrogSongRupees", 0);
cvarSettings[RSK_ITEM_POOL] = CVar_GetS32("gRandomizeItemPool", 1);
cvarSettings[RSK_ICE_TRAPS] = CVar_GetS32("gRandomizeIceTraps", 1);
cvarSettings[RSK_GOSSIP_STONE_HINTS] = CVar_GetS32("gRandomizeGossipStoneHints", 1);
@ -3474,7 +3470,10 @@ void GenerateRandomizerImgui() {
cvarSettings[RSK_CUCCO_COUNT] = CVar_GetS32("gRandomizeCuccosToReturn", 7);
cvarSettings[RSK_BIG_POE_COUNT] = CVar_GetS32("gRandomizeBigPoeTargetCount", 10);
cvarSettings[RSK_SKIP_CHILD_STEALTH] = CVar_GetS32("gRandomizeSkipChildStealth", 0);
// If we skip child zelda, skip child stealth is pointless, so this needs to be reflected in the spoiler log
cvarSettings[RSK_SKIP_CHILD_STEALTH] =
!CVar_GetS32("gRandomizeSkipChildZelda", 0) && CVar_GetS32("gRandomizeSkipChildStealth", 0);
cvarSettings[RSK_SKIP_EPONA_RACE] = CVar_GetS32("gRandomizeSkipEponaRace", 0);
cvarSettings[RSK_SKIP_TOWER_ESCAPE] = CVar_GetS32("gRandomizeSkipTowerEscape", 0);
@ -3539,6 +3538,7 @@ void DrawRandoEditor(bool& open) {
const char* randoShuffleGerudoToken[2] = { "Off", "On" };
const char* randoShuffleMagicBeans[2] = { "Off", "On" };
const char* randoShuffleMerchants[3] = { "Off", "On (no hints)", "On (with hints)" };
const char* randoShuffleFrogSongRupees[2] = { "Off", "On" };
const char* randoShuffleAdultTrade[2] = { "Off", "On" };
// Shuffle Dungeon Items Settings
@ -3961,9 +3961,8 @@ void DrawRandoEditor(bool& open) {
PaddedSeparator();
// Shuffle Cows
ImGui::Text(Settings::ShuffleCows.GetName().c_str());
SohImGui::EnhancementCheckbox(Settings::ShuffleCows.GetName().c_str(), "gRandomizeShuffleCows");
InsertHelpHoverText("Cows give a randomized item from the pool upon performing Epona's Song in front of them.");
SohImGui::EnhancementCombobox("gRandomizeShuffleCows", randoShuffleCows, 2, 0);
PaddedSeparator();
// Shuffle Adult Trade Quest
@ -4041,6 +4040,17 @@ void DrawRandoEditor(bool& open) {
"The Gerudo Card is required to enter the Gerudo Training Grounds, opening "
"the gate to Haunted Wasteland and the Horseback Archery minigame."
);
PaddedSeparator();
// Shuffle Frog Song Rupees
SohImGui::EnhancementCheckbox(Settings::ShuffleFrogSongRupees.GetName().c_str(), "gRandomizeShuffleFrogSongRupees");
InsertHelpHoverText(
"Shuffles 5 Purple Rupees into to the item pool, and allows\n"
"you to earn items by playing songs at the Frog Choir.\n"
"\n"
"This setting does not effect the item earned from playing\n"
"the Song of Storms and the frog song minigame."
);
}
ImGui::PopItemWidth();
@ -4390,8 +4400,74 @@ void DrawRandoEditor(bool& open) {
ImGui::End();
}
void CreateGetItemMessages(std::vector<GetItemMessage> messageEntries) {
CustomMessageManager* customMessageManager = CustomMessageManager::Instance;
customMessageManager->AddCustomMessageTable(Randomizer::getItemMessageTableID);
for (GetItemMessage messageEntry : messageEntries) {
if (messageEntry.giid == GI_ICE_TRAP) {
customMessageManager->CreateMessage(Randomizer::getItemMessageTableID, messageEntry.giid,
{ TEXTBOX_TYPE_BLUE, TEXTBOX_POS_BOTTOM, messageEntry.english,
messageEntry.german, messageEntry.french });
} else {
customMessageManager->CreateGetItemMessage(Randomizer::getItemMessageTableID, messageEntry.giid,
messageEntry.iid,
{ TEXTBOX_TYPE_BLUE, TEXTBOX_POS_BOTTOM, messageEntry.english,
messageEntry.german, messageEntry.french });
}
}
}
void CreateScrubMessages() {
CustomMessageManager* customMessageManager = CustomMessageManager::Instance;
customMessageManager->AddCustomMessageTable(Randomizer::scrubMessageTableID);
const std::vector<u8> prices = { 10, 40 };
for (u8 price : prices) {
customMessageManager->CreateMessage(Randomizer::scrubMessageTableID, price,
{ TEXTBOX_TYPE_BLACK, TEXTBOX_POS_BOTTOM,
"\x12\x38\x82\All right! You win! In return for&sparing me, I will sell you a&%gmysterious item%w!&%r" +
std::to_string(price) + " Rupees%w it is!\x07\x10\xA3",
// RANDTODO: Translate the below string to German.
"\x12\x38\x82\All right! You win! In return for&sparing me, I will sell you a&%gmysterious item%w!&%r" +
std::to_string(price) + " Rupees%w it is!\x07\x10\xA3",
"\x12\x38\x82J'abandonne! Tu veux bien m'acheter&un %gobjet mystérieux%w?&Ça fera %r" +
std::to_string(price) + " Rubis%w!\x07\x10\xA3"
});
}
}
void Randomizer::CreateCustomMessages() {
// RANDTODO: Translate into french and german and replace GIMESSAGE_UNTRANSLATED
// with GIMESSAGE(getItemID, itemID, english, german, french).
const std::vector<GetItemMessage> getItemMessages = {
GIMESSAGE(GI_ICE_TRAP, ITEM_NONE, "\x08\x06\x30You are a %bFOWL%w!\x0E\x20",
"\x08\x06\x15 Du bist ein %bDUMMKOPF%w!\x0E\x20", "\x08\x06\x50%bIDIOT%w\x0E\x20"),
GIMESSAGE_UNTRANSLATED(GI_BOTTLE_WITH_BLUE_FIRE, ITEM_BLUE_FIRE,
"You got a %rBottle with Blue &Fire%w! Use it to melt Red Ice!"),
GIMESSAGE_UNTRANSLATED(GI_BOTTLE_WITH_BIG_POE, ITEM_BIG_POE,
"You got a %rBig Poe in a Bottle%w!&Sell it to the Ghost Shop!"),
GIMESSAGE_UNTRANSLATED(
GI_BOTTLE_WITH_BLUE_POTION, ITEM_POTION_BLUE,
"You got a %rBottle of Blue Potion%w!&Drink it to replenish your&%ghealth%w and %bmagic%w!"),
GIMESSAGE_UNTRANSLATED(
GI_BOTTLE_WITH_FISH, ITEM_FISH,
"You got a %rFish in a Bottle%w!&It looks fresh and delicious!&They say Jabu-Jabu loves them!"),
GIMESSAGE_UNTRANSLATED(GI_BOTTLE_WITH_BUGS, ITEM_BUG,
"You got a %rBug in a Bottle%w!&They love to burrow in&dirt holes!"),
GIMESSAGE_UNTRANSLATED(GI_BOTTLE_WITH_FAIRY, ITEM_FAIRY, "You got a %rFairy in a Bottle%w!&Use it wisely!"),
GIMESSAGE_UNTRANSLATED(GI_BOTTLE_WITH_RED_POTION, ITEM_POTION_RED,
"You got a %rBottle of Red Potion%w!&Drink it to replenish your&%ghealth%w!"),
GIMESSAGE_UNTRANSLATED(GI_BOTTLE_WITH_GREEN_POTION, ITEM_POTION_GREEN,
"You got a %rBottle of Green Potion%w!&Drink it to replenish your&%bmagic%w!"),
GIMESSAGE_UNTRANSLATED(GI_BOTTLE_WITH_POE, ITEM_POE,
"You got a %rPoe in a Bottle%w!&That creepy Ghost Shop might&be interested in this..."),
};
CreateGetItemMessages(getItemMessages);
CreateScrubMessages();
}
void InitRando() {
SohImGui::AddWindow("Randomizer", "Randomizer Settings", DrawRandoEditor);
Randomizer::CreateCustomMessages();
}
extern "C" {

View File

@ -4,7 +4,8 @@
#include <string>
#include "../../../include/ultra64.h"
#include "../../../include/z64item.h"
#include "soh/Enhancements/randomizer/randomizerTypes.h"
#include <memory>
#include <soh/Enhancements/randomizer/randomizerTypes.h>
class Randomizer {
private:
@ -25,6 +26,10 @@ class Randomizer {
Randomizer();
~Randomizer();
static const std::string getItemMessageTableID;
static const std::string hintMessageTableID;
static const std::string scrubMessageTableID;
static Sprite* GetSeedTexture(uint8_t index);
s16 GetItemModelFromId(s16 itemId);
s32 GetItemIDFromGetItemID(s32 getItemId);
@ -38,9 +43,9 @@ class Randomizer {
std::string GetAdultAltarText() const;
std::string GetGanonText() const;
std::string GetGanonHintText() const;
std::string GetHintFromCheck(RandomizerCheck check);
GetItemID GetRandomizedItemIdFromKnownCheck(RandomizerCheck randomizerCheck, GetItemID ogId);
GetItemID GetRandomizedItemId(GetItemID ogId, s16 actorId, s16 actorParams, s16 sceneNum);
static void CreateCustomMessages();
};
#ifdef __cplusplus

View File

@ -978,6 +978,7 @@ typedef enum {
RSK_SHUFFLE_COWS,
RSK_SHUFFLE_WEIRD_EGG,
RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD,
RSK_SHUFFLE_FROG_SONG_RUPEES,
RSK_ITEM_POOL,
RSK_ICE_TRAPS,
RSK_GOSSIP_STONE_HINTS,

View File

@ -1,8 +1,9 @@
#pragma once
static struct {
std::thread thread;
std::condition_variable cv_to_thread, cv_from_thread;
std::mutex mutex;
bool initialized;
bool running;
bool processing;
} audio;

View File

@ -32,7 +32,6 @@
#include "Enhancements/cosmetics/CosmeticsEditor.h"
#include "Enhancements/debugconsole.h"
#include "Enhancements/debugger/debugger.h"
#include "Enhancements/randomizer/randomizer.h"
#include <soh/Enhancements/randomizer/randomizer_item_tracker.h>
#include "Enhancements/n64_weird_frame_data.inc"
#include "soh/frame_interpolation.h"
@ -40,6 +39,7 @@
#include "macros.h"
#include <Utils/StringHelper.h>
#include "Hooks.h"
#include <soh/Enhancements/custom-message/CustomMessageManager.h>
#ifdef __APPLE__
#include <SDL_scancode.h>
@ -52,9 +52,12 @@
#endif
#include <Audio.h>
#include <soh/Enhancements/custom-message/CustomMessageTypes.h>
#include <functions.h>
OTRGlobals* OTRGlobals::Instance;
SaveManager* SaveManager::Instance;
CustomMessageManager* CustomMessageManager::Instance;
OTRGlobals::OTRGlobals() {
context = Ship::GlobalCtx2::CreateInstance("Ship of Harkinian");
@ -83,11 +86,73 @@ extern "C" void ResourceMgr_CacheDirectory(const char* resName);
extern "C" SequenceData ResourceMgr_LoadSeqByName(const char* path);
std::unordered_map<std::string, ExtensionEntry> ExtensionCache;
void OTRAudio_Thread() {
while (audio.running) {
{
std::unique_lock<std::mutex> Lock(audio.mutex);
while (!audio.processing && audio.running) {
audio.cv_to_thread.wait(Lock);
}
if (!audio.running) {
break;
}
}
std::unique_lock<std::mutex> Lock(audio.mutex);
//AudioMgr_ThreadEntry(&gAudioMgr);
// 528 and 544 relate to 60 fps at 32 kHz 32000/60 = 533.333..
// in an ideal world, one third of the calls should use num_samples=544 and two thirds num_samples=528
//#define SAMPLES_HIGH 560
//#define SAMPLES_LOW 528
// PAL values
//#define SAMPLES_HIGH 656
//#define SAMPLES_LOW 624
// 44KHZ values
#define SAMPLES_HIGH 752
#define SAMPLES_LOW 720
#define AUDIO_FRAMES_PER_UPDATE (R_UPDATE_RATE > 0 ? R_UPDATE_RATE : 1 )
#define NUM_AUDIO_CHANNELS 2
int samples_left = AudioPlayer_Buffered();
u32 num_audio_samples = samples_left < AudioPlayer_GetDesiredBuffered() ? SAMPLES_HIGH : SAMPLES_LOW;
// 3 is the maximum authentic frame divisor.
s16 audio_buffer[SAMPLES_HIGH * NUM_AUDIO_CHANNELS * 3];
for (int i = 0; i < AUDIO_FRAMES_PER_UPDATE; i++) {
AudioMgr_CreateNextAudioBuffer(audio_buffer + i * (num_audio_samples * NUM_AUDIO_CHANNELS), num_audio_samples);
}
AudioPlayer_Play((u8*)audio_buffer, num_audio_samples * (sizeof(int16_t) * NUM_AUDIO_CHANNELS * AUDIO_FRAMES_PER_UPDATE));
audio.processing = false;
audio.cv_from_thread.notify_one();
}
}
// C->C++ Bridge
extern "C" void OTRAudio_Init()
{
// Precache all our samples, sequences, etc...
ResourceMgr_CacheDirectory("audio");
if (!audio.running) {
audio.running = true;
audio.thread = std::thread(OTRAudio_Thread);
}
}
extern "C" void OTRAudio_Exit() {
// Tell the audio thread to stop
{
std::unique_lock<std::mutex> Lock(audio.mutex);
audio.running = false;
}
audio.cv_to_thread.notify_all();
// Wait until the audio thread quit
audio.thread.join();
}
extern "C" void OTRExtScanner() {
@ -109,6 +174,7 @@ extern "C" void InitOTR() {
#endif
OTRGlobals::Instance = new OTRGlobals();
SaveManager::Instance = new SaveManager();
CustomMessageManager::Instance = new CustomMessageManager();
auto t = OTRGlobals::Instance->context->GetResourceManager()->LoadFile("version");
if (!t->bHasLoadError)
@ -128,6 +194,10 @@ extern "C" void InitOTR() {
OTRExtScanner();
}
extern "C" void DeinitOTR() {
OTRAudio_Exit();
}
#ifdef _WIN32
extern "C" uint64_t GetFrequency() {
LARGE_INTEGER nFreq;
@ -227,56 +297,10 @@ extern "C" void Graph_StartFrame() {
// C->C++ Bridge
extern "C" void Graph_ProcessGfxCommands(Gfx* commands) {
#ifndef __SWITCH__
if (!audio.initialized) {
audio.initialized = true;
std::thread([]() {
for (;;) {
{
std::unique_lock<std::mutex> Lock(audio.mutex);
while (!audio.processing) {
audio.cv_to_thread.wait(Lock);
}
}
std::unique_lock<std::mutex> Lock(audio.mutex);
//AudioMgr_ThreadEntry(&gAudioMgr);
// 528 and 544 relate to 60 fps at 32 kHz 32000/60 = 533.333..
// in an ideal world, one third of the calls should use num_samples=544 and two thirds num_samples=528
//#define SAMPLES_HIGH 560
//#define SAMPLES_LOW 528
// PAL values
//#define SAMPLES_HIGH 656
//#define SAMPLES_LOW 624
// 44KHZ values
#define SAMPLES_HIGH 752
#define SAMPLES_LOW 720
#define AUDIO_FRAMES_PER_UPDATE (R_UPDATE_RATE > 0 ? R_UPDATE_RATE : 1 )
#define NUM_AUDIO_CHANNELS 2
int samples_left = AudioPlayer_Buffered();
u32 num_audio_samples = samples_left < AudioPlayer_GetDesiredBuffered() ? SAMPLES_HIGH : SAMPLES_LOW;
// 3 is the maximum authentic frame divisor.
s16 audio_buffer[SAMPLES_HIGH * NUM_AUDIO_CHANNELS * 3];
for (int i = 0; i < AUDIO_FRAMES_PER_UPDATE; i++) {
AudioMgr_CreateNextAudioBuffer(audio_buffer + i * (num_audio_samples * NUM_AUDIO_CHANNELS), num_audio_samples);
}
AudioPlayer_Play((u8*)audio_buffer, num_audio_samples * (sizeof(int16_t) * NUM_AUDIO_CHANNELS * AUDIO_FRAMES_PER_UPDATE));
audio.processing = false;
audio.cv_from_thread.notify_one();
}
}).detach();
}
{
std::unique_lock<std::mutex> Lock(audio.mutex);
audio.processing = true;
}
#endif
audio.cv_to_thread.notify_one();
std::vector<std::unordered_map<Mtx*, MtxF>> mtx_replacements;
@ -319,14 +343,12 @@ extern "C" void Graph_ProcessGfxCommands(Gfx* commands) {
last_fps = fps;
last_update_rate = R_UPDATE_RATE;
#ifndef __SWITCH__
{
std::unique_lock<std::mutex> Lock(audio.mutex);
while (audio.processing) {
audio.cv_from_thread.wait(Lock);
}
}
#endif
// OTRTODO: FIGURE OUT END FRAME POINT
/* if (OTRGlobals::Instance->context->GetWindow()->lastScancode != -1)
@ -1413,99 +1435,39 @@ extern "C" RandomizerCheck Randomizer_GetCheckFromActor(s16 sceneNum, s16 actorI
return OTRGlobals::Instance->gRandomizer->GetCheckFromActor(sceneNum, actorId, actorParams);
}
extern "C" int CopyScrubMessage(u16 scrubTextId, char* buffer, const int maxBufferSize) {
std::string scrubText("");
int language = CVar_GetS32("gLanguages", 0);
extern "C" CustomMessageEntry Randomizer_GetScrubMessage(u16 scrubTextId) {
int price = 0;
switch (scrubTextId) {
case 0x10A2:
case TEXT_SCRUB_POH:
price = 10;
break;
case 0x10DC:
case 0x10DD:
case TEXT_SCRUB_STICK_UPGRADE:
case TEXT_SCRUB_NUT_UPGRADE:
price = 40;
break;
}
switch (language) {
case 0: default:
scrubText += 0x12; // add the sound
scrubText += 0x38; // sound id
scrubText += 0x82; // sound id
scrubText += "All right! You win! In return for";
scrubText += 0x01; // newline
scrubText += "sparing me, I will sell you a";
scrubText += 0x01; // newline
scrubText += 0x05; // change the color
scrubText += 0x42; // green
scrubText += "mysterious item";
scrubText += 0x05; // change the color
scrubText += 0x40; // white
scrubText += "!";
scrubText += 0x01; // newline
scrubText += 0x05; // change the color
scrubText += 0x41; // red
scrubText += std::to_string(price);
scrubText += price > 1 ? " Rupees" : " Rupee";
scrubText += 0x05; // change the color
scrubText += 0x40; // white
scrubText += " it is!";
scrubText += 0x07; // go to a new message
scrubText += 0x10; // message id
scrubText += 0xA3; // message id
break;
case 2:
scrubText += 0x12; // add the sound
scrubText += 0x38; // sound id
scrubText += 0x82; // sound id
scrubText += "J'abandonne! Tu veux bien m'acheter";
scrubText += 0x01; // newline
scrubText += "un ";
scrubText += 0x05; // change the color
scrubText += 0x42; // green
scrubText += "objet myst\x96rieux";
//scrubText += ";
scrubText += 0x05; // change the color
scrubText += 0x40; // white
scrubText += "?";
scrubText += 0x01; // newline
scrubText += "\x84";
scrubText += "a fera ";
scrubText += 0x05; // change the color
scrubText += 0x41; // red
scrubText += std::to_string(price) + " Rubis";
scrubText += 0x05; // change the color
scrubText += 0x40; // white
scrubText += "!";
scrubText += 0x07; // go to a new message
scrubText += 0x10; // message id
scrubText += 0xA3; // message id
break;
}
return CopyStringToCharBuffer(scrubText, buffer, maxBufferSize);
return CustomMessageManager::Instance->RetrieveMessage(Randomizer::scrubMessageTableID, price);
}
extern "C" int Randomizer_CopyAltarMessage(char* buffer, const int maxBufferSize) {
const std::string& altarText = (LINK_IS_ADULT) ? OTRGlobals::Instance->gRandomizer->GetAdultAltarText()
: OTRGlobals::Instance->gRandomizer->GetChildAltarText();
return CopyStringToCharBuffer(altarText, buffer, maxBufferSize);
extern "C" CustomMessageEntry Randomizer_GetAltarMessage() {
return (LINK_IS_ADULT)
? CustomMessageManager::Instance->RetrieveMessage(Randomizer::hintMessageTableID, TEXT_ALTAR_ADULT)
: CustomMessageManager::Instance->RetrieveMessage(Randomizer::hintMessageTableID, TEXT_ALTAR_CHILD);
}
extern "C" int Randomizer_CopyGanonText(char* buffer, const int maxBufferSize) {
const std::string& ganonText = OTRGlobals::Instance->gRandomizer->GetGanonText();
return CopyStringToCharBuffer(ganonText, buffer, maxBufferSize);
extern "C" CustomMessageEntry Randomizer_GetGanonText() {
return CustomMessageManager::Instance->RetrieveMessage(Randomizer::hintMessageTableID, TEXT_GANONDORF_NOHINT);
}
extern "C" int Randomizer_CopyGanonHintText(char* buffer, const int maxBufferSize) {
const std::string& ganonText = OTRGlobals::Instance->gRandomizer->GetGanonHintText();
return CopyStringToCharBuffer(ganonText, buffer, maxBufferSize);
extern "C" CustomMessageEntry Randomizer_GetGanonHintText() {
return CustomMessageManager::Instance->RetrieveMessage(Randomizer::hintMessageTableID, TEXT_GANONDORF);
}
extern "C" int Randomizer_CopyHintFromCheck(RandomizerCheck check, char* buffer, const int maxBufferSize) {
// we don't want to make a copy of the std::string returned from GetHintFromCheck
extern "C" CustomMessageEntry Randomizer_GetHintFromCheck(RandomizerCheck check) {
// we don't want to make a copy of the std::string returned from GetHintFromCheck
// so we're just going to let RVO take care of it
const std::string& hintText = OTRGlobals::Instance->gRandomizer->GetHintFromCheck(check);
return CopyStringToCharBuffer(hintText, buffer, maxBufferSize);
const CustomMessageEntry hintText = CustomMessageManager::Instance->RetrieveMessage(Randomizer::hintMessageTableID, check);
return hintText;
}
extern "C" s32 Randomizer_GetRandomizedItemId(GetItemID ogId, s16 actorId, s16 actorParams, s16 sceneNum) {
@ -1524,3 +1486,97 @@ extern "C" bool Randomizer_ObtainedFreestandingIceTrap(RandomizerCheck randomize
extern "C" bool Randomizer_ItemIsIceTrap(RandomizerCheck randomizerCheck, GetItemID ogId) {
return gSaveContext.n64ddFlag && Randomizer_GetItemIdFromKnownCheck(randomizerCheck, ogId) == GI_ICE_TRAP;
}
extern "C" CustomMessageEntry Randomizer_GetCustomGetItemMessage(GetItemID giid, char* buffer, const int maxBufferSize) {
const CustomMessageEntry getItemText = CustomMessageManager::Instance->RetrieveMessage(Randomizer::getItemMessageTableID, giid);
return getItemText;
}
extern "C" int CustomMessage_RetrieveIfExists(GlobalContext* globalCtx) {
MessageContext* msgCtx = &globalCtx->msgCtx;
uint16_t textId = msgCtx->textId;
Font* font = &msgCtx->font;
char* buffer = font->msgBuf;
const int maxBufferSize = sizeof(font->msgBuf);
CustomMessageEntry messageEntry;
if (gSaveContext.n64ddFlag) {
if (textId == TEXT_RANDOMIZER_CUSTOM_ITEM) {
messageEntry =
Randomizer_GetCustomGetItemMessage((GetItemID)GET_PLAYER(globalCtx)->getItemId, buffer, maxBufferSize);
} else if (textId == TEXT_RANDOMIZER_GOSSIP_STONE_HINTS && Randomizer_GetSettingValue(RSK_GOSSIP_STONE_HINTS) != 0 &&
(Randomizer_GetSettingValue(RSK_GOSSIP_STONE_HINTS) == 1 ||
(Randomizer_GetSettingValue(RSK_GOSSIP_STONE_HINTS) == 2 &&
Player_GetMask(globalCtx) == PLAYER_MASK_TRUTH) ||
(Randomizer_GetSettingValue(RSK_GOSSIP_STONE_HINTS) == 3 && CHECK_QUEST_ITEM(QUEST_STONE_OF_AGONY)))) {
s16 actorParams = msgCtx->talkActor->params;
// if we're in a generic grotto
if (globalCtx->sceneNum == 62 && actorParams == 14360) {
// look for the chest in the actorlist to determine
// which grotto we're in
int numOfActorLists =
sizeof(globalCtx->actorCtx.actorLists) / sizeof(globalCtx->actorCtx.actorLists[0]);
for (int i = 0; i < numOfActorLists; i++) {
if (globalCtx->actorCtx.actorLists[i].length) {
if (globalCtx->actorCtx.actorLists[i].head->id == 10) {
// set the params for the hint check to be negative chest params
actorParams = 0 - globalCtx->actorCtx.actorLists[i].head->params;
}
}
}
}
RandomizerCheck hintCheck =
Randomizer_GetCheckFromActor(globalCtx->sceneNum, msgCtx->talkActor->id, actorParams);
messageEntry = Randomizer_GetHintFromCheck(hintCheck);
} else if (textId == TEXT_ALTAR_CHILD || textId == TEXT_ALTAR_ADULT) {
// rando hints at altar
messageEntry = Randomizer_GetAltarMessage();
} else if (textId == TEXT_GANONDORF) {
if (INV_CONTENT(ITEM_ARROW_LIGHT) == ITEM_ARROW_LIGHT) {
messageEntry = Randomizer_GetGanonText();
} else {
messageEntry = Randomizer_GetGanonHintText();
}
} else if (textId == TEXT_SCRUB_POH || textId == TEXT_SCRUB_STICK_UPGRADE || textId == TEXT_SCRUB_NUT_UPGRADE) {
messageEntry = Randomizer_GetScrubMessage(textId);
}
}
if (textId == TEXT_GS_NO_FREEZE || textId == TEXT_GS_FREEZE) {
if (CVar_GetS32("gInjectSkulltulaCount", 0) != 0) {
// The freeze text cannot be manually dismissed and must be auto-dismissed.
// This is fine and even wanted when skull tokens are not shuffled, but when
// when they are shuffled we don't want to be able to manually dismiss the box.
// Otherwise if we get a token from a chest or an NPC we get stuck in the ItemGet
// animation until the text box auto-dismisses.
// RANDOTODO: Implement a way to determine if an item came from a skulltula and
// inject the auto-dismiss control code if it did.
if (CVar_GetS32("gSkulltulaFreeze", 0) != 0 &&
!(gSaveContext.n64ddFlag && Randomizer_GetSettingValue(RSK_SHUFFLE_TOKENS) > 0)) {
textId = TEXT_GS_NO_FREEZE;
} else {
textId = TEXT_GS_FREEZE;
}
messageEntry = CustomMessageManager::Instance->RetrieveMessage(customMessageTableID, textId);
}
}
if (messageEntry.textBoxType != -1) {
font->charTexBuf[0] = (messageEntry.textBoxType << 4) | messageEntry.textBoxPos;
switch (gSaveContext.language) {
case LANGUAGE_FRA:
return msgCtx->msgLength = font->msgLength =
CopyStringToCharBuffer(messageEntry.french, buffer, maxBufferSize);
case LANGUAGE_GER:
return msgCtx->msgLength = font->msgLength =
CopyStringToCharBuffer(messageEntry.german, buffer, maxBufferSize);
case LANGUAGE_ENG:
default:
return msgCtx->msgLength = font->msgLength =
CopyStringToCharBuffer(messageEntry.english, buffer, maxBufferSize);
}
}
return false;
}

View File

@ -10,6 +10,8 @@
#include "Enhancements/savestates.h"
#include "Enhancements/randomizer/randomizer.h"
const std::string customMessageTableID = "BaseGameOverrides";
class OTRGlobals
{
public:
@ -91,10 +93,6 @@ Sprite* GetSeedTexture(uint8_t index);
void Randomizer_LoadSettings(const char* spoilerFileName);
u8 Randomizer_GetSettingValue(RandomizerSettingKey randoSettingKey);
RandomizerCheck Randomizer_GetCheckFromActor(s16 actorId, s16 actorParams, s16 sceneNum);
int Randomizer_CopyAltarMessage(char* buffer, const int maxBufferSize);
int Randomizer_CopyHintFromCheck(RandomizerCheck check, char* buffer, const int maxBufferSize);
int Randomizer_CopyGanonText(char* buffer, const int maxBufferSize);
int Randomizer_CopyGanonHintText(char* buffer, const int maxBufferSize);
void Randomizer_LoadHintLocations(const char* spoilerFileName);
void Randomizer_LoadItemLocations(const char* spoilerFileName, bool silent);
s16 Randomizer_GetItemModelFromId(s16 itemId);
@ -103,6 +101,7 @@ s32 Randomizer_GetRandomizedItemId(GetItemID ogId, s16 actorId, s16 actorParams,
s32 Randomizer_GetItemIdFromKnownCheck(RandomizerCheck randomizerCheck, GetItemID ogId);
bool Randomizer_ObtainedFreestandingIceTrap(RandomizerCheck randomizerCheck, GetItemID ogId, Actor* actor);
bool Randomizer_ItemIsIceTrap(RandomizerCheck randomizerCheck, GetItemID ogId);
int CustomMessage_RetrieveIfExists(GlobalContext* globalCtx);
#endif
#endif

View File

@ -1,11 +1,13 @@
#include "OTRGlobals.h"
#include "ResourceMgr.h"
#include "Scene.h"
#include "message_data_static.h"
#include "Utils/StringHelper.h"
#include "global.h"
#include "vt.h"
#include <Text.h>
#include <message_data_static.h>
#include <soh/Enhancements/custom-message/CustomMessageManager.h>
#include <soh/Enhancements/custom-message/CustomMessageTypes.h>
extern "C" MessageTableEntry* sNesMessageEntryTablePtr;
extern "C" MessageTableEntry* sGerMessageEntryTablePtr;
@ -92,4 +94,22 @@ extern "C" void OTRMessage_Init()
sStaffMessageEntryTablePtr[i].segment = file2->messages[i].msg.c_str();
sStaffMessageEntryTablePtr[i].msgSize = file2->messages[i].msg.size();
}
}
CustomMessageManager::Instance->AddCustomMessageTable(customMessageTableID);
CustomMessageManager::Instance->CreateGetItemMessage(
customMessageTableID, (GetItemID)TEXT_GS_NO_FREEZE, ITEM_SKULL_TOKEN,
{
TEXTBOX_TYPE_BLUE, TEXTBOX_POS_BOTTOM,
"You got a %rGold Skulltula Token%w!&You've collected %r\x19%w tokens&in total!\x0E\x3C",
"Du erhälst ein %rGoldene&Skulltula-Symbol%w! Du hast&insgesamt %r\x19%w symbol gesammelt!\x0E\x3C",
"Vous obtenez un %rSymbole de&Skulltula d'or%w! Vous avez&collecté %r\x19\%w symboles en tout!\x0E\x3C"
}
);
CustomMessageManager::Instance->CreateGetItemMessage(
customMessageTableID, (GetItemID)TEXT_GS_FREEZE, ITEM_SKULL_TOKEN,
{
TEXTBOX_TYPE_BLUE, TEXTBOX_POS_BOTTOM,
"You got a %rGold Skulltula Token%w!&You've collected %r\x19%w tokens&in total!",
"Du erhälst ein %rGoldene&Skulltula-Symbol%w! Du hast&insgesamt %r\x19%w symbol gesammelt!",
"Vous obtenez un %rSymbole de&Skulltula d'or%w! Vous avez&collecté %r\x19\%w symboles en tout!" });
}

View File

@ -481,22 +481,6 @@ static void RunFrame()
uint64_t ticksA, ticksB;
ticksA = GetPerfCounter();
#ifdef __SWITCH__
#define SAMPLES_HIGH 752
#define SAMPLES_LOW 720
#define AUDIO_FRAMES_PER_UPDATE (R_UPDATE_RATE > 0 ? R_UPDATE_RATE : 1 )
#define NUM_AUDIO_CHANNELS 2
int samples_left = AudioPlayer_Buffered();
u32 num_audio_samples = samples_left < AudioPlayer_GetDesiredBuffered() ? SAMPLES_HIGH : SAMPLES_LOW;
s16 audio_buffer[SAMPLES_HIGH * NUM_AUDIO_CHANNELS * 3];
for (int i = 0; i < AUDIO_FRAMES_PER_UPDATE; i++) {
AudioMgr_CreateNextAudioBuffer(audio_buffer + i * (num_audio_samples * NUM_AUDIO_CHANNELS), num_audio_samples);
}
AudioPlayer_Play((u8*)audio_buffer, num_audio_samples * (sizeof(int16_t) * NUM_AUDIO_CHANNELS * AUDIO_FRAMES_PER_UPDATE));
#endif
Graph_StartFrame();
// TODO: Workaround for rumble being too long. Implement os thread functions.

View File

@ -36,12 +36,14 @@ void Main_LogSystemHeap(void) {
osSyncPrintf(VT_RST);
}
void main(int argc, char** argv)
int main(int argc, char** argv)
{
GameConsole_Init();
InitOTR();
BootCommands_Init();
Main(0);
DeinitOTR();
return 0;
}
void Main(void* arg) {

View File

@ -3961,7 +3961,7 @@ void Actor_DrawDoorLock(GlobalContext* globalCtx, s32 frame, s32 type) {
f32 chainsTranslateX;
f32 chainsTranslateY;
f32 rotZStep;
static s32 epoch = 0;
static u32 epoch = 0;
epoch++;
entry = &sDoorLocksInfo[type];

View File

@ -492,9 +492,10 @@ void Cutscene_Command_Terminator(GlobalContext* globalCtx, CutsceneContext* csCt
s32 temp = 0;
// Automatically skip certain cutscenes when in rando
// cmd->base == 33: Zelda escaping with impa cutscene
// cmd->base == 8: Traveling back/forward in time cutscene
bool randoCsSkip = (gSaveContext.n64ddFlag && (cmd->base == 33 || cmd->base == 8));
// cmd->base == 24: Dropping a fish for Jabu Jabu
// cmd->base == 33: Zelda escaping with impa cutscene
bool randoCsSkip = (gSaveContext.n64ddFlag && (cmd->base == 8 || cmd->base == 24 || cmd->base == 33));
bool debugCsSkip = (CHECK_BTN_ALL(globalCtx->state.input[0].press.button, BTN_START) &&
(gSaveContext.fileNum != 0xFEDC) && CVar_GetS32("gDebugEnabled", 0));
@ -2090,6 +2091,8 @@ void Cutscene_HandleConditionalTriggers(GlobalContext* globalCtx) {
osSyncPrintf("\ngame_info.mode=[%d] restart_flag", ((void)0, gSaveContext.respawnFlag));
if ((gSaveContext.gameMode == 0) && (gSaveContext.respawnFlag <= 0) && (gSaveContext.cutsceneIndex < 0xFFF0)) {
const bool bShouldTowerRandoSkip =
(gSaveContext.n64ddFlag && Randomizer_GetSettingValue(RSK_SKIP_TOWER_ESCAPE));
if ((gSaveContext.entranceIndex == 0x01E1) && !Flags_GetEventChkInf(0xAC)) {
if (!gSaveContext.n64ddFlag) {
Flags_SetEventChkInf(0xAC);
@ -2118,14 +2121,15 @@ void Cutscene_HandleConditionalTriggers(GlobalContext* globalCtx) {
gSaveContext.entranceIndex = 0x0053;
gSaveContext.cutsceneIndex = 0xFFF8;
}
} else if (!Flags_GetEventChkInf(0xC7) &&
(gEntranceTable[((void)0, gSaveContext.entranceIndex)].scene == SCENE_GANON_DEMO)) {
} else if ((!Flags_GetEventChkInf(0xC7) &&
gEntranceTable[((void)0, gSaveContext.entranceIndex)].scene == SCENE_GANON_DEMO) ||
(bShouldTowerRandoSkip &&
gEntranceTable[((void)0, gSaveContext.entranceIndex)].scene == SCENE_GANON_FINAL)) {
Flags_SetEventChkInf(0xC7);
gSaveContext.entranceIndex = 0x0517;
// If we are rando and tower escape skip is on, then set the flag to say we saw the towers fall
// and exit.
if (gSaveContext.n64ddFlag && Randomizer_GetSettingValue(RSK_SKIP_TOWER_ESCAPE)) {
if (bShouldTowerRandoSkip) {
return;
}
gSaveContext.cutsceneIndex = 0xFFF0;

View File

@ -1460,7 +1460,7 @@ void Environment_DrawLensFlare(GlobalContext* globalCtx, EnvironmentContext* env
LENS_FLARE_RING, LENS_FLARE_CIRCLE1, LENS_FLARE_CIRCLE1, LENS_FLARE_CIRCLE1, LENS_FLARE_CIRCLE1,
LENS_FLARE_CIRCLE1, LENS_FLARE_CIRCLE1, LENS_FLARE_CIRCLE1, LENS_FLARE_CIRCLE1, LENS_FLARE_CIRCLE1,
};
static s32 epoch = 0;
static u32 epoch = 0;
epoch++;
OPEN_DISPS(gfxCtx);
@ -1643,7 +1643,7 @@ void Environment_DrawRain(GlobalContext* globalCtx, View* view, GraphicsContext*
Vec3f unused = { 0.0f, 0.0f, 0.0f };
Vec3f windDirection = { 0.0f, 0.0f, 0.0f };
Player* player = GET_PLAYER(globalCtx);
static s32 epoch = 0;
static u32 epoch = 0;
epoch++;
if (!(globalCtx->cameraPtrs[0]->unk_14C & 0x100) && (globalCtx->envCtx.unk_EE[2] == 0)) {
@ -1926,7 +1926,7 @@ void Environment_DrawLightning(GlobalContext* globalCtx, s32 unused) {
s32 pad[2];
Vec3f unused1 = { 0.0f, 0.0f, 0.0f };
Vec3f unused2 = { 0.0f, 0.0f, 0.0f };
static s32 epoch = 0;
static u32 epoch = 0;
epoch++;
OPEN_DISPS(globalCtx->state.gfxCtx);

View File

@ -413,7 +413,7 @@ void HealthMeter_Draw(GlobalContext* globalCtx) {
s32 curCombineModeSet = 0;
u8* curBgImgLoaded = NULL;
s32 ddHeartCountMinusOne = gSaveContext.inventory.defenseHearts - 1;
static s32 epoch = 0;
static u32 epoch = 0;
epoch++;
OPEN_DISPS(gfxCtx);

View File

@ -1663,7 +1663,10 @@ void Message_OpenText(GlobalContext* globalCtx, u16 textId) {
gSaveContext.eventInf[0] = gSaveContext.eventInf[1] = gSaveContext.eventInf[2] = gSaveContext.eventInf[3] = 0;
}
if (sTextIsCredits) {
// RANDOTODO: Use this for ice trap messages
if (CustomMessage_RetrieveIfExists(globalCtx)) {
osSyncPrintf("Found custom message");
} else if (sTextIsCredits) {
Message_FindCreditsMessage(globalCtx, textId);
msgCtx->msgLength = font->msgLength;
char* src = (uintptr_t)font->msgOffset;
@ -1674,87 +1677,9 @@ void Message_OpenText(GlobalContext* globalCtx, u16 textId) {
//font->msgLength, __FILE__, __LINE__);
} else {
Message_FindMessage(globalCtx, textId);
// if we're rando'd and talking to a gossip stone
if (gSaveContext.n64ddFlag &&
textId == 0x2053 &&
Randomizer_GetSettingValue(RSK_GOSSIP_STONE_HINTS) != 0 &&
(Randomizer_GetSettingValue(RSK_GOSSIP_STONE_HINTS) == 1 ||
(Randomizer_GetSettingValue(RSK_GOSSIP_STONE_HINTS) == 2 &&
Player_GetMask(globalCtx) == PLAYER_MASK_TRUTH) ||
(Randomizer_GetSettingValue(RSK_GOSSIP_STONE_HINTS) == 3 &&
CHECK_QUEST_ITEM(QUEST_STONE_OF_AGONY)))) {
s16 actorParams = msgCtx->talkActor->params;
// if we're in a generic grotto
if (globalCtx->sceneNum == 62 && actorParams == 14360) {
// look for the chest in the actorlist to determine
// which grotto we're in
int numOfActorLists = sizeof(globalCtx->actorCtx.actorLists)/sizeof(globalCtx->actorCtx.actorLists[0]);
for(int i = 0; i < numOfActorLists; i++) {
if(globalCtx->actorCtx.actorLists[i].length) {
if(globalCtx->actorCtx.actorLists[i].head->id == 10) {
// set the params for the hint check to be negative chest params
actorParams = 0 - globalCtx->actorCtx.actorLists[i].head->params;
}
}
}
}
RandomizerCheck hintCheck = Randomizer_GetCheckFromActor(globalCtx->sceneNum, msgCtx->talkActor->id, actorParams);
// Pass the sizeof the message buffer so we don't hardcode any sizes and can rely on globals.
// If no hint can be found, this just returns 0 size and doesn't modify the buffer, so no worries.
msgCtx->msgLength = font->msgLength = Randomizer_CopyHintFromCheck(hintCheck, font->msgBuf, sizeof(font->msgBuf));
} else if (gSaveContext.n64ddFlag && (textId == 0x7040 || textId == 0x7088)) {
// rando hints at altar
msgCtx->msgLength = font->msgLength = Randomizer_CopyAltarMessage(font->msgBuf, sizeof(font->msgBuf));
} else if (textId == 0x00b4 && CVar_GetS32("gInjectSkulltulaCount", 0) != 0) {
switch (gSaveContext.language) {
case LANGUAGE_FRA:
strcpy(font->msgBuf, "\x08\x13\x71Vous obtenez un \x05\x41Symbole de\x01Skulltula d'or\x05\x40! "
"Vous avez\x01\collect\x96 "
"\x05\x41\x19\x05\x40 symboles en tout!\x02");
break;
case LANGUAGE_GER:
strcpy(font->msgBuf, "\x08\x13\x71\Du erh\x93lst ein \x05\x41Goldene\x01Skulltula-Symbol\x05\x40\! "
"Du hast\x01insgesamt "
"\x05\x41\x19\x05\x40 symbol gesammelt!\x02");
break;
case LANGUAGE_ENG: default:
strcpy(font->msgBuf,
"\x08\x13\x71You got a \x05\x41Gold Skulltula Token\x05\x40!\x01You've collected "
"\x05\x41\x19\x05\x40 tokens\x01in total!\x02");
break;
}
msgCtx->msgLength = font->msgLength = strlen(font->msgBuf);
} else if (gSaveContext.n64ddFlag && (textId == 0x10A2 || textId == 0x10DC || textId == 0x10DD)) {
msgCtx->msgLength = font->msgLength = CopyScrubMessage(textId, font->msgBuf, sizeof(font->msgBuf));
} else if (gSaveContext.n64ddFlag && textId == 0x70CC) {
if (INV_CONTENT(ITEM_ARROW_LIGHT) == ITEM_ARROW_LIGHT) {
msgCtx->msgLength = font->msgLength = Randomizer_CopyGanonText(font->msgBuf, sizeof(font->msgBuf));
} else {
msgCtx->msgLength = font->msgLength = Randomizer_CopyGanonHintText(font->msgBuf, sizeof(font->msgBuf));
}
} else if (textId == 0xF8 && GET_PLAYER(globalCtx)->getItemId == GI_ICE_TRAP) {
switch (gSaveContext.language) {
case LANGUAGE_FRA:
strcpy(font->msgBuf, "\x08\x06\x50\x05\x43IDIOT\x0E\x20\x02");
break;
case LANGUAGE_GER:
strcpy(font->msgBuf, "\x08\x06\x15 Du bist ein\x05\x43 DUMMKOPF\x05\x40!\x0E\x20\x02");
break;
case LANGUAGE_ENG:
default:
strcpy(font->msgBuf, "\x08\x06\x30You are a\x05\x43 FOWL\x05\x40!\x0E\x20\x02");
break;
}
msgCtx->msgLength = font->msgLength = strlen(font->msgBuf);
} else {
msgCtx->msgLength = font->msgLength;
char* src = (uintptr_t)font->msgOffset;
memcpy(font->msgBuf, src, font->msgLength);
}
msgCtx->msgLength = font->msgLength;
char* src = (uintptr_t)font->msgOffset;
memcpy(font->msgBuf, src, font->msgLength);
}
msgCtx->textBoxProperties = font->charTexBuf[0];

View File

@ -277,11 +277,15 @@ void Gameplay_Init(GameState* thisx) {
u8 tempSetupIndex;
s32 pad[2];
if (gSaveContext.n64ddFlag && Randomizer_GetSettingValue(RSK_SKIP_CHILD_STEALTH)) {
// Skip Child Stealth when option is enabled, Zelda's Letter isn't obtained and Impa's reward hasn't been received
// eventChkInf[4] & 1 = Got Zelda's Letter
// eventChkInf[5] & 0x200 = Got Impa's reward
// entranceIndex 0x7A, Castle Courtyard - Day from crawlspace
// entranceIndex 0x400, Zelda's Courtyard
if (gSaveContext.n64ddFlag && Randomizer_GetSettingValue(RSK_SKIP_CHILD_STEALTH) &&
!(gSaveContext.eventChkInf[4] & 1) && !(gSaveContext.eventChkInf[5] & 0x200)) {
if (gSaveContext.entranceIndex == 0x7A) {
gSaveContext.entranceIndex = 0x400;
} else if (gSaveContext.entranceIndex == 0x296) {
gSaveContext.entranceIndex = 0x23D;
}
}

View File

@ -801,12 +801,12 @@ void Sram_InitSave(FileChooseContext* fileChooseCtx) {
gSaveContext.eventChkInf[1] |= (1 << 3);
gSaveContext.eventChkInf[1] |= (1 << 4);
// Set "Got Zelda's Letter" flag. Also ensures Saria is back at SFM. TODO: Is this flag used for anything else?
gSaveContext.eventChkInf[4] |= 1;
// Got item from impa
gSaveContext.eventChkInf[5] |= 0x200;
// make sure saria is at SFM
gSaveContext.eventChkInf[4] |= (1 << 0);
// set this at the end to ensure we always start with the letter
// this is for the off chance we got the weird egg from impa (which should never happen)
INV_CONTENT(ITEM_LETTER_ZELDA) = ITEM_LETTER_ZELDA;

View File

@ -24,7 +24,7 @@ typedef struct {
/* 0x36 */ f32 pitch;
/* 0x36 */ f32 yaw;
/* 0x40 */ f32 roll;
/* 0x44 */ s32 epoch;
/* 0x44 */ u32 epoch;
} BgDyYoseizoParticle; // size = 0x48
typedef struct BgDyYoseizo {

View File

@ -13,7 +13,7 @@ typedef struct {
/* 0x0C */ Vec3f vel;
/* 0x18 */ s16 rotVelX;
/* 0x1A */ s16 rotVelY;
/* 0x1C */ s32 epoch;
/* 0x1C */ u32 epoch;
} BgJyaMegamiPiece; // size = 0x20
typedef struct BgJyaMegami {

View File

@ -261,7 +261,7 @@ void BgSpot00Hanebasi_DrawTorches(Actor* thisx, GlobalContext* globalCtx2) {
GlobalContext* globalCtx = globalCtx2;
f32 angle;
s32 i;
static s32 epoch = 0;
static u32 epoch = 0;
epoch++;
OPEN_DISPS(globalCtx->state.gfxCtx);

View File

@ -17,7 +17,7 @@ typedef struct {
/* 0x26 */ Color_RGB8 color;
/* 0x2A */ s16 alpha;
/* 0x2C */ f32 unk_2C;
/* 0x30 */ s32 epoch;
/* 0x30 */ u32 epoch;
} BossDodongoEffect; // Size = 0x34
typedef struct BossDodongo {

View File

@ -1835,7 +1835,7 @@ void BossFd_DrawBody(GlobalContext* globalCtx, BossFd* this) {
s16 i;
f32 temp_float;
Mtx* tempMat = Graph_Alloc(globalCtx->state.gfxCtx, 18 * sizeof(Mtx));
static s32 epoch = 0;
static u32 epoch = 0;
epoch++;
OPEN_DISPS(globalCtx->state.gfxCtx);

View File

@ -87,7 +87,7 @@ typedef struct BossFd2 {
/* 0x1394 */ BossFd2Cam camData;
/* 0x141C */ ColliderJntSph collider;
/* 0x143C */ ColliderJntSphElement elements[9];
/* 0x167C */ s32 epoch;
/* 0x167C */ u32 epoch;
} BossFd2; // size = 0x1680
#endif

View File

@ -3356,7 +3356,7 @@ void BossGanon_DrawShock(BossGanon* this, GlobalContext* globalCtx) {
s32 pad;
GraphicsContext* gfxCtx = globalCtx->state.gfxCtx;
s16 i;
static s32 epoch = 0;
static u32 epoch = 0;
epoch++;
OPEN_DISPS(gfxCtx);
@ -3463,7 +3463,7 @@ void BossGanon_DrawBigMagicCharge(BossGanon* this, GlobalContext* globalCtx) {
f32 yRot;
GraphicsContext* gfxCtx = globalCtx->state.gfxCtx;
s16 i;
static s32 epoch = 0;
static u32 epoch = 0;
epoch++;
OPEN_DISPS(gfxCtx);
@ -4158,7 +4158,7 @@ void BossGanon_LightBall_Draw(Actor* thisx, GlobalContext* globalCtx) {
s16 i;
f32 alpha;
s32 pad;
static s32 epoch = 0;
static u32 epoch = 0;
epoch++;
OPEN_DISPS(globalCtx->state.gfxCtx);

View File

@ -2464,7 +2464,7 @@ void func_80904340(BossGanon2* this, GlobalContext* globalCtx) {
f32 angle;
f32 sin;
f32 cos;
static s32 epoch = 0;
static u32 epoch = 0;
epoch++;
OPEN_DISPS(globalCtx->state.gfxCtx);
@ -2637,7 +2637,7 @@ void BossGanon2_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dLis
void func_80904D88(BossGanon2* this, GlobalContext* globalCtx) {
s32 pad;
s16 i;
static s32 epoch = 0;
static u32 epoch = 0;
epoch++;
OPEN_DISPS(globalCtx->state.gfxCtx);
@ -2701,7 +2701,7 @@ void func_80904FC8(BossGanon2* this, GlobalContext* globalCtx) {
void func_8090523C(BossGanon2* this, GlobalContext* globalCtx) {
Player* player;
f32 phi_f20;
static s32 epoch = 0;
static u32 epoch = 0;
epoch++;
OPEN_DISPS(globalCtx->state.gfxCtx);
@ -2933,7 +2933,7 @@ void func_809060E8(GlobalContext* globalCtx) {
BossGanon2Effect* effect;
s16 i;
BossGanon2Effect* effects;
static s32 epoch = 0;
static u32 epoch = 0;
epoch++;
effects = effect = globalCtx->specialEffects;

View File

@ -2443,7 +2443,7 @@ void BossMo_DrawTentacle(BossMo* this, GlobalContext* globalCtx) {
f32 phi_f20;
f32 phi_f22;
Vec3f sp110;
static s32 epoch = 0;
static u32 epoch = 0;
epoch++;
OPEN_DISPS(globalCtx->state.gfxCtx);

View File

@ -2706,7 +2706,7 @@ s32 BossSst_OverrideHandTrailDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx**
void BossSst_DrawHand(Actor* thisx, GlobalContext* globalCtx) {
BossSst* this = (BossSst*)thisx;
static s32 epoch = 0;
static u32 epoch = 0;
epoch++;
OPEN_DISPS(globalCtx->state.gfxCtx);

View File

@ -17,7 +17,7 @@ typedef struct {
/* 0x0020 */ s16 move;
/* 0x0022 */ s16 status;
/* 0x0024 */ u8 alpha;
/* 0x0028 */ s32 epoch;
/* 0x0028 */ u32 epoch;
} BossSstEffect; // size = 0x2C
typedef struct {

View File

@ -3329,7 +3329,7 @@ void func_80942180(BossTw* this, GlobalContext* globalCtx) {
void func_809426F0(BossTw* this, GlobalContext* globalCtx) {
s32 pad;
s16 i;
static s32 epoch = 0;
static u32 epoch = 0;
epoch++;
OPEN_DISPS(globalCtx->state.gfxCtx);
@ -4418,7 +4418,7 @@ void BossTw_BlastDraw(Actor* thisx, GlobalContext* globalCtx2) {
f32 scaleFactor;
s16 tailIdx;
s16 i;
static s32 epoch = 0;
static u32 epoch = 0;
epoch++;
OPEN_DISPS(globalCtx->state.gfxCtx);
@ -4492,7 +4492,7 @@ void BossTw_DrawDeathBall(Actor* thisx, GlobalContext* globalCtx2) {
f32 scaleFactor;
s16 tailIdx;
s16 i;
static s32 epoch = 0;
static u32 epoch = 0;
epoch++;
OPEN_DISPS(globalCtx->state.gfxCtx);

View File

@ -43,7 +43,7 @@ typedef struct {
/* 0x002E */ s16 work[EFF_WORK_MAX];
/* 0x0034 */ f32 workf[EFF_FWORK_MAX];
/* 0x0044 */ Actor* target;
s32 epoch;
u32 epoch;
} BossTwEffect;
typedef enum {

View File

@ -4006,7 +4006,7 @@ void BossVa_DrawDoor(GlobalContext* globalCtx, s16 scale) {
f32 yScale;
f32 segAngle = 0.0f;
s32 i;
static s32 epoch = 0;
static u32 epoch = 0;
epoch++;
OPEN_DISPS(globalCtx->state.gfxCtx);

View File

@ -564,7 +564,7 @@ void func_80967FFC(Actor* thisx, GlobalContext* globalCtx) {
Demo6K* this = (Demo6K*)thisx;
s32 pad;
u16 timer1 = this->timer1;
static s32 epoch = 0;
static u32 epoch = 0;
epoch++;
OPEN_DISPS(globalCtx->state.gfxCtx);
@ -695,7 +695,7 @@ void func_809688C4(Actor* thisx, GlobalContext* globalCtx2) {
GlobalContext* globalCtx = globalCtx2;
u32 frames = globalCtx->state.frames;
s32 i;
static s32 epoch = 0;
static u32 epoch = 0;
epoch++;
if ((i = (globalCtx->csCtx.state != CS_STATE_IDLE) && (globalCtx->csCtx.npcActions[1] != NULL)) &&

View File

@ -522,7 +522,7 @@ void DemoKankyo_DrawRain(Actor* thisx, GlobalContext* globalCtx) {
f32 translateY;
f32 translateZ;
s16 j;
static s32 epoch = 0;
static u32 epoch = 0;
epoch++;
OPEN_DISPS(globalCtx->state.gfxCtx);
@ -663,7 +663,7 @@ void DemoKankyo_DrawClouds(Actor* thisx, GlobalContext* globalCtx) {
f32 dx;
f32 dy;
f32 dz;
static s32 epoch = 0;
static u32 epoch = 0;
epoch++;
OPEN_DISPS(globalCtx->state.gfxCtx);
@ -784,7 +784,7 @@ void DemoKankyo_DrawWarpSparkles(Actor* thisx, GlobalContext* globalCtx) {
f32 translateZ;
PosRot posRot;
u8 linkAge = gSaveContext.linkAge;
static s32 epoch = 0;
static u32 epoch = 0;
epoch++;
OPEN_DISPS(globalCtx->state.gfxCtx);
@ -933,7 +933,7 @@ void DemoKankyo_DrawSparkles(Actor* thisx, GlobalContext* globalCtx) {
f32 scale;
s16 i;
PosRot posRot;
static s32 epoch = 0;
static u32 epoch = 0;
epoch++;
OPEN_DISPS(globalCtx->state.gfxCtx);

View File

@ -19,7 +19,7 @@ typedef struct {
/* 0x30 */ char unk_2C[4];
/* 0x34 */ f32 scale;
/* 0x38 */ char unk_34[8];
/* 0x3C */ s32 epoch;
/* 0x3C */ u32 epoch;
} EfcErupcParticles; // size 0x40
#define EFC_ERUPC_NUM_PARTICLES 100

View File

@ -269,7 +269,7 @@ void EffDust_DrawFunc_8099E4F4(Actor* thisx, GlobalContext* globalCtx2) {
f32* distanceTraveled;
s32 i;
f32 aux;
static s32 epoch = 0;
static u32 epoch = 0;
epoch++;
OPEN_DISPS(gfxCtx);
@ -321,7 +321,7 @@ void EffDust_DrawFunc_8099E784(Actor* thisx, GlobalContext* globalCtx2) {
s32 i;
f32 aux;
Player* player = GET_PLAYER(globalCtx);
static s32 epoch = 0;
static u32 epoch = 0;
epoch++;
OPEN_DISPS(gfxCtx);

View File

@ -19,7 +19,7 @@ typedef struct EnAnubiceFire {
/* 0x015E */ s16 unk_15E;
/* 0x0178 */ Vec3f unk_160[6];
/* 0x01A8 */ ColliderCylinder cylinder;
/* 0x01F4 */ s32 epoch;
/* 0x01F4 */ u32 epoch;
} EnAnubiceFire; // size = 0x01F8
#endif

View File

@ -32,7 +32,7 @@ typedef struct EnBa {
/* 0x031C */ s16 unk31C;
/* 0x0320 */ ColliderJntSph collider;
/* 0x0340 */ ColliderJntSphElement colliderItems[2];
/* 0x03C0 */ s32 epoch;
/* 0x03C0 */ u32 epoch;
} EnBa; // size = 0x03C4
#endif

View File

@ -167,12 +167,12 @@ void EnBoom_Fly(EnBoom* this, GlobalContext* globalCtx) {
// Decrement the return timer and check if it's 0. If it is, check if Link can catch it and handle accordingly.
// Otherwise handle grabbing and colliding.
if (DECR(this->returnTimer) == 0) {
if (DECR(this->returnTimer) == 0 || player->boomerangQuickRecall) {
distFromLink = Math_Vec3f_DistXYZ(&this->actor.world.pos, &player->actor.focus.pos);
this->moveTo = &player->actor;
// If the boomerang is less than 40 units away from Link, he can catch it.
if (distFromLink < 40.0f) {
if (distFromLink < 40.0f || player->boomerangQuickRecall) {
target = this->grabbed;
if (target != NULL) {
Math_Vec3f_Copy(&target->world.pos, &player->actor.world.pos);
@ -187,7 +187,8 @@ void EnBoom_Fly(EnBoom* this, GlobalContext* globalCtx) {
}
}
// Set player flags and kill the boomerang beacause Link caught it.
player->stateFlags1 &= ~0x02000000;
player->stateFlags1 &= ~PLAYER_STATE1_25;
player->boomerangQuickRecall = false;
Actor_Kill(&this->actor);
}
} else {

View File

@ -16,7 +16,7 @@ typedef struct EnBx {
/* 0x01B4 */ Vec3s unk_1B4[4];
/* 0x01CC */ ColliderCylinder collider;
/* 0x0218 */ ColliderQuad colliderQuad;
/* 0x0298 */ s32 epoch;
/* 0x0298 */ u32 epoch;
} EnBx; // size = 0x029C
#endif

View File

@ -88,7 +88,7 @@ typedef struct EnClearTagEffect {
/* 0x0058 */ f32 rotationX;
/* 0x005C */ f32 floorHeight;
/* 0x0060 */ Vec3f floorTangent;
/* 0x006C */ s32 epoch;
/* 0x006C */ u32 epoch;
} EnClearTagEffect; // size = 0x70
#define CLEAR_TAG_EFFECT_MAX_COUNT 100

View File

@ -18,9 +18,8 @@ void func_809E0070(Actor* thisx, GlobalContext* globalCtx);
void func_809DF494(EnCow* this, GlobalContext* globalCtx);
void func_809DF6BC(EnCow* this, GlobalContext* globalCtx);
struct CowInfo EnCow_GetInfo(EnCow* this, GlobalContext* globalCtx);
CowInfo EnCow_GetInfo(EnCow* this, GlobalContext* globalCtx);
void EnCow_MoveForRandomizer(EnCow* this, GlobalContext* globalCtx);
GetItemID EnCow_GetRandomizerItem(EnCow* this, GlobalContext* globalCtx);
void func_809DF778(EnCow* this, GlobalContext* globalCtx);
void func_809DF7D8(EnCow* this, GlobalContext* globalCtx);
void func_809DF870(EnCow* this, GlobalContext* globalCtx);
@ -216,12 +215,7 @@ void func_809DF730(EnCow* this, GlobalContext* globalCtx) {
}
}
struct CowInfo {
int cowId;
RandomizerCheck randomizerCheck;
};
struct CowInfo EnCow_GetInfo(EnCow* this, GlobalContext* globalCtx) {
CowInfo EnCow_GetInfo(EnCow* this, GlobalContext* globalCtx) {
struct CowInfo cowInfo;
cowInfo.cowId = -1;
@ -294,21 +288,10 @@ void EnCow_MoveForRandomizer(EnCow* this, GlobalContext* globalCtx) {
}
void EnCow_SetCowMilked(EnCow* this, GlobalContext* globalCtx) {
struct CowInfo cowInfo = EnCow_GetInfo(this, globalCtx);
gSaveContext.cowsMilked[cowInfo.cowId] = 1;
}
GetItemID EnCow_GetRandomizerItem(EnCow* this, GlobalContext* globalCtx) {
GetItemID itemId = ITEM_NONE;
struct CowInfo cowInfo = EnCow_GetInfo(this, globalCtx);
if (!gSaveContext.cowsMilked[cowInfo.cowId]) {
itemId = Randomizer_GetItemIdFromKnownCheck(cowInfo.randomizerCheck, GI_MILK);
} else if (Inventory_HasEmptyBottle()) {
itemId = GI_MILK;
}
return itemId;
CowInfo cowInfo = EnCow_GetInfo(this, globalCtx);
Player* player = GET_PLAYER(globalCtx);
player->pendingFlag.flagID = cowInfo.cowId;
player->pendingFlag.flagType = FLAG_COW_MILKED;
}
void func_809DF778(EnCow* this, GlobalContext* globalCtx) {
@ -316,16 +299,7 @@ void func_809DF778(EnCow* this, GlobalContext* globalCtx) {
this->actor.parent = NULL;
this->actionFunc = func_809DF730;
} else {
if (gSaveContext.n64ddFlag) {
GetItemID itemId = EnCow_GetRandomizerItem(this, globalCtx);
func_8002F434(&this->actor, globalCtx, itemId, 10000.0f, 100.0f);
EnCow_SetCowMilked(this, globalCtx);
if (itemId == GI_ICE_TRAP) {
Message_StartTextbox(globalCtx, 0xF8, &this->actor);
}
} else {
func_8002F434(&this->actor, globalCtx, GI_MILK, 10000.0f, 100.0f);
}
func_8002F434(&this->actor, globalCtx, GI_MILK, 10000.0f, 100.0f);
}
}
@ -334,19 +308,13 @@ void func_809DF7D8(EnCow* this, GlobalContext* globalCtx) {
this->actor.flags &= ~ACTOR_FLAG_16;
Message_CloseTextbox(globalCtx);
this->actionFunc = func_809DF778;
if (!gSaveContext.n64ddFlag) {
func_8002F434(&this->actor, globalCtx, GI_MILK, 10000.0f, 100.0f);
}
func_8002F434(&this->actor, globalCtx, GI_MILK, 10000.0f, 100.0f);
}
}
void func_809DF870(EnCow* this, GlobalContext* globalCtx) {
if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) {
if (Inventory_HasEmptyBottle() ||
(gSaveContext.n64ddFlag &&
Randomizer_GetSettingValue(RSK_SHUFFLE_COWS) &&
EnCow_GetRandomizerItem(this, globalCtx) != ITEM_NONE)
) {
if (Inventory_HasEmptyBottle()) {
Message_ContinueTextbox(globalCtx, 0x2007);
this->actionFunc = func_809DF7D8;
} else {
@ -367,6 +335,23 @@ void func_809DF8FC(EnCow* this, GlobalContext* globalCtx) {
func_809DF494(this, globalCtx);
}
bool EnCow_HasBeenMilked(EnCow* this, GlobalContext* globalCtx) {
CowInfo cowInfo = EnCow_GetInfo(this, globalCtx);
return gSaveContext.cowsMilked[cowInfo.cowId];
}
void EnCow_GivePlayerRandomizedItem(EnCow* this, GlobalContext* globalCtx) {
if (!EnCow_HasBeenMilked(this, globalCtx)) {
CowInfo cowInfo = EnCow_GetInfo(this, globalCtx);
GetItemID itemId = Randomizer_GetItemIdFromKnownCheck(cowInfo.randomizerCheck, GI_MILK);
func_8002F434(&this->actor, globalCtx, itemId, 10000.0f, 100.0f);
} else {
// once we've gotten the rando reward from the cow,
// return them to the their default action function
this->actionFunc = func_809DF96C;
}
}
void func_809DF96C(EnCow* this, GlobalContext* globalCtx) {
if ((globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_00) || (globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_04)) {
if (DREG(53) != 0) {
@ -377,6 +362,19 @@ void func_809DF96C(EnCow* this, GlobalContext* globalCtx) {
if ((this->actor.xzDistToPlayer < 150.0f) &&
(ABS((s16)(this->actor.yawTowardsPlayer - this->actor.shape.rot.y)) < 0x61A8)) {
DREG(53) = 0;
// when randomized with cowsanity, if we haven't gotten the
// reward from this cow yet, give that, otherwise use the
// vanilla cow behavior
if (gSaveContext.n64ddFlag &&
Randomizer_GetSettingValue(RSK_SHUFFLE_COWS) &&
!EnCow_HasBeenMilked(this, globalCtx)) {
EnCow_SetCowMilked(this, globalCtx);
// setting the ocarina mode here prevents intermittent issues
// with the item get not triggering until walking away
globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_00;
this->actionFunc = EnCow_GivePlayerRandomizedItem;
return;
}
this->actionFunc = func_809DF8FC;
this->actor.flags |= ACTOR_FLAG_16;
func_8002F2CC(&this->actor, globalCtx, 170.0f);

View File

@ -21,4 +21,9 @@ typedef struct EnCow {
/* 0x027C */ EnCowActionFunc actionFunc;
} EnCow; // size = 0x0280
typedef struct CowInfo {
int cowId;
RandomizerCheck randomizerCheck;
} CowInfo;
#endif

View File

@ -14,7 +14,7 @@ typedef struct {
/* 0x0010 */ u8 isAlive;
/* 0x0014 */ Vec3f moveDirection;
/* 0x0020 */ Vec3f rot;
/* 0x002C */ s32 epoch;
/* 0x002C */ u32 epoch;
} EnEncount2Particle; // size = 0x30
typedef struct EnEncount2 {

View File

@ -25,7 +25,7 @@ typedef struct {
/* 0x0014 */ Vec3f pos;
/* 0x0020 */ Vec3f velocity;
/* 0x002C */ Vec3f accel;
s32 epoch;
u32 epoch;
} EnFdEffect; // size = 0x38
typedef struct EnFd {

View File

@ -43,6 +43,7 @@ void EnFr_OcarinaMistake(EnFr* this, GlobalContext* globalCtx);
void EnFr_SetupReward(EnFr* this, GlobalContext* globalCtx, u8 unkCondition);
void EnFr_PrintTextBox(EnFr* this, GlobalContext* globalCtx);
void EnFr_TalkBeforeReward(EnFr* this, GlobalContext* globalCtx);
RandomizerCheck EnFr_RandomizerCheckFromSongIndex(u16 songIndex);
void EnFr_SetReward(EnFr* this, GlobalContext* globalCtx);
// Deactivate
@ -920,6 +921,23 @@ void EnFr_TalkBeforeReward(EnFr* this, GlobalContext* globalCtx) {
}
}
RandomizerCheck EnFr_RandomizerCheckFromSongIndex(u16 songIndex) {
switch (songIndex) {
case FROG_ZL:
return RC_ZR_FROGS_ZELDAS_LULLABY;
case FROG_EPONA:
return RC_ZR_FROGS_EPONAS_SONG;
case FROG_SARIA:
return RC_ZR_FROGS_SARIAS_SONG;
case FROG_SUNS:
return RC_ZR_FROGS_SUNS_SONG;
case FROG_SOT:
return RC_ZR_FROGS_SONG_OF_TIME;
default:
return RC_UNKNOWN_CHECK;
}
}
void EnFr_SetReward(EnFr* this, GlobalContext* globalCtx) {
u16 songIndex;
@ -930,7 +948,11 @@ void EnFr_SetReward(EnFr* this, GlobalContext* globalCtx) {
if ((songIndex >= FROG_ZL) && (songIndex <= FROG_SOT)) {
if (!(gSaveContext.eventChkInf[13] & sSongIndex[songIndex])) {
gSaveContext.eventChkInf[13] |= sSongIndex[songIndex];
this->reward = GI_RUPEE_PURPLE;
if (!gSaveContext.n64ddFlag) {
this->reward = GI_RUPEE_PURPLE;
} else {
this->reward = Randomizer_GetItemIdFromKnownCheck(EnFr_RandomizerCheckFromSongIndex(songIndex), GI_RUPEE_PURPLE);
}
} else {
this->reward = GI_RUPEE_BLUE;
}
@ -1016,7 +1038,7 @@ void EnFr_GiveReward(EnFr* this, GlobalContext* globalCtx) {
}
void EnFr_SetIdle(EnFr* this, GlobalContext* globalCtx) {
if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_DONE) && Message_ShouldAdvance(globalCtx)) {
if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_DONE) && Message_ShouldAdvance(globalCtx) || (gSaveContext.n64ddFlag && this->reward == GI_ICE_TRAP)) {
this->actionFunc = EnFr_Idle;
}
}

View File

@ -19,7 +19,7 @@ typedef struct {
/* 0x0014 */ Vec3f pos;
/* 0x0020 */ Vec3f velocity;
/* 0x002C */ Vec3f accel;
s32 epoch;
u32 epoch;
} EnFwEffect;
typedef struct EnFw {

View File

@ -21,7 +21,7 @@ typedef struct {
/* 0x0030 */ f32 xyScale; //
/* 0x0034 */ f32 xyScaleTarget;
/* 0x0038 */ u8 isTimerMod8; // conditional, used to run CollisionCheck_SetAT
s32 epoch;
u32 epoch;
} EnFzEffectSsIceSmoke; // size = 0x3C
typedef struct EnFz {

View File

@ -32,7 +32,7 @@ typedef struct {
/* 0x12 */ u8 flag;
/* 0x14 */ Vec3f velocity;
/* 0x20 */ Vec3f rot;
s32 epoch;
u32 epoch;
} EnGSwitchEffect; // size = 0x2C
typedef struct EnGSwitch {

View File

@ -27,7 +27,7 @@ typedef struct {
/* 0x20 */ f32 unk_20;
/* 0x24 */ f32 unk_24;
/* 0x28 */ f32 unk_28;
s32 epoch;
u32 epoch;
} EnGbCagedSoul; // size = 0x2C
typedef struct EnGb {

View File

@ -34,7 +34,7 @@ typedef struct {
/* 0x0014 */ Vec3f pos;
/* 0x0020 */ Vec3f velocity;
/* 0x002C */ Vec3f accel;
s32 epoch;
u32 epoch;
} EnGoEffect; // size = 0x38
typedef struct EnGo {

View File

@ -112,14 +112,23 @@ void EnHeishi1_Init(Actor* thisx, GlobalContext* globalCtx) {
}
}
// eventChkInf[4] & 1 = Got Zelda's Letter
// eventChkInf[5] & 0x200 = Got item from impa
// eventChkInf[8] & 1 = Ocarina thrown in moat
bool metZelda = (gSaveContext.eventChkInf[4] & 1) && (gSaveContext.eventChkInf[5] & 0x200);
if (this->type != 5) {
if (((gSaveContext.dayTime < 0xB888) || IS_DAY) && (gSaveContext.n64ddFlag || !(gSaveContext.eventChkInf[8] & 1))) {
if ((gSaveContext.dayTime < 0xB888 || IS_DAY) &&
((!gSaveContext.n64ddFlag && !(gSaveContext.eventChkInf[8] & 1)) ||
(gSaveContext.n64ddFlag && !metZelda))) {
this->actionFunc = EnHeishi1_SetupWalk;
} else {
Actor_Kill(&this->actor);
}
} else {
if ((gSaveContext.dayTime >= 0xB889) || !IS_DAY || (!gSaveContext.n64ddFlag && (gSaveContext.eventChkInf[8] & 1))) {
if ((gSaveContext.dayTime >= 0xB889) || !IS_DAY ||
(!gSaveContext.n64ddFlag && gSaveContext.eventChkInf[8] & 1) ||
(gSaveContext.n64ddFlag && metZelda)) {
this->actionFunc = EnHeishi1_SetupWaitNight;
} else {
Actor_Kill(&this->actor);

View File

@ -18,7 +18,7 @@ typedef struct {
/* 0x002C */ f32 scale;
/* 0x0030 */ f32 unk_30;
/* 0x0034 */ u8 timer;
s32 epoch;
u32 epoch;
} EnNiwFeather; // size = 0x0038
typedef struct EnNiw {

View File

@ -23,7 +23,7 @@ typedef struct EnNwcChick {
/* 0x36 */ u16 height;
/* 0x38 */ CollisionPoly* floorPoly;
/* 0x44 */ char unk_3C[0x20];
s32 epoch;
u32 epoch;
} EnNwcChick; // size = 0x5C
typedef struct EnNwc {

View File

@ -30,7 +30,7 @@ typedef struct EnNy {
/* 0x01F0 */ f32 unk_1F0;
/* 0x01F4 */ f32 unk_1F4;
/* 0x01F8 */ Vec3f unk_1F8[16];
s32 epoch;
u32 epoch;
} EnNy; // size = 0x02B8
#endif

View File

@ -29,7 +29,7 @@ typedef struct EnPoSisters {
/* 0x029C */ LightInfo lightInfo;
/* 0x02AC */ ColliderCylinder collider;
/* 0x02F8 */ MtxF unk_2F8;
s32 epoch;
u32 epoch;
} EnPoSisters; // size = 0x0338
#endif

View File

@ -760,6 +760,14 @@ void func_80AEC2C0(EnRu1* this, GlobalContext* globalCtx) {
func_80AEC070(this, globalCtx, something);
}
// Convenience function used so that Ruto always spawns in Jabu in rando, even after she's been kidnapped
// Equivalent to !(gSaveContext.infTable[20] & 0x20) in vanilla
bool shouldSpawnRuto() {
// gSaveContext.infTable[20] & 0x40 check is to prevent Ruto from spawning during the short period of time when
// she's on the Zora's Sapphire pedestal but hasn't been kidnapped yet (would result in multiple Rutos otherwise)
return !(gSaveContext.infTable[20] & 0x20) || (gSaveContext.n64ddFlag && (gSaveContext.infTable[20] & 0x40));
}
void func_80AEC320(EnRu1* this, GlobalContext* globalCtx) {
s8 actorRoom;
@ -767,8 +775,7 @@ void func_80AEC320(EnRu1* this, GlobalContext* globalCtx) {
func_80AEB264(this, &gRutoChildWait2Anim, 0, 0, 0);
this->action = 7;
EnRu1_SetMouthIndex(this, 1);
} else if ((gSaveContext.infTable[20] & 0x80) && !(gSaveContext.infTable[20] & 1) &&
!(gSaveContext.infTable[20] & 0x20)) {
} else if ((gSaveContext.infTable[20] & 0x80) && !(gSaveContext.infTable[20] & 1) && shouldSpawnRuto()) {
if (!func_80AEB020(this, globalCtx)) {
func_80AEB264(this, &gRutoChildWait2Anim, 0, 0, 0);
actorRoom = this->actor.room;
@ -1172,7 +1179,7 @@ void func_80AED414(EnRu1* this, GlobalContext* globalCtx) {
void func_80AED44C(EnRu1* this, GlobalContext* globalCtx) {
s8 actorRoom;
if ((gSaveContext.infTable[20] & 2) && !(gSaveContext.infTable[20] & 0x20) && !(gSaveContext.infTable[20] & 1) &&
if ((gSaveContext.infTable[20] & 2) && shouldSpawnRuto() && !(gSaveContext.infTable[20] & 1) &&
!(gSaveContext.infTable[20] & 0x80)) {
if (!func_80AEB020(this, globalCtx)) {
func_80AEB264(this, &gRutoChildWait2Anim, 0, 0, 0);
@ -2179,7 +2186,7 @@ void func_80AEFF40(EnRu1* this, GlobalContext* globalCtx) {
void func_80AEFF94(EnRu1* this, GlobalContext* globalCtx) {
s8 actorRoom;
if ((gSaveContext.infTable[20] & 2) && (gSaveContext.infTable[20] & 1) && !(gSaveContext.infTable[20] & 0x20) &&
if ((gSaveContext.infTable[20] & 2) && (gSaveContext.infTable[20] & 1) && shouldSpawnRuto() &&
(!(func_80AEB020(this, globalCtx)))) {
func_80AEB264(this, &gRutoChildWait2Anim, 0, 0, 0);
actorRoom = this->actor.room;

View File

@ -108,6 +108,7 @@ void func_80AFB768(EnSi* this, GlobalContext* globalCtx) {
giveItemId = sGetItemTable[getItemId - 1].itemId;
Item_Give(globalCtx, giveItemId);
}
player->getItemId = getItemId;
} else {
Item_Give(globalCtx, giveItemId);
}
@ -150,6 +151,7 @@ void func_80AFB89C(EnSi* this, GlobalContext* globalCtx) {
giveItemId = sGetItemTable[getItemId - 1].itemId;
Item_Give(globalCtx, giveItemId);
}
player->getItemId = getItemId;
} else {
Item_Give(globalCtx, giveItemId);
}

View File

@ -18,7 +18,7 @@ typedef struct {
/* 0x2C */ f32 unk_2C;
/* 0x30 */ f32 unk_30;
/* 0x34 */ u8 unk_34;
s32 epoch;
u32 epoch;
} EnSyatekiNiw_1; // size = 0x38
typedef struct EnSyatekiNiw {

View File

@ -460,10 +460,13 @@ void func_80B14AF4(EnTa* this, GlobalContext* globalCtx) {
void func_80B14B6C(EnTa* this, GlobalContext* globalCtx) {
if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) {
OnePointCutscene_Init(globalCtx, 4175, -99, &this->actor, MAIN_CAM);
s16 csCamIdx = OnePointCutscene_Init(globalCtx, 4175, -99, &this->actor, MAIN_CAM);
func_80B13AA0(this, func_80B14AF4, func_80B167C0);
this->unk_2CC = 5;
gSaveContext.eventChkInf[1] |= 0x10;
if (gSaveContext.n64ddFlag) {
OnePointCutscene_EndCutscene(globalCtx, csCamIdx);
}
Animation_PlayOnce(&this->skelAnime, &gTalonRunTransitionAnim);
this->currentAnimation = &gTalonRunAnim;
}

View File

@ -17,7 +17,7 @@ typedef struct EnTkEff {
/* 0x0014 */ Vec3f pos;
/* 0x0020 */ Vec3f speed;
/* 0x002C */ Vec3f accel;
s32 epoch;
u32 epoch;
} EnTkEff; // size = 0x0038
struct EnTk;

View File

@ -56,7 +56,7 @@ typedef struct {
/* 0x28 */ f32 scale;
/* 0x2C */ f32 lerpFactor;
/* 0x30 */ u8 state;
s32 epoch;
u32 epoch;
} EnViewerFireEffect; // size = 0x34
typedef struct EnViewer {

View File

@ -90,7 +90,7 @@ typedef struct {
/* 0x32 */ s16 timer;
/* 0x34 */ u8 shouldDraw;
/* 0x38 */ f32 drawDistance;
s32 epoch;
u32 epoch;
} FishingProp; // size = 0x3C
typedef enum {
@ -116,7 +116,7 @@ typedef struct {
/* 0x40 */ s16 unk_40;
/* 0x42 */ s16 unk_42;
/* 0x44 */ u8 shouldDraw;
s32 epoch;
u32 epoch;
} FishingGroupFish; // size = 0x48
#define LINE_SEG_COUNT 200
@ -1766,7 +1766,7 @@ static f32 sSinkingLureSizes[] = {
void Fishing_DrawSinkingLure(GlobalContext* globalCtx) {
s16 i;
f32 scale;
static s32 epoch = 0;
static u32 epoch = 0;
epoch++;
OPEN_DISPS(globalCtx->state.gfxCtx);

View File

@ -482,7 +482,7 @@ void MirRay_Draw(Actor* thisx, GlobalContext* globalCtx) {
s32 i;
MirRayShieldReflection reflection[6];
s32 temp;
static s32 epoch = 0;
static u32 epoch = 0;
epoch++;
this->reflectIntensity = 0.0f;

View File

@ -20,6 +20,7 @@
#include "objects/gameplay_keep/gameplay_keep.h"
#include "objects/object_link_child/object_link_child.h"
#include "textures/icon_item_24_static/icon_item_24_static.h"
#include <soh/Enhancements/custom-message/CustomMessageTypes.h>
#define GET_ITEM(itemId, objectId, drawId, textId, field, chestAnim) \
{ itemId, field, (chestAnim != CHEST_ANIM_SHORT ? 1 : -1) * (drawId + 1), textId, objectId }
@ -138,10 +139,10 @@ s32 func_808353D8(Player* this, GlobalContext* globalCtx);
s32 func_80835588(Player* this, GlobalContext* globalCtx);
s32 func_808356E8(Player* this, GlobalContext* globalCtx);
s32 func_80835800(Player* this, GlobalContext* globalCtx);
s32 func_80835884(Player* this, GlobalContext* globalCtx);
s32 func_808358F0(Player* this, GlobalContext* globalCtx);
s32 func_808359FC(Player* this, GlobalContext* globalCtx);
s32 func_80835B60(Player* this, GlobalContext* globalCtx);
s32 func_80835884(Player* this, GlobalContext* globalCtx); // Start aiming boomerang
s32 func_808358F0(Player* this, GlobalContext* globalCtx); // Aim boomerang
s32 func_808359FC(Player* this, GlobalContext* globalCtx); // Throw boomerang
s32 func_80835B60(Player* this, GlobalContext* globalCtx); // Boomerang active
s32 func_80835C08(Player* this, GlobalContext* globalCtx);
void func_80835F44(GlobalContext* globalCtx, Player* this, s32 item);
void func_80839F90(Player* this, GlobalContext* globalCtx);
@ -479,8 +480,8 @@ static s32 D_80853604 = 0;
static s32 D_80853608 = 0;
static s32 D_8085360C = 0;
static s16 D_80853610 = 0;
static s32 D_80853614 = 0;
static s32 D_80853618 = 0;
static s32 D_80853614 = 0; // Held item button just pressed?
static s32 D_80853618 = 0; // Held item button currently down?
static u16 D_8085361C[] = {
NA_SE_VO_LI_SWEAT,
@ -645,15 +646,15 @@ GetItemEntry sGetItemTable[] = {
GET_ITEM(ITEM_DOUBLE_MAGIC, OBJECT_GI_MAGICPOT, GID_MAGIC_LARGE, 0xE8, 0x80, CHEST_ANIM_LONG),
GET_ITEM(ITEM_DOUBLE_DEFENSE, OBJECT_GI_HEARTS, GID_HEART_CONTAINER, 0xE9, 0x80, CHEST_ANIM_LONG),
GET_ITEM(ITEM_BOTTLE_WITH_RED_POTION, OBJECT_GI_LIQUID, GID_POTION_RED, 0x43, 0x80, CHEST_ANIM_LONG),
GET_ITEM(ITEM_BOTTLE_WITH_GREEN_POTION, OBJECT_GI_LIQUID, GID_POTION_GREEN, 0x44, 0x80, CHEST_ANIM_LONG),
GET_ITEM(ITEM_BOTTLE_WITH_BLUE_POTION, OBJECT_GI_LIQUID, GID_POTION_BLUE, 0x45, 0x80, CHEST_ANIM_LONG),
GET_ITEM(ITEM_BOTTLE_WITH_FAIRY, OBJECT_GI_BOTTLE, GID_BOTTLE, 0x46, 0x80, CHEST_ANIM_LONG),
GET_ITEM(ITEM_BOTTLE_WITH_FISH, OBJECT_GI_FISH, GID_FISH, 0x47, 0x80, CHEST_ANIM_LONG),
GET_ITEM(ITEM_BOTTLE_WITH_BLUE_FIRE, OBJECT_GI_FIRE, GID_BLUE_FIRE, 0x5D, 0x80, CHEST_ANIM_LONG),
GET_ITEM(ITEM_BOTTLE_WITH_BUGS, OBJECT_GI_INSECT, GID_BUG, 0x7A, 0x80, CHEST_ANIM_LONG),
GET_ITEM(ITEM_BOTTLE_WITH_POE, OBJECT_GI_GHOST, GID_POE, 0x97, 0x80, CHEST_ANIM_LONG),
GET_ITEM(ITEM_BOTTLE_WITH_BIG_POE, OBJECT_GI_GHOST, GID_BIG_POE, 0xF9, 0x80, CHEST_ANIM_LONG),
GET_ITEM(ITEM_BOTTLE_WITH_RED_POTION, OBJECT_GI_LIQUID, GID_POTION_RED, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG),
GET_ITEM(ITEM_BOTTLE_WITH_GREEN_POTION, OBJECT_GI_LIQUID, GID_POTION_GREEN, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG),
GET_ITEM(ITEM_BOTTLE_WITH_BLUE_POTION, OBJECT_GI_LIQUID, GID_POTION_BLUE, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG),
GET_ITEM(ITEM_BOTTLE_WITH_FAIRY, OBJECT_GI_BOTTLE, GID_BOTTLE, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG),
GET_ITEM(ITEM_BOTTLE_WITH_FISH, OBJECT_GI_FISH, GID_FISH, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG),
GET_ITEM(ITEM_BOTTLE_WITH_BLUE_FIRE, OBJECT_GI_FIRE, GID_BLUE_FIRE, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG),
GET_ITEM(ITEM_BOTTLE_WITH_BUGS, OBJECT_GI_INSECT, GID_BUG, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG),
GET_ITEM(ITEM_BOTTLE_WITH_POE, OBJECT_GI_GHOST, GID_POE, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG),
GET_ITEM(ITEM_BOTTLE_WITH_BIG_POE, OBJECT_GI_GHOST, GID_BIG_POE, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG),
GET_ITEM_NONE,
GET_ITEM_NONE,
@ -947,6 +948,7 @@ static s8 sItemActionParams[] = {
static u8 sMaskMemory;
// Used to map action params to update functions
static s32(*D_80853EDC[])(Player* this, GlobalContext* globalCtx) = {
func_8083485C, func_8083485C, func_8083485C, func_808349DC, func_808349DC, func_808349DC, func_8083485C,
func_8083485C, func_8083501C, func_8083501C, func_8083501C, func_8083501C, func_8083501C, func_8083501C,
@ -2721,6 +2723,10 @@ s32 func_80835B60(Player* this, GlobalContext* globalCtx) {
return 1;
}
if (D_80853614 && CVar_GetS32("gFastBoomerang", 0)) {
this->boomerangQuickRecall = true;
}
return 0;
}
@ -6245,6 +6251,9 @@ void Player_SetPendingFlag(Player* this, GlobalContext* globalCtx) {
case FLAG_SCENE_TREASURE:
Flags_SetTreasure(globalCtx, this->pendingFlag.flagID);
break;
case FLAG_COW_MILKED:
gSaveContext.cowsMilked[this->pendingFlag.flagID] = 1;
break;
case FLAG_NONE:
default:
break;

View File

@ -11,7 +11,7 @@ typedef struct {
/* 0x24 */ s16 scale;
/* 0x26 */ s16 scaleStep;
/* 0x28 */ u8 drawMode;
/* 0x29 */ s32 epoch;
/* 0x29 */ u32 epoch;
} EffectSsBomb2InitParams; // size = 0x30
#endif

View File

@ -15,12 +15,12 @@ bool gSelectingMask;
bool gSelectingAdultTrade;
static s16 sAmmoVtxOffset[] = {
0, 2, 4, 6, 99, 99, 8, 99, 99, 10, 99, 99, 99, 99, 99, 99, 12,
0, 2, 4, 6, 99, 99, 8, 99, 10, 99, 99, 99, 99, 99, 12,
};
extern const char* _gAmmoDigit0Tex[];
void KaleidoScope_DrawAmmoCount(PauseContext* pauseCtx, GraphicsContext* gfxCtx, s16 item) {
void KaleidoScope_DrawAmmoCount(PauseContext* pauseCtx, GraphicsContext* gfxCtx, s16 item, int slot) {
s16 ammo;
s16 i;
@ -54,7 +54,7 @@ void KaleidoScope_DrawAmmoCount(PauseContext* pauseCtx, GraphicsContext* gfxCtx,
gDPPipeSync(POLY_KAL_DISP++);
if (i != 0) {
gSPVertex(POLY_KAL_DISP++, &pauseCtx->itemVtx[(sAmmoVtxOffset[item] + 31) * 4], 4, 0);
gSPVertex(POLY_KAL_DISP++, &pauseCtx->itemVtx[(sAmmoVtxOffset[slot] + 31) * 4], 4, 0);
gDPLoadTextureBlock(POLY_KAL_DISP++, ((u8*)_gAmmoDigit0Tex[i]), G_IM_FMT_IA, G_IM_SIZ_8b, 8, 8, 0,
G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD,
@ -63,7 +63,7 @@ void KaleidoScope_DrawAmmoCount(PauseContext* pauseCtx, GraphicsContext* gfxCtx,
gSP1Quadrangle(POLY_KAL_DISP++, 0, 2, 3, 1, 0);
}
gSPVertex(POLY_KAL_DISP++, &pauseCtx->itemVtx[(sAmmoVtxOffset[item] + 32) * 4], 4, 0);
gSPVertex(POLY_KAL_DISP++, &pauseCtx->itemVtx[(sAmmoVtxOffset[slot] + 32) * 4], 4, 0);
gDPLoadTextureBlock(POLY_KAL_DISP++, ((u8*)_gAmmoDigit0Tex[ammo]), G_IM_FMT_IA, G_IM_SIZ_8b, 8, 8, 0,
G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD,
@ -520,7 +520,7 @@ void KaleidoScope_DrawItemSelect(GlobalContext* globalCtx) {
for (i = 0; i < 15; i++) {
if ((gAmmoItems[i] != ITEM_NONE) && (gSaveContext.inventory.items[i] != ITEM_NONE)) {
KaleidoScope_DrawAmmoCount(pauseCtx, globalCtx->state.gfxCtx, gSaveContext.inventory.items[i]);
KaleidoScope_DrawAmmoCount(pauseCtx, globalCtx->state.gfxCtx, gSaveContext.inventory.items[i], i);
}
}