Rando: Master Sword Shuffle (#2981)

* The mother of all commits

* Removed `GI_SWORD_MASTER`;
"Master Sword" Items now actually give MS

* Removed dupe MS entries in item pool;
updated GIMESSAGE (should stop crashing on non-Windows);
re-added MS in item list

* Give Adult Link a freebie with shuffle MS on;
cihld -> adult no longer gives MS;
ToT Master Sword now gives correct item

* add master sword GI draw func based on ToT MS object

* Force `MasterSword` logic var to only update upon getting MS

* Dorf funny line now activates with LA and MS in inv

* Apply suggestions

* Updated RAND_INF;
Check Tracker changes;
Gave RAND_INF and ice trap logic to ToT MS check;
Fixed swordless behavior for HBA/fishing

* ToT MS Check now works in check tracker;
Visual bug where box hovers over non-existent MS gone;
Fixed RAND_INF check with ToT MS pedestal;
Ganon no longer gives free MS

* adult equips no longer reset in MS shuffle

* Apply (most) locacc review suggestions

Co-authored-by: inspectredc <78732756+inspectredc@users.noreply.github.com>

* Reorganized swordless check for interface to fit edge cases;
getting master sword no longer highlights box

* Edge case for BGS but no bow

* Fix implicit declaration error for GI hooks (#9)

* Adjusted `CanAdultAttack/Damage`; applied logic suggestions

* Fixed build errors (hopefully)

* Cleanup merge

* get shit working again

* Tidied up remaining uses of DD flag as rando indicator

* make master sword invisible and fix ms flag (#10)

* Add text to sheik if go mode is obtained but barrier is still up

* overhaul swordless behavior in `func_80083108`

* reworked ToT MS Check to have an actual GI

* suggestions

* Apply suggestions

* Better swordless handling with temp B (#11)

* better swordless handling with temp B

* prevent auto save in fishing pond

* prevent auto save during bombchu bowling

* enum fix

---------

Co-authored-by: Adam Bird <archez39@me.com>
Co-authored-by: inspectredc <78732756+inspectredc@users.noreply.github.com>
Co-authored-by: RaelCappra <rael.cappra@gmail.com>
Co-authored-by: Adam Bird <Archez@users.noreply.github.com>
This commit is contained in:
Ralphie Morell 2023-10-21 21:51:37 -04:00 committed by GitHub
parent e6445e0ce3
commit 2eaed8d81e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
57 changed files with 558 additions and 310 deletions

View file

@ -811,6 +811,20 @@ void func_80082850(PlayState* play, s16 maxAlpha) {
}
}
// buttonStatus[0] is used to represent if the B button is disabled, but also tracks
// the last active B button item during mini-games/epona (temp B)
// Since ITEM_NONE is the same as BTN_DISABLED (255), we need a different value to help us track
// that the player was swordless before like ITEM_NONE_FE (254)
#define SWORDLESS_STATUS ITEM_NONE_FE
// Restores swordless state when using the custom value for temp B and then clears temp B
void Interface_RandoRestoreSwordless(void) {
if (IS_RANDO && gSaveContext.buttonStatus[0] == SWORDLESS_STATUS) {
gSaveContext.equips.buttonItems[0] = ITEM_NONE;
gSaveContext.buttonStatus[0] = BTN_ENABLED;
}
}
void func_80083108(PlayState* play) {
MessageContext* msgCtx = &play->msgCtx;
Player* player = GET_PLAYER(play);
@ -818,13 +832,20 @@ void func_80083108(PlayState* play) {
s16 i;
s16 sp28 = 0;
// Check for the player being swordless in rando (no item on B and swordless flag set)
// Child is always assumed due to not finding kokiri sword yet. Adult is only checked with MS shuffle on.
u8 randoIsSwordless = IS_RANDO && (LINK_IS_CHILD || Randomizer_GetSettingValue(RSK_SHUFFLE_MASTER_SWORD)) &&
gSaveContext.equips.buttonItems[0] == ITEM_NONE && Flags_GetInfTable(INFTABLE_SWORDLESS);
u8 randoWasSwordlessBefore = IS_RANDO && gSaveContext.buttonStatus[0] == SWORDLESS_STATUS;
u8 randoCanTrackSwordless = randoIsSwordless && !randoWasSwordlessBefore;
if ((gSaveContext.cutsceneIndex < 0xFFF0) ||
((play->sceneNum == SCENE_LON_LON_RANCH) && (gSaveContext.cutsceneIndex == 0xFFF0))) {
gSaveContext.unk_13E7 = 0;
if ((player->stateFlags1 & 0x00800000) || (play->shootingGalleryStatus > 1) ||
if ((player->stateFlags1 & PLAYER_STATE1_ON_HORSE) || (play->shootingGalleryStatus > 1) ||
((play->sceneNum == SCENE_BOMBCHU_BOWLING_ALLEY) && Flags_GetSwitch(play, 0x38))) {
if (gSaveContext.equips.buttonItems[0] != ITEM_NONE) {
if (gSaveContext.equips.buttonItems[0] != ITEM_NONE || randoCanTrackSwordless) {
gSaveContext.unk_13E7 = 1;
if (gSaveContext.buttonStatus[0] == BTN_DISABLED) {
@ -837,9 +858,14 @@ void func_80083108(PlayState* play) {
if ((gSaveContext.equips.buttonItems[0] != ITEM_SLINGSHOT) &&
(gSaveContext.equips.buttonItems[0] != ITEM_BOW) &&
(gSaveContext.equips.buttonItems[0] != ITEM_BOMBCHU) &&
(gSaveContext.equips.buttonItems[0] != ITEM_NONE)) {
(gSaveContext.equips.buttonItems[0] != ITEM_NONE || randoCanTrackSwordless)) {
gSaveContext.buttonStatus[0] = gSaveContext.equips.buttonItems[0];
// Track swordless status for restoration later
if (randoCanTrackSwordless) {
gSaveContext.buttonStatus[0] = SWORDLESS_STATUS;
}
if ((play->sceneNum == SCENE_BOMBCHU_BOWLING_ALLEY) && Flags_GetSwitch(play, 0x38)) {
gSaveContext.equips.buttonItems[0] = ITEM_BOMBCHU;
Interface_LoadItemIcon1(play, 0);
@ -875,11 +901,11 @@ void func_80083108(PlayState* play) {
Interface_ChangeAlpha(8);
} else if ((play->sceneNum == SCENE_BOMBCHU_BOWLING_ALLEY) && Flags_GetSwitch(play, 0x38)) {
Interface_ChangeAlpha(8);
} else if (player->stateFlags1 & 0x00800000) {
} else if (player->stateFlags1 & PLAYER_STATE1_ON_HORSE) {
Interface_ChangeAlpha(12);
}
} else {
if (player->stateFlags1 & 0x00800000) {
if (player->stateFlags1 & PLAYER_STATE1_ON_HORSE) {
Interface_ChangeAlpha(12);
}
}
@ -891,6 +917,12 @@ void func_80083108(PlayState* play) {
if (play->interfaceCtx.unk_260 != 0) {
if (gSaveContext.equips.buttonItems[0] != ITEM_FISHING_POLE) {
gSaveContext.buttonStatus[0] = gSaveContext.equips.buttonItems[0];
// Track swordless status for restoration later
if (randoCanTrackSwordless) {
gSaveContext.buttonStatus[0] = SWORDLESS_STATUS;
}
gSaveContext.equips.buttonItems[0] = ITEM_FISHING_POLE;
gSaveContext.unk_13EA = 0;
Interface_LoadItemIcon1(play, 0);
@ -904,6 +936,8 @@ void func_80083108(PlayState* play) {
gSaveContext.equips.buttonItems[0] = gSaveContext.buttonStatus[0];
gSaveContext.unk_13EA = 0;
Interface_RandoRestoreSwordless();
if (gSaveContext.equips.buttonItems[0] != ITEM_NONE) {
Interface_LoadItemIcon1(play, 0);
}
@ -974,7 +1008,7 @@ void func_80083108(PlayState* play) {
}
Interface_ChangeAlpha(50);
} else if ((player->stateFlags1 & 0x00200000) || (player->stateFlags2 & PLAYER_STATE2_CRAWLING)) {
} else if ((player->stateFlags1 & PLAYER_STATE1_CLIMBING_LADDER) || (player->stateFlags2 & PLAYER_STATE2_CRAWLING)) {
if (gSaveContext.buttonStatus[0] != BTN_DISABLED) {
gSaveContext.buttonStatus[0] = BTN_DISABLED;
gSaveContext.buttonStatus[1] = BTN_DISABLED;
@ -988,7 +1022,7 @@ void func_80083108(PlayState* play) {
Interface_ChangeAlpha(50);
}
} else if ((gSaveContext.eventInf[0] & 0xF) == 1) {
if (player->stateFlags1 & 0x00800000) {
if (player->stateFlags1 & PLAYER_STATE1_ON_HORSE) {
if ((gSaveContext.equips.buttonItems[0] != ITEM_NONE) &&
(gSaveContext.equips.buttonItems[0] != ITEM_BOW)) {
if (gSaveContext.inventory.items[SLOT_BOW] == ITEM_NONE) {
@ -1009,6 +1043,8 @@ void func_80083108(PlayState* play) {
(gSaveContext.equips.buttonItems[0] != ITEM_SWORD_BGS) &&
(gSaveContext.equips.buttonItems[0] != ITEM_SWORD_KNIFE)) {
gSaveContext.equips.buttonItems[0] = gSaveContext.buttonStatus[0];
Interface_RandoRestoreSwordless();
} else {
gSaveContext.buttonStatus[0] = gSaveContext.equips.buttonItems[0];
}
@ -1048,8 +1084,12 @@ void func_80083108(PlayState* play) {
(gSaveContext.equips.buttonItems[0] == ITEM_BOW) ||
(gSaveContext.equips.buttonItems[0] == ITEM_BOMBCHU) ||
(gSaveContext.equips.buttonItems[0] == ITEM_NONE)) {
if ((gSaveContext.equips.buttonItems[0] != ITEM_NONE) || (gSaveContext.infTable[29] == 0)) {
if ((gSaveContext.equips.buttonItems[0] != ITEM_NONE) || (gSaveContext.infTable[29] == 0) ||
randoWasSwordlessBefore) {
gSaveContext.equips.buttonItems[0] = gSaveContext.buttonStatus[0];
Interface_RandoRestoreSwordless();
sp28 = 1;
if (gSaveContext.equips.buttonItems[0] != ITEM_NONE) {
@ -1071,8 +1111,12 @@ void func_80083108(PlayState* play) {
(gSaveContext.equips.buttonItems[0] == ITEM_BOW) ||
(gSaveContext.equips.buttonItems[0] == ITEM_BOMBCHU) ||
(gSaveContext.equips.buttonItems[0] == ITEM_NONE)) {
if ((gSaveContext.equips.buttonItems[0] != ITEM_NONE) || (gSaveContext.infTable[29] == 0)) {
if ((gSaveContext.equips.buttonItems[0] != ITEM_NONE) || (gSaveContext.infTable[29] == 0) ||
randoWasSwordlessBefore) {
gSaveContext.equips.buttonItems[0] = gSaveContext.buttonStatus[0];
Interface_RandoRestoreSwordless();
sp28 = 1;
if (gSaveContext.equips.buttonItems[0] != ITEM_NONE) {
@ -1412,15 +1456,20 @@ void Inventory_SwapAgeEquipment(void) {
}
// When becoming adult, remove swordless flag since we'll get master sword
// (Unless Master Sword is shuffled)
// Only in rando to keep swordless link bugs in vanilla
if (IS_RANDO) {
if (IS_RANDO && !Randomizer_GetSettingValue(RSK_SHUFFLE_MASTER_SWORD)) {
Flags_UnsetInfTable(INFTABLE_SWORDLESS);
}
gSaveContext.childEquips.equipment = gSaveContext.equips.equipment;
if (gSaveContext.adultEquips.buttonItems[0] == ITEM_NONE) {
gSaveContext.equips.buttonItems[0] = ITEM_SWORD_MASTER;
if (gSaveContext.adultEquips.buttonItems[0] == ITEM_NONE && !(IS_RANDO && Randomizer_GetSettingValue(RSK_SHUFFLE_MASTER_SWORD) && gSaveContext.adultEquips.equipment)) {
if (!IS_RANDO || !Randomizer_GetSettingValue(RSK_SHUFFLE_MASTER_SWORD)) {
gSaveContext.equips.buttonItems[0] = ITEM_SWORD_MASTER;
} else {
gSaveContext.equips.buttonItems[0] = ITEM_NONE;
}
if (gSaveContext.inventory.items[SLOT_NUT] != ITEM_NONE) {
gSaveContext.equips.buttonItems[1] = ITEM_NUT;
@ -1433,7 +1482,9 @@ void Inventory_SwapAgeEquipment(void) {
gSaveContext.equips.buttonItems[3] = gSaveContext.inventory.items[SLOT_OCARINA];
gSaveContext.equips.cButtonSlots[1] = SLOT_BOMB;
gSaveContext.equips.cButtonSlots[2] = SLOT_OCARINA;
gSaveContext.equips.equipment = 0x1122;
gSaveContext.equips.equipment = (IS_RANDO && Randomizer_GetSettingValue(RSK_SHUFFLE_MASTER_SWORD) &&
gSaveContext.equips.buttonItems[0] == ITEM_NONE) ? 0x1120 :0x1122;
// Set the dpad to nothing
gSaveContext.equips.buttonItems[4] = ITEM_NONE;
@ -1626,11 +1677,13 @@ void func_80084BF4(PlayState* play, u16 flag) {
(gSaveContext.equips.buttonItems[0] == ITEM_BOMBCHU) ||
(gSaveContext.equips.buttonItems[0] == ITEM_FISHING_POLE)) {
gSaveContext.equips.buttonItems[0] = gSaveContext.buttonStatus[0];
Interface_RandoRestoreSwordless();
Interface_LoadItemIcon1(play, 0);
}
} else if (gSaveContext.equips.buttonItems[0] == ITEM_NONE) {
if ((gSaveContext.equips.buttonItems[0] != ITEM_NONE) || (gSaveContext.infTable[29] == 0)) {
gSaveContext.equips.buttonItems[0] = gSaveContext.buttonStatus[0];
Interface_RandoRestoreSwordless();
Interface_LoadItemIcon1(play, 0);
}
}
@ -2556,6 +2609,13 @@ u16 Randomizer_Item_Give(PlayState* play, GetItemEntry giEntry) {
return Return_Item_Entry(giEntry, RG_NONE);
}
if (item == RG_MASTER_SWORD) {
if (!CHECK_OWNED_EQUIP(EQUIP_SWORD, 1)) {
gSaveContext.inventory.equipment |= gBitFlags[1] << gEquipShifts[EQUIP_SWORD];
}
return Return_Item_Entry(giEntry, RG_NONE);
}
temp = gSaveContext.inventory.items[slot];
osSyncPrintf("Item_Register(%d)=%d %d\n", slot, item, temp);
INV_CONTENT(item) = item;
@ -5723,6 +5783,7 @@ void Interface_Draw(PlayState* play) {
(gSaveContext.equips.buttonItems[0] != ITEM_SWORD_KNIFE)) {
if (gSaveContext.buttonStatus[0] != BTN_ENABLED) {
gSaveContext.equips.buttonItems[0] = gSaveContext.buttonStatus[0];
Interface_RandoRestoreSwordless();
} else {
gSaveContext.equips.buttonItems[0] = ITEM_NONE;
}