Vanilla Behavior Overhaul

Co-authored-by: jordanpg <jordanpg@users.noreply.github.com>
This commit is contained in:
Garrett Cox 2024-01-08 09:25:47 -06:00
parent fe9dcf1fc3
commit 596ea5ebbb
99 changed files with 4191 additions and 2912 deletions

View File

@ -183,6 +183,8 @@ void __osSetWatchLo(u32);
EnItem00* Item_DropCollectible(PlayState* play, Vec3f* spawnPos, s16 params); EnItem00* Item_DropCollectible(PlayState* play, Vec3f* spawnPos, s16 params);
EnItem00* Item_DropCollectible2(PlayState* play, Vec3f* spawnPos, s16 params); EnItem00* Item_DropCollectible2(PlayState* play, Vec3f* spawnPos, s16 params);
void EnItem00_CustomItemsParticles(Actor* Parent, PlayState* play, GetItemEntry giEntry); void EnItem00_CustomItemsParticles(Actor* Parent, PlayState* play, GetItemEntry giEntry);
void EnItem00_SetupAction(EnItem00* this, EnItem00ActionFunc actionFunc);
void func_8001E5C8(EnItem00* this, PlayState* play);
void Item_DropCollectibleRandom(PlayState* play, Actor* fromActor, Vec3f* spawnPos, s16 params); void Item_DropCollectibleRandom(PlayState* play, Actor* fromActor, Vec3f* spawnPos, s16 params);
void EffectBlure_ChangeType(EffectBlure* this, int type); void EffectBlure_ChangeType(EffectBlure* this, int type);
void EffectBlure_AddVertex(EffectBlure* this, Vec3f* p1, Vec3f* p2); void EffectBlure_AddVertex(EffectBlure* this, Vec3f* p1, Vec3f* p2);
@ -415,6 +417,7 @@ f32 Actor_WorldDistXZToPoint(Actor* actor, Vec3f* refPoint);
void func_8002DBD0(Actor* actor, Vec3f* result, Vec3f* arg2); void func_8002DBD0(Actor* actor, Vec3f* result, Vec3f* arg2);
f32 Actor_HeightDiff(Actor* actorA, Actor* actorB); f32 Actor_HeightDiff(Actor* actorA, Actor* actorB);
f32 Player_GetHeight(Player* player); f32 Player_GetHeight(Player* player);
s32 func_8083E5A8(Player* player, PlayState* play);
f32 func_8002DCE4(Player* player); f32 func_8002DCE4(Player* player);
s32 func_8002DD6C(Player* player); s32 func_8002DD6C(Player* player);
s32 func_8002DD78(Player* player); s32 func_8002DD78(Player* player);
@ -1106,6 +1109,7 @@ s32 FrameAdvance_Update(FrameAdvanceContext* frameAdvCtx, Input* input);
u8 PlayerGrounded(Player* player); u8 PlayerGrounded(Player* player);
void Player_SetBootData(PlayState* play, Player* player); void Player_SetBootData(PlayState* play, Player* player);
s32 Player_InBlockingCsMode(PlayState* play, Player* player); s32 Player_InBlockingCsMode(PlayState* play, Player* player);
s32 Player_TryCsAction(PlayState* play, Actor* actor, s32 csAction);
s32 Player_InCsMode(PlayState* play); s32 Player_InCsMode(PlayState* play);
s32 func_8008E9C4(Player* player); s32 func_8008E9C4(Player* player);
s32 Player_IsChildWithHylianShield(Player* player); s32 Player_IsChildWithHylianShield(Player* player);

View File

@ -266,7 +266,12 @@ typedef enum {
/* 0x17 */ ITEM00_TUNIC_ZORA, /* 0x17 */ ITEM00_TUNIC_ZORA,
/* 0x18 */ ITEM00_TUNIC_GORON, /* 0x18 */ ITEM00_TUNIC_GORON,
/* 0x19 */ ITEM00_BOMBS_SPECIAL, /* 0x19 */ ITEM00_BOMBS_SPECIAL,
/* 0x20 */ ITEM00_BOMBCHU, /* 0x1A */ ITEM00_BOMBCHU,
/* 0x1B */ ITEM00_SOH_DUMMY,
/* 0x1C */ ITEM00_SOH_GIVE_ITEM_ENTRY,
/* 0x1D */ ITEM00_SOH_GIVE_ITEM_ENTRY_GI,
/* 0x1E */ ITEM00_MAX,
/* 0xFF */ ITEM00_NONE = 0xFF
} Item00Type; } Item00Type;
struct EnItem00; struct EnItem00;
@ -284,10 +289,13 @@ typedef struct EnItem00 {
/* 0x15A */ s16 unk_15A; /* 0x15A */ s16 unk_15A;
/* 0x15C */ f32 scale; /* 0x15C */ f32 scale;
/* 0x160 */ ColliderCylinder collider; /* 0x160 */ ColliderCylinder collider;
s16 ogParams; // #region SOH [Randomizer]
GetItemEntry randoGiEntry; GetItemEntry randoGiEntry;
RandomizerCheck randoCheck; RandomizerCheck randoCheck;
RandomizerInf randoInf; RandomizerInf randoInf;
/* */ s16 ogParams;
/* */ GetItemEntry itemEntry;
// #endregion
} EnItem00; // size = 0x1AC } EnItem00; // size = 0x1AC
// Only A_OBJ_SIGNPOST_OBLONG and A_OBJ_SIGNPOST_ARROW are used in room files. // Only A_OBJ_SIGNPOST_OBLONG and A_OBJ_SIGNPOST_ARROW are used in room files.

View File

@ -505,6 +505,7 @@ const std::vector<FlagTable> flagTables = {
{ RAND_INF_CHILD_FISHING, "RAND_INF_CHILD_FISHING" }, { RAND_INF_CHILD_FISHING, "RAND_INF_CHILD_FISHING" },
{ RAND_INF_ADULT_FISHING, "RAND_INF_ADULT_FISHING" }, { RAND_INF_ADULT_FISHING, "RAND_INF_ADULT_FISHING" },
{ RAND_INF_10_BIG_POES, "RAND_INF_10_BIG_POES" }, { RAND_INF_10_BIG_POES, "RAND_INF_10_BIG_POES" },
{ RAND_INF_GRANT_GANONS_BOSSKEY, "RAND_INF_GRANT_GANONS_BOSSKEY" },
{ RAND_INF_GOHMA_SOUL, "RAND_INF_GOHMA_SOUL" }, { RAND_INF_GOHMA_SOUL, "RAND_INF_GOHMA_SOUL" },
{ RAND_INF_KING_DODONGO_SOUL, "RAND_INF_KING_DODONGO_SOUL" }, { RAND_INF_KING_DODONGO_SOUL, "RAND_INF_KING_DODONGO_SOUL" },
@ -515,7 +516,6 @@ const std::vector<FlagTable> flagTables = {
{ RAND_INF_BONGO_BONGO_SOUL, "RAND_INF_BONGO_BONGO_SOUL" }, { RAND_INF_BONGO_BONGO_SOUL, "RAND_INF_BONGO_BONGO_SOUL" },
{ RAND_INF_TWINROVA_SOUL, "RAND_INF_TWINROVA_SOUL" }, { RAND_INF_TWINROVA_SOUL, "RAND_INF_TWINROVA_SOUL" },
{ RAND_INF_GANON_SOUL, "RAND_INF_GANON_SOUL" }, { RAND_INF_GANON_SOUL, "RAND_INF_GANON_SOUL" },
{ RAND_INF_GRANT_GANONS_BOSSKEY, "RAND_INF_GRANT_GANONS_BOSSKEY" },
{ RAND_INF_HAS_OCARINA_A, "RAND_INF_HAS_OCARINA_A"}, { RAND_INF_HAS_OCARINA_A, "RAND_INF_HAS_OCARINA_A"},
{ RAND_INF_HAS_OCARINA_C_UP, "RAND_INF_HAS_OCARINA_C_UP" }, { RAND_INF_HAS_OCARINA_C_UP, "RAND_INF_HAS_OCARINA_C_UP" },
@ -607,7 +607,11 @@ const std::vector<FlagTable> flagTables = {
{ RAND_INF_ZD_FISH_2, "RAND_INF_ZD_FISH_2" }, { RAND_INF_ZD_FISH_2, "RAND_INF_ZD_FISH_2" },
{ RAND_INF_ZD_FISH_3, "RAND_INF_ZD_FISH_3" }, { RAND_INF_ZD_FISH_3, "RAND_INF_ZD_FISH_3" },
{ RAND_INF_ZD_FISH_4, "RAND_INF_ZD_FISH_4" }, { RAND_INF_ZD_FISH_4, "RAND_INF_ZD_FISH_4" },
{ RAND_INF_ZD_FISH_5, "RAND_INF_ZD_FISH_5" } { RAND_INF_ZD_FISH_5, "RAND_INF_ZD_FISH_5" },
{ RAND_INF_LINKS_POCKET, "RAND_INF_LINKS_POCKET" },
{ RAND_INF_LEARNED_EPONA_SONG, "RAND_INF_LEARNED_EPONA_SONG" },
{ RAND_INF_DARUNIAS_JOY, "RAND_INF_DARUNIAS_JOY" },
} }, } },
}; };

View File

@ -34,6 +34,8 @@ std::vector<ValueTableElement> valueTable = {
{ "Text ID", "play->msgCtx.textId", "TEXTID:", TYPE_U16, true, []() -> void* { return &gPlayState->msgCtx.textId; }, WHITE }, { "Text ID", "play->msgCtx.textId", "TEXTID:", TYPE_U16, true, []() -> void* { return &gPlayState->msgCtx.textId; }, WHITE },
{ "Analog Stick X", "play->state.input->cur.stick_x", "AX:", TYPE_S8, true, []() -> void* { return &gPlayState->state.input->cur.stick_x; }, WHITE }, { "Analog Stick X", "play->state.input->cur.stick_x", "AX:", TYPE_S8, true, []() -> void* { return &gPlayState->state.input->cur.stick_x; }, WHITE },
{ "Analog Stick Y", "play->state.input->cur.stick_y", "AY:", TYPE_S8, true, []() -> void* { return &gPlayState->state.input->cur.stick_y; }, WHITE }, { "Analog Stick Y", "play->state.input->cur.stick_y", "AY:", TYPE_S8, true, []() -> void* { return &gPlayState->state.input->cur.stick_y; }, WHITE },
{ "getItemID", "Player->getItemId", "ITEM:", TYPE_S16, true, []() -> void* { return &GET_PLAYER(gPlayState)->getItemId; }, WHITE },
{ "getItemEntry", "Player->getItemEntry", "IE:", TYPE_S16, true, []() -> void* { return &GET_PLAYER(gPlayState)->getItemEntry.itemId; }, WHITE },
/* TODO: Find these (from GZ) /* TODO: Find these (from GZ)
"XZ Units Traveled (Camera based speed variable)" f32 0x801C9018 "XZ Units Traveled (Camera based speed variable)" f32 0x801C9018
"Movement Angle" x16 0x801DBB1C "Movement Angle" x16 0x801DBB1C

View File

@ -14,6 +14,13 @@ typedef enum {
CSMC_SIZE CSMC_SIZE
} ChestStyleMatchesContentsType; } ChestStyleMatchesContentsType;
typedef enum {
SGIA_DISABLED,
SGIA_JUNK,
SGIA_ALL,
SGIA_SIZE
} SkipGetItemAnimationType;
typedef enum { typedef enum {
BUNNY_HOOD_VANILLA, BUNNY_HOOD_VANILLA,
BUNNY_HOOD_FAST_AND_JUMP, BUNNY_HOOD_FAST_AND_JUMP,

View File

@ -3,6 +3,7 @@
#ifndef GameInteractor_h #ifndef GameInteractor_h
#define GameInteractor_h #define GameInteractor_h
#include "libultraship/libultraship.h"
#include "GameInteractionEffect.h" #include "GameInteractionEffect.h"
#include "soh/Enhancements/item-tables/ItemTableTypes.h" #include "soh/Enhancements/item-tables/ItemTableTypes.h"
#include <z64.h> #include <z64.h>
@ -67,6 +68,272 @@ typedef enum {
/* */ GI_TP_DEST_PRELUDE = ENTR_TEMPLE_OF_TIME_7, /* */ GI_TP_DEST_PRELUDE = ENTR_TEMPLE_OF_TIME_7,
} GITeleportDestinations; } GITeleportDestinations;
typedef enum {
// Vanilla condition: gSaveContext.showTitleCard
GI_VB_SHOW_TITLE_CARD,
// Opt: *EnWonderTalk2
GI_VB_WONDER_TALK,
// Opt: *ElfMsg
GI_VB_NAVI_TALK,
// Vanilla condition: INFTABLE_GREETED_BY_SARIA
GI_VB_NOT_BE_GREETED_BY_SARIA,
// Opt: *EnMd
// Vanilla condition: EnMd->interactInfo.talkState == NPC_TALK_STATE_ACTION
GI_VB_MOVE_MIDO_IN_KOKIRI_FOREST,
// Opt: *EnMd
// Vanilla condition: CHECK_QUEST_ITEM(QUEST_KOKIRI_EMERALD)
GI_VB_MIDO_CONSIDER_DEKU_TREE_DEAD,
// Opt: *EnKo
// Vanilla condition: CHECK_QUEST_ITEM(QUEST_KOKIRI_EMERALD)
GI_VB_OPEN_KOKIRI_FOREST,
// Opt: *EnOwl
// Vanilla condition: EnOwl->actor.xzDistToPlayer < targetDist
GI_VB_OWL_INTERACTION,
// Vanilla condition: EVENTCHKINF_TALON_RETURNED_FROM_CASTLE
GI_VB_MALON_RETURN_FROM_CASTLE,
// Vanilla condition: CUR_UPG_VALUE(UPG_STRENGTH) <= 0
GI_VB_BE_ELIGIBLE_FOR_DARUNIAS_JOY_REWARD,
/* Vanilla condition:
```
LINK_IS_ADULT &&
(gEntranceTable[((void)0, gSaveContext.entranceIndex)].scene == SCENE_TEMPLE_OF_TIME) &&
CHECK_QUEST_ITEM(QUEST_MEDALLION_SPIRIT) &&
CHECK_QUEST_ITEM(QUEST_MEDALLION_SHADOW) &&
!Flags_GetEventChkInf(EVENTCHKINF_RETURNED_TO_TEMPLE_OF_TIME_WITH_ALL_MEDALLIONS);
```
*/
GI_VB_BE_ELIGIBLE_FOR_LIGHT_ARROWS,
// Vanilla condition: !CHECK_QUEST_ITEM(QUEST_SONG_SARIA)
GI_VB_BE_ELIGIBLE_FOR_SARIAS_SONG,
// Vanilla condition: CHECK_QUEST_ITEM(QUEST_SONG_EPONA)
GI_VB_MALON_ALREADY_TAUGHT_EPONAS_SONG,
// Vanilla condition: CHECK_OWNED_EQUIP(EQUIP_TYPE_BOOTS, EQUIP_INV_BOOTS_IRON) && !Flags_GetEventChkInf(EVENTCHKINF_LEARNED_SERENADE_OF_WATER)
GI_VB_BE_ELIGIBLE_FOR_SERENADE_OF_WATER,
// Vanilla condition: (!CHECK_OWNED_EQUIP(EQUIP_TYPE_BOOTS, EQUIP_INV_BOOTS_IRON) && !Flags_GetEventChkInf(EVENTCHKINF_LEARNED_SERENADE_OF_WATER)) && LINK_IS_ADULT
GI_VB_SHIEK_PREPARE_TO_GIVE_SERENADE_OF_WATER,
// Vanilla condition: !EVENTCHKINF_LEARNED_PRELUDE_OF_LIGHT and EVENTCHKINF_USED_FOREST_TEMPLE_BLUE_WARP
GI_VB_BE_ELIGIBLE_FOR_PRELUDE_OF_LIGHT,
/* Vanilla Condition:
```
LINK_IS_ADULT &&
gSaveContext.entranceIndex == ENTR_KAKARIKO_VILLAGE_0 &&
Flags_GetEventChkInf(EVENTCHKINF_USED_FOREST_TEMPLE_BLUE_WARP) &&
Flags_GetEventChkInf(EVENTCHKINF_USED_FIRE_TEMPLE_BLUE_WARP) &&
Flags_GetEventChkInf(EVENTCHKINF_USED_WATER_TEMPLE_BLUE_WARP) &&
!Flags_GetEventChkInf(EVENTCHKINF_BONGO_BONGO_ESCAPED_FROM_WELL);
```
*/
GI_VB_BE_ELIGIBLE_FOR_NOCTURNE_OF_SHADOW,
// Opt: *EnGo2
// Vanilla condition: CUR_CAPACITY(UPG_BOMB_BAG) >= 20 && this->waypoint > 7 && this->waypoint < 12
GI_VB_BE_ELIGIBLE_FOR_CHILD_ROLLING_GORON_REWARD,
// Vanilla condition: !CHECK_OWNED_EQUIP_ALT(EQUIP_TYPE_SWORD, EQUIP_INV_SWORD_BIGGORON)
GI_VB_BE_ELIGIBLE_FOR_GIANTS_KNIFE_PURCHASE,
// Opt: *EnMs
// Vanilla condition: gSaveContext.rupees >= sPrices[BEANS_BOUGHT]
GI_VB_BE_ELIGIBLE_FOR_MAGIC_BEANS_PURCHASE,
// Opt: *EnItem00
// Vanilla condition: Flags_GetCollectible(play, this->collectibleFlag)
GI_VB_ITEM00_DESPAWN,
// Opt: *EnTk
// Vanilla condition: gSaveContext.dayTime <= 0xC000 || gSaveContext.dayTime >= 0xE000 || LINK_IS_ADULT || play->sceneNum != SCENE_GRAVEYARD
GI_VB_DAMPE_IN_GRAVEYARD_DESPAWN,
// Opt: *EnTk
// Vanilla condition: this->validDigHere == 1
GI_VB_BE_VALID_GRAVEDIGGING_SPOT,
// Opt: *EnTk
// Vanilla condition: this->currentReward == 3
GI_VB_BE_DAMPE_GRAVEDIGGING_GRAND_PRIZE,
// Opt: *EnTk
// Vanilla condition: !Flags_GetItemGetInf(ITEMGETINF_1C)
GI_VB_DAMPE_GRAVEDIGGING_GRAND_PRIZE_BE_HEART_PIECE,
// Opt: *EnShopnuts
/* Vanilla Condition:
```
((this->actor.params == 0x0002) && (Flags_GetItemGetInf(ITEMGETINF_0B))) ||
((this->actor.params == 0x0009) && (Flags_GetInfTable(INFTABLE_192))) ||
((this->actor.params == 0x000A) && (Flags_GetInfTable(INFTABLE_193)))
```
*/
GI_VB_BUSINESS_SCRUB_DESPAWN,
// Opt: *EnCow
// Vanilla condition: play->sceneNum == SCENE_LINKS_HOUSE && (!LINK_IS_ADULT || !Flags_GetEventChkInf(EVENTCHKINF_WON_COW_IN_MALONS_RACE))
GI_VB_DESPAWN_HORSE_RACE_COW,
// Opt: *EnHs
// Vanilla condition: Flags_GetItemGetInf(ITEMGETINF_30)
GI_VB_DESPAWN_GROG,
// Opt: *EnKo
// Vanilla condition: (INV_CONTENT(ITEM_TRADE_ADULT) == ITEM_ODD_POTION) ? true : false;
GI_VB_SPAWN_LW_FADO,
// Opt: *EnMk
GI_VB_PLAY_EYEDROP_CREATION_ANIM,
// Opt: *EnDs
GI_VB_PLAY_ODD_POTION_ANIM,
// Opt: *EnMk
// Vanilla condition: INV_CONTENT(ITEM_ODD_MUSHROOM) == ITEM_EYEDROPS
GI_VB_USE_EYEDROP_DIALOGUE,
// Opt: *EnMk
// Vanilla condition: Flags_GetItemGetInf(ITEMGETINF_30)
GI_VB_OFFER_BLUE_POTION,
// Vanilla condition: Inventory_HasEmptyBottle() == 0
GI_VB_NEED_BOTTLE_FOR_GRANNYS_ITEM,
// Opt: *EnNiwLady
GI_VB_SET_CUCCO_COUNT,
// Opt: *EnKz
// Vanilla condition: CHECK_QUEST_ITEM(QUEST_ZORA_SAPPHIRE)
GI_VB_KING_ZORA_THANK_CHILD,
// Opt: *EnKz
// Vanilla condition: this->actor.textId == 0x401A
GI_VB_BE_ABLE_TO_EXCHANGE_RUTOS_LETTER,
// Opt: *EnKz
// Vanilla condition: Flags_GetEventChkInf(EVENTCHKINF_KING_ZORA_MOVED)
GI_VB_KING_ZORA_BE_MOVED,
// Vanilla condition: gSaveState.bgsFlag
GI_VB_BIGGORON_CONSIDER_TRADE_COMPLETE,
// Vanilla condition: gSaveState.bgsFlag
GI_VB_BIGGORON_CONSIDER_SWORD_COLLECTED,
// Vanilla condition: Environment_GetBgsDayCount() >= 3
GI_VB_BIGGORON_CONSIDER_SWORD_FORGED,
// Vanilla condition: CHECK_QUEST_ITEM(QUEST_MEDALLION_FIRE)
GI_VB_GORONS_CONSIDER_FIRE_TEMPLE_FINISHED,
// Vanilla condition: CHECK_QUEST_ITEM(QUEST_GORON_RUBY)
GI_VB_GORONS_CONSIDER_DODONGOS_CAVERN_FINISHED,
// Opt: *uint16_t
// Vanilla condition: false
GI_VB_OVERRIDE_LINK_THE_GORON_DIALOGUE,
// Opt: *EnGo2
GI_VB_EN_GO2_RESET_AFTER_GET_ITEM,
/*** Play Cutscenes ***/
GI_VB_PLAY_TRANSITION_CS,
// Opt: *EventChkInf flag
GI_VB_PLAY_ENTRANCE_CS,
// Opt: *cutsceneId
GI_VB_PLAY_ONEPOINT_CS,
// Opt: *actor
GI_VB_PLAY_ONEPOINT_ACTOR_CS,
// Opt: *BgTreemouth
GI_VB_PLAY_DEKU_TREE_INTRO_CS,
// Vanilla condition: !EventChkInf except for spirit & shadow temple which are !medallion, and Jabu which always is true
GI_VB_PLAY_BLUE_WARP_CS,
GI_VB_PLAY_DARUNIAS_JOY_CS,
GI_VB_PLAY_SHIEK_BLOCK_MASTER_SWORD_CS,
// Vanilla condition: !EVENTCHKINF_PULLED_MASTER_SWORD_FROM_PEDESTAL
GI_VB_PLAY_PULL_MASTER_SWORD_CS,
GI_VB_PLAY_DROP_FISH_FOR_JABU_CS,
// Vanilla condition: player->getItemId == GI_GAUNTLETS_SILVER
GI_VB_PLAY_NABOORU_CAPTURED_CS,
GI_VB_PLAY_ZELDAS_LULLABY_CS,
// Opt: *EnSa
GI_VB_PLAY_SARIAS_SONG_CS,
GI_VB_PLAY_PRELUDE_OF_LIGHT_CS,
GI_VB_PLAY_MINUET_OF_FOREST_CS,
GI_VB_PLAY_BOLERO_OF_FIRE_CS,
GI_VB_PLAY_SERENADE_OF_WATER_CS,
GI_VB_PLAY_EYEDROPS_CS,
/*** Give Items ***/
GI_VB_GIVE_ITEM_FROM_CHEST,
GI_VB_GIVE_ITEM_FROM_BLUE_WARP,
// Opt: *EnItem00
GI_VB_GIVE_ITEM_FROM_ITEM_00,
// Opt: *EnSi
GI_VB_GIVE_ITEM_SKULL_TOKEN,
// Opt: *EnCow
GI_VB_GIVE_ITEM_FROM_COW,
// Opt: *EnDns
GI_VB_GIVE_ITEM_FROM_BUSINESS_SCRUB,
// Opt: *EnMk
GI_VB_GIVE_ITEM_FROM_LAB_DIVE,
// Opt: *EnDs
GI_VB_GIVE_ITEM_FROM_GRANNYS_SHOP,
// Opt: *EnNiwLady
GI_VB_GIVE_ITEM_FROM_ANJU_AS_CHILD,
// Opt: *EnNiwLady
GI_VB_GIVE_ITEM_FROM_ANJU_AS_ADULT,
// Opt: *EnKz
// Vanilla condition: !CHECK_OWNED_EQUIP(EQUIP_TYPE_TUNIC, EQUIP_INV_TUNIC_ZORA)
GI_VB_GIVE_ITEM_FROM_THAWING_KING_ZORA,
// Opt: *EnGo2
GI_VB_GIVE_ITEM_FROM_ROLLING_GORON_AS_CHILD,
// Opt: *EnGo2
GI_VB_GIVE_ITEM_FROM_ROLLING_GORON_AS_ADULT,
// Opt: *EnJs
GI_VB_GIVE_ITEM_FROM_CARPET_SALESMAN,
// Opt: *EnGm
GI_VB_GIVE_ITEM_FROM_MEDIGORON,
// Opt: *EnMs
GI_VB_GIVE_ITEM_FROM_MAGIC_BEAN_SALESMAN,
// Opt: *EnFr
GI_VB_GIVE_ITEM_FROM_FROGS,
GI_VB_GIVE_ITEM_FAIRY_OCARINA,
GI_VB_GIVE_ITEM_WEIRD_EGG,
GI_VB_GIVE_ITEM_LIGHT_ARROW,
GI_VB_GIVE_ITEM_STRENGTH_1,
GI_VB_GIVE_ITEM_ZELDAS_LETTER,
GI_VB_GIVE_ITEM_MASTER_SWORD,
GI_VB_GIVE_ITEM_OCARINA_OF_TIME,
GI_VB_GIVE_ITEM_KOKIRI_EMERALD,
GI_VB_GIVE_ITEM_GORON_RUBY,
GI_VB_GIVE_ITEM_ZORA_SAPPHIRE,
GI_VB_GIVE_ITEM_LIGHT_MEDALLION,
GI_VB_GIVE_ITEM_FOREST_MEDALLION,
GI_VB_GIVE_ITEM_FIRE_MEDALLION,
GI_VB_GIVE_ITEM_WATER_MEDALLION,
GI_VB_GIVE_ITEM_SPIRIT_MEDALLION,
GI_VB_GIVE_ITEM_SHADOW_MEDALLION,
/*** Give Songs ***/
GI_VB_GIVE_ITEM_ZELDAS_LULLABY,
GI_VB_GIVE_ITEM_SARIAS_SONG,
GI_VB_GIVE_ITEM_EPONAS_SONG,
GI_VB_GIVE_ITEM_SUNS_SONG,
GI_VB_GIVE_ITEM_SONG_OF_TIME,
GI_VB_GIVE_ITEM_SONG_OF_STORMS,
GI_VB_GIVE_ITEM_MINUET_OF_FOREST,
GI_VB_GIVE_ITEM_BOLERO_OF_FIRE,
GI_VB_GIVE_ITEM_SERENADE_OF_WATER,
GI_VB_GIVE_ITEM_REQUIEM_OF_SPIRIT,
GI_VB_GIVE_ITEM_NOCTURNE_OF_SHADOW,
GI_VB_GIVE_ITEM_PRELUDE_OF_LIGHT,
/*** Adult Trade ***/
// Opt: *EnNiwLady
GI_VB_TRADE_POCKET_CUCCO,
// Opt: *EnHs
GI_VB_TRADE_COJIRO,
// Opt: *EnDs
GI_VB_TRADE_ODD_MUSHROOM,
// Opt: *EnKo
GI_VB_TRADE_ODD_POTION,
// Opt: *EnToryo
GI_VB_TRADE_SAW,
// Opt: *EnGo2
GI_VB_TRADE_BROKEN_SWORD,
// Opt: *EnKz,
GI_VB_TRADE_PRESCRIPTION,
// Opt: *EnMk
GI_VB_TRADE_FROG,
// Opt: *EnGo2
GI_VB_TRADE_EYEDROPS,
// Opt: *EnGo2
GI_VB_TRADE_CLAIM_CHECK,
GI_VB_TRADE_TIMER_ODD_MUSHROOM,
GI_VB_TRADE_TIMER_EYEDROPS,
GI_VB_TRADE_TIMER_FROG,
// Opt: *EnNiwLady
GI_VB_ANJU_SET_OBTAINED_TRADE_ITEM,
/*** Fixes ***/
// Vanilla condition: false
GI_VB_FIX_SAW_SOFTLOCK,
} GIVanillaBehavior;
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
@ -163,11 +430,28 @@ public:
static GameInteractionEffectQueryResult RemoveEffect(RemovableGameInteractionEffect* effect); static GameInteractionEffectQueryResult RemoveEffect(RemovableGameInteractionEffect* effect);
// Game Hooks // Game Hooks
template <typename H> struct RegisteredGameHooks { inline static std::vector<typename H::fn> functions; }; uint32_t nextHookId = 0;
template <typename H> void RegisterGameHook(typename H::fn h) { RegisteredGameHooks<H>::functions.push_back(h); } template <typename H> struct RegisteredGameHooks { inline static std::unordered_map<uint32_t, typename H::fn> functions; };
template <typename H> struct HooksToUnregister { inline static std::vector<uint32_t> hooks; };
template <typename H> uint32_t RegisterGameHook(typename H::fn h) {
// Ensure hook id is unique
while (RegisteredGameHooks<H>::functions.find(this->nextHookId) != RegisteredGameHooks<H>::functions.end()) {
this->nextHookId++;
}
RegisteredGameHooks<H>::functions[this->nextHookId] = h;
return this->nextHookId++;
}
template <typename H> void UnregisterGameHook(uint32_t id) {
HooksToUnregister<H>::hooks.push_back(id);
}
template <typename H, typename... Args> void ExecuteHooks(Args&&... args) { template <typename H, typename... Args> void ExecuteHooks(Args&&... args) {
for (auto& fn : RegisteredGameHooks<H>::functions) { for (auto& hookId : HooksToUnregister<H>::hooks) {
fn(std::forward<Args>(args)...); RegisteredGameHooks<H>::functions.erase(hookId);
}
HooksToUnregister<H>::hooks.clear();
for (auto& hook : RegisteredGameHooks<H>::functions) {
hook.second(std::forward<Args>(args)...);
} }
} }
@ -194,6 +478,8 @@ public:
DEFINE_HOOK(OnPlayDestroy, void()); DEFINE_HOOK(OnPlayDestroy, void());
DEFINE_HOOK(OnPlayDrawEnd, void()); DEFINE_HOOK(OnPlayDrawEnd, void());
DEFINE_HOOK(OnVanillaBehavior, void(GIVanillaBehavior flag, bool* result, void* opt));
DEFINE_HOOK(OnSaveFile, void(int32_t fileNum)); DEFINE_HOOK(OnSaveFile, void(int32_t fileNum));
DEFINE_HOOK(OnLoadFile, void(int32_t fileNum)); DEFINE_HOOK(OnLoadFile, void(int32_t fileNum));
DEFINE_HOOK(OnDeleteFile, void(int32_t fileNum)); DEFINE_HOOK(OnDeleteFile, void(int32_t fileNum));

View File

@ -90,6 +90,11 @@ void GameInteractor_ExecuteOnPlayDrawEnd() {
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnPlayDrawEnd>(); GameInteractor::Instance->ExecuteHooks<GameInteractor::OnPlayDrawEnd>();
} }
bool GameInteractor_Should(GIVanillaBehavior flag, bool result, void* opt) {
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnVanillaBehavior>(flag, &result, opt);
return result;
}
// MARK: - Save Files // MARK: - Save Files
void GameInteractor_ExecuteOnSaveFile(int32_t fileNum) { void GameInteractor_ExecuteOnSaveFile(int32_t fileNum) {

View File

@ -27,6 +27,7 @@ void GameInteractor_ExecuteOnOcarinaSongAction();
void GameInteractor_ExecuteOnShopSlotChangeHooks(uint8_t cursorIndex, int16_t price); void GameInteractor_ExecuteOnShopSlotChangeHooks(uint8_t cursorIndex, int16_t price);
void GameInteractor_ExecuteOnPlayDestroy(); void GameInteractor_ExecuteOnPlayDestroy();
void GameInteractor_ExecuteOnPlayDrawEnd(); void GameInteractor_ExecuteOnPlayDrawEnd();
bool GameInteractor_Should(GIVanillaBehavior flag, bool result, void* opt);
// MARK: - Save Files // MARK: - Save Files
void GameInteractor_ExecuteOnSaveFile(int32_t fileNum); void GameInteractor_ExecuteOnSaveFile(int32_t fileNum);

View File

@ -10,6 +10,8 @@
#include "soh/Enhancements/cosmetics/authenticGfxPatches.h" #include "soh/Enhancements/cosmetics/authenticGfxPatches.h"
#include <soh/Enhancements/item-tables/ItemTableManager.h> #include <soh/Enhancements/item-tables/ItemTableManager.h>
#include "soh/Enhancements/nametag.h" #include "soh/Enhancements/nametag.h"
#include "soh/Enhancements/timesaver_hook_handlers.h"
#include "soh/Enhancements/randomizer/hook_handlers.h"
#include "src/overlays/actors/ovl_En_Bb/z_en_bb.h" #include "src/overlays/actors/ovl_En_Bb/z_en_bb.h"
#include "src/overlays/actors/ovl_En_Dekubaba/z_en_dekubaba.h" #include "src/overlays/actors/ovl_En_Dekubaba/z_en_dekubaba.h"
@ -1596,6 +1598,8 @@ void RegisterFishsanity() {
} }
void InitMods() { void InitMods() {
RandomizerRegisterHooks();
TimeSaverRegisterHooks();
RegisterTTS(); RegisterTTS();
RegisterInfiniteMoney(); RegisterInfiniteMoney();
RegisterInfiniteHealth(); RegisterInfiniteHealth();

View File

@ -1122,7 +1122,19 @@ int Fill() {
std::vector<RandomizerGet> remainingPool = FilterAndEraseFromPool(ItemPool, [](const auto i) { return true; }); std::vector<RandomizerGet> remainingPool = FilterAndEraseFromPool(ItemPool, [](const auto i) { return true; });
FastFill(remainingPool, GetAllEmptyLocations(), false); FastFill(remainingPool, GetAllEmptyLocations(), false);
//Add prices for scrubsanity, this is unique to SoH because we write/read scrub prices to/from the spoilerfile. //Add default prices to scrubs
for (size_t i = 0; i < Rando::StaticData::scrubLocations.size(); i++) {
if (Rando::StaticData::scrubLocations[i] == RC_LW_DEKU_SCRUB_NEAR_BRIDGE || Rando::StaticData::scrubLocations[i] == RC_LW_DEKU_SCRUB_GROTTO_FRONT) {
ctx->GetItemLocation(Rando::StaticData::scrubLocations[i])->SetCustomPrice(40);
} else if (Rando::StaticData::scrubLocations[i] == RC_HF_DEKU_SCRUB_GROTTO) {
ctx->GetItemLocation(Rando::StaticData::scrubLocations[i])->SetCustomPrice(10);
} else {
auto loc = Rando::StaticData::GetLocation(Rando::StaticData::scrubLocations[i]);
auto item = Rando::StaticData::RetrieveItem(loc->GetVanillaItem());
ctx->GetItemLocation(Rando::StaticData::scrubLocations[i])->SetCustomPrice(item.GetPrice());
}
}
if (ctx->GetOption(RSK_SHUFFLE_SCRUBS).Is(RO_SCRUBS_AFFORDABLE)) { if (ctx->GetOption(RSK_SHUFFLE_SCRUBS).Is(RO_SCRUBS_AFFORDABLE)) {
for (size_t i = 0; i < Rando::StaticData::scrubLocations.size(); i++) { for (size_t i = 0; i < Rando::StaticData::scrubLocations.size(); i++) {
ctx->GetItemLocation(Rando::StaticData::scrubLocations[i])->SetCustomPrice(10); ctx->GetItemLocation(Rando::StaticData::scrubLocations[i])->SetCustomPrice(10);

View File

@ -18,16 +18,12 @@ typedef enum {
SPOILER_CHK_ITEM_GET_INF, SPOILER_CHK_ITEM_GET_INF,
SPOILER_CHK_EVENT_CHK_INF, SPOILER_CHK_EVENT_CHK_INF,
SPOILER_CHK_INF_TABLE, SPOILER_CHK_INF_TABLE,
SPOILER_CHK_COW,
SPOILER_CHK_FISH, SPOILER_CHK_FISH,
SPOILER_CHK_MINIGAME, SPOILER_CHK_MINIGAME,
SPOILER_CHK_SCRUB,
SPOILER_CHK_GERUDO_MEMBERSHIP_CARD, SPOILER_CHK_GERUDO_MEMBERSHIP_CARD,
SPOILER_CHK_POE_POINTS, SPOILER_CHK_POE_POINTS,
SPOILER_CHK_SHOP_ITEM, SPOILER_CHK_SHOP_ITEM,
SPOILER_CHK_MAGIC_BEANS,
SPOILER_CHK_MASTER_SWORD, SPOILER_CHK_MASTER_SWORD,
SPOILER_CHK_MERCHANT,
SPOILER_CHK_GRAVEDIGGER, SPOILER_CHK_GRAVEDIGGER,
SPOILER_CHK_RANDOMIZER_INF, SPOILER_CHK_RANDOMIZER_INF,
} SpoilerCollectionCheckType; } SpoilerCollectionCheckType;

View File

@ -0,0 +1,975 @@
#include <libultraship/bridge.h>
#include "soh/OTRGlobals.h"
#include "soh/Enhancements/enhancementTypes.h"
#include "soh/Enhancements/custom-message/CustomMessageTypes.h"
#include "soh/Enhancements/randomizer/randomizerTypes.h"
#include "soh/Enhancements/game-interactor/GameInteractor.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
extern "C" {
#include "macros.h"
#include "functions.h"
#include "variables.h"
#include "soh/Enhancements/randomizer/adult_trade_shuffle.h"
#include "src/overlays/actors/ovl_En_Si/z_en_si.h"
#include "src/overlays/actors/ovl_En_Cow/z_en_cow.h"
#include "src/overlays/actors/ovl_En_Shopnuts/z_en_shopnuts.h"
#include "src/overlays/actors/ovl_En_Dns/z_en_dns.h"
#include "src/overlays/actors/ovl_Item_B_Heart/z_item_b_heart.h"
#include "src/overlays/actors/ovl_En_Ko/z_en_ko.h"
#include "src/overlays/actors/ovl_En_Mk/z_en_mk.h"
#include "src/overlays/actors/ovl_En_Niw_Lady/z_en_niw_lady.h"
#include "src/overlays/actors/ovl_En_Kz/z_en_kz.h"
#include "src/overlays/actors/ovl_En_Go2/z_en_go2.h"
#include "src/overlays/actors/ovl_En_Ms/z_en_ms.h"
#include "src/overlays/actors/ovl_En_Fr/z_en_fr.h"
#include "adult_trade_shuffle.h"
extern SaveContext gSaveContext;
extern PlayState* gPlayState;
}
#define RAND_GET_OPTION(option) Rando::Context::GetInstance()->GetOption(option).GetSelectedOptionIndex()
RandomizerCheck GetRandomizerCheckFromFlag(int16_t flagType, int16_t flag) {
for (auto& loc : Rando::StaticData::GetLocationTable()) {
if (loc.GetCollectionCheck().flag == flag && (
(flagType == FLAG_EVENT_CHECK_INF && loc.GetCollectionCheck().type == SPOILER_CHK_EVENT_CHK_INF) ||
(flagType == FLAG_ITEM_GET_INF && loc.GetCollectionCheck().type == SPOILER_CHK_ITEM_GET_INF) ||
(flagType == FLAG_RANDOMIZER_INF && loc.GetCollectionCheck().type == SPOILER_CHK_RANDOMIZER_INF)
) ||
(loc.GetActorParams() == flag && flagType == FLAG_GS_TOKEN && loc.GetCollectionCheck().type == SPOILER_CHK_GOLD_SKULLTULA)
) {
return loc.GetRandomizerCheck();
}
}
return RC_UNKNOWN_CHECK;
}
RandomizerCheck GetRandomizerCheckFromSceneFlag(int16_t sceneNum, int16_t flagType, int16_t flag) {
for (auto& loc : Rando::StaticData::GetLocationTable()) {
if (loc.GetCollectionCheck().scene == sceneNum && loc.GetCollectionCheck().flag == flag && (
(flagType == FLAG_SCENE_TREASURE && loc.GetCollectionCheck().type == SPOILER_CHK_CHEST) ||
(flagType == FLAG_SCENE_COLLECTIBLE && loc.GetCollectionCheck().type == SPOILER_CHK_COLLECTABLE) ||
(flagType == FLAG_GS_TOKEN && loc.GetCollectionCheck().type == SPOILER_CHK_GOLD_SKULLTULA)
)) {
return loc.GetRandomizerCheck();
}
}
return RC_UNKNOWN_CHECK;
}
bool MeetsLACSRequirements() {
switch (RAND_GET_OPTION(RSK_GANONS_BOSS_KEY)) {
case RO_GANON_BOSS_KEY_LACS_STONES:
if ((CheckStoneCount() + CheckLACSRewardCount()) >= RAND_GET_OPTION(RSK_LACS_STONE_COUNT)) {
return true;
}
break;
case RO_GANON_BOSS_KEY_LACS_MEDALLIONS:
if ((CheckMedallionCount() + CheckLACSRewardCount()) >= RAND_GET_OPTION(RSK_LACS_MEDALLION_COUNT)) {
return true;
}
break;
case RO_GANON_BOSS_KEY_LACS_REWARDS:
if ((CheckMedallionCount() + CheckStoneCount() + CheckLACSRewardCount()) >= RAND_GET_OPTION(RSK_LACS_REWARD_COUNT)) {
return true;
}
break;
case RO_GANON_BOSS_KEY_LACS_DUNGEONS:
if ((CheckDungeonCount() + CheckLACSRewardCount()) >= RAND_GET_OPTION(RSK_LACS_DUNGEON_COUNT)) {
return true;
}
break;
case RO_GANON_BOSS_KEY_LACS_TOKENS:
if (gSaveContext.inventory.gsTokens >= RAND_GET_OPTION(RSK_LACS_TOKEN_COUNT)) {
return true;
}
break;
default:
if (CHECK_QUEST_ITEM(QUEST_MEDALLION_SPIRIT) && CHECK_QUEST_ITEM(QUEST_MEDALLION_SHADOW)) {
return true;
}
break;
}
return false;
}
// Todo Move this to randomizer context, clear it out on save load etc
static std::queue<RandomizerCheck> randomizerQueuedChecks;
static RandomizerCheck randomizerQueuedCheck = RC_UNKNOWN_CHECK;
static GetItemEntry randomizerQueuedItemEntry = GET_ITEM_NONE;
void RandomizerOnFlagSetHandler(int16_t flagType, int16_t flag) {
RandomizerCheck rc = GetRandomizerCheckFromFlag(flagType, flag);
if (rc == RC_UNKNOWN_CHECK) return;
auto loc = Rando::Context::GetInstance()->GetItemLocation(rc);
if (loc == nullptr || loc->HasObtained()) return;
SPDLOG_INFO("Queuing RC: {}", rc);
randomizerQueuedChecks.push(rc);
}
void RandomizerOnSceneFlagSetHandler(int16_t sceneNum, int16_t flagType, int16_t flag) {
RandomizerCheck rc = GetRandomizerCheckFromSceneFlag(sceneNum, flagType, flag);
if (rc == RC_UNKNOWN_CHECK) return;
auto loc = Rando::Context::GetInstance()->GetItemLocation(rc);
if (loc == nullptr || loc->HasObtained()) return;
SPDLOG_INFO("Queuing RC: {}", rc);
randomizerQueuedChecks.push(rc);
}
static Vec3f spawnPos = { 0.0f, -999.0f, 0.0f };
void RandomizerOnPlayerUpdateForRCQueueHandler() {
// If we're already queued, don't queue again
if (randomizerQueuedCheck != RC_UNKNOWN_CHECK) return;
// If there's nothing to queue, don't queue
if (randomizerQueuedChecks.size() < 1) return;
// If we're in a cutscene, don't queue
Player* player = GET_PLAYER(gPlayState);
if (Player_InBlockingCsMode(gPlayState, player) || player->stateFlags1 & PLAYER_STATE1_IN_ITEM_CS || player->stateFlags1 & PLAYER_STATE1_GETTING_ITEM || player->stateFlags1 & PLAYER_STATE1_ITEM_OVER_HEAD) {
return;
}
RandomizerCheck rc = randomizerQueuedChecks.front();
auto loc = Rando::Context::GetInstance()->GetItemLocation(rc);
GetItemEntry getItemEntry = Rando::Context::GetInstance()->GetFinalGIEntry(rc, true, (GetItemID)Rando::StaticData::GetLocation(rc)->GetVanillaItem());
if (loc->HasObtained()) {
SPDLOG_INFO("RC {} already obtained, skipping", rc);
} else {
randomizerQueuedCheck = rc;
randomizerQueuedItemEntry = getItemEntry;
SPDLOG_INFO("Queueing Item mod {} item {} from RC {}", getItemEntry.modIndex, getItemEntry.itemId, rc);
if (
// Skipping ItemGet animation incompatible with checks that require closing a text box to finish
rc != RC_HF_OCARINA_OF_TIME_ITEM &&
rc != RC_SPIRIT_TEMPLE_SILVER_GAUNTLETS_CHEST &&
// Always show ItemGet animation for ice traps
!(getItemEntry.modIndex == MOD_RANDOMIZER && getItemEntry.getItemId == RG_ICE_TRAP) &&
(
CVarGetInteger("gTimeSavers.SkipGetItemAnimation", SGIA_DISABLED) == SGIA_ALL ||
(
CVarGetInteger("gTimeSavers.SkipGetItemAnimation", SGIA_DISABLED) == SGIA_JUNK &&
(
getItemEntry.getItemCategory == ITEM_CATEGORY_JUNK ||
getItemEntry.getItemCategory == ITEM_CATEGORY_SKULLTULA_TOKEN ||
getItemEntry.getItemCategory == ITEM_CATEGORY_LESSER
)
)
)
) {
Item_DropCollectible(gPlayState, &spawnPos, ITEM00_SOH_GIVE_ITEM_ENTRY | 0x8000);
}
}
randomizerQueuedChecks.pop();
}
void RandomizerOnPlayerUpdateForItemQueueHandler() {
if (randomizerQueuedCheck == RC_UNKNOWN_CHECK) return;
Player* player = GET_PLAYER(gPlayState);
if (player == NULL || Player_InBlockingCsMode(gPlayState, player) || player->stateFlags1 & PLAYER_STATE1_IN_ITEM_CS || player->stateFlags1 & PLAYER_STATE1_GETTING_ITEM || player->stateFlags1 & PLAYER_STATE1_ITEM_OVER_HEAD) {
return;
}
SPDLOG_INFO("Attempting to give Item mod {} item {} from RC {}", randomizerQueuedItemEntry.modIndex, randomizerQueuedItemEntry.itemId, randomizerQueuedCheck);
GiveItemEntryWithoutActor(gPlayState, randomizerQueuedItemEntry);
if (player->stateFlags1 & PLAYER_STATE1_IN_WATER) {
// Allow the player to receive the item while swimming
player->stateFlags2 |= PLAYER_STATE2_UNDERWATER;
func_8083E5A8(player, gPlayState);
}
}
void RandomizerOnItemReceiveHandler(GetItemEntry receivedItemEntry) {
if (randomizerQueuedCheck == RC_UNKNOWN_CHECK) return;
auto loc = Rando::Context::GetInstance()->GetItemLocation(randomizerQueuedCheck);
if (randomizerQueuedItemEntry.modIndex == receivedItemEntry.modIndex && randomizerQueuedItemEntry.itemId == receivedItemEntry.itemId) {
SPDLOG_INFO("Item received mod {} item {} from RC {}", receivedItemEntry.modIndex, receivedItemEntry.itemId, randomizerQueuedCheck);
loc->MarkAsObtained();
randomizerQueuedCheck = RC_UNKNOWN_CHECK;
randomizerQueuedItemEntry = GET_ITEM_NONE;
}
if (
receivedItemEntry.modIndex == MOD_NONE && (
receivedItemEntry.itemId == ITEM_HEART_PIECE ||
receivedItemEntry.itemId == ITEM_HEART_PIECE_2 ||
receivedItemEntry.itemId == ITEM_HEART_CONTAINER
)
) {
gSaveContext.healthAccumulator = 0x140; // Refill 20 hearts
if ((s32)(gSaveContext.inventory.questItems & 0xF0000000) == 0x40000000) {
gSaveContext.inventory.questItems ^= 0x40000000;
gSaveContext.healthCapacity += 0x10;
gSaveContext.health += 0x10;
}
}
if (loc->GetRandomizerCheck() == RC_SPIRIT_TEMPLE_SILVER_GAUNTLETS_CHEST && !CVarGetInteger("gTimeSavers.SkipCutscene.Story", 0)) {
static uint32_t updateHook;
updateHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnPlayerUpdate>([]() {
Player* player = GET_PLAYER(gPlayState);
if (player == NULL || Player_InBlockingCsMode(gPlayState, player) || player->stateFlags1 & PLAYER_STATE1_IN_ITEM_CS || player->stateFlags1 & PLAYER_STATE1_GETTING_ITEM || player->stateFlags1 & PLAYER_STATE1_ITEM_OVER_HEAD) {
return;
}
gPlayState->nextEntranceIndex = ENTR_DESERT_COLOSSUS_0;
gPlayState->transitionTrigger = TRANS_TRIGGER_START;
gSaveContext.nextCutsceneIndex = 0xFFF1;
gPlayState->transitionType = TRANS_TYPE_SANDSTORM_END;
GET_PLAYER(gPlayState)->stateFlags1 &= ~PLAYER_STATE1_IN_CUTSCENE;
Player_TryCsAction(gPlayState, NULL, 8);
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnPlayerUpdate>(updateHook);
});
}
}
void EnItem00_DrawRandomizedItem(EnItem00* enItem00, PlayState* play) {
f32 mtxScale = CVarGetFloat("gTimeSavers.SkipGetItemAnimationScale", 10.0f);
Matrix_Scale(mtxScale, mtxScale, mtxScale, MTXMODE_APPLY);
EnItem00_CustomItemsParticles(&enItem00->actor, play, enItem00->itemEntry);
GetItemEntry_Draw(play, enItem00->itemEntry);
}
void ItemBHeart_DrawRandomizedItem(ItemBHeart* itemBHeart, PlayState* play) {
EnItem00_CustomItemsParticles(&itemBHeart->actor, play, itemBHeart->sohItemEntry);
GetItemEntry_Draw(play, itemBHeart->sohItemEntry);
}
void ItemBHeart_UpdateRandomizedItem(Actor* actor, PlayState* play) {
ItemBHeart* itemBHeart = (ItemBHeart*)actor;
func_80B85264(itemBHeart, play);
Actor_UpdateBgCheckInfo(play, &itemBHeart->actor, 0.0f, 0.0f, 0.0f, 4);
if ((itemBHeart->actor.xzDistToPlayer < 30.0f) && (fabsf(itemBHeart->actor.yDistToPlayer) < 40.0f)) {
Flags_SetCollectible(play, 0x1F);
Actor_Kill(&itemBHeart->actor);
}
}
void EnCow_MoveForRandomizer(EnCow* enCow, PlayState* play) {
bool moved = false;
// Don't reposition the tail
if (enCow->actor.params != 0) {
return;
}
// Move left cow in lon lon tower
if (play->sceneNum == SCENE_LON_LON_BUILDINGS && enCow->actor.world.pos.x == -108 &&
enCow->actor.world.pos.z == -65) {
enCow->actor.world.pos.x = -229.0f;
enCow->actor.world.pos.z = 157.0f;
enCow->actor.shape.rot.y = 15783.0f;
moved = true;
// Move right cow in lon lon stable
} else if (play->sceneNum == SCENE_STABLE && enCow->actor.world.pos.x == -3 && enCow->actor.world.pos.z == -254) {
enCow->actor.world.pos.x += 119.0f;
moved = true;
}
if (moved) {
// Reposition collider
func_809DEE9C(enCow);
}
}
u8 EnDs_RandoCanGetGrannyItem() {
return RAND_GET_OPTION(RSK_SHUFFLE_MERCHANTS) != RO_SHUFFLE_MERCHANTS_OFF &&
!Flags_GetRandomizerInf(RAND_INF_MERCHANTS_GRANNYS_SHOP) &&
// Traded odd mushroom when adult trade is on
((RAND_GET_OPTION(RSK_SHUFFLE_ADULT_TRADE) && Flags_GetItemGetInf(ITEMGETINF_30)) ||
// Found claim check when adult trade is off
(!RAND_GET_OPTION(RSK_SHUFFLE_ADULT_TRADE) &&
INV_CONTENT(ITEM_CLAIM_CHECK) == ITEM_CLAIM_CHECK));
}
RandomizerCheck EnFr_RandomizerCheckFromSongIndex(u16 songIndex) {
switch (songIndex) {
case FROG_ZL:
return RC_ZR_FROGS_ZELDAS_LULLABY;
case FROG_EPONA:
return RC_ZR_FROGS_EPONAS_SONG;
case FROG_SARIA:
return RC_ZR_FROGS_SARIAS_SONG;
case FROG_SUNS:
return RC_ZR_FROGS_SUNS_SONG;
case FROG_SOT:
return RC_ZR_FROGS_SONG_OF_TIME;
case FROG_STORMS:
return RC_ZR_FROGS_IN_THE_RAIN;
case FROG_CHOIR_SONG:
return RC_ZR_FROGS_OCARINA_GAME;
default:
return RC_UNKNOWN_CHECK;
}
}
void RandomizerOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, void* optionalArg) {
switch (id) {
case GI_VB_GIVE_ITEM_FROM_CHEST: {
Player* player = GET_PLAYER(gPlayState);
player->unk_850 = 1;
player->getItemId = GI_NONE;
player->getItemEntry = GetItemEntry(GET_ITEM_NONE);
*should = false;
break;
}
case GI_VB_PLAY_NABOORU_CAPTURED_CS:
// This behavior is replicated for randomizer in RandomizerOnItemReceiveHandler
*should = false;
break;
case GI_VB_SHIEK_PREPARE_TO_GIVE_SERENADE_OF_WATER: {
*should = !Flags_GetEventChkInf(EVENTCHKINF_LEARNED_SERENADE_OF_WATER) && !Flags_GetTreasure(gPlayState, 0x2);
break;
}
case GI_VB_BE_ELIGIBLE_FOR_SERENADE_OF_WATER:
*should = !Flags_GetEventChkInf(EVENTCHKINF_LEARNED_SERENADE_OF_WATER) && Flags_GetTreasure(gPlayState, 0x2);
break;
case GI_VB_BE_ELIGIBLE_FOR_PRELUDE_OF_LIGHT:
*should = !Flags_GetEventChkInf(EVENTCHKINF_LEARNED_PRELUDE_OF_LIGHT) && CHECK_QUEST_ITEM(QUEST_MEDALLION_FOREST);
break;
case GI_VB_MOVE_MIDO_IN_KOKIRI_FOREST:
if (RAND_GET_OPTION(RSK_FOREST) == RO_FOREST_OPEN) {
*should = true;
}
break;
case GI_VB_MIDO_CONSIDER_DEKU_TREE_DEAD:
*should = Flags_GetEventChkInf(EVENTCHKINF_OBTAINED_KOKIRI_EMERALD_DEKU_TREE_DEAD);
break;
case GI_VB_OPEN_KOKIRI_FOREST:
*should = Flags_GetEventChkInf(EVENTCHKINF_OBTAINED_KOKIRI_EMERALD_DEKU_TREE_DEAD) || RAND_GET_OPTION(RSK_FOREST) != RO_FOREST_CLOSED;
break;
case GI_VB_BE_ELIGIBLE_FOR_DARUNIAS_JOY_REWARD:
*should = !Flags_GetRandomizerInf(RAND_INF_DARUNIAS_JOY);
break;
case GI_VB_BE_ELIGIBLE_FOR_LIGHT_ARROWS:
*should = !Flags_GetEventChkInf(EVENTCHKINF_RETURNED_TO_TEMPLE_OF_TIME_WITH_ALL_MEDALLIONS) && MeetsLACSRequirements();
break;
case GI_VB_BE_ELIGIBLE_FOR_NOCTURNE_OF_SHADOW:
*should =
!Flags_GetEventChkInf(EVENTCHKINF_BONGO_BONGO_ESCAPED_FROM_WELL) &&
LINK_IS_ADULT &&
gEntranceTable[((void)0, gSaveContext.entranceIndex)].scene == SCENE_KAKARIKO_VILLAGE &&
CHECK_QUEST_ITEM(QUEST_MEDALLION_FOREST) &&
CHECK_QUEST_ITEM(QUEST_MEDALLION_FIRE) &&
CHECK_QUEST_ITEM(QUEST_MEDALLION_WATER);
break;
case GI_VB_BE_ELIGIBLE_FOR_CHILD_ROLLING_GORON_REWARD: {
// Don't require a bomb bag to get prize in rando
*should = true;
break;
}
case GI_VB_BE_ELIGIBLE_FOR_MAGIC_BEANS_PURCHASE: {
if (RAND_GET_OPTION(RSK_SHUFFLE_MAGIC_BEANS)) {
*should = gSaveContext.rupees >= 60;
}
break;
}
case GI_VB_GIVE_ITEM_MASTER_SWORD:
if (RAND_GET_OPTION(RSK_SHUFFLE_MASTER_SWORD)) {
*should = false;
} else {
*should = true;
Rando::Context::GetInstance()->GetItemLocation(RC_TOT_MASTER_SWORD)->MarkAsObtained();
}
break;
case GI_VB_ITEM00_DESPAWN: {
EnItem00* item00 = static_cast<EnItem00*>(optionalArg);
if (item00->actor.params == ITEM00_HEART_PIECE || item00->actor.params == ITEM00_SMALL_KEY) {
RandomizerCheck rc = OTRGlobals::Instance->gRandomizer->GetCheckFromActor(item00->actor.id, gPlayState->sceneNum, item00->ogParams);
if (rc != RC_UNKNOWN_CHECK) {
item00->actor.params = ITEM00_SOH_DUMMY;
item00->itemEntry = Rando::Context::GetInstance()->GetFinalGIEntry(rc, true, (GetItemID)Rando::StaticData::GetLocation(rc)->GetVanillaItem());
item00->actor.draw = (ActorFunc)EnItem00_DrawRandomizedItem;
*should = Rando::Context::GetInstance()->GetItemLocation(rc)->HasObtained();
}
} else if (item00->actor.params == ITEM00_SOH_GIVE_ITEM_ENTRY || item00->actor.params == ITEM00_SOH_GIVE_ITEM_ENTRY_GI) {
GetItemEntry itemEntry = randomizerQueuedItemEntry;
item00->itemEntry = itemEntry;
item00->actor.draw = (ActorFunc)EnItem00_DrawRandomizedItem;
}
break;
}
case GI_VB_MALON_ALREADY_TAUGHT_EPONAS_SONG: {
*should = Flags_GetRandomizerInf(RAND_INF_LEARNED_EPONA_SONG);
break;
}
case GI_VB_SET_CUCCO_COUNT: {
EnNiwLady* enNiwLady = static_cast<EnNiwLady*>(optionalArg);
// Override starting Cucco count using setting value
enNiwLady->cuccosInPen = 7 - RAND_GET_OPTION(RSK_CUCCO_COUNT);
*should = false;
break;
}
case GI_VB_KING_ZORA_THANK_CHILD: {
// Allow turning in Ruto's letter even if you have already rescued her
if (!Flags_GetEventChkInf(EVENTCHKINF_KING_ZORA_MOVED)) {
GET_PLAYER(gPlayState)->exchangeItemId = EXCH_ITEM_LETTER_RUTO;
}
*should = Flags_GetRandomizerInf(RAND_INF_DUNGEONS_DONE_JABU_JABUS_BELLY);
break;
}
case GI_VB_BE_ABLE_TO_EXCHANGE_RUTOS_LETTER: {
*should = LINK_IS_CHILD;
break;
}
case GI_VB_KING_ZORA_BE_MOVED: {
*should = false;
switch (RAND_GET_OPTION(RSK_ZORAS_FOUNTAIN)) {
case RO_ZF_CLOSED:
if (Flags_GetEventChkInf(EVENTCHKINF_KING_ZORA_MOVED)) {
*should = true;
}
break;
case RO_ZF_CLOSED_CHILD:
if (LINK_IS_ADULT) {
*should = true;
} else if (Flags_GetEventChkInf(EVENTCHKINF_KING_ZORA_MOVED)) {
*should = true;
}
break;
case RO_ZF_OPEN:
*should = true;
break;
}
break;
}
case GI_VB_BIGGORON_CONSIDER_SWORD_COLLECTED: {
*should = Flags_GetRandomizerInf(RAND_INF_ADULT_TRADES_DMT_TRADE_CLAIM_CHECK);
break;
}
case GI_VB_BIGGORON_CONSIDER_TRADE_COMPLETE: {
*should = INV_CONTENT(ITEM_TRADE_ADULT) == ITEM_CLAIM_CHECK && Flags_GetRandomizerInf(RAND_INF_ADULT_TRADES_DMT_TRADE_CLAIM_CHECK);
break;
}
case GI_VB_GORONS_CONSIDER_FIRE_TEMPLE_FINISHED: {
*should = Flags_GetEventChkInf(EVENTCHKINF_USED_FIRE_TEMPLE_BLUE_WARP);
break;
}
case GI_VB_GORONS_CONSIDER_DODONGOS_CAVERN_FINISHED: {
*should = Flags_GetEventChkInf(EVENTCHKINF_USED_DODONGOS_CAVERN_BLUE_WARP);
break;
}
case GI_VB_OVERRIDE_LINK_THE_GORON_DIALOGUE: {
u16* textId = static_cast<u16*>(optionalArg);
*should = true;
// For rando, prioritize opening the doors in GC when Link the goron has been stopped when
// the doors are not opened, otherwise let him talk about the DMC exit or that gorons are saved
if (!Flags_GetInfTable(INFTABLE_STOPPED_GORON_LINKS_ROLLING)) {
*textId = 0x3030;
} else if (!Flags_GetInfTable(INFTABLE_GORON_CITY_DOORS_UNLOCKED)) {
*textId = 0x3036;
} else if (Flags_GetEventChkInf(EVENTCHKINF_USED_FIRE_TEMPLE_BLUE_WARP)) {
*textId = 0x3041;
} else {
*textId = Flags_GetInfTable(INFTABLE_SPOKE_TO_GORON_LINK) ? 0x3038 : 0x3037;
}
break;
}
case GI_VB_EN_GO2_RESET_AFTER_GET_ITEM: {
EnGo2* enGo2 = static_cast<EnGo2*>(optionalArg);
// For randomizer, handle updating the states for the gorons after receiving the item based on
// the goron type rather then the item being received
switch (enGo2->actor.params & 0x1F) {
case GORON_DMT_BIGGORON:
// Resolves #1301. unk_13EE is used to set the opacity of the HUD. The trade sequence discussion
// with Biggoron sets the HUD to transparent, and it is restored at z_message_PAL:3549, but by
// specifically watching for trade sequence items, this leaves it transparent for non-trade sequence
// items (in rando) so we fix that here
gSaveContext.unk_13EE = 0x32;
break;
case GORON_CITY_LINK:
EnGo2_GetItemAnimation(enGo2, gPlayState);
break;
case GORON_CITY_ROLLING_BIG:
EnGo2_RollingAnimation(enGo2, gPlayState);
enGo2->actionFunc = (EnGo2ActionFunc)EnGo2_GoronRollingBigContinueRolling;
break;
default:
enGo2->actionFunc = func_80A46B40;
break;
}
break;
}
case GI_VB_GIVE_ITEM_FROM_ITEM_00: {
EnItem00* item00 = static_cast<EnItem00*>(optionalArg);
if (item00->actor.params == ITEM00_SOH_DUMMY) {
Flags_SetCollectible(gPlayState, item00->collectibleFlag);
Actor_Kill(&item00->actor);
*should = false;
} else if (item00->actor.params == ITEM00_SOH_GIVE_ITEM_ENTRY) {
Audio_PlaySoundGeneral(NA_SE_SY_GET_ITEM, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8);
if (item00->itemEntry.modIndex == MOD_NONE) {
if (item00->itemEntry.getItemId == GI_SWORD_BGS) {
gSaveContext.bgsFlag = true;
}
Item_Give(gPlayState, item00->itemEntry.itemId);
} else if (item00->itemEntry.modIndex == MOD_RANDOMIZER) {
if (item00->itemEntry.getItemId == RG_ICE_TRAP) {
gSaveContext.pendingIceTrapCount++;
} else {
Randomizer_Item_Give(gPlayState, item00->itemEntry);
}
}
// EnItem00_SetupAction(item00, func_8001E5C8);
// *should = false;
} else if (item00->actor.params == ITEM00_SOH_GIVE_ITEM_ENTRY_GI) {
if (!Actor_HasParent(&item00->actor, gPlayState)) {
GiveItemEntryFromActorWithFixedRange(&item00->actor, gPlayState, item00->itemEntry);
}
EnItem00_SetupAction(item00, func_8001E5C8);
*should = false;
}
break;
}
case GI_VB_BE_ELIGIBLE_FOR_SARIAS_SONG: {
*should = !Flags_GetEventChkInf(EVENTCHKINF_LEARNED_SARIAS_SONG);
break;
}
case GI_VB_GIVE_ITEM_FROM_COW: {
if (!RAND_GET_OPTION(RSK_SHUFFLE_COWS)) {
break;
}
EnCow* enCow = static_cast<EnCow*>(optionalArg);
CowIdentity cowIdentity = OTRGlobals::Instance->gRandomizer->IdentifyCow(gPlayState->sceneNum, enCow->actor.world.pos.x, enCow->actor.world.pos.z);
// Has this cow already rewarded an item?
if (Flags_GetRandomizerInf(cowIdentity.randomizerInf)) {
break;
}
Flags_SetRandomizerInf(cowIdentity.randomizerInf);
// setting the ocarina mode here prevents intermittent issues
// with the item get not triggering until walking away
gPlayState->msgCtx.ocarinaMode = OCARINA_MODE_00;
*should = false;
break;
}
case GI_VB_GIVE_ITEM_FROM_GRANNYS_SHOP: {
if (!EnDs_RandoCanGetGrannyItem()) {
break;
}
// Only setting the inf if we've actually gotten the rando item and not the vanilla blue potion
Flags_SetRandomizerInf(RAND_INF_MERCHANTS_GRANNYS_SHOP);
*should = false;
break;
}
case GI_VB_GIVE_ITEM_FROM_THAWING_KING_ZORA: {
EnKz* enKz = static_cast<EnKz*>(optionalArg);
// If we aren't setting up the item offer, then we're just checking if it should be possible.
if (enKz->actionFunc != (EnKzActionFunc)EnKz_SetupGetItem) {
// Always give the reward in rando
*should = true;
break;
}
*should = false;
break;
}
case GI_VB_GIVE_ITEM_FROM_ANJU_AS_CHILD: {
Flags_SetItemGetInf(ITEMGETINF_0C);
*should = false;
break;
}
case GI_VB_GIVE_ITEM_FROM_ANJU_AS_ADULT: {
Flags_SetItemGetInf(ITEMGETINF_2C);
*should = false;
break;
}
case GI_VB_GIVE_ITEM_FROM_ROLLING_GORON_AS_ADULT: {
EnGo2* enGo2 = static_cast<EnGo2*>(optionalArg);
*should = false;
if (!Flags_GetRandomizerInf(RAND_INF_ROLLING_GORON_AS_ADULT)) {
Flags_SetInfTable(INFTABLE_GORON_CITY_DOORS_UNLOCKED);
enGo2->interactInfo.talkState = NPC_TALK_STATE_ACTION;
}
break;
}
case GI_VB_GIVE_ITEM_FROM_CARPET_SALESMAN: {
*should = RAND_GET_OPTION(RSK_SHUFFLE_MERCHANTS) == RO_SHUFFLE_MERCHANTS_OFF ||
// If the rando check has already been awarded, use vanilla behavior.
Flags_GetRandomizerInf(RAND_INF_MERCHANTS_CARPET_SALESMAN);
break;
}
case GI_VB_GIVE_ITEM_FROM_MEDIGORON: {
// fallthrough
case GI_VB_BE_ELIGIBLE_FOR_GIANTS_KNIFE_PURCHASE:
if (RAND_GET_OPTION(RSK_SHUFFLE_MERCHANTS) != RO_SHUFFLE_MERCHANTS_OFF &&
!Flags_GetRandomizerInf(RAND_INF_MERCHANTS_MEDIGORON)) {
if (id == GI_VB_GIVE_ITEM_FROM_MEDIGORON) {
Flags_SetInfTable(INFTABLE_B1);
*should = false;
} else {
// Resets "Talked to Medigoron" flag in infTable to restore initial conversation state
Flags_UnsetInfTable(INFTABLE_B1);
*should = true;
}
}
break;
}
case GI_VB_GIVE_ITEM_FROM_MAGIC_BEAN_SALESMAN: {
EnMs* enMs = static_cast<EnMs*>(optionalArg);
if (RAND_GET_OPTION(RSK_SHUFFLE_MAGIC_BEANS)) {
Rupees_ChangeBy(-60);
BEANS_BOUGHT = 10;
// Only set inf for buying rando check
Flags_SetRandomizerInf(RAND_INF_MERCHANTS_MAGIC_BEAN_SALESMAN);
enMs->actionFunc = (EnMsActionFunc)EnMs_Wait;
*should = false;
}
break;
}
case GI_VB_GIVE_ITEM_FROM_FROGS: {
EnFr* enFr = static_cast<EnFr*>(optionalArg);
// Skip GiveReward+SetIdle action func if the reward is an ice trap
if (enFr->actionFunc == (EnFrActionFunc)EnFr_GiveReward) {
RandomizerCheck rc = EnFr_RandomizerCheckFromSongIndex(enFr->songIndex);
GetItemEntry gi = Rando::Context::GetInstance()->GetFinalGIEntry(rc, true, (GetItemID)Rando::StaticData::GetLocation(rc)->GetVanillaItem());
if (gi.getItemId == RG_ICE_TRAP) {
enFr->actionFunc = (EnFrActionFunc)EnFr_Idle;
}
}
*should = false;
break;
}
case GI_VB_TRADE_POCKET_CUCCO: {
Randomizer_ConsumeAdultTradeItem(gPlayState, ITEM_POCKET_CUCCO);
// Trigger the reward now
Flags_SetItemGetInf(ITEMGETINF_2E);
*should = false;
break;
}
case GI_VB_TRADE_COJIRO: {
Randomizer_ConsumeAdultTradeItem(gPlayState, ITEM_COJIRO);
*should = false;
break;
}
case GI_VB_TRADE_ODD_MUSHROOM: {
Randomizer_ConsumeAdultTradeItem(gPlayState, ITEM_ODD_MUSHROOM);
// Trigger the reward now
Flags_SetItemGetInf(ITEMGETINF_30);
*should = false;
break;
}
case GI_VB_TRADE_ODD_POTION: {
EnKo* enKo = static_cast<EnKo*>(optionalArg);
Randomizer_ConsumeAdultTradeItem(gPlayState, ITEM_ODD_POTION);
// Trigger the reward now
Flags_SetItemGetInf(ITEMGETINF_31);
*should = false;
break;
}
case GI_VB_TRADE_SAW: {
Randomizer_ConsumeAdultTradeItem(gPlayState, ITEM_SAW);
*should = false;
break;
}
case GI_VB_TRADE_PRESCRIPTION: {
EnKz* enKz = static_cast<EnKz*>(optionalArg);
// If we aren't setting up the item offer, then we're just checking if it should be possible.
if (enKz->actionFunc != (EnKzActionFunc)EnKz_SetupGetItem) {
*should = !Flags_GetRandomizerInf(RAND_INF_ADULT_TRADES_ZD_TRADE_PRESCRIPTION);
break;
}
Randomizer_ConsumeAdultTradeItem(gPlayState, ITEM_PRESCRIPTION);
*should = false;
break;
}
case GI_VB_TRADE_FROG: {
Randomizer_ConsumeAdultTradeItem(gPlayState, ITEM_FROG);
*should = false;
break;
}
case GI_VB_TRADE_BROKEN_SWORD: {
Randomizer_ConsumeAdultTradeItem(gPlayState, ITEM_SWORD_BROKEN);
*should = false;
break;
}
case GI_VB_TRADE_EYEDROPS: {
Randomizer_ConsumeAdultTradeItem(gPlayState, ITEM_EYEDROPS);
*should = false;
break;
}
case GI_VB_DESPAWN_HORSE_RACE_COW: {
if (!RAND_GET_OPTION(RSK_SHUFFLE_COWS)) {
break;
}
EnCow* enCow = static_cast<EnCow*>(optionalArg);
// If this is a cow we have to move, then move it now.
EnCow_MoveForRandomizer(enCow, gPlayState);
break;
}
case GI_VB_BUSINESS_SCRUB_DESPAWN: {
EnShopnuts* enShopnuts = static_cast<EnShopnuts*>(optionalArg);
s16 respawnData = gSaveContext.respawn[RESPAWN_MODE_RETURN].data & ((1 << 8) - 1);
ScrubIdentity scrubIdentity = OTRGlobals::Instance->gRandomizer->IdentifyScrub(gPlayState->sceneNum, enShopnuts->actor.params, respawnData);
if (scrubIdentity.isShuffled) {
*should = Flags_GetRandomizerInf(scrubIdentity.randomizerInf);
}
break;
}
case GI_VB_GIVE_ITEM_FROM_BUSINESS_SCRUB: {
EnDns* enDns = static_cast<EnDns*>(optionalArg);
*should = !enDns->sohScrubIdentity.isShuffled;
break;
}
// To explain the logic because Fado and Grog are linked:
// - If you have Cojiro, then spawn Grog and not Fado.
// - If you don't have Cojiro but do have Odd Potion, spawn Fado and not Grog.
// - If you don't have either, spawn Grog if you haven't traded the Odd Mushroom.
// - If you don't have either but have traded the mushroom, don't spawn either.
case GI_VB_DESPAWN_GROG: {
if (!RAND_GET_OPTION(RSK_SHUFFLE_ADULT_TRADE)) {
break;
}
if (PLAYER_HAS_SHUFFLED_ADULT_TRADE_ITEM(ITEM_COJIRO)) {
*should = false;
} else if (PLAYER_HAS_SHUFFLED_ADULT_TRADE_ITEM(ITEM_ODD_POTION)) {
*should = true;
} else {
*should = Flags_GetItemGetInf(ITEMGETINF_30); // Traded odd mushroom
}
break;
}
case GI_VB_SPAWN_LW_FADO: {
if (!RAND_GET_OPTION(RSK_SHUFFLE_ADULT_TRADE)) {
break;
}
if (PLAYER_HAS_SHUFFLED_ADULT_TRADE_ITEM(ITEM_COJIRO)) {
*should = false;
} else {
*should = PLAYER_HAS_SHUFFLED_ADULT_TRADE_ITEM(ITEM_ODD_POTION);
}
break;
}
case GI_VB_USE_EYEDROP_DIALOGUE: {
// Skip eye drop text on rando if Link went in the water, so you can still receive the dive check
EnMk* enMk = static_cast<EnMk*>(optionalArg);
*should &= enMk->swimFlag == 0;
break;
}
case GI_VB_OFFER_BLUE_POTION: {
// Always offer blue potion when adult trade is off
*should |= RAND_GET_OPTION(RSK_SHUFFLE_ADULT_TRADE) == RO_GENERIC_OFF;
break;
}
case GI_VB_NEED_BOTTLE_FOR_GRANNYS_ITEM: {
// Allow buying the rando item regardless of having a bottle
*should &= !EnDs_RandoCanGetGrannyItem();
break;
}
case GI_VB_TRADE_CLAIM_CHECK:
case GI_VB_TRADE_TIMER_ODD_MUSHROOM:
case GI_VB_TRADE_TIMER_EYEDROPS:
case GI_VB_TRADE_TIMER_FROG:
case GI_VB_ANJU_SET_OBTAINED_TRADE_ITEM:
case GI_VB_GIVE_ITEM_FROM_ROLLING_GORON_AS_CHILD:
case GI_VB_GIVE_ITEM_FROM_LAB_DIVE:
case GI_VB_GIVE_ITEM_SKULL_TOKEN:
case GI_VB_GIVE_ITEM_FROM_BLUE_WARP:
case GI_VB_GIVE_ITEM_FAIRY_OCARINA:
case GI_VB_GIVE_ITEM_WEIRD_EGG:
case GI_VB_GIVE_ITEM_LIGHT_ARROW:
case GI_VB_GIVE_ITEM_STRENGTH_1:
case GI_VB_GIVE_ITEM_ZELDAS_LETTER:
case GI_VB_GIVE_ITEM_OCARINA_OF_TIME:
case GI_VB_GIVE_ITEM_KOKIRI_EMERALD:
case GI_VB_GIVE_ITEM_GORON_RUBY:
case GI_VB_GIVE_ITEM_ZORA_SAPPHIRE:
case GI_VB_GIVE_ITEM_LIGHT_MEDALLION:
case GI_VB_GIVE_ITEM_FOREST_MEDALLION:
case GI_VB_GIVE_ITEM_FIRE_MEDALLION:
case GI_VB_GIVE_ITEM_WATER_MEDALLION:
case GI_VB_GIVE_ITEM_SPIRIT_MEDALLION:
case GI_VB_GIVE_ITEM_SHADOW_MEDALLION:
*should = false;
break;
}
}
void RandomizerOnSceneInitHandler(int16_t sceneNum) {
// LACs & Prelude checks
static uint32_t updateHook = 0;
if (updateHook) {
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnPlayerUpdate>(updateHook);
updateHook = 0;
}
if (
sceneNum != SCENE_TEMPLE_OF_TIME ||
(
Flags_GetEventChkInf(EVENTCHKINF_LEARNED_PRELUDE_OF_LIGHT) &&
Flags_GetEventChkInf(EVENTCHKINF_RETURNED_TO_TEMPLE_OF_TIME_WITH_ALL_MEDALLIONS)
)
) return;
updateHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnPlayerUpdate>([]() {
if (!Flags_GetEventChkInf(EVENTCHKINF_LEARNED_PRELUDE_OF_LIGHT) && LINK_IS_ADULT && CHECK_QUEST_ITEM(QUEST_MEDALLION_FOREST) && gPlayState->roomCtx.curRoom.num == 0) {
Flags_SetEventChkInf(EVENTCHKINF_LEARNED_PRELUDE_OF_LIGHT);
}
if (!Flags_GetEventChkInf(EVENTCHKINF_RETURNED_TO_TEMPLE_OF_TIME_WITH_ALL_MEDALLIONS) && MeetsLACSRequirements()) {
Flags_SetEventChkInf(EVENTCHKINF_RETURNED_TO_TEMPLE_OF_TIME_WITH_ALL_MEDALLIONS);
}
if (
Flags_GetEventChkInf(EVENTCHKINF_LEARNED_PRELUDE_OF_LIGHT) &&
Flags_GetEventChkInf(EVENTCHKINF_RETURNED_TO_TEMPLE_OF_TIME_WITH_ALL_MEDALLIONS)
) {
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnPlayerUpdate>(updateHook);
}
});
}
void EnSi_DrawRandomizedItem(EnSi* enSi, PlayState* play) {
func_8002ED80(&enSi->actor, play, 0);
func_8002EBCC(&enSi->actor, play, 0);
EnItem00_CustomItemsParticles(&enSi->actor, play, enSi->sohGetItemEntry);
GetItemEntry_Draw(play, enSi->sohGetItemEntry);
}
u32 EnDns_RandomizerPurchaseableCheck(EnDns* enDns) {
if (Flags_GetRandomizerInf(enDns->sohScrubIdentity.randomizerInf)) {
return 3; // Can't get this now
}
if (gSaveContext.rupees < enDns->dnsItemEntry->itemPrice) {
return 0; // Not enough rupees
}
return 4;
}
void EnDns_RandomizerPurchase(EnDns* enDns) {
Rupees_ChangeBy(-enDns->dnsItemEntry->itemPrice);
Flags_SetRandomizerInf(enDns->sohScrubIdentity.randomizerInf);
}
void RandomizerOnActorInitHandler(void* actorRef) {
Actor* actor = static_cast<Actor*>(actorRef);
if (actor->id == ACTOR_EN_SI) {
RandomizerCheck rc = OTRGlobals::Instance->gRandomizer->GetCheckFromActor(actor->id, gPlayState->sceneNum, actor->params);
if (rc != RC_UNKNOWN_CHECK) {
EnSi* enSi = static_cast<EnSi*>(actorRef);
enSi->sohGetItemEntry = Rando::Context::GetInstance()->GetFinalGIEntry(rc, true, (GetItemID)Rando::StaticData::GetLocation(rc)->GetVanillaItem());
actor->draw = (ActorFunc)EnSi_DrawRandomizedItem;
}
}
if (actor->id == ACTOR_ITEM_B_HEART) {
ItemBHeart* itemBHeart = static_cast<ItemBHeart*>(actorRef);
RandomizerCheck rc = OTRGlobals::Instance->gRandomizer->GetCheckFromActor(itemBHeart->actor.id, gPlayState->sceneNum, itemBHeart->actor.params);
if (rc != RC_UNKNOWN_CHECK) {
itemBHeart->sohItemEntry = Rando::Context::GetInstance()->GetFinalGIEntry(rc, true, (GetItemID)Rando::StaticData::GetLocation(rc)->GetVanillaItem());
itemBHeart->actor.draw = (ActorFunc)ItemBHeart_DrawRandomizedItem;
itemBHeart->actor.update = (ActorFunc)ItemBHeart_UpdateRandomizedItem;
}
}
if (actor->id == ACTOR_EN_DNS) {
EnDns* enDns = static_cast<EnDns*>(actorRef);
s16 respawnData = gSaveContext.respawn[RESPAWN_MODE_RETURN].data & ((1 << 8) - 1);
enDns->sohScrubIdentity = OTRGlobals::Instance->gRandomizer->IdentifyScrub(gPlayState->sceneNum, enDns->actor.params, respawnData);
if (enDns->sohScrubIdentity.isShuffled) {
// DNS uses pointers so we're creating our own entry instead of modifying the original
enDns->sohDnsItemEntry = {
enDns->dnsItemEntry->itemPrice,
1,
enDns->sohScrubIdentity.getItemId,
EnDns_RandomizerPurchaseableCheck,
EnDns_RandomizerPurchase,
};
enDns->dnsItemEntry = &enDns->sohDnsItemEntry;
if (enDns->sohScrubIdentity.itemPrice != -1) {
enDns->dnsItemEntry->itemPrice = enDns->sohScrubIdentity.itemPrice;
}
enDns->actor.textId = TEXT_SCRUB_RANDOM;
static uint32_t enDnsUpdateHook = 0;
static uint32_t enDnsKillHook = 0;
if (!enDnsUpdateHook) {
enDnsUpdateHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnActorUpdate>([](void* innerActorRef) {
Actor* innerActor = static_cast<Actor*>(innerActorRef);
if (innerActor->id == ACTOR_EN_DNS) {
EnDns* innerEnDns = static_cast<EnDns*>(innerActorRef);
if (innerEnDns->sohScrubIdentity.isShuffled) {
innerActor->textId = TEXT_SCRUB_RANDOM;
}
}
});
enDnsKillHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSceneInit>([](int16_t sceneNum) {
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnActorUpdate>(enDnsUpdateHook);
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnSceneInit>(enDnsKillHook);
enDnsUpdateHook = 0;
enDnsKillHook = 0;
});
}
}
}
}
void RandomizerRegisterHooks() {
static uint32_t onFlagSetHook = 0;
static uint32_t onSceneFlagSetHook = 0;
static uint32_t onPlayerUpdateForRCQueueHook = 0;
static uint32_t onPlayerUpdateForItemQueueHook = 0;
static uint32_t onItemReceiveHook = 0;
static uint32_t onVanillaBehaviorHook = 0;
static uint32_t onSceneInitHook = 0;
static uint32_t onActorInitHook = 0;
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnLoadGame>([](int32_t fileNum) {
randomizerQueuedChecks = std::queue<RandomizerCheck>();
randomizerQueuedCheck = RC_UNKNOWN_CHECK;
randomizerQueuedItemEntry = GET_ITEM_NONE;
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnFlagSet>(onFlagSetHook);
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnSceneFlagSet>(onSceneFlagSetHook);
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnPlayerUpdate>(onPlayerUpdateForRCQueueHook);
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnPlayerUpdate>(onPlayerUpdateForItemQueueHook);
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnItemReceive>(onItemReceiveHook);
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnVanillaBehavior>(onVanillaBehaviorHook);
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnSceneInit>(onSceneInitHook);
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnActorInit>(onActorInitHook);
onFlagSetHook = 0;
onSceneFlagSetHook = 0;
onPlayerUpdateForRCQueueHook = 0;
onPlayerUpdateForItemQueueHook = 0;
onItemReceiveHook = 0;
onVanillaBehaviorHook = 0;
onSceneInitHook = 0;
onActorInitHook = 0;
if (!IS_RANDO) return;
onFlagSetHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnFlagSet>(RandomizerOnFlagSetHandler);
onSceneFlagSetHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSceneFlagSet>(RandomizerOnSceneFlagSetHandler);
onPlayerUpdateForRCQueueHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnPlayerUpdate>(RandomizerOnPlayerUpdateForRCQueueHandler);
onPlayerUpdateForItemQueueHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnPlayerUpdate>(RandomizerOnPlayerUpdateForItemQueueHandler);
onItemReceiveHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnItemReceive>(RandomizerOnItemReceiveHandler);
onVanillaBehaviorHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnVanillaBehavior>(RandomizerOnVanillaBehaviorHandler);
onSceneInitHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSceneInit>(RandomizerOnSceneInitHandler);
onActorInitHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnActorInit>(RandomizerOnActorInitHandler);
});
}

View File

@ -0,0 +1,6 @@
#ifndef RANDOMIZER_HOOK_HANDLERS_H
#define RANDOMIZER_HOOK_HANDLERS_H
void RandomizerRegisterHooks();
#endif // RANDOMIZER_HOOK_HANDLERS_H

View File

@ -101,6 +101,18 @@ void ItemLocation::SetCustomPrice(const uint16_t price_) {
hasCustomPrice = true; hasCustomPrice = true;
} }
bool ItemLocation::HasObtained() const {
return obtained;
}
void ItemLocation::MarkAsObtained() {
obtained = true;
}
void ItemLocation::MarkAsNotObtained() {
obtained = false;
}
bool ItemLocation::IsHintable() const { bool ItemLocation::IsHintable() const {
return isHintable; return isHintable;
} }
@ -201,5 +213,6 @@ void ItemLocation::ResetVariables() {
wothCandidate = false; wothCandidate = false;
barrenCandidate = false; barrenCandidate = false;
area = RA_NONE; area = RA_NONE;
obtained = false;
} }
} }

View File

@ -31,6 +31,9 @@ class ItemLocation {
void SetPrice(uint16_t price_); void SetPrice(uint16_t price_);
bool HasCustomPrice() const; bool HasCustomPrice() const;
void SetCustomPrice(uint16_t price_); void SetCustomPrice(uint16_t price_);
bool HasObtained() const;
void MarkAsObtained();
void MarkAsNotObtained();
bool IsHintable() const; bool IsHintable() const;
void SetAsHintable(); void SetAsHintable();
bool IsHintedAt() const; bool IsHintedAt() const;
@ -67,5 +70,6 @@ class ItemLocation {
bool visibleInImGui = false; bool visibleInImGui = false;
bool wothCandidate = false; bool wothCandidate = false;
bool barrenCandidate = false; bool barrenCandidate = false;
bool obtained = false;
}; };
} // namespace Rando } // namespace Rando

View File

@ -51,10 +51,6 @@ class SpoilerCollectionCheck {
return SpoilerCollectionCheck(SPOILER_CHK_CHEST, scene, flag); return SpoilerCollectionCheck(SPOILER_CHK_CHEST, scene, flag);
} }
static auto Cow(const uint8_t scene, const uint8_t flag) {
return SpoilerCollectionCheck(SPOILER_CHK_COW, scene, flag);
}
static auto Fish(const uint8_t flag, const uint8_t scene = SCENE_FISHING_POND) { static auto Fish(const uint8_t flag, const uint8_t scene = SCENE_FISHING_POND) {
return SpoilerCollectionCheck(SPOILER_CHK_FISH, scene, flag); return SpoilerCollectionCheck(SPOILER_CHK_FISH, scene, flag);
} }
@ -63,10 +59,6 @@ class SpoilerCollectionCheck {
return SpoilerCollectionCheck(SPOILER_CHK_MINIGAME, 0x00, bit); return SpoilerCollectionCheck(SPOILER_CHK_MINIGAME, 0x00, bit);
} }
static auto Scrub(const uint8_t scene, const uint8_t bit) {
return SpoilerCollectionCheck(SPOILER_CHK_SCRUB, scene, bit);
}
static auto GerudoToken() { static auto GerudoToken() {
return SpoilerCollectionCheck(SPOILER_CHK_GERUDO_MEMBERSHIP_CARD, 0x00, 0x00); return SpoilerCollectionCheck(SPOILER_CHK_GERUDO_MEMBERSHIP_CARD, 0x00, 0x00);
} }
@ -83,20 +75,12 @@ class SpoilerCollectionCheck {
return SpoilerCollectionCheck(SPOILER_CHK_SHOP_ITEM, scene, itemSlot); return SpoilerCollectionCheck(SPOILER_CHK_SHOP_ITEM, scene, itemSlot);
} }
static auto MagicBeans(const uint8_t scene, const uint8_t flag) {
return SpoilerCollectionCheck(SPOILER_CHK_MAGIC_BEANS, scene, flag);
}
static auto MasterSword() { static auto MasterSword() {
return SpoilerCollectionCheck(SPOILER_CHK_MASTER_SWORD, 0x00, 0x00); return SpoilerCollectionCheck(SPOILER_CHK_MASTER_SWORD, 0x00, 0x00);
} }
static auto Merchant(const int8_t scene, const uint8_t flag) { static auto RandomizerInf(const uint8_t flag) {
return SpoilerCollectionCheck(SPOILER_CHK_MERCHANT, scene, flag); return SpoilerCollectionCheck(SPOILER_CHK_RANDOMIZER_INF, 0x00, flag);
}
static auto RandomizerInf(const int8_t scene, const uint8_t flag) {
return SpoilerCollectionCheck(SPOILER_CHK_RANDOMIZER_INF, scene, flag);
} }
}; };

File diff suppressed because it is too large Load Diff

View File

@ -419,11 +419,8 @@ bool HasItemBeenCollected(RandomizerCheck rc) {
case SpoilerCollectionCheckType::SPOILER_CHK_COLLECTABLE: case SpoilerCollectionCheckType::SPOILER_CHK_COLLECTABLE:
return (gPlayState->sceneNum == scene && gPlayState->actorCtx.flags.collect & (1 << flag)) || return (gPlayState->sceneNum == scene && gPlayState->actorCtx.flags.collect & (1 << flag)) ||
gSaveContext.sceneFlags[scene].collect & (1 << flag); gSaveContext.sceneFlags[scene].collect & (1 << flag);
case SpoilerCollectionCheckType::SPOILER_CHK_MERCHANT:
case SpoilerCollectionCheckType::SPOILER_CHK_SHOP_ITEM: case SpoilerCollectionCheckType::SPOILER_CHK_SHOP_ITEM:
case SpoilerCollectionCheckType::SPOILER_CHK_COW:
case SpoilerCollectionCheckType::SPOILER_CHK_FISH: case SpoilerCollectionCheckType::SPOILER_CHK_FISH:
case SpoilerCollectionCheckType::SPOILER_CHK_SCRUB:
case SpoilerCollectionCheckType::SPOILER_CHK_RANDOMIZER_INF: case SpoilerCollectionCheckType::SPOILER_CHK_RANDOMIZER_INF:
case SpoilerCollectionCheckType::SPOILER_CHK_MASTER_SWORD: case SpoilerCollectionCheckType::SPOILER_CHK_MASTER_SWORD:
return Flags_GetRandomizerInf(OTRGlobals::Instance->gRandomizer->GetRandomizerInfFromCheck(rc)); return Flags_GetRandomizerInf(OTRGlobals::Instance->gRandomizer->GetRandomizerInfFromCheck(rc));
@ -437,8 +434,6 @@ bool HasItemBeenCollected(RandomizerCheck rc) {
return gSaveContext.infTable[scene] & INDEX_TO_16BIT_LITTLE_ENDIAN_BITMASK(flag); return gSaveContext.infTable[scene] & INDEX_TO_16BIT_LITTLE_ENDIAN_BITMASK(flag);
case SpoilerCollectionCheckType::SPOILER_CHK_ITEM_GET_INF: case SpoilerCollectionCheckType::SPOILER_CHK_ITEM_GET_INF:
return gSaveContext.itemGetInf[flag / 16] & INDEX_TO_16BIT_LITTLE_ENDIAN_BITMASK(flag); return gSaveContext.itemGetInf[flag / 16] & INDEX_TO_16BIT_LITTLE_ENDIAN_BITMASK(flag);
case SpoilerCollectionCheckType::SPOILER_CHK_MAGIC_BEANS:
return BEANS_BOUGHT >= 10;
case SpoilerCollectionCheckType::SPOILER_CHK_NONE: case SpoilerCollectionCheckType::SPOILER_CHK_NONE:
return false; return false;
case SpoilerCollectionCheckType::SPOILER_CHK_GRAVEDIGGER: case SpoilerCollectionCheckType::SPOILER_CHK_GRAVEDIGGER:
@ -749,11 +744,8 @@ void CheckTrackerFlagSet(int16_t flagType, int32_t flag) {
Rando::SpoilerCollectionCheck scCheck = loc.GetCollectionCheck(); Rando::SpoilerCollectionCheck scCheck = loc.GetCollectionCheck();
SpoilerCollectionCheckType scCheckType = scCheck.type; SpoilerCollectionCheckType scCheckType = scCheck.type;
if (checkMatchType == SpoilerCollectionCheckType::SPOILER_CHK_RANDOMIZER_INF && if (checkMatchType == SpoilerCollectionCheckType::SPOILER_CHK_RANDOMIZER_INF &&
(scCheckType == SpoilerCollectionCheckType::SPOILER_CHK_MERCHANT || (scCheckType == SpoilerCollectionCheckType::SPOILER_CHK_SHOP_ITEM ||
scCheckType == SpoilerCollectionCheckType::SPOILER_CHK_SHOP_ITEM ||
scCheckType == SpoilerCollectionCheckType::SPOILER_CHK_COW ||
scCheckType == SpoilerCollectionCheckType::SPOILER_CHK_FISH || scCheckType == SpoilerCollectionCheckType::SPOILER_CHK_FISH ||
scCheckType == SpoilerCollectionCheckType::SPOILER_CHK_SCRUB ||
scCheckType == SpoilerCollectionCheckType::SPOILER_CHK_MASTER_SWORD || scCheckType == SpoilerCollectionCheckType::SPOILER_CHK_MASTER_SWORD ||
scCheckType == SpoilerCollectionCheckType::SPOILER_CHK_RANDOMIZER_INF)) { scCheckType == SpoilerCollectionCheckType::SPOILER_CHK_RANDOMIZER_INF)) {
if (flag == OTRGlobals::Instance->gRandomizer->GetRandomizerInfFromCheck(loc.GetRandomizerCheck())) { if (flag == OTRGlobals::Instance->gRandomizer->GetRandomizerInfFromCheck(loc.GetRandomizerCheck())) {
@ -1343,6 +1335,11 @@ void DrawLocation(RandomizerCheck rc) {
Rando::ItemLocation* itemLoc = OTRGlobals::Instance->gRandoContext->GetItemLocation(rc); Rando::ItemLocation* itemLoc = OTRGlobals::Instance->gRandoContext->GetItemLocation(rc);
RandomizerCheckTrackerData checkData = gSaveContext.checkTrackerData[rc]; RandomizerCheckTrackerData checkData = gSaveContext.checkTrackerData[rc];
RandomizerCheckStatus status = checkData.status; RandomizerCheckStatus status = checkData.status;
if (itemLoc->HasObtained()) {
status = RCSHOW_COLLECTED;
}
bool skipped = checkData.skipped; bool skipped = checkData.skipped;
if (status == RCSHOW_COLLECTED) { if (status == RCSHOW_COLLECTED) {
if (!showHidden && CVarGetInteger("gCheckTrackerCollectedHide", 0)) { if (!showHidden && CVarGetInteger("gCheckTrackerCollectedHide", 0)) {
@ -1677,12 +1674,12 @@ void CheckTrackerWindow::InitElement() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnExitGame>([](uint32_t fileNum) { GameInteractor::Instance->RegisterGameHook<GameInteractor::OnExitGame>([](uint32_t fileNum) {
Teardown(); Teardown();
}); });
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnItemReceive>(CheckTrackerItemReceive); // GameInteractor::Instance->RegisterGameHook<GameInteractor::OnItemReceive>(CheckTrackerItemReceive);
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>(CheckTrackerFrame); GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>(CheckTrackerFrame);
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnTransitionEnd>(CheckTrackerTransition); GameInteractor::Instance->RegisterGameHook<GameInteractor::OnTransitionEnd>(CheckTrackerTransition);
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnShopSlotChange>(CheckTrackerShopSlotChange); GameInteractor::Instance->RegisterGameHook<GameInteractor::OnShopSlotChange>(CheckTrackerShopSlotChange);
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSceneFlagSet>(CheckTrackerSceneFlagSet); // GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSceneFlagSet>(CheckTrackerSceneFlagSet);
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnFlagSet>(CheckTrackerFlagSet); // GameInteractor::Instance->RegisterGameHook<GameInteractor::OnFlagSet>(CheckTrackerFlagSet);
hideShopRightChecks = CVarGetInteger("gCheckTrackerOptionHideRightShopChecks", 1); hideShopRightChecks = CVarGetInteger("gCheckTrackerOptionHideRightShopChecks", 1);
alwaysShowGS = CVarGetInteger("gCheckTrackerOptionAlwaysShowGSLocs", 0); alwaysShowGS = CVarGetInteger("gCheckTrackerOptionAlwaysShowGSLocs", 0);

View File

@ -461,34 +461,31 @@ void Entrance_SetWarpSongEntrance(void) {
} }
void Entrance_OverrideBlueWarp(void) { void Entrance_OverrideBlueWarp(void) {
// Set nextEntranceIndex as a flag so that Grotto_CheckSpecialEntrance // Handles first time entering bluewarp (with item give)
// won't return index 0x7FFF, which can't work to override blue warps. switch (gSaveContext.entranceIndex) {
gPlayState->nextEntranceIndex = 0; case ENTR_KOKIRI_FOREST_11: // Gohma blue warp
case ENTR_DEATH_MOUNTAIN_TRAIL_5: // KD blue warp
case ENTR_ZORAS_FOUNTAIN_0: // Barinade blue warp
case ENTR_SACRED_FOREST_MEADOW_3: // Phantom Ganon blue warp
case ENTR_DEATH_MOUNTAIN_CRATER_5: // Volvagia blue warp
case ENTR_LAKE_HYLIA_9: // Morpha blue warp
case ENTR_DESERT_COLOSSUS_8: // Bongo-Bongo blue warp
case ENTR_GRAVEYARD_8: // Twinrova blue warp
gSaveContext.entranceIndex = Entrance_OverrideNextIndex(gSaveContext.entranceIndex);
return;
}
switch (gPlayState->sceneNum) { // Handles second+ times entering bluewarp
case SCENE_DEKU_TREE_BOSS: // Ghoma boss room switch (gPlayState->nextEntranceIndex) {
gPlayState->nextEntranceIndex = Entrance_OverrideNextIndex(ENTR_KOKIRI_FOREST_11); case ENTR_KOKIRI_FOREST_11: // Gohma blue warp
return; case ENTR_DEATH_MOUNTAIN_TRAIL_5: // KD blue warp
case SCENE_DODONGOS_CAVERN_BOSS: // King Dodongo boss room case ENTR_ZORAS_FOUNTAIN_0: // Barinade blue warp
gPlayState->nextEntranceIndex = Entrance_OverrideNextIndex(ENTR_DEATH_MOUNTAIN_TRAIL_5); case ENTR_SACRED_FOREST_MEADOW_3: // Phantom Ganon blue warp
return; case ENTR_DEATH_MOUNTAIN_CRATER_5: // Volvagia blue warp
case SCENE_JABU_JABU_BOSS: // Barinade boss room case ENTR_LAKE_HYLIA_9: // Morpha blue warp
gPlayState->nextEntranceIndex = Entrance_OverrideNextIndex(ENTR_ZORAS_FOUNTAIN_0); case ENTR_DESERT_COLOSSUS_8: // Bongo-Bongo blue warp
return; case ENTR_GRAVEYARD_8: // Twinrova blue warp
case SCENE_FOREST_TEMPLE_BOSS: // Phantom Ganon boss room gPlayState->nextEntranceIndex = Entrance_OverrideNextIndex(gPlayState->nextEntranceIndex);
gPlayState->nextEntranceIndex = Entrance_OverrideNextIndex(ENTR_SACRED_FOREST_MEADOW_3);
return;
case SCENE_FIRE_TEMPLE_BOSS: // Volvagia boss room
gPlayState->nextEntranceIndex = Entrance_OverrideNextIndex(ENTR_DEATH_MOUNTAIN_CRATER_5);
return;
case SCENE_WATER_TEMPLE_BOSS: // Morpha boss room
gPlayState->nextEntranceIndex = Entrance_OverrideNextIndex(ENTR_LAKE_HYLIA_9);
return;
case SCENE_SPIRIT_TEMPLE_BOSS: // Twinrova boss room
gPlayState->nextEntranceIndex = Entrance_OverrideNextIndex(ENTR_DESERT_COLOSSUS_8);
return;
case SCENE_SHADOW_TEMPLE_BOSS: // Bongo-Bongo boss room
gPlayState->nextEntranceIndex = Entrance_OverrideNextIndex(ENTR_GRAVEYARD_8);
return; return;
} }
} }

View File

@ -143,12 +143,15 @@ typedef enum {
RAND_INF_MERCHANTS_CARPET_SALESMAN, RAND_INF_MERCHANTS_CARPET_SALESMAN,
RAND_INF_MERCHANTS_MEDIGORON, RAND_INF_MERCHANTS_MEDIGORON,
RAND_INF_MERCHANTS_GRANNYS_SHOP, RAND_INF_MERCHANTS_GRANNYS_SHOP,
RAND_INF_MERCHANTS_MAGIC_BEAN_SALESMAN,
RAND_INF_ADULT_TRADES_LW_TRADE_COJIRO, RAND_INF_ADULT_TRADES_LW_TRADE_COJIRO,
RAND_INF_ADULT_TRADES_GV_TRADE_SAW, RAND_INF_ADULT_TRADES_GV_TRADE_SAW,
RAND_INF_ADULT_TRADES_DMT_TRADE_BROKEN_SWORD, RAND_INF_ADULT_TRADES_DMT_TRADE_BROKEN_SWORD,
RAND_INF_ADULT_TRADES_ZD_TRADE_PRESCRIPTION,
RAND_INF_ADULT_TRADES_LH_TRADE_FROG, RAND_INF_ADULT_TRADES_LH_TRADE_FROG,
RAND_INF_ADULT_TRADES_DMT_TRADE_EYEDROPS, RAND_INF_ADULT_TRADES_DMT_TRADE_EYEDROPS,
RAND_INF_ADULT_TRADES_DMT_TRADE_CLAIM_CHECK,
RAND_INF_KAK_100_GOLD_SKULLTULA_REWARD, RAND_INF_KAK_100_GOLD_SKULLTULA_REWARD,
@ -267,6 +270,13 @@ typedef enum {
RAND_INF_ZD_FISH_4, RAND_INF_ZD_FISH_4,
RAND_INF_ZD_FISH_5, RAND_INF_ZD_FISH_5,
RAND_INF_LINKS_POCKET,
RAND_INF_LEARNED_EPONA_SONG,
RAND_INF_DARUNIAS_JOY,
RAND_INF_KING_ZORA_THAWED,
RAND_INF_ROLLING_GORON_AS_CHILD,
RAND_INF_ROLLING_GORON_AS_ADULT,
// If you add anything to this list, you need to update the size of randomizerInf in z64save.h to be ceil(RAND_INF_MAX / 16) // If you add anything to this list, you need to update the size of randomizerInf in z64save.h to be ceil(RAND_INF_MAX / 16)
RAND_INF_MAX, RAND_INF_MAX,

View File

@ -97,6 +97,9 @@ void GiveLinksPocketItem() {
if (Randomizer_GetSettingValue(RSK_LINKS_POCKET) != RO_LINKS_POCKET_NOTHING) { if (Randomizer_GetSettingValue(RSK_LINKS_POCKET) != RO_LINKS_POCKET_NOTHING) {
GetItemEntry getItemEntry = Randomizer_GetItemFromKnownCheck(RC_LINKS_POCKET, (GetItemID)RG_NONE); GetItemEntry getItemEntry = Randomizer_GetItemFromKnownCheck(RC_LINKS_POCKET, (GetItemID)RG_NONE);
StartingItemGive(getItemEntry); StartingItemGive(getItemEntry);
Rando::Context::GetInstance()->GetItemLocation(RC_LINKS_POCKET)->MarkAsObtained();
// If we re-add the above, we'll get the item on save creation, now it's given on first load
Flags_SetRandomizerInf(RAND_INF_LINKS_POCKET);
} }
} }
@ -202,40 +205,42 @@ void SetStartingItems() {
} }
extern "C" void Randomizer_InitSaveFile() { extern "C" void Randomizer_InitSaveFile() {
// Sets all rando flags to false // Now handled by cutscene skips
for (s32 i = 0; i < ARRAY_COUNT(gSaveContext.randomizerInf); i++) { // gSaveContext.cutsceneIndex = 0; // no intro cutscene
gSaveContext.randomizerInf[i] = 0; // Starts pending ice traps out at 0 before potentially incrementing them down the line.
} gSaveContext.pendingIceTrapCount = 0;
// Reset triforce pieces collected // Reset triforce pieces collected
gSaveContext.triforcePiecesCollected = 0; gSaveContext.triforcePiecesCollected = 0;
gSaveContext.cutsceneIndex = 0; // no intro cutscene
// Starts pending ice traps out at 0 before potentially incrementing them down the line.
gSaveContext.pendingIceTrapCount = 0;
// Set Cutscene flags and texts to skip them // Set Cutscene flags and texts to skip them
Flags_SetInfTable(INFTABLE_GREETED_BY_SARIA); // Now handled by cutscene skips
// Flags_SetInfTable(INFTABLE_GREETED_BY_SARIA);
Flags_SetEventChkInf(EVENTCHKINF_FIRST_SPOKE_TO_MIDO); Flags_SetEventChkInf(EVENTCHKINF_FIRST_SPOKE_TO_MIDO);
Flags_SetEventChkInf(EVENTCHKINF_MET_DEKU_TREE); // Now handled by cutscene skips
Flags_SetEventChkInf(EVENTCHKINF_DEKU_TREE_OPENED_MOUTH); // Flags_SetEventChkInf(EVENTCHKINF_MET_DEKU_TREE);
// Flags_SetEventChkInf(EVENTCHKINF_DEKU_TREE_OPENED_MOUTH);
Flags_SetInfTable(INFTABLE_SPOKE_TO_KAEPORA_IN_LAKE_HYLIA); Flags_SetInfTable(INFTABLE_SPOKE_TO_KAEPORA_IN_LAKE_HYLIA);
Flags_SetEventChkInf(EVENTCHKINF_ENTERED_MASTER_SWORD_CHAMBER); // Now handled by cutscene skips
Flags_SetEventChkInf(EVENTCHKINF_PULLED_MASTER_SWORD_FROM_PEDESTAL); // Flags_SetEventChkInf(EVENTCHKINF_ENTERED_MASTER_SWORD_CHAMBER);
// Now using this to grant master sword check
// Flags_SetEventChkInf(EVENTCHKINF_PULLED_MASTER_SWORD_FROM_PEDESTAL);
Flags_SetEventChkInf(EVENTCHKINF_SHEIK_SPAWNED_AT_MASTER_SWORD_PEDESTAL); Flags_SetEventChkInf(EVENTCHKINF_SHEIK_SPAWNED_AT_MASTER_SWORD_PEDESTAL);
Flags_SetEventChkInf(EVENTCHKINF_RETURNED_TO_TEMPLE_OF_TIME_WITH_ALL_MEDALLIONS); // Now used to give player LACS rewards
// Flags_SetEventChkInf(EVENTCHKINF_RETURNED_TO_TEMPLE_OF_TIME_WITH_ALL_MEDALLIONS);
Flags_SetEventChkInf(EVENTCHKINF_RENTED_HORSE_FROM_INGO); Flags_SetEventChkInf(EVENTCHKINF_RENTED_HORSE_FROM_INGO);
Flags_SetInfTable(INFTABLE_SPOKE_TO_POE_COLLECTOR_IN_RUINED_MARKET); Flags_SetInfTable(INFTABLE_SPOKE_TO_POE_COLLECTOR_IN_RUINED_MARKET);
Flags_SetEventChkInf(EVENTCHKINF_WATCHED_GANONS_CASTLE_COLLAPSE_CAUGHT_BY_GERUDO); Flags_SetEventChkInf(EVENTCHKINF_WATCHED_GANONS_CASTLE_COLLAPSE_CAUGHT_BY_GERUDO);
Flags_SetEventChkInf(EVENTCHKINF_SPOKE_TO_NABOORU_IN_SPIRIT_TEMPLE); Flags_SetEventChkInf(EVENTCHKINF_SPOKE_TO_NABOORU_IN_SPIRIT_TEMPLE);
Flags_SetInfTable(INFTABLE_MET_CHILD_MALON_AT_CASTLE_OR_MARKET); // Now handled by cutscene skips
Flags_SetEventChkInf(EVENTCHKINF_SPOKE_TO_CHILD_MALON_AT_CASTLE_OR_MARKET); // Flags_SetInfTable(INFTABLE_MET_CHILD_MALON_AT_CASTLE_OR_MARKET);
Flags_SetEventChkInf(EVENTCHKINF_SPOKE_TO_INGO_AT_RANCH_BEFORE_TALON_RETURNS); // Flags_SetEventChkInf(EVENTCHKINF_SPOKE_TO_CHILD_MALON_AT_CASTLE_OR_MARKET);
Flags_SetEventChkInf(EVENTCHKINF_SPOKE_TO_CHILD_MALON_AT_RANCH); // Flags_SetEventChkInf(EVENTCHKINF_SPOKE_TO_INGO_AT_RANCH_BEFORE_TALON_RETURNS);
Flags_SetEventChkInf(EVENTCHKINF_INVITED_TO_SING_WITH_CHILD_MALON); // Flags_SetEventChkInf(EVENTCHKINF_SPOKE_TO_CHILD_MALON_AT_RANCH);
Flags_SetInfTable(INFTABLE_CHILD_MALON_SAID_EPONA_WAS_AFRAID_OF_YOU); // Flags_SetEventChkInf(EVENTCHKINF_INVITED_TO_SING_WITH_CHILD_MALON);
Flags_SetInfTable(INFTABLE_SPOKE_TO_INGO_ONCE_AS_ADULT); // Flags_SetInfTable(INFTABLE_CHILD_MALON_SAID_EPONA_WAS_AFRAID_OF_YOU);
// Flags_SetInfTable(INFTABLE_SPOKE_TO_INGO_ONCE_AS_ADULT);
// Ruto already met in jabu and spawns down the hole immediately // Ruto already met in jabu and spawns down the hole immediately
Flags_SetInfTable(INFTABLE_RUTO_IN_JJ_MEET_RUTO); Flags_SetInfTable(INFTABLE_RUTO_IN_JJ_MEET_RUTO);
@ -246,51 +251,55 @@ extern "C" void Randomizer_InitSaveFile() {
Flags_SetEventChkInf(EVENTCHKINF_BEGAN_NABOORU_BATTLE); Flags_SetEventChkInf(EVENTCHKINF_BEGAN_NABOORU_BATTLE);
Flags_SetEventChkInf(EVENTCHKINF_NABOORU_ORDERED_TO_FIGHT_BY_TWINROVA); Flags_SetEventChkInf(EVENTCHKINF_NABOORU_ORDERED_TO_FIGHT_BY_TWINROVA);
// Now handled by cutscene skips
// Skip boss cutscenes // Skip boss cutscenes
Flags_SetEventChkInf(EVENTCHKINF_BEGAN_GOHMA_BATTLE); // Flags_SetEventChkInf(EVENTCHKINF_BEGAN_GOHMA_BATTLE);
Flags_SetEventChkInf(EVENTCHKINF_BEGAN_KING_DODONGO_BATTLE); // Flags_SetEventChkInf(EVENTCHKINF_BEGAN_KING_DODONGO_BATTLE);
Flags_SetEventChkInf(EVENTCHKINF_BEGAN_PHANTOM_GANON_BATTLE); // Flags_SetEventChkInf(EVENTCHKINF_BEGAN_PHANTOM_GANON_BATTLE);
Flags_SetEventChkInf(EVENTCHKINF_BEGAN_VOLVAGIA_BATTLE); // Flags_SetEventChkInf(EVENTCHKINF_BEGAN_VOLVAGIA_BATTLE);
Flags_SetEventChkInf(EVENTCHKINF_BEGAN_MORPHA_BATTLE); // Flags_SetEventChkInf(EVENTCHKINF_BEGAN_MORPHA_BATTLE);
Flags_SetEventChkInf(EVENTCHKINF_BEGAN_TWINROVA_BATTLE); // Flags_SetEventChkInf(EVENTCHKINF_BEGAN_TWINROVA_BATTLE);
Flags_SetEventChkInf(EVENTCHKINF_BEGAN_BARINA_BATTLE); // Flags_SetEventChkInf(EVENTCHKINF_BEGAN_BARINA_BATTLE);
Flags_SetEventChkInf(EVENTCHKINF_BEGAN_BONGO_BONGO_BATTLE); // Flags_SetEventChkInf(EVENTCHKINF_BEGAN_BONGO_BONGO_BATTLE);
// Entered areas // Now handled by cutscene skips
Flags_SetEventChkInf(EVENTCHKINF_ENTERED_HYRULE_FIELD); // Flags_SetEventChkInf(EVENTCHKINF_ENTERED_HYRULE_FIELD);
Flags_SetEventChkInf(EVENTCHKINF_ENTERED_DEATH_MOUNTAIN_TRAIL); // Flags_SetEventChkInf(EVENTCHKINF_ENTERED_DEATH_MOUNTAIN_TRAIL);
Flags_SetEventChkInf(EVENTCHKINF_ENTERED_KAKARIKO_VILLAGE); // Flags_SetEventChkInf(EVENTCHKINF_ENTERED_KAKARIKO_VILLAGE);
Flags_SetEventChkInf(EVENTCHKINF_ENTERED_ZORAS_DOMAIN); // Flags_SetEventChkInf(EVENTCHKINF_ENTERED_ZORAS_DOMAIN);
Flags_SetEventChkInf(EVENTCHKINF_ENTERED_HYRULE_CASTLE); // Flags_SetEventChkInf(EVENTCHKINF_ENTERED_HYRULE_CASTLE);
Flags_SetEventChkInf(EVENTCHKINF_ENTERED_GORON_CITY); // Flags_SetEventChkInf(EVENTCHKINF_ENTERED_GORON_CITY);
Flags_SetEventChkInf(EVENTCHKINF_ENTERED_TEMPLE_OF_TIME); // Flags_SetEventChkInf(EVENTCHKINF_ENTERED_TEMPLE_OF_TIME);
Flags_SetEventChkInf(EVENTCHKINF_ENTERED_DEKU_TREE); // Flags_SetEventChkInf(EVENTCHKINF_ENTERED_DEKU_TREE);
Flags_SetEventChkInf(EVENTCHKINF_ENTERED_DODONGOS_CAVERN); // Flags_SetEventChkInf(EVENTCHKINF_ENTERED_DODONGOS_CAVERN);
Flags_SetEventChkInf(EVENTCHKINF_ENTERED_LAKE_HYLIA); // Flags_SetEventChkInf(EVENTCHKINF_ENTERED_LAKE_HYLIA);
Flags_SetEventChkInf(EVENTCHKINF_ENTERED_GERUDO_VALLEY); // Flags_SetEventChkInf(EVENTCHKINF_ENTERED_GERUDO_VALLEY);
Flags_SetEventChkInf(EVENTCHKINF_ENTERED_GERUDOS_FORTRESS); // Flags_SetEventChkInf(EVENTCHKINF_ENTERED_GERUDOS_FORTRESS);
Flags_SetEventChkInf(EVENTCHKINF_ENTERED_LON_LON_RANCH); // Flags_SetEventChkInf(EVENTCHKINF_ENTERED_LON_LON_RANCH);
Flags_SetEventChkInf(EVENTCHKINF_ENTERED_JABU_JABUS_BELLY); // Flags_SetEventChkInf(EVENTCHKINF_ENTERED_JABU_JABUS_BELLY);
Flags_SetEventChkInf(EVENTCHKINF_ENTERED_GRAVEYARD); // Flags_SetEventChkInf(EVENTCHKINF_ENTERED_GRAVEYARD);
Flags_SetEventChkInf(EVENTCHKINF_ENTERED_ZORAS_FOUNTAIN); // Flags_SetEventChkInf(EVENTCHKINF_ENTERED_ZORAS_FOUNTAIN);
Flags_SetEventChkInf(EVENTCHKINF_ENTERED_DESERT_COLOSSUS); // Flags_SetEventChkInf(EVENTCHKINF_ENTERED_DESERT_COLOSSUS);
Flags_SetEventChkInf(EVENTCHKINF_ENTERED_DEATH_MOUNTAIN_CRATER); // Flags_SetEventChkInf(EVENTCHKINF_ENTERED_DEATH_MOUNTAIN_CRATER);
Flags_SetEventChkInf(EVENTCHKINF_ENTERED_GANONS_CASTLE_EXTERIOR); // Flags_SetEventChkInf(EVENTCHKINF_ENTERED_GANONS_CASTLE_EXTERIOR);
Flags_SetInfTable(INFTABLE_ENTERED_HYRULE_CASTLE); // Ensure Malon appears at castle first time you enter
// Flags_SetInfTable(INFTABLE_ENTERED_HYRULE_CASTLE);
// skip the z target talk instructions by the kokiri shop // skip the z target talk instructions by the kokiri shop
gSaveContext.sceneFlags[SCENE_KOKIRI_FOREST].swch |= (1 << 0x1F); // Now handled by cutscene skips
// gSaveContext.sceneFlags[SCENE_KOKIRI_FOREST].swch |= (1 << 0x1F);
// Go away ruto (water temple first cutscene) // Go away ruto (water temple first cutscene)
gSaveContext.sceneFlags[SCENE_WATER_TEMPLE].swch |= (1 << 0x10); gSaveContext.sceneFlags[SCENE_WATER_TEMPLE].swch |= (1 << 0x10);
// Now handled by cutscene skips
// no more kaepora // no more kaepora
gSaveContext.sceneFlags[SCENE_HYRULE_FIELD].swch |= (1 << 0xC); // hyrule field kaepora outside kokiri forest // gSaveContext.sceneFlags[SCENE_HYRULE_FIELD].swch |= (1 << 0xC); // hyrule field kaepora outside kokiri forest
gSaveContext.sceneFlags[SCENE_HYRULE_FIELD].swch |= (1 << 0xB); // hyrule field kaepora outside lake hylia // gSaveContext.sceneFlags[SCENE_HYRULE_FIELD].swch |= (1 << 0xB); // hyrule field kaepora outside lake hylia
gSaveContext.sceneFlags[SCENE_LOST_WOODS].swch |= (1 << 0x7); // lost woods kaepora pre-saria // gSaveContext.sceneFlags[SCENE_LOST_WOODS].swch |= (1 << 0x7); // lost woods kaepora pre-saria
gSaveContext.sceneFlags[SCENE_LOST_WOODS].swch |= (1 << 0x8); // lost woods kaepora post-saria // gSaveContext.sceneFlags[SCENE_LOST_WOODS].swch |= (1 << 0x8); // lost woods kaepora post-saria
gSaveContext.sceneFlags[SCENE_DESERT_COLOSSUS].swch |= (1 << 0x1F); // desert colossus kaepora // gSaveContext.sceneFlags[SCENE_DESERT_COLOSSUS].swch |= (1 << 0x1F); // desert colossus kaepora
gSaveContext.sceneFlags[SCENE_HYRULE_CASTLE].swch |= (1 << 0x5); // hyrule castle kaepora // gSaveContext.sceneFlags[SCENE_HYRULE_CASTLE].swch |= (1 << 0x5); // hyrule castle kaepora
if (!Randomizer_GetSettingValue(RSK_ENABLE_GLITCH_CUTSCENES)) { if (!Randomizer_GetSettingValue(RSK_ENABLE_GLITCH_CUTSCENES)) {
Flags_SetInfTable(INFTABLE_SPOKE_TO_DARUNIA_IN_FIRE_TEMPLE); // Darunia in Fire Temple Flags_SetInfTable(INFTABLE_SPOKE_TO_DARUNIA_IN_FIRE_TEMPLE); // Darunia in Fire Temple
@ -398,15 +407,16 @@ extern "C" void Randomizer_InitSaveFile() {
gSaveContext.sceneFlags[SCENE_WATER_TEMPLE].swch |= (1 << 0x15); gSaveContext.sceneFlags[SCENE_WATER_TEMPLE].swch |= (1 << 0x15);
} }
int openForest = Randomizer_GetSettingValue(RSK_FOREST); // Now handled on the fly
switch (openForest) { // int openForest = Randomizer_GetSettingValue(RSK_FOREST);
case RO_FOREST_OPEN: // switch (openForest) {
Flags_SetEventChkInf(EVENTCHKINF_SHOWED_MIDO_SWORD_SHIELD); // case RO_FOREST_OPEN:
// Fallthrough // Flags_SetEventChkInf(EVENTCHKINF_SHOWED_MIDO_SWORD_SHIELD);
case RO_FOREST_CLOSED_DEKU: // // Fallthrough
Flags_SetEventChkInf(EVENTCHKINF_OBTAINED_KOKIRI_EMERALD_DEKU_TREE_DEAD); // case RO_FOREST_CLOSED_DEKU:
break; // Flags_SetEventChkInf(EVENTCHKINF_OBTAINED_KOKIRI_EMERALD_DEKU_TREE_DEAD);
} // break;
// }
int doorOfTime = Randomizer_GetSettingValue(RSK_DOOR_OF_TIME); int doorOfTime = Randomizer_GetSettingValue(RSK_DOOR_OF_TIME);
switch (doorOfTime) { switch (doorOfTime) {

View File

@ -0,0 +1,788 @@
#include <libultraship/bridge.h>
#include "soh/OTRGlobals.h"
#include "soh/Enhancements/randomizer/randomizerTypes.h"
#include "soh/Enhancements/game-interactor/GameInteractor.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
extern "C" {
#include "src/overlays/actors/ovl_En_Wonder_Talk2/z_en_wonder_talk2.h"
#include "src/overlays/actors/ovl_Elf_Msg/z_elf_msg.h"
#include "src/overlays/actors/ovl_Obj_Switch/z_obj_switch.h"
#include "src/overlays/actors/ovl_Bg_Bdan_Switch/z_bg_bdan_switch.h"
#include "src/overlays/actors/ovl_Bg_Treemouth/z_bg_treemouth.h"
#include "src/overlays/actors/ovl_En_Owl/z_en_owl.h"
#include "src/overlays/actors/ovl_En_Ko/z_en_ko.h"
#include "src/overlays/actors/ovl_En_Ma1/z_en_ma1.h"
#include "src/overlays/actors/ovl_En_Zl4/z_en_zl4.h"
#include "src/overlays/actors/ovl_Demo_Im/z_demo_im.h"
#include "src/overlays/actors/ovl_En_Sa/z_en_sa.h"
#include "src/overlays/actors/ovl_Bg_Ddan_Kd/z_bg_ddan_kd.h"
#include "src/overlays/actors/ovl_En_Tk/z_en_tk.h"
extern SaveContext gSaveContext;
extern PlayState* gPlayState;
}
#define RAND_GET_OPTION(option) Rando::Context::GetInstance()->GetOption(option).GetSelectedOptionIndex()
void EnKo_MoveWhenReady(EnKo* enKo, PlayState* play) {
func_80A995CC(enKo, play);
if ((enKo->actor.params & 0xFF) == ENKO_TYPE_CHILD_3) {
// Typically this doesn't get get live updated in vanilla, but we need to
// live update it if we're skipping a certain cutscene or in randomizer
if (GameInteractor_Should(GI_VB_OPEN_KOKIRI_FOREST, CHECK_QUEST_ITEM(QUEST_KOKIRI_EMERALD), NULL)) {
enKo->collider.dim.height -= 200;
Path_CopyLastPoint(enKo->path, &enKo->actor.world.pos);
enKo->actionFunc = func_80A99384;
}
}
}
void EnMa1_EndTeachSong(EnMa1* enMa1, PlayState* play) {
if (Message_GetState(&gPlayState->msgCtx) == TEXT_STATE_CLOSING) {
Flags_SetRandomizerInf(RAND_INF_LEARNED_EPONA_SONG);
func_80078884(NA_SE_SY_CORRECT_CHIME);
enMa1->actor.flags &= ~ACTOR_FLAG_WILL_TALK;
play->msgCtx.ocarinaMode = OCARINA_MODE_04;
enMa1->actionFunc = func_80AA0D88;
enMa1->unk_1E0 = 1;
enMa1->interactInfo.talkState = NPC_TALK_STATE_IDLE;
return;
}
}
u16 EnZl4_GiveItemTextId(PlayState* play, Actor* actor) {
return 0x207D;
}
void EnZl4_SkipToGivingZeldasLetter(EnZl4* enZl4, PlayState* play) {
if (enZl4->csState == 0 && enZl4->actor.xzDistToPlayer < 600.0f && EnZl4_SetNextAnim(enZl4, 3)) {
Audio_PlayFanfare(NA_BGM_APPEAR);
enZl4->csState = 8; // ZL4_CS_PLAN
} else {
Npc_UpdateTalking(play, &enZl4->actor, &enZl4->interactInfo.talkState, enZl4->collider.dim.radius + 60.0f, EnZl4_GiveItemTextId, func_80B5B9B0);
func_80B5BB78(enZl4, play);
if (enZl4->interactInfo.talkState != NPC_TALK_STATE_IDLE) {
enZl4->talkState = 6;
enZl4->actionFunc = EnZl4_Cutscene;
}
}
}
static int successChimeCooldown = 0;
void RateLimitedSuccessChime() {
if (successChimeCooldown == 0) {
func_80078884(NA_SE_SY_CORRECT_CHIME);
successChimeCooldown = 120;
}
}
void TimeSaverOnGameFrameUpdateHandler() {
if (successChimeCooldown > 0) {
successChimeCooldown--;
}
}
void TimeSaverOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, void* opt) {
switch (id) {
case GI_VB_PLAY_TRANSITION_CS:
if (CVarGetInteger("gTimeSavers.SkipCutscene.Intro", 0) && gSaveContext.entranceIndex == ENTR_LINKS_HOUSE_0 && gSaveContext.cutsceneIndex == 0xFFF1) {
gSaveContext.cutsceneIndex = 0;
*should = false;
}
if (CVarGetInteger("gTimeSavers.SkipCutscene.LearnSong", 0) || IS_RANDO) {
// Song of Time
if (gSaveContext.entranceIndex == ENTR_TEMPLE_OF_TIME_0 && gSaveContext.cutsceneIndex == 0xFFF7) {
gSaveContext.entranceIndex = ENTR_HYRULE_FIELD_16;
gSaveContext.cutsceneIndex = 0;
gSaveContext.nextTransitionType = 3;
*should = false;
}
// Requiem of Spirit
if ((gSaveContext.entranceIndex == ENTR_DESERT_COLOSSUS_1) && !Flags_GetEventChkInf(EVENTCHKINF_LEARNED_REQUIEM_OF_SPIRIT)) {
Flags_SetEventChkInf(EVENTCHKINF_LEARNED_REQUIEM_OF_SPIRIT);
// Normally happens in the cutscene
if (GameInteractor_Should(GI_VB_GIVE_ITEM_REQUIEM_OF_SPIRIT, true, NULL)) {
Item_Give(gPlayState, ITEM_SONG_REQUIEM);
}
*should = false;
}
u8 meetsBurningKakRequirements =
LINK_IS_ADULT &&
gSaveContext.entranceIndex == ENTR_KAKARIKO_VILLAGE_0 &&
Flags_GetEventChkInf(EVENTCHKINF_USED_FOREST_TEMPLE_BLUE_WARP) &&
Flags_GetEventChkInf(EVENTCHKINF_USED_FIRE_TEMPLE_BLUE_WARP) &&
Flags_GetEventChkInf(EVENTCHKINF_USED_WATER_TEMPLE_BLUE_WARP) &&
!Flags_GetEventChkInf(EVENTCHKINF_BONGO_BONGO_ESCAPED_FROM_WELL);
if (GameInteractor_Should(GI_VB_BE_ELIGIBLE_FOR_NOCTURNE_OF_SHADOW, meetsBurningKakRequirements, NULL)) {
Flags_SetEventChkInf(EVENTCHKINF_BONGO_BONGO_ESCAPED_FROM_WELL);
// Normally happens in the cutscene
Flags_SetEventChkInf(EVENTCHKINF_LEARNED_NOCTURNE_OF_SHADOW);
if (GameInteractor_Should(GI_VB_GIVE_ITEM_NOCTURNE_OF_SHADOW, true, NULL)) {
Item_Give(gPlayState, ITEM_SONG_NOCTURNE);
}
*should = false;
}
}
if (CVarGetInteger("gTimeSavers.SkipCutscene.Story", 0)) {
uint8_t isBlueWarp = 0;
// Deku Tree Blue warp
if (gSaveContext.entranceIndex == ENTR_KOKIRI_FOREST_0 && gSaveContext.cutsceneIndex == 0xFFF1) {
gSaveContext.dayTime = gSaveContext.skyboxTime = 0x8000;
gSaveContext.entranceIndex = ENTR_KOKIRI_FOREST_11;
isBlueWarp = 1;
// Dodongo's Cavern Blue warp
} else if (gSaveContext.entranceIndex == ENTR_DEATH_MOUNTAIN_TRAIL_0 && gSaveContext.cutsceneIndex == 0xFFF1) {
gSaveContext.entranceIndex = ENTR_DEATH_MOUNTAIN_TRAIL_5;
isBlueWarp = 1;
// Jabu Jabu's Blue warp
} else if (gSaveContext.entranceIndex == ENTR_ZORAS_FOUNTAIN_0 && gSaveContext.cutsceneIndex == 0xFFF0) {
gSaveContext.entranceIndex = ENTR_ZORAS_FOUNTAIN_0;
isBlueWarp = 1;
// Forest Temple Blue warp
} else if (gSaveContext.entranceIndex == ENTR_CHAMBER_OF_THE_SAGES_0 && gSaveContext.cutsceneIndex == 0x0 && gSaveContext.chamberCutsceneNum == CHAMBER_CS_FOREST) {
// Normally set in the blue warp cutscene
Flags_SetEventChkInf(EVENTCHKINF_SPOKE_TO_DEKU_TREE_SPROUT);
gSaveContext.dayTime = gSaveContext.skyboxTime = 0x8000;
gSaveContext.entranceIndex = ENTR_SACRED_FOREST_MEADOW_3;
isBlueWarp = 1;
// Fire Temple Blue warp
} else if (gSaveContext.entranceIndex == ENTR_KAKARIKO_VILLAGE_0 && gSaveContext.cutsceneIndex == 0xFFF3) {
gSaveContext.entranceIndex = ENTR_DEATH_MOUNTAIN_CRATER_5;
isBlueWarp = 1;
// Water Temple Blue warp
} else if (gSaveContext.entranceIndex == ENTR_CHAMBER_OF_THE_SAGES_0 && gSaveContext.cutsceneIndex == 0x0 && gSaveContext.chamberCutsceneNum == CHAMBER_CS_WATER) {
gSaveContext.entranceIndex = ENTR_LAKE_HYLIA_9;
isBlueWarp = 1;
// Spirit Temple Blue warp
} else if (gSaveContext.entranceIndex == ENTR_CHAMBER_OF_THE_SAGES_0 && gSaveContext.cutsceneIndex == 0x0 && gSaveContext.chamberCutsceneNum == CHAMBER_CS_SPIRIT) {
gSaveContext.entranceIndex = ENTR_DESERT_COLOSSUS_8;
isBlueWarp = 1;
// Shadow Temple Blue warp
} else if (gSaveContext.entranceIndex == ENTR_CHAMBER_OF_THE_SAGES_0 && gSaveContext.cutsceneIndex == 0x0 && gSaveContext.chamberCutsceneNum == CHAMBER_CS_SHADOW) {
gSaveContext.entranceIndex = ENTR_GRAVEYARD_8;
isBlueWarp = 1;
}
if (isBlueWarp) {
*should = false;
gSaveContext.cutsceneIndex = 0;
if (IS_RANDO && (RAND_GET_OPTION(RSK_SHUFFLE_DUNGEON_ENTRANCES) != RO_DUNGEON_ENTRANCE_SHUFFLE_OFF || RAND_GET_OPTION(RSK_SHUFFLE_BOSS_ENTRANCES) != RO_BOSS_ROOM_ENTRANCE_SHUFFLE_OFF)) {
Entrance_OverrideBlueWarp();
}
}
// Flee hyrule castle cutscene
if (gSaveContext.entranceIndex == ENTR_HYRULE_FIELD_0 && gSaveContext.cutsceneIndex == 0xFFF1) {
gSaveContext.cutsceneIndex = 0;
*should = false;
}
// Lost Woods Bridge
if ((gSaveContext.entranceIndex == ENTR_LOST_WOODS_9) && !Flags_GetEventChkInf(EVENTCHKINF_SPOKE_TO_SARIA_ON_BRIDGE)) {
Flags_SetEventChkInf(EVENTCHKINF_SPOKE_TO_SARIA_ON_BRIDGE);
if (GameInteractor_Should(GI_VB_GIVE_ITEM_FAIRY_OCARINA, true, NULL)) {
Item_Give(gPlayState, ITEM_OCARINA_FAIRY);
}
*should = false;
}
// LACS
u8 meetsLACSRequirements =
LINK_IS_ADULT &&
(gEntranceTable[((void)0, gSaveContext.entranceIndex)].scene == SCENE_TEMPLE_OF_TIME) &&
CHECK_QUEST_ITEM(QUEST_MEDALLION_SPIRIT) &&
CHECK_QUEST_ITEM(QUEST_MEDALLION_SHADOW) &&
!Flags_GetEventChkInf(EVENTCHKINF_RETURNED_TO_TEMPLE_OF_TIME_WITH_ALL_MEDALLIONS);
if (GameInteractor_Should(GI_VB_BE_ELIGIBLE_FOR_LIGHT_ARROWS, meetsLACSRequirements, NULL)) {
Flags_SetEventChkInf(EVENTCHKINF_RETURNED_TO_TEMPLE_OF_TIME_WITH_ALL_MEDALLIONS);
if (GameInteractor_Should(GI_VB_GIVE_ITEM_LIGHT_ARROW, true, NULL)) {
Item_Give(gPlayState, ITEM_ARROW_LIGHT);
}
*should = false;
}
}
break;
case GI_VB_PLAY_ENTRANCE_CS: {
s32* entranceFlag = static_cast<s32*>(opt);
if (CVarGetInteger("gTimeSavers.SkipCutscene.Entrances", 0) && (*entranceFlag != EVENTCHKINF_EPONA_OBTAINED)) {
*should = false;
}
break;
}
case GI_VB_PLAY_ONEPOINT_CS: {
if (CVarGetInteger("gTimeSavers.SkipCutscene.OnePoint", 0)) {
s16* csId = static_cast<s16*>(opt);
switch (*csId) {
case 4180:
case 4100:
*should = false;
RateLimitedSuccessChime();
break;
default:
SPDLOG_INFO("GI_VB_PLAY_ONEPOINT_CS {}", *csId);
break;
}
}
break;
}
case GI_VB_PLAY_ONEPOINT_ACTOR_CS: {
if (CVarGetInteger("gTimeSavers.SkipCutscene.OnePoint", 0)) {
Actor* actor = static_cast<Actor*>(opt);
switch (actor->category) {
case ACTORCAT_BG:
if (actor->id == ACTOR_BG_DDAN_KD) {
BgDdanKd* ddanKd = static_cast<BgDdanKd*>(opt);
Flags_SetSwitch(gPlayState, ddanKd->dyna.actor.params);
}
if (actor->id == ACTOR_BG_MORI_HINERI) {
break;
}
RateLimitedSuccessChime();
*should = false;
break;
}
switch (actor->id) {
case ACTOR_OBJ_SWITCH: {
ObjSwitch *switchActor = static_cast<ObjSwitch*>(opt);
switchActor->cooldownTimer = 0;
*should = false;
RateLimitedSuccessChime();
break;
}
case ACTOR_BG_BDAN_SWITCH: {
BgBdanSwitch* switchActor = static_cast<BgBdanSwitch*>(opt);
switchActor->unk_1D8 = 0;
switchActor->unk_1DA = 0;
*should = false;
RateLimitedSuccessChime();
break;
}
// case ACTOR_PLAYER: // This might cause issues
case ACTOR_EN_TA:
case ACTOR_DOOR_SHUTTER:
case ACTOR_EN_BOX:
case ACTOR_OBJ_SYOKUDAI:
case ACTOR_OBJ_TIMEBLOCK:
case ACTOR_EN_PO_SISTERS:
// Prop
case ACTOR_OBJ_ICE_POLY:
case ACTOR_BG_YDAN_MARUTA:
case ACTOR_BG_SPOT18_SHUTTER:
case ACTOR_BG_SPOT05_SOKO:
case ACTOR_BG_SPOT18_BASKET:
// BG
// case ACTOR_BG_YDAN_SP:
// case ACTOR_BG_YDAN_HASI:
// case ACTOR_BG_DODOAGO:
// case ACTOR_BG_DDAN_KD:
// case ACTOR_BG_DDAN_JD:
*should = false;
RateLimitedSuccessChime();
break;
}
if (*should) {
SPDLOG_INFO("GI_VB_PLAY_ONEPOINT_ACTOR_CS ID:{} Cat:{}", actor->id, actor->category);
}
}
break;
}
case GI_VB_SHOW_TITLE_CARD:
if (CVarGetInteger("gTimeSavers.DisableTitleCard", 0)) {
*should = false;
}
break;
case GI_VB_WONDER_TALK: {
if (CVarGetInteger("gTimeSavers.NoForcedDialog", 0)) {
*should = false;
}
break;
}
case GI_VB_NAVI_TALK: {
if (CVarGetInteger("gTimeSavers.NoForcedDialog", 0)) {
ElfMsg* naviTalk = static_cast<ElfMsg*>(opt);
Flags_SetSwitch(gPlayState, (naviTalk->actor.params >> 8) & 0x3F);
Actor_Kill(&naviTalk->actor);
*should = false;
}
break;
}
case GI_VB_NOT_BE_GREETED_BY_SARIA:
if (CVarGetInteger("gTimeSavers.SkipCutscene.Entrances", 0) && !Flags_GetInfTable(INFTABLE_GREETED_BY_SARIA)) {
Flags_SetInfTable(INFTABLE_GREETED_BY_SARIA);
*should = true;
}
break;
case GI_VB_MOVE_MIDO_IN_KOKIRI_FOREST:
if (
CVarGetInteger("gTimeSavers.SkipMiscInteractions", 0) &&
!Flags_GetEventChkInf(EVENTCHKINF_SHOWED_MIDO_SWORD_SHIELD) &&
(CUR_EQUIP_VALUE(EQUIP_TYPE_SHIELD) == EQUIP_VALUE_SHIELD_DEKU) &&
(CUR_EQUIP_VALUE(EQUIP_TYPE_SWORD) == EQUIP_VALUE_SWORD_KOKIRI)
) {
Flags_SetEventChkInf(EVENTCHKINF_SHOWED_MIDO_SWORD_SHIELD);
*should = true;
}
break;
case GI_VB_PLAY_DEKU_TREE_INTRO_CS: {
if (CVarGetInteger("gTimeSavers.SkipCutscene.Story", 0)) {
BgTreemouth* treeMouth = static_cast<BgTreemouth*>(opt);
Flags_SetEventChkInf(EVENTCHKINF_DEKU_TREE_OPENED_MOUTH);
Audio_PlaySoundGeneral(NA_SE_EV_WOODDOOR_OPEN, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8);
BgTreemouth_SetupAction(treeMouth, func_808BC6F8);
*should = false;
}
break;
}
case GI_VB_GIVE_ITEM_FROM_BLUE_WARP:
case GI_VB_PLAY_SHIEK_BLOCK_MASTER_SWORD_CS:
case GI_VB_GIVE_ITEM_FAIRY_OCARINA:
case GI_VB_GIVE_ITEM_LIGHT_ARROW:
if (CVarGetInteger("gTimeSavers.SkipCutscene.Story", 0)) {
*should = false;
}
break;
case GI_VB_PLAY_NABOORU_CAPTURED_CS:
if (*should == true && CVarGetInteger("gTimeSavers.SkipCutscene.Story", 0)) {
Flags_SetEventChkInf(EVENTCHKINF_NABOORU_CAPTURED_BY_TWINROVA);
*should = false;
}
break;
case GI_VB_PLAY_PULL_MASTER_SWORD_CS:
if (CVarGetInteger("gTimeSavers.SkipCutscene.Story", 0)) {
if (!Flags_GetEventChkInf(EVENTCHKINF_PULLED_MASTER_SWORD_FROM_PEDESTAL)) {
// Normally, these would be done in the cutscene, but we're skipping it
Flags_SetEventChkInf(EVENTCHKINF_PULLED_MASTER_SWORD_FROM_PEDESTAL);
Flags_SetEventChkInf(EVENTCHKINF_ENTERED_MASTER_SWORD_CHAMBER);
Flags_SetEventChkInf(EVENTCHKINF_SHEIK_SPAWNED_AT_MASTER_SWORD_PEDESTAL);
Flags_SetEventChkInf(EVENTCHKINF_TIME_TRAVELED_TO_ADULT);
if (GameInteractor_Should(GI_VB_GIVE_ITEM_LIGHT_MEDALLION, true, NULL)) {
Item_Give(gPlayState, ITEM_MEDALLION_LIGHT);
}
}
*should = false;
}
break;
case GI_VB_OWL_INTERACTION: {
if (CVarGetInteger("gTimeSavers.SkipOwlInteractions", 0) && *should) {
EnOwl* enOwl = static_cast<EnOwl*>(opt);
s32 owlType = (enOwl->actor.params & 0xFC0) >> 6;
if (((enOwl->actor.params & 0xFC0) >> 6) == 1) {
Flags_SetEventChkInf(EVENTCHKINF_SPOKE_TO_KAEPORA_BY_LOST_WOODS);
}
func_80ACA62C(enOwl, gPlayState);
*should = false;
}
break;
}
case GI_VB_PLAY_EYEDROP_CREATION_ANIM:
case GI_VB_PLAY_EYEDROPS_CS:
case GI_VB_PLAY_DROP_FISH_FOR_JABU_CS:
case GI_VB_PLAY_DARUNIAS_JOY_CS:
if (CVarGetInteger("gTimeSavers.SkipMiscInteractions", 0)) {
*should = false;
}
break;
case GI_VB_PLAY_ZELDAS_LULLABY_CS: {
if (CVarGetInteger("gTimeSavers.SkipCutscene.LearnSong", 0) || IS_RANDO) {
DemoIm* demoIm = static_cast<DemoIm*>(opt);
Player* player = GET_PLAYER(gPlayState);
player->stateFlags1 |= PLAYER_STATE1_IN_CUTSCENE;
player->stateFlags1 |= PLAYER_STATE1_GETTING_ITEM;
func_80986794(demoIm);
static uint32_t demoImUpdateHook = 0;
static uint32_t demoImKillHook = 0;
demoImUpdateHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnActorUpdate>([](void* actorRef) mutable {
Actor* actor = static_cast<Actor*>(actorRef);
if (actor->id == ACTOR_DEMO_IM && (CVarGetInteger("gTimeSavers.SkipCutscene.LearnSong", 0) || IS_RANDO)) {
DemoIm* demoIm = static_cast<DemoIm*>(actorRef);
Player* player = GET_PLAYER(gPlayState);
player->stateFlags1 |= PLAYER_STATE1_IN_CUTSCENE;
player->stateFlags1 |= PLAYER_STATE1_GETTING_ITEM;
if (Animation_OnFrame(&demoIm->skelAnime, 25.0f)) {
Audio_PlaySoundGeneral(NA_SE_IT_DEKU, &demoIm->actor.projectedPos, 4, &D_801333E0, &D_801333E0, &D_801333E8);
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnActorUpdate>(demoImUpdateHook);
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnSceneInit>(demoImKillHook);
demoImUpdateHook = 0;
demoImKillHook = 0;
} else if (Animation_OnFrame(&demoIm->skelAnime, 15.0f)) {
Player* player = GET_PLAYER(gPlayState);
// SOH [Randomizer] In entrance rando have impa bring link back to the front of castle grounds
if (IS_RANDO && RAND_GET_OPTION(RSK_SHUFFLE_OVERWORLD_ENTRANCES)) {
gPlayState->nextEntranceIndex = ENTR_HYRULE_CASTLE_0;
} else {
gPlayState->nextEntranceIndex = ENTR_HYRULE_FIELD_17;
}
gPlayState->transitionType = TRANS_TYPE_FADE_WHITE;
gPlayState->transitionTrigger = TRANS_TRIGGER_START;
gSaveContext.nextTransitionType = 2;
func_8002DF54(gPlayState, &player->actor, 8);
}
}
});
demoImKillHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSceneInit>([](int16_t sceneNum) mutable {
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnActorUpdate>(demoImUpdateHook);
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnSceneInit>(demoImKillHook);
demoImUpdateHook = 0;
demoImKillHook = 0;
});
*should = false;
}
break;
}
case GI_VB_PLAY_SARIAS_SONG_CS: {
if (CVarGetInteger("gTimeSavers.SkipCutscene.LearnSong", 0) || IS_RANDO) {
EnSa* enSa = static_cast<EnSa*>(opt);
enSa->actionFunc = func_80AF6B20;
*should = false;
}
break;
}
case GI_VB_DESPAWN_HORSE_RACE_COW: {
if (Flags_GetEventChkInf(EVENTCHKINF_WON_COW_IN_MALONS_RACE) && CVarGetInteger("gCowOfTime", 0)) {
*should = false;
}
break;
}
case GI_VB_GIVE_ITEM_MINUET_OF_FOREST:
case GI_VB_GIVE_ITEM_BOLERO_OF_FIRE:
case GI_VB_GIVE_ITEM_SERENADE_OF_WATER:
case GI_VB_GIVE_ITEM_REQUIEM_OF_SPIRIT:
case GI_VB_GIVE_ITEM_NOCTURNE_OF_SHADOW:
case GI_VB_GIVE_ITEM_PRELUDE_OF_LIGHT:
case GI_VB_GIVE_ITEM_ZELDAS_LULLABY:
case GI_VB_GIVE_ITEM_EPONAS_SONG:
case GI_VB_GIVE_ITEM_SARIAS_SONG:
case GI_VB_GIVE_ITEM_SUNS_SONG:
case GI_VB_GIVE_ITEM_SONG_OF_TIME:
case GI_VB_GIVE_ITEM_SONG_OF_STORMS:
case GI_VB_PLAY_MINUET_OF_FOREST_CS:
case GI_VB_PLAY_BOLERO_OF_FIRE_CS:
case GI_VB_PLAY_SERENADE_OF_WATER_CS:
case GI_VB_PLAY_PRELUDE_OF_LIGHT_CS:
if (CVarGetInteger("gTimeSavers.SkipCutscene.LearnSong", 0) || IS_RANDO) {
*should = false;
}
break;
case GI_VB_DAMPE_IN_GRAVEYARD_DESPAWN:
if (CVarGetInteger("gDampeAllNight", 0)) {
*should = LINK_IS_ADULT || gPlayState->sceneNum != SCENE_GRAVEYARD;
}
break;
case GI_VB_BE_VALID_GRAVEDIGGING_SPOT:
if (CVarGetInteger("gDampeWin", 0)) {
EnTk *enTk = static_cast<EnTk*>(opt);
enTk->validDigHere = true;
*should = true;
}
break;
case GI_VB_BE_DAMPE_GRAVEDIGGING_GRAND_PRIZE:
if (CVarGetInteger("gDampeWin", 0)) {
EnTk *enTk = static_cast<EnTk*>(opt);
enTk->currentReward = 3;
*should = true;
}
break;
case GI_VB_DAMPE_GRAVEDIGGING_GRAND_PRIZE_BE_HEART_PIECE:
if (CVarGetInteger("gGravediggingTourFix", 0) || IS_RANDO) {
*should = !Flags_GetCollectible(gPlayState, COLLECTFLAG_GRAVEDIGGING_HEART_PIECE);
}
break;
case GI_VB_FIX_SAW_SOFTLOCK:
// Animation Count should be no more than 1 to guarantee putaway is complete after giving the saw
// As this is vanilla behavior, it only applies with the Fix toggle or Skip Text enabled.
*should = (CVarGetInteger("gFixSawSoftlock", 0) != 0 || CVarGetInteger("gSkipText", 0) != 0) ? gPlayState->animationCtx.animationCount > 1 : *should;
break;
case GI_VB_BIGGORON_CONSIDER_SWORD_FORGED:
*should = Environment_GetBgsDayCount() >= CVarGetInteger("gForgeTime", 3);
break;
}
}
static uint32_t enKoUpdateHook = 0;
static uint32_t enKoKillHook = 0;
static uint32_t itemOcarinaUpdateHook = 0;
static uint32_t itemOcarinaframesSinceSpawn = 0;
static uint32_t enMa1UpdateHook = 0;
static uint32_t enMa1KillHook = 0;
void TimeSaverOnActorInitHandler(void* actorRef) {
Actor* actor = static_cast<Actor*>(actorRef);
if (actor->id == ACTOR_EN_KO && (actor->params & 0xFF) == ENKO_TYPE_CHILD_3) {
enKoUpdateHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnActorUpdate>([](void* innerActorRef) mutable {
Actor* innerActor = static_cast<Actor*>(innerActorRef);
if (innerActor->id == ACTOR_EN_KO && (innerActor->params & 0xFF) == ENKO_TYPE_CHILD_3 && (CVarGetInteger("gTimeSavers.SkipCutscene.Story", 0) || IS_RANDO)) {
EnKo* enKo = static_cast<EnKo*>(innerActorRef);
// They haven't moved yet, wrap their update function so we check every frame
if (enKo->actionFunc == func_80A995CC) {
enKo->actionFunc = EnKo_MoveWhenReady;
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnActorUpdate>(enKoUpdateHook);
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnSceneInit>(enKoKillHook);
enKoUpdateHook = 0;
enKoKillHook = 0;
// They have already moved
} else if (enKo->actionFunc == func_80A99384) {
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnActorUpdate>(enKoUpdateHook);
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnSceneInit>(enKoKillHook);
enKoUpdateHook = 0;
enKoKillHook = 0;
}
}
});
enKoKillHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSceneInit>([](int16_t sceneNum) mutable {
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnActorUpdate>(enKoUpdateHook);
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnSceneInit>(enKoKillHook);
enKoUpdateHook = 0;
enKoKillHook = 0;
});
}
if (actor->id == ACTOR_ITEM_OCARINA && actor->params == 3 && CVarGetInteger("gTimeSavers.SkipCutscene.Story", 0)) {
itemOcarinaframesSinceSpawn = 0;
itemOcarinaUpdateHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnActorUpdate>([](void* innerActorRef) mutable {
Actor* innerActor = static_cast<Actor*>(innerActorRef);
if (innerActor->id != ACTOR_ITEM_OCARINA || innerActor->params != 3) return;
itemOcarinaframesSinceSpawn++;
if (itemOcarinaframesSinceSpawn > 20) {
Audio_PlayActorSound2(innerActor, NA_SE_EV_BOMB_DROP_WATER);
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnActorUpdate>(itemOcarinaUpdateHook);
itemOcarinaUpdateHook = 0;
}
});
}
if (actor->id == ACTOR_EN_MA1 && gPlayState->sceneNum == SCENE_LON_LON_RANCH) {
enMa1UpdateHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnActorUpdate>([](void* innerActorRef) mutable {
Actor* innerActor = static_cast<Actor*>(innerActorRef);
if (innerActor->id == ACTOR_EN_MA1 && (CVarGetInteger("gTimeSavers.SkipCutscene.LearnSong", 0) || IS_RANDO)) {
EnMa1* enMa1 = static_cast<EnMa1*>(innerActorRef);
if (enMa1->actionFunc == func_80AA106C) {
enMa1->actionFunc = EnMa1_EndTeachSong;
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnActorUpdate>(enMa1UpdateHook);
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnSceneInit>(enMa1KillHook);
enMa1UpdateHook = 0;
enMa1KillHook = 0;
// They've already learned the song
} else if (enMa1->actionFunc == func_80AA0D88) {
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnActorUpdate>(enMa1UpdateHook);
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnSceneInit>(enMa1KillHook);
enMa1UpdateHook = 0;
enMa1KillHook = 0;
}
}
});
enMa1KillHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSceneInit>([](int16_t sceneNum) mutable {
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnActorUpdate>(enMa1UpdateHook);
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnSceneInit>(enMa1KillHook);
enMa1UpdateHook = 0;
enMa1KillHook = 0;
});
}
if (actor->id == ACTOR_EN_ZL4 && CVarGetInteger("gTimeSavers.SkipCutscene.Story", 0)) {
EnZl4* enZl4 = static_cast<EnZl4*>(actorRef);
if (enZl4->actionFunc != EnZl4_Cutscene || enZl4->csState != 0) return;
enZl4->actionFunc = EnZl4_SkipToGivingZeldasLetter;
}
}
void TimeSaverOnSceneInitHandler(int16_t sceneNum) {
switch (sceneNum) {
case SCENE_HYRULE_CASTLE:
if (CVarGetInteger("gTimeSavers.SkipMiscInteractions", 0) && !Flags_GetInfTable(INFTABLE_ENTERED_HYRULE_CASTLE)) {
Flags_SetInfTable(INFTABLE_ENTERED_HYRULE_CASTLE);
Flags_SetInfTable(INFTABLE_MET_CHILD_MALON_AT_CASTLE_OR_MARKET);
Flags_SetEventChkInf(EVENTCHKINF_SPOKE_TO_CHILD_MALON_AT_CASTLE_OR_MARKET);
}
break;
case SCENE_LON_LON_RANCH:
if (CVarGetInteger("gTimeSavers.SkipMiscInteractions", 0) && GameInteractor_Should(GI_VB_MALON_RETURN_FROM_CASTLE, Flags_GetEventChkInf(EVENTCHKINF_TALON_RETURNED_FROM_CASTLE), NULL)) {
Flags_SetEventChkInf(EVENTCHKINF_SPOKE_TO_CHILD_MALON_AT_RANCH);
Flags_SetInfTable(INFTABLE_CHILD_MALON_SAID_EPONA_WAS_AFRAID_OF_YOU);
Flags_SetEventChkInf(EVENTCHKINF_INVITED_TO_SING_WITH_CHILD_MALON);
}
break;
case SCENE_DEKU_TREE_BOSS:
if (CVarGetInteger("gTimeSavers.SkipCutscene.BossIntro", 0)) {
if (!Flags_GetEventChkInf(EVENTCHKINF_BEGAN_GOHMA_BATTLE)) {
Flags_SetEventChkInf(EVENTCHKINF_BEGAN_GOHMA_BATTLE);
}
}
break;
case SCENE_DODONGOS_CAVERN_BOSS:
if (CVarGetInteger("gTimeSavers.SkipCutscene.BossIntro", 0)) {
if (!Flags_GetEventChkInf(EVENTCHKINF_BEGAN_KING_DODONGO_BATTLE)) {
Flags_SetEventChkInf(EVENTCHKINF_BEGAN_KING_DODONGO_BATTLE);
}
}
break;
case SCENE_JABU_JABU_BOSS:
if (CVarGetInteger("gTimeSavers.SkipCutscene.BossIntro", 0)) {
if (!Flags_GetEventChkInf(EVENTCHKINF_BEGAN_BARINA_BATTLE)) {
Flags_SetEventChkInf(EVENTCHKINF_BEGAN_BARINA_BATTLE);
}
}
break;
}
}
static GetItemEntry vanillaQueuedItemEntry = GET_ITEM_NONE;
void TimeSaverOnFlagSetHandler(int16_t flagType, int16_t flag) {
if (!CVarGetInteger("gTimeSavers.SkipCutscene.Story", 0)) return;
switch (flagType) {
case FLAG_EVENT_CHECK_INF:
switch (flag) {
case EVENTCHKINF_SPOKE_TO_SARIA_ON_BRIDGE:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_FAIRY_OCARINA).GetGIEntry_Copy();
break;
case EVENTCHKINF_LEARNED_ZELDAS_LULLABY:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_ZELDAS_LULLABY).GetGIEntry_Copy();
break;
case EVENTCHKINF_LEARNED_MINUET_OF_FOREST:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_MINUET_OF_FOREST).GetGIEntry_Copy();
break;
case EVENTCHKINF_LEARNED_BOLERO_OF_FIRE:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_BOLERO_OF_FIRE).GetGIEntry_Copy();
break;
case EVENTCHKINF_LEARNED_SERENADE_OF_WATER:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_SERENADE_OF_WATER).GetGIEntry_Copy();
break;
case EVENTCHKINF_LEARNED_REQUIEM_OF_SPIRIT:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_REQUIEM_OF_SPIRIT).GetGIEntry_Copy();
break;
case EVENTCHKINF_BONGO_BONGO_ESCAPED_FROM_WELL:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_NOCTURNE_OF_SHADOW).GetGIEntry_Copy();
break;
case EVENTCHKINF_LEARNED_PRELUDE_OF_LIGHT:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_PRELUDE_OF_LIGHT).GetGIEntry_Copy();
break;
case EVENTCHKINF_LEARNED_SARIAS_SONG:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_SARIAS_SONG).GetGIEntry_Copy();
break;
case EVENTCHKINF_OBTAINED_KOKIRI_EMERALD_DEKU_TREE_DEAD:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_KOKIRI_EMERALD).GetGIEntry_Copy();
break;
case EVENTCHKINF_USED_DODONGOS_CAVERN_BLUE_WARP:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_GORON_RUBY).GetGIEntry_Copy();
break;
case EVENTCHKINF_USED_JABU_JABUS_BELLY_BLUE_WARP:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_ZORA_SAPPHIRE).GetGIEntry_Copy();
break;
case EVENTCHKINF_USED_FOREST_TEMPLE_BLUE_WARP:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_FOREST_MEDALLION).GetGIEntry_Copy();
break;
case EVENTCHKINF_USED_FIRE_TEMPLE_BLUE_WARP:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_FIRE_MEDALLION).GetGIEntry_Copy();
break;
case EVENTCHKINF_USED_WATER_TEMPLE_BLUE_WARP:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_WATER_MEDALLION).GetGIEntry_Copy();
break;
case EVENTCHKINF_RETURNED_TO_TEMPLE_OF_TIME_WITH_ALL_MEDALLIONS:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_LIGHT_ARROWS).GetGIEntry_Copy();
break;
case EVENTCHKINF_TIME_TRAVELED_TO_ADULT:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_LIGHT_MEDALLION).GetGIEntry_Copy();
break;
case EVENTCHKINF_LEARNED_SONG_OF_TIME:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_SONG_OF_TIME).GetGIEntry_Copy();
break;
}
break;
case FLAG_RANDOMIZER_INF:
switch (flag) {
case RAND_INF_LEARNED_EPONA_SONG:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_EPONAS_SONG).GetGIEntry_Copy();
break;
case RAND_INF_DUNGEONS_DONE_SHADOW_TEMPLE:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_SHADOW_MEDALLION).GetGIEntry_Copy();
break;
case RAND_INF_DUNGEONS_DONE_SPIRIT_TEMPLE:
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_SPIRIT_MEDALLION).GetGIEntry_Copy();
break;
}
break;
}
}
void TimeSaverOnPlayerUpdateHandler() {
if (vanillaQueuedItemEntry.itemId == ITEM_NONE) return;
Player* player = GET_PLAYER(gPlayState);
if (player == NULL || Player_InBlockingCsMode(gPlayState, player) || player->stateFlags1 & PLAYER_STATE1_IN_ITEM_CS || player->stateFlags1 & PLAYER_STATE1_GETTING_ITEM || player->stateFlags1 & PLAYER_STATE1_ITEM_OVER_HEAD) {
return;
}
SPDLOG_INFO("Attempting to give Item: mod {} item {}", vanillaQueuedItemEntry.modIndex, vanillaQueuedItemEntry.itemId);
GiveItemEntryWithoutActor(gPlayState, vanillaQueuedItemEntry);
if (player->stateFlags1 & PLAYER_STATE1_IN_WATER) {
// Allow the player to receive the item while swimming
player->stateFlags2 |= PLAYER_STATE2_UNDERWATER;
func_8083E5A8(player, gPlayState);
}
}
void TimeSaverOnItemReceiveHandler(GetItemEntry receivedItemEntry) {
if (vanillaQueuedItemEntry.itemId == ITEM_NONE) return;
if (vanillaQueuedItemEntry.modIndex == receivedItemEntry.modIndex && vanillaQueuedItemEntry.itemId == receivedItemEntry.itemId) {
SPDLOG_INFO("Item received: mod {} item {}", receivedItemEntry.modIndex, receivedItemEntry.itemId);
vanillaQueuedItemEntry = GET_ITEM_NONE;
}
}
static uint32_t onSceneInitHook = 0;
static uint32_t onVanillaBehaviorHook = 0;
static uint32_t onActorInitHook = 0;
static uint32_t onGameFrameUpdate = 0;
static uint32_t onFlagSetHook = 0;
static uint32_t onPlayerUpdateHook = 0;
static uint32_t onItemReceiveHook = 0;
void TimeSaverRegisterHooks() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnLoadGame>([](int32_t fileNum) mutable {
vanillaQueuedItemEntry = GET_ITEM_NONE;
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnSceneInit>(onSceneInitHook);
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnVanillaBehavior>(onVanillaBehaviorHook);
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnActorInit>(onActorInitHook);
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnGameFrameUpdate>(onGameFrameUpdate);
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnFlagSet>(onFlagSetHook);
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnPlayerUpdate>(onPlayerUpdateHook);
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnItemReceive>(onItemReceiveHook);
onSceneInitHook = 0;
onVanillaBehaviorHook = 0;
onActorInitHook = 0;
onGameFrameUpdate = 0;
onFlagSetHook = 0;
onPlayerUpdateHook = 0;
onItemReceiveHook = 0;
onSceneInitHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSceneInit>(TimeSaverOnSceneInitHandler);
onVanillaBehaviorHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnVanillaBehavior>(TimeSaverOnVanillaBehaviorHandler);
onActorInitHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnActorInit>(TimeSaverOnActorInitHandler);
onGameFrameUpdate = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>(TimeSaverOnGameFrameUpdateHandler);
if (IS_RANDO) return;
onFlagSetHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnFlagSet>(TimeSaverOnFlagSetHandler);
onPlayerUpdateHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnPlayerUpdate>(TimeSaverOnPlayerUpdateHandler);
onItemReceiveHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnItemReceive>(TimeSaverOnItemReceiveHandler);
});
}

View File

@ -0,0 +1,6 @@
#ifndef TIMESAVER_HOOK_HANDLERS_H
#define TIMESAVER_HOOK_HANDLERS_H
void TimeSaverRegisterHooks();
#endif // TIMESAVER_HOOK_HANDLERS_H

View File

@ -122,6 +122,10 @@ GameInteractorSail* GameInteractorSail::Instance;
#include "soh/config/ConfigUpdaters.h" #include "soh/config/ConfigUpdaters.h"
extern "C" {
#include "src/overlays/actors/ovl_En_Dns/z_en_dns.h"
}
void SoH_ProcessDroppedFiles(std::string filePath); void SoH_ProcessDroppedFiles(std::string filePath);
OTRGlobals* OTRGlobals::Instance; OTRGlobals* OTRGlobals::Instance;
@ -2567,9 +2571,9 @@ extern "C" int CustomMessage_RetrieveIfExists(PlayState* play) {
} else if (textId == TEXT_SHEIK_NEED_HOOK || textId == TEXT_SHEIK_HAVE_HOOK) { } else if (textId == TEXT_SHEIK_NEED_HOOK || textId == TEXT_SHEIK_HAVE_HOOK) {
messageEntry = OTRGlobals::Instance->gRandomizer->GetSheikMessage(gPlayState->sceneNum, textId); messageEntry = OTRGlobals::Instance->gRandomizer->GetSheikMessage(gPlayState->sceneNum, textId);
// textId: TEXT_SCRUB_RANDOM + (randomizerInf - RAND_INF_SCRUBS_PURCHASED_DODONGOS_CAVERN_DEKU_SCRUB_NEAR_BOMB_BAG_LEFT) // textId: TEXT_SCRUB_RANDOM + (randomizerInf - RAND_INF_SCRUBS_PURCHASED_DODONGOS_CAVERN_DEKU_SCRUB_NEAR_BOMB_BAG_LEFT)
} else if (textId >= TEXT_SCRUB_RANDOM && textId <= TEXT_SCRUB_RANDOM + NUM_SCRUBS) { } else if (textId == TEXT_SCRUB_RANDOM) {
RandomizerInf randoInf = (RandomizerInf)((textId - TEXT_SCRUB_RANDOM) + RAND_INF_SCRUBS_PURCHASED_DODONGOS_CAVERN_DEKU_SCRUB_NEAR_BOMB_BAG_LEFT); EnDns* enDns = (EnDns*)GET_PLAYER(play)->targetActor;
messageEntry = OTRGlobals::Instance->gRandomizer->GetMerchantMessage(randoInf, TEXT_SCRUB_RANDOM, Randomizer_GetSettingValue(RSK_SCRUB_TEXT_HINT) == RO_GENERIC_OFF); messageEntry = OTRGlobals::Instance->gRandomizer->GetMerchantMessage(enDns->sohScrubIdentity.randomizerInf, TEXT_SCRUB_RANDOM, Randomizer_GetSettingValue(RSK_SCRUB_TEXT_HINT) == RO_GENERIC_OFF);
// Shop items each have two message entries, second one offset by NUM_SHOP_ITEMS // Shop items each have two message entries, second one offset by NUM_SHOP_ITEMS
// textId: TEXT_SHOP_ITEM_RANDOM + (randomizerInf - RAND_INF_SHOP_ITEMS_KF_SHOP_ITEM_1) // textId: TEXT_SHOP_ITEM_RANDOM + (randomizerInf - RAND_INF_SHOP_ITEMS_KF_SHOP_ITEM_1)
// textId: TEXT_SHOP_ITEM_RANDOM + ((randomizerInf - RAND_INF_SHOP_ITEMS_KF_SHOP_ITEM_1) + NUM_SHOP_ITEMS) // textId: TEXT_SHOP_ITEM_RANDOM + ((randomizerInf - RAND_INF_SHOP_ITEMS_KF_SHOP_ITEM_1) + NUM_SHOP_ITEMS)

View File

@ -351,6 +351,13 @@ void SaveManager::LoadRandomizerVersion3() {
// all ItemLocations is 0 anyway. // all ItemLocations is 0 anyway.
randoContext->GetItemLocation(i)->SetCustomPrice(price); randoContext->GetItemLocation(i)->SetCustomPrice(price);
} }
uint16_t obtained = 0;
SaveManager::Instance->LoadData("obtained", obtained, (uint16_t)0);
if (obtained) {
randoContext->GetItemLocation(i)->MarkAsObtained();
} else {
randoContext->GetItemLocation(i)->MarkAsNotObtained();
}
}); });
}); });
@ -449,6 +456,7 @@ void SaveManager::SaveRandomizer(SaveContext* saveContext, int sectionID, bool f
if (randoContext->GetItemLocation(i)->HasCustomPrice()) { if (randoContext->GetItemLocation(i)->HasCustomPrice()) {
SaveManager::Instance->SaveData("price", randoContext->GetItemLocation(i)->GetPrice()); SaveManager::Instance->SaveData("price", randoContext->GetItemLocation(i)->GetPrice());
} }
SaveManager::Instance->SaveData("obtained", randoContext->GetItemLocation(i)->HasObtained());
}); });
}); });
@ -727,6 +735,9 @@ void SaveManager::InitFileNormal() {
for (int flag = 0; flag < ARRAY_COUNT(gSaveContext.infTable); flag++) { for (int flag = 0; flag < ARRAY_COUNT(gSaveContext.infTable); flag++) {
gSaveContext.infTable[flag] = 0; gSaveContext.infTable[flag] = 0;
} }
for (int flag = 0; flag < ARRAY_COUNT(gSaveContext.randomizerInf); flag++) {
gSaveContext.randomizerInf[flag] = 0;
}
gSaveContext.worldMapAreaData = 0; gSaveContext.worldMapAreaData = 0;
gSaveContext.scarecrowLongSongSet = 0; gSaveContext.scarecrowLongSongSet = 0;
for (int i = 0; i < ARRAY_COUNT(gSaveContext.scarecrowLongSong); i++) { for (int i = 0; i < ARRAY_COUNT(gSaveContext.scarecrowLongSong); i++) {

View File

@ -69,6 +69,7 @@ static const char* imguiScaleOptions[4] = { "Small", "Normal", "Large", "X-Large
}; };
static const char* chestStyleMatchesContentsOptions[4] = { "Disabled", "Both", "Texture Only", "Size Only" }; static const char* chestStyleMatchesContentsOptions[4] = { "Disabled", "Both", "Texture Only", "Size Only" };
static const char* skipGetItemAnimationOptions[3] = { "Disabled", "Junk Items", "All Items" };
static const char* bunnyHoodOptions[3] = { "Disabled", "Faster Run & Longer Jump", "Faster Run" }; static const char* bunnyHoodOptions[3] = { "Disabled", "Faster Run & Longer Jump", "Faster Run" };
static const char* mirroredWorldModes[9] = { static const char* mirroredWorldModes[9] = {
"Disabled", "Always", "Random", "Random (Seeded)", "Dungeons", "Disabled", "Always", "Random", "Random (Seeded)", "Dungeons",
@ -555,6 +556,83 @@ void DrawEnhancementsMenu() {
UIWidgets::Spacer(0); UIWidgets::Spacer(0);
ImGui::Text("Speed-ups:"); ImGui::Text("Speed-ups:");
UIWidgets::PaddedSeparator(); UIWidgets::PaddedSeparator();
bool allChecked =
CVarGetInteger("gTimeSavers.SkipCutscene.Intro", 0) &&
CVarGetInteger("gTimeSavers.SkipCutscene.Entrances", 0) &&
CVarGetInteger("gTimeSavers.SkipCutscene.Story", 0) &&
CVarGetInteger("gTimeSavers.SkipCutscene.LearnSong", 0) &&
CVarGetInteger("gTimeSavers.SkipCutscene.BossIntro", 0) &&
CVarGetInteger("gTimeSavers.SkipCutscene.GlitchAiding", 0) &&
CVarGetInteger("gTimeSavers.SkipCutscene.OnePoint", 0) &&
CVarGetInteger("gTimeSavers.NoForcedDialog", 0) &&
CVarGetInteger("gTimeSavers.SkipOwlInteractions", 0) &&
CVarGetInteger("gTimeSavers.SkipMiscInteractions", 0) &&
CVarGetInteger("gTimeSavers.DisableTitleCard", 0);
bool someChecked =
CVarGetInteger("gTimeSavers.SkipCutscene.Intro", 0) ||
CVarGetInteger("gTimeSavers.SkipCutscene.Entrances", 0) ||
CVarGetInteger("gTimeSavers.SkipCutscene.Story", 0) ||
CVarGetInteger("gTimeSavers.SkipCutscene.LearnSong", 0) ||
CVarGetInteger("gTimeSavers.SkipCutscene.BossIntro", 0) ||
CVarGetInteger("gTimeSavers.SkipCutscene.GlitchAiding", 0) ||
CVarGetInteger("gTimeSavers.SkipCutscene.OnePoint", 0) ||
CVarGetInteger("gTimeSavers.NoForcedDialog", 0) ||
CVarGetInteger("gTimeSavers.SkipOwlInteractions", 0) ||
CVarGetInteger("gTimeSavers.SkipMiscInteractions", 0) ||
CVarGetInteger("gTimeSavers.DisableTitleCard", 0);
ImGuiContext* g = ImGui::GetCurrentContext();
ImGuiItemFlags backup_item_flags = g->CurrentItemFlags;
if (!allChecked && someChecked) g->CurrentItemFlags |= ImGuiItemFlags_MixedValue;
if (ImGui::Checkbox("All", &allChecked)) {
if (allChecked) {
CVarSetInteger("gTimeSavers.SkipCutscene.Intro", 1);
CVarSetInteger("gTimeSavers.SkipCutscene.Entrances", 1);
CVarSetInteger("gTimeSavers.SkipCutscene.Story", 1);
CVarSetInteger("gTimeSavers.SkipCutscene.LearnSong", 1);
CVarSetInteger("gTimeSavers.SkipCutscene.BossIntro", 1);
CVarSetInteger("gTimeSavers.SkipCutscene.GlitchAiding", 1);
CVarSetInteger("gTimeSavers.SkipCutscene.OnePoint", 1);
CVarSetInteger("gTimeSavers.NoForcedDialog", 1);
CVarSetInteger("gTimeSavers.SkipOwlInteractions", 1);
CVarSetInteger("gTimeSavers.SkipMiscInteractions", 1);
CVarSetInteger("gTimeSavers.DisableTitleCard", 1);
} else {
CVarSetInteger("gTimeSavers.SkipCutscene.Intro", 0);
CVarSetInteger("gTimeSavers.SkipCutscene.Entrances", 0);
CVarSetInteger("gTimeSavers.SkipCutscene.Story", 0);
CVarSetInteger("gTimeSavers.SkipCutscene.LearnSong", 0);
CVarSetInteger("gTimeSavers.SkipCutscene.BossIntro", 0);
CVarSetInteger("gTimeSavers.SkipCutscene.GlitchAiding", 0);
CVarSetInteger("gTimeSavers.SkipCutscene.OnePoint", 0);
CVarSetInteger("gTimeSavers.NoForcedDialog", 0);
CVarSetInteger("gTimeSavers.SkipOwlInteractions", 0);
CVarSetInteger("gTimeSavers.SkipMiscInteractions", 0);
CVarSetInteger("gTimeSavers.DisableTitleCard", 0);
}
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
}
g->CurrentItemFlags = backup_item_flags;
UIWidgets::PaddedEnhancementCheckbox("Skip Intro", "gTimeSavers.SkipCutscene.Intro");
UIWidgets::PaddedEnhancementCheckbox("Skip Entrance Cutscenes", "gTimeSavers.SkipCutscene.Entrances");
UIWidgets::PaddedEnhancementCheckbox("Skip Story Cutscenes", "gTimeSavers.SkipCutscene.Story");
UIWidgets::PaddedEnhancementCheckbox("Skip Song Cutscenes", "gTimeSavers.SkipCutscene.LearnSong");
UIWidgets::PaddedEnhancementCheckbox("Skip Boss Introductions", "gTimeSavers.SkipCutscene.BossIntro");
UIWidgets::PaddedEnhancementCheckbox("Skip Glitch-Aiding Cutscenes", "gTimeSavers.SkipCutscene.GlitchAiding");
UIWidgets::Tooltip("Skip cutscenes that are associated with useful glitches, currently this is only the Fire Temple Darunia CS and Forest Temple Poe Sisters CS");
UIWidgets::PaddedEnhancementCheckbox("Skip One Point Cutscenes (Chests, Door Unlocks, etc)", "gTimeSavers.SkipCutscene.OnePoint");
UIWidgets::PaddedEnhancementCheckbox("No Forced Dialog", "gTimeSavers.NoForcedDialog");
UIWidgets::Tooltip("Prevent forced conversations with Navi or other NPCs");
UIWidgets::PaddedEnhancementCheckbox("Skip Owl Interactions", "gTimeSavers.SkipOwlInteractions");
UIWidgets::PaddedEnhancementCheckbox("Skip Misc Interactions", "gTimeSavers.SkipMiscInteractions");
UIWidgets::PaddedEnhancementCheckbox("Disable Title Card", "gTimeSavers.DisableTitleCard");
UIWidgets::PaddedText("Skip Get Item Animations", true, false);
UIWidgets::EnhancementCombobox("gTimeSavers.SkipGetItemAnimation", skipGetItemAnimationOptions, SGIA_DISABLED);
if (CVarGetInteger("gTimeSavers.SkipGetItemAnimation", SGIA_DISABLED) != SGIA_DISABLED) {
UIWidgets::EnhancementSliderFloat("Item Scale: %f", "##ItemScale", "gTimeSavers.SkipGetItemAnimationScale", 5.0f, 15.0f, "", 10.0f, false);
UIWidgets::Tooltip("The size of the item when it is picked up");
}
UIWidgets::PaddedEnhancementSliderInt("Text Speed: %dx", "##TEXTSPEED", "gTextSpeed", 1, 5, "", 1, true, false, true); UIWidgets::PaddedEnhancementSliderInt("Text Speed: %dx", "##TEXTSPEED", "gTextSpeed", 1, 5, "", 1, true, false, true);
UIWidgets::PaddedEnhancementCheckbox("Skip Text", "gSkipText", false, true); UIWidgets::PaddedEnhancementCheckbox("Skip Text", "gSkipText", false, true);
@ -565,37 +643,8 @@ void DrawEnhancementsMenu() {
UIWidgets::PaddedEnhancementSliderInt("Crawl speed %dx", "##CRAWLSPEED", "gCrawlSpeed", 1, 5, "", 1, true, false, true); UIWidgets::PaddedEnhancementSliderInt("Crawl speed %dx", "##CRAWLSPEED", "gCrawlSpeed", 1, 5, "", 1, true, false, true);
UIWidgets::PaddedEnhancementCheckbox("Faster Heavy Block Lift", "gFasterHeavyBlockLift", false, false); UIWidgets::PaddedEnhancementCheckbox("Faster Heavy Block Lift", "gFasterHeavyBlockLift", false, false);
UIWidgets::Tooltip("Speeds up lifting silver rocks and obelisks"); UIWidgets::Tooltip("Speeds up lifting silver rocks and obelisks");
UIWidgets::PaddedEnhancementCheckbox("Skip Pickup Messages", "gFastDrops", true, false); UIWidgets::PaddedEnhancementCheckbox("Link as default file name", "gLinkDefaultName", true, false);
UIWidgets::Tooltip("Skip pickup messages for new consumable items and bottle swipes"); UIWidgets::Tooltip("Allows you to have \"Link\" as a premade file name");
UIWidgets::PaddedEnhancementCheckbox("Fast Ocarina Playback", "gFastOcarinaPlayback", true, false);
UIWidgets::Tooltip("Skip the part where the Ocarina playback is called when you play a song");
bool forceSkipScarecrow = IS_RANDO && OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SKIP_SCARECROWS_SONG);
static const char* forceSkipScarecrowText = "This setting is forcefully enabled because a savefile\nwith \"Skip Scarecrow Song\" is loaded";
UIWidgets::PaddedEnhancementCheckbox("Skip Scarecrow Song", "gSkipScarecrow", true, false,
forceSkipScarecrow, forceSkipScarecrowText, UIWidgets::CheckboxGraphics::Checkmark);
UIWidgets::Tooltip("Pierre appears when Ocarina is pulled out. Requires learning scarecrow song.");
UIWidgets::PaddedEnhancementCheckbox("Skip Magic Arrow Equip Animation", "gSkipArrowAnimation", true, false);
UIWidgets::PaddedEnhancementCheckbox("Skip save confirmation", "gSkipSaveConfirmation", true, false);
UIWidgets::Tooltip("Skip the \"Game saved.\" confirmation screen");
UIWidgets::PaddedEnhancementCheckbox("Faster Farore's Wind", "gFastFarores", true, false);
UIWidgets::Tooltip("Greatly decreases cast time of Farore's Wind magic spell.");
UIWidgets::PaddedEnhancementCheckbox("Skip water take breath animation", "gSkipSwimDeepEndAnim", true, false);
UIWidgets::Tooltip("Skips Link's taking breath animation after coming up from water. This setting does not interfere with getting items from underwater.");
ImGui::TableNextColumn();
UIWidgets::Spacer(0);
ImGui::Text("Changes:");
UIWidgets::PaddedSeparator();
UIWidgets::PaddedEnhancementSliderInt("Biggoron Forge Time: %d days", "##FORGETIME", "gForgeTime", 0, 3, "", 3, true, false, true);
UIWidgets::Tooltip("Allows you to change the number of days it takes for Biggoron to forge the Biggoron Sword");
UIWidgets::PaddedEnhancementCheckbox("Remember Save Location", "gRememberSaveLocation", false, false);
UIWidgets::Tooltip("When loading a save, places Link at the last entrance he went through.\n"
"This doesn't work if the save was made in a grotto.");
UIWidgets::PaddedEnhancementCheckbox("No Forced Navi", "gNoForcedNavi", true, false);
UIWidgets::Tooltip("Prevent forced Navi conversations");
UIWidgets::PaddedEnhancementCheckbox("Navi Timer Resets", "gEnhancements.ResetNaviTimer", true, false);
UIWidgets::Tooltip("Resets the Navi timer on scene change. If you have already talked to her, she will try and talk to you again, instead of needing a save warp or death. ");
UIWidgets::PaddedEnhancementCheckbox("No Skulltula Freeze", "gSkulltulaFreeze", true, false); UIWidgets::PaddedEnhancementCheckbox("No Skulltula Freeze", "gSkulltulaFreeze", true, false);
UIWidgets::Tooltip("Stops the game from freezing the player when picking up Gold Skulltulas"); UIWidgets::Tooltip("Stops the game from freezing the player when picking up Gold Skulltulas");
UIWidgets::PaddedEnhancementCheckbox("Nighttime GS Always Spawn", "gNightGSAlwaysSpawn", true, false); UIWidgets::PaddedEnhancementCheckbox("Nighttime GS Always Spawn", "gNightGSAlwaysSpawn", true, false);
@ -1164,7 +1213,8 @@ void DrawEnhancementsMenu() {
UIWidgets::Tooltip("Removes the dungeon entrance icon on the top-left corner of the screen when no dungeon is present on the current map"); UIWidgets::Tooltip("Removes the dungeon entrance icon on the top-left corner of the screen when no dungeon is present on the current map");
UIWidgets::PaddedEnhancementCheckbox("Fix Two Handed idle animations", "gTwoHandedIdle", true, false); UIWidgets::PaddedEnhancementCheckbox("Fix Two Handed idle animations", "gTwoHandedIdle", true, false);
UIWidgets::Tooltip("Re-enables the two-handed idle animation, a seemingly finished animation that was disabled on accident in the original game"); UIWidgets::Tooltip("Re-enables the two-handed idle animation, a seemingly finished animation that was disabled on accident in the original game");
UIWidgets::PaddedEnhancementCheckbox("Fix the Gravedigging Tour Glitch", "gGravediggingTourFix", true, false); UIWidgets::PaddedEnhancementCheckbox("Fix the Gravedigging Tour Glitch", "gGravediggingTourFix", true, false, SaveManager::Instance->IsRandoFile(),
"This setting is always enabled in randomizer files", UIWidgets::CheckboxGraphics::Checkmark);
UIWidgets::Tooltip("Fixes a bug where the Gravedigging Tour Heart Piece disappears if the area reloads"); UIWidgets::Tooltip("Fixes a bug where the Gravedigging Tour Heart Piece disappears if the area reloads");
UIWidgets::PaddedEnhancementCheckbox("Fix Deku Nut upgrade", "gDekuNutUpgradeFix", true, false); UIWidgets::PaddedEnhancementCheckbox("Fix Deku Nut upgrade", "gDekuNutUpgradeFix", true, false);
UIWidgets::Tooltip("Prevents the Forest Stage Deku Nut upgrade from becoming unobtainable after receiving the Poacher's Saw"); UIWidgets::Tooltip("Prevents the Forest Stage Deku Nut upgrade from becoming unobtainable after receiving the Poacher's Saw");

View File

@ -32,6 +32,7 @@
#include "scenes/misc/hakaana_ouke/hakaana_ouke_scene.h" #include "scenes/misc/hakaana_ouke/hakaana_ouke_scene.h"
#include "soh/Enhancements/randomizer/randomizer_entrance.h" #include "soh/Enhancements/randomizer/randomizer_entrance.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
u16 D_8011E1C0 = 0; u16 D_8011E1C0 = 0;
u16 D_8011E1C4 = 0; u16 D_8011E1C4 = 0;
@ -485,6 +486,7 @@ void func_80065134(PlayState* play, CutsceneContext* csCtx, CsCmdDayTime* cmd) {
gSaveContext.dayTime = temp1 + temp2; gSaveContext.dayTime = temp1 + temp2;
gSaveContext.skyboxTime = temp1 + temp2; gSaveContext.skyboxTime = temp1 + temp2;
LUSLOG_INFO("SET TIME %d", gSaveContext.dayTime);
} }
} }
@ -493,11 +495,16 @@ void Cutscene_Command_Terminator(PlayState* play, CutsceneContext* csCtx, CsCmdB
Player* player = GET_PLAYER(play); Player* player = GET_PLAYER(play);
s32 temp = 0; s32 temp = 0;
// Automatically skip certain cutscenes when in rando bool shouldSkipCommand = false;
// cmd->base == 8: Traveling back/forward in time cutscene
// cmd->base == 24: Dropping a fish for Jabu Jabu if (cmd->base == 8 && !GameInteractor_Should(GI_VB_PLAY_PULL_MASTER_SWORD_CS, true, NULL)) {
// cmd->base == 33: Zelda escaping with impa cutscene shouldSkipCommand = true;
bool randoCsSkip = (IS_RANDO && (cmd->base == 8 || cmd->base == 24 || cmd->base == 33)); }
if (cmd->base == 24 && !GameInteractor_Should(GI_VB_PLAY_DROP_FISH_FOR_JABU_CS, true, NULL)) {
shouldSkipCommand = true;
}
bool debugCsSkip = (CHECK_BTN_ALL(play->state.input[0].press.button, BTN_START) && bool debugCsSkip = (CHECK_BTN_ALL(play->state.input[0].press.button, BTN_START) &&
(gSaveContext.fileNum != 0xFEDC) && CVarGetInteger("gDebugEnabled", 0)); (gSaveContext.fileNum != 0xFEDC) && CVarGetInteger("gDebugEnabled", 0));
@ -558,7 +565,7 @@ void Cutscene_Command_Terminator(PlayState* play, CutsceneContext* csCtx, CsCmdB
} }
} }
if (playCutscene || (temp != 0) || ((csCtx->frames > 20) && (randoCsSkip || debugCsSkip))) { if (playCutscene || (temp != 0) || ((csCtx->frames > 20) && (shouldSkipCommand || debugCsSkip))) {
csCtx->state = CS_STATE_UNSKIPPABLE_EXEC; csCtx->state = CS_STATE_UNSKIPPABLE_EXEC;
Audio_SetCutsceneFlag(0); Audio_SetCutsceneFlag(0);
@ -624,7 +631,7 @@ void Cutscene_Command_Terminator(PlayState* play, CutsceneContext* csCtx, CsCmdB
gSaveContext.fw.set = 0; gSaveContext.fw.set = 0;
gSaveContext.respawn[RESPAWN_MODE_TOP].data = 0; gSaveContext.respawn[RESPAWN_MODE_TOP].data = 0;
} }
if (!Flags_GetEventChkInf(EVENTCHKINF_PULLED_MASTER_SWORD_FROM_PEDESTAL)) { if (GameInteractor_Should(GI_VB_PLAY_PULL_MASTER_SWORD_CS, !Flags_GetEventChkInf(EVENTCHKINF_PULLED_MASTER_SWORD_FROM_PEDESTAL), NULL)) {
Flags_SetEventChkInf(EVENTCHKINF_PULLED_MASTER_SWORD_FROM_PEDESTAL); Flags_SetEventChkInf(EVENTCHKINF_PULLED_MASTER_SWORD_FROM_PEDESTAL);
play->nextEntranceIndex = ENTR_CUTSCENE_MAP_0; play->nextEntranceIndex = ENTR_CUTSCENE_MAP_0;
play->transitionTrigger = TRANS_TRIGGER_START; play->transitionTrigger = TRANS_TRIGGER_START;
@ -716,7 +723,9 @@ void Cutscene_Command_Terminator(PlayState* play, CutsceneContext* csCtx, CsCmdB
play->transitionType = TRANS_TYPE_FADE_WHITE; play->transitionType = TRANS_TYPE_FADE_WHITE;
break; break;
case 22: case 22:
Item_Give(play, ITEM_SONG_REQUIEM); if (GameInteractor_Should(GI_VB_GIVE_ITEM_REQUIEM_OF_SPIRIT, true, NULL)) {
Item_Give(play, ITEM_SONG_REQUIEM);
}
play->nextEntranceIndex = ENTR_DESERT_COLOSSUS_0; play->nextEntranceIndex = ENTR_DESERT_COLOSSUS_0;
play->transitionTrigger = TRANS_TRIGGER_START; play->transitionTrigger = TRANS_TRIGGER_START;
gSaveContext.cutsceneIndex = 0xFFF0; gSaveContext.cutsceneIndex = 0xFFF0;
@ -768,7 +777,9 @@ void Cutscene_Command_Terminator(PlayState* play, CutsceneContext* csCtx, CsCmdB
play->nextEntranceIndex = ENTR_CHAMBER_OF_THE_SAGES_0; play->nextEntranceIndex = ENTR_CHAMBER_OF_THE_SAGES_0;
play->transitionTrigger = TRANS_TRIGGER_START; play->transitionTrigger = TRANS_TRIGGER_START;
play->transitionType = TRANS_TYPE_FADE_WHITE; play->transitionType = TRANS_TYPE_FADE_WHITE;
Item_Give(play, ITEM_MEDALLION_FIRE); if (GameInteractor_Should(GI_VB_GIVE_ITEM_FIRE_MEDALLION, true, NULL)) {
Item_Give(play, ITEM_MEDALLION_FIRE);
}
gSaveContext.chamberCutsceneNum = 1; gSaveContext.chamberCutsceneNum = 1;
break; break;
case 31: case 31:
@ -848,7 +859,9 @@ void Cutscene_Command_Terminator(PlayState* play, CutsceneContext* csCtx, CsCmdB
play->transitionType = TRANS_TYPE_FADE_BLACK_FAST; play->transitionType = TRANS_TYPE_FADE_BLACK_FAST;
break; break;
case 47: case 47:
Item_Give(play, ITEM_SONG_NOCTURNE); if (GameInteractor_Should(GI_VB_GIVE_ITEM_NOCTURNE_OF_SHADOW, true, NULL)) {
Item_Give(play, ITEM_SONG_NOCTURNE);
}
Flags_SetEventChkInf(EVENTCHKINF_LEARNED_NOCTURNE_OF_SHADOW); Flags_SetEventChkInf(EVENTCHKINF_LEARNED_NOCTURNE_OF_SHADOW);
play->nextEntranceIndex = ENTR_KAKARIKO_VILLAGE_0; play->nextEntranceIndex = ENTR_KAKARIKO_VILLAGE_0;
play->transitionTrigger = TRANS_TRIGGER_START; play->transitionTrigger = TRANS_TRIGGER_START;
@ -1292,7 +1305,7 @@ void Cutscene_Command_Terminator(PlayState* play, CutsceneContext* csCtx, CsCmdB
break; break;
} }
if (randoCsSkip) { if (shouldSkipCommand && IS_RANDO) {
Entrance_OverrideCutsceneEntrance(cmd->base); Entrance_OverrideCutsceneEntrance(cmd->base);
} }
} }
@ -1561,7 +1574,49 @@ void Cutscene_Command_Textbox(PlayState* play, CutsceneContext* csCtx, CsCmdText
} else if ((cmd->type == 4) && CHECK_QUEST_ITEM(QUEST_GORON_RUBY)) { } else if ((cmd->type == 4) && CHECK_QUEST_ITEM(QUEST_GORON_RUBY)) {
Message_StartTextbox(play, cmd->textId1, NULL); Message_StartTextbox(play, cmd->textId1, NULL);
} else { } else {
GetItemEntry getItemEntry = GET_ITEM_NONE;
if (IS_RANDO) {
switch (cmd->base) {
case 0x80:
getItemEntry = Randomizer_GetItemFromKnownCheck(RC_QUEEN_GOHMA, RG_KOKIRI_EMERALD);
break;
case 0x81:
getItemEntry = Randomizer_GetItemFromKnownCheck(RC_KING_DODONGO, RG_GORON_RUBY);
break;
case 0x82:
getItemEntry = Randomizer_GetItemFromKnownCheck(RC_BARINADE, RG_ZORA_SAPPHIRE);
break;
case 0x3E:
getItemEntry = Randomizer_GetItemFromKnownCheck(RC_PHANTOM_GANON, RG_FOREST_MEDALLION);
break;
case 0x3C:
getItemEntry = Randomizer_GetItemFromKnownCheck(RC_VOLVAGIA, RG_FIRE_MEDALLION);
break;
case 0x3D:
getItemEntry = Randomizer_GetItemFromKnownCheck(RC_MORPHA, RG_WATER_MEDALLION);
break;
case 0x3F:
getItemEntry = Randomizer_GetItemFromKnownCheck(RC_TWINROVA, RG_SPIRIT_MEDALLION);
break;
case 0x41:
getItemEntry = Randomizer_GetItemFromKnownCheck(RC_BONGO_BONGO, RG_SHADOW_MEDALLION);
break;
case 0x40:
getItemEntry = Randomizer_GetItemFromKnownCheck(RC_GIFT_FROM_SAGES, RG_LIGHT_MEDALLION);
break;
case 0x72:
getItemEntry = Randomizer_GetItemFromKnownCheck(RC_TOT_LIGHT_ARROWS_CUTSCENE, RG_LIGHT_ARROWS);
break;
}
if (getItemEntry.getItemId != GI_NONE) {
// cmd->base = getItemEntry.textId;
// GET_PLAYER(play)->getItemEntry = getItemEntry;
}
}
Message_StartTextbox(play, cmd->base, NULL); Message_StartTextbox(play, cmd->base, NULL);
if (IS_RANDO && getItemEntry.getItemId != GI_NONE) {
// GET_PLAYER(play)->getItemEntry = (GetItemEntry)GET_ITEM_NONE;
}
} }
return; return;
} }
@ -1584,12 +1639,14 @@ void Cutscene_Command_Textbox(PlayState* play, CutsceneContext* csCtx, CsCmdText
if ((dialogState == TEXT_STATE_CHOICE) && Message_ShouldAdvance(play)) { if ((dialogState == TEXT_STATE_CHOICE) && Message_ShouldAdvance(play)) {
if (play->msgCtx.choiceIndex == 0) { if (play->msgCtx.choiceIndex == 0) {
if (cmd->textId1 != 0xFFFF) { if (cmd->textId1 != 0xFFFF) {
// LUSLOG_INFO("Cutscene_Command_Textbox D: base:0x%x textId1:0x%x textId2:0x%x", cmd->base, cmd->textId1, cmd->textId2);
Message_ContinueTextbox(play, cmd->textId1); Message_ContinueTextbox(play, cmd->textId1);
} else { } else {
csCtx->frames++; csCtx->frames++;
} }
} else { } else {
if (cmd->textId2 != 0xFFFF) { if (cmd->textId2 != 0xFFFF) {
// LUSLOG_INFO("Cutscene_Command_Textbox E: base:0x%x textId1:0x%x textId2:0x%x", cmd->base, cmd->textId1, cmd->textId2);
Message_ContinueTextbox(play, cmd->textId2); Message_ContinueTextbox(play, cmd->textId2);
} else { } else {
csCtx->frames++; csCtx->frames++;
@ -1599,6 +1656,7 @@ void Cutscene_Command_Textbox(PlayState* play, CutsceneContext* csCtx, CsCmdText
if (dialogState == TEXT_STATE_9) { if (dialogState == TEXT_STATE_9) {
if (cmd->textId1 != 0xFFFF) { if (cmd->textId1 != 0xFFFF) {
// LUSLOG_INFO("Cutscene_Command_Textbox F: base:0x%x textId1:0x%x textId2:0x%x", cmd->base, cmd->textId1, cmd->textId2);
Message_ContinueTextbox(play, cmd->textId1); Message_ContinueTextbox(play, cmd->textId1);
} else { } else {
csCtx->frames++; csCtx->frames++;
@ -2113,34 +2171,23 @@ void Cutscene_HandleEntranceTriggers(PlayState* play) {
u8 requiredAge; u8 requiredAge;
s16 i; s16 i;
if (IS_RANDO &&
// don't skip epona escape cutscenes
gSaveContext.entranceIndex != ENTR_HYRULE_FIELD_11 &&
gSaveContext.entranceIndex != ENTR_HYRULE_FIELD_12 &&
gSaveContext.entranceIndex != ENTR_HYRULE_FIELD_13 &&
gSaveContext.entranceIndex != ENTR_HYRULE_FIELD_15 &&
// don't skip nabooru iron knuckle cs
gSaveContext.entranceIndex != ENTR_SPIRIT_TEMPLE_BOSS_0) {
gSaveContext.showTitleCard = false;
return;
}
for (i = 0; i < ARRAY_COUNT(sEntranceCutsceneTable); i++) { for (i = 0; i < ARRAY_COUNT(sEntranceCutsceneTable); i++) {
entranceCutscene = &sEntranceCutsceneTable[i]; entranceCutscene = &sEntranceCutsceneTable[i];
requiredAge = entranceCutscene->ageRestriction; requiredAge = entranceCutscene->ageRestriction;
if (requiredAge == 2) { if (requiredAge == 2) {
requiredAge = gSaveContext.linkAge; requiredAge = gSaveContext.linkAge;
} }
if ((gSaveContext.entranceIndex == entranceCutscene->entrance) && if ((gSaveContext.entranceIndex == entranceCutscene->entrance) &&
(!Flags_GetEventChkInf(entranceCutscene->flag) || (entranceCutscene->flag == 0x18)) && (!Flags_GetEventChkInf(entranceCutscene->flag) || (entranceCutscene->flag == EVENTCHKINF_EPONA_OBTAINED)) &&
(gSaveContext.cutsceneIndex < 0xFFF0) && ((u8)gSaveContext.linkAge == requiredAge) && (gSaveContext.cutsceneIndex < 0xFFF0) && ((u8)gSaveContext.linkAge == requiredAge) &&
(gSaveContext.respawnFlag <= 0)) { (gSaveContext.respawnFlag <= 0)) {
Flags_SetEventChkInf(entranceCutscene->flag); Flags_SetEventChkInf(entranceCutscene->flag);
Cutscene_SetSegment(play, entranceCutscene->segAddr); if (GameInteractor_Should(GI_VB_PLAY_ENTRANCE_CS, true, &entranceCutscene->flag)) {
gSaveContext.cutsceneTrigger = 2; Cutscene_SetSegment(play, entranceCutscene->segAddr);
gSaveContext.showTitleCard = false; gSaveContext.cutsceneTrigger = 2;
gSaveContext.showTitleCard = false;
}
break; break;
} }
} }
@ -2148,48 +2195,48 @@ void Cutscene_HandleEntranceTriggers(PlayState* play) {
void Cutscene_HandleConditionalTriggers(PlayState* play) { void Cutscene_HandleConditionalTriggers(PlayState* play) {
osSyncPrintf("\ngame_info.mode=[%d] restart_flag", ((void)0, gSaveContext.respawnFlag)); osSyncPrintf("\ngame_info.mode=[%d] restart_flag", ((void)0, gSaveContext.respawnFlag));
LUSLOG_INFO("Cutscene_HandleConditionalTriggers - entranceIndex: %#x cutsceneIndex: %#x", gSaveContext.entranceIndex, gSaveContext.cutsceneIndex);
if (!GameInteractor_Should(GI_VB_PLAY_TRANSITION_CS, true, NULL)) {
return;
}
if ((gSaveContext.gameMode == 0) && (gSaveContext.respawnFlag <= 0) && (gSaveContext.cutsceneIndex < 0xFFF0)) { if ((gSaveContext.gameMode == 0) && (gSaveContext.respawnFlag <= 0) && (gSaveContext.cutsceneIndex < 0xFFF0)) {
const bool bShouldTowerRandoSkip =
(IS_RANDO && Randomizer_GetSettingValue(RSK_SKIP_TOWER_ESCAPE));
if ((gSaveContext.entranceIndex == ENTR_DESERT_COLOSSUS_1) && !Flags_GetEventChkInf(EVENTCHKINF_LEARNED_REQUIEM_OF_SPIRIT)) { if ((gSaveContext.entranceIndex == ENTR_DESERT_COLOSSUS_1) && !Flags_GetEventChkInf(EVENTCHKINF_LEARNED_REQUIEM_OF_SPIRIT)) {
if (!IS_RANDO) { Flags_SetEventChkInf(EVENTCHKINF_LEARNED_REQUIEM_OF_SPIRIT);
Flags_SetEventChkInf(EVENTCHKINF_LEARNED_REQUIEM_OF_SPIRIT); gSaveContext.entranceIndex = ENTR_DESERT_COLOSSUS_0;
gSaveContext.entranceIndex = ENTR_DESERT_COLOSSUS_0; gSaveContext.cutsceneIndex = 0xFFF0;
gSaveContext.cutsceneIndex = 0xFFF0; } else if (GameInteractor_Should(GI_VB_BE_ELIGIBLE_FOR_NOCTURNE_OF_SHADOW, (
} (gSaveContext.entranceIndex == ENTR_KAKARIKO_VILLAGE_0) &&
} else if ((gSaveContext.entranceIndex == ENTR_KAKARIKO_VILLAGE_0) && LINK_IS_ADULT && (Flags_GetEventChkInf(EVENTCHKINF_USED_FOREST_TEMPLE_BLUE_WARP)) && LINK_IS_ADULT &&
(Flags_GetEventChkInf(EVENTCHKINF_USED_FIRE_TEMPLE_BLUE_WARP)) && (Flags_GetEventChkInf(EVENTCHKINF_USED_WATER_TEMPLE_BLUE_WARP)) && Flags_GetEventChkInf(EVENTCHKINF_USED_FOREST_TEMPLE_BLUE_WARP) &&
!Flags_GetEventChkInf(EVENTCHKINF_BONGO_BONGO_ESCAPED_FROM_WELL)) { Flags_GetEventChkInf(EVENTCHKINF_USED_FIRE_TEMPLE_BLUE_WARP) &&
if (!IS_RANDO) { Flags_GetEventChkInf(EVENTCHKINF_USED_WATER_TEMPLE_BLUE_WARP) &&
Flags_SetEventChkInf(EVENTCHKINF_BONGO_BONGO_ESCAPED_FROM_WELL); !Flags_GetEventChkInf(EVENTCHKINF_BONGO_BONGO_ESCAPED_FROM_WELL)
gSaveContext.cutsceneIndex = 0xFFF0; ), NULL)) {
} Flags_SetEventChkInf(EVENTCHKINF_BONGO_BONGO_ESCAPED_FROM_WELL);
gSaveContext.cutsceneIndex = 0xFFF0;
} else if ((gSaveContext.entranceIndex == ENTR_LOST_WOODS_9) && !Flags_GetEventChkInf(EVENTCHKINF_SPOKE_TO_SARIA_ON_BRIDGE)) { } else if ((gSaveContext.entranceIndex == ENTR_LOST_WOODS_9) && !Flags_GetEventChkInf(EVENTCHKINF_SPOKE_TO_SARIA_ON_BRIDGE)) {
if (!IS_RANDO) { Flags_SetEventChkInf(EVENTCHKINF_SPOKE_TO_SARIA_ON_BRIDGE);
Flags_SetEventChkInf(EVENTCHKINF_SPOKE_TO_SARIA_ON_BRIDGE); if (GameInteractor_Should(GI_VB_GIVE_ITEM_FAIRY_OCARINA, true, NULL)) {
Item_Give(play, ITEM_OCARINA_FAIRY); Item_Give(play, ITEM_OCARINA_FAIRY);
gSaveContext.entranceIndex = ENTR_LOST_WOODS_0;
gSaveContext.cutsceneIndex = 0xFFF0;
} }
} else if (CHECK_QUEST_ITEM(QUEST_MEDALLION_SPIRIT) && CHECK_QUEST_ITEM(QUEST_MEDALLION_SHADOW) && gSaveContext.entranceIndex = ENTR_LOST_WOODS_0;
LINK_IS_ADULT && !Flags_GetEventChkInf(EVENTCHKINF_RETURNED_TO_TEMPLE_OF_TIME_WITH_ALL_MEDALLIONS) && gSaveContext.cutsceneIndex = 0xFFF0;
(gEntranceTable[((void)0, gSaveContext.entranceIndex)].scene == SCENE_TEMPLE_OF_TIME)) { } else if (GameInteractor_Should(GI_VB_BE_ELIGIBLE_FOR_LIGHT_ARROWS, (
if (!IS_RANDO) { CHECK_QUEST_ITEM(QUEST_MEDALLION_SPIRIT) &&
Flags_SetEventChkInf(EVENTCHKINF_RETURNED_TO_TEMPLE_OF_TIME_WITH_ALL_MEDALLIONS); CHECK_QUEST_ITEM(QUEST_MEDALLION_SHADOW) &&
gSaveContext.entranceIndex = ENTR_TEMPLE_OF_TIME_0; LINK_IS_ADULT &&
gSaveContext.cutsceneIndex = 0xFFF8; !Flags_GetEventChkInf(EVENTCHKINF_RETURNED_TO_TEMPLE_OF_TIME_WITH_ALL_MEDALLIONS) &&
} (gEntranceTable[((void)0, gSaveContext.entranceIndex)].scene == SCENE_TEMPLE_OF_TIME)
} else if ((!Flags_GetEventChkInf(EVENTCHKINF_WATCHED_GANONS_CASTLE_COLLAPSE_CAUGHT_BY_GERUDO) && ), NULL)) {
gEntranceTable[((void)0, gSaveContext.entranceIndex)].scene == SCENE_GANON_BOSS) || Flags_SetEventChkInf(EVENTCHKINF_RETURNED_TO_TEMPLE_OF_TIME_WITH_ALL_MEDALLIONS);
(bShouldTowerRandoSkip && gSaveContext.entranceIndex = ENTR_TEMPLE_OF_TIME_0;
gEntranceTable[((void)0, gSaveContext.entranceIndex)].scene == SCENE_GANONS_TOWER_COLLAPSE_EXTERIOR)) { gSaveContext.cutsceneIndex = 0xFFF8;
} else if (!Flags_GetEventChkInf(EVENTCHKINF_WATCHED_GANONS_CASTLE_COLLAPSE_CAUGHT_BY_GERUDO) &&
(gEntranceTable[((void)0, gSaveContext.entranceIndex)].scene == SCENE_GANON_BOSS)) {
Flags_SetEventChkInf(EVENTCHKINF_WATCHED_GANONS_CASTLE_COLLAPSE_CAUGHT_BY_GERUDO); Flags_SetEventChkInf(EVENTCHKINF_WATCHED_GANONS_CASTLE_COLLAPSE_CAUGHT_BY_GERUDO);
gSaveContext.entranceIndex = ENTR_GANON_BOSS_0; gSaveContext.entranceIndex = ENTR_GANON_BOSS_0;
// In rando, skip the cutscene for the tower falling down after the escape.
if (IS_RANDO) {
return;
}
gSaveContext.cutsceneIndex = 0xFFF0; gSaveContext.cutsceneIndex = 0xFFF0;
} }
} }

View File

@ -3,6 +3,7 @@
#include "objects/gameplay_keep/gameplay_keep.h" #include "objects/gameplay_keep/gameplay_keep.h"
#include "overlays/effects/ovl_Effect_Ss_Dead_Sound/z_eff_ss_dead_sound.h" #include "overlays/effects/ovl_Effect_Ss_Dead_Sound/z_eff_ss_dead_sound.h"
#include "textures/icon_item_static/icon_item_static.h" #include "textures/icon_item_static/icon_item_static.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#define FLAGS 0 #define FLAGS 0
@ -349,6 +350,7 @@ void EnItem00_Init(Actor* thisx, PlayState* play) {
s32 getItemId = GI_NONE; s32 getItemId = GI_NONE;
this->randoGiEntry = (GetItemEntry)GET_ITEM_NONE; this->randoGiEntry = (GetItemEntry)GET_ITEM_NONE;
this->randoCheck = (RandomizerCheck)RC_UNKNOWN_CHECK; this->randoCheck = (RandomizerCheck)RC_UNKNOWN_CHECK;
this->itemEntry = (GetItemEntry)GET_ITEM_NONE;
s16 spawnParam8000 = this->actor.params & 0x8000; s16 spawnParam8000 = this->actor.params & 0x8000;
s32 pad1; s32 pad1;
@ -358,7 +360,7 @@ void EnItem00_Init(Actor* thisx, PlayState* play) {
this->actor.params &= 0xFF; this->actor.params &= 0xFF;
if (Flags_GetCollectible(play, this->collectibleFlag)) { if (GameInteractor_Should(GI_VB_ITEM00_DESPAWN, Flags_GetCollectible(play, this->collectibleFlag), this)) {
Actor_Kill(&this->actor); Actor_Kill(&this->actor);
return; return;
} }
@ -381,12 +383,7 @@ void EnItem00_Init(Actor* thisx, PlayState* play) {
this->unk_158 = 0; this->unk_158 = 0;
Actor_SetScale(&this->actor, 0.03f); Actor_SetScale(&this->actor, 0.03f);
this->scale = 0.03f; this->scale = 0.03f;
// Offset keys in randomizer slightly higher for their GID replacement yOffset = 350.0f;
if (!IS_RANDO) {
yOffset = 350.0f;
} else {
yOffset = 430.0f;
}
break; break;
case ITEM00_HEART_PIECE: case ITEM00_HEART_PIECE:
this->unk_158 = 0; this->unk_158 = 0;
@ -479,6 +476,14 @@ void EnItem00_Init(Actor* thisx, PlayState* play) {
Actor_SetScale(&this->actor, 0.03f); Actor_SetScale(&this->actor, 0.03f);
this->scale = 0.03f; this->scale = 0.03f;
break; break;
case ITEM00_SOH_GIVE_ITEM_ENTRY:
case ITEM00_SOH_GIVE_ITEM_ENTRY_GI:
case ITEM00_SOH_DUMMY:
this->unk_158 = 0;
Actor_SetScale(&this->actor, 0.03f);
this->scale = 0.03f;
yOffset = 430.0f;
break;
} }
this->unk_156 = 0; this->unk_156 = 0;
@ -507,6 +512,10 @@ void EnItem00_Init(Actor* thisx, PlayState* play) {
this->actor.velocity.y = 0.0f; this->actor.velocity.y = 0.0f;
this->actor.gravity = 0.0f; this->actor.gravity = 0.0f;
if (!GameInteractor_Should(GI_VB_GIVE_ITEM_FROM_ITEM_00, true, this)) {
return;
}
switch (this->actor.params) { switch (this->actor.params) {
case ITEM00_RUPEE_GREEN: case ITEM00_RUPEE_GREEN:
Item_Give(play, ITEM_RUPEE_GREEN); Item_Give(play, ITEM_RUPEE_GREEN);
@ -576,16 +585,8 @@ void EnItem00_Init(Actor* thisx, PlayState* play) {
break; break;
} }
if (!Actor_HasParent(&this->actor, play)) { if ((getItemId != GI_NONE) && !Actor_HasParent(&this->actor, play)) {
if (getItemId != GI_NONE) { func_8002F554(&this->actor, play, getItemId);
if (!IS_RANDO || this->randoGiEntry.getItemId == GI_NONE) {
func_8002F554(&this->actor, play, getItemId);
} else {
if (GiveItemEntryFromActorWithFixedRange(&this->actor, play, this->randoGiEntry) && this->randoInf != RAND_INF_MAX) {
Flags_SetRandomizerInf(this->randoInf);
}
}
}
} }
EnItem00_SetupAction(this, func_8001E5C8); EnItem00_SetupAction(this, func_8001E5C8);
@ -603,8 +604,7 @@ void func_8001DFC8(EnItem00* this, PlayState* play) {
(this->actor.params == ITEM00_HEART_PIECE)) { (this->actor.params == ITEM00_HEART_PIECE)) {
this->actor.shape.rot.y += 960; this->actor.shape.rot.y += 960;
} else { } else {
if ((this->actor.params >= ITEM00_SHIELD_DEKU) && (this->actor.params != ITEM00_BOMBS_SPECIAL) && if ((this->actor.params >= ITEM00_SHIELD_DEKU) && (this->actor.params < ITEM00_BOMBS_SPECIAL)) {
(this->actor.params != ITEM00_BOMBCHU)) {
if (this->unk_15A == -1) { if (this->unk_15A == -1) {
if (Math_SmoothStepToS(&this->actor.shape.rot.x, this->actor.world.rot.x - 0x4000, 2, 3000, 1500) == if (Math_SmoothStepToS(&this->actor.shape.rot.x, this->actor.world.rot.x - 0x4000, 2, 3000, 1500) ==
0) { 0) {
@ -697,8 +697,7 @@ void func_8001E304(EnItem00* this, PlayState* play) {
if (this->actor.params <= ITEM00_RUPEE_RED) { if (this->actor.params <= ITEM00_RUPEE_RED) {
this->actor.shape.rot.y += 960; this->actor.shape.rot.y += 960;
} else if ((this->actor.params >= ITEM00_SHIELD_DEKU) && (this->actor.params != ITEM00_BOMBS_SPECIAL) && } else if ((this->actor.params >= ITEM00_SHIELD_DEKU) && (this->actor.params < ITEM00_BOMBS_SPECIAL)) {
(this->actor.params != ITEM00_BOMBCHU)) {
this->actor.world.rot.x -= 700; this->actor.world.rot.x -= 700;
this->actor.shape.rot.y += 400; this->actor.shape.rot.y += 400;
this->actor.shape.rot.x = this->actor.world.rot.x - 0x4000; this->actor.shape.rot.x = this->actor.world.rot.x - 0x4000;
@ -733,13 +732,7 @@ void func_8001E5C8(EnItem00* this, PlayState* play) {
Player* player = GET_PLAYER(play); Player* player = GET_PLAYER(play);
if (this->getItemId != GI_NONE) { if (this->getItemId != GI_NONE) {
if (!Actor_HasParent(&this->actor, play)) { if (!Actor_HasParent(&this->actor, play)) {
if (!IS_RANDO) { func_8002F434(&this->actor, play, this->getItemId, 50.0f, 80.0f);
func_8002F434(&this->actor, play, this->getItemId, 50.0f, 80.0f);
} else {
if (GiveItemEntryFromActor(&this->actor, play, this->randoGiEntry, 50.0f, 80.0f) && this->randoInf != RAND_INF_MAX) {
Flags_SetRandomizerInf(this->randoInf);
}
}
this->unk_15A++; this->unk_15A++;
} else { } else {
this->getItemId = GI_NONE; this->getItemId = GI_NONE;
@ -781,13 +774,15 @@ void EnItem00_Update(Actor* thisx, PlayState* play) {
s32 pad; s32 pad;
// Rotate some drops when 3D drops are on, otherwise reset rotation back to 0 for billboard effect // Rotate some drops when 3D drops are on, otherwise reset rotation back to 0 for billboard effect
if ((this->actor.params == ITEM00_HEART && this->unk_15A >= 0) || if (
(this->actor.params == ITEM00_HEART && this->unk_15A >= 0) ||
(this->actor.params >= ITEM00_ARROWS_SMALL && this->actor.params <= ITEM00_SMALL_KEY) || (this->actor.params >= ITEM00_ARROWS_SMALL && this->actor.params <= ITEM00_SMALL_KEY) ||
this->actor.params == ITEM00_BOMBS_A || this->actor.params == ITEM00_ARROWS_SINGLE || this->actor.params == ITEM00_BOMBS_A ||
this->actor.params == ITEM00_BOMBS_SPECIAL || this->actor.params == ITEM00_BOMBCHU) { this->actor.params == ITEM00_ARROWS_SINGLE ||
if (CVarGetInteger("gNewDrops", 0) || this->actor.params == ITEM00_BOMBS_SPECIAL ||
// Keys in randomizer need to always rotate for their GID replacement (this->actor.params >= ITEM00_BOMBCHU && this->actor.params <= ITEM00_SOH_GIVE_ITEM_ENTRY_GI)
(IS_RANDO && this->actor.params == ITEM00_SMALL_KEY)) { ) {
if (CVarGetInteger("gNewDrops", 0) || (this->actor.params >= ITEM00_SOH_DUMMY && this->actor.params <= ITEM00_SOH_GIVE_ITEM_ENTRY_GI)) {
this->actor.shape.rot.y += 960; this->actor.shape.rot.y += 960;
} else { } else {
this->actor.shape.rot.y = 0; this->actor.shape.rot.y = 0;
@ -871,6 +866,10 @@ void EnItem00_Update(Actor* thisx, PlayState* play) {
return; return;
} }
if (!GameInteractor_Should(GI_VB_GIVE_ITEM_FROM_ITEM_00, true, this)) {
return;
}
switch (this->actor.params) { switch (this->actor.params) {
case ITEM00_RUPEE_GREEN: case ITEM00_RUPEE_GREEN:
Item_Give(play, ITEM_RUPEE_GREEN); Item_Give(play, ITEM_RUPEE_GREEN);
@ -955,14 +954,7 @@ void EnItem00_Update(Actor* thisx, PlayState* play) {
params = &this->actor.params; params = &this->actor.params;
if ((getItemId != GI_NONE) && !Actor_HasParent(&this->actor, play)) { if ((getItemId != GI_NONE) && !Actor_HasParent(&this->actor, play)) {
if (!IS_RANDO || this->randoGiEntry.getItemId == GI_NONE) { func_8002F554(&this->actor, play, getItemId);
func_8002F554(&this->actor, play, getItemId);
} else {
getItemId = this->randoGiEntry.getItemId;
if (GiveItemEntryFromActorWithFixedRange(&this->actor, play, this->randoGiEntry) && this->randoInf != RAND_INF_MAX) {
Flags_SetRandomizerInf(this->randoInf);
}
}
} }
switch (*params) { switch (*params) {
@ -1052,7 +1044,7 @@ void EnItem00_Draw(Actor* thisx, PlayState* play) {
} }
break; break;
case ITEM00_HEART_PIECE: case ITEM00_HEART_PIECE:
if (CVarGetInteger("gNewDrops", 0) && !IS_RANDO) { if (CVarGetInteger("gNewDrops", 0)) {
mtxScale = 21.0f; mtxScale = 21.0f;
Matrix_Scale(mtxScale, mtxScale, mtxScale, MTXMODE_APPLY); Matrix_Scale(mtxScale, mtxScale, mtxScale, MTXMODE_APPLY);
GetItem_Draw(play, GID_HEART_PIECE); GetItem_Draw(play, GID_HEART_PIECE);
@ -1162,7 +1154,7 @@ void EnItem00_Draw(Actor* thisx, PlayState* play) {
break; break;
} }
case ITEM00_SMALL_KEY: case ITEM00_SMALL_KEY:
if (CVarGetInteger("gNewDrops", 0) && !IS_RANDO) { if (CVarGetInteger("gNewDrops", 0)) {
mtxScale = 8.0f; mtxScale = 8.0f;
Matrix_Scale(mtxScale, mtxScale, mtxScale, MTXMODE_APPLY); Matrix_Scale(mtxScale, mtxScale, mtxScale, MTXMODE_APPLY);
GetItem_Draw(play, GID_KEY_SMALL); GetItem_Draw(play, GID_KEY_SMALL);
@ -1368,17 +1360,7 @@ static const Vtx customDropVtx[] = {
* Draw Function used for most collectible types of En_Item00 (ammo, bombs, sticks, nuts, magic...). * Draw Function used for most collectible types of En_Item00 (ammo, bombs, sticks, nuts, magic...).
*/ */
void EnItem00_DrawCollectible(EnItem00* this, PlayState* play) { void EnItem00_DrawCollectible(EnItem00* this, PlayState* play) {
if (IS_RANDO && (this->getItemId != GI_NONE || this->actor.params == ITEM00_SMALL_KEY)) { if (this->actor.params == ITEM00_BOMBCHU) {
if (this->randoCheck != RC_UNKNOWN_CHECK) {
this->randoGiEntry = Randomizer_GetItemFromKnownCheck(this->randoCheck, GI_NONE);
this->randoGiEntry.getItemFrom = ITEM_FROM_FREESTANDING;
}
f32 mtxScale = 10.67f;
Matrix_Scale(mtxScale, mtxScale, mtxScale, MTXMODE_APPLY);
EnItem00_CustomItemsParticles(&this->actor, play, this->randoGiEntry);
GetItemEntry_Draw(play, this->randoGiEntry);
} else if (this->actor.params == ITEM00_BOMBCHU) {
OPEN_DISPS(play->state.gfxCtx); OPEN_DISPS(play->state.gfxCtx);
Matrix_ReplaceRotation(&play->billboardMtxF); Matrix_ReplaceRotation(&play->billboardMtxF);
@ -1458,29 +1440,17 @@ void EnItem00_DrawHeartContainer(EnItem00* this, PlayState* play) {
* Draw Function used for the Piece of Heart type of En_Item00. * Draw Function used for the Piece of Heart type of En_Item00.
*/ */
void EnItem00_DrawHeartPiece(EnItem00* this, PlayState* play) { void EnItem00_DrawHeartPiece(EnItem00* this, PlayState* play) {
if (IS_RANDO) { s32 pad;
if (this->randoCheck != RC_UNKNOWN_CHECK) {
this->randoGiEntry = Randomizer_GetItemFromKnownCheck(this->randoCheck, GI_NONE);
this->randoGiEntry.getItemFrom = ITEM_FROM_FREESTANDING;
}
f32 mtxScale = 16.0f; OPEN_DISPS(play->state.gfxCtx);
Matrix_Scale(mtxScale, mtxScale, mtxScale, MTXMODE_APPLY);
EnItem00_CustomItemsParticles(&this->actor, play, this->randoGiEntry);
GetItemEntry_Draw(play, this->randoGiEntry);
} else {
s32 pad;
OPEN_DISPS(play->state.gfxCtx); Gfx_SetupDL_25Xlu(play->state.gfxCtx);
func_8002ED80(&this->actor, play, 0);
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(play->state.gfxCtx),
G_MTX_MODELVIEW | G_MTX_LOAD);
gSPDisplayList(POLY_XLU_DISP++, gHeartPieceInteriorDL);
Gfx_SetupDL_25Xlu(play->state.gfxCtx); CLOSE_DISPS(play->state.gfxCtx);
func_8002ED80(&this->actor, play, 0);
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(play->state.gfxCtx),
G_MTX_MODELVIEW | G_MTX_LOAD);
gSPDisplayList(POLY_XLU_DISP++, gHeartPieceInteriorDL);
CLOSE_DISPS(play->state.gfxCtx);
}
} }
/** /**

View File

@ -2603,8 +2603,23 @@ void Message_DrawMain(PlayState* play, Gfx** p) {
msgCtx->lastPlayedSong = msgCtx->ocarinaStaff->state; msgCtx->lastPlayedSong = msgCtx->ocarinaStaff->state;
msgCtx->msgMode = MSGMODE_SONG_PLAYBACK_SUCCESS; msgCtx->msgMode = MSGMODE_SONG_PLAYBACK_SUCCESS;
if (!IS_RANDO) { u8 songItemId = ITEM_SONG_MINUET + gOcarinaSongItemMap[msgCtx->ocarinaStaff->state];
Item_Give(play, ITEM_SONG_MINUET + gOcarinaSongItemMap[msgCtx->ocarinaStaff->state]);
if (
(songItemId == ITEM_SONG_MINUET && GameInteractor_Should(GI_VB_GIVE_ITEM_MINUET_OF_FOREST, true, NULL)) ||
(songItemId == ITEM_SONG_BOLERO && GameInteractor_Should(GI_VB_GIVE_ITEM_BOLERO_OF_FIRE, true, NULL)) ||
(songItemId == ITEM_SONG_SERENADE && GameInteractor_Should(GI_VB_GIVE_ITEM_SERENADE_OF_WATER, true, NULL)) ||
(songItemId == ITEM_SONG_REQUIEM && GameInteractor_Should(GI_VB_GIVE_ITEM_REQUIEM_OF_SPIRIT, true, NULL)) ||
(songItemId == ITEM_SONG_NOCTURNE && GameInteractor_Should(GI_VB_GIVE_ITEM_NOCTURNE_OF_SHADOW, true, NULL)) ||
(songItemId == ITEM_SONG_PRELUDE && GameInteractor_Should(GI_VB_GIVE_ITEM_PRELUDE_OF_LIGHT, true, NULL)) ||
(songItemId == ITEM_SONG_LULLABY && GameInteractor_Should(GI_VB_GIVE_ITEM_ZELDAS_LULLABY, true, NULL)) ||
(songItemId == ITEM_SONG_EPONA && GameInteractor_Should(GI_VB_GIVE_ITEM_EPONAS_SONG, true, NULL)) ||
(songItemId == ITEM_SONG_SARIA && GameInteractor_Should(GI_VB_GIVE_ITEM_SARIAS_SONG, true, NULL)) ||
(songItemId == ITEM_SONG_SUN && GameInteractor_Should(GI_VB_GIVE_ITEM_SUNS_SONG, true, NULL)) ||
(songItemId == ITEM_SONG_TIME && GameInteractor_Should(GI_VB_GIVE_ITEM_SONG_OF_TIME, true, NULL)) ||
(songItemId == ITEM_SONG_STORMS && GameInteractor_Should(GI_VB_GIVE_ITEM_SONG_OF_STORMS, true, NULL))
) {
Item_Give(play, songItemId);
} }
osSyncPrintf(VT_FGCOL(YELLOW)); osSyncPrintf(VT_FGCOL(YELLOW));

View File

@ -1,6 +1,7 @@
#include "global.h" #include "global.h"
#include "vt.h" #include "vt.h"
#include "overlays/actors/ovl_En_Sw/z_en_sw.h" #include "overlays/actors/ovl_En_Sw/z_en_sw.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
static s16 sDisableAttention = false; static s16 sDisableAttention = false;
static s16 sUnused = -1; static s16 sUnused = -1;
@ -810,12 +811,7 @@ s32 OnePointCutscene_SetInfo(PlayState* play, s16 camIdx, s16 csId, Actor* actor
break; break;
case 4100: case 4100:
csInfo->keyFrames = D_801225D4; csInfo->keyFrames = D_801225D4;
// RANDO: Waterfall opening cutscene skips to the end of the cutscene data earlier by doing this csInfo->keyFrameCnt = ARRAY_COUNT(D_801225D4);
if (!(IS_RANDO)) {
csInfo->keyFrameCnt = 5;
} else {
csInfo->keyFrameCnt = 2;
}
player->actor.shape.rot.y = player->actor.world.rot.y = player->currentYaw = 0x3FFC; player->actor.shape.rot.y = player->actor.world.rot.y = player->currentYaw = 0x3FFC;
func_800C0808(play, camIdx, player, CAM_SET_CS_C); func_800C0808(play, camIdx, player, CAM_SET_CS_C);
@ -1176,6 +1172,16 @@ s16 OnePointCutscene_Init(PlayState* play, s16 csId, s16 timer, Actor* actor, s1
s16 csCamIdx; s16 csCamIdx;
Camera* csCam; Camera* csCam;
if (actor != NULL && actor->id != ACTOR_PLAYER) {
if (!GameInteractor_Should(GI_VB_PLAY_ONEPOINT_ACTOR_CS, true, actor)) {
return;
}
} else {
if (!GameInteractor_Should(GI_VB_PLAY_ONEPOINT_CS, true, &csId)) {
return;
}
}
if (parentCamIdx == SUBCAM_ACTIVE) { if (parentCamIdx == SUBCAM_ACTIVE) {
parentCamIdx = play->activeCamera; parentCamIdx = play->activeCamera;
} }

View File

@ -2128,7 +2128,9 @@ u8 Item_Give(PlayState* play, u8 item) {
AMMO(ITEM_STICK) = CUR_CAPACITY(UPG_STICKS); AMMO(ITEM_STICK) = CUR_CAPACITY(UPG_STICKS);
} }
} }
item = ITEM_STICK; // [SOH] This results in the same behavior as the original code, but also allows us to get an accurate ReceivedItemEntry hook
INV_CONTENT(ITEM_STICK) = ITEM_STICK;
return Return_Item(item, MOD_NONE, returnItem);
} else if (item == ITEM_NUT) { } else if (item == ITEM_NUT) {
if (gSaveContext.inventory.items[slot] == ITEM_NONE) { if (gSaveContext.inventory.items[slot] == ITEM_NONE) {
Inventory_ChangeUpgrade(UPG_NUTS, 1); Inventory_ChangeUpgrade(UPG_NUTS, 1);
@ -2152,7 +2154,9 @@ u8 Item_Give(PlayState* play, u8 item) {
AMMO(ITEM_NUT) = CUR_CAPACITY(UPG_NUTS); AMMO(ITEM_NUT) = CUR_CAPACITY(UPG_NUTS);
} }
} }
item = ITEM_NUT; // [SOH] This results in the same behavior as the original code, but also allows us to get an accurate ReceivedItemEntry hook
INV_CONTENT(ITEM_NUT) = ITEM_NUT;
return Return_Item(item, MOD_NONE, returnItem);
} else if (item == ITEM_BOMB) { } else if (item == ITEM_BOMB) {
// "Bomb Bomb Bomb Bomb Bomb Bomb Bomb" // "Bomb Bomb Bomb Bomb Bomb Bomb Bomb"
osSyncPrintf(" 爆弾 爆弾 爆弾 爆弾 爆弾 爆弾 爆弾 \n"); osSyncPrintf(" 爆弾 爆弾 爆弾 爆弾 爆弾 爆弾 爆弾 \n");

View File

@ -226,60 +226,6 @@ void Play_Destroy(GameState* thisx) {
gPlayState = NULL; gPlayState = NULL;
} }
void GivePlayerRandoRewardSongOfTime(PlayState* play, RandomizerCheck check) {
Player* player = GET_PLAYER(play);
if (gSaveContext.entranceIndex == ENTR_HYRULE_FIELD_16 && player != NULL && !Player_InBlockingCsMode(play, player) &&
!Flags_GetTreasure(play, 0x1F) && gSaveContext.nextTransitionType == TRANS_NEXT_TYPE_DEFAULT && !gSaveContext.pendingIceTrapCount) {
GetItemEntry getItemEntry = Randomizer_GetItemFromKnownCheck(check, RG_SONG_OF_TIME);
GiveItemEntryWithoutActor(play, getItemEntry);
player->pendingFlag.flagID = 0x1F;
player->pendingFlag.flagType = FLAG_SCENE_TREASURE;
}
}
void GivePlayerRandoRewardNocturne(PlayState* play, RandomizerCheck check) {
Player* player = GET_PLAYER(play);
if ((gSaveContext.entranceIndex == ENTR_KAKARIKO_VILLAGE_0 ||
gSaveContext.entranceIndex == ENTR_KAKARIKO_VILLAGE_1 ||
gSaveContext.entranceIndex == ENTR_KAKARIKO_VILLAGE_2) && LINK_IS_ADULT && CHECK_QUEST_ITEM(QUEST_MEDALLION_FOREST) &&
CHECK_QUEST_ITEM(QUEST_MEDALLION_FIRE) && CHECK_QUEST_ITEM(QUEST_MEDALLION_WATER) && player != NULL &&
!Player_InBlockingCsMode(play, player) && !Flags_GetEventChkInf(EVENTCHKINF_BONGO_BONGO_ESCAPED_FROM_WELL)) {
GetItemEntry getItemEntry = Randomizer_GetItemFromKnownCheck(check, RG_NOCTURNE_OF_SHADOW);
GiveItemEntryWithoutActor(play, getItemEntry);
player->pendingFlag.flagID = 0xAA;
player->pendingFlag.flagType = FLAG_EVENT_CHECK_INF;
}
}
void GivePlayerRandoRewardRequiem(PlayState* play, RandomizerCheck check) {
Player* player = GET_PLAYER(play);
if ((gSaveContext.gameMode == 0) && (gSaveContext.respawnFlag <= 0) && (gSaveContext.cutsceneIndex < 0xFFF0)) {
if ((gSaveContext.entranceIndex == ENTR_DESERT_COLOSSUS_1) && !Flags_GetEventChkInf(EVENTCHKINF_LEARNED_REQUIEM_OF_SPIRIT) && player != NULL &&
!Player_InBlockingCsMode(play, player)) {
GetItemEntry getItemEntry = Randomizer_GetItemFromKnownCheck(check, RG_SONG_OF_TIME);
GiveItemEntryWithoutActor(play, getItemEntry);
player->pendingFlag.flagID = 0xAC;
player->pendingFlag.flagType = FLAG_EVENT_CHECK_INF;
}
}
}
void GivePlayerRandoRewardMasterSword(PlayState* play, RandomizerCheck check) {
Player* player = GET_PLAYER(play);
if (gSaveContext.entranceIndex == ENTR_TEMPLE_OF_TIME_2 && LINK_IS_ADULT && player != NULL &&
!Player_InBlockingCsMode(play, player) && Randomizer_GetSettingValue(RSK_SHUFFLE_MASTER_SWORD) &&
!Flags_GetRandomizerInf(RAND_INF_TOT_MASTER_SWORD)) {
GetItemEntry getItemEntry = Randomizer_GetItemFromKnownCheck(check, RG_MASTER_SWORD);
GiveItemEntryWithoutActor(play, getItemEntry);
player->pendingFlag.flagID = RAND_INF_TOT_MASTER_SWORD;
player->pendingFlag.flagType = FLAG_RANDOMIZER_INF;
}
}
u8 CheckStoneCount() { u8 CheckStoneCount() {
u8 stoneCount = 0; u8 stoneCount = 0;
@ -402,69 +348,6 @@ u8 CheckLACSRewardCount() {
return lacsRewardCount; return lacsRewardCount;
} }
void GivePlayerRandoRewardZeldaLightArrowsGift(PlayState* play, RandomizerCheck check) {
Player* player = GET_PLAYER(play);
u8 meetsRequirements = 0;
switch (Randomizer_GetSettingValue(RSK_GANONS_BOSS_KEY)) {
case RO_GANON_BOSS_KEY_LACS_STONES:
if ((CheckStoneCount() + CheckLACSRewardCount()) >= Randomizer_GetSettingValue(RSK_LACS_STONE_COUNT)) {
meetsRequirements = true;
}
break;
case RO_GANON_BOSS_KEY_LACS_MEDALLIONS:
if ((CheckMedallionCount() + CheckLACSRewardCount()) >= Randomizer_GetSettingValue(RSK_LACS_MEDALLION_COUNT)) {
meetsRequirements = true;
}
break;
case RO_GANON_BOSS_KEY_LACS_REWARDS:
if ((CheckMedallionCount() + CheckStoneCount() + CheckLACSRewardCount()) >= Randomizer_GetSettingValue(RSK_LACS_REWARD_COUNT)) {
meetsRequirements = true;
}
break;
case RO_GANON_BOSS_KEY_LACS_DUNGEONS:
if ((CheckDungeonCount() + CheckLACSRewardCount()) >= Randomizer_GetSettingValue(RSK_LACS_DUNGEON_COUNT)) {
meetsRequirements = true;
}
break;
case RO_GANON_BOSS_KEY_LACS_TOKENS:
if (gSaveContext.inventory.gsTokens >= Randomizer_GetSettingValue(RSK_LACS_TOKEN_COUNT)) {
meetsRequirements = true;
}
break;
default:
if (CHECK_QUEST_ITEM(QUEST_MEDALLION_SPIRIT) && CHECK_QUEST_ITEM(QUEST_MEDALLION_SHADOW)) {
meetsRequirements = true;
}
break;
}
if (meetsRequirements && LINK_IS_ADULT &&
(gEntranceTable[((void)0, gSaveContext.entranceIndex)].scene == SCENE_TEMPLE_OF_TIME) &&
!Flags_GetTreasure(play, 0x1E) && player != NULL && !Player_InBlockingCsMode(play, player) &&
play->transitionTrigger == TRANS_TRIGGER_OFF) {
GetItemEntry getItem = Randomizer_GetItemFromKnownCheck(check, GI_ARROW_LIGHT);
if (GiveItemEntryWithoutActor(play, getItem)) {
player->pendingFlag.flagID = 0x1E;
player->pendingFlag.flagType = FLAG_SCENE_TREASURE;
}
}
}
void GivePlayerRandoRewardSariaGift(PlayState* play, RandomizerCheck check) {
Player* player = GET_PLAYER(play);
if (gSaveContext.entranceIndex == ENTR_LOST_WOODS_9) {
GetItemEntry getItemEntry = Randomizer_GetItemFromKnownCheck(check, RG_ZELDAS_LULLABY);
if (!Flags_GetEventChkInf(EVENTCHKINF_SPOKE_TO_SARIA_ON_BRIDGE) && player != NULL && !Player_InBlockingCsMode(play, player)) {
GiveItemEntryWithoutActor(play, getItemEntry);
player->pendingFlag.flagType = FLAG_EVENT_CHECK_INF;
player->pendingFlag.flagID = 0xC1;
}
}
}
void Play_Init(GameState* thisx) { void Play_Init(GameState* thisx) {
PlayState* play = (PlayState*)thisx; PlayState* play = (PlayState*)thisx;
GraphicsContext* gfxCtx = play->state.gfxCtx; GraphicsContext* gfxCtx = play->state.gfxCtx;
@ -1448,15 +1331,6 @@ skip:
Environment_Update(play, &play->envCtx, &play->lightCtx, &play->pauseCtx, &play->msgCtx, Environment_Update(play, &play->envCtx, &play->lightCtx, &play->pauseCtx, &play->msgCtx,
&play->gameOverCtx, play->state.gfxCtx); &play->gameOverCtx, play->state.gfxCtx);
if (IS_RANDO) {
GivePlayerRandoRewardSariaGift(play, RC_LW_GIFT_FROM_SARIA);
GivePlayerRandoRewardSongOfTime(play, RC_SONG_FROM_OCARINA_OF_TIME);
GivePlayerRandoRewardZeldaLightArrowsGift(play, RC_TOT_LIGHT_ARROWS_CUTSCENE);
GivePlayerRandoRewardNocturne(play, RC_SHEIK_IN_KAKARIKO);
GivePlayerRandoRewardRequiem(play, RC_SHEIK_AT_COLOSSUS);
GivePlayerRandoRewardMasterSword(play, RC_TOT_MASTER_SWORD);
}
} }
void Play_DrawOverlayElements(PlayState* play) { void Play_DrawOverlayElements(PlayState* play) {

View File

@ -564,8 +564,8 @@ uint8_t Player_IsCustomLinkModel() {
} }
s32 Player_InBlockingCsMode(PlayState* play, Player* this) { s32 Player_InBlockingCsMode(PlayState* play, Player* this) {
return (this->stateFlags1 & 0x20000080) || (this->csAction != 0) || (play->transitionTrigger == TRANS_TRIGGER_START) || return (this->stateFlags1 & (PLAYER_STATE1_DEAD | PLAYER_STATE1_IN_CUTSCENE)) || (this->csAction != 0) || (play->transitionTrigger == TRANS_TRIGGER_START) ||
(this->stateFlags1 & 1) || (this->stateFlags3 & 0x80) || (this->stateFlags1 & PLAYER_STATE1_LOADING) || (this->stateFlags3 & PLAYER_STATE3_HOOKSHOT_TRAVELLING) ||
((gSaveContext.magicState != MAGIC_STATE_IDLE) && (Player_ActionToMagicSpell(this, this->itemAction) >= 0)); ((gSaveContext.magicState != MAGIC_STATE_IDLE) && (Player_ActionToMagicSpell(this, this->itemAction) >= 0));
} }

View File

@ -6,6 +6,7 @@
#include "z_bg_bdan_switch.h" #include "z_bg_bdan_switch.h"
#include "objects/object_bdan_objects/object_bdan_objects.h" #include "objects/object_bdan_objects/object_bdan_objects.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#define FLAGS ACTOR_FLAG_UPDATE_WHILE_CULLED #define FLAGS ACTOR_FLAG_UPDATE_WHILE_CULLED
@ -224,10 +225,12 @@ void func_8086D4B4(BgBdanSwitch* this, PlayState* play) {
if (!Flags_GetSwitch(play, (this->dyna.actor.params >> 8) & 0x3F)) { if (!Flags_GetSwitch(play, (this->dyna.actor.params >> 8) & 0x3F)) {
type = this->dyna.actor.params & 0xFF; type = this->dyna.actor.params & 0xFF;
Flags_SetSwitch(play, (this->dyna.actor.params >> 8) & 0x3F); Flags_SetSwitch(play, (this->dyna.actor.params >> 8) & 0x3F);
if (type == BLUE || type == YELLOW_TALL_2) { if (GameInteractor_Should(GI_VB_PLAY_ONEPOINT_ACTOR_CS, true, this)) {
OnePointCutscene_AttentionSetSfx(play, &this->dyna.actor, NA_SE_SY_TRE_BOX_APPEAR); if (type == BLUE || type == YELLOW_TALL_2) {
} else { OnePointCutscene_AttentionSetSfx(play, &this->dyna.actor, NA_SE_SY_TRE_BOX_APPEAR);
OnePointCutscene_AttentionSetSfx(play, &this->dyna.actor, NA_SE_SY_CORRECT_CHIME); } else {
OnePointCutscene_AttentionSetSfx(play, &this->dyna.actor, NA_SE_SY_CORRECT_CHIME);
}
} }
} }
} }
@ -236,7 +239,9 @@ void func_8086D548(BgBdanSwitch* this, PlayState* play) {
if (Flags_GetSwitch(play, (this->dyna.actor.params >> 8) & 0x3F)) { if (Flags_GetSwitch(play, (this->dyna.actor.params >> 8) & 0x3F)) {
Flags_UnsetSwitch(play, (this->dyna.actor.params >> 8) & 0x3F); Flags_UnsetSwitch(play, (this->dyna.actor.params >> 8) & 0x3F);
if ((this->dyna.actor.params & 0xFF) == YELLOW_TALL_2) { if ((this->dyna.actor.params & 0xFF) == YELLOW_TALL_2) {
OnePointCutscene_AttentionSetSfx(play, &this->dyna.actor, NA_SE_SY_TRE_BOX_APPEAR); if (GameInteractor_Should(GI_VB_PLAY_ONEPOINT_ACTOR_CS, true, this)) {
OnePointCutscene_AttentionSetSfx(play, &this->dyna.actor, NA_SE_SY_TRE_BOX_APPEAR);
}
} }
} }
} }

View File

@ -8,6 +8,7 @@
#include "scenes/dungeons/ddan/ddan_scene.h" #include "scenes/dungeons/ddan/ddan_scene.h"
#include "objects/object_bwall/object_bwall.h" #include "objects/object_bwall/object_bwall.h"
#include "objects/object_kingdodongo/object_kingdodongo.h" #include "objects/object_kingdodongo/object_kingdodongo.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#define FLAGS ACTOR_FLAG_UPDATE_WHILE_CULLED #define FLAGS ACTOR_FLAG_UPDATE_WHILE_CULLED
@ -300,10 +301,13 @@ void BgBreakwall_Wait(BgBreakwall* this, PlayState* play) {
if ((wallType == BWALL_DC_ENTRANCE) && (!Flags_GetEventChkInf(EVENTCHKINF_ENTERED_DODONGOS_CAVERN))) { if ((wallType == BWALL_DC_ENTRANCE) && (!Flags_GetEventChkInf(EVENTCHKINF_ENTERED_DODONGOS_CAVERN))) {
Flags_SetEventChkInf(EVENTCHKINF_ENTERED_DODONGOS_CAVERN); Flags_SetEventChkInf(EVENTCHKINF_ENTERED_DODONGOS_CAVERN);
Cutscene_SetSegment(play, gDcOpeningCs); s32 flag = EVENTCHKINF_ENTERED_DODONGOS_CAVERN;
gSaveContext.cutsceneTrigger = 1; if (GameInteractor_Should(GI_VB_PLAY_ENTRANCE_CS, true, &flag)) {
Cutscene_SetSegment(play, gDcOpeningCs);
gSaveContext.cutsceneTrigger = 1;
func_8002DF54(play, NULL, 0x31);
}
Audio_PlaySoundGeneral(NA_SE_SY_CORRECT_CHIME, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); Audio_PlaySoundGeneral(NA_SE_SY_CORRECT_CHIME, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8);
func_8002DF54(play, NULL, 0x31);
} }
if (this->dyna.actor.params < 0) { if (this->dyna.actor.params < 0) {

View File

@ -6,6 +6,7 @@
#include "z_bg_mori_bigst.h" #include "z_bg_mori_bigst.h"
#include "objects/object_mori_objects/object_mori_objects.h" #include "objects/object_mori_objects/object_mori_objects.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#define FLAGS ACTOR_FLAG_UPDATE_WHILE_CULLED #define FLAGS ACTOR_FLAG_UPDATE_WHILE_CULLED
@ -149,7 +150,9 @@ void BgMoriBigst_StalfosFight(BgMoriBigst* this, PlayState* play) {
if ((this->dyna.actor.home.rot.z == 0) && if ((this->dyna.actor.home.rot.z == 0) &&
((this->dyna.actor.home.pos.y - 5.0f) <= GET_PLAYER(play)->actor.world.pos.y)) { ((this->dyna.actor.home.pos.y - 5.0f) <= GET_PLAYER(play)->actor.world.pos.y)) {
BgMoriBigst_SetupFall(this, play); BgMoriBigst_SetupFall(this, play);
OnePointCutscene_Init(play, 3220, 72, &this->dyna.actor, MAIN_CAM); if (GameInteractor_Should(GI_VB_PLAY_ONEPOINT_ACTOR_CS, true, this)) {
OnePointCutscene_Init(play, 3220, 72, &this->dyna.actor, MAIN_CAM);
}
} }
} }
@ -163,8 +166,10 @@ void BgMoriBigst_Fall(BgMoriBigst* this, PlayState* play) {
this->dyna.actor.world.pos.y = this->dyna.actor.home.pos.y; this->dyna.actor.world.pos.y = this->dyna.actor.home.pos.y;
BgMoriBigst_SetupLanding(this, play); BgMoriBigst_SetupLanding(this, play);
Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_STONE_BOUND); Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_STONE_BOUND);
OnePointCutscene_Init(play, 1020, 8, &this->dyna.actor, MAIN_CAM); if (GameInteractor_Should(GI_VB_PLAY_ONEPOINT_ACTOR_CS, true, this)) {
func_8002DF38(play, NULL, 0x3C); OnePointCutscene_Init(play, 1020, 8, &this->dyna.actor, MAIN_CAM);
func_8002DF38(play, NULL, 0x3C);
}
} }
} }

View File

@ -6,6 +6,7 @@
#include "z_bg_toki_swd.h" #include "z_bg_toki_swd.h"
#include "objects/object_toki_objects/object_toki_objects.h" #include "objects/object_toki_objects/object_toki_objects.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#define FLAGS ACTOR_FLAG_UPDATE_WHILE_CULLED #define FLAGS ACTOR_FLAG_UPDATE_WHILE_CULLED
@ -115,14 +116,17 @@ void func_808BAF40(BgTokiSwd* this, PlayState* play) {
if (((Flags_GetEventChkInf(EVENTCHKINF_ENTERED_MASTER_SWORD_CHAMBER)) == 0) && (gSaveContext.sceneSetupIndex < 4) && if (((Flags_GetEventChkInf(EVENTCHKINF_ENTERED_MASTER_SWORD_CHAMBER)) == 0) && (gSaveContext.sceneSetupIndex < 4) &&
Actor_IsFacingAndNearPlayer(&this->actor, 800.0f, 0x7530) && !Play_InCsMode(play)) { Actor_IsFacingAndNearPlayer(&this->actor, 800.0f, 0x7530) && !Play_InCsMode(play)) {
Flags_SetEventChkInf(EVENTCHKINF_ENTERED_MASTER_SWORD_CHAMBER); Flags_SetEventChkInf(EVENTCHKINF_ENTERED_MASTER_SWORD_CHAMBER);
play->csCtx.segment = D_808BBD90; s32 flag = EVENTCHKINF_ENTERED_MASTER_SWORD_CHAMBER;
gSaveContext.cutsceneTrigger = 1; if (GameInteractor_Should(GI_VB_PLAY_ENTRANCE_CS, true, &flag)) {
play->csCtx.segment = D_808BBD90;
gSaveContext.cutsceneTrigger = 1;
}
} }
if (!LINK_IS_ADULT || (Flags_GetEventChkInf(EVENTCHKINF_LEARNED_PRELUDE_OF_LIGHT) && !IS_RANDO) || IS_RANDO) { if (!LINK_IS_ADULT || (Flags_GetEventChkInf(EVENTCHKINF_LEARNED_PRELUDE_OF_LIGHT) && !IS_RANDO) || IS_RANDO) {
if (Actor_HasParent(&this->actor, play)) { if (Actor_HasParent(&this->actor, play)) {
if (!LINK_IS_ADULT) { if (!LINK_IS_ADULT) {
if (!IS_RANDO || !Randomizer_GetSettingValue(RSK_SHUFFLE_MASTER_SWORD)) { if (GameInteractor_Should(GI_VB_GIVE_ITEM_MASTER_SWORD, true, NULL)) {
Item_Give(play, ITEM_SWORD_MASTER); Item_Give(play, ITEM_SWORD_MASTER);
} }
play->csCtx.segment = D_808BB2F0; play->csCtx.segment = D_808BB2F0;

View File

@ -7,6 +7,7 @@
#include "z_bg_treemouth.h" #include "z_bg_treemouth.h"
#include "objects/object_spot04_objects/object_spot04_objects.h" #include "objects/object_spot04_objects/object_spot04_objects.h"
#include "overlays/effects/ovl_Effect_Ss_Hahen/z_eff_ss_hahen.h" #include "overlays/effects/ovl_Effect_Ss_Hahen/z_eff_ss_hahen.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#define FLAGS (ACTOR_FLAG_UPDATE_WHILE_CULLED | ACTOR_FLAG_DRAW_WHILE_CULLED) #define FLAGS (ACTOR_FLAG_UPDATE_WHILE_CULLED | ACTOR_FLAG_DRAW_WHILE_CULLED)
@ -73,11 +74,7 @@ void BgTreemouth_Init(Actor* thisx, PlayState* play) {
if ((gSaveContext.sceneSetupIndex < 4) && !LINK_IS_ADULT) { if ((gSaveContext.sceneSetupIndex < 4) && !LINK_IS_ADULT) {
BgTreemouth_SetupAction(this, func_808BC8B8); BgTreemouth_SetupAction(this, func_808BC8B8);
// If dungeon entrance randomizer is on, keep the tree mouth open } else if (LINK_IS_ADULT || (gSaveContext.sceneSetupIndex == 7)) {
// when Link is adult and sword & shield have been shown to Mido
} else if ((LINK_IS_ADULT && (!IS_RANDO ||
Randomizer_GetSettingValue(RSK_SHUFFLE_DUNGEON_ENTRANCES) == RO_DUNGEON_ENTRANCE_SHUFFLE_OFF) ||
!Flags_GetEventChkInf(EVENTCHKINF_SHOWED_MIDO_SWORD_SHIELD)) || (gSaveContext.sceneSetupIndex == 7)) {
this->unk_168 = 0.0f; this->unk_168 = 0.0f;
BgTreemouth_SetupAction(this, BgTreemouth_DoNothing); BgTreemouth_SetupAction(this, BgTreemouth_DoNothing);
} else { } else {
@ -157,9 +154,11 @@ void func_808BC8B8(BgTreemouth* this, PlayState* play) {
} }
} else if (Actor_IsFacingAndNearPlayer(&this->dyna.actor, 1658.0f, 0x4E20)) { } else if (Actor_IsFacingAndNearPlayer(&this->dyna.actor, 1658.0f, 0x4E20)) {
Flags_SetEventChkInf(EVENTCHKINF_MET_DEKU_TREE); Flags_SetEventChkInf(EVENTCHKINF_MET_DEKU_TREE);
play->csCtx.segment = D_808BCE20; if (GameInteractor_Should(GI_VB_PLAY_DEKU_TREE_INTRO_CS, true, this)) {
gSaveContext.cutsceneTrigger = 1; play->csCtx.segment = D_808BCE20;
BgTreemouth_SetupAction(this, func_808BC9EC); gSaveContext.cutsceneTrigger = 1;
BgTreemouth_SetupAction(this, func_808BC9EC);
}
} }
} }
} else { } else {

View File

@ -15,4 +15,7 @@ typedef struct BgTreemouth {
/* 0x016C */ BgTreemouthActionFunc actionFunc; /* 0x016C */ BgTreemouthActionFunc actionFunc;
} BgTreemouth; // size = 0x0170 } BgTreemouth; // size = 0x0170
void BgTreemouth_SetupAction(BgTreemouth* actor, BgTreemouthActionFunc actionFunc);
void func_808BC6F8(BgTreemouth* actor, PlayState* play);
#endif #endif

View File

@ -3,6 +3,7 @@
#include "overlays/actors/ovl_Demo_Effect/z_demo_effect.h" #include "overlays/actors/ovl_Demo_Effect/z_demo_effect.h"
#include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h" #include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h"
#include "vt.h" #include "vt.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#define FLAGS ACTOR_FLAG_UPDATE_WHILE_CULLED #define FLAGS ACTOR_FLAG_UPDATE_WHILE_CULLED
@ -184,7 +185,9 @@ void func_80969F38(DemoDu* this, PlayState* play) {
Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_DEMO_EFFECT, posX, posY, posZ, 0, 0, 0, Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_DEMO_EFFECT, posX, posY, posZ, 0, 0, 0,
DEMO_EFFECT_MEDAL_FIRE); DEMO_EFFECT_MEDAL_FIRE);
Item_Give(play, ITEM_MEDALLION_FIRE); if (GameInteractor_Should(GI_VB_GIVE_ITEM_FIRE_MEDALLION, true, NULL)) {
Item_Give(play, ITEM_MEDALLION_FIRE);
}
} }
void func_80969FB4(DemoDu* this, PlayState* play) { void func_80969FB4(DemoDu* this, PlayState* play) {
@ -201,7 +204,9 @@ void DemoDu_CsFireMedallion_AdvanceTo01(DemoDu* this, PlayState* play) {
this->updateIndex = CS_FIREMEDALLION_SUBSCENE(1); this->updateIndex = CS_FIREMEDALLION_SUBSCENE(1);
play->csCtx.segment = D_8096C1A4; play->csCtx.segment = D_8096C1A4;
gSaveContext.cutsceneTrigger = 2; gSaveContext.cutsceneTrigger = 2;
Item_Give(play, ITEM_MEDALLION_FIRE); if (GameInteractor_Should(GI_VB_GIVE_ITEM_FIRE_MEDALLION, true, NULL)) {
Item_Give(play, ITEM_MEDALLION_FIRE);
}
player->actor.world.rot.y = player->actor.shape.rot.y = this->actor.world.rot.y + 0x8000; player->actor.world.rot.y = player->actor.shape.rot.y = this->actor.world.rot.y + 0x8000;
} }

View File

@ -142,7 +142,7 @@ f32 DemoEffect_InterpolateCsFrames(PlayState* play, s32 csActionId) {
*/ */
void DemoEffect_InitJewel(PlayState* play, DemoEffect* this) { void DemoEffect_InitJewel(PlayState* play, DemoEffect* this) {
this->initDrawFunc = DemoEffect_DrawJewel; this->initDrawFunc = DemoEffect_DrawJewel;
if (IS_RANDO && play->sceneNum == SCENE_JABU_JABU) { if (IS_RANDO && (play->sceneNum != SCENE_TEMPLE_OF_TIME || this->actor.params == DEMO_EFFECT_LIGHTARROW)) {
this->initDrawFunc = DemoEffect_DrawGetItem; this->initDrawFunc = DemoEffect_DrawGetItem;
} }
if (!LINK_IS_ADULT) { if (!LINK_IS_ADULT) {
@ -156,7 +156,7 @@ void DemoEffect_InitJewel(PlayState* play, DemoEffect* this) {
Actor_SetScale(&this->actor, 0.10f); Actor_SetScale(&this->actor, 0.10f);
} }
this->csActionId = 1; this->csActionId = 1;
this->actor.shape.rot.x = (IS_RANDO && play->sceneNum == SCENE_JABU_JABU) ? 0 : 16384; this->actor.shape.rot.x = (IS_RANDO && (play->sceneNum != SCENE_TEMPLE_OF_TIME || this->actor.params == DEMO_EFFECT_LIGHTARROW)) ? 0 : 16384;
DemoEffect_InitJewelColor(this); DemoEffect_InitJewelColor(this);
this->jewel.alpha = 0; this->jewel.alpha = 0;
this->jewelCsRotation.x = this->jewelCsRotation.y = this->jewelCsRotation.z = 0; this->jewelCsRotation.x = this->jewelCsRotation.y = this->jewelCsRotation.z = 0;
@ -2087,13 +2087,48 @@ void DemoEffect_DrawGetItem(Actor* thisx, PlayState* play) {
this->getItem.isLoaded = 1; this->getItem.isLoaded = 1;
return; return;
} }
if (IS_RANDO && play->sceneNum == SCENE_JABU_JABU) { if (IS_RANDO && (play->sceneNum != SCENE_TEMPLE_OF_TIME || this->actor.params == DEMO_EFFECT_LIGHTARROW)) {
GetItemEntry getItemEntry = Randomizer_GetItemFromKnownCheck(RC_BARINADE, RG_ZORA_SAPPHIRE); GetItemEntry getItemEntry = GET_ITEM_NONE;
this->getItem.drawId = getItemEntry.gid;
func_8002EBCC(thisx, play, 0); switch (this->actor.params) {
func_8002ED80(thisx, play, 0); case DEMO_EFFECT_JEWEL_KOKIRI:
GetItemEntry_Draw(play, getItemEntry); getItemEntry = Randomizer_GetItemFromKnownCheck(RC_QUEEN_GOHMA, RG_KOKIRI_EMERALD);
return; break;
case DEMO_EFFECT_JEWEL_GORON:
getItemEntry = Randomizer_GetItemFromKnownCheck(RC_KING_DODONGO, RG_GORON_RUBY);
break;
case DEMO_EFFECT_JEWEL_ZORA:
getItemEntry = Randomizer_GetItemFromKnownCheck(RC_BARINADE, RG_ZORA_SAPPHIRE);
break;
case DEMO_EFFECT_MEDAL_FOREST:
getItemEntry = Randomizer_GetItemFromKnownCheck(RC_PHANTOM_GANON, RG_FOREST_MEDALLION);
break;
case DEMO_EFFECT_MEDAL_FIRE:
getItemEntry = Randomizer_GetItemFromKnownCheck(RC_VOLVAGIA, RG_FIRE_MEDALLION);
break;
case DEMO_EFFECT_MEDAL_WATER:
getItemEntry = Randomizer_GetItemFromKnownCheck(RC_MORPHA, RG_WATER_MEDALLION);
break;
case DEMO_EFFECT_MEDAL_SPIRIT:
getItemEntry = Randomizer_GetItemFromKnownCheck(RC_TWINROVA, RG_SPIRIT_MEDALLION);
break;
case DEMO_EFFECT_MEDAL_SHADOW:
getItemEntry = Randomizer_GetItemFromKnownCheck(RC_BONGO_BONGO, RG_SHADOW_MEDALLION);
break;
case DEMO_EFFECT_MEDAL_LIGHT:
getItemEntry = Randomizer_GetItemFromKnownCheck(RC_GIFT_FROM_SAGES, RG_LIGHT_MEDALLION);
break;
case DEMO_EFFECT_LIGHTARROW:
getItemEntry = Randomizer_GetItemFromKnownCheck(RC_TOT_LIGHT_ARROWS_CUTSCENE, RG_LIGHT_ARROWS);
break;
}
if (getItemEntry.getItemId != GI_NONE) {
this->getItem.drawId = getItemEntry.gid;
func_8002EBCC(thisx, play, 0);
func_8002ED80(thisx, play, 0);
GetItemEntry_Draw(play, getItemEntry);
return;
}
} }
func_8002EBCC(thisx, play, 0); func_8002EBCC(thisx, play, 0);
func_8002ED80(thisx, play, 0); func_8002ED80(thisx, play, 0);

View File

@ -10,6 +10,7 @@
#include "scenes/indoors/nakaniwa/nakaniwa_scene.h" #include "scenes/indoors/nakaniwa/nakaniwa_scene.h"
#include "objects/object_im/object_im.h" #include "objects/object_im/object_im.h"
#include "vt.h" #include "vt.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_UPDATE_WHILE_CULLED) #define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_UPDATE_WHILE_CULLED)
@ -318,7 +319,9 @@ void func_809853B4(DemoIm* this, PlayState* play) {
Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_DEMO_EFFECT, playerX, playerY, playerZ, 0, Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_DEMO_EFFECT, playerX, playerY, playerZ, 0,
0, 0, 0xD); 0, 0, 0xD);
Item_Give(play, ITEM_MEDALLION_SHADOW); if (GameInteractor_Should(GI_VB_GIVE_ITEM_SHADOW_MEDALLION, true, NULL)) {
Item_Give(play, ITEM_MEDALLION_SHADOW);
}
} }
void func_80985430(DemoIm* this, PlayState* play) { void func_80985430(DemoIm* this, PlayState* play) {
@ -334,7 +337,9 @@ void func_8098544C(DemoIm* this, PlayState* play) {
this->action = 1; this->action = 1;
play->csCtx.segment = D_8098786C; play->csCtx.segment = D_8098786C;
gSaveContext.cutsceneTrigger = 2; gSaveContext.cutsceneTrigger = 2;
Item_Give(play, ITEM_MEDALLION_SHADOW); if (GameInteractor_Should(GI_VB_GIVE_ITEM_SHADOW_MEDALLION, true, NULL)) {
Item_Give(play, ITEM_MEDALLION_SHADOW);
}
player->actor.world.rot.y = player->actor.shape.rot.y = this->actor.world.rot.y + 0x8000; player->actor.world.rot.y = player->actor.shape.rot.y = this->actor.world.rot.y + 0x8000;
} }
} }
@ -903,40 +908,17 @@ void func_80986BF8(DemoIm* this, PlayState* play) {
} }
} }
void GivePlayerRandoRewardImpa(Actor* impa, PlayState* play, RandomizerCheck check) {
GetItemEntry getItemEntry = Randomizer_GetItemFromKnownCheck(check, RG_ZELDAS_LULLABY);
if (impa->parent != NULL && impa->parent->id == GET_PLAYER(play)->actor.id &&
!Flags_GetTreasure(play, 0x1F)) {
Flags_SetTreasure(play, 0x1F);
} else if (!Flags_GetTreasure(play, 0x1F) && !Randomizer_GetSettingValue(RSK_SKIP_CHILD_ZELDA)) {
GiveItemEntryFromActor(impa, play, getItemEntry, 75.0f, 50.0f);
} else if (!Player_InBlockingCsMode(play, GET_PLAYER(play))) {
Flags_SetEventChkInf(EVENTCHKINF_LEARNED_ZELDAS_LULLABY);
play->transitionTrigger = TRANS_TRIGGER_START;
play->transitionType = TRANS_TYPE_FADE_WHITE;
gSaveContext.nextTransitionType = TRANS_TYPE_FADE_WHITE;
// In entrance rando have impa bring link back to the front of castle grounds
if (Randomizer_GetSettingValue(RSK_SHUFFLE_OVERWORLD_ENTRANCES)) {
play->nextEntranceIndex = ENTR_HYRULE_CASTLE_0;
} else {
play->nextEntranceIndex = ENTR_HYRULE_FIELD_17;
}
gSaveContext.nextCutsceneIndex = 0;
}
}
void func_80986C30(DemoIm* this, PlayState* play) { void func_80986C30(DemoIm* this, PlayState* play) {
if (func_80986A5C(this, play)) { if (func_80986A5C(this, play)) {
if (IS_RANDO) { if (GameInteractor_Should(GI_VB_PLAY_ZELDAS_LULLABY_CS, true, this)) {
GivePlayerRandoRewardImpa(this, play, RC_SONG_FROM_IMPA);
} else {
play->csCtx.segment = SEGMENTED_TO_VIRTUAL(gZeldasCourtyardLullabyCs); play->csCtx.segment = SEGMENTED_TO_VIRTUAL(gZeldasCourtyardLullabyCs);
gSaveContext.cutsceneTrigger = 1; gSaveContext.cutsceneTrigger = 1;
Flags_SetEventChkInf(EVENTCHKINF_LEARNED_ZELDAS_LULLABY);
Item_Give(play, ITEM_SONG_LULLABY);
func_80985F54(this); func_80985F54(this);
} }
Flags_SetEventChkInf(EVENTCHKINF_LEARNED_ZELDAS_LULLABY);
if (GameInteractor_Should(GI_VB_GIVE_ITEM_ZELDAS_LULLABY, true, NULL)) {
Item_Give(play, ITEM_SONG_LULLABY);
}
} }
} }
@ -960,7 +942,7 @@ void func_80986D40(DemoIm* this, PlayState* play) {
if (gSaveContext.sceneSetupIndex == 6) { if (gSaveContext.sceneSetupIndex == 6) {
this->action = 19; this->action = 19;
this->drawConfig = 1; this->drawConfig = 1;
} else if ((Flags_GetEventChkInf(EVENTCHKINF_ZELDA_FLED_HYRULE_CASTLE)) && !IS_RANDO) { } else if ((Flags_GetEventChkInf(EVENTCHKINF_ZELDA_FLED_HYRULE_CASTLE)) && !IS_RANDO) { // SoH [Randomizer] Not sure why we're not killing impa here.
Actor_Kill(&this->actor); Actor_Kill(&this->actor);
} else if (!Flags_GetEventChkInf(EVENTCHKINF_LEARNED_ZELDAS_LULLABY)) { } else if (!Flags_GetEventChkInf(EVENTCHKINF_LEARNED_ZELDAS_LULLABY)) {
this->action = 23; this->action = 23;

View File

@ -51,4 +51,6 @@ typedef struct DemoIm {
/* 0x02D4 */ NpcInteractInfo interactInfo; /* 0x02D4 */ NpcInteractInfo interactInfo;
} DemoIm; // size = 0x02FC } DemoIm; // size = 0x02FC
void func_80986794(DemoIm* demoIm);
#endif #endif

View File

@ -9,6 +9,7 @@
#include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h" #include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h"
#include "objects/object_sa/object_sa.h" #include "objects/object_sa/object_sa.h"
#include "soh/Enhancements/boss-rush/BossRush.h" #include "soh/Enhancements/boss-rush/BossRush.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#include "vt.h" #include "vt.h"
@ -241,7 +242,9 @@ void func_8098E8C8(DemoSa* this, PlayState* play) {
Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_DEMO_EFFECT, posX, posY, posZ, 0, 0, 0, Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_DEMO_EFFECT, posX, posY, posZ, 0, 0, 0,
0xB); 0xB);
Item_Give(play, ITEM_MEDALLION_FOREST); if (GameInteractor_Should(GI_VB_GIVE_ITEM_FOREST_MEDALLION, true, NULL)) {
Item_Give(play, ITEM_MEDALLION_FOREST);
}
} }
void func_8098E944(DemoSa* this, PlayState* play) { void func_8098E944(DemoSa* this, PlayState* play) {
@ -258,7 +261,9 @@ void func_8098E960(DemoSa* this, PlayState* play) {
this->action = 1; this->action = 1;
play->csCtx.segment = D_8099010C; play->csCtx.segment = D_8099010C;
gSaveContext.cutsceneTrigger = 2; gSaveContext.cutsceneTrigger = 2;
Item_Give(play, ITEM_MEDALLION_FOREST); if (GameInteractor_Should(GI_VB_GIVE_ITEM_FOREST_MEDALLION, true, NULL)) {
Item_Give(play, ITEM_MEDALLION_FOREST);
}
player->actor.world.rot.y = player->actor.shape.rot.y = this->actor.world.rot.y + 0x8000; player->actor.world.rot.y = player->actor.shape.rot.y = this->actor.world.rot.y + 0x8000;
} else { } else {
this->action = 1; this->action = 1;

View File

@ -23,6 +23,7 @@
#include "objects/object_menkuri_objects/object_menkuri_objects.h" #include "objects/object_menkuri_objects/object_menkuri_objects.h"
#include "objects/object_demo_kekkai/object_demo_kekkai.h" #include "objects/object_demo_kekkai/object_demo_kekkai.h"
#include "objects/object_ouke_haka/object_ouke_haka.h" #include "objects/object_ouke_haka/object_ouke_haka.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#define FLAGS ACTOR_FLAG_UPDATE_WHILE_CULLED #define FLAGS ACTOR_FLAG_UPDATE_WHILE_CULLED
@ -354,9 +355,11 @@ void func_80996A54(DoorShutter* this, PlayState* play) {
if (Flags_GetClear(play, this->dyna.actor.room) || Flags_GetTempClear(play, this->dyna.actor.room)) { if (Flags_GetClear(play, this->dyna.actor.room) || Flags_GetTempClear(play, this->dyna.actor.room)) {
Flags_SetClear(play, this->dyna.actor.room); Flags_SetClear(play, this->dyna.actor.room);
DoorShutter_SetupAction(this, func_80997150); DoorShutter_SetupAction(this, func_80997150);
OnePointCutscene_Attention(play, &this->dyna.actor); if (GameInteractor_Should(GI_VB_PLAY_ONEPOINT_ACTOR_CS, true, this)) {
OnePointCutscene_Attention(play, &GET_PLAYER(play)->actor); OnePointCutscene_Attention(play, &this->dyna.actor);
this->unk_16F = -100; OnePointCutscene_Attention(play, &GET_PLAYER(play)->actor);
this->unk_16F = -100;
}
} else if (func_809968D4(this, play) != 0) { } else if (func_809968D4(this, play) != 0) {
Player* player = GET_PLAYER(play); Player* player = GET_PLAYER(play);
@ -470,8 +473,10 @@ void func_80996EE8(DoorShutter* this, PlayState* play) {
if (func_80996E08(this, play, 1.0f)) { if (func_80996E08(this, play, 1.0f)) {
if (Flags_GetSwitch(play, this->dyna.actor.params & 0x3F)) { if (Flags_GetSwitch(play, this->dyna.actor.params & 0x3F)) {
DoorShutter_SetupAction(this, func_80997150); DoorShutter_SetupAction(this, func_80997150);
OnePointCutscene_Attention(play, &this->dyna.actor); if (GameInteractor_Should(GI_VB_PLAY_ONEPOINT_ACTOR_CS, true, this)) {
this->unk_16F = -100; OnePointCutscene_Attention(play, &this->dyna.actor);
this->unk_16F = -100;
}
} else if (func_809968D4(this, play)) { } else if (func_809968D4(this, play)) {
Player* player = GET_PLAYER(play); Player* player = GET_PLAYER(play);
// Jabu navi text for switch doors is different // Jabu navi text for switch doors is different

View File

@ -2,6 +2,7 @@
#include "objects/object_warp1/object_warp1.h" #include "objects/object_warp1/object_warp1.h"
#include "soh/Enhancements/randomizer/randomizer_entrance.h" #include "soh/Enhancements/randomizer/randomizer_entrance.h"
#include "soh/Enhancements/boss-rush/BossRush.h" #include "soh/Enhancements/boss-rush/BossRush.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#define FLAGS 0 #define FLAGS 0
@ -487,44 +488,6 @@ s32 DoorWarp1_PlayerInRange(DoorWarp1* this, PlayState* play) {
return ret; return ret;
} }
void GivePlayerRandoReward(DoorWarp1* this, Player* player, PlayState* play, u8 ruto, u8 adult) {
GetItemEntry getItemEntry = Randomizer_GetItemFromActor(this->actor.id, play->sceneNum, 0x00, GI_NONE);
if (this->actor.parent != NULL && this->actor.parent->id == GET_PLAYER(play)->actor.id &&
!Flags_GetTreasure(play, 0x1F)) {
Flags_SetTreasure(play, 0x1F);
} else if (!Flags_GetTreasure(play, 0x1F)) {
GiveItemEntryFromActor(&this->actor, play, getItemEntry, 10000.0f, 100.0f);
} else if (!Player_InBlockingCsMode(play, GET_PLAYER(play))) {
if (adult) {
OnePointCutscene_Init(play, 0x25E8, 999, &this->actor, MAIN_CAM);
func_8002DF54(play, &this->actor, 10);
player->unk_450.x = this->actor.world.pos.x;
player->unk_450.z = this->actor.world.pos.z;
this->unk_1B2 = 20;
DoorWarp1_SetupAction(this, func_8099A508);
} else {
if (ruto) {
this->rutoWarpState = WARP_BLUE_RUTO_STATE_ENTERED;
func_8002DF54(play, &this->actor, 10);
this->unk_1B2 = 1;
DoorWarp1_SetupAction(this, func_80999EE0);
} else {
Audio_PlaySoundGeneral(NA_SE_EV_LINK_WARP, &player->actor.projectedPos, 4, &D_801333E0, &D_801333E0,
&D_801333E8);
OnePointCutscene_Init(play, 0x25E7, 999, &this->actor, MAIN_CAM);
func_8002DF54(play, &this->actor, 10);
player->unk_450.x = this->actor.world.pos.x;
player->unk_450.z = this->actor.world.pos.z;
this->unk_1B2 = 1;
DoorWarp1_SetupAction(this, DoorWarp1_ChildWarpOut);
}
}
}
}
void DoorWarp1_ChildWarpIdle(DoorWarp1* this, PlayState* play) { void DoorWarp1_ChildWarpIdle(DoorWarp1* this, PlayState* play) {
Player* player; Player* player;
@ -532,11 +495,6 @@ void DoorWarp1_ChildWarpIdle(DoorWarp1* this, PlayState* play) {
if (DoorWarp1_PlayerInRange(this, play)) { if (DoorWarp1_PlayerInRange(this, play)) {
player = GET_PLAYER(play); player = GET_PLAYER(play);
if (IS_RANDO) {
GivePlayerRandoReward(this, player, play, 0, 0);
return;
}
Audio_PlaySoundGeneral(NA_SE_EV_LINK_WARP, &player->actor.projectedPos, 4, &D_801333E0, &D_801333E0, Audio_PlaySoundGeneral(NA_SE_EV_LINK_WARP, &player->actor.projectedPos, 4, &D_801333E0, &D_801333E0,
&D_801333E8); &D_801333E8);
@ -570,36 +528,26 @@ void DoorWarp1_ChildWarpOut(DoorWarp1* this, PlayState* play) {
osSyncPrintf("\n\n\nじかんがきたからおーしまい fade_direction=[%d]", play->transitionTrigger, TRANS_TRIGGER_START); osSyncPrintf("\n\n\nじかんがきたからおーしまい fade_direction=[%d]", play->transitionTrigger, TRANS_TRIGGER_START);
if (play->sceneNum == SCENE_DODONGOS_CAVERN_BOSS) { if (play->sceneNum == SCENE_DODONGOS_CAVERN_BOSS) {
if (!Flags_GetEventChkInf(EVENTCHKINF_USED_DODONGOS_CAVERN_BLUE_WARP)) { if (GameInteractor_Should(GI_VB_PLAY_BLUE_WARP_CS, !Flags_GetEventChkInf(EVENTCHKINF_USED_DODONGOS_CAVERN_BLUE_WARP), EVENTCHKINF_USED_DODONGOS_CAVERN_BLUE_WARP)) {
Flags_SetEventChkInf(EVENTCHKINF_USED_DODONGOS_CAVERN_BLUE_WARP); Flags_SetEventChkInf(EVENTCHKINF_USED_DODONGOS_CAVERN_BLUE_WARP);
Flags_SetRandomizerInf(RAND_INF_DUNGEONS_DONE_DODONGOS_CAVERN); if (GameInteractor_Should(GI_VB_GIVE_ITEM_FROM_BLUE_WARP, true, ITEM_GORON_RUBY)) {
if (IS_RANDO) {
play->nextEntranceIndex = ENTR_DEATH_MOUNTAIN_TRAIL_5;
gSaveContext.nextCutsceneIndex = 0;
} else {
Item_Give(play, ITEM_GORON_RUBY); Item_Give(play, ITEM_GORON_RUBY);
play->nextEntranceIndex = ENTR_DEATH_MOUNTAIN_TRAIL_0;
gSaveContext.nextCutsceneIndex = 0xFFF1;
} }
play->nextEntranceIndex = ENTR_DEATH_MOUNTAIN_TRAIL_0;
gSaveContext.nextCutsceneIndex = 0xFFF1;
} else { } else {
play->nextEntranceIndex = ENTR_DEATH_MOUNTAIN_TRAIL_5; play->nextEntranceIndex = ENTR_DEATH_MOUNTAIN_TRAIL_5;
gSaveContext.nextCutsceneIndex = 0; gSaveContext.nextCutsceneIndex = 0;
} }
} else if (play->sceneNum == SCENE_DEKU_TREE_BOSS) { } else if (play->sceneNum == SCENE_DEKU_TREE_BOSS) {
if (!Flags_GetEventChkInf(EVENTCHKINF_OBTAINED_KOKIRI_EMERALD_DEKU_TREE_DEAD) || IS_RANDO) { if (GameInteractor_Should(GI_VB_PLAY_BLUE_WARP_CS, !Flags_GetEventChkInf(EVENTCHKINF_OBTAINED_KOKIRI_EMERALD_DEKU_TREE_DEAD), EVENTCHKINF_OBTAINED_KOKIRI_EMERALD_DEKU_TREE_DEAD)) {
Flags_SetEventChkInf(EVENTCHKINF_OBTAINED_KOKIRI_EMERALD_DEKU_TREE_DEAD); Flags_SetEventChkInf(EVENTCHKINF_OBTAINED_KOKIRI_EMERALD_DEKU_TREE_DEAD);
Flags_SetEventChkInf(EVENTCHKINF_USED_DEKU_TREE_BLUE_WARP); Flags_SetEventChkInf(EVENTCHKINF_USED_DEKU_TREE_BLUE_WARP);
Flags_SetRandomizerInf(RAND_INF_DUNGEONS_DONE_DEKU_TREE); if (GameInteractor_Should(GI_VB_GIVE_ITEM_FROM_BLUE_WARP, true, ITEM_KOKIRI_EMERALD)) {
if (IS_RANDO) {
play->nextEntranceIndex = ENTR_KOKIRI_FOREST_11;
gSaveContext.nextCutsceneIndex = 0;
// Skip Mido complaining about dead Deku tree
Flags_SetEventChkInf(EVENTCHKINF_SPOKE_TO_MIDO_AFTER_DEKU_TREES_DEATH);
} else {
Item_Give(play, ITEM_KOKIRI_EMERALD); Item_Give(play, ITEM_KOKIRI_EMERALD);
play->nextEntranceIndex = ENTR_KOKIRI_FOREST_0;
gSaveContext.nextCutsceneIndex = 0xFFF1;
} }
play->nextEntranceIndex = ENTR_KOKIRI_FOREST_0;
gSaveContext.nextCutsceneIndex = 0xFFF1;
} else { } else {
play->nextEntranceIndex = ENTR_KOKIRI_FOREST_11; play->nextEntranceIndex = ENTR_KOKIRI_FOREST_11;
gSaveContext.nextCutsceneIndex = 0; gSaveContext.nextCutsceneIndex = 0;
@ -635,12 +583,6 @@ void DoorWarp1_RutoWarpIdle(DoorWarp1* this, PlayState* play) {
Audio_PlayActorSound2(&this->actor, NA_SE_EV_WARP_HOLE - SFX_FLAG); Audio_PlayActorSound2(&this->actor, NA_SE_EV_WARP_HOLE - SFX_FLAG);
if (this->rutoWarpState != WARP_BLUE_RUTO_STATE_INITIAL && DoorWarp1_PlayerInRange(this, play)) { if (this->rutoWarpState != WARP_BLUE_RUTO_STATE_INITIAL && DoorWarp1_PlayerInRange(this, play)) {
if (IS_RANDO) {
GivePlayerRandoReward(this, GET_PLAYER(play), play, 1, 0);
return;
}
this->rutoWarpState = WARP_BLUE_RUTO_STATE_ENTERED; this->rutoWarpState = WARP_BLUE_RUTO_STATE_ENTERED;
func_8002DF54(play, &this->actor, 10); func_8002DF54(play, &this->actor, 10);
this->unk_1B2 = 1; this->unk_1B2 = 1;
@ -670,9 +612,8 @@ void func_80999EE0(DoorWarp1* this, PlayState* play) {
Play_CameraSetAtEye(play, sRutoWarpSubCamId, &at, &eye); Play_CameraSetAtEye(play, sRutoWarpSubCamId, &at, &eye);
Play_CameraSetFov(play, sRutoWarpSubCamId, 90.0f); Play_CameraSetFov(play, sRutoWarpSubCamId, 90.0f);
this->rutoWarpState = WARP_BLUE_RUTO_STATE_TALKING; this->rutoWarpState = WARP_BLUE_RUTO_STATE_TALKING;
if (!IS_RANDO) { // TODO: Why was this disabled in rando?
Message_StartTextbox(play, 0x4022, NULL); Message_StartTextbox(play, 0x4022, NULL);
}
DoorWarp1_SetupAction(this, func_80999FE4); DoorWarp1_SetupAction(this, func_80999FE4);
} }
} }
@ -704,17 +645,14 @@ void DoorWarp1_RutoWarpOut(DoorWarp1* this, PlayState* play) {
this->warpTimer++; this->warpTimer++;
if (this->warpTimer > sWarpTimerTarget && gSaveContext.nextCutsceneIndex == 0xFFEF) { if (this->warpTimer > sWarpTimerTarget && gSaveContext.nextCutsceneIndex == 0xFFEF) {
Flags_SetEventChkInf(EVENTCHKINF_USED_JABU_JABUS_BELLY_BLUE_WARP); if (GameInteractor_Should(GI_VB_PLAY_BLUE_WARP_CS, true, EVENTCHKINF_USED_JABU_JABUS_BELLY_BLUE_WARP)) {
Flags_SetRandomizerInf(RAND_INF_DUNGEONS_DONE_JABU_JABUS_BELLY); Flags_SetEventChkInf(EVENTCHKINF_USED_JABU_JABUS_BELLY_BLUE_WARP);
if (GameInteractor_Should(GI_VB_GIVE_ITEM_FROM_BLUE_WARP, true, ITEM_ZORA_SAPPHIRE)) {
if (IS_RANDO) { Item_Give(play, ITEM_ZORA_SAPPHIRE);
play->nextEntranceIndex = ENTR_ZORAS_FOUNTAIN_0; }
gSaveContext.nextCutsceneIndex = 0;
} else {
Item_Give(play, ITEM_ZORA_SAPPHIRE);
play->nextEntranceIndex = ENTR_ZORAS_FOUNTAIN_0;
gSaveContext.nextCutsceneIndex = 0xFFF0; gSaveContext.nextCutsceneIndex = 0xFFF0;
} }
play->nextEntranceIndex = ENTR_ZORAS_FOUNTAIN_0;
if (IS_RANDO && (Randomizer_GetSettingValue(RSK_SHUFFLE_DUNGEON_ENTRANCES) != RO_DUNGEON_ENTRANCE_SHUFFLE_OFF || if (IS_RANDO && (Randomizer_GetSettingValue(RSK_SHUFFLE_DUNGEON_ENTRANCES) != RO_DUNGEON_ENTRANCE_SHUFFLE_OFF ||
Randomizer_GetSettingValue(RSK_SHUFFLE_BOSS_ENTRANCES) != RO_BOSS_ROOM_ENTRANCE_SHUFFLE_OFF)) { Randomizer_GetSettingValue(RSK_SHUFFLE_BOSS_ENTRANCES) != RO_BOSS_ROOM_ENTRANCE_SHUFFLE_OFF)) {
@ -767,11 +705,6 @@ void DoorWarp1_AdultWarpIdle(DoorWarp1* this, PlayState* play) {
player = GET_PLAYER(play); player = GET_PLAYER(play);
if (IS_RANDO) {
GivePlayerRandoReward(this, player, play, 0, 1);
return;
}
OnePointCutscene_Init(play, 0x25E8, 999, &this->actor, MAIN_CAM); OnePointCutscene_Init(play, 0x25E8, 999, &this->actor, MAIN_CAM);
func_8002DF54(play, &this->actor, 10); func_8002DF54(play, &this->actor, 10);
player->unk_450.x = this->actor.world.pos.x; player->unk_450.x = this->actor.world.pos.x;
@ -829,19 +762,14 @@ void DoorWarp1_AdultWarpOut(DoorWarp1* this, PlayState* play) {
if (IS_BOSS_RUSH) { if (IS_BOSS_RUSH) {
BossRush_HandleBlueWarp(play, this->actor.world.pos.x, this->actor.world.pos.z); BossRush_HandleBlueWarp(play, this->actor.world.pos.x, this->actor.world.pos.z);
} else if (play->sceneNum == SCENE_FOREST_TEMPLE_BOSS) { } else if (play->sceneNum == SCENE_FOREST_TEMPLE_BOSS) {
if (!Flags_GetEventChkInf(EVENTCHKINF_USED_FOREST_TEMPLE_BLUE_WARP)) { if (GameInteractor_Should(GI_VB_PLAY_BLUE_WARP_CS, !Flags_GetEventChkInf(EVENTCHKINF_USED_FOREST_TEMPLE_BLUE_WARP), EVENTCHKINF_USED_FOREST_TEMPLE_BLUE_WARP)) {
Flags_SetEventChkInf(EVENTCHKINF_USED_FOREST_TEMPLE_BLUE_WARP); Flags_SetEventChkInf(EVENTCHKINF_USED_FOREST_TEMPLE_BLUE_WARP);
Flags_SetRandomizerInf(RAND_INF_DUNGEONS_DONE_FOREST_TEMPLE); if (GameInteractor_Should(GI_VB_GIVE_ITEM_FROM_BLUE_WARP, true, ITEM_MEDALLION_FOREST)) {
if (IS_RANDO) {
play->nextEntranceIndex = ENTR_SACRED_FOREST_MEADOW_3;
gSaveContext.nextCutsceneIndex = 0;
} else {
Item_Give(play, ITEM_MEDALLION_FOREST); Item_Give(play, ITEM_MEDALLION_FOREST);
play->nextEntranceIndex = ENTR_CHAMBER_OF_THE_SAGES_0;
gSaveContext.nextCutsceneIndex = 0;
gSaveContext.chamberCutsceneNum = CHAMBER_CS_FOREST;
} }
play->nextEntranceIndex = ENTR_CHAMBER_OF_THE_SAGES_0;
gSaveContext.nextCutsceneIndex = 0;
gSaveContext.chamberCutsceneNum = CHAMBER_CS_FOREST;
} else { } else {
if (!LINK_IS_ADULT) { if (!LINK_IS_ADULT) {
play->nextEntranceIndex = ENTR_SACRED_FOREST_MEADOW_2; play->nextEntranceIndex = ENTR_SACRED_FOREST_MEADOW_2;
@ -851,20 +779,13 @@ void DoorWarp1_AdultWarpOut(DoorWarp1* this, PlayState* play) {
gSaveContext.nextCutsceneIndex = 0; gSaveContext.nextCutsceneIndex = 0;
} }
} else if (play->sceneNum == SCENE_FIRE_TEMPLE_BOSS) { } else if (play->sceneNum == SCENE_FIRE_TEMPLE_BOSS) {
if (!Flags_GetEventChkInf(EVENTCHKINF_USED_FIRE_TEMPLE_BLUE_WARP)) { if (GameInteractor_Should(GI_VB_PLAY_BLUE_WARP_CS, !Flags_GetEventChkInf(EVENTCHKINF_USED_FIRE_TEMPLE_BLUE_WARP), EVENTCHKINF_USED_FIRE_TEMPLE_BLUE_WARP)) {
Flags_SetEventChkInf(EVENTCHKINF_USED_FIRE_TEMPLE_BLUE_WARP); Flags_SetEventChkInf(EVENTCHKINF_USED_FIRE_TEMPLE_BLUE_WARP);
Flags_SetRandomizerInf(RAND_INF_DUNGEONS_DONE_FIRE_TEMPLE); if (GameInteractor_Should(GI_VB_GIVE_ITEM_FROM_BLUE_WARP, true, ITEM_MEDALLION_FIRE)) {
if (IS_RANDO) {
play->nextEntranceIndex = ENTR_DEATH_MOUNTAIN_CRATER_5;
gSaveContext.nextCutsceneIndex = 0;
// Change Death Mountain cloud since we aren't warping to the cutscene
Flags_SetEventChkInf(EVENTCHKINF_DEATH_MOUNTAIN_ERUPTED);
} else {
Item_Give(play, ITEM_MEDALLION_FIRE); Item_Give(play, ITEM_MEDALLION_FIRE);
play->nextEntranceIndex = ENTR_KAKARIKO_VILLAGE_0;
gSaveContext.nextCutsceneIndex = 0xFFF3;
} }
play->nextEntranceIndex = ENTR_KAKARIKO_VILLAGE_0;
gSaveContext.nextCutsceneIndex = 0xFFF3;
} else { } else {
if (!LINK_IS_ADULT) { if (!LINK_IS_ADULT) {
play->nextEntranceIndex = ENTR_DEATH_MOUNTAIN_CRATER_4; play->nextEntranceIndex = ENTR_DEATH_MOUNTAIN_CRATER_4;
@ -874,21 +795,14 @@ void DoorWarp1_AdultWarpOut(DoorWarp1* this, PlayState* play) {
gSaveContext.nextCutsceneIndex = 0; gSaveContext.nextCutsceneIndex = 0;
} }
} else if (play->sceneNum == SCENE_WATER_TEMPLE_BOSS) { } else if (play->sceneNum == SCENE_WATER_TEMPLE_BOSS) {
if (!Flags_GetEventChkInf(EVENTCHKINF_USED_WATER_TEMPLE_BLUE_WARP)) { if (GameInteractor_Should(GI_VB_PLAY_BLUE_WARP_CS, !Flags_GetEventChkInf(EVENTCHKINF_USED_WATER_TEMPLE_BLUE_WARP), EVENTCHKINF_USED_WATER_TEMPLE_BLUE_WARP)) {
Flags_SetEventChkInf(EVENTCHKINF_USED_WATER_TEMPLE_BLUE_WARP); Flags_SetEventChkInf(EVENTCHKINF_USED_WATER_TEMPLE_BLUE_WARP);
Flags_SetRandomizerInf(RAND_INF_DUNGEONS_DONE_WATER_TEMPLE); if (GameInteractor_Should(GI_VB_GIVE_ITEM_FROM_BLUE_WARP, true, ITEM_MEDALLION_WATER)) {
if (IS_RANDO) {
play->nextEntranceIndex = ENTR_LAKE_HYLIA_9;
gSaveContext.nextCutsceneIndex = 0;
// Fill Lake Hylia since we aren't warping to the cutscene
Flags_SetEventChkInf(EVENTCHKINF_RAISED_LAKE_HYLIA_WATER);
} else {
Item_Give(play, ITEM_MEDALLION_WATER); Item_Give(play, ITEM_MEDALLION_WATER);
play->nextEntranceIndex = ENTR_CHAMBER_OF_THE_SAGES_0;
gSaveContext.nextCutsceneIndex = 0;
gSaveContext.chamberCutsceneNum = CHAMBER_CS_WATER;
} }
play->nextEntranceIndex = ENTR_CHAMBER_OF_THE_SAGES_0;
gSaveContext.nextCutsceneIndex = 0;
gSaveContext.chamberCutsceneNum = CHAMBER_CS_WATER;
} else { } else {
if (!LINK_IS_ADULT) { if (!LINK_IS_ADULT) {
play->nextEntranceIndex = ENTR_LAKE_HYLIA_8; play->nextEntranceIndex = ENTR_LAKE_HYLIA_8;
@ -898,18 +812,14 @@ void DoorWarp1_AdultWarpOut(DoorWarp1* this, PlayState* play) {
gSaveContext.nextCutsceneIndex = 0; gSaveContext.nextCutsceneIndex = 0;
} }
} else if (play->sceneNum == SCENE_SPIRIT_TEMPLE_BOSS) { } else if (play->sceneNum == SCENE_SPIRIT_TEMPLE_BOSS) {
if (!CHECK_QUEST_ITEM(QUEST_MEDALLION_SPIRIT) || IS_RANDO) { if (GameInteractor_Should(GI_VB_PLAY_BLUE_WARP_CS, !CHECK_QUEST_ITEM(QUEST_MEDALLION_SPIRIT), RAND_INF_DUNGEONS_DONE_SPIRIT_TEMPLE)) {
Flags_SetRandomizerInf(RAND_INF_DUNGEONS_DONE_SPIRIT_TEMPLE); Flags_SetRandomizerInf(RAND_INF_DUNGEONS_DONE_SPIRIT_TEMPLE);
if (GameInteractor_Should(GI_VB_GIVE_ITEM_FROM_BLUE_WARP, true, ITEM_MEDALLION_SPIRIT)) {
if (IS_RANDO) {
play->nextEntranceIndex = ENTR_DESERT_COLOSSUS_8;
gSaveContext.nextCutsceneIndex = 0;
} else {
Item_Give(play, ITEM_MEDALLION_SPIRIT); Item_Give(play, ITEM_MEDALLION_SPIRIT);
play->nextEntranceIndex = ENTR_CHAMBER_OF_THE_SAGES_0;
gSaveContext.nextCutsceneIndex = 0;
gSaveContext.chamberCutsceneNum = CHAMBER_CS_SPIRIT;
} }
play->nextEntranceIndex = ENTR_CHAMBER_OF_THE_SAGES_0;
gSaveContext.nextCutsceneIndex = 0;
gSaveContext.chamberCutsceneNum = CHAMBER_CS_SPIRIT;
} else { } else {
if (!LINK_IS_ADULT) { if (!LINK_IS_ADULT) {
play->nextEntranceIndex = ENTR_DESERT_COLOSSUS_5; play->nextEntranceIndex = ENTR_DESERT_COLOSSUS_5;
@ -919,18 +829,14 @@ void DoorWarp1_AdultWarpOut(DoorWarp1* this, PlayState* play) {
gSaveContext.nextCutsceneIndex = 0; gSaveContext.nextCutsceneIndex = 0;
} }
} else if (play->sceneNum == SCENE_SHADOW_TEMPLE_BOSS) { } else if (play->sceneNum == SCENE_SHADOW_TEMPLE_BOSS) {
if (!CHECK_QUEST_ITEM(QUEST_MEDALLION_SHADOW) || IS_RANDO) { if (GameInteractor_Should(GI_VB_PLAY_BLUE_WARP_CS, !CHECK_QUEST_ITEM(QUEST_MEDALLION_SHADOW), RAND_INF_DUNGEONS_DONE_SHADOW_TEMPLE)) {
Flags_SetRandomizerInf(RAND_INF_DUNGEONS_DONE_SHADOW_TEMPLE); Flags_SetRandomizerInf(RAND_INF_DUNGEONS_DONE_SHADOW_TEMPLE);
if (GameInteractor_Should(GI_VB_GIVE_ITEM_FROM_BLUE_WARP, true, ITEM_MEDALLION_SHADOW)) {
if (IS_RANDO) {
play->nextEntranceIndex = ENTR_GRAVEYARD_8;
gSaveContext.nextCutsceneIndex = 0;
} else {
Item_Give(play, ITEM_MEDALLION_SHADOW); Item_Give(play, ITEM_MEDALLION_SHADOW);
play->nextEntranceIndex = ENTR_CHAMBER_OF_THE_SAGES_0;
gSaveContext.nextCutsceneIndex = 0;
gSaveContext.chamberCutsceneNum = CHAMBER_CS_SHADOW;
} }
play->nextEntranceIndex = ENTR_CHAMBER_OF_THE_SAGES_0;
gSaveContext.nextCutsceneIndex = 0;
gSaveContext.chamberCutsceneNum = CHAMBER_CS_SHADOW;
} else { } else {
if (!LINK_IS_ADULT) { if (!LINK_IS_ADULT) {
play->nextEntranceIndex = ENTR_GRAVEYARD_7; play->nextEntranceIndex = ENTR_GRAVEYARD_7;

View File

@ -7,6 +7,7 @@
#include "z_elf_msg.h" #include "z_elf_msg.h"
#include "vt.h" #include "vt.h"
#include "overlays/actors/ovl_En_Elf/z_en_elf.h" #include "overlays/actors/ovl_En_Elf/z_en_elf.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#define FLAGS ACTOR_FLAG_UPDATE_WHILE_CULLED #define FLAGS ACTOR_FLAG_UPDATE_WHILE_CULLED
@ -110,7 +111,7 @@ void ElfMsg_Destroy(Actor* thisx, PlayState* play) {
s32 ElfMsg_GetMessageId(ElfMsg* this) { s32 ElfMsg_GetMessageId(ElfMsg* this) {
// Negative message ID forces link to talk to Navi // Negative message ID forces link to talk to Navi
if (this->actor.params & 0x8000 || CVarGetInteger("gNoForcedNavi", 0) != 0) { if (this->actor.params & 0x8000) {
return (this->actor.params & 0xFF) + 0x100; return (this->actor.params & 0xFF) + 0x100;
} else { } else {
return -((this->actor.params & 0xFF) + 0x100); return -((this->actor.params & 0xFF) + 0x100);
@ -125,8 +126,10 @@ void ElfMsg_CallNaviCuboid(ElfMsg* this, PlayState* play) {
(this->actor.world.pos.y <= player->actor.world.pos.y) && (this->actor.world.pos.y <= player->actor.world.pos.y) &&
((player->actor.world.pos.y - this->actor.world.pos.y) < (100.0f * this->actor.scale.y)) && ((player->actor.world.pos.y - this->actor.world.pos.y) < (100.0f * this->actor.scale.y)) &&
(fabsf(player->actor.world.pos.z - this->actor.world.pos.z) < (100.0f * this->actor.scale.z))) { (fabsf(player->actor.world.pos.z - this->actor.world.pos.z) < (100.0f * this->actor.scale.z))) {
player->naviTextId = ElfMsg_GetMessageId(this); if (GameInteractor_Should(GI_VB_NAVI_TALK, true, this)) {
navi->elfMsg = this; player->naviTextId = ElfMsg_GetMessageId(this);
navi->elfMsg = this;
}
} }
} }
@ -145,8 +148,10 @@ void ElfMsg_CallNaviCylinder(ElfMsg* this, PlayState* play) {
if (ElfMsg_WithinXZDistance(&player->actor.world.pos, &this->actor.world.pos, this->actor.scale.x * 100.0f) && if (ElfMsg_WithinXZDistance(&player->actor.world.pos, &this->actor.world.pos, this->actor.scale.x * 100.0f) &&
(this->actor.world.pos.y <= player->actor.world.pos.y) && (this->actor.world.pos.y <= player->actor.world.pos.y) &&
((player->actor.world.pos.y - this->actor.world.pos.y) < (100.0f * this->actor.scale.y))) { ((player->actor.world.pos.y - this->actor.world.pos.y) < (100.0f * this->actor.scale.y))) {
player->naviTextId = ElfMsg_GetMessageId(this); if (GameInteractor_Should(GI_VB_NAVI_TALK, true, this)) {
navi->elfMsg = this; player->naviTextId = ElfMsg_GetMessageId(this);
navi->elfMsg = this;
}
} }
} }

View File

@ -3,6 +3,7 @@
#include "soh_assets.h" #include "soh_assets.h"
#include "soh/Enhancements/enhancementTypes.h" #include "soh/Enhancements/enhancementTypes.h"
#include <assert.h> #include <assert.h>
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#define FLAGS 0 #define FLAGS 0
@ -192,10 +193,12 @@ void EnBox_Init(Actor* thisx, PlayState* play2) {
SkelAnime_Init(play, &this->skelanime, &gTreasureChestSkel, anim, this->jointTable, this->morphTable, 5); SkelAnime_Init(play, &this->skelanime, &gTreasureChestSkel, anim, this->jointTable, this->morphTable, 5);
Animation_Change(&this->skelanime, anim, 1.5f, animFrameStart, endFrame, ANIMMODE_ONCE, 0.0f); Animation_Change(&this->skelanime, anim, 1.5f, animFrameStart, endFrame, ANIMMODE_ONCE, 0.0f);
this->getItemEntry = ItemTable_RetrieveEntry(MOD_NONE, this->dyna.actor.params >> 5 & 0x7F);
if (IS_RANDO) { if (IS_RANDO) {
this->getItemEntry = Randomizer_GetItemFromActor(this->dyna.actor.id, play->sceneNum, this->dyna.actor.params, this->dyna.actor.params >> 5 & 0x7F); RandomizerCheck rc = Randomizer_GetCheckFromActor(this->dyna.actor.id, play->sceneNum, this->dyna.actor.params);
} else { if (rc != RC_UNKNOWN_CHECK) {
this->getItemEntry = ItemTable_RetrieveEntry(MOD_NONE, this->dyna.actor.params >> 5 & 0x7F); this->getItemEntry = Randomizer_GetItemFromKnownCheck(rc, this->dyna.actor.params >> 5 & 0x7F);
}
} }
EnBox_UpdateSizeAndTexture(this, play); EnBox_UpdateSizeAndTexture(this, play);
@ -275,7 +278,9 @@ void EnBox_Fall(EnBox* this, PlayState* play) {
this->dyna.actor.shape.rot.z = 0; this->dyna.actor.shape.rot.z = 0;
this->dyna.actor.world.pos.y = this->dyna.actor.floorHeight; this->dyna.actor.world.pos.y = this->dyna.actor.floorHeight;
EnBox_SetupAction(this, EnBox_WaitOpen); EnBox_SetupAction(this, EnBox_WaitOpen);
OnePointCutscene_EndCutscene(play, this->unk_1AC); if (GameInteractor_Should(GI_VB_PLAY_ONEPOINT_ACTOR_CS, true, this)) {
OnePointCutscene_EndCutscene(play, this->unk_1AC);
}
} }
Audio_PlaySoundGeneral(NA_SE_EV_COFFIN_CAP_BOUND, &this->dyna.actor.projectedPos, 4, &D_801333E0, &D_801333E0, Audio_PlaySoundGeneral(NA_SE_EV_COFFIN_CAP_BOUND, &this->dyna.actor.projectedPos, 4, &D_801333E0, &D_801333E0,
&D_801333E8); &D_801333E8);
@ -445,74 +450,12 @@ void EnBox_WaitOpen(EnBox* this, PlayState* play) {
} }
osSyncPrintf("Actor_Environment_Tbox_On() %d\n", this->dyna.actor.params & 0x1F); osSyncPrintf("Actor_Environment_Tbox_On() %d\n", this->dyna.actor.params & 0x1F);
Flags_SetTreasure(play, this->dyna.actor.params & 0x1F); Flags_SetTreasure(play, this->dyna.actor.params & 0x1F);
// treasure chest game rando
if (Randomizer_GetSettingValue(RSK_SHUFFLE_CHEST_MINIGAME)) {
if (IS_RANDO && play->sceneNum == SCENE_TREASURE_BOX_SHOP && (this->dyna.actor.params & 0x60) != 0x20) {
if((this->dyna.actor.params & 0xF) < 2) {
Flags_SetCollectible(play, 0x1B);
}
if((this->dyna.actor.params & 0xF) >= 2 && (this->dyna.actor.params & 0xF) < 4) {
Flags_SetCollectible(play, 0x1C);
}
if((this->dyna.actor.params & 0xF) >= 4 && (this->dyna.actor.params & 0xF) < 6) {
Flags_SetCollectible(play, 0x1D);
}
if((this->dyna.actor.params & 0xF) >= 6 && (this->dyna.actor.params & 0xF) < 8) {
Flags_SetCollectible(play, 0x1E);
}
if((this->dyna.actor.params & 0xF) >= 8 && (this->dyna.actor.params & 0xF) < 10) {
Flags_SetCollectible(play, 0x1F);
}
}
}
} else { } else {
player = GET_PLAYER(play); player = GET_PLAYER(play);
func_8002DBD0(&this->dyna.actor, &sp4C, &player->actor.world.pos); func_8002DBD0(&this->dyna.actor, &sp4C, &player->actor.world.pos);
if (sp4C.z > -50.0f && sp4C.z < 0.0f && fabsf(sp4C.y) < 10.0f && fabsf(sp4C.x) < 20.0f && if (sp4C.z > -50.0f && sp4C.z < 0.0f && fabsf(sp4C.y) < 10.0f && fabsf(sp4C.x) < 20.0f &&
Player_IsFacingActor(&this->dyna.actor, 0x3000, play)) { Player_IsFacingActor(&this->dyna.actor, 0x3000, play)) {
GetItemEntry sItem = Randomizer_GetItemFromActor(this->dyna.actor.id, play->sceneNum, this->dyna.actor.params, this->dyna.actor.params >> 5 & 0x7F);
GetItemEntry blueRupee = ItemTable_RetrieveEntry(MOD_NONE, GI_RUPEE_BLUE);
// RANDOTODO treasure chest game rando
if (Randomizer_GetSettingValue(RSK_SHUFFLE_CHEST_MINIGAME)) {
if (IS_RANDO && play->sceneNum == SCENE_TREASURE_BOX_SHOP && (this->dyna.actor.params & 0x60) != 0x20) {
if((this->dyna.actor.params & 0xF) < 2) {
if(Flags_GetCollectible(play, 0x1B)) {
sItem = blueRupee;
}
}
if((this->dyna.actor.params & 0xF) >= 2 && (this->dyna.actor.params & 0xF) < 4) {
if(Flags_GetCollectible(play, 0x1C)) {
sItem = blueRupee;
}
}
if((this->dyna.actor.params & 0xF) >= 4 && (this->dyna.actor.params & 0xF) < 6) {
if(Flags_GetCollectible(play, 0x1D)) {
sItem = blueRupee;
}
}
if((this->dyna.actor.params & 0xF) >= 6 && (this->dyna.actor.params & 0xF) < 8) {
if(Flags_GetCollectible(play, 0x1E)) {
sItem = blueRupee;
}
}
if((this->dyna.actor.params & 0xF) >= 8 && (this->dyna.actor.params & 0xF) < 10) {
if(Flags_GetCollectible(play, 0x1F)) {
sItem = blueRupee;
}
}
}
}
// Chests need to have a negative getItemId in order to not immediately give their item
// when approaching.
if (IS_RANDO) {
sItem.getItemId = 0 - sItem.getItemId;
sItem.getItemFrom = ITEM_FROM_CHEST;
GiveItemEntryFromActorWithFixedRange(&this->dyna.actor, play, sItem);
} else {
func_8002F554(&this->dyna.actor, play, -(this->dyna.actor.params >> 5 & 0x7F)); func_8002F554(&this->dyna.actor, play, -(this->dyna.actor.params >> 5 & 0x7F));
}
} }
if (Flags_GetTreasure(play, this->dyna.actor.params & 0x1F)) { if (Flags_GetTreasure(play, this->dyna.actor.params & 0x1F)) {
EnBox_SetupAction(this, EnBox_Open); EnBox_SetupAction(this, EnBox_Open);
@ -626,12 +569,9 @@ void EnBox_Update(Actor* thisx, PlayState* play) {
Actor_SetFocus(&this->dyna.actor, 40.0f); Actor_SetFocus(&this->dyna.actor, 40.0f);
} }
if (((!IS_RANDO && ((this->dyna.actor.params >> 5 & 0x7F) == 0x7C)) || if ((this->dyna.actor.params >> 5 & 0x7F) == GI_ICE_TRAP && this->actionFunc == EnBox_Open &&
(IS_RANDO && this->getItemEntry.getItemId == RG_ICE_TRAP)) && this->skelanime.curFrame > 45 && this->iceSmokeTimer < 100) {
this->actionFunc == EnBox_Open && this->skelanime.curFrame > 45 && this->iceSmokeTimer < 100) { EnBox_SpawnIceSmoke(this, play);
if (!CVarGetInteger("gAddTraps.enabled", 0)) {
EnBox_SpawnIceSmoke(this, play);
}
} }
} }

View File

@ -4,6 +4,7 @@
#include <libultraship/libultra.h> #include <libultraship/libultra.h>
#include "global.h" #include "global.h"
#define ENBOX_PARAMS(type, itemId, treasureFlag) ((type) << 12 | (itemId) << 5 | (treasureFlag))
#define ENBOX_TREASURE_FLAG_UNK_MIN 20 #define ENBOX_TREASURE_FLAG_UNK_MIN 20
#define ENBOX_TREASURE_FLAG_UNK_MAX 32 #define ENBOX_TREASURE_FLAG_UNK_MAX 32
@ -45,7 +46,7 @@ typedef struct EnBox {
/* 0x01F9 */ u8 type; /* 0x01F9 */ u8 type;
/* 0x01FA */ u8 iceSmokeTimer; /* 0x01FA */ u8 iceSmokeTimer;
/* 0x01FB */ u8 unk_1FB; /* 0x01FB */ u8 unk_1FB;
/* */ GetItemEntry getItemEntry; /* */ GetItemEntry getItemEntry; // This is only to determine the Chest Style, randomzier item gives are handled elsewhere
/* */ Gfx* boxLidDL; /* */ Gfx* boxLidDL;
/* */ Gfx* boxBodyDL; /* */ Gfx* boxBodyDL;
} EnBox; // size = 0x01FC } EnBox; // size = 0x01FC

View File

@ -6,6 +6,7 @@
#include "z_en_cow.h" #include "z_en_cow.h"
#include "objects/object_cow/object_cow.h" #include "objects/object_cow/object_cow.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY) #define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY)
@ -18,7 +19,6 @@ void func_809E0070(Actor* thisx, PlayState* play);
void func_809DF494(EnCow* this, PlayState* play); void func_809DF494(EnCow* this, PlayState* play);
void func_809DF6BC(EnCow* this, PlayState* play); void func_809DF6BC(EnCow* this, PlayState* play);
void EnCow_MoveForRandomizer(EnCow* this, PlayState* play);
void func_809DF778(EnCow* this, PlayState* play); void func_809DF778(EnCow* this, PlayState* play);
void func_809DF7D8(EnCow* this, PlayState* play); void func_809DF7D8(EnCow* this, PlayState* play);
void func_809DF870(EnCow* this, PlayState* play); void func_809DF870(EnCow* this, PlayState* play);
@ -107,10 +107,6 @@ void EnCow_Init(Actor* thisx, PlayState* play) {
EnCow* this = (EnCow*)thisx; EnCow* this = (EnCow*)thisx;
s32 pad; s32 pad;
if (IS_RANDO && Randomizer_GetSettingValue(RSK_SHUFFLE_COWS)) {
EnCow_MoveForRandomizer(thisx, play);
}
ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 72.0f); ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 72.0f);
switch (this->actor.params) { switch (this->actor.params) {
case 0: case 0:
@ -122,18 +118,15 @@ void EnCow_Init(Actor* thisx, PlayState* play) {
Collider_SetCylinder(play, &this->colliders[1], &this->actor, &sCylinderInit); Collider_SetCylinder(play, &this->colliders[1], &this->actor, &sCylinderInit);
func_809DEE9C(this); func_809DEE9C(this);
this->actionFunc = func_809DF96C; this->actionFunc = func_809DF96C;
if (play->sceneNum == SCENE_LINKS_HOUSE) { if (GameInteractor_Should(GI_VB_DESPAWN_HORSE_RACE_COW, (
if (!LINK_IS_ADULT && !CVarGetInteger("gCowOfTime", 0)) { play->sceneNum == SCENE_LINKS_HOUSE && (!LINK_IS_ADULT || !Flags_GetEventChkInf(EVENTCHKINF_WON_COW_IN_MALONS_RACE))
Actor_Kill(&this->actor); ), this)) {
return; Actor_Kill(&this->actor);
} return;
if (!Flags_GetEventChkInf(EVENTCHKINF_WON_COW_IN_MALONS_RACE)) {
Actor_Kill(&this->actor);
return;
}
} }
Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_EN_COW, this->actor.world.pos.x, Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_EN_COW, this->actor.world.pos.x,
this->actor.world.pos.y, this->actor.world.pos.z, 0, this->actor.shape.rot.y, 0, 1); this->actor.world.pos.y, this->actor.world.pos.z, 0, this->actor.shape.rot.y, 0, 1);
this->unk_278 = Rand_ZeroFloat(1000.0f) + 40.0f; this->unk_278 = Rand_ZeroFloat(1000.0f) + 40.0f;
this->unk_27A = 0; this->unk_27A = 0;
this->actor.targetMode = 6; this->actor.targetMode = 6;
@ -216,30 +209,6 @@ void func_809DF730(EnCow* this, PlayState* play) {
} }
} }
void EnCow_MoveForRandomizer(EnCow* this, PlayState* play) {
// Only move the cow body (the tail will be moved with the body)
if (this->actor.params != 0) {
return;
}
// Move left cow in lon lon tower
if (play->sceneNum == SCENE_LON_LON_BUILDINGS && this->actor.world.pos.x == -108 && this->actor.world.pos.z == -65) {
this->actor.world.pos.x = -229.0f;
this->actor.world.pos.z = 157.0f;
this->actor.shape.rot.y = 15783.0f;
// Move right cow in lon lon stable
} else if (play->sceneNum == SCENE_STABLE && this->actor.world.pos.x == -3 && this->actor.world.pos.z == -254) {
this->actor.world.pos.x += 119.0f;
}
}
void EnCow_SetCowMilked(EnCow* this, PlayState* play) {
CowIdentity cowIdentity = Randomizer_IdentifyCow(play->sceneNum, this->actor.world.pos.x, this->actor.world.pos.z);
Player* player = GET_PLAYER(play);
player->pendingFlag.flagID = cowIdentity.randomizerInf;
player->pendingFlag.flagType = FLAG_RANDOMIZER_INF;
}
void func_809DF778(EnCow* this, PlayState* play) { void func_809DF778(EnCow* this, PlayState* play) {
if (Actor_HasParent(&this->actor, play)) { if (Actor_HasParent(&this->actor, play)) {
this->actor.parent = NULL; this->actor.parent = NULL;
@ -281,23 +250,6 @@ void func_809DF8FC(EnCow* this, PlayState* play) {
func_809DF494(this, play); func_809DF494(this, play);
} }
bool EnCow_HasBeenMilked(EnCow* this, PlayState* play) {
CowIdentity cowIdentity = Randomizer_IdentifyCow(play->sceneNum, this->actor.world.pos.x, this->actor.world.pos.z);
return Flags_GetRandomizerInf(cowIdentity.randomizerInf);
}
void EnCow_GivePlayerRandomizedItem(EnCow* this, PlayState* play) {
if (!EnCow_HasBeenMilked(this, play)) {
CowIdentity cowIdentity = Randomizer_IdentifyCow(play->sceneNum, this->actor.world.pos.x, this->actor.world.pos.z);
GetItemEntry itemEntry = Randomizer_GetItemFromKnownCheck(cowIdentity.randomizerCheck, GI_MILK);
GiveItemEntryFromActor(&this->actor, play, itemEntry, 10000.0f, 100.0f);
} else {
// once we've gotten the rando reward from the cow,
// return them to the their default action function
this->actionFunc = func_809DF96C;
}
}
void func_809DF96C(EnCow* this, PlayState* play) { void func_809DF96C(EnCow* this, PlayState* play) {
if ((play->msgCtx.ocarinaMode == OCARINA_MODE_00) || (play->msgCtx.ocarinaMode == OCARINA_MODE_04)) { if ((play->msgCtx.ocarinaMode == OCARINA_MODE_00) || (play->msgCtx.ocarinaMode == OCARINA_MODE_04)) {
if (DREG(53) != 0) { if (DREG(53) != 0) {
@ -308,23 +260,14 @@ void func_809DF96C(EnCow* this, PlayState* play) {
if ((this->actor.xzDistToPlayer < 150.0f) && if ((this->actor.xzDistToPlayer < 150.0f) &&
(ABS((s16)(this->actor.yawTowardsPlayer - this->actor.shape.rot.y)) < 0x61A8)) { (ABS((s16)(this->actor.yawTowardsPlayer - this->actor.shape.rot.y)) < 0x61A8)) {
DREG(53) = 0; DREG(53) = 0;
// when randomized with cowsanity, if we haven't gotten the if (GameInteractor_Should(GI_VB_GIVE_ITEM_FROM_COW, true, this)) {
// reward from this cow yet, give that, otherwise use the this->actionFunc = func_809DF8FC;
// vanilla cow behavior this->actor.flags |= ACTOR_FLAG_WILL_TALK;
if (IS_RANDO && func_8002F2CC(&this->actor, play, 170.0f);
Randomizer_GetSettingValue(RSK_SHUFFLE_COWS) && this->actor.textId = 0x2006;
!EnCow_HasBeenMilked(this, play)) { } else {
EnCow_SetCowMilked(this, play);
// setting the ocarina mode here prevents intermittent issues
// with the item get not triggering until walking away
play->msgCtx.ocarinaMode = OCARINA_MODE_00;
this->actionFunc = EnCow_GivePlayerRandomizedItem;
return; return;
} }
this->actionFunc = func_809DF8FC;
this->actor.flags |= ACTOR_FLAG_WILL_TALK;
func_8002F2CC(&this->actor, play, 170.0f);
this->actor.textId = 0x2006;
} else { } else {
this->unk_276 |= 4; this->unk_276 |= 4;
} }

View File

@ -21,4 +21,6 @@ typedef struct EnCow {
/* 0x027C */ EnCowActionFunc actionFunc; /* 0x027C */ EnCowActionFunc actionFunc;
} EnCow; // size = 0x0280 } EnCow; // size = 0x0280
void func_809DEE9C(EnCow* enCow);
#endif #endif

View File

@ -16,7 +16,6 @@ void EnDns_Destroy(Actor* thisx, PlayState* play);
void EnDns_Update(Actor* thisx, PlayState* play); void EnDns_Update(Actor* thisx, PlayState* play);
void EnDns_Draw(Actor* thisx, PlayState* play); void EnDns_Draw(Actor* thisx, PlayState* play);
u32 EnDns_RandomizerPurchaseableCheck(EnDns* this);
u32 func_809EF5A4(EnDns* this); u32 func_809EF5A4(EnDns* this);
u32 func_809EF658(EnDns* this); u32 func_809EF658(EnDns* this);
u32 func_809EF70C(EnDns* this); u32 func_809EF70C(EnDns* this);
@ -26,7 +25,6 @@ u32 func_809EF854(EnDns* this);
u32 func_809EF8F4(EnDns* this); u32 func_809EF8F4(EnDns* this);
u32 func_809EF9A4(EnDns* this); u32 func_809EF9A4(EnDns* this);
void EnDns_RandomizerPurchase(EnDns* this);
void func_809EF9F8(EnDns* this); void func_809EF9F8(EnDns* this);
void func_809EFA28(EnDns* this); void func_809EFA28(EnDns* this);
void func_809EFA58(EnDns* this); void func_809EFA58(EnDns* this);
@ -168,32 +166,6 @@ void EnDns_Init(Actor* thisx, PlayState* play) {
this->actor.gravity = -1.0f; this->actor.gravity = -1.0f;
this->actor.textId = D_809F040C[this->actor.params]; this->actor.textId = D_809F040C[this->actor.params];
this->dnsItemEntry = sItemEntries[this->actor.params]; this->dnsItemEntry = sItemEntries[this->actor.params];
if (IS_RANDO) {
// Ugly, but the best way we can identify which grotto we are in, same method 3DRando uses, but we'll need to account for entrance rando
s16 respawnData = gSaveContext.respawn[RESPAWN_MODE_RETURN].data & ((1 << 8) - 1);
this->scrubIdentity = Randomizer_IdentifyScrub(play->sceneNum, this->actor.params, respawnData);
if ((Randomizer_GetSettingValue(RSK_SHUFFLE_SCRUBS) == RO_SCRUBS_AFFORDABLE ||
Randomizer_GetSettingValue(RSK_SHUFFLE_SCRUBS) == RO_SCRUBS_RANDOM) &&
this->scrubIdentity.itemPrice != -1) {
this->dnsItemEntry->itemPrice = this->scrubIdentity.itemPrice;
}
if (Randomizer_GetSettingValue(RSK_SHUFFLE_SCRUBS) == RO_SCRUBS_EXPENSIVE) {
// temporary workaround: always use 40 rupees as price instead of 70
if (this->actor.params == 0x0006) {
this->dnsItemEntry->itemPrice = 40;
}
}
if (this->scrubIdentity.isShuffled) {
this->dnsItemEntry->getItemId = this->scrubIdentity.getItemId;
this->dnsItemEntry->purchaseableCheck = EnDns_RandomizerPurchaseableCheck;
this->dnsItemEntry->setRupeesAndFlags = EnDns_RandomizerPurchase;
this->dnsItemEntry->itemAmount = 1;
this->actor.textId = 0x9000 + (this->scrubIdentity.randomizerInf - RAND_INF_SCRUBS_PURCHASED_DODONGOS_CAVERN_DEKU_SCRUB_NEAR_BOMB_BAG_LEFT);
}
}
this->actionFunc = EnDns_SetupWait; this->actionFunc = EnDns_SetupWait;
} }
@ -216,13 +188,6 @@ void EnDns_ChangeAnim(EnDns* this, u8 index) {
/* Item give checking functions */ /* Item give checking functions */
u32 EnDns_RandomizerPurchaseableCheck(EnDns* this) {
if (gSaveContext.rupees < this->dnsItemEntry->itemPrice || Flags_GetRandomizerInf(this->scrubIdentity.randomizerInf)) {
return 0;
}
return 4;
}
u32 func_809EF5A4(EnDns* this) { u32 func_809EF5A4(EnDns* this) {
if ((CUR_CAPACITY(UPG_NUTS) != 0) && (AMMO(ITEM_NUT) >= CUR_CAPACITY(UPG_NUTS))) { if ((CUR_CAPACITY(UPG_NUTS) != 0) && (AMMO(ITEM_NUT) >= CUR_CAPACITY(UPG_NUTS))) {
return 1; return 1;
@ -319,10 +284,6 @@ u32 func_809EF9A4(EnDns* this) {
} }
/* Paying and flagging functions */ /* Paying and flagging functions */
void EnDns_RandomizerPurchase(EnDns* this) {
Rupees_ChangeBy(-this->dnsItemEntry->itemPrice);
Flags_SetRandomizerInf(this->scrubIdentity.randomizerInf);
}
void func_809EF9F8(EnDns* this) { void func_809EF9F8(EnDns* this) {
Rupees_ChangeBy(-this->dnsItemEntry->itemPrice); Rupees_ChangeBy(-this->dnsItemEntry->itemPrice);
@ -412,44 +373,39 @@ void EnDns_Talk(EnDns* this, PlayState* play) {
void func_809EFDD0(EnDns* this, PlayState* play) { void func_809EFDD0(EnDns* this, PlayState* play) {
u16 pendingGetItemId; u16 pendingGetItemId;
if (!IS_RANDO || !this->scrubIdentity.isShuffled) { if (this->actor.params == 0x9) {
if (this->actor.params == 0x9) { if (CUR_UPG_VALUE(UPG_STICKS) < 2) {
if (CUR_UPG_VALUE(UPG_STICKS) < 2) { pendingGetItemId = GI_STICK_UPGRADE_20;
pendingGetItemId = GI_STICK_UPGRADE_20;
} else {
pendingGetItemId = GI_STICK_UPGRADE_30;
}
} else if (this->actor.params == 0xA) {
if (CUR_UPG_VALUE(UPG_NUTS) < 2) {
pendingGetItemId = GI_NUT_UPGRADE_30;
} else {
pendingGetItemId = GI_NUT_UPGRADE_40;
}
} else { } else {
pendingGetItemId = this->dnsItemEntry->getItemId; pendingGetItemId = GI_STICK_UPGRADE_30;
}
} else if (this->actor.params == 0xA) {
if (CUR_UPG_VALUE(UPG_NUTS) < 2) {
pendingGetItemId = GI_NUT_UPGRADE_30;
} else {
pendingGetItemId = GI_NUT_UPGRADE_40;
} }
GetItemEntry itemEntry = ItemTable_Retrieve(pendingGetItemId);
gSaveContext.pendingSale = itemEntry.itemId;
gSaveContext.pendingSaleMod = itemEntry.modIndex;
func_8002F434(&this->actor, play, pendingGetItemId, 130.0f, 100.0f);
} else { } else {
GetItemEntry itemEntry = Randomizer_GetItemFromKnownCheck(this->scrubIdentity.randomizerCheck, this->scrubIdentity.getItemId); pendingGetItemId = this->dnsItemEntry->getItemId;
gSaveContext.pendingSale = itemEntry.itemId;
gSaveContext.pendingSaleMod = itemEntry.modIndex;
GiveItemEntryFromActor(&this->actor, play, itemEntry, 130.0f, 100.0f);
} }
GetItemEntry itemEntry = ItemTable_Retrieve(pendingGetItemId);
gSaveContext.pendingSale = itemEntry.itemId;
gSaveContext.pendingSaleMod = itemEntry.modIndex;
func_8002F434(&this->actor, play, pendingGetItemId, 130.0f, 100.0f);
} }
void func_809EFEE8(EnDns* this, PlayState* play) { void func_809EFEE8(EnDns* this, PlayState* play) {
if ((Message_GetState(&play->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(play)) { if ((Message_GetState(&play->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(play)) {
Message_CloseTextbox(play); Message_CloseTextbox(play);
func_809EFDD0(this, play); if (GameInteractor_Should(GI_VB_GIVE_ITEM_FROM_BUSINESS_SCRUB, true, this)) {
func_809EFDD0(this, play);
}
this->actionFunc = func_809EFF50; this->actionFunc = func_809EFF50;
} }
} }
void func_809EFF50(EnDns* this, PlayState* play) { void func_809EFF50(EnDns* this, PlayState* play) {
if (Actor_HasParent(&this->actor, play)) { if (Actor_HasParent(&this->actor, play) || !GameInteractor_Should(GI_VB_GIVE_ITEM_FROM_BUSINESS_SCRUB, true, this)) {
this->actor.parent = NULL; this->actor.parent = NULL;
this->actionFunc = func_809EFF98; this->actionFunc = func_809EFF98;
} else { } else {
@ -533,9 +489,6 @@ void EnDns_Update(Actor* thisx, PlayState* play) {
this->dustTimer++; this->dustTimer++;
this->actor.textId = D_809F040C[this->actor.params]; this->actor.textId = D_809F040C[this->actor.params];
if (IS_RANDO && this->scrubIdentity.isShuffled) {
this->actor.textId = 0x9000 + (this->scrubIdentity.randomizerInf - RAND_INF_SCRUBS_PURCHASED_DODONGOS_CAVERN_DEKU_SCRUB_NEAR_BOMB_BAG_LEFT);
}
Actor_SetFocus(&this->actor, 60.0f); Actor_SetFocus(&this->actor, 60.0f);
Actor_SetScale(&this->actor, 0.01f); Actor_SetScale(&this->actor, 0.01f);
SkelAnime_Update(&this->skelAnime); SkelAnime_Update(&this->skelAnime);

View File

@ -32,7 +32,10 @@ typedef struct EnDns {
/* 0x02BD */ u8 dropCollectible; /* 0x02BD */ u8 dropCollectible;
/* 0x02C0 */ DnsItemEntry* dnsItemEntry; /* 0x02C0 */ DnsItemEntry* dnsItemEntry;
/* 0x02C4 */ f32 yInitPos; /* 0x02C4 */ f32 yInitPos;
/* */ ScrubIdentity scrubIdentity; // #region SOH [Randomizer]
/* */ DnsItemEntry sohDnsItemEntry;
/* */ ScrubIdentity sohScrubIdentity;
// #endregion
} EnDns; // size = 0x02C8 } EnDns; // size = 0x02C8
#endif #endif

View File

@ -6,7 +6,7 @@
#include "z_en_ds.h" #include "z_en_ds.h"
#include "objects/object_ds/object_ds.h" #include "objects/object_ds/object_ds.h"
#include "soh/Enhancements/randomizer/adult_trade_shuffle.h" #include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY) #define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY)
@ -90,19 +90,12 @@ void EnDs_DisplayOddPotionText(EnDs* this, PlayState* play) {
} }
void EnDs_GiveOddPotion(EnDs* this, PlayState* play) { void EnDs_GiveOddPotion(EnDs* this, PlayState* play) {
if (Actor_HasParent(&this->actor, play)) { if (Actor_HasParent(&this->actor, play) || !GameInteractor_Should(GI_VB_TRADE_ODD_MUSHROOM, true, this)) {
this->actor.parent = NULL; this->actor.parent = NULL;
this->actionFunc = EnDs_DisplayOddPotionText; this->actionFunc = EnDs_DisplayOddPotionText;
gSaveContext.timer2State = 0; gSaveContext.timer2State = 0;
} else { } else {
u32 itemId = GI_ODD_POTION; func_8002F434(&this->actor, play, GI_ODD_POTION, 10000.0f, 50.0f);
if (IS_RANDO) {
GetItemEntry itemEntry = Randomizer_GetItemFromKnownCheck(RC_KAK_TRADE_ODD_MUSHROOM, GI_ODD_POTION);
GiveItemEntryFromActor(&this->actor, play, itemEntry, 10000.0f, 50.0f);
Randomizer_ConsumeAdultTradeItem(play, ITEM_ODD_MUSHROOM);
return;
}
func_8002F434(&this->actor, play, itemId, 10000.0f, 50.0f);
} }
} }
@ -111,13 +104,9 @@ void EnDs_TalkAfterBrewOddPotion(EnDs* this, PlayState* play) {
Message_CloseTextbox(play); Message_CloseTextbox(play);
this->actionFunc = EnDs_GiveOddPotion; this->actionFunc = EnDs_GiveOddPotion;
u32 itemId = GI_ODD_POTION; u32 itemId = GI_ODD_POTION;
if (IS_RANDO) { if (GameInteractor_Should(GI_VB_TRADE_ODD_MUSHROOM, true, this)) {
GetItemEntry itemEntry = Randomizer_GetItemFromKnownCheck(RC_KAK_TRADE_ODD_MUSHROOM, GI_ODD_POTION); func_8002F434(&this->actor, play, itemId, 10000.0f, 50.0f);
GiveItemEntryFromActor(&this->actor, play, itemEntry, 10000.0f, 50.0f);
Randomizer_ConsumeAdultTradeItem(play, ITEM_ODD_MUSHROOM);
return;
} }
func_8002F434(&this->actor, play, itemId, 10000.0f, 50.0f);
} }
} }
@ -138,7 +127,7 @@ void EnDs_BrewOddPotion2(EnDs* this, PlayState* play) {
this->brewTimer -= 1; this->brewTimer -= 1;
} else { } else {
this->actionFunc = EnDs_BrewOddPotion3; this->actionFunc = EnDs_BrewOddPotion3;
this->brewTimer = IS_RANDO ? 0 : 60; this->brewTimer = GameInteractor_Should(GI_VB_PLAY_EYEDROP_CREATION_ANIM, true, this) ? 60 : 0;
Flags_UnsetSwitch(play, 0x3F); Flags_UnsetSwitch(play, 0x3F);
} }
} }
@ -148,7 +137,7 @@ void EnDs_BrewOddPotion1(EnDs* this, PlayState* play) {
this->brewTimer -= 1; this->brewTimer -= 1;
} else { } else {
this->actionFunc = EnDs_BrewOddPotion2; this->actionFunc = EnDs_BrewOddPotion2;
this->brewTimer = IS_RANDO ? 0 : 20; this->brewTimer = GameInteractor_Should(GI_VB_PLAY_EYEDROP_CREATION_ANIM, true, this) ? 20 : 0;
} }
Math_StepToF(&this->unk_1E4, 1.0f, 0.01f); Math_StepToF(&this->unk_1E4, 1.0f, 0.01f);
@ -162,7 +151,7 @@ void EnDs_OfferOddPotion(EnDs* this, PlayState* play) {
switch (play->msgCtx.choiceIndex) { switch (play->msgCtx.choiceIndex) {
case 0: // yes case 0: // yes
this->actionFunc = EnDs_BrewOddPotion1; this->actionFunc = EnDs_BrewOddPotion1;
this->brewTimer = IS_RANDO ? 0 : 60; this->brewTimer = GameInteractor_Should(GI_VB_PLAY_EYEDROP_CREATION_ANIM, true, this) ? 60 : 0;
Flags_SetSwitch(play, 0x3F); Flags_SetSwitch(play, 0x3F);
play->msgCtx.msgMode = MSGMODE_PAUSED; play->msgCtx.msgMode = MSGMODE_PAUSED;
player->exchangeItemId = EXCH_ITEM_NONE; player->exchangeItemId = EXCH_ITEM_NONE;
@ -174,22 +163,10 @@ void EnDs_OfferOddPotion(EnDs* this, PlayState* play) {
} }
} }
u8 EnDs_RandoCanGetGrannyItem() {
return IS_RANDO && Randomizer_GetSettingValue(RSK_SHUFFLE_MERCHANTS) != RO_SHUFFLE_MERCHANTS_OFF &&
!Flags_GetRandomizerInf(RAND_INF_MERCHANTS_GRANNYS_SHOP) &&
// Traded odd mushroom when adult trade is on
((Randomizer_GetSettingValue(RSK_SHUFFLE_ADULT_TRADE) && Flags_GetItemGetInf(ITEMGETINF_30)) ||
// Found claim check when adult trade is off
(!Randomizer_GetSettingValue(RSK_SHUFFLE_ADULT_TRADE) &&
INV_CONTENT(ITEM_CLAIM_CHECK) == ITEM_CLAIM_CHECK));
}
s32 EnDs_CheckRupeesAndBottle() { s32 EnDs_CheckRupeesAndBottle() {
if (gSaveContext.rupees < 100) { if (gSaveContext.rupees < 100) {
return 0; return 0;
} else if (EnDs_RandoCanGetGrannyItem()) { // Allow buying the rando item regardless of having a bottle } else if (GameInteractor_Should(GI_VB_NEED_BOTTLE_FOR_GRANNYS_ITEM, Inventory_HasEmptyBottle() == 0, NULL)) {
return 2;
} else if (Inventory_HasEmptyBottle() == 0) {
return 1; return 1;
} else { } else {
return 2; return 2;
@ -197,20 +174,11 @@ s32 EnDs_CheckRupeesAndBottle() {
} }
void EnDs_GiveBluePotion(EnDs* this, PlayState* play) { void EnDs_GiveBluePotion(EnDs* this, PlayState* play) {
if (Actor_HasParent(&this->actor, play)) { if (Actor_HasParent(&this->actor, play) || !GameInteractor_Should(GI_VB_GIVE_ITEM_FROM_GRANNYS_SHOP, true, this)) {
if (EnDs_RandoCanGetGrannyItem()) {
Flags_SetRandomizerInf(RAND_INF_MERCHANTS_GRANNYS_SHOP);
}
this->actor.parent = NULL; this->actor.parent = NULL;
this->actionFunc = EnDs_Talk; this->actionFunc = EnDs_Talk;
} else { } else {
if (EnDs_RandoCanGetGrannyItem()) { func_8002F434(&this->actor, play, GI_POTION_BLUE, 10000.0f, 50.0f);
GetItemEntry entry = Randomizer_GetItemFromKnownCheck(RC_KAK_GRANNYS_SHOP, GI_POTION_BLUE);
GiveItemEntryFromActor(&this->actor, play, entry, 10000.0f, 50.0f);
} else {
func_8002F434(&this->actor, play, GI_POTION_BLUE, 10000.0f, 50.0f);
}
} }
} }
@ -229,18 +197,14 @@ void EnDs_OfferBluePotion(EnDs* this, PlayState* play) {
case 2: // have 100 rupees and empty bottle case 2: // have 100 rupees and empty bottle
Rupees_ChangeBy(-100); Rupees_ChangeBy(-100);
this->actor.flags &= ~ACTOR_FLAG_WILL_TALK; this->actor.flags &= ~ACTOR_FLAG_WILL_TALK;
GetItemEntry itemEntry;
if (EnDs_RandoCanGetGrannyItem()) { if (GameInteractor_Should(GI_VB_GIVE_ITEM_FROM_GRANNYS_SHOP, true, this)) {
itemEntry = Randomizer_GetItemFromKnownCheck(RC_KAK_GRANNYS_SHOP, GI_POTION_BLUE); GetItemEntry itemEntry = ItemTable_Retrieve(GI_POTION_BLUE);
GiveItemEntryFromActor(&this->actor, play, itemEntry, 10000.0f, 50.0f);
} else {
itemEntry = ItemTable_Retrieve(GI_POTION_BLUE);
func_8002F434(&this->actor, play, GI_POTION_BLUE, 10000.0f, 50.0f); func_8002F434(&this->actor, play, GI_POTION_BLUE, 10000.0f, 50.0f);
gSaveContext.pendingSale = itemEntry.itemId;
gSaveContext.pendingSaleMod = itemEntry.modIndex;
} }
gSaveContext.pendingSale = itemEntry.itemId;
gSaveContext.pendingSaleMod = itemEntry.modIndex;
this->actionFunc = EnDs_GiveBluePotion; this->actionFunc = EnDs_GiveBluePotion;
return; return;
} }
@ -261,10 +225,7 @@ void EnDs_Wait(EnDs* this, PlayState* play) {
Audio_PlaySoundGeneral(NA_SE_SY_TRE_BOX_APPEAR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); Audio_PlaySoundGeneral(NA_SE_SY_TRE_BOX_APPEAR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8);
player->actor.textId = 0x504A; player->actor.textId = 0x504A;
this->actionFunc = EnDs_OfferOddPotion; this->actionFunc = EnDs_OfferOddPotion;
} else if ( } else if (GameInteractor_Should(GI_VB_OFFER_BLUE_POTION, Flags_GetItemGetInf(ITEMGETINF_30), this)) { // Traded odd mushroom
// Always offer blue potion when adult trade is off
(IS_RANDO && Randomizer_GetSettingValue(RSK_SHUFFLE_ADULT_TRADE) == RO_GENERIC_OFF) ||
Flags_GetItemGetInf(ITEMGETINF_30)) { // Traded odd mushroom
player->actor.textId = 0x500C; player->actor.textId = 0x500C;
this->actionFunc = EnDs_OfferBluePotion; this->actionFunc = EnDs_OfferBluePotion;
} else { } else {

View File

@ -1,6 +1,7 @@
#include "z_en_du.h" #include "z_en_du.h"
#include "objects/object_du/object_du.h" #include "objects/object_du/object_du.h"
#include "scenes/overworld/spot18/spot18_scene.h" #include "scenes/overworld/spot18/spot18_scene.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY | ACTOR_FLAG_NO_FREEZE_OCARINA) #define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY | ACTOR_FLAG_NO_FREEZE_OCARINA)
@ -376,7 +377,7 @@ void func_809FE4A4(EnDu* this, PlayState* play) {
play->msgCtx.ocarinaMode = OCARINA_MODE_00; play->msgCtx.ocarinaMode = OCARINA_MODE_00;
EnDu_SetupAction(this, func_809FE3C0); EnDu_SetupAction(this, func_809FE3C0);
} else if (play->msgCtx.ocarinaMode >= OCARINA_MODE_06) { } else if (play->msgCtx.ocarinaMode >= OCARINA_MODE_06) {
if (!IS_RANDO) { if (GameInteractor_Should(GI_VB_PLAY_DARUNIAS_JOY_CS, true, NULL)) {
play->csCtx.segment = SEGMENTED_TO_VIRTUAL(gGoronCityDaruniaWrongCs); play->csCtx.segment = SEGMENTED_TO_VIRTUAL(gGoronCityDaruniaWrongCs);
gSaveContext.cutsceneTrigger = 1; gSaveContext.cutsceneTrigger = 1;
} }
@ -385,7 +386,7 @@ void func_809FE4A4(EnDu* this, PlayState* play) {
play->msgCtx.ocarinaMode = OCARINA_MODE_04; play->msgCtx.ocarinaMode = OCARINA_MODE_04;
} else if (play->msgCtx.ocarinaMode == OCARINA_MODE_03) { } else if (play->msgCtx.ocarinaMode == OCARINA_MODE_03) {
Audio_PlaySoundGeneral(NA_SE_SY_CORRECT_CHIME, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); Audio_PlaySoundGeneral(NA_SE_SY_CORRECT_CHIME, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8);
if (!IS_RANDO) { if (GameInteractor_Should(GI_VB_PLAY_DARUNIAS_JOY_CS, true, NULL)) {
play->csCtx.segment = SEGMENTED_TO_VIRTUAL(gGoronCityDaruniaCorrectCs); play->csCtx.segment = SEGMENTED_TO_VIRTUAL(gGoronCityDaruniaCorrectCs);
gSaveContext.cutsceneTrigger = 1; gSaveContext.cutsceneTrigger = 1;
} }
@ -474,10 +475,7 @@ void func_809FE890(EnDu* this, PlayState* play) {
Vec3f velocity = { 0.0f, 0.0f, 0.0f }; Vec3f velocity = { 0.0f, 0.0f, 0.0f };
CsCmdActorAction* csAction; CsCmdActorAction* csAction;
if (play->csCtx.state == CS_STATE_IDLE || IS_RANDO) { if (play->csCtx.state == CS_STATE_IDLE) {
if (IS_RANDO) {
play->csCtx.state = CS_STATE_IDLE;
}
func_8002DF54(play, &this->actor, 1); func_8002DF54(play, &this->actor, 1);
EnDu_SetupAction(this, func_809FEB08); EnDu_SetupAction(this, func_809FEB08);
return; return;
@ -556,11 +554,8 @@ void func_809FEB08(EnDu* this, PlayState* play) {
EnDu_SetupAction(this, func_809FE3C0); EnDu_SetupAction(this, func_809FE3C0);
return; return;
} }
if ((!IS_RANDO && CUR_UPG_VALUE(UPG_STRENGTH) <= 0) || if (GameInteractor_Should(GI_VB_BE_ELIGIBLE_FOR_DARUNIAS_JOY_REWARD, CUR_UPG_VALUE(UPG_STRENGTH) <= 0, NULL)) {
(IS_RANDO && !Flags_GetTreasure(play, 0x1E))) { Flags_SetRandomizerInf(RAND_INF_DARUNIAS_JOY);
if (IS_RANDO) {
Flags_SetTreasure(play, 0x1E);
}
this->actor.textId = 0x301C; this->actor.textId = 0x301C;
EnDu_SetupAction(this, func_809FEC14); EnDu_SetupAction(this, func_809FEC14);
} else { } else {
@ -581,17 +576,13 @@ void func_809FEC14(EnDu* this, PlayState* play) {
} }
void func_809FEC70(EnDu* this, PlayState* play) { void func_809FEC70(EnDu* this, PlayState* play) {
if (Actor_HasParent(&this->actor, play)) { if (Actor_HasParent(&this->actor, play) || !GameInteractor_Should(GI_VB_GIVE_ITEM_STRENGTH_1, true, NULL)) {
this->actor.parent = NULL; this->actor.parent = NULL;
EnDu_SetupAction(this, func_809FECE4); EnDu_SetupAction(this, func_809FECE4);
} else { } else {
f32 xzRange = this->actor.xzDistToPlayer + 1.0f; f32 xzRange = this->actor.xzDistToPlayer + 1.0f;
if (!IS_RANDO) {
func_8002F434(&this->actor, play, GI_BRACELET, xzRange, fabsf(this->actor.yDistToPlayer) + 1.0f); func_8002F434(&this->actor, play, GI_BRACELET, xzRange, fabsf(this->actor.yDistToPlayer) + 1.0f);
} else {
GetItemEntry getItemEntry = Randomizer_GetItemFromKnownCheck(RC_GC_DARUNIAS_JOY, GI_BRACELET);
GiveItemEntryFromActor(&this->actor, play, getItemEntry, xzRange, fabsf(this->actor.yDistToPlayer) + 1.0f);
}
} }
} }

View File

@ -45,7 +45,6 @@ void EnFr_OcarinaMistake(EnFr* this, PlayState* play);
void EnFr_SetupReward(EnFr* this, PlayState* play, u8 unkCondition); void EnFr_SetupReward(EnFr* this, PlayState* play, u8 unkCondition);
void EnFr_PrintTextBox(EnFr* this, PlayState* play); void EnFr_PrintTextBox(EnFr* this, PlayState* play);
void EnFr_TalkBeforeReward(EnFr* this, PlayState* play); void EnFr_TalkBeforeReward(EnFr* this, PlayState* play);
RandomizerCheck EnFr_RandomizerCheckFromSongIndex(u16 songIndex);
void EnFr_SetReward(EnFr* this, PlayState* play); void EnFr_SetReward(EnFr* this, PlayState* play);
// Deactivate // Deactivate
@ -618,7 +617,6 @@ void EnFr_Idle(EnFr* this, PlayState* play) {
player->actor.world.pos.z = this->actor.world.pos.z; // z = -1220.0f player->actor.world.pos.z = this->actor.world.pos.z; // z = -1220.0f
player->currentYaw = player->actor.world.rot.y = player->actor.shape.rot.y = this->actor.world.rot.y; player->currentYaw = player->actor.world.rot.y = player->actor.shape.rot.y = this->actor.world.rot.y;
this->reward = GI_NONE; this->reward = GI_NONE;
this->getItemEntry = (GetItemEntry)GET_ITEM_NONE;
this->actionFunc = EnFr_Activate; this->actionFunc = EnFr_Activate;
} else if (EnFr_IsAboveAndWithin30DistXZ(player, this)) { } else if (EnFr_IsAboveAndWithin30DistXZ(player, this)) {
player->unk_6A8 = &this->actor; player->unk_6A8 = &this->actor;
@ -854,7 +852,6 @@ s32 EnFr_IsFrogSongComplete(EnFr* this, PlayState* play) {
void EnFr_OcarinaMistake(EnFr* this, PlayState* play) { void EnFr_OcarinaMistake(EnFr* this, PlayState* play) {
Message_CloseTextbox(play); Message_CloseTextbox(play);
this->reward = GI_NONE; this->reward = GI_NONE;
this->getItemEntry = (GetItemEntry)GET_ITEM_NONE;
func_80078884(NA_SE_SY_OCARINA_ERROR); func_80078884(NA_SE_SY_OCARINA_ERROR);
Audio_OcaSetInstrument(0); Audio_OcaSetInstrument(0);
sEnFrPointers.flags = 12; sEnFrPointers.flags = 12;
@ -939,23 +936,6 @@ void EnFr_TalkBeforeReward(EnFr* this, PlayState* play) {
} }
} }
RandomizerCheck EnFr_RandomizerCheckFromSongIndex(u16 songIndex) {
switch (songIndex) {
case FROG_ZL:
return RC_ZR_FROGS_ZELDAS_LULLABY;
case FROG_EPONA:
return RC_ZR_FROGS_EPONAS_SONG;
case FROG_SARIA:
return RC_ZR_FROGS_SARIAS_SONG;
case FROG_SUNS:
return RC_ZR_FROGS_SUNS_SONG;
case FROG_SOT:
return RC_ZR_FROGS_SONG_OF_TIME;
default:
return RC_UNKNOWN_CHECK;
}
}
void EnFr_SetReward(EnFr* this, PlayState* play) { void EnFr_SetReward(EnFr* this, PlayState* play) {
u16 songIndex; u16 songIndex;
@ -963,16 +943,12 @@ void EnFr_SetReward(EnFr* this, PlayState* play) {
songIndex = this->songIndex; songIndex = this->songIndex;
this->actionFunc = EnFr_Deactivate; this->actionFunc = EnFr_Deactivate;
this->reward = GI_NONE; this->reward = GI_NONE;
this->getItemEntry = (GetItemEntry)GET_ITEM_NONE;
if ((songIndex >= FROG_ZL) && (songIndex <= FROG_SOT)) { if ((songIndex >= FROG_ZL) && (songIndex <= FROG_SOT)) {
if (!(gSaveContext.eventChkInf[13] & sSongIndex[songIndex])) { if (!(gSaveContext.eventChkInf[13] & sSongIndex[songIndex])) {
gSaveContext.eventChkInf[13] |= sSongIndex[songIndex]; gSaveContext.eventChkInf[13] |= sSongIndex[songIndex];
GameInteractor_ExecuteOnFlagSet(FLAG_EVENT_CHECK_INF, (EVENTCHKINF_SONGS_FOR_FROGS_INDEX << 4) + sSongIndexShift[songIndex]); GameInteractor_ExecuteOnFlagSet(FLAG_EVENT_CHECK_INF, (EVENTCHKINF_SONGS_FOR_FROGS_INDEX << 4) + sSongIndexShift[songIndex]);
if (!IS_RANDO) { if (GameInteractor_Should(GI_VB_GIVE_ITEM_FROM_FROGS, true, this)) {
this->reward = GI_RUPEE_PURPLE; this->reward = GI_RUPEE_PURPLE;
} else {
this->getItemEntry = Randomizer_GetItemFromKnownCheck(EnFr_RandomizerCheckFromSongIndex(songIndex), GI_RUPEE_PURPLE);
this->reward = this->getItemEntry.getItemId;
} }
} else { } else {
this->reward = GI_RUPEE_BLUE; this->reward = GI_RUPEE_BLUE;
@ -981,11 +957,8 @@ void EnFr_SetReward(EnFr* this, PlayState* play) {
if (!(gSaveContext.eventChkInf[13] & sSongIndex[songIndex])) { if (!(gSaveContext.eventChkInf[13] & sSongIndex[songIndex])) {
gSaveContext.eventChkInf[13] |= sSongIndex[songIndex]; gSaveContext.eventChkInf[13] |= sSongIndex[songIndex];
GameInteractor_ExecuteOnFlagSet(FLAG_EVENT_CHECK_INF, (EVENTCHKINF_SONGS_FOR_FROGS_INDEX << 4) + sSongIndexShift[songIndex]); GameInteractor_ExecuteOnFlagSet(FLAG_EVENT_CHECK_INF, (EVENTCHKINF_SONGS_FOR_FROGS_INDEX << 4) + sSongIndexShift[songIndex]);
if (!IS_RANDO) { if (GameInteractor_Should(GI_VB_GIVE_ITEM_FROM_FROGS, true, this)) {
this->reward = GI_HEART_PIECE; this->reward = GI_HEART_PIECE;
} else {
this->getItemEntry = Randomizer_GetItemFromKnownCheck(RC_ZR_FROGS_IN_THE_RAIN, GI_HEART_PIECE);
this->reward = this->getItemEntry.getItemId;
} }
} else { } else {
this->reward = GI_RUPEE_BLUE; this->reward = GI_RUPEE_BLUE;
@ -994,11 +967,8 @@ void EnFr_SetReward(EnFr* this, PlayState* play) {
if (!(gSaveContext.eventChkInf[13] & sSongIndex[songIndex])) { if (!(gSaveContext.eventChkInf[13] & sSongIndex[songIndex])) {
gSaveContext.eventChkInf[13] |= sSongIndex[songIndex]; gSaveContext.eventChkInf[13] |= sSongIndex[songIndex];
GameInteractor_ExecuteOnFlagSet(FLAG_EVENT_CHECK_INF, (EVENTCHKINF_SONGS_FOR_FROGS_INDEX << 4) + sSongIndexShift[songIndex]); GameInteractor_ExecuteOnFlagSet(FLAG_EVENT_CHECK_INF, (EVENTCHKINF_SONGS_FOR_FROGS_INDEX << 4) + sSongIndexShift[songIndex]);
if (!IS_RANDO) { if (GameInteractor_Should(GI_VB_GIVE_ITEM_FROM_FROGS, true, this)) {
this->reward = GI_HEART_PIECE; this->reward = GI_HEART_PIECE;
} else {
this->getItemEntry = Randomizer_GetItemFromKnownCheck(RC_ZR_FROGS_OCARINA_GAME, GI_HEART_PIECE);
this->reward = this->getItemEntry.getItemId;
} }
} else { } else {
this->reward = GI_RUPEE_PURPLE; this->reward = GI_RUPEE_PURPLE;
@ -1049,29 +1019,23 @@ void EnFr_Deactivate(EnFr* this, PlayState* play) {
this->actionFunc = EnFr_Idle; this->actionFunc = EnFr_Idle;
} else { } else {
this->actionFunc = EnFr_GiveReward; this->actionFunc = EnFr_GiveReward;
if (!IS_RANDO || this->getItemEntry.getItemId == GI_NONE) { if (GameInteractor_Should(GI_VB_GIVE_ITEM_FROM_FROGS, true, this)) {
func_8002F434(&this->actor, play, this->reward, 30.0f, 100.0f); func_8002F434(&this->actor, play, this->reward, 30.0f, 100.0f);
} else {
GiveItemEntryFromActor(&this->actor, play, this->getItemEntry, 30.0f, 100.0f);
} }
} }
} }
void EnFr_GiveReward(EnFr* this, PlayState* play) { void EnFr_GiveReward(EnFr* this, PlayState* play) {
if (Actor_HasParent(&this->actor, play)) { if (Actor_HasParent(&this->actor, play) || !GameInteractor_Should(GI_VB_GIVE_ITEM_FROM_FROGS, true, this)) {
this->actor.parent = NULL; this->actor.parent = NULL;
this->actionFunc = EnFr_SetIdle; this->actionFunc = EnFr_SetIdle;
} else { } else {
if (!IS_RANDO || this->getItemEntry.getItemId == GI_NONE) { func_8002F434(&this->actor, play, this->reward, 30.0f, 100.0f);
func_8002F434(&this->actor, play, this->reward, 30.0f, 100.0f);
} else {
GiveItemEntryFromActor(&this->actor, play, this->getItemEntry, 30.0f, 100.0f);
}
} }
} }
void EnFr_SetIdle(EnFr* this, PlayState* play) { void EnFr_SetIdle(EnFr* this, PlayState* play) {
if ((Message_GetState(&play->msgCtx) == TEXT_STATE_DONE) && Message_ShouldAdvance(play) || (IS_RANDO && this->reward == RG_ICE_TRAP)) { if ((Message_GetState(&play->msgCtx) == TEXT_STATE_DONE) && Message_ShouldAdvance(play)) {
this->actionFunc = EnFr_Idle; this->actionFunc = EnFr_Idle;
} }
} }

View File

@ -73,7 +73,6 @@ typedef struct EnFr {
/* 0x03AA */ s16 xyAngleButterfly; // Butterfly Travels along random angles in the x-y plane /* 0x03AA */ s16 xyAngleButterfly; // Butterfly Travels along random angles in the x-y plane
/* 0x03AC */ Vec3f posButterfly; // Position/Coordinates of the Butterfly /* 0x03AC */ Vec3f posButterfly; // Position/Coordinates of the Butterfly
/* 0x03B8 */ Vec3f posButterflyLight; // Used in Lights_PointNoGlowSetInfo() /* 0x03B8 */ Vec3f posButterflyLight; // Used in Lights_PointNoGlowSetInfo()
/* */ GetItemEntry getItemEntry;
} EnFr; // size = 0x03C4 } EnFr; // size = 0x03C4
typedef struct { typedef struct {
@ -81,4 +80,7 @@ typedef struct {
EnFr* frogs[5]; EnFr* frogs[5];
} EnFrPointers; } EnFrPointers;
void EnFr_Idle(EnFr* enFr, PlayState* play);
void EnFr_GiveReward(EnFr* enFr, PlayState* play);
#endif #endif

View File

@ -8,6 +8,7 @@
#include "objects/object_oF1d_map/object_oF1d_map.h" #include "objects/object_oF1d_map/object_oF1d_map.h"
#include "objects/object_gm/object_gm.h" #include "objects/object_gm/object_gm.h"
#include "vt.h" #include "vt.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#include <assert.h> #include <assert.h>
#define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY | ACTOR_FLAG_UPDATE_WHILE_CULLED) #define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY | ACTOR_FLAG_UPDATE_WHILE_CULLED)
@ -96,10 +97,9 @@ void EnGm_Destroy(Actor* thisx, PlayState* play) {
s32 func_80A3D7C8(void) { s32 func_80A3D7C8(void) {
if (LINK_AGE_IN_YEARS == YEARS_CHILD) { if (LINK_AGE_IN_YEARS == YEARS_CHILD) {
return 0; return 0;
} else if ((IS_RANDO && Randomizer_GetSettingValue(RSK_SHUFFLE_MERCHANTS) != RO_SHUFFLE_MERCHANTS_OFF) && } else if (GameInteractor_Should(GI_VB_BE_ELIGIBLE_FOR_GIANTS_KNIFE_PURCHASE, (
!Flags_GetRandomizerInf(RAND_INF_MERCHANTS_MEDIGORON)) { !CHECK_OWNED_EQUIP_ALT(EQUIP_TYPE_SWORD, EQUIP_INV_SWORD_BIGGORON) // Don't have giant's knife
return 1; ), NULL)) {
} else if (!CHECK_OWNED_EQUIP_ALT(EQUIP_TYPE_SWORD, EQUIP_INV_SWORD_BIGGORON)) { // Don't have giant's knife
return 1; return 1;
} else if (CHECK_OWNED_EQUIP_ALT(EQUIP_TYPE_SWORD, EQUIP_INV_SWORD_BROKENGIANTKNIFE)) { // Have broken giant's knife } else if (CHECK_OWNED_EQUIP_ALT(EQUIP_TYPE_SWORD, EQUIP_INV_SWORD_BROKENGIANTKNIFE)) { // Have broken giant's knife
return 2; return 2;
@ -215,11 +215,6 @@ void func_80A3DC44(EnGm* this, PlayState* play) {
return; return;
case 1: case 1:
Flags_SetInfTable(INFTABLE_B1); Flags_SetInfTable(INFTABLE_B1);
if (IS_RANDO && Randomizer_GetSettingValue(RSK_SHUFFLE_MERCHANTS) != RO_SHUFFLE_MERCHANTS_OFF &&
!Flags_GetRandomizerInf(RAND_INF_MERCHANTS_MEDIGORON)) {
//Resets "Talked to Medigoron" flag in infTable to restore initial conversation state
Flags_UnsetInfTable(INFTABLE_B1);
}
case 2: case 2:
this->actionFunc = EnGm_ProcessChoiceIndex; this->actionFunc = EnGm_ProcessChoiceIndex;
default: default:
@ -254,20 +249,10 @@ void EnGm_ProcessChoiceIndex(EnGm* this, PlayState* play) {
Message_ContinueTextbox(play, 0xC8); Message_ContinueTextbox(play, 0xC8);
this->actionFunc = func_80A3DD7C; this->actionFunc = func_80A3DD7C;
} else { } else {
GetItemEntry itemEntry; if (GameInteractor_Should(GI_VB_GIVE_ITEM_FROM_MEDIGORON, true, this)) {
if (IS_RANDO && Randomizer_GetSettingValue(RSK_SHUFFLE_MERCHANTS) != RO_SHUFFLE_MERCHANTS_OFF &&
!Flags_GetRandomizerInf(RAND_INF_MERCHANTS_MEDIGORON)) {
itemEntry = Randomizer_GetItemFromKnownCheck(RC_GC_MEDIGORON, GI_SWORD_KNIFE);
GiveItemEntryFromActor(&this->actor, play, itemEntry, 415.0f, 10.0f);
Flags_SetInfTable(INFTABLE_B1);
} else {
itemEntry = ItemTable_Retrieve(GI_SWORD_KNIFE);
func_8002F434(&this->actor, play, GI_SWORD_KNIFE, 415.0f, 10.0f); func_8002F434(&this->actor, play, GI_SWORD_KNIFE, 415.0f, 10.0f);
} }
gSaveContext.pendingSale = itemEntry.itemId;
gSaveContext.pendingSaleMod = itemEntry.modIndex;
this->actionFunc = func_80A3DF00; this->actionFunc = func_80A3DF00;
} }
break; break;
@ -280,24 +265,12 @@ void EnGm_ProcessChoiceIndex(EnGm* this, PlayState* play) {
} }
void func_80A3DF00(EnGm* this, PlayState* play) { void func_80A3DF00(EnGm* this, PlayState* play) {
if (Actor_HasParent(&this->actor, play)) { if (Actor_HasParent(&this->actor, play) || !GameInteractor_Should(GI_VB_GIVE_ITEM_FROM_MEDIGORON, true, this)) {
if (IS_RANDO && Randomizer_GetSettingValue(RSK_SHUFFLE_MERCHANTS) != RO_SHUFFLE_MERCHANTS_OFF && Flags_SetRandomizerInf(RAND_INF_MERCHANTS_MEDIGORON);
!Flags_GetRandomizerInf(RAND_INF_MERCHANTS_MEDIGORON)) {
Flags_SetRandomizerInf(RAND_INF_MERCHANTS_MEDIGORON);
}
this->actor.parent = NULL; this->actor.parent = NULL;
this->actionFunc = func_80A3DF60; this->actionFunc = func_80A3DF60;
} else { } else {
if (IS_RANDO && Randomizer_GetSettingValue(RSK_SHUFFLE_MERCHANTS) != RO_SHUFFLE_MERCHANTS_OFF && func_8002F434(&this->actor, play, GI_SWORD_KNIFE, 415.0f, 10.0f);
!Flags_GetRandomizerInf(RAND_INF_MERCHANTS_MEDIGORON)) {
GetItemEntry itemEntry = Randomizer_GetItemFromKnownCheck(RC_GC_MEDIGORON, GI_SWORD_KNIFE);
GiveItemEntryFromActor(&this->actor, play, itemEntry, 415.0f, 10.0f);
Flags_SetInfTable(INFTABLE_B1);
}
else {
func_8002F434(&this->actor, play, GI_SWORD_KNIFE, 415.0f, 10.0f);
}
} }
} }

View File

@ -3,7 +3,6 @@
#include "objects/gameplay_keep/gameplay_keep.h" #include "objects/gameplay_keep/gameplay_keep.h"
#include "objects/object_oF1d_map/object_oF1d_map.h" #include "objects/object_oF1d_map/object_oF1d_map.h"
#include "soh/frame_interpolation.h" #include "soh/frame_interpolation.h"
#include "soh/Enhancements/randomizer/adult_trade_shuffle.h"
#define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY | ACTOR_FLAG_UPDATE_WHILE_CULLED | ACTOR_FLAG_DRAW_WHILE_CULLED) #define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY | ACTOR_FLAG_UPDATE_WHILE_CULLED | ACTOR_FLAG_DRAW_WHILE_CULLED)
@ -31,9 +30,11 @@ void func_80A40C78(EnGo* this, PlayState* play);
void EnGo_Eyedrops(EnGo* this, PlayState* play); void EnGo_Eyedrops(EnGo* this, PlayState* play);
void func_80A40DCC(EnGo* this, PlayState* play); void func_80A40DCC(EnGo* this, PlayState* play);
void EnGo_AddDust(EnGo* this, Vec3f* pos, Vec3f* velocity, Vec3f* accel, u8 initialTimer, f32 scale, f32 scaleStep);
void EnGo_UpdateDust(EnGo* this); void EnGo_SpawnEffectDust(EnGo* this, Vec3f* pos, Vec3f* velocity, Vec3f* accel, u8 initialTimer, f32 scale,
void EnGo_DrawDust(EnGo* this, PlayState* play); f32 scaleStep);
void EnGo_UpdateEffects(EnGo* this);
void EnGo_DrawEffects(EnGo* this, PlayState* play);
const ActorInit En_Go_InitVars = { const ActorInit En_Go_InitVars = {
ACTOR_EN_GO, ACTOR_EN_GO,
@ -95,10 +96,10 @@ u16 EnGo_GetTextID(PlayState* play, Actor* thisx) {
switch (thisx->params & 0xF0) { switch (thisx->params & 0xF0) {
case 0x90: case 0x90:
if (!IS_RANDO && gSaveContext.bgsFlag) { if (gSaveContext.bgsFlag) {
return 0x305E; return 0x305E;
} else if (INV_CONTENT(ITEM_TRADE_ADULT) >= ITEM_CLAIM_CHECK) { } else if (INV_CONTENT(ITEM_TRADE_ADULT) >= ITEM_CLAIM_CHECK) {
if (Environment_GetBgsDayCount() >= CVarGetInteger("gForgeTime", 3)) { if (Environment_GetBgsDayCount() >= 3) {
return 0x305E; return 0x305E;
} else { } else {
return 0x305D; return 0x305D;
@ -113,8 +114,7 @@ u16 EnGo_GetTextID(PlayState* play, Actor* thisx) {
return 0x3053; return 0x3053;
} }
case 0x00: case 0x00:
if ((!IS_RANDO && CHECK_QUEST_ITEM(QUEST_MEDALLION_FIRE)) || if (CHECK_QUEST_ITEM(QUEST_MEDALLION_FIRE)) {
(IS_RANDO && Flags_GetRandomizerInf(RAND_INF_DUNGEONS_DONE_FIRE_TEMPLE))) {
if (Flags_GetInfTable(INFTABLE_10F)) { if (Flags_GetInfTable(INFTABLE_10F)) {
return 0x3042; return 0x3042;
} else { } else {
@ -199,7 +199,7 @@ u16 EnGo_GetTextID(PlayState* play, Actor* thisx) {
} }
s16 EnGo_UpdateTalkState(PlayState* play, Actor* thisx) { s16 EnGo_UpdateTalkState(PlayState* play, Actor* thisx) {
s16 unkState = NPC_TALK_STATE_TALKING; s16 talkState = NPC_TALK_STATE_TALKING;
f32 xzRange; f32 xzRange;
f32 yRange = fabsf(thisx->yDistToPlayer) + 1.0f; f32 yRange = fabsf(thisx->yDistToPlayer) + 1.0f;
@ -210,51 +210,51 @@ s16 EnGo_UpdateTalkState(PlayState* play, Actor* thisx) {
switch (thisx->textId) { switch (thisx->textId) {
case 0x3008: case 0x3008:
Flags_SetInfTable(INFTABLE_E0); Flags_SetInfTable(INFTABLE_E0);
unkState = NPC_TALK_STATE_IDLE; talkState = NPC_TALK_STATE_IDLE;
break; break;
case 0x300B: case 0x300B:
Flags_SetInfTable(INFTABLE_EB); Flags_SetInfTable(INFTABLE_EB);
unkState = NPC_TALK_STATE_IDLE; talkState = NPC_TALK_STATE_IDLE;
break; break;
case 0x3014: case 0x3014:
Flags_SetInfTable(INFTABLE_F0); Flags_SetInfTable(INFTABLE_F0);
unkState = NPC_TALK_STATE_IDLE; talkState = NPC_TALK_STATE_IDLE;
break; break;
case 0x3016: case 0x3016:
Flags_SetInfTable(INFTABLE_F4); Flags_SetInfTable(INFTABLE_F4);
unkState = NPC_TALK_STATE_IDLE; talkState = NPC_TALK_STATE_IDLE;
break; break;
case 0x3018: case 0x3018:
Flags_SetInfTable(INFTABLE_F8); Flags_SetInfTable(INFTABLE_F8);
unkState = NPC_TALK_STATE_IDLE; talkState = NPC_TALK_STATE_IDLE;
break; break;
case 0x3036: case 0x3036:
func_8002F434(thisx, play, GI_TUNIC_GORON, xzRange, yRange); func_8002F434(thisx, play, GI_TUNIC_GORON, xzRange, yRange);
Flags_SetInfTable(INFTABLE_10D); // EnGo exclusive flag Flags_SetInfTable(INFTABLE_10D); // EnGo exclusive flag
unkState = NPC_TALK_STATE_ACTION; talkState = NPC_TALK_STATE_ACTION;
break; break;
case 0x3037: case 0x3037:
Flags_SetInfTable(INFTABLE_SPOKE_TO_GORON_LINK); Flags_SetInfTable(INFTABLE_SPOKE_TO_GORON_LINK);
unkState = NPC_TALK_STATE_IDLE; talkState = NPC_TALK_STATE_IDLE;
break; break;
case 0x3041: case 0x3041:
Flags_SetInfTable(INFTABLE_10F); Flags_SetInfTable(INFTABLE_10F);
unkState = NPC_TALK_STATE_IDLE; talkState = NPC_TALK_STATE_IDLE;
break; break;
case 0x3059: case 0x3059:
unkState = NPC_TALK_STATE_ACTION; talkState = NPC_TALK_STATE_ACTION;
break; break;
case 0x3052: case 0x3052:
case 0x3054: case 0x3054:
case 0x3055: case 0x3055:
case 0x305A: case 0x305A:
unkState = NPC_TALK_STATE_ACTION; talkState = NPC_TALK_STATE_ACTION;
break; break;
case 0x305E: case 0x305E:
unkState = NPC_TALK_STATE_ACTION; talkState = NPC_TALK_STATE_ACTION;
break; break;
default: default:
unkState = NPC_TALK_STATE_IDLE; talkState = NPC_TALK_STATE_IDLE;
break; break;
} }
break; break;
@ -272,7 +272,7 @@ s16 EnGo_UpdateTalkState(PlayState* play, Actor* thisx) {
thisx->textId = 0x300D; thisx->textId = 0x300D;
} }
Message_ContinueTextbox(play, thisx->textId); Message_ContinueTextbox(play, thisx->textId);
unkState = NPC_TALK_STATE_TALKING; talkState = NPC_TALK_STATE_TALKING;
break; break;
case 0x3034: case 0x3034:
if (play->msgCtx.choiceIndex == 0) { if (play->msgCtx.choiceIndex == 0) {
@ -287,16 +287,16 @@ s16 EnGo_UpdateTalkState(PlayState* play, Actor* thisx) {
thisx->textId = 0x3033; thisx->textId = 0x3033;
} }
Message_ContinueTextbox(play, thisx->textId); Message_ContinueTextbox(play, thisx->textId);
unkState = NPC_TALK_STATE_TALKING; talkState = NPC_TALK_STATE_TALKING;
break; break;
case 0x3054: case 0x3054:
case 0x3055: case 0x3055:
if (play->msgCtx.choiceIndex == 0) { if (play->msgCtx.choiceIndex == 0) {
unkState = NPC_TALK_STATE_ACTION; talkState = NPC_TALK_STATE_ACTION;
} else { } else {
thisx->textId = 0x3056; thisx->textId = 0x3056;
Message_ContinueTextbox(play, thisx->textId); Message_ContinueTextbox(play, thisx->textId);
unkState = NPC_TALK_STATE_TALKING; talkState = NPC_TALK_STATE_TALKING;
} }
Flags_SetInfTable(INFTABLE_B4); Flags_SetInfTable(INFTABLE_B4);
break; break;
@ -312,17 +312,17 @@ s16 EnGo_UpdateTalkState(PlayState* play, Actor* thisx) {
case 0x3033: case 0x3033:
thisx->textId = 0x3034; thisx->textId = 0x3034;
Message_ContinueTextbox(play, thisx->textId); Message_ContinueTextbox(play, thisx->textId);
unkState = NPC_TALK_STATE_TALKING; talkState = NPC_TALK_STATE_TALKING;
break; break;
default: default:
unkState = NPC_TALK_STATE_ACTION; talkState = NPC_TALK_STATE_ACTION;
break; break;
} }
} }
break; break;
case TEXT_STATE_DONE: case TEXT_STATE_DONE:
if (Message_ShouldAdvance(play)) { if (Message_ShouldAdvance(play)) {
unkState = NPC_TALK_STATE_ITEM_GIVEN; talkState = NPC_TALK_STATE_ITEM_GIVEN;
} }
break; break;
case TEXT_STATE_NONE: case TEXT_STATE_NONE:
@ -332,21 +332,21 @@ s16 EnGo_UpdateTalkState(PlayState* play, Actor* thisx) {
case TEXT_STATE_9: case TEXT_STATE_9:
break; break;
} }
return unkState; return talkState;
} }
s32 func_80A3ED24(PlayState* play, EnGo* this, NpcInteractInfo* interactInfo, f32 arg3, NpcGetTextIdFunc getTextId, s32 EnGo_UpdateTalking(PlayState* play, Actor* thisx, s16* talkState, f32 interactRange, NpcGetTextIdFunc getTextId,
NpcUpdateTalkStateFunc updateTalkState) { NpcUpdateTalkStateFunc updateTalkState) {
if (interactInfo->talkState != NPC_TALK_STATE_IDLE) { if (*talkState != NPC_TALK_STATE_IDLE) {
interactInfo->talkState = updateTalkState(play, &this->actor); *talkState = updateTalkState(play, thisx);
return false; return false;
} else if (Actor_ProcessTalkRequest(&this->actor, play)) { } else if (Actor_ProcessTalkRequest(thisx, play)) {
interactInfo->talkState = NPC_TALK_STATE_TALKING; *talkState = NPC_TALK_STATE_TALKING;
return true; return true;
} else if (!func_8002F2CC(&this->actor, play, arg3)) { } else if (!func_8002F2CC(thisx, play, interactRange)) {
return false; return false;
} else { } else {
this->actor.textId = getTextId(play, &this->actor); thisx->textId = getTextId(play, thisx);
return false; return false;
} }
} }
@ -379,7 +379,7 @@ s32 EnGo_IsActorSpawned(EnGo* this, PlayState* play) {
} }
} }
f32 EnGo_GetGoronSize(EnGo* this) { f32 EnGo_GetPlayerTrackingYOffset(EnGo* this) {
switch (this->actor.params & 0xF0) { switch (this->actor.params & 0xF0) {
case 0x00: case 0x00:
return 10.0f; return 10.0f;
@ -398,16 +398,16 @@ f32 EnGo_GetGoronSize(EnGo* this) {
void func_80A3F060(EnGo* this, PlayState* play) { void func_80A3F060(EnGo* this, PlayState* play) {
Player* player = GET_PLAYER(play); Player* player = GET_PLAYER(play);
s16 npcTrackingMode; s16 trackingMode;
if (this->actionFunc != EnGo_BiggoronActionFunc && this->actionFunc != EnGo_FireGenericActionFunc && if (this->actionFunc != EnGo_BiggoronActionFunc && this->actionFunc != EnGo_FireGenericActionFunc &&
this->actionFunc != func_80A40B1C) { this->actionFunc != func_80A40B1C) {
npcTrackingMode = NPC_TRACKING_NONE; trackingMode = NPC_TRACKING_NONE;
} }
this->interactInfo.trackPos = player->actor.world.pos; this->interactInfo.trackPos = player->actor.world.pos;
this->interactInfo.yOffset = EnGo_GetGoronSize(this); this->interactInfo.yOffset = EnGo_GetPlayerTrackingYOffset(this);
Npc_TrackPoint(&this->actor, &this->interactInfo, 4, npcTrackingMode); Npc_TrackPoint(&this->actor, &this->interactInfo, 4, trackingMode);
} }
void func_80A3F0E4(EnGo* this) { void func_80A3F0E4(EnGo* this) {
@ -421,23 +421,23 @@ void func_80A3F0E4(EnGo* this) {
} }
s32 EnGo_IsCameraModified(EnGo* this, PlayState* play) { s32 EnGo_IsCameraModified(EnGo* this, PlayState* play) {
f32 xyzDist; f32 xyzDistSq;
s16 yawDiff = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; s16 yawDiff = this->actor.yawTowardsPlayer - this->actor.shape.rot.y;
Camera* camera = play->cameraPtrs[MAIN_CAM]; Camera* mainCam = play->cameraPtrs[MAIN_CAM];
if (fabsf(yawDiff) > 10920.0f) { if (fabsf(yawDiff) > 10920.0f) {
return 0; return 0;
} }
xyzDist = (this->actor.scale.x / 0.01f) * 10000.0f; xyzDistSq = (this->actor.scale.x / 0.01f) * SQ(100.0f);
if ((this->actor.params & 0xF0) == 0x90) { if ((this->actor.params & 0xF0) == 0x90) {
Camera_ChangeSetting(camera, CAM_SET_DIRECTED_YAW); Camera_ChangeSetting(mainCam, CAM_SET_DIRECTED_YAW);
xyzDist *= 4.8f; xyzDistSq *= 4.8f;
} }
if (fabsf(this->actor.xyzDistToPlayerSq) > xyzDist) { if (fabsf(this->actor.xyzDistToPlayerSq) > xyzDistSq) {
if (camera->setting == CAM_SET_DIRECTED_YAW) { if (mainCam->setting == CAM_SET_DIRECTED_YAW) {
Camera_ChangeSetting(camera, CAM_SET_NORMAL0); Camera_ChangeSetting(mainCam, CAM_SET_NORMAL0);
} }
return 0; return 0;
} else { } else {
@ -480,7 +480,7 @@ s32 EnGo_FollowPath(EnGo* this, PlayState* play) {
pointPos += this->unk_218; pointPos += this->unk_218;
xDist = pointPos->x - this->actor.world.pos.x; xDist = pointPos->x - this->actor.world.pos.x;
zDist = pointPos->z - this->actor.world.pos.z; zDist = pointPos->z - this->actor.world.pos.z;
Math_SmoothStepToS(&this->actor.world.rot.y, (s16)(Math_FAtan2F(xDist, zDist) * ((f32)0x8000 / M_PI)), 10, 1000, 1); Math_SmoothStepToS(&this->actor.world.rot.y, RADF_TO_BINANG(Math_FAtan2F(xDist, zDist)), 10, 1000, 1);
if ((SQ(xDist) + SQ(zDist)) < 600.0f) { if ((SQ(xDist) + SQ(zDist)) < 600.0f) {
this->unk_218++; this->unk_218++;
@ -536,7 +536,7 @@ s32 EnGo_SpawnDust(EnGo* this, u8 initialTimer, f32 scale, f32 scaleStep, s32 nu
accel.z = (Rand_ZeroOne() - 0.5f) * xzAccel; accel.z = (Rand_ZeroOne() - 0.5f) * xzAccel;
pos.x = (Math_SinS(angle) * radius) + this->actor.world.pos.x; pos.x = (Math_SinS(angle) * radius) + this->actor.world.pos.x;
pos.z = (Math_CosS(angle) * radius) + this->actor.world.pos.z; pos.z = (Math_CosS(angle) * radius) + this->actor.world.pos.z;
EnGo_AddDust(this, &pos, &velocity, &accel, initialTimer, scale, scaleStep); EnGo_SpawnEffectDust(this, &pos, &velocity, &accel, initialTimer, scale, scaleStep);
angle += (s16)(0x10000 / numDustEffects); angle += (s16)(0x10000 / numDustEffects);
i--; i--;
} }
@ -549,7 +549,7 @@ s32 EnGo_IsRollingOnGround(EnGo* this, s16 unkArg1, f32 unkArg2) {
} else if (this->interactInfo.talkState != NPC_TALK_STATE_IDLE) { } else if (this->interactInfo.talkState != NPC_TALK_STATE_IDLE) {
return true; return true;
} else if (DECR(this->unk_21C)) { } else if (DECR(this->unk_21C)) {
if ((this->unk_21C & 1)) { if (this->unk_21C & 1) {
this->actor.world.pos.y += 1.5f; this->actor.world.pos.y += 1.5f;
} else { } else {
this->actor.world.pos.y -= 1.5f; this->actor.world.pos.y -= 1.5f;
@ -573,29 +573,29 @@ s32 EnGo_IsRollingOnGround(EnGo* this, s16 unkArg1, f32 unkArg2) {
void func_80A3F908(EnGo* this, PlayState* play) { void func_80A3F908(EnGo* this, PlayState* play) {
Player* player = GET_PLAYER(play); Player* player = GET_PLAYER(play);
f32 float1; f32 interactRange;
s32 isUnkCondition; s32 dialogStarted;
if (this->actionFunc == EnGo_BiggoronActionFunc || this->actionFunc == EnGo_GoronLinkRolling || if (this->actionFunc == EnGo_BiggoronActionFunc || this->actionFunc == EnGo_GoronLinkRolling ||
this->actionFunc == EnGo_FireGenericActionFunc || this->actionFunc == EnGo_Eyedrops || this->actionFunc == EnGo_FireGenericActionFunc || this->actionFunc == EnGo_Eyedrops ||
this->actionFunc == func_80A40DCC || this->actionFunc == EnGo_GetItem || this->actionFunc == func_80A40C78 || this->actionFunc == func_80A40DCC || this->actionFunc == EnGo_GetItem || this->actionFunc == func_80A40C78 ||
this->actionFunc == func_80A40B1C) { this->actionFunc == func_80A40B1C) {
float1 = (this->collider.dim.radius + 30.0f); interactRange = (this->collider.dim.radius + 30.0f);
float1 *= (this->actor.scale.x / 0.01f); interactRange *= (this->actor.scale.x / 0.01f);
if ((this->actor.params & 0xF0) == 0x90) { if ((this->actor.params & 0xF0) == 0x90) {
float1 *= 4.8f; interactRange *= 4.8f;
} }
if ((this->actor.params & 0xF0) == 0x90) { if ((this->actor.params & 0xF0) == 0x90) {
isUnkCondition = dialogStarted =
func_80A3ED24(play, this, &this->interactInfo, float1, EnGo_GetTextID, EnGo_UpdateTalkState); EnGo_UpdateTalking(play, &this->actor, &this->interactInfo.talkState, interactRange, EnGo_GetTextID, EnGo_UpdateTalkState);
} else { } else {
isUnkCondition = Npc_UpdateTalking(play, &this->actor, &this->interactInfo.talkState, float1, dialogStarted = Npc_UpdateTalking(play, &this->actor, &this->interactInfo.talkState, interactRange,
EnGo_GetTextID, EnGo_UpdateTalkState); EnGo_GetTextID, EnGo_UpdateTalkState);
} }
if (((this->actor.params & 0xF0) == 0x90) && (isUnkCondition == true)) { if (((this->actor.params & 0xF0) == 0x90) && (dialogStarted == true)) {
if (INV_CONTENT(ITEM_TRADE_ADULT) == ITEM_SWORD_BROKEN) { if (INV_CONTENT(ITEM_TRADE_ADULT) == ITEM_SWORD_BROKEN) {
if (func_8002F368(play) == EXCH_ITEM_SWORD_BROKEN) { if (func_8002F368(play) == EXCH_ITEM_SWORD_BROKEN) {
if (Flags_GetInfTable(INFTABLE_B4)) { if (Flags_GetInfTable(INFTABLE_B4)) {
@ -628,7 +628,7 @@ void EnGo_Init(Actor* thisx, PlayState* play) {
Vec3f D_80A41BA8 = { 0.0f, 0.0f, 0.0f }; // unused Vec3f D_80A41BA8 = { 0.0f, 0.0f, 0.0f }; // unused
ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 30.0f); ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 30.0f);
SkelAnime_InitFlex(play, &this->skelAnime, &gGoronSkel, NULL, 0, 0, 0); SkelAnime_InitFlex(play, &this->skelAnime, &gGoronSkel, NULL, NULL, NULL, 0);
Collider_InitCylinder(play, &this->collider); Collider_InitCylinder(play, &this->collider);
Collider_SetCylinder(play, &this->collider, &this->actor, &sCylinderInit); Collider_SetCylinder(play, &this->collider, &this->actor, &sCylinderInit);
CollisionCheck_SetInfo2(&this->actor.colChkInfo, DamageTable_Get(0x16), &sColChkInfoInit); CollisionCheck_SetInfo2(&this->actor.colChkInfo, DamageTable_Get(0x16), &sColChkInfoInit);
@ -711,8 +711,8 @@ void EnGo_StopRolling(EnGo* this, PlayState* play) {
EnBom* bomb; EnBom* bomb;
if (DECR(this->unk_20E) == 0) { if (DECR(this->unk_20E) == 0) {
if (this->collider.base.ocFlags2 & 1) { if (this->collider.base.ocFlags2 & OC2_HIT_PLAYER) {
this->collider.base.ocFlags2 &= ~1; this->collider.base.ocFlags2 &= ~OC2_HIT_PLAYER;
play->damagePlayer(play, -4); play->damagePlayer(play, -4);
func_8002F71C(play, &this->actor, 4.0f, this->actor.yawTowardsPlayer, 6.0f); func_8002F71C(play, &this->actor, 4.0f, this->actor.yawTowardsPlayer, 6.0f);
this->unk_20E = 0x10; this->unk_20E = 0x10;
@ -859,7 +859,7 @@ void func_80A405CC(EnGo* this, PlayState* play) {
void EnGo_BiggoronActionFunc(EnGo* this, PlayState* play) { void EnGo_BiggoronActionFunc(EnGo* this, PlayState* play) {
if (((this->actor.params & 0xF0) == 0x90) && (this->interactInfo.talkState == NPC_TALK_STATE_ACTION)) { if (((this->actor.params & 0xF0) == 0x90) && (this->interactInfo.talkState == NPC_TALK_STATE_ACTION)) {
if (!IS_RANDO && gSaveContext.bgsFlag) { if (gSaveContext.bgsFlag) {
this->interactInfo.talkState = NPC_TALK_STATE_IDLE; this->interactInfo.talkState = NPC_TALK_STATE_IDLE;
} else { } else {
if (INV_CONTENT(ITEM_TRADE_ADULT) == ITEM_EYEDROPS) { if (INV_CONTENT(ITEM_TRADE_ADULT) == ITEM_EYEDROPS) {
@ -958,45 +958,25 @@ void EnGo_GetItem(EnGo* this, PlayState* play) {
this->unk_20C = 0; this->unk_20C = 0;
if ((this->actor.params & 0xF0) == 0x90) { if ((this->actor.params & 0xF0) == 0x90) {
if (INV_CONTENT(ITEM_TRADE_ADULT) == ITEM_CLAIM_CHECK) { if (INV_CONTENT(ITEM_TRADE_ADULT) == ITEM_CLAIM_CHECK) {
if (!IS_RANDO) { getItemId = GI_SWORD_BGS;
getItemId = GI_SWORD_BGS;
} else {
getItemEntry = Randomizer_GetItemFromKnownCheck(RC_DMT_TRADE_CLAIM_CHECK, GI_SWORD_BGS);
getItemId = getItemEntry.getItemId;
}
this->unk_20C = 1; this->unk_20C = 1;
} }
if (INV_CONTENT(ITEM_TRADE_ADULT) == ITEM_EYEDROPS) { if (INV_CONTENT(ITEM_TRADE_ADULT) == ITEM_EYEDROPS) {
if (IS_RANDO) { getItemId = GI_CLAIM_CHECK;
getItemEntry = Randomizer_GetItemFromKnownCheck(RC_DMT_TRADE_EYEDROPS, GI_CLAIM_CHECK);
getItemId = getItemEntry.getItemId;
Randomizer_ConsumeAdultTradeItem(play, ITEM_EYEDROPS);
} else {
getItemId = GI_CLAIM_CHECK;
}
} }
if (INV_CONTENT(ITEM_TRADE_ADULT) == ITEM_SWORD_BROKEN) { if (INV_CONTENT(ITEM_TRADE_ADULT) == ITEM_SWORD_BROKEN) {
if (IS_RANDO) { getItemId = GI_PRESCRIPTION;
getItemEntry = Randomizer_GetItemFromKnownCheck(RC_DMT_TRADE_BROKEN_SWORD, GI_PRESCRIPTION);
Randomizer_ConsumeAdultTradeItem(play, ITEM_SWORD_BROKEN);
getItemId = getItemEntry.getItemId;
} else {
getItemId = GI_PRESCRIPTION;
}
} }
} }
if ((this->actor.params & 0xF0) == 0) { if ((this->actor.params & 0xF0) == 0) {
getItemId = GI_TUNIC_GORON; getItemId = GI_TUNIC_GORON;
Flags_SetRandomizerInf(RAND_INF_ROLLING_GORON_AS_ADULT);
} }
yDist = fabsf(this->actor.yDistToPlayer) + 1.0f; yDist = fabsf(this->actor.yDistToPlayer) + 1.0f;
xzDist = this->actor.xzDistToPlayer + 1.0f; xzDist = this->actor.xzDistToPlayer + 1.0f;
if (!IS_RANDO || getItemEntry.getItemId == GI_NONE) { func_8002F434(&this->actor, play, getItemId, xzDist, yDist);
func_8002F434(&this->actor, play, getItemId, xzDist, yDist);
} else {
GiveItemEntryFromActor(&this->actor, play, getItemEntry, xzDist, yDist);
}
} }
} }
@ -1105,32 +1085,30 @@ void EnGo_DrawRolling(EnGo* this, PlayState* play) {
s32 EnGo_OverrideLimbDraw(PlayState* play, s32 limb, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { s32 EnGo_OverrideLimbDraw(PlayState* play, s32 limb, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) {
EnGo* this = (EnGo*)thisx; EnGo* this = (EnGo*)thisx;
Vec3s vec1; Vec3s limbRot;
f32 float1; f32 float1;
if (limb == 17) { if (limb == 17) {
Matrix_Translate(2800.0f, 0.0f, 0.0f, MTXMODE_APPLY); Matrix_Translate(2800.0f, 0.0f, 0.0f, MTXMODE_APPLY);
vec1 = this->interactInfo.headRot; limbRot = this->interactInfo.headRot;
float1 = (vec1.y / (f32)0x8000) * M_PI; float1 = (limbRot.y / (f32)0x8000) * M_PI;
Matrix_RotateX(float1, MTXMODE_APPLY); Matrix_RotateX(float1, MTXMODE_APPLY);
float1 = (vec1.x / (f32)0x8000) * M_PI; float1 = (limbRot.x / (f32)0x8000) * M_PI;
Matrix_RotateZ(float1, MTXMODE_APPLY); Matrix_RotateZ(float1, MTXMODE_APPLY);
Matrix_Translate(-2800.0f, 0.0f, 0.0f, MTXMODE_APPLY); Matrix_Translate(-2800.0f, 0.0f, 0.0f, MTXMODE_APPLY);
} }
if (limb == 10) { if (limb == 10) {
vec1 = this->interactInfo.torsoRot; limbRot = this->interactInfo.torsoRot;
float1 = (vec1.y / (f32)0x8000) * M_PI; float1 = (limbRot.y / (f32)0x8000) * M_PI;
Matrix_RotateY(float1, MTXMODE_APPLY); Matrix_RotateY(float1, MTXMODE_APPLY);
float1 = (vec1.x / (f32)0x8000) * M_PI; float1 = (limbRot.x / (f32)0x8000) * M_PI;
Matrix_RotateX(float1, MTXMODE_APPLY); Matrix_RotateX(float1, MTXMODE_APPLY);
} }
if ((limb == 10) || (limb == 11) || (limb == 14)) { if ((limb == 10) || (limb == 11) || (limb == 14)) {
float1 = Math_SinS(this->jointTable[limb]); rot->y += Math_SinS(this->jointTable[limb]) * 200.0f;
rot->y += float1 * 200.0f; rot->z += Math_CosS(this->morphTable[limb]) * 200.0f;
float1 = Math_CosS(this->morphTable[limb]);
rot->z += float1 * 200.0f;
} }
return 0; return 0;
@ -1150,9 +1128,9 @@ void EnGo_Draw(Actor* thisx, PlayState* play) {
OPEN_DISPS(play->state.gfxCtx); OPEN_DISPS(play->state.gfxCtx);
EnGo_UpdateDust(this); EnGo_UpdateEffects(this);
Matrix_Push(); Matrix_Push();
EnGo_DrawDust(this, play); EnGo_DrawEffects(this, play);
Matrix_Pop(); Matrix_Pop();
if (this->actionFunc == EnGo_CurledUp) { if (this->actionFunc == EnGo_CurledUp) {
@ -1167,39 +1145,36 @@ void EnGo_Draw(Actor* thisx, PlayState* play) {
gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(gGoronCsMouthNeutralTex)); gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(gGoronCsMouthNeutralTex));
SkelAnime_DrawSkeletonOpa(play, &this->skelAnime, EnGo_OverrideLimbDraw, EnGo_PostLimbDraw, &this->actor); SkelAnime_DrawSkeletonOpa(play, &this->skelAnime, EnGo_OverrideLimbDraw, EnGo_PostLimbDraw, &this->actor);
EnGo_DrawDust(this, play); EnGo_DrawEffects(this, play);
} }
CLOSE_DISPS(play->state.gfxCtx); CLOSE_DISPS(play->state.gfxCtx);
} }
void EnGo_AddDust(EnGo* this, Vec3f* pos, Vec3f* velocity, Vec3f* accel, u8 initialTimer, f32 scale, f32 scaleStep) { void EnGo_SpawnEffectDust(EnGo* this, Vec3f* pos, Vec3f* velocity, Vec3f* accel, u8 initialTimer, f32 scale, f32 scaleStep) {
EnGoEffect* dustEffect = this->dustEffects; EnGoEffect* dustEffect = this->effects;
s16 i; s16 i;
s16 timer;
for (i = 0; i < ARRAY_COUNT(this->dustEffects); i++, dustEffect++) { for (i = 0; i < EN_GO_EFFECT_COUNT; i++, dustEffect++) {
if (dustEffect->type != 1) { if (dustEffect->type != 1) {
dustEffect->epoch++; dustEffect->epoch++;
dustEffect->scale = scale; dustEffect->scale = scale;
dustEffect->scaleStep = scaleStep; dustEffect->scaleStep = scaleStep;
timer = initialTimer; dustEffect->initialTimer = dustEffect->timer = initialTimer;
dustEffect->timer = timer;
dustEffect->type = 1; dustEffect->type = 1;
dustEffect->initialTimer = initialTimer;
dustEffect->pos = *pos; dustEffect->pos = *pos;
dustEffect->accel = *accel; dustEffect->accel = *accel;
dustEffect->velocity = *velocity; dustEffect->velocity = *velocity;
return; break;
} }
} }
} }
void EnGo_UpdateDust(EnGo* this) { void EnGo_UpdateEffects(EnGo* this) {
EnGoEffect* dustEffect = this->dustEffects; EnGoEffect* dustEffect = this->effects;
f32 randomNumber; f32 randomNumber;
s16 i; s16 i;
for (i = 0; i < ARRAY_COUNT(this->dustEffects); i++, dustEffect++) { for (i = 0; i < EN_GO_EFFECT_COUNT; i++, dustEffect++) {
if (dustEffect->type) { if (dustEffect->type) {
dustEffect->timer--; dustEffect->timer--;
if (dustEffect->timer == 0) { if (dustEffect->timer == 0) {
@ -1220,24 +1195,24 @@ void EnGo_UpdateDust(EnGo* this) {
} }
} }
void EnGo_DrawDust(EnGo* this, PlayState* play) { void EnGo_DrawEffects(EnGo* this, PlayState* play) {
static void* dustTex[] = { gDust8Tex, gDust7Tex, gDust6Tex, gDust5Tex, gDust4Tex, gDust3Tex, gDust2Tex, gDust1Tex }; static void* dustTex[] = { gDust8Tex, gDust7Tex, gDust6Tex, gDust5Tex, gDust4Tex, gDust3Tex, gDust2Tex, gDust1Tex };
EnGoEffect* dustEffect = this->dustEffects; EnGoEffect* dustEffect = this->effects;
s16 alpha; s16 alpha;
s16 firstDone; s16 materialFlag;
s16 index; s16 index;
s16 i; s16 i;
OPEN_DISPS(play->state.gfxCtx); OPEN_DISPS(play->state.gfxCtx);
firstDone = false; materialFlag = false;
Gfx_SetupDL_25Xlu(play->state.gfxCtx); Gfx_SetupDL_25Xlu(play->state.gfxCtx);
for (i = 0; i < ARRAY_COUNT(this->dustEffects); i++, dustEffect++) { for (i = 0; i < EN_GO_EFFECT_COUNT; i++, dustEffect++) {
if (dustEffect->type) { if (dustEffect->type) {
if (!firstDone) { if (!materialFlag) {
POLY_XLU_DISP = Gfx_SetupDL(POLY_XLU_DISP, 0); POLY_XLU_DISP = Gfx_SetupDL(POLY_XLU_DISP, 0);
gSPDisplayList(POLY_XLU_DISP++, gGoronDL_00FD40); gSPDisplayList(POLY_XLU_DISP++, gGoronDL_00FD40);
gDPSetEnvColor(POLY_XLU_DISP++, 100, 60, 20, 0); gDPSetEnvColor(POLY_XLU_DISP++, 100, 60, 20, 0);
firstDone = true; materialFlag = true;
} }
FrameInterpolation_RecordOpenChild(dustEffect, dustEffect->epoch); FrameInterpolation_RecordOpenChild(dustEffect, dustEffect->epoch);

View File

@ -16,12 +16,13 @@ typedef s16 (*callback2_80A3ED24)(PlayState*, struct EnGo*);
// /* 0x20 */ GORON1_DMT_DC_ENTRANCE, // /* 0x20 */ GORON1_DMT_DC_ENTRANCE,
// /* 0x30 */ GORON1_DMT_ROLLING_SMALL, // /* 0x30 */ GORON1_DMT_ROLLING_SMALL,
// /* 0x40 */ GORON1_DMT_BOMB_FLOWER, // /* 0x40 */ GORON1_DMT_BOMB_FLOWER,
// /* 0x50 */ GORON1_CITY_ENTRANCE, // /* 0x50 */ GORON1_CITY_ENTRANCE,
// /* 0x60 */ GORON1_CITY_ISLAND, // /* 0x60 */ GORON1_CITY_ISLAND,
// /* 0x70 */ GORON1_CITY_LOST_WOODS, // /* 0x70 */ GORON1_CITY_LOST_WOODS,
// /* 0x80 */ // Not Used // /* 0x80 */ // Not Used
// /* 0x90 */ GORON1_DMT_BIGGORON, // /* 0x90 */ GORON1_DMT_BIGGORON,
#define EN_GO_EFFECT_COUNT 20
typedef struct { typedef struct {
/* 0x0000 */ u8 type; /* 0x0000 */ u8 type;
@ -56,7 +57,7 @@ typedef struct EnGo {
/* 0x021E */ s16 unk_21E; /* 0x021E */ s16 unk_21E;
/* 0x0220 */ s16 jointTable[18]; /* 0x0220 */ s16 jointTable[18];
/* 0x0244 */ s16 morphTable[18]; /* 0x0244 */ s16 morphTable[18];
/* 0x0268 */ EnGoEffect dustEffects[20]; /* 0x0268 */ EnGoEffect effects[EN_GO_EFFECT_COUNT];
} EnGo; // size = 0x06C8 } EnGo; // size = 0x06C8
#endif #endif

View File

@ -3,7 +3,7 @@
#include "objects/gameplay_keep/gameplay_keep.h" #include "objects/gameplay_keep/gameplay_keep.h"
#include "objects/object_oF1d_map/object_oF1d_map.h" #include "objects/object_oF1d_map/object_oF1d_map.h"
#include "soh/frame_interpolation.h" #include "soh/frame_interpolation.h"
#include "soh/Enhancements/randomizer/adult_trade_shuffle.h" #include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY | ACTOR_FLAG_UPDATE_WHILE_CULLED | ACTOR_FLAG_DRAW_WHILE_CULLED) #define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY | ACTOR_FLAG_UPDATE_WHILE_CULLED | ACTOR_FLAG_DRAW_WHILE_CULLED)
@ -335,7 +335,8 @@ u16 EnGo2_GoronFireGenericGetTextId(EnGo2* this) {
u16 EnGo2_GetTextIdGoronCityRollingBig(PlayState* play, EnGo2* this) { u16 EnGo2_GetTextIdGoronCityRollingBig(PlayState* play, EnGo2* this) {
if (Flags_GetInfTable(INFTABLE_11E)) { if (Flags_GetInfTable(INFTABLE_11E)) {
return 0x3013; return 0x3013;
} else if ((CUR_CAPACITY(UPG_BOMB_BAG) >= 20 || IS_RANDO) && this->waypoint > 7 && this->waypoint < 12) { } else if (GameInteractor_Should(GI_VB_BE_ELIGIBLE_FOR_CHILD_ROLLING_GORON_REWARD, CUR_CAPACITY(UPG_BOMB_BAG) >= 2, this)
&& this->waypoint > 7 && this->waypoint < 12) {
return 0x3012; return 0x3012;
} else { } else {
return 0x3011; return 0x3011;
@ -350,13 +351,14 @@ s16 EnGo2_UpdateTalkStateGoronCityRollingBig(PlayState* play, EnGo2* this) {
if (Message_ShouldAdvance(play)) { if (Message_ShouldAdvance(play)) {
if (this->actor.textId == 0x3012) { if (this->actor.textId == 0x3012) {
this->actionFunc = EnGo2_SetupGetItem; this->actionFunc = EnGo2_SetupGetItem;
if(!IS_RANDO) { if(GameInteractor_Should(GI_VB_GIVE_ITEM_FROM_ROLLING_GORON_AS_CHILD, true, this)) {
EnGo2_GetItem(this, play, CUR_CAPACITY(UPG_BOMB_BAG) == 30 ? GI_BOMB_BAG_40 : GI_BOMB_BAG_30); EnGo2_GetItem(this, play, CUR_CAPACITY(UPG_BOMB_BAG) == 30 ? GI_BOMB_BAG_40 : GI_BOMB_BAG_30);
} else { } else {
EnGo2_GetItemEntry(this, play, Randomizer_GetItemFromKnownCheck(RC_GC_ROLLING_GORON_AS_CHILD, GI_BOMB_BAG_40)); this->actionFunc = EnGo2_SetGetItem;
} }
Message_CloseTextbox(play); Message_CloseTextbox(play);
Flags_SetInfTable(INFTABLE_11E); Flags_SetInfTable(INFTABLE_11E);
Flags_SetRandomizerInf(RAND_INF_ROLLING_GORON_AS_CHILD);
return NPC_TALK_STATE_ACTION; return NPC_TALK_STATE_ACTION;
} else { } else {
return NPC_TALK_STATE_ACTION; return NPC_TALK_STATE_ACTION;
@ -416,11 +418,9 @@ s16 EnGo2_UpdateTalkStateGoronDmtRollingSmall(PlayState* play, EnGo2* this) {
} }
u16 EnGo2_GetTextIdGoronDmtDcEntrance(PlayState* play, EnGo2* this) { u16 EnGo2_GetTextIdGoronDmtDcEntrance(PlayState* play, EnGo2* this) {
if (((!IS_RANDO && CHECK_QUEST_ITEM(QUEST_MEDALLION_FIRE)) || if (GameInteractor_Should(GI_VB_GORONS_CONSIDER_FIRE_TEMPLE_FINISHED, CHECK_QUEST_ITEM(QUEST_MEDALLION_FIRE), NULL) && LINK_IS_ADULT) {
(IS_RANDO && Flags_GetRandomizerInf(RAND_INF_DUNGEONS_DONE_FIRE_TEMPLE))) && LINK_IS_ADULT) {
return 0x3043; return 0x3043;
} else if ((!IS_RANDO && CHECK_QUEST_ITEM(QUEST_GORON_RUBY)) || } else if (GameInteractor_Should(GI_VB_GORONS_CONSIDER_DODONGOS_CAVERN_FINISHED, CHECK_QUEST_ITEM(QUEST_GORON_RUBY), NULL)) {
(IS_RANDO && Flags_GetRandomizerInf(RAND_INF_DUNGEONS_DONE_DODONGOS_CAVERN))) {
return 0x3027; return 0x3027;
} else { } else {
return Flags_GetEventChkInf(EVENTCHKINF_BOMBED_DODONGOS_CAVERN_ENTRANCE) ? 0x3021 : Flags_GetInfTable(INFTABLE_E0) ? 0x302A : 0x3008; return Flags_GetEventChkInf(EVENTCHKINF_BOMBED_DODONGOS_CAVERN_ENTRANCE) ? 0x3021 : Flags_GetInfTable(INFTABLE_E0) ? 0x302A : 0x3008;
@ -439,11 +439,9 @@ s16 EnGo2_UpdateTalkStateGoronDmtDcEntrance(PlayState* play, EnGo2* this) {
} }
u16 EnGo2_GetTextIdGoronCityEntrance(PlayState* play, EnGo2* this) { u16 EnGo2_GetTextIdGoronCityEntrance(PlayState* play, EnGo2* this) {
if (((!IS_RANDO && CHECK_QUEST_ITEM(QUEST_MEDALLION_FIRE)) || if (GameInteractor_Should(GI_VB_GORONS_CONSIDER_FIRE_TEMPLE_FINISHED, CHECK_QUEST_ITEM(QUEST_MEDALLION_FIRE), NULL) && LINK_IS_ADULT) {
(IS_RANDO && Flags_GetRandomizerInf(RAND_INF_DUNGEONS_DONE_FIRE_TEMPLE))) && LINK_IS_ADULT) {
return 0x3043; return 0x3043;
} else if ((!IS_RANDO && CHECK_QUEST_ITEM(QUEST_GORON_RUBY)) || } else if (GameInteractor_Should(GI_VB_GORONS_CONSIDER_DODONGOS_CAVERN_FINISHED, CHECK_QUEST_ITEM(QUEST_GORON_RUBY), NULL)) {
(IS_RANDO && Flags_GetRandomizerInf(RAND_INF_DUNGEONS_DONE_DODONGOS_CAVERN))) {
return 0x3027; return 0x3027;
} else { } else {
return Flags_GetInfTable(INFTABLE_F0) ? 0x3015 : 0x3014; return Flags_GetInfTable(INFTABLE_F0) ? 0x3015 : 0x3014;
@ -462,11 +460,9 @@ s16 EnGo2_UpdateTalkStateGoronCityEntrance(PlayState* play, EnGo2* this) {
} }
u16 EnGo2_GetTextIdGoronCityIsland(PlayState* play, EnGo2* this) { u16 EnGo2_GetTextIdGoronCityIsland(PlayState* play, EnGo2* this) {
if (((!IS_RANDO && CHECK_QUEST_ITEM(QUEST_MEDALLION_FIRE)) || if (GameInteractor_Should(GI_VB_GORONS_CONSIDER_FIRE_TEMPLE_FINISHED, CHECK_QUEST_ITEM(QUEST_MEDALLION_FIRE), NULL) && LINK_IS_ADULT) {
(IS_RANDO && Flags_GetRandomizerInf(RAND_INF_DUNGEONS_DONE_FIRE_TEMPLE))) && LINK_IS_ADULT) {
return 0x3043; return 0x3043;
} else if ((!IS_RANDO && CHECK_QUEST_ITEM(QUEST_GORON_RUBY)) || } else if (GameInteractor_Should(GI_VB_GORONS_CONSIDER_DODONGOS_CAVERN_FINISHED, CHECK_QUEST_ITEM(QUEST_GORON_RUBY), NULL)) {
(IS_RANDO && Flags_GetRandomizerInf(RAND_INF_DUNGEONS_DONE_DODONGOS_CAVERN))) {
return 0x3027; return 0x3027;
} else { } else {
return Flags_GetInfTable(INFTABLE_F4) ? 0x3017 : 0x3016; return Flags_GetInfTable(INFTABLE_F4) ? 0x3017 : 0x3016;
@ -485,11 +481,9 @@ s16 EnGo2_UpdateTalkStateGoronCityIsland(PlayState* play, EnGo2* this) {
} }
u16 EnGo2_GetTextIdGoronCityLowestFloor(PlayState* play, EnGo2* this) { u16 EnGo2_GetTextIdGoronCityLowestFloor(PlayState* play, EnGo2* this) {
if (((!IS_RANDO && CHECK_QUEST_ITEM(QUEST_MEDALLION_FIRE)) || if (GameInteractor_Should(GI_VB_GORONS_CONSIDER_FIRE_TEMPLE_FINISHED, CHECK_QUEST_ITEM(QUEST_MEDALLION_FIRE), NULL) && LINK_IS_ADULT) {
(IS_RANDO && Flags_GetRandomizerInf(RAND_INF_DUNGEONS_DONE_FIRE_TEMPLE))) && LINK_IS_ADULT) {
return 0x3043; return 0x3043;
} else if ((!IS_RANDO && CHECK_QUEST_ITEM(QUEST_GORON_RUBY)) || } else if (GameInteractor_Should(GI_VB_GORONS_CONSIDER_DODONGOS_CAVERN_FINISHED, CHECK_QUEST_ITEM(QUEST_GORON_RUBY), NULL)) {
(IS_RANDO && Flags_GetRandomizerInf(RAND_INF_DUNGEONS_DONE_DODONGOS_CAVERN))) {
return 0x3027; return 0x3027;
} else { } else {
return CUR_UPG_VALUE(UPG_STRENGTH) != 0 ? 0x302C return CUR_UPG_VALUE(UPG_STRENGTH) != 0 ? 0x302C
@ -511,18 +505,10 @@ s16 EnGo2_UpdateTalkStateGoronCityLowestFloor(PlayState* play, EnGo2* this) {
} }
u16 EnGo2_GetTextIdGoronCityLink(PlayState* play, EnGo2* this) { u16 EnGo2_GetTextIdGoronCityLink(PlayState* play, EnGo2* this) {
// For rando, prioritize opening the doors in GC when Link the goron has been stopped when // In case a hook neglects to set the override, fall back to the first dialogue
// the doors are not opened, otherwise let him talk about the DMC exit or that gorons are saved u16 overrideTextId = 0x3030;
if (IS_RANDO) { if (GameInteractor_Should(GI_VB_OVERRIDE_LINK_THE_GORON_DIALOGUE, false, &overrideTextId)) {
if (!Flags_GetInfTable(INFTABLE_STOPPED_GORON_LINKS_ROLLING)) { return overrideTextId;
return 0x3030;
} else if (!Flags_GetInfTable(INFTABLE_GORON_CITY_DOORS_UNLOCKED)) {
return 0x3036;
} else if (Flags_GetRandomizerInf(RAND_INF_DUNGEONS_DONE_FIRE_TEMPLE)) {
return 0x3041;
} else {
return Flags_GetInfTable(INFTABLE_SPOKE_TO_GORON_LINK) ? 0x3038 : 0x3037;
}
} }
if (CHECK_QUEST_ITEM(QUEST_MEDALLION_FIRE)) { if (CHECK_QUEST_ITEM(QUEST_MEDALLION_FIRE)) {
@ -543,20 +529,15 @@ s16 EnGo2_UpdateTalkStateGoronCityLink(PlayState* play, EnGo2* this) {
case TEXT_STATE_CLOSING: case TEXT_STATE_CLOSING:
switch (this->actor.textId) { switch (this->actor.textId) {
case 0x3036: case 0x3036:
if (!IS_RANDO) { Flags_SetRandomizerInf(RAND_INF_ROLLING_GORON_AS_ADULT);
this->interactInfo.talkState = NPC_TALK_STATE_IDLE;
if (GameInteractor_Should(GI_VB_GIVE_ITEM_FROM_ROLLING_GORON_AS_ADULT, true, this)) {
EnGo2_GetItem(this, play, GI_TUNIC_GORON); EnGo2_GetItem(this, play, GI_TUNIC_GORON);
this->actionFunc = EnGo2_SetupGetItem; this->actionFunc = EnGo2_SetupGetItem;
return NPC_TALK_STATE_ACTION; return NPC_TALK_STATE_ACTION;
} else { } else {
if (Flags_GetTreasure(play, 0x1F)) { this->actionFunc = EnGo2_SetGetItem;
return NPC_TALK_STATE_IDLE; return this->interactInfo.talkState;
}
Flags_SetInfTable(INFTABLE_GORON_CITY_DOORS_UNLOCKED);
EnGo2_GetItemEntry(this, play, Randomizer_GetItemFromKnownCheck(RC_GC_ROLLING_GORON_AS_ADULT, GI_TUNIC_GORON));
this->actionFunc = EnGo2_SetupGetItem;
Flags_SetTreasure(play, 0x1F);
return NPC_TALK_STATE_ACTION;
} }
case 0x3037: case 0x3037:
Flags_SetInfTable(INFTABLE_SPOKE_TO_GORON_LINK); Flags_SetInfTable(INFTABLE_SPOKE_TO_GORON_LINK);
@ -605,7 +586,7 @@ s16 EnGo2_UpdateTalkStateGoronCityLink(PlayState* play, EnGo2* this) {
u16 EnGo2_GetTextIdGoronDmtBiggoron(PlayState* play, EnGo2* this) { u16 EnGo2_GetTextIdGoronDmtBiggoron(PlayState* play, EnGo2* this) {
Player* player = GET_PLAYER(play); Player* player = GET_PLAYER(play);
if (!IS_RANDO && gSaveContext.bgsFlag) { if (GameInteractor_Should(GI_VB_BIGGORON_CONSIDER_TRADE_COMPLETE, gSaveContext.bgsFlag, NULL)) {
player->exchangeItemId = EXCH_ITEM_CLAIM_CHECK; player->exchangeItemId = EXCH_ITEM_CLAIM_CHECK;
return 0x305E; return 0x305E;
} else if (INV_CONTENT(ITEM_TRADE_ADULT) >= ITEM_CLAIM_CHECK) { } else if (INV_CONTENT(ITEM_TRADE_ADULT) >= ITEM_CLAIM_CHECK) {
@ -627,17 +608,17 @@ s16 EnGo2_UpdateTalkStateGoronDmtBiggoron(PlayState* play, EnGo2* this) {
switch (EnGo2_GetDialogState(this, play)) { switch (EnGo2_GetDialogState(this, play)) {
case TEXT_STATE_DONE: case TEXT_STATE_DONE:
if (this->actor.textId == 0x305E) { if (this->actor.textId == 0x305E) {
if((!IS_RANDO && gSaveContext.bgsFlag) || (IS_RANDO && Flags_GetTreasure(play, 0x1F))) { if (GameInteractor_Should(GI_VB_BIGGORON_CONSIDER_SWORD_COLLECTED, gSaveContext.bgsFlag, NULL)) {
return NPC_TALK_STATE_IDLE; return NPC_TALK_STATE_IDLE;
} }
Flags_SetRandomizerInf(RAND_INF_ADULT_TRADES_DMT_TRADE_CLAIM_CHECK);
if(IS_RANDO) { if (GameInteractor_Should(GI_VB_TRADE_CLAIM_CHECK, true, this)) {
EnGo2_GetItemEntry(this, play, Randomizer_GetItemFromKnownCheck(RC_DMT_TRADE_CLAIM_CHECK, GI_SWORD_BGS));
Flags_SetTreasure(play, 0x1F);
} else {
EnGo2_GetItem(this, play, GI_SWORD_BGS); EnGo2_GetItem(this, play, GI_SWORD_BGS);
this->actionFunc = EnGo2_SetupGetItem;
} else {
this->actionFunc = EnGo2_SetGetItem;
} }
this->actionFunc = EnGo2_SetupGetItem;
return NPC_TALK_STATE_ACTION; return NPC_TALK_STATE_ACTION;
} else { } else {
return NPC_TALK_STATE_IDLE; return NPC_TALK_STATE_IDLE;
@ -663,16 +644,13 @@ s16 EnGo2_UpdateTalkStateGoronDmtBiggoron(PlayState* play, EnGo2* this) {
if (Message_ShouldAdvance(play)) { if (Message_ShouldAdvance(play)) {
if ((this->actor.textId == 0x3054) || (this->actor.textId == 0x3055)) { if ((this->actor.textId == 0x3054) || (this->actor.textId == 0x3055)) {
if (play->msgCtx.choiceIndex == 0) { if (play->msgCtx.choiceIndex == 0) {
if (IS_RANDO) { Flags_SetRandomizerInf(RAND_INF_ADULT_TRADES_DMT_TRADE_BROKEN_SWORD);
GetItemEntry getItemEntry = Randomizer_GetItemFromKnownCheck(RC_DMT_TRADE_BROKEN_SWORD, GI_PRESCRIPTION); if (GameInteractor_Should(GI_VB_TRADE_BROKEN_SWORD, true, this)) {
Randomizer_ConsumeAdultTradeItem(play, ITEM_SWORD_BROKEN); EnGo2_GetItem(this, play, GI_PRESCRIPTION);
EnGo2_GetItemEntry(this, play, getItemEntry); this->actionFunc = EnGo2_SetupGetItem;
Flags_SetRandomizerInf(RAND_INF_ADULT_TRADES_DMT_TRADE_BROKEN_SWORD);
} else { } else {
u32 getItemId = GI_PRESCRIPTION; this->actionFunc = EnGo2_SetGetItem;
EnGo2_GetItem(this, play, getItemId);
} }
this->actionFunc = EnGo2_SetupGetItem;
return NPC_TALK_STATE_ACTION; return NPC_TALK_STATE_ACTION;
} }
this->actor.textId = 0x3056; this->actor.textId = 0x3056;
@ -1070,7 +1048,7 @@ void EnGo2_BiggoronSetTextId(EnGo2* this, PlayState* play, Player* player) {
u16 textId; u16 textId;
if ((this->actor.params & 0x1F) == GORON_DMT_BIGGORON) { if ((this->actor.params & 0x1F) == GORON_DMT_BIGGORON) {
if ((!IS_RANDO && gSaveContext.bgsFlag)) { if (GameInteractor_Should(GI_VB_BIGGORON_CONSIDER_TRADE_COMPLETE, gSaveContext.bgsFlag, NULL)) {
if (func_8002F368(play) == EXCH_ITEM_CLAIM_CHECK) { if (func_8002F368(play) == EXCH_ITEM_CLAIM_CHECK) {
this->actor.textId = 0x3003; this->actor.textId = 0x3003;
} else { } else {
@ -1080,18 +1058,14 @@ void EnGo2_BiggoronSetTextId(EnGo2* this, PlayState* play, Player* player) {
} else if (INV_CONTENT(ITEM_TRADE_ADULT) == ITEM_CLAIM_CHECK) { } else if (INV_CONTENT(ITEM_TRADE_ADULT) == ITEM_CLAIM_CHECK) {
if (func_8002F368(play) == EXCH_ITEM_CLAIM_CHECK) { if (func_8002F368(play) == EXCH_ITEM_CLAIM_CHECK) {
if (IS_RANDO && Flags_GetTreasure(play, 0x1F)) { if (GameInteractor_Should(GI_VB_BIGGORON_CONSIDER_SWORD_FORGED, Environment_GetBgsDayCount() >= 3, NULL)) {
textId = 0x3003;
} else if (Environment_GetBgsDayCount() >= CVarGetInteger("gForgeTime", 3)) {
textId = 0x305E; textId = 0x305E;
} else { } else {
textId = 0x305D; textId = 0x305D;
} }
this->actor.textId = textId; this->actor.textId = textId;
} else { } else {
if (IS_RANDO && Flags_GetTreasure(play, 0x1F)) { if (GameInteractor_Should(GI_VB_BIGGORON_CONSIDER_SWORD_FORGED, Environment_GetBgsDayCount() >= 3, NULL)) {
textId = 0x305E;
} else if (Environment_GetBgsDayCount() >= CVarGetInteger("gForgeTime", 3)) {
textId = 0x3002; textId = 0x3002;
} else { } else {
textId = 0x305D; textId = 0x305D;
@ -1219,8 +1193,7 @@ s32 EnGo2_IsCameraModified(EnGo2* this, PlayState* play) {
(this->actor.params & 0x1F) == GORON_CITY_STAIRWELL || (this->actor.params & 0x1F) == GORON_DMT_BIGGORON || (this->actor.params & 0x1F) == GORON_CITY_STAIRWELL || (this->actor.params & 0x1F) == GORON_DMT_BIGGORON ||
(this->actor.params & 0x1F) == GORON_MARKET_BAZAAR) { (this->actor.params & 0x1F) == GORON_MARKET_BAZAAR) {
return true; return true;
} else if (((!IS_RANDO && !CHECK_QUEST_ITEM(QUEST_MEDALLION_FIRE)) || } else if (GameInteractor_Should(GI_VB_GORONS_CONSIDER_FIRE_TEMPLE_FINISHED, CHECK_QUEST_ITEM(QUEST_MEDALLION_FIRE), NULL) &&
(IS_RANDO && !Flags_GetRandomizerInf(RAND_INF_DUNGEONS_DONE_FIRE_TEMPLE))) &&
CHECK_OWNED_EQUIP(EQUIP_TYPE_TUNIC, EQUIP_INV_TUNIC_GORON)) { CHECK_OWNED_EQUIP(EQUIP_TYPE_TUNIC, EQUIP_INV_TUNIC_GORON)) {
return true; return true;
} else { } else {
@ -1278,8 +1251,7 @@ void EnGo2_SelectGoronWakingUp(EnGo2* this) {
EnGo2_BiggoronWakingUp(this); EnGo2_BiggoronWakingUp(this);
break; break;
case GORON_CITY_LINK: case GORON_CITY_LINK:
if (((!IS_RANDO && !CHECK_QUEST_ITEM(QUEST_MEDALLION_FIRE)) || if (GameInteractor_Should(GI_VB_GORONS_CONSIDER_FIRE_TEMPLE_FINISHED, CHECK_QUEST_ITEM(QUEST_MEDALLION_FIRE), NULL) &&
(IS_RANDO && !Flags_GetRandomizerInf(RAND_INF_DUNGEONS_DONE_FIRE_TEMPLE))) &&
CHECK_OWNED_EQUIP(EQUIP_TYPE_TUNIC, EQUIP_INV_TUNIC_GORON)) { CHECK_OWNED_EQUIP(EQUIP_TYPE_TUNIC, EQUIP_INV_TUNIC_GORON)) {
EnGo2_WakingUp(this); EnGo2_WakingUp(this);
break; break;
@ -1624,8 +1596,7 @@ void EnGo2_Init(Actor* thisx, PlayState* play) {
case GORON_CITY_LOWEST_FLOOR: case GORON_CITY_LOWEST_FLOOR:
case GORON_CITY_STAIRWELL: case GORON_CITY_STAIRWELL:
case GORON_CITY_LOST_WOODS: case GORON_CITY_LOST_WOODS:
if (((!IS_RANDO && !CHECK_QUEST_ITEM(QUEST_MEDALLION_FIRE)) || if (GameInteractor_Should(GI_VB_GORONS_CONSIDER_FIRE_TEMPLE_FINISHED, CHECK_QUEST_ITEM(QUEST_MEDALLION_FIRE), NULL) && LINK_IS_ADULT) {
(IS_RANDO && !Flags_GetRandomizerInf(RAND_INF_DUNGEONS_DONE_FIRE_TEMPLE))) && LINK_IS_ADULT) {
Actor_Kill(&this->actor); Actor_Kill(&this->actor);
} }
this->actionFunc = EnGo2_CurledUp; this->actionFunc = EnGo2_CurledUp;
@ -1640,8 +1611,7 @@ void EnGo2_Init(Actor* thisx, PlayState* play) {
if ((Flags_GetInfTable(INFTABLE_GORON_CITY_DOORS_UNLOCKED))) { if ((Flags_GetInfTable(INFTABLE_GORON_CITY_DOORS_UNLOCKED))) {
Path_CopyLastPoint(this->path, &this->actor.world.pos); Path_CopyLastPoint(this->path, &this->actor.world.pos);
this->actor.home.pos = this->actor.world.pos; this->actor.home.pos = this->actor.world.pos;
if (((!IS_RANDO && !CHECK_QUEST_ITEM(QUEST_MEDALLION_FIRE)) || if (GameInteractor_Should(GI_VB_GORONS_CONSIDER_FIRE_TEMPLE_FINISHED, CHECK_QUEST_ITEM(QUEST_MEDALLION_FIRE), NULL) &&
(IS_RANDO && !Flags_GetRandomizerInf(RAND_INF_DUNGEONS_DONE_FIRE_TEMPLE))) &&
CHECK_OWNED_EQUIP(EQUIP_TYPE_TUNIC, EQUIP_INV_TUNIC_GORON)) { CHECK_OWNED_EQUIP(EQUIP_TYPE_TUNIC, EQUIP_INV_TUNIC_GORON)) {
EnGo2_GetItemAnimation(this, play); EnGo2_GetItemAnimation(this, play);
} else { } else {
@ -1862,11 +1832,7 @@ void EnGo2_SetupGetItem(EnGo2* this, PlayState* play) {
this->actor.parent = NULL; this->actor.parent = NULL;
this->actionFunc = EnGo2_SetGetItem; this->actionFunc = EnGo2_SetGetItem;
} else { } else {
if (!IS_RANDO || this->getItemEntry.getItemId == GI_NONE) { func_8002F434(&this->actor, play, this->getItemId, this->actor.xzDistToPlayer + 1.0f, fabsf(this->actor.yDistToPlayer) + 1.0f);
func_8002F434(&this->actor, play, this->getItemId, this->actor.xzDistToPlayer + 1.0f, fabsf(this->actor.yDistToPlayer) + 1.0f);
} else {
GiveItemEntryFromActor(&this->actor, play, this->getItemEntry, this->actor.xzDistToPlayer + 1.0f, fabsf(this->actor.yDistToPlayer) + 1.0f);
}
} }
} }
@ -1874,47 +1840,27 @@ void EnGo2_SetGetItem(EnGo2* this, PlayState* play) {
if ((Message_GetState(&play->msgCtx) == TEXT_STATE_DONE) && Message_ShouldAdvance(play)) { if ((Message_GetState(&play->msgCtx) == TEXT_STATE_DONE) && Message_ShouldAdvance(play)) {
this->interactInfo.talkState = NPC_TALK_STATE_IDLE; this->interactInfo.talkState = NPC_TALK_STATE_IDLE;
// For randomizer, handle updating the states for the gorons after receiving the item based on if (GameInteractor_Should(GI_VB_EN_GO2_RESET_AFTER_GET_ITEM, true, this)) {
// the goron type rather then the item being received switch (this->getItemId) {
if (IS_RANDO) { case GI_CLAIM_CHECK:
switch (this->actor.params & 0x1F) { Environment_ClearBgsDayCount();
case GORON_DMT_BIGGORON:
// Resolves #1301. unk_13EE is used to set the opacity of the HUD. The trade sequence discussion with Biggoron
// sets the HUD to transparent, and it is restored at z_message_PAL:3549, but by specifically watching for
// trade sequence items, this leaves it transparent for non-trade sequence items (in rando) so we fix that here
gSaveContext.unk_13EE = 0x32;
return;
case GORON_CITY_LINK:
EnGo2_GetItemAnimation(this, play); EnGo2_GetItemAnimation(this, play);
return; return;
case GORON_CITY_ROLLING_BIG: case GI_TUNIC_GORON:
Flags_SetInfTable(INFTABLE_GORON_CITY_DOORS_UNLOCKED);
EnGo2_GetItemAnimation(this, play);
return;
case GI_SWORD_BGS:
gSaveContext.bgsFlag = true;
break;
case GI_BOMB_BAG_30:
case GI_BOMB_BAG_40:
EnGo2_RollingAnimation(this, play); EnGo2_RollingAnimation(this, play);
this->actionFunc = EnGo2_GoronRollingBigContinueRolling; this->actionFunc = EnGo2_GoronRollingBigContinueRolling;
return; return;
} }
this->actionFunc = func_80A46B40; this->actionFunc = func_80A46B40;
return;
} }
switch (this->getItemId) {
case GI_CLAIM_CHECK:
Environment_ClearBgsDayCount();
EnGo2_GetItemAnimation(this, play);
return;
case GI_TUNIC_GORON:
Flags_SetInfTable(INFTABLE_GORON_CITY_DOORS_UNLOCKED);
EnGo2_GetItemAnimation(this, play);
return;
case GI_SWORD_BGS:
gSaveContext.bgsFlag = true;
break;
case GI_BOMB_BAG_30:
case GI_BOMB_BAG_40:
EnGo2_RollingAnimation(this, play);
this->actionFunc = EnGo2_GoronRollingBigContinueRolling;
return;
}
this->actionFunc = func_80A46B40;
} }
} }
@ -1925,12 +1871,12 @@ void EnGo2_BiggoronEyedrops(EnGo2* this, PlayState* play) {
this->actor.flags &= ~ACTOR_FLAG_TARGETABLE; this->actor.flags &= ~ACTOR_FLAG_TARGETABLE;
this->actor.shape.rot.y += 0x5B0; this->actor.shape.rot.y += 0x5B0;
this->trackingMode = NPC_TRACKING_NONE; this->trackingMode = NPC_TRACKING_NONE;
this->animTimer = IS_RANDO ? 0 : (this->skelAnime.endFrame + 60.0f + 60.0f); // eyeDrops animation timer this->animTimer = !GameInteractor_Should(GI_VB_PLAY_EYEDROPS_CS, true, NULL) ? 0 : (this->skelAnime.endFrame + 60.0f + 60.0f); // eyeDrops animation timer
this->eyeMouthTexState = 2; this->eyeMouthTexState = 2;
this->unk_20C = 0; this->unk_20C = 0;
this->goronState++; this->goronState++;
func_800F483C(0x28, 5); func_800F483C(0x28, 5);
if (!IS_RANDO) { if (GameInteractor_Should(GI_VB_PLAY_EYEDROPS_CS, true, NULL)) {
OnePointCutscene_Init(play, 4190, -99, &this->actor, MAIN_CAM); OnePointCutscene_Init(play, 4190, -99, &this->actor, MAIN_CAM);
} }
break; break;
@ -1959,16 +1905,15 @@ void EnGo2_BiggoronEyedrops(EnGo2* this, PlayState* play) {
this->trackingMode = NPC_TRACKING_HEAD_AND_TORSO; this->trackingMode = NPC_TRACKING_HEAD_AND_TORSO;
this->skelAnime.playSpeed = 0.0f; this->skelAnime.playSpeed = 0.0f;
this->skelAnime.curFrame = this->skelAnime.endFrame; this->skelAnime.curFrame = this->skelAnime.endFrame;
if (IS_RANDO) {
GetItemEntry getItemEntry = Randomizer_GetItemFromKnownCheck(RC_DMT_TRADE_EYEDROPS, GI_CLAIM_CHECK); Flags_SetRandomizerInf(RAND_INF_ADULT_TRADES_DMT_TRADE_EYEDROPS);
Randomizer_ConsumeAdultTradeItem(play, ITEM_EYEDROPS); if(GameInteractor_Should(GI_VB_TRADE_EYEDROPS, true, this)) {
EnGo2_GetItemEntry(this, play, getItemEntry);
Flags_SetRandomizerInf(RAND_INF_ADULT_TRADES_DMT_TRADE_EYEDROPS);
} else {
u32 getItemId = GI_CLAIM_CHECK; u32 getItemId = GI_CLAIM_CHECK;
EnGo2_GetItem(this, play, getItemId); EnGo2_GetItem(this, play, getItemId);
this->actionFunc = EnGo2_SetupGetItem;
} else {
this->actionFunc = EnGo2_SetGetItem;
} }
this->actionFunc = EnGo2_SetupGetItem;
this->goronState = 0; this->goronState = 0;
} }
break; break;

View File

@ -107,4 +107,9 @@ typedef struct EnGo2 {
/* */ GetItemEntry getItemEntry; /* */ GetItemEntry getItemEntry;
} EnGo2; // size = 0x05A0 } EnGo2; // size = 0x05A0
void EnGo2_GetItemAnimation(EnGo2* enGo2, PlayState* play);
void EnGo2_RollingAnimation(EnGo2* enGo2, PlayState* play);
void EnGo2_GoronRollingBigContinueRolling(EnGo2* enGo2, PlayState* play);
void func_80A46B40(EnGo2* enGo2, PlayState* play);
#endif #endif

View File

@ -7,7 +7,7 @@
#include "z_en_hs.h" #include "z_en_hs.h"
#include "vt.h" #include "vt.h"
#include "objects/object_hs/object_hs.h" #include "objects/object_hs/object_hs.h"
#include "soh/Enhancements/randomizer/adult_trade_shuffle.h" #include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY) #define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY)
@ -79,25 +79,7 @@ void EnHs_Init(Actor* thisx, PlayState* play) {
// "chicken shop (adult era)" // "chicken shop (adult era)"
osSyncPrintf(VT_FGCOL(CYAN) " ヒヨコの店(大人の時) \n" VT_RST); osSyncPrintf(VT_FGCOL(CYAN) " ヒヨコの店(大人の時) \n" VT_RST);
func_80A6E3A0(this, func_80A6E9AC); func_80A6E3A0(this, func_80A6E9AC);
bool shouldSpawn; if (GameInteractor_Should(GI_VB_DESPAWN_GROG, Flags_GetItemGetInf(ITEMGETINF_30), this)) {
bool tradedMushroom = Flags_GetItemGetInf(ITEMGETINF_30);
if (IS_RANDO && Randomizer_GetSettingValue(RSK_SHUFFLE_ADULT_TRADE)) {
// To explain the logic because Fado and Grog are linked:
// - If you have Cojiro, then spawn Grog and not Fado.
// - If you don't have Cojiro but do have Odd Potion, spawn Fado and not Grog.
// - If you don't have either, spawn Grog if you haven't traded the Odd Mushroom.
// - If you don't have either but have traded the mushroom, don't spawn either.
if (PLAYER_HAS_SHUFFLED_ADULT_TRADE_ITEM(ITEM_COJIRO)) {
shouldSpawn = true;
} else if (PLAYER_HAS_SHUFFLED_ADULT_TRADE_ITEM(ITEM_ODD_POTION)) {
shouldSpawn = false;
} else {
shouldSpawn = !tradedMushroom;
}
} else {
shouldSpawn = !tradedMushroom;
}
if (!shouldSpawn) {
// "chicken shop closed" // "chicken shop closed"
osSyncPrintf(VT_FGCOL(CYAN) " ヒヨコ屋閉店 \n" VT_RST); osSyncPrintf(VT_FGCOL(CYAN) " ヒヨコ屋閉店 \n" VT_RST);
Actor_Kill(&this->actor); Actor_Kill(&this->actor);
@ -148,11 +130,11 @@ void func_80A6E5EC(EnHs* this, PlayState* play) {
void func_80A6E630(EnHs* this, PlayState* play) { void func_80A6E630(EnHs* this, PlayState* play) {
if ((Message_GetState(&play->msgCtx) == TEXT_STATE_DONE) && Message_ShouldAdvance(play)) { if ((Message_GetState(&play->msgCtx) == TEXT_STATE_DONE) && Message_ShouldAdvance(play)) {
if (!IS_RANDO) { if (GameInteractor_Should(GI_VB_TRADE_TIMER_ODD_MUSHROOM, true, NULL)) {
func_80088AA0(180); func_80088AA0(180);
gSaveContext.eventInf[1] &= ~1;
} }
func_80A6E3A0(this, func_80A6E6B0); func_80A6E3A0(this, func_80A6E6B0);
gSaveContext.eventInf[1] &= ~1;
} }
this->unk_2A8 |= 1; this->unk_2A8 |= 1;
@ -175,19 +157,12 @@ void func_80A6E70C(EnHs* this, PlayState* play) {
} }
void func_80A6E740(EnHs* this, PlayState* play) { void func_80A6E740(EnHs* this, PlayState* play) {
if (Actor_HasParent(&this->actor, play)) { if (Actor_HasParent(&this->actor, play) || !GameInteractor_Should(GI_VB_TRADE_COJIRO, true, this)) {
this->actor.parent = NULL; this->actor.parent = NULL;
Flags_SetRandomizerInf(RAND_INF_ADULT_TRADES_LW_TRADE_COJIRO);
func_80A6E3A0(this, func_80A6E630); func_80A6E3A0(this, func_80A6E630);
} else { } else {
if (IS_RANDO) { func_8002F434(&this->actor, play, GI_ODD_MUSHROOM, 10000.0f, 50.0f);
GetItemEntry itemEntry = Randomizer_GetItemFromKnownCheck(RC_LW_TRADE_COJIRO, GI_ODD_MUSHROOM);
Randomizer_ConsumeAdultTradeItem(play, ITEM_COJIRO);
GiveItemEntryFromActor(&this->actor, play, itemEntry, 10000.0f, 50.0f);
Flags_SetRandomizerInf(RAND_INF_ADULT_TRADES_LW_TRADE_COJIRO);
} else {
s32 itemId = GI_ODD_MUSHROOM;
func_8002F434(&this->actor, play, itemId, 10000.0f, 50.0f);
}
} }
this->unk_2A8 |= 1; this->unk_2A8 |= 1;
@ -198,14 +173,8 @@ void func_80A6E7BC(EnHs* this, PlayState* play) {
switch (play->msgCtx.choiceIndex) { switch (play->msgCtx.choiceIndex) {
case 0: case 0:
func_80A6E3A0(this, func_80A6E740); func_80A6E3A0(this, func_80A6E740);
if (IS_RANDO) { if (GameInteractor_Should(GI_VB_TRADE_COJIRO, true, this)) {
GetItemEntry itemEntry = Randomizer_GetItemFromKnownCheck(RC_LW_TRADE_COJIRO, GI_ODD_MUSHROOM); func_8002F434(&this->actor, play, GI_ODD_MUSHROOM, 10000.0f, 50.0f);
Randomizer_ConsumeAdultTradeItem(play, ITEM_COJIRO);
GiveItemEntryFromActor(&this->actor, play, itemEntry, 10000.0f, 50.0f);
Flags_SetRandomizerInf(RAND_INF_ADULT_TRADES_LW_TRADE_COJIRO);
} else {
s32 itemId = GI_ODD_MUSHROOM;
func_8002F434(&this->actor, play, itemId, 10000.0f, 50.0f);
} }
break; break;
case 1: case 1:

View File

@ -6,6 +6,7 @@
#include "z_en_js.h" #include "z_en_js.h"
#include "objects/object_js/object_js.h" #include "objects/object_js/object_js.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY) #define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY)
@ -127,23 +128,15 @@ void func_80A8910C(EnJs* this, PlayState* play) {
} }
void func_80A89160(EnJs* this, PlayState* play) { void func_80A89160(EnJs* this, PlayState* play) {
if (Actor_HasParent(&this->actor, play)) { if (Actor_HasParent(&this->actor, play) || !GameInteractor_Should(GI_VB_GIVE_ITEM_FROM_CARPET_SALESMAN, true, this)) {
this->actor.parent = NULL; this->actor.parent = NULL;
En_Js_SetupAction(this, func_80A8910C); En_Js_SetupAction(this, func_80A8910C);
Flags_SetRandomizerInf(RAND_INF_MERCHANTS_CARPET_SALESMAN);
} else { } else {
if (IS_RANDO && Randomizer_GetSettingValue(RSK_SHUFFLE_MERCHANTS) != RO_SHUFFLE_MERCHANTS_OFF && GetItemEntry itemEntry = ItemTable_Retrieve(GI_BOMBCHUS_10);
!Flags_GetRandomizerInf(RAND_INF_MERCHANTS_CARPET_SALESMAN)) { gSaveContext.pendingSale = itemEntry.itemId;
GetItemEntry itemEntry = Randomizer_GetItemFromKnownCheck(RC_WASTELAND_BOMBCHU_SALESMAN, GI_BOMBCHUS_10); gSaveContext.pendingSaleMod = itemEntry.modIndex;
gSaveContext.pendingSale = itemEntry.itemId; func_8002F434(&this->actor, play, GI_BOMBCHUS_10, 10000.0f, 50.0f);
gSaveContext.pendingSaleMod = itemEntry.modIndex;
GiveItemEntryFromActor(&this->actor, play, itemEntry, 10000.0f, 50.0f);
Flags_SetRandomizerInf(RAND_INF_MERCHANTS_CARPET_SALESMAN);
} else {
GetItemEntry itemEntry = ItemTable_Retrieve(GI_BOMBCHUS_10);
gSaveContext.pendingSale = itemEntry.itemId;
gSaveContext.pendingSaleMod = itemEntry.modIndex;
func_8002F434(&this->actor, play, GI_BOMBCHUS_10, 10000.0f, 50.0f);
}
} }
} }

View File

@ -10,7 +10,7 @@
#include "objects/object_km1/object_km1.h" #include "objects/object_km1/object_km1.h"
#include "objects/object_kw1/object_kw1.h" #include "objects/object_kw1/object_kw1.h"
#include "vt.h" #include "vt.h"
#include "soh/Enhancements/randomizer/adult_trade_shuffle.h" #include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY | ACTOR_FLAG_UPDATE_WHILE_CULLED) #define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY | ACTOR_FLAG_UPDATE_WHILE_CULLED)
@ -1027,20 +1027,9 @@ s32 EnKo_CanSpawn(EnKo* this, PlayState* play) {
} }
case SCENE_LOST_WOODS: case SCENE_LOST_WOODS:
if (IS_RANDO && Randomizer_GetSettingValue(RSK_SHUFFLE_ADULT_TRADE)) { return GameInteractor_Should(GI_VB_SPAWN_LW_FADO, (
// To explain the logic because Fado and Grog are linked: (INV_CONTENT(ITEM_TRADE_ADULT) == ITEM_ODD_POTION) ? true : false
// - If you have Cojiro, then spawn Grog and not Fado. ), this);
// - If you don't have Cojiro but do have Odd Potion, spawn Fado and not Grog.
// - If you don't have either, spawn Grog if you haven't traded the Odd Mushroom.
// - If you don't have either but have traded the mushroom, don't spawn either.
if (PLAYER_HAS_SHUFFLED_ADULT_TRADE_ITEM(ITEM_COJIRO)) {
return false;
} else {
return PLAYER_HAS_SHUFFLED_ADULT_TRADE_ITEM(ITEM_ODD_POTION);
}
} else {
return (INV_CONTENT(ITEM_TRADE_ADULT) == ITEM_ODD_POTION) ? true : false;
}
default: default:
return false; return false;
} }
@ -1186,18 +1175,10 @@ void func_80A99048(EnKo* this, PlayState* play) {
Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_EN_ELF, this->actor.world.pos.x, Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_EN_ELF, this->actor.world.pos.x,
this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, 3); this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, 3);
if (ENKO_TYPE == ENKO_TYPE_CHILD_3) { if (ENKO_TYPE == ENKO_TYPE_CHILD_3) {
if (!IS_RANDO) { if (!GameInteractor_Should(GI_VB_OPEN_KOKIRI_FOREST, CHECK_QUEST_ITEM(QUEST_KOKIRI_EMERALD), this)) {
if (!CHECK_QUEST_ITEM(QUEST_KOKIRI_EMERALD)) { this->collider.dim.height += 200;
this->collider.dim.height += 200; this->actionFunc = func_80A995CC;
this->actionFunc = func_80A995CC; return;
return;
}
} else {
if (!Flags_GetEventChkInf(EVENTCHKINF_OBTAINED_KOKIRI_EMERALD_DEKU_TREE_DEAD)) {
this->collider.dim.height += 200;
this->actionFunc = func_80A995CC;
return;
}
} }
Path_CopyLastPoint(this->path, &this->actor.world.pos); Path_CopyLastPoint(this->path, &this->actor.world.pos);
} }
@ -1230,18 +1211,11 @@ void func_80A99438(EnKo* this, PlayState* play) {
} }
void func_80A99504(EnKo* this, PlayState* play) { void func_80A99504(EnKo* this, PlayState* play) {
if (Actor_HasParent(&this->actor, play)) { if (Actor_HasParent(&this->actor, play) || !GameInteractor_Should(GI_VB_TRADE_ODD_POTION, true, this)) {
this->actor.parent = NULL; this->actor.parent = NULL;
this->actionFunc = func_80A99560; this->actionFunc = func_80A99560;
} else { } else {
if (IS_RANDO) { func_8002F434(&this->actor, play, GI_SAW, 120.0f, 10.0f);
GetItemEntry itemEntry = Randomizer_GetItemFromKnownCheck(RC_LW_TRADE_ODD_POTION, GI_SAW);
Randomizer_ConsumeAdultTradeItem(play, ITEM_ODD_POTION);
GiveItemEntryFromActor(&this->actor, play, itemEntry, 120.0f, 10.0f);
} else {
s32 itemId = GI_SAW;
func_8002F434(&this->actor, play, itemId, 120.0f, 10.0f);
}
} }
} }

View File

@ -57,4 +57,8 @@ typedef enum {
ENKO_FQS_ADULT_SAVED ENKO_FQS_ADULT_SAVED
} KokiriForestQuestState; } KokiriForestQuestState;
void func_80A995CC(EnKo* actor, PlayState* play);
void func_80A99384(EnKo* actor, PlayState* play);
void func_80A99560(EnKo* actor, PlayState* play);
#endif #endif

View File

@ -6,7 +6,7 @@
#include "z_en_kz.h" #include "z_en_kz.h"
#include "objects/object_kz/object_kz.h" #include "objects/object_kz/object_kz.h"
#include "soh/Enhancements/randomizer/adult_trade_shuffle.h" #include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY) #define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY)
@ -73,12 +73,9 @@ static AnimationInfo sAnimationInfo[] = {
u16 EnKz_GetTextNoMaskChild(PlayState* play, EnKz* this) { u16 EnKz_GetTextNoMaskChild(PlayState* play, EnKz* this) {
Player* player = GET_PLAYER(play); Player* player = GET_PLAYER(play);
if ((IS_RANDO && Flags_GetRandomizerInf(RAND_INF_DUNGEONS_DONE_JABU_JABUS_BELLY)) || if (GameInteractor_Should(GI_VB_KING_ZORA_THANK_CHILD, (
(!IS_RANDO && CHECK_QUEST_ITEM(QUEST_ZORA_SAPPHIRE))) { CHECK_QUEST_ITEM(QUEST_ZORA_SAPPHIRE)
// Allow turning in Ruto's letter even if you have already rescued her ), this)) {
if (IS_RANDO && !Flags_GetEventChkInf(EVENTCHKINF_KING_ZORA_MOVED)) {
player->exchangeItemId = EXCH_ITEM_LETTER_RUTO;
}
return 0x402B; return 0x402B;
} else if (Flags_GetEventChkInf(EVENTCHKINF_KING_ZORA_MOVED)) { } else if (Flags_GetEventChkInf(EVENTCHKINF_KING_ZORA_MOVED)) {
return 0x401C; return 0x401C;
@ -94,8 +91,10 @@ u16 EnKz_GetTextNoMaskAdult(PlayState* play, EnKz* this) {
// this works because both ITEM_NONE and later trade items are > ITEM_FROG // this works because both ITEM_NONE and later trade items are > ITEM_FROG
if (INV_CONTENT(ITEM_TRADE_ADULT) >= ITEM_FROG) { if (INV_CONTENT(ITEM_TRADE_ADULT) >= ITEM_FROG) {
if (!Flags_GetInfTable(INFTABLE_139)) { if (!Flags_GetInfTable(INFTABLE_139)) {
if (!IS_RANDO) { if (!GameInteractor_Should(GI_VB_GIVE_ITEM_FROM_THAWING_KING_ZORA, (
return CHECK_OWNED_EQUIP(EQUIP_TYPE_TUNIC, EQUIP_INV_TUNIC_ZORA) ? 0x401F : 0x4012; !CHECK_OWNED_EQUIP(EQUIP_TYPE_TUNIC, EQUIP_INV_TUNIC_ZORA)
), this)) {
return 0x401F;
} else { } else {
return 0x4012; return 0x4012;
} }
@ -243,7 +242,9 @@ void func_80A9CB18(EnKz* this, PlayState* play) {
Player* player = GET_PLAYER(play); Player* player = GET_PLAYER(play);
if (func_80A9C95C(play, this, &this->interactInfo.talkState, 340.0f, EnKz_GetText, func_80A9C6C0)) { if (func_80A9C95C(play, this, &this->interactInfo.talkState, 340.0f, EnKz_GetText, func_80A9C6C0)) {
if (((IS_RANDO && LINK_IS_CHILD) || this->actor.textId == 0x401A) && !Flags_GetEventChkInf(EVENTCHKINF_KING_ZORA_MOVED)) { if (GameInteractor_Should(GI_VB_BE_ABLE_TO_EXCHANGE_RUTOS_LETTER, (this->actor.textId == 0x401A), this) &&
!Flags_GetEventChkInf(EVENTCHKINF_KING_ZORA_MOVED))
{
if (func_8002F368(play) == EXCH_ITEM_LETTER_RUTO) { if (func_8002F368(play) == EXCH_ITEM_LETTER_RUTO) {
this->actor.textId = 0x401B; this->actor.textId = 0x401B;
this->sfxPlayed = false; this->sfxPlayed = false;
@ -257,7 +258,7 @@ void func_80A9CB18(EnKz* this, PlayState* play) {
if (LINK_IS_ADULT) { if (LINK_IS_ADULT) {
if ((INV_CONTENT(ITEM_TRADE_ADULT) == ITEM_PRESCRIPTION) && if ((INV_CONTENT(ITEM_TRADE_ADULT) == ITEM_PRESCRIPTION) &&
(func_8002F368(play) == EXCH_ITEM_PRESCRIPTION)) { (func_8002F368(play) == EXCH_ITEM_PRESCRIPTION)) {
if (!IS_RANDO || !Flags_GetTreasure(play, 0x1F)) { if (GameInteractor_Should(GI_VB_TRADE_PRESCRIPTION, true, this)) {
this->actor.textId = 0x4014; this->actor.textId = 0x4014;
this->sfxPlayed = false; this->sfxPlayed = false;
player->actor.textId = this->actor.textId; player->actor.textId = this->actor.textId;
@ -271,11 +272,11 @@ void func_80A9CB18(EnKz* this, PlayState* play) {
this->actor.textId = CHECK_QUEST_ITEM(QUEST_SONG_SERENADE) ? 0x4045 : 0x401A; this->actor.textId = CHECK_QUEST_ITEM(QUEST_SONG_SERENADE) ? 0x4045 : 0x401A;
player->actor.textId = this->actor.textId; player->actor.textId = this->actor.textId;
} else { } else {
if (!IS_RANDO) { this->actor.textId =
this->actor.textId = CHECK_OWNED_EQUIP(EQUIP_TYPE_TUNIC, EQUIP_INV_TUNIC_ZORA) ? 0x401F : 0x4012; !GameInteractor_Should(GI_VB_GIVE_ITEM_FROM_THAWING_KING_ZORA,
} else { (!CHECK_OWNED_EQUIP(EQUIP_TYPE_TUNIC, EQUIP_INV_TUNIC_ZORA)), this)
this->actor.textId = 0x4012; ? 0x401F
} : 0x4012;
player->actor.textId = this->actor.textId; player->actor.textId = this->actor.textId;
} }
@ -344,29 +345,10 @@ void EnKz_Init(Actor* thisx, PlayState* play) {
this->interactInfo.talkState = NPC_TALK_STATE_IDLE; this->interactInfo.talkState = NPC_TALK_STATE_IDLE;
Animation_ChangeByInfo(&this->skelanime, sAnimationInfo, ENKZ_ANIM_0); Animation_ChangeByInfo(&this->skelanime, sAnimationInfo, ENKZ_ANIM_0);
if (!IS_RANDO) { if (GameInteractor_Should(GI_VB_KING_ZORA_BE_MOVED, (
if (Flags_GetEventChkInf(EVENTCHKINF_KING_ZORA_MOVED)) { Flags_GetEventChkInf(EVENTCHKINF_KING_ZORA_MOVED)
EnKz_SetMovedPos(this, play); ), this)) {
} EnKz_SetMovedPos(this, play);
} else {
int zorasFountain = Randomizer_GetSettingValue(RSK_ZORAS_FOUNTAIN);
switch (zorasFountain) {
case 0:
if (Flags_GetEventChkInf(EVENTCHKINF_KING_ZORA_MOVED)) {
EnKz_SetMovedPos(this, play);
}
break;
case 1:
if (LINK_IS_ADULT) {
EnKz_SetMovedPos(this, play);
} else if (Flags_GetEventChkInf(EVENTCHKINF_KING_ZORA_MOVED)) {
EnKz_SetMovedPos(this, play);
}
break;
case 2:
EnKz_SetMovedPos(this, play);
break;
}
} }
if (LINK_IS_ADULT) { if (LINK_IS_ADULT) {
@ -465,37 +447,29 @@ void EnKz_SetupGetItem(EnKz* this, PlayState* play) {
f32 xzRange; f32 xzRange;
f32 yRange; f32 yRange;
if (Actor_HasParent(&this->actor, play)) { if (Actor_HasParent(&this->actor, play) || (
(this->isTrading && !GameInteractor_Should(GI_VB_TRADE_PRESCRIPTION, true, this)) ||
(!this->isTrading && !GameInteractor_Should(GI_VB_GIVE_ITEM_FROM_THAWING_KING_ZORA, true, this))
)) {
this->actor.parent = NULL; this->actor.parent = NULL;
this->interactInfo.talkState = NPC_TALK_STATE_TALKING; this->interactInfo.talkState = NPC_TALK_STATE_TALKING;
this->actionFunc = EnKz_StartTimer; this->actionFunc = EnKz_StartTimer;
} else { if (!this->isTrading) {
if (IS_RANDO) { Flags_SetRandomizerInf(RAND_INF_KING_ZORA_THAWED);
if (this->isTrading) {
getItemEntry = Randomizer_GetItemFromKnownCheck(RC_ZD_TRADE_PRESCRIPTION, GI_FROG);
getItemId = getItemEntry.getItemId;
Randomizer_ConsumeAdultTradeItem(play, ITEM_PRESCRIPTION);
Flags_SetTreasure(play, 0x1F);
} else {
getItemEntry = Randomizer_GetItemFromKnownCheck(RC_ZD_KING_ZORA_THAWED, GI_TUNIC_ZORA);
getItemId = getItemEntry.getItemId;
}
} else { } else {
getItemId = this->isTrading ? GI_FROG : GI_TUNIC_ZORA; Flags_SetRandomizerInf(RAND_INF_ADULT_TRADES_ZD_TRADE_PRESCRIPTION);
} }
} else {
getItemId = this->isTrading ? GI_FROG : GI_TUNIC_ZORA;
yRange = fabsf(this->actor.yDistToPlayer) + 1.0f; yRange = fabsf(this->actor.yDistToPlayer) + 1.0f;
xzRange = this->actor.xzDistToPlayer + 1.0f; xzRange = this->actor.xzDistToPlayer + 1.0f;
if (!IS_RANDO || getItemEntry.getItemId == GI_NONE) { func_8002F434(&this->actor, play, getItemId, xzRange, yRange);
func_8002F434(&this->actor, play, getItemId, xzRange, yRange);
} else {
GiveItemEntryFromActor(&this->actor, play, getItemEntry, xzRange, yRange);
}
} }
} }
void EnKz_StartTimer(EnKz* this, PlayState* play) { void EnKz_StartTimer(EnKz* this, PlayState* play) {
if ((Message_GetState(&play->msgCtx) == TEXT_STATE_DONE) && Message_ShouldAdvance(play)) { if ((Message_GetState(&play->msgCtx) == TEXT_STATE_DONE) && Message_ShouldAdvance(play)) {
if (INV_CONTENT(ITEM_TRADE_ADULT) == ITEM_FROG && !IS_RANDO) { if (INV_CONTENT(ITEM_TRADE_ADULT) == ITEM_FROG && GameInteractor_Should(GI_VB_TRADE_TIMER_FROG, true, NULL)) {
func_80088AA0(180); // start timer2 with 3 minutes func_80088AA0(180); // start timer2 with 3 minutes
gSaveContext.eventInf[1] &= ~1; gSaveContext.eventInf[1] &= ~1;
} }

View File

@ -28,4 +28,6 @@ typedef struct EnKz {
/* 0x02BE */ s16 unk_2BE[12]; /* 0x02BE */ s16 unk_2BE[12];
} EnKz; // size = 0x02D8 } EnKz; // size = 0x02D8
void EnKz_SetupGetItem(EnKz* enKz, PlayState* play);
#endif #endif

View File

@ -6,6 +6,7 @@
#include "z_en_ma1.h" #include "z_en_ma1.h"
#include "objects/object_ma1/object_ma1.h" #include "objects/object_ma1/object_ma1.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY | ACTOR_FLAG_UPDATE_WHILE_CULLED | ACTOR_FLAG_DRAW_WHILE_CULLED | ACTOR_FLAG_NO_FREEZE_OCARINA) #define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY | ACTOR_FLAG_UPDATE_WHILE_CULLED | ACTOR_FLAG_DRAW_WHILE_CULLED | ACTOR_FLAG_NO_FREEZE_OCARINA)
@ -25,7 +26,6 @@ void func_80AA106C(EnMa1* this, PlayState* play);
void func_80AA10EC(EnMa1* this, PlayState* play); void func_80AA10EC(EnMa1* this, PlayState* play);
void func_80AA1150(EnMa1* this, PlayState* play); void func_80AA1150(EnMa1* this, PlayState* play);
void EnMa1_DoNothing(EnMa1* this, PlayState* play); void EnMa1_DoNothing(EnMa1* this, PlayState* play);
void EnMa1_WaitForSongGive(EnMa1* this, PlayState* play);
const ActorInit En_Ma1_InitVars = { const ActorInit En_Ma1_InitVars = {
ACTOR_EN_MA1, ACTOR_EN_MA1,
@ -90,25 +90,16 @@ static void* sEyeTextures[] = {
gMalonChildEyeClosedTex, gMalonChildEyeClosedTex,
}; };
bool Randomizer_ObtainedMalonHCReward() {
return Flags_GetEventChkInf(EVENTCHKINF_OBTAINED_POCKET_EGG);
}
u16 EnMa1_GetText(PlayState* play, Actor* thisx) { u16 EnMa1_GetText(PlayState* play, Actor* thisx) {
// Special case for Malon Hyrule Castle Text. Placing it here at the beginning bool malonReturnedFromCastle = GameInteractor_Should(GI_VB_MALON_RETURN_FROM_CASTLE, Flags_GetEventChkInf(EVENTCHKINF_TALON_RETURNED_FROM_CASTLE), NULL);
// has the added benefit of circumventing mask text if wearing bunny hood. bool malonTaughtEponasSong = GameInteractor_Should(GI_VB_MALON_ALREADY_TAUGHT_EPONAS_SONG, CHECK_QUEST_ITEM(QUEST_SONG_EPONA), NULL);
if (IS_RANDO && play->sceneNum == SCENE_HYRULE_CASTLE) {
return Randomizer_ObtainedMalonHCReward() ? 0x2044 : 0x2043;
}
u16 faceReaction = Text_GetFaceReaction(play, 0x17); u16 faceReaction = Text_GetFaceReaction(play, 0x17);
if (faceReaction != 0) { if (faceReaction != 0) {
return faceReaction; return faceReaction;
} }
if (!IS_RANDO) { if (malonTaughtEponasSong) {
if (CHECK_QUEST_ITEM(QUEST_SONG_EPONA)) { return 0x204A;
return 0x204A;
}
} }
if (Flags_GetEventChkInf(EVENTCHKINF_INVITED_TO_SING_WITH_CHILD_MALON)) { if (Flags_GetEventChkInf(EVENTCHKINF_INVITED_TO_SING_WITH_CHILD_MALON)) {
return 0x2049; return 0x2049;
@ -120,7 +111,7 @@ u16 EnMa1_GetText(PlayState* play, Actor* thisx) {
return 0x2048; return 0x2048;
} }
} }
if (Flags_GetEventChkInf(EVENTCHKINF_TALON_RETURNED_FROM_CASTLE)) { if (malonReturnedFromCastle) {
return 0x2047; return 0x2047;
} }
if (Flags_GetEventChkInf(EVENTCHKINF_OBTAINED_POCKET_EGG)) { if (Flags_GetEventChkInf(EVENTCHKINF_OBTAINED_POCKET_EGG)) {
@ -194,6 +185,8 @@ s16 func_80AA0778(PlayState* play, Actor* thisx) {
} }
s32 func_80AA08C4(EnMa1* this, PlayState* play) { s32 func_80AA08C4(EnMa1* this, PlayState* play) {
bool malonReturnedFromCastle = GameInteractor_Should(GI_VB_MALON_RETURN_FROM_CASTLE, Flags_GetEventChkInf(EVENTCHKINF_TALON_RETURNED_FROM_CASTLE), NULL);
if ((this->actor.shape.rot.z == 3) && (gSaveContext.sceneSetupIndex == 5)) { if ((this->actor.shape.rot.z == 3) && (gSaveContext.sceneSetupIndex == 5)) {
return 1; return 1;
} }
@ -202,32 +195,24 @@ s32 func_80AA08C4(EnMa1* this, PlayState* play) {
} }
// Causes Malon to appear in the market if you haven't met her yet. // Causes Malon to appear in the market if you haven't met her yet.
if (((play->sceneNum == SCENE_MARKET_NIGHT) || (play->sceneNum == SCENE_MARKET_DAY)) && if (((play->sceneNum == SCENE_MARKET_NIGHT) || (play->sceneNum == SCENE_MARKET_DAY)) &&
!Flags_GetEventChkInf(EVENTCHKINF_TALON_RETURNED_FROM_CASTLE) && !Flags_GetInfTable(INFTABLE_ENTERED_HYRULE_CASTLE)) { !malonReturnedFromCastle && !Flags_GetInfTable(INFTABLE_ENTERED_HYRULE_CASTLE)) {
return 1; return 1;
} }
if ((play->sceneNum == SCENE_HYRULE_CASTLE) && // if we're at hyrule castle if ((play->sceneNum == SCENE_HYRULE_CASTLE) && !malonReturnedFromCastle) {
(!Flags_GetEventChkInf(EVENTCHKINF_TALON_RETURNED_FROM_CASTLE) || // and talon hasn't left if (Flags_GetInfTable(INFTABLE_ENTERED_HYRULE_CASTLE)) {
(IS_RANDO && return 1;
!Randomizer_ObtainedMalonHCReward()))) { // or we're rando'd and haven't gotten malon's HC check } else {
if (Flags_GetInfTable(INFTABLE_ENTERED_HYRULE_CASTLE)) { // if we've met malon Flags_SetInfTable(INFTABLE_ENTERED_HYRULE_CASTLE);
return 1; // make her appear at the castle return 0;
} else { // if we haven't met malon
Flags_SetInfTable(INFTABLE_ENTERED_HYRULE_CASTLE); // set the flag for meeting malon
return 0; // don't make her appear at the castle
} }
} }
// Malon asleep in her bed if Talon has left Hyrule Castle and it is nighttime. if ((play->sceneNum == SCENE_LON_LON_BUILDINGS) && IS_NIGHT && malonReturnedFromCastle) {
if ((play->sceneNum == SCENE_LON_LON_BUILDINGS) && IS_NIGHT && (Flags_GetEventChkInf(EVENTCHKINF_TALON_RETURNED_FROM_CASTLE))) {
return 1; return 1;
} }
// Don't spawn Malon if none of the above are true and we are not in Lon Lon Ranch.
if (play->sceneNum != SCENE_LON_LON_RANCH) { if (play->sceneNum != SCENE_LON_LON_RANCH) {
return 0; return 0;
} }
// If we've gotten this far, we're in Lon Lon Ranch. Spawn Malon if it is daytime, Talon has left Hyrule Castle, and if ((this->actor.shape.rot.z == 3) && IS_DAY && malonReturnedFromCastle) {
// either we are not randomized, or we are and we have received Malon's item at Hyrule Castle.
if ((this->actor.shape.rot.z == 3) && IS_DAY && (Flags_GetEventChkInf(EVENTCHKINF_TALON_RETURNED_FROM_CASTLE)) &&
((IS_RANDO && Randomizer_ObtainedMalonHCReward()) || !IS_RANDO)) {
return 1; return 1;
} }
return 0; return 0;
@ -284,6 +269,8 @@ void func_80AA0B74(EnMa1* this) {
void EnMa1_Init(Actor* thisx, PlayState* play) { void EnMa1_Init(Actor* thisx, PlayState* play) {
EnMa1* this = (EnMa1*)thisx; EnMa1* this = (EnMa1*)thisx;
bool malonReturnedFromCastle = GameInteractor_Should(GI_VB_MALON_RETURN_FROM_CASTLE, Flags_GetEventChkInf(EVENTCHKINF_TALON_RETURNED_FROM_CASTLE), NULL);
bool malonTaughtEponasSong = GameInteractor_Should(GI_VB_MALON_ALREADY_TAUGHT_EPONAS_SONG, CHECK_QUEST_ITEM(QUEST_SONG_EPONA), NULL);
s32 pad; s32 pad;
ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 18.0f); ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 18.0f);
@ -292,12 +279,6 @@ void EnMa1_Init(Actor* thisx, PlayState* play) {
Collider_SetCylinder(play, &this->collider, &this->actor, &sCylinderInit); Collider_SetCylinder(play, &this->collider, &this->actor, &sCylinderInit);
CollisionCheck_SetInfo2(&this->actor.colChkInfo, DamageTable_Get(22), &sColChkInfoInit); CollisionCheck_SetInfo2(&this->actor.colChkInfo, DamageTable_Get(22), &sColChkInfoInit);
if (IS_RANDO) { // Skip Malon's multiple textboxes before getting an item
Flags_SetInfTable(INFTABLE_ENTERED_HYRULE_CASTLE);
Flags_SetInfTable(INFTABLE_MET_CHILD_MALON_AT_CASTLE_OR_MARKET);
Flags_SetEventChkInf(EVENTCHKINF_SPOKE_TO_CHILD_MALON_AT_CASTLE_OR_MARKET);
}
if (!func_80AA08C4(this, play)) { if (!func_80AA08C4(this, play)) {
Actor_Kill(&this->actor); Actor_Kill(&this->actor);
return; return;
@ -308,21 +289,10 @@ void EnMa1_Init(Actor* thisx, PlayState* play) {
this->actor.targetMode = 6; this->actor.targetMode = 6;
this->interactInfo.talkState = NPC_TALK_STATE_IDLE; this->interactInfo.talkState = NPC_TALK_STATE_IDLE;
// To avoid missing a check, we want Malon to have the actionFunc for singing, but not reacting to Ocarina, if any of if (!malonReturnedFromCastle || malonTaughtEponasSong) {
// the following are true.
// 1. Talon has not left Hyrule Castle.
// 2. We are Randomized and have not obtained Malon's Weird Egg Check.
// 3. We are not Randomized and have obtained Epona's Song
if (!Flags_GetEventChkInf(EVENTCHKINF_TALON_RETURNED_FROM_CASTLE) || (IS_RANDO && !Randomizer_ObtainedMalonHCReward()) || (CHECK_QUEST_ITEM(QUEST_SONG_EPONA) && !IS_RANDO) ||
(IS_RANDO && Flags_GetTreasure(play, 0x1F))) {
this->actionFunc = func_80AA0D88; this->actionFunc = func_80AA0D88;
EnMa1_ChangeAnim(this, ENMA1_ANIM_2); EnMa1_ChangeAnim(this, ENMA1_ANIM_2);
// If none of the above conditions were true, set Malon up to teach Epona's Song.
} else { } else {
if (IS_RANDO) { // Skip straight to "let's sing it together" textbox in the ranch
Flags_SetEventChkInf(EVENTCHKINF_INVITED_TO_SING_WITH_CHILD_MALON);
}
this->actionFunc = func_80AA0F44; this->actionFunc = func_80AA0F44;
EnMa1_ChangeAnim(this, ENMA1_ANIM_2); EnMa1_ChangeAnim(this, ENMA1_ANIM_2);
} }
@ -336,6 +306,9 @@ void EnMa1_Destroy(Actor* thisx, PlayState* play) {
} }
void func_80AA0D88(EnMa1* this, PlayState* play) { void func_80AA0D88(EnMa1* this, PlayState* play) {
bool malonReturnedFromCastle = GameInteractor_Should(GI_VB_MALON_RETURN_FROM_CASTLE, Flags_GetEventChkInf(EVENTCHKINF_TALON_RETURNED_FROM_CASTLE), NULL);
bool malonTaughtEponasSong = GameInteractor_Should(GI_VB_MALON_ALREADY_TAUGHT_EPONAS_SONG, CHECK_QUEST_ITEM(QUEST_SONG_EPONA), NULL);
if (this->interactInfo.talkState != NPC_TALK_STATE_IDLE) { if (this->interactInfo.talkState != NPC_TALK_STATE_IDLE) {
if (this->skelAnime.animation != &gMalonChildIdleAnim) { if (this->skelAnime.animation != &gMalonChildIdleAnim) {
EnMa1_ChangeAnim(this, ENMA1_ANIM_1); EnMa1_ChangeAnim(this, ENMA1_ANIM_1);
@ -346,16 +319,9 @@ void func_80AA0D88(EnMa1* this, PlayState* play) {
} }
} }
// We want to Kill Malon's Actor outside of randomizer when Talon is freed. In Randomizer we don't kill Malon's if ((play->sceneNum == SCENE_HYRULE_CASTLE) && malonReturnedFromCastle) {
// Actor here, otherwise if we wake up Talon first and then get her check she will spontaneously
// disappear.
if ((play->sceneNum == SCENE_HYRULE_CASTLE) && (!IS_RANDO && Flags_GetEventChkInf(EVENTCHKINF_TALON_RETURNED_FROM_CASTLE))) {
Actor_Kill(&this->actor); Actor_Kill(&this->actor);
// We want Malon to give the Weird Egg Check (see function below) in the following situations: } else if (!malonReturnedFromCastle || malonTaughtEponasSong) {
// 1. Talon as not left Hyrule Castle (Vanilla) OR
// 2. We haven't obtained Malon's Weird Egg Check (Randomizer only) OR
// 3. We have Epona's Song? (Vanilla only, not sure why it's here but I didn't write that one)
} else if ((!Flags_GetEventChkInf(EVENTCHKINF_TALON_RETURNED_FROM_CASTLE) || (IS_RANDO && !Randomizer_ObtainedMalonHCReward())) || (CHECK_QUEST_ITEM(QUEST_SONG_EPONA) && !IS_RANDO)) {
if (this->interactInfo.talkState == NPC_TALK_STATE_ACTION) { if (this->interactInfo.talkState == NPC_TALK_STATE_ACTION) {
this->actionFunc = func_80AA0EA0; this->actionFunc = func_80AA0EA0;
play->msgCtx.stateTimer = 4; play->msgCtx.stateTimer = 4;
@ -365,21 +331,16 @@ void func_80AA0D88(EnMa1* this, PlayState* play) {
} }
void func_80AA0EA0(EnMa1* this, PlayState* play) { void func_80AA0EA0(EnMa1* this, PlayState* play) {
if (Actor_HasParent(&this->actor, play)) { if (Actor_HasParent(&this->actor, play) || !GameInteractor_Should(GI_VB_GIVE_ITEM_WEIRD_EGG, true, NULL)) {
this->actor.parent = NULL; this->actor.parent = NULL;
this->actionFunc = func_80AA0EFC; this->actionFunc = func_80AA0EFC;
} else { } else {
if (!IS_RANDO) { func_8002F434(&this->actor, play, GI_WEIRD_EGG, 120.0f, 10.0f);
func_8002F434(&this->actor, play, GI_WEIRD_EGG, 120.0f, 10.0f);
} else {
GetItemEntry getItemEntry = Randomizer_GetItemFromKnownCheck(RC_HC_MALON_EGG, GI_WEIRD_EGG);
GiveItemEntryFromActor(&this->actor, play, getItemEntry, 120.0f, 10.0f);
}
} }
} }
void func_80AA0EFC(EnMa1* this, PlayState* play) { void func_80AA0EFC(EnMa1* this, PlayState* play) {
if (this->interactInfo.talkState == NPC_TALK_STATE_ITEM_GIVEN) { if (this->interactInfo.talkState == NPC_TALK_STATE_ITEM_GIVEN || !GameInteractor_Should(GI_VB_GIVE_ITEM_WEIRD_EGG, true, NULL)) {
this->interactInfo.talkState = NPC_TALK_STATE_IDLE; this->interactInfo.talkState = NPC_TALK_STATE_IDLE;
this->actionFunc = func_80AA0D88; this->actionFunc = func_80AA0D88;
Flags_SetEventChkInf(EVENTCHKINF_OBTAINED_POCKET_EGG); Flags_SetEventChkInf(EVENTCHKINF_OBTAINED_POCKET_EGG);
@ -387,24 +348,6 @@ void func_80AA0EFC(EnMa1* this, PlayState* play) {
} }
} }
void GivePlayerRandoRewardMalon(EnMa1* malon, PlayState* play, RandomizerCheck check) {
GetItemEntry getItemEntry = Randomizer_GetItemFromKnownCheck(check, RG_EPONAS_SONG);
// Prevents flag from getting set if we weren't able to get the item (i.e. Player is holding shield
// when closing the textbox).
if (malon->actor.parent != NULL && malon->actor.parent->id == GET_PLAYER(play)->actor.id &&
!Flags_GetTreasure(play, 0x1F)) {
Flags_SetTreasure(play, 0x1F);
// puts malon in the action that vanilla has her in after learning the song
// (confirmed via breakpoints in a vanilla save).
malon->actionFunc = func_80AA0D88;
} else if (!Flags_GetTreasure(play, 0x1F)) {
GiveItemEntryFromActor(&malon->actor, play, getItemEntry, 10000.0f, 100.0f);
}
// make malon sing again after giving the item.
malon->interactInfo.talkState = NPC_TALK_STATE_IDLE;
malon->unk_1E0 = 1;
}
void func_80AA0F44(EnMa1* this, PlayState* play) { void func_80AA0F44(EnMa1* this, PlayState* play) {
Player* player = GET_PLAYER(play); Player* player = GET_PLAYER(play);
@ -419,7 +362,6 @@ void func_80AA0F44(EnMa1* this, PlayState* play) {
} }
if (Flags_GetEventChkInf(EVENTCHKINF_INVITED_TO_SING_WITH_CHILD_MALON)) { if (Flags_GetEventChkInf(EVENTCHKINF_INVITED_TO_SING_WITH_CHILD_MALON)) {
// When the player pulls out the Ocarina while close to Malon
if (player->stateFlags2 & 0x1000000) { if (player->stateFlags2 & 0x1000000) {
player->stateFlags2 |= 0x2000000; player->stateFlags2 |= 0x2000000;
player->unk_6A8 = &this->actor; player->unk_6A8 = &this->actor;
@ -427,19 +369,10 @@ void func_80AA0F44(EnMa1* this, PlayState* play) {
Message_StartTextbox(play, this->actor.textId, NULL); Message_StartTextbox(play, this->actor.textId, NULL);
this->interactInfo.talkState = NPC_TALK_STATE_TALKING; this->interactInfo.talkState = NPC_TALK_STATE_TALKING;
this->actor.flags |= ACTOR_FLAG_WILL_TALK; this->actor.flags |= ACTOR_FLAG_WILL_TALK;
// when rando'ed, skip to the Item Giving. Otherwise go to the song teaching code. this->actionFunc = func_80AA106C;
this->actionFunc = IS_RANDO ? func_80AA1150 : func_80AA106C;
} else if (this->actor.xzDistToPlayer < 30.0f + (f32)this->collider.dim.radius) { } else if (this->actor.xzDistToPlayer < 30.0f + (f32)this->collider.dim.radius) {
// somehow flags that the player is close to malon so that pulling out the Ocarina
// triggers the code above this.
player->stateFlags2 |= 0x800000; player->stateFlags2 |= 0x800000;
} }
// If rando'ed, a textbox is closing, it's malon's 'my mom wrote this song' text, AND we do have an ocarina
// in our inventory. This allows us to grant the check when talking to malon with the ocarina in our inventory.
if (IS_RANDO && (Actor_TextboxIsClosing(&this->actor, play) && play->msgCtx.textId == 0x2049) &&
(INV_CONTENT(ITEM_OCARINA_FAIRY) != ITEM_NONE || INV_CONTENT(ITEM_OCARINA_TIME) != ITEM_NONE)) {
this->actionFunc = EnMa1_WaitForSongGive;
}
} }
} }
@ -461,48 +394,16 @@ void func_80AA10EC(EnMa1* this, PlayState* play) {
} }
} }
void EnMa1_WaitForSongGive(EnMa1* this, PlayState* play) {
// Actually give the song check.
GivePlayerRandoRewardMalon(this, play, RC_SONG_FROM_MALON);
}
// Sets an Ocarina State necessary to not softlock in rando.
// This function should only be called in rando.
void EnMa1_EndTeachSong(EnMa1* this, PlayState* play) {
if (play->csCtx.state == CS_STATE_IDLE) {
this->actionFunc = func_80AA0F44;
play->msgCtx.ocarinaMode = OCARINA_MODE_04;
}
if (IS_RANDO) {
// Transition to the giving the song check on the next update run.
this->actionFunc = EnMa1_WaitForSongGive;
}
}
void func_80AA1150(EnMa1* this, PlayState* play) { void func_80AA1150(EnMa1* this, PlayState* play) {
GET_PLAYER(play)->stateFlags2 |= 0x800000; GET_PLAYER(play)->stateFlags2 |= 0x800000;
// When rando'ed, trigger the "song learned" Ocarina mode.
if (IS_RANDO && (Message_GetState(&play->msgCtx) == TEXT_STATE_CLOSING)) {
play->msgCtx.ocarinaMode = OCARINA_MODE_03;
}
if (play->msgCtx.ocarinaMode == OCARINA_MODE_03) { if (play->msgCtx.ocarinaMode == OCARINA_MODE_03) {
if (!IS_RANDO) { Flags_SetRandomizerInf(RAND_INF_LEARNED_EPONA_SONG);
play->nextEntranceIndex = ENTR_LON_LON_RANCH_0; play->nextEntranceIndex = ENTR_LON_LON_RANCH_0;
gSaveContext.nextCutsceneIndex = 0xFFF1; gSaveContext.nextCutsceneIndex = 0xFFF1;
play->transitionType = TRANS_TYPE_CIRCLE(TCA_WAVE, TCC_WHITE, TCS_FAST); play->transitionType = TRANS_TYPE_CIRCLE(TCA_WAVE, TCC_WHITE, TCS_FAST);
play->transitionTrigger = TRANS_TRIGGER_START; play->transitionTrigger = TRANS_TRIGGER_START;
this->actionFunc = EnMa1_DoNothing; this->actionFunc = EnMa1_DoNothing;
} else {
// When rando'ed, skip the cutscene, play the chime, reset some flags,
// and give the song on next update.
func_80078884(NA_SE_SY_CORRECT_CHIME);
this->actionFunc = EnMa1_EndTeachSong;
this->actor.flags &= ~ACTOR_FLAG_WILL_TALK;
play->msgCtx.ocarinaMode = OCARINA_MODE_00;
}
} }
} }

View File

@ -20,4 +20,7 @@ typedef struct EnMa1 {
/* 0x01E8 */ NpcInteractInfo interactInfo; /* 0x01E8 */ NpcInteractInfo interactInfo;
} EnMa1; // size = 0x0210 } EnMa1; // size = 0x0210
void func_80AA106C(EnMa1* enMa1, PlayState* play);
void func_80AA0D88(EnMa1* enMa1, PlayState* play);
#endif #endif

View File

@ -7,6 +7,7 @@
#include "z_en_md.h" #include "z_en_md.h"
#include "objects/object_md/object_md.h" #include "objects/object_md/object_md.h"
#include "overlays/actors/ovl_En_Elf/z_en_elf.h" #include "overlays/actors/ovl_En_Elf/z_en_elf.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY | ACTOR_FLAG_UPDATE_WHILE_CULLED | ACTOR_FLAG_NO_FREEZE_OCARINA) #define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY | ACTOR_FLAG_UPDATE_WHILE_CULLED | ACTOR_FLAG_NO_FREEZE_OCARINA)
@ -372,11 +373,7 @@ u16 EnMd_GetTextKokiriForest(PlayState* play, EnMd* this) {
this->unk_208 = 0; this->unk_208 = 0;
this->unk_209 = TEXT_STATE_NONE; this->unk_209 = TEXT_STATE_NONE;
// In rando, skip talking about the tree being dead so we can have the prompt for sword and shield instead if (GameInteractor_Should(GI_VB_MIDO_CONSIDER_DEKU_TREE_DEAD, CHECK_QUEST_ITEM(QUEST_KOKIRI_EMERALD), this)) {
if ((!IS_RANDO && CHECK_QUEST_ITEM(QUEST_KOKIRI_EMERALD)) ||
(IS_RANDO && Flags_GetEventChkInf(EVENTCHKINF_SHOWED_MIDO_SWORD_SHIELD) &&
Flags_GetRandomizerInf(RAND_INF_DUNGEONS_DONE_DEKU_TREE) &&
!Flags_GetEventChkInf(EVENTCHKINF_SPOKE_TO_MIDO_AFTER_DEKU_TREES_DEATH))) {
return 0x1045; return 0x1045;
} }
@ -486,25 +483,6 @@ s16 func_80AAAF04(PlayState* play, Actor* thisx) {
} }
u8 EnMd_ShouldSpawn(EnMd* this, PlayState* play) { u8 EnMd_ShouldSpawn(EnMd* this, PlayState* play) {
// In rando, Mido's spawn logic is adjusted to support closed deku/forest options
// He will spawn in the forest if you haven't showed the sword and shield, and will remain
// in the forest until you've obtained Zelda's letter or Deku Tree dies
// This is to ensure Deku Tree can still be opened in dungeon entrance rando even if Ghoma is defeated
if (IS_RANDO) {
if (play->sceneNum == SCENE_LOST_WOODS) {
return 1;
}
if (Flags_GetEventChkInf(EVENTCHKINF_SHOWED_MIDO_SWORD_SHIELD) &&
Flags_GetEventChkInf(EVENTCHKINF_SPOKE_TO_MIDO_AFTER_DEKU_TREES_DEATH) &&
(Flags_GetEventChkInf(EVENTCHKINF_OBTAINED_ZELDAS_LETTER) ||
Flags_GetEventChkInf(EVENTCHKINF_OBTAINED_KOKIRI_EMERALD_DEKU_TREE_DEAD))) {
return play->sceneNum == SCENE_MIDOS_HOUSE && !LINK_IS_ADULT;
}
return play->sceneNum == SCENE_KOKIRI_FOREST;
}
if (play->sceneNum == SCENE_KOKIRI_FOREST) { if (play->sceneNum == SCENE_KOKIRI_FOREST) {
if (!Flags_GetEventChkInf(EVENTCHKINF_SPOKE_TO_MIDO_AFTER_DEKU_TREES_DEATH) && !Flags_GetEventChkInf(EVENTCHKINF_OBTAINED_ZELDAS_LETTER)) { if (!Flags_GetEventChkInf(EVENTCHKINF_SPOKE_TO_MIDO_AFTER_DEKU_TREES_DEATH) && !Flags_GetEventChkInf(EVENTCHKINF_OBTAINED_ZELDAS_LETTER)) {
return 1; return 1;
@ -512,7 +490,7 @@ u8 EnMd_ShouldSpawn(EnMd* this, PlayState* play) {
} }
if (play->sceneNum == SCENE_MIDOS_HOUSE) { if (play->sceneNum == SCENE_MIDOS_HOUSE) {
if (((Flags_GetEventChkInf(EVENTCHKINF_SPOKE_TO_MIDO_AFTER_DEKU_TREES_DEATH)) != 0) || ((Flags_GetEventChkInf(EVENTCHKINF_OBTAINED_ZELDAS_LETTER)) != 0)) { if (Flags_GetEventChkInf(EVENTCHKINF_SPOKE_TO_MIDO_AFTER_DEKU_TREES_DEATH) || Flags_GetEventChkInf(EVENTCHKINF_OBTAINED_ZELDAS_LETTER)) {
if (!LINK_IS_ADULT) { if (!LINK_IS_ADULT) {
return 1; return 1;
} }
@ -681,9 +659,8 @@ void EnMd_Init(Actor* thisx, PlayState* play) {
this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, FAIRY_KOKIRI); this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, FAIRY_KOKIRI);
if (((play->sceneNum == SCENE_KOKIRI_FOREST) && !Flags_GetEventChkInf(EVENTCHKINF_SHOWED_MIDO_SWORD_SHIELD)) || if (((play->sceneNum == SCENE_KOKIRI_FOREST) && !Flags_GetEventChkInf(EVENTCHKINF_SHOWED_MIDO_SWORD_SHIELD)) ||
((play->sceneNum == SCENE_KOKIRI_FOREST) && (Flags_GetEventChkInf(EVENTCHKINF_SHOWED_MIDO_SWORD_SHIELD)) && ((play->sceneNum == SCENE_KOKIRI_FOREST) && Flags_GetEventChkInf(EVENTCHKINF_SHOWED_MIDO_SWORD_SHIELD) &&
((!IS_RANDO && CHECK_QUEST_ITEM(QUEST_KOKIRI_EMERALD)) || CHECK_QUEST_ITEM(QUEST_KOKIRI_EMERALD)) ||
(IS_RANDO && Flags_GetRandomizerInf(RAND_INF_DUNGEONS_DONE_DEKU_TREE)))) ||
((play->sceneNum == SCENE_LOST_WOODS) && !Flags_GetEventChkInf(EVENTCHKINF_PLAYED_SARIAS_SONG_FOR_MIDO_AS_ADULT))) { ((play->sceneNum == SCENE_LOST_WOODS) && !Flags_GetEventChkInf(EVENTCHKINF_PLAYED_SARIAS_SONG_FOR_MIDO_AS_ADULT))) {
this->actor.home.pos = this->actor.world.pos; this->actor.home.pos = this->actor.world.pos;
this->actionFunc = func_80AAB948; this->actionFunc = func_80AAB948;
@ -745,10 +722,11 @@ void func_80AAB948(EnMd* this, PlayState* play) {
this->skelAnime.playSpeed = CLAMP(temp, 1.0f, 3.0f); this->skelAnime.playSpeed = CLAMP(temp, 1.0f, 3.0f);
} }
if (this->interactInfo.talkState == NPC_TALK_STATE_ACTION) { if (
if ((!IS_RANDO && CHECK_QUEST_ITEM(QUEST_KOKIRI_EMERALD) || (GameInteractor_Should(GI_VB_MOVE_MIDO_IN_KOKIRI_FOREST, this->interactInfo.talkState == NPC_TALK_STATE_ACTION, this) && play->sceneNum == SCENE_KOKIRI_FOREST) ||
IS_RANDO && Flags_GetRandomizerInf(RAND_INF_DUNGEONS_DONE_DEKU_TREE) && this->interactInfo.talkState == NPC_TALK_STATE_ACTION
Flags_GetEventChkInf(EVENTCHKINF_SHOWED_MIDO_SWORD_SHIELD)) && !Flags_GetEventChkInf(EVENTCHKINF_SPOKE_TO_MIDO_AFTER_DEKU_TREES_DEATH) && ) {
if (CHECK_QUEST_ITEM(QUEST_KOKIRI_EMERALD) && !Flags_GetEventChkInf(EVENTCHKINF_SPOKE_TO_MIDO_AFTER_DEKU_TREES_DEATH) &&
(play->sceneNum == SCENE_KOKIRI_FOREST)) { (play->sceneNum == SCENE_KOKIRI_FOREST)) {
play->msgCtx.msgMode = MSGMODE_PAUSED; play->msgCtx.msgMode = MSGMODE_PAUSED;
} }
@ -815,9 +793,7 @@ void func_80AABD0C(EnMd* this, PlayState* play) {
return; return;
} }
if ((!IS_RANDO && CHECK_QUEST_ITEM(QUEST_KOKIRI_EMERALD) || if (CHECK_QUEST_ITEM(QUEST_KOKIRI_EMERALD) && !Flags_GetEventChkInf(EVENTCHKINF_SPOKE_TO_MIDO_AFTER_DEKU_TREES_DEATH) &&
IS_RANDO && Flags_GetRandomizerInf(RAND_INF_DUNGEONS_DONE_DEKU_TREE) &&
Flags_GetEventChkInf(EVENTCHKINF_SHOWED_MIDO_SWORD_SHIELD)) && !Flags_GetEventChkInf(EVENTCHKINF_SPOKE_TO_MIDO_AFTER_DEKU_TREES_DEATH) &&
(play->sceneNum == SCENE_KOKIRI_FOREST)) { (play->sceneNum == SCENE_KOKIRI_FOREST)) {
Message_CloseTextbox(play); Message_CloseTextbox(play);
Flags_SetEventChkInf(EVENTCHKINF_SPOKE_TO_MIDO_AFTER_DEKU_TREES_DEATH); Flags_SetEventChkInf(EVENTCHKINF_SPOKE_TO_MIDO_AFTER_DEKU_TREES_DEATH);

View File

@ -6,7 +6,7 @@
#include "z_en_mk.h" #include "z_en_mk.h"
#include "objects/object_mk/object_mk.h" #include "objects/object_mk/object_mk.h"
#include "soh/Enhancements/randomizer/adult_trade_shuffle.h" #include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY | ACTOR_FLAG_UPDATE_WHILE_CULLED) #define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY | ACTOR_FLAG_UPDATE_WHILE_CULLED)
@ -93,37 +93,24 @@ void func_80AACA40(EnMk* this, PlayState* play) {
} }
void func_80AACA94(EnMk* this, PlayState* play) { void func_80AACA94(EnMk* this, PlayState* play) {
if (Actor_HasParent(&this->actor, play) != 0) { if (Actor_HasParent(&this->actor, play) != 0 || !GameInteractor_Should(GI_VB_TRADE_FROG, true, this)) {
this->actor.parent = NULL; this->actor.parent = NULL;
this->actionFunc = func_80AACA40; this->actionFunc = func_80AACA40;
if (!IS_RANDO) { Flags_SetRandomizerInf(RAND_INF_ADULT_TRADES_LH_TRADE_FROG);
if (GameInteractor_Should(GI_VB_TRADE_TIMER_EYEDROPS, true, NULL)) {
func_80088AA0(240); func_80088AA0(240);
gSaveContext.eventInf[1] &= ~1; gSaveContext.eventInf[1] &= ~1;
} }
} else { } else {
if (IS_RANDO) { func_8002F434(&this->actor, play, GI_EYEDROPS, 10000.0f, 50.0f);
GetItemEntry getItemEntry = Randomizer_GetItemFromKnownCheck(RC_LH_TRADE_FROG, GI_EYEDROPS);
Randomizer_ConsumeAdultTradeItem(play, ITEM_FROG);
GiveItemEntryFromActor(&this->actor, play, getItemEntry, 10000.0f, 50.0f);
Flags_SetRandomizerInf(RAND_INF_ADULT_TRADES_LH_TRADE_FROG);
} else {
s32 getItemID = GI_EYEDROPS;
func_8002F434(&this->actor, play, getItemID, 10000.0f, 50.0f);
}
} }
} }
void func_80AACB14(EnMk* this, PlayState* play) { void func_80AACB14(EnMk* this, PlayState* play) {
if (Actor_TextboxIsClosing(&this->actor, play)) { if (Actor_TextboxIsClosing(&this->actor, play)) {
this->actionFunc = func_80AACA94; this->actionFunc = func_80AACA94;
if (IS_RANDO) { if (GameInteractor_Should(GI_VB_TRADE_FROG, true, this)) {
GetItemEntry getItemEntry = Randomizer_GetItemFromKnownCheck(RC_LH_TRADE_FROG, GI_EYEDROPS); func_8002F434(&this->actor, play, GI_EYEDROPS, 10000.0f, 50.0f);
Randomizer_ConsumeAdultTradeItem(play, ITEM_FROG);
GiveItemEntryFromActor(&this->actor, play, getItemEntry, 10000.0f, 50.0f);
Flags_SetRandomizerInf(RAND_INF_ADULT_TRADES_LH_TRADE_FROG);
} else {
s32 getItemID = GI_EYEDROPS;
func_8002F434(&this->actor, play, getItemID, 10000.0f, 50.0f);
} }
} }
} }
@ -150,7 +137,7 @@ void func_80AACC04(EnMk* this, PlayState* play) {
if (this->timer > 0) { if (this->timer > 0) {
this->timer--; this->timer--;
} else { } else {
this->timer = IS_RANDO ? 0 : 16; this->timer = GameInteractor_Should(GI_VB_PLAY_EYEDROP_CREATION_ANIM, true, this) ? 16 : 0;
this->actionFunc = func_80AACBAC; this->actionFunc = func_80AACBAC;
Animation_Change(&this->skelAnime, &object_mk_Anim_000D88, 1.0f, 0.0f, Animation_Change(&this->skelAnime, &object_mk_Anim_000D88, 1.0f, 0.0f,
Animation_GetLastFrame(&object_mk_Anim_000D88), ANIMMODE_LOOP, -4.0f); Animation_GetLastFrame(&object_mk_Anim_000D88), ANIMMODE_LOOP, -4.0f);
@ -163,7 +150,7 @@ void func_80AACCA0(EnMk* this, PlayState* play) {
this->timer--; this->timer--;
this->actor.shape.rot.y += 0x800; this->actor.shape.rot.y += 0x800;
} else { } else {
this->timer = IS_RANDO ? 0 : 120; this->timer = GameInteractor_Should(GI_VB_PLAY_EYEDROP_CREATION_ANIM, true, this) ? 120 : 0;
this->actionFunc = func_80AACC04; this->actionFunc = func_80AACC04;
Animation_Change(&this->skelAnime, &object_mk_Anim_000724, 1.0f, 0.0f, Animation_Change(&this->skelAnime, &object_mk_Anim_000724, 1.0f, 0.0f,
Animation_GetLastFrame(&object_mk_Anim_000724), ANIMMODE_LOOP, -4.0f); Animation_GetLastFrame(&object_mk_Anim_000724), ANIMMODE_LOOP, -4.0f);
@ -179,7 +166,7 @@ void func_80AACD48(EnMk* this, PlayState* play) {
this->actionFunc = func_80AACCA0; this->actionFunc = func_80AACCA0;
play->msgCtx.msgMode = MSGMODE_PAUSED; play->msgCtx.msgMode = MSGMODE_PAUSED;
player->exchangeItemId = EXCH_ITEM_NONE; player->exchangeItemId = EXCH_ITEM_NONE;
this->timer = IS_RANDO ? 0 : 16; this->timer = GameInteractor_Should(GI_VB_PLAY_EYEDROP_CREATION_ANIM, true, this) ? 16 : 0;
Animation_Change(&this->skelAnime, &object_mk_Anim_000D88, 1.0f, 0.0f, Animation_Change(&this->skelAnime, &object_mk_Anim_000D88, 1.0f, 0.0f,
Animation_GetLastFrame(&object_mk_Anim_000D88), ANIMMODE_LOOP, -4.0f); Animation_GetLastFrame(&object_mk_Anim_000D88), ANIMMODE_LOOP, -4.0f);
this->flags &= ~2; this->flags &= ~2;
@ -213,29 +200,20 @@ void func_80AACEE8(EnMk* this, PlayState* play) {
} }
void func_80AACFA0(EnMk* this, PlayState* play) { void func_80AACFA0(EnMk* this, PlayState* play) {
if (Actor_HasParent(&this->actor, play)) { if (Actor_HasParent(&this->actor, play) || !GameInteractor_Should(GI_VB_GIVE_ITEM_FROM_LAB_DIVE, true, this)) {
this->actor.parent = NULL; this->actor.parent = NULL;
this->actionFunc = func_80AACA40; this->actionFunc = func_80AACA40;
Flags_SetItemGetInf(ITEMGETINF_10); Flags_SetItemGetInf(ITEMGETINF_10);
} else { } else {
// not sure when/how/if this is getting called func_8002F434(&this->actor, play, GI_HEART_PIECE, 10000.0f, 50.0f);
if (!IS_RANDO) {
func_8002F434(&this->actor, play, GI_HEART_PIECE, 10000.0f, 50.0f);
} else {
GetItemEntry getItemEntry = Randomizer_GetItemFromKnownCheck(RC_LH_LAB_DIVE, GI_HEART_PIECE);
GiveItemEntryFromActor(&this->actor, play, getItemEntry, 10000.0f, 50.0f);
}
} }
} }
void func_80AAD014(EnMk* this, PlayState* play) { void func_80AAD014(EnMk* this, PlayState* play) {
if (Actor_TextboxIsClosing(&this->actor, play)) { if (Actor_TextboxIsClosing(&this->actor, play)) {
this->actionFunc = func_80AACFA0; this->actionFunc = func_80AACFA0;
if (!IS_RANDO) { if (GameInteractor_Should(GI_VB_GIVE_ITEM_FROM_LAB_DIVE, true, this)) {
func_8002F434(&this->actor, play, GI_HEART_PIECE, 10000.0f, 50.0f); func_8002F434(&this->actor, play, GI_HEART_PIECE, 10000.0f, 50.0f);
} else {
GetItemEntry getItemEntry = Randomizer_GetItemFromKnownCheck(RC_LH_LAB_DIVE, GI_HEART_PIECE);
GiveItemEntryFromActor(&this->actor, play, getItemEntry, 10000.0f, 50.0f);
} }
} }
@ -255,9 +233,9 @@ void EnMk_Wait(EnMk* this, PlayState* play) {
player->actor.textId = this->actor.textId; player->actor.textId = this->actor.textId;
this->actionFunc = func_80AACA40; this->actionFunc = func_80AACA40;
} else { } else {
// Skip eye drop text on rando if Link went in the water, so you can still receive the dive check if (GameInteractor_Should(GI_VB_USE_EYEDROP_DIALOGUE, (
if (INV_CONTENT(ITEM_ODD_MUSHROOM) == ITEM_EYEDROPS && INV_CONTENT(ITEM_ODD_MUSHROOM) == ITEM_EYEDROPS
(!IS_RANDO || this->swimFlag == 0)) { ), this)) {
player->actor.textId = 0x4032; player->actor.textId = 0x4032;
this->actionFunc = func_80AACA40; this->actionFunc = func_80AACA40;
} else { } else {

View File

@ -6,6 +6,7 @@
#include "z_en_ms.h" #include "z_en_ms.h"
#include "objects/object_ms/object_ms.h" #include "objects/object_ms/object_ms.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY) #define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY)
@ -127,20 +128,16 @@ void EnMs_Talk(EnMs* this, PlayState* play) {
} else if (Message_ShouldAdvance(play)) { } else if (Message_ShouldAdvance(play)) {
switch (play->msgCtx.choiceIndex) { switch (play->msgCtx.choiceIndex) {
case 0: // yes case 0: // yes
if (gSaveContext.rupees < if (!GameInteractor_Should(GI_VB_BE_ELIGIBLE_FOR_MAGIC_BEANS_PURCHASE, (
((IS_RANDO && Randomizer_GetSettingValue(RSK_SHUFFLE_MAGIC_BEANS)) gSaveContext.rupees >= sPrices[BEANS_BOUGHT]), this)) {
? 60
: sPrices[BEANS_BOUGHT])) {
Message_ContinueTextbox(play, 0x4069); // not enough rupees text Message_ContinueTextbox(play, 0x4069); // not enough rupees text
return; return;
} }
if (IS_RANDO && Randomizer_GetSettingValue(RSK_SHUFFLE_MAGIC_BEANS)) {
GiveItemEntryFromActor(&this->actor, play, if (GameInteractor_Should(GI_VB_GIVE_ITEM_FROM_MAGIC_BEAN_SALESMAN, true, this)) {
Randomizer_GetItemFromKnownCheck(RC_ZR_MAGIC_BEAN_SALESMAN, GI_BEAN), 90.0f, 10.0f);
} else {
func_8002F434(&this->actor, play, GI_BEAN, 90.0f, 10.0f); func_8002F434(&this->actor, play, GI_BEAN, 90.0f, 10.0f);
this->actionFunc = EnMs_Sell;
} }
this->actionFunc = EnMs_Sell;
return; return;
case 1: // no case 1: // no
Message_ContinueTextbox(play, 0x4068); Message_ContinueTextbox(play, 0x4068);
@ -152,23 +149,14 @@ void EnMs_Talk(EnMs* this, PlayState* play) {
void EnMs_Sell(EnMs* this, PlayState* play) { void EnMs_Sell(EnMs* this, PlayState* play) {
if (Actor_HasParent(&this->actor, play)) { if (Actor_HasParent(&this->actor, play)) {
Rupees_ChangeBy((IS_RANDO && Randomizer_GetSettingValue(RSK_SHUFFLE_MAGIC_BEANS)) ? -60 : -sPrices[BEANS_BOUGHT]); Rupees_ChangeBy(-sPrices[BEANS_BOUGHT]);
this->actor.parent = NULL; this->actor.parent = NULL;
this->actionFunc = this->actionFunc = EnMs_TalkAfterPurchase;
(IS_RANDO && Randomizer_GetSettingValue(RSK_SHUFFLE_MAGIC_BEANS)) ? EnMs_Wait : EnMs_TalkAfterPurchase;
} else { } else {
if (IS_RANDO && Randomizer_GetSettingValue(RSK_SHUFFLE_MAGIC_BEANS)) { GetItemEntry entry = ItemTable_Retrieve(GI_BEAN);
GetItemEntry itemEntry = Randomizer_GetItemFromKnownCheck(RC_ZR_MAGIC_BEAN_SALESMAN, GI_BEAN); gSaveContext.pendingSaleMod = entry.modIndex;
gSaveContext.pendingSale = itemEntry.itemId; gSaveContext.pendingSale = entry.itemId;
gSaveContext.pendingSaleMod = itemEntry.modIndex; func_8002F434(&this->actor, play, GI_BEAN, 90.0f, 10.0f);
GiveItemEntryFromActor(&this->actor, play, itemEntry, 90.0f, 10.0f);
BEANS_BOUGHT = 10;
} else {
GetItemEntry entry = ItemTable_Retrieve(GI_BEAN);
gSaveContext.pendingSaleMod = entry.modIndex;
gSaveContext.pendingSale = entry.itemId;
func_8002F434(&this->actor, play, GI_BEAN, 90.0f, 10.0f);
}
} }
} }

View File

@ -18,4 +18,7 @@ typedef struct EnMs {
/* 0x024C */ s16 activeTimer; /* 0x024C */ s16 activeTimer;
} EnMs; // size = 0x0250 } EnMs; // size = 0x0250
void EnMs_TalkAfterPurchase(EnMs* enMs, PlayState* play);
void EnMs_Wait(EnMs* enMs, PlayState* play);
#endif #endif

View File

@ -8,6 +8,7 @@
#include "vt.h" #include "vt.h"
#include "objects/object_nb/object_nb.h" #include "objects/object_nb/object_nb.h"
#include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h" #include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#define FLAGS ACTOR_FLAG_UPDATE_WHILE_CULLED #define FLAGS ACTOR_FLAG_UPDATE_WHILE_CULLED
@ -326,7 +327,9 @@ void EnNb_GiveMedallion(EnNb* this, PlayState* play) {
Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_DEMO_EFFECT, posX, posY, posZ, 0, 0, 0, Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_DEMO_EFFECT, posX, posY, posZ, 0, 0, 0,
0xC); 0xC);
Item_Give(play, ITEM_MEDALLION_SPIRIT); if (GameInteractor_Should(GI_VB_GIVE_ITEM_SPIRIT_MEDALLION, true, NULL)) {
Item_Give(play, ITEM_MEDALLION_SPIRIT);
}
} }
void EnNb_ComeUpImpl(EnNb* this, PlayState* play) { void EnNb_ComeUpImpl(EnNb* this, PlayState* play) {
@ -342,7 +345,9 @@ void EnNb_SetupChamberCsImpl(EnNb* this, PlayState* play) {
this->action = NB_CHAMBER_UNDERGROUND; this->action = NB_CHAMBER_UNDERGROUND;
play->csCtx.segment = &D_80AB431C; play->csCtx.segment = &D_80AB431C;
gSaveContext.cutsceneTrigger = 2; gSaveContext.cutsceneTrigger = 2;
Item_Give(play, ITEM_MEDALLION_SPIRIT); if (GameInteractor_Should(GI_VB_GIVE_ITEM_SPIRIT_MEDALLION, true, NULL)) {
Item_Give(play, ITEM_MEDALLION_SPIRIT);
}
player->actor.world.rot.y = player->actor.shape.rot.y = this->actor.world.rot.y + 0x8000; player->actor.world.rot.y = player->actor.shape.rot.y = this->actor.world.rot.y + 0x8000;
} }
} }

View File

@ -3,7 +3,7 @@
#include "objects/object_os_anime/object_os_anime.h" #include "objects/object_os_anime/object_os_anime.h"
#include "overlays/actors/ovl_En_Niw/z_en_niw.h" #include "overlays/actors/ovl_En_Niw/z_en_niw.h"
#include "vt.h" #include "vt.h"
#include "soh/Enhancements/randomizer/adult_trade_shuffle.h" #include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY | ACTOR_FLAG_UPDATE_WHILE_CULLED) #define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY | ACTOR_FLAG_UPDATE_WHILE_CULLED)
@ -204,7 +204,9 @@ void func_80ABA244(EnNiwLady* this, PlayState* play) {
EnNiw* currentCucco; EnNiw* currentCucco;
s32 phi_s1; s32 phi_s1;
this->cuccosInPen = IS_RANDO ? (7 - Randomizer_GetSettingValue(RSK_CUCCO_COUNT)) : 0; if (GameInteractor_Should(GI_VB_SET_CUCCO_COUNT, true, this)) {
this->cuccosInPen = 0;
}
currentCucco = (EnNiw*)play->actorCtx.actorLists[ACTORCAT_PROP].head; currentCucco = (EnNiw*)play->actorCtx.actorLists[ACTORCAT_PROP].head;
while (currentCucco != NULL) { while (currentCucco != NULL) {
if (currentCucco->actor.id == ACTOR_EN_NIW) { if (currentCucco->actor.id == ACTOR_EN_NIW) {
@ -239,9 +241,11 @@ void func_80ABA244(EnNiwLady* this, PlayState* play) {
phi_s1 = 7; phi_s1 = 7;
} }
} }
// Completed minigame and then threw Cucco(s) out of the pen
if ((this->unk_26C != 0) && (phi_s1 < 7)) { if ((this->unk_26C != 0) && (phi_s1 < 7)) {
phi_s1 = 9; phi_s1 = 9;
} }
this->actor.textId = sMissingCuccoTextIds[phi_s1]; this->actor.textId = sMissingCuccoTextIds[phi_s1];
if (Text_GetFaceReaction(play, 8) != 0) { if (Text_GetFaceReaction(play, 8) != 0) {
this->actor.textId = Text_GetFaceReaction(play, 8); this->actor.textId = Text_GetFaceReaction(play, 8);
@ -308,16 +312,18 @@ void func_80ABA654(EnNiwLady* this, PlayState* play) {
if (!Flags_GetItemGetInf(ITEMGETINF_0C)) { if (!Flags_GetItemGetInf(ITEMGETINF_0C)) {
this->actor.parent = NULL; this->actor.parent = NULL;
if (!IS_RANDO) { if (GameInteractor_Should(GI_VB_GIVE_ITEM_FROM_ANJU_AS_CHILD, true, this)) {
this->getItemId = GI_BOTTLE; this->getItemId = GI_BOTTLE;
func_8002F434(&this->actor, play, GI_BOTTLE, 100.0f, 50.0f); func_8002F434(&this->actor, play, GI_BOTTLE, 100.0f, 50.0f);
} else { } else {
this->getItemEntry = Randomizer_GetItemFromKnownCheck(RC_KAK_ANJU_AS_CHILD, GI_BOTTLE); // Circumvent the item offer action
GiveItemEntryFromActor(&this->actor, play, this->getItemEntry, 100.0f, 50.0f); this->actionFunc = func_80ABAC84;
return;
} }
this->actionFunc = func_80ABAC00; this->actionFunc = func_80ABAC00;
return; return;
} }
if (this->unk_26C == 1) { if (this->unk_26C == 1) {
this->getItemId = GI_RUPEE_PURPLE; this->getItemId = GI_RUPEE_PURPLE;
@ -398,15 +404,15 @@ void func_80ABA9B8(EnNiwLady* this, PlayState* play) {
Message_CloseTextbox(play); Message_CloseTextbox(play);
this->actor.parent = NULL; this->actor.parent = NULL;
if (!IS_RANDO) { if (GameInteractor_Should(GI_VB_GIVE_ITEM_FROM_ANJU_AS_ADULT, true, this)) {
func_8002F434(&this->actor, play, GI_POCKET_EGG, 200.0f, 100.0f); func_8002F434(&this->actor, play, GI_POCKET_EGG, 200.0f, 100.0f);
this->actionFunc = func_80ABAC00;
} else { } else {
this->getItemEntry = Randomizer_GetItemFromKnownCheck(RC_KAK_ANJU_AS_ADULT, GI_POCKET_EGG); // Circumvent the item offer action
GiveItemEntryFromActor(&this->actor, play, this->getItemEntry, 200.0f, 100.0f); this->actionFunc = func_80ABAC84;
Flags_SetItemGetInf(ITEMGETINF_2C); return;
} }
this->actionFunc = func_80ABAC00;
break; break;
case 1: case 1:
this->actor.textId = sTradeItemTextIds[3]; this->actor.textId = sTradeItemTextIds[3];
@ -433,15 +439,14 @@ void func_80ABAB08(EnNiwLady* this, PlayState* play) {
case 0: case 0:
Message_CloseTextbox(play); Message_CloseTextbox(play);
this->actor.parent = NULL; this->actor.parent = NULL;
if (!IS_RANDO) { if (GameInteractor_Should(GI_VB_TRADE_POCKET_CUCCO, true, this)) {
func_8002F434(&this->actor, play, GI_COJIRO, 200.0f, 100.0f); func_8002F434(&this->actor, play, GI_COJIRO, 200.0f, 100.0f);
this->actionFunc = func_80ABAC00;
} else { } else {
this->getItemEntry = Randomizer_GetItemFromKnownCheck(RC_KAK_TRADE_POCKET_CUCCO, GI_COJIRO); // Circumvent the item offer action
Randomizer_ConsumeAdultTradeItem(play, ITEM_POCKET_CUCCO); this->actionFunc = func_80ABAC84;
GiveItemEntryFromActor(&this->actor, play, this->getItemEntry, 200.0f, 100.0f); return;
Flags_SetItemGetInf(ITEMGETINF_2E);
} }
this->actionFunc = func_80ABAC00;
break; break;
case 1: case 1:
Message_CloseTextbox(play); Message_CloseTextbox(play);
@ -462,12 +467,6 @@ void func_80ABAC00(EnNiwLady* this, PlayState* play) {
if (Actor_HasParent(&this->actor, play)) { if (Actor_HasParent(&this->actor, play)) {
this->actionFunc = func_80ABAC84; this->actionFunc = func_80ABAC84;
} else { } else {
if (IS_RANDO) {
getItemId = this->getItemEntry.getItemId;
GiveItemEntryFromActor(&this->actor, play, this->getItemEntry, 200.0f, 100.0f);
return;
}
getItemId = this->getItemId; getItemId = this->getItemId;
if (LINK_IS_ADULT) { if (LINK_IS_ADULT) {
getItemId = !Flags_GetItemGetInf(ITEMGETINF_2C) ? GI_POCKET_EGG : GI_COJIRO; getItemId = !Flags_GetItemGetInf(ITEMGETINF_2C) ? GI_POCKET_EGG : GI_COJIRO;
@ -482,8 +481,7 @@ void func_80ABAC84(EnNiwLady* this, PlayState* play) {
} }
osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 正常終了 ☆☆☆☆☆ \n" VT_RST); osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 正常終了 ☆☆☆☆☆ \n" VT_RST);
if (LINK_IS_ADULT) { if (LINK_IS_ADULT) {
// Flags for randomizer gives are set in the original message prompt choice handling if (GameInteractor_Should(GI_VB_ANJU_SET_OBTAINED_TRADE_ITEM, true, this)) {
if (!IS_RANDO) {
if (!Flags_GetItemGetInf(ITEMGETINF_2C)) { if (!Flags_GetItemGetInf(ITEMGETINF_2C)) {
Flags_SetItemGetInf(ITEMGETINF_2C); Flags_SetItemGetInf(ITEMGETINF_2C);
} else { } else {

View File

@ -10,6 +10,7 @@
#include "scenes/overworld/spot16/spot16_scene.h" #include "scenes/overworld/spot16/spot16_scene.h"
#include "vt.h" #include "vt.h"
#include <assert.h> #include <assert.h>
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY | ACTOR_FLAG_UPDATE_WHILE_CULLED) #define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY | ACTOR_FLAG_UPDATE_WHILE_CULLED)
@ -42,6 +43,7 @@ void func_80ACAF74(EnOwl* this, PlayState* play);
void func_80ACC30C(EnOwl* this, PlayState* play); void func_80ACC30C(EnOwl* this, PlayState* play);
void func_80ACB4FC(EnOwl* this, PlayState* play); void func_80ACB4FC(EnOwl* this, PlayState* play);
void func_80ACB680(EnOwl* this, PlayState* play); void func_80ACB680(EnOwl* this, PlayState* play);
void func_80ACA62C(EnOwl* this, PlayState* play);
void func_80ACC460(EnOwl* this); void func_80ACC460(EnOwl* this);
void func_80ACBEA0(EnOwl*, PlayState*); void func_80ACBEA0(EnOwl*, PlayState*);
@ -137,9 +139,7 @@ void EnOwl_Init(Actor* thisx, PlayState* play) {
// "conversation owl %4x no = %d, sv = %d" // "conversation owl %4x no = %d, sv = %d"
osSyncPrintf(VT_FGCOL(CYAN) " 会話フクロウ %4x no = %d, sv = %d\n" VT_RST, this->actor.params, owlType, switchFlag); osSyncPrintf(VT_FGCOL(CYAN) " 会話フクロウ %4x no = %d, sv = %d\n" VT_RST, this->actor.params, owlType, switchFlag);
if (((owlType != OWL_DEFAULT) && (switchFlag < 0x20) && Flags_GetSwitch(play, switchFlag)) || if ((owlType != OWL_DEFAULT) && (switchFlag < 0x20) && Flags_GetSwitch(play, switchFlag)) {
// Owl shortcuts at SPOT06: Lake Hylia and SPOT16: Death Mountain Trail
(IS_RANDO && !(play->sceneNum == SCENE_LAKE_HYLIA || play->sceneNum == SCENE_DEATH_MOUNTAIN_TRAIL))) {
osSyncPrintf("savebitでフクロウ退避\n"); // "Save owl with savebit" osSyncPrintf("savebitでフクロウ退避\n"); // "Save owl with savebit"
Actor_Kill(&this->actor); Actor_Kill(&this->actor);
return; return;
@ -283,7 +283,7 @@ s32 EnOwl_CheckInitTalk(EnOwl* this, PlayState* play, u16 textId, f32 targetDist
} else { } else {
this->actor.textId = textId; this->actor.textId = textId;
distCheck = (flags & 2) ? 200.0f : 1000.0f; distCheck = (flags & 2) ? 200.0f : 1000.0f;
if (this->actor.xzDistToPlayer < targetDist) { if (GameInteractor_Should(GI_VB_OWL_INTERACTION, this->actor.xzDistToPlayer < targetDist, this)) {
this->actor.flags |= ACTOR_FLAG_WILL_TALK; this->actor.flags |= ACTOR_FLAG_WILL_TALK;
func_8002F1C4(&this->actor, play, targetDist, distCheck, 0); func_8002F1C4(&this->actor, play, targetDist, distCheck, 0);
} }

View File

@ -43,4 +43,6 @@ typedef struct EnOwl {
/* 0x0410 */ OwlFunc unk_410; /* 0x0410 */ OwlFunc unk_410;
} EnOwl; // size = 0x0414 } EnOwl; // size = 0x0414
void func_80ACA62C(EnOwl* enOwl, PlayState* play);
#endif #endif

View File

@ -7,6 +7,7 @@
#include "z_en_rl.h" #include "z_en_rl.h"
#include "vt.h" #include "vt.h"
#include "objects/object_rl/object_rl.h" #include "objects/object_rl/object_rl.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#define FLAGS ACTOR_FLAG_UPDATE_WHILE_CULLED #define FLAGS ACTOR_FLAG_UPDATE_WHILE_CULLED
@ -126,7 +127,9 @@ void func_80AE7590(EnRl* this, PlayState* play) {
pos.y = player->actor.world.pos.y + 80.0f; pos.y = player->actor.world.pos.y + 80.0f;
pos.z = player->actor.world.pos.z; pos.z = player->actor.world.pos.z;
Actor_Spawn(&play->actorCtx, play, ACTOR_DEMO_EFFECT, pos.x, pos.y, pos.z, 0, 0, 0, 0xE, true); Actor_Spawn(&play->actorCtx, play, ACTOR_DEMO_EFFECT, pos.x, pos.y, pos.z, 0, 0, 0, 0xE, true);
Item_Give(play, ITEM_MEDALLION_LIGHT); if (GameInteractor_Should(GI_VB_GIVE_ITEM_LIGHT_MEDALLION, true, NULL)) {
Item_Give(play, ITEM_MEDALLION_LIGHT);
}
this->lightMedallionGiven = 1; this->lightMedallionGiven = 1;
} }
} }

View File

@ -8,6 +8,7 @@
#include "objects/object_ru2/object_ru2.h" #include "objects/object_ru2/object_ru2.h"
#include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h" #include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h"
#include "vt.h" #include "vt.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#define FLAGS ACTOR_FLAG_UPDATE_WHILE_CULLED #define FLAGS ACTOR_FLAG_UPDATE_WHILE_CULLED
@ -260,7 +261,9 @@ void func_80AF2A38(EnRu2* this, PlayState* play) {
f32 posZ = player->actor.world.pos.z; f32 posZ = player->actor.world.pos.z;
Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_DEMO_EFFECT, posX, posY, posZ, 0, 0, 0, 10); Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_DEMO_EFFECT, posX, posY, posZ, 0, 0, 0, 10);
Item_Give(play, ITEM_MEDALLION_WATER); if (GameInteractor_Should(GI_VB_GIVE_ITEM_WATER_MEDALLION, true, NULL)) {
Item_Give(play, ITEM_MEDALLION_WATER);
}
} }
void func_80AF2AB4(EnRu2* this, PlayState* play) { void func_80AF2AB4(EnRu2* this, PlayState* play) {
@ -273,7 +276,9 @@ void func_80AF2AB4(EnRu2* this, PlayState* play) {
this->action = 1; this->action = 1;
play->csCtx.segment = &D_80AF411C; play->csCtx.segment = &D_80AF411C;
gSaveContext.cutsceneTrigger = 2; gSaveContext.cutsceneTrigger = 2;
Item_Give(play, ITEM_MEDALLION_WATER); if (GameInteractor_Should(GI_VB_GIVE_ITEM_WATER_MEDALLION, true, NULL)) {
Item_Give(play, ITEM_MEDALLION_WATER);
}
temp = this->actor.world.rot.y + 0x8000; temp = this->actor.world.rot.y + 0x8000;
player->actor.shape.rot.y = temp; player->actor.shape.rot.y = temp;
player->actor.world.rot.y = temp; player->actor.world.rot.y = temp;

View File

@ -3,6 +3,7 @@
#include "objects/object_sa/object_sa.h" #include "objects/object_sa/object_sa.h"
#include "scenes/overworld/spot04/spot04_scene.h" #include "scenes/overworld/spot04/spot04_scene.h"
#include "scenes/overworld/spot05/spot05_scene.h" #include "scenes/overworld/spot05/spot05_scene.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY | ACTOR_FLAG_UPDATE_WHILE_CULLED | ACTOR_FLAG_NO_FREEZE_OCARINA) #define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY | ACTOR_FLAG_UPDATE_WHILE_CULLED | ACTOR_FLAG_NO_FREEZE_OCARINA)
@ -390,13 +391,10 @@ s32 func_80AF5DFC(EnSa* this, PlayState* play) {
return 1; return 1;
} }
if (play->sceneNum == SCENE_SACRED_FOREST_MEADOW && (Flags_GetEventChkInf(EVENTCHKINF_OBTAINED_ZELDAS_LETTER))) { if (play->sceneNum == SCENE_SACRED_FOREST_MEADOW && (Flags_GetEventChkInf(EVENTCHKINF_OBTAINED_ZELDAS_LETTER))) {
if (IS_RANDO) { return GameInteractor_Should(GI_VB_BE_ELIGIBLE_FOR_SARIAS_SONG, !CHECK_QUEST_ITEM(QUEST_SONG_SARIA), NULL) ? 5 : 2;
return 5;
}
return CHECK_QUEST_ITEM(QUEST_SONG_SARIA) ? 2 : 5;
} }
if (play->sceneNum == SCENE_KOKIRI_FOREST && !CHECK_QUEST_ITEM(QUEST_KOKIRI_EMERALD)) { if (play->sceneNum == SCENE_KOKIRI_FOREST && !CHECK_QUEST_ITEM(QUEST_KOKIRI_EMERALD)) {
if (Flags_GetInfTable(INFTABLE_GREETED_BY_SARIA)) { if (GameInteractor_Should(GI_VB_NOT_BE_GREETED_BY_SARIA, Flags_GetInfTable(INFTABLE_GREETED_BY_SARIA), NULL)) {
return 1; return 1;
} }
return 4; return 4;
@ -622,28 +620,17 @@ void func_80AF67D0(EnSa* this, PlayState* play) {
} }
} }
void GivePlayerRandoRewardSaria(EnSa* saria, PlayState* play, RandomizerCheck check) {
GetItemEntry getItemEntry = Randomizer_GetItemFromKnownCheck(check, RG_SARIAS_SONG);
if (saria->actor.parent != NULL && saria->actor.parent->id == GET_PLAYER(play)->actor.id &&
!Flags_GetTreasure(play, 0x1F)) {
Flags_SetTreasure(play, 0x1F);
} else if (!Flags_GetTreasure(play, 0x1F)) {
GiveItemEntryFromActor(&saria->actor, play, getItemEntry, 10000.0f, 100.0f);
}
}
void func_80AF683C(EnSa* this, PlayState* play) { void func_80AF683C(EnSa* this, PlayState* play) {
Player* player = GET_PLAYER(play); Player* player = GET_PLAYER(play);
if (!(player->actor.world.pos.z >= -2220.0f) && !Play_InCsMode(play)) { if (!(player->actor.world.pos.z >= -2220.0f) && !Play_InCsMode(play)) {
if (IS_RANDO) { // SOH [General] This flag was previously unused, but was named accordingly so we will make use of it. (Normally we should opt for soh_inf)
GivePlayerRandoRewardSaria(this, play, RC_SONG_FROM_SARIA); Flags_SetEventChkInf(EVENTCHKINF_LEARNED_SARIAS_SONG);
return; if (GameInteractor_Should(GI_VB_PLAY_SARIAS_SONG_CS, true, this)) {
play->csCtx.segment = SEGMENTED_TO_VIRTUAL(spot05_scene_Cs_005730);
gSaveContext.cutsceneTrigger = 1;
this->actionFunc = func_80AF68E4;
} }
play->csCtx.segment = SEGMENTED_TO_VIRTUAL(spot05_scene_Cs_005730);
gSaveContext.cutsceneTrigger = 1;
this->actionFunc = func_80AF68E4;
} }
} }
@ -721,7 +708,9 @@ void func_80AF68E4(EnSa* this, PlayState* play) {
void func_80AF6B20(EnSa* this, PlayState* play) { void func_80AF6B20(EnSa* this, PlayState* play) {
if (play->sceneNum == SCENE_SACRED_FOREST_MEADOW) { if (play->sceneNum == SCENE_SACRED_FOREST_MEADOW) {
Item_Give(play, ITEM_SONG_SARIA); if (GameInteractor_Should(GI_VB_GIVE_ITEM_SARIAS_SONG, true, NULL)) {
Item_Give(play, ITEM_SONG_SARIA);
}
EnSa_ChangeAnim(this, ENSA_ANIM1_6); EnSa_ChangeAnim(this, ENSA_ANIM1_6);
} }

View File

@ -30,4 +30,6 @@ typedef struct EnSa {
/* 0x0286 */ Vec3s morphTable[17]; /* 0x0286 */ Vec3s morphTable[17];
} EnSa; // size = 0x02EC } EnSa; // size = 0x02EC
void func_80AF6B20(EnSa* enSa, PlayState* play);
#endif #endif

View File

@ -1,5 +1,6 @@
#include "z_en_shopnuts.h" #include "z_en_shopnuts.h"
#include "objects/object_shopnuts/object_shopnuts.h" #include "objects/object_shopnuts/object_shopnuts.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_HOSTILE) #define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_HOSTILE)
@ -69,18 +70,11 @@ void EnShopnuts_Init(Actor* thisx, PlayState* play) {
CollisionCheck_SetInfo(&this->actor.colChkInfo, NULL, &sColChkInfoInit); CollisionCheck_SetInfo(&this->actor.colChkInfo, NULL, &sColChkInfoInit);
Collider_UpdateCylinder(&this->actor, &this->collider); Collider_UpdateCylinder(&this->actor, &this->collider);
if (IS_RANDO) { if (GameInteractor_Should(GI_VB_BUSINESS_SCRUB_DESPAWN,
s16 respawnData = gSaveContext.respawn[RESPAWN_MODE_RETURN].data & ((1 << 8) - 1); ((this->actor.params == 0x0002) && (Flags_GetItemGetInf(ITEMGETINF_0B))) ||
ScrubIdentity scrubIdentity = Randomizer_IdentifyScrub(play->sceneNum, this->actor.params, respawnData);
if (scrubIdentity.isShuffled && Flags_GetRandomizerInf(scrubIdentity.randomizerInf)) {
Actor_Kill(&this->actor);
}
}
if (((this->actor.params == 0x0002) && (Flags_GetItemGetInf(ITEMGETINF_0B))) ||
((this->actor.params == 0x0009) && (Flags_GetInfTable(INFTABLE_192))) || ((this->actor.params == 0x0009) && (Flags_GetInfTable(INFTABLE_192))) ||
((this->actor.params == 0x000A) && (Flags_GetInfTable(INFTABLE_193)))) { ((this->actor.params == 0x000A) && (Flags_GetInfTable(INFTABLE_193))),
this)) {
Actor_Kill(&this->actor); Actor_Kill(&this->actor);
} else { } else {
EnShopnuts_SetupWait(this); EnShopnuts_SetupWait(this);

View File

@ -7,9 +7,6 @@
#include "z_en_si.h" #include "z_en_si.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
extern void func_8083C148(Player*, PlayState*);
extern void func_80078884(uint16_t);
#define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_HOOKSHOT_DRAGS) #define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_HOOKSHOT_DRAGS)
void EnSi_Init(Actor* thisx, PlayState* play); void EnSi_Init(Actor* thisx, PlayState* play);
@ -21,13 +18,6 @@ s32 func_80AFB748(EnSi* this, PlayState* play);
void func_80AFB768(EnSi* this, PlayState* play); void func_80AFB768(EnSi* this, PlayState* play);
void func_80AFB89C(EnSi* this, PlayState* play); void func_80AFB89C(EnSi* this, PlayState* play);
void func_80AFB950(EnSi* this, PlayState* play); void func_80AFB950(EnSi* this, PlayState* play);
void Randomizer_UpdateSkullReward(EnSi* this, PlayState* play);
void Randomizer_GiveSkullReward(EnSi* this, PlayState* play);
s32 textId = 0xB4;
s32 giveItemId = ITEM_SKULL_TOKEN;
s32 getItemId;
GetItemEntry getItem;
static ColliderCylinderInit sCylinderInit = { static ColliderCylinderInit sCylinderInit = {
{ {
@ -104,36 +94,12 @@ void func_80AFB768(EnSi* this, PlayState* play) {
if (this->collider.base.ocFlags2 & OC2_HIT_PLAYER) { if (this->collider.base.ocFlags2 & OC2_HIT_PLAYER) {
this->collider.base.ocFlags2 &= ~OC2_HIT_PLAYER; this->collider.base.ocFlags2 &= ~OC2_HIT_PLAYER;
if (GameInteractor_Should(GI_VB_GIVE_ITEM_SKULL_TOKEN, true, this)) {
if (IS_RANDO) { Item_Give(play, ITEM_SKULL_TOKEN);
Randomizer_UpdateSkullReward(this, play);
} else {
Item_Give(play, giveItemId);
}
if ((!CVarGetInteger("gSkulltulaFreeze", 0) || giveItemId != ITEM_SKULL_TOKEN) &&
getItemId != RG_ICE_TRAP) {
player->actor.freezeTimer = 20;
}
if (getItemId == RG_ICE_TRAP && Message_GetState(&play->msgCtx) != TEXT_STATE_CLOSING) {
player->actor.freezeTimer = 10; player->actor.freezeTimer = 10;
} Message_StartTextbox(play, 0xB4, NULL);
Message_StartTextbox(play, textId, NULL);
if (IS_RANDO) {
if (getItemId != RG_ICE_TRAP) {
Randomizer_GiveSkullReward(this, play);
Audio_PlayFanfare_Rando(getItem);
} else {
gSaveContext.pendingIceTrapCount++;
Audio_PlayFanfare(NA_BGM_SMALL_ITEM_GET);
}
} else {
Audio_PlayFanfare(NA_BGM_SMALL_ITEM_GET); Audio_PlayFanfare(NA_BGM_SMALL_ITEM_GET);
} }
player->getItemEntry = (GetItemEntry)GET_ITEM_NONE;
this->actionFunc = func_80AFB950; this->actionFunc = func_80AFB950;
} else { } else {
Collider_UpdateCylinder(&this->actor, &this->collider); Collider_UpdateCylinder(&this->actor, &this->collider);
@ -146,32 +112,18 @@ void func_80AFB768(EnSi* this, PlayState* play) {
void func_80AFB89C(EnSi* this, PlayState* play) { void func_80AFB89C(EnSi* this, PlayState* play) {
Player* player = GET_PLAYER(play); Player* player = GET_PLAYER(play);
Math_SmoothStepToF(&this->actor.scale.x, 0.25f, 0.4f, 1.0f, 0.0f); Math_SmoothStepToF(&this->actor.scale.x, 0.25f, 0.4f, 1.0f, 0.0f);
Actor_SetScale(&this->actor, this->actor.scale.x); Actor_SetScale(&this->actor, this->actor.scale.x);
this->actor.shape.rot.y += 0x400; this->actor.shape.rot.y += 0x400;
if (!CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_HOOKSHOT_ATTACHED)) { if (!CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_HOOKSHOT_ATTACHED)) {
if (IS_RANDO) { if (GameInteractor_Should(GI_VB_GIVE_ITEM_SKULL_TOKEN, true, this)) {
Randomizer_UpdateSkullReward(this, play); Item_Give(play, ITEM_SKULL_TOKEN);
} else { player->actor.freezeTimer = 10;
Item_Give(play, giveItemId); Message_StartTextbox(play, 0xB4, NULL);
}
Message_StartTextbox(play, textId, NULL);
if (IS_RANDO) {
if (getItemId != RG_ICE_TRAP) {
Randomizer_GiveSkullReward(this, play);
Audio_PlayFanfare_Rando(getItem);
} else {
gSaveContext.pendingIceTrapCount++;
Audio_PlayFanfare(NA_BGM_SMALL_ITEM_GET);
}
} else {
Audio_PlayFanfare(NA_BGM_SMALL_ITEM_GET); Audio_PlayFanfare(NA_BGM_SMALL_ITEM_GET);
} }
player->getItemEntry = (GetItemEntry)GET_ITEM_NONE;
this->actionFunc = func_80AFB950; this->actionFunc = func_80AFB950;
} }
} }
@ -179,19 +131,12 @@ void func_80AFB89C(EnSi* this, PlayState* play) {
void func_80AFB950(EnSi* this, PlayState* play) { void func_80AFB950(EnSi* this, PlayState* play) {
Player* player = GET_PLAYER(play); Player* player = GET_PLAYER(play);
if (Message_GetState(&play->msgCtx) != TEXT_STATE_CLOSING && if (Message_GetState(&play->msgCtx) != TEXT_STATE_CLOSING && GameInteractor_Should(GI_VB_GIVE_ITEM_SKULL_TOKEN, true, this)) {
(!CVarGetInteger("gSkulltulaFreeze", 0) || getItemId == RG_ICE_TRAP || giveItemId != ITEM_SKULL_TOKEN)) {
player->actor.freezeTimer = 10; player->actor.freezeTimer = 10;
} else { } else {
SET_GS_FLAGS((this->actor.params & 0x1F00) >> 8, this->actor.params & 0xFF); SET_GS_FLAGS((this->actor.params & 0x1F00) >> 8, this->actor.params & 0xFF);
GameInteractor_ExecuteOnFlagSet(FLAG_GS_TOKEN, this->actor.params); GameInteractor_ExecuteOnFlagSet(FLAG_GS_TOKEN, this->actor.params);
Actor_Kill(&this->actor); Actor_Kill(&this->actor);
if (gSaveContext.pendingIceTrapCount > 0 && player->heldItemId == 11) {
player->actor.freezeTimer = 0;
func_8083C148(GET_PLAYER(play), play);
func_80078884(NA_SE_SY_CAMERA_ZOOM_UP);
player->currentYaw = player->actor.shape.rot.y;
}
} }
} }
@ -210,44 +155,6 @@ void EnSi_Draw(Actor* thisx, PlayState* play) {
if (this->actionFunc != func_80AFB950) { if (this->actionFunc != func_80AFB950) {
func_8002ED80(&this->actor, play, 0); func_8002ED80(&this->actor, play, 0);
func_8002EBCC(&this->actor, play, 0); func_8002EBCC(&this->actor, play, 0);
if (!IS_RANDO) { GetItem_Draw(play, GID_SKULL_TOKEN_2);
GetItem_Draw(play, GID_SKULL_TOKEN_2);
} else {
getItem = Randomizer_GetItemFromActor(this->actor.id, play->sceneNum, this->actor.params, GI_SKULL_TOKEN);
EnItem00_CustomItemsParticles(&this->actor, play, getItem);
if (getItem.itemId != ITEM_SKULL_TOKEN) {
f32 mtxScale = 1.5f;
Matrix_Scale(mtxScale, mtxScale, mtxScale, MTXMODE_APPLY);
}
GetItemEntry_Draw(play, getItem);
}
}
}
void Randomizer_UpdateSkullReward(EnSi* this, PlayState* play) {
Player* player = GET_PLAYER(play);
getItem = Randomizer_GetItemFromActor(this->actor.id, play->sceneNum, this->actor.params, GI_SKULL_TOKEN);
getItemId = getItem.getItemId;
if (getItemId == RG_ICE_TRAP) {
textId = 0xF8;
} else {
textId = getItem.textId;
giveItemId = getItem.itemId;
}
player->getItemEntry = getItem;
}
void Randomizer_GiveSkullReward(EnSi* this, PlayState* play) {
Player* player = GET_PLAYER(play);
if (getItem.modIndex == MOD_NONE) {
// RANDOTOD: Move this into Item_Give() or some other more central location
if (getItem.getItemId == GI_SWORD_BGS) {
gSaveContext.bgsFlag = true;
}
Item_Give(play, giveItemId);
} else if (getItem.modIndex == MOD_RANDOMIZER) {
Randomizer_Item_Give(play, getItem);
} }
} }

View File

@ -13,6 +13,9 @@ typedef struct EnSi {
/* 0x014C */ EnSiActionFunc actionFunc; /* 0x014C */ EnSiActionFunc actionFunc;
/* 0x0150 */ ColliderCylinder collider; /* 0x0150 */ ColliderCylinder collider;
/* 0x019C */ u8 unk_19C; /* 0x019C */ u8 unk_19C;
// #region SOH [Randomizer] Caching the get item entry for the draw function for performance
/* */ GetItemEntry sohGetItemEntry;
// #endregion
} EnSi; // size = 0x01A0 } EnSi; // size = 0x01A0
#endif #endif

View File

@ -7,6 +7,7 @@
#include "z_en_ta.h" #include "z_en_ta.h"
#include "vt.h" #include "vt.h"
#include "objects/object_ta/object_ta.h" #include "objects/object_ta/object_ta.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY) #define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY)
@ -466,7 +467,7 @@ void func_80B14B6C(EnTa* this, PlayState* play) {
func_80B13AA0(this, func_80B14AF4, func_80B167C0); func_80B13AA0(this, func_80B14AF4, func_80B167C0);
this->unk_2CC = 5; this->unk_2CC = 5;
Flags_SetEventChkInf(EVENTCHKINF_TALON_RETURNED_FROM_CASTLE); Flags_SetEventChkInf(EVENTCHKINF_TALON_RETURNED_FROM_CASTLE);
if (IS_RANDO) { if (GameInteractor_Should(GI_VB_PLAY_ONEPOINT_ACTOR_CS, true, this)) {
OnePointCutscene_EndCutscene(play, csCamIdx); OnePointCutscene_EndCutscene(play, csCamIdx);
} }
Animation_PlayOnce(&this->skelAnime, &gTalonRunTransitionAnim); Animation_PlayOnce(&this->skelAnime, &gTalonRunTransitionAnim);

View File

@ -8,12 +8,9 @@
#include "objects/gameplay_keep/gameplay_keep.h" #include "objects/gameplay_keep/gameplay_keep.h"
#include "objects/object_tk/object_tk.h" #include "objects/object_tk/object_tk.h"
#include "soh/frame_interpolation.h" #include "soh/frame_interpolation.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY) #define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY)
#define COLLECTFLAG_GRAVEDIGGING_HEART_PIECE 0x19
#define ITEMGETINFFLAG_GRAVEDIGGING_HEART_PIECE 0x1000
bool heartPieceSpawned;
void EnTk_Init(Actor* thisx, PlayState* play); void EnTk_Init(Actor* thisx, PlayState* play);
void EnTk_Destroy(Actor* thisx, PlayState* play); void EnTk_Destroy(Actor* thisx, PlayState* play);
@ -408,10 +405,6 @@ s32 EnTk_ChooseReward(EnTk* this) {
f32 luck; f32 luck;
s32 reward; s32 reward;
if ((IS_RANDO || CVarGetInteger("gDampeWin", 0)) && !Flags_GetCollectible(gPlayState, COLLECTFLAG_GRAVEDIGGING_HEART_PIECE) && this->heartPieceSpawned == 0) {
return 3;
}
luck = Rand_ZeroOne(); luck = Rand_ZeroOne();
if (luck < 0.4f) { if (luck < 0.4f) {
@ -502,12 +495,7 @@ void EnTk_Init(Actor* thisx, PlayState* play) {
CollisionCheck_SetInfo2(&this->actor.colChkInfo, NULL, &sColChkInfoInit); CollisionCheck_SetInfo2(&this->actor.colChkInfo, NULL, &sColChkInfoInit);
if (CVarGetInteger("gDampeAllNight", 0)) { if (GameInteractor_Should(GI_VB_DAMPE_IN_GRAVEYARD_DESPAWN, gSaveContext.dayTime <= 0xC000 || gSaveContext.dayTime >= 0xE000 || LINK_IS_ADULT || play->sceneNum != SCENE_GRAVEYARD, this)) {
if (!!LINK_IS_ADULT || play->sceneNum != SCENE_GRAVEYARD) {
Actor_Kill(&this->actor);
return;
}
} else if (gSaveContext.dayTime <= 0xC000 || gSaveContext.dayTime >= 0xE000 || !!LINK_IS_ADULT || play->sceneNum != SCENE_GRAVEYARD) {
Actor_Kill(&this->actor); Actor_Kill(&this->actor);
return; return;
} }
@ -519,7 +507,6 @@ void EnTk_Init(Actor* thisx, PlayState* play) {
this->currentReward = -1; this->currentReward = -1;
this->currentSpot = NULL; this->currentSpot = NULL;
this->actionFunc = EnTk_Rest; this->actionFunc = EnTk_Rest;
heartPieceSpawned = false;
} }
void EnTk_Destroy(Actor* thisx, PlayState* play) { void EnTk_Destroy(Actor* thisx, PlayState* play) {
@ -599,7 +586,11 @@ void EnTk_Dig(EnTk* this, PlayState* play) {
Vec3f rewardOrigin; Vec3f rewardOrigin;
Vec3f rewardPos; Vec3f rewardPos;
s32 rewardParams[] = { s32 rewardParams[] = {
ITEM00_RUPEE_GREEN, ITEM00_RUPEE_BLUE, ITEM00_RUPEE_RED, ITEM00_RUPEE_PURPLE, ITEM00_HEART_PIECE, ITEM00_RUPEE_GREEN, ITEM00_RUPEE_BLUE, ITEM00_RUPEE_RED, ITEM00_RUPEE_PURPLE,
// #region SOH [General] Typically this heart piece would have no collectible flag set when it's picked up, but for both randomizer
// and gGravediggingTourFix we want to set one, and rely on it instead of the ItemGetInf flag that is set when the heart is spawned
((COLLECTFLAG_GRAVEDIGGING_HEART_PIECE & 0x3F) << 8) | ITEM00_HEART_PIECE,
// #endregion
}; };
EnTk_DigEff(this); EnTk_DigEff(this);
@ -610,7 +601,7 @@ void EnTk_Dig(EnTk* this, PlayState* play) {
this->rewardTimer = 0; this->rewardTimer = 0;
if (this->validDigHere == 1 || IS_RANDO || CVarGetInteger("gDampeWin", 0)) { if (GameInteractor_Should(GI_VB_BE_VALID_GRAVEDIGGING_SPOT, this->validDigHere == 1, this)) {
rewardOrigin.x = 0.0f; rewardOrigin.x = 0.0f;
rewardOrigin.y = 0.0f; rewardOrigin.y = 0.0f;
rewardOrigin.z = -40.0f; rewardOrigin.z = -40.0f;
@ -624,51 +615,24 @@ void EnTk_Dig(EnTk* this, PlayState* play) {
this->currentReward = EnTk_ChooseReward(this); this->currentReward = EnTk_ChooseReward(this);
if (this->currentReward == 3) { if (GameInteractor_Should(GI_VB_BE_DAMPE_GRAVEDIGGING_GRAND_PRIZE, this->currentReward == 3, this)) {
if (IS_RANDO || CVarGetInteger("gDampeWin", 0)) {
/*
* Upgrade the purple rupee reward to the heart piece if this
* is the first grand prize dig.
*/
if (!Flags_GetItemGetInf(ITEMGETINF_1C) && !(IS_RANDO || CVarGetInteger("gDampeWin", 0))) {
Flags_SetItemGetInf(ITEMGETINF_1C);
this->currentReward = 4;
} else if ((IS_RANDO || CVarGetInteger("gDampeWin", 0)) && !Flags_GetCollectible(gPlayState, COLLECTFLAG_GRAVEDIGGING_HEART_PIECE) && this->heartPieceSpawned == 0) {
this->currentReward = 4;
}
}
/* /*
* Upgrade the purple rupee reward to the heart piece if this * Upgrade the purple rupee reward to the heart piece if this
* is the first grand prize dig. * is the first grand prize dig.
*/ */
// If vanilla itemGetInf flag is not set, it's impossible for the new flag to be set, so return true. if (GameInteractor_Should(GI_VB_DAMPE_GRAVEDIGGING_GRAND_PRIZE_BE_HEART_PIECE, !Flags_GetItemGetInf(ITEMGETINF_1C), this)) {
// Otherwise if the gGravediggingTourFix is enabled and the new flag hasn't been set, return true. Flags_SetItemGetInf(ITEMGETINF_1C);
// If true, spawn the heart piece and set the vanilla itemGetInf flag and new temp clear flag.
if (!heartPieceSpawned &&
(!(gSaveContext.itemGetInf[1] & ITEMGETINFFLAG_GRAVEDIGGING_HEART_PIECE) ||
CVarGetInteger("gGravediggingTourFix", 0) &&
!Flags_GetCollectible(play, COLLECTFLAG_GRAVEDIGGING_HEART_PIECE))) {
this->currentReward = 4; this->currentReward = 4;
gSaveContext.itemGetInf[1] |= ITEMGETINFFLAG_GRAVEDIGGING_HEART_PIECE;
heartPieceSpawned = true;
} }
} }
if (IS_RANDO && this->currentReward == 4) { Item_DropCollectible(play, &rewardPos, rewardParams[this->currentReward]);
Actor_Spawn(&play->actorCtx, play, ACTOR_EN_ITEM00, rewardPos.x, rewardPos.y, rewardPos.z, 0, 0, 0, 0x1906, true);
this->heartPieceSpawned = 1;
} else {
EnItem00* reward = Item_DropCollectible(play, &rewardPos, rewardParams[this->currentReward]);
if (this->currentReward == 4) {
reward->collectibleFlag = COLLECTFLAG_GRAVEDIGGING_HEART_PIECE;
}
}
} }
} }
if (this->skelAnime.curFrame >= 32.0f && this->rewardTimer == 10) { if (this->skelAnime.curFrame >= 32.0f && this->rewardTimer == 10) {
/* Play a reward sound shortly after digging */ /* Play a reward sound shortly after digging */
if (!(IS_RANDO || CVarGetInteger("gDampeWin", 0)) && this->validDigHere == 0) { if (this->validDigHere == 0) {
/* Bad dig spot */ /* Bad dig spot */
Audio_PlayActorSound2(&this->actor, NA_SE_SY_ERROR); Audio_PlayActorSound2(&this->actor, NA_SE_SY_ERROR);
} else if (this->currentReward == 4) { } else if (this->currentReward == 4) {

View File

@ -8,6 +8,8 @@
/* Dirt particle effect */ /* Dirt particle effect */
struct EnTkEff; struct EnTkEff;
#define COLLECTFLAG_GRAVEDIGGING_HEART_PIECE 0x19
typedef struct EnTkEff { typedef struct EnTkEff {
/* 0x0000 */ u8 active; /* 0x0000 */ u8 active;
/* 0x0001 */ u8 timeLeft; /* 0x0001 */ u8 timeLeft;

View File

@ -6,7 +6,7 @@
#include "z_en_toryo.h" #include "z_en_toryo.h"
#include "objects/object_toryo/object_toryo.h" #include "objects/object_toryo/object_toryo.h"
#include "soh/Enhancements/randomizer/adult_trade_shuffle.h" #include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY) #define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY)
@ -291,10 +291,7 @@ void func_80B20768(EnToryo* this, PlayState* play) {
s16 sp32; s16 sp32;
s16 sp30; s16 sp30;
// Animation Count should be no more than 1 to guarantee putaway is complete after giving the saw if (this->unk_1E4 == 3 && !GameInteractor_Should(GI_VB_FIX_SAW_SOFTLOCK, false, NULL)) {
// As this is vanilla behavior, it only applies with the Fix toggle or Skip Text enabled.
bool checkAnim = (CVarGetInteger("gFixSawSoftlock", 0) != 0 || CVarGetInteger("gSkipText", 0) != 0) ? play->animationCtx.animationCount <= 1 : true;
if (this->unk_1E4 == 3 && checkAnim) {
Actor_ProcessTalkRequest(&this->actor, play); Actor_ProcessTalkRequest(&this->actor, play);
Message_ContinueTextbox(play, this->actor.textId); Message_ContinueTextbox(play, this->actor.textId);
this->unk_1E4 = 1; this->unk_1E4 = 1;
@ -315,19 +312,12 @@ void func_80B20768(EnToryo* this, PlayState* play) {
} }
if (this->unk_1E4 == 4) { if (this->unk_1E4 == 4) {
if (Actor_HasParent(&this->actor, play)) { if (Actor_HasParent(&this->actor, play) || !GameInteractor_Should(GI_VB_TRADE_SAW, true, this)) {
this->actor.parent = NULL; this->actor.parent = NULL;
this->unk_1E4 = 5; this->unk_1E4 = 5;
Flags_SetRandomizerInf(RAND_INF_ADULT_TRADES_GV_TRADE_SAW);
} else { } else {
if (IS_RANDO) { func_8002F434(&this->actor, play, GI_SWORD_BROKEN, 100.0f, 10.0f);
GetItemEntry itemEntry = Randomizer_GetItemFromKnownCheck(RC_GV_TRADE_SAW, GI_SWORD_BROKEN);
Randomizer_ConsumeAdultTradeItem(play, ITEM_SAW);
GiveItemEntryFromActor(&this->actor, play, itemEntry, 100.0f, 10.0f);
Flags_SetRandomizerInf(RAND_INF_ADULT_TRADES_GV_TRADE_SAW);
} else {
s32 itemId = GI_SWORD_BROKEN;
func_8002F434(&this->actor, play, itemId, 100.0f, 10.0f);
}
} }
return; return;
} }

View File

@ -6,6 +6,7 @@
#include "z_en_wonder_talk2.h" #include "z_en_wonder_talk2.h"
#include "vt.h" #include "vt.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY | ACTOR_FLAG_NO_LOCKON) #define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY | ACTOR_FLAG_NO_LOCKON)
@ -194,7 +195,9 @@ void func_80B3A3D4(EnWonderTalk2* this, PlayState* play) {
this->unk_15A = true; this->unk_15A = true;
} }
this->actor.flags &= ~(ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_UPDATE_WHILE_CULLED); this->actor.flags &= ~(ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_UPDATE_WHILE_CULLED);
func_8002DF54(play, NULL, 7); if (GameInteractor_Should(GI_VB_WONDER_TALK, true, this)) {
func_8002DF54(play, NULL, 7);
}
this->unk_156 = true; this->unk_156 = true;
this->actionFunc = func_80B3A4F8; this->actionFunc = func_80B3A4F8;
break; break;
@ -252,42 +255,12 @@ void func_80B3A4F8(EnWonderTalk2* this, PlayState* play) {
} }
this->unk_158 = 0; this->unk_158 = 0;
if (!this->unk_156) { if (!this->unk_156) {
// Whether or not to skip the text in rando if (GameInteractor_Should(GI_VB_WONDER_TALK, true, this)) {
bool randoSkipText = false;
if (IS_RANDO) {
// Scenes for which all of this type of wonder talk should be skipped.
switch (play->sceneNum) {
case SCENE_SHADOW_TEMPLE: // Shadow Temple
randoSkipText = true;
break;
case SCENE_GERUDO_TRAINING_GROUND: // Gerudo Training Grounds
randoSkipText = true;
break;
case SCENE_THIEVES_HIDEOUT: // Inside Gerudo Fortress
randoSkipText = true;
break;
default:
break;
}
// individual textIds that should be skipped, or that should be preserved
// in a scene that otherwise has all wonder talk skipped.
//switch (this->actor.textId) {
// case: 0x023c //textId we want to skip
// randoSkipText = true;
// break;
// case 0x023c: // textId in a skipped scene that we don't want to skip
// randoSkipText = false;
// break;
// default:
// break;
//}
}
if (!(randoSkipText)) {
Message_StartTextbox(play, this->actor.textId, NULL); Message_StartTextbox(play, this->actor.textId, NULL);
func_8002DF54(play, NULL, 8); func_8002DF54(play, NULL, 8);
this->actor.flags |= ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_UPDATE_WHILE_CULLED;
this->actionFunc = func_80B3A3D4;
} }
this->actor.flags |= ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_UPDATE_WHILE_CULLED;
this->actionFunc = func_80B3A3D4;
} }
} else { } else {

View File

@ -13,6 +13,7 @@
#include "scenes/indoors/tokinoma/tokinoma_scene.h" #include "scenes/indoors/tokinoma/tokinoma_scene.h"
#include "scenes/dungeons/ice_doukutu/ice_doukutu_scene.h" #include "scenes/dungeons/ice_doukutu/ice_doukutu_scene.h"
#include "vt.h" #include "vt.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#define FLAGS ACTOR_FLAG_UPDATE_WHILE_CULLED #define FLAGS ACTOR_FLAG_UPDATE_WHILE_CULLED
@ -277,6 +278,7 @@ void func_80B3C9EC(EnXc* this) {
this->action = SHEIK_ACTION_BLOCK_PEDESTAL; this->action = SHEIK_ACTION_BLOCK_PEDESTAL;
this->drawMode = SHEIK_DRAW_DEFAULT; this->drawMode = SHEIK_DRAW_DEFAULT;
this->unk_30C = 1; this->unk_30C = 1;
// SOH [Randomizer] We don't want sheik blocking the pedestal in randomizer
if (IS_RANDO) { if (IS_RANDO) {
Actor_Kill(&this->actor); Actor_Kill(&this->actor);
} }
@ -292,24 +294,6 @@ void func_80B3CA38(EnXc* this, PlayState* play) {
} }
} }
void GivePlayerRandoRewardSheikSong(EnXc* sheik, PlayState* play, RandomizerCheck check, int sheikType, GetItemID ogSongId) {
Player* player = GET_PLAYER(play);
if (!(gSaveContext.eventChkInf[5] & sheikType)) {
GetItemEntry getItemEntry = Randomizer_GetItemFromKnownCheck(check, ogSongId);
if (check == RC_SHEIK_AT_TEMPLE && !Flags_GetTreasure(play, 0x1F)) {
if (GiveItemEntryFromActor(&sheik->actor, play, getItemEntry, 10000.0f, 100.0f)) {
player->pendingFlag.flagID = 0x1F;
player->pendingFlag.flagType = FLAG_SCENE_TREASURE;
}
} else if (check != RC_SHEIK_AT_TEMPLE) {
if (GiveItemEntryFromActor(&sheik->actor, play, getItemEntry, 10000.0f, 100.0f)) {
player->pendingFlag.flagID = (0x5 << 4) | (sheikType & 0xF) >> 1;
player->pendingFlag.flagType = FLAG_EVENT_CHECK_INF;
}
}
}
}
s32 EnXc_MinuetCS(EnXc* this, PlayState* play) { s32 EnXc_MinuetCS(EnXc* this, PlayState* play) {
if (this->actor.params == SHEIK_TYPE_MINUET) { if (this->actor.params == SHEIK_TYPE_MINUET) {
Player* player = GET_PLAYER(play); Player* player = GET_PLAYER(play);
@ -317,16 +301,17 @@ s32 EnXc_MinuetCS(EnXc* this, PlayState* play) {
if (z < -2225.0f) { if (z < -2225.0f) {
if (!Play_InCsMode(play)) { if (!Play_InCsMode(play)) {
if (!IS_RANDO) { if (GameInteractor_Should(GI_VB_PLAY_MINUET_OF_FOREST_CS, true, NULL)) {
play->csCtx.segment = SEGMENTED_TO_VIRTUAL(&gMinuetCs); play->csCtx.segment = SEGMENTED_TO_VIRTUAL(&gMinuetCs);
gSaveContext.cutsceneTrigger = 1; gSaveContext.cutsceneTrigger = 1;
Flags_SetEventChkInf(EVENTCHKINF_LEARNED_MINUET_OF_FOREST);
Item_Give(play, ITEM_SONG_MINUET);
} else {
GivePlayerRandoRewardSheikSong(this, play, RC_SHEIK_IN_FOREST, 1, RG_MINUET_OF_FOREST);
return false;
} }
return true; Flags_SetEventChkInf(EVENTCHKINF_LEARNED_MINUET_OF_FOREST);
if (GameInteractor_Should(GI_VB_GIVE_ITEM_MINUET_OF_FOREST, true, NULL)) {
Item_Give(play, ITEM_SONG_MINUET);
}
if (GameInteractor_Should(GI_VB_PLAY_MINUET_OF_FOREST_CS, true, NULL)) {
return true;
}
} }
} }
return false; return false;
@ -353,16 +338,17 @@ s32 EnXc_BoleroCS(EnXc* this, PlayState* play) {
if ((posRot->pos.x > -784.0f) && (posRot->pos.x < -584.0f) && (posRot->pos.y > 447.0f) && if ((posRot->pos.x > -784.0f) && (posRot->pos.x < -584.0f) && (posRot->pos.y > 447.0f) &&
(posRot->pos.y < 647.0f) && (posRot->pos.z > -446.0f) && (posRot->pos.z < -246.0f) && (posRot->pos.y < 647.0f) && (posRot->pos.z > -446.0f) && (posRot->pos.z < -246.0f) &&
!Play_InCsMode(play)) { !Play_InCsMode(play)) {
if (!IS_RANDO) { if (GameInteractor_Should(GI_VB_PLAY_BOLERO_OF_FIRE_CS, true, NULL)) {
play->csCtx.segment = SEGMENTED_TO_VIRTUAL(&gDeathMountainCraterBoleroCs); play->csCtx.segment = SEGMENTED_TO_VIRTUAL(&gDeathMountainCraterBoleroCs);
gSaveContext.cutsceneTrigger = 1; gSaveContext.cutsceneTrigger = 1;
Flags_SetEventChkInf(EVENTCHKINF_LEARNED_BOLERO_OF_FIRE);
Item_Give(play, ITEM_SONG_BOLERO);
} else {
GivePlayerRandoRewardSheikSong(this, play, RC_SHEIK_IN_CRATER, 2, RG_BOLERO_OF_FIRE);
return false;
} }
return true; Flags_SetEventChkInf(EVENTCHKINF_LEARNED_BOLERO_OF_FIRE);
if (GameInteractor_Should(GI_VB_GIVE_ITEM_BOLERO_OF_FIRE, true, NULL)) {
Item_Give(play, ITEM_SONG_BOLERO);
}
if (GameInteractor_Should(GI_VB_PLAY_BOLERO_OF_FIRE_CS, true, NULL)) {
return true;
}
} }
return false; return false;
} }
@ -370,13 +356,8 @@ s32 EnXc_BoleroCS(EnXc* this, PlayState* play) {
} }
void EnXc_SetupSerenadeAction(EnXc* this, PlayState* play) { void EnXc_SetupSerenadeAction(EnXc* this, PlayState* play) {
if (IS_RANDO) {
this->action = SHEIK_ACTION_SERENADE;
return;
}
// Player is adult and does not have iron boots and has not learned Serenade // Player is adult and does not have iron boots and has not learned Serenade
if ((!CHECK_OWNED_EQUIP(EQUIP_TYPE_BOOTS, EQUIP_INV_BOOTS_IRON) && !Flags_GetEventChkInf(EVENTCHKINF_LEARNED_SERENADE_OF_WATER)) && LINK_IS_ADULT) { if (GameInteractor_Should(GI_VB_SHIEK_PREPARE_TO_GIVE_SERENADE_OF_WATER, (!CHECK_OWNED_EQUIP(EQUIP_TYPE_BOOTS, EQUIP_INV_BOOTS_IRON) && !Flags_GetEventChkInf(EVENTCHKINF_LEARNED_SERENADE_OF_WATER)) && LINK_IS_ADULT, NULL)) {
this->action = SHEIK_ACTION_SERENADE; this->action = SHEIK_ACTION_SERENADE;
osSyncPrintf("水のセレナーデ シーク誕生!!!!!!!!!!!!!!!!!!\n"); osSyncPrintf("水のセレナーデ シーク誕生!!!!!!!!!!!!!!!!!!\n");
} else { } else {
@ -389,22 +370,20 @@ s32 EnXc_SerenadeCS(EnXc* this, PlayState* play) {
if (this->actor.params == SHEIK_TYPE_SERENADE) { if (this->actor.params == SHEIK_TYPE_SERENADE) {
Player* player = GET_PLAYER(play); Player* player = GET_PLAYER(play);
s32 stateFlags = player->stateFlags1; s32 stateFlags = player->stateFlags1;
if (GameInteractor_Should(GI_VB_BE_ELIGIBLE_FOR_SERENADE_OF_WATER, CHECK_OWNED_EQUIP(EQUIP_TYPE_BOOTS, EQUIP_INV_BOOTS_IRON) && !Flags_GetEventChkInf(EVENTCHKINF_LEARNED_SERENADE_OF_WATER), NULL) &&
if (((CHECK_OWNED_EQUIP(EQUIP_TYPE_BOOTS, EQUIP_INV_BOOTS_IRON) && !IS_RANDO) || !(stateFlags & PLAYER_STATE1_IN_CUTSCENE) && !Play_InCsMode(play)) {
(Flags_GetTreasure(play, 2) && IS_RANDO)) && if (GameInteractor_Should(GI_VB_PLAY_SERENADE_OF_WATER_CS, true, NULL)) {
!Flags_GetEventChkInf(EVENTCHKINF_LEARNED_SERENADE_OF_WATER) && !(stateFlags & 0x20000000) &&
!Play_InCsMode(play)) {
if (!IS_RANDO) {
Cutscene_SetSegment(play, &gIceCavernSerenadeCs); Cutscene_SetSegment(play, &gIceCavernSerenadeCs);
gSaveContext.cutsceneTrigger = 1; gSaveContext.cutsceneTrigger = 1;
Flags_SetEventChkInf(EVENTCHKINF_LEARNED_SERENADE_OF_WATER); // Learned Serenade of Water Flag }
Flags_SetEventChkInf(EVENTCHKINF_LEARNED_SERENADE_OF_WATER); // Learned Serenade of Water Flag
if (GameInteractor_Should(GI_VB_GIVE_ITEM_SERENADE_OF_WATER, true, NULL)) {
Item_Give(play, ITEM_SONG_SERENADE); Item_Give(play, ITEM_SONG_SERENADE);
} else {
GivePlayerRandoRewardSheikSong(this, play, RC_SHEIK_IN_ICE_CAVERN, 4, RG_SERENADE_OF_WATER);
return false;
} }
osSyncPrintf("ブーツを取った!!!!!!!!!!!!!!!!!!\n"); osSyncPrintf("ブーツを取った!!!!!!!!!!!!!!!!!!\n");
return true; if (GameInteractor_Should(GI_VB_PLAY_SERENADE_OF_WATER_CS, true, NULL)) {
return true;
}
} }
osSyncPrintf("はやくブーツを取るべし!!!!!!!!!!!!!!!!!!\n"); osSyncPrintf("はやくブーツを取るべし!!!!!!!!!!!!!!!!!!\n");
return false; return false;
@ -415,7 +394,7 @@ s32 EnXc_SerenadeCS(EnXc* this, PlayState* play) {
void EnXc_DoNothing(EnXc* this, PlayState* play) { void EnXc_DoNothing(EnXc* this, PlayState* play) {
} }
void EnXc_RandoStand(EnXc* this, PlayState* play) { void SoH_EnXc_RandoStand(EnXc* this, PlayState* play) {
//Replaces Ganondorf Light Arrow hint. also stands in ToT //Replaces Ganondorf Light Arrow hint. also stands in ToT
if (play->sceneNum == SCENE_TEMPLE_OF_TIME) { if (play->sceneNum == SCENE_TEMPLE_OF_TIME) {
EnXc_ChangeAnimation(this, &gSheikArmsCrossedIdleAnim, ANIMMODE_LOOP, 0.0f, false); EnXc_ChangeAnimation(this, &gSheikArmsCrossedIdleAnim, ANIMMODE_LOOP, 0.0f, false);
@ -2209,22 +2188,21 @@ void EnXc_InitTempleOfTime(EnXc* this, PlayState* play) {
if (LINK_IS_ADULT) { if (LINK_IS_ADULT) {
if (!Flags_GetEventChkInf(EVENTCHKINF_SHEIK_SPAWNED_AT_MASTER_SWORD_PEDESTAL)) { if (!Flags_GetEventChkInf(EVENTCHKINF_SHEIK_SPAWNED_AT_MASTER_SWORD_PEDESTAL)) {
Flags_SetEventChkInf(EVENTCHKINF_SHEIK_SPAWNED_AT_MASTER_SWORD_PEDESTAL); Flags_SetEventChkInf(EVENTCHKINF_SHEIK_SPAWNED_AT_MASTER_SWORD_PEDESTAL);
play->csCtx.segment = SEGMENTED_TO_VIRTUAL(gTempleOfTimeFirstAdultCs); if (GameInteractor_Should(GI_VB_PLAY_SHIEK_BLOCK_MASTER_SWORD_CS, true, NULL)) {
gSaveContext.cutsceneTrigger = 1; play->csCtx.segment = SEGMENTED_TO_VIRTUAL(gTempleOfTimeFirstAdultCs);
gSaveContext.cutsceneTrigger = 1;
}
func_80B3EBF0(this, play); func_80B3EBF0(this, play);
} else if ((!Flags_GetEventChkInf(EVENTCHKINF_LEARNED_PRELUDE_OF_LIGHT) && (Flags_GetEventChkInf(EVENTCHKINF_USED_FOREST_TEMPLE_BLUE_WARP)) && } else if (GameInteractor_Should(GI_VB_BE_ELIGIBLE_FOR_PRELUDE_OF_LIGHT, !Flags_GetEventChkInf(EVENTCHKINF_LEARNED_PRELUDE_OF_LIGHT) && Flags_GetEventChkInf(EVENTCHKINF_USED_FOREST_TEMPLE_BLUE_WARP), NULL)) {
!IS_RANDO) || Flags_SetEventChkInf(EVENTCHKINF_LEARNED_PRELUDE_OF_LIGHT);
(!Flags_GetEventChkInf(EVENTCHKINF_LEARNED_PRELUDE_OF_LIGHT) && CHECK_QUEST_ITEM(QUEST_MEDALLION_FOREST) && if (GameInteractor_Should(GI_VB_GIVE_ITEM_PRELUDE_OF_LIGHT, true, NULL)) {
IS_RANDO)) {
if (!IS_RANDO) {
Flags_SetEventChkInf(EVENTCHKINF_LEARNED_PRELUDE_OF_LIGHT);
Item_Give(play, ITEM_SONG_PRELUDE); Item_Give(play, ITEM_SONG_PRELUDE);
}
if (GameInteractor_Should(GI_VB_PLAY_PRELUDE_OF_LIGHT_CS, true, NULL)) {
play->csCtx.segment = SEGMENTED_TO_VIRTUAL(gTempleOfTimePreludeCs); play->csCtx.segment = SEGMENTED_TO_VIRTUAL(gTempleOfTimePreludeCs);
gSaveContext.cutsceneTrigger = 1; gSaveContext.cutsceneTrigger = 1;
this->action = SHEIK_ACTION_30;
} else {
GivePlayerRandoRewardSheikSong(this, play, RC_SHEIK_AT_TEMPLE, 0x20, RG_PRELUDE_OF_LIGHT);
} }
this->action = SHEIK_ACTION_30; // Not sure what this does exactly
} else if (!Flags_GetEventChkInf(EVENTCHKINF_LEARNED_PRELUDE_OF_LIGHT)) { } else if (!Flags_GetEventChkInf(EVENTCHKINF_LEARNED_PRELUDE_OF_LIGHT)) {
func_80B3C9EC(this); func_80B3C9EC(this);
} else { } else {
@ -2378,14 +2356,6 @@ void EnXc_Update(Actor* thisx, PlayState* play) {
EnXc* this = (EnXc*)thisx; EnXc* this = (EnXc*)thisx;
s32 action = this->action; s32 action = this->action;
if (this->actor.params == SHEIK_TYPE_9) {
if (IS_RANDO && LINK_IS_ADULT) {
if (CHECK_QUEST_ITEM(QUEST_MEDALLION_FOREST) && !Flags_GetEventChkInf(EVENTCHKINF_LEARNED_PRELUDE_OF_LIGHT)) {
GivePlayerRandoRewardSheikSong(this, play, RC_SHEIK_AT_TEMPLE, 0x20, RG_PRELUDE_OF_LIGHT);
}
}
}
if ((action < 0) || (action >= ARRAY_COUNT(sActionFuncs)) || (sActionFuncs[action] == NULL)) { if ((action < 0) || (action >= ARRAY_COUNT(sActionFuncs)) || (sActionFuncs[action] == NULL)) {
osSyncPrintf(VT_FGCOL(RED) "メインモードがおかしい!!!!!!!!!!!!!!!!!!!!!!!!!\n" VT_RST); osSyncPrintf(VT_FGCOL(RED) "メインモードがおかしい!!!!!!!!!!!!!!!!!!!!!!!!!\n" VT_RST);
} else { } else {
@ -2433,7 +2403,7 @@ void EnXc_Init(Actor* thisx, PlayState* play) {
EnXc_DoNothing(this, play); EnXc_DoNothing(this, play);
break; break;
case SHEIK_TYPE_RANDO: case SHEIK_TYPE_RANDO:
EnXc_RandoStand(this, play); SoH_EnXc_RandoStand(this, play);
break; break;
default: default:
osSyncPrintf(VT_FGCOL(RED) " En_Oa2 の arg_data がおかしい!!!!!!!!!!!!!!!!!!!!!!!!!\n" VT_RST); osSyncPrintf(VT_FGCOL(RED) " En_Oa2 の arg_data がおかしい!!!!!!!!!!!!!!!!!!!!!!!!!\n" VT_RST);

View File

@ -10,6 +10,7 @@
#include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h" #include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h"
#include "objects/object_zl2/object_zl2.h" #include "objects/object_zl2/object_zl2.h"
#include "objects/object_zl2_anime1/object_zl2_anime1.h" #include "objects/object_zl2_anime1/object_zl2_anime1.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#define FLAGS ACTOR_FLAG_UPDATE_WHILE_CULLED #define FLAGS ACTOR_FLAG_UPDATE_WHILE_CULLED
@ -633,7 +634,9 @@ void EnZl2_GiveLightArrows(EnZl2* this, PlayState* play) {
posY = player->actor.world.pos.y + 80.0f; posY = player->actor.world.pos.y + 80.0f;
posZ = player->actor.world.pos.z; posZ = player->actor.world.pos.z;
Actor_Spawn(&play->actorCtx, play, ACTOR_DEMO_EFFECT, posX, posY, posZ, 0, 0, 0, 0x17, true); Actor_Spawn(&play->actorCtx, play, ACTOR_DEMO_EFFECT, posX, posY, posZ, 0, 0, 0, 0x17, true);
Item_Give(play, ITEM_ARROW_LIGHT); if (GameInteractor_Should(GI_VB_GIVE_ITEM_LIGHT_ARROW, true, NULL)) {
Item_Give(play, ITEM_ARROW_LIGHT);
}
this->unk_244 = 1; this->unk_244 = 1;
} }
} }

View File

@ -7,6 +7,7 @@
#include "z_en_zl4.h" #include "z_en_zl4.h"
#include "objects/object_zl4/object_zl4.h" #include "objects/object_zl4/object_zl4.h"
#include "scenes/indoors/nakaniwa/nakaniwa_scene.h" #include "scenes/indoors/nakaniwa/nakaniwa_scene.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY | ACTOR_FLAG_UPDATE_WHILE_CULLED) #define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_FRIENDLY | ACTOR_FLAG_UPDATE_WHILE_CULLED)
@ -227,20 +228,6 @@ u16 EnZl4_GetText(PlayState* play, Actor* thisx) {
return ret; return ret;
} }
void GivePlayerRandoRewardZeldaChild(EnZl4* zelda, PlayState* play, RandomizerCheck check) {
if (zelda->actor.parent != NULL && zelda->actor.parent->id == GET_PLAYER(play)->actor.id &&
!Flags_GetTreasure(play, 0x1E)) {
Flags_SetTreasure(play, 0x1E);
} else if (!Flags_GetTreasure(play, 0x1E) && !Randomizer_GetSettingValue(RSK_SKIP_CHILD_ZELDA) && Actor_TextboxIsClosing(&zelda->actor, play) &&
(play->msgCtx.textId == 0x703C || play->msgCtx.textId == 0x703D)) {
GetItemEntry getItemEntry = Randomizer_GetItemFromKnownCheck(check, GI_LETTER_ZELDA);
GiveItemEntryFromActor(&zelda->actor, play, getItemEntry, 10000.0f, 100.0f);
} else if (Flags_GetTreasure(play, 0x1E) && !Player_InBlockingCsMode(play, GET_PLAYER(play))) {
gSaveContext.unk_13EE = 0x32;
Flags_SetEventChkInf(EVENTCHKINF_OBTAINED_ZELDAS_LETTER);
}
}
s16 func_80B5B9B0(PlayState* play, Actor* thisx) { s16 func_80B5B9B0(PlayState* play, Actor* thisx) {
EnZl4* this = (EnZl4*)thisx; EnZl4* this = (EnZl4*)thisx;
@ -389,12 +376,6 @@ void EnZl4_Init(Actor* thisx, PlayState* play) {
this->actor.textId = -1; this->actor.textId = -1;
this->eyeExpression = this->mouthExpression = ZL4_MOUTH_NEUTRAL; this->eyeExpression = this->mouthExpression = ZL4_MOUTH_NEUTRAL;
if (IS_RANDO) {
Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ZL4_ANIM_0);
this->actionFunc = EnZl4_Idle;
return;
}
if (gSaveContext.sceneSetupIndex >= 4) { if (gSaveContext.sceneSetupIndex >= 4) {
Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ZL4_ANIM_0); Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ZL4_ANIM_0);
this->actionFunc = EnZl4_TheEnd; this->actionFunc = EnZl4_TheEnd;
@ -1127,14 +1108,16 @@ s32 EnZl4_CsMakePlan(EnZl4* this, PlayState* play) {
Camera_ChangeSetting(GET_ACTIVE_CAM(play), 1); Camera_ChangeSetting(GET_ACTIVE_CAM(play), 1);
this->talkState = 7; this->talkState = 7;
play->talkWithPlayer(play, &this->actor); play->talkWithPlayer(play, &this->actor);
func_8002F434(&this->actor, play, GI_LETTER_ZELDA, fabsf(this->actor.xzDistToPlayer) + 1.0f, if (GameInteractor_Should(GI_VB_GIVE_ITEM_ZELDAS_LETTER, true, NULL)) {
fabsf(this->actor.yDistToPlayer) + 1.0f); func_8002F434(&this->actor, play, GI_LETTER_ZELDA, fabsf(this->actor.xzDistToPlayer) + 1.0f,
fabsf(this->actor.yDistToPlayer) + 1.0f);
}
play->msgCtx.stateTimer = 4; play->msgCtx.stateTimer = 4;
play->msgCtx.msgMode = MSGMODE_TEXT_CLOSING; play->msgCtx.msgMode = MSGMODE_TEXT_CLOSING;
} }
break; break;
case 7: case 7:
if (Actor_HasParent(&this->actor, play)) { if (Actor_HasParent(&this->actor, play) || !GameInteractor_Should(GI_VB_GIVE_ITEM_ZELDAS_LETTER, true, NULL)) {
Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ZL4_ANIM_0); Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ZL4_ANIM_0);
this->talkState++; this->talkState++;
} else { } else {
@ -1225,11 +1208,6 @@ void EnZl4_Idle(EnZl4* this, PlayState* play) {
Npc_UpdateTalking(play, &this->actor, &this->interactInfo.talkState, this->collider.dim.radius + 60.0f, Npc_UpdateTalking(play, &this->actor, &this->interactInfo.talkState, this->collider.dim.radius + 60.0f,
EnZl4_GetText, func_80B5B9B0); EnZl4_GetText, func_80B5B9B0);
func_80B5BB78(this, play); func_80B5BB78(this, play);
if (IS_RANDO) {
GivePlayerRandoRewardZeldaChild(this, play, RC_HC_ZELDAS_LETTER);
return;
}
} }
void EnZl4_TheEnd(EnZl4* this, PlayState* play) { void EnZl4_TheEnd(EnZl4* this, PlayState* play) {

View File

@ -31,4 +31,9 @@ typedef struct EnZl4 {
/* 0x0284 */ Vec3s morphTable[18]; /* 0x0284 */ Vec3s morphTable[18];
} EnZl4; // size = 0x02F0 } EnZl4; // size = 0x02F0
s16 func_80B5B9B0(PlayState* play, Actor* actor);
void func_80B5BB78(EnZl4* enZl4, PlayState* play);
void EnZl4_Cutscene(EnZl4* enZl4, PlayState* play);
s32 EnZl4_SetNextAnim(EnZl4* enZl4, s32 nextAnim);
#endif #endif

View File

@ -59,12 +59,7 @@ void ItemBHeart_Update(Actor* thisx, PlayState* play) {
Flags_SetCollectible(play, 0x1F); Flags_SetCollectible(play, 0x1F);
Actor_Kill(&this->actor); Actor_Kill(&this->actor);
} else { } else {
if (!IS_RANDO) { func_8002F434(&this->actor, play, GI_HEART_CONTAINER_2, 30.0f, 40.0f);
func_8002F434(&this->actor, play, GI_HEART_CONTAINER_2, 30.0f, 40.0f);
} else {
GetItemEntry getItemEntry = Randomizer_GetItemFromActor(this->actor.id, play->sceneNum, this->actor.params, GI_HEART_CONTAINER_2);
GiveItemEntryFromActor(&this->actor, play, getItemEntry, 30.0f, 40.0f);
}
} }
} }
@ -98,23 +93,18 @@ void ItemBHeart_Draw(Actor* thisx, PlayState* play) {
actorIt = actorIt->next; actorIt = actorIt->next;
} }
if (IS_RANDO) { if (flag) {
GetItemEntry_Draw(play, Randomizer_GetItemFromActor(this->actor.id, Gfx_SetupDL_25Xlu(play->state.gfxCtx);
play->sceneNum,this->actor.params, GI_HEART_CONTAINER_2)); gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(play->state.gfxCtx),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_XLU_DISP++, gGiHeartBorderDL);
gSPDisplayList(POLY_XLU_DISP++, gGiHeartContainerDL);
} else { } else {
if (flag) { Gfx_SetupDL_25Opa(play->state.gfxCtx);
Gfx_SetupDL_25Xlu(play->state.gfxCtx); gSPMatrix(POLY_OPA_DISP++, MATRIX_NEWMTX(play->state.gfxCtx),
gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(POLY_OPA_DISP++, gGiHeartBorderDL);
gSPDisplayList(POLY_XLU_DISP++, gGiHeartBorderDL); gSPDisplayList(POLY_OPA_DISP++, gGiHeartContainerDL);
gSPDisplayList(POLY_XLU_DISP++, gGiHeartContainerDL);
} else {
Gfx_SetupDL_25Opa(play->state.gfxCtx);
gSPMatrix(POLY_OPA_DISP++, MATRIX_NEWMTX(play->state.gfxCtx),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_OPA_DISP++, gGiHeartBorderDL);
gSPDisplayList(POLY_OPA_DISP++, gGiHeartContainerDL);
}
} }
CLOSE_DISPS(play->state.gfxCtx); CLOSE_DISPS(play->state.gfxCtx);

View File

@ -13,6 +13,11 @@ typedef struct ItemBHeart {
/* 0x015C */ char unk_15C[0x8]; /* 0x015C */ char unk_15C[0x8];
/* 0x0164 */ s16 unk_164; /* 0x0164 */ s16 unk_164;
/* 0x0166 */ char unk_166[0x6]; /* 0x0166 */ char unk_166[0x6];
// #region SOH [Randomizer] Cached for drawing for performance
/* */ GetItemEntry sohItemEntry;
// #endregion
} ItemBHeart; // size = 0x016C } ItemBHeart; // size = 0x016C
void func_80B85264(ItemBHeart* itemBHeart, PlayState* play);
#endif #endif

View File

@ -6,6 +6,7 @@
#include "z_item_ocarina.h" #include "z_item_ocarina.h"
#include "scenes/overworld/spot00/spot00_scene.h" #include "scenes/overworld/spot00/spot00_scene.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#define FLAGS ACTOR_FLAG_UPDATE_WHILE_CULLED #define FLAGS ACTOR_FLAG_UPDATE_WHILE_CULLED
@ -169,31 +170,27 @@ void ItemOcarina_DoNothing(ItemOcarina* this, PlayState* play) {
void ItemOcarina_StartSoTCutscene(ItemOcarina* this, PlayState* play) { void ItemOcarina_StartSoTCutscene(ItemOcarina* this, PlayState* play) {
if (Actor_TextboxIsClosing(&this->actor, play)) { if (Actor_TextboxIsClosing(&this->actor, play)) {
if (!IS_RANDO) { play->csCtx.segment = SEGMENTED_TO_VIRTUAL(gHyruleFieldZeldaSongOfTimeCs);
play->csCtx.segment = SEGMENTED_TO_VIRTUAL(gHyruleFieldZeldaSongOfTimeCs); gSaveContext.cutsceneTrigger = 1;
gSaveContext.cutsceneTrigger = 1;
} else {
play->transitionTrigger = TRANS_TRIGGER_START;
play->transitionType = TRANS_TYPE_FADE_WHITE;
gSaveContext.nextTransitionType = TRANS_TYPE_FADE_WHITE;
play->nextEntranceIndex = ENTR_HYRULE_FIELD_16;
gSaveContext.nextCutsceneIndex = 0;
}
} }
} }
void ItemOcarina_WaitInWater(ItemOcarina* this, PlayState* play) { void ItemOcarina_WaitInWater(ItemOcarina* this, PlayState* play) {
if (Actor_HasParent(&this->actor, play)) { if (
Actor_HasParent(&this->actor, play) ||
(
!GameInteractor_Should(GI_VB_GIVE_ITEM_OCARINA_OF_TIME, true, NULL) &&
(this->actor.xzDistToPlayer < 20.0f) && (fabsf(this->actor.yDistToPlayer) < 10.0f) &&
GET_PLAYER(play)->stateFlags2 & PLAYER_STATE2_DIVING
)
) {
Flags_SetEventChkInf(EVENTCHKINF_OBTAINED_OCARINA_OF_TIME); Flags_SetEventChkInf(EVENTCHKINF_OBTAINED_OCARINA_OF_TIME);
Flags_SetSwitch(play, 3); Flags_SetSwitch(play, 3);
this->actionFunc = ItemOcarina_StartSoTCutscene; this->actionFunc = ItemOcarina_StartSoTCutscene;
this->actor.draw = NULL; this->actor.draw = NULL;
} else { } else {
if (!IS_RANDO) { if (GameInteractor_Should(GI_VB_GIVE_ITEM_OCARINA_OF_TIME, true, NULL)) {
func_8002F434(&this->actor, play, GI_OCARINA_OOT, 30.0f, 50.0f); func_8002F434(&this->actor, play, GI_OCARINA_OOT, 30.0f, 50.0f);
} else {
GetItemEntry getItemEntry = Randomizer_GetItemFromKnownCheck(RC_HF_OCARINA_OF_TIME_ITEM, GI_OCARINA_OOT);
GiveItemEntryFromActor(&this->actor, play, getItemEntry, 30.0f, 50.0f);
} }
if ((play->gameplayFrames & 13) == 0) { if ((play->gameplayFrames & 13) == 0) {

View File

@ -7,6 +7,7 @@
#include "z_obj_switch.h" #include "z_obj_switch.h"
#include "objects/gameplay_dangeon_keep/gameplay_dangeon_keep.h" #include "objects/gameplay_dangeon_keep/gameplay_dangeon_keep.h"
#include "vt.h" #include "vt.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#define FLAGS ACTOR_FLAG_UPDATE_WHILE_CULLED #define FLAGS ACTOR_FLAG_UPDATE_WHILE_CULLED
@ -249,10 +250,12 @@ void ObjSwitch_SetOn(ObjSwitch* this, PlayState* play) {
subType = (this->dyna.actor.params >> 4 & 7); subType = (this->dyna.actor.params >> 4 & 7);
Flags_SetSwitch(play, (this->dyna.actor.params >> 8 & 0x3F)); Flags_SetSwitch(play, (this->dyna.actor.params >> 8 & 0x3F));
if (subType == 0 || subType == 4) { if (GameInteractor_Should(GI_VB_PLAY_ONEPOINT_ACTOR_CS, true, this)) {
OnePointCutscene_AttentionSetSfx(play, &this->dyna.actor, NA_SE_SY_CORRECT_CHIME); if (subType == 0 || subType == 4) {
} else { OnePointCutscene_AttentionSetSfx(play, &this->dyna.actor, NA_SE_SY_CORRECT_CHIME);
OnePointCutscene_AttentionSetSfx(play, &this->dyna.actor, NA_SE_SY_TRE_BOX_APPEAR); } else {
OnePointCutscene_AttentionSetSfx(play, &this->dyna.actor, NA_SE_SY_TRE_BOX_APPEAR);
}
} }
this->cooldownOn = true; this->cooldownOn = true;
@ -266,7 +269,9 @@ void ObjSwitch_SetOff(ObjSwitch* this, PlayState* play) {
Flags_UnsetSwitch(play, (this->dyna.actor.params >> 8 & 0x3F)); Flags_UnsetSwitch(play, (this->dyna.actor.params >> 8 & 0x3F));
if ((this->dyna.actor.params >> 4 & 7) == 1) { if ((this->dyna.actor.params >> 4 & 7) == 1) {
OnePointCutscene_AttentionSetSfx(play, &this->dyna.actor, NA_SE_SY_TRE_BOX_APPEAR); if (GameInteractor_Should(GI_VB_PLAY_ONEPOINT_ACTOR_CS, true, this)) {
OnePointCutscene_AttentionSetSfx(play, &this->dyna.actor, NA_SE_SY_TRE_BOX_APPEAR);
}
this->cooldownOn = true; this->cooldownOn = true;
} }
} }

View File

@ -31,6 +31,7 @@
#include "soh/Enhancements/randomizer/randomizer_grotto.h" #include "soh/Enhancements/randomizer/randomizer_grotto.h"
#include "soh/Enhancements/randomizer/fishsanity.h" #include "soh/Enhancements/randomizer/fishsanity.h"
#include "soh/frame_interpolation.h" #include "soh/frame_interpolation.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
@ -6658,7 +6659,14 @@ s32 func_8083E5A8(Player* this, PlayState* play) {
uint8_t showItemCutscene = play->sceneNum == SCENE_BOMBCHU_BOWLING_ALLEY || Item_CheckObtainability(giEntry.itemId) == ITEM_NONE || IS_RANDO; uint8_t showItemCutscene = play->sceneNum == SCENE_BOMBCHU_BOWLING_ALLEY || Item_CheckObtainability(giEntry.itemId) == ITEM_NONE || IS_RANDO;
// Only skip cutscenes for drops when they're items/consumables from bushes/rocks/enemies. // Only skip cutscenes for drops when they're items/consumables from bushes/rocks/enemies.
uint8_t isDropToSkip = (interactedActor->id == ACTOR_EN_ITEM00 && interactedActor->params != 6 && interactedActor->params != 17) || uint8_t isDropToSkip =
(
interactedActor->id == ACTOR_EN_ITEM00 &&
interactedActor->params != ITEM00_HEART_PIECE &&
interactedActor->params != ITEM00_SMALL_KEY &&
interactedActor->params != ITEM00_SOH_GIVE_ITEM_ENTRY &&
interactedActor->params != ITEM00_SOH_GIVE_ITEM_ENTRY_GI
) ||
interactedActor->id == ACTOR_EN_KAREBABA || interactedActor->id == ACTOR_EN_KAREBABA ||
interactedActor->id == ACTOR_EN_DEKUBABA; interactedActor->id == ACTOR_EN_DEKUBABA;
@ -6716,8 +6724,10 @@ s32 func_8083E5A8(Player* this, PlayState* play) {
} }
func_80836898(play, this, func_8083A434); func_80836898(play, this, func_8083A434);
if (GameInteractor_Should(GI_VB_GIVE_ITEM_FROM_CHEST, true, &giEntry)) {
this->stateFlags1 |= PLAYER_STATE1_GETTING_ITEM | PLAYER_STATE1_ITEM_OVER_HEAD | PLAYER_STATE1_IN_CUTSCENE; this->stateFlags1 |= PLAYER_STATE1_GETTING_ITEM | PLAYER_STATE1_ITEM_OVER_HEAD | PLAYER_STATE1_IN_CUTSCENE;
func_8083AE40(this, giEntry.objectId); func_8083AE40(this, giEntry.objectId);
}
this->actor.world.pos.x = this->actor.world.pos.x =
chest->dyna.actor.world.pos.x - (Math_SinS(chest->dyna.actor.shape.rot.y) * 29.4343f); chest->dyna.actor.world.pos.x - (Math_SinS(chest->dyna.actor.shape.rot.y) * 29.4343f);
this->actor.world.pos.z = this->actor.world.pos.z =
@ -10087,7 +10097,7 @@ void Player_Init(Actor* thisx, PlayState* play2) {
if ((sp50 == 0) || (sp50 < -1)) { if ((sp50 == 0) || (sp50 < -1)) {
titleFileSize = scene->titleFile.vromEnd - scene->titleFile.vromStart; titleFileSize = scene->titleFile.vromEnd - scene->titleFile.vromStart;
if (gSaveContext.showTitleCard) { if (GameInteractor_Should(GI_VB_SHOW_TITLE_CARD, gSaveContext.showTitleCard, NULL)) {
if ((gSaveContext.sceneSetupIndex < 4) && if ((gSaveContext.sceneSetupIndex < 4) &&
(gEntranceTable[((void)0, gSaveContext.entranceIndex) + ((void)0, gSaveContext.sceneSetupIndex)].field & (gEntranceTable[((void)0, gSaveContext.entranceIndex) + ((void)0, gSaveContext.sceneSetupIndex)].field &
ENTRANCE_INFO_DISPLAY_TITLE_CARD_FLAG) && ENTRANCE_INFO_DISPLAY_TITLE_CARD_FLAG) &&
@ -13393,7 +13403,7 @@ s32 func_8084DFF4(PlayState* play, Player* this) {
play->msgCtx.msgMode = MSGMODE_TEXT_DONE; play->msgCtx.msgMode = MSGMODE_TEXT_DONE;
} else { } else {
if (Message_GetState(&play->msgCtx) == TEXT_STATE_CLOSING) { if (Message_GetState(&play->msgCtx) == TEXT_STATE_CLOSING) {
if (this->getItemId == GI_GAUNTLETS_SILVER && !IS_RANDO) { if (GameInteractor_Should(GI_VB_PLAY_NABOORU_CAPTURED_CS, this->getItemId == GI_GAUNTLETS_SILVER, NULL)) {
play->nextEntranceIndex = ENTR_DESERT_COLOSSUS_0; play->nextEntranceIndex = ENTR_DESERT_COLOSSUS_0;
play->transitionTrigger = TRANS_TRIGGER_START; play->transitionTrigger = TRANS_TRIGGER_START;
gSaveContext.nextCutsceneIndex = 0xFFF1; gSaveContext.nextCutsceneIndex = 0xFFF1;
@ -13409,11 +13419,13 @@ s32 func_8084DFF4(PlayState* play, Player* this) {
this->unk_862 = 0; this->unk_862 = 0;
} }
// #region SOH [Randomizer] TODO Better Ice trap handling?
if (this->getItemEntry.itemId == RG_ICE_TRAP && this->getItemEntry.modIndex == MOD_RANDOMIZER) { if (this->getItemEntry.itemId == RG_ICE_TRAP && this->getItemEntry.modIndex == MOD_RANDOMIZER) {
this->unk_862 = 0; this->unk_862 = 0;
gSaveContext.pendingIceTrapCount++; gSaveContext.pendingIceTrapCount++;
Player_SetPendingFlag(this, play); Player_SetPendingFlag(this, play);
} }
// #endregion
this->getItemId = GI_NONE; this->getItemId = GI_NONE;
this->getItemEntry = (GetItemEntry)GET_ITEM_NONE; this->getItemEntry = (GetItemEntry)GET_ITEM_NONE;
@ -15675,6 +15687,7 @@ void func_8085283C(PlayState* play, Player* this, CsCmdActorAction* arg2) {
if (LinkAnimation_Update(play, &this->skelAnime)) { if (LinkAnimation_Update(play, &this->skelAnime)) {
func_80852944(play, this, arg2); func_80852944(play, this, arg2);
} else if (this->unk_850 == 0) { } else if (this->unk_850 == 0) {
// This is when link picks up the sword in the Ganon fight
Item_Give(play, ITEM_SWORD_MASTER); Item_Give(play, ITEM_SWORD_MASTER);
func_80846720(play, this, 0); func_80846720(play, this, 0);
} else { } else {