Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ shipofharkinian.json
imgui.ini
saves/*
randomizer/*
2S2HTimeSplitData.json

mm/libultraship/extern/Debug/ImGui.lib

Expand Down
4 changes: 2 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ 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")
set(GAME_STR "MM")
project(2s2h VERSION 3.0.1 LANGUAGES C CXX)
project(2s2h VERSION 3.0.2 LANGUAGES C CXX)
include(CMake/2ship-cvars.cmake)
include(CMake/lus-cvars.cmake)
set(SPDLOG_LEVEL_TRACE 0)
set(SPDLOG_LEVEL_OFF 6)
set(SPDLOG_MIN_CUTOFF SPDLOG_LEVEL_TRACE CACHE STRING "cutoff at trace")
set(PROJECT_BUILD_NAME "Mion Bravo" CACHE STRING "" FORCE)
set(PROJECT_BUILD_NAME "Mion Charlie" CACHE STRING "" FORCE)
set(PROJECT_TEAM "github.com/harbourmasters" CACHE STRING "" FORCE)

execute_process(
Expand Down
16 changes: 16 additions & 0 deletions mm/2s2h/BenGui/BenMenu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,7 @@ void BenMenu::AddSettings() {
.Options(ButtonOptions().Tooltip("Enables the separate Bindings Window.").Size(Sizes::Inline));

path.sidebarName = "Overlay";
path.column = SECTION_COLUMN_1;
AddSidebarEntry("Settings", "Overlay", 2);
AddWidget(path, "Notifications", WIDGET_SEPARATOR_TEXT);
AddWidget(path, "Position", WIDGET_CVAR_COMBOBOX)
Expand Down Expand Up @@ -993,6 +994,11 @@ void BenMenu::AddEnhancements() {
.CVar("gEnhancements.Equipment.InvertShieldY")
.Options(CheckboxOptions().Tooltip(
"Invert the Y axis while holding the shield so that it moves up with the left stick."));
AddWidget(path, "Great Fairy Sword B-Button Attack", WIDGET_CVAR_CHECKBOX)
.CVar("gEnhancements.Equipment.GreatFairySwordBButton")
.Options(CheckboxOptions().Tooltip(
"When the Great Fairy's Sword is held, pressing B attacks with it instead of drawing "
"your equipped sword. The sword can still be put away with A as normal."));

path.column = SECTION_COLUMN_2;
AddWidget(path, "Modes", WIDGET_SEPARATOR_TEXT);
Expand Down Expand Up @@ -1390,6 +1396,12 @@ void BenMenu::AddEnhancements() {
.CVar("gEnhancements.Timesavers.SkipBalladOfWindfish")
.Options(CheckboxOptions().Tooltip(
"Play the complete Ballad after playing in one form if you have all three transformation masks."));
AddWidget(path, "Auto Bank Deposit", WIDGET_CVAR_CHECKBOX)
.CVar("gEnhancements.Timesavers.AutoBankDeposit")
.Options(CheckboxOptions().Tooltip(
"Automatically deposits excess Rupees into your bank account when your wallet is full. "
"Deposits stop when the bank reaches maximum capacity. "
"Bank rewards are granted automatically. Notifications display deposit amount and new balance."));

// Fixes
path = { "Enhancements", "Fixes", SECTION_COLUMN_1 };
Expand Down Expand Up @@ -1641,6 +1653,10 @@ void BenMenu::AddEnhancements() {
AddWidget(path, "Invincible", WIDGET_CVAR_CHECKBOX)
.CVar("gEnhancements.Minigames.BoatArcheryInvincible")
.Options(CheckboxOptions().Tooltip("Koume's health does not decrease when hit."));
AddWidget(path, "Treasure Chest Shop Show Full Maze", WIDGET_CVAR_CHECKBOX)
.CVar("gEnhancements.Minigames.TreasureChestShopShowFullMaze")
.Options(CheckboxOptions().Tooltip("Shows the entire maze layout in the Treasure Chest Shop minigame "
"instead of only revealing tiles near Link."));

path.column = SECTION_COLUMN_3;
AddWidget(path, "Other", WIDGET_SEPARATOR_TEXT);
Expand Down
2 changes: 2 additions & 0 deletions mm/2s2h/BenPort.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ CrowdControl* CrowdControl::Instance;
#include "2s2h/GameInteractor/GameInteractor.h"
#include "2s2h/Enhancements/Enhancements.h"
#include "2s2h/Enhancements/GfxPatcher/AuthenticGfxPatches.h"
#include "2s2h/Enhancements/GfxPatcher/PlayerCustomFlipbooks.h"
#include "2s2h/DeveloperTools/DebugConsole.h"
#include "2s2h/Rando/Rando.h"
#include "2s2h/Rando/Spoiler/Spoiler.h"
Expand Down Expand Up @@ -719,6 +720,7 @@ extern "C" void InitOTR() {
OTRMessage_Init();
OTRAudio_Init();
OTRExtScanner();
PlayerCustomFlipbooks_Patch();

// Just came up with arbitrary numbers that seemed to work, this is
// usually set once(?) in currently stubbed out areas of code.
Expand Down
67 changes: 67 additions & 0 deletions mm/2s2h/DeveloperTools/SaveEditor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include "2s2h/BenGui/UIWidgets.hpp"
#include "2s2h/GameInteractor/GameInteractor.h"
#include "2s2h/Rando/Rando.h"
#include "2s2h/Rando/MiscBehavior/ClockShuffle.h"
#include "2s2h/CustomMessage/CustomMessage.h"
#include "2s2h/CustomItem/CustomItem.h"
#include "2s2h/BenGui/Notification.h"
Expand Down Expand Up @@ -896,6 +897,72 @@ void DrawItemsAndMasksTab() {
UIWidgets::Checkbox("Safe Mode", &safeMode);

if (gSaveContext.save.shipSaveInfo.saveType == SAVETYPE_RANDO) {
if (RANDO_SAVE_OPTIONS[RO_CLOCK_SHUFFLE]) {
// Time Items Management Section
ImGui::SeparatorText("Time Items");

// Individual time items in 3x2 grid with static positioning
RandoItemId clockItems[] = { RI_TIME_DAY_1, RI_TIME_DAY_2, RI_TIME_DAY_3,
RI_TIME_NIGHT_1, RI_TIME_NIGHT_2, RI_TIME_NIGHT_3 };

const char* clockNames[] = { "Day 1", "Day 2", "Day 3", "Night 1", "Night 2", "Night 3" };

// Use table for static positioning - 3 columns, 2 rows
if (ImGui::BeginTable("ClockItemsTable", 3, ImGuiTableFlags_None)) {
ImGui::TableSetupColumn("Day 1", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn("Day 2", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn("Day 3", ImGuiTableColumnFlags_WidthStretch);

// First row - Day items
ImGui::TableNextRow();
for (int i = 0; i < 3; i++) {
ImGui::TableNextColumn();
RandoItemId clockItem = clockItems[i];
int halfIndex = Rando::ClockItems::GetHalfDayIndexFromClockItem(clockItem);
bool isOwned = Flags_GetRandoInf(static_cast<RandoInf>(RANDO_INF_OBTAINED_CLOCK_DAY_1 + halfIndex));

std::string buttonText =
isOwned ? ("Remove " + std::string(clockNames[i])) : ("No Item##" + std::to_string(i));
std::string tooltipText = "";
if (!isOwned) {
tooltipText = "You don't own " + std::string(clockNames[i]);
}
UIWidgets::ButtonOptions buttonOpts;
buttonOpts.disabled = !isOwned;
buttonOpts.disabledTooltip = !isOwned ? tooltipText.c_str() : "";
if (UIWidgets::Button(buttonText.c_str(), buttonOpts)) {
Rando::RemoveItem(clockItem);
}
}

// Second row - Night items
ImGui::TableNextRow();
for (int i = 3; i < 6; i++) {
ImGui::TableNextColumn();
RandoItemId clockItem = clockItems[i];
int halfIndex = Rando::ClockItems::GetHalfDayIndexFromClockItem(clockItem);
bool isOwned = Flags_GetRandoInf(static_cast<RandoInf>(RANDO_INF_OBTAINED_CLOCK_DAY_1 + halfIndex));

std::string buttonText =
isOwned ? ("Remove " + std::string(clockNames[i])) : ("No Item##" + std::to_string(i));
std::string tooltipText = "";
if (!isOwned) {
tooltipText = "You don't own " + std::string(clockNames[i]);
}
UIWidgets::ButtonOptions buttonOpts;
buttonOpts.disabled = !isOwned;
buttonOpts.disabledTooltip = !isOwned ? tooltipText.c_str() : "";
if (UIWidgets::Button(buttonText.c_str(), buttonOpts)) {
Rando::RemoveItem(clockItem);
}
}

ImGui::EndTable();
}
}

// Queue Randomizer Item Gives section
ImGui::Spacing();
ImGui::SeparatorText("Queue Randomizer Item Gives");

static ImGuiTextFilter riFilter;
Expand Down
26 changes: 26 additions & 0 deletions mm/2s2h/Enhancements/Equipment/GreatFairySwordBButton.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#include <libultraship/bridge/consolevariablebridge.h>
#include "2s2h/GameInteractor/GameInteractor.h"
#include "2s2h/ShipInit.hpp"

extern "C" {
#include "variables.h"
extern Input* sPlayerControlInput;
}

#define CVAR_NAME "gEnhancements.Equipment.GreatFairySwordBButton"
#define CVAR CVarGetInteger(CVAR_NAME, 0)

void RegisterGreatFairySwordBButton() {
COND_VB_SHOULD(VB_GET_ITEM_ON_BUTTON, CVAR, {
Player* player = GET_PLAYER(gPlayState);
EquipSlot slot = (EquipSlot)va_arg(args, int);
ItemId* item = va_arg(args, ItemId*);

if (slot == EQUIP_SLOT_B && player->transformation == PLAYER_FORM_HUMAN &&
player->heldItemId == ITEM_SWORD_GREAT_FAIRY) {
*item = ITEM_SWORD_GREAT_FAIRY;
}
});
}

static RegisterShipInitFunc initFunc(RegisterGreatFairySwordBButton, { CVAR_NAME });
133 changes: 133 additions & 0 deletions mm/2s2h/Enhancements/GfxPatcher/PlayerCustomFlipbooks.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
#include "PlayerCustomFlipbooks.h"
#include "2s2h/BenPort.h"

extern "C" {
#include "z64player.h"
extern TexturePtr sPlayerEyesTextures[PLAYER_FORM_MAX][PLAYER_EYES_MAX];
extern TexturePtr sPlayerMouthTextures[PLAYER_FORM_MAX][PLAYER_MOUTH_MAX];
uint8_t ResourceMgr_FileExists(const char* resName);
}

static const char* sFDEyesTextures[PLAYER_EYES_MAX] = {
"__OTR__objects/object_link_boy/gLinkFierceDeityEyesOpenTex",
"__OTR__objects/object_link_boy/gLinkFierceDeityEyesHalfTex",
"__OTR__objects/object_link_boy/gLinkFierceDeityEyesClosedTex",
"__OTR__objects/object_link_boy/gLinkFierceDeityEyesRightTex",
"__OTR__objects/object_link_boy/gLinkFierceDeityEyesLeftTex",
"__OTR__objects/object_link_boy/gLinkFierceDeityEyesUpTex",
"__OTR__objects/object_link_boy/gLinkFierceDeityEyesDownTex",
"__OTR__objects/object_link_boy/gLinkFierceDeityEyesWincingTex",
};

static const char* sFDMouthTextures[PLAYER_MOUTH_MAX] = {
"__OTR__objects/object_link_boy/gLinkFierceDeityMouthClosedTex",
"__OTR__objects/object_link_boy/gLinkFierceDeityMouthHalfTex",
"__OTR__objects/object_link_boy/gLinkFierceDeityMouthOpenTex",
"__OTR__objects/object_link_boy/gLinkFierceDeityMouthSmileTex",
};

static const char* sDekuEyesTextures[PLAYER_EYES_MAX] = {
"__OTR__objects/object_link_nuts/gLinkDekuEyesOpenTex", "__OTR__objects/object_link_nuts/gLinkDekuEyesHalfTex",
"__OTR__objects/object_link_nuts/gLinkDekuEyesClosedTex", "__OTR__objects/object_link_nuts/gLinkDekuEyesRightTex",
"__OTR__objects/object_link_nuts/gLinkDekuEyesLeftTex", "__OTR__objects/object_link_nuts/gLinkDekuEyesUpTex",
"__OTR__objects/object_link_nuts/gLinkDekuEyesDownTex", "__OTR__objects/object_link_nuts/gLinkDekuEyesWincingTex",
};

static const char* sDekuMouthTextures[PLAYER_MOUTH_MAX] = {
"__OTR__objects/object_link_nuts/gLinkDekuMouthClosedTex",
"__OTR__objects/object_link_nuts/gLinkDekuMouthHalfTex",
"__OTR__objects/object_link_nuts/gLinkDekuMouthOpenTex",
"__OTR__objects/object_link_nuts/gLinkDekuMouthSmileTex",
};

static const char* sGoronMouthTextures[PLAYER_MOUTH_MAX] = {
"__OTR__objects/object_link_goron/gLinkGoronMouthClosedTex",
"__OTR__objects/object_link_goron/gLinkGoronMouthHalfTex",
"__OTR__objects/object_link_goron/gLinkGoronMouthOpenTex",
"__OTR__objects/object_link_goron/gLinkGoronMouthSmileTex",
};

static s32 sFacePatchState = 0;

static void PlayerCustomFlipbooks_PatchOnce(void) {
if (sFacePatchState != 0) {
return;
}

bool EyesPatch = true;
bool MouthPatch = true;
bool DekuEyesPatch = true;
bool DekuMouthPatch = true;
bool GoronMouthPatch = true;

for (s32 i = 0; i < PLAYER_EYES_MAX; i++) {
if (!ResourceMgr_FileExists(sFDEyesTextures[i])) {
EyesPatch = false;
break;
}
}

for (s32 i = 0; i < PLAYER_MOUTH_MAX; i++) {
if (!ResourceMgr_FileExists(sFDMouthTextures[i])) {
MouthPatch = false;
break;
}
}

for (s32 i = 0; i < PLAYER_EYES_MAX; i++) {
if (!ResourceMgr_FileExists(sDekuEyesTextures[i])) {
DekuEyesPatch = false;
break;
}
}

for (s32 i = 0; i < PLAYER_MOUTH_MAX; i++) {
if (!ResourceMgr_FileExists(sDekuMouthTextures[i])) {
DekuMouthPatch = false;
break;
}
}

for (s32 i = 0; i < PLAYER_MOUTH_MAX; i++) {
if (!ResourceMgr_FileExists(sGoronMouthTextures[i])) {
GoronMouthPatch = false;
break;
}
}

if (EyesPatch) {
for (s32 i = 0; i < PLAYER_EYES_MAX; i++) {
sPlayerEyesTextures[PLAYER_FORM_FIERCE_DEITY][i] = (TexturePtr)sFDEyesTextures[i];
}
}

if (MouthPatch) {
for (s32 i = 0; i < PLAYER_MOUTH_MAX; i++) {
sPlayerMouthTextures[PLAYER_FORM_FIERCE_DEITY][i] = (TexturePtr)sFDMouthTextures[i];
}
}

if (DekuEyesPatch) {
for (s32 i = 0; i < PLAYER_EYES_MAX; i++) {
sPlayerEyesTextures[PLAYER_FORM_DEKU][i] = (TexturePtr)sDekuEyesTextures[i];
}
}

if (DekuMouthPatch) {
for (s32 i = 0; i < PLAYER_MOUTH_MAX; i++) {
sPlayerMouthTextures[PLAYER_FORM_DEKU][i] = (TexturePtr)sDekuMouthTextures[i];
}
}

if (GoronMouthPatch) {
for (s32 i = 0; i < PLAYER_MOUTH_MAX; i++) {
sPlayerMouthTextures[PLAYER_FORM_GORON][i] = (TexturePtr)sGoronMouthTextures[i];
}
}

sFacePatchState = 1;
}

void PlayerCustomFlipbooks_Patch(void) {
PlayerCustomFlipbooks_PatchOnce();
}
3 changes: 3 additions & 0 deletions mm/2s2h/Enhancements/GfxPatcher/PlayerCustomFlipbooks.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#pragma once

void PlayerCustomFlipbooks_Patch(void);
40 changes: 40 additions & 0 deletions mm/2s2h/Enhancements/Minigames/TreasureChestShopFullMaze.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#include <libultraship/bridge/consolevariablebridge.h>
#include "2s2h/GameInteractor/GameInteractor.h"
#include "2s2h/ShipInit.hpp"

// Re-definitions to avoid modifying source headers
#define TAKARAYA_WALL_ROWS 11
#define TAKARAYA_WALL_COLUMNS 8

extern "C" {
extern f32 sTakarayaWallHeights[TAKARAYA_WALL_ROWS][TAKARAYA_WALL_COLUMNS];
extern u8 sTakarayaWallStates[TAKARAYA_WALL_ROWS][TAKARAYA_WALL_COLUMNS];
}

// Re-definition to avoid modifying source headers
typedef enum { TAKARAYA_WALL_INACTIVE, TAKARAYA_WALL_RISING, TAKARAYA_WALL_FALLING } TakarayaWallCellState;

#define CVAR_NAME "gEnhancements.Minigames.TreasureChestShopShowFullMaze"
#define CVAR CVarGetInteger(CVAR_NAME, 0)

static void RegisterTreasureChestShopFullMaze() {
COND_ID_HOOK(OnActorUpdate, ACTOR_OBJ_TAKARAYA_WALL, CVAR, [](Actor* actor) {
if (gSaveContext.timerStates[TIMER_ID_MINIGAME_2] == TIMER_STATE_OFF) {
return;
}

for (int i = 0; i < TAKARAYA_WALL_ROWS; i++) {
for (int j = 0; j < TAKARAYA_WALL_COLUMNS; j++) {
if (sTakarayaWallHeights[i][j] >= 0.0f) {
if (Math_StepToF(&sTakarayaWallHeights[i][j], 120.0f, 15.0f)) {
sTakarayaWallStates[i][j] = TAKARAYA_WALL_INACTIVE;
} else {
sTakarayaWallStates[i][j] = TAKARAYA_WALL_RISING;
}
}
}
}
});
}

static RegisterShipInitFunc initFunc(RegisterTreasureChestShopFullMaze, { CVAR_NAME });
4 changes: 3 additions & 1 deletion mm/2s2h/Enhancements/Saving/SavingEnhancements.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ extern "C" int SavingEnhancements_GetSaveEntrance() {
for (int i = 0; i < RESPAWN_MODE_MAX; i++) {
gSaveContext.save.shipSaveInfo.respawn[i] = gSaveContext.respawn[i];
}
return entranceToSave;
// Daytelop on new game, with Time Shuffle, makes it possible for entranceToSave to be -1. Given that the player
// must be at this entrance in that scenario, just use it as a fallback.
return entranceToSave < 0 ? ENTRANCE(SOUTH_CLOCK_TOWN, 0) : entranceToSave;
} else {
switch (gPlayState->sceneId) {
// Woodfall Temple + Odolwa
Expand Down
Loading
Loading