mirror of
https://github.com/HarbourMasters/Shipwright
synced 2026-07-12 15:42:44 -04:00
GIM Restoration (#6865)
All edge cases for bottle combinations and replacement/filling mirrors the real game, as well we produce the compass text, and does not render the item exactly as the original game does. (I would have preferred rendering the item since its only cosmetic but I couldn't find a way to do it). This also swaps the table redirect stuff for using the direct item id's which is a lot more clear and does not substitute anything for 'good enough' and covers all the edge case ids that have been documented. This is purely a cosmetic change to present the visual of the item without affecting the cutscene making any tricks for GIM still work and just looks more polished. This supplies effects of ALL game versions (including MZX build) as well as providing text table. To note: we still can crash with GIM even when returning item None EVEN at least with returning Kokiri Tunic (I can't really figure out why) and there are a few items in the table that use extra data that I don't know how to implement here just yet but the info is in the docs under "Variable". In the case of disabling the gim restoration, the game would still crash. This change adds a text id protection to prevent crashing, as well prevents invalid items from crashing directly, and also makes it so the game will no longer crash when having GIM disabled in the menu. Co-authored-by: serprex <159546+serprex@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,461 @@
|
||||
#include "GetItemManipulation.h"
|
||||
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
|
||||
#include "soh/Enhancements/item-tables/ItemTableManager.h"
|
||||
#include "soh/OTRGlobals.h"
|
||||
#include "soh/ShipInit.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include "macros.h"
|
||||
#include "variables.h"
|
||||
extern PlayState* gPlayState;
|
||||
GetItemID RetrieveGetItemIDFromItemID(ItemID itemID);
|
||||
}
|
||||
|
||||
// On N64, Get Item Manipulation (GIM) makes the player receive an item with a negative getItemId,
|
||||
// so the vanilla lookup `sGetItemTable[getItemId - 1]` reads out of bounds below the table. The
|
||||
// bytes it lands on are fixed player overlay data, so each negative getItemId deterministically
|
||||
// resolves to the itemId below, indexed by getItemId + 128. getItemId is s8 on console, so
|
||||
// [-128, -1] covers every value reachable there.
|
||||
//
|
||||
// Tables from:
|
||||
// ItemID ->
|
||||
// https://docs.google.com/spreadsheets/d/1SLJzamokLb7wDOaJh5x8DsxmMBy9oIYawyDN3dAWppw/edit?gid=870929937#gid=870929937
|
||||
// Text -> https://docs.google.com/spreadsheets/d/13N9lJqF5JSBZkVkVTvkrorJ8PlEBMowUH4nkrG93BuQ/edit?gid=0#gid=0
|
||||
|
||||
// Format for matrix below:
|
||||
// [NTSC 1.0], [NTSC 1.1], [NTSC 1.2], [PAL1.0/1.1], [(GC U/J) / (MQ J)], [(GC E) / (MQ U/E)], [IQUE CHN/TWN], [MQ
|
||||
// Debug], [MZX NTSC], [MZX PAL]
|
||||
static const ItemID sGimItemIdsVer[40][10] = {
|
||||
/* 80 */ { ITEM_POE, ITEM_MASK_GORON, ITEM_POE, ITEM_STICK, ITEM_STICK, ITEM_STICK, ITEM_STICK, ITEM_BULLET_BAG_30,
|
||||
ITEM_POE, ITEM_STICK },
|
||||
/* 81 */
|
||||
{ ITEM_BOMB, ITEM_STICK, ITEM_BOMB, ITEM_ARROW_ICE, ITEM_ARROW_ICE, ITEM_ARROW_ICE, ITEM_ARROW_ICE, ITEM_BEAN,
|
||||
ITEM_BOMB, ITEM_ARROW_ICE },
|
||||
/* 82 */
|
||||
{ ITEM_CHICKEN, ITEM_STICK, ITEM_MASK_KEATON, ITEM_ODD_MUSHROOM, ITEM_ODD_MUSHROOM, ITEM_ODD_MUSHROOM,
|
||||
ITEM_ODD_MUSHROOM, ITEM_BOMB, ITEM_NONE, ITEM_ODD_MUSHROOM },
|
||||
/* 83 */
|
||||
{ ITEM_BEAN, ITEM_BOMBS_10, ITEM_BEAN, ITEM_BOMBS_5, ITEM_BOMBS_5, ITEM_BOMBS_5, ITEM_BOMBS_5, ITEM_BOMB, ITEM_BEAN,
|
||||
ITEM_BOMBS_5 },
|
||||
/* 84 */
|
||||
{ ITEM_NUT, ITEM_MASK_GORON, ITEM_NUT, ITEM_STICK, ITEM_STICK, ITEM_STICK, ITEM_STICK, ITEM_SONG_MINUET, ITEM_NUT,
|
||||
ITEM_STICK },
|
||||
/* 85 */
|
||||
{ ITEM_BOMB, ITEM_NONE, ITEM_BOMB, ITEM_ARROW_ICE, ITEM_ARROW_ICE, ITEM_ARROW_ICE, ITEM_ARROW_ICE, ITEM_BOMBS_10,
|
||||
ITEM_BOMB, ITEM_ARROW_ICE },
|
||||
/* 86 */
|
||||
{ ITEM_PRESCRIPTION, ITEM_STICK, ITEM_EYEDROPS, ITEM_STICK, ITEM_STICK, ITEM_STICK, ITEM_STICK, ITEM_TUNIC_GORON,
|
||||
ITEM_NONE, ITEM_STICK },
|
||||
/* 87 */
|
||||
{ ITEM_BOMBS_10, ITEM_BOOTS_KOKIRI, ITEM_BOMBS_10, ITEM_SWORD_MASTER, ITEM_SWORD_MASTER, ITEM_SWORD_MASTER,
|
||||
ITEM_SWORD_MASTER, ITEM_NONE, ITEM_BOMBS_10, ITEM_SWORD_MASTER },
|
||||
/* 88 */
|
||||
{ ITEM_TUNIC_GORON, ITEM_STICK, ITEM_TUNIC_GORON, ITEM_BRACELET, ITEM_BRACELET, ITEM_BRACELET, ITEM_BRACELET,
|
||||
ITEM_ARROW_FIRE, ITEM_TUNIC_GORON, ITEM_BRACELET },
|
||||
/* 89 */
|
||||
{ ITEM_NONE, ITEM_MASK_KEATON, ITEM_NONE, ITEM_SWORD_MASTER, ITEM_SWORD_MASTER, ITEM_SWORD_MASTER,
|
||||
ITEM_SWORD_MASTER, ITEM_BOOTS_HOVER, ITEM_NONE, ITEM_SWORD_MASTER },
|
||||
/* 8A */
|
||||
{ ITEM_ARROW_FIRE, ITEM_SHIELD_MIRROR, ITEM_ARROW_FIRE, ITEM_ODD_POTION, ITEM_ODD_POTION, ITEM_ODD_POTION,
|
||||
ITEM_ODD_POTION, ITEM_STICK, ITEM_ARROW_FIRE, ITEM_ODD_POTION },
|
||||
/* 8B */
|
||||
{ ITEM_BOOTS_HOVER, ITEM_BOOTS_IRON, ITEM_BOOTS_HOVER, ITEM_BOMBS_10, ITEM_BOMBS_10, ITEM_BOMBS_10, ITEM_BOMBS_10,
|
||||
ITEM_STICK, ITEM_BOOTS_HOVER, ITEM_BOMBS_10 },
|
||||
/* 8C */
|
||||
{ ITEM_STICK, ITEM_STICK, ITEM_STICK, ITEM_STICK, ITEM_STICK, ITEM_STICK, ITEM_STICK, ITEM_ARROW_FIRE, ITEM_STICK,
|
||||
ITEM_STICK },
|
||||
/* 8D */
|
||||
{ ITEM_STICK, ITEM_BEAN, ITEM_STICK, ITEM_SWORD_MASTER, ITEM_SWORD_MASTER, ITEM_SWORD_MASTER, ITEM_SWORD_MASTER,
|
||||
ITEM_MASK_KEATON, ITEM_STICK, ITEM_SWORD_MASTER },
|
||||
/* 8E */
|
||||
{ ITEM_ARROW_FIRE, ITEM_POCKET_EGG, ITEM_ARROW_FIRE, ITEM_STICK, ITEM_STICK, ITEM_STICK, ITEM_STICK,
|
||||
ITEM_BULLET_BAG_50, ITEM_ARROW_FIRE, ITEM_STICK },
|
||||
/* 8F */
|
||||
{ ITEM_MASK_KEATON, ITEM_BOMB, ITEM_MASK_KEATON, ITEM_ARROW_ICE, ITEM_ARROW_ICE, ITEM_ARROW_ICE, ITEM_ARROW_ICE,
|
||||
ITEM_BOMBS_5, ITEM_MASK_KEATON, ITEM_ARROW_ICE },
|
||||
/* 90 */
|
||||
{ ITEM_LETTER_ZELDA, ITEM_NUT, ITEM_MASK_KEATON, ITEM_MASK_GORON, ITEM_MASK_GORON, ITEM_MASK_GORON, ITEM_MASK_GORON,
|
||||
ITEM_ARROW_FIRE, ITEM_NONE, ITEM_MASK_GORON },
|
||||
/* 91 */
|
||||
{ ITEM_BOMBS_5, ITEM_MASK_SKULL, ITEM_BOMBS_5, ITEM_SWORD_MASTER, ITEM_SWORD_MASTER, ITEM_SWORD_MASTER,
|
||||
ITEM_SWORD_MASTER, ITEM_POTION_RED, ITEM_BOMBS_5, ITEM_SWORD_MASTER },
|
||||
/* 92 */
|
||||
{ ITEM_ARROW_FIRE, ITEM_STICK, ITEM_ARROW_FIRE, ITEM_POCKET_EGG, ITEM_POCKET_EGG, ITEM_POCKET_EGG, ITEM_POCKET_EGG,
|
||||
ITEM_STICK, ITEM_ARROW_FIRE, ITEM_POCKET_EGG },
|
||||
/* 93 */
|
||||
{ ITEM_POTION_RED, ITEM_BOMB, ITEM_POTION_RED, ITEM_BOMBS_10, ITEM_BOMBS_10, ITEM_BOMBS_10, ITEM_BOMBS_10,
|
||||
ITEM_ARROW_ICE, ITEM_POTION_RED, ITEM_BOMBS_10 },
|
||||
/* 94 */
|
||||
{ ITEM_STICK, ITEM_MASK_SPOOKY, ITEM_STICK, ITEM_MASK_GORON, ITEM_MASK_GORON, ITEM_MASK_GORON, ITEM_MASK_GORON,
|
||||
ITEM_STICK, ITEM_STICK, ITEM_MASK_GORON },
|
||||
/* 95 */
|
||||
{ ITEM_ARROW_ICE, ITEM_ARROW_ICE, ITEM_ARROW_ICE, ITEM_MASK_KEATON, ITEM_MASK_KEATON, ITEM_MASK_KEATON,
|
||||
ITEM_MASK_KEATON, ITEM_BOMB, ITEM_ARROW_ICE, ITEM_MASK_KEATON },
|
||||
/* 96 */
|
||||
{ ITEM_STICK, ITEM_POE, ITEM_STICK, ITEM_MILK_BOTTLE, ITEM_MASK_SPOOKY, ITEM_BLUE_FIRE, ITEM_ARROW_LIGHT,
|
||||
ITEM_SLINGSHOT, ITEM_STICK, ITEM_NONE },
|
||||
/* 97 */
|
||||
{ ITEM_BOMB, ITEM_SWORD_MASTER, ITEM_BOMB, ITEM_BOMBS_5, ITEM_BOMBS_5, ITEM_BOMBS_5, ITEM_BOMBS_5,
|
||||
ITEM_PRESCRIPTION, ITEM_BOMB, ITEM_BOMBS_5 },
|
||||
/* 98 */
|
||||
{ ITEM_SLINGSHOT, ITEM_STICK, ITEM_SLINGSHOT, ITEM_POE, ITEM_POE, ITEM_POE, ITEM_POE, ITEM_NONE, ITEM_SLINGSHOT,
|
||||
ITEM_POE },
|
||||
/* 99 */
|
||||
{ ITEM_PRESCRIPTION, ITEM_NONE, ITEM_PRESCRIPTION, ITEM_BOW, ITEM_BOW, ITEM_BOW, ITEM_BOW, ITEM_BOMBS_5,
|
||||
ITEM_PRESCRIPTION, ITEM_BOW },
|
||||
/* 9A */
|
||||
{ ITEM_NONE, ITEM_SLINGSHOT, ITEM_NONE, ITEM_SLINGSHOT, ITEM_SLINGSHOT, ITEM_SLINGSHOT, ITEM_SLINGSHOT,
|
||||
ITEM_SLINGSHOT, ITEM_NONE, ITEM_SLINGSHOT },
|
||||
/* 9B */
|
||||
{ ITEM_BOMBS_5, ITEM_SCALE_GOLDEN, ITEM_BOMBS_5, ITEM_BOMBS_5, ITEM_BOMBS_5, ITEM_BOMBS_5, ITEM_BOMBS_5,
|
||||
ITEM_BOMBS_10, ITEM_BOMBS_5, ITEM_BOMBS_5 },
|
||||
/* 9C */
|
||||
{ ITEM_SLINGSHOT, ITEM_STICK, ITEM_SLINGSHOT, ITEM_STICK, ITEM_STICK, ITEM_STICK, ITEM_STICK, ITEM_NUT,
|
||||
ITEM_SLINGSHOT, ITEM_STICK },
|
||||
/* 9D */
|
||||
{ ITEM_BOMBS_10, ITEM_MASK_KEATON, ITEM_BOMBS_10, ITEM_ARROWS_LARGE, ITEM_ARROWS_LARGE, ITEM_ARROWS_LARGE,
|
||||
ITEM_ARROWS_LARGE, ITEM_ODD_POTION, ITEM_BOMBS_10, ITEM_ARROWS_LARGE },
|
||||
/* 9E */
|
||||
{ ITEM_NUT, ITEM_NONE, ITEM_NUT, ITEM_BOMB, ITEM_BOMB, ITEM_BOMB, ITEM_BOMB, ITEM_STICK, ITEM_NUT, ITEM_BOMB },
|
||||
/* 9F */
|
||||
{ ITEM_ODD_POTION, ITEM_BOMBS_10, ITEM_ODD_POTION, ITEM_GAUNTLETS_SILVER, ITEM_GAUNTLETS_SILVER,
|
||||
ITEM_GAUNTLETS_SILVER, ITEM_GAUNTLETS_SILVER, ITEM_NUT, ITEM_ODD_POTION, ITEM_GAUNTLETS_SILVER },
|
||||
/* A0 */
|
||||
{ ITEM_STICK, ITEM_STICK, ITEM_STICK, ITEM_STICK, ITEM_STICK, ITEM_STICK, ITEM_STICK, ITEM_STICK, ITEM_STICK,
|
||||
ITEM_STICK },
|
||||
/* A1 */
|
||||
{ ITEM_NUTS_5, ITEM_FROG, ITEM_NUTS_5, ITEM_MASK_KEATON, ITEM_MASK_KEATON, ITEM_MASK_KEATON, ITEM_MASK_KEATON,
|
||||
ITEM_NONE, ITEM_NUT, ITEM_MASK_KEATON },
|
||||
/* A2 */
|
||||
{ ITEM_STICK, ITEM_STICK, ITEM_STICK, ITEM_NUT, ITEM_NUT, ITEM_NUT, ITEM_NUT, ITEM_SEEDS, ITEM_STICK, ITEM_NUT },
|
||||
/* A3 */
|
||||
{ ITEM_NONE, ITEM_BOMBS_10, ITEM_NONE, ITEM_ARROW_ICE, ITEM_ARROW_ICE, ITEM_ARROW_ICE, ITEM_ARROW_ICE,
|
||||
ITEM_BOMBS_10, ITEM_NONE, ITEM_ARROW_ICE },
|
||||
/* A4 */
|
||||
{ ITEM_SAW, ITEM_STICK, ITEM_PRESCRIPTION, ITEM_STICK, ITEM_STICK, ITEM_STICK, ITEM_STICK, ITEM_STICK, ITEM_NONE,
|
||||
ITEM_STICK },
|
||||
/* A5 */
|
||||
{ ITEM_BOMBS_10, ITEM_MASK_BUNNY, ITEM_BOMBS_10, ITEM_BOMBS_10, ITEM_BOMBS_10, ITEM_BOMBS_10, ITEM_BOMBS_10,
|
||||
ITEM_BOW, ITEM_BOMBS_10, ITEM_BOMBS_10 },
|
||||
/* A6 */
|
||||
{ ITEM_STICK, ITEM_STICK, ITEM_STICK, ITEM_STICK, ITEM_STICK, ITEM_STICK, ITEM_STICK, ITEM_STICK, ITEM_STICK,
|
||||
ITEM_STICK },
|
||||
/* A7 */
|
||||
{ ITEM_BOW, ITEM_STICK, ITEM_BOW, ITEM_STICK, ITEM_STICK, ITEM_STICK, ITEM_STICK, ITEM_STICK, ITEM_BOW, ITEM_STICK }
|
||||
};
|
||||
|
||||
// These values are consistent reguardless of version.
|
||||
static const ItemID sGimItemIdsExt[88] = {
|
||||
/* A8 */ ITEM_STICK,
|
||||
/* A9 */ ITEM_STICK,
|
||||
/* AA */ ITEM_NUT,
|
||||
/* AB */ ITEM_NUT,
|
||||
/* AC */ ITEM_STICK,
|
||||
/* AD */ ITEM_TUNIC_GORON,
|
||||
/* AE */ ITEM_STICK,
|
||||
/* AF */ ITEM_TUNIC_GORON,
|
||||
/* B0 */ ITEM_NONE,
|
||||
/* B1 */ ITEM_TUNIC_GORON,
|
||||
/* B2 */ ITEM_STICK,
|
||||
/* B3 */ ITEM_TUNIC_GORON,
|
||||
/* B4 */ ITEM_STICK,
|
||||
/* B5 */ ITEM_TUNIC_GORON,
|
||||
/* B6 */ ITEM_STICK,
|
||||
/* B7 */ ITEM_TUNIC_GORON,
|
||||
/* B8 */ ITEM_ARROW_LIGHT,
|
||||
/* B9 */ ITEM_ARROW_LIGHT,
|
||||
/* BA */ ITEM_POTION_BLUE,
|
||||
/* BB */ ITEM_ARROW_LIGHT,
|
||||
/* BC */ ITEM_POTION_BLUE,
|
||||
/* BD */ ITEM_POTION_BLUE,
|
||||
/* BE */ ITEM_BIG_POE,
|
||||
/* BF */ ITEM_POTION_BLUE,
|
||||
/* C0 */ ITEM_BIG_POE,
|
||||
/* C1 */ ITEM_ARROW_LIGHT,
|
||||
/* C2 */ ITEM_POTION_BLUE,
|
||||
/* C3 */ ITEM_ARROW_LIGHT,
|
||||
/* C4 */ ITEM_POTION_BLUE,
|
||||
/* C5 */ ITEM_STICK,
|
||||
/* C6 */ ITEM_MASK_BUNNY,
|
||||
/* C7 */ ITEM_ARROW_FIRE,
|
||||
/* C8 */ ITEM_POCKET_CUCCO,
|
||||
/* C9 */ ITEM_ARROW_FIRE,
|
||||
/* CA */ ITEM_POCKET_CUCCO,
|
||||
/* CB */ ITEM_ARROW_FIRE,
|
||||
/* CC */ ITEM_POCKET_EGG,
|
||||
/* CD */ ITEM_ARROW_FIRE,
|
||||
/* CE */ ITEM_POCKET_CUCCO,
|
||||
/* CF */ ITEM_ARROW_FIRE,
|
||||
/* D0 */ ITEM_STICK,
|
||||
/* D1 */ ITEM_SHIELD_HYLIAN,
|
||||
/* D2 */ ITEM_STICK,
|
||||
/* D3 */ ITEM_TUNIC_GORON,
|
||||
/* D4 */ ITEM_STICK,
|
||||
/* D5 */ ITEM_TUNIC_KOKIRI,
|
||||
/* D6 */ ITEM_STICK,
|
||||
/* D7 */ ITEM_TUNIC_GORON,
|
||||
/* D8 */ ITEM_STICK,
|
||||
/* D9 */ ITEM_TUNIC_KOKIRI,
|
||||
/* DA */ ITEM_STICK,
|
||||
/* DB */ ITEM_NONE,
|
||||
/* DC */ ITEM_NONE,
|
||||
/* DD */ ITEM_NONE,
|
||||
/* DE */ ITEM_STICK,
|
||||
/* DF */ ITEM_STICK,
|
||||
/* E0 */ ITEM_NONE,
|
||||
/* E1 */ ITEM_NONE,
|
||||
/* E2 */ ITEM_STICK,
|
||||
/* E3 */ ITEM_STICK,
|
||||
/* E4 */ ITEM_STICK,
|
||||
/* E5 */ ITEM_STICK,
|
||||
/* E6 */ ITEM_NONE,
|
||||
/* E7 */ ITEM_NONE,
|
||||
/* E8 */ ITEM_STICK,
|
||||
/* E9 */ ITEM_ARROW_FIRE,
|
||||
/* EA */ ITEM_LETTER_ZELDA,
|
||||
/* EB */ ITEM_ARROW_FIRE,
|
||||
/* EC */ ITEM_LETTER_ZELDA,
|
||||
/* ED */ ITEM_ARROW_FIRE,
|
||||
/* EE */ ITEM_POCKET_EGG,
|
||||
/* EF */ ITEM_ARROW_FIRE,
|
||||
/* F0 */ ITEM_POCKET_EGG,
|
||||
/* F1 */ ITEM_ARROW_FIRE,
|
||||
/* F2 */ ITEM_LETTER_ZELDA,
|
||||
/* F3 */ ITEM_STICK,
|
||||
/* F4 */ ITEM_STICK,
|
||||
/* F5 */ ITEM_NONE /* variable */,
|
||||
/* F6 */ ITEM_STICK,
|
||||
/* F7 */ ITEM_SHIELD_HYLIAN,
|
||||
/* F8 */ ITEM_STICK,
|
||||
/* F9 */ ITEM_STICK,
|
||||
/* FA */ ITEM_STICK,
|
||||
/* FB */ ITEM_NONE /* variable */,
|
||||
/* FC */ ITEM_STICK,
|
||||
/* FD */ ITEM_STICK,
|
||||
/* FE */ ITEM_STICK,
|
||||
/* FF */ ITEM_STICK
|
||||
};
|
||||
|
||||
// Text type seems universal?
|
||||
static const uint16_t sGimTextIds[128] = {
|
||||
/* 80 */ 0x40,
|
||||
/* 81 */ 0x15,
|
||||
/* 82 */ 0xA4,
|
||||
/* 83 */ 0x25,
|
||||
/* 84 */ 0xA7,
|
||||
/* 85 */ 0x34,
|
||||
/* 86 */ 0x81,
|
||||
/* 87 */ 0x90,
|
||||
/* 88 */ 0xC6,
|
||||
/* 89 */ 0x3C,
|
||||
/* 8A */ 0x00,
|
||||
/* 8B */ 0x00,
|
||||
/* 8C */ 0x00,
|
||||
/* 8D */ 0xF0,
|
||||
/* 8E */ 0x00,
|
||||
/* 8F */ 0xBC,
|
||||
/* 90 */ 0xCE,
|
||||
/* 91 */ 0x04,
|
||||
/* 92 */ 0x00,
|
||||
/* 93 */ 0xD6,
|
||||
/* 94 */ 0x20,
|
||||
/* 95 */ 0x25,
|
||||
/* 96 */ 0x01,
|
||||
/* 97 */ 0x40,
|
||||
/* 98 */ 0x19,
|
||||
/* 99 */ 0x94,
|
||||
/* 9A */ 0x62,
|
||||
/* 9B */ 0x1C,
|
||||
/* 9C */ 0x01,
|
||||
/* 9D */ 0x00,
|
||||
/* 9E */ 0xBF,
|
||||
/* 9F */ 0x04,
|
||||
/* A0 */ 0x4B,
|
||||
/* A1 */ 0x04,
|
||||
/* A2 */ 0xA4,
|
||||
/* A3 */ 0x1C,
|
||||
/* A4 */ 0xBD,
|
||||
/* A5 */ 0x08,
|
||||
/* A6 */ 0x00,
|
||||
/* A7 */ 0x00,
|
||||
/* A8 */ 0x00,
|
||||
/* A9 */ 0x00,
|
||||
/* AA */ 0x01,
|
||||
/* AB */ 0x01,
|
||||
/* AC */ 0x60,
|
||||
/* AD */ 0x00,
|
||||
/* AE */ 0xDE,
|
||||
/* AF */ 0x00,
|
||||
/* B0 */ 0x6C,
|
||||
/* B1 */ 0x00,
|
||||
/* B2 */ 0x10,
|
||||
/* B3 */ 0x33,
|
||||
/* B4 */ 0x88,
|
||||
/* B5 */ 0x00,
|
||||
/* B6 */ 0x70,
|
||||
/* B7 */ 0x00,
|
||||
/* B8 */ 0x67,
|
||||
/* B9 */ 0x7C,
|
||||
/* BA */ 0x67,
|
||||
/* BB */ 0x7C,
|
||||
/* BC */ 0x67,
|
||||
/* BD */ 0x67,
|
||||
/* BE */ 0x7C,
|
||||
/* BF */ 0x67,
|
||||
/* C0 */ 0x7C,
|
||||
/* C1 */ 0x7C,
|
||||
/* C2 */ 0x67,
|
||||
/* C3 */ 0x7C,
|
||||
/* C4 */ 0x67,
|
||||
/* C5 */ 0x00,
|
||||
/* C6 */ 0x00,
|
||||
/* C7 */ 0x38,
|
||||
/* C8 */ 0x00,
|
||||
/* C9 */ 0x80,
|
||||
/* CA */ 0x00,
|
||||
/* CB */ 0x98,
|
||||
/* CC */ 0x00,
|
||||
/* CD */ 0x50,
|
||||
/* CE */ 0x00,
|
||||
/* CF */ 0x60,
|
||||
/* D0 */ 0x70,
|
||||
/* D1 */ 0xA6,
|
||||
/* D2 */ 0x48,
|
||||
/* D3 */ 0x00,
|
||||
/* D4 */ 0xD8,
|
||||
/* D5 */ 0x00,
|
||||
/* D6 */ 0xEC,
|
||||
/* D7 */ 0x00,
|
||||
/* D8 */ 0x35,
|
||||
/* D9 */ 0x00,
|
||||
/* DA */ 0x5C,
|
||||
/* DB */ 0xED,
|
||||
/* DC */ 0x92,
|
||||
/* DD */ 0x71,
|
||||
/* DE */ 0x56,
|
||||
/* DF */ 0xEA,
|
||||
/* E0 */ 0x71,
|
||||
/* E1 */ 0x5F,
|
||||
/* E2 */ 0xEA,
|
||||
/* E3 */ 0x0D,
|
||||
/* E4 */ 0x56,
|
||||
/* E5 */ 0xEA,
|
||||
/* E6 */ 0x56,
|
||||
/* E7 */ 0xEA,
|
||||
/* E8 */ 0x00,
|
||||
/* E9 */ 0x18,
|
||||
/* EA */ 0x00,
|
||||
/* EB */ 0xE0,
|
||||
/* EC */ 0x00,
|
||||
/* ED */ 0xF8,
|
||||
/* EE */ 0x00,
|
||||
/* EF */ 0x70,
|
||||
/* F0 */ 0x00,
|
||||
/* F1 */ 0xC8,
|
||||
/* F2 */ 0x00,
|
||||
/* F3 */ 0x00,
|
||||
/* F4 */ 0x00,
|
||||
/* F5 */ 0x00,
|
||||
/* F6 */ 0x00,
|
||||
/* F7 */ 0x00,
|
||||
/* F8 */ 0x00,
|
||||
/* F9 */ 0x00,
|
||||
/* FA */ 0x00,
|
||||
/* FB */ 0x00,
|
||||
/* FC */ 0x00,
|
||||
/* FD */ 0x00,
|
||||
/* FE */ 0x00,
|
||||
/* FF */ 0x00
|
||||
};
|
||||
|
||||
extern "C" GetItemEntry Gim_RetrieveOobGetItemEntry(int16_t getItemId) {
|
||||
int32_t version = CVarGetInteger(CVAR_ENHANCEMENT("GetItemManipulation"), GIM_DISABLED);
|
||||
|
||||
uint8_t tableOffset = getItemId + 128;
|
||||
uint8_t mVersionOffset = 0;
|
||||
|
||||
// SoH widened Player.getItemId from s8 to s16, so values below the console range can
|
||||
// exist here; treat them (and the restoration being disabled) as an item table miss.
|
||||
if (getItemId < -128)
|
||||
return GET_ITEM_NONE;
|
||||
|
||||
switch (version) {
|
||||
case GIM_DISABLED:
|
||||
return GET_ITEM_NONE;
|
||||
|
||||
case GIM_NTSC_1_0:
|
||||
mVersionOffset = 0;
|
||||
break;
|
||||
|
||||
case GIM_NTSC_1_1:
|
||||
mVersionOffset = 1;
|
||||
break;
|
||||
|
||||
case GIM_NTSC_1_2:
|
||||
mVersionOffset = 2;
|
||||
break;
|
||||
|
||||
case GIM_PAL_1_0:
|
||||
case GIM_PAL_1_1:
|
||||
mVersionOffset = 3;
|
||||
break;
|
||||
|
||||
case GIM_GC_U:
|
||||
case GIM_GC_J:
|
||||
case GIM_MQ_J:
|
||||
mVersionOffset = 4;
|
||||
break;
|
||||
|
||||
case GIM_GC_E:
|
||||
case GIM_MQ_U:
|
||||
case GIM_MQ_E:
|
||||
mVersionOffset = 5;
|
||||
break;
|
||||
|
||||
case GIM_IQUE_CHN:
|
||||
case GIM_IQUE_TWN:
|
||||
mVersionOffset = 6;
|
||||
break;
|
||||
|
||||
case GIM_MQ_DEBUG:
|
||||
mVersionOffset = 7;
|
||||
break;
|
||||
|
||||
case GIM_MZX_NTSC:
|
||||
mVersionOffset = 8;
|
||||
break;
|
||||
|
||||
case GIM_MZX_PAL:
|
||||
mVersionOffset = 9;
|
||||
break;
|
||||
}
|
||||
|
||||
// Only the received itemId of the console OOB read is documented.
|
||||
GetItemID giId;
|
||||
ItemID itemId;
|
||||
|
||||
// Determine if the item id is universal or unique
|
||||
if ((getItemId & 0xFF) <= 0xA7) {
|
||||
itemId = sGimItemIdsVer[tableOffset][mVersionOffset];
|
||||
giId = RetrieveGetItemIDFromItemID(itemId);
|
||||
} else {
|
||||
itemId = sGimItemIdsExt[tableOffset - 40];
|
||||
giId = RetrieveGetItemIDFromItemID(itemId);
|
||||
}
|
||||
|
||||
GetItemEntry giEntry;
|
||||
|
||||
// Safety in case we retrieved an incompatible GI item type
|
||||
if (giId == GI_MAX) {
|
||||
// We give them no item because we don't have a valid 'get item'.
|
||||
// Allow the games original text value to pass through from text table though.
|
||||
giEntry = GET_ITEM_NONE;
|
||||
} else {
|
||||
giEntry = ItemTableManager::Instance->RetrieveItemEntry(MOD_NONE, giId);
|
||||
}
|
||||
|
||||
// Fetch the correct textbox to use.
|
||||
giEntry.textId = sGimTextIds[tableOffset];
|
||||
|
||||
return giEntry;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "soh/Enhancements/item-tables/ItemTableTypes.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
GIM_DISABLED,
|
||||
GIM_NTSC_1_0,
|
||||
GIM_NTSC_1_1,
|
||||
GIM_NTSC_1_2,
|
||||
GIM_PAL_1_0,
|
||||
GIM_PAL_1_1,
|
||||
GIM_GC_U,
|
||||
GIM_GC_E,
|
||||
GIM_GC_J,
|
||||
GIM_MQ_U,
|
||||
GIM_MQ_E,
|
||||
GIM_MQ_J,
|
||||
GIM_IQUE_CHN,
|
||||
GIM_IQUE_TWN,
|
||||
GIM_MQ_DEBUG,
|
||||
GIM_MZX_NTSC,
|
||||
GIM_MZX_PAL
|
||||
} GimVersion;
|
||||
|
||||
GetItemEntry Gim_RetrieveOobGetItemEntry(int16_t getItemId);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -76,6 +76,7 @@
|
||||
|
||||
#include <functions.h>
|
||||
#include "Enhancements/item-tables/ItemTableManager.h"
|
||||
#include "Enhancements/Restorations/GetItemManipulation.h"
|
||||
#include "Enhancements/Lang/Lang.h"
|
||||
#include "soh/SohGui/ImGuiUtils.h"
|
||||
#include "ActorDB.h"
|
||||
@@ -2349,6 +2350,11 @@ extern "C" ShopItemIdentity Randomizer_IdentifyShopItem(s32 sceneNum, u8 slotInd
|
||||
}
|
||||
|
||||
extern "C" GetItemEntry ItemTable_Retrieve(int16_t getItemID) {
|
||||
// A negative getItemId makes the vanilla lookup `sGetItemTable[getItemId - 1]` read out of
|
||||
// bounds below the table (Get Item Manipulation); reproduce the console result of that read.
|
||||
if (getItemID < 0) {
|
||||
return Gim_RetrieveOobGetItemEntry(getItemID);
|
||||
}
|
||||
GetItemEntry giEntry = ItemTableManager::Instance->RetrieveItemEntry(MOD_NONE, getItemID);
|
||||
return giEntry;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <soh/Enhancements/cosmetics/authenticGfxPatches.h>
|
||||
#include <soh/Enhancements/TimeDisplay/TimeDisplay.h>
|
||||
#include "soh/Enhancements/randomizer/randomizer.h"
|
||||
#include "soh/Enhancements/Restorations/GetItemManipulation.h"
|
||||
#include <ship/Context.h>
|
||||
|
||||
extern "C" {
|
||||
@@ -62,6 +63,18 @@ static const std::map<int32_t, const char*> timeTravelOptions = {
|
||||
{ TIME_TRAVEL_ANY_MS, "Any Ocarina + Master Sword" },
|
||||
};
|
||||
|
||||
static const std::map<int32_t, const char*> getItemManipulationOptions = {
|
||||
{ GIM_DISABLED, "Disabled" }, { GIM_NTSC_1_0, "NTSC 1.0" },
|
||||
{ GIM_NTSC_1_1, "NTSC 1.1" }, { GIM_NTSC_1_2, "NTSC 1.2" },
|
||||
{ GIM_PAL_1_0, "PAL 1.0" }, { GIM_PAL_1_1, "PAL 1.1" },
|
||||
{ GIM_GC_U, "GC U" }, { GIM_GC_E, "GC E" },
|
||||
{ GIM_GC_J, "GC J" }, { GIM_MQ_U, "MQ U" },
|
||||
{ GIM_MQ_E, "MQ E" }, { GIM_MQ_J, "MQ J" },
|
||||
{ GIM_IQUE_CHN, "IQUE CHN" }, { GIM_IQUE_TWN, "IQUE TWN" },
|
||||
{ GIM_MQ_DEBUG, "MQ DEBUG" }, { GIM_MZX_NTSC, "MZX NTSC 1.0" },
|
||||
{ GIM_MZX_PAL, "MZX PAL 1.1" },
|
||||
};
|
||||
|
||||
static const std::map<int32_t, const char*> sleepingWaterfallOptions = {
|
||||
{ WATERFALL_ALWAYS, "Always" },
|
||||
{ WATERFALL_ONCE, "Once" },
|
||||
@@ -1258,6 +1271,13 @@ void SohMenu::AddMenuEnhancements() {
|
||||
.Options(CheckboxOptions().Tooltip(
|
||||
"Restores a bug from NTSC 1.0/1.1 that allows you to obtain the eyeball frog from King Zora "
|
||||
"instead of the Zora Tunic by Holding Shield."));
|
||||
AddWidget(path, "Get Item Manipulation", WIDGET_CVAR_COMBOBOX)
|
||||
.CVar(CVAR_ENHANCEMENT("GetItemManipulation"))
|
||||
.Options(ComboboxOptions()
|
||||
.ComboMap(getItemManipulationOptions)
|
||||
.DefaultIndex(GIM_DISABLED)
|
||||
.Tooltip("Restores Get Item Manipulation.\n"
|
||||
"NTSC and PAL have separate tables."));
|
||||
|
||||
AddWidget(path, "Misc Restorations", WIDGET_SEPARATOR_TEXT);
|
||||
AddWidget(path, "Fix L&Z Page Switch in Pause Menu", WIDGET_CVAR_CHECKBOX)
|
||||
|
||||
@@ -2736,7 +2736,9 @@ void Message_OpenText(PlayState* play, u16 textId) {
|
||||
textId = 0xB; // Traded Giant's Knife for Biggoron Sword
|
||||
} else if (!IS_RANDO &&
|
||||
(msgCtx->textId == 0xB4 && (Flags_GetEventChkInf(EVENTCHKINF_SPOKE_TO_CURSED_MAN_IN_SKULL_HOUSE)))) {
|
||||
textId = 0xB5; // Destroyed Gold Skulltula
|
||||
textId = 0xB5; // Destroyed Gold Skulltula
|
||||
} else if (textId == 0x0) { // Invalid text probably from GIM or SRM
|
||||
textId = 1;
|
||||
}
|
||||
// Ocarina Staff + Dialog
|
||||
if (textId == 0x4077 || // Pierre?
|
||||
|
||||
@@ -2383,7 +2383,7 @@ u8 Item_Give(PlayState* play, u8 item) {
|
||||
}
|
||||
|
||||
gSaveContext.inventory.items[temp + i] = item;
|
||||
break;
|
||||
return Return_Item(item, MOD_NONE, ITEM_NONE);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -2393,11 +2393,14 @@ u8 Item_Give(PlayState* play, u8 item) {
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (gSaveContext.inventory.items[temp + i] == ITEM_NONE) {
|
||||
gSaveContext.inventory.items[temp + i] = item;
|
||||
break;
|
||||
return Return_Item(item, MOD_NONE, ITEM_NONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Return_Item(item, MOD_NONE, ITEM_NONE);
|
||||
|
||||
if (IS_RANDO) {
|
||||
return Return_Item(item, MOD_NONE, ITEM_NONE);
|
||||
}
|
||||
} else if ((item >= ITEM_WEIRD_EGG) && (item <= ITEM_CLAIM_CHECK)) {
|
||||
if (GameInteractor_Should(VB_POACHERS_SAW_SET_DEKU_NUT_UPGRADE_FLAG, item == ITEM_SAW)) {
|
||||
Flags_SetItemGetInf(ITEMGETINF_OBTAINED_NUT_UPGRADE_FROM_STAGE);
|
||||
|
||||
@@ -14027,7 +14027,10 @@ s32 func_8084DFF4(PlayState* play, Player* this) {
|
||||
gSaveContext.bgsFlag = true;
|
||||
gSaveContext.swordHealth = 8;
|
||||
}
|
||||
Item_Give(play, giEntry.itemId);
|
||||
|
||||
// Prevent OOB Items from crashing game.
|
||||
if (giEntry.itemId != ITEM_NONE)
|
||||
Item_Give(play, giEntry.itemId);
|
||||
} else {
|
||||
Randomizer_Item_Give(play, giEntry);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user