diff --git a/code/include/z3D/z3D.h b/code/include/z3D/z3D.h index 9f654ae3..2fefda29 100644 --- a/code/include/z3D/z3D.h +++ b/code/include/z3D/z3D.h @@ -536,13 +536,15 @@ typedef struct GameState { /* 0x04 */ void (*main)(struct GameState*); /* 0x08 */ void (*destroy)(struct GameState*); // "cleanup" /* 0x0C */ void (*init)(struct GameState*); - // TODO + /* 0x10 */ u32 size; + /* 0x14 */ char unk_14[0xED]; + /* 0x101*/ u8 running; } GameState; +_Static_assert(sizeof(GameState) == 0x104, "GameState size"); // Global Context (ram start: 0871E840) typedef struct GlobalContext { - // /* 0x0000 */ GameState state; - /* 0x0000 */ char unk_0[0x0104]; + /* 0x0000 */ GameState state; /* 0x0104 */ s16 sceneNum; /* 0x0106 */ char unk_106[0x0012]; /* 0x0118 */ SubGlobalContext_118 sub118; diff --git a/code/src/actor.c b/code/src/actor.c index 34810b6d..56dc9bea 100644 --- a/code/src/actor.c +++ b/code/src/actor.c @@ -62,6 +62,7 @@ #include "shabom.h" #include "anubis.h" #include "link_puppet.h" +#include "fishing.h" #define OBJECT_GI_KEY 170 #define OBJECT_GI_BOSSKEY 185 @@ -154,6 +155,10 @@ void Actor_Init() { gActorOverlayTable[0xF1].initInfo->destroy = ItemOcarina_rDestroy; gActorOverlayTable[0xF1].initInfo->draw = ItemOcarina_rDraw; + gActorOverlayTable[0xFE].initInfo->init = Fishing_rInit; + gActorOverlayTable[0xFE].initInfo->update = Fishing_rUpdateFish; + gActorOverlayTable[0xFE].initInfo->draw = Fishing_rDrawFish; + gActorOverlayTable[0xFF].initInfo->update = ObjOshihiki_rUpdate; gActorOverlayTable[0x104].initInfo->init = BgSpot01Idomizu_rInit; diff --git a/code/src/fishing.c b/code/src/fishing.c index 639cfd92..7be1b552 100644 --- a/code/src/fishing.c +++ b/code/src/fishing.c @@ -1,5 +1,40 @@ #include "z3D/z3D.h" +#include "fishing.h" +#include "models.h" +#include "settings.h" u32 isFishing(void) { return gSaveContext.equips.buttonItems[0] == ITEM_FISHING_POLE && gGlobalContext->sceneNum == 73; } + +static s32 rewardObtained(void) { + u8 obtainedRewardFlag = gSaveContext.linkAge == AGE_ADULT ? 0x8 : 0x4; + return gSaveContext.fishingStats.flags & obtainedRewardFlag; +} + +void Fishing_rInit(Actor* thisx, GlobalContext* globalCtx) { + u16 baseItemId = gSaveContext.linkAge == AGE_ADULT ? GI_SCALE_GOLD : GI_HEART_PIECE; + Fishing_Init(thisx, globalCtx); + if (gSettingsContext.fishingHints && thisx->params == EN_FISH_AQUARIUM && !rewardObtained()) { + Model_SpawnByActor(thisx, globalCtx, baseItemId); + } +} + +void Fishing_rUpdateFish(Actor* thisx, GlobalContext* globalCtx) { + Fishing_UpdateFish(thisx, globalCtx); + if (gSettingsContext.fishingHints && thisx->params == EN_FISH_AQUARIUM && !rewardObtained()) { + thisx->textId = 0x40AE; // custom text, overrides vanilla "pond owner" record text + } +} + +void Fishing_rDrawFish(Actor* thisx, GlobalContext* globalCtx) { + static s16 sPrizeRotation = 0x8000; // used because shape.rot.y is reset between update cycles + + if (gSettingsContext.fishingHints && thisx->params == EN_FISH_AQUARIUM && !rewardObtained()) { + sPrizeRotation += 0x100; + thisx->shape.rot.y = sPrizeRotation; + Model_DrawByActor(thisx); + } else { + Fishing_DrawFish(thisx, globalCtx); + } +} diff --git a/code/src/fishing.h b/code/src/fishing.h new file mode 100644 index 00000000..cf4867c3 --- /dev/null +++ b/code/src/fishing.h @@ -0,0 +1,16 @@ +#ifndef _FISHING_H_ +#define _FISHING_H_ + +#include "z3D/z3D.h" + +#define Fishing_Init ((ActorFunc)GAME_ADDR(0x1C0AD8)) +#define Fishing_UpdateFish ((ActorFunc)GAME_ADDR(0x1F9ACC)) +#define Fishing_DrawFish ((ActorFunc)GAME_ADDR(0x1F98B4)) + +#define EN_FISH_AQUARIUM 200 // param for record fish in tank. + +void Fishing_rInit(Actor* thisx, GlobalContext* globalCtx); +void Fishing_rUpdateFish(Actor* thisx, GlobalContext* globalCtx); +void Fishing_rDrawFish(Actor* thisx, GlobalContext* globalCtx); + +#endif //_FISHING_H_ diff --git a/code/src/main.c b/code/src/main.c index ff7181cc..8b73987b 100644 --- a/code/src/main.c +++ b/code/src/main.c @@ -74,4 +74,8 @@ void after_GlobalContext_Update() { } Multiplayer_Sync_Update(); + + if (gGlobalContext->state.running == 0) { + Model_DestroyAll(); + } } diff --git a/code/src/settings.h b/code/src/settings.h index 71d43bc5..bc4a79a9 100644 --- a/code/src/settings.h +++ b/code/src/settings.h @@ -564,6 +564,7 @@ typedef struct { u8 sheikHints; u8 dampeHint; u8 skulltulaHints; + u8 fishingHints; u8 compassesShowReward; u8 compassesShowWotH; u8 mapsShowDungeonMode; diff --git a/source/custom_messages.cpp b/source/custom_messages.cpp index 3f22c6e5..556226b0 100644 --- a/source/custom_messages.cpp +++ b/source/custom_messages.cpp @@ -1082,6 +1082,16 @@ void CreateAlwaysIncludedMessages() { }; CreateMessageFromTextObject(0x9003, 0, 2, 3, AddColorsAndFormat(triforceMsg, { QM_RED, QM_RED })); } + + if (Settings::FishingHints) { + Text aquariumText = + Text{ /*english*/ "You can have this if you catch a fish to put in the aquarium.", + /*french */ "Tu peux avoir ce qu'il y a dans cet aquarium si tu pêches un poisson pour le remplacer.", + /*spanish*/ "Puedes tener esto si pescas un pez para ponerlo en el acuario.", + /*italian*/ "Puoi avere questo se catturi un pesce da mettere nell'acquario.", + /*german */ "Das kannst du haben, wenn du einen Fisch für das Aquarium fängst." }; + CreateMessageFromTextObject(0x40AE, 0, 2, 3, AddColorsAndFormat(aquariumText, {})); + } } std::vector CreateBaseCompassTexts() { diff --git a/source/descriptions.cpp b/source/descriptions.cpp index ac2a292d..be14c508 100644 --- a/source/descriptions.cpp +++ b/source/descriptions.cpp @@ -850,6 +850,8 @@ string_view dampeHintDesc = "Reading Dampe's diary will reveal the l string_view skulltulaHintDesc = "Talking to a cursed House of Skulltula resident\n"// "will tell you the reward they will give you for\n"// "removing their curse."; // +string_view fishingHintsDesc = "The aquarium at the fishing pond will show what\n"// + "reward you can win as your current age."; // /*------------------------------ // | MAP AND COMPASS GIVES INFO | // ------------------------------*/ // diff --git a/source/descriptions.hpp b/source/descriptions.hpp index 3fac049e..84781ae8 100644 --- a/source/descriptions.hpp +++ b/source/descriptions.hpp @@ -275,6 +275,7 @@ extern string_view totAltarHintsDesc; extern string_view ganonHintsDesc; extern string_view dampeHintDesc; extern string_view skulltulaHintDesc; +extern string_view fishingHintsDesc; extern string_view compassesShowRewardsDesc; extern string_view compassesShowWotHDesc; diff --git a/source/settings.cpp b/source/settings.cpp index 618d0a0a..a6f914be 100644 --- a/source/settings.cpp +++ b/source/settings.cpp @@ -339,6 +339,7 @@ Option ToTAltarHints = Option::Bool(2, "Temple of Time Altar",{"Off", "On" Option GanonHints = Option::Bool(2, "Ganondorf", {"Off", "On"}, {ganonHintsDesc}); Option DampeHint = Option::Bool(2, "Dampe's Diary", {"Off", "On"}, {dampeHintDesc}); Option SkulltulaHints = Option::Bool(2, "House of Skulltula", {"Off", "On"}, {skulltulaHintDesc}); +Option FishingHints = Option::Bool(2, "Fishing Prizes", {"Off", "On"}, {fishingHintsDesc}); Option ClearerHints = Option::U8 ("Hint Clarity", {"Obscure", "Ambiguous", "Clear"}, {obscureHintsDesc, ambiguousHintsDesc, clearHintsDesc}); Option CompassesShowReward = Option::U8 ("Compasses Show Rewards", {"No", "Yes"}, {compassesShowRewardsDesc}); Option CompassesShowWotH = Option::U8 ("Compasses Show WotH", {"No", "Yes"}, {compassesShowWotHDesc}, OptionCategory::Setting, ON); @@ -360,6 +361,7 @@ std::vector