mirror of
https://github.com/HarbourMasters/Shipwright
synced 2026-05-24 15:21:22 -04:00
Compare commits
54 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 60faf3f750 | |||
| ef910a02f7 | |||
| f607afc754 | |||
| 865bcc57a7 | |||
| b5caf33a9b | |||
| f655ab592d | |||
| e6fc34e4c2 | |||
| d370ca93fd | |||
| fcf2141266 | |||
| e2f1cebfb5 | |||
| 907b770676 | |||
| ea49196bae | |||
| f65b711376 | |||
| 269e9faa46 | |||
| 35301556d9 | |||
| fb0f7169d7 | |||
| fbc397a131 | |||
| 818addfdda | |||
| f14c390364 | |||
| dd5e72a023 | |||
| d523b104d8 | |||
| fdcd9a7508 | |||
| c4f34624ba | |||
| 33fe8776bb | |||
| 02256fac66 | |||
| 621afc99f0 | |||
| 9d215b6dce | |||
| 420bdab328 | |||
| 717074ff86 | |||
| 3ab16b70c2 | |||
| 3cf9d655a7 | |||
| 95f27ace2e | |||
| d50ad4779d | |||
| f2df029efa | |||
| bdb201201e | |||
| f4e4545180 | |||
| 0ddb0711ad | |||
| 3234256b03 | |||
| d8a7a6c764 | |||
| 2dfbbc63e3 | |||
| 044d32a46f | |||
| afe032ea21 | |||
| fb45b66903 | |||
| ba987c49e2 | |||
| bd0672767a | |||
| e66eb8756d | |||
| bf31f2b330 | |||
| 4e9040d761 | |||
| 304016ddd2 | |||
| fe9c0fa4f7 | |||
| 384403edb5 | |||
| 60687aff0d | |||
| cf88b3d2bf | |||
| 78ffb41cd2 |
+2
-2
@@ -5,8 +5,8 @@ set(CMAKE_CXX_STANDARD 20 CACHE STRING "The C++ standard to use")
|
||||
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version")
|
||||
|
||||
project(Ship VERSION 8.0.0 LANGUAGES C CXX)
|
||||
set(PROJECT_BUILD_NAME "MacReady Alfa" CACHE STRING "")
|
||||
project(Ship VERSION 8.0.4 LANGUAGES C CXX)
|
||||
set(PROJECT_BUILD_NAME "MacReady Echo" CACHE STRING "")
|
||||
set(PROJECT_TEAM "github.com/harbourmasters" CACHE STRING "")
|
||||
|
||||
set_property(DIRECTORY ${CMAKE_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT soh)
|
||||
|
||||
+1
-1
Submodule OTRExporter updated: 0d8f5570a8...04b85b95fa
+1
-1
Submodule libultraship updated: c75ff3653f...b4abd7c366
@@ -79,7 +79,7 @@
|
||||
<Vtx/>
|
||||
</Array>
|
||||
|
||||
<Array Name="gMorphaVtx_007BB8" Count="4" Offset="0x7BB8">
|
||||
<Array Name="gMorphaVtx_006A18" Count="286" Offset="0x006A18">
|
||||
<Vtx/>
|
||||
</Array>
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<Root>
|
||||
<File Name="ovl_Boss_Dodongo" BaseAddress="0x808C1190" RangeStart="0x6238" RangeEnd="0x9238">
|
||||
<Texture Name="sLavaFloorLavaTex" OutName="lava_floor_lava" Format="rgba16" Width="64" Height="64" Offset="0x6238"/>
|
||||
<Texture Name="sLavaFloorLavaTex" OutName="lava_floor_lava" Format="rgba16" Width="32" Height="64" Offset="0x6238"/>
|
||||
<Texture Name="sLavaFloorRockTex" OutName="lava_floor_rock" Format="rgba16" Width="32" Height="64" Offset="0x8238"/>
|
||||
</File>
|
||||
</Root>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<!-- Morpha's Title Card -->
|
||||
<Texture Name="gMorphaTitleCardTex" Format="i8" Width="128" Height="120" Offset="0x1010"/>
|
||||
<Texture Name="gMorphaWaterTex" Format="rgba16" Width="32" Height="32" Offset="0x8870"/>
|
||||
|
||||
|
||||
<!-- DLists for Morpha's Core -->
|
||||
<DList Name="gMorphaCoreMembraneDL" Offset="0x6700"/>
|
||||
<DList Name="gMorphaCoreNucleusDL" Offset="0x6838"/>
|
||||
@@ -69,17 +69,17 @@
|
||||
<!-- Unused content -->
|
||||
|
||||
<!-- This is the dlist for EnVbBall for some reason. -->
|
||||
<DList Name="gMorphaDL_000550" Offset="0x550"/>
|
||||
<DList Name="gMorphaDL_000550" Offset="0x550"/>
|
||||
|
||||
<DList Name="gMorphaDL_000EC0" Offset="0xEC0"/>
|
||||
<DList Name="gMorphaDL_000EF8" Offset="0xEF8"/>
|
||||
<DList Name="gMorphaDL_007BF8" Offset="0x7BF8"/>
|
||||
|
||||
|
||||
<Array Name="gMorphaVtx_006938" Count="14" Offset="0x6938">
|
||||
<Vtx/>
|
||||
</Array>
|
||||
|
||||
<Array Name="gMorphaVtx_007BB8" Count="4" Offset="0x7BB8">
|
||||
<Array Name="gMorphaVtx_006A18" Count="286" Offset="0x006A18">
|
||||
<Vtx/>
|
||||
</Array>
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<Root>
|
||||
<File Name="ovl_Boss_Dodongo" BaseAddress="0x808B7370" RangeStart="0x61E8" RangeEnd="0x9238">
|
||||
<Texture Name="sLavaFloorLavaTex" OutName="lava_floor_lava" Format="rgba16" Width="64" Height="64" Offset="0x61E8"/>
|
||||
<File Name="ovl_Boss_Dodongo" BaseAddress="0x808B7370" RangeStart="0x61E8" RangeEnd="0x91E8">
|
||||
<Texture Name="sLavaFloorLavaTex" OutName="lava_floor_lava" Format="rgba16" Width="32" Height="64" Offset="0x61E8"/>
|
||||
<Texture Name="sLavaFloorRockTex" OutName="lava_floor_rock" Format="rgba16" Width="32" Height="64" Offset="0x81E8"/>
|
||||
</File>
|
||||
</Root>
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
<Root>
|
||||
<File Name="nes_message_data_static">
|
||||
<Text Name="nes_message_data_static" CodeOffset="0xF6910"/>
|
||||
<Text Name="nes_message_data_static" CodeOffset="0xF68F0"/>
|
||||
</File>
|
||||
<File Name="ger_message_data_static">
|
||||
<Text Name="ger_message_data_static" CodeOffset="0xF6910" LangOffset="0xFAB38"/>
|
||||
<Text Name="ger_message_data_static" CodeOffset="0xF68F0" LangOffset="0xFAB18"/>
|
||||
</File>
|
||||
<File Name="fra_message_data_static">
|
||||
<Text Name="fra_message_data_static" CodeOffset="0xF6910" LangOffset="0xFCC48"/>
|
||||
<Text Name="fra_message_data_static" CodeOffset="0xF68F0" LangOffset="0xFCC28"/>
|
||||
</File>
|
||||
<File Name="staff_message_data_static">
|
||||
<Text Name="staff_message_data_static" CodeOffset="0xFED58"/>
|
||||
<Text Name="staff_message_data_static" CodeOffset="0xFED38"/>
|
||||
</File>
|
||||
</Root>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<!-- Morpha's Title Card -->
|
||||
<Texture Name="gMorphaTitleCardTex" Format="i8" Width="128" Height="120" Offset="0x1010"/>
|
||||
<Texture Name="gMorphaWaterTex" Format="rgba16" Width="32" Height="32" Offset="0x8870"/>
|
||||
|
||||
|
||||
<!-- DLists for Morpha's Core -->
|
||||
<DList Name="gMorphaCoreMembraneDL" Offset="0x6700"/>
|
||||
<DList Name="gMorphaCoreNucleusDL" Offset="0x6838"/>
|
||||
@@ -69,17 +69,17 @@
|
||||
<!-- Unused content -->
|
||||
|
||||
<!-- This is the dlist for EnVbBall for some reason. -->
|
||||
<DList Name="gMorphaDL_000550" Offset="0x550"/>
|
||||
<DList Name="gMorphaDL_000550" Offset="0x550"/>
|
||||
|
||||
<DList Name="gMorphaDL_000EC0" Offset="0xEC0"/>
|
||||
<DList Name="gMorphaDL_000EF8" Offset="0xEF8"/>
|
||||
<DList Name="gMorphaDL_007BF8" Offset="0x7BF8"/>
|
||||
|
||||
|
||||
<Array Name="gMorphaVtx_006938" Count="14" Offset="0x6938">
|
||||
<Vtx/>
|
||||
</Array>
|
||||
|
||||
<Array Name="gMorphaVtx_007BB8" Count="4" Offset="0x7BB8">
|
||||
<Array Name="gMorphaVtx_006A18" Count="286" Offset="0x006A18">
|
||||
<Vtx/>
|
||||
</Array>
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<Root>
|
||||
<File Name="ovl_Boss_Dodongo" BaseAddress="0x808C1210" RangeStart="0x6238" RangeEnd="0x9238">
|
||||
<Texture Name="sLavaFloorLavaTex" OutName="lava_floor_lava" Format="rgba16" Width="64" Height="64" Offset="0x6238"/>
|
||||
<Texture Name="sLavaFloorLavaTex" OutName="lava_floor_lava" Format="rgba16" Width="32" Height="64" Offset="0x6238"/>
|
||||
<Texture Name="sLavaFloorRockTex" OutName="lava_floor_rock" Format="rgba16" Width="32" Height="64" Offset="0x8238"/>
|
||||
</File>
|
||||
</Root>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<!-- Morpha's Title Card -->
|
||||
<Texture Name="gMorphaTitleCardTex" Format="i8" Width="128" Height="120" Offset="0x1010"/>
|
||||
<Texture Name="gMorphaWaterTex" Format="rgba16" Width="32" Height="32" Offset="0x8870"/>
|
||||
|
||||
|
||||
<!-- DLists for Morpha's Core -->
|
||||
<DList Name="gMorphaCoreMembraneDL" Offset="0x6700"/>
|
||||
<DList Name="gMorphaCoreNucleusDL" Offset="0x6838"/>
|
||||
@@ -69,17 +69,17 @@
|
||||
<!-- Unused content -->
|
||||
|
||||
<!-- This is the dlist for EnVbBall for some reason. -->
|
||||
<DList Name="gMorphaDL_000550" Offset="0x550"/>
|
||||
<DList Name="gMorphaDL_000550" Offset="0x550"/>
|
||||
|
||||
<DList Name="gMorphaDL_000EC0" Offset="0xEC0"/>
|
||||
<DList Name="gMorphaDL_000EF8" Offset="0xEF8"/>
|
||||
<DList Name="gMorphaDL_007BF8" Offset="0x7BF8"/>
|
||||
|
||||
|
||||
<Array Name="gMorphaVtx_006938" Count="14" Offset="0x6938">
|
||||
<Vtx/>
|
||||
</Array>
|
||||
|
||||
<Array Name="gMorphaVtx_007BB8" Count="4" Offset="0x7BB8">
|
||||
<Array Name="gMorphaVtx_006A18" Count="286" Offset="0x006A18">
|
||||
<Vtx/>
|
||||
</Array>
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<Root>
|
||||
<File Name="ovl_Boss_Dodongo" BaseAddress="0x808C1190" RangeStart="0x6238" RangeEnd="0x9238">
|
||||
<Texture Name="sLavaFloorLavaTex" OutName="lava_floor_lava" Format="rgba16" Width="64" Height="64" Offset="0x6238"/>
|
||||
<Texture Name="sLavaFloorRockTex" OutName="lava_floor_rock" Format="rgba16" Width="32" Height="64" Offset="0x8238"/>
|
||||
<File Name="ovl_Boss_Dodongo" BaseAddress="0x808C1190" RangeStart="0x61E8" RangeEnd="0x91E8">
|
||||
<Texture Name="sLavaFloorLavaTex" OutName="lava_floor_lava" Format="rgba16" Width="32" Height="64" Offset="0x61E8"/>
|
||||
<Texture Name="sLavaFloorRockTex" OutName="lava_floor_rock" Format="rgba16" Width="32" Height="64" Offset="0x81E8"/>
|
||||
</File>
|
||||
</Root>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<!-- Morpha's Title Card -->
|
||||
<Texture Name="gMorphaTitleCardTex" Format="i8" Width="128" Height="120" Offset="0x1010"/>
|
||||
<Texture Name="gMorphaWaterTex" Format="rgba16" Width="32" Height="32" Offset="0x8870"/>
|
||||
|
||||
|
||||
<!-- DLists for Morpha's Core -->
|
||||
<DList Name="gMorphaCoreMembraneDL" Offset="0x6700"/>
|
||||
<DList Name="gMorphaCoreNucleusDL" Offset="0x6838"/>
|
||||
@@ -69,17 +69,17 @@
|
||||
<!-- Unused content -->
|
||||
|
||||
<!-- This is the dlist for EnVbBall for some reason. -->
|
||||
<DList Name="gMorphaDL_000550" Offset="0x550"/>
|
||||
<DList Name="gMorphaDL_000550" Offset="0x550"/>
|
||||
|
||||
<DList Name="gMorphaDL_000EC0" Offset="0xEC0"/>
|
||||
<DList Name="gMorphaDL_000EF8" Offset="0xEF8"/>
|
||||
<DList Name="gMorphaDL_007BF8" Offset="0x7BF8"/>
|
||||
|
||||
|
||||
<Array Name="gMorphaVtx_006938" Count="14" Offset="0x6938">
|
||||
<Vtx/>
|
||||
</Array>
|
||||
|
||||
<Array Name="gMorphaVtx_007BB8" Count="4" Offset="0x7BB8">
|
||||
<Array Name="gMorphaVtx_006A18" Count="286" Offset="0x006A18">
|
||||
<Vtx/>
|
||||
</Array>
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<Root>
|
||||
<File Name="ovl_Boss_Dodongo" BaseAddress="0x8089E470" RangeStart="0x61C8" RangeEnd="0x91C8">
|
||||
<Texture Name="sLavaFloorLavaTex" OutName="lava_floor_lava" Format="rgba16" Width="64" Height="64" Offset="0x61C8"/>
|
||||
<Texture Name="sLavaFloorLavaTex" OutName="lava_floor_lava" Format="rgba16" Width="32" Height="64" Offset="0x61C8"/>
|
||||
<Texture Name="sLavaFloorRockTex" OutName="lava_floor_rock" Format="rgba16" Width="32" Height="64" Offset="0x81C8"/>
|
||||
</File>
|
||||
</Root>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<!-- Morpha's Title Card -->
|
||||
<Texture Name="gMorphaTitleCardTex" Format="i8" Width="128" Height="120" Offset="0x1010"/>
|
||||
<Texture Name="gMorphaWaterTex" Format="rgba16" Width="32" Height="32" Offset="0x8870"/>
|
||||
|
||||
|
||||
<!-- DLists for Morpha's Core -->
|
||||
<DList Name="gMorphaCoreMembraneDL" Offset="0x6700"/>
|
||||
<DList Name="gMorphaCoreNucleusDL" Offset="0x6838"/>
|
||||
@@ -69,17 +69,17 @@
|
||||
<!-- Unused content -->
|
||||
|
||||
<!-- This is the dlist for EnVbBall for some reason. -->
|
||||
<DList Name="gMorphaDL_000550" Offset="0x550"/>
|
||||
<DList Name="gMorphaDL_000550" Offset="0x550"/>
|
||||
|
||||
<DList Name="gMorphaDL_000EC0" Offset="0xEC0"/>
|
||||
<DList Name="gMorphaDL_000EF8" Offset="0xEF8"/>
|
||||
<DList Name="gMorphaDL_007BF8" Offset="0x7BF8"/>
|
||||
|
||||
|
||||
<Array Name="gMorphaVtx_006938" Count="14" Offset="0x6938">
|
||||
<Vtx/>
|
||||
</Array>
|
||||
|
||||
<Array Name="gMorphaVtx_007BB8" Count="4" Offset="0x7BB8">
|
||||
<Array Name="gMorphaVtx_006A18" Count="286" Offset="0x006A18">
|
||||
<Vtx/>
|
||||
</Array>
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<Root>
|
||||
<File Name="ovl_Boss_Dodongo" BaseAddress="0x808C1190" RangeStart="0x61C8" RangeEnd="0x91C8">
|
||||
<Texture Name="sLavaFloorLavaTex" OutName="lava_floor_lava" Format="rgba16" Width="64" Height="64" Offset="0x61C8"/>
|
||||
<Texture Name="sLavaFloorLavaTex" OutName="lava_floor_lava" Format="rgba16" Width="32" Height="64" Offset="0x61C8"/>
|
||||
<Texture Name="sLavaFloorRockTex" OutName="lava_floor_rock" Format="rgba16" Width="32" Height="64" Offset="0x81C8"/>
|
||||
</File>
|
||||
</Root>
|
||||
|
||||
+1
-1
@@ -439,7 +439,7 @@ static std::unordered_map<u16, const char*> actorDescriptions = {
|
||||
{ ACTOR_EN_DAIKU_KAKARIKO, "Carpenters (Kakariko)" },
|
||||
{ ACTOR_BG_BOWL_WALL, "Bombchu Bowling Alley Wall" },
|
||||
{ ACTOR_EN_WALL_TUBO, "Bombchu Bowling Alley Bullseyes" },
|
||||
{ ACTOR_EN_PO_DESERT, "Poe Guide (Desert Wasteland)" },
|
||||
{ ACTOR_EN_PO_DESERT, "Poe Guide (Haunted Wasteland)" },
|
||||
{ ACTOR_EN_CROW, "Guay" },
|
||||
{ ACTOR_DOOR_KILLER, "Fake Door" },
|
||||
{ ACTOR_BG_SPOT11_OASIS, "Oasis (Desert Colossus)" },
|
||||
|
||||
@@ -400,8 +400,9 @@ void AudioCollection::InitializeShufflePool() {
|
||||
if (shufflePoolInitialized) return;
|
||||
|
||||
for (auto& [seqId, seqInfo] : sequenceMap) {
|
||||
if (!seqInfo.canBeUsedAsReplacement) continue;
|
||||
const std::string cvarKey = "gAudioEditor.Excluded." + seqInfo.sfxKey;
|
||||
if (CVarGetInteger(cvarKey.c_str(), 0) && !seqInfo.canBeUsedAsReplacement) {
|
||||
if (CVarGetInteger(cvarKey.c_str(), 0)) {
|
||||
excludedSequences.insert(&seqInfo);
|
||||
} else {
|
||||
includedSequences.insert(&seqInfo);
|
||||
|
||||
@@ -312,6 +312,7 @@ namespace GameControlEditor {
|
||||
DrawHelpIcon("Allows the cursor on the pause menu to be over any slot. Sometimes required in rando to select "
|
||||
"certain items.");
|
||||
UIWidgets::Spacer(0);
|
||||
ImGui::BeginDisabled(CVarGetInteger("gDisableChangingSettings", 0));
|
||||
UIWidgets::PaddedEnhancementCheckbox("Enable walk speed modifiers", "gEnableWalkModify", true, false);
|
||||
DrawHelpIcon("Hold the assigned button to change the maximum walking speed\nTo change the assigned button, go into the Ports tabs above");
|
||||
if (CVarGetInteger("gEnableWalkModify", 0)) {
|
||||
@@ -323,6 +324,7 @@ namespace GameControlEditor {
|
||||
UIWidgets::PaddedEnhancementSliderFloat("Modifier 2: %d %%", "##WalkMod2", "gWalkModifierTwo", 0.0f, 5.0f, "", 1.0f, true, true, false, true);
|
||||
window->EndGroupPanelPublic(0);
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
UIWidgets::Spacer(0);
|
||||
UIWidgets::PaddedEnhancementCheckbox("Answer Navi Prompt with L Button", "gNaviOnL");
|
||||
DrawHelpIcon("Speak to Navi with L but enter first-person camera with C-Up");
|
||||
|
||||
@@ -1485,6 +1485,7 @@ void Draw_Placements(){
|
||||
}
|
||||
|
||||
void DrawSillyTab() {
|
||||
ImGui::BeginDisabled(CVarGetInteger("gDisableChangingSettings", 0));
|
||||
if (CVarGetInteger("gLetItSnow", 0)) {
|
||||
if (UIWidgets::EnhancementCheckbox("Let It Snow", "gLetItSnow")) {
|
||||
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
|
||||
@@ -1569,6 +1570,7 @@ void DrawSillyTab() {
|
||||
CVarClear("gCosmetics.Kak_Windmill_Speed.Changed");
|
||||
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
|
||||
// Copies the RGB values from one cosmetic option to another, multiplied by the passed in amount, this
|
||||
@@ -1767,6 +1769,10 @@ void CosmeticsEditorWindow::DrawElement() {
|
||||
ImGui::SameLine();
|
||||
UIWidgets::EnhancementCombobox("gCosmetics.DefaultColorScheme", colorSchemes, COLORSCHEME_N64);
|
||||
UIWidgets::EnhancementCheckbox("Advanced Mode", "gCosmetics.AdvancedMode");
|
||||
UIWidgets::InsertHelpHoverText(
|
||||
"Some cosmetic options may not apply if you have any mods that provide custom models for the cosmetic option.\n\n"
|
||||
"For example, if you have custom Link model, then the Link's Hair color option will most likely not apply."
|
||||
);
|
||||
if (CVarGetInteger("gCosmetics.AdvancedMode", 0)) {
|
||||
if (ImGui::Button("Lock All Advanced", ImVec2(ImGui::GetContentRegionAvail().x / 2, 30.0f))) {
|
||||
for (auto& [id, cosmeticOption] : cosmeticOptions) {
|
||||
|
||||
@@ -380,13 +380,13 @@ CrowdControl::Effect* CrowdControl::ParseMessage(char payload[512]) {
|
||||
effect->category = kEffectCatBoots;
|
||||
effect->timeRemaining = 30000;
|
||||
effect->giEffect = new GameInteractionEffect::ForceEquipBoots();
|
||||
effect->giEffect->parameters[0] = PLAYER_BOOTS_IRON;
|
||||
effect->giEffect->parameters[0] = EQUIP_VALUE_BOOTS_IRON;
|
||||
break;
|
||||
case kEffectForceHoverBoots:
|
||||
effect->category = kEffectCatBoots;
|
||||
effect->timeRemaining = 30000;
|
||||
effect->giEffect = new GameInteractionEffect::ForceEquipBoots();
|
||||
effect->giEffect->parameters[0] = PLAYER_BOOTS_HOVER;
|
||||
effect->giEffect->parameters[0] = EQUIP_VALUE_BOOTS_HOVER;
|
||||
break;
|
||||
case kEffectSlipperyFloor:
|
||||
effect->category = kEffectCatSlipperyFloor;
|
||||
|
||||
@@ -51,6 +51,7 @@ typedef enum {
|
||||
TEXT_WARP_NOCTURNE_OF_SHADOW = 0x891,
|
||||
TEXT_WARP_PRELUDE_OF_LIGHT = 0x892,
|
||||
TEXT_WARP_RANDOM_REPLACED_TEXT = 0x9200,
|
||||
TEXT_SHOOTING_GALLERY_MAN_COME_BACK_WITH_BOW = 0x9210,
|
||||
TEXT_LAKE_HYLIA_WATER_SWITCH_SIGN = 0x346, // 0x3yy for cuttable sign range
|
||||
TEXT_LAKE_HYLIA_WATER_SWITCH_NAVI = 0x1B3, // 0x1yy for Navi msg range
|
||||
} TextIDs;
|
||||
|
||||
@@ -207,6 +207,7 @@ static bool ResetHandler(std::shared_ptr<LUS::Console> Console, std::vector<std:
|
||||
return 1;
|
||||
}
|
||||
|
||||
gPlayState->gameplayFrames = 0;
|
||||
SET_NEXT_GAMESTATE(&gPlayState->state, TitleSetup_Init, GameState);
|
||||
gPlayState->state.running = false;
|
||||
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnExitGame>(gSaveContext.fileNum);
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
#ifndef _ENHANCEMENT_TYPES_H_
|
||||
#define _ENHANCEMENT_TYPES_H_
|
||||
|
||||
typedef enum {
|
||||
WARP_MODE_OVERRIDE_OFF,
|
||||
WARP_MODE_OVERRIDE_MQ_AS_VANILLA,
|
||||
@@ -74,3 +77,5 @@ typedef enum {
|
||||
DEKU_STICK_UNBREAKABLE,
|
||||
DEKU_STICK_UNBREAKABLE_AND_ALWAYS_ON_FIRE,
|
||||
} DekuStickType;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -94,6 +94,7 @@ void GameInteractor_SetTriforceHuntCreditsWarpActive(uint8_t state);
|
||||
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
#define DEFINE_HOOK(name, type) \
|
||||
struct name { \
|
||||
@@ -193,6 +194,9 @@ public:
|
||||
|
||||
DEFINE_HOOK(OnSetGameLanguage, void());
|
||||
|
||||
DEFINE_HOOK(OnFileDropped, void(std::string filePath));
|
||||
DEFINE_HOOK(OnAssetAltChange, void());
|
||||
|
||||
// Helpers
|
||||
static bool IsSaveLoaded();
|
||||
static bool IsGameplayPaused();
|
||||
|
||||
@@ -181,3 +181,9 @@ void GameInteractor_ExecuteOnUpdateFileNameSelection(int16_t charCode) {
|
||||
void GameInteractor_ExecuteOnSetGameLanguage() {
|
||||
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnSetGameLanguage>();
|
||||
}
|
||||
|
||||
// MARK: - System
|
||||
|
||||
void GameInteractor_RegisterOnAssetAltChange(void (*fn)(void)) {
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnAssetAltChange>(fn);
|
||||
}
|
||||
|
||||
@@ -56,6 +56,10 @@ void GameInteractor_ExecuteOnUpdateFileNameSelection(int16_t charCode);
|
||||
|
||||
// MARK: - Game
|
||||
void GameInteractor_ExecuteOnSetGameLanguage();
|
||||
|
||||
// MARK: - System
|
||||
void GameInteractor_RegisterOnAssetAltChange(void (*fn)(void));
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -618,7 +618,7 @@ void DrawGameplayStatsOptionsTab() {
|
||||
}
|
||||
|
||||
void GameplayStatsWindow::DrawElement() {
|
||||
ImGui::SetNextWindowSize(ImVec2(480, 550), ImGuiCond_Appearing);
|
||||
ImGui::SetNextWindowSize(ImVec2(480, 550), ImGuiCond_FirstUseEver);
|
||||
if (!ImGui::Begin("Gameplay Stats", &mIsVisible, ImGuiWindowFlags_NoFocusOnAppearing)) {
|
||||
ImGui::End();
|
||||
return;
|
||||
|
||||
@@ -35,8 +35,6 @@ extern PlayState* gPlayState;
|
||||
extern void Overlay_DisplayText(float duration, const char* text);
|
||||
uint32_t ResourceMgr_IsSceneMasterQuest(s16 sceneNum);
|
||||
}
|
||||
bool performDelayedSave = false;
|
||||
bool performSave = false;
|
||||
|
||||
// TODO: When there's more uses of something like this, create a new GI::RawAction?
|
||||
void ReloadSceneTogglingLinkAge() {
|
||||
@@ -49,6 +47,7 @@ void ReloadSceneTogglingLinkAge() {
|
||||
|
||||
void RegisterInfiniteMoney() {
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
|
||||
if (!GameInteractor::IsSaveLoaded()) return;
|
||||
if (CVarGetInteger("gInfiniteMoney", 0) != 0) {
|
||||
if (gSaveContext.rupees < CUR_CAPACITY(UPG_WALLET)) {
|
||||
gSaveContext.rupees = CUR_CAPACITY(UPG_WALLET);
|
||||
@@ -59,6 +58,7 @@ void RegisterInfiniteMoney() {
|
||||
|
||||
void RegisterInfiniteHealth() {
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
|
||||
if (!GameInteractor::IsSaveLoaded()) return;
|
||||
if (CVarGetInteger("gInfiniteHealth", 0) != 0) {
|
||||
if (gSaveContext.health < gSaveContext.healthCapacity) {
|
||||
gSaveContext.health = gSaveContext.healthCapacity;
|
||||
@@ -69,6 +69,7 @@ void RegisterInfiniteHealth() {
|
||||
|
||||
void RegisterInfiniteAmmo() {
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
|
||||
if (!GameInteractor::IsSaveLoaded()) return;
|
||||
if (CVarGetInteger("gInfiniteAmmo", 0) != 0) {
|
||||
// Deku Sticks
|
||||
if (AMMO(ITEM_STICK) < CUR_CAPACITY(UPG_STICKS)) {
|
||||
@@ -105,6 +106,7 @@ void RegisterInfiniteAmmo() {
|
||||
|
||||
void RegisterInfiniteMagic() {
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
|
||||
if (!GameInteractor::IsSaveLoaded()) return;
|
||||
if (CVarGetInteger("gInfiniteMagic", 0) != 0) {
|
||||
if (gSaveContext.isMagicAcquired && gSaveContext.magic != (gSaveContext.isDoubleMagicAcquired + 1) * 0x30) {
|
||||
gSaveContext.magic = (gSaveContext.isDoubleMagicAcquired + 1) * 0x30;
|
||||
@@ -115,6 +117,7 @@ void RegisterInfiniteMagic() {
|
||||
|
||||
void RegisterInfiniteNayrusLove() {
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
|
||||
if (!GameInteractor::IsSaveLoaded()) return;
|
||||
if (CVarGetInteger("gInfiniteNayru", 0) != 0) {
|
||||
gSaveContext.nayrusLoveTimer = 0x44B;
|
||||
}
|
||||
@@ -123,7 +126,7 @@ void RegisterInfiniteNayrusLove() {
|
||||
|
||||
void RegisterMoonJumpOnL() {
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
|
||||
if (!gPlayState) return;
|
||||
if (!GameInteractor::IsSaveLoaded()) return;
|
||||
|
||||
if (CVarGetInteger("gMoonJumpOnL", 0) != 0) {
|
||||
Player* player = GET_PLAYER(gPlayState);
|
||||
@@ -138,7 +141,7 @@ void RegisterMoonJumpOnL() {
|
||||
|
||||
void RegisterInfiniteISG() {
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
|
||||
if (!gPlayState) return;
|
||||
if (!GameInteractor::IsSaveLoaded()) return;
|
||||
|
||||
if (CVarGetInteger("gEzISG", 0) != 0) {
|
||||
Player* player = GET_PLAYER(gPlayState);
|
||||
@@ -150,7 +153,7 @@ void RegisterInfiniteISG() {
|
||||
//Permanent quick put away (QPA) glitched damage value
|
||||
void RegisterEzQPA() {
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
|
||||
if (!gPlayState) return;
|
||||
if (!GameInteractor::IsSaveLoaded()) return;
|
||||
|
||||
if (CVarGetInteger("gEzQPA", 0) != 0) {
|
||||
Player* player = GET_PLAYER(gPlayState);
|
||||
@@ -162,7 +165,7 @@ void RegisterEzQPA() {
|
||||
|
||||
void RegisterUnrestrictedItems() {
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
|
||||
if (!gPlayState) return;
|
||||
if (!GameInteractor::IsSaveLoaded()) return;
|
||||
|
||||
if (CVarGetInteger("gNoRestrictItems", 0) != 0) {
|
||||
u8 sunsBackup = gPlayState->interfaceCtx.restrictions.sunsSong;
|
||||
@@ -190,14 +193,16 @@ void RegisterFreezeTime() {
|
||||
/// Switches Link's age and respawns him at the last entrance he entered.
|
||||
void RegisterSwitchAge() {
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
|
||||
if (!GameInteractor::IsSaveLoaded()) {
|
||||
CVarClear("gSwitchAge");
|
||||
return;
|
||||
}
|
||||
static bool warped = false;
|
||||
static Vec3f playerPos;
|
||||
static int16_t playerYaw;
|
||||
static RoomContext* roomCtx;
|
||||
static s32 roomNum;
|
||||
|
||||
if (!gPlayState) return;
|
||||
|
||||
if (CVarGetInteger("gSwitchAge", 0) && !warped) {
|
||||
playerPos = GET_PLAYER(gPlayState)->actor.world.pos;
|
||||
playerYaw = GET_PLAYER(gPlayState)->actor.shape.rot.y;
|
||||
@@ -217,7 +222,7 @@ void RegisterSwitchAge() {
|
||||
func_80097534(gPlayState, roomCtx); // load map for new room (unloading the previous room)
|
||||
}
|
||||
warped = false;
|
||||
CVarSetInteger("gSwitchAge", 0);
|
||||
CVarClear("gSwitchAge");
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -226,7 +231,8 @@ void RegisterSwitchAge() {
|
||||
void RegisterOcarinaTimeTravel() {
|
||||
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnOcarinaSongAction>([]() {
|
||||
if (!gPlayState) {
|
||||
if (!GameInteractor::IsSaveLoaded()) {
|
||||
CVarClear("gTimeTravel");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -258,14 +264,12 @@ void RegisterOcarinaTimeTravel() {
|
||||
|
||||
void AutoSave(GetItemEntry itemEntry) {
|
||||
u8 item = itemEntry.itemId;
|
||||
bool performSave = false;
|
||||
// Don't autosave immediately after buying items from shops to prevent getting them for free!
|
||||
// Don't autosave in the Chamber of Sages since resuming from that map breaks the game
|
||||
// Don't autosave during the Ganon fight when picking up the Master Sword
|
||||
// Don't autosave in the fishing pond to prevent getting rod on B outside of the pond
|
||||
// Don't autosave in the bombchu bowling alley to prevent having chus on B outside of the minigame
|
||||
// Don't autosave in grottos since resuming from grottos breaks the game.
|
||||
if ((CVarGetInteger("gAutosave", AUTOSAVE_OFF) != AUTOSAVE_OFF) && (gPlayState != NULL) && (gSaveContext.pendingSale == ITEM_NONE) &&
|
||||
(gPlayState->gameplayFrames > 60 && gSaveContext.cutsceneIndex < 0xFFF0) && (gPlayState->sceneNum != SCENE_GANON_BOSS)) {
|
||||
(gPlayState->gameplayFrames > 60 && gSaveContext.cutsceneIndex < 0xFFF0) && (gPlayState->sceneNum != SCENE_GANON_BOSS) && (gPlayState->sceneNum != SCENE_CHAMBER_OF_THE_SAGES)) {
|
||||
if (((CVarGetInteger("gAutosave", AUTOSAVE_OFF) == AUTOSAVE_LOCATION_AND_ALL_ITEMS) || (CVarGetInteger("gAutosave", AUTOSAVE_OFF) == AUTOSAVE_ALL_ITEMS)) && (item != ITEM_NONE)) {
|
||||
// Autosave for all items
|
||||
performSave = true;
|
||||
@@ -326,25 +330,9 @@ void AutoSave(GetItemEntry itemEntry) {
|
||||
CVarGetInteger("gAutosave", AUTOSAVE_OFF) == AUTOSAVE_LOCATION) {
|
||||
performSave = true;
|
||||
}
|
||||
if (gPlayState->sceneNum == SCENE_FAIRYS_FOUNTAIN || gPlayState->sceneNum == SCENE_GROTTOS ||
|
||||
gPlayState->sceneNum == SCENE_CHAMBER_OF_THE_SAGES || gPlayState->sceneNum == SCENE_FISHING_POND ||
|
||||
gPlayState->sceneNum == SCENE_BOMBCHU_BOWLING_ALLEY) {
|
||||
if (CVarGetInteger("gAutosave", AUTOSAVE_OFF) == AUTOSAVE_LOCATION_AND_MAJOR_ITEMS ||
|
||||
CVarGetInteger("gAutosave", AUTOSAVE_OFF) == AUTOSAVE_LOCATION_AND_ALL_ITEMS ||
|
||||
CVarGetInteger("gAutosave", AUTOSAVE_OFF) == AUTOSAVE_LOCATION) {
|
||||
performSave = false;
|
||||
return;
|
||||
}
|
||||
if (performSave) {
|
||||
performSave = false;
|
||||
performDelayedSave = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (performSave || performDelayedSave) {
|
||||
if (performSave) {
|
||||
Play_PerformSave(gPlayState);
|
||||
performSave = false;
|
||||
performDelayedSave = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1045,8 +1033,16 @@ void RegisterRandomizedEnemySizes() {
|
||||
Player* player = GET_PLAYER(gPlayState);
|
||||
Actor* actor = static_cast<Actor*>(refActor);
|
||||
|
||||
// Only apply to enemies and bosses. Exclude the wobbly platforms in Jabu because they need to act like platforms.
|
||||
if (!CVarGetInteger("gRandomizedEnemySizes", 0) || (actor->category != ACTORCAT_ENEMY && actor->category != ACTORCAT_BOSS) || actor->id == ACTOR_EN_BROB) {
|
||||
// Exclude wobbly platforms in Jabu because they need to act like platforms.
|
||||
// Exclude Dead Hand hands and Bongo Bongo main body because they make the fights (near) impossible.
|
||||
uint8_t excludedEnemy = actor->id == ACTOR_EN_BROB || actor->id == ACTOR_EN_DHA || (actor->id == ACTOR_BOSS_SST && actor->params == -1);
|
||||
|
||||
// Dodongo, Volvagia and Dead Hand are always smaller because they're impossible when bigger.
|
||||
uint8_t smallOnlyEnemy = actor->id == ACTOR_BOSS_DODONGO || actor->id == ACTOR_BOSS_FD ||
|
||||
actor->id == ACTOR_BOSS_FD2 || actor->id == ACTOR_EN_DH;
|
||||
|
||||
// Only apply to enemies and bosses.
|
||||
if (!CVarGetInteger("gRandomizedEnemySizes", 0) || (actor->category != ACTORCAT_ENEMY && actor->category != ACTORCAT_BOSS) || excludedEnemy) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1055,9 +1051,8 @@ void RegisterRandomizedEnemySizes() {
|
||||
|
||||
uint8_t bigActor = rand() % 2;
|
||||
|
||||
// Big actor. Dodongo and Volvagia are always smaller because they're impossible when bigger.
|
||||
if (bigActor && actor->id != ACTOR_BOSS_DODONGO && actor->id != ACTOR_BOSS_FD &&
|
||||
actor->id != ACTOR_BOSS_FD2) {
|
||||
// Big actor
|
||||
if (bigActor && !smallOnlyEnemy) {
|
||||
randomNumber = rand() % 200;
|
||||
// Between 100% and 300% size.
|
||||
randomScale = 1.0f + (randomNumber / 100);
|
||||
|
||||
@@ -217,6 +217,31 @@ const std::vector<const char*> enhancementsCvars = {
|
||||
"gFixTexturesOOB",
|
||||
"gIvanCoopModeEnabled",
|
||||
"gEnemySpawnsOverWaterboxes",
|
||||
"gTreeStickDrops",
|
||||
"gShadowTag",
|
||||
"gRandomizedEnemySizes",
|
||||
"gRandomizedEnemies",
|
||||
"gMirroredWorldMode",
|
||||
"gMirroredWorld",
|
||||
"gHyperEnemies",
|
||||
"gHookshotableReticle",
|
||||
"gHideBunnyHood",
|
||||
"gFixVineFall",
|
||||
"gFileSelectMoreInfo",
|
||||
"gEnemyHealthBar",
|
||||
"gBushDropFix",
|
||||
"gAllDogsRichard",
|
||||
"gAddTraps.enabled",
|
||||
"gAddTraps.Ammo",
|
||||
"gAddTraps.Bomb",
|
||||
"gAddTraps.Burn",
|
||||
"gAddTraps.Ice",
|
||||
"gAddTraps.Kill",
|
||||
"gAddTraps.Knock",
|
||||
"gAddTraps.Shock",
|
||||
"gAddTraps.Speed",
|
||||
"gAddTraps.Tele",
|
||||
"gAddTraps.Void",
|
||||
};
|
||||
|
||||
const std::vector<const char*> cheatCvars = {
|
||||
@@ -269,7 +294,23 @@ const std::vector<const char*> cheatCvars = {
|
||||
"gNoRedeadFreeze",
|
||||
"gBombTimerMultiplier",
|
||||
"gNoFishDespawn",
|
||||
"gNoBugsDespawn"
|
||||
"gNoBugsDespawn",
|
||||
"gWalkModifierDoesntChangeJump",
|
||||
"gStatsEnabled",
|
||||
"gSaveStatesEnabled",
|
||||
"gSaveStatePromise",
|
||||
"gRegEditEnabled",
|
||||
"gPreset0",
|
||||
"gPreset1",
|
||||
"gDekuStickCheat",
|
||||
"gDebugWarpScreenTranslation",
|
||||
"gDebugSaveFileMode",
|
||||
"gCosmetics.Link_BodyScale.Changed",
|
||||
"gCosmetics.Link_BodyScale.Value",
|
||||
"gCosmetics.Link_HeadScale.Changed",
|
||||
"gCosmetics.Link_HeadScale.Value",
|
||||
"gCosmetics.Link_SwordScale.Changed",
|
||||
"gCosmetics.Link_SwordScale.Value",
|
||||
};
|
||||
|
||||
const std::vector<const char*> randomizerCvars = {
|
||||
@@ -399,6 +440,15 @@ const std::vector<const char*> randomizerCvars = {
|
||||
"gRandomizeGregHint",
|
||||
"gRandoManualSeedEntry",
|
||||
"gRandomizerSettingsEnabled",
|
||||
"gRandomizeTriforceHuntTotalPieces",
|
||||
"gRandomizeTriforceHuntRequiredPieces",
|
||||
"gRandomizeTriforceHunt",
|
||||
"gRandomizeShuffleMasterSword",
|
||||
"gRandomizeSariaHint",
|
||||
"gRandomizeRupeeNames",
|
||||
"gRandomizeFrogsHint",
|
||||
"gRandoRelevantNavi",
|
||||
"gRandoQuestItemFanfares",
|
||||
};
|
||||
|
||||
const std::vector<PresetEntry> vanillaPlusPresetEntries = {
|
||||
|
||||
@@ -24,11 +24,11 @@ void LocationTable_Init() {
|
||||
|
||||
//Lost Woods
|
||||
locationTable[LW_NEAR_SHORTCUTS_GROTTO_CHEST] = ItemLocation::Chest (RC_LW_NEAR_SHORTCUTS_GROTTO_CHEST, 0x3E, 0x14, "LW Near Shortcuts Grotto Chest", LW_NEAR_SHORTCUTS_GROTTO_CHEST, BLUE_RUPEE, {}, SpoilerCollectionCheckGroup::GROUP_LOST_WOODS);
|
||||
locationTable[LW_SKULL_KID] = ItemLocation::Base (RC_LW_SKULL_KID, 0x5B, "LW Skull Kid", LW_SKULL_KID, PIECE_OF_HEART, {}, SpoilerCollectionCheck::ItemGetInf(30), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS);
|
||||
locationTable[LW_SKULL_KID] = ItemLocation::Base (RC_LW_SKULL_KID, 0x5B, "LW Skull Kid", LW_SKULL_KID, PIECE_OF_HEART, {}, SpoilerCollectionCheck::ItemGetInf(22), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS);
|
||||
locationTable[LW_TRADE_COJIRO] = ItemLocation::Base (RC_LW_TRADE_COJIRO, 0x5B, "LW Trade Cojiro", LW_TRADE_COJIRO, ODD_MUSHROOM, {Category::cAdultTrade}, SpoilerCollectionCheck::RandomizerInf(), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS);
|
||||
locationTable[LW_TRADE_ODD_POTION] = ItemLocation::Base (RC_LW_TRADE_ODD_POTION, 0x5B, "LW Trade Odd Potion", LW_TRADE_ODD_POTION, ODD_POTION, {Category::cAdultTrade}, SpoilerCollectionCheck::ItemGetInf(57), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS);
|
||||
locationTable[LW_OCARINA_MEMORY_GAME] = ItemLocation::Base (RC_LW_OCARINA_MEMORY_GAME, 0x5B, "LW Ocarina Memory Game", LW_OCARINA_MEMORY_GAME, PIECE_OF_HEART, {}, SpoilerCollectionCheck::ItemGetInf(31), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS);
|
||||
locationTable[LW_TARGET_IN_WOODS] = ItemLocation::Base (RC_LW_TARGET_IN_WOODS, 0x5B, "LW Target in Woods", LW_TARGET_IN_WOODS, PROGRESSIVE_SLINGSHOT, {}, SpoilerCollectionCheck::ItemGetInf(21), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS);
|
||||
locationTable[LW_TRADE_ODD_POTION] = ItemLocation::Base (RC_LW_TRADE_ODD_POTION, 0x5B, "LW Trade Odd Potion", LW_TRADE_ODD_POTION, ODD_POTION, {Category::cAdultTrade}, SpoilerCollectionCheck::ItemGetInf(49), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS);
|
||||
locationTable[LW_OCARINA_MEMORY_GAME] = ItemLocation::Base (RC_LW_OCARINA_MEMORY_GAME, 0x5B, "LW Ocarina Memory Game", LW_OCARINA_MEMORY_GAME, PIECE_OF_HEART, {}, SpoilerCollectionCheck::ItemGetInf(23), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS);
|
||||
locationTable[LW_TARGET_IN_WOODS] = ItemLocation::Base (RC_LW_TARGET_IN_WOODS, 0x5B, "LW Target in Woods", LW_TARGET_IN_WOODS, PROGRESSIVE_SLINGSHOT, {}, SpoilerCollectionCheck::ItemGetInf(29), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS);
|
||||
locationTable[LW_DEKU_SCRUB_NEAR_DEKU_THEATER_RIGHT] = ItemLocation::Base (RC_LW_DEKU_SCRUB_NEAR_DEKU_THEATER_RIGHT, 0x5B, "LW Deku Scrub Near Deku Theater Right",LW_DEKU_SCRUB_NEAR_DEKU_THEATER_RIGHT, BUY_DEKU_NUT_5, {Category::cDekuScrub}, SpoilerCollectionCheck::Scrub(), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS);
|
||||
locationTable[LW_DEKU_SCRUB_NEAR_DEKU_THEATER_LEFT] = ItemLocation::Base (RC_LW_DEKU_SCRUB_NEAR_DEKU_THEATER_LEFT, 0x5B, "LW Deku Scrub Near Deku Theater Left", LW_DEKU_SCRUB_NEAR_DEKU_THEATER_LEFT, BUY_DEKU_STICK_1, {Category::cDekuScrub}, SpoilerCollectionCheck::Scrub(), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS);
|
||||
locationTable[LW_DEKU_SCRUB_NEAR_BRIDGE] = ItemLocation::Base (RC_LW_DEKU_SCRUB_NEAR_BRIDGE, 0x5B, "LW Deku Scrub Near Bridge", LW_DEKU_SCRUB_NEAR_BRIDGE, PROGRESSIVE_STICK_UPGRADE, {Category::cDekuScrub, Category::cDekuScrubUpgrades}, SpoilerCollectionCheck::Scrub(), SpoilerCollectionCheckGroup::GROUP_LOST_WOODS);
|
||||
@@ -53,7 +53,7 @@ void LocationTable_Init() {
|
||||
//Lake Hylia
|
||||
locationTable[LH_CHILD_FISHING] = ItemLocation::Base (RC_LH_CHILD_FISHING, 0x49, "LH Child Fishing", LH_CHILD_FISHING, PIECE_OF_HEART, {}, SpoilerCollectionCheck::RandomizerInf(), SpoilerCollectionCheckGroup::GROUP_LAKE_HYLIA);
|
||||
locationTable[LH_ADULT_FISHING] = ItemLocation::Base (RC_LH_ADULT_FISHING, 0x49, "LH Adult Fishing", LH_ADULT_FISHING, PROGRESSIVE_SCALE, {}, SpoilerCollectionCheck::RandomizerInf(), SpoilerCollectionCheckGroup::GROUP_LAKE_HYLIA);
|
||||
locationTable[LH_LAB_DIVE] = ItemLocation::Base (RC_LH_LAB_DIVE, 0x38, "LH Lab Dive", LH_LAB_DIVE, PIECE_OF_HEART, {}, SpoilerCollectionCheck::ItemGetInf(24), SpoilerCollectionCheckGroup::GROUP_LAKE_HYLIA);
|
||||
locationTable[LH_LAB_DIVE] = ItemLocation::Base (RC_LH_LAB_DIVE, 0x38, "LH Lab Dive", LH_LAB_DIVE, PIECE_OF_HEART, {}, SpoilerCollectionCheck::ItemGetInf(16), SpoilerCollectionCheckGroup::GROUP_LAKE_HYLIA);
|
||||
locationTable[LH_TRADE_FROG] = ItemLocation::Base (RC_LH_TRADE_FROG, 0x38, "LH Lab Trade Eyeball Frog", LH_TRADE_FROG, EYEDROPS, {Category::cAdultTrade}, SpoilerCollectionCheck::RandomizerInf(), SpoilerCollectionCheckGroup::GROUP_LAKE_HYLIA);
|
||||
locationTable[LH_UNDERWATER_ITEM] = ItemLocation::Base (RC_LH_UNDERWATER_ITEM, 0x57, "LH Underwater Item", LH_UNDERWATER_ITEM, RUTOS_LETTER, {}, SpoilerCollectionCheck::EventChkInf(0x31), SpoilerCollectionCheckGroup::GROUP_LAKE_HYLIA);
|
||||
locationTable[LH_SUN] = ItemLocation::Base (RC_LH_SUN, 0x57, "LH Sun", LH_SUN, FIRE_ARROWS, {}, SpoilerCollectionCheck::Chest(0x57, 0x1F), SpoilerCollectionCheckGroup::GROUP_LAKE_HYLIA);
|
||||
@@ -73,7 +73,7 @@ void LocationTable_Init() {
|
||||
//Gerudo Fortress
|
||||
locationTable[GF_CHEST] = ItemLocation::Chest (RC_GF_CHEST, 0x5D, 0x00, "GF Chest", GF_CHEST, PIECE_OF_HEART, {}, SpoilerCollectionCheckGroup::GROUP_GERUDO_VALLEY);
|
||||
locationTable[GF_HBA_1000_POINTS] = ItemLocation::Base (RC_GF_HBA_1000_POINTS, 0x5D, "GF HBA 1000 Points", GF_HBA_1000_POINTS, PIECE_OF_HEART, {}, SpoilerCollectionCheck::InfTable(0x19, 0x08), SpoilerCollectionCheckGroup::GROUP_GERUDO_VALLEY);
|
||||
locationTable[GF_HBA_1500_POINTS] = ItemLocation::Base (RC_GF_HBA_1500_POINTS, 0x5D, "GF HBA 1500 Points", GF_HBA_1500_POINTS, PROGRESSIVE_BOW, {}, SpoilerCollectionCheck::ItemGetInf(7), SpoilerCollectionCheckGroup::GROUP_GERUDO_VALLEY);
|
||||
locationTable[GF_HBA_1500_POINTS] = ItemLocation::Base (RC_GF_HBA_1500_POINTS, 0x5D, "GF HBA 1500 Points", GF_HBA_1500_POINTS, PROGRESSIVE_BOW, {}, SpoilerCollectionCheck::ItemGetInf(15), SpoilerCollectionCheckGroup::GROUP_GERUDO_VALLEY);
|
||||
locationTable[GF_GERUDO_MEMBERSHIP_CARD] = ItemLocation::Base (RC_GF_GERUDO_MEMBERSHIP_CARD, 0x0C, "GF Gerudo Membership Card", GF_GERUDO_MEMBERSHIP_CARD, GERUDO_MEMBERSHIP_CARD, {}, SpoilerCollectionCheck::GerudoToken(), SpoilerCollectionCheckGroup::GROUP_GERUDO_VALLEY);
|
||||
locationTable[GF_NORTH_F1_CARPENTER] = ItemLocation::Collectable(RC_GF_NORTH_F1_CARPENTER, 0x0C, 0x0C, "GF North F1 Carpenter", GF_NORTH_F1_CARPENTER, GERUDO_FORTRESS_SMALL_KEY, {Category::cVanillaGFSmallKey}, SpoilerCollectionCheckGroup::GROUP_GERUDO_VALLEY);
|
||||
locationTable[GF_NORTH_F2_CARPENTER] = ItemLocation::Collectable(RC_GF_NORTH_F2_CARPENTER, 0x0C, 0x0A, "GF North F2 Carpenter", GF_NORTH_F2_CARPENTER, GERUDO_FORTRESS_SMALL_KEY, {Category::cVanillaGFSmallKey}, SpoilerCollectionCheckGroup::GROUP_GERUDO_VALLEY);
|
||||
@@ -90,12 +90,12 @@ void LocationTable_Init() {
|
||||
locationTable[COLOSSUS_DEKU_SCRUB_GROTTO_FRONT] = ItemLocation::GrottoScrub(RC_COLOSSUS_DEKU_SCRUB_GROTTO_FRONT, 0xFD, "Colossus Deku Scrub Grotto Front", COLOSSUS_DEKU_SCRUB_GROTTO_FRONT, BUY_GREEN_POTION, {Category::cDekuScrub}, SpoilerCollectionCheck::Scrub(), SpoilerCollectionCheckGroup::GROUP_GERUDO_VALLEY);
|
||||
|
||||
//Market
|
||||
locationTable[MARKET_TREASURE_CHEST_GAME_REWARD] = ItemLocation::Chest (RC_MARKET_TREASURE_CHEST_GAME_REWARD, 0x10, "MK Treasure Chest Game Reward", MARKET_TREASURE_CHEST_GAME_REWARD, TREASURE_GAME_HEART, {}, SpoilerCollectionCheck::ItemGetInf(19), SpoilerCollectionCheckGroup::GROUP_HYRULE_CASTLE);
|
||||
locationTable[MARKET_BOMBCHU_BOWLING_FIRST_PRIZE] = ItemLocation::Base (RC_MARKET_BOMBCHU_BOWLING_FIRST_PRIZE, 0x4B, "MK Bombchu Bowling First Prize", MARKET_BOMBCHU_BOWLING_FIRST_PRIZE, PROGRESSIVE_BOMB_BAG, {}, SpoilerCollectionCheck::ItemGetInf(25), SpoilerCollectionCheckGroup::GROUP_HYRULE_CASTLE);
|
||||
locationTable[MARKET_BOMBCHU_BOWLING_SECOND_PRIZE] = ItemLocation::Base (RC_MARKET_BOMBCHU_BOWLING_SECOND_PRIZE, 0x4B, "MK Bombchu Bowling Second Prize", MARKET_BOMBCHU_BOWLING_SECOND_PRIZE, PIECE_OF_HEART, {}, SpoilerCollectionCheck::ItemGetInf(26), SpoilerCollectionCheckGroup::GROUP_HYRULE_CASTLE);
|
||||
locationTable[MARKET_TREASURE_CHEST_GAME_REWARD] = ItemLocation::Chest (RC_MARKET_TREASURE_CHEST_GAME_REWARD, 0x10, "MK Treasure Chest Game Reward", MARKET_TREASURE_CHEST_GAME_REWARD, TREASURE_GAME_HEART, {}, SpoilerCollectionCheck::ItemGetInf(27), SpoilerCollectionCheckGroup::GROUP_HYRULE_CASTLE);
|
||||
locationTable[MARKET_BOMBCHU_BOWLING_FIRST_PRIZE] = ItemLocation::Base (RC_MARKET_BOMBCHU_BOWLING_FIRST_PRIZE, 0x4B, "MK Bombchu Bowling First Prize", MARKET_BOMBCHU_BOWLING_FIRST_PRIZE, PROGRESSIVE_BOMB_BAG, {}, SpoilerCollectionCheck::ItemGetInf(17), SpoilerCollectionCheckGroup::GROUP_HYRULE_CASTLE);
|
||||
locationTable[MARKET_BOMBCHU_BOWLING_SECOND_PRIZE] = ItemLocation::Base (RC_MARKET_BOMBCHU_BOWLING_SECOND_PRIZE, 0x4B, "MK Bombchu Bowling Second Prize", MARKET_BOMBCHU_BOWLING_SECOND_PRIZE, PIECE_OF_HEART, {}, SpoilerCollectionCheck::ItemGetInf(18), SpoilerCollectionCheckGroup::GROUP_HYRULE_CASTLE);
|
||||
locationTable[MARKET_BOMBCHU_BOWLING_BOMBCHUS] = ItemLocation::Base (RC_MARKET_BOMBCHU_BOWLING_BOMBCHUS, 0x4B, "MK Bombchu Bowling Bombchus", NONE, BOMBCHU_DROP, {}, SpoilerCollectionCheck::None(), SpoilerCollectionCheckGroup::GROUP_HYRULE_CASTLE);
|
||||
locationTable[MARKET_LOST_DOG] = ItemLocation::Base (RC_MARKET_LOST_DOG, 0x35, "MK Lost Dog", MARKET_LOST_DOG, PIECE_OF_HEART, {}, SpoilerCollectionCheck::InfTable(0x19, 0x09), SpoilerCollectionCheckGroup::GROUP_HYRULE_CASTLE);
|
||||
locationTable[MARKET_SHOOTING_GALLERY_REWARD] = ItemLocation::Base (RC_MARKET_SHOOTING_GALLERY_REWARD, 0x42, "MK Shooting Gallery", MARKET_SHOOTING_GALLERY_REWARD, PROGRESSIVE_SLINGSHOT, {}, SpoilerCollectionCheck::ItemGetInf(5), SpoilerCollectionCheckGroup::GROUP_HYRULE_CASTLE);
|
||||
locationTable[MARKET_SHOOTING_GALLERY_REWARD] = ItemLocation::Base (RC_MARKET_SHOOTING_GALLERY_REWARD, 0x42, "MK Shooting Gallery", MARKET_SHOOTING_GALLERY_REWARD, PROGRESSIVE_SLINGSHOT, {}, SpoilerCollectionCheck::ItemGetInf(13), SpoilerCollectionCheckGroup::GROUP_HYRULE_CASTLE);
|
||||
locationTable[MARKET_10_BIG_POES] = ItemLocation::Base (RC_MARKET_10_BIG_POES, 0x4D, "MK 10 Big Poes", MARKET_10_BIG_POES, EMPTY_BOTTLE, {}, SpoilerCollectionCheck::RandomizerInf(), SpoilerCollectionCheckGroup::GROUP_HYRULE_CASTLE);
|
||||
locationTable[MARKET_TREASURE_CHEST_GAME_ITEM_1] = ItemLocation::Chest (RC_MARKET_TREASURE_CHEST_GAME_ITEM_1, 0x10, 0x01, "MK Chest Game First Room Chest", MARKET_TREASURE_CHEST_GAME_ITEM_1, TREASURE_GAME_SMALL_KEY, {Category::cChestMinigame}, SpoilerCollectionCheckGroup::GROUP_HYRULE_CASTLE);
|
||||
locationTable[MARKET_TREASURE_CHEST_GAME_ITEM_2] = ItemLocation::Chest (RC_MARKET_TREASURE_CHEST_GAME_ITEM_2, 0x10, 0x03, "MK Chest Game Second Room Chest", MARKET_TREASURE_CHEST_GAME_ITEM_2, TREASURE_GAME_SMALL_KEY, {Category::cChestMinigame}, SpoilerCollectionCheckGroup::GROUP_HYRULE_CASTLE);
|
||||
@@ -115,14 +115,14 @@ void LocationTable_Init() {
|
||||
locationTable[KAK_30_GOLD_SKULLTULA_REWARD] = ItemLocation::Base (RC_KAK_30_GOLD_SKULLTULA_REWARD, 0x50, "Kak 30 Gold Skulltula Reward", KAK_30_GOLD_SKULLTULA_REWARD, PROGRESSIVE_WALLET, {}, SpoilerCollectionCheck::EventChkInf(0xDC), SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
|
||||
locationTable[KAK_40_GOLD_SKULLTULA_REWARD] = ItemLocation::Base (RC_KAK_40_GOLD_SKULLTULA_REWARD, 0x50, "Kak 40 Gold Skulltula Reward", KAK_40_GOLD_SKULLTULA_REWARD, BOMBCHU_10, {}, SpoilerCollectionCheck::EventChkInf(0xDD), SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
|
||||
locationTable[KAK_50_GOLD_SKULLTULA_REWARD] = ItemLocation::Base (RC_KAK_50_GOLD_SKULLTULA_REWARD, 0x50, "Kak 50 Gold Skulltula Reward", KAK_50_GOLD_SKULLTULA_REWARD, PIECE_OF_HEART, {}, SpoilerCollectionCheck::EventChkInf(0xDE), SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
|
||||
locationTable[KAK_100_GOLD_SKULLTULA_REWARD] = ItemLocation::Base (RC_KAK_100_GOLD_SKULLTULA_REWARD, 0x50, "Kak 100 Gold Skulltula Reward", KAK_100_GOLD_SKULLTULA_REWARD, HUGE_RUPEE, {}, SpoilerCollectionCheck::EventChkInf(0xDF), SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
|
||||
locationTable[KAK_MAN_ON_ROOF] = ItemLocation::Base (RC_KAK_MAN_ON_ROOF, 0x52, "Kak Man on Roof", KAK_MAN_ON_ROOF, PIECE_OF_HEART, {}, SpoilerCollectionCheck::ItemGetInf(29), SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
|
||||
locationTable[KAK_100_GOLD_SKULLTULA_REWARD] = ItemLocation::Base (RC_KAK_100_GOLD_SKULLTULA_REWARD, 0x50, "Kak 100 Gold Skulltula Reward", KAK_100_GOLD_SKULLTULA_REWARD, HUGE_RUPEE, {}, SpoilerCollectionCheck::RandomizerInf(), SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
|
||||
locationTable[KAK_MAN_ON_ROOF] = ItemLocation::Base (RC_KAK_MAN_ON_ROOF, 0x52, "Kak Man on Roof", KAK_MAN_ON_ROOF, PIECE_OF_HEART, {}, SpoilerCollectionCheck::ItemGetInf(21), SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
|
||||
locationTable[KAK_SHOOTING_GALLERY_REWARD] = ItemLocation::Base (RC_KAK_SHOOTING_GALLERY_REWARD, 0x42, "Kak Shooting Gallery Reward", KAK_SHOOTING_GALLERY_REWARD, PROGRESSIVE_BOW, {}, SpoilerCollectionCheck::Chest(0x42, 0x1F), SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
|
||||
locationTable[KAK_TRADE_ODD_MUSHROOM] = ItemLocation::Base (RC_KAK_TRADE_ODD_MUSHROOM, 0x4E, "Kak Trade Odd Mushroom", KAK_TRADE_ODD_MUSHROOM, ODD_POTION, {Category::cAdultTrade}, SpoilerCollectionCheck::ItemGetInf(56), SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
|
||||
locationTable[KAK_TRADE_ODD_MUSHROOM] = ItemLocation::Base (RC_KAK_TRADE_ODD_MUSHROOM, 0x4E, "Kak Trade Odd Mushroom", KAK_TRADE_ODD_MUSHROOM, ODD_POTION, {Category::cAdultTrade}, SpoilerCollectionCheck::ItemGetInf(48), SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
|
||||
locationTable[KAK_GRANNYS_SHOP] = ItemLocation::Base (RC_KAK_GRANNYS_SHOP, 0x4E, "Kak Granny's Shop", KAK_GRANNYS_SHOP, BLUE_POTION_REFILL, {Category::cMerchant}, SpoilerCollectionCheck::RandomizerInf(), SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
|
||||
locationTable[KAK_ANJU_AS_ADULT] = ItemLocation::Base (RC_KAK_ANJU_AS_ADULT, 0x52, "Kak Anju as Adult", KAK_ANJU_AS_ADULT, CLAIM_CHECK, {}, SpoilerCollectionCheck::ItemGetInf(36), SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
|
||||
locationTable[KAK_ANJU_AS_CHILD] = ItemLocation::Base (RC_KAK_ANJU_AS_CHILD, 0x52, "Kak Anju as Child", KAK_ANJU_AS_CHILD, EMPTY_BOTTLE, {}, SpoilerCollectionCheck::ItemGetInf(4), SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
|
||||
locationTable[KAK_TRADE_POCKET_CUCCO] = ItemLocation::Base (RC_KAK_TRADE_POCKET_CUCCO, 0x52, "Kak Trade Pocket Cucco", KAK_TRADE_POCKET_CUCCO, COJIRO, {Category::cAdultTrade}, SpoilerCollectionCheck::ItemGetInf(38), SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
|
||||
locationTable[KAK_ANJU_AS_ADULT] = ItemLocation::Base (RC_KAK_ANJU_AS_ADULT, 0x52, "Kak Anju as Adult", KAK_ANJU_AS_ADULT, CLAIM_CHECK, {}, SpoilerCollectionCheck::ItemGetInf(44), SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
|
||||
locationTable[KAK_ANJU_AS_CHILD] = ItemLocation::Base (RC_KAK_ANJU_AS_CHILD, 0x52, "Kak Anju as Child", KAK_ANJU_AS_CHILD, EMPTY_BOTTLE, {}, SpoilerCollectionCheck::ItemGetInf(12), SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
|
||||
locationTable[KAK_TRADE_POCKET_CUCCO] = ItemLocation::Base (RC_KAK_TRADE_POCKET_CUCCO, 0x52, "Kak Trade Pocket Cucco", KAK_TRADE_POCKET_CUCCO, COJIRO, {Category::cAdultTrade}, SpoilerCollectionCheck::ItemGetInf(46), SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
|
||||
locationTable[KAK_IMPAS_HOUSE_FREESTANDING_POH] = ItemLocation::Collectable(RC_KAK_IMPAS_HOUSE_FREESTANDING_POH, 0x37, 0x01, "Kak Impas House Freestanding PoH", KAK_IMPAS_HOUSE_FREESTANDING_POH, PIECE_OF_HEART, {}, SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
|
||||
locationTable[KAK_WINDMILL_FREESTANDING_POH] = ItemLocation::Collectable(RC_KAK_WINDMILL_FREESTANDING_POH, 0x48, 0x01, "Kak Windmill Freestanding PoH", KAK_WINDMILL_FREESTANDING_POH, PIECE_OF_HEART, {}, SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
|
||||
|
||||
@@ -133,7 +133,7 @@ void LocationTable_Init() {
|
||||
locationTable[GRAVEYARD_HOOKSHOT_CHEST] = ItemLocation::Chest (RC_GRAVEYARD_HOOKSHOT_CHEST, 0x48, 0x00, "GY Hookshot Chest", GRAVEYARD_HOOKSHOT_CHEST, PROGRESSIVE_HOOKSHOT, {}, SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
|
||||
locationTable[GRAVEYARD_DAMPE_RACE_FREESTANDING_POH] = ItemLocation::Collectable(RC_GRAVEYARD_DAMPE_RACE_FREESTANDING_POH, 0x48, 0x07, "GY Dampe Race Freestanding PoH", GRAVEYARD_DAMPE_RACE_FREESTANDING_POH, PIECE_OF_HEART, {}, SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
|
||||
locationTable[GRAVEYARD_FREESTANDING_POH] = ItemLocation::Collectable(RC_GRAVEYARD_FREESTANDING_POH, 0x53, 0x04, "GY Freestanding PoH", GRAVEYARD_FREESTANDING_POH, PIECE_OF_HEART, {}, SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
|
||||
locationTable[GRAVEYARD_DAMPE_GRAVEDIGGING_TOUR] = ItemLocation::Collectable(RC_GRAVEYARD_DAMPE_GRAVEDIGGING_TOUR, 0x53, "GY Dampe Gravedigging Tour", GRAVEYARD_DAMPE_GRAVEDIGGING_TOUR, PIECE_OF_HEART, {}, SpoilerCollectionCheck::Gravedigger(0x53, 0x1F), SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
|
||||
locationTable[GRAVEYARD_DAMPE_GRAVEDIGGING_TOUR] = ItemLocation::Collectable(RC_GRAVEYARD_DAMPE_GRAVEDIGGING_TOUR, 0x53, "GY Dampe Gravedigging Tour", GRAVEYARD_DAMPE_GRAVEDIGGING_TOUR, PIECE_OF_HEART, {}, SpoilerCollectionCheck::Gravedigger(0x53, 0x19), SpoilerCollectionCheckGroup::GROUP_KAKARIKO);
|
||||
|
||||
//Death Mountain
|
||||
locationTable[DMT_CHEST] = ItemLocation::Chest (RC_DMT_CHEST, 0x60, 0x01, "DMT Chest", DMT_CHEST, PURPLE_RUPEE, {}, SpoilerCollectionCheckGroup::GROUP_DEATH_MOUNTAIN);
|
||||
@@ -191,7 +191,7 @@ void LocationTable_Init() {
|
||||
locationTable[ZF_BOTTOM_FREESTANDING_POH] = ItemLocation::Collectable(RC_ZF_BOTTOM_FREESTANDING_POH, 0x59, 0x14, "ZF Bottom Freestanding PoH", ZF_BOTTOM_FREESTANDING_POH, PIECE_OF_HEART, {}, SpoilerCollectionCheckGroup::GROUP_ZORAS_DOMAIN);
|
||||
|
||||
//Lon Lon Ranch
|
||||
locationTable[LLR_TALONS_CHICKENS] = ItemLocation::Base (RC_LLR_TALONS_CHICKENS, 0x4C, "LLR Talons Chickens", LLR_TALONS_CHICKENS, BOTTLE_WITH_MILK, {}, SpoilerCollectionCheck::ItemGetInf(10), SpoilerCollectionCheckGroup::GROUP_LON_LON_RANCH);
|
||||
locationTable[LLR_TALONS_CHICKENS] = ItemLocation::Base (RC_LLR_TALONS_CHICKENS, 0x4C, "LLR Talons Chickens", LLR_TALONS_CHICKENS, BOTTLE_WITH_MILK, {}, SpoilerCollectionCheck::ItemGetInf(2), SpoilerCollectionCheckGroup::GROUP_LON_LON_RANCH);
|
||||
locationTable[LLR_FREESTANDING_POH] = ItemLocation::Collectable(RC_LLR_FREESTANDING_POH, 0x4C, 0x01, "LLR Freestanding PoH", LLR_FREESTANDING_POH, PIECE_OF_HEART, {}, SpoilerCollectionCheckGroup::GROUP_LON_LON_RANCH);
|
||||
locationTable[LLR_DEKU_SCRUB_GROTTO_LEFT] = ItemLocation::GrottoScrub(RC_LLR_DEKU_SCRUB_GROTTO_LEFT, 0xFC, "LLR Deku Scrub Grotto Left", LLR_DEKU_SCRUB_GROTTO_LEFT, BUY_DEKU_NUT_5, {Category::cDekuScrub}, SpoilerCollectionCheck::Scrub(), SpoilerCollectionCheckGroup::GROUP_LON_LON_RANCH);
|
||||
locationTable[LLR_DEKU_SCRUB_GROTTO_RIGHT] = ItemLocation::GrottoScrub(RC_LLR_DEKU_SCRUB_GROTTO_RIGHT, 0xFC, "LLR Deku Scrub Grotto Right", LLR_DEKU_SCRUB_GROTTO_RIGHT, BUY_BOMBS_535, {Category::cDekuScrub}, SpoilerCollectionCheck::Scrub(), SpoilerCollectionCheckGroup::GROUP_LON_LON_RANCH);
|
||||
|
||||
@@ -104,7 +104,7 @@ void AreaTable_Init_DeathMountain() {
|
||||
Entrance(DEATH_MOUNTAIN_TRAIL, {[]{return true;}}),
|
||||
Entrance(GC_WOODS_WARP, {[]{return GCWoodsWarpOpen;}}),
|
||||
Entrance(GC_SHOP, {[]{return (IsAdult && StopGCRollingGoronAsAdult) || (IsChild && (CanBlastOrSmash || GoronBracelet || GoronCityChildFire || CanUse(BOW)));}}),
|
||||
Entrance(GC_DARUNIAS_CHAMBER, {[]{return (IsAdult && StopGCRollingGoronAsAdult) || GCDaruniasDoorOpenChild;}}),
|
||||
Entrance(GC_DARUNIAS_CHAMBER, {[]{return (IsAdult && StopGCRollingGoronAsAdult) || (IsChild && GCDaruniasDoorOpenChild);}}),
|
||||
Entrance(GC_GROTTO_PLATFORM, {[]{return IsAdult && ((CanPlay(SongOfTime) && ((EffectiveHealth > 2) || CanUse(GORON_TUNIC) || CanUse(LONGSHOT) || CanUse(NAYRUS_LOVE))) || (EffectiveHealth > 1 && CanUse(GORON_TUNIC) && CanUse(HOOKSHOT)) || (CanUse(NAYRUS_LOVE) && CanUse(HOOKSHOT)) || (EffectiveHealth > 2 && CanUse(HOOKSHOT) && LogicGoronCityGrotto));}}),
|
||||
});
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "playthrough.hpp"
|
||||
|
||||
#include <libultraship/libultraship.h>
|
||||
#include <boost_custom/container_hash/hash_32.hpp>
|
||||
#include "custom_messages.hpp"
|
||||
#include "fill.hpp"
|
||||
@@ -8,6 +9,7 @@
|
||||
#include "random.hpp"
|
||||
#include "spoiler_log.hpp"
|
||||
#include "soh/Enhancements/randomizer/randomizerTypes.h"
|
||||
#include "variables.h"
|
||||
|
||||
namespace Playthrough {
|
||||
|
||||
@@ -39,6 +41,10 @@ int Playthrough_Init(uint32_t seed, std::unordered_map<RandomizerSettingKey, uin
|
||||
}
|
||||
}
|
||||
|
||||
if (CVarGetInteger("gRandomizerDontGenerateSpoiler", 0)) {
|
||||
settingsStr += (char*)gBuildVersion;
|
||||
}
|
||||
|
||||
uint32_t finalHash = boost::hash_32<std::string>{}(std::to_string(Settings::seed) + settingsStr);
|
||||
Random_Init(finalHash);
|
||||
Settings::hash = std::to_string(finalHash);
|
||||
|
||||
@@ -360,29 +360,12 @@ std::unordered_map<std::string, RandomizerSettingKey> SpoilerfileSettingNameToEn
|
||||
{ "Shuffle Dungeon Quest:Ganon's Castle", RSK_MQ_GANONS_CASTLE },
|
||||
};
|
||||
|
||||
std::string sanitize(std::string stringValue) {
|
||||
// Add backslashes.
|
||||
for (auto i = stringValue.begin();;) {
|
||||
auto const pos = std::find_if(i, stringValue.end(), [](char const c) { return '\\' == c || '\'' == c || '"' == c; });
|
||||
if (pos == stringValue.end()) {
|
||||
break;
|
||||
}
|
||||
i = std::next(stringValue.insert(pos, '\\'), 2);
|
||||
}
|
||||
|
||||
// Removes others.
|
||||
stringValue.erase(std::remove_if(stringValue.begin(), stringValue.end(), [](char const c) {
|
||||
return '\n' == c || '\r' == c || '\0' == c || '\x1A' == c; }), stringValue.end());
|
||||
|
||||
return stringValue;
|
||||
}
|
||||
|
||||
#pragma optimize("", off)
|
||||
#pragma GCC push_options
|
||||
#pragma GCC optimize ("O0")
|
||||
bool Randomizer::SpoilerFileExists(const char* spoilerFileName) {
|
||||
if (strcmp(spoilerFileName, "") != 0) {
|
||||
std::ifstream spoilerFileStream(sanitize(spoilerFileName));
|
||||
std::ifstream spoilerFileStream(SohUtils::Sanitize(spoilerFileName));
|
||||
if (!spoilerFileStream) {
|
||||
return false;
|
||||
} else {
|
||||
@@ -496,6 +479,13 @@ void Randomizer::LoadHintLocations(const char* spoilerFileName) {
|
||||
"Zu {{location}}?\x1B&%gOK&No%w\x02",
|
||||
"Se téléporter vers&{{location}}?\x1B&%gOK!&Non%w\x02"));
|
||||
|
||||
// Bow Shooting Gallery reminder
|
||||
CustomMessageManager::Instance->CreateMessage(Randomizer::hintMessageTableID, TEXT_SHOOTING_GALLERY_MAN_COME_BACK_WITH_BOW,
|
||||
CustomMessage("Come back when you have your own&bow and you'll get a %rdifferent prize%w!",
|
||||
"Komm wieder sobald du deinen eigenen&Bogen hast, um einen %rspeziellen Preis%w zu&erhalten!",
|
||||
"J'aurai %rune autre récompense%w pour toi&lorsque tu auras ton propre arc."));
|
||||
|
||||
// Lake Hylia water level system
|
||||
CustomMessageManager::Instance->CreateMessage(Randomizer::hintMessageTableID, TEXT_LAKE_HYLIA_WATER_SWITCH_SIGN,
|
||||
CustomMessage("Water level control system.&Keep away!",
|
||||
"Wasserstand Kontrollsystem&Finger weg!",
|
||||
@@ -659,7 +649,7 @@ void Randomizer::LoadMasterQuestDungeons(const char* spoilerFileName) {
|
||||
}
|
||||
|
||||
void Randomizer::ParseRandomizerSettingsFile(const char* spoilerFileName) {
|
||||
std::ifstream spoilerFileStream(sanitize(spoilerFileName));
|
||||
std::ifstream spoilerFileStream(SohUtils::Sanitize(spoilerFileName));
|
||||
if (!spoilerFileStream)
|
||||
return;
|
||||
|
||||
@@ -1293,7 +1283,7 @@ std::string FormatJsonHintText(std::string jsonHint) {
|
||||
}
|
||||
|
||||
void Randomizer::ParseHintLocationsFile(const char* spoilerFileName) {
|
||||
std::ifstream spoilerFileStream(sanitize(spoilerFileName));
|
||||
std::ifstream spoilerFileStream(SohUtils::Sanitize(spoilerFileName));
|
||||
if (!spoilerFileStream)
|
||||
return;
|
||||
|
||||
@@ -1394,7 +1384,7 @@ void Randomizer::ParseHintLocationsFile(const char* spoilerFileName) {
|
||||
}
|
||||
|
||||
void Randomizer::ParseRequiredTrialsFile(const char* spoilerFileName) {
|
||||
std::ifstream spoilerFileStream(sanitize(spoilerFileName));
|
||||
std::ifstream spoilerFileStream(SohUtils::Sanitize(spoilerFileName));
|
||||
if (!spoilerFileStream) {
|
||||
return;
|
||||
}
|
||||
@@ -1415,7 +1405,7 @@ void Randomizer::ParseRequiredTrialsFile(const char* spoilerFileName) {
|
||||
}
|
||||
|
||||
void Randomizer::ParseMasterQuestDungeonsFile(const char* spoilerFileName) {
|
||||
std::ifstream spoilerFileStream(sanitize(spoilerFileName));
|
||||
std::ifstream spoilerFileStream(SohUtils::Sanitize(spoilerFileName));
|
||||
if (!spoilerFileStream) {
|
||||
return;
|
||||
}
|
||||
@@ -1495,7 +1485,7 @@ int16_t Randomizer::GetVanillaMerchantPrice(RandomizerCheck check) {
|
||||
}
|
||||
|
||||
void Randomizer::ParseItemLocationsFile(const char* spoilerFileName, bool silent) {
|
||||
std::ifstream spoilerFileStream(sanitize(spoilerFileName));
|
||||
std::ifstream spoilerFileStream(SohUtils::Sanitize(spoilerFileName));
|
||||
if (!spoilerFileStream)
|
||||
return;
|
||||
|
||||
@@ -1558,7 +1548,7 @@ void Randomizer::ParseItemLocationsFile(const char* spoilerFileName, bool silent
|
||||
}
|
||||
|
||||
void Randomizer::ParseEntranceDataFile(const char* spoilerFileName, bool silent) {
|
||||
std::ifstream spoilerFileStream(sanitize(spoilerFileName));
|
||||
std::ifstream spoilerFileStream(SohUtils::Sanitize(spoilerFileName));
|
||||
if (!spoilerFileStream) {
|
||||
return;
|
||||
}
|
||||
@@ -2547,6 +2537,7 @@ std::map<RandomizerCheck, RandomizerInf> rcToRandomizerInf = {
|
||||
{ RC_LH_CHILD_FISHING, RAND_INF_CHILD_FISHING },
|
||||
{ RC_LH_ADULT_FISHING, RAND_INF_ADULT_FISHING },
|
||||
{ RC_MARKET_10_BIG_POES, RAND_INF_10_BIG_POES },
|
||||
{ RC_KAK_100_GOLD_SKULLTULA_REWARD, RAND_INF_KAK_100_GOLD_SKULLTULA_REWARD },
|
||||
};
|
||||
|
||||
RandomizerCheckObject Randomizer::GetCheckObjectFromActor(s16 actorId, s16 sceneNum, s32 actorParams = 0x00) {
|
||||
@@ -3143,7 +3134,9 @@ void RandomizerSettingsWindow::DrawElement() {
|
||||
UIWidgets::DisableComponent(ImGui::GetStyle().Alpha * 0.5f);
|
||||
}
|
||||
|
||||
ImGui::BeginDisabled(CVarGetInteger("gDisableChangingSettings", 0));
|
||||
DrawPresetSelector(PRESET_TYPE_RANDOMIZER);
|
||||
ImGui::EndDisabled();
|
||||
|
||||
UIWidgets::Spacer(0);
|
||||
UIWidgets::EnhancementCheckbox("Manual seed entry", "gRandoManualSeedEntry", false, "");
|
||||
@@ -3166,13 +3159,17 @@ void RandomizerSettingsWindow::DrawElement() {
|
||||
}
|
||||
|
||||
UIWidgets::Spacer(0);
|
||||
ImGui::BeginDisabled(CVarGetInteger("gRandomizerDontGenerateSpoiler", 0) && gSaveContext.gameMode != GAMEMODE_FILE_SELECT);
|
||||
if (ImGui::Button("Generate Randomizer")) {
|
||||
GenerateRandomizer(CVarGetInteger("gRandoManualSeedEntry", 0) ? seedString : "");
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
|
||||
UIWidgets::Spacer(0);
|
||||
std::string spoilerfilepath = CVarGetString("gSpoilerLog", "");
|
||||
ImGui::Text("Spoiler File: %s", spoilerfilepath.c_str());
|
||||
if (!CVarGetInteger("gRandomizerDontGenerateSpoiler", 0)) {
|
||||
std::string spoilerfilepath = CVarGetString("gSpoilerLog", "");
|
||||
ImGui::Text("Spoiler File: %s", spoilerfilepath.c_str());
|
||||
}
|
||||
|
||||
// RANDOTODO settings presets
|
||||
// std::string presetfilepath = CVarGetString("gLoadedPreset", "");
|
||||
@@ -3180,6 +3177,8 @@ void RandomizerSettingsWindow::DrawElement() {
|
||||
|
||||
UIWidgets::PaddedSeparator();
|
||||
|
||||
ImGui::BeginDisabled(CVarGetInteger("gDisableChangingSettings", 0));
|
||||
|
||||
ImGuiWindow* window = ImGui::GetCurrentWindow();
|
||||
static ImVec2 cellPadding(8.0f, 8.0f);
|
||||
|
||||
@@ -5224,6 +5223,8 @@ void RandomizerSettingsWindow::DrawElement() {
|
||||
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
|
||||
ImGui::EndDisabled();
|
||||
|
||||
if (disableEditingRandoSettings) {
|
||||
UIWidgets::ReEnableComponent("");
|
||||
|
||||
@@ -39,7 +39,7 @@ std::map<RandomizerCheck, RandomizerCheckObject> rcObjects = {
|
||||
RC_OBJECT(RC_LW_DEKU_SCRUB_NEAR_DEKU_THEATER_LEFT, RCVORMQ_BOTH, RCTYPE_SCRUB, RCAREA_LOST_WOODS, ACTOR_EN_DNS, SCENE_LOST_WOODS, 0x01, GI_STICKS_1, "Deku Scrub Near Deku Theater Left", "LW Deku Scrub Near Deku Theater Left", false),
|
||||
RC_OBJECT(RC_LW_DEKU_SCRUB_NEAR_BRIDGE, RCVORMQ_BOTH, RCTYPE_SCRUB, RCAREA_LOST_WOODS, ACTOR_EN_DNS, SCENE_LOST_WOODS, 0x09, GI_STICK_UPGRADE_20, "Deku Scrub Near Bridge", "LW Deku Scrub Near Bridge", true),
|
||||
RC_OBJECT(RC_LW_DEKU_SCRUB_GROTTO_REAR, RCVORMQ_BOTH, RCTYPE_SCRUB, RCAREA_LOST_WOODS, ACTOR_EN_DNS, SCENE_GROTTOS, TWO_ACTOR_PARAMS(0x03,0xF5), GI_SEEDS_30, "Deku Scrub Grotto Rear", "LW Deku Scrub Grotto Rear", false),
|
||||
RC_OBJECT(RC_LW_DEKU_SCRUB_GROTTO_FRONT, RCVORMQ_BOTH, RCTYPE_SCRUB, RCAREA_LOST_WOODS, ACTOR_EN_DNS, SCENE_GROTTOS, TWO_ACTOR_PARAMS(0x0A,0xF5), GI_NUT_UPGRADE_30, "Deku Scrub Grotto Front", "LW Deku Scrub Grotto Front", false),
|
||||
RC_OBJECT(RC_LW_DEKU_SCRUB_GROTTO_FRONT, RCVORMQ_BOTH, RCTYPE_SCRUB, RCAREA_LOST_WOODS, ACTOR_EN_DNS, SCENE_GROTTOS, TWO_ACTOR_PARAMS(0x0A,0xF5), GI_NUT_UPGRADE_30, "Deku Scrub Grotto Front", "LW Deku Scrub Grotto Front", true),
|
||||
RC_OBJECT(RC_DEKU_THEATER_SKULL_MASK, RCVORMQ_BOTH, RCTYPE_STANDARD, RCAREA_LOST_WOODS, ACTOR_ID_MAX, SCENE_GROTTOS, 0x00, GI_NONE, "Deku Theater Skull Mask", "Deku Theater Skull Mask", true),
|
||||
RC_OBJECT(RC_DEKU_THEATER_MASK_OF_TRUTH, RCVORMQ_BOTH, RCTYPE_STANDARD, RCAREA_LOST_WOODS, ACTOR_ID_MAX, SCENE_GROTTOS, 0x00, GI_NONE, "Deku Theater Mask of Truth", "Deku Theater Mask of Truth", true),
|
||||
RC_OBJECT(RC_LW_GS_BEAN_PATCH_NEAR_BRIDGE, RCVORMQ_BOTH, RCTYPE_SKULL_TOKEN, RCAREA_LOST_WOODS, ACTOR_EN_SI, SCENE_LOST_WOODS, 27905, GI_SKULL_TOKEN, "GS Bean Patch Near Bridge", "LW GS Bean Patch Near Bridge", true),
|
||||
@@ -238,7 +238,7 @@ std::map<RandomizerCheck, RandomizerCheckObject> rcObjects = {
|
||||
RC_OBJECT(RC_GRAVEYARD_HOOKSHOT_CHEST, RCVORMQ_BOTH, RCTYPE_STANDARD, RCAREA_GRAVEYARD, ACTOR_EN_BOX, SCENE_WINDMILL_AND_DAMPES_GRAVE, 4352, GI_HOOKSHOT, "Hookshot Chest", "GY Hookshot Chest", true),
|
||||
RC_OBJECT(RC_GRAVEYARD_DAMPE_RACE_FREESTANDING_POH, RCVORMQ_BOTH, RCTYPE_STANDARD, RCAREA_GRAVEYARD, ACTOR_EN_ITEM00, SCENE_WINDMILL_AND_DAMPES_GRAVE, 1798, GI_HEART_PIECE, "Dampe Race Freestanding PoH", "GY Dampe Race Freestanding PoH", true),
|
||||
RC_OBJECT(RC_GRAVEYARD_FREESTANDING_POH, RCVORMQ_BOTH, RCTYPE_STANDARD, RCAREA_GRAVEYARD, ACTOR_EN_ITEM00, SCENE_GRAVEYARD, 1030, GI_HEART_PIECE, "Freestanding PoH", "GY Freestanding PoH", true),
|
||||
RC_OBJECT(RC_GRAVEYARD_DAMPE_GRAVEDIGGING_TOUR, RCVORMQ_BOTH, RCTYPE_STANDARD, RCAREA_GRAVEYARD, ACTOR_EN_ITEM00, SCENE_GRAVEYARD, 7942, GI_HEART_PIECE, "Dampe Gravedigging Tour", "GY Dampe Gravedigging Tour", true),
|
||||
RC_OBJECT(RC_GRAVEYARD_DAMPE_GRAVEDIGGING_TOUR, RCVORMQ_BOTH, RCTYPE_STANDARD, RCAREA_GRAVEYARD, ACTOR_EN_ITEM00, SCENE_GRAVEYARD, 6406, GI_HEART_PIECE, "Dampe Gravedigging Tour", "GY Dampe Gravedigging Tour", true),
|
||||
RC_OBJECT(RC_GRAVEYARD_GS_WALL, RCVORMQ_BOTH, RCTYPE_SKULL_TOKEN, RCAREA_GRAVEYARD, ACTOR_EN_SI, SCENE_GRAVEYARD, 20608, GI_SKULL_TOKEN, "GS Wall", "Graveyard GS Wall", true),
|
||||
RC_OBJECT(RC_GRAVEYARD_GS_BEAN_PATCH, RCVORMQ_BOTH, RCTYPE_SKULL_TOKEN, RCAREA_GRAVEYARD, ACTOR_EN_SI, SCENE_GRAVEYARD, 28673, GI_SKULL_TOKEN, "GS Bean Patch", "Graveyard GS Bean Patch", true),
|
||||
RC_OBJECT(RC_SONG_FROM_ROYAL_FAMILYS_TOMB, RCVORMQ_BOTH, RCTYPE_SONG_LOCATION, RCAREA_GRAVEYARD, ACTOR_ID_MAX, SCENE_ID_MAX, 0x00, GI_NONE, "Song from Composers Grave", "Song from Composers Grave", true),
|
||||
@@ -783,7 +783,7 @@ std::map<RandomizerCheckArea, std::string> rcAreaNames = {
|
||||
{ RCAREA_LAKE_HYLIA, "Lake Hylia"},
|
||||
{ RCAREA_GERUDO_VALLEY, "Gerudo Valley"},
|
||||
{ RCAREA_GERUDO_FORTRESS, "Gerudo Fortress"},
|
||||
{ RCAREA_WASTELAND, "Desert Wasteland"},
|
||||
{ RCAREA_WASTELAND, "Haunted Wasteland"},
|
||||
{ RCAREA_DESERT_COLOSSUS, "Desert Colossus"},
|
||||
{ RCAREA_MARKET, "Hyrule Market"},
|
||||
{ RCAREA_HYRULE_CASTLE, "Hyrule Castle"},
|
||||
|
||||
@@ -73,20 +73,12 @@ bool showLinksPocket;
|
||||
bool fortressFast;
|
||||
bool fortressNormal;
|
||||
|
||||
bool bypassRandoCheck = true;
|
||||
// persistent during gameplay
|
||||
bool initialized;
|
||||
bool doAreaScroll;
|
||||
bool previousShowHidden = false;
|
||||
bool hideShopRightChecks = true;
|
||||
|
||||
bool checkCollected = false;
|
||||
int checkLoops = 0;
|
||||
int checkCounter = 0;
|
||||
u16 savedFrames = 0;
|
||||
bool messageCloseCheck = false;
|
||||
bool pendingSaleCheck = false;
|
||||
bool transitionCheck = false;
|
||||
bool alwaysShowGS = false;
|
||||
|
||||
std::map<uint32_t, RandomizerCheck> startingShopItem = { { SCENE_KOKIRI_SHOP, RC_KF_SHOP_ITEM_1 },
|
||||
{ SCENE_BAZAAR, RC_MARKET_BAZAAR_ITEM_1 },
|
||||
@@ -118,12 +110,9 @@ bool showVOrMQ;
|
||||
s8 areaChecksGotten[32]; //| "Kokiri Forest (4/9)"
|
||||
bool optCollapseAll; // A bool that will collapse all checks once
|
||||
bool optExpandAll; // A bool that will expand all checks once
|
||||
RandomizerCheck lastItemGetCheck = RC_UNKNOWN_CHECK;
|
||||
RandomizerCheck lastLocationChecked = RC_UNKNOWN_CHECK;
|
||||
RandomizerCheckArea previousArea = RCAREA_INVALID;
|
||||
RandomizerCheckArea currentArea = RCAREA_INVALID;
|
||||
std::vector<RandomizerCheckArea> checkAreas;
|
||||
std::vector<GetItemEntry> itemsReceived;
|
||||
OSContPad* trackerButtonsPressed;
|
||||
|
||||
void BeginFloatWindows(std::string UniqueName, bool& open, ImGuiWindowFlags flags = 0);
|
||||
@@ -194,10 +183,6 @@ Color_RGBA8 Color_Saved_Extra = { 0, 185, 0, 255 }; // Green
|
||||
std::vector<uint32_t> buttons = { BTN_A, BTN_B, BTN_CUP, BTN_CDOWN, BTN_CLEFT, BTN_CRIGHT, BTN_L,
|
||||
BTN_Z, BTN_R, BTN_START, BTN_DUP, BTN_DDOWN, BTN_DLEFT, BTN_DRIGHT };
|
||||
|
||||
void SetLastItemGetRC(RandomizerCheck rc) {
|
||||
lastItemGetCheck = rc;
|
||||
}
|
||||
|
||||
void DefaultCheckData(RandomizerCheck rc) {
|
||||
gSaveContext.checkTrackerData[rc].status = RCSHOW_UNCHECKED;
|
||||
gSaveContext.checkTrackerData[rc].skipped = 0;
|
||||
@@ -253,9 +238,6 @@ void SetCheckCollected(RandomizerCheck rc) {
|
||||
} else {
|
||||
gSaveContext.checkTrackerData[rc].skipped = false;
|
||||
}
|
||||
if (!checkAreas.empty()) {
|
||||
checkAreas.erase(checkAreas.begin());
|
||||
}
|
||||
SaveManager::Instance->SaveSection(gSaveContext.fileNum, sectionId, true);
|
||||
|
||||
doAreaScroll = true;
|
||||
@@ -360,32 +342,18 @@ bool vector_contains_scene(std::vector<SceneID> vec, const int16_t scene) {
|
||||
|
||||
std::vector<SceneID> skipScenes = {SCENE_GANON_BOSS, SCENE_GANONS_TOWER_COLLAPSE_EXTERIOR, SCENE_GANON_BOSS, SCENE_INSIDE_GANONS_CASTLE_COLLAPSE, SCENE_GANONS_TOWER_COLLAPSE_INTERIOR};
|
||||
|
||||
bool EvaluateCheck(RandomizerCheckObject rco) {
|
||||
if (HasItemBeenCollected(rco.rc) && gSaveContext.checkTrackerData[rco.rc].status != RCSHOW_COLLECTED &&
|
||||
gSaveContext.checkTrackerData[rco.rc].status != RCSHOW_SAVED) {
|
||||
SetCheckCollected(rco.rc);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CheckByArea(RandomizerCheckArea area = RCAREA_INVALID) {
|
||||
if (area == RCAREA_INVALID) {
|
||||
area = checkAreas.front();
|
||||
}
|
||||
if (area != RCAREA_INVALID) {
|
||||
auto areaChecks = checksByArea.find(area)->second;
|
||||
if (checkCounter >= areaChecks.size()) {
|
||||
checkCounter = 0;
|
||||
checkLoops++;
|
||||
}
|
||||
auto rco = areaChecks.at(checkCounter);
|
||||
return EvaluateCheck(rco);
|
||||
void ClearAreaChecksAndTotals() {
|
||||
for (auto& [rcArea, vec] : checksByArea) {
|
||||
vec.clear();
|
||||
areaChecksGotten[rcArea] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void SetShopSeen(uint32_t sceneNum, bool prices) {
|
||||
RandomizerCheck start = startingShopItem.find(sceneNum)->second;
|
||||
if (sceneNum == SCENE_POTION_SHOP_KAKARIKO && !LINK_IS_ADULT) {
|
||||
return;
|
||||
}
|
||||
if (GetCheckArea() == RCAREA_KAKARIKO_VILLAGE && sceneNum == SCENE_BAZAAR) {
|
||||
start = RC_KAK_BAZAAR_ITEM_1;
|
||||
}
|
||||
@@ -452,266 +420,31 @@ bool HasItemBeenCollected(RandomizerCheck rc) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void CheckTrackerDialogClosed() {
|
||||
if (messageCloseCheck) {
|
||||
messageCloseCheck = false;
|
||||
}
|
||||
}
|
||||
|
||||
void CheckTrackerShopSlotChange(uint8_t cursorSlot, int16_t basePrice) {
|
||||
if (gPlayState->sceneNum == SCENE_HAPPY_MASK_SHOP) { // Happy Mask Shop is not used in rando, so is not tracked
|
||||
return;
|
||||
}
|
||||
|
||||
auto slot = startingShopItem.find(gPlayState->sceneNum)->second + cursorSlot;
|
||||
if (GetCheckArea() == RCAREA_KAKARIKO_VILLAGE && gPlayState->sceneNum == SCENE_BAZAAR) {
|
||||
slot = RC_KAK_BAZAAR_ITEM_1 + cursorSlot;
|
||||
}
|
||||
auto status = gSaveContext.checkTrackerData[slot].status;
|
||||
if (status == RCSHOW_SEEN) {
|
||||
gSaveContext.checkTrackerData[slot].status = RCSHOW_IDENTIFIED;
|
||||
gSaveContext.checkTrackerData[slot].price = basePrice;
|
||||
SaveManager::Instance->SaveSection(gSaveContext.fileNum, sectionId, true);
|
||||
}
|
||||
}
|
||||
|
||||
void CheckTrackerTransition(uint32_t sceneNum) {
|
||||
if (!GameInteractor::IsSaveLoaded()) {
|
||||
return;
|
||||
}
|
||||
gSaveContext;
|
||||
if (transitionCheck) {
|
||||
transitionCheck = false;
|
||||
}
|
||||
doAreaScroll = true;
|
||||
previousArea = currentArea;
|
||||
currentArea = GetCheckArea();
|
||||
switch (sceneNum) {
|
||||
case SCENE_KOKIRI_SHOP:
|
||||
case SCENE_BAZAAR:
|
||||
case SCENE_POTION_SHOP_MARKET:
|
||||
case SCENE_BOMBCHU_SHOP:
|
||||
case SCENE_POTION_SHOP_KAKARIKO:
|
||||
case SCENE_GORON_SHOP:
|
||||
case SCENE_ZORA_SHOP:
|
||||
SetShopSeen(sceneNum, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CheckTrackerFrame() {
|
||||
if (!GameInteractor::IsSaveLoaded()) {
|
||||
return;
|
||||
}
|
||||
if (!checkAreas.empty() && !transitionCheck && !messageCloseCheck && !pendingSaleCheck) {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
if (CheckByArea()) {
|
||||
checkCounter = 0;
|
||||
break;
|
||||
} else {
|
||||
checkCounter++;
|
||||
}
|
||||
}
|
||||
if (checkLoops > 15) {
|
||||
checkAreas.erase(checkAreas.begin());
|
||||
checkLoops = 0;
|
||||
}
|
||||
}
|
||||
if (savedFrames > 0 && !pendingSaleCheck && !messageCloseCheck) {
|
||||
savedFrames--;
|
||||
}
|
||||
}
|
||||
|
||||
void CheckTrackerSaleEnd(GetItemEntry giEntry) {
|
||||
if (pendingSaleCheck) {
|
||||
pendingSaleCheck = false;
|
||||
}
|
||||
}
|
||||
|
||||
void CheckTrackerItemReceive(GetItemEntry giEntry) {
|
||||
if (!GameInteractor::IsSaveLoaded() || vector_contains_scene(skipScenes, gPlayState->sceneNum)) {
|
||||
return;
|
||||
}
|
||||
auto scene = static_cast<SceneID>(gPlayState->sceneNum);
|
||||
// Vanilla special item checks
|
||||
if (!IS_RANDO) {
|
||||
if (giEntry.itemId == ITEM_SHIELD_DEKU) {
|
||||
SetCheckCollected(RC_KF_SHOP_ITEM_3);
|
||||
return;
|
||||
}else if (giEntry.itemId == ITEM_KOKIRI_EMERALD) {
|
||||
SetCheckCollected(RC_QUEEN_GOHMA);
|
||||
return;
|
||||
} else if (giEntry.itemId == ITEM_GORON_RUBY) {
|
||||
SetCheckCollected(RC_KING_DODONGO);
|
||||
return;
|
||||
} else if (giEntry.itemId == ITEM_ZORA_SAPPHIRE) {
|
||||
SetCheckCollected(RC_BARINADE);
|
||||
return;
|
||||
} else if (giEntry.itemId == ITEM_MEDALLION_FOREST) {
|
||||
SetCheckCollected(RC_PHANTOM_GANON);
|
||||
return;
|
||||
} else if (giEntry.itemId == ITEM_MEDALLION_FIRE) {
|
||||
SetCheckCollected(RC_VOLVAGIA);
|
||||
return;
|
||||
} else if (giEntry.itemId == ITEM_MEDALLION_WATER) {
|
||||
SetCheckCollected(RC_MORPHA);
|
||||
return;
|
||||
} else if (giEntry.itemId == ITEM_MEDALLION_SHADOW) {
|
||||
SetCheckCollected(RC_BONGO_BONGO);
|
||||
return;
|
||||
} else if (giEntry.itemId == ITEM_MEDALLION_SPIRIT) {
|
||||
SetCheckCollected(RC_TWINROVA);
|
||||
return;
|
||||
} else if (giEntry.itemId == ITEM_MEDALLION_LIGHT) {
|
||||
SetCheckCollected(RC_GIFT_FROM_SAGES);
|
||||
return;
|
||||
} else if (giEntry.itemId == ITEM_SONG_LULLABY) {
|
||||
SetCheckCollected(RC_SONG_FROM_IMPA);
|
||||
return;
|
||||
} else if (giEntry.itemId == ITEM_SONG_EPONA) {
|
||||
SetCheckCollected(RC_SONG_FROM_MALON);
|
||||
return;
|
||||
} else if (giEntry.itemId == ITEM_SONG_SARIA) {
|
||||
SetCheckCollected(RC_SONG_FROM_SARIA);
|
||||
return;
|
||||
} else if (giEntry.itemId == ITEM_SONG_SUN) {
|
||||
SetCheckCollected(RC_SONG_FROM_ROYAL_FAMILYS_TOMB);
|
||||
return;
|
||||
} else if (giEntry.itemId == ITEM_SONG_TIME) {
|
||||
SetCheckCollected(RC_SONG_FROM_OCARINA_OF_TIME);
|
||||
return;
|
||||
} else if (giEntry.itemId == ITEM_SONG_STORMS) {
|
||||
SetCheckCollected(RC_SONG_FROM_WINDMILL);
|
||||
return;
|
||||
} else if (giEntry.itemId == ITEM_SONG_MINUET) {
|
||||
SetCheckCollected(RC_SHEIK_IN_FOREST);
|
||||
return;
|
||||
} else if (giEntry.itemId == ITEM_SONG_BOLERO) {
|
||||
SetCheckCollected(RC_SHEIK_IN_CRATER);
|
||||
return;
|
||||
} else if (giEntry.itemId == ITEM_SONG_SERENADE) {
|
||||
SetCheckCollected(RC_SHEIK_IN_ICE_CAVERN);
|
||||
return;
|
||||
} else if (giEntry.itemId == ITEM_SONG_NOCTURNE) {
|
||||
SetCheckCollected(RC_SHEIK_IN_KAKARIKO);
|
||||
return;
|
||||
} else if (giEntry.itemId == ITEM_SONG_REQUIEM) {
|
||||
SetCheckCollected(RC_SHEIK_AT_COLOSSUS);
|
||||
return;
|
||||
} else if (giEntry.itemId == ITEM_SONG_PRELUDE) {
|
||||
SetCheckCollected(RC_SHEIK_AT_TEMPLE);
|
||||
return;
|
||||
} else if (giEntry.itemId == ITEM_BRACELET) {
|
||||
SetCheckCollected(RC_GC_DARUNIAS_JOY);
|
||||
return;
|
||||
} else if (giEntry.itemId == ITEM_LETTER_ZELDA) {
|
||||
SetCheckCollected(RC_HC_ZELDAS_LETTER);
|
||||
return;
|
||||
} else if (giEntry.itemId == ITEM_WEIRD_EGG) {
|
||||
SetCheckCollected(RC_HC_MALON_EGG);
|
||||
return;
|
||||
} else if (giEntry.itemId == ITEM_BEAN) {
|
||||
SetCheckCollected(RC_ZR_MAGIC_BEAN_SALESMAN);
|
||||
return;
|
||||
}
|
||||
}
|
||||
auto checkArea = GetCheckArea();
|
||||
if (gSaveContext.pendingSale != ITEM_NONE) {
|
||||
pendingSaleCheck = true;
|
||||
checkAreas.push_back(checkArea);
|
||||
return;
|
||||
}
|
||||
if (scene == SCENE_DESERT_COLOSSUS && (gSaveContext.entranceIndex == 485 || gSaveContext.entranceIndex == 489)) {
|
||||
checkAreas.push_back(RCAREA_SPIRIT_TEMPLE);
|
||||
return;
|
||||
}
|
||||
if (GET_PLAYER(gPlayState) == nullptr) {
|
||||
transitionCheck = true;
|
||||
return;
|
||||
}
|
||||
if (gPlayState->msgCtx.msgMode != MSGMODE_NONE) {
|
||||
checkAreas.push_back(checkArea);
|
||||
messageCloseCheck = true;
|
||||
return;
|
||||
}
|
||||
if (IS_RANDO || (!IS_RANDO && giEntry.getItemCategory != ITEM_CATEGORY_JUNK)) {
|
||||
checkAreas.push_back(checkArea);
|
||||
checkCollected = true;
|
||||
}
|
||||
}
|
||||
|
||||
void InitTrackerData(bool isDebug) {
|
||||
TrySetAreas();
|
||||
for (auto& [rc, rco] : RandomizerCheckObjects::GetAllRCObjects()) {
|
||||
if (rc != RC_UNKNOWN_CHECK && rc != RC_MAX) {
|
||||
DefaultCheckData(rc);
|
||||
}
|
||||
}
|
||||
UpdateAllOrdering();
|
||||
UpdateInventoryChecks();
|
||||
}
|
||||
|
||||
void SaveTrackerData(SaveContext* saveContext, int sectionID, bool gameSave) {
|
||||
SaveManager::Instance->SaveArray("checks", ARRAY_COUNT(saveContext->checkTrackerData), [&](size_t i) {
|
||||
if (saveContext->checkTrackerData[i].status == RCSHOW_COLLECTED) {
|
||||
if (gameSave || savedFrames > 0) {
|
||||
gSaveContext.checkTrackerData[i].status = saveContext->checkTrackerData[i].status = RCSHOW_SAVED;
|
||||
UpdateAllOrdering();
|
||||
UpdateInventoryChecks();
|
||||
} else {
|
||||
saveContext->checkTrackerData[i].status = RCSHOW_SCUMMED;
|
||||
}
|
||||
}
|
||||
SaveManager::Instance->SaveStruct("", [&]() {
|
||||
SaveManager::Instance->SaveData("status", saveContext->checkTrackerData[i].status);
|
||||
SaveManager::Instance->SaveData("skipped", saveContext->checkTrackerData[i].skipped);
|
||||
SaveManager::Instance->SaveData("price", saveContext->checkTrackerData[i].price);
|
||||
SaveManager::Instance->SaveData("hintItem", saveContext->checkTrackerData[i].hintItem);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void SaveFile(SaveContext* saveContext, int sectionID, bool fullSave) {
|
||||
SaveTrackerData(saveContext, sectionID, fullSave);
|
||||
if (fullSave) {
|
||||
savedFrames = 40;
|
||||
}
|
||||
}
|
||||
|
||||
void LoadFile() {
|
||||
Teardown();
|
||||
void CheckTrackerLoadGame(int32_t fileNum) {
|
||||
LoadSettings();
|
||||
TrySetAreas();
|
||||
SaveManager::Instance->LoadArray("checks", RC_MAX, [](size_t i) {
|
||||
SaveManager::Instance->LoadStruct("", [&]() {
|
||||
SaveManager::Instance->LoadData("status", gSaveContext.checkTrackerData[i].status);
|
||||
SaveManager::Instance->LoadData("skipped", gSaveContext.checkTrackerData[i].skipped);
|
||||
SaveManager::Instance->LoadData("price", gSaveContext.checkTrackerData[i].price);
|
||||
SaveManager::Instance->LoadData("hintItem", gSaveContext.checkTrackerData[i].hintItem);
|
||||
});
|
||||
RandomizerCheckTrackerData entry = gSaveContext.checkTrackerData[i];
|
||||
RandomizerCheck rc = static_cast<RandomizerCheck>(i);
|
||||
if (rc == RC_UNKNOWN_CHECK || rc == RC_MAX ||
|
||||
for (auto [rc, rcObj] : RandomizerCheckObjects::GetAllRCObjects()) {
|
||||
RandomizerCheckTrackerData rcTrackerData = gSaveContext.checkTrackerData[rc];
|
||||
if (rc == RC_UNKNOWN_CHECK || rc == RC_MAX || rc == RC_LINKS_POCKET ||
|
||||
!RandomizerCheckObjects::GetAllRCObjects().contains(rc))
|
||||
return;
|
||||
continue;
|
||||
|
||||
RandomizerCheckObject entry2;
|
||||
RandomizerCheckObject realRcObj;
|
||||
if (rc == RC_GIFT_FROM_SAGES && !IS_RANDO) {
|
||||
entry2 = RCO_RAORU;
|
||||
realRcObj = RCO_RAORU;
|
||||
} else {
|
||||
entry2 = RandomizerCheckObjects::GetAllRCObjects().find(rc)->second;
|
||||
}
|
||||
if (!IsVisibleInCheckTracker(entry2)) return;
|
||||
|
||||
checksByArea.find(entry2.rcArea)->second.push_back(entry2);
|
||||
if (entry.status == RCSHOW_SAVED || entry.skipped) {
|
||||
areaChecksGotten[entry2.rcArea]++;
|
||||
realRcObj = rcObj;
|
||||
}
|
||||
|
||||
if (areaChecksGotten[entry2.rcArea] != 0 || RandomizerCheckObjects::AreaIsOverworld(entry2.rcArea)) {
|
||||
areasSpoiled |= (1 << entry2.rcArea);
|
||||
checksByArea.find(realRcObj.rcArea)->second.push_back(realRcObj);
|
||||
if (rcTrackerData.status == RCSHOW_SAVED || rcTrackerData.skipped) {
|
||||
areaChecksGotten[realRcObj.rcArea]++;
|
||||
}
|
||||
});
|
||||
|
||||
if (areaChecksGotten[realRcObj.rcArea] != 0 || RandomizerCheckObjects::AreaIsOverworld(realRcObj.rcArea)) {
|
||||
areasSpoiled |= (1 << realRcObj.rcArea);
|
||||
}
|
||||
}
|
||||
if (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_LINKS_POCKET) != RO_LINKS_POCKET_NOTHING && IS_RANDO) {
|
||||
s8 startingAge = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_STARTING_AGE);
|
||||
RandomizerCheckArea startingArea;
|
||||
@@ -743,16 +476,311 @@ void LoadFile() {
|
||||
UpdateInventoryChecks();
|
||||
}
|
||||
|
||||
void CheckTrackerShopSlotChange(uint8_t cursorSlot, int16_t basePrice) {
|
||||
if (gPlayState->sceneNum == SCENE_HAPPY_MASK_SHOP) { // Happy Mask Shop is not used in rando, so is not tracked
|
||||
return;
|
||||
}
|
||||
|
||||
auto slot = startingShopItem.find(gPlayState->sceneNum)->second + cursorSlot;
|
||||
if (GetCheckArea() == RCAREA_KAKARIKO_VILLAGE && gPlayState->sceneNum == SCENE_BAZAAR) {
|
||||
slot = RC_KAK_BAZAAR_ITEM_1 + cursorSlot;
|
||||
}
|
||||
auto status = gSaveContext.checkTrackerData[slot].status;
|
||||
if (status == RCSHOW_SEEN) {
|
||||
gSaveContext.checkTrackerData[slot].status = RCSHOW_IDENTIFIED;
|
||||
gSaveContext.checkTrackerData[slot].price = basePrice;
|
||||
SaveManager::Instance->SaveSection(gSaveContext.fileNum, sectionId, true);
|
||||
}
|
||||
}
|
||||
|
||||
void CheckTrackerTransition(uint32_t sceneNum) {
|
||||
if (!GameInteractor::IsSaveLoaded()) {
|
||||
return;
|
||||
}
|
||||
doAreaScroll = true;
|
||||
previousArea = currentArea;
|
||||
currentArea = GetCheckArea();
|
||||
switch (sceneNum) {
|
||||
case SCENE_KOKIRI_SHOP:
|
||||
case SCENE_BAZAAR:
|
||||
case SCENE_POTION_SHOP_MARKET:
|
||||
case SCENE_BOMBCHU_SHOP:
|
||||
case SCENE_POTION_SHOP_KAKARIKO:
|
||||
case SCENE_GORON_SHOP:
|
||||
case SCENE_ZORA_SHOP:
|
||||
SetShopSeen(sceneNum, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CheckTrackerFrame() {
|
||||
if (IS_RANDO) {
|
||||
hideShopRightChecks = CVarGetInteger("gCheckTrackerOptionHideRightShopChecks", 1);
|
||||
alwaysShowGS = CVarGetInteger("gCheckTrackerOptionAlwaysShowGSLocs", 0);
|
||||
}
|
||||
if (!GameInteractor::IsSaveLoaded()) {
|
||||
return;
|
||||
}
|
||||
// TODO: Move to OnAmmoChange hook once it gets added.
|
||||
if (gSaveContext.checkTrackerData[RC_ZR_MAGIC_BEAN_SALESMAN].status != RCSHOW_COLLECTED &&
|
||||
gSaveContext.checkTrackerData[RC_ZR_MAGIC_BEAN_SALESMAN].status != RCSHOW_SAVED) {
|
||||
if (BEANS_BOUGHT >= 10) {
|
||||
SetCheckCollected(RC_ZR_MAGIC_BEAN_SALESMAN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CheckTrackerItemReceive(GetItemEntry giEntry) {
|
||||
if (!GameInteractor::IsSaveLoaded() || vector_contains_scene(skipScenes, gPlayState->sceneNum)) {
|
||||
return;
|
||||
}
|
||||
auto scene = static_cast<SceneID>(gPlayState->sceneNum);
|
||||
// Vanilla special item checks
|
||||
if (!IS_RANDO) {
|
||||
if (giEntry.itemId == ITEM_SHIELD_DEKU) {
|
||||
SetCheckCollected(RC_KF_SHOP_ITEM_1);
|
||||
return;
|
||||
}else if (giEntry.itemId == ITEM_KOKIRI_EMERALD) {
|
||||
SetCheckCollected(RC_QUEEN_GOHMA);
|
||||
return;
|
||||
} else if (giEntry.itemId == ITEM_GORON_RUBY) {
|
||||
SetCheckCollected(RC_KING_DODONGO);
|
||||
return;
|
||||
} else if (giEntry.itemId == ITEM_ZORA_SAPPHIRE) {
|
||||
SetCheckCollected(RC_BARINADE);
|
||||
return;
|
||||
} else if (giEntry.itemId == ITEM_MEDALLION_FOREST) {
|
||||
SetCheckCollected(RC_PHANTOM_GANON);
|
||||
return;
|
||||
} else if (giEntry.itemId == ITEM_MEDALLION_FIRE) {
|
||||
SetCheckCollected(RC_VOLVAGIA);
|
||||
return;
|
||||
} else if (giEntry.itemId == ITEM_MEDALLION_WATER) {
|
||||
SetCheckCollected(RC_MORPHA);
|
||||
return;
|
||||
} else if (giEntry.itemId == ITEM_MEDALLION_SHADOW) {
|
||||
SetCheckCollected(RC_BONGO_BONGO);
|
||||
return;
|
||||
} else if (giEntry.itemId == ITEM_MEDALLION_SPIRIT) {
|
||||
SetCheckCollected(RC_TWINROVA);
|
||||
return;
|
||||
} else if (giEntry.itemId == ITEM_MEDALLION_LIGHT) {
|
||||
SetCheckCollected(RC_GIFT_FROM_SAGES);
|
||||
return;
|
||||
} else if (giEntry.itemId == ITEM_SONG_EPONA) {
|
||||
SetCheckCollected(RC_SONG_FROM_MALON);
|
||||
return;
|
||||
} else if (giEntry.itemId == ITEM_SONG_SARIA) {
|
||||
SetCheckCollected(RC_SONG_FROM_SARIA);
|
||||
return;
|
||||
} else if (giEntry.itemId == ITEM_BEAN) {
|
||||
SetCheckCollected(RC_ZR_MAGIC_BEAN_SALESMAN);
|
||||
return;
|
||||
} else if (giEntry.itemId == ITEM_BRACELET) {
|
||||
SetCheckCollected(RC_GC_DARUNIAS_JOY);
|
||||
return;
|
||||
}/* else if (giEntry.itemId == ITEM_SONG_SUN) {
|
||||
SetCheckCollected(RC_SONG_FROM_ROYAL_FAMILYS_TOMB);
|
||||
return;
|
||||
} else if (giEntry.itemId == ITEM_SONG_TIME) {
|
||||
SetCheckCollected(RC_SONG_FROM_OCARINA_OF_TIME);
|
||||
return;
|
||||
} else if (giEntry.itemId == ITEM_SONG_STORMS) {
|
||||
SetCheckCollected(RC_SONG_FROM_WINDMILL);
|
||||
return;
|
||||
} else if (giEntry.itemId == ITEM_SONG_MINUET) {
|
||||
SetCheckCollected(RC_SHEIK_IN_FOREST);
|
||||
return;
|
||||
} else if (giEntry.itemId == ITEM_SONG_BOLERO) {
|
||||
SetCheckCollected(RC_SHEIK_IN_CRATER);
|
||||
return;
|
||||
} else if (giEntry.itemId == ITEM_SONG_SERENADE) {
|
||||
SetCheckCollected(RC_SHEIK_IN_ICE_CAVERN);
|
||||
return;
|
||||
} else if (giEntry.itemId == ITEM_SONG_NOCTURNE) {
|
||||
SetCheckCollected(RC_SHEIK_IN_KAKARIKO);
|
||||
return;
|
||||
} else if (giEntry.itemId == ITEM_SONG_REQUIEM) {
|
||||
SetCheckCollected(RC_SHEIK_AT_COLOSSUS);
|
||||
return;
|
||||
} else if (giEntry.itemId == ITEM_SONG_PRELUDE) {
|
||||
SetCheckCollected(RC_SHEIK_AT_TEMPLE);
|
||||
return;
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
void CheckTrackerSceneFlagSet(int16_t sceneNum, int16_t flagType, int32_t flag) {
|
||||
if (flagType != FLAG_SCENE_TREASURE && flagType != FLAG_SCENE_COLLECTIBLE) {
|
||||
return;
|
||||
}
|
||||
if (sceneNum == SCENE_GRAVEYARD && flag == 0x19 && flagType == FLAG_SCENE_COLLECTIBLE) { // Gravedigging tour special case
|
||||
SetCheckCollected(RC_GRAVEYARD_DAMPE_GRAVEDIGGING_TOUR);
|
||||
return;
|
||||
}
|
||||
for (auto [rc, rcObj] : RandomizerCheckObjects::GetAllRCObjects()) {
|
||||
if (!IsVisibleInCheckTracker(rcObj)) {
|
||||
continue;
|
||||
}
|
||||
SpoilerCollectionCheckType checkMatchType = flagType == FLAG_SCENE_TREASURE ? SpoilerCollectionCheckType::SPOILER_CHK_CHEST : SpoilerCollectionCheckType::SPOILER_CHK_COLLECTABLE;
|
||||
SpoilerCollectionCheck scCheck = Location(rc)->GetCollectionCheck();
|
||||
if (scCheck.scene == sceneNum && scCheck.flag == flag && scCheck.type == checkMatchType) {
|
||||
SetCheckCollected(rc);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CheckTrackerFlagSet(int16_t flagType, int32_t flag) {
|
||||
SpoilerCollectionCheckType checkMatchType = SpoilerCollectionCheckType::SPOILER_CHK_NONE;
|
||||
switch (flagType) {
|
||||
case FLAG_GS_TOKEN:
|
||||
checkMatchType = SpoilerCollectionCheckType::SPOILER_CHK_GOLD_SKULLTULA;
|
||||
break;
|
||||
case FLAG_EVENT_CHECK_INF:
|
||||
if ((flag == EVENTCHKINF_CARPENTERS_FREE(0) || flag == EVENTCHKINF_CARPENTERS_FREE(1) ||
|
||||
flag == EVENTCHKINF_CARPENTERS_FREE(2) || flag == EVENTCHKINF_CARPENTERS_FREE(3))
|
||||
&& GET_EVENTCHKINF_CARPENTERS_FREE_ALL()) {
|
||||
SetCheckCollected(RC_GF_GERUDO_MEMBERSHIP_CARD);
|
||||
return;
|
||||
}
|
||||
checkMatchType = SpoilerCollectionCheckType::SPOILER_CHK_EVENT_CHK_INF;
|
||||
break;
|
||||
case FLAG_INF_TABLE:
|
||||
if (flag == INFTABLE_190) {
|
||||
SetCheckCollected(RC_GF_HBA_1000_POINTS);
|
||||
return;
|
||||
} else if (flag == INFTABLE_11E) {
|
||||
SetCheckCollected(RC_GC_ROLLING_GORON_AS_CHILD);
|
||||
return;
|
||||
} else if (flag == INFTABLE_GORON_CITY_DOORS_UNLOCKED) {
|
||||
SetCheckCollected(RC_GC_ROLLING_GORON_AS_ADULT);
|
||||
return;
|
||||
} else if (flag == INFTABLE_139) {
|
||||
SetCheckCollected(RC_ZD_KING_ZORA_THAWED);
|
||||
return;
|
||||
} else if (flag == INFTABLE_191) {
|
||||
SetCheckCollected(RC_MARKET_LOST_DOG);
|
||||
return;
|
||||
}
|
||||
if (!IS_RANDO) {
|
||||
if (flag == INFTABLE_192) {
|
||||
SetCheckCollected(RC_LW_DEKU_SCRUB_NEAR_BRIDGE);
|
||||
return;
|
||||
} else if (flag == INFTABLE_193) {
|
||||
SetCheckCollected(RC_LW_DEKU_SCRUB_GROTTO_FRONT);
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case FLAG_ITEM_GET_INF:
|
||||
if (!IS_RANDO) {
|
||||
if (flag == ITEMGETINF_OBTAINED_STICK_UPGRADE_FROM_STAGE) {
|
||||
SetCheckCollected(RC_DEKU_THEATER_SKULL_MASK);
|
||||
return;
|
||||
} else if (flag == ITEMGETINF_OBTAINED_NUT_UPGRADE_FROM_STAGE) {
|
||||
SetCheckCollected(RC_DEKU_THEATER_MASK_OF_TRUTH);
|
||||
return;
|
||||
} else if (flag == ITEMGETINF_0B) {
|
||||
SetCheckCollected(RC_HF_DEKU_SCRUB_GROTTO);
|
||||
return;
|
||||
}
|
||||
}
|
||||
checkMatchType = SpoilerCollectionCheckType::SPOILER_CHK_ITEM_GET_INF;
|
||||
break;
|
||||
case FLAG_RANDOMIZER_INF:
|
||||
checkMatchType = SpoilerCollectionCheckType::SPOILER_CHK_RANDOMIZER_INF;
|
||||
break;
|
||||
}
|
||||
if (checkMatchType == SpoilerCollectionCheckType::SPOILER_CHK_NONE) {
|
||||
return;
|
||||
}
|
||||
for (auto [rc, rcObj] : RandomizerCheckObjects::GetAllRCObjects()) {
|
||||
if ((!IS_RANDO && ((rcObj.vOrMQ == RCVORMQ_MQ && !IS_MASTER_QUEST) ||
|
||||
(rcObj.vOrMQ == RCVORMQ_VANILLA && IS_MASTER_QUEST))) ||
|
||||
(IS_RANDO && ((OTRGlobals::Instance->gRandomizer->masterQuestDungeons.contains(rcObj.sceneId) &&
|
||||
rcObj.vOrMQ == RCVORMQ_VANILLA) ||
|
||||
!OTRGlobals::Instance->gRandomizer->masterQuestDungeons.contains(rcObj.sceneId) &&
|
||||
rcObj.vOrMQ == RCVORMQ_MQ))) {
|
||||
continue;
|
||||
}
|
||||
SpoilerCollectionCheck scCheck = Location(rc)->GetCollectionCheck();
|
||||
SpoilerCollectionCheckType scCheckType = scCheck.type;
|
||||
if (checkMatchType == SpoilerCollectionCheckType::SPOILER_CHK_RANDOMIZER_INF &&
|
||||
(scCheckType == SpoilerCollectionCheckType::SPOILER_CHK_MERCHANT ||
|
||||
scCheckType == SpoilerCollectionCheckType::SPOILER_CHK_SHOP_ITEM ||
|
||||
scCheckType == SpoilerCollectionCheckType::SPOILER_CHK_COW ||
|
||||
scCheckType == SpoilerCollectionCheckType::SPOILER_CHK_SCRUB ||
|
||||
scCheckType == SpoilerCollectionCheckType::SPOILER_CHK_MASTER_SWORD ||
|
||||
scCheckType == SpoilerCollectionCheckType::SPOILER_CHK_RANDOMIZER_INF)) {
|
||||
if (flag == OTRGlobals::Instance->gRandomizer->GetRandomizerInfFromCheck(rc)) {
|
||||
SetCheckCollected(rc);
|
||||
return;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
int16_t checkFlag = scCheck.flag;
|
||||
if (checkMatchType == SpoilerCollectionCheckType::SPOILER_CHK_GOLD_SKULLTULA) {
|
||||
checkFlag = rcObj.actorParams;
|
||||
}
|
||||
if (checkFlag == flag && scCheck.type == checkMatchType) {
|
||||
SetCheckCollected(rc);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InitTrackerData(bool isDebug) {
|
||||
TrySetAreas();
|
||||
for (auto& [rc, rco] : RandomizerCheckObjects::GetAllRCObjects()) {
|
||||
if (rc != RC_UNKNOWN_CHECK && rc != RC_MAX) {
|
||||
DefaultCheckData(rc);
|
||||
}
|
||||
}
|
||||
UpdateAllOrdering();
|
||||
UpdateInventoryChecks();
|
||||
}
|
||||
|
||||
void SaveTrackerData(SaveContext* saveContext, int sectionID, bool gameSave) {
|
||||
SaveManager::Instance->SaveArray("checks", ARRAY_COUNT(saveContext->checkTrackerData), [&](size_t i) {
|
||||
if (saveContext->checkTrackerData[i].status == RCSHOW_COLLECTED) {
|
||||
if (gameSave) {
|
||||
gSaveContext.checkTrackerData[i].status = saveContext->checkTrackerData[i].status = RCSHOW_SAVED;
|
||||
UpdateAllOrdering();
|
||||
UpdateInventoryChecks();
|
||||
} else {
|
||||
saveContext->checkTrackerData[i].status = RCSHOW_SCUMMED;
|
||||
}
|
||||
}
|
||||
SaveManager::Instance->SaveStruct("", [&]() {
|
||||
SaveManager::Instance->SaveData("status", saveContext->checkTrackerData[i].status);
|
||||
SaveManager::Instance->SaveData("skipped", saveContext->checkTrackerData[i].skipped);
|
||||
SaveManager::Instance->SaveData("price", saveContext->checkTrackerData[i].price);
|
||||
SaveManager::Instance->SaveData("hintItem", saveContext->checkTrackerData[i].hintItem);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void SaveFile(SaveContext* saveContext, int sectionID, bool fullSave) {
|
||||
SaveTrackerData(saveContext, sectionID, fullSave);
|
||||
}
|
||||
|
||||
void LoadFile() {
|
||||
SaveManager::Instance->LoadArray("checks", RC_MAX, [](size_t i) {
|
||||
SaveManager::Instance->LoadStruct("", [&]() {
|
||||
SaveManager::Instance->LoadData("status", gSaveContext.checkTrackerData[i].status);
|
||||
SaveManager::Instance->LoadData("skipped", gSaveContext.checkTrackerData[i].skipped);
|
||||
SaveManager::Instance->LoadData("price", gSaveContext.checkTrackerData[i].price);
|
||||
SaveManager::Instance->LoadData("hintItem", gSaveContext.checkTrackerData[i].hintItem);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void Teardown() {
|
||||
initialized = false;
|
||||
for (auto& [rcArea, vec] : checksByArea) {
|
||||
vec.clear();
|
||||
areaChecksGotten[rcArea] = 0;
|
||||
}
|
||||
ClearAreaChecksAndTotals();
|
||||
checksByArea.clear();
|
||||
areasSpoiled = 0;
|
||||
checkCollected = false;
|
||||
checkLoops = 0;
|
||||
|
||||
lastLocationChecked = RC_UNKNOWN_CHECK;
|
||||
}
|
||||
@@ -922,7 +950,11 @@ void CheckTrackerWindow::DrawElement() {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(extraColor.r / 255.0f, extraColor.g / 255.0f,
|
||||
extraColor.b / 255.0f, extraColor.a / 255.0f));
|
||||
|
||||
isThisAreaSpoiled = areasSpoiled & areaMask || CVarGetInteger("gCheckTrackerOptionMQSpoilers", 0);
|
||||
isThisAreaSpoiled = areasSpoiled & areaMask || CVarGetInteger("gCheckTrackerOptionMQSpoilers", 0) || !IS_RANDO ||
|
||||
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_RANDOM_MQ_DUNGEONS) == RO_MQ_DUNGEONS_NONE ||
|
||||
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_RANDOM_MQ_DUNGEONS) == RO_MQ_DUNGEONS_SELECTION ||
|
||||
(OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_RANDOM_MQ_DUNGEONS) == RO_MQ_DUNGEONS_SET_NUMBER &&
|
||||
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_MQ_DUNGEON_COUNT) == 12);
|
||||
|
||||
if (isThisAreaSpoiled) {
|
||||
if (showVOrMQ && RandomizerCheckObjects::AreaIsDungeon(rcArea)) {
|
||||
@@ -1056,7 +1088,6 @@ void LoadSettings() {
|
||||
showLinksPocket = IS_RANDO ? // don't show Link's Pocket if not randomizer, or if rando and pocket is disabled
|
||||
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_LINKS_POCKET) != RO_LINKS_POCKET_NOTHING
|
||||
:false;
|
||||
hideShopRightChecks = IS_RANDO ? CVarGetInteger("gCheckTrackerOptionHideRightShopChecks", 1) : false;
|
||||
|
||||
if (IS_RANDO) {
|
||||
switch (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_TOKENS)) {
|
||||
@@ -1120,7 +1151,7 @@ bool IsVisibleInCheckTracker(RandomizerCheckObject rcObj) {
|
||||
) &&
|
||||
(rcObj.rcType != RCTYPE_MERCHANT || showMerchants) &&
|
||||
(rcObj.rcType != RCTYPE_OCARINA || showOcarinas) &&
|
||||
(rcObj.rcType != RCTYPE_SKULL_TOKEN ||
|
||||
(rcObj.rcType != RCTYPE_SKULL_TOKEN || alwaysShowGS ||
|
||||
(showOverworldTokens && RandomizerCheckObjects::AreaIsOverworld(rcObj.rcArea)) ||
|
||||
(showDungeonTokens && RandomizerCheckObjects::AreaIsDungeon(rcObj.rcArea))
|
||||
) &&
|
||||
@@ -1148,8 +1179,8 @@ bool IsVisibleInCheckTracker(RandomizerCheckObject rcObj) {
|
||||
}
|
||||
else if (rcObj.vanillaCompletion) {
|
||||
return (rcObj.vOrMQ == RCVORMQ_BOTH ||
|
||||
rcObj.vOrMQ == RCVORMQ_MQ && OTRGlobals::Instance->gRandomizer->masterQuestDungeons.contains(rcObj.sceneId) ||
|
||||
rcObj.vOrMQ == RCVORMQ_VANILLA && !OTRGlobals::Instance->gRandomizer->masterQuestDungeons.contains(rcObj.sceneId) ||
|
||||
(rcObj.vOrMQ == RCVORMQ_MQ && IS_MASTER_QUEST) ||
|
||||
(rcObj.vOrMQ == RCVORMQ_VANILLA && !IS_MASTER_QUEST) ||
|
||||
rcObj.rc == RC_GIFT_FROM_SAGES) && rcObj.rc != RC_LINKS_POCKET;
|
||||
}
|
||||
return false;
|
||||
@@ -1490,7 +1521,9 @@ void CheckTrackerSettingsWindow::DrawElement() {
|
||||
UIWidgets::EnhancementCheckbox("Vanilla/MQ Dungeon Spoilers", "gCheckTrackerOptionMQSpoilers");
|
||||
UIWidgets::Tooltip("If enabled, Vanilla/MQ dungeons will show on the tracker immediately. Otherwise, Vanilla/MQ dungeon locations must be unlocked.");
|
||||
UIWidgets::EnhancementCheckbox("Hide right-side shop item checks", "gCheckTrackerOptionHideRightShopChecks", false, "", UIWidgets::CheckboxGraphics::Cross, true);
|
||||
UIWidgets::Tooltip("If enabled, will prevent the tracker from displaying slots 1-4 in all shops. Requires save reload.");
|
||||
UIWidgets::Tooltip("If enabled, will prevent the tracker from displaying slots 1-4 in all shops.");
|
||||
UIWidgets::EnhancementCheckbox("Always show gold skulltulas", "gCheckTrackerOptionAlwaysShowGSLocs", false, "");
|
||||
UIWidgets::Tooltip("If enabled, will show GS locations in the tracker regardless of tokensanity settings.");
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
@@ -1533,14 +1566,16 @@ void CheckTrackerWindow::InitElement() {
|
||||
SaveManager::Instance->AddInitFunction(InitTrackerData);
|
||||
sectionId = SaveManager::Instance->AddSaveFunction("trackerData", 1, SaveFile, true, -1);
|
||||
SaveManager::Instance->AddLoadFunction("trackerData", 1, LoadFile);
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnLoadGame>(CheckTrackerLoadGame);
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnExitGame>([](uint32_t fileNum) {
|
||||
Teardown();
|
||||
});
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnItemReceive>(CheckTrackerItemReceive);
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSaleEnd>(CheckTrackerSaleEnd);
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>(CheckTrackerFrame);
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnTransitionEnd>(CheckTrackerTransition);
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnShopSlotChange>(CheckTrackerShopSlotChange);
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSceneFlagSet>(CheckTrackerSceneFlagSet);
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnFlagSet>(CheckTrackerFlagSet);
|
||||
|
||||
LocationTable_Init();
|
||||
}
|
||||
|
||||
@@ -48,10 +48,7 @@ void Teardown();
|
||||
void UpdateAllOrdering();
|
||||
bool IsVisibleInCheckTracker(RandomizerCheckObject rcObj);
|
||||
void InitTrackerData(bool isDebug);
|
||||
void SetLastItemGetRC(RandomizerCheck rc);
|
||||
RandomizerCheckArea GetCheckArea();
|
||||
void CheckTrackerDialogClosed();
|
||||
void ToggleShopRightChecks();
|
||||
void UpdateCheck(uint32_t, RandomizerCheckTrackerData);
|
||||
} // namespace CheckTracker
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ static s16 newIceCavernEntrance = ICE_CAVERN_ENTRANCE;
|
||||
static s8 hasCopiedEntranceTable = 0;
|
||||
static s8 hasModifiedEntranceTable = 0;
|
||||
|
||||
void Entrance_SetEntranceDiscovered(u16 entranceIndex);
|
||||
void Entrance_SetEntranceDiscovered(u16 entranceIndex, u8 isReversedEntrance);
|
||||
|
||||
u8 Entrance_EntranceIsNull(EntranceOverride* entranceOverride) {
|
||||
return entranceOverride->index == 0 && entranceOverride->destination == 0 && entranceOverride->blueWarp == 0
|
||||
@@ -276,13 +276,13 @@ s16 Entrance_OverrideNextIndex(s16 nextEntranceIndex) {
|
||||
return nextEntranceIndex;
|
||||
}
|
||||
|
||||
Entrance_SetEntranceDiscovered(nextEntranceIndex);
|
||||
Entrance_SetEntranceDiscovered(nextEntranceIndex, false);
|
||||
EntranceTracker_SetLastEntranceOverride(nextEntranceIndex);
|
||||
return Grotto_OverrideSpecialEntrance(Entrance_GetOverride(nextEntranceIndex));
|
||||
}
|
||||
|
||||
s16 Entrance_OverrideDynamicExit(s16 dynamicExitIndex) {
|
||||
Entrance_SetEntranceDiscovered(dynamicExitList[dynamicExitIndex]);
|
||||
Entrance_SetEntranceDiscovered(dynamicExitList[dynamicExitIndex], false);
|
||||
EntranceTracker_SetLastEntranceOverride(dynamicExitList[dynamicExitIndex]);
|
||||
return Grotto_OverrideSpecialEntrance(Entrance_GetOverride(dynamicExitList[dynamicExitIndex]));
|
||||
}
|
||||
@@ -784,7 +784,7 @@ u8 Entrance_GetIsEntranceDiscovered(u16 entranceIndex) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Entrance_SetEntranceDiscovered(u16 entranceIndex) {
|
||||
void Entrance_SetEntranceDiscovered(u16 entranceIndex, u8 isReversedEntrance) {
|
||||
// Skip if already set to save time from setting the connected entrance or
|
||||
// if this entrance is outside of the randomized entrance range (i.e. is a dynamic entrance)
|
||||
if (entranceIndex > MAX_ENTRANCE_RANDO_USED_INDEX || Entrance_GetIsEntranceDiscovered(entranceIndex)) {
|
||||
@@ -796,14 +796,20 @@ void Entrance_SetEntranceDiscovered(u16 entranceIndex) {
|
||||
if (idx < SAVEFILE_ENTRANCES_DISCOVERED_IDX_COUNT) {
|
||||
u32 entranceBit = 1 << (entranceIndex - (idx * bitsPerIndex));
|
||||
gSaveContext.sohStats.entrancesDiscovered[idx] |= entranceBit;
|
||||
// Set connected
|
||||
for (size_t i = 0; i < ENTRANCE_OVERRIDES_MAX_COUNT; i++) {
|
||||
if (entranceIndex == gSaveContext.entranceOverrides[i].index) {
|
||||
Entrance_SetEntranceDiscovered(gSaveContext.entranceOverrides[i].overrideDestination);
|
||||
break;
|
||||
|
||||
// Set reverse entrance when not decoupled
|
||||
if (!Randomizer_GetSettingValue(RSK_DECOUPLED_ENTRANCES) && !isReversedEntrance) {
|
||||
for (size_t i = 0; i < ENTRANCE_OVERRIDES_MAX_COUNT; i++) {
|
||||
if (entranceIndex == gSaveContext.entranceOverrides[i].index) {
|
||||
Entrance_SetEntranceDiscovered(gSaveContext.entranceOverrides[i].overrideDestination, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Save entrancesDiscovered
|
||||
Save_SaveSection(SECTION_ID_ENTRANCES);
|
||||
|
||||
// Save entrancesDiscovered when it is not the reversed entrance
|
||||
if (!isReversedEntrance) {
|
||||
Save_SaveSection(SECTION_ID_ENTRANCES);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ void Entrance_OverrideSpawnScene(int32_t sceneNum, int32_t spawn);
|
||||
int32_t Entrance_OverrideSpawnSceneRoom(int32_t sceneNum, int32_t spawn, int32_t room);
|
||||
void Entrance_EnableFW(void);
|
||||
uint8_t Entrance_GetIsEntranceDiscovered(uint16_t entranceIndex);
|
||||
void Entrance_SetEntranceDiscovered(uint16_t entranceIndex);
|
||||
void Entrance_SetEntranceDiscovered(uint16_t entranceIndex, uint8_t isReversedEntrance);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -140,7 +140,7 @@ s16 Grotto_OverrideSpecialEntrance(s16 nextEntranceIndex) {
|
||||
// If Link hits a grotto exit, load the entrance index from the grotto exit list
|
||||
// based on the current grotto ID
|
||||
if (nextEntranceIndex == 0x7FFF) {
|
||||
Entrance_SetEntranceDiscovered(ENTRANCE_RANDO_GROTTO_EXIT_START + grottoId);
|
||||
Entrance_SetEntranceDiscovered(ENTRANCE_RANDO_GROTTO_EXIT_START + grottoId, false);
|
||||
EntranceTracker_SetLastEntranceOverride(ENTRANCE_RANDO_GROTTO_EXIT_START + grottoId);
|
||||
nextEntranceIndex = grottoExitList[grottoId];
|
||||
}
|
||||
@@ -211,7 +211,7 @@ void Grotto_OverrideActorEntrance(Actor* thisx) {
|
||||
|
||||
if (grottoContent == grottoLoadTable[index].content && gPlayState->sceneNum == grottoLoadTable[index].scene) {
|
||||
// Find the override for the matching index from the grotto Load List
|
||||
Entrance_SetEntranceDiscovered(ENTRANCE_RANDO_GROTTO_LOAD_START + index);
|
||||
Entrance_SetEntranceDiscovered(ENTRANCE_RANDO_GROTTO_LOAD_START + index, false);
|
||||
EntranceTracker_SetLastEntranceOverride(ENTRANCE_RANDO_GROTTO_LOAD_START + index);
|
||||
index = grottoLoadList[index];
|
||||
|
||||
|
||||
@@ -219,7 +219,7 @@ std::unordered_map<RandomizerTrickArea, std::string> rtAreaNames = {
|
||||
{ RTAREA_LAKE_HYLIA, "Lake Hylia"},
|
||||
{ RTAREA_GERUDO_VALLEY, "Gerudo Valley"},
|
||||
{ RTAREA_GERUDO_FORTRESS, "Gerudo Fortress"},
|
||||
{ RTAREA_WASTELAND, "Desert Wasteland"},
|
||||
{ RTAREA_WASTELAND, "Haunted Wasteland"},
|
||||
{ RTAREA_DESERT_COLOSSUS, "Desert Colossus"},
|
||||
{ RTAREA_MARKET, "Hyrule Market"},
|
||||
{ RTAREA_HYRULE_CASTLE, "Hyrule Castle"},
|
||||
|
||||
+170
-13
@@ -49,6 +49,9 @@
|
||||
#include "Fonts.h"
|
||||
#include <Utils/StringHelper.h>
|
||||
#include "Enhancements/custom-message/CustomMessageManager.h"
|
||||
#include "Enhancements/presets.h"
|
||||
#include "util.h"
|
||||
#include <boost_custom/container_hash/hash_32.hpp>
|
||||
|
||||
#if not defined (__SWITCH__) && not defined(__WIIU__)
|
||||
#include "Extractor/Extract.h"
|
||||
@@ -115,6 +118,8 @@ CrowdControl* CrowdControl::Instance;
|
||||
|
||||
#include "soh/config/ConfigUpdaters.h"
|
||||
|
||||
void SoH_ProcessDroppedFiles(std::string filePath);
|
||||
|
||||
OTRGlobals* OTRGlobals::Instance;
|
||||
SaveManager* SaveManager::Instance;
|
||||
CustomMessageManager* CustomMessageManager::Instance;
|
||||
@@ -253,15 +258,26 @@ OTRGlobals::OTRGlobals() {
|
||||
OTRFiles.push_back(sohOtrPath);
|
||||
}
|
||||
std::string patchesPath = LUS::Context::LocateFileAcrossAppDirs("mods", appShortName);
|
||||
std::vector<std::string> patchOTRs = {};
|
||||
if (patchesPath.length() > 0 && std::filesystem::exists(patchesPath)) {
|
||||
if (std::filesystem::is_directory(patchesPath)) {
|
||||
for (const auto& p : std::filesystem::recursive_directory_iterator(patchesPath)) {
|
||||
for (const auto& p : std::filesystem::recursive_directory_iterator(patchesPath, std::filesystem::directory_options::follow_directory_symlink)) {
|
||||
if (StringHelper::IEquals(p.path().extension().string(), ".otr")) {
|
||||
OTRFiles.push_back(p.path().generic_string());
|
||||
patchOTRs.push_back(p.path().generic_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
std::sort(patchOTRs.begin(), patchOTRs.end(), [](const std::string& a, const std::string& b) {
|
||||
return std::lexicographical_compare(
|
||||
a.begin(), a.end(),
|
||||
b.begin(), b.end(),
|
||||
[](char c1, char c2) {
|
||||
return std::tolower(c1) < std::tolower(c2);
|
||||
}
|
||||
);
|
||||
});
|
||||
OTRFiles.insert(OTRFiles.end(), patchOTRs.begin(), patchOTRs.end());
|
||||
std::unordered_set<uint32_t> ValidHashes = {
|
||||
OOT_PAL_MQ,
|
||||
OOT_NTSC_JP_MQ,
|
||||
@@ -1021,9 +1037,9 @@ extern "C" void InitOTR() {
|
||||
OTRGlobals::Instance = new OTRGlobals();
|
||||
CustomMessageManager::Instance = new CustomMessageManager();
|
||||
ItemTableManager::Instance = new ItemTableManager();
|
||||
GameInteractor::Instance = new GameInteractor();
|
||||
SaveManager::Instance = new SaveManager();
|
||||
SohGui::SetupGuiElements();
|
||||
GameInteractor::Instance = new GameInteractor();
|
||||
AudioCollection::Instance = new AudioCollection();
|
||||
ActorDB::Instance = new ActorDB();
|
||||
#ifdef __APPLE__
|
||||
@@ -1043,6 +1059,11 @@ extern "C" void InitOTR() {
|
||||
|
||||
InitMods();
|
||||
ActorDB::AddBuiltInCustomActors();
|
||||
// #region SOH [Randomizer] TODO: Remove these and refactor spoiler file handling for randomizer
|
||||
CVarClear("gRandomizerNewFileDropped");
|
||||
CVarClear("gRandomizerDroppedFile");
|
||||
// #endregion
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnFileDropped>(SoH_ProcessDroppedFiles);
|
||||
|
||||
time_t now = time(NULL);
|
||||
tm *tm_now = localtime(&now);
|
||||
@@ -1131,7 +1152,7 @@ extern "C" void Graph_ProcessFrame(void (*run_one_game_iter)(void)) {
|
||||
OTRGlobals::Instance->context->GetWindow()->MainLoop(run_one_game_iter);
|
||||
}
|
||||
|
||||
extern bool ShouldClearTextureCacheAtEndOfFrame;
|
||||
extern bool ToggleAltAssetsAtEndOfFrame;
|
||||
|
||||
extern "C" void Graph_StartFrame() {
|
||||
#ifndef __WIIU__
|
||||
@@ -1214,13 +1235,21 @@ extern "C" void Graph_StartFrame() {
|
||||
}
|
||||
#endif
|
||||
case KbScancode::LUS_KB_TAB: {
|
||||
// Toggle HD Assets
|
||||
CVarSetInteger("gAltAssets", !CVarGetInteger("gAltAssets", 0));
|
||||
ShouldClearTextureCacheAtEndOfFrame = true;
|
||||
ToggleAltAssetsAtEndOfFrame = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (CVarGetInteger("gNewFileDropped", 0)) {
|
||||
std::string filePath = SohUtils::Sanitize(CVarGetString("gDroppedFile", ""));
|
||||
if (!filePath.empty()) {
|
||||
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnFileDropped>(filePath);
|
||||
}
|
||||
CVarClear("gNewFileDropped");
|
||||
CVarClear("gDroppedFile");
|
||||
}
|
||||
|
||||
OTRGlobals::Instance->context->GetWindow()->StartFrame();
|
||||
}
|
||||
|
||||
@@ -1286,10 +1315,14 @@ extern "C" void Graph_ProcessGfxCommands(Gfx* commands) {
|
||||
}
|
||||
}
|
||||
|
||||
if (ShouldClearTextureCacheAtEndOfFrame) {
|
||||
if (ToggleAltAssetsAtEndOfFrame) {
|
||||
ToggleAltAssetsAtEndOfFrame = false;
|
||||
|
||||
// Actually update the CVar now before runing the alt asset update listeners
|
||||
CVarSetInteger("gAltAssets", !CVarGetInteger("gAltAssets", 0));
|
||||
gfx_texture_cache_clear();
|
||||
LUS::SkeletonPatcher::UpdateSkeletons();
|
||||
ShouldClearTextureCacheAtEndOfFrame = false;
|
||||
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnAssetAltChange>();
|
||||
}
|
||||
|
||||
// OTRTODO: FIGURE OUT END FRAME POINT
|
||||
@@ -1415,6 +1448,14 @@ extern "C" void ResourceMgr_DirtyDirectory(const char* resName) {
|
||||
LUS::Context::GetInstance()->GetResourceManager()->DirtyDirectory(resName);
|
||||
}
|
||||
|
||||
extern "C" void ResourceMgr_UnloadResource(const char* resName) {
|
||||
std::string path = resName;
|
||||
if (path.substr(0, 7) == "__OTR__") {
|
||||
path = path.substr(7);
|
||||
}
|
||||
auto res = LUS::Context::GetInstance()->GetResourceManager()->UnloadResource(path);
|
||||
}
|
||||
|
||||
// OTRTODO: There is probably a more elegant way to go about this...
|
||||
// Kenix: This is definitely leaking memory when it's called.
|
||||
extern "C" char** ResourceMgr_ListFiles(const char* searchMask, int* resultSize) {
|
||||
@@ -1441,6 +1482,27 @@ extern "C" uint8_t ResourceMgr_FileExists(const char* filePath) {
|
||||
return ExtensionCache.contains(path);
|
||||
}
|
||||
|
||||
extern "C" uint8_t ResourceMgr_FileAltExists(const char* filePath) {
|
||||
std::string path = filePath;
|
||||
if (path.substr(0, 7) == "__OTR__") {
|
||||
path = path.substr(7);
|
||||
}
|
||||
|
||||
if (path.substr(0, 4) != "alt/") {
|
||||
path = "alt/" + path;
|
||||
}
|
||||
|
||||
return ExtensionCache.contains(path);
|
||||
}
|
||||
|
||||
// Unloads a resource if an alternate version exists when alt assets are enabled
|
||||
// The resource is only removed from the internal cache to prevent it from used in the next resource lookup
|
||||
extern "C" void ResourceMgr_UnloadOriginalWhenAltExists(const char* resName) {
|
||||
if (CVarGetInteger("gAltAssets", 0) && ResourceMgr_FileAltExists((char*) resName)) {
|
||||
ResourceMgr_UnloadResource((char*) resName);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void ResourceMgr_LoadFile(const char* resName) {
|
||||
LUS::Context::GetInstance()->GetResourceManager()->LoadResource(resName);
|
||||
}
|
||||
@@ -1480,6 +1542,11 @@ extern "C" char* ResourceMgr_LoadFileFromDisk(const char* filePath) {
|
||||
return data;
|
||||
}
|
||||
|
||||
extern "C" uint8_t ResourceMgr_TexIsRaw(const char* texPath) {
|
||||
auto res = std::static_pointer_cast<LUS::Texture>(GetResourceByNameHandlingMQ(texPath));
|
||||
return res->Flags & TEX_FLAG_LOAD_AS_RAW;
|
||||
}
|
||||
|
||||
extern "C" uint8_t ResourceMgr_ResourceIsBackground(char* texPath) {
|
||||
auto res = GetResourceByNameHandlingMQ(texPath);
|
||||
return res->GetInitData()->Type == LUS::ResourceType::SOH_Background;
|
||||
@@ -1565,10 +1632,20 @@ extern "C" void ResourceMgr_PushCurrentDirectory(char* path)
|
||||
|
||||
extern "C" Gfx* ResourceMgr_LoadGfxByName(const char* path)
|
||||
{
|
||||
// When an alt resource exists for the DL, we need to unload the original asset
|
||||
// to clear the cache so the alt asset will be loaded instead
|
||||
// OTRTODO: If Alt loading over original cache is fixed, this line can most likely be removed
|
||||
ResourceMgr_UnloadOriginalWhenAltExists(path);
|
||||
|
||||
auto res = std::static_pointer_cast<LUS::DisplayList>(GetResourceByNameHandlingMQ(path));
|
||||
return (Gfx*)&res->Instructions[0];
|
||||
}
|
||||
|
||||
extern "C" uint8_t ResourceMgr_FileIsCustomByName(const char* path) {
|
||||
auto res = std::static_pointer_cast<LUS::DisplayList>(GetResourceByNameHandlingMQ(path));
|
||||
return res->GetInitData()->IsCustom;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
int index;
|
||||
Gfx instruction;
|
||||
@@ -1600,6 +1677,11 @@ extern "C" void ResourceMgr_PatchGfxByName(const char* path, const char* patchNa
|
||||
// index /= 2;
|
||||
// }
|
||||
|
||||
// Do not patch custom assets as they most likely do not have the same instructions as authentic assets
|
||||
if (res->GetInitData()->IsCustom) {
|
||||
return;
|
||||
}
|
||||
|
||||
Gfx* gfx = (Gfx*)&res->Instructions[index];
|
||||
|
||||
if (!originalGfx.contains(path) || !originalGfx[path].contains(patchName)) {
|
||||
@@ -1616,6 +1698,11 @@ extern "C" void ResourceMgr_PatchGfxCopyCommandByName(const char* path, const ch
|
||||
auto res = std::static_pointer_cast<LUS::DisplayList>(
|
||||
LUS::Context::GetInstance()->GetResourceManager()->LoadResource(path));
|
||||
|
||||
// Do not patch custom assets as they most likely do not have the same instructions as authentic assets
|
||||
if (res->GetInitData()->IsCustom) {
|
||||
return;
|
||||
}
|
||||
|
||||
Gfx* destinationGfx = (Gfx*)&res->Instructions[destinationIndex];
|
||||
Gfx sourceGfx = res->Instructions[sourceIndex];
|
||||
|
||||
@@ -2030,10 +2117,10 @@ Color_RGB8 GetColorForControllerLED() {
|
||||
if (source == LED_SOURCE_CUSTOM) {
|
||||
color = CVarGetColor24("gLedPort1Color", { 255, 255, 255 });
|
||||
}
|
||||
if (criticalOverride || source == LED_SOURCE_HEALTH) {
|
||||
if (gPlayState && (criticalOverride || source == LED_SOURCE_HEALTH)) {
|
||||
if (HealthMeter_IsCritical()) {
|
||||
color = { 0xFF, 0, 0 };
|
||||
} else if (source == LED_SOURCE_HEALTH) {
|
||||
} else if (gSaveContext.healthCapacity != 0 && source == LED_SOURCE_HEALTH) {
|
||||
if (gSaveContext.health / gSaveContext.healthCapacity <= 0.4f) {
|
||||
color = { 0xFF, 0xFF, 0 };
|
||||
} else {
|
||||
@@ -2419,6 +2506,8 @@ extern "C" int CustomMessage_RetrieveIfExists(PlayState* play) {
|
||||
messageEntry = OTRGlobals::Instance->gRandomizer->GetWarpSongMessage(textId, Randomizer_GetSettingValue(RSK_WARP_SONG_HINTS) == RO_GENERIC_OFF);
|
||||
} else if (textId == TEXT_LAKE_HYLIA_WATER_SWITCH_NAVI || textId == TEXT_LAKE_HYLIA_WATER_SWITCH_SIGN) {
|
||||
messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::hintMessageTableID, textId);
|
||||
} else if (textId == TEXT_SHOOTING_GALLERY_MAN_COME_BACK_WITH_BOW) {
|
||||
messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::hintMessageTableID, TEXT_SHOOTING_GALLERY_MAN_COME_BACK_WITH_BOW);
|
||||
} else if (textId == 0x3052 || (textId >= 0x3069 && textId <= 0x3070)) { //Fire Temple gorons
|
||||
u16 choice = Random(0, NUM_GORON_MESSAGES);
|
||||
messageEntry = OTRGlobals::Instance->gRandomizer->GetGoronMessage(choice);
|
||||
@@ -2508,6 +2597,74 @@ extern "C" void Gfx_RegisterBlendedTexture(const char* name, u8* mask, u8* repla
|
||||
gfx_register_blended_texture(name, mask, replacement);
|
||||
}
|
||||
|
||||
extern "C" void CheckTracker_OnMessageClose() {
|
||||
CheckTracker::CheckTrackerDialogClosed();
|
||||
void SoH_ProcessDroppedFiles(std::string filePath) {
|
||||
try {
|
||||
std::ifstream configStream(filePath);
|
||||
if (!configStream) {
|
||||
return;
|
||||
}
|
||||
|
||||
nlohmann::json configJson;
|
||||
configStream >> configJson;
|
||||
|
||||
// #region SOH [Randomizer] TODO: Refactor spoiler file handling for randomizer
|
||||
if (configJson.contains("version") && configJson.contains("finalSeed")) {
|
||||
CVarSetString("gRandomizerDroppedFile", filePath.c_str());
|
||||
CVarSetInteger("gRandomizerNewFileDropped", 1);
|
||||
return;
|
||||
}
|
||||
// #endregion
|
||||
|
||||
if (!configJson.contains("CVars")) {
|
||||
return;
|
||||
}
|
||||
|
||||
clearCvars(enhancementsCvars);
|
||||
clearCvars(cheatCvars);
|
||||
clearCvars(randomizerCvars);
|
||||
|
||||
// Flatten everything under CVars into a single array
|
||||
auto cvars = configJson["CVars"].flatten();
|
||||
|
||||
for (auto& [key, value] : cvars.items()) {
|
||||
// Replace slashes with dots in key, and remove leading dot
|
||||
std::string path = key;
|
||||
std::replace(path.begin(), path.end(), '/', '.');
|
||||
if (path[0] == '.') {
|
||||
path.erase(0, 1);
|
||||
}
|
||||
if (value.is_string()) {
|
||||
CVarSetString(path.c_str(), value.get<std::string>().c_str());
|
||||
} else if (value.is_number_integer()) {
|
||||
CVarSetInteger(path.c_str(), value.get<int>());
|
||||
} else if (value.is_number_float()) {
|
||||
CVarSetFloat(path.c_str(), value.get<float>());
|
||||
}
|
||||
}
|
||||
|
||||
auto gui = LUS::Context::GetInstance()->GetWindow()->GetGui();
|
||||
gui->GetGuiWindow("Console")->Hide();
|
||||
gui->GetGuiWindow("Actor Viewer")->Hide();
|
||||
gui->GetGuiWindow("Collision Viewer")->Hide();
|
||||
gui->GetGuiWindow("Save Editor")->Hide();
|
||||
gui->GetGuiWindow("Display List Viewer")->Hide();
|
||||
gui->GetGuiWindow("Stats")->Hide();
|
||||
std::dynamic_pointer_cast<LUS::ConsoleWindow>(LUS::Context::GetInstance()->GetWindow()->GetGui()->GetGuiWindow("Console"))->ClearBindings();
|
||||
|
||||
gui->SaveConsoleVariablesOnNextTick();
|
||||
|
||||
uint32_t finalHash = boost::hash_32<std::string>{}(configJson.dump());
|
||||
gui->GetGameOverlay()->TextDrawNotification(30.0f, true, "Configuration Loaded. Hash: %d", finalHash);
|
||||
} catch (std::exception& e) {
|
||||
SPDLOG_ERROR("Failed to load config file: {}", e.what());
|
||||
auto gui = LUS::Context::GetInstance()->GetWindow()->GetGui();
|
||||
gui->GetGameOverlay()->TextDrawNotification(30.0f, true, "Failed to load config file");
|
||||
return;
|
||||
} catch (...) {
|
||||
SPDLOG_ERROR("Failed to load config file");
|
||||
auto gui = LUS::Context::GetInstance()->GetWindow()->GetGui();
|
||||
gui->GetGameOverlay()->TextDrawNotification(30.0f, true, "Failed to load config file");
|
||||
return;
|
||||
}
|
||||
}
|
||||
// #endregion
|
||||
|
||||
@@ -86,11 +86,15 @@ uint32_t ResourceMgr_GetGameVersion(int index);
|
||||
uint32_t ResourceMgr_GetGamePlatform(int index);
|
||||
uint32_t ResourceMgr_GetGameRegion(int index);
|
||||
void ResourceMgr_LoadDirectory(const char* resName);
|
||||
void ResourceMgr_UnloadResource(const char* resName);
|
||||
char** ResourceMgr_ListFiles(const char* searchMask, int* resultSize);
|
||||
uint8_t ResourceMgr_FileExists(const char* resName);
|
||||
uint8_t ResourceMgr_FileAltExists(const char* resName);
|
||||
void ResourceMgr_UnloadOriginalWhenAltExists(const char* resName);
|
||||
char* GetResourceDataByNameHandlingMQ(const char* path);
|
||||
void ResourceMgr_LoadFile(const char* resName);
|
||||
char* ResourceMgr_LoadFileFromDisk(const char* filePath);
|
||||
uint8_t ResourceMgr_TexIsRaw(const char* texPath);
|
||||
uint8_t ResourceMgr_ResourceIsBackground(char* texPath);
|
||||
char* ResourceMgr_LoadJPEG(char* data, size_t dataSize);
|
||||
uint16_t ResourceMgr_LoadTexWidthByName(char* texPath);
|
||||
@@ -101,6 +105,7 @@ AnimationHeaderCommon* ResourceMgr_LoadAnimByName(const char* path);
|
||||
char* ResourceMgr_GetNameByCRC(uint64_t crc, char* alloc);
|
||||
Gfx* ResourceMgr_LoadGfxByCRC(uint64_t crc);
|
||||
Gfx* ResourceMgr_LoadGfxByName(const char* path);
|
||||
uint8_t ResourceMgr_FileIsCustomByName(const char* path);
|
||||
void ResourceMgr_PatchGfxByName(const char* path, const char* patchName, int index, Gfx instruction);
|
||||
void ResourceMgr_UnpatchGfxByName(const char* path, const char* patchName);
|
||||
char* ResourceMgr_LoadArrayByNameAsVec3s(const char* path);
|
||||
|
||||
+63
-30
@@ -47,6 +47,11 @@ std::filesystem::path SaveManager::GetFileName(int fileNum) {
|
||||
return sSavePath / ("file" + std::to_string(fileNum + 1) + ".sav");
|
||||
}
|
||||
|
||||
std::filesystem::path SaveManager::GetFileTempName(int fileNum) {
|
||||
const std::filesystem::path sSavePath(LUS::Context::GetPathRelativeToAppDirectory("Save"));
|
||||
return sSavePath / ("file" + std::to_string(fileNum + 1) + ".temp");
|
||||
}
|
||||
|
||||
SaveManager::SaveManager() {
|
||||
coreSectionIDsByName["base"] = SECTION_ID_BASE;
|
||||
coreSectionIDsByName["randomizer"] = SECTION_ID_RANDOMIZER;
|
||||
@@ -65,6 +70,10 @@ SaveManager::SaveManager() {
|
||||
|
||||
AddInitFunction(InitFileImpl);
|
||||
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnExitGame>([this](uint32_t fileNum) { ThreadPoolWait(); });
|
||||
|
||||
smThreadPool = std::make_shared<BS::thread_pool>(1);
|
||||
|
||||
for (SaveFileMetaInfo& info : fileMetaInfo) {
|
||||
info.valid = false;
|
||||
info.deaths = 0;
|
||||
@@ -357,12 +366,14 @@ void SaveManager::SaveRandomizer(SaveContext* saveContext, int sectionID, bool f
|
||||
});
|
||||
}
|
||||
|
||||
// Init() here is an extension of InitSram, and thus not truly an initializer for SaveManager itself. don't put any class initialization stuff here
|
||||
void SaveManager::Init() {
|
||||
// Wait on saves that snuck through the Wait in OnExitGame
|
||||
ThreadPoolWait();
|
||||
const std::filesystem::path sSavePath(LUS::Context::GetPathRelativeToAppDirectory("Save"));
|
||||
const std::filesystem::path sGlobalPath = sSavePath / std::string("global.sav");
|
||||
auto sOldSavePath = LUS::Context::GetPathRelativeToAppDirectory("oot_save.sav");
|
||||
auto sOldBackupSavePath = LUS::Context::GetPathRelativeToAppDirectory("oot_save.bak");
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnExitGame>([this](uint32_t fileNum) { ThreadPoolWait(); });
|
||||
|
||||
// If the save directory does not exist, create it
|
||||
if (!std::filesystem::exists(sSavePath)) {
|
||||
@@ -403,7 +414,6 @@ void SaveManager::Init() {
|
||||
} else {
|
||||
CreateDefaultGlobal();
|
||||
}
|
||||
smThreadPool = std::make_shared<BS::thread_pool>(1);
|
||||
|
||||
// Load files to initialize metadata
|
||||
for (int fileNum = 0; fileNum < MaxFiles; fileNum++) {
|
||||
@@ -869,6 +879,32 @@ void SaveManager::InitFileMaxed() {
|
||||
gSaveContext.sceneFlags[5].swch = 0x40000000;
|
||||
}
|
||||
|
||||
#if defined(__WIIU__) || defined(__SWITCH__)
|
||||
// std::filesystem::copy_file doesn't work properly with the Wii U's toolchain atm
|
||||
int copy_file(const char* src, const char* dst) {
|
||||
alignas(0x40) uint8_t buf[4096];
|
||||
FILE* r = fopen(src, "r");
|
||||
if (!r) {
|
||||
return -1;
|
||||
}
|
||||
FILE* w = fopen(dst, "w");
|
||||
if (!w) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
size_t res;
|
||||
while ((res = fread(buf, 1, sizeof(buf), r)) > 0) {
|
||||
if (fwrite(buf, 1, res, w) != res) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(r);
|
||||
fclose(w);
|
||||
return res >= 0 ? 0 : res;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Threaded SaveFile takes copy of gSaveContext for local unmodified storage
|
||||
|
||||
void SaveManager::SaveFileThreaded(int fileNum, SaveContext* saveContext, int sectionID) {
|
||||
@@ -910,19 +946,42 @@ void SaveManager::SaveFileThreaded(int fileNum, SaveContext* saveContext, int se
|
||||
svi.func(saveContext, sectionID, false);
|
||||
}
|
||||
|
||||
std::filesystem::path fileName = GetFileName(fileNum);
|
||||
std::filesystem::path tempFile = GetFileTempName(fileNum);
|
||||
|
||||
if (std::filesystem::exists(tempFile)) {
|
||||
std::filesystem::remove(tempFile);
|
||||
}
|
||||
|
||||
#if defined(__SWITCH__) || defined(__WIIU__)
|
||||
FILE* w = fopen(GetFileName(fileNum).c_str(), "w");
|
||||
FILE* w = fopen(tempFile.c_str(), "w");
|
||||
std::string json_string = saveBlock.dump(4);
|
||||
fwrite(json_string.c_str(), sizeof(char), json_string.length(), w);
|
||||
fclose(w);
|
||||
#else
|
||||
std::ofstream output(GetFileName(fileNum));
|
||||
std::ofstream output(tempFile);
|
||||
output << std::setw(4) << saveBlock << std::endl;
|
||||
output.close();
|
||||
#endif
|
||||
|
||||
if (std::filesystem::exists(fileName)) {
|
||||
std::filesystem::remove(fileName);
|
||||
}
|
||||
|
||||
#if defined(__SWITCH__) || defined(__WIIU__)
|
||||
copy_file(tempFile.c_str(), fileName.c_str());
|
||||
#else
|
||||
std::filesystem::copy_file(tempFile, fileName);
|
||||
#endif
|
||||
|
||||
if (std::filesystem::exists(tempFile)) {
|
||||
std::filesystem::remove(tempFile);
|
||||
}
|
||||
|
||||
delete saveContext;
|
||||
InitMeta(fileNum);
|
||||
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnSaveFile>(fileNum);
|
||||
SPDLOG_INFO("Save File Finish - fileNum: {}", fileNum);
|
||||
}
|
||||
|
||||
// SaveSection creates a copy of gSaveContext to prevent mid-save data modification, and passes its reference to SaveFileThreaded
|
||||
@@ -2105,32 +2164,6 @@ void SaveManager::LoadStruct(const std::string& name, LoadStructFunc func) {
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(__WIIU__) || defined(__SWITCH__)
|
||||
// std::filesystem::copy_file doesn't work properly with the Wii U's toolchain atm
|
||||
int copy_file(const char* src, const char* dst) {
|
||||
alignas(0x40) uint8_t buf[4096];
|
||||
FILE* r = fopen(src, "r");
|
||||
if (!r) {
|
||||
return -1;
|
||||
}
|
||||
FILE* w = fopen(dst, "w");
|
||||
if (!w) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
size_t res;
|
||||
while ((res = fread(buf, 1, sizeof(buf), r)) > 0) {
|
||||
if (fwrite(buf, 1, res, w) != res) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(r);
|
||||
fclose(w);
|
||||
return res >= 0 ? 0 : res;
|
||||
}
|
||||
#endif
|
||||
|
||||
void SaveManager::CopyZeldaFile(int from, int to) {
|
||||
assert(std::filesystem::exists(GetFileName(from)));
|
||||
DeleteZeldaFile(to);
|
||||
|
||||
@@ -142,6 +142,7 @@ class SaveManager {
|
||||
|
||||
private:
|
||||
std::filesystem::path GetFileName(int fileNum);
|
||||
std::filesystem::path GetFileTempName(int fileNum);
|
||||
nlohmann::json saveBlock;
|
||||
|
||||
void ConvertFromUnversioned();
|
||||
|
||||
+1
-1
@@ -38,7 +38,7 @@
|
||||
#include "Enhancements/game-interactor/GameInteractor.h"
|
||||
#include "Enhancements/cosmetics/authenticGfxPatches.h"
|
||||
|
||||
bool ShouldClearTextureCacheAtEndOfFrame = false;
|
||||
bool ToggleAltAssetsAtEndOfFrame = false;
|
||||
bool isBetaQuestEnabled = false;
|
||||
|
||||
extern "C" {
|
||||
|
||||
+26
-4
@@ -28,7 +28,7 @@
|
||||
#include "Enhancements/randomizer/randomizer_item_tracker.h"
|
||||
#include "Enhancements/randomizer/randomizer_settings_window.h"
|
||||
|
||||
extern bool ShouldClearTextureCacheAtEndOfFrame;
|
||||
extern bool ToggleAltAssetsAtEndOfFrame;
|
||||
extern bool isBetaQuestEnabled;
|
||||
|
||||
extern "C" PlayState* gPlayState;
|
||||
@@ -492,6 +492,8 @@ extern std::shared_ptr<GameplayStatsWindow> mGameplayStatsWindow;
|
||||
void DrawEnhancementsMenu() {
|
||||
if (ImGui::BeginMenu("Enhancements"))
|
||||
{
|
||||
ImGui::BeginDisabled(CVarGetInteger("gDisableChangingSettings", 0));
|
||||
|
||||
DrawPresetSelector(PRESET_TYPE_ENHANCEMENTS);
|
||||
|
||||
UIWidgets::PaddedSeparator();
|
||||
@@ -532,6 +534,8 @@ void DrawEnhancementsMenu() {
|
||||
" - Small keys: Small silver chest\n"
|
||||
" - Boss keys: Vanilla size and texture\n"
|
||||
" - Skulltula Tokens: Small skulltula chest\n"
|
||||
"\n"
|
||||
"NOTE: Textures will not apply if you are using a mod pack with a custom chest model."
|
||||
);
|
||||
if (CVarGetInteger("gChestSizeAndTextureMatchesContents", CSMC_DISABLED) != CSMC_DISABLED) {
|
||||
UIWidgets::PaddedEnhancementCheckbox("Chests of Agony", "gChestSizeDependsStoneOfAgony", true, false);
|
||||
@@ -603,7 +607,7 @@ void DrawEnhancementsMenu() {
|
||||
UIWidgets::PaddedEnhancementCheckbox("Nuts explode bombs", "gNutsExplodeBombs", true, false);
|
||||
UIWidgets::Tooltip("Makes nuts explode bombs, similar to how they interact with bombchus. This does not affect bombflowers.");
|
||||
UIWidgets::PaddedEnhancementCheckbox("Equip Multiple Arrows at Once", "gSeparateArrows", true, false);
|
||||
UIWidgets::Tooltip("Allow the bow and magic arrows to be equipped at the same time on different slots");
|
||||
UIWidgets::Tooltip("Allow the bow and magic arrows to be equipped at the same time on different slots. (Note this will disable the behaviour of the 'Equip Dupe' glitch)");
|
||||
UIWidgets::PaddedEnhancementCheckbox("Bow as Child/Slingshot as Adult", "gBowSlingShotAmmoFix", true, false);
|
||||
UIWidgets::Tooltip("Allows child to use bow with arrows.\nAllows adult to use slingshot with seeds.\n\nRequires glitches or 'Timeless Equipment' cheat to equip.");
|
||||
UIWidgets::PaddedEnhancementCheckbox("Better Farore's Wind", "gBetterFW", true, false);
|
||||
@@ -904,7 +908,10 @@ void DrawEnhancementsMenu() {
|
||||
{
|
||||
if (ImGui::BeginMenu("Mods")) {
|
||||
if (UIWidgets::PaddedEnhancementCheckbox("Use Alternate Assets", "gAltAssets", false, false)) {
|
||||
ShouldClearTextureCacheAtEndOfFrame = true;
|
||||
// The checkbox will flip the alt asset CVar, but we instead want it to change at the end of the game frame
|
||||
// We toggle it back while setting the flag to update the CVar later
|
||||
CVarSetInteger("gAltAssets", !CVarGetInteger("gAltAssets", 0));
|
||||
ToggleAltAssetsAtEndOfFrame = true;
|
||||
}
|
||||
UIWidgets::Tooltip("Toggle between standard assets and alternate assets. Usually mods will indicate if this setting has to be used or not.");
|
||||
UIWidgets::PaddedEnhancementCheckbox("Disable Bomb Billboarding", "gDisableBombBillboarding", true, false);
|
||||
@@ -1059,6 +1066,11 @@ void DrawEnhancementsMenu() {
|
||||
UIWidgets::Tooltip("Fixes the bushes to drop items correctly rather than spawning undefined items.");
|
||||
UIWidgets::PaddedEnhancementCheckbox("Fix falling from vine edges", "gFixVineFall", true, false);
|
||||
UIWidgets::Tooltip("Prevents immediately falling off climbable surfaces if climbing on the edges.");
|
||||
UIWidgets::PaddedEnhancementCheckbox("Fix Link's eyes open while sleeping", "gFixEyesOpenWhileSleeping", true, false);
|
||||
UIWidgets::Tooltip("Fixes Link's eyes being open in the opening cutscene when he is supposed to be sleeping.");
|
||||
UIWidgets::PaddedEnhancementCheckbox("Fix Darunia dancing too fast", "gEnhancements.FixDaruniaDanceSpeed",
|
||||
true, false, false, "", UIWidgets::CheckboxGraphics::Cross, true);
|
||||
UIWidgets::Tooltip("Fixes Darunia's dancing speed so he dances to the beat of Saria's Song, like in vanilla.");
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
@@ -1194,6 +1206,8 @@ void DrawEnhancementsMenu() {
|
||||
|
||||
UIWidgets::PaddedSeparator(true, true, 2.0f, 2.0f);
|
||||
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(12.0f, 6.0f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0, 0));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
|
||||
@@ -1235,6 +1249,8 @@ void DrawEnhancementsMenu() {
|
||||
void DrawCheatsMenu() {
|
||||
if (ImGui::BeginMenu("Cheats"))
|
||||
{
|
||||
ImGui::BeginDisabled(CVarGetInteger("gDisableChangingSettings", 0));
|
||||
|
||||
if (ImGui::BeginMenu("Infinite...")) {
|
||||
UIWidgets::EnhancementCheckbox("Money", "gInfiniteMoney");
|
||||
UIWidgets::PaddedEnhancementCheckbox("Health", "gInfiniteHealth", true, false);
|
||||
@@ -1389,6 +1405,8 @@ void DrawCheatsMenu() {
|
||||
}
|
||||
UIWidgets::Tooltip("Clears the cutscene pointer to a value safe for wrong warps.");
|
||||
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
@@ -1402,6 +1420,8 @@ extern std::shared_ptr<DLViewerWindow> mDLViewerWindow;
|
||||
|
||||
void DrawDeveloperToolsMenu() {
|
||||
if (ImGui::BeginMenu("Developer Tools")) {
|
||||
ImGui::BeginDisabled(CVarGetInteger("gDisableChangingSettings", 0));
|
||||
|
||||
UIWidgets::EnhancementCheckbox("OoT Debug Mode", "gDebugEnabled");
|
||||
UIWidgets::Tooltip("Enables Debug Mode, allowing you to select maps with L + R + Z, noclip with L + D-pad Right, and open the debug menu with L on the pause screen");
|
||||
if (CVarGetInteger("gDebugEnabled", 0)) {
|
||||
@@ -1474,6 +1494,8 @@ void DrawDeveloperToolsMenu() {
|
||||
ImGui::PopStyleVar(3);
|
||||
ImGui::PopStyleColor(1);
|
||||
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
@@ -1622,4 +1644,4 @@ void SohMenuBar::DrawElement() {
|
||||
ImGui::EndMenuBar();
|
||||
}
|
||||
}
|
||||
} // namespace SohGui
|
||||
} // namespace SohGui
|
||||
|
||||
@@ -103,38 +103,42 @@ void SkeletonFactoryV0::ParseFileXML(tinyxml2::XMLElement* reader, std::shared_p
|
||||
{
|
||||
std::shared_ptr<Skeleton> skel = std::static_pointer_cast<Skeleton>(resource);
|
||||
|
||||
std::string skeletonType = reader->Attribute("Type");
|
||||
// std::string skeletonLimbType = reader->Attribute("LimbType");
|
||||
int numLimbs = reader->IntAttribute("LimbCount");
|
||||
int numDLs = reader->IntAttribute("DisplayListCount");
|
||||
skel->type = SkeletonType::Flex; // Default to Flex for legacy reasons
|
||||
if (reader->FindAttribute("Type")) {
|
||||
std::string skeletonType = reader->Attribute("Type");
|
||||
|
||||
if (skeletonType == "Flex") {
|
||||
skel->type = SkeletonType::Flex;
|
||||
} else if (skeletonType == "Curve") {
|
||||
skel->type = SkeletonType::Curve;
|
||||
} else if (skeletonType == "Normal") {
|
||||
skel->type = SkeletonType::Normal;
|
||||
if (skeletonType == "Flex") {
|
||||
skel->type = SkeletonType::Flex;
|
||||
} else if (skeletonType == "Curve") {
|
||||
skel->type = SkeletonType::Curve;
|
||||
} else if (skeletonType == "Normal") {
|
||||
skel->type = SkeletonType::Normal;
|
||||
}
|
||||
}
|
||||
|
||||
skel->type = SkeletonType::Flex;
|
||||
skel->limbType = LimbType::LOD;
|
||||
skel->limbType = LimbType::LOD; // Default to LOD for legacy reasons
|
||||
if (reader->FindAttribute("LimbType")) {
|
||||
std::string skeletonLimbType = reader->Attribute("LimbType");
|
||||
|
||||
// if (skeletonLimbType == "Standard")
|
||||
// skel->limbType = LimbType::Standard;
|
||||
// else if (skeletonLimbType == "LOD")
|
||||
// skel->limbType = LimbType::LOD;
|
||||
// else if (skeletonLimbType == "Curve")
|
||||
// skel->limbType = LimbType::Curve;
|
||||
// else if (skeletonLimbType == "Skin")
|
||||
// skel->limbType = LimbType::Skin;
|
||||
// else if (skeletonLimbType == "Legacy")
|
||||
// Sskel->limbType = LimbType::Legacy;
|
||||
if (skeletonLimbType == "Standard") {
|
||||
skel->limbType = LimbType::Standard;
|
||||
} else if (skeletonLimbType == "LOD") {
|
||||
skel->limbType = LimbType::LOD;
|
||||
} else if (skeletonLimbType == "Curve") {
|
||||
skel->limbType = LimbType::Curve;
|
||||
} else if (skeletonLimbType == "Skin") {
|
||||
skel->limbType = LimbType::Skin;
|
||||
} else if (skeletonLimbType == "Legacy") {
|
||||
skel->limbType = LimbType::Legacy;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
skel->limbCount = reader->IntAttribute("LimbCount");
|
||||
skel->dListCount = reader->IntAttribute("DisplayListCount");
|
||||
|
||||
auto child = reader->FirstChildElement();
|
||||
|
||||
skel->limbCount = numLimbs;
|
||||
skel->dListCount = numDLs;
|
||||
|
||||
while (child != nullptr) {
|
||||
std::string childName = child->Name();
|
||||
|
||||
|
||||
@@ -132,15 +132,15 @@ void LUS::SkeletonLimbFactoryV0::ParseFileBinary(std::shared_ptr<BinaryReader> r
|
||||
skeletonLimb->limbData.lodLimb.sibling = skeletonLimb->siblingIndex;
|
||||
|
||||
if (skeletonLimb->dListPtr != "") {
|
||||
auto dList = LUS::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(skeletonLimb->dListPtr.c_str());
|
||||
skeletonLimb->limbData.lodLimb.dLists[0] = (Gfx*)(dList ? dList->GetRawPointer() : nullptr);
|
||||
skeletonLimb->dListPtr = "__OTR__" + skeletonLimb->dListPtr;
|
||||
skeletonLimb->limbData.lodLimb.dLists[0] = (Gfx*)skeletonLimb->dListPtr.c_str();
|
||||
} else {
|
||||
skeletonLimb->limbData.lodLimb.dLists[0] = nullptr;
|
||||
}
|
||||
|
||||
if (skeletonLimb->dList2Ptr != "") {
|
||||
auto dList = LUS::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(skeletonLimb->dList2Ptr.c_str());
|
||||
skeletonLimb->limbData.lodLimb.dLists[1] = (Gfx*)(dList ? dList->GetRawPointer() : nullptr);
|
||||
skeletonLimb->dList2Ptr = "__OTR__" + skeletonLimb->dList2Ptr;
|
||||
skeletonLimb->limbData.lodLimb.dLists[1] = (Gfx*)skeletonLimb->dList2Ptr.c_str();
|
||||
} else {
|
||||
skeletonLimb->limbData.lodLimb.dLists[1] = nullptr;
|
||||
}
|
||||
@@ -153,8 +153,8 @@ void LUS::SkeletonLimbFactoryV0::ParseFileBinary(std::shared_ptr<BinaryReader> r
|
||||
skeletonLimb->limbData.standardLimb.dList = nullptr;
|
||||
|
||||
if (!skeletonLimb->dListPtr.empty()) {
|
||||
const auto dList = LUS::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(skeletonLimb->dListPtr.c_str());
|
||||
skeletonLimb->limbData.standardLimb.dList = (Gfx*)(dList ? dList->GetRawPointer() : nullptr);
|
||||
skeletonLimb->dListPtr = "__OTR__" + skeletonLimb->dListPtr;
|
||||
skeletonLimb->limbData.standardLimb.dList = (Gfx*)skeletonLimb->dListPtr.c_str();
|
||||
}
|
||||
} else if (skeletonLimb->limbType == LUS::LimbType::Curve) {
|
||||
skeletonLimb->limbData.skelCurveLimb.firstChildIdx = skeletonLimb->childIndex;
|
||||
@@ -163,13 +163,13 @@ void LUS::SkeletonLimbFactoryV0::ParseFileBinary(std::shared_ptr<BinaryReader> r
|
||||
skeletonLimb->limbData.skelCurveLimb.dList[1] = nullptr;
|
||||
|
||||
if (!skeletonLimb->dListPtr.empty()) {
|
||||
const auto dList = LUS::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(skeletonLimb->dListPtr.c_str());
|
||||
skeletonLimb->limbData.skelCurveLimb.dList[0] = (Gfx*)(dList ? dList->GetRawPointer() : nullptr);
|
||||
skeletonLimb->dListPtr = "__OTR__" + skeletonLimb->dListPtr;
|
||||
skeletonLimb->limbData.skelCurveLimb.dList[0] = (Gfx*)skeletonLimb->dListPtr.c_str();
|
||||
}
|
||||
|
||||
if (!skeletonLimb->dList2Ptr.empty()) {
|
||||
const auto dList = LUS::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(skeletonLimb->dList2Ptr.c_str());
|
||||
skeletonLimb->limbData.skelCurveLimb.dList[1] = (Gfx*)(dList ? dList->GetRawPointer() : nullptr);
|
||||
skeletonLimb->dList2Ptr = "__OTR__" + skeletonLimb->dList2Ptr;
|
||||
skeletonLimb->limbData.skelCurveLimb.dList[1] = (Gfx*)skeletonLimb->dList2Ptr.c_str();
|
||||
}
|
||||
} else if (skeletonLimb->limbType == LUS::LimbType::Skin) {
|
||||
skeletonLimb->limbData.skinLimb.jointPos.x = skeletonLimb->transX;
|
||||
@@ -189,14 +189,23 @@ void LUS::SkeletonLimbFactoryV0::ParseFileBinary(std::shared_ptr<BinaryReader> r
|
||||
}
|
||||
|
||||
if (skeletonLimb->skinSegmentType == LUS::ZLimbSkinType::SkinType_DList) {
|
||||
auto res = LUS::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(skeletonLimb->skinDList.c_str());
|
||||
skeletonLimb->limbData.skinLimb.segment = res ? res->GetRawPointer() : nullptr;
|
||||
if (skeletonLimb->skinDList != "") {
|
||||
skeletonLimb->skinDList = "__OTR__" + skeletonLimb->skinDList;
|
||||
skeletonLimb->limbData.skinLimb.segment = (Gfx*)skeletonLimb->skinDList.c_str();
|
||||
} else {
|
||||
skeletonLimb->limbData.skinLimb.segment = nullptr;
|
||||
}
|
||||
} else if (skeletonLimb->skinSegmentType == LUS::ZLimbSkinType::SkinType_4) {
|
||||
skeletonLimb->skinAnimLimbData.totalVtxCount = skeletonLimb->skinVtxCnt;
|
||||
skeletonLimb->skinAnimLimbData.limbModifCount = skeletonLimb->skinLimbModifCount;
|
||||
skeletonLimb->skinAnimLimbData.limbModifications = skeletonLimb->skinLimbModifArray.data();
|
||||
auto res = LUS::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(skeletonLimb->skinDList2.c_str());
|
||||
skeletonLimb->skinAnimLimbData.dlist = (Gfx*)(res ? res->GetRawPointer() : nullptr);
|
||||
|
||||
if (skeletonLimb->skinDList2 != "") {
|
||||
skeletonLimb->skinDList2 = "__OTR__" + skeletonLimb->skinDList2;
|
||||
skeletonLimb->skinAnimLimbData.dlist = (Gfx*)skeletonLimb->skinDList2.c_str();
|
||||
} else {
|
||||
skeletonLimb->skinAnimLimbData.dlist = nullptr;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < skeletonLimb->skinLimbModifArray.size(); i++) {
|
||||
skeletonLimb->skinAnimLimbData.limbModifications[i].vtxCount = skeletonLimb->skinLimbModifVertexArrays[i].size();
|
||||
@@ -254,8 +263,8 @@ void SkeletonLimbFactoryV0::ParseFileXML(tinyxml2::XMLElement* reader, std::shar
|
||||
limbData.lodLimb.jointPos.z = skelLimb->transZ;
|
||||
|
||||
if (skelLimb->dListPtr != "") {
|
||||
auto res = LUS::Context::GetInstance()->GetResourceManager()->LoadResourceProcess((const char*)skelLimb->dListPtr.c_str());
|
||||
limbData.lodLimb.dLists[0] = (Gfx*)(res ? res->GetRawPointer() : nullptr);
|
||||
skelLimb->dListPtr = "__OTR__" + skelLimb->dListPtr;
|
||||
limbData.lodLimb.dLists[0] = (Gfx*)skelLimb->dListPtr.c_str();
|
||||
} else {
|
||||
limbData.lodLimb.dLists[0] = nullptr;
|
||||
}
|
||||
|
||||
+1
-1
@@ -260,7 +260,7 @@ void Audio_osWritebackDCache(void* mem, s32 size)
|
||||
|
||||
s32 osAiSetFrequency(u32 freq)
|
||||
{
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
s32 osEPiStartDma(OSPiHandle* handle, OSIoMesg* mb, s32 direction)
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <string.h>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
std::vector<std::string> sceneNames = {
|
||||
"Inside the Deku Tree",
|
||||
@@ -318,3 +319,20 @@ void SohUtils::CopyStringToCharArray(char* destination, std::string source, size
|
||||
strncpy(destination, source.c_str(), size - 1);
|
||||
destination[size - 1] = '\0';
|
||||
}
|
||||
|
||||
std::string SohUtils::Sanitize(std::string stringValue) {
|
||||
// Add backslashes.
|
||||
for (auto i = stringValue.begin();;) {
|
||||
auto const pos = std::find_if(i, stringValue.end(), [](char const c) { return '\\' == c || '\'' == c || '"' == c; });
|
||||
if (pos == stringValue.end()) {
|
||||
break;
|
||||
}
|
||||
i = std::next(stringValue.insert(pos, '\\'), 2);
|
||||
}
|
||||
|
||||
// Removes others.
|
||||
stringValue.erase(std::remove_if(stringValue.begin(), stringValue.end(), [](char const c) {
|
||||
return '\n' == c || '\r' == c || '\0' == c || '\x1A' == c; }), stringValue.end());
|
||||
|
||||
return stringValue;
|
||||
}
|
||||
|
||||
@@ -12,4 +12,6 @@ namespace SohUtils {
|
||||
// Copies a string and ensures the destination is null terminated if the source string is larger than size
|
||||
// Only up to size-1 characters are copied from the source string
|
||||
void CopyStringToCharArray(char* destination, std::string source, size_t size);
|
||||
|
||||
std::string Sanitize(std::string stringValue);
|
||||
} // namespace SohUtils
|
||||
|
||||
@@ -72,6 +72,9 @@ MessageTableEntry* OTRMessage_LoadTable(const char* filePath, bool isNES) {
|
||||
_message_0xFFFC_nes = (char*)file->messages[i].msg.c_str();
|
||||
}
|
||||
|
||||
// Assert that the first message starts at the first text ID
|
||||
assert(table[0].textId == 0x0001);
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
@@ -104,6 +107,9 @@ extern "C" void OTRMessage_Init()
|
||||
sStaffMessageEntryTablePtr[i].segment = file2->messages[i].msg.c_str();
|
||||
sStaffMessageEntryTablePtr[i].msgSize = file2->messages[i].msg.size();
|
||||
}
|
||||
|
||||
// Assert staff credits start at the first credits ID
|
||||
assert(sStaffMessageEntryTablePtr[0].textId == 0x0500);
|
||||
}
|
||||
|
||||
CustomMessageManager::Instance->AddCustomMessageTable(customMessageTableID);
|
||||
|
||||
@@ -204,6 +204,17 @@ u8 Inventory_DeleteEquipment(PlayState* play, s16 equipment) {
|
||||
|
||||
if (equipment == EQUIP_TYPE_TUNIC) {
|
||||
gSaveContext.equips.equipment |= EQUIP_VALUE_TUNIC_KOKIRI << (EQUIP_TYPE_TUNIC * 4);
|
||||
// non-vanilla: remove goron and zora tunics from item buttons if assignable tunics is on
|
||||
if (CVarGetInteger("gAssignableTunicsAndBoots", 0) && equipValue != EQUIP_VALUE_TUNIC_KOKIRI) {
|
||||
ItemID item = (equipValue == EQUIP_VALUE_TUNIC_GORON ? ITEM_TUNIC_GORON : ITEM_TUNIC_ZORA);
|
||||
for (int i = 1; i < ARRAY_COUNT(gSaveContext.equips.buttonItems); i++) {
|
||||
if (gSaveContext.equips.buttonItems[i] == item) {
|
||||
gSaveContext.equips.buttonItems[i] = ITEM_NONE;
|
||||
gSaveContext.equips.cButtonSlots[i - 1] = SLOT_NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
// end non-vanilla
|
||||
}
|
||||
|
||||
if (equipment == EQUIP_TYPE_SWORD) {
|
||||
|
||||
@@ -3384,7 +3384,7 @@ void Message_Update(PlayState* play) {
|
||||
}
|
||||
sLastPlayedSong = 0xFF;
|
||||
osSyncPrintf("OCARINA_MODE=%d chk_ocarina_no=%d\n", play->msgCtx.ocarinaMode, msgCtx->unk_E3F2);
|
||||
CheckTracker_OnMessageClose();
|
||||
// TODO: OnMessageClose hook
|
||||
break;
|
||||
case MSGMODE_PAUSED:
|
||||
break;
|
||||
|
||||
@@ -1469,6 +1469,7 @@ void Inventory_SwapAgeEquipment(void) {
|
||||
gSaveContext.equips.buttonItems[0] = ITEM_SWORD_MASTER;
|
||||
} else {
|
||||
gSaveContext.equips.buttonItems[0] = ITEM_NONE;
|
||||
Flags_SetInfTable(INFTABLE_SWORDLESS);
|
||||
}
|
||||
|
||||
if (gSaveContext.inventory.items[SLOT_NUT] != ITEM_NONE) {
|
||||
@@ -1520,18 +1521,25 @@ void Inventory_SwapAgeEquipment(void) {
|
||||
}
|
||||
}
|
||||
|
||||
// In Rando, when switching to adult for the second+ time, if a sword was not previously
|
||||
// equiped in MS shuffle, then we need to set the swordless flag again
|
||||
if (IS_RANDO && Randomizer_GetSettingValue(RSK_SHUFFLE_MASTER_SWORD) &&
|
||||
gSaveContext.equips.buttonItems[0] == ITEM_NONE) {
|
||||
Flags_SetInfTable(INFTABLE_SWORDLESS);
|
||||
}
|
||||
|
||||
gSaveContext.equips.equipment = gSaveContext.adultEquips.equipment;
|
||||
}
|
||||
} else {
|
||||
// When becoming child, set swordless flag if player doesn't have kokiri sword
|
||||
// Only in rando to keep swordless link bugs in vanilla
|
||||
if (IS_RANDO && (EQUIP_INV_SWORD_KOKIRI << (EQUIP_TYPE_SWORD * 4) & gSaveContext.inventory.equipment) == 0) {
|
||||
if (IS_RANDO && CHECK_OWNED_EQUIP(EQUIP_TYPE_SWORD, EQUIP_INV_SWORD_KOKIRI) == 0) {
|
||||
Flags_SetInfTable(INFTABLE_SWORDLESS);
|
||||
}
|
||||
|
||||
// When using enhancements, set swordless flag if player doesn't have kokiri sword or hasn't equipped a sword yet.
|
||||
// Then set the child equips button items to item none to ensure kokiri sword is not equipped
|
||||
if ((CVarGetInteger("gSwitchAge", 0) || CVarGetInteger("gSwitchTimeline", 0)) && ((EQUIP_INV_SWORD_KOKIRI << (EQUIP_TYPE_SWORD * 4) & gSaveContext.inventory.equipment) == 0 || Flags_GetInfTable(INFTABLE_SWORDLESS))) {
|
||||
if ((CVarGetInteger("gSwitchAge", 0) || CVarGetInteger("gSwitchTimeline", 0)) && (CHECK_OWNED_EQUIP(EQUIP_TYPE_SWORD, EQUIP_INV_SWORD_KOKIRI) == 0 || Flags_GetInfTable(INFTABLE_SWORDLESS))) {
|
||||
Flags_SetInfTable(INFTABLE_SWORDLESS);
|
||||
gSaveContext.childEquips.buttonItems[0] = ITEM_NONE;
|
||||
}
|
||||
@@ -1567,7 +1575,7 @@ void Inventory_SwapAgeEquipment(void) {
|
||||
gSaveContext.equips.equipment = gSaveContext.childEquips.equipment;
|
||||
gSaveContext.equips.equipment &= (u16) ~(0xF << (EQUIP_TYPE_SWORD * 4));
|
||||
// Equips kokiri sword in the inventory screen only if kokiri sword exists in inventory and a sword has been equipped already
|
||||
if (!((EQUIP_INV_SWORD_KOKIRI << (EQUIP_TYPE_SWORD * 4) & gSaveContext.inventory.equipment) == 0) && !Flags_GetInfTable(INFTABLE_SWORDLESS)) {
|
||||
if (!(CHECK_OWNED_EQUIP(EQUIP_TYPE_SWORD, EQUIP_INV_SWORD_KOKIRI) == 0) && !Flags_GetInfTable(INFTABLE_SWORDLESS)) {
|
||||
gSaveContext.equips.equipment |= EQUIP_VALUE_SWORD_KOKIRI << (EQUIP_TYPE_SWORD * 4);
|
||||
}
|
||||
} else if (gSaveContext.childEquips.buttonItems[0] != ITEM_NONE) {
|
||||
@@ -1588,6 +1596,13 @@ void Inventory_SwapAgeEquipment(void) {
|
||||
}
|
||||
}
|
||||
|
||||
// In Rando, when switching to child from a swordless adult, and child Link previously had a
|
||||
// sword equiped, then we need to unset the swordless flag to match
|
||||
if (IS_RANDO && Randomizer_GetSettingValue(RSK_SHUFFLE_MASTER_SWORD) &&
|
||||
gSaveContext.equips.buttonItems[0] != ITEM_NONE) {
|
||||
Flags_UnsetInfTable(INFTABLE_SWORDLESS);
|
||||
}
|
||||
|
||||
gSaveContext.equips.equipment = gSaveContext.childEquips.equipment;
|
||||
gSaveContext.equips.equipment &= (u16) ~(0xF << (EQUIP_TYPE_SWORD * 4));
|
||||
gSaveContext.equips.equipment |= EQUIP_VALUE_SWORD_KOKIRI << (EQUIP_TYPE_SWORD * 4);
|
||||
@@ -1597,7 +1612,7 @@ void Inventory_SwapAgeEquipment(void) {
|
||||
(only kokiri tunic/boots equipped, no sword, no C-button items, no D-Pad items).
|
||||
When becoming child, set swordless flag if player doesn't have kokiri sword
|
||||
Only in rando to keep swordless link bugs in vanilla*/
|
||||
if (EQUIP_INV_SWORD_KOKIRI << (EQUIP_TYPE_SWORD * 4) & gSaveContext.inventory.equipment == 0) {
|
||||
if (CHECK_OWNED_EQUIP(EQUIP_TYPE_SWORD, EQUIP_INV_SWORD_KOKIRI) == 0) {
|
||||
Flags_SetInfTable(INFTABLE_SWORDLESS);
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ u64 D_801614D0[0xA00];
|
||||
#endif
|
||||
|
||||
PlayState* gPlayState;
|
||||
s16 firstInit = 0;
|
||||
|
||||
s16 gEnPartnerId;
|
||||
|
||||
@@ -490,6 +491,12 @@ void Play_Init(GameState* thisx) {
|
||||
}
|
||||
}
|
||||
|
||||
// Properly initialize the frame counter so it doesn't use garbage data
|
||||
if (!firstInit) {
|
||||
play->gameplayFrames = 0;
|
||||
firstInit = 1;
|
||||
}
|
||||
|
||||
// Invalid entrance, so immediately exit the game to opening title
|
||||
if (gSaveContext.entranceIndex == -1) {
|
||||
gSaveContext.entranceIndex = 0;
|
||||
@@ -2329,8 +2336,28 @@ void Play_PerformSave(PlayState* play) {
|
||||
if (play != NULL && gSaveContext.fileNum != 0xFF) {
|
||||
Play_SaveSceneFlags(play);
|
||||
gSaveContext.savedSceneNum = play->sceneNum;
|
||||
|
||||
// Track values from temp B
|
||||
uint8_t prevB = gSaveContext.equips.buttonItems[0];
|
||||
uint8_t prevStatus = gSaveContext.buttonStatus[0];
|
||||
|
||||
// Replicate the B button restore from minigames/epona that kaleido does
|
||||
if (gSaveContext.equips.buttonItems[0] == ITEM_SLINGSHOT ||
|
||||
gSaveContext.equips.buttonItems[0] == ITEM_BOW ||
|
||||
gSaveContext.equips.buttonItems[0] == ITEM_BOMBCHU ||
|
||||
gSaveContext.equips.buttonItems[0] == ITEM_FISHING_POLE ||
|
||||
(gSaveContext.equips.buttonItems[0] == ITEM_NONE && !Flags_GetInfTable(INFTABLE_SWORDLESS))) {
|
||||
|
||||
gSaveContext.equips.buttonItems[0] = gSaveContext.buttonStatus[0];
|
||||
Interface_RandoRestoreSwordless();
|
||||
}
|
||||
|
||||
Save_SaveFile();
|
||||
|
||||
// Restore temp B values back
|
||||
gSaveContext.equips.buttonItems[0] = prevB;
|
||||
gSaveContext.buttonStatus[0] = prevStatus;
|
||||
|
||||
uint8_t triforceHuntCompleted =
|
||||
IS_RANDO &&
|
||||
gSaveContext.triforcePiecesCollected == Randomizer_GetSettingValue(RSK_TRIFORCE_HUNT_PIECES_REQUIRED) &&
|
||||
|
||||
@@ -277,6 +277,11 @@ void func_8009638C(Gfx** displayList, void* source, void* tlut, u16 width, u16 h
|
||||
bg->b.imagePal = 0;
|
||||
bg->b.imageFlip = CVarGetInteger("gMirroredWorld", 0) ? G_BG_FLAG_FLIPS : 0;
|
||||
|
||||
// When an alt resource exists for the background, we need to unload the original asset
|
||||
// to clear the cache so the alt asset will be loaded instead
|
||||
// OTRTODO: If Alt loading over original cache is fixed, this line can most likely be removed
|
||||
ResourceMgr_UnloadOriginalWhenAltExists((char*) source);
|
||||
|
||||
if (ResourceMgr_ResourceIsBackground((char*) source)) {
|
||||
char* blob = (char*) ResourceGetDataByName((char *) source);
|
||||
swapAndConvertJPEG(blob);
|
||||
|
||||
@@ -417,7 +417,12 @@ void func_800AEFC8(SkyboxContext* skyboxCtx, s16 skyboxId) {
|
||||
s32 j;
|
||||
s32 phi_s3 = 0;
|
||||
|
||||
if (skyboxId == SKYBOX_BAZAAR || (skyboxId > SKYBOX_HOUSE_KAKARIKO && skyboxId <= SKYBOX_BOMBCHU_SHOP)) {
|
||||
//! @bug All shops only provide 2 faces for their sky box. Mask shop is missing from the condition
|
||||
// meaning that the Mask shop will calculate 4 faces
|
||||
// This effect is not noticed as the faces are behind the camera, but will cause a crash in SoH.
|
||||
// SOH General: We have added the Mask shop to this check so only the 2 expected faces are calculated.
|
||||
if (skyboxId == SKYBOX_BAZAAR || skyboxId == SKYBOX_HAPPY_MASK_SHOP ||
|
||||
(skyboxId >= SKYBOX_KOKIRI_SHOP && skyboxId <= SKYBOX_BOMBCHU_SHOP)) {
|
||||
for (j = 0, i = 0; i < 2; i++, j += 2) {
|
||||
phi_s3 = func_800ADBB0(skyboxCtx, skyboxCtx->roomVtx, phi_s3, D_8012AEBC[i].unk_0, D_8012AEBC[i].unk_4,
|
||||
D_8012AEBC[i].unk_8, D_8012AEBC[i].unk_C, D_8012AEBC[i].unk_10, i, j);
|
||||
|
||||
@@ -59,8 +59,14 @@ void SkyboxDraw_Draw(SkyboxContext* skyboxCtx, GraphicsContext* gfxCtx, s16 skyb
|
||||
gSPDisplayList(POLY_OPA_DISP++, skyboxCtx->dListBuf[2]);
|
||||
gSPDisplayList(POLY_OPA_DISP++, skyboxCtx->dListBuf[3]);
|
||||
|
||||
if (skyboxId != SKYBOX_BAZAAR) {
|
||||
if (skyboxId <= SKYBOX_HOUSE_KAKARIKO || skyboxId > SKYBOX_BOMBCHU_SHOP) {
|
||||
//! @bug All shops only provide 2 faces for their sky box. Mask shop is missing from the condition
|
||||
// meaning that the Mask shop will render the previously loaded sky box values, or uninitialized data.
|
||||
// This effect is not noticed as the faces are behind the camera, but will cause a crash in SoH.
|
||||
// SOH General: We have added the Mask shop to this check so only the 2 expected faces are rendered.
|
||||
if (skyboxId != SKYBOX_BAZAAR && skyboxId != SKYBOX_HAPPY_MASK_SHOP) {
|
||||
if (skyboxId < SKYBOX_KOKIRI_SHOP || skyboxId > SKYBOX_BOMBCHU_SHOP) {
|
||||
// Skip remaining faces for most shop skyboxes
|
||||
|
||||
gDPPipeSync(POLY_OPA_DISP++);
|
||||
gDPLoadTLUT_pal256(POLY_OPA_DISP++, skyboxCtx->palettes[2]);
|
||||
gSPDisplayList(POLY_OPA_DISP++, skyboxCtx->dListBuf[4]);
|
||||
|
||||
@@ -5,9 +5,21 @@
|
||||
#include "scenes/dungeons/ddan_boss/ddan_boss_room_1.h"
|
||||
#include "soh/frame_interpolation.h"
|
||||
#include "soh/Enhancements/boss-rush/BossRush.h"
|
||||
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
|
||||
|
||||
#include <stdlib.h> // malloc
|
||||
#include <string.h> // memcpy
|
||||
|
||||
// OTRTODO: Replace usage of this method when we can clear the cache
|
||||
// for a single texture without the need of a DL opcode in the render code
|
||||
void gfx_texture_cache_clear();
|
||||
|
||||
#define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_HOSTILE | ACTOR_FLAG_UPDATE_WHILE_CULLED | ACTOR_FLAG_DRAW_WHILE_CULLED)
|
||||
|
||||
#define LAVA_TEX_WIDTH 32
|
||||
#define LAVA_TEX_HEIGHT 64
|
||||
#define LAVA_TEX_SIZE 2048
|
||||
|
||||
void BossDodongo_Init(Actor* thisx, PlayState* play);
|
||||
void BossDodongo_Destroy(Actor* thisx, PlayState* play);
|
||||
void BossDodongo_Update(Actor* thisx, PlayState* play);
|
||||
@@ -57,7 +69,14 @@ static u8 sMaskTex16x32[16 * 32] = { { 0 } };
|
||||
static u8 sMaskTex32x16[32 * 16] = { { 0 } };
|
||||
static u8 sMaskTex8x8[8 * 8] = { { 0 } };
|
||||
static u8 sMaskTex8x32[8 * 32] = { { 0 } };
|
||||
static u8 sMaskTexLava[32 * 64] = { { 0 } };
|
||||
static u8 sMaskTexLava[LAVA_TEX_WIDTH * LAVA_TEX_HEIGHT] = { { 0 } };
|
||||
|
||||
static u32* sLavaFloorModifiedTexRaw = NULL;
|
||||
static u32* sLavaWavyTexRaw = NULL;
|
||||
static u16 sLavaFloorModifiedTex[LAVA_TEX_SIZE];
|
||||
static u16 sLavaWavyTex[LAVA_TEX_SIZE];
|
||||
|
||||
static u8 hasRegisteredBlendedHook = 0;
|
||||
|
||||
static InitChainEntry sInitChain[] = {
|
||||
ICHAIN_U8(targetMode, 5, ICHAIN_CONTINUE),
|
||||
@@ -66,6 +85,89 @@ static InitChainEntry sInitChain[] = {
|
||||
ICHAIN_F32(targetArrowOffset, 8200.0f, ICHAIN_STOP),
|
||||
};
|
||||
|
||||
void BossDodongo_RegisterBlendedLavaTextureUpdate() {
|
||||
// Not in scene so there is nothing to do
|
||||
if (gPlayState == NULL || gPlayState->sceneNum != SCENE_DODONGOS_CAVERN_BOSS) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Free old textures
|
||||
if (sLavaFloorModifiedTexRaw != NULL) {
|
||||
free(sLavaFloorModifiedTexRaw);
|
||||
sLavaFloorModifiedTexRaw = NULL;
|
||||
}
|
||||
if (sLavaWavyTexRaw != NULL) {
|
||||
free(sLavaWavyTexRaw);
|
||||
sLavaWavyTexRaw = NULL;
|
||||
}
|
||||
|
||||
// Unload original textures to bypass cache result for lookups
|
||||
ResourceMgr_UnloadOriginalWhenAltExists(sLavaFloorLavaTex);
|
||||
ResourceMgr_UnloadOriginalWhenAltExists(sLavaFloorRockTex);
|
||||
ResourceMgr_UnloadOriginalWhenAltExists(gDodongosCavernBossLavaFloorTex);
|
||||
|
||||
// When the texture is HD (raw) we need to work with u32 values for RGBA32
|
||||
// Otherwise the original asset is u16 for RGBA16
|
||||
if (ResourceMgr_TexIsRaw(gDodongosCavernBossLavaFloorTex)) {
|
||||
u32* lavaTex = ResourceGetDataByName(sLavaFloorLavaTex);
|
||||
size_t lavaSize = ResourceGetSizeByName(sLavaFloorLavaTex);
|
||||
size_t floorSize = ResourceGetSizeByName(gDodongosCavernBossLavaFloorTex);
|
||||
size_t rockSize = ResourceGetSizeByName(sLavaFloorRockTex);
|
||||
|
||||
// If the sizes don't match, then don't bother with the blended effect to avoid crashing
|
||||
if (floorSize != lavaSize || floorSize != rockSize) {
|
||||
uint8_t maskVal = !!Flags_GetClear(gPlayState, gPlayState->roomCtx.curRoom.num);
|
||||
|
||||
if (sMaskTexLava[0] != maskVal) {
|
||||
for (int i = 0; i < ARRAY_COUNT(sMaskTexLava); i++) {
|
||||
sMaskTexLava[i] = maskVal;
|
||||
}
|
||||
}
|
||||
Gfx_RegisterBlendedTexture(gDodongosCavernBossLavaFloorTex, sMaskTexLava, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
sLavaFloorModifiedTexRaw = malloc(lavaSize);
|
||||
sLavaWavyTexRaw = malloc(floorSize);
|
||||
|
||||
memcpy(sLavaFloorModifiedTexRaw, lavaTex, lavaSize);
|
||||
|
||||
// When KD is dead, just immediately copy the rock texture
|
||||
if (Flags_GetClear(gPlayState, gPlayState->roomCtx.curRoom.num)) {
|
||||
u32* rockTex = ResourceGetDataByName(sLavaFloorRockTex);
|
||||
memcpy(sLavaFloorModifiedTexRaw, rockTex, rockSize);
|
||||
}
|
||||
|
||||
memcpy(sLavaWavyTexRaw, sLavaFloorModifiedTexRaw, floorSize);
|
||||
|
||||
// Register the blended effect for the raw texture
|
||||
Gfx_RegisterBlendedTexture(gDodongosCavernBossLavaFloorTex, sMaskTexLava, sLavaWavyTexRaw);
|
||||
} else {
|
||||
// When KD is dead, just immediately copy the rock texture
|
||||
if (Flags_GetClear(gPlayState, gPlayState->roomCtx.curRoom.num)) {
|
||||
u16* rockTex = ResourceGetDataByName(sLavaFloorRockTex);
|
||||
memcpy(sLavaFloorModifiedTex, rockTex, sizeof(sLavaFloorModifiedTex));
|
||||
} else {
|
||||
u16* lavaTex = ResourceGetDataByName(sLavaFloorLavaTex);
|
||||
memcpy(sLavaFloorModifiedTex, lavaTex, sizeof(sLavaFloorModifiedTex));
|
||||
}
|
||||
|
||||
// Register the blended effect for the non-raw texture
|
||||
memcpy(sLavaWavyTex, sLavaFloorModifiedTex, sizeof(sLavaWavyTex));
|
||||
|
||||
Gfx_RegisterBlendedTexture(gDodongosCavernBossLavaFloorTex, sMaskTexLava, sLavaWavyTex);
|
||||
}
|
||||
|
||||
// Set all true for the lava as it will always replace the scene texture
|
||||
if (sMaskTexLava[0] == 0) {
|
||||
for (int i = 0; i < ARRAY_COUNT(sMaskTexLava); i++) {
|
||||
sMaskTexLava[i] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
gfx_texture_cache_clear();
|
||||
}
|
||||
|
||||
void func_808C12C4(u8* arg1, s16 arg2) {
|
||||
if (arg2[arg1] != 0) {
|
||||
sMaskTex8x16[arg2 / 2] = 1;
|
||||
@@ -86,12 +188,53 @@ void func_808C12C4(u8* arg1, s16 arg2) {
|
||||
}
|
||||
}
|
||||
|
||||
void func_808C1554(void* arg0, void* floorTex, s32 arg2, f32 arg3) {
|
||||
arg0 = GetResourceDataByNameHandlingMQ(arg0);
|
||||
floorTex = ResourceGetDataByName(floorTex);
|
||||
// Same as func_808C1554 but works with u32 values for RGBA32 raw textures
|
||||
void func_808C1554_Raw(void* arg0, void* floorTex, s32 arg2, f32 arg3) {
|
||||
// Raw lava not registered, so abort the wave modification
|
||||
if (sLavaWavyTexRaw == NULL || sLavaFloorModifiedTexRaw == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
u16* temp_s3 = SEGMENTED_TO_VIRTUAL(arg0);
|
||||
u16* temp_s1 = SEGMENTED_TO_VIRTUAL(floorTex);
|
||||
u16 width = ResourceGetTexWidthByName(arg0);
|
||||
s32 size = ResourceGetTexHeightByName(arg0) * width;
|
||||
|
||||
u32* temp_s3 = sLavaWavyTexRaw;
|
||||
u32* temp_s1 = sLavaFloorModifiedTexRaw;
|
||||
s32 i;
|
||||
s32 i2;
|
||||
u32* sp54 = malloc(size * sizeof(u32)); // Match the size for lava floor tex
|
||||
s32 temp;
|
||||
s32 temp2;
|
||||
|
||||
// Multiplier is used to try to scale the wavy effect to match the scale of the HD texture
|
||||
// Applying sqrt(multiplier) to arg3 is to control how many pixels move left/right for the selected row
|
||||
// Applying to arg2 and M_PI help to space out the wave effect
|
||||
// It's not perfect but close enough
|
||||
u16 multiplier = width / LAVA_TEX_WIDTH;
|
||||
|
||||
for (i = 0; i < size; i += width) {
|
||||
temp = sinf((((i / width) + (s32)(((arg2 * multiplier) * 50.0f) / 100.0f)) & (width - 1)) * (M_PI / (16 * multiplier))) * (arg3 * sqrt(multiplier));
|
||||
for (i2 = 0; i2 < width; i2++) {
|
||||
sp54[i + ((temp + i2) & (width - 1))] = temp_s1[i + i2];
|
||||
}
|
||||
}
|
||||
for (i = 0; i < width; i++) {
|
||||
temp = sinf(((i + (s32)(((arg2 * multiplier) * 80.0f) / 100.0f)) & (width - 1)) * (M_PI / (16 * multiplier))) * (arg3 * sqrt(multiplier));
|
||||
temp *= width;
|
||||
for (i2 = 0; i2 < size; i2 += width) {
|
||||
temp2 = (temp + i2) & (size - 1);
|
||||
temp_s3[i + temp2] = sp54[i + i2];
|
||||
}
|
||||
}
|
||||
|
||||
free(sp54);
|
||||
}
|
||||
|
||||
// Modified to support CPU modified texture with the resource system
|
||||
// Used for the original non-raw asset working with u16 values
|
||||
void func_808C1554(void* arg0, void* floorTex, s32 arg2, f32 arg3) {
|
||||
u16* temp_s3 = sLavaWavyTex;
|
||||
u16* temp_s1 = sLavaFloorModifiedTex;
|
||||
s16 i;
|
||||
s16 i2;
|
||||
u16 sp54[2048];
|
||||
@@ -187,27 +330,21 @@ void BossDodongo_Init(Actor* thisx, PlayState* play) {
|
||||
Collider_SetJntSph(play, &this->collider, &this->actor, &sJntSphInit, this->items);
|
||||
|
||||
if (Flags_GetClear(play, play->roomCtx.curRoom.num)) { // KD is dead
|
||||
u16* LavaFloorTex = ResourceGetDataByName(gDodongosCavernBossLavaFloorTex);
|
||||
u16* LavaFloorRockTex = ResourceGetDataByName(sLavaFloorRockTex);
|
||||
temp_s1_3 = SEGMENTED_TO_VIRTUAL(LavaFloorTex);
|
||||
temp_s2 = SEGMENTED_TO_VIRTUAL(LavaFloorRockTex);
|
||||
// SOH [General]
|
||||
// Applying the "cooled off" lava rock CPU modified texture for re-visiting the scene
|
||||
// is now handled by BossDodongo_RegisterBlendedLavaTextureUpdate below
|
||||
|
||||
Actor_Kill(&this->actor);
|
||||
Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_DOOR_WARP1, -890.0f, -1523.76f,
|
||||
-3304.0f, 0, 0, 0, WARP_DUNGEON_CHILD);
|
||||
Actor_Spawn(&play->actorCtx, play, ACTOR_BG_BREAKWALL, -890.0f, -1523.76f, -3304.0f, 0, 0, 0, 0x6000, true);
|
||||
Actor_Spawn(&play->actorCtx, play, ACTOR_ITEM_B_HEART, -690.0f, -1523.76f, -3304.0f, 0, 0, 0, 0, true);
|
||||
|
||||
for (int i = 0; i < ARRAY_COUNT(sMaskTexLava); i++) {
|
||||
sMaskTexLava[i] = 1;
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < ARRAY_COUNT(sMaskTexLava); i++) {
|
||||
sMaskTexLava[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
this->actor.flags &= ~ACTOR_FLAG_TARGETABLE;
|
||||
|
||||
// #region SOH [General]
|
||||
// Init mask values for all KD blended textures
|
||||
for (int i = 0; i < ARRAY_COUNT(sMaskTex8x16); i++) {
|
||||
sMaskTex8x16[i] = 0;
|
||||
}
|
||||
@@ -223,6 +360,8 @@ void BossDodongo_Init(Actor* thisx, PlayState* play) {
|
||||
for (int i = 0; i < ARRAY_COUNT(sMaskTex32x16); i++) {
|
||||
sMaskTex32x16[i] = 0;
|
||||
}
|
||||
|
||||
// Register all blended textures
|
||||
Gfx_RegisterBlendedTexture(object_kingdodongo_Tex_015890, sMaskTex8x16, NULL);
|
||||
Gfx_RegisterBlendedTexture(object_kingdodongo_Tex_017210, sMaskTex8x32, NULL);
|
||||
Gfx_RegisterBlendedTexture(object_kingdodongo_Tex_015D90, sMaskTex16x16, NULL);
|
||||
@@ -234,10 +373,14 @@ void BossDodongo_Init(Actor* thisx, PlayState* play) {
|
||||
Gfx_RegisterBlendedTexture(object_kingdodongo_Tex_016990, sMaskTex32x16, NULL);
|
||||
Gfx_RegisterBlendedTexture(object_kingdodongo_Tex_016E10, sMaskTex32x16, NULL);
|
||||
|
||||
// OTRTODO: This is causing OOB memory reads with HD assets
|
||||
// commenting this out means the lava will stay lava even after beating king d
|
||||
//
|
||||
// Gfx_RegisterBlendedTexture(gDodongosCavernBossLavaFloorTex, sMaskTexLava, sLavaFloorRockTex);
|
||||
BossDodongo_RegisterBlendedLavaTextureUpdate();
|
||||
|
||||
// Register alt listener to update the blended lava for the replacement texture based on alt path
|
||||
if (!hasRegisteredBlendedHook) {
|
||||
GameInteractor_RegisterOnAssetAltChange(BossDodongo_RegisterBlendedLavaTextureUpdate);
|
||||
hasRegisteredBlendedHook = 1;
|
||||
}
|
||||
// #endregion
|
||||
}
|
||||
|
||||
void BossDodongo_Destroy(Actor* thisx, PlayState* play) {
|
||||
@@ -1014,21 +1157,72 @@ void BossDodongo_Update(Actor* thisx, PlayState* play2) {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO The lave floor bubbles with an effect that modifies the texture. This needs to be recreated shader-side.
|
||||
//func_808C1554(gDodongosCavernBossLavaFloorTex, sLavaFloorLavaTex, this->unk_19E, this->unk_224);
|
||||
// The lava bubbles with a wavy effect as a CPU modified texture
|
||||
// This has been done by maintaining copied/modified texture values in the actor code
|
||||
// The "cooling off" effect for the lava is pre-applied to the lava texture before applying
|
||||
// the wavy effect. Since this is two effects and closely related to the actor, I've opted
|
||||
// to handle them here rather than as a shader effect.
|
||||
//
|
||||
// Apply the corresponding wavy effect based on the texture being raw or not
|
||||
if (ResourceMgr_TexIsRaw(gDodongosCavernBossLavaFloorTex)) {
|
||||
func_808C1554_Raw(gDodongosCavernBossLavaFloorTex, sLavaFloorLavaTex, this->unk_19E, this->unk_224);
|
||||
} else {
|
||||
func_808C1554(gDodongosCavernBossLavaFloorTex, sLavaFloorLavaTex, this->unk_19E, this->unk_224);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply the "cooling off" effect for the lava
|
||||
if (this->unk_1C6 != 0) {
|
||||
u16* ptr1 = ResourceGetDataByName(sLavaFloorLavaTex);
|
||||
u16* ptr2 = ResourceGetDataByName(sLavaFloorRockTex);
|
||||
s16 i2;
|
||||
// Similar to above, the cooling off effect is a CPU modified texture effect
|
||||
// Apply corresponding to the texture being raw or not
|
||||
if (ResourceMgr_TexIsRaw(sLavaFloorRockTex)) {
|
||||
u32* ptr1 = sLavaFloorModifiedTexRaw;
|
||||
u32* ptr2 = ResourceGetDataByName(sLavaFloorRockTex);
|
||||
u16 width = ResourceGetTexWidthByName(sLavaFloorRockTex);
|
||||
u16 height = ResourceGetTexHeightByName(sLavaFloorRockTex);
|
||||
s16 i2;
|
||||
|
||||
for (i2 = 0; i2 < 20; i2++) {
|
||||
s16 new_var = this->unk_1C2 & 0x7FF;
|
||||
// Get the scale based on the original texture size
|
||||
u16 widthScale = width / LAVA_TEX_WIDTH;
|
||||
u16 heightScale = height / LAVA_TEX_HEIGHT;
|
||||
u32 size = width * height;
|
||||
|
||||
sMaskTexLava[new_var] = 1;
|
||||
this->unk_1C2 += 37;
|
||||
for (i2 = 0; i2 < 20; i2++) {
|
||||
s16 new_var = this->unk_1C2 & (LAVA_TEX_SIZE - 1);
|
||||
|
||||
// Raw lava must be registered, otherwise skip the effect for incompatible texture pack
|
||||
// and instead set the mask to simulate the lava disappearing by turning black
|
||||
if (sLavaFloorModifiedTexRaw != NULL) {
|
||||
// Compute the index to a scaled position (scaling pseudo x,y as a 1D value)
|
||||
s32 indexStart =
|
||||
((new_var % LAVA_TEX_WIDTH) * widthScale) + ((new_var / LAVA_TEX_WIDTH) * width * heightScale);
|
||||
|
||||
// From the starting index, apply extra pixels right/down based on the scale
|
||||
for (size_t j = 0; j < heightScale; j++) {
|
||||
for (size_t i3 = 0; i3 < widthScale; i3++) {
|
||||
s32 scaledIndex = (indexStart + i3 + (j * width)) & (size - 1);
|
||||
ptr1[scaledIndex] = ptr2[scaledIndex];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
sMaskTexLava[new_var] = 1;
|
||||
}
|
||||
|
||||
this->unk_1C2 += 37;
|
||||
}
|
||||
} else {
|
||||
u16* ptr1 = sLavaFloorModifiedTex;
|
||||
u16* ptr2 = ResourceGetDataByName(sLavaFloorRockTex);
|
||||
s16 i2;
|
||||
|
||||
for (i2 = 0; i2 < 20; i2++) {
|
||||
s16 new_var = this->unk_1C2 & 0x7FF;
|
||||
|
||||
ptr1[new_var] = ptr2[new_var];
|
||||
this->unk_1C2 += 37;
|
||||
}
|
||||
}
|
||||
|
||||
Math_SmoothStepToF(&this->unk_224, 0.0f, 1.0f, 0.01f, 0.0f);
|
||||
}
|
||||
|
||||
@@ -1151,8 +1345,16 @@ void BossDodongo_Draw(Actor* thisx, PlayState* play) {
|
||||
gSPInvalidateTexCache(POLY_OPA_DISP++, sMaskTex32x16);
|
||||
}
|
||||
|
||||
if (this->unk_1C6 != 0) {
|
||||
gSPInvalidateTexCache(POLY_OPA_DISP++, sMaskTexLava);
|
||||
gSPInvalidateTexCache(POLY_OPA_DISP++, sMaskTexLava);
|
||||
|
||||
// Using WORK_DISP to invalidate these textures as they are used in drawing the scene textures which happens
|
||||
// before actors are drawn. WORK_DISP comes before POLAY_OPA_DISP. It is probably not meant for this, but it
|
||||
// at least works for now.
|
||||
// Alternatively, having a way to invalidate just these pointers from the Update func should be sufficient.
|
||||
if (sLavaFloorModifiedTexRaw != NULL) {
|
||||
gSPInvalidateTexCache(WORK_DISP++, sLavaWavyTexRaw);
|
||||
} else {
|
||||
gSPInvalidateTexCache(WORK_DISP++, sLavaWavyTex);
|
||||
}
|
||||
|
||||
if ((this->unk_1C0 >= 2) && (this->unk_1C0 & 1)) {
|
||||
|
||||
@@ -75,7 +75,6 @@ static InitChainEntry sInitChain[] = {
|
||||
};
|
||||
|
||||
static UNK_TYPE sUnused;
|
||||
GetItemEntry sItem;
|
||||
|
||||
Gfx gSkullTreasureChestChestSideAndLidDL[116] = {0};
|
||||
Gfx gGoldTreasureChestChestSideAndLidDL[116] = {0};
|
||||
@@ -88,6 +87,7 @@ Gfx gKeyTreasureChestChestFrontDL[128] = {0};
|
||||
Gfx gChristmasRedTreasureChestChestFrontDL[128] = {0};
|
||||
Gfx gChristmasGreenTreasureChestChestFrontDL[128] = {0};
|
||||
u8 hasCreatedRandoChestTextures = 0;
|
||||
u8 hasCustomChestDLs = 0;
|
||||
u8 hasChristmasChestTexturesAvailable = 0;
|
||||
|
||||
void EnBox_SetupAction(EnBox* this, EnBoxActionFunc actionFunc) {
|
||||
@@ -471,7 +471,7 @@ void EnBox_WaitOpen(EnBox* this, PlayState* play) {
|
||||
func_8002DBD0(&this->dyna.actor, &sp4C, &player->actor.world.pos);
|
||||
if (sp4C.z > -50.0f && sp4C.z < 0.0f && fabsf(sp4C.y) < 10.0f && fabsf(sp4C.x) < 20.0f &&
|
||||
Player_IsFacingActor(&this->dyna.actor, 0x3000, play)) {
|
||||
sItem = Randomizer_GetItemFromActor(this->dyna.actor.id, play->sceneNum, this->dyna.actor.params, this->dyna.actor.params >> 5 & 0x7F);
|
||||
GetItemEntry sItem = Randomizer_GetItemFromActor(this->dyna.actor.id, play->sceneNum, this->dyna.actor.params, this->dyna.actor.params >> 5 & 0x7F);
|
||||
GetItemEntry blueRupee = ItemTable_RetrieveEntry(MOD_NONE, GI_RUPEE_BLUE);
|
||||
|
||||
// RANDOTODO treasure chest game rando
|
||||
@@ -627,7 +627,7 @@ void EnBox_Update(Actor* thisx, PlayState* play) {
|
||||
}
|
||||
|
||||
if (((!IS_RANDO && ((this->dyna.actor.params >> 5 & 0x7F) == 0x7C)) ||
|
||||
(IS_RANDO && ABS(sItem.getItemId) == RG_ICE_TRAP)) &&
|
||||
(IS_RANDO && this->getItemEntry.getItemId == RG_ICE_TRAP)) &&
|
||||
this->actionFunc == EnBox_Open && this->skelanime.curFrame > 45 && this->iceSmokeTimer < 100) {
|
||||
if (!CVarGetInteger("gAddTraps.enabled", 0)) {
|
||||
EnBox_SpawnIceSmoke(this, play);
|
||||
@@ -690,7 +690,7 @@ void EnBox_UpdateSizeAndTexture(EnBox* this, PlayState* play) {
|
||||
}
|
||||
|
||||
// Change texture
|
||||
if (!isVanilla && (csmc == CSMC_BOTH || csmc == CSMC_TEXTURE)) {
|
||||
if (!isVanilla && hasCreatedRandoChestTextures && !hasCustomChestDLs && (csmc == CSMC_BOTH || csmc == CSMC_TEXTURE)) {
|
||||
switch (getItemCategory) {
|
||||
case ITEM_CATEGORY_MAJOR:
|
||||
this->boxBodyDL = gGoldTreasureChestChestFrontDL;
|
||||
@@ -725,7 +725,7 @@ void EnBox_UpdateSizeAndTexture(EnBox* this, PlayState* play) {
|
||||
}
|
||||
}
|
||||
|
||||
if (CVarGetInteger("gLetItSnow", 0) && hasChristmasChestTexturesAvailable) {
|
||||
if (CVarGetInteger("gLetItSnow", 0) && hasChristmasChestTexturesAvailable && hasCreatedRandoChestTextures && !hasCustomChestDLs) {
|
||||
if (this->dyna.actor.scale.x == 0.01f) {
|
||||
this->boxBodyDL = gChristmasRedTreasureChestChestFrontDL;
|
||||
this->boxLidDL = gChristmasRedTreasureChestChestSideAndLidDL;
|
||||
@@ -767,7 +767,18 @@ void EnBox_UpdateSizeAndTexture(EnBox* this, PlayState* play) {
|
||||
}
|
||||
|
||||
void EnBox_CreateExtraChestTextures() {
|
||||
// Don't patch textures for custom chest models, as they do not import textures the exact same way as vanilla chests
|
||||
// OTRTODO: Make it so model packs can provide a unique DL per chest type, instead of us copying the brown chest and attempting to patch
|
||||
if (ResourceMgr_FileIsCustomByName(gTreasureChestChestFrontDL) ||
|
||||
ResourceMgr_FileIsCustomByName(gTreasureChestChestSideAndLidDL)) {
|
||||
hasCustomChestDLs = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
hasCustomChestDLs = 0;
|
||||
|
||||
if (hasCreatedRandoChestTextures) return;
|
||||
|
||||
Gfx gTreasureChestChestTextures[] = {
|
||||
gsDPSetTextureImage(G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, gSkullTreasureChestFrontTex),
|
||||
gsDPSetTextureImage(G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, gSkullTreasureChestSideAndTopTex),
|
||||
|
||||
@@ -95,6 +95,26 @@ static AnimationInfo sAnimationInfo[] = {
|
||||
{ &gDaruniaDancingEndAnim, 1.0f, 0.0f, -1.0f, ANIMMODE_ONCE, -6.0f },
|
||||
};
|
||||
|
||||
// #region SOH [Enhancement] Only animations too fast need to be slowed down, otherwise not touched
|
||||
static AnimationInfo sAnimationInfoFix[] = {
|
||||
{ NULL },
|
||||
{ NULL },
|
||||
{ NULL },
|
||||
{ NULL },
|
||||
{ NULL },
|
||||
{ NULL },
|
||||
{ NULL },
|
||||
{ &gDaruniaDancingLoop1Anim, 0.78f, 0.0f, -1.0f, ANIMMODE_ONCE, -10.0f }, //
|
||||
{ &gDaruniaDancingLoop1Anim, 0.77f, 0.0f, -1.0f, ANIMMODE_ONCE, 0.0f }, // hop
|
||||
{ &gDaruniaDancingLoop2Anim, 0.78f, 0.0f, -1.0f, ANIMMODE_ONCE, 0.0f }, // from hop to spin
|
||||
{ &gDaruniaDancingLoop3Anim, 0.77f, 0.0f, -1.0f, ANIMMODE_ONCE, 0.0f }, // spin
|
||||
{ NULL },
|
||||
{ NULL },
|
||||
{ &gDaruniaDancingLoop4Anim, 0.78f, 0.0f, -1.0f, ANIMMODE_ONCE, 0.0f }, // from spin to hop
|
||||
{ NULL },
|
||||
};
|
||||
// #endregion
|
||||
|
||||
void EnDu_SetupAction(EnDu* this, EnDuActionFunc actionFunc) {
|
||||
this->actionFunc = actionFunc;
|
||||
}
|
||||
@@ -255,7 +275,13 @@ void func_809FE040(EnDu* this) {
|
||||
if (this->unk_1E6 >= 8) {
|
||||
this->unk_1E6 = 0;
|
||||
}
|
||||
Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, animationIndices[this->unk_1E6]);
|
||||
// #region SOH[Enhancement]
|
||||
if (CVarGetInteger("gEnhancements.FixDaruniaDanceSpeed", 1)) {
|
||||
Animation_ChangeByInfo(&this->skelAnime, sAnimationInfoFix, animationIndices[this->unk_1E6]);
|
||||
// #endregion
|
||||
} else {
|
||||
Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, animationIndices[this->unk_1E6]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,7 +297,13 @@ void func_809FE104(EnDu* this) {
|
||||
if (Animation_OnFrame(&this->skelAnime, this->skelAnime.endFrame)) {
|
||||
this->unk_1E6++;
|
||||
if (this->unk_1E6 < 4) {
|
||||
Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, animationIndices[this->unk_1E6]);
|
||||
// #region SOH[Enhancement]
|
||||
if (CVarGetInteger("gEnhancements.FixDaruniaDanceSpeed", 1) && this->unk_1E6 <= 1) {
|
||||
Animation_ChangeByInfo(&this->skelAnime, sAnimationInfoFix, animationIndices[this->unk_1E6]);
|
||||
// #endregion
|
||||
} else {
|
||||
Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, animationIndices[this->unk_1E6]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -465,7 +497,13 @@ void func_809FE890(EnDu* this, PlayState* play) {
|
||||
}
|
||||
if (csAction->action == 7 || csAction->action == 8) {
|
||||
this->unk_1E6 = 0;
|
||||
Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENDU_ANIM_7);
|
||||
// #region SOH[Enhancement]
|
||||
if (CVarGetInteger("gEnhancements.FixDaruniaDanceSpeed", 1)) {
|
||||
Animation_ChangeByInfo(&this->skelAnime, sAnimationInfoFix, ENDU_ANIM_7);
|
||||
// #endregion
|
||||
} else {
|
||||
Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENDU_ANIM_7);
|
||||
}
|
||||
}
|
||||
this->unk_1EA = csAction->action;
|
||||
if (this->unk_1EA == 7) {
|
||||
|
||||
@@ -211,6 +211,13 @@ void EnInsect_Init(Actor* thisx, PlayState* play2) {
|
||||
|
||||
func_80A7D39C(this);
|
||||
|
||||
// For bugs that aren't linked to a soil patch, we remove the "short lived" flag to prevent them from despawning
|
||||
// And exit early to not increment the "bugs dropped count"
|
||||
if (CVarGetInteger("gNoBugsDespawn", 0) && this->soilActor == NULL) {
|
||||
this->unk_314 &= ~4;
|
||||
return;
|
||||
}
|
||||
|
||||
D_80A7DEB8++;
|
||||
} else {
|
||||
rand = Rand_ZeroOne();
|
||||
@@ -394,9 +401,6 @@ void func_80A7CAD0(EnInsect* this, PlayState* play) {
|
||||
}
|
||||
|
||||
void func_80A7CBC8(EnInsect* this) {
|
||||
if (CVarGetInteger("gNoBugsDespawn", 0) != 0) {
|
||||
return;
|
||||
}
|
||||
this->unk_31A = 60;
|
||||
func_80A7BF58(this);
|
||||
this->skelAnime.playSpeed = 1.9f;
|
||||
|
||||
@@ -313,7 +313,9 @@ void EnSth_GiveReward(EnSth* this, PlayState* play) {
|
||||
this->actor.parent = NULL;
|
||||
EnSth_SetupAction(this, EnSth_RewardObtainedTalk);
|
||||
gSaveContext.eventChkInf[EVENTCHKINF_SKULLTULA_REWARD_INDEX] |= this->eventFlag;
|
||||
GameInteractor_ExecuteOnFlagSet(FLAG_EVENT_CHECK_INF, (EVENTCHKINF_SKULLTULA_REWARD_INDEX << 4) + sEventFlagsShift[this->actor.params]);
|
||||
if (this->eventFlag != 0) {
|
||||
GameInteractor_ExecuteOnFlagSet(FLAG_EVENT_CHECK_INF, (EVENTCHKINF_SKULLTULA_REWARD_INDEX << 4) + sEventFlagsShift[this->actor.params]);
|
||||
}
|
||||
} else {
|
||||
EnSth_GivePlayerItem(this, play);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "overlays/actors/ovl_En_Syateki_Itm/z_en_syateki_itm.h"
|
||||
#include "objects/object_ossan/object_ossan.h"
|
||||
#include "soh/Enhancements/randomizer/randomizer_entrance.h"
|
||||
#include "soh/Enhancements/custom-message/CustomMessageTypes.h"
|
||||
|
||||
#define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY | ACTOR_FLAG_UPDATE_WHILE_CULLED | ACTOR_FLAG_NO_LOCKON)
|
||||
|
||||
@@ -371,7 +372,8 @@ void EnSyatekiMan_EndGame(EnSyatekiMan* this, PlayState* play) {
|
||||
this->getItemId = GI_RUPEE_PURPLE;
|
||||
}
|
||||
} else {
|
||||
if(IS_RANDO && !Flags_GetTreasure(play, 0x1F)) {
|
||||
// Only give the adult rando reward when the player has a quiver
|
||||
if (IS_RANDO && !Flags_GetTreasure(play, 0x1F) && CUR_UPG_VALUE(UPG_QUIVER) > 0) {
|
||||
this->getItemEntry = Randomizer_GetItemFromKnownCheck(RC_KAK_SHOOTING_GALLERY_REWARD, GI_QUIVER_50);
|
||||
this->getItemId = this->getItemEntry.getItemId;
|
||||
Flags_SetTreasure(play, 0x1F);
|
||||
@@ -448,6 +450,9 @@ void EnSyatekiMan_FinishPrize(EnSyatekiMan* this, PlayState* play) {
|
||||
Flags_SetItemGetInf(ITEMGETINF_0D);
|
||||
} else if ((this->getItemId == GI_QUIVER_40) || (this->getItemId == GI_QUIVER_50)) {
|
||||
Flags_SetItemGetInf(ITEMGETINF_0E);
|
||||
} else if (IS_RANDO && LINK_IS_ADULT && CUR_UPG_VALUE(UPG_QUIVER) == 0) {
|
||||
// In Rando without a quiver, display a message reminding the player to come back with a bow
|
||||
Message_StartTextbox(play, TEXT_SHOOTING_GALLERY_MAN_COME_BACK_WITH_BOW, NULL);
|
||||
}
|
||||
this->gameResult = SYATEKI_RESULT_NONE;
|
||||
this->actor.parent = this->tempGallery;
|
||||
|
||||
@@ -408,7 +408,7 @@ s32 EnTk_ChooseReward(EnTk* this) {
|
||||
f32 luck;
|
||||
s32 reward;
|
||||
|
||||
if ((IS_RANDO || CVarGetInteger("gDampeWin", 0)) && !Flags_GetCollectible(gPlayState, 0x1F) && this->heartPieceSpawned == 0) {
|
||||
if ((IS_RANDO || CVarGetInteger("gDampeWin", 0)) && !Flags_GetCollectible(gPlayState, COLLECTFLAG_GRAVEDIGGING_HEART_PIECE) && this->heartPieceSpawned == 0) {
|
||||
return 3;
|
||||
}
|
||||
|
||||
@@ -624,10 +624,8 @@ void EnTk_Dig(EnTk* this, PlayState* play) {
|
||||
|
||||
this->currentReward = EnTk_ChooseReward(this);
|
||||
|
||||
// merging in dampe tour fix seems messy, so i'm just wrapping this whole thing
|
||||
// in an n64dd check for now
|
||||
if (IS_RANDO || CVarGetInteger("gDampeWin", 0)) {
|
||||
if (this->currentReward == 3) {
|
||||
if (this->currentReward == 3) {
|
||||
if (IS_RANDO || CVarGetInteger("gDampeWin", 0)) {
|
||||
/*
|
||||
* Upgrade the purple rupee reward to the heart piece if this
|
||||
* is the first grand prize dig.
|
||||
@@ -635,37 +633,31 @@ void EnTk_Dig(EnTk* this, PlayState* play) {
|
||||
if (!Flags_GetItemGetInf(ITEMGETINF_1C) && !(IS_RANDO || CVarGetInteger("gDampeWin", 0))) {
|
||||
Flags_SetItemGetInf(ITEMGETINF_1C);
|
||||
this->currentReward = 4;
|
||||
} else if ((IS_RANDO || CVarGetInteger("gDampeWin", 0)) && !Flags_GetCollectible(gPlayState, 0x1F) && this->heartPieceSpawned == 0) {
|
||||
} else if ((IS_RANDO || CVarGetInteger("gDampeWin", 0)) && !Flags_GetCollectible(gPlayState, COLLECTFLAG_GRAVEDIGGING_HEART_PIECE) && this->heartPieceSpawned == 0) {
|
||||
this->currentReward = 4;
|
||||
}
|
||||
}
|
||||
|
||||
if ((IS_RANDO || CVarGetInteger("gDampeWin", 0)) && this->currentReward == 4) {
|
||||
Actor_Spawn(&play->actorCtx, play, ACTOR_EN_ITEM00, rewardPos.x, rewardPos.y, rewardPos.z, 0,
|
||||
0, 0, 0x1F06, true);
|
||||
this->heartPieceSpawned = 1;
|
||||
} else {
|
||||
Item_DropCollectible(play, &rewardPos, rewardParams[this->currentReward]);
|
||||
/*
|
||||
* Upgrade the purple rupee reward to the heart piece if this
|
||||
* is the first grand prize dig.
|
||||
*/
|
||||
// If vanilla itemGetInf flag is not set, it's impossible for the new flag to be set, so return true.
|
||||
// Otherwise if the gGravediggingTourFix is enabled and the new flag hasn't been set, return true.
|
||||
// If true, spawn the heart piece and set the vanilla itemGetInf flag and new temp clear flag.
|
||||
if (!heartPieceSpawned &&
|
||||
(!(gSaveContext.itemGetInf[1] & ITEMGETINFFLAG_GRAVEDIGGING_HEART_PIECE) ||
|
||||
CVarGetInteger("gGravediggingTourFix", 0) &&
|
||||
!Flags_GetCollectible(play, COLLECTFLAG_GRAVEDIGGING_HEART_PIECE))) {
|
||||
this->currentReward = 4;
|
||||
gSaveContext.itemGetInf[1] |= ITEMGETINFFLAG_GRAVEDIGGING_HEART_PIECE;
|
||||
heartPieceSpawned = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (IS_RANDO && this->currentReward == 4) {
|
||||
Actor_Spawn(&play->actorCtx, play, ACTOR_EN_ITEM00, rewardPos.x, rewardPos.y, rewardPos.z, 0, 0, 0, 0x1906, true);
|
||||
this->heartPieceSpawned = 1;
|
||||
} else {
|
||||
if (this->currentReward == 3) {
|
||||
/*
|
||||
* Upgrade the purple rupee reward to the heart piece if this
|
||||
* is the first grand prize dig.
|
||||
*/
|
||||
// If vanilla itemGetInf flag is not set, it's impossible for the new flag to be set, so return true.
|
||||
// Otherwise if the gGravediggingTourFix is enabled and the new flag hasn't been set, return true.
|
||||
// If true, spawn the heart piece and set the vanilla itemGetInf flag and new temp clear flag.
|
||||
if (!heartPieceSpawned &&
|
||||
(!(gSaveContext.itemGetInf[1] & ITEMGETINFFLAG_GRAVEDIGGING_HEART_PIECE) ||
|
||||
CVarGetInteger("gGravediggingTourFix", 0) &&
|
||||
!Flags_GetCollectible(play, COLLECTFLAG_GRAVEDIGGING_HEART_PIECE))) {
|
||||
this->currentReward = 4;
|
||||
gSaveContext.itemGetInf[1] |= ITEMGETINFFLAG_GRAVEDIGGING_HEART_PIECE;
|
||||
heartPieceSpawned = true;
|
||||
}
|
||||
}
|
||||
|
||||
EnItem00* reward = Item_DropCollectible(play, &rewardPos, rewardParams[this->currentReward]);
|
||||
if (this->currentReward == 4) {
|
||||
reward->collectibleFlag = COLLECTFLAG_GRAVEDIGGING_HEART_PIECE;
|
||||
|
||||
@@ -10101,7 +10101,8 @@ void func_80847BA0(PlayState* play, Player* this) {
|
||||
|
||||
D_808535F0 = func_80041DB8(&play->colCtx, this->actor.wallPoly, this->actor.wallBgId);
|
||||
|
||||
if (CVarGetInteger("gFixVineFall", 0)) {
|
||||
// conflicts arise from these two being enabled at once, and with ClimbEverything on, FixVineFall is redundant anyway
|
||||
if (CVarGetInteger("gFixVineFall", 0) && !CVarGetInteger("gClimbEverything", 0)) {
|
||||
/* This fixes the "started climbing a wall and then immediately fell off" bug.
|
||||
* The main idea is if a climbing wall is detected, double-check that it will
|
||||
* still be valid once climbing begins by doing a second raycast with a small
|
||||
@@ -10628,7 +10629,14 @@ void Player_UseTunicBoots(Player* this, PlayState* play) {
|
||||
s32 i;
|
||||
s32 item;
|
||||
s32 actionParam;
|
||||
if (!(this->stateFlags1 & PLAYER_STATE1_INPUT_DISABLED || this->stateFlags1 & PLAYER_STATE1_IN_ITEM_CS || this->stateFlags1 & PLAYER_STATE1_IN_CUTSCENE || this->stateFlags1 & PLAYER_STATE1_TEXT_ON_SCREEN || this->stateFlags2 & PLAYER_STATE2_OCARINA_PLAYING)) {
|
||||
if (!(
|
||||
this->stateFlags1 & PLAYER_STATE1_INPUT_DISABLED ||
|
||||
this->stateFlags1 & PLAYER_STATE1_IN_ITEM_CS ||
|
||||
this->stateFlags1 & PLAYER_STATE1_IN_CUTSCENE ||
|
||||
this->stateFlags1 & PLAYER_STATE1_TEXT_ON_SCREEN ||
|
||||
this->stateFlags1 & PLAYER_STATE1_DEAD ||
|
||||
this->stateFlags2 & PLAYER_STATE2_OCARINA_PLAYING
|
||||
)) {
|
||||
for (i = 0; i < ARRAY_COUNT(D_80854388); i++) {
|
||||
if (CHECK_BTN_ALL(sControlInput->press.button, D_80854388[i])) {
|
||||
break;
|
||||
@@ -15169,6 +15177,10 @@ void func_80852C50(PlayState* play, Player* this, CsCmdActorAction* arg2) {
|
||||
|
||||
sp24 = D_808547C4[this->unk_446];
|
||||
func_80852B4C(play, this, linkCsAction, &D_80854E50[ABS(sp24)]);
|
||||
|
||||
if (CVarGetInteger("gFixEyesOpenWhileSleeping", 0) && (play->csCtx.linkAction->action == 28 || play->csCtx.linkAction->action == 29)) {
|
||||
this->skelAnime.jointTable[22].x = 8;
|
||||
}
|
||||
}
|
||||
|
||||
void func_80852E14(Player* this, PlayState* play) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "file_choose.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "textures/title_static/title_static.h"
|
||||
#include "textures/parameter_static/parameter_static.h"
|
||||
@@ -1024,23 +1025,23 @@ void FileChoose_UpdateRandomizer() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!SpoilerFileExists(CVarGetString("gSpoilerLog", ""))) {
|
||||
if (!SpoilerFileExists(CVarGetString("gSpoilerLog", "")) && !CVarGetInteger("gRandomizerDontGenerateSpoiler", 0)) {
|
||||
CVarSetString("gSpoilerLog", "");
|
||||
fileSelectSpoilerFileLoaded = false;
|
||||
}
|
||||
|
||||
if ((CVarGetInteger("gNewFileDropped", 0) != 0) || (CVarGetInteger("gNewSeedGenerated", 0) != 0) ||
|
||||
if ((CVarGetInteger("gRandomizerNewFileDropped", 0) != 0) || (CVarGetInteger("gNewSeedGenerated", 0) != 0) ||
|
||||
(!fileSelectSpoilerFileLoaded && SpoilerFileExists(CVarGetString("gSpoilerLog", "")))) {
|
||||
if (CVarGetInteger("gNewFileDropped", 0) != 0) {
|
||||
CVarSetString("gSpoilerLog", CVarGetString("gDroppedFile", "None"));
|
||||
if (CVarGetInteger("gRandomizerNewFileDropped", 0) != 0) {
|
||||
CVarSetString("gSpoilerLog", CVarGetString("gRandomizerDroppedFile", "None"));
|
||||
}
|
||||
bool silent = true;
|
||||
if ((CVarGetInteger("gNewFileDropped", 0) != 0) || (CVarGetInteger("gNewSeedGenerated", 0) != 0)) {
|
||||
if ((CVarGetInteger("gRandomizerNewFileDropped", 0) != 0) || (CVarGetInteger("gNewSeedGenerated", 0) != 0)) {
|
||||
silent = false;
|
||||
}
|
||||
CVarSetInteger("gNewSeedGenerated", 0);
|
||||
CVarSetInteger("gNewFileDropped", 0);
|
||||
CVarSetString("gDroppedFile", "");
|
||||
CVarSetInteger("gRandomizerNewFileDropped", 0);
|
||||
CVarSetString("gRandomizerDroppedFile", "");
|
||||
fileSelectSpoilerFileLoaded = false;
|
||||
const char* fileLoc = CVarGetString("gSpoilerLog", "");
|
||||
Randomizer_LoadSettings(fileLoc);
|
||||
@@ -1051,6 +1052,10 @@ void FileChoose_UpdateRandomizer() {
|
||||
Randomizer_LoadMasterQuestDungeons(fileLoc);
|
||||
Randomizer_LoadEntranceOverrides(fileLoc, silent);
|
||||
fileSelectSpoilerFileLoaded = true;
|
||||
|
||||
if (SpoilerFileExists(CVarGetString("gSpoilerLog", "")) && CVarGetInteger("gRandomizerDontGenerateSpoiler", 0)) {
|
||||
remove(fileLoc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3686,4 +3691,7 @@ void FileChoose_Init(GameState* thisx) {
|
||||
Font_LoadOrderedFont(&this->font);
|
||||
Audio_QueueSeqCmd(0xF << 28 | SEQ_PLAYER_BGM_MAIN << 24 | 0xA);
|
||||
func_800F5E18(SEQ_PLAYER_BGM_MAIN, NA_BGM_FILE_SELECT, 0, 7, 1);
|
||||
|
||||
// Originally this was only set when transitioning from the title screen, but gSkipLogoTitle skips that process so we're ensuring it's set here
|
||||
gSaveContext.gameMode = GAMEMODE_FILE_SELECT;
|
||||
}
|
||||
|
||||
@@ -456,7 +456,6 @@ void FileChoose_DrawNameEntry(GameState* thisx) {
|
||||
this->prevConfigMode = CM_MAIN_MENU;
|
||||
this->configMode = CM_NAME_ENTRY_TO_MAIN;
|
||||
CVarSetInteger("gOnFileSelectNameEntry", 0);
|
||||
CVarSetInteger("gNewFileDropped", 0);
|
||||
this->nameBoxAlpha[this->buttonIndex] = this->nameAlpha[this->buttonIndex] = 200;
|
||||
this->connectorAlpha[this->buttonIndex] = 255;
|
||||
func_800AA000(300.0f, 0xB4, 0x14, 0x64);
|
||||
|
||||
@@ -1054,8 +1054,9 @@ void KaleidoScope_UpdateItemEquip(PlayState* play) {
|
||||
|
||||
//Fix for Equip Dupe
|
||||
if (pauseCtx->equipTargetItem == ITEM_BOW) {
|
||||
if ((gSaveContext.equips.buttonItems[otherButtonIndex] >= ITEM_BOW_ARROW_FIRE) &&
|
||||
(gSaveContext.equips.buttonItems[otherButtonIndex] <= ITEM_BOW_ARROW_LIGHT)) {
|
||||
if (gSaveContext.equips.buttonItems[otherButtonIndex] >= ITEM_BOW_ARROW_FIRE &&
|
||||
gSaveContext.equips.buttonItems[otherButtonIndex] <= ITEM_BOW_ARROW_LIGHT &&
|
||||
!CVarGetInteger("gSeparateArrows", 0)) {
|
||||
gSaveContext.equips.buttonItems[otherButtonIndex] = gSaveContext.equips.buttonItems[targetButtonIndex];
|
||||
gSaveContext.equips.cButtonSlots[otherSlotIndex] = gSaveContext.equips.cButtonSlots[pauseCtx->equipTargetCBtn];
|
||||
Interface_LoadItemIcon2(play, otherButtonIndex);
|
||||
|
||||
@@ -343,7 +343,7 @@ void KaleidoScope_DrawDungeonMap(PlayState* play, GraphicsContext* gfxCtx) {
|
||||
// Offset the U value of each vertex to be in the mirror boundary for the map textures
|
||||
if (mirroredWorld) {
|
||||
for (size_t i = 0; i < 8; i++) {
|
||||
pauseCtx->mapPageVtx[60 + i].v.tc[0] += 48 << 5;
|
||||
pauseCtx->mapPageVtx[60 + i].v.tc[0] += MAP_48x85_TEX_WIDTH << 5;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -353,8 +353,9 @@ void KaleidoScope_DrawDungeonMap(PlayState* play, GraphicsContext* gfxCtx) {
|
||||
gSPInvalidateTexCache(POLY_KAL_DISP++, interfaceCtx->mapSegment[0]);
|
||||
gSPInvalidateTexCache(POLY_KAL_DISP++, interfaceCtx->mapSegment[1]);
|
||||
|
||||
gDPLoadTextureBlock_4b(POLY_KAL_DISP++, interfaceCtx->mapSegmentName[0], G_IM_FMT_CI, 48, 85, 0, G_TX_WRAP | mirrorMode,
|
||||
G_TX_WRAP | G_TX_NOMIRROR, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD);
|
||||
gDPLoadTextureBlock_4b(POLY_KAL_DISP++, interfaceCtx->mapSegmentName[0], G_IM_FMT_CI, MAP_48x85_TEX_WIDTH,
|
||||
MAP_48x85_TEX_HEIGHT, 0, G_TX_WRAP | mirrorMode, G_TX_WRAP | G_TX_NOMIRROR, G_TX_NOMASK,
|
||||
G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD);
|
||||
|
||||
// Swap vertices to render left half on the right and vice-versa
|
||||
if (mirroredWorld) {
|
||||
@@ -363,9 +364,9 @@ void KaleidoScope_DrawDungeonMap(PlayState* play, GraphicsContext* gfxCtx) {
|
||||
gSP1Quadrangle(POLY_KAL_DISP++, 0, 2, 3, 1, 0);
|
||||
}
|
||||
|
||||
gDPLoadTextureBlock_4b(POLY_KAL_DISP++, interfaceCtx->mapSegmentName[1], G_IM_FMT_CI, 48, 85, 0,
|
||||
G_TX_WRAP | mirrorMode, G_TX_WRAP | G_TX_NOMIRROR, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD,
|
||||
G_TX_NOLOD);
|
||||
gDPLoadTextureBlock_4b(POLY_KAL_DISP++, interfaceCtx->mapSegmentName[1], G_IM_FMT_CI, MAP_48x85_TEX_WIDTH,
|
||||
MAP_48x85_TEX_HEIGHT, 0, G_TX_WRAP | mirrorMode, G_TX_WRAP | G_TX_NOMIRROR, G_TX_NOMASK,
|
||||
G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD);
|
||||
|
||||
if (mirroredWorld) {
|
||||
gSP1Quadrangle(POLY_KAL_DISP++, 0, 2, 3, 1, 0);
|
||||
|
||||
@@ -13,6 +13,10 @@ extern u8 gItemAgeReqs[];
|
||||
extern u8 gAreaGsFlags[];
|
||||
extern bool gSelectingMask;
|
||||
|
||||
#define MAP_48x85_TEX_WIDTH 48
|
||||
#define MAP_48x85_TEX_HEIGHT 85
|
||||
#define MAP_48x85_TEX_SIZE ((MAP_48x85_TEX_WIDTH * MAP_48x85_TEX_HEIGHT) / 2) // 48x85 CI4 texture
|
||||
|
||||
#define AGE_REQ_ADULT LINK_AGE_ADULT
|
||||
#define AGE_REQ_CHILD LINK_AGE_CHILD
|
||||
#define AGE_REQ_NONE 9
|
||||
|
||||
@@ -1205,6 +1205,8 @@ Gfx* KaleidoScope_DrawPageSections(Gfx* gfx, Vtx* vertices, void** textures) {
|
||||
return gfx;
|
||||
}
|
||||
|
||||
static uint8_t mapBlendMask[MAP_48x85_TEX_WIDTH * MAP_48x85_TEX_HEIGHT];
|
||||
|
||||
void KaleidoScope_DrawPages(PlayState* play, GraphicsContext* gfxCtx) {
|
||||
static Color_RGB8 D_8082ACF4[12] = {
|
||||
{ 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 255, 255, 0 }, { 0, 0, 0 },
|
||||
@@ -1373,6 +1375,10 @@ void KaleidoScope_DrawPages(PlayState* play, GraphicsContext* gfxCtx) {
|
||||
}
|
||||
}
|
||||
|
||||
// Need to invalidate the blend mask every frame. Ideally this would be done in KaleidoScope_DrawDungeonMap
|
||||
// but the reference is not shared between files
|
||||
gSPInvalidateTexCache(POLY_KAL_DISP++, mapBlendMask);
|
||||
|
||||
if (pauseCtx->pageIndex) { // pageIndex != PAUSE_ITEM
|
||||
gDPPipeSync(OVERLAY_DISP++);
|
||||
gDPSetCombineMode(OVERLAY_DISP++, G_CC_MODULATEIA, G_CC_MODULATEIA);
|
||||
@@ -3315,13 +3321,118 @@ void KaleidoScope_UpdateCursorSize(PauseContext* pauseCtx) {
|
||||
pauseCtx->cursorVtx[14].v.ob[1] = pauseCtx->cursorVtx[15].v.ob[1] = pauseCtx->cursorVtx[12].v.ob[1] - 16;
|
||||
}
|
||||
|
||||
// Modifed map texture buffers for registered blend effects and the room indicator color
|
||||
static uint8_t mapLeftTexModified[MAP_48x85_TEX_SIZE];
|
||||
static uint8_t mapRightTexModified[MAP_48x85_TEX_SIZE];
|
||||
static uint8_t* mapLeftTexModifiedRaw = NULL;
|
||||
static uint8_t* mapRightTexModifiedRaw = NULL;
|
||||
|
||||
// Load dungeon maps into the interface context
|
||||
// SoH [General] - Modified to account for our resource system and HD textures
|
||||
void KaleidoScope_LoadDungeonMap(PlayState* play) {
|
||||
InterfaceContext* interfaceCtx = &play->interfaceCtx;
|
||||
|
||||
// Free old textures
|
||||
if (mapLeftTexModifiedRaw != NULL) {
|
||||
free(mapLeftTexModifiedRaw);
|
||||
mapLeftTexModifiedRaw = NULL;
|
||||
}
|
||||
if (mapRightTexModifiedRaw != NULL) {
|
||||
free(mapRightTexModifiedRaw);
|
||||
mapRightTexModifiedRaw = NULL;
|
||||
}
|
||||
|
||||
// Unload original textures to bypass cache result for lookups
|
||||
ResourceMgr_UnloadOriginalWhenAltExists(sDungeonMapTexs[R_MAP_TEX_INDEX]);
|
||||
ResourceMgr_UnloadOriginalWhenAltExists(sDungeonMapTexs[R_MAP_TEX_INDEX + 1]);
|
||||
|
||||
interfaceCtx->mapSegmentName[0] = sDungeonMapTexs[R_MAP_TEX_INDEX];
|
||||
interfaceCtx->mapSegmentName[1] = sDungeonMapTexs[R_MAP_TEX_INDEX + 1];
|
||||
interfaceCtx->mapSegment[0] = ResourceGetDataByName(sDungeonMapTexs[R_MAP_TEX_INDEX]);
|
||||
interfaceCtx->mapSegment[1] = ResourceGetDataByName(sDungeonMapTexs[R_MAP_TEX_INDEX + 1]);
|
||||
|
||||
// When the texture is HD (raw) we need to copy a dynamic amount of data
|
||||
// Otherwise the original asset has a static size
|
||||
if (ResourceMgr_TexIsRaw(interfaceCtx->mapSegmentName[0])) {
|
||||
u32 width = ResourceGetTexWidthByName(interfaceCtx->mapSegmentName[0]);
|
||||
u32 height = ResourceGetTexHeightByName(interfaceCtx->mapSegmentName[0]);
|
||||
size_t size = (width * height) / 2; // account for CI4 size
|
||||
|
||||
// Resource size being larger than the calculated CI size means it is most likely not a CI4 texture
|
||||
// Abort early end undo the blended effect by clearing the mask to avoid crashing
|
||||
if (size < ResourceGetTexSizeByName(interfaceCtx->mapSegmentName[0])) {
|
||||
if (mapBlendMask[0] != 0) {
|
||||
for (size_t i = 0; i < ARRAY_COUNT(mapBlendMask); i++) {
|
||||
mapBlendMask[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
interfaceCtx->mapSegment[0] = NULL;
|
||||
interfaceCtx->mapSegment[1] = NULL;
|
||||
|
||||
Gfx_RegisterBlendedTexture(interfaceCtx->mapSegmentName[0], mapBlendMask, NULL);
|
||||
Gfx_RegisterBlendedTexture(interfaceCtx->mapSegmentName[1], mapBlendMask, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
u8* map1TexRaw = ResourceGetDataByName(interfaceCtx->mapSegmentName[0]);
|
||||
u8* map2TexRaw = ResourceGetDataByName(interfaceCtx->mapSegmentName[1]);
|
||||
|
||||
mapLeftTexModifiedRaw = malloc(size);
|
||||
mapRightTexModifiedRaw = malloc(size);
|
||||
|
||||
memcpy(mapLeftTexModifiedRaw, map1TexRaw, size);
|
||||
memcpy(mapRightTexModifiedRaw, map2TexRaw, size);
|
||||
|
||||
interfaceCtx->mapSegment[0] = mapLeftTexModifiedRaw;
|
||||
interfaceCtx->mapSegment[1] = mapRightTexModifiedRaw;
|
||||
} else {
|
||||
u8* map1Tex = ResourceGetDataByName(interfaceCtx->mapSegmentName[0]);
|
||||
u8* map2Tex = ResourceGetDataByName(interfaceCtx->mapSegmentName[1]);
|
||||
|
||||
memcpy(mapLeftTexModified, map1Tex, MAP_48x85_TEX_SIZE);
|
||||
memcpy(mapRightTexModified, map2Tex, MAP_48x85_TEX_SIZE);
|
||||
|
||||
interfaceCtx->mapSegment[0] = mapLeftTexModified;
|
||||
interfaceCtx->mapSegment[1] = mapRightTexModified;
|
||||
}
|
||||
|
||||
// Mark and register the blend mask for the copied textures
|
||||
if (mapBlendMask[0] != 1) {
|
||||
for (size_t i = 0; i < ARRAY_COUNT(mapBlendMask); i++) {
|
||||
mapBlendMask[i] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
Gfx_RegisterBlendedTexture(interfaceCtx->mapSegmentName[0], mapBlendMask, interfaceCtx->mapSegment[0]);
|
||||
Gfx_RegisterBlendedTexture(interfaceCtx->mapSegmentName[1], mapBlendMask, interfaceCtx->mapSegment[1]);
|
||||
}
|
||||
|
||||
static uint8_t registeredDungeonMapTextureHook = false;
|
||||
|
||||
void KaleidoScope_RegisterUpdatedDungeonMapTexture() {
|
||||
if (gPlayState == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
PauseContext* pauseCtx = &gPlayState->pauseCtx;
|
||||
|
||||
// Kaleido is not open in a dungeon so there is nothing to do
|
||||
if (R_PAUSE_MENU_MODE < 3 || pauseCtx->state < 4 || pauseCtx->state > 7 || !sInDungeonScene) {
|
||||
return;
|
||||
}
|
||||
|
||||
KaleidoScope_UpdateDungeonMap(gPlayState);
|
||||
|
||||
// KaleidoScope_UpdateDungeonMap will update the palette index for the current floor if the cursor is on the floor
|
||||
// If the player toggles alt assets while the cursor is not in the floor level, then we handle the palette index here
|
||||
if (gPlayState->sceneNum >= SCENE_DEKU_TREE && gPlayState->sceneNum <= SCENE_TREASURE_BOX_SHOP &&
|
||||
(VREG(30) + 3) == pauseCtx->dungeonMapSlot && (VREG(30) + 3) != pauseCtx->cursorPoint[PAUSE_MAP]) {
|
||||
|
||||
InterfaceContext* interfaceCtx = &gPlayState->interfaceCtx;
|
||||
int32_t size = ResourceGetTexSizeByName(interfaceCtx->mapSegmentName[0]);
|
||||
|
||||
KaleidoScope_OverridePalIndexCI4(interfaceCtx->mapSegment[0], size, interfaceCtx->mapPaletteIndex, 14);
|
||||
KaleidoScope_OverridePalIndexCI4(interfaceCtx->mapSegment[1], size, interfaceCtx->mapPaletteIndex, 14);
|
||||
}
|
||||
}
|
||||
|
||||
void KaleidoScope_UpdateDungeonMap(PlayState* play) {
|
||||
@@ -3333,19 +3444,29 @@ void KaleidoScope_UpdateDungeonMap(PlayState* play) {
|
||||
KaleidoScope_LoadDungeonMap(play);
|
||||
Map_SetFloorPalettesData(play, pauseCtx->dungeonMapSlot - 3);
|
||||
|
||||
s32 size = MAP_48x85_TEX_SIZE;
|
||||
|
||||
if (ResourceMgr_TexIsRaw(interfaceCtx->mapSegmentName[0])) {
|
||||
size = ResourceGetTexSizeByName(interfaceCtx->mapSegmentName[0]);
|
||||
}
|
||||
|
||||
if ((play->sceneNum >= SCENE_DEKU_TREE) && (play->sceneNum <= SCENE_TREASURE_BOX_SHOP)) {
|
||||
if ((VREG(30) + 3) == pauseCtx->cursorPoint[PAUSE_MAP]) {
|
||||
// HDTODO: Handle Runtime Modified Textures (HD)
|
||||
KaleidoScope_OverridePalIndexCI4(interfaceCtx->mapSegment[0], 2040, interfaceCtx->mapPaletteIndex, 14);
|
||||
KaleidoScope_OverridePalIndexCI4(interfaceCtx->mapSegment[0], size, interfaceCtx->mapPaletteIndex, 14);
|
||||
}
|
||||
}
|
||||
|
||||
if ((play->sceneNum >= SCENE_DEKU_TREE) && (play->sceneNum <= SCENE_TREASURE_BOX_SHOP)) {
|
||||
if ((VREG(30) + 3) == pauseCtx->cursorPoint[PAUSE_MAP]) {
|
||||
// HDTODO: Handle Runtime Modified Textures (HD)
|
||||
KaleidoScope_OverridePalIndexCI4(interfaceCtx->mapSegment[1], 2040, interfaceCtx->mapPaletteIndex, 14);
|
||||
KaleidoScope_OverridePalIndexCI4(interfaceCtx->mapSegment[1], size, interfaceCtx->mapPaletteIndex, 14);
|
||||
}
|
||||
}
|
||||
|
||||
// Register alt listener to update the blended dungeon map textures on alt toggle
|
||||
if (!registeredDungeonMapTextureHook) {
|
||||
registeredDungeonMapTextureHook = true;
|
||||
GameInteractor_RegisterOnAssetAltChange(KaleidoScope_RegisterUpdatedDungeonMapTexture);
|
||||
}
|
||||
}
|
||||
|
||||
void KaleidoScope_Update(PlayState* play)
|
||||
@@ -4290,6 +4411,8 @@ void KaleidoScope_Update(PlayState* play)
|
||||
if (IS_RANDO && Randomizer_GetSettingValue(RSK_SHUFFLE_ENTRANCES)) {
|
||||
Grotto_ForceGrottoReturn();
|
||||
}
|
||||
// Reset frame counter to prevent autosave on respawn
|
||||
play->gameplayFrames = 0;
|
||||
gSaveContext.nextTransitionType = 2;
|
||||
gSaveContext.health = CVarGetInteger("gFullHealthSpawn", 0) ? gSaveContext.healthCapacity : 0x30;
|
||||
Audio_QueueSeqCmd(0xF << 28 | SEQ_PLAYER_BGM_MAIN << 24 | 0xA);
|
||||
|
||||
@@ -1762,6 +1762,13 @@ static MapMarkData sMapMarkJabuJabuBellyMq[] = {
|
||||
} },
|
||||
{ MAP_MARK_NONE, 0, { 0 } },
|
||||
},
|
||||
// Jabu-Jabu's Belly minimap 16
|
||||
// SoH [General] - This entry corresponds to Big Octorok's room and is missing in the MQ game
|
||||
// N64 hardware does an OoB read and lands on MQ Forest Temple room 0
|
||||
// To avoid UB with OoB for SoH, the correct entry is now added below
|
||||
{
|
||||
{ MAP_MARK_NONE, 0, { 0 } },
|
||||
},
|
||||
};
|
||||
|
||||
static MapMarkData sMapMarkForestTempleMq[] = {
|
||||
|
||||
Reference in New Issue
Block a user