Skip to content

Commit

Permalink
Add option to shuffle Zelda's Letter (#630)
Browse files Browse the repository at this point in the history
* Add ZL shuffle

* Run automatic format script as code does not match clang format rules.

Developers please rebase to avoid merge conflicts!

* Fix weird egg hatching multiple times

Co-authored-by: Roberto-Nessy <[email protected]>
  • Loading branch information
Roberto-Nessy and Roberto-Nessy authored Jan 19, 2023
1 parent 7588f8c commit 9871131
Show file tree
Hide file tree
Showing 16 changed files with 261 additions and 9 deletions.
24 changes: 24 additions & 0 deletions code/oot.ld
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,10 @@ SECTIONS
*(.patch_MedigoronItemOverrideTwo)
}

.patch_MaskSalesmanBorrowMask 0x16D148 : {
*(patch_MaskSalesmanBorrowMask)
}

.patch_KingZoraIgnoreSapphire 0x1736B8 : {
*(.patch_KingZoraIgnoreSapphire)
}
Expand Down Expand Up @@ -584,6 +588,10 @@ SECTIONS
*(.patch_RedBoulderExplode)
}

.patch_MaskSalesmanGiveMaskOfTruth 0x271D48 : {
*(.patch_MaskSalesmanGiveMaskOfTruth)
}

.patch_SongOfTimeJingle 0x274B1C : {
*(.patch_SongOfTimeJingle)
}
Expand Down Expand Up @@ -864,6 +872,14 @@ SECTIONS
*(.patch_IncomingGetItemID)
}

.patch_MaskSalesmanCheckNoMaskOne 0x372B64 : {
*(.patch_MaskSalesmanCheckNoMask)
}

.patch_MaskSalesmanCheckNoMaskTwo 0x372B70 : {
*(.patch_MaskSalesmanCheckNoMaskTwo)
}

.patch_ConvertBombDropTwo 0x3747A4 : {
*(.patch_ConvertBombDropTwo)
}
Expand Down Expand Up @@ -1172,6 +1188,10 @@ SECTIONS
*(.patch_OverrideFogDuringGameplayInit)
}

.patch_CheckForWeirdEggHatchGameplayInit 0x448A64 : {
*(.patch_CheckForWeirdEggHatchGameplayInit)
}

.patch_CheckForPocketCuccoHatchGameplayInit 0x448A80 : {
*(.patch_CheckForPocketCuccoHatchGameplayInit)
}
Expand Down Expand Up @@ -1340,6 +1360,10 @@ SECTIONS
*(.patch_ItemsMenuNumSprites)
}

.patch_CheckForWeirdEggHatchKankyo 0x470D00 : {
*(.patch_CheckForWeirdEggHatchKankyo)
}

.patch_CheckForPocketCuccoHatchKankyo 0x470D1C : {
*(.patch_CheckForPocketCuccoHatchKankyo)
}
Expand Down
24 changes: 24 additions & 0 deletions code/oot_e.ld
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,10 @@ SECTIONS
*(.patch_MedigoronItemOverrideTwo)
}

.patch_MaskSalesmanBorrowMask 0x16D148 : {
*(patch_MaskSalesmanBorrowMask)
}

.patch_KingZoraIgnoreSapphire 0x1736B8 : {
*(.patch_KingZoraIgnoreSapphire)
}
Expand Down Expand Up @@ -584,6 +588,10 @@ SECTIONS
*(.patch_RedBoulderExplode)
}

.patch_MaskSalesmanGiveMaskOfTruth 0x271D48 : {
*(.patch_MaskSalesmanGiveMaskOfTruth)
}

.patch_SongOfTimeJingle 0x274B1C : {
*(.patch_SongOfTimeJingle)
}
Expand Down Expand Up @@ -864,6 +872,14 @@ SECTIONS
*(.patch_IncomingGetItemID)
}

.patch_MaskSalesmanCheckNoMaskOne 0x372B64 : {
*(.patch_MaskSalesmanCheckNoMask)
}

.patch_MaskSalesmanCheckNoMaskTwo 0x372B70 : {
*(.patch_MaskSalesmanCheckNoMaskTwo)
}

.patch_ConvertBombDropTwo 0x3747A4 : {
*(.patch_ConvertBombDropTwo)
}
Expand Down Expand Up @@ -1172,6 +1188,10 @@ SECTIONS
*(.patch_OverrideFogDuringGameplayInit)
}

.patch_CheckForWeirdEggHatchGameplayInit 0x448A84 : {
*(.patch_CheckForWeirdEggHatchGameplayInit)
}

.patch_CheckForPocketCuccoHatchGameplayInit 0x448AA0 : {
*(.patch_CheckForPocketCuccoHatchGameplayInit)
}
Expand Down Expand Up @@ -1340,6 +1360,10 @@ SECTIONS
*(.patch_ItemsMenuNumSprites)
}

.patch_CheckForWeirdEggHatchKankyo 0x470D20 : {
*(.patch_CheckForWeirdEggHatchKankyo)
}

.patch_CheckForPocketCuccoHatchKankyo 0x470D3C : {
*(.patch_CheckForPocketCuccoHatchKankyo)
}
Expand Down
19 changes: 19 additions & 0 deletions code/src/hooks.s
Original file line number Diff line number Diff line change
Expand Up @@ -1776,6 +1776,25 @@ hook_TargetPointerColor:
b 0x47BB30
.endif

.global hook_MaskSalesmanBorrowMask
hook_MaskSalesmanBorrowMask:
ldrsh r1,[r6,#0x1C]
push {r0-r12,lr}
cpy r0,r1
bl SaveFile_BorrowMask
pop {r0-r12,lr}
bx lr

.global hook_MaskSalesmanGiveMaskOfTruth
hook_MaskSalesmanGiveMaskOfTruth:
orr r1,r1,#0x400
push {r0-r12,lr}
mov r0,#0x22 @ Mask of Truth SI id
bl SaveFile_BorrowMask
pop {r0-r12,lr}
bx lr


@ ----------------------------------
@ ----------------------------------

Expand Down
6 changes: 6 additions & 0 deletions code/src/item_effect.c
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,8 @@ void ItemEffect_FillWalletUpgrade(SaveContext* saveCtx, s16 arg1, s16 arg2) {
}

void ItemEffect_OpenMaskShop(SaveContext* saveCtx, s16 arg1, s16 arg2) {
gSaveContext.sceneFlags[0x60].unk |= 0x1 << 0x11;

if (gSettingsContext.openKakariko == OPENKAKARIKO_OPEN) {
gSaveContext.infTable[7] |= 0x40; // "Spoke to Gate Guard About Mask Shop"
if (!gSettingsContext.completeMaskQuest) {
Expand Down Expand Up @@ -416,3 +418,7 @@ void ItemEffect_OwnAdultTrade(SaveContext* saveCtx, s16 arg1, s16 arg2) {
ItemEffect_GrannySellsPotions(saveCtx, arg1, arg2);
}
}

void ItemEffect_GiveWeirdEgg(SaveContext* saveCtx, s16 arg1, s16 arg2) {
gSaveContext.sceneFlags[0x60].unk |= 0x1 << 0x10;
}
1 change: 1 addition & 0 deletions code/src/item_effect.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,5 @@ void ItemEffect_GiveMedallion(SaveContext* saveCtx, s16 mask, s16 arg2);
void ItemEffect_MoveNabooru(SaveContext* saveCtx, s16 arg1, s16 arg2);
void ItemEffect_GrannySellsPotions(SaveContext* saveCtx, s16 arg1, s16 arg2);
void ItemEffect_OwnAdultTrade(SaveContext* saveCtx, s16 arg1, s16 arg2);
void ItemEffect_GiveWeirdEgg(SaveContext* saveCtx, s16 arg1, s16 arg2);
#endif //_ITEM_EFFECT_H_
2 changes: 1 addition & 1 deletion code/src/item_table.c
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ static ItemRow rItemTable[] = {
[0x44] = ITEM_ROW(0x53, 1, 0x79, 0x0052, 0x00CD, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x1F, ItemUpgrade_None, ItemEffect_None, -1, -1), // Large Magic Jar
[0x45] = ITEM_ROW(0x53, 0, 0x56, 0x005E, 0x00D1, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x22, ItemUpgrade_None, ItemEffect_FillWalletUpgrade, 1, -1), // Adult's Wallet
[0x46] = ITEM_ROW(0x53, 0, 0x57, 0x005F, 0x00D1, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x23, ItemUpgrade_None, ItemEffect_FillWalletUpgrade, 2, -1), // Giant's Wallet
[0x47] = ITEM_ROW(0x53, 0, 0x21, 0x009A, 0x00DA, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x29, ItemUpgrade_None, ItemEffect_None, -1, -1), // Weird Egg
[0x47] = ITEM_ROW(0x53, 0, 0x21, 0x009A, 0x00DA, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x29, ItemUpgrade_None, ItemEffect_GiveWeirdEgg, -1, -1), // Weird Egg
[0x48] = ITEM_ROW(0x4D, 1, 0x83, 0x0055, 0x00B7, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x09, ItemUpgrade_None, ItemEffect_None, -1, -1), // Recovery Heart
[0x49] = ITEM_ROW(0x4D, 1, 0x92, 0x00E6, 0x00D8, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x25, ItemUpgrade_ArrowsToRupee, ItemEffect_None, -1, -1), // Arrows (5)
[0x4A] = ITEM_ROW(0x4D, 1, 0x93, 0x00E6, 0x00D8, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x26, ItemUpgrade_ArrowsToRupee, ItemEffect_None, -1, -1), // Arrows (10)
Expand Down
38 changes: 38 additions & 0 deletions code/src/menus.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,44 @@ void ItemsMenu_Draw(void) {
gSaveContext.items[selectedItemSlot]);
}
}

// Obtained both weird egg and Zelda's letter and pressed l/r on child trade slot
if (selectedItemSlot == SLOT_TRADE_CHILD && SaveFile_ChildTradeSlots() == 2 &&
(rInputCtx.pressed.l || rInputCtx.pressed.r)) {
u8 item = gSaveContext.items[selectedItemSlot];

// Switch to ZL/mask slot
if (item == ITEM_WEIRD_EGG || item == ITEM_CHICKEN) {
// ZL has been replaced with a mask
if (gSaveContext.itemGetInf[2] & (0x1 << 0x3)) {
// Sold mask but not paid for it
if (SaveFile_MaskSlotValue() == ITEM_SOLD_OUT) {
// Only change item to no mask if not equipped
if (gItemsMenuSelectedSlot % 6 != 5) {
item = ITEM_SOLD_OUT;
}
} else {
item = ITEM_MASK_KEATON + SaveFile_CurrentMask();
}
} else {
item = ITEM_LETTER_ZELDA;
}
}
// Switch to egg/cucco slot
else {
if (SaveFile_WeirdEggHatched()) {
item = ITEM_CHICKEN;
} else {
item = ITEM_WEIRD_EGG;
}
}

gSaveContext.items[selectedItemSlot] = item;
MenuSpritesManager_RegisterItemSprite(gItemsMenuSpritesManager, gItemsMenuSelectedSlot,
gSaveContext.items[selectedItemSlot]);
MenuSpritesManager_RegisterItemSprite(gItemsMenuGlowSpritesManager, 0,
gSaveContext.items[selectedItemSlot]);
}
}
}

Expand Down
32 changes: 32 additions & 0 deletions code/src/patches.s
Original file line number Diff line number Diff line change
Expand Up @@ -1442,11 +1442,21 @@ KingZoraDontStartTimer_patch:
KingZoraSetTradedPrescriptionFlag_patch:
beq hook_KingZoraSetTradedPrescriptionFlag

.section .patch_CheckForWeirdEggHatchGameplayInit
.global CheckForWeirdEggHatchGameplayInit_patch
CheckForWeirdEggHatchGameplayInit_patch:
bl SaveFile_CheckForWeirdEggHatch

.section .patch_CheckForPocketCuccoHatchGameplayInit
.global CheckForPocketCuccoHatchGameplayInit_patch
CheckForPocketCuccoHatchGameplayInit_patch:
bl SaveFile_CheckForPocketCuccoHatch

.section .patch_CheckForWeirdEggHatchKankyo
.global CheckForWeirdEggHatchKankyo_patch
CheckForWeirdEggHatchKankyo_patch:
bl SaveFile_CheckForWeirdEggHatch

.section .patch_CheckForPocketCuccoHatchKankyo
.global CheckForPocketCuccoHatchKankyo_patch
CheckForPocketCuccoHatchKankyo_patch:
Expand Down Expand Up @@ -2041,6 +2051,28 @@ ShadowShip_Accel_patch:
ShadowShip_TopSpeed_patch:
.word 0x41A00000

.section .patch_MaskSalesmanCheckNoMaskOne
.global MaskSalesmanCheckNoMaskOne_patch
MaskSalesmanCheckNoMaskOne_patch:
push {r0,r1,r4-r12,lr}
bl SaveFile_MaskSlotValue

.section .patch_MaskSalesmanCheckNoMaskTwo
.global MaskSalesmanCheckNoMaskTwo_patch
MaskSalesmanCheckNoMaskTwo_patch:
cpy r2,r0
pop {r0,r1,r4-r12,lr}

.section .patch_MaskSalesmanBorrowMask
.global MaskSalesmanBorrowMask_patch
MaskSalesmanBorrowMask_patch:
bl hook_MaskSalesmanBorrowMask

.section .patch_MaskSalesmanGiveMaskOfTruth
.global MaskSalesmanGiveMaskOfTruth_patch
MaskSalesmanGiveMaskOfTruth_patch:
bl hook_MaskSalesmanGiveMaskOfTruth

@ ----------------------------------
@ ----------------------------------

Expand Down
83 changes: 83 additions & 0 deletions code/src/savefile.c
Original file line number Diff line number Diff line change
Expand Up @@ -559,10 +559,93 @@ u32 SaveFile_TradeItemIsOwned(u8 itemId) {
return (gSaveContext.sceneFlags[0x60].unk & (0x1 << tradeItemNum)) != 0;
}

u8 SaveFile_WeirdEggOwned(void) {
return (gSaveContext.sceneFlags[0x60].unk >> 0x10) & 0x1;
}

u8 SaveFile_ZeldasLetterOwned(void) {
return (gSaveContext.sceneFlags[0x60].unk >> 0x11) & 0x1;
}

u8 SaveFile_ChildTradeSlots(void) {
return SaveFile_WeirdEggOwned() + SaveFile_ZeldasLetterOwned();
}

u8 SaveFile_WeirdEggHatched(void) {
return (gSaveContext.sceneFlags[0x60].unk >> 0x12) & 0x1;
}

u8 SaveFile_CurrentMask(void) {
return (gSaveContext.sceneFlags[0x60].unk >> 0x13) & 0x7;
}

u32 SaveFile_MaskSlotValue(void) {
u8 mask = SaveFile_CurrentMask();

// Sold but not paid for mask
if (mask < 4 && gSaveContext.itemGetInf[3] & (0x1 << (0x8 + mask)) &&
!(gSaveContext.eventChkInf[8] & (0x1 << (0xC + mask)))) {
return ITEM_SOLD_OUT;
}

return ITEM_MASK_KEATON + mask;
}

void SaveFile_BorrowMask(s16 SI_ItemId) {
// SI ids are in a different order to normal item ids so need to convert
u8 itemId;
switch (SI_ItemId) {
case 0x1E:
itemId = ITEM_MASK_KEATON;
break;
case 0x1F:
itemId = ITEM_MASK_SPOOKY;
break;
case 0x20:
itemId = ITEM_MASK_SKULL;
break;
case 0x21:
itemId = ITEM_MASK_BUNNY;
break;
case 0x22:
itemId = ITEM_MASK_TRUTH;
break;
case 0x23:
itemId = ITEM_MASK_ZORA;
break;
case 0x24:
itemId = ITEM_MASK_GORON;
break;
case 0x25:
itemId = ITEM_MASK_GERUDO;
break;
}

// Ensure flag for obtaining Keaton mask is set in case complete mask quest is enabled
gSaveContext.itemGetInf[2] |= 0x1 << 0x3;

gSaveContext.sceneFlags[0x60].unk &= ~(0x7 << 0x13);
gSaveContext.sceneFlags[0x60].unk |= (itemId - ITEM_MASK_KEATON) << 0x13;
}

typedef s32 (*Inventory_ReplaceItem_proc)(GlobalContext* globalCtx, u16 oldItem, u16 newItem);
#define Inventory_ReplaceItem_addr 0x316CEC
#define Inventory_ReplaceItem ((Inventory_ReplaceItem_proc)Inventory_ReplaceItem_addr)

u32 SaveFile_CheckForWeirdEggHatch(void) {
// Force the egg into the child trade slot so that it can hatch
if (SaveFile_WeirdEggOwned() & !SaveFile_WeirdEggHatched()) {
gSaveContext.items[SLOT_TRADE_CHILD] = ITEM_WEIRD_EGG;
}

if (Inventory_ReplaceItem(gGlobalContext, ITEM_WEIRD_EGG, ITEM_CHICKEN)) {
gSaveContext.sceneFlags[0x60].unk |= 0x1 << 0x12;
return 1;
} else {
return 0;
}
}

u32 SaveFile_CheckForPocketCuccoHatch(void) {
// Force the egg into the adult trade slot so that it can hatch
if (SaveFile_TradeItemIsOwned(ITEM_POCKET_EGG)) {
Expand Down
4 changes: 4 additions & 0 deletions code/src/savefile.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ void SaveFile_SetStartingInventory(void);
void SaveFile_SetTradeItemAsOwned(u8 itemId);
void SaveFile_UnsetTradeItemAsOwned(u8 itemId);
u32 SaveFile_TradeItemIsOwned(u8 itemId);
u8 SaveFile_ChildTradeSlots(void);
u8 SaveFile_WeirdEggHatched(void);
u8 SaveFile_CurrentMask(void);
u32 SaveFile_MaskSlotValue(void);
void SaveFile_SetOwnedTradeItemEquipped(void);
void SaveFile_ResetItemSlotsIfMatchesID(u8 itemSlot);
u8 SaveFile_InventoryMenuHasSlot(u8 adult, u8 itemSlot);
Expand Down
14 changes: 9 additions & 5 deletions source/descriptions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -461,11 +461,15 @@ string_view weirdEggDesc = "Enabling this shuffles the Weird Egg fr
"into the item pool.\n" //
"This will require finding the Weird Egg to talk to"
"Zelda in Hyrule Castle, which in turn locks\n" //
"rewards from Impa, Saria, Malon, and Talon, as\n" //
"well as the Happy Mask Sidequest. The Weird Egg is"
"also required for Zelda's Letter to unlock the\n" //
"Kakariko Gate as child which can lock some\n" //
"progression"; //
"rewards from Impa, Saria, Malon, and Talon."; //
/*------------------------------ //
| SHUFFLE ZELDAS LETTER | //
------------------------------*/ //
string_view zeldasLetterDesc = "Enabling this shuffles Zelda's Letter into the\n" //
"item pool.\n" //
"This will require finding the letter to open the\n"
"Happy Mask Shop and the gate in Kakariko if it is\n"
"set to closed."; //
/*------------------------------ //
| SHUFFLE GERUDO TOKEN | //
------------------------------*/ //
Expand Down
Loading

0 comments on commit 9871131

Please sign in to comment.