diff --git a/.gitignore b/.gitignore index 9d261c2a..8a230e82 100644 --- a/.gitignore +++ b/.gitignore @@ -22,7 +22,7 @@ oldcode/loader/build *.smdh *.pyc *.ips -!basecode1.1.ips +!deltapatch.ips *.zar source/patch_symbols.hpp .DS_Store \ No newline at end of file diff --git a/code/basecode1.1.ips b/code/deltapatch.ips similarity index 89% rename from code/basecode1.1.ips rename to code/deltapatch.ips index f266c623..64bf2b0e 100644 Binary files a/code/basecode1.1.ips and b/code/deltapatch.ips differ diff --git a/code/include/hid.h b/code/include/hid.h index 9e6eb262..01c3ca13 100644 --- a/code/include/hid.h +++ b/code/include/hid.h @@ -111,5 +111,9 @@ typedef struct { #define BUTTON_L1 (1 << 9) #define BUTTON_X (1 << 10) #define BUTTON_Y (1 << 11) +#define CPAD_RIGHT (1 << 28) +#define CPAD_LEFT (1 << 29) +#define CPAD_UP (1 << 30) +#define CPAD_DOWN (1 << 31) #endif // HID_H diff --git a/code/include/draw.h b/code/include/rnd/draw.h similarity index 69% rename from code/include/draw.h rename to code/include/rnd/draw.h index b20ae0bf..bdf4d682 100644 --- a/code/include/draw.h +++ b/code/include/rnd/draw.h @@ -25,8 +25,10 @@ * or requiring that modified versions of such material be marked in * reasonable ways as different from the original version. */ +extern "C" { #include <3ds/gfx.h> #include <3ds/types.h> +} #define FB_BOTTOM_VRAM_ADDR ((void*)0x1F48F000) // cached #define FB_BOTTOM_VRAM_PA 0x1848F000 @@ -42,22 +44,73 @@ #define SPACING_Y 11 #define SPACING_X 6 +#define SPACING_SMALL_Y 9 +#define SPACING_SMALL_X 6 #define RGB8(r, g, b) (((b)&0xFF) | (((g)&0xFF) << 8) | (((r)&0xFF) << 16)) #define COLOR_TITLE RGB8(0x33, 0x33, 0xFF) #define COLOR_WHITE RGB8(0xFF, 0xFF, 0xFF) #define COLOR_RED RGB8(0xFF, 0x00, 0x00) #define COLOR_GREEN RGB8(0x00, 0xFF, 0x00) +#define COLOR_BLUE RGB8(0x56, 0xB4, 0xE9) +#define COLOR_ORANGE RGB8(0xE6, 0x9F, 0x00) +#define COLOR_YELLOW RGB8(0xF0, 0xE4, 0x42) +#define COLOR_PINK RGB8(0xCC, 0x79, 0xA7) #define COLOR_BLACK RGB8(0x00, 0x00, 0x00) +#define COLOR_DARK_GRAY RGB8(0x29, 0x29, 0x29) +#define COLOR_LIGHT_GRAY RGB8(0x71, 0x71, 0x71) #define DRAW_MAX_FORMATTED_STRING_SIZE 512 +#define ICON_WIDTH 8 +#define ICON_HEIGHT 8 + +typedef enum { + DISPLAY_0 = 0x400, + DISPLAY_1 = 0x401, + DISPLAY_BOTH = 0x402, + DISPLAY0_EXT = 0x410, +} Draw_Display; + +typedef enum { + ICON_SMALL_KEY, + ICON_BOSS_KEY, + ICON_TRIFORCE, + ICON_FOOL, + ICON_CHECK, + ICON_NO, + ICON_VANILLA, + ICON_MASTER_QUEST, + ICON_MAP, + ICON_COMPASS, + ICON_BUTTON_R, + ICON_BUTTON_R_WIDE_1, + ICON_BUTTON_R_WIDE_2, + ICON_BUTTON_L, + ICON_BUTTON_L_WIDE_1, + ICON_BUTTON_L_WIDE_2, + ICON_BUTTON_A, + ICON_BUTTON_B, + ICON_BUTTON_X, + ICON_BUTTON_Y, + ICON_BUTTON_DPAD, + ICON_BUTTON_FACE, + ICON_BUTTON_FACEH, + ICON_BUTTON_FACEV, + ICON_BUTTON_JOYSTICK, + ICONS_COUNT +} Draw_IconType; + void Draw_Lock(void); void Draw_Unlock(void); +void Draw_DrawIcon(u32 posX, u32 posY, u32 color, Draw_IconType icon); +void Draw_DrawRect(u32 posX, u32 posY, u32 width, u32 height, u32 color); void Draw_DrawCharacter(u32 posX, u32 posY, u32 color, char character); u32 Draw_DrawString(u32 posX, u32 posY, u32 color, const char* string); +u32 Draw_DrawString_Small(u32 posX, u32 posY, u32 color, const char* string); u32 Draw_DrawFormattedString(u32 posX, u32 posY, u32 color, const char* fmt, ...); +u32 Draw_DrawFormattedString_Small(u32 posX, u32 posY, u32 color, const char* fmt, ...); void Draw_DrawCharacterTop(u32 posX, u32 posY, u32 color, char character); u32 Draw_DrawStringTop(u32 posX, u32 posY, u32 color, const char* string); @@ -66,6 +119,9 @@ u32 Draw_DrawFormattedStringTop(u32 posX, u32 posY, u32 color, const char* fmt, void Draw_FillFramebuffer(u32 value); void Draw_ClearFramebuffer(void); void Draw_SetupFramebuffer(void); +void Draw_FillBackBuffer(void); +void Draw_ClearBackbuffer(void); +void Draw_CopyBackBuffer(void); void Draw_FlushFramebuffer(void); void Draw_FlushFramebufferTop(void); @@ -97,4 +153,4 @@ struct Graphics { Framebuffer top1; Framebuffer top2; Framebuffer bottom; -}; \ No newline at end of file +}; diff --git a/code/include/rnd/dungeon.h b/code/include/rnd/dungeon.h new file mode 100644 index 00000000..ecd59e5b --- /dev/null +++ b/code/include/rnd/dungeon.h @@ -0,0 +1,56 @@ +#ifndef _RND_DUNGEON_H +#define _RND_DUNGEON_H + +#include +#include "rnd/savefile.h" +#include "rnd/settings.h" +#include "rnd/spoiler_data.h" +//#include "rnd/draw.h" +extern "C" { +#include <3ds/svc.h> +} + +#define WOODFALL_KEY_COUNT 1 +#define SNOWHEAD_KEY_COUNT 3 +#define GREAT_KEY_COUNT 1 +#define STONE_KEY_COUNT 4 + +namespace rnd { + + typedef struct { + u16 spoilerIndex; + u8 keyAmount; + } KeyData; + + typedef enum { + DUNGEON_WOODFALL = 0, + DUNGEON_SNOWHEAD, + DUNGEON_GREAT_BAY, + DUNGEON_STONE_TOWER, + DUNGEON_PIRATE_FORTRESS, + DUNGEON_BENEATH_THE_WELL, + DUNGEON_IKANA_CASTLE, + DUNGEON_SECRET_SHRINE, + DUNGEON_THE_MOON, + DUNGEON_SWAMP_SKULLTULA_HOUSE, + DUNGEON_OCEAN_SKULLTULA_HOUSE, + } DungeonId; + + extern const char DungeonNames[][25]; + + static const char* const smallKeyStringWoodfall = "Woodfall Temple Small Key"; + static const char* const smallKeyStringSnowhead = "Snowhead Temple Small Key"; + static const char* const smallKeyStringGreatBay = "Great Bay Temple Small Key"; + static const char* const smallKeyStringStone = "Stone Tower Temple Small Key"; + + static const char* const keyRingStringWoodfall = "Woodfall Temple Key Ring"; + static const char* const keyRingStringSnowhead = "Snowhead Temple Key Ring"; + static const char* const keyRingStringGreatBay = "Great Bay Temple Key Ring"; + static const char* const keyRingStringStone = "Stone Tower Temple Key Ring"; + + u8 Dungeon_KeyAmount(u32); + u8 Dungeon_FoundSmallKeys(u32); + +} // namespace rnd + +#endif \ No newline at end of file diff --git a/code/include/rnd/gfx.h b/code/include/rnd/gfx.h new file mode 100644 index 00000000..66f0b1a6 --- /dev/null +++ b/code/include/rnd/gfx.h @@ -0,0 +1,67 @@ +#ifndef _RND_GFX_H_ +#define _RND_GFX_H_ + +#include "common/types.h" +#include "hid.h" +#include "rnd/custom_models.h" +#include "rnd/draw.h" +#include "rnd/dungeon.h" +#include "rnd/input.h" +#include "rnd/rheap.h" +#include "rnd/savefile.h" +#include "rnd/settings.h" +#include "rnd/spoiler_data.h" +#include "rnd/title_screen.h" +extern "C" { +#include <3ds/svc.h> +} + +namespace rnd { +#define TICKS_PER_SEC 268123480 +#define MAX_TICK_DELTA (TICKS_PER_SEC * 3) + +#define UP_ARROW_CHR 24 +#define DOWN_ARROW_CHR 25 +#define LEFT_ARROW_CHR 27 +#define RIGHT_ARROW_CHR 26 +#define H_DOUBLE_ARROW_CHR 29 +#define UP_SOLID_ARROW_CHR 30 +#define DOWN_SOLID_ARROW_CHR 31 + +#define MAX_ENTRY_LINES 9 +#define SCROLL_BAR_THICKNESS 2 +#define SCROLL_BAR_MIN_THUMB_SIZE 4 +#define COLOR_WARN RGB8(0xD1, 0xDF, 0x3C) +#define COLOR_SCROLL_BAR_BG RGB8(0x58, 0x58, 0x58) + +#define COLOR_ICON_BOSS_KEY RGB8(0x20, 0xF9, 0x25) +#define COLOR_ICON_MAP RGB8(0xF9, 0x97, 0xFF) +#define COLOR_ICON_COMPASS RGB8(0x20, 0x3A, 0xF9) +#define COLOR_ICON_WOTH RGB8(0xFF, 0xF8, 0x2D) +#define COLOR_ICON_FOOL RGB8(0xFF, 0x2D, 0x4B) + +#define COLOR_BUTTON_A RGB8(0xFF, 0x49, 0x3E) +#define COLOR_BUTTON_B RGB8(0xFD, 0xDD, 0x68) +#define COLOR_BUTTON_X RGB8(0x32, 0x7D, 0xFE) +#define COLOR_BUTTON_Y RGB8(0x00, 0xD0, 0x98) + + typedef enum { + PAGE_SEEDHASH, + PAGE_DUNGEONITEMS, + PAGE_SPHERES, + PAGE_ITEMTRACKER_ALL, + PAGE_ITEMTRACKER_GROUPS, + PAGE_ENTRANCETRACKER_ALL, + PAGE_ENTRANCETRACKER_GROUPS, + PAGE_OPTIONS, + } GfxPage; + + void Gfx_Init(void); + static u8 openingButton(); + extern "C" void Gfx_Update(); + extern "C" void Gfx_SleepQueryCallback(); + extern "C" void Gfx_AwakeCallback(); + +} // namespace rnd + +#endif //_RND_GFX_H_ \ No newline at end of file diff --git a/code/include/rnd/icons.h b/code/include/rnd/icons.h new file mode 100644 index 00000000..c10b236c --- /dev/null +++ b/code/include/rnd/icons.h @@ -0,0 +1,289 @@ +#ifndef _RND_ICONS_H_ +#define _RND_ICONS_H_ +/** + * @file icons.h + * @author Giometric (https://github.com/Giometric) + * @brief + * @date 2023-10-25 + * + * Brought in from the OoT3DR libraries. + */ +#define ICON_WIDTH 8 +#define ICON_HEIGHT 8 + +static const unsigned char rIcons[][ICON_HEIGHT] = {{ + /* Small Key */ + 0x18, /* 00011000 */ + 0x24, /* 00100100 */ + 0x24, /* 00100100 */ + 0x18, /* 00011000 */ + 0x08, /* 00001000 */ + 0x18, /* 00011000 */ + 0x08, /* 00001000 */ + 0x18, /* 00011000 */ + }, + { + /* Boss Key */ + 0xDB, /* 11011011 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3C, /* 00111100 */ + 0x08, /* 00001000 */ + 0x38, /* 00111000 */ + 0x08, /* 00001000 */ + 0x38, /* 00111000 */ + }, + { + /* Triforce */ + 0x00, /* 00000000 */ + 0x08, /* 00001000 */ + 0x1C, /* 00011100 */ + 0x1C, /* 00011100 */ + 0x22, /* 00100010 */ + 0x77, /* 01110111 */ + 0x7F, /* 01111111 */ + 0x00, /* 00000000 */ + }, + { + /* Foolforce */ + 0x00, /* 00000000 */ + 0x7F, /* 01111111 */ + 0x77, /* 01110111 */ + 0x22, /* 00100010 */ + 0x1C, /* 00011100 */ + 0x1C, /* 00011100 */ + 0x08, /* 00001000 */ + 0x00, /* 00000000 */ + }, + { + /* Check */ + 0x01, /* 00000001 */ + 0x03, /* 00000011 */ + 0x06, /* 00000110 */ + 0x0C, /* 00001100 */ + 0x1B, /* 11011000 */ + 0x70, /* 01110000 */ + 0x20, /* 00100000 */ + 0x00, /* 00000000 */ + }, + { + /* No */ + 0x1C, /* 00011100 */ + 0x22, /* 00100010 */ + 0x45, /* 01000101 */ + 0x49, /* 01001001 */ + 0x51, /* 01010001 */ + 0x22, /* 00100010 */ + 0x1C, /* 00011100 */ + 0x00, /* 00000000 */ + }, + { + /* Vanilla */ + 0xC6, /* 11000110 */ + 0xC6, /* 11000110 */ + 0xC6, /* 11000110 */ + 0xC6, /* 11000110 */ + 0x6C, /* 01101100 */ + 0x38, /* 00111000 */ + 0x10, /* 00010000 */ + 0x00, /* 00000000 */ + }, + { + /* MasterQuest */ + 0x88, /* 10001000 */ + 0xD8, /* 11011000 */ + 0xA8, /* 10101000 */ + 0x88, /* 10001000 */ + 0x8C, /* 10001100 */ + 0x12, /* 00010010 */ + 0x16, /* 00010110 */ + 0x0F, /* 00001111 */ + }, + { + /* Map */ + 0x0E, /* 00001110 */ + 0x5F, /* 01011111 */ + 0xD3, /* 11010011 */ + 0xDF, /* 11011111 */ + 0xD9, /* 11011001 */ + 0xDF, /* 11011111 */ + 0xD1, /* 11010001 */ + 0x60, /* 01100000 */ + }, + { + /* Compass */ + 0x1C, /* 00011100 */ + 0x22, /* 00100010 */ + 0x49, /* 01001001 */ + 0x4D, /* 01001101 */ + 0x63, /* 01100011 */ + 0x3E, /* 00111110 */ + 0x1C, /* 00011100 */ + 0x00, /* 00000000 */ + }, + { + /* Button_R */ + 0x00, /* 00000000 */ + 0xfc, /* 11111100 */ + 0xce, /* 11001110 */ + 0xd7, /* 11010111 */ + 0xcf, /* 11001111 */ + 0xd7, /* 11010111 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + }, + { + /* Button_R_Wide_1 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xfc, /* 11111100 */ + 0xfd, /* 11111101 */ + 0xfc, /* 11111100 */ + 0xfd, /* 11111101 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + }, + { + /* Button_R_Wide_2 */ + 0xe0, /* 11100000 */ + 0xf8, /* 11111000 */ + 0xfc, /* 11111100 */ + 0x7e, /* 01111110 */ + 0xfe, /* 11111110 */ + 0x7f, /* 01111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + }, + { + /* Button_L */ + 0x00, /* 00000000 */ + 0x3f, /* 00111111 */ + 0x6f, /* 01101111 */ + 0xef, /* 11101111 */ + 0xef, /* 11101111 */ + 0xe3, /* 11100011 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + }, + { + /* Button_L_Wide_1 */ + 0x07, /* 00000111 */ + 0x1f, /* 00011111 */ + 0x3e, /* 00111110 */ + 0x7e, /* 01111110 */ + 0x7e, /* 01111110 */ + 0xfe, /* 11111110 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + }, + { + /* Button_L_Wide_2 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0x3f, /* 00111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + }, + { + /* Button_A */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xd6, /* 11010110 */ + 0xc6, /* 11000110 */ + 0xd6, /* 11010110 */ + 0x54, /* 01010100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + }, + { + /* Button_B */ + 0x38, /* 00111000 */ + 0x4c, /* 01001100 */ + 0xd6, /* 11010110 */ + 0xce, /* 11001110 */ + 0xd6, /* 11010110 */ + 0x4c, /* 01001100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + }, + { + /* Button_X */ + 0x38, /* 00111000 */ + 0x54, /* 01010100 */ + 0xd6, /* 11010110 */ + 0xee, /* 11101110 */ + 0xd6, /* 11010110 */ + 0x54, /* 01010100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + }, + { + /* Button_Y */ + 0x38, /* 00111000 */ + 0x54, /* 01010100 */ + 0xd6, /* 11010110 */ + 0xee, /* 11101110 */ + 0xee, /* 11101110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + }, + { + /* Button_DPad */ + 0x38, /* 00111000 */ + 0x38, /* 00111000 */ + 0xd6, /* 11010110 */ + 0xee, /* 11101110 */ + 0xd6, /* 11010110 */ + 0x38, /* 00111000 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + }, + { + /* Button_Face */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x06, /* 00000110 */ + 0xc6, /* 11000110 */ + 0xc0, /* 11000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + }, + { + /* Button_FaceH */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x06, /* 00000110 */ + 0xc6, /* 11000110 */ + 0xc0, /* 11000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + }, + { + /* Button_FaceV */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + }, + { + /* Button_Joystick */ + 0x38, /* 00111000 */ + 0x44, /* 01000100 */ + 0xa2, /* 10100010 */ + 0xa2, /* 10100010 */ + 0xba, /* 10111010 */ + 0x44, /* 01000100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + }}; +#endif \ No newline at end of file diff --git a/code/include/rnd/input.h b/code/include/rnd/input.h index a3eed1ae..efe79fa3 100644 --- a/code/include/rnd/input.h +++ b/code/include/rnd/input.h @@ -4,21 +4,23 @@ #include "hid.h" #include "z3d/z3DVec.h" -typedef struct { - btn_t cur; - btn_t up; - btn_t pressed; - btn_t old; -} InputContext; +namespace rnd { + typedef struct { + btn_t cur; + btn_t up; + btn_t pressed; + btn_t old; + } InputContext; -void Input_Update(void); -u32 Input_WaitWithTimeout(u32 msec, u32 closingButton); -u32 Input_Wait(void); + void Input_Update(void); + u32 Input_WaitWithTimeout(u32 msec, u32 closingButton); + u32 Input_Wait(void); // Use the shared game HID. -#define real_hid_addr 0x007b2d34 -#define real_hid (*(hid_mem_t*)real_hid_addr) +#define real_hid_addr 0x007B2D34 +#define real_hid (*(hid_mem_t**)real_hid_addr) -extern InputContext rInputCtx; + extern "C" InputContext rInputCtx; +} // namespace rnd #endif \ No newline at end of file diff --git a/code/include/rnd/savefile.h b/code/include/rnd/savefile.h index b897ea08..106a1b6f 100644 --- a/code/include/rnd/savefile.h +++ b/code/include/rnd/savefile.h @@ -27,6 +27,7 @@ namespace rnd { bool SaveFile_IsValidSettingsHealth(void); void SaveFile_InitExtSaveData(u32 fileBaseIndex); void SaveFile_LoadExtSaveData(u32 saveNumber); + u8 SaveFile_GetIsSceneDiscovered(u8 sceneNum); extern "C" void SaveFile_SaveExtSaveData(); typedef struct { diff --git a/code/include/rnd/settings.h b/code/include/rnd/settings.h index 48bc8463..4403dc75 100644 --- a/code/include/rnd/settings.h +++ b/code/include/rnd/settings.h @@ -234,9 +234,23 @@ namespace rnd { SHUFFLEKOKIRISWORD_VANILLA, SHUFFLEKOKIRISWROD_ANYWHERE, }; + + typedef enum { + DUNGEON_NEITHER, + DUNGEON_BARREN, + DUNGEON_WOTH, + } DungeonInfo; + + typedef enum { + PLAY_ON_CONSOLE, + PLAY_ON_CITRA, + } PlayOption; + typedef struct { u8 hashIndexes[5]; + u8 playOption; + u8 logic; u8 locationsReachable; @@ -275,6 +289,7 @@ namespace rnd { u8 gossipStoneHints; u8 chestAnimations; u8 chestSize; + u8 compassesShowWotH; u8 generateSpoilerLog; u8 ingameSpoilers; u8 menuOpeningButton; diff --git a/code/include/rnd/spoiler_data.h b/code/include/rnd/spoiler_data.h index eb6caeec..546ebc85 100644 --- a/code/include/rnd/spoiler_data.h +++ b/code/include/rnd/spoiler_data.h @@ -1,8 +1,11 @@ #ifndef _RND_SPOILER_DATA_H_ #define _RND_SPOILER_DATA_H_ +#include "rnd/item_override.h" +#include "rnd/savefile.h" #include "z3d/z3DVec.h" +#define SPOILER_LOCDATS 2 #define SPOILER_SPHERES_MAX 50 #define SPOILER_ITEMS_MAX 512 #define SPOILER_STRING_DATA_SIZE 16384 @@ -29,36 +32,31 @@ namespace rnd { // Location groups for checks, used to group the checks by logical location typedef enum { GROUP_NO_GROUP, - GROUP_STARTING_ITEM, - GROUP_DEKU_PALACE, - GROUP_EAST_CLOCK_TOWN, - GROUP_DUNGEON_WOODFALL_TEMPLE, - GROUP_DUNGEON_SNOWHEAD_TEMPLE, - GROUP_E_CLOCK_TOWN, - GROUP_GORON_VILLAGE, - GROUP_GREAT_BAY_COAST, - GROUP_IKANA_CANYON, - GROUP_IKANA_GRAVEYARD, - GROUP_LAUNDRY_POOL, - GROUP_MILK_ROAD, - GROUP_MOUNTAIN_VILLAGE, - GROUP_N_CLOCK_TOWN, - GROUP_PATH_SNOWHEAD, - GROUP_PINNACLE_ROCK, - GROUP_ROAD_IKANA, - GROUP_ROAD_SWAMP, - GROUP_ROMANI_RANCH, GROUP_S_CLOCK_TOWN, - GROUP_SNOWHEAD, - GROUP_SOUTHERN_SWAMP, + GROUP_LAUNDRY_POOL, + GROUP_E_CLOCK_TOWN, GROUP_STOCKPOTINN, - GROUP_STONE_TOWER, - GROUP_TERMINA_FIELD, - GROUP_TWIN_ISLANDS, GROUP_W_CLOCK_TOWN, + GROUP_N_CLOCK_TOWN, + GROUP_TERMINA_FIELD, + GROUP_SOUTHERN_SWAMP, + GROUP_DEKU_PALACE, GROUP_WOODFALL, + GROUP_SNOWHEAD, + GROUP_MOUNTAIN_VILLAGE, + GROUP_TWIN_ISLANDS, + GROUP_GORON_VILLAGE, + GROUP_MILK_ROAD, + GROUP_ROMANI_RANCH, + GROUP_GREAT_BAY_COAST, + GROUP_PINNACLE_ROCK, GROUP_ZORA_CAPE, GROUP_ZORA_HALL, + GROUP_IKANA_CANYON, + GROUP_IKANA_GRAVEYARD, + GROUP_STONE_TOWER, + GROUP_DUNGEON_WOODFALL_TEMPLE, + GROUP_DUNGEON_SNOWHEAD_TEMPLE, GROUP_DUNGEON_GREAT_BAY, GROUP_DUNGEON_STONE_TOWER, GROUP_DUNGEON_PIRATE_FORTRESS, @@ -74,6 +72,18 @@ namespace rnd { // Grottos are all 0x3E } SpoilerCollectionCheckGroup; + typedef enum { + COLLECTTYPE_NORMAL, + COLLECTTYPE_REPEATABLE, + COLLECTTYPE_NEVER, + } SpoilerItemCollectType; + + typedef enum { + REVEALTYPE_NORMAL, + REVEALTYPE_SCENE, + REVEALTYPE_ALWAYS, + } SpoilerItemRevealType; + typedef struct { u16 LocationStrOffset; u16 ItemStrOffset; @@ -81,6 +91,9 @@ namespace rnd { u8 LocationScene; u8 LocationFlag; SpoilerCollectionCheckGroup Group; + SpoilerItemCollectType CollectType; + SpoilerItemRevealType RevealType; + ItemOverride_Type OverrideType; } SpoilerItemLocation; typedef struct { @@ -99,8 +112,16 @@ namespace rnd { u16 GroupOffsets[SPOILER_COLLECTION_GROUP_COUNT]; } SpoilerData; + typedef struct { + SpoilerItemLocation ItemLocations[SPOILER_ITEMS_MAX]; + char StringData[SPOILER_STRING_DATA_SIZE]; + } SpoilerDataLocs; + extern "C" SpoilerData gSpoilerData; + SpoilerItemLocation* SpoilerData_ItemLoc(u16 itemIndex); + char* SpoilerData_StringData(u16 itemIndex); + char* SpoilerData_GetItemLocationString(u16 itemIndex); char* SpoilerData_GetItemNameString(u16 itemIndex); SpoilerItemLocation GetSpoilerItemLocation(u8 sphere, u16 itemIndex); @@ -115,6 +136,8 @@ namespace rnd { u8 SpoilerData_ScrubCheck(SpoilerItemLocation itemLoc); u8 SpoilerData_ShopItemCheck(SpoilerItemLocation itemLoc); u8 SpoilerData_MagicBeansCheck(SpoilerItemLocation itemLoc); + u8 SpoilerData_GetIsItemLocationRevealed(u16 itemIndex); + u8 SpoilerLog_UpdateIngameLog(ItemOverride_Type type, u8 scene, u8 flag); } // namespace rnd #endif // _RND_SPOILER_DATA_H_ \ No newline at end of file diff --git a/code/mm.ld b/code/mm.ld index 30f93b94..3d115cd0 100644 --- a/code/mm.ld +++ b/code/mm.ld @@ -25,6 +25,18 @@ SECTIONS{ *(.patch_DecoupleStartSelect) } + .patch_AwakeCallback 0x124DE8 : { + *(.patch_AwakeCallback) + } + + .patch_SleepQueryCallback 0x124DF0 : { + *(.patch_SleepQueryCallback) + } + + .patch_Gfx_Update 0x1376DC : { + *(.patch_Gfx_Update) + } + .patch_RemoveSOHCutesceneAfterMessage 0x186810 : { *(.patch_RemoveSOHCutesceneAfterMessage) } @@ -55,6 +67,14 @@ SECTIONS{ *(.patch_DoNotRemoveKeys) } + .patch_DoNotGiveSwordBackOnReset 0x1C98FC : { + *(.patch_DoNotGiveSwordBackOnReset) + } + + .patch_RemoveItemBUsabilityOnReset 0x1C99F4 : { + *(.patch_RemoveItemBUsabilityOnReset) + } + .patch_RemoveDekuMaskCheckSoT 0x1D8008 : { *(.patch_RemoveDekuMaskCheckSoT) } @@ -119,6 +139,14 @@ SECTIONS{ *(.patch_ThirdZoraSwimCheck) } + .patch_CheckMagicForZoraFastSwim 0x1FFDE8 : { + *(.patch_CheckMagicForZoraFastSwim) + } + + .patch_ZoraBarrierCheckMagicAcquired 0x207230 : { + *(.patch_ZoraBarrierCheckMagicAcquired) + } + .patch_ChangeTriggerAandRToA 0x220EFC : { *(.patch_ChangeTriggerAandRToA) } @@ -316,7 +344,7 @@ SECTIONS{ } .patch_OverrideBankerWalletReward 0x459044 : { - *(.patch_OverrideProgessiveWallet) + *(.patch_OverrideProgessiveWalletTwo) } .patch_CouplesMaskGiveItem 0x46E228 : { @@ -396,7 +424,6 @@ SECTIONS{ *(.data) *(.bss) *(COMMON) - rCustomMessages = . ; __text_end = . ; } } \ No newline at end of file diff --git a/code/source/asm/hooks.s b/code/source/asm/hooks.s index 0e4d9892..e8c22dc6 100644 --- a/code/source/asm/hooks.s +++ b/code/source/asm/hooks.s @@ -32,6 +32,29 @@ hook_MainLoop: ldr r1, [r0,#0x138] b 0x0106770 +.global hook_Gfx_Update +hook_Gfx_Update: + push {r0-r12, lr} + bl Gfx_Update + pop {r0-r12, lr} + pop {r4-r8, pc} + +.global hook_Gfx_AwakeCallback +hook_Gfx_AwakeCallback: + push {r0-r12, lr} + bl Gfx_AwakeCallback + pop {r0-r12, lr} + add r0,r0,#0xC + b 0x124DEC + +.global hook_Gfx_SleepQueryCallback +hook_Gfx_SleepQueryCallback: + push {r0-r12, lr} + bl Gfx_SleepQueryCallback + pop {r0-r12, lr} + add r0,r0,#0xC + b 0x124DF4 + .global hook_OverrideCutsceneNextEntrance hook_OverrideCutsceneNextEntrance: push {r0-r12, lr} diff --git a/code/source/asm/patches.s b/code/source/asm/patches.s index 4cb67eab..aa224c6a 100644 --- a/code/source/asm/patches.s +++ b/code/source/asm/patches.s @@ -39,6 +39,21 @@ patch_MainLoop: patch_DecoupleStartSelect: nop +.section .patch_AwakeCallback +.global AwakeCallback_patch +AwakeCallback_patch: + b hook_Gfx_AwakeCallback + +.section .patch_SleepQueryCallback +.global SleepQueryCallback_patch +SleepQueryCallback_patch: + b hook_Gfx_SleepQueryCallback + +.section .patch_Gfx_Update +.global Gfx_Update_patch +Gfx_Update_patch: + b hook_Gfx_Update + @ This should remove the overwriting message for when the @ user receives the Zora Mask. @ Largely untested, need to check for any UB. @@ -52,6 +67,7 @@ patch_RemoveSOHCutesceneAfterMessage: patch_OverrideBombersNotebook: b hook_OverrideHMSBombers + .section .patch_OverrideCutsceneNextEntrance .global patch_OverrideCutsceneNextEntrance patch_OverrideCutsceneNextEntrance: @@ -93,6 +109,21 @@ patch_DoNotRemoveKeys: nop nop +@ NOP out the bit of code that checks your sword and gives it back if it +@ is not a razor sword. This should prevent us from ever getting Kokiri sword on +@ cycle reset. +.section .patch_DoNotGiveSwordBackOnReset +.global patch_DoNotGiveSwordBackOnReset +patch_DoNotGiveSwordBackOnReset: + nop + nop + nop + +.section .patch_RemoveItemBUsabilityOnReset +.global patch_RemoveItemBUsabilityOnReset +patch_RemoveItemBUsabilityOnReset: + nop + .section .patch_RemoveDekuMaskCheckSoT .global patch_RemoveDekuMaskCheckSoT patch_RemoveDekuMaskCheckSoT: @@ -291,6 +322,12 @@ patch_OverrideProgessiveWallet: @Override to use the progressive wallet instead. mov r2,#0x48 +.section .patch_OverrideProgessiveWalletTwo +.global patch_OverrideProgessiveWalletTwo +patch_OverrideProgessiveWalletTwo: +@Override to use the progressive wallet instead. + mov r2,#0x48 + .section .patch_CheckMoTRequirement .global patch_CheckMoTRequirement patch_CheckMoTRequirement: @@ -339,4 +376,4 @@ patch_RemoveCouplesMaskMessage: .section .patch_loader .global loader_patch loader_patch: - b hook_into_loader + b hook_into_loader \ No newline at end of file diff --git a/code/source/asm/zora_hooks.s b/code/source/asm/zora_hooks.s index 4048d601..01e712ca 100644 --- a/code/source/asm/zora_hooks.s +++ b/code/source/asm/zora_hooks.s @@ -2,6 +2,7 @@ @ https://github.com/leoetlino/project-restoration/blob/181ecbf6e806fc10c8d1f8b2d74489b0bd7f5e67/hooks/rst_zora_swim.hks .arm .text +.syntax unified .global hook_ZoraInWaterFastSwim hook_ZoraInWaterFastSwim: @@ -61,6 +62,28 @@ doThirdFastSwim: bl runZoraPatch b 0x1FFDC0 +.global hook_CheckMagicForZoraFastSwim +hook_CheckMagicForZoraFastSwim: + beq 0x200010 + push {r0} + bl CheckIfMagicAcquired + cmp r0, #0x0 + pop {r0} + beq 0x200010 + @cmp r0,#0x0 + b 0x1FFDEC + +.global hook_ZoraBarrierCheckMagicAcquired +hook_ZoraBarrierCheckMagicAcquired: + beq 0x2072B4 + push {r0} + bl CheckIfMagicAcquired + cmp r0, #0x0 + pop {r0} + beq 0x2072B4 + cmp r1, #0x0 + b 0x207234 + .global hook_ChangeTriggerAandRToA hook_ChangeTriggerAandRToA: push {r0-r12, lr} diff --git a/code/source/asm/zora_patches.s b/code/source/asm/zora_patches.s index 4de2fe36..79d96cc8 100644 --- a/code/source/asm/zora_patches.s +++ b/code/source/asm/zora_patches.s @@ -20,6 +20,16 @@ patch_UseZoraASwimSecond: patch_ThirdZoraSwimCheck: bl hook_ThirdZoraSwimCheck +.section .patch_CheckMagicForZoraFastSwim +.global patch_CheckMagicForZoraFastSwim +patch_CheckMagicForZoraFastSwim: + bl hook_CheckMagicForZoraFastSwim + +.section .patch_ZoraBarrierCheckMagicAcquired +.global patch_ZoraBarrierCheckMagicAcquired +patch_ZoraCheckMagicAcquired: + bl hook_ZoraBarrierCheckMagicAcquired + .section .patch_ChangeTriggerAandRToA .global patch_ChangeTriggerAandRToA patch_ChangeTriggerAandRToA: diff --git a/code/source/main.cpp b/code/source/main.cpp index a3139f43..17eba190 100644 --- a/code/source/main.cpp +++ b/code/source/main.cpp @@ -8,6 +8,7 @@ #include "game/ui.h" #include "rnd/extdata.h" #include "rnd/icetrap.h" +#include "rnd/input.h" #include "rnd/item_override.h" #include "rnd/link.h" #include "rnd/rheap.h" @@ -73,7 +74,7 @@ namespace rnd { #endif context.gctx = static_cast(state); - + Input_Update(); if (context.gctx->GetPlayerActor()) { ItemOverride_Update(); link::HandleFastOcarina(context.gctx); @@ -112,7 +113,8 @@ namespace rnd { game::ui::OpenScreen(game::ui::ScreenType::Map); gctx->pad_state.input.buttons.Clear(game::pad::Button::Select); gctx->pad_state.input.new_buttons.Clear(game::pad::Button::Select); - } else if (newButtons == (u32)game::pad::Button::Select || newButtons == (u32)game::pad::Button::Start) { + } else if ((gSettingsContext.customIngameSpoilerButton != 4 && newButtons == (u32)game::pad::Button::Select) || + (gSettingsContext.customIngameSpoilerButton != 8 && newButtons == (u32)game::pad::Button::Start)) { if (game::GetCommonData().save.inventory.collect_register.bombers_notebook != 0) game::ui::OpenScreen(game::ui::ScreenType::Schedule); else diff --git a/code/source/rnd/custom_messages.cpp b/code/source/rnd/custom_messages.cpp index e00a80e8..26ed0a9b 100644 --- a/code/source/rnd/custom_messages.cpp +++ b/code/source/rnd/custom_messages.cpp @@ -194,6 +194,10 @@ class MsgBuilder { if (msg.sfxAndFlags & INSTANT_FLAG) instant(); + // Tingle Map Choices. Add 3 choices, 2 for maps and 1 for no thanks. + if (msg.id >= 0x1D11 && msg.id <= 0x1D16) + addCom(0x2F, 3); + while (++idx < MAX_UNFORMATTED_SIZE && msg.text[idx]) { resolvedChar = msg.text[idx]; @@ -357,14 +361,17 @@ class MsgBuilder { if (inCol) rnd::util::Print("Warning formatting message, colour not closed: %s\n", msg.text); #endif - end(); + if (msg.id >= 0x1D11 && msg.id <= 0x1D16) + addCom(0x00); + else + end(); } }; MsgBuilder builder; CustomMessage customMsg; -volatile const UnformattedMessage* ptrCustomMessageEntries = {0}; +volatile const UnformattedMessage rCustomMessages[512] = {0}; volatile const u32 numCustomMessageEntries = {0}; bool SetCustomMessage(u16 id, game::MessageResEntry* msgResEntry) { @@ -381,7 +388,7 @@ bool SetCustomMessage(u16 id, game::MessageResEntry* msgResEntry) { while (start <= end) { current = (start + end) / 2; // Compiler isn't happy with assigning volatile const to not so reference/dereference to get data - customMsgData = *(UnformattedMessage*)&ptrCustomMessageEntries[current]; + customMsgData = *(UnformattedMessage*)&rCustomMessages[current]; if (customMsgData.id < id) start = current + 1; else if (customMsgData.id > id) diff --git a/code/source/rnd/draw.cpp b/code/source/rnd/draw.cpp new file mode 100644 index 00000000..5eb9c036 --- /dev/null +++ b/code/source/rnd/draw.cpp @@ -0,0 +1,363 @@ +/* + * This file is a modified part of Luma3DS + * Copyright (C) 2016-2019 Aurora Wright, TuxSH + * Modified 2020 Gamestabled + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Additional Terms 7.b and 7.c of GPLv3 apply to this file: + * * Requiring preservation of specified reasonable legal notices or + * author attributions in that material or in the Appropriate Legal + * Notices displayed by works containing it. + * * Prohibiting misrepresentation of the origin of that material, + * or requiring that modified versions of such material be marked in + * reasonable ways as different from the original version. + */ + +#include "rnd/draw.h" +#include "rnd/icons.h" +#include "utils.h" + +#include +#include "fonts/ascii_font.h" +#include "fonts/ascii_font_small.h" +#include "rnd/custom_messages.h" + +extern "C" { +#include <3ds/svc.h> +#include <3ds/synchronization.h> +} + +static u8* FRAMEBUFFER[6]; +static u8 backBufferBtm[FB_BOTTOM_SIZE]; +static u32 frontBufferIdx_btm = 0; + +void Draw_PreSwapBuffers(Draw_Display display) { + if (display == DISPLAY_1 || display == DISPLAY_BOTH) { + frontBufferIdx_btm++; + frontBufferIdx_btm %= 2; + } +} + +static RecursiveLock lock; + +void Draw_Lock(void) { + static bool lockInitialized = false; + if (!lockInitialized) { + RecursiveLock_Init(&lock); + lockInitialized = true; + } + + RecursiveLock_Lock(&lock); +} + +void Draw_Unlock(void) { + RecursiveLock_Unlock(&lock); +} + +void Draw_DrawIcon(u32 posX, u32 posY, u32 color, Draw_IconType icon) { + // Skip drawing entirely if we're off-screen + if (posX >= SCREEN_BOT_WIDTH) { + return; + } + if (posY >= SCREEN_BOT_HEIGHT) { + return; + } + + u8 sizeX = ICON_WIDTH; + u8 sizeY = ICON_HEIGHT; + + // Clamp size to screen bounds + if (posX + sizeX > SCREEN_BOT_WIDTH) { + sizeX = SCREEN_BOT_WIDTH - posX; + } + if (posY + sizeY > SCREEN_BOT_HEIGHT) { + sizeY = SCREEN_BOT_HEIGHT - posY; + } + + const u8 sizeXMinusOne = ICON_WIDTH - 1; + + const unsigned char* glyph = rIcons[icon]; + + for (s32 y = 0; y < sizeY; y++) { + const unsigned char glyphRow = glyph[y]; + const u32 screenPosY = (posX * SCREEN_BOT_HEIGHT) + (SCREEN_BOT_HEIGHT - y - posY - 1); + + for (s32 x = 0; x < sizeX; x++) { + const u32 shift = sizeXMinusOne - x; + const u32 screenPos = (screenPosY + x * SCREEN_BOT_HEIGHT) * 3; + const u32 pixelColor = ((glyphRow >> shift) & 1) ? color : COLOR_BLACK; + + backBufferBtm[screenPos] = (pixelColor)&0xFF; + backBufferBtm[screenPos + 1] = (pixelColor >> 8) & 0xFF; + backBufferBtm[screenPos + 2] = (pixelColor >> 16) & 0xFF; + } + } +} + +void Draw_DrawRect(u32 posX, u32 posY, u32 width, u32 height, u32 color) { + // Skip drawing entirely if we're off-screen + if (posX >= SCREEN_BOT_WIDTH) { + return; + } + if (posY >= SCREEN_BOT_HEIGHT) { + return; + } + + // Clamp size to screen bounds + if (posX + width > SCREEN_BOT_WIDTH) { + width = SCREEN_BOT_WIDTH - posX; + } + if (posY + height > SCREEN_BOT_HEIGHT) { + height = SCREEN_BOT_HEIGHT - posY; + } + + for (u32 y = 0; y < height; y++) { + const u32 screenPosY = (posX * SCREEN_BOT_HEIGHT) + (SCREEN_BOT_HEIGHT - y - posY - 1); + for (u32 x = 0; x < width; x++) { + const u32 screenPos = (screenPosY + x * SCREEN_BOT_HEIGHT) * 3; + + backBufferBtm[screenPos] = (color)&0xFF; + backBufferBtm[screenPos + 1] = (color >> 8) & 0xFF; + backBufferBtm[screenPos + 2] = (color >> 16) & 0xFF; + } + } +} + +void Draw_DrawCharacter_Impl(u32 posX, u32 posY, u32 color, char character, const unsigned char* font, u8 sizeX, + u8 sizeY) { + const u32 shiftBase = sizeX - 1 + (8 - sizeX); // If sizeX is smaller than 8, we must shift further left + + for (u32 y = 0; y < sizeY; y++) { + const unsigned char charPos = font[character * sizeY + y]; + const u32 screenPosY = (posX * SCREEN_BOT_HEIGHT) + (SCREEN_BOT_HEIGHT - y - posY - 1); + + for (u32 x = 0; x < sizeX; x++) { + const u32 shift = shiftBase - x; + const u32 screenPos = (screenPosY + x * SCREEN_BOT_HEIGHT) * 3; + const u32 pixelColor = ((charPos >> shift) & 1) ? color : COLOR_BLACK; + + backBufferBtm[screenPos] = (pixelColor)&0xFF; + backBufferBtm[screenPos + 1] = (pixelColor >> 8) & 0xFF; + backBufferBtm[screenPos + 2] = (pixelColor >> 16) & 0xFF; + } + } +} + +void Draw_DrawCharacter(u32 posX, u32 posY, u32 color, char character) { + Draw_DrawCharacter_Impl(posX, posY, color, character, ascii_font, FONT_WIDTH, FONT_HEIGHT); +} + +void Draw_DrawCharacterTop(u32 posX, u32 posY, u32 color, char character) { + volatile u8* const fb2 = (volatile u8* const)FRAMEBUFFER[2]; + volatile u8* const fb3 = (volatile u8* const)FRAMEBUFFER[3]; + volatile u8* const fb4 = (volatile u8* const)FRAMEBUFFER[4]; + volatile u8* const fb5 = (volatile u8* const)FRAMEBUFFER[5]; + + for (u32 y = 0; y < 10; y++) { + const char charPos = ascii_font[character * 10 + y]; + + for (u32 x = 6; x >= 1; x--) { + const u32 screenPos = + (posX * SCREEN_TOP_HEIGHT + (SCREEN_TOP_HEIGHT - y - posY - 1)) + (5 - x) * SCREEN_TOP_HEIGHT; + const u32 pixelColor = ((charPos >> x) & 1) ? color : COLOR_BLACK; + + fb2[screenPos * 3] = (pixelColor)&0xFF; + fb2[screenPos * 3 + 1] = (pixelColor >> 8) & 0xFF; + fb2[screenPos * 3 + 2] = (pixelColor >> 16) & 0xFF; + fb3[screenPos * 3] = (pixelColor)&0xFF; + fb3[screenPos * 3 + 1] = (pixelColor >> 8) & 0xFF; + fb3[screenPos * 3 + 2] = (pixelColor >> 16) & 0xFF; + fb4[screenPos * 3] = (pixelColor)&0xFF; + fb4[screenPos * 3 + 1] = (pixelColor >> 8) & 0xFF; + fb4[screenPos * 3 + 2] = (pixelColor >> 16) & 0xFF; + fb5[screenPos * 3] = (pixelColor)&0xFF; + fb5[screenPos * 3 + 1] = (pixelColor >> 8) & 0xFF; + fb5[screenPos * 3 + 2] = (pixelColor >> 16) & 0xFF; + } + } +} + +u32 Draw_DrawString(u32 posX, u32 posY, u32 color, const char* string) { + for (u32 i = 0, line_i = 0; i < strlen(string); i++) + switch (string[i]) { + case '\n': + posY += SPACING_Y; + line_i = 0; + break; + + case '\t': + line_i += 2; + break; + + default: + // Make sure we never get out of the screen + if (line_i >= ((SCREEN_BOT_WIDTH)-posX) / SPACING_X) { + posY += SPACING_Y; + line_i = 1; // Little offset so we know the same string continues + if (string[i] == ' ') + break; // Spaces at the start look weird + } + + Draw_DrawCharacter_Impl(posX + line_i * SPACING_X, posY, color, string[i], ascii_font, FONT_WIDTH, FONT_HEIGHT); + + line_i++; + break; + } + + return posY; +} + +u32 Draw_DrawString_Small(u32 posX, u32 posY, u32 color, const char* string) { + for (u32 i = 0, line_i = 0; i < strlen(string); i++) + switch (string[i]) { + case '\n': + posY += SPACING_SMALL_Y; + line_i = 0; + break; + + case '\t': + line_i += 2; + break; + + default: + // Make sure we never get out of the screen + if (line_i >= ((SCREEN_BOT_WIDTH)-posX) / SPACING_SMALL_X) { + posY += SPACING_SMALL_Y; + line_i = 1; // Little offset so we know the same string continues + if (string[i] == ' ') + break; // Spaces at the start look weird + } + + Draw_DrawCharacter_Impl(posX + line_i * SPACING_SMALL_X, posY, color, string[i], ascii_font_small, + FONT_WIDTH_SMALL, FONT_HEIGHT_SMALL); + + line_i++; + break; + } + + return posY; +} + +u32 Draw_DrawStringTop(u32 posX, u32 posY, u32 color, const char* string) { + for (u32 i = 0, line_i = 0; i < strlen(string); i++) + switch (string[i]) { + case '\n': + posY += SPACING_Y; + line_i = 0; + break; + + case '\t': + line_i += 2; + break; + + default: + // Make sure we never get out of the screen + if (line_i >= ((SCREEN_TOP_WIDTH)-posX) / SPACING_X) { + posY += SPACING_Y; + line_i = 1; // Little offset so we know the same string continues + if (string[i] == ' ') + break; // Spaces at the start look weird + } + + Draw_DrawCharacterTop(posX + line_i * SPACING_X, posY, color, string[i]); + + line_i++; + break; + } + + return posY; +} + +u32 Draw_DrawFormattedString(u32 posX, u32 posY, u32 color, const char* fmt, ...) { + char buf[DRAW_MAX_FORMATTED_STRING_SIZE + 1]; + va_list args; + va_start(args, fmt); + // vsprintf(buf, fmt, args); + vsnprintf_(buf, DRAW_MAX_FORMATTED_STRING_SIZE, fmt, args); + va_end(args); + + return Draw_DrawString(posX, posY, color, buf); +} + +u32 Draw_DrawFormattedString_Small(u32 posX, u32 posY, u32 color, const char* fmt, ...) { + char buf[DRAW_MAX_FORMATTED_STRING_SIZE + 1]; + va_list args; + va_start(args, fmt); + // vsprintf(buf, fmt, args); + vsnprintf_(buf, DRAW_MAX_FORMATTED_STRING_SIZE, fmt, args); + va_end(args); + + return Draw_DrawString_Small(posX, posY, color, buf); +} + +u32 Draw_DrawFormattedStringTop(u32 posX, u32 posY, u32 color, const char* fmt, ...) { + char buf[DRAW_MAX_FORMATTED_STRING_SIZE + 1]; + va_list args; + va_start(args, fmt); + // vsprintf(buf, fmt, args); + vsnprintf_(buf, DRAW_MAX_FORMATTED_STRING_SIZE, fmt, args); + va_end(args); + + return Draw_DrawStringTop(posX, posY, color, buf); +} + +void Draw_FillFramebuffer(u32 value) { + // Odd little thing - we need to set both bottom buffers or else + // we run into a small issue with the menu not actually drawing. + // memset(FRAMEBUFFER[frontBufferIdx_btm], value, FB_BOTTOM_SIZE); + memset(FRAMEBUFFER[0], value, FB_BOTTOM_SIZE); + memset(FRAMEBUFFER[1], value, FB_BOTTOM_SIZE); +} + +void Draw_ClearFramebuffer(void) { + Draw_FillFramebuffer(0); +} + +void Draw_SetupFramebuffer(void) { + Graphics* graphics = *rnd::util::GetPointer(0x6a3a4c); + FRAMEBUFFER[0] = graphics->bottom.display_buffers[0]; + FRAMEBUFFER[1] = graphics->bottom.display_buffers[1]; + FRAMEBUFFER[2] = graphics->top1.display_buffers[0]; + FRAMEBUFFER[3] = graphics->top1.display_buffers[1]; + FRAMEBUFFER[4] = graphics->top2.display_buffers[0]; + FRAMEBUFFER[5] = graphics->top2.display_buffers[1]; +} + +void Draw_FillBackbuffer(u32 value) { + memset(backBufferBtm, value, FB_BOTTOM_SIZE); +} + +void Draw_ClearBackbuffer(void) { + Draw_FillBackbuffer(0); +} + +void Draw_CopyBackBuffer(void) { + // Odd little thing - we need to set both bottom buffers or else + // we run into a small issue with the menu not actually drawing. + // memcpy(FRAMEBUFFER[frontBufferIdx_btm], backBufferBtm, FB_BOTTOM_SIZE); + memcpy(FRAMEBUFFER[1], backBufferBtm, FB_BOTTOM_SIZE); + memcpy(FRAMEBUFFER[0], backBufferBtm, FB_BOTTOM_SIZE); +} + +void Draw_FlushFramebuffer(void) { + svcFlushProcessDataCache(CUR_PROCESS_HANDLE, u32(FRAMEBUFFER[frontBufferIdx_btm]), FB_BOTTOM_SIZE); +} + +void Draw_FlushFramebufferTop(void) { + svcFlushProcessDataCache(CUR_PROCESS_HANDLE, u32(FRAMEBUFFER[2]), FB_TOP_SIZE); + svcFlushProcessDataCache(CUR_PROCESS_HANDLE, u32(FRAMEBUFFER[3]), FB_TOP_SIZE); + svcFlushProcessDataCache(CUR_PROCESS_HANDLE, u32(FRAMEBUFFER[4]), FB_TOP_SIZE); + svcFlushProcessDataCache(CUR_PROCESS_HANDLE, u32(FRAMEBUFFER[5]), FB_TOP_SIZE); +} \ No newline at end of file diff --git a/code/source/rnd/dungeon.cpp b/code/source/rnd/dungeon.cpp new file mode 100644 index 00000000..263cf24d --- /dev/null +++ b/code/source/rnd/dungeon.cpp @@ -0,0 +1,125 @@ +#include "rnd/dungeon.h" + +namespace rnd { + static u8 keyFinderInit = 0; + static KeyData keyData[DUNGEON_STONE_TOWER + 1][10]; + + const char* spoilerEntranceGroupNames[] = { + "Randomized Entrances", + "Spawns/Warps", + "Clock Town", + "Termina Field", + "Southern Swamp", + "Snowhead", + "Great Bay", + "Ikana", + "Milk Road", + "The Moon", + }; + + const char DungeonNames[][25] = { + "Woodfall Temple", "Snowhead Temple", "Great Bay Temple", "Stone Tower Temple", + "Pirate's Fortress", "Beneath The Well", "Ikana Castle", "Secret Shrine", + "The Moon", "Swamp Skulltula House", "Ocean Skulltula House", + }; + + // Used to compare key strings and item string of spoiler data. + // The regular strcmp won't be enough since items in shops have a colon and price added after. + static u8 strcmp_key(char* str, const char* keyStr) { + for (size_t i = 0; i <= strlen(keyStr); i++) { + if (keyStr[i] == '\0' && (str[i] == '\0' || str[i] == ':')) { + return 1; + } + if (keyStr[i] != str[i]) { + return 0; + } + } + return 0; + } + + static const char* GetKeyName(DungeonId id, u8 keyRing) { + static const char* noStr = ""; + + switch (id) { + case DUNGEON_WOODFALL: + return keyRing ? keyRingStringWoodfall : smallKeyStringWoodfall; + case DUNGEON_SNOWHEAD: + return keyRing ? keyRingStringSnowhead : smallKeyStringSnowhead; + case DUNGEON_GREAT_BAY: + return keyRing ? keyRingStringGreatBay : smallKeyStringGreatBay; + case DUNGEON_STONE_TOWER: + return keyRing ? keyRingStringStone : smallKeyStringStone; + default: + return noStr; + } + } + + u8 Dungeon_KeyAmount(u32 id) { + switch (id) { + case DUNGEON_WOODFALL: + return WOODFALL_KEY_COUNT; + case DUNGEON_SNOWHEAD: + return SNOWHEAD_KEY_COUNT; + case DUNGEON_GREAT_BAY: + return GREAT_KEY_COUNT; + case DUNGEON_STONE_TOWER: + return STONE_KEY_COUNT; + default: + return 0; + } + } + + // Stores the indexes of the gSpoilerData where small keys and key rings are for faster lookup. + static void InitKeyFinder(void) { + if (keyFinderInit) { + return; + } + keyFinderInit = 1; + + for (size_t i = 0; i < ARR_SIZE(keyData); i++) { + for (size_t j = 0; j < ARR_SIZE(keyData[0]); j++) { + keyData[i][j].spoilerIndex = -1; + } + } + + u8 keyDataIndex[DUNGEON_STONE_TOWER + 1] = {0}; + + for (size_t item = 0; item < gSpoilerData.ItemLocationsCount; item++) { + for (u32 dungeonId = DUNGEON_WOODFALL; dungeonId <= DUNGEON_STONE_TOWER; dungeonId++) { + if (dungeonId == DUNGEON_PIRATE_FORTRESS || dungeonId == DUNGEON_BENEATH_THE_WELL || + dungeonId == DUNGEON_IKANA_CASTLE || dungeonId == DUNGEON_SECRET_SHRINE || dungeonId == DUNGEON_THE_MOON || + dungeonId == DUNGEON_SWAMP_SKULLTULA_HOUSE || dungeonId == DUNGEON_OCEAN_SKULLTULA_HOUSE) { + continue; + } + if (strcmp_key(SpoilerData_GetItemNameString(item), GetKeyName(DungeonId(dungeonId), 0))) { + keyData[dungeonId][keyDataIndex[dungeonId]].spoilerIndex = item; + keyData[dungeonId][keyDataIndex[dungeonId]].keyAmount = 1; + keyDataIndex[dungeonId]++; + break; + } else if (strcmp_key(SpoilerData_GetItemNameString(item), GetKeyName(DungeonId(dungeonId), 1))) { + keyData[dungeonId][keyDataIndex[dungeonId]].spoilerIndex = item; + keyData[dungeonId][keyDataIndex[dungeonId]].keyAmount = Dungeon_KeyAmount(dungeonId); + keyDataIndex[dungeonId]++; + break; + } + } + } + } + + u8 Dungeon_FoundSmallKeys(u32 id) { + if (!keyFinderInit) { + InitKeyFinder(); + } + + u8 amount = 0; + for (size_t i = 0; i < ARR_SIZE(keyData[id]); i++) { + if (keyData[id][i].spoilerIndex == -1) { + break; + } + if (SpoilerData_GetIsItemLocationCollected(keyData[id][i].spoilerIndex)) { + amount += keyData[id][i].keyAmount; + } + } + return amount; + } +} // namespace rnd diff --git a/code/source/rnd/gfx.cpp b/code/source/rnd/gfx.cpp new file mode 100644 index 00000000..4e4841c0 --- /dev/null +++ b/code/source/rnd/gfx.cpp @@ -0,0 +1,782 @@ +#include "rnd/gfx.h" + +namespace rnd { + static u8 GfxInit = 0; + static u32 closingButton = 0; + static u8 currentSphere = 0; + static s16 spoilerScroll = 0; + + static s16 allItemsScroll = 0; + static s16 groupItemsScroll = 0; + static s8 currentItemGroup = 1; + + static s32 curMenuIdx = 0; + static bool showingLegend = false; + static u64 lastTick = 0; + static u64 ticksElapsed = 0; + static bool isAsleep = false; + DungeonInfo rDungeonInfoData[10]; + + u32 pressed; + bool handledInput; + const char* spoilerCollectionGroupNames[] = { + "All Item Locations", "South Clock Town", "Laundry Pool", "East Clock Town", "StockPotInn", + "West Clock Town", "North Clock Town", "Termina Field", "Southern Swamp", "Deku Palace", + "Woodfall", "Snowhead", "Mountain Village", "Twin Islands", "Goron Village", + "Milk Road", "Romani Ranch", "Great Bay Coast", "Pinnacle Rock", "Zora Cape", + "Zora Hall", "Ikana Canyon", "Ikana Graveyard", "Stone Tower", "Woodfall Temple", + "Snowhead Temple", "Greay Bay Temple", "Stone Tower Temple", "Pirate Fortress", "Beneath the Well", + "Ikana Castle", "Secret Shrine", "The Moon", "Swamp Skulltula House", "Ocean Skulltula House", + }; + + static s8 spoilerGroupDungeonIds[] = { + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + GROUP_DUNGEON_WOODFALL_TEMPLE, + GROUP_DUNGEON_SNOWHEAD_TEMPLE, + GROUP_DUNGEON_GREAT_BAY, + GROUP_DUNGEON_STONE_TOWER, + GROUP_DUNGEON_PIRATE_FORTRESS, + GROUP_DUNGEON_BENEATH_THE_WELL, + GROUP_DUNGEON_IKANA_CASTLE, + GROUP_DUNGEON_SECRET_SHRINE, + GROUP_DUNGEON_THE_MOON, + GROUP_SWAMP_SKULLTULA_HOUSE, + GROUP_OCEAN_SKULLTULA_HOUSE, + }; + + static bool IsDungeonDiscovered(s8 dungeonId) { + game::SaveData& saveData = game::GetCommonData().save; + if (dungeonId == DUNGEON_THE_MOON) { + return false; + } + + u8 idToModeKnown[] = {0, 1, 2, 3, 4, 5, 6, 7, 8}; + if (idToModeKnown[dungeonId]) { + return true; + } + + // A dungeon is considered discovered if we've visited the dungeon, have the map, + // Or known bc of settings + bool hasMap = 0; + if (dungeonId == 0) { + hasMap = saveData.inventory.woodfall_dungeon_items.map.Value(); + } else if (dungeonId == 1) { + hasMap = saveData.inventory.snowhead_dungeon_items.map.Value(); + } else if (dungeonId == 2) { + hasMap = saveData.inventory.great_bay_dungeon_items.map.Value(); + } else if (dungeonId == 3) { + hasMap = saveData.inventory.stone_tower_dungeon_items.map.Value(); + } + return (hasMap = 1); // to-do: check overworld map for GB & Ikana areas for other dungeons. & scene check. + } + + static bool CanShowSpoilerGroup(SpoilerCollectionCheckGroup group) { + s8 dungeonId = spoilerGroupDungeonIds[group]; + return dungeonId == -1 || IsDungeonDiscovered(dungeonId); + } + + static void Gfx_DrawScrollBar(u16 barX, u16 barY, u16 barSize, u16 currentScroll, u16 maxScroll, u16 pageSize) { + Draw_DrawRect(barX, barY, SCROLL_BAR_THICKNESS, barSize, COLOR_SCROLL_BAR_BG); + + float thumbSizePercent = pageSize / (float)maxScroll; + if (thumbSizePercent > 1.0f) { + thumbSizePercent = 1.0f; + } + u16 thumbSize = (u16)(thumbSizePercent * barSize); + if (thumbSize < SCROLL_BAR_MIN_THUMB_SIZE) { + thumbSize = SCROLL_BAR_MIN_THUMB_SIZE; + } + float barThumbPosPercent = (float)currentScroll / (float)(maxScroll - pageSize); + u16 barThumbPosY = (u16)(barThumbPosPercent * (barSize - thumbSize)); + Draw_DrawRect(barX, barY + barThumbPosY, SCROLL_BAR_THICKNESS, thumbSize, COLOR_WHITE); + } + + static void NextItemGroup() { + groupItemsScroll = 0; + s8 prevGroup = currentItemGroup; + do { + ++currentItemGroup; + if (currentItemGroup >= SPOILER_COLLECTION_GROUP_COUNT) { + currentItemGroup = 1; + } + } while (gSpoilerData.GroupItemCounts[currentItemGroup] == 0 && currentItemGroup != prevGroup); + } + + static void PrevItemGroup() { + groupItemsScroll = 0; + s8 prevGroup = currentItemGroup; + do { + --currentItemGroup; + if (currentItemGroup < 1) { + currentItemGroup = SPOILER_COLLECTION_GROUP_COUNT - 1; + } + } while (gSpoilerData.GroupItemCounts[currentItemGroup] == 0 && currentItemGroup != prevGroup); + } + + static void Gfx_DrawButtonPrompts(void) { + u32 promptY = SCREEN_BOT_HEIGHT - 16; + u32 textY = promptY - 1; + // Close prompt, always shown + Draw_DrawIcon(SCREEN_BOT_WIDTH - 50, promptY, COLOR_BUTTON_B, ICON_BUTTON_B); + Draw_DrawString(SCREEN_BOT_WIDTH - 38, textY, COLOR_TITLE, "Close"); + + static const u8 buttonSpacing = 12; + u16 offsetX = 10; + const char* nextStr = NULL; + + if (curMenuIdx == PAGE_DUNGEONITEMS) { + Draw_DrawIcon(offsetX, promptY, COLOR_BUTTON_A, ICON_BUTTON_A); + offsetX += buttonSpacing; + Draw_DrawString(offsetX, textY, COLOR_TITLE, "Toggle Legend"); + } else if (curMenuIdx == PAGE_SPHERES) { + Draw_DrawIcon(offsetX, promptY, COLOR_WHITE, ICON_BUTTON_DPAD); + offsetX += buttonSpacing; + Draw_DrawString(offsetX, textY, COLOR_TITLE, "Browse spoiler log"); + } else if (curMenuIdx == PAGE_ITEMTRACKER_ALL || curMenuIdx == PAGE_ITEMTRACKER_GROUPS || + curMenuIdx == PAGE_ENTRANCETRACKER_ALL || curMenuIdx == PAGE_ENTRANCETRACKER_GROUPS) { + Draw_DrawIcon(offsetX, promptY, COLOR_WHITE, ICON_BUTTON_DPAD); + offsetX += buttonSpacing; + nextStr = "Browse entries"; + Draw_DrawString(offsetX, textY, COLOR_TITLE, nextStr); + offsetX += (strlen(nextStr) + 1) * SPACING_X; + if (curMenuIdx == PAGE_ITEMTRACKER_GROUPS || curMenuIdx == PAGE_ENTRANCETRACKER_GROUPS) { + Draw_DrawIcon(offsetX, promptY, COLOR_BUTTON_Y, ICON_BUTTON_Y); + offsetX += 8; + Draw_DrawString(offsetX, textY, COLOR_TITLE, "/"); + offsetX += 8; + Draw_DrawIcon(offsetX, promptY, COLOR_BUTTON_A, ICON_BUTTON_A); + offsetX += buttonSpacing; + nextStr = "Change group"; + Draw_DrawString(offsetX, textY, COLOR_TITLE, nextStr); + offsetX += (strlen(nextStr) + 1) * SPACING_X; + } else if (curMenuIdx == PAGE_ITEMTRACKER_ALL || curMenuIdx == PAGE_ENTRANCETRACKER_ALL) { + Draw_DrawIcon(offsetX, promptY, COLOR_BUTTON_A, ICON_BUTTON_A); + offsetX += buttonSpacing; + nextStr = "Toggle Legend"; + Draw_DrawString(offsetX, textY, COLOR_TITLE, nextStr); + offsetX += (strlen(nextStr) + 1) * SPACING_X; + } + } + } + + static void Gfx_UpdatePlayTime(void) { + u64 currentTick = svcGetSystemTick(); + if (!isAsleep) { + ticksElapsed += currentTick - lastTick; + if (ticksElapsed > MAX_TICK_DELTA) { + // Assume that if more ticks than MAX_TICK_DELTA have passed, it has been a long + // time since we last checked, which means the the system may have been asleep or the home button pressed. + // Reset the timer so we don't artificially inflate the play time. + ticksElapsed = 0; + } else { + while (ticksElapsed >= TICKS_PER_SEC) { + ticksElapsed -= TICKS_PER_SEC; + ++gExtSaveData.playtimeSeconds; + } + } + } + lastTick = currentTick; + } + + static void Gfx_DrawSeedHash(void) { + u8 offsetY = 0; + Draw_DrawFormattedString(10, 16 + (SPACING_Y * offsetY++), COLOR_TITLE, "Seed Hash:"); + for (u32 hashIndex = 0; hashIndex < ARR_SIZE(gSettingsContext.hashIndexes); ++hashIndex) { + Draw_DrawFormattedString(10 + (SPACING_X * 4), 16 + (SPACING_Y * offsetY++), COLOR_WHITE, "%s", + hashIconNames[gSettingsContext.hashIndexes[hashIndex]]); + } + offsetY++; + + Draw_DrawString(10, 16 + (SPACING_Y * offsetY++), COLOR_TITLE, "Play time:"); + u32 hours = gExtSaveData.playtimeSeconds / 3600; + u32 minutes = (gExtSaveData.playtimeSeconds / 60) % 60; + u32 seconds = gExtSaveData.playtimeSeconds % 60; + Draw_DrawFormattedString(10 + (SPACING_X * 4), 16 + (SPACING_Y * offsetY++), COLOR_WHITE, "%02u:%02u:%02u", hours, + minutes, seconds); + offsetY++; + } + + static void Gfx_DrawDungeonItems(void) { + static const u8 spacingY = 13; + game::SaveData& saveData = game::GetCommonData().save; + if (showingLegend) { + u8 offsetY = 0; + + Draw_DrawString(10, 16 + (spacingY * offsetY++), COLOR_TITLE, "Dungeon Items Legend"); + offsetY++; + Draw_DrawIcon(10, 16 + (spacingY * offsetY), COLOR_WHITE, ICON_SMALL_KEY); + Draw_DrawString(24, 16 + (spacingY * offsetY++), COLOR_WHITE, "Small Keys: Have / Found"); + Draw_DrawIcon(10, 16 + (spacingY * offsetY), COLOR_ICON_BOSS_KEY, ICON_BOSS_KEY); + Draw_DrawString(24, 16 + (spacingY * offsetY++), COLOR_WHITE, "Boss Key"); + Draw_DrawIcon(10, 16 + (spacingY * offsetY), COLOR_ICON_MAP, ICON_MAP); + Draw_DrawString(24, 16 + (spacingY * offsetY++), COLOR_WHITE, "Map"); + Draw_DrawIcon(10, 16 + (spacingY * offsetY), COLOR_ICON_COMPASS, ICON_COMPASS); + Draw_DrawString(24, 16 + (spacingY * offsetY++), COLOR_WHITE, "Compass"); + offsetY++; + Draw_DrawIcon(10, 16 + (spacingY * offsetY), COLOR_ICON_WOTH, ICON_TRIFORCE); + Draw_DrawString(24, 16 + (spacingY * offsetY++), COLOR_WHITE, "Way of the Hero"); + Draw_DrawIcon(10, 16 + (spacingY * offsetY), COLOR_ICON_FOOL, ICON_FOOL); + Draw_DrawString(24, 16 + (spacingY * offsetY++), COLOR_WHITE, "Barren Location"); + Draw_DrawString(10, 16 + (spacingY * offsetY), COLOR_WHITE, "-"); + Draw_DrawString(24, 16 + (spacingY * offsetY++), COLOR_WHITE, "Non-WotH / Non-Barren Location"); + return; + } + Draw_DrawString(10, 16, COLOR_TITLE, "Dungeon Items"); + // Draw header icons + Draw_DrawIcon(214, 16, COLOR_WHITE, ICON_SMALL_KEY); + Draw_DrawIcon(240, 16, COLOR_WHITE, ICON_BOSS_KEY); + Draw_DrawIcon(260, 16, COLOR_WHITE, ICON_MAP); + Draw_DrawIcon(280, 16, COLOR_WHITE, ICON_COMPASS); + if (gSettingsContext.compassesShowWotH) { + Draw_DrawIcon(300, 16, COLOR_WHITE, ICON_TRIFORCE); + } + + u8 yPos = 30; + for (u32 dungeonId = 0; dungeonId <= DUNGEON_STONE_TOWER; ++dungeonId) { + bool hasBossKey = 0; + if ((dungeonId = DUNGEON_WOODFALL)) { + hasBossKey = saveData.inventory.woodfall_dungeon_items.boss_key.Value(); + } + if ((dungeonId = DUNGEON_SNOWHEAD)) { + hasBossKey = saveData.inventory.snowhead_dungeon_items.boss_key.Value(); + } + if ((dungeonId = DUNGEON_GREAT_BAY)) { + hasBossKey = saveData.inventory.great_bay_dungeon_items.boss_key.Value(); + } + if ((dungeonId = DUNGEON_STONE_TOWER)) { + hasBossKey = saveData.inventory.stone_tower_dungeon_items.boss_key.Value(); + } + bool hasCompass = 0; + if ((dungeonId = DUNGEON_WOODFALL)) { + hasBossKey = saveData.inventory.woodfall_dungeon_items.compass.Value(); + } + if ((dungeonId = DUNGEON_SNOWHEAD)) { + hasBossKey = saveData.inventory.snowhead_dungeon_items.compass.Value(); + } + if ((dungeonId = DUNGEON_GREAT_BAY)) { + hasBossKey = saveData.inventory.great_bay_dungeon_items.compass.Value(); + } + if ((dungeonId = DUNGEON_STONE_TOWER)) { + hasBossKey = saveData.inventory.stone_tower_dungeon_items.compass.Value(); + } + bool hasMap = 0; + if ((dungeonId = DUNGEON_WOODFALL)) { + hasBossKey = saveData.inventory.woodfall_dungeon_items.map.Value(); + } + if ((dungeonId = DUNGEON_SNOWHEAD)) { + hasBossKey = saveData.inventory.snowhead_dungeon_items.map.Value(); + } + if ((dungeonId = DUNGEON_GREAT_BAY)) { + hasBossKey = saveData.inventory.great_bay_dungeon_items.map.Value(); + } + if ((dungeonId = DUNGEON_STONE_TOWER)) { + hasBossKey = saveData.inventory.stone_tower_dungeon_items.map.Value(); + } + + Draw_DrawString(24, yPos, COLOR_WHITE, DungeonNames[dungeonId]); + + // Small Keys + if (dungeonId <= DUNGEON_STONE_TOWER) { + u8 keysHave = 0; + if ((dungeonId = DUNGEON_WOODFALL)) { + keysHave = saveData.inventory.woodfall_temple_keys; + } + if ((dungeonId = DUNGEON_SNOWHEAD)) { + keysHave = saveData.inventory.snowhead_temple_keys; + } + if ((dungeonId = DUNGEON_GREAT_BAY)) { + keysHave = saveData.inventory.great_bay_temple_keys; + } + if ((dungeonId = DUNGEON_STONE_TOWER)) { + keysHave = saveData.inventory.stone_tower_temple_keys; + } + Draw_DrawFormattedString(208, yPos, keysHave > 0 ? COLOR_WHITE : COLOR_DARK_GRAY, "%d", keysHave); + Draw_DrawString(214, yPos, COLOR_WHITE, "/"); + + u8 keysFound = Dungeon_FoundSmallKeys(dungeonId); + if ((gSettingsContext.keysanity == u8(rnd::KeysanitySetting::KEYSANITY_START_WITH)) && + (dungeonId <= DUNGEON_STONE_TOWER)) { + keysFound += Dungeon_KeyAmount(dungeonId); + } + u32 keysFoundColor = COLOR_WHITE; + if (keysFound >= Dungeon_KeyAmount(dungeonId) || IsDungeonDiscovered(dungeonId)) { + keysFoundColor = COLOR_GREEN; + } else if (keysFound == 0) { + keysFoundColor = COLOR_DARK_GRAY; + } + Draw_DrawFormattedString(220, yPos, keysFoundColor, "%d", keysFound); + } + + // Boss Key + if ((dungeonId <= DUNGEON_STONE_TOWER)) { + Draw_DrawIcon(240, yPos, hasBossKey ? COLOR_ICON_BOSS_KEY : COLOR_DARK_GRAY, ICON_BOSS_KEY); + } + + if (dungeonId >= DUNGEON_PIRATE_FORTRESS) { + // Map and Compassz + Draw_DrawIcon(260, yPos, hasMap ? COLOR_ICON_MAP : COLOR_DARK_GRAY, ICON_MAP); + Draw_DrawIcon(280, yPos, hasCompass ? COLOR_ICON_COMPASS : COLOR_DARK_GRAY, ICON_COMPASS); + + // Way of the Hero + if (gSettingsContext.compassesShowWotH) { + if (hasCompass) { + if (rDungeonInfoData[dungeonId] == DUNGEON_WOTH) { + Draw_DrawIcon(300, yPos, COLOR_ICON_WOTH, ICON_TRIFORCE); + } else if (rDungeonInfoData[dungeonId] == DUNGEON_BARREN) { + Draw_DrawIcon(300, yPos, COLOR_ICON_FOOL, ICON_FOOL); + } else { + Draw_DrawCharacter(300, yPos, COLOR_WHITE, '-'); + } + } else { + Draw_DrawCharacter(300, yPos, COLOR_DARK_GRAY, '?'); + } + } + } + + yPos += spacingY; + } + } + + static void Gfx_DrawSpoilerData(void) { + if (gSpoilerData.SphereCount > 0) { + u16 itemCount = gSpoilerData.Spheres[currentSphere].ItemCount; + + Draw_DrawFormattedString(10, 16, COLOR_TITLE, "Spoiler Log - Sphere %i / %i", currentSphere + 1, + gSpoilerData.SphereCount); + + u16 sphereItemLocOffset = gSpoilerData.Spheres[currentSphere].ItemLocationsOffset; + u16 listTopY = 32; + for (u32 item = 0; item < MAX_ENTRY_LINES; ++item) { + u32 locIndex = item + spoilerScroll; + if (locIndex >= gSpoilerData.Spheres[currentSphere].ItemCount) { + break; + } + + u32 locPosY = listTopY + ((SPACING_SMALL_Y + 1) * item * 2); + u32 itemPosY = locPosY + SPACING_SMALL_Y; + u16 itemIndex = gSpoilerData.SphereItemLocations[sphereItemLocOffset + locIndex]; + u32 color = COLOR_WHITE; + if (SpoilerData_GetIsItemLocationCollected(itemIndex)) { + color = COLOR_GREEN; + } else if (SpoilerData_ItemLoc(itemIndex)->CollectType == COLLECTTYPE_REPEATABLE) { + color = COLOR_BLUE; + } else if (SpoilerData_ItemLoc(itemIndex)->CollectType == COLLECTTYPE_NEVER) { + color = COLOR_ORANGE; + } + Draw_DrawString_Small(10, locPosY, color, SpoilerData_GetItemLocationString(itemIndex)); + Draw_DrawString_Small(10 + (SPACING_SMALL_X * 2), itemPosY, color, SpoilerData_GetItemNameString(itemIndex)); + } + + Gfx_DrawScrollBar(SCREEN_BOT_WIDTH - 3, listTopY, SCREEN_BOT_HEIGHT - 40 - listTopY, spoilerScroll, itemCount, + MAX_ENTRY_LINES); + } else { + Draw_DrawString(10, 16, COLOR_TITLE, "Spoiler Log"); + Draw_DrawString(10, 46, COLOR_WHITE, "No spoiler log generated!"); + } + } + + static u8 ViewingGroups() { + return curMenuIdx == PAGE_ITEMTRACKER_GROUPS || curMenuIdx == PAGE_ENTRANCETRACKER_GROUPS; + } + + static void Gfx_DrawItemTracker(void) { + if (!ViewingGroups() && showingLegend) { + Draw_DrawString(10, 16, COLOR_TITLE, "Item Color Legend"); + + static const u8 squareWidth = 9; + u16 offsetY = 2; + Draw_DrawRect(10, 16 + SPACING_Y * offsetY, squareWidth, squareWidth, COLOR_GREEN); + Draw_DrawString(10 + SPACING_X * 2, 16 + SPACING_Y * offsetY++, COLOR_WHITE, "Collected"); + Draw_DrawRect(10, 16 + SPACING_Y * offsetY, squareWidth, squareWidth, COLOR_BLUE); + Draw_DrawString(10 + SPACING_X * 2, 16 + SPACING_Y * offsetY++, COLOR_WHITE, "Repeatable"); + Draw_DrawRect(10, 16 + SPACING_Y * offsetY, squareWidth, squareWidth, COLOR_ORANGE); + Draw_DrawString(10 + SPACING_X * 2, 16 + SPACING_Y * offsetY++, COLOR_WHITE, "Uncollectable"); + return; + } + if (ViewingGroups() && !CanShowSpoilerGroup(SpoilerCollectionCheckGroup(currentItemGroup))) { + Draw_DrawString(10, 16, COLOR_TITLE, spoilerCollectionGroupNames[currentItemGroup]); + Draw_DrawString(10, 46, COLOR_WHITE, "Reveal this dungeon to see the item list."); + Draw_DrawString(10, 57, COLOR_WHITE, " - Enter the dungeon at least once"); + + return; + } + + u16 itemCount = ViewingGroups() ? gSpoilerData.GroupItemCounts[currentItemGroup] : gSpoilerData.ItemLocationsCount; + u16 startIndex = ViewingGroups() ? gSpoilerData.GroupOffsets[currentItemGroup] : 0; + s16* itemScroll = ViewingGroups() ? &groupItemsScroll : &allItemsScroll; + + // Gather up completed items to calculate how far along this group is + u16 completeItems = 0; + u16 uncollectableItems = 0; + for (u32 i = 0; i < itemCount; ++i) { + u32 locIndex = i + startIndex; + if (SpoilerData_GetIsItemLocationCollected(locIndex)) { + completeItems++; + } else if (SpoilerData_ItemLoc(locIndex)->CollectType == COLLECTTYPE_NEVER || + (SpoilerData_ItemLoc(locIndex)->CollectType == COLLECTTYPE_REPEATABLE && + SpoilerData_GetIsItemLocationRevealed(locIndex))) { + uncollectableItems++; + } + } + u16 collectableItems = itemCount - uncollectableItems; + float groupPercent = ((float)completeItems / (float)collectableItems) * 100.0f; + Draw_DrawFormattedString(SCREEN_BOT_WIDTH - 10 - (SPACING_X * 6), 16, + completeItems == collectableItems ? COLOR_GREEN : COLOR_WHITE, "%5.1f%%", groupPercent); + + u16 firstItem = *itemScroll + 1; + u16 lastItem = *itemScroll + MAX_ENTRY_LINES; + if (lastItem > itemCount) { + lastItem = itemCount; + } + Draw_DrawFormattedString(10, 16, COLOR_TITLE, "%s - (%d - %d) / %d", + spoilerCollectionGroupNames[ViewingGroups() ? currentItemGroup : 0], firstItem, lastItem, + itemCount); + + u16 listTopY = 32; + u32 itemGroupIndex = 1; // Keep the last picked group index around to start the search from + for (u32 item = 0; item < MAX_ENTRY_LINES; ++item) { + u32 locIndex = item + startIndex + *itemScroll; + if (item >= itemCount) { + break; + } + + u32 locPosY = listTopY + ((SPACING_SMALL_Y + 1) * item * 2); + u32 itemPosY = locPosY + SPACING_SMALL_Y; + bool isCollected = SpoilerData_GetIsItemLocationCollected(locIndex); + + // Find this item's group index, so we can see if we should hide + // its name because it's located in an undiscovered dungeon + for (u32 group = itemGroupIndex; group < SPOILER_COLLECTION_GROUP_COUNT; ++group) { + u16 groupOffset = gSpoilerData.GroupOffsets[group]; + if (locIndex >= groupOffset && locIndex < groupOffset + gSpoilerData.GroupItemCounts[group]) { + itemGroupIndex = group; + break; + } + } + bool canShowGroup = isCollected || CanShowSpoilerGroup(SpoilerCollectionCheckGroup(itemGroupIndex)); + + u32 color = COLOR_WHITE; + if (isCollected) { + color = COLOR_GREEN; + } else if (canShowGroup) { + if (SpoilerData_ItemLoc(locIndex)->CollectType == COLLECTTYPE_REPEATABLE && + SpoilerData_GetIsItemLocationRevealed(locIndex)) { + color = COLOR_BLUE; + } else if (SpoilerData_ItemLoc(locIndex)->CollectType == COLLECTTYPE_NEVER) { + color = COLOR_ORANGE; + } + } + bool itemRevealed = canShowGroup && (isCollected || SpoilerData_GetIsItemLocationRevealed(locIndex)); + + if (canShowGroup) { + Draw_DrawString_Small(10, locPosY, color, SpoilerData_GetItemLocationString(locIndex)); + } else { + Draw_DrawFormattedString_Small(10, locPosY, color, "%s (Undiscovered)", + spoilerCollectionGroupNames[itemGroupIndex]); + } + const char* itemText = itemRevealed ? SpoilerData_GetItemNameString(locIndex) : "???"; + Draw_DrawString_Small(10 + (SPACING_SMALL_X * 2), itemPosY, color, itemText); + } + + Gfx_DrawScrollBar(SCREEN_BOT_WIDTH - 3, listTopY, SCREEN_BOT_HEIGHT - 40 - listTopY, *itemScroll, itemCount, + MAX_ENTRY_LINES); + } + + static void (*menu_draw_funcs[])(void) = { + // Make sure these line up with the GfxPage enum above + Gfx_DrawSeedHash, Gfx_DrawDungeonItems, Gfx_DrawSpoilerData, + Gfx_DrawItemTracker, // All + Gfx_DrawItemTracker, // Groups + // Gfx_DrawEntranceTracker, // All + // Gfx_DrawEntranceTracker, // Groups + // Gfx_DrawOptions, + }; + + static void Gfx_DrawHeader() { + const u32 totalTabsWidth = 280; + u32 tabsCount = ARR_SIZE(menu_draw_funcs); + u32 tabWidthPlusSpace = totalTabsWidth / tabsCount; + u32 tabXStart = 20; + u32 tabYStart = 3; + u32 tabHeightSmall = 4; + u32 tabHeightBig = 6; + + Draw_DrawIcon(3, 2, COLOR_WHITE, ICON_BUTTON_L_WIDE_1); + Draw_DrawIcon(11, 2, COLOR_WHITE, ICON_BUTTON_L_WIDE_2); + Draw_DrawIcon(SCREEN_BOT_WIDTH - 19, 2, COLOR_WHITE, ICON_BUTTON_R_WIDE_1); + Draw_DrawIcon(SCREEN_BOT_WIDTH - 11, 2, COLOR_WHITE, ICON_BUTTON_R_WIDE_2); + + for (u32 i = 0; i < tabsCount; i++) { + bool isAvailable = menu_draw_funcs[i] != NULL; + bool isCurrent = static_cast(curMenuIdx) == i; + u32 tabX = (u32)(i * tabWidthPlusSpace); + Draw_DrawRect(tabXStart + tabX, isCurrent ? tabYStart : tabYStart + 2, + i == tabsCount - 1 ? totalTabsWidth - tabX : (tabWidthPlusSpace - 1), + isCurrent ? tabHeightBig : tabHeightSmall, + isCurrent ? COLOR_WHITE : (isAvailable ? COLOR_LIGHT_GRAY : COLOR_DARK_GRAY)); + } + } + + static s16 Gfx_Scroll(s16 current, s16 scrollDelta, u16 itemCount) { + s16 maxScroll = itemCount > MAX_ENTRY_LINES ? itemCount - MAX_ENTRY_LINES : 0; + current += scrollDelta; + if (current < 0) { + current = 0; + } else if (current > maxScroll) { + current = maxScroll; + } + return current; + } + + static void Gfx_ShowMenu(void) { + pressed = 0; + Draw_ClearFramebuffer(); + if (gSettingsContext.playOption == PLAY_ON_CONSOLE) { + Draw_FlushFramebuffer(); + } + do { + // End the loop if the system has gone to sleep, so the game can properly respond + if (isAsleep) { + break; + } + + handledInput = false; + // Controls for spoiler log and all-items pages come first, as the user may have chosen + // one of the directional buttons as their menu open/close button and we need to use them + if (curMenuIdx == PAGE_DUNGEONITEMS) { + if (pressed & BUTTON_A) { + showingLegend = !showingLegend; + handledInput = true; + } + } else if (curMenuIdx == PAGE_SPHERES && gSpoilerData.SphereCount > 0) { + // Spoiler log + u16 itemCount = gSpoilerData.Spheres[currentSphere].ItemCount; + if (pressed & (BUTTON_LEFT | CPAD_LEFT)) { + if (currentSphere == 0) { + currentSphere = gSpoilerData.SphereCount - 1; + } else { + currentSphere--; + } + spoilerScroll = 0; + handledInput = true; + } else if (pressed & (BUTTON_RIGHT | CPAD_RIGHT)) { + if (currentSphere < gSpoilerData.SphereCount - 1) { + currentSphere++; + } else { + currentSphere = 0; + } + spoilerScroll = 0; + handledInput = true; + } else if (pressed & (BUTTON_UP | CPAD_UP)) { + spoilerScroll = Gfx_Scroll(spoilerScroll, -1, itemCount); + handledInput = true; + } else if (pressed & (BUTTON_DOWN | CPAD_DOWN)) { + spoilerScroll = Gfx_Scroll(spoilerScroll, 1, itemCount); + handledInput = true; + } + } else if (curMenuIdx == PAGE_ITEMTRACKER_ALL && gSpoilerData.ItemLocationsCount > 0) { + // All Items list + if (pressed & BUTTON_A) { + showingLegend = !showingLegend; + handledInput = true; + } else if (!showingLegend) { + u16 itemCount = gSpoilerData.ItemLocationsCount; + if (pressed & (BUTTON_LEFT | CPAD_LEFT)) { + allItemsScroll = Gfx_Scroll(allItemsScroll, -MAX_ENTRY_LINES * 10, itemCount); + handledInput = true; + } else if (pressed & (BUTTON_RIGHT | CPAD_RIGHT)) { + allItemsScroll = Gfx_Scroll(allItemsScroll, MAX_ENTRY_LINES * 10, itemCount); + handledInput = true; + } else if (pressed & (BUTTON_UP | CPAD_UP)) { + allItemsScroll = Gfx_Scroll(allItemsScroll, -MAX_ENTRY_LINES, itemCount); + handledInput = true; + } else if (pressed & (BUTTON_DOWN | CPAD_DOWN)) { + allItemsScroll = Gfx_Scroll(allItemsScroll, MAX_ENTRY_LINES, itemCount); + handledInput = true; + } + } + } else if (curMenuIdx == PAGE_ITEMTRACKER_GROUPS && gSpoilerData.ItemLocationsCount > 0) { + // Grouped Items list + u16 itemCount = gSpoilerData.GroupItemCounts[currentItemGroup]; + if (pressed & (BUTTON_LEFT | CPAD_LEFT)) { + groupItemsScroll = Gfx_Scroll(groupItemsScroll, -MAX_ENTRY_LINES, itemCount); + handledInput = true; + } else if (pressed & (BUTTON_RIGHT | CPAD_RIGHT)) { + groupItemsScroll = Gfx_Scroll(groupItemsScroll, MAX_ENTRY_LINES, itemCount); + handledInput = true; + } else if (pressed & (BUTTON_UP | CPAD_UP)) { + groupItemsScroll = Gfx_Scroll(groupItemsScroll, -1, itemCount); + handledInput = true; + } else if (pressed & (BUTTON_DOWN | CPAD_DOWN)) { + groupItemsScroll = Gfx_Scroll(groupItemsScroll, 1, itemCount); + handledInput = true; + } else if (pressed & BUTTON_A) { + NextItemGroup(); + handledInput = true; + } else if (pressed & BUTTON_Y) { + PrevItemGroup(); + handledInput = true; + } + } + + if (!handledInput) { + if (pressed & closingButton) { + showingLegend = false; + Draw_ClearBackbuffer(); + Draw_CopyBackBuffer(); + if (gSettingsContext.playOption == PLAY_ON_CONSOLE) { + Draw_FlushFramebuffer(); + } + break; + } else if (pressed & BUTTON_R1) { + showingLegend = false; + do { + curMenuIdx++; + if (static_cast(curMenuIdx) >= ARR_SIZE(menu_draw_funcs)) { + curMenuIdx = 0; + } + } while (menu_draw_funcs[curMenuIdx] == NULL); + handledInput = true; + } else if (pressed & BUTTON_L1) { + showingLegend = false; + do { + curMenuIdx--; + if (curMenuIdx < 0) { + curMenuIdx = (ARR_SIZE(menu_draw_funcs) - 1); + } + } while (menu_draw_funcs[curMenuIdx] == NULL); + handledInput = true; + } + } + + // Keep updating while in the in-game menu + Draw_ClearBackbuffer(); + Draw_ClearFramebuffer(); + + // Continue counting up play time while in the in-game menu + Gfx_UpdatePlayTime(); + + menu_draw_funcs[curMenuIdx](); + Gfx_DrawButtonPrompts(); + Gfx_DrawHeader(); + Draw_CopyBackBuffer(); + if (gSettingsContext.playOption == PLAY_ON_CONSOLE) { + Draw_FlushFramebuffer(); + } + pressed = Input_WaitWithTimeout(1000, closingButton); + + } while (true); + } + + void Gfx_Init(void) { + Draw_SetupFramebuffer(); + Draw_ClearBackbuffer(); + + // Setup the title screen logo edits + // gActorOverlayTable[0x171].initInfo->init = EnMag_rInit; + + if (gSettingsContext.menuOpeningButton == 0) + closingButton = BUTTON_B | BUTTON_SELECT; + else if (gSettingsContext.menuOpeningButton == 1) + closingButton = BUTTON_B | BUTTON_START; + else if (gSettingsContext.menuOpeningButton == 2) + closingButton = BUTTON_B | BUTTON_UP; + else if (gSettingsContext.menuOpeningButton == 3) + closingButton = BUTTON_B | BUTTON_DOWN; + else if (gSettingsContext.menuOpeningButton == 4) + closingButton = BUTTON_B | BUTTON_RIGHT; + else if (gSettingsContext.menuOpeningButton == 5) + closingButton = BUTTON_B | BUTTON_LEFT; + + if (!gSettingsContext.ingameSpoilers) { + menu_draw_funcs[PAGE_SPHERES] = NULL; + } + if (gSpoilerData.ItemLocationsCount == 0) { + menu_draw_funcs[PAGE_ITEMTRACKER_ALL] = NULL; + menu_draw_funcs[PAGE_ITEMTRACKER_GROUPS] = NULL; + } + + // Call these to go to the first non-empty group page + if (gSpoilerData.ItemLocationsCount > 0 && gSpoilerData.GroupItemCounts[currentItemGroup] == 0) { + NextItemGroup(); + } + + // InitOptions(); + + GfxInit = 1; + } + + static u8 openingButton() { + return (gSettingsContext.customIngameSpoilerButton != 0 && + rInputCtx.cur.val == gSettingsContext.customIngameSpoilerButton); + } + + extern "C" { + void Gfx_Update() { + if (!GfxInit) { + Gfx_Init(); + lastTick = svcGetSystemTick(); + } + // The update is called here so it works while in different game modes (title screen, file select, boss challenge, + // credits, MQ unlock) + static u64 lastTickM = 0; + static u64 elapsedTicksM = 0; + elapsedTicksM += svcGetSystemTick() - lastTickM; + if (elapsedTicksM >= TICKS_PER_SEC) { + elapsedTicksM = 0; + } + lastTickM = svcGetSystemTick(); + + Gfx_UpdatePlayTime(); + + if (!isAsleep && openingButton()) { //&& context.has_initialised +#if defined ENABLE_DEBUG || defined DEBUG_PRINT + rnd::util::Print("%s: Attempting to show menu. Are we asleep? %u openingButtons is %u.\n", __func__, isAsleep, + openingButton()); +#endif + Gfx_ShowMenu(); + // Check again as it's possible the system was put to sleep while the menu was open + if (!isAsleep) { + svcSleepThread(1000 * 1000 * 300LL); + // Update lastTick one more time so we don't count the added 0.3s sleep + lastTick = svcGetSystemTick(); + } + } + } + + void Gfx_SleepQueryCallback() { + ticksElapsed = 0; + isAsleep = true; + } + + void Gfx_AwakeCallback() { + ticksElapsed = 0; + lastTick = svcGetSystemTick(); + isAsleep = false; + } + } + +} // namespace rnd diff --git a/code/source/rnd/input.cpp b/code/source/rnd/input.cpp index 70e08c1a..c905954e 100644 --- a/code/source/rnd/input.cpp +++ b/code/source/rnd/input.cpp @@ -1,83 +1,84 @@ #include "rnd/input.h" +#include "common/debug.h" #include "hid.h" #include "utils.h" #include "z3d/z3DVec.h" - extern "C" { #include <3ds/svc.h> } - -u32 GetCurrentPadState(void) { - u32 hid_shared_mem = *(u32*)(0x007b2d34); - return *(volatile u32*)(hid_shared_mem + 0x1C); -} +namespace rnd { + u32 GetCurrentPadState(void) { + u32 hid_shared_mem = *(u32*)(0x007b2d34); + return *(volatile u32*)(hid_shared_mem + 0x1C); + } #define HID_PAD (GetCurrentPadState()) -InputContext rInputCtx; - -// Needed for menus external to the game for spoiler logs. -void Input_Update(void) { - rInputCtx.cur.val = real_hid.pad.pads[real_hid.pad.index].curr.val; - rInputCtx.pressed.val = (rInputCtx.cur.val) & (~rInputCtx.old.val); - rInputCtx.up.val = (~rInputCtx.cur.val) & (rInputCtx.old.val); - rInputCtx.old.val = rInputCtx.cur.val; -} + InputContext rInputCtx = {}; -u32 buttonCheck(u32 key) { - for (u32 i = 0x26000; i > 0; i--) { - if (key != real_hid.pad.pads[real_hid.pad.index].curr.val) - return 0; + // Needed for menus external to the game for spoiler logs. + void Input_Update() { + rInputCtx.cur.val = real_hid->pad.pads[real_hid->pad.index].curr.val; + rInputCtx.pressed.val = (rInputCtx.cur.val) & (~rInputCtx.old.val); + rInputCtx.up.val = (~rInputCtx.cur.val) & (rInputCtx.old.val); + rInputCtx.old.val = rInputCtx.cur.val; } - return 1; -} - -u32 Input_WaitWithTimeout(u32 msec, u32 closingButton) { - u32 pressedKey = 0; - u32 key = 0; - u32 n = 0; - u32 startingButtonState = HID_PAD; - // Wait for no keys to be pressed - while (HID_PAD && (msec == 0 || n <= msec)) { - svcSleepThread(reinterpret_cast(1 * 1000 * 1000LL)); - n++; - - // If the player presses the closing button while still holding other buttons, the menu closes - // (useful for buffering); - u32 tempButtons = HID_PAD; - if (tempButtons != startingButtonState && buttonCheck(tempButtons)) { - if (tempButtons & closingButton) { - break; - } else { - startingButtonState = tempButtons; - } + u32 buttonCheck(u32 key) { + for (u32 i = 0x26000; i > 0; i--) { + if (key != real_hid->pad.pads[real_hid->pad.index].curr.val) + return 0; } + return 1; } - if (msec != 0 && n >= msec) { - return 0; - } + u32 Input_WaitWithTimeout(u32 msec, u32 closingButton) { + u32 pressedKey = 0; + u32 key = 0; + u32 n = 0; + u32 startingButtonState = HID_PAD; - do { - // Wait for a key to be pressed - while (!HID_PAD && (msec == 0 || n < msec)) { - svcSleepThread(1 * 1000 * 1000LL); + // Wait for no keys to be pressed + while (HID_PAD && (msec == 0 || n <= msec)) { + svcSleepThread(reinterpret_cast(1 * 1000 * 1000LL)); n++; + + // If the player presses the closing button while still holding other buttons, the menu closes + // (useful for buffering); + u32 tempButtons = HID_PAD; + if (tempButtons != startingButtonState && buttonCheck(tempButtons)) { + if (tempButtons & closingButton) { + break; + } else { + startingButtonState = tempButtons; + } + } } if (msec != 0 && n >= msec) { return 0; } - key = HID_PAD; + do { + // Wait for a key to be pressed + while (!HID_PAD && (msec == 0 || n < msec)) { + svcSleepThread(1 * 1000 * 1000LL); + n++; + } + + if (msec != 0 && n >= msec) { + return 0; + } - // Make sure it's pressed - pressedKey = buttonCheck(key); - } while (!pressedKey); + key = HID_PAD; - return key; -} + // Make sure it's pressed + pressedKey = buttonCheck(key); + } while (!pressedKey); -u32 Input_Wait(void) { - return Input_WaitWithTimeout(0, 0); -} \ No newline at end of file + return key; + } + + u32 Input_Wait(void) { + return Input_WaitWithTimeout(0, 0); + } +} // namespace rnd diff --git a/code/source/rnd/item_effect.cpp b/code/source/rnd/item_effect.cpp index 404594da..da8c2b4e 100644 --- a/code/source/rnd/item_effect.cpp +++ b/code/source/rnd/item_effect.cpp @@ -61,23 +61,18 @@ namespace rnd { } void ItemEffect_GiveSmallKey(game::CommonData* comData, s16 dungeonId, s16 arg2) { - s8 keys; switch (dungeonId) { case 0: - keys = comData->save.inventory.woodfall_temple_keys < 0 ? 0 : comData->save.inventory.woodfall_temple_keys; - comData->save.inventory.woodfall_temple_keys = keys + 1; + comData->save.inventory.woodfall_temple_keys = comData->save.inventory.woodfall_temple_keys + 1; break; case 1: - keys = comData->save.inventory.snowhead_temple_keys < 0 ? 0 : comData->save.inventory.snowhead_temple_keys; - comData->save.inventory.snowhead_temple_keys = keys + 1; + comData->save.inventory.snowhead_temple_keys = comData->save.inventory.snowhead_temple_keys + 1; break; case 2: - keys = comData->save.inventory.great_bay_temple_keys < 0 ? 0 : comData->save.inventory.great_bay_temple_keys; - comData->save.inventory.great_bay_temple_keys = keys + 1; + comData->save.inventory.great_bay_temple_keys = comData->save.inventory.great_bay_temple_keys + 1; break; case 3: - keys = comData->save.inventory.stone_tower_temple_keys < 0 ? 0 : comData->save.inventory.stone_tower_temple_keys; - comData->save.inventory.stone_tower_temple_keys = keys + 1; + comData->save.inventory.stone_tower_temple_keys = comData->save.inventory.stone_tower_temple_keys + 1; break; default: break; @@ -209,6 +204,8 @@ namespace rnd { comData->save.inventory.items[(u32)game::ItemId::DekuNuts] = game::ItemId::DekuNuts; comData->save.inventory.item_counts[14] += 10; break; + default: + break; } } else { switch (arg2) { @@ -226,6 +223,8 @@ namespace rnd { comData->save.inventory.items[(u32)game::ItemId::DekuNuts] = game::ItemId::DekuNuts; comData->save.inventory.item_counts[14] = (10 + 10 * arg1); break; + default: + break; } } } @@ -260,6 +259,8 @@ namespace rnd { case 3: comData->save.inventory.collect_register.twinmolds_remains = 1; break; + default: + break; } } @@ -297,6 +298,8 @@ namespace rnd { else if (mask == 3) comData->save.inventory.stone_tower_dungeon_items.map = 1; break; + default: + break; } } @@ -327,6 +330,8 @@ namespace rnd { case 3: comData->save.inventory.collect_register.twinmolds_remains = 1; break; + default: + break; } // Make this call as we need to update field_11 in Sub1 CommonData. rnd::util::GetPointer(0x222BCC)(rnd::GetContext().gctx, 0); diff --git a/code/source/rnd/item_override.cpp b/code/source/rnd/item_override.cpp index 90ca8c34..cc823e02 100644 --- a/code/source/rnd/item_override.cpp +++ b/code/source/rnd/item_override.cpp @@ -5,6 +5,7 @@ #include "rnd/item_table.h" #include "rnd/rheap.h" #include "rnd/savefile.h" +#include "rnd/spoiler_data.h" #if defined ENABLE_DEBUG || defined DEBUG_PRINT #include "common/debug.h" @@ -278,6 +279,7 @@ namespace rnd { return; } ItemOverride_AfterKeyReceived(key); + SpoilerLog_UpdateIngameLog(key.type, key.scene, key.flag); ItemOverride_Clear(); } diff --git a/code/source/rnd/item_table.cpp b/code/source/rnd/item_table.cpp index 2c2cc6f6..2f1876b6 100644 --- a/code/source/rnd/item_table.cpp +++ b/code/source/rnd/item_table.cpp @@ -611,15 +611,15 @@ namespace rnd { (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, 0xFF, (rnd::upgradeFunc)ItemUpgrade_None, ItemEffect_BeanPack, (s16)-1, (s16)-1), // Magic Beans - [0x76] = - ITEM_ROW((u32)GetItemID::GI_RUPEE_BLUE, ChestType::DECORATED_SMALL, (u8)game::ItemId::SmallKey, 0x003C, - 0x00086, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, 0xFF, (rnd::upgradeFunc)ItemUpgrade_None, - ItemEffect_GiveSmallKey, (s16)0, (s16)-1), // Small Key (Woodfall) + [0x76] = ITEM_ROW((u32)GetItemID::GI_RUPEE_BLUE, ChestType::DECORATED_SMALL, (u8)game::ItemId::SmallKey, 0x6133, + 0x00086, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s32)DrawGraphicItemID::DI_SMALL_KEY, + (rnd::upgradeFunc)ItemUpgrade_None, ItemEffect_GiveSmallKey, (s16)0, + (s16)-1), // Small Key (Woodfall) - [0x77] = - ITEM_ROW((u32)GetItemID::GI_RUPEE_BLUE, ChestType::DECORATED_SMALL, (u8)game::ItemId::SmallKey, 0x003C, - 0x00086, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, 0xFF, (rnd::upgradeFunc)ItemUpgrade_None, - ItemEffect_GiveSmallKey, (s16)1, (s16)-1), // Small Key (Snowhead) + [0x77] = ITEM_ROW((u32)GetItemID::GI_RUPEE_BLUE, ChestType::DECORATED_SMALL, (u8)game::ItemId::SmallKey, 0x6134, + 0x00086, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s32)DrawGraphicItemID::DI_SMALL_KEY, + (rnd::upgradeFunc)ItemUpgrade_None, ItemEffect_GiveSmallKey, (s16)1, + (s16)-1), // Small Key (Snowhead) [0x78] = ITEM_ROW((u32)GetItemID::GI_NUTS_30, ChestType::WOODEN_BIG, (u8)game::ItemId::DekuMask, 0x0078, 0x01BD, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s32)DrawGraphicItemID::DI_DEKU_MASK, @@ -729,10 +729,10 @@ namespace rnd { (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s32)DrawGraphicItemID::DI_KAFEI_MASK, (rnd::upgradeFunc)ItemUpgrade_None, ItemEffect_None, (s16)-1, (s16)-1), // Kafei Mask - [0x90] = - ITEM_ROW((u32)GetItemID::GI_RUPEE_BLUE, ChestType::DECORATED_SMALL, (u8)game::ItemId::SmallKey, 0x003C, - 0x00086, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, 0xFF, (rnd::upgradeFunc)ItemUpgrade_None, - ItemEffect_GiveSmallKey, (s16)2, (s16)-1), // Small Key (Great Bay) + [0x90] = ITEM_ROW((u32)GetItemID::GI_RUPEE_BLUE, ChestType::DECORATED_SMALL, (u8)game::ItemId::SmallKey, 0x6135, + 0x00086, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s32)DrawGraphicItemID::DI_SMALL_KEY, + (rnd::upgradeFunc)ItemUpgrade_None, ItemEffect_GiveSmallKey, (s16)2, + (s16)-1), // Small Key (Great Bay) // The following bottle items either give one bottle if you do not have one, or refills any // bottle if you do have an empty one. Essentially acts as if you caught something in a @@ -827,40 +827,40 @@ namespace rnd { (rnd::upgradeFunc)ItemUpgrade_None, ItemEffect_None, (s16)-1, (s16)-1), // Letter To Mama - [0xA2] = - ITEM_ROW((u32)GetItemID::GI_RUPEE_BLUE, ChestType::DECORATED_SMALL, (u8)game::ItemId::SmallKey, 0x003C, - 0x00086, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, 0xFF, (rnd::upgradeFunc)ItemUpgrade_None, - ItemEffect_GiveSmallKey, (s16)3, (s16)-1), // Small Key (Stone Tower) + [0xA2] = ITEM_ROW((u32)GetItemID::GI_RUPEE_BLUE, ChestType::DECORATED_SMALL, (u8)game::ItemId::SmallKey, 0x6136, + 0x00086, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s32)DrawGraphicItemID::DI_SMALL_KEY, + (rnd::upgradeFunc)ItemUpgrade_None, ItemEffect_GiveSmallKey, (s16)3, + (s16)-1), // Small Key (Stone Tower) - [0xA3] = - ITEM_ROW((u32)GetItemID::GI_NUTS_30, ChestType::DECORATED_BIG, (u8)game::ItemId::BossKey, 0x003D, 0x00092, - (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, 0xFF, (rnd::upgradeFunc)ItemUpgrade_None, - ItemEffect_GiveDungeonItem, (s16)1, (s16)0), // Boss Key (Woodfall) + [0xA3] = ITEM_ROW((u32)GetItemID::GI_NUTS_30, ChestType::DECORATED_BIG, (u8)game::ItemId::BossKey, 0x613F, + 0x00092, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s32)DrawGraphicItemID::DI_BOSS_KEY, + (rnd::upgradeFunc)ItemUpgrade_None, ItemEffect_GiveDungeonItem, (s16)1, + (s16)0), // Boss Key (Woodfall) - [0xA4] = - ITEM_ROW((u32)GetItemID::GI_NUTS_30, ChestType::DECORATED_BIG, (u8)game::ItemId::BossKey, 0x003D, 0x00092, - (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, 0xFF, (rnd::upgradeFunc)ItemUpgrade_None, - ItemEffect_GiveDungeonItem, (s16)1, (s16)1), // Boss Key (Snowhead) + [0xA4] = ITEM_ROW((u32)GetItemID::GI_NUTS_30, ChestType::DECORATED_BIG, (u8)game::ItemId::BossKey, 0x6140, + 0x00092, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s32)DrawGraphicItemID::DI_BOSS_KEY, + (rnd::upgradeFunc)ItemUpgrade_None, ItemEffect_GiveDungeonItem, (s16)1, + (s16)1), // Boss Key (Snowhead) - [0xA5] = - ITEM_ROW((u32)GetItemID::GI_NUTS_30, ChestType::DECORATED_BIG, (u8)game::ItemId::BossKey, 0x003D, 0x00092, - (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, 0xFF, (rnd::upgradeFunc)ItemUpgrade_None, - ItemEffect_GiveDungeonItem, (s16)1, (s16)2), // Boss Key (Great Bay) + [0xA5] = ITEM_ROW((u32)GetItemID::GI_NUTS_30, ChestType::DECORATED_BIG, (u8)game::ItemId::BossKey, 0x6141, + 0x00092, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s32)DrawGraphicItemID::DI_BOSS_KEY, + (rnd::upgradeFunc)ItemUpgrade_None, ItemEffect_GiveDungeonItem, (s16)1, + (s16)2), // Boss Key (Great Bay) - [0xA6] = - ITEM_ROW((u32)GetItemID::GI_NUTS_30, ChestType::DECORATED_BIG, (u8)game::ItemId::BossKey, 0x003D, 0x00092, - (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, 0xFF, (rnd::upgradeFunc)ItemUpgrade_None, - ItemEffect_GiveDungeonItem, (s16)1, (s16)3), // Boss Key (Stone Tower) + [0xA6] = ITEM_ROW((u32)GetItemID::GI_NUTS_30, ChestType::DECORATED_BIG, (u8)game::ItemId::BossKey, 0x6142, + 0x00092, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s32)DrawGraphicItemID::DI_BOSS_KEY, + (rnd::upgradeFunc)ItemUpgrade_None, ItemEffect_GiveDungeonItem, (s16)1, + (s16)3), // Boss Key (Stone Tower) - [0xA7] = - ITEM_ROW((u32)GetItemID::GI_NUTS_30, ChestType::DECORATED_BIG, (u8)game::ItemId::Compass, 0x003F, 0x00091, - (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, 0xFF, (rnd::upgradeFunc)ItemUpgrade_None, - ItemEffect_GiveDungeonItem, (s16)2, (s16)0), // Compass (Woodfall) + [0xA7] = ITEM_ROW((u32)GetItemID::GI_NUTS_30, ChestType::DECORATED_BIG, (u8)game::ItemId::Compass, 0x613B, + 0x00091, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s32)DrawGraphicItemID::DI_COMPASS, + (rnd::upgradeFunc)ItemUpgrade_None, ItemEffect_GiveDungeonItem, (s16)2, + (s16)0), // Compass (Woodfall) - [0xA8] = - ITEM_ROW((u32)GetItemID::GI_NUTS_30, ChestType::DECORATED_BIG, (u8)game::ItemId::Compass, 0x003F, 0x00091, - (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, 0xFF, (rnd::upgradeFunc)ItemUpgrade_None, - ItemEffect_GiveDungeonItem, (s16)2, (s16)1), // Compass (Snowhead) + [0xA8] = ITEM_ROW((u32)GetItemID::GI_NUTS_30, ChestType::DECORATED_BIG, (u8)game::ItemId::Compass, 0x613C, + 0x00091, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s32)DrawGraphicItemID::DI_COMPASS, + (rnd::upgradeFunc)ItemUpgrade_None, ItemEffect_GiveDungeonItem, (s16)2, + (s16)1), // Compass (Snowhead) [0xA9] = ITEM_ROW((u32)GetItemID::GI_RUPEE_BLUE, ChestType::WOODEN_SMALL, (u8)game::ItemId::Bottle, 0x0090, 0x0009E, @@ -878,34 +878,34 @@ namespace rnd { (s32)DrawGraphicItemID::DI_PENDANT_OF_MEMORIES, (rnd::upgradeFunc)ItemUpgrade_None, ItemEffect_None, (s16)-1, (s16)-1), // Pendant Of Memories - [0xAC] = - ITEM_ROW((u32)GetItemID::GI_NUTS_30, ChestType::DECORATED_BIG, (u8)game::ItemId::Compass, 0x003F, 0x00091, - (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, 0xFF, (rnd::upgradeFunc)ItemUpgrade_None, - ItemEffect_GiveDungeonItem, (s16)2, (s16)2), // Compass (Great Bay) + [0xAC] = ITEM_ROW((u32)GetItemID::GI_NUTS_30, ChestType::DECORATED_BIG, (u8)game::ItemId::Compass, 0x613D, + 0x00091, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s32)DrawGraphicItemID::DI_COMPASS, + (rnd::upgradeFunc)ItemUpgrade_None, ItemEffect_GiveDungeonItem, (s16)2, + (s16)2), // Compass (Great Bay) - [0xAD] = - ITEM_ROW((u32)GetItemID::GI_NUTS_30, ChestType::DECORATED_BIG, (u8)game::ItemId::Compass, 0x003F, 0x00091, - (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, 0xFF, (rnd::upgradeFunc)ItemUpgrade_None, - ItemEffect_GiveDungeonItem, (s16)2, (s16)3), // Compass (Stone Tower) + [0xAD] = ITEM_ROW((u32)GetItemID::GI_NUTS_30, ChestType::DECORATED_BIG, (u8)game::ItemId::Compass, 0x613E, + 0x00091, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s32)DrawGraphicItemID::DI_COMPASS, + (rnd::upgradeFunc)ItemUpgrade_None, ItemEffect_GiveDungeonItem, (s16)2, + (s16)3), // Compass (Stone Tower) - [0xAE] = ITEM_ROW((u32)GetItemID::GI_NUTS_30, ChestType::DECORATED_BIG, (u8)game::ItemId::Map, 0x003E, 0x000A0, - (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, 0xFF, (rnd::upgradeFunc)ItemUpgrade_None, - ItemEffect_GiveDungeonItem, (s16)3, - (s16)0), // Map (Woodfall) + [0xAE] = + ITEM_ROW((u32)GetItemID::GI_NUTS_30, ChestType::DECORATED_BIG, (u8)game::ItemId::Map, 0x6137, 0x000A0, + (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s32)DrawGraphicItemID::DI_DUNGEON_MAP_MAYBE, + (rnd::upgradeFunc)ItemUpgrade_None, ItemEffect_GiveDungeonItem, (s16)3, (s16)0), // Map (Woodfall) - [0xAF] = ITEM_ROW((u32)GetItemID::GI_NUTS_30, ChestType::DECORATED_BIG, (u8)game::ItemId::Map, 0x003E, 0x000A0, - (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, 0xFF, (rnd::upgradeFunc)ItemUpgrade_None, - ItemEffect_GiveDungeonItem, (s16)3, - (s16)1), // Map (Snowhead) + [0xAF] = + ITEM_ROW((u32)GetItemID::GI_NUTS_30, ChestType::DECORATED_BIG, (u8)game::ItemId::Map, 0x6138, 0x000A0, + (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s32)DrawGraphicItemID::DI_DUNGEON_MAP_MAYBE, + (rnd::upgradeFunc)ItemUpgrade_None, ItemEffect_GiveDungeonItem, (s16)3, (s16)1), // Map (Snowhead) - [0xB0] = ITEM_ROW((u32)GetItemID::GI_NUTS_30, ChestType::DECORATED_BIG, (u8)game::ItemId::Map, 0x003E, 0x000A0, - (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, 0xFF, (rnd::upgradeFunc)ItemUpgrade_None, - ItemEffect_GiveDungeonItem, (s16)3, - (s16)2), // Map (Great Bay) + [0xB0] = + ITEM_ROW((u32)GetItemID::GI_NUTS_30, ChestType::DECORATED_BIG, (u8)game::ItemId::Map, 0x6139, 0x000A0, + (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s32)DrawGraphicItemID::DI_DUNGEON_MAP_MAYBE, + (rnd::upgradeFunc)ItemUpgrade_None, ItemEffect_GiveDungeonItem, (s16)3, (s16)2), // Map (Great Bay) - [0xB1] = ITEM_ROW((u32)GetItemID::GI_NUTS_30, ChestType::DECORATED_BIG, (u8)game::ItemId::Map, 0x003E, 0x000A0, - (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, 0xFF, (rnd::upgradeFunc)ItemUpgrade_None, - ItemEffect_GiveDungeonItem, (s16)3, + [0xB1] = ITEM_ROW((u32)GetItemID::GI_NUTS_30, ChestType::DECORATED_BIG, (u8)game::ItemId::Map, 0x613A, 0x000A0, + (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s32)DrawGraphicItemID::DI_DUNGEON_MAP_MAYBE, + (rnd::upgradeFunc)ItemUpgrade_None, ItemEffect_GiveDungeonItem, (s16)3, (s16)3), // Map (Stone Tower) [0xB2] = ITEM_ROW((u32)GetItemID::GI_NUTS_30, ChestType::WOODEN_BIG, (u8)game::ItemId::None, 0x000CB, 0x00096, diff --git a/code/source/rnd/link.cpp b/code/source/rnd/link.cpp index 6ea6c3c7..c50ed555 100644 --- a/code/source/rnd/link.cpp +++ b/code/source/rnd/link.cpp @@ -225,6 +225,14 @@ namespace rnd::link { do_switch(gctx, player, check_magic); } + bool CheckIfMagicAcquired() { + const game::CommonData& cdata = game::GetCommonData(); + if (cdata.save.player.magic_acquired == 0) + return 0; + else + return 1; + } + // This patch is all taken care of in ASM now. No need to loop into the main Calc function. void HandleFastTransform() { const game::GlobalContext* gctx = GetContext().gctx; diff --git a/code/source/rnd/savefile.cpp b/code/source/rnd/savefile.cpp index 10c2cf92..e7c38f5c 100644 --- a/code/source/rnd/savefile.cpp +++ b/code/source/rnd/savefile.cpp @@ -73,7 +73,7 @@ namespace rnd { saveData.inventory.stone_tower_dungeon_items.compass = 1; saveData.inventory.stone_tower_dungeon_items.boss_key = 1; saveData.inventory.woodfall_fairies = 14; - saveData.player.magic_acquired = 1; // Game does not check if value = 0, magic items still + // saveData.player.magic_acquired = 1; // Game does not check if value = 0, magic items still saveData.player.magic_size_type = 0; // saveData.player.magic = 10; saveData.player.magic_num_upgrades = 0; saveData.equipment.data[3].item_btns[0] = game::ItemId::DekuNuts; @@ -138,7 +138,7 @@ namespace rnd { saveData.player.owl_statue_flags.clock_town = 1; #ifdef ENABLE_DEBUG gSettingsContext.startingKokiriSword = 0; - gSettingsContext.startingShield = 1; + gSettingsContext.startingShield = 0; #endif SaveFile_SetStartingInventory(); @@ -345,6 +345,7 @@ namespace rnd { void SaveFile_SetStartingInventory(void) { game::PlayerData& playerData = game::GetCommonData().save.player; game::EquipmentData& equipmentData = game::GetCommonData().save.equipment; + game::SaveData& saveBackupData = game::GetCommonData().save_backup; game::SaveData& saveData = game::GetCommonData().save; // give maps and compasses if (gSettingsContext.mapsAndCompasses == (u8)MapsAndCompassesSetting::MAPSANDCOMPASSES_ANY_DUNGEON) { @@ -828,6 +829,19 @@ namespace rnd { extDataUnmount(fsa); extDataClose(fileHandle); } + + u8 SaveFile_GetIsSceneDiscovered(u8 sceneNum) { + // TODO: ENSURE THE SCENES ARE CHECKED WITH + // OUR BITFLAGS. NOT USING <<. + /*u32 numBits = sizeof(u32) * 8; + u32 idx = sceneNum / numBits; + if (idx < SAVEFILE_SCENES_DISCOVERED_IDX_COUNT) { + u32 bit = 1 << (sceneNum - (idx * numBits)); + return (gExtSaveData.scenesDiscovered[idx] & bit) != 0; + }*/ + return 0; + } + extern "C" void SaveFile_SaveExtSaveData() { #if defined ENABLE_DEBUG || defined DEBUG_PRINT rnd::util::Print("%s: Saving extdata.\n", __func__); diff --git a/code/source/rnd/spoiler_data.cpp b/code/source/rnd/spoiler_data.cpp index 4777d15a..ee47094a 100644 --- a/code/source/rnd/spoiler_data.cpp +++ b/code/source/rnd/spoiler_data.cpp @@ -1,10 +1,20 @@ #include "rnd/spoiler_data.h" #include "game/common_data.h" +#include "rnd/item_override.h" #include "rnd/settings.h" #include "z3d/z3DVec.h" namespace rnd { SpoilerData gSpoilerData = {0}; + SpoilerDataLocs gSpoilerDataLocs[SPOILER_LOCDATS] = {0}; + + SpoilerItemLocation* SpoilerData_ItemLoc(u16 itemIndex) { + return &gSpoilerDataLocs[itemIndex / SPOILER_ITEMS_MAX].ItemLocations[itemIndex % SPOILER_ITEMS_MAX]; + } + + char* SpoilerData_StringData(u16 itemIndex) { + return gSpoilerDataLocs[itemIndex / SPOILER_ITEMS_MAX].StringData; + } char* SpoilerData_GetItemLocationString(u16 itemIndex) { return &gSpoilerData.StringData[gSpoilerData.ItemLocations[itemIndex].LocationStrOffset]; @@ -20,11 +30,29 @@ namespace rnd { } u8 SpoilerData_ChestCheck(SpoilerItemLocation itemLoc) { - // TODO: Implement Chest Checking. No need to use bits as we have - // builtin BitField classes. - // Reference: - // https://github.com/gamestabled/OoT3D_Randomizer/blob/e53be23c14090b15c6c39e08933ca7af54f747f7/code/src/spoiler_data.c#L25-L32 - return 0; + return -1; + } + + u8 SpoilerLog_UpdateIngameLog(ItemOverride_Type type, u8 scene, u8 flag) { + // SpoilerData currentCheck = {0}; + for (int i = 0; i < gSpoilerData.ItemLocationsCount; i++) { + if (gSpoilerData.ItemLocations[i].LocationScene == scene) { + if (gSpoilerData.ItemLocations[i].OverrideType == type) { + if (gSpoilerData.ItemLocations[i].LocationFlag == flag) { + // reveal the check + return -1; + } + } + } + } + /* + for (gSpoilerData.ItemLocations->OverrideType == type && gSpoilerData.ItemLocations->LocationScene == scene && + gSpoilerData.ItemLocations->LocationFlag == flag) + { + //maybe do it this way? + return -1; + }*/ + return -1; } u8 SpoilerData_CollectableCheck(SpoilerItemLocation itemLoc) { @@ -32,7 +60,7 @@ namespace rnd { // builtin BitField classes. // Reference: // https://github.com/gamestabled/OoT3D_Randomizer/blob/e53be23c14090b15c6c39e08933ca7af54f747f7/code/src/spoiler_data.c#L34-L41 - return 0; + return -1; } // Shop checks, will need to be decomped, most likely in common_data.h. @@ -157,4 +185,20 @@ namespace rnd { return 0; } + u8 SpoilerData_GetIsItemLocationRevealed(u16 itemIndex) { + if (gSettingsContext.ingameSpoilers) { + return 1; + } + + SpoilerItemLocation* itemLoc = SpoilerData_ItemLoc(itemIndex); + + if (itemLoc->RevealType == REVEALTYPE_ALWAYS) { + return 1; + } else if (itemLoc->RevealType == REVEALTYPE_NORMAL) { + return 0; + } + + return SaveFile_GetIsSceneDiscovered(itemLoc->LocationScene); + } + } // namespace rnd \ No newline at end of file diff --git a/romfs/exheader.bin b/romfs/exheader.bin index 3aee248f..6196c355 100644 Binary files a/romfs/exheader.bin and b/romfs/exheader.bin differ