From 892d905840563ce5892fee28b27eb48068586d16 Mon Sep 17 00:00:00 2001 From: PhlexPlexico Date: Sat, 17 Feb 2024 14:31:12 -0600 Subject: [PATCH] Trade item swaps (#35) * Import GearScreen and identify value of the index update. Need to work on using L to go back in items. * Full L and R rotation of items in the deed slot now working. * Finalize trade item swap logic. TODO: Include ext item collection in itemeffect calls. * Complete work for keeping trade items over cycle and cycling through trade items. Update some fields in common data to match mm decomp. Hopefully fix bottle issues once and for all. * Formatting change. Debug changes as well. * Update bottle logic. Update mask base items to be a mask so they get mask fanfare. Update drawing items for refill items. * Minor decomp work. Find blast mask timer. * WIP: Attempt a new way of giving items from chests. * Update more bottle conditions. Update chest conditions to give items back if the user lacks them. * Bring in rough chest content matching for future purposes. * Attempt at breaking up inventory space and moving it to its own hooks. WIP: removing items if the user gives them away. * Fix item swapping and deletion of items on giving items. * Change chest function to be more reflective of what it does. * Finish adjusting Kafei letter in mail. Last to do it pendant. * Formatting fixes. * Revert mask changes as we cannot override animations on pickup just yet. --- code/Makefile | 2 +- code/include/game/actor.h | 12 +- code/include/game/actors/chest.h | 55 ++++++ code/include/game/common_data.h | 8 +- code/include/game/player.h | 3 +- code/include/game/skelanime.h | 39 +++++ code/include/game/ui.h | 27 +++ code/include/game/ui/screens/gearscreen.h | 69 ++++++++ code/include/rnd/chest.h | 21 ++- code/include/rnd/custom_screen.h | 26 +++ code/include/rnd/item_effect.h | 1 + code/include/rnd/savefile.h | 21 ++- code/mm.ld | 36 ++++ code/source/asm/hooks.s | 20 +++ code/source/asm/patches.s | 7 + code/source/asm/trade_item_hooks.s | 66 +++++++ code/source/asm/trade_item_patches.s | 41 +++++ code/source/game/ui/screens/gearscreen.cpp | 8 + code/source/main.cpp | 7 +- code/source/rnd/chest.cpp | 37 ++++ code/source/rnd/custom_screen.cpp | 189 +++++++++++++++++++++ code/source/rnd/item_effect.cpp | 32 ++++ code/source/rnd/item_override.cpp | 87 ++++++---- code/source/rnd/item_table.cpp | 93 +++++----- code/source/rnd/item_upgrade.cpp | 14 +- code/source/rnd/savefile.cpp | 82 ++++++++- 26 files changed, 888 insertions(+), 115 deletions(-) create mode 100644 code/include/game/actors/chest.h create mode 100644 code/include/game/skelanime.h create mode 100644 code/include/game/ui/screens/gearscreen.h create mode 100644 code/include/rnd/custom_screen.h create mode 100644 code/source/asm/trade_item_hooks.s create mode 100644 code/source/asm/trade_item_patches.s create mode 100644 code/source/game/ui/screens/gearscreen.cpp create mode 100644 code/source/rnd/chest.cpp create mode 100644 code/source/rnd/custom_screen.cpp diff --git a/code/Makefile b/code/Makefile index 50bd9edd..a31fc4f5 100644 --- a/code/Makefile +++ b/code/Makefile @@ -33,7 +33,7 @@ include $(DEVKITARM)/3ds_rules #--------------------------------------------------------------------------------- TARGET := $(notdir $(CURDIR)) BUILD := build -SOURCES := source source/asm source/lib source/common source/game source/rnd +SOURCES := source source/asm source/lib source/common source/game source/rnd source/game/ui/screens DATA := data INCLUDES := include GRAPHICS := gfx diff --git a/code/include/game/actor.h b/code/include/game/actor.h index bf2cff96..b5db33a2 100644 --- a/code/include/game/actor.h +++ b/code/include/game/actor.h @@ -325,14 +325,14 @@ namespace game::act { // Name courtesy of the OoT decomp project. struct DynaPolyActor : Actor { - u32 dyna_poly_id; - float field_1FC; + u32 bg_id; + float push_force; float field_200; - u16 field_204; - u8 field_206; - u32 dyna_poly_flags; + s16 y_rotation; + u32 transform_flags; + u8 interact_flags; }; - static_assert(sizeof(DynaPolyActor) == 0x20C); + static_assert(sizeof(DynaPolyActor) == 0x210); struct DayTimerActor { Actor common_actor; diff --git a/code/include/game/actors/chest.h b/code/include/game/actors/chest.h new file mode 100644 index 00000000..0c3a8db7 --- /dev/null +++ b/code/include/game/actors/chest.h @@ -0,0 +1,55 @@ +#ifndef _GAME_ACTORS_CHEST_H +#define _GAME_ACTORS_CHEST_H + +#include "game/actor.h" +#include "game/as.h" +#include "game/collision.h" +#include "game/skelanime.h" +#include "rnd/item_override.h" + +namespace game::actors { + enum class EnBoxType : u8 { + ENBOX_TYPE_BIG = 0, // E.g. Hookshot Chest + ENBOX_TYPE_BIG_ROOM_CLEAR = 1, + ENBOX_TYPE_BIG_ORNATE = 2, // E.g. Boss Key Chests + ENBOX_TYPE_BIG_SWITCH_FLAG_FALL = 3, + ENBOX_TYPE_BIG_INVISIBLE = 4, + ENBOX_TYPE_SMALL = 5, + ENBOX_TYPE_SMALL_INVISIBLE = 6, + ENBOX_TYPE_SMALL_ROOM_CLEAR = 7, + ENBOX_TYPE_SMALL_SWITCH_FLAG_FALL = 8, + ENBOX_TYPE_BIG_SONG_ZELDAS_LULLABY = 9, + ENBOX_TYPE_BIG_SONG_SUNS = 10, + ENBOX_TYPE_BIG_SWITCH_FLAG = 11, + ENBOX_TYPE_SMALL_SWITCH_FLAG = 12 + }; + + struct En_Box { + game::act::DynaPolyActor dyna; + SkelAnime* skel_anime; + u8 gap_210[52]; + CollisionInfoCylinder* collision_cylinder; + u8 gap_248[84]; + int field_29C; + u8 gap_2A0[8]; + void* some_fn; + u8 gap_2AC[312]; + u16 some_item_check; + u8 movement_flags; + u8 alpha; + u8 switch_flag; + EnBoxType chest_type; + u8 iceSmokeTImer; + s8 field_3EB; + u8 gap_3EC[16]; + void* field_3fc; + u8 gap_400[16]; + game::act::Actor* field_410; + int csId2; + rnd::GetItemID GetItemID; + s32 collectableFlag; + u8 gap_420[8]; + }; +} // namespace game::actors + +#endif \ No newline at end of file diff --git a/code/include/game/common_data.h b/code/include/game/common_data.h index c1e67186..74d11f23 100644 --- a/code/include/game/common_data.h +++ b/code/include/game/common_data.h @@ -49,7 +49,7 @@ namespace game { struct PlayerData { char field_11C[4]; u8 gap_124[2]; - u16 song_of_time_counter; // Plays song of time cutscene when == 0 + u16 three_day_reset_count; // Plays song of time cutscene when == 0 char16_t playerName[8]; u16 anonymous_h; @@ -226,13 +226,13 @@ namespace game { /// In-game time. /// 0x0000 is midnight, 0x4000 is 6am, 0x8000 is noon, 0xc000 is 6pm. u16 time; - u16 anonymous_3; + u16 jinxTimer; u16 rupee_accumulator; act::Player::Form player_form; char anonymous_5; bool has_tatl; char anonymous_7; - char anonymous_8; + char bButtonUsability; char anonymous_9; char anonymous_10; char anonymous_11; @@ -316,7 +316,7 @@ namespace game { int previous_defeated_boss; // or last viewed giant cutscene, values 4 and greater makes // woodfall giant repeat for all temples. // u8 gap1221[3]; - int anonymous_63; + int stolenItems; u8 gap1220[8]; u16 bank_rupee_count; u16 anonymous_64; diff --git a/code/include/game/player.h b/code/include/game/player.h index 0c081d84..48aae954 100644 --- a/code/include/game/player.h +++ b/code/include/game/player.h @@ -416,7 +416,8 @@ namespace game::act { u8 gap_11EC0[6]; char field_11EC6; char field_11EC7; - u16 disable_b_if_255; + // u8 field_11EC8; + u16 blastMaskTimer; u16 zora_barrier_timer; u16 field_11ECC; char field_11ECE[1]; diff --git a/code/include/game/skelanime.h b/code/include/game/skelanime.h new file mode 100644 index 00000000..84aa6d7b --- /dev/null +++ b/code/include/game/skelanime.h @@ -0,0 +1,39 @@ +#ifndef _GAME_SKELANIME_H +#define _GAME_SKELANIME_H + +#include "common/types.h" +#include "game/context.h" +#include "z3d/z3DVec.h" + +namespace game { + + struct SkelAnime { + u8 limb_count; + u8 mode; + u8 d_list_count; + s8 taper; + void** skeleton; /* An array of pointers to limbs. Can be StandardLimb, LodLimb, or SkinLimb. */ + void* animation; /* Can be an AnimationHeader or PlayerAnimationHeader. */ + float start_frame; + float end_frame; + float animation_length; + float cur_frame; + float play_speed; + z3dVec3s* joint_table; + z3dVec3s* morph_table; + float morph_weight; + float morph_rate; + union { + s32 (*normal)(struct SkelAnime*); // Can be Loop, Partial loop, Play once, Morph, or Tapered morph + s32 (*player)(struct game::GlobalContext*, struct SkelAnime*); // Loop, Play once, and Morph + } update; + s8 init_flags; + u8 move_flags; + s16 prev_yaw; + z3dVec3s prev_transl; + z3dVec3s base_transl; + }; + +} // namespace game + +#endif \ No newline at end of file diff --git a/code/include/game/ui.h b/code/include/game/ui.h index 5bd91c50..5538b1bd 100644 --- a/code/include/game/ui.h +++ b/code/include/game/ui.h @@ -438,6 +438,33 @@ namespace game::ui { }; static_assert(sizeof(Layout) == 0x170); + struct ItemIcon { + Layout thisx; + u8 initialized; + u8 gap_171[3]; + int icon_id; + int item_count; + int icon_number; + int rank_ten; + AnimPlayer* item_icon_player; + AnimPlayer* rank_ten_player; + AnimPlayer* rank_a_player; + AnimPlayer* color_player; + AnimPlayer* main_player; + Anim* idle_anim; + Anim* pressed_anim; + Anim* activated_anim; + Anim* disabled_anim; + Anim* unusable_anim; + Anim* selected_anim; + AnimPlayer* return_player; + Anim* return_to_start_anim; + Anim* return_vanish_idle_anim; + Anim* return_remain_idle_anim; + Anim* return_empty_idle_anim; + Anim* equip_anim; + void* item_icon_pane; + }; struct CommonLayouts { Layout* touch_panel; Layout* background; diff --git a/code/include/game/ui/screens/gearscreen.h b/code/include/game/ui/screens/gearscreen.h new file mode 100644 index 00000000..48954429 --- /dev/null +++ b/code/include/game/ui/screens/gearscreen.h @@ -0,0 +1,69 @@ +#include "common/utils.h" +#include "game/message.h" +#include "game/ui.h" + +#ifndef _GAME_UI_GEARSCREEN_H +#define _GAME_UI_GEARSCREEN_H + +namespace game::ui::screens { + struct GearScreen { + Screen* screen; + int press_handler; + u16 field_8; + u16 field_A; + void* subMenuTitle; + void* subMenuUpCollect; + void* menuCollect; + void* commonBottomBg; + void* btn_return_top_l; + void* btn_return; + void* btn_option_l; + void* main_player; + void* in_menu_collect_anim; + void* out_menu_collect_anim; + u8 gap_34[240]; + int field_124; + int field_128; + int field_12C; + ItemIcon* cursorItemCollect; + ItemIcon* icon_event00_l; + ItemIcon* icon_event01_l; + ItemIcon* icon_event02_l; + ItemIcon* icon_event03_l; + ItemIcon* icon_shadowgraph_box_l; + ItemIcon* icon_purse_l; + ItemIcon* icon_bomb_bag_l; + ItemIcon* icon_arrow_l; + ItemIcon* icon_sword_l; + ItemIcon* icon_shield_l; + ItemIcon* icon_boss00_l; + ItemIcon* icon_boss02_l; + ItemIcon* icon_boss01_l; + ItemIcon* icon_boss03_l; + ItemIcon* icon_schedule_l; + ItemIcon* icon_okarina_l; + ItemIcon* icon_trumpet_l; + ItemIcon* icon_drum_l; + ItemIcon* icon_guitar_l; + ItemIcon* icon_f_tickets_l; + ItemIcon* heart_pieces_l; + void* frame_deformation_player; + void* blink_picture_g; + void* small_picture_g; + u8 field_194; + u8 cursorIndex; // Possible Index For Selected Item on Gear Screen + u8 field_196; + u8 field_197; + int pressed_btn_option; + int field_19C; + int field_1A0; + u8 has_opened_option_menu; + u8 gap_1a5[3]; + }; + static_assert(sizeof(GearScreen) == 0x1A8); + static_assert(offsetof(GearScreen, press_handler) == 0x00004); + + GearScreen* GetGearScreen(); +} // namespace game::ui::screens + +#endif \ No newline at end of file diff --git a/code/include/rnd/chest.h b/code/include/rnd/chest.h index 30b30ed9..da86bea6 100644 --- a/code/include/rnd/chest.h +++ b/code/include/rnd/chest.h @@ -2,8 +2,16 @@ #define _RND_CHEST_H_ #include "common/advanced_context.h" +#include "game/actors/chest.h" #include "game/context.h" - +#include "rnd/item_override.h" +#include "rnd/settings.h" +#if defined ENABLE_DEBUG || defined DEBUG_PRINT +#include "common/debug.h" +extern "C" { +#include <3ds/svc.h> +} +#endif namespace rnd { enum class ChestSize : u8 { VANILLA_SIZE, @@ -22,10 +30,11 @@ namespace rnd { DECORATED_SMALL, }; - void EnBox_rInit(game::act::Actor* thisx, game::GlobalContext* globalCtx); - void EnBox_rUpdate(game::act::Actor* thisx, game::GlobalContext* globalCtx); - u8 Chest_OverrideAnimation(); - u8 Chest_OverrideDecoration(); - u8 Chest_OverrideIceSmoke(game::act::Actor* thisx); + extern "C" game::actors::EnBoxType Chest_OverrideSize(game::actors::En_Box*, game::GlobalContext*, s16); + // void EnBox_rInit(game::act::Actor* thisx, game::GlobalContext* globalCtx); + // void EnBox_rUpdate(game::act::Actor* thisx, game::GlobalContext* globalCtx); + // u8 Chest_OverrideAnimation(); + // u8 Chest_OverrideDecoration(); + // u8 Chest_OverrideIceSmoke(game::act::Actor* thisx); } // namespace rnd #endif //_CHEST_H_ \ No newline at end of file diff --git a/code/include/rnd/custom_screen.h b/code/include/rnd/custom_screen.h new file mode 100644 index 00000000..46938495 --- /dev/null +++ b/code/include/rnd/custom_screen.h @@ -0,0 +1,26 @@ +#ifndef _RND_CUSTOM_SCREEN_H_ +#define _RND_CUSTOM_SCREEN_H_ + +#include "common/advanced_context.h" +#include "common/types.h" +#include "game/ui/screens/gearscreen.h" +#include "rnd/savefile.h" +#if defined ENABLE_DEBUG || defined DEBUG_PRINT +#include "common/debug.h" +extern "C" { +#include <3ds/svc.h> +} +#endif + +namespace rnd { + namespace gearscreen { + extern "C" void GearScreen_GetStoredTradeItem(game::ui::screens::GearScreen*); + void GearScreen_DrawAndShowItem(game::ItemId, game::ui::Anim*, u16, int); + bool GearScreen_LoopTradeItemsForward(game::ui::Anim*, int, game::ItemId, game::ItemId, int); + bool GearScreen_LoopTradeItemsBackward(game::ui::Anim*, int, game::ItemId, game::ItemId, int); + int GearScreen_GetTextIdFromItemId(game::ItemId); + int GearScreen_GetModelIdFromItemId(game::ItemId); + } // namespace gearscreen +} // namespace rnd + +#endif \ No newline at end of file diff --git a/code/include/rnd/item_effect.h b/code/include/rnd/item_effect.h index 5afb8bf9..198d2ce0 100644 --- a/code/include/rnd/item_effect.h +++ b/code/include/rnd/item_effect.h @@ -31,6 +31,7 @@ namespace rnd { void ItemEffect_FillWalletUpgrade(game::CommonData* comData, s16 arg1, s16 arg2); void ItemEffect_GiveRemains(game::CommonData* comData, s16 mask, s16 arg2); void ItemEffect_GiveMask(game::CommonData* comData, s16 mask, s16 arg2); + void ItemEffect_GiveTradeItem(game::CommonData* comData, s16 mask, s16 arg2); } // namespace rnd #endif //_ITEM_EFFECT_H_ \ No newline at end of file diff --git a/code/include/rnd/savefile.h b/code/include/rnd/savefile.h index 8aa600cf..5b353818 100644 --- a/code/include/rnd/savefile.h +++ b/code/include/rnd/savefile.h @@ -3,11 +3,12 @@ #include "common/bitfield.h" #include "game/common_data.h" +#include "game/ui/screens/gearscreen.h" #include "rnd/extdata.h" #include "z3d/z3DVec.h" // Increment the version number whenever the ExtSaveData structure is changed -#define EXTSAVEDATA_VERSION 15 +#define EXTSAVEDATA_VERSION 16 #define SAVEFILE_SCENES_DISCOVERED_IDX_COUNT 4 #define SAVEFILE_SPOILER_ITEM_MAX 512 @@ -32,6 +33,9 @@ namespace rnd { void SaveFile_LoadExtSaveData(u32 saveNumber); u8 SaveFile_GetIsSceneDiscovered(u8 sceneNum); extern "C" void SaveFile_SaveExtSaveData(); + extern "C" void SaveFile_RemoveStoredTradeItem(u16, u8); + extern "C" void SaveFile_RemoveTradeItemFromSlot(u16, u8); + extern "C" u8 SaveFile_GetItemCurrentlyInSlot(u8); typedef struct { u32 version; // Needs to always be the first field of the structure @@ -84,7 +88,7 @@ namespace rnd { BitField<41, 1, u64> bottleGoldDustGiven; BitField<42, 1, u64> bottleSeahorseGiven; BitField<43, 1, u64> bottleChateuGiven; - BitField<44, 1, u64> bottleMysteryMilkGiven; + BitField<44, 1, u64> bottleRedPotionGiven; BitField<45, 2, u64> progressiveSwordUpgrade; BitField<46, 18, u64> unused; }; @@ -92,12 +96,12 @@ namespace rnd { union FairyCollectRegister { u8 raw; - BitField<0, 1, u8> nct; - BitField<1, 1, u8> woodfall; - BitField<2, 1, u8> snowhead; - BitField<3, 1, u8> great_bay; - BitField<4, 1, u8> ikana; - BitField<5, 3, u8> unused; + BitField<0, 2, u8> nct; + BitField<2, 1, u8> woodfall; + BitField<3, 1, u8> snowhead; + BitField<4, 1, u8> great_bay; + BitField<5, 1, u8> ikana; + BitField<6, 2, u8> unused; }; FairyCollectRegister fairyRewards; union TingleCollectRegister { @@ -115,6 +119,7 @@ namespace rnd { u32 scenesDiscovered[SAVEFILE_SCENES_DISCOVERED_IDX_COUNT]; u8 itemCollected[SAVEFILE_SPOILER_ITEM_MAX]; u8 chestRewarded[116][32]; // Reward table that's stored by scene and chest param/flag. + game::ItemId collectedTradeItems[9]; } ExtSaveData; extern "C" ExtSaveData gExtSaveData; diff --git a/code/mm.ld b/code/mm.ld index 583b803e..fdb664ea 100644 --- a/code/mm.ld +++ b/code/mm.ld @@ -55,6 +55,10 @@ SECTIONS{ *(.patch_KeepBowOnEpona) } + .patch_RemoveTradeItemFromSlot 0x1AFE8C : { + *(.patch_RemoveTradeItemFromSlot) + } + .patch_OverrideCutsceneNextEntrance 0x1B1834 : { *(.patch_OverrideCutsceneNextEntrance) } @@ -67,6 +71,10 @@ SECTIONS{ *(.patch_DoNotResetTempleFlags) } + .patch_DoNotRemoveTradeItems 0x1c9aa8 : { + *(.patch_DoNotRemoveTradeItems) + } + .patch_DoNotRemoveKeys 0x1C9B94 : { *(.patch_DoNotRemoveKeys) } @@ -343,6 +351,10 @@ SECTIONS{ *(.patch_IceArrowsAnywhere) } + .patch_changeChestTypeToMatchContents 0x31cad4 : { + *(.patch_changeChestTypeToMatchContents) + } + .patch_RemoveZoraMaskCheckMikau 0x32BBB8 : { *(.patch_RemoveZoraMaskCheckMikau) } @@ -355,6 +367,10 @@ SECTIONS{ *(.patch_CheckMoTRequirement) } + .patch_TradeItemDeleteMoonsTear 0x3B5214 : { + *(.patch_TradeItemDeleteMoonsTear) + } + .patch_OverrideWalletSpiderHouseTwo 0x3BF078 : { *(.patch_OverrideWalletSpiderHouseTwo) } @@ -395,10 +411,26 @@ SECTIONS{ *(.patch_CouplesMaskGiveItem) } + .patch_TradeItemDeleteLandTitleDeed 0x477710 : { + *(.patch_TradeItemDeleteLandTitleDeed) + } + + .patch_TradeItemDeleteSwampTitleDeed 0x47771C : { + *(.patch_TradeItemDeleteSwampTitleDeed) + } + + .patch_TradeItemDeleteMountainTitleDeed 0x477728 : { + *(.patch_TradeItemDeleteMountainTitleDeed) + } + .patch_LoadExtData 0x48C760 : { *(.patch_LoadExtData) } + .patch_RemoveKafeiItemFromExtSlot 0x4AD1B8 : { + *(.patch_RemoveKafeiItemFromExtSlot) + } + .patch_IncomingGetItemID 0x4B1394 : { *(.patch_IncomingGetItemID) } @@ -415,6 +447,10 @@ SECTIONS{ *(.patch_DisableExistingTrigger) } + .patch_SwapStoredTradeItems 0x5a6b44 : { + *(.patch_SwapStoredTradeItems) + } + .patch_ShorterAnimationOpen 0x5B1758 : { *(.patch_ShorterAnimationOpenOne) } diff --git a/code/source/asm/hooks.s b/code/source/asm/hooks.s index 41fa7943..8e030624 100644 --- a/code/source/asm/hooks.s +++ b/code/source/asm/hooks.s @@ -347,6 +347,26 @@ hook_OwlExtDataSave: cpy r6,r0 b 0x317008 +.global hook_changeChestTypeToMatchContents +hook_changeChestTypeToMatchContents: + push {r0-r2, lr} + cpy r0, r4 + cpy r1, r5 + ldrh r2,[r4,#0x1C] + lsl r2, r2, #0x14 + lsr r2,r2, #0x19 + bl Chest_OverrideSize + cmp r0,#0xFF + beq doNotOverrideChestType + strb r0,[r4,#0x3e9] + pop {r0-r2, lr} + bx lr +doNotOverrideChestType: + pop {r0-r2, lr} + strb r2,[r4,#0x3e9] + bx lr + + .global hook_MikauRewardCheck hook_MikauRewardCheck: push {r0-r12, lr} diff --git a/code/source/asm/patches.s b/code/source/asm/patches.s index 4806f978..bb1ca6cf 100644 --- a/code/source/asm/patches.s +++ b/code/source/asm/patches.s @@ -104,6 +104,7 @@ patch_RemoveMysteryMilkTimer: patch_DoNotResetTempleFlags: bl hook_DoNotResetTempleFlags + @ Skips past a loop that resets all @ values in the each dungeon for @ keys/fairies/boss key/etc @@ -379,6 +380,12 @@ patch_SaveExtDataOnOwl: patch_IceArrowsAnywhere: nop +.section .patch_changeChestTypeToMatchContents +.global patch_changeChestTypeToMatchContents +patch_changeChestTypeToMatchContents: + bl hook_changeChestTypeToMatchContents + @mov r2, #0x02 + .section .patch_RemoveZoraMaskCheckMikau .global patch_RemoveZoraMaskCheckMikau patch_RemoveZoraMaskCheckMikau: diff --git a/code/source/asm/trade_item_hooks.s b/code/source/asm/trade_item_hooks.s new file mode 100644 index 00000000..e4ad598a --- /dev/null +++ b/code/source/asm/trade_item_hooks.s @@ -0,0 +1,66 @@ +.arm +.text + +.global hook_RemoveTradeItemFromInventory +hook_RemoveTradeItemFromInventory: + push {r0-r12,lr} + cpy r0,r2 + mov r1,#0x5 + sub r0,r0,#0x1 + bl SaveFile_RemoveStoredTradeItem + pop {r0-r12,lr} + beq 0x477748 + bx lr + +.global hook_RemoveMoonTearFromInventory +hook_RemoveMoonTearFromInventory: + push {r0-r12,lr} + mov r2, #0x97 + cpy r0,r2 + mov r1,#0x5 + sub r0,r0,#0x1 + bl SaveFile_RemoveStoredTradeItem + pop {r0-r12,lr} + cpy r5,r1 + bx lr + +.global hook_RemoveTradeItemFromExtSlot +hook_RemoveTradeItemFromExtSlot: + push {r0-r12, lr} + bl SaveFile_RemoveTradeItemFromSlot + pop {r0-r12, lr} + cpy r2,r1 + bx lr + +.global hook_RemoveKafeiItemFromExtSlot +hook_RemoveKafeiItemFromExtSlot: + push {r0-r12, lr} + bl SaveFile_GetItemCurrentlyInSlot + cmp r0,#0x2F + beq changeLetter + cmp r0,#0x30 + beq changePendant + pop {r0-r12, lr} + cpy r5,r0 + bx lr +changeLetter: + mov r0,#0xAA + mov r1,#0x11 + bl SaveFile_RemoveStoredTradeItem + pop {r0-r12,lr} + b 0x4AD1C4 +changePendant: + mov r0,#0xAB + mov r1,#0x11 + bl SaveFile_RemoveStoredTradeItem + pop {r0-r12,lr} + b 0x4AD1C4 + + +.global hook_SwapStoredTradeItems +hook_SwapStoredTradeItems: + push {r0-r12, lr} + bl GearScreen_GetStoredTradeItem + pop {r0-r12, lr} + cpy r4, r0 + bx lr \ No newline at end of file diff --git a/code/source/asm/trade_item_patches.s b/code/source/asm/trade_item_patches.s new file mode 100644 index 00000000..292acef4 --- /dev/null +++ b/code/source/asm/trade_item_patches.s @@ -0,0 +1,41 @@ +.arm + +.section .patch_RemoveTradeItemFromSlot +.global patch_RemoveTradeItemFromSlot +patch_RemoveTradeItemFromSlot: + bleq hook_RemoveTradeItemFromExtSlot + +.section .patch_DoNotRemoveTradeItems +.global patch_DoNotRemoveTradeItems +patch_DoNotRemoveTradeItems: + b 0x1C9AD0 + +.section .patch_TradeItemDeleteMoonsTear +.global patch_TradeItemDeleteMoonsTear +patch_TradeItemDeleteMoonsTear: + bl hook_RemoveMoonTearFromInventory + +.section .patch_TradeItemDeleteLandTitleDeed +.global patch_TradeItemDeleteLandTitleDeed +patch_TradeItemDeleteLandTitleDeed: + bleq hook_RemoveTradeItemFromInventory + +.section .patch_TradeItemDeleteSwampTitleDeed +.global patch_TradeItemDeleteSwampTitleDeed +patch_TradeItemDeleteSwampTitleDeed: + bleq hook_RemoveTradeItemFromInventory + +.section .patch_TradeItemDeleteMountainTitleDeed +.global patch_TradeItemDeleteMountainTitleDeed +patch_TradeItemDeleteMountainTitleDeed: + bleq hook_RemoveTradeItemFromInventory + +.section .patch_RemoveKafeiItemFromExtSlot +.global patch_RemoveKafeiItemFromExtSlot +patch_RemoveKafeiItemFromExtSlot: + bl hook_RemoveKafeiItemFromExtSlot + +.section .patch_SwapStoredTradeItems +.global patch_SwapStoredTradeItems +patch_SwapStoredTradeItems: + bl hook_SwapStoredTradeItems \ No newline at end of file diff --git a/code/source/game/ui/screens/gearscreen.cpp b/code/source/game/ui/screens/gearscreen.cpp new file mode 100644 index 00000000..3362502d --- /dev/null +++ b/code/source/game/ui/screens/gearscreen.cpp @@ -0,0 +1,8 @@ +#include "game/ui/screens/gearscreen.h" + +namespace game::ui::screens { + GearScreen* GetGearScreen() { + return rnd::util::GetPointer(0x6F4130); + } + +} // namespace game::ui::screens \ No newline at end of file diff --git a/code/source/main.cpp b/code/source/main.cpp index ac69d41b..cfa40cd8 100644 --- a/code/source/main.cpp +++ b/code/source/main.cpp @@ -6,6 +6,7 @@ #include "game/sound.h" #include "game/states/state.h" #include "game/ui.h" +#include "game/ui/screens/gearscreen.h" #include "rnd/extdata.h" #include "rnd/icetrap.h" #include "rnd/input.h" @@ -99,11 +100,7 @@ namespace rnd { const u32 pressedButtons = gctx->pad_state.input.buttons.flags; const u32 newButtons = gctx->pad_state.input.new_buttons.flags; -#if defined ENABLE_DEBUG || defined DEBUG_PRINT - auto* saveData = GetContext().gctx->GetPlayerActor(); - if (newButtons == (u32)game::pad::Button::ZR) - rnd::util::Print("%s: Flag is %#08x\n", __func__, saveData->flags1); -#endif + if (gSettingsContext.customMaskButton != 0 && pressedButtons == gSettingsContext.customMaskButton) { game::ui::OpenScreen(game::ui::ScreenType::Masks); } else if (gSettingsContext.customItemButton != 0 && pressedButtons == gSettingsContext.customItemButton) { diff --git a/code/source/rnd/chest.cpp b/code/source/rnd/chest.cpp new file mode 100644 index 00000000..8b8e7dae --- /dev/null +++ b/code/source/rnd/chest.cpp @@ -0,0 +1,37 @@ +#include "rnd/chest.h" +#include "rnd/item_table.h" + +namespace rnd { + extern "C" { + game::actors::EnBoxType Chest_OverrideSize(game::actors::En_Box* actor, game::GlobalContext* gctx, s16 gid) { + // First check to see if setting is enabled. + // TODO: Create setting + if (gSettingsContext.chestSize == 0) { + return (game::actors::EnBoxType)0xFF; + } + game::SceneId scene = gctx->scene; + ItemOverride override = ItemOverride_Lookup((game::act::Actor*)&actor->dyna, (u16)scene, gid); + if (override.key.all != 0) { + ItemRow* itemToBeGiven = ItemTable_GetItemRow(override.value.getItemId); + if (actor->chest_type == game::actors::EnBoxType::ENBOX_TYPE_SMALL || + actor->chest_type == game::actors::EnBoxType::ENBOX_TYPE_BIG || + actor->chest_type == game::actors::EnBoxType::ENBOX_TYPE_BIG_ORNATE) { + if (itemToBeGiven->baseItemId == 0x02) + return game::actors::EnBoxType::ENBOX_TYPE_SMALL; + else if (itemToBeGiven->baseItemId == 0x2B || itemToBeGiven->baseItemId == 0x78) + return game::actors::EnBoxType::ENBOX_TYPE_BIG; + } else if (actor->chest_type == game::actors::EnBoxType::ENBOX_TYPE_BIG_INVISIBLE || + actor->chest_type == game::actors::EnBoxType::ENBOX_TYPE_SMALL_INVISIBLE) { + if (itemToBeGiven->baseItemId == 0x02) + return game::actors::EnBoxType::ENBOX_TYPE_SMALL_INVISIBLE; + else if (itemToBeGiven->baseItemId == 0x2B || itemToBeGiven->baseItemId == 0x78) + return game::actors::EnBoxType::ENBOX_TYPE_BIG_INVISIBLE; + } + } else { + return (game::actors::EnBoxType)0xFF; + } + return (game::actors::EnBoxType)0xFF; + } + } + +} // namespace rnd \ No newline at end of file diff --git a/code/source/rnd/custom_screen.cpp b/code/source/rnd/custom_screen.cpp new file mode 100644 index 00000000..49e4785d --- /dev/null +++ b/code/source/rnd/custom_screen.cpp @@ -0,0 +1,189 @@ +#include "rnd/custom_screen.h" + +namespace rnd { + + namespace gearscreen { + + extern "C" void GearScreen_GetStoredTradeItem(game::ui::screens::GearScreen* gearScreen) { + auto* gctx = rnd::GetContext().gctx; + auto& items = game::GetCommonData().save.inventory.items; + game::ItemId firstFoundItem = game::ItemId::None; + const u32 newButtons = gctx->pad_state.input.new_buttons.flags; + if (gearScreen->cursorIndex == 0) { + // Sixth Slot [5] of Inventory array is the Title deed slot. + if (newButtons == (u32)game::pad::Button::R) { + // Check what is in current slot. + // After receiving value, check array from 0-4 to see what items we have obtained. + // We always know the order of the array, MoonsTear -> Land Title -> Swamp -> Mountain -> Ocean + for (int i = 0; i < 5; i++) { + if (firstFoundItem == game::ItemId::None) { + firstFoundItem = gExtSaveData.collectedTradeItems[i]; + } + if (GearScreen_LoopTradeItemsForward(gearScreen->icon_event00_l->return_empty_idle_anim, i, firstFoundItem, + items[5], 4)) + break; + } + } else if (newButtons == (u32)game::pad::Button::L) { + for (int i = 4; i >= 0; i--) { + if (firstFoundItem == game::ItemId::None) { + firstFoundItem = gExtSaveData.collectedTradeItems[i]; + } + if (GearScreen_LoopTradeItemsBackward(gearScreen->icon_event00_l->return_empty_idle_anim, i, firstFoundItem, + items[5], 0)) + break; + } + } + } else if (gearScreen->cursorIndex == 1) { + // 18 [17] is letter to kafei/mama. + if (newButtons == (u32)game::pad::Button::R) { + // Check what is in current slot. + // After receiving value, check array from 0-4 to see what items we have obtained. + // We always know the order of the array, MoonsTear -> Land Title -> Swamp -> Mountain -> Ocean + for (int i = 6; i < 8; i++) { + if (firstFoundItem == game::ItemId::None) { + firstFoundItem = gExtSaveData.collectedTradeItems[i]; + } + if (GearScreen_LoopTradeItemsForward(gearScreen->icon_event01_l->return_empty_idle_anim, i, firstFoundItem, + items[17], 7)) + break; + } + } else if (newButtons == (u32)game::pad::Button::L) { + for (int i = 7; i > 5; i--) { + if (firstFoundItem == game::ItemId::None) { + firstFoundItem = gExtSaveData.collectedTradeItems[i]; + } + if (GearScreen_LoopTradeItemsBackward(gearScreen->icon_event01_l->return_empty_idle_anim, i, firstFoundItem, + items[17], 4)) + break; + } + } + } + return; + } + + bool GearScreen_LoopTradeItemsForward(game::ui::Anim* gearScreenIdleAnim, int i, game::ItemId firstFoundItem, + game::ItemId itemInSlot, int lastItemSlot) { + int textId; + int modelId; + + // Base case, if we have a none item, give the first available item. + if (itemInSlot == game::ItemId::None && gExtSaveData.collectedTradeItems[i] != game::ItemId::None) { + textId = GearScreen_GetTextIdFromItemId(gExtSaveData.collectedTradeItems[i]); + modelId = GearScreen_GetModelIdFromItemId(gExtSaveData.collectedTradeItems[i]); + GearScreen_DrawAndShowItem(gExtSaveData.collectedTradeItems[i], gearScreenIdleAnim, textId, modelId); + return true; + } + // Second case - we're not the last element and we contain an item, then swap that item. + if (i != lastItemSlot && gExtSaveData.collectedTradeItems[i] != game::ItemId::None && + (int)itemInSlot < (int)gExtSaveData.collectedTradeItems[i]) { + textId = GearScreen_GetTextIdFromItemId(gExtSaveData.collectedTradeItems[i]); + modelId = GearScreen_GetModelIdFromItemId(gExtSaveData.collectedTradeItems[i]); + GearScreen_DrawAndShowItem(gExtSaveData.collectedTradeItems[i], gearScreenIdleAnim, textId, modelId); + return true; + } else if (i == lastItemSlot && gExtSaveData.collectedTradeItems[i] != game::ItemId::None && + gExtSaveData.collectedTradeItems[i] != itemInSlot) { + textId = GearScreen_GetTextIdFromItemId(gExtSaveData.collectedTradeItems[i]); + modelId = GearScreen_GetModelIdFromItemId(gExtSaveData.collectedTradeItems[i]); + GearScreen_DrawAndShowItem(gExtSaveData.collectedTradeItems[i], gearScreenIdleAnim, textId, modelId); + return true; + } else if (i == lastItemSlot && firstFoundItem != game::ItemId::None) { + textId = GearScreen_GetTextIdFromItemId(firstFoundItem); + modelId = GearScreen_GetModelIdFromItemId(firstFoundItem); + GearScreen_DrawAndShowItem(firstFoundItem, gearScreenIdleAnim, textId, modelId); + return true; + } + return false; + } + + bool GearScreen_LoopTradeItemsBackward(game::ui::Anim* gearScreenIdleAnim, int i, game::ItemId firstFoundItem, + game::ItemId itemInSlot, int lastItemSlot) { + int textId; + int modelId; + + // Base case, if we have a none item, give the first available item. + if (itemInSlot == game::ItemId::None && gExtSaveData.collectedTradeItems[i] != game::ItemId::None) { + textId = GearScreen_GetTextIdFromItemId(gExtSaveData.collectedTradeItems[i]); + modelId = GearScreen_GetModelIdFromItemId(gExtSaveData.collectedTradeItems[i]); + GearScreen_DrawAndShowItem(gExtSaveData.collectedTradeItems[i], gearScreenIdleAnim, textId, modelId); + return true; + } + // Second case - we're not the last element and we contain an item, then swap that item. + if (i != lastItemSlot && gExtSaveData.collectedTradeItems[i] != game::ItemId::None && + (int)itemInSlot > (int)gExtSaveData.collectedTradeItems[i]) { + textId = GearScreen_GetTextIdFromItemId(gExtSaveData.collectedTradeItems[i]); + modelId = GearScreen_GetModelIdFromItemId(gExtSaveData.collectedTradeItems[i]); + GearScreen_DrawAndShowItem(gExtSaveData.collectedTradeItems[i], gearScreenIdleAnim, textId, modelId); + return true; + } else if (i == lastItemSlot && gExtSaveData.collectedTradeItems[i] != game::ItemId::None && + gExtSaveData.collectedTradeItems[i] != itemInSlot) { + textId = GearScreen_GetTextIdFromItemId(gExtSaveData.collectedTradeItems[i]); + modelId = GearScreen_GetModelIdFromItemId(gExtSaveData.collectedTradeItems[i]); + GearScreen_DrawAndShowItem(gExtSaveData.collectedTradeItems[i], gearScreenIdleAnim, textId, modelId); + return true; + } else if (i == lastItemSlot && firstFoundItem != game::ItemId::None) { + textId = GearScreen_GetTextIdFromItemId(firstFoundItem); + modelId = GearScreen_GetModelIdFromItemId(firstFoundItem); + GearScreen_DrawAndShowItem(firstFoundItem, gearScreenIdleAnim, textId, modelId); + return true; + } + return false; + } + + int GearScreen_GetTextIdFromItemId(game::ItemId itemId) { + // Since we're limited on what items we got, just hard code these. + if (itemId == game::ItemId::MoonTear) + return 0x1728; + else if (itemId == game::ItemId::LandTitleDeed) + return 0x1729; + else if (itemId == game::ItemId::SwampTitleDeed) + return 0x172A; + else if (itemId == game::ItemId::MountainTitleDeed) + return 0x172B; + else if (itemId == game::ItemId::OceanTitleDeed) + return 0x172C; + else if (itemId == game::ItemId::LetterToKafei) + return 0x172F; + else if (itemId == game::ItemId::PendantOfMemories) + return 0x1730; + return 0x00; + } + + int GearScreen_GetModelIdFromItemId(game::ItemId itemId) { + // Since we're limited on what items we got, just hard code these. + if (itemId == game::ItemId::MoonTear) + return 0x59; + else if (itemId == game::ItemId::LandTitleDeed) + return 0x5A; + else if (itemId == game::ItemId::SwampTitleDeed) + return 0x40; + else if (itemId == game::ItemId::MountainTitleDeed) + return 0x41; + else if (itemId == game::ItemId::OceanTitleDeed) + return 0x43; + else if (itemId == game::ItemId::LetterToKafei) + return 0x6D; + else if (itemId == game::ItemId::PendantOfMemories) + return 0x80; + return 0x00; + } + + void GearScreen_DrawAndShowItem(game::ItemId itemId, game::ui::Anim* emptyIdleAnim, u16 textId, int modelId) { + game::MessageMgr mgr = game::MessageMgr::Instance(); + game::ui::ScreenContext& sctx = game::ui::GetScreenContext(); + + game::GiveItem(itemId); + + int iconId = util::GetPointer(0x601E18)(itemId); + + util::GetPointer(0x601a1c)(emptyIdleAnim, iconId); + util::GetPointer(0x1ccfe4)(mgr.message_window, textId, 0); + + int modelType = util::GetPointer(0x6120e4)(emptyIdleAnim); + modelType = modelType + 0x1700; + util::GetPointer(0x5a1aac)(sctx.ctx, modelId); + game::sound::PlayEffect(game::sound::EffectId::NA_SE_SY_CURSOR); + return; + } + } // namespace gearscreen + +} // namespace rnd \ No newline at end of file diff --git a/code/source/rnd/item_effect.cpp b/code/source/rnd/item_effect.cpp index 5e8b19d9..9277b496 100644 --- a/code/source/rnd/item_effect.cpp +++ b/code/source/rnd/item_effect.cpp @@ -348,4 +348,36 @@ namespace rnd { rnd::util::GetPointer(0x222BCC)(rnd::GetContext().gctx, 0); } + void ItemEffect_GiveTradeItem(game::CommonData* comData, s16 mask, s16 arg2) { + switch (mask) { + case 0: // Moon's Tear + gExtSaveData.collectedTradeItems[0] = game::ItemId::MoonTear; + break; + case 1: // Town Title Deed + gExtSaveData.collectedTradeItems[1] = game::ItemId::LandTitleDeed; + break; + case 2: // Swamp Title Deed + gExtSaveData.collectedTradeItems[2] = game::ItemId::SwampTitleDeed; + break; + case 3: // Mtn Title Deed + gExtSaveData.collectedTradeItems[3] = game::ItemId::MountainTitleDeed; + break; + case 4: // Ocean Title Deed + gExtSaveData.collectedTradeItems[4] = game::ItemId::OceanTitleDeed; + break; + case 5: // Room Key + gExtSaveData.collectedTradeItems[5] = game::ItemId::RoomKey; + break; + case 6: // Letter To Kafei + gExtSaveData.collectedTradeItems[6] = game::ItemId::LetterToKafei; + break; + case 7: // Pendant + gExtSaveData.collectedTradeItems[7] = game::ItemId::PendantOfMemories; + break; + case 8: // Letter To Mama + gExtSaveData.collectedTradeItems[8] = game::ItemId::LetterToMama; + break; + } + } + } // namespace rnd \ No newline at end of file diff --git a/code/source/rnd/item_override.cpp b/code/source/rnd/item_override.cpp index facdb86e..90d8aae5 100644 --- a/code/source/rnd/item_override.cpp +++ b/code/source/rnd/item_override.cpp @@ -44,9 +44,9 @@ namespace rnd { rItemOverrides[0].value.getItemId = 0x26; rItemOverrides[0].value.looksLikeItemId = 0x26; rItemOverrides[1].key.scene = 0x6F; - rItemOverrides[1].key.type = ItemOverride_Type::OVR_CHEST; - rItemOverrides[1].value.getItemId = 0x32; - rItemOverrides[1].value.looksLikeItemId = 0x32; + rItemOverrides[1].key.type = ItemOverride_Type::OVR_COLLECTABLE; + rItemOverrides[1].value.getItemId = 0x01; + rItemOverrides[1].value.looksLikeItemId = 0x01; rItemOverrides[2].key.scene = 0x12; rItemOverrides[2].key.type = ItemOverride_Type::OVR_COLLECTABLE; rItemOverrides[2].value.getItemId = 0x37; @@ -472,7 +472,7 @@ namespace rnd { gExtSaveData.givenItemChecks.enOshGivenItem = 1; } else if (storedGetItemId == GetItemID::GI_POWDER_KEG) { gExtSaveData.givenItemChecks.enGoGivenItem = 1; - } else if ((s16)storedGetItemId == -(s16)GetItemID::GI_MASK_GIANTS) { + } else if (storedGetItemId == GetItemID::GI_MASK_GIANTS) { gExtSaveData.givenItemChecks.enBoss02GivenItem = 1; } else if (storedActorId == game::act::Id::EnGinkoMan) { if (gExtSaveData.givenItemChecks.enGinkoManGivenItem == 0) { @@ -494,18 +494,12 @@ namespace rnd { gExtSaveData.givenItemChecks.enOcnDeedGivenItem = 1; } else if (storedGetItemId == GetItemID::GI_BOTTLE_MILK) { gExtSaveData.givenItemChecks.bottleMilkGiven = 1; - } else if (storedGetItemId == GetItemID::GI_BOTTLE_GOLD_DUST || - (s16)storedGetItemId == -(s16)GetItemID::GI_BOTTLE_GOLD_DUST) { + } else if (storedGetItemId == GetItemID::GI_BOTTLE_GOLD_DUST) { gExtSaveData.givenItemChecks.bottleGoldDustGiven = 1; - } else if (storedGetItemId == GetItemID::GI_BOTTLE_CHATEAU_ROMANI || - (s16)storedGetItemId == -(s16)GetItemID::GI_BOTTLE_CHATEAU_ROMANI) { + } else if (storedGetItemId == GetItemID::GI_BOTTLE_CHATEAU_ROMANI) { gExtSaveData.givenItemChecks.bottleChateuGiven = 1; - } else if (storedGetItemId == GetItemID::GI_BOTTLE_SEAHORSE || - (s16)storedGetItemId == -(s16)GetItemID::GI_BOTTLE_SEAHORSE) { + } else if (storedGetItemId == GetItemID::GI_BOTTLE_SEAHORSE) { gExtSaveData.givenItemChecks.bottleSeahorseGiven = 1; - } else if (storedGetItemId == GetItemID::GI_BOTTLE_MYSTERY_MILK || - (s16)storedGetItemId == -(s16)GetItemID::GI_BOTTLE_MYSTERY_MILK) { - gExtSaveData.givenItemChecks.bottleMysteryMilkGiven = 1; } } @@ -613,12 +607,14 @@ namespace rnd { void ItemOverride_GetItemTextAndItemID(game::act::Player* actor) { if (rActiveItemRow != NULL) { if (rActiveItemOverride.key.type == ItemOverride_Type::OVR_CHEST) { - // Check and see if we have trade items or repeatable bottles and add to the array. - if (rActiveItemRow->itemId < 0x28 || (rActiveItemRow->itemId > 0x30 && rActiveItemRow->itemId < 0x9f)) { - // XXX: Hacky fix but maybe we need to redo how we track chests. Mark Giant's Mask Chest - // to be repeatably obtainable since we're not extending this array to 126 in the second dimension. - if (rActiveItemOverride.key.flag < 0x20) - gExtSaveData.chestRewarded[rActiveItemOverride.key.scene][rActiveItemOverride.key.flag] = 1; +// Check and see if we have trade items or repeatable bottles and add to the array. +#if defined ENABLE_DEBUG || defined DEBUG_PRINT + rnd::util::Print("%s: Active item row item ID is %#04x and key flag is %#04x\n", __func__, + rActiveItemRow->itemId, rActiveItemOverride.key.flag); +#endif + // Only set if we're not a trade item or bottled item. + if ((rActiveItemRow->itemId < 0x12 || rActiveItemRow->itemId > 0x30) && (rActiveItemRow->itemId < 0x9F)) { + gExtSaveData.chestRewarded[rActiveItemOverride.key.scene][rActiveItemOverride.key.flag] = 1; } } game::GlobalContext* gctx = GetContext().gctx; @@ -650,8 +646,15 @@ namespace rnd { if (fromActor != NULL && incomingGetItemId != 0) { s16 getItemId = ItemOverride_CheckNpc(fromActor->id, incomingGetItemId, incomingNegative); storedActorId = fromActor->id; - storedGetItemId = (GetItemID)incomingGetItemId; + storedGetItemId = incomingNegative ? (GetItemID)-incomingGetItemId : (GetItemID)incomingGetItemId; override = ItemOverride_Lookup(fromActor, (u16)gctx->scene, getItemId); + if (override.key.all != 0) { + // Override the stored get item if we are a bottled item. + if (override.value.getItemId == 0x59 || override.value.getItemId == 0x60 || override.value.getItemId == 0x6A || + override.value.getItemId == 0x6E || override.value.getItemId == 0x6F || override.value.getItemId == 0x70) { + storedGetItemId = (GetItemID) override.value.getItemId; + } + } } if (override.key.all == 0) { // No override, use base game's item code @@ -659,14 +662,18 @@ namespace rnd { player->get_item_id = incomingGetItemId; return; } else if (override.key.type == ItemOverride_Type::OVR_CHEST && - gExtSaveData.chestRewarded[override.key.scene][override.key.flag] == 1 && - (override.value.getItemId != 0x60 || override.value.getItemId != 0x6A || - (override.value.getItemId < 0x6E && override.value.getItemId > 0x70))) { - // Override was already given, check to see if we're a refill item now, if not, give a blue rupee instead. + gExtSaveData.chestRewarded[override.key.scene][override.key.flag] == 1) { + // Override was already given, check to see if the item exists in inventory, if it does + // then we give a blue rupee. Only check for inventory items. If an item is a heart piece + // do not give multiples. // Only do this for items that are not bottle refills. - // Bottle logic is taken care of in the ItemUpgrade function. - override.value.getItemId = 0x02; - override.value.looksLikeItemId = 0x02; + // Bottle logic is taken care of in the ItemUpgrade function for each bottle. + ItemRow* itemToBeGiven = ItemTable_GetItemRow(override.value.getItemId); + if (game::HasMask((game::ItemId)itemToBeGiven->itemId) || game::HasItem((game::ItemId)itemToBeGiven->itemId) || + itemToBeGiven->itemId > 0x49) { + override.value.getItemId = 0x02; + override.value.looksLikeItemId = 0x02; + } } // This check is mainly to ensure we do not have repeatable progressive items within these base items. @@ -722,10 +729,16 @@ namespace rnd { void ItemOverride_GetFairyRewardItem(game::GlobalContext* gctx, game::act::GreatFairy* fromActor, s16 incomingItemId) { int fairyEntrance = game::GetCommonData().sub1.entrance; - if (fairyEntrance == 0x4600 && gExtSaveData.fairyRewards.nct != 1) { - gExtSaveData.fairyRewards.nct = 1; - ItemOverride_PushPendingFairyRewardItem(gctx, fromActor, 0x86); - ItemOverride_PushPendingFairyRewardItem(gctx, fromActor, 0x0E); + if (fairyEntrance == 0x4600) { + if (gExtSaveData.fairyRewards.nct == 0) { + ItemOverride_PushPendingFairyRewardItem(gctx, fromActor, 0x0E); + gExtSaveData.fairyRewards.nct = 1; + return; + } else if (gExtSaveData.fairyRewards.nct == 1 && game::HasMask(game::ItemId::DekuMask)) { + ItemOverride_PushPendingFairyRewardItem(gctx, fromActor, 0x86); + gExtSaveData.fairyRewards.nct = 2; + return; + } return; } else if (fairyEntrance == 0x4610 && gExtSaveData.fairyRewards.woodfall != 1) { gExtSaveData.fairyRewards.woodfall = 1; @@ -747,6 +760,11 @@ namespace rnd { } void ItemOverride_GetSoHItem(game::GlobalContext* gctx, game::act::Actor* fromActor, s16 incomingItemId) { + game::act::Player* link = gctx->GetPlayerActor(); + // Run only once. Once the get item is assigned, we shouldn't have to worry about running it again. + // This is mainly prevalent when the item override is in a calc function (Anju). + if (link->get_item_id != 0x00) + return; if (incomingItemId == 0x7A) { gExtSaveData.givenItemChecks.enZogGivenItem = 1; } else if (incomingItemId == 0x79) { @@ -760,6 +778,7 @@ namespace rnd { } else if (incomingItemId == 0x85) { gExtSaveData.givenItemChecks.kafeiGivenItem = 1; } + ItemOverride_GetItem(gctx, fromActor, gctx->GetPlayerActor(), incomingItemId); return; } @@ -917,5 +936,11 @@ namespace rnd { u32 ItemOverride_GetOshExtData() { return (u32)gExtSaveData.givenItemChecks.enOshGivenItem; } + + void DebugStatement(game::ItemId curItemSlot) { +#if defined ENABLE_DEBUG || defined DEBUG_PRINT + rnd::util::Print("%s: Current item slot is %#04x\n", __func__, curItemSlot); +#endif + } } } // namespace rnd diff --git a/code/source/rnd/item_table.cpp b/code/source/rnd/item_table.cpp index 3c4a8f0a..0e250915 100644 --- a/code/source/rnd/item_table.cpp +++ b/code/source/rnd/item_table.cpp @@ -472,7 +472,7 @@ namespace rnd { [0x59] = ITEM_ROW((u32)GetItemID::GI_NUTS_30, ChestType::WOODEN_BIG, (u8)game::ItemId::HookshotUnused, 0x0059, 0x00196, - (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s32)DrawGraphicItemID::DI_RED_POTION, + (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s32)DrawGraphicItemID::DI_BOTTLE_RED_POTION, (rnd::upgradeFunc)ItemUpgrade_None, ItemEffect_None, (s16)3, (s16)-1), // Red Potion? @@ -552,7 +552,7 @@ namespace rnd { (rnd::upgradeFunc)ItemUpgrade_None, ItemEffect_None, (s16)-1, (s16)-1), // Bottle With Zora Egg [0x6A] = ITEM_ROW((u32)GetItemID::GI_NUTS_30, ChestType::WOODEN_BIG, (u8)game::ItemId::GoldDust, 0x006A, 0x01E9, - (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s32)DrawGraphicItemID::DI_GOLD_DUST, + (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s32)DrawGraphicItemID::DI_BOTTLE_GOLD_DUST, (rnd::upgradeFunc)ItemUpgrade_RefillBottle, ItemEffect_None, (s16)-1, (s16)-1), // Bottle With Gold Dust @@ -747,11 +747,10 @@ namespace rnd { (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s32)DrawGraphicItemID::DI_BOTTLE_MILK, (rnd::upgradeFunc)ItemUpgrade_None, ItemEffect_None, (s16)-1, (s16)-1), // Milk Refill - [0x93] = - ITEM_ROW((u32)GetItemID::GI_NUTS_30, ChestType::WOODEN_BIG, (u8)game::ItemId::GoldDustFill, 0x0093, 0x001E8, - (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s32)DrawGraphicItemID::DI_BOTTLE_GOLD_DUST, - (rnd::upgradeFunc)ItemUpgrade_None, ItemEffect_None, (s16)-1, - (s16)-1), // Gold Dust Refill + [0x93] = ITEM_ROW((u32)GetItemID::GI_NUTS_30, ChestType::WOODEN_BIG, (u8)game::ItemId::GoldDustFill, 0x0093, + 0x001E8, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s32)DrawGraphicItemID::DI_GOLD_DUST, + (rnd::upgradeFunc)ItemUpgrade_None, ItemEffect_None, (s16)-1, + (s16)-1), // Gold Dust Refill [0x94] = ITEM_ROW((u32)GetItemID::GI_NUTS_30, ChestType::WOODEN_BIG, (u8)game::ItemId::MysteryMilkFill, 0x00CE, 0x00B6, @@ -767,29 +766,29 @@ namespace rnd { // TODO: Trade quest items [0x96] = ITEM_ROW((u32)GetItemID::GI_NUTS_30, ChestType::WOODEN_BIG, (u8)game::ItemId::MoonTear, 0x0096, 0x01B1, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s32)DrawGraphicItemID::DI_MOONS_TEAR, - (rnd::upgradeFunc)ItemUpgrade_None, ItemEffect_None, (s16)-1, (s16)-1), // Moon's Tear + (rnd::upgradeFunc)ItemUpgrade_None, ItemEffect_GiveTradeItem, (s16)0, (s16)-1), // Moon's Tear [0x97] = ITEM_ROW((u32)GetItemID::GI_NUTS_30, ChestType::WOODEN_BIG, (u8)game::ItemId::LandTitleDeed, 0x0097, 0x01B2, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s32)DrawGraphicItemID::DI_TOWN_TITLE_DEED, - (rnd::upgradeFunc)ItemUpgrade_None, ItemEffect_None, (s16)-1, + (rnd::upgradeFunc)ItemUpgrade_None, ItemEffect_GiveTradeItem, (s16)1, (s16)-1), // Land Title Deed [0x98] = ITEM_ROW((u32)GetItemID::GI_NUTS_30, ChestType::WOODEN_BIG, (u8)game::ItemId::SwampTitleDeed, 0x0098, 0x01B2, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s32)DrawGraphicItemID::DI_SWAMP_TITLE_DEED, - (rnd::upgradeFunc)ItemUpgrade_None, ItemEffect_None, (s16)-1, + (rnd::upgradeFunc)ItemUpgrade_None, ItemEffect_GiveTradeItem, (s16)2, (s16)-1), // Swamp Title Deed [0x99] = ITEM_ROW((u32)GetItemID::GI_NUTS_30, ChestType::WOODEN_BIG, (u8)game::ItemId::MountainTitleDeed, 0x0099, 0x01B2, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s32)DrawGraphicItemID::DI_MOUNTAIN_TITLE_DEED, (rnd::upgradeFunc)ItemUpgrade_None, - ItemEffect_None, (s16)-1, (s16)-1), // Mountain Title Deed + ItemEffect_GiveTradeItem, (s16)3, (s16)-1), // Mountain Title Deed [0x9A] = ITEM_ROW((u32)GetItemID::GI_NUTS_30, ChestType::WOODEN_BIG, (u8)game::ItemId::OceanTitleDeed, 0x009A, 0x01B2, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s32)DrawGraphicItemID::DI_OCEAN_TITLE_DEED, - (rnd::upgradeFunc)ItemUpgrade_None, ItemEffect_None, (s16)-1, + (rnd::upgradeFunc)ItemUpgrade_None, ItemEffect_GiveTradeItem, (s16)4, (s16)-1), // Ocean Title Deed [0x9B] = ITEM_ROW((u32)GetItemID::GI_NUTS_30, ChestType::WOODEN_BIG, (u8)game::ItemId::GreatFairySword, 0x009B, @@ -820,12 +819,12 @@ namespace rnd { [0xA0] = ITEM_ROW((u32)GetItemID::GI_NUTS_30, ChestType::WOODEN_BIG, (u8)game::ItemId::RoomKey, 0x00A0, 0x020F, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s32)DrawGraphicItemID::DI_ROOM_KEY, - (rnd::upgradeFunc)ItemUpgrade_None, ItemEffect_None, (s16)-1, (s16)-1), // Room Key + (rnd::upgradeFunc)ItemUpgrade_None, ItemEffect_GiveTradeItem, (s16)5, (s16)-1), // Room Key [0xA1] = ITEM_ROW((u32)GetItemID::GI_NUTS_30, ChestType::WOODEN_BIG, (u8)game::ItemId::LetterToMama, 0x00A1, 0x0245, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s32)DrawGraphicItemID::DI_MAMAS_LETTER, - (rnd::upgradeFunc)ItemUpgrade_None, ItemEffect_None, (s16)-1, + (rnd::upgradeFunc)ItemUpgrade_None, ItemEffect_GiveTradeItem, (s16)8, (s16)-1), // Letter To Mama [0xA2] = ITEM_ROW((u32)GetItemID::GI_RUPEE_BLUE, ChestType::DECORATED_SMALL, (u8)game::ItemId::SmallKey, 0x6136, @@ -871,13 +870,13 @@ namespace rnd { [0xAA] = ITEM_ROW((u32)GetItemID::GI_NUTS_30, ChestType::WOODEN_BIG, (u8)game::ItemId::LetterToKafei, 0x00AA, 0x0210, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s32)DrawGraphicItemID::DI_LETTER_TO_KAFEI, - (rnd::upgradeFunc)ItemUpgrade_None, ItemEffect_None, (s16)-1, + (rnd::upgradeFunc)ItemUpgrade_None, ItemEffect_GiveTradeItem, (s16)6, (s16)-1), // Letter To Kafei [0xAB] = ITEM_ROW((u32)GetItemID::GI_NUTS_30, ChestType::WOODEN_BIG, (u8)game::ItemId::PendantOfMemories, 0x00AB, 0x0215, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s32)DrawGraphicItemID::DI_PENDANT_OF_MEMORIES, (rnd::upgradeFunc)ItemUpgrade_None, - ItemEffect_None, (s16)-1, (s16)-1), // Pendant Of Memories + ItemEffect_GiveTradeItem, (s16)7, (s16)-1), // Pendant Of Memories [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, @@ -891,21 +890,21 @@ namespace rnd { [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, + (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s32)DrawGraphicItemID::DI_TOWN_MAP, (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, 0x6138, 0x000A0, - (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s32)DrawGraphicItemID::DI_DUNGEON_MAP_MAYBE, + (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s32)DrawGraphicItemID::DI_TOWN_MAP, (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, 0x6139, 0x000A0, - (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s32)DrawGraphicItemID::DI_DUNGEON_MAP_MAYBE, + (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s32)DrawGraphicItemID::DI_TOWN_MAP, (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, 0x613A, 0x000A0, - (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s32)DrawGraphicItemID::DI_DUNGEON_MAP_MAYBE, + (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s32)DrawGraphicItemID::DI_TOWN_MAP, (rnd::upgradeFunc)ItemUpgrade_None, ItemEffect_GiveDungeonItem, (s16)3, (s16)3), // Map (Stone Tower) @@ -918,35 +917,41 @@ namespace rnd { 0x000C8, 0x000A4, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, 0xFF, (rnd::upgradeFunc)ItemUpgrade_None, ItemEffect_GiveMagic, (s16)-1, (s16)-1), // Small Magic - [0xB4] = ITEM_ROW((u32)GetItemID::GI_RUPEE_BLUE, ChestType::WOODEN_SMALL, (u8)game::ItemId::MapUnused, 0x00B4, - 0x024D, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s32)DrawGraphicItemID::DI_TOWN_MAP, - (rnd::upgradeFunc)ItemUpgrade_None, ItemEffect_None, (s16)-1, - (s16)-1), // Map of Clocktown + [0xB4] = + ITEM_ROW((u32)GetItemID::GI_RUPEE_BLUE, ChestType::WOODEN_SMALL, (u8)game::ItemId::MapUnused, 0x00B4, 0x024D, + (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s32)DrawGraphicItemID::DI_DUNGEON_MAP_MAYBE, + (rnd::upgradeFunc)ItemUpgrade_None, ItemEffect_None, (s16)-1, + (s16)-1), // Map of Clocktown - [0xB5] = ITEM_ROW((u32)GetItemID::GI_RUPEE_BLUE, ChestType::WOODEN_SMALL, (u8)game::ItemId::MapUnused, 0x00B5, - 0x024D, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s32)DrawGraphicItemID::DI_TOWN_MAP, - (rnd::upgradeFunc)ItemUpgrade_None, ItemEffect_None, (s16)-1, - (s16)-1), // Map of Woodfall + [0xB5] = + ITEM_ROW((u32)GetItemID::GI_RUPEE_BLUE, ChestType::WOODEN_SMALL, (u8)game::ItemId::MapUnused, 0x00B5, 0x024D, + (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s32)DrawGraphicItemID::DI_DUNGEON_MAP_MAYBE, + (rnd::upgradeFunc)ItemUpgrade_None, ItemEffect_None, (s16)-1, + (s16)-1), // Map of Woodfall - [0xB6] = ITEM_ROW((u32)GetItemID::GI_RUPEE_BLUE, ChestType::WOODEN_SMALL, (u8)game::ItemId::MapUnused, 0x00B6, - 0x024D, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s32)DrawGraphicItemID::DI_TOWN_MAP, - (rnd::upgradeFunc)ItemUpgrade_None, ItemEffect_None, (s16)-1, - (s16)-1), // Map of Snowhead + [0xB6] = + ITEM_ROW((u32)GetItemID::GI_RUPEE_BLUE, ChestType::WOODEN_SMALL, (u8)game::ItemId::MapUnused, 0x00B6, 0x024D, + (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s32)DrawGraphicItemID::DI_DUNGEON_MAP_MAYBE, + (rnd::upgradeFunc)ItemUpgrade_None, ItemEffect_None, (s16)-1, + (s16)-1), // Map of Snowhead - [0xB7] = ITEM_ROW((u32)GetItemID::GI_RUPEE_BLUE, ChestType::WOODEN_SMALL, (u8)game::ItemId::MapUnused, 0x00B7, - 0x024D, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s32)DrawGraphicItemID::DI_TOWN_MAP, - (rnd::upgradeFunc)ItemUpgrade_None, ItemEffect_None, (s16)-1, - (s16)-1), // Map of Romani Ranch + [0xB7] = + ITEM_ROW((u32)GetItemID::GI_RUPEE_BLUE, ChestType::WOODEN_SMALL, (u8)game::ItemId::MapUnused, 0x00B7, 0x024D, + (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s32)DrawGraphicItemID::DI_DUNGEON_MAP_MAYBE, + (rnd::upgradeFunc)ItemUpgrade_None, ItemEffect_None, (s16)-1, + (s16)-1), // Map of Romani Ranch - [0xB8] = ITEM_ROW((u32)GetItemID::GI_RUPEE_BLUE, ChestType::WOODEN_SMALL, (u8)game::ItemId::MapUnused, 0x00B8, - 0x024D, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s32)DrawGraphicItemID::DI_TOWN_MAP, - (rnd::upgradeFunc)ItemUpgrade_None, ItemEffect_None, (s16)-1, - (s16)-1), // Map of Great Bay + [0xB8] = + ITEM_ROW((u32)GetItemID::GI_RUPEE_BLUE, ChestType::WOODEN_SMALL, (u8)game::ItemId::MapUnused, 0x00B8, 0x024D, + (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s32)DrawGraphicItemID::DI_DUNGEON_MAP_MAYBE, + (rnd::upgradeFunc)ItemUpgrade_None, ItemEffect_None, (s16)-1, + (s16)-1), // Map of Great Bay - [0xB9] = ITEM_ROW((u32)GetItemID::GI_RUPEE_BLUE, ChestType::WOODEN_SMALL, (u8)game::ItemId::MapUnused, 0x00B9, - 0x024D, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s32)DrawGraphicItemID::DI_TOWN_MAP, - (rnd::upgradeFunc)ItemUpgrade_None, ItemEffect_None, (s16)-1, - (s16)-1), // Map of Stone Tower + [0xB9] = + ITEM_ROW((u32)GetItemID::GI_RUPEE_BLUE, ChestType::WOODEN_SMALL, (u8)game::ItemId::MapUnused, 0x00B9, 0x024D, + (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s8)0xFF, (s32)DrawGraphicItemID::DI_DUNGEON_MAP_MAYBE, + (rnd::upgradeFunc)ItemUpgrade_None, ItemEffect_None, (s16)-1, + (s16)-1), // Map of Stone Tower [0xBA] = ITEM_ROW((u32)GetItemID::GI_RUPEE_BLUE, ChestType::WOODEN_SMALL, (u8)game::ItemId::FishingPass, 0x00CF, diff --git a/code/source/rnd/item_upgrade.cpp b/code/source/rnd/item_upgrade.cpp index 05bf823a..d1c3fa94 100644 --- a/code/source/rnd/item_upgrade.cpp +++ b/code/source/rnd/item_upgrade.cpp @@ -88,7 +88,16 @@ namespace rnd { } GetItemID ItemUpgrade_RefillBottle(game::SaveData* saveCtx, GetItemID getItemId) { + // As you can see mystery milk avoids this check since it's wiped from your inventory + // on cycle reset. It also happens to remove whichever bottle the milk is in, + // so we should avoid refills and just give the bottle until a user can get that + // side quest fulfilled. switch (getItemId) { + case GetItemID::GI_BOTTLE_POTION_RED: + if (gExtSaveData.givenItemChecks.bottleRedPotionGiven == 1) { + return GetItemID::GI_POTION_RED; + } + break; case GetItemID::GI_BOTTLE_MILK: if (gExtSaveData.givenItemChecks.bottleMilkGiven == 1) { return GetItemID::GI_BOTTLE_MILK_REFILL; @@ -112,11 +121,6 @@ namespace rnd { return GetItemID::GI_BOTTLE_CHATEAU_ROMANI_REFILL; } break; - case GetItemID::GI_BOTTLE_MYSTERY_MILK: - if (gExtSaveData.givenItemChecks.bottleMysteryMilkGiven == 1) { - return GetItemID::GI_BOTTLE_MYSTERY_MILK_REFILL; - } - break; default: return getItemId; } diff --git a/code/source/rnd/savefile.cpp b/code/source/rnd/savefile.cpp index 9baae0e8..4ef58609 100644 --- a/code/source/rnd/savefile.cpp +++ b/code/source/rnd/savefile.cpp @@ -3,6 +3,7 @@ extern "C" { } #include #include "rnd/item_effect.h" +#include "rnd/item_table.h" #include "rnd/savefile.h" #include "rnd/settings.h" #if defined ENABLE_DEBUG || defined DEBUG_PRINT @@ -26,6 +27,8 @@ namespace rnd { saveData.anonymous_162 = saveData.anonymous_162 | 0x6000; rnd::util::GetPointer(0x22b14c)(game::ItemId::MaskOfTruth); rnd::util::GetPointer(0x22b14c)(game::ItemId::PictographBox); + rnd::util::GetPointer(0x22b14c)(game::ItemId::BlastMask); + rnd::util::GetPointer(0x22b14c)(game::ItemId::KafeiMask); // rnd::util::GetPointer(0x22b14c)(game::ItemId::PowderKeg); // saveData.inventory.inventory_count_register.quiver_upgrade = game::Quiver::Quiver50; saveData.inventory.inventory_count_register.bomb_bag_upgrade = game::BombBag::BombBag40; @@ -50,6 +53,7 @@ namespace rnd { saveData.inventory.masks[5] = game::ItemId::DekuMask; rnd::util::GetPointer(0x22b14c)(game::ItemId::BremenMask); + rnd::util::GetPointer(0x22b14c)(game::ItemId::Bottle); saveData.inventory.masks[11] = game::ItemId::GoronMask; saveData.inventory.masks[17] = game::ItemId::ZoraMask; saveData.inventory.masks[23] = game::ItemId::FierceDeityMask; @@ -107,7 +111,6 @@ namespace rnd { gSettingsContext.freeScarecrow = 1; saveData.activate_dungeon_skip_portal_0xF0_for_all = 0xF0; SaveFile_FillOverWorldMapData(); - #endif // TODO: Decomp event flags. Most likely in the large anonymous structs in the SaveData. u8 isNewFile = saveData.has_completed_intro; @@ -134,7 +137,7 @@ namespace rnd { // saveData.inventory.collect_register.song_of_healing = 1; // until happy mask salesman is overridden saveData.player.owl_statue_flags.clock_town = 1; #ifdef ENABLE_DEBUG - gSettingsContext.startingKokiriSword = 0; + gSettingsContext.startingKokiriSword = 3; gSettingsContext.startingShield = 0; #endif SaveFile_SetStartingInventory(); @@ -212,7 +215,7 @@ namespace rnd { saveData.turtle_flags.skip_swimming_to_great_bay_temple_cutscene = 1; // Needs to be greater than zero to skip first time song of time cutscene - saveData.player.song_of_time_counter = 1; + saveData.player.three_day_reset_count = 1; } void SaveFile_SetFastAnimationFlags() { @@ -705,6 +708,9 @@ namespace rnd { } else { // Player initially is given magic 0x30 on save creation. This prevents that. playerData.magic = 0x0; +#ifdef ENABLE_DEBUG + playerData.magic = 0x30; +#endif } if (gSettingsContext.startingDoubleDefense) { @@ -782,8 +788,23 @@ namespace rnd { memset(&gExtSaveData.chestRewarded, 0, sizeof(gExtSaveData.chestRewarded)); memset(&gExtSaveData.scenesDiscovered, 0, sizeof(gExtSaveData.scenesDiscovered)); memset(&gExtSaveData.itemCollected, 0, sizeof(gExtSaveData.itemCollected)); +#ifdef ENABLE_DEBUG + gExtSaveData.collectedTradeItems[0] = game::ItemId::MoonTear; + gExtSaveData.collectedTradeItems[1] = game::ItemId::LandTitleDeed; + gExtSaveData.collectedTradeItems[2] = game::ItemId::SwampTitleDeed; + gExtSaveData.collectedTradeItems[3] = game::ItemId::MountainTitleDeed; + gExtSaveData.collectedTradeItems[4] = game::ItemId::OceanTitleDeed; + gExtSaveData.collectedTradeItems[5] = game::ItemId::RoomKey; + gExtSaveData.collectedTradeItems[6] = game::ItemId::LetterToKafei; + gExtSaveData.collectedTradeItems[7] = game::ItemId::PendantOfMemories; + gExtSaveData.collectedTradeItems[8] = game::ItemId::LetterToMama; +#else + for (int i = 0; i < 9; i++) { + gExtSaveData.collectedTradeItems[i] = game::ItemId::None; + } +#endif + // TODO: Settings options belong in ext. - // memset(&gExtSaveData.scenesDiscovered, 0, sizeof(gExtSaveData.scenesDiscovered)); // memset(&gExtSaveData.entrancesDiscovered, 0, sizeof(gExtSaveData.entrancesDiscovered)); // // Ingame Options // gExtSaveData.option_EnableBGM = gSettingsContext.playMusic; @@ -888,4 +909,57 @@ namespace rnd { extDataUnmount(fsa); } + extern "C" void SaveFile_RemoveStoredTradeItem(u16 item, u8 slot) { + // This is a get item ID, we need to translate it to the regular item ID. +#if defined ENABLE_DEBUG || defined DEBUG_PRINT + rnd::util::Print("%s: Item and slot are %#04x %u\n", __func__, item, slot); +#endif + if (slot != 5 && slot != 17) + return; + ItemRow* gidItemRow = ItemTable_GetItemRowFromIndex(item); + game::ItemId firstItem = game::ItemId::None; + + for (int i = 0; i < 9; i++) { + if (gidItemRow->itemId != (u8)gExtSaveData.collectedTradeItems[i] && firstItem == game::ItemId::None) { + if (slot == 17 && i > 5 && i < 8) { +#if defined ENABLE_DEBUG || defined DEBUG_PRINT + rnd::util::Print("%s: Slot is 17 and our found item is %#04x\n", __func__, + gExtSaveData.collectedTradeItems[i]); +#endif + firstItem = gExtSaveData.collectedTradeItems[i]; + } + + else if (slot == 5 && i < 5) + firstItem = gExtSaveData.collectedTradeItems[i]; + } + if (gidItemRow->itemId == (u8)gExtSaveData.collectedTradeItems[i]) { + gExtSaveData.collectedTradeItems[i] = game::ItemId::None; + } + } + // Place the item in inventory, if there is no item to place it simply places none. + game::SaveData& saveData = game::GetCommonData().save; + saveData.inventory.items[slot] = firstItem; + } + extern "C" void SaveFile_RemoveTradeItemFromSlot(u16 item, u8 slot) { + // This is a get item ID, we need to translate it to the regular item ID. + if (slot == 5) { + for (int i = 0; i < 4; i++) { + if (item == (u16)gExtSaveData.collectedTradeItems[i]) { + gExtSaveData.collectedTradeItems[i] = game::ItemId::None; + break; + } + } + } + } + + extern "C" u8 SaveFile_GetItemCurrentlyInSlot(u8 slot) { +#if defined ENABLE_DEBUG || defined DEBUG_PRINT + rnd::util::Print("%s: Current slot is %#04x\n", __func__, game::GetCommonData().save.inventory.items[slot]); +#endif + return (u8)game::GetCommonData().save.inventory.items[slot]; + } + // SaveFile_DrawAndShowUIMessage() { + + // } + } // namespace rnd