Various cleanup

This commit is contained in:
Garrett Cox
2024-12-16 13:22:09 -06:00
committed by Malkierian
parent 06aefb30f7
commit d37a68c7cc
22 changed files with 657 additions and 274 deletions

View File

@@ -66,6 +66,7 @@ static void DrawMenu() {
if (UIWidgets::EnhancementCheckbox("Evil Gossip Stone", CVAR("EvilGossipStone"))) {
OnConfigurationChanged();
}
UIWidgets::Tooltip("Don't you dare talk to them.");
}
static void RegisterMod() {

View File

@@ -5,6 +5,8 @@
#include "soh/Enhancements/game-interactor/GameInteractor.h"
#include "soh/Enhancements/custom-message/CustomMessageManager.h"
#include "soh/Enhancements/randomizer/randomizer.h"
#include "soh/frame_interpolation.h"
#include "soh_assets.h"
extern "C" {
#include "macros.h"
@@ -80,48 +82,138 @@ void RandomizeBoulder(Actor* refActor) {
Actor_Kill(actor);
}
static void OnPresentChange() {
isExchangeDisabled = !CVarGetInteger(CVAR("OrnExch.Enabled"), 0);
COND_ID_HOOK(OnActorKill, ACTOR_EN_OE2, CVarGetInteger(CVAR("OrnExch.Enabled"), 0), [](void* actorRef) {
bool spawningPresents = false;
struct Present {
};
std::unordered_map<Actor*, Present> presents;
void Present_Init(Actor* actor, PlayState* play) {
Present present;
presents[actor] = present;
actor->gravity = -1;
Actor_MoveXZGravity(actor);
actor->shape.rot.y = Random(0, 0xFFFF);
Actor_UpdateBgCheckInfo(play, actor, 10.0f, 10.0f, 0.0f, 0xFF);
}
void Present_Update(Actor* actor, PlayState* play) {
Present* present = &presents[actor];
if (actor->xzDistToPlayer < 50.0f && actor->yDistToPlayer < 50.0f) {
uint32_t giftsCollected = CVarGetInteger(CVAR("GiftsCollected"), 0);
giftsCollected++;
CVarSetInteger(CVAR("GiftsCollected"), giftsCollected);
Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
std::string msg = std::to_string(giftsCollected).c_str();
msg += " Gifts in Inventory.";
Notification::Emit({ .itemIcon = "RG_TRIFORCE_PIECE", .message = msg });
});
COND_ID_HOOK(
OnOpenText, 0x204A, CVarGetInteger(CVAR("OrnExch.Enabled"), 0), [](u16* textId, bool* loadFromMessageTable) {
auto messageEntry = CustomMessage("");
bool reduceGifts = false;
uint32_t giftsCollected = CVarGetInteger(CVAR("GiftsCollected"), 0);
uint32_t giftsRequired = CVarGetInteger(CVAR("OrnExch.Amount"), 15);
if (giftsCollected < giftsRequired) {
std::string msg = "You only have %r " + std::to_string(giftsCollected) + "%w If you bring me %g" +
std::to_string(giftsRequired) + "%w I'll give you a reward!";
messageEntry = CustomMessage(msg);
} else {
std::string msg = "A present? And %g" + std::to_string(giftsRequired) +
"%w to boot? Here's your reward, bring me more if you find any!";
messageEntry = CustomMessage(msg);
reduceGifts = true;
}
messageEntry.AutoFormat();
messageEntry.LoadIntoFont();
*loadFromMessageTable = false;
if (reduceGifts) {
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_TRIFORCE_PIECE).GetGIEntry_Copy();
giftsCollected -= giftsRequired;
CVarSetInteger(CVAR("GiftsCollected"), giftsCollected);
Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
std::string msg = std::to_string(giftsCollected).c_str();
msg += " Gifts in Inventory.";
Notification::Emit({ .itemIcon = "RG_TRIFORCE_PIECE", .message = msg });
}
Notification::Emit({
.itemIcon = "RG_TRIFORCE_PIECE",
.message = msg,
.messageColor = ImVec4(1.0f, 1.0f, 1.0f, 1.0f),
});
Actor_Kill(actor);
}
}
void Present_Draw(Actor* actor, PlayState* play) {
OPEN_DISPS(play->state.gfxCtx);
Gfx_SetupDL_25Opa(play->state.gfxCtx);
Matrix_Scale(30.0f, 30.0f, 30.0f, MTXMODE_APPLY);
Matrix_Translate(49.20f, 0.0f, -106.60f, MTXMODE_APPLY);
gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(play->state.gfxCtx, (char*)__FILE__, __LINE__), G_MTX_MODELVIEW | G_MTX_LOAD);
gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 255, 255, 255);
gSPDisplayList(POLY_OPA_DISP++, (Gfx*)gXmasDecor100DL);
CLOSE_DISPS(play->state.gfxCtx);
}
void Present_Destroy(Actor* actor, PlayState* play) {
presents.erase(actor);
}
static void OnPresentChange() {
isExchangeDisabled = !CVarGetInteger(CVAR("OrnExch.Enabled"), 0);
COND_ID_HOOK(OnOpenText, 0x204A, CVarGetInteger(CVAR("OrnExch.Enabled"), 0), [](u16 * textId, bool* loadFromMessageTable) {
auto messageEntry = CustomMessage("");
bool reduceGifts = false;
uint32_t giftsCollected = CVarGetInteger(CVAR("GiftsCollected"), 0);
uint32_t giftsRequired = CVarGetInteger(CVAR("OrnExch.Amount"), 15);
if (giftsCollected < giftsRequired) {
std::string msg = "You only have %r " + std::to_string(giftsCollected) + "%w If you bring me %g"
+ std::to_string(giftsRequired) + "%w I'll give you a reward!";
messageEntry = CustomMessage(msg);
} else {
std::string msg = "A present? And %g" + std::to_string(giftsRequired) +
"%w to boot? Here's your reward, bring me more if you find any!";
messageEntry = CustomMessage(msg);
reduceGifts = true;
}
messageEntry.AutoFormat();
messageEntry.LoadIntoFont();
*loadFromMessageTable = false;
if (reduceGifts) {
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_TRIFORCE_PIECE).GetGIEntry_Copy();
giftsCollected -= giftsRequired;
CVarSetInteger(CVAR("GiftsCollected"), giftsCollected);
Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame();
std::string msg = std::to_string(giftsCollected).c_str();
msg += " Gifts in Inventory.";
Notification::Emit({
.itemIcon = "RG_TRIFORCE_PIECE",
.message = msg
});
}
});
COND_HOOK(OnSceneSpawnActors, CVarGetInteger(CVAR("OrnExch.Enabled"), 0), []() {
presents.clear();
Vec3f pos;
static CollisionPoly presentPoly;
static f32 raycastResult;
pos.y = 9999.0f;
int spawnAttempts = 0;
while (spawnAttempts < 20) {
if (GET_PLAYER(gPlayState) != nullptr) {
pos.x = GET_PLAYER(gPlayState)->actor.world.pos.x;
pos.z = GET_PLAYER(gPlayState)->actor.world.pos.z;
} else {
pos.x = 0;
pos.z = 0;
}
// X/Z anywhere from -1000.0 to +1000.0 from player
pos.x += (float)(Random(0, 20000)) - 10000.0f;
pos.z += (float)(Random(0, 20000)) - 10000.0f;
raycastResult = BgCheck_AnyRaycastFloor1(&gPlayState->colCtx, &presentPoly, &pos);
if (raycastResult > BGCHECK_Y_MIN) {
spawningPresents = true;
Actor* actor = Actor_Spawn(&gPlayState->actorCtx, gPlayState, ACTOR_EN_OE2, pos.x, raycastResult, pos.z, 0, 0, 0, 0, false);
spawningPresents = false;
// break;
}
spawnAttempts++;
}
});
COND_ID_HOOK(ShouldActorInit, ACTOR_EN_OE2, CVarGetInteger(CVAR("OrnExch.Enabled"), 0), [](void* actorRef, bool* should) {
Actor* actor = (Actor*)actorRef;
if (spawningPresents) {
actor->init = Present_Init;
actor->update = Present_Update;
actor->draw = Present_Draw;
actor->destroy = Present_Destroy;
}
});
}
static void OnBlitzChange() {
@@ -185,31 +277,32 @@ static void DrawMenu() {
}
UIWidgets::Tooltip("Can you beat your objective before the Fever sets in?/n"
"- Obtaining Ice Traps extends your timer.");
if (UIWidgets::EnhancementSliderFloat("", "##FontScale", CVAR("FontScale"), 1.0f, 5.0f, "Font: %.1fx", 1.0f, false,
false, isFeverDisabled)) {
OnFeverConfigurationChanged();
if (CVarGetInteger(CVAR("Fever.Enabled"), 0)) {
if (UIWidgets::EnhancementSliderFloat("", "##FontScale", CVAR("FontScale"),
1.0f, 5.0f, "Font: %.1fx", 1.0f, false, false, isFeverDisabled)) {
OnFeverConfigurationChanged();
}
UIWidgets::PaddedEnhancementSliderInt("Starting Timer: %d minutes", "##StartTime", CVAR("StartTimer"),
5, 30, "", 15, true, true, false, isFeverDisabled);
UIWidgets::PaddedEnhancementSliderInt("Time Extensions: %d minutes", "##ExtendTime", CVAR("ExtendTimer"),
1, 10, "", 5, true, true, false, isFeverDisabled);
}
UIWidgets::PaddedEnhancementSliderInt("Starting Timer: %d minutes", "##StartTime", CVAR("StartTimer"), 5, 30, "",
15, true, true, false, isFeverDisabled);
UIWidgets::PaddedEnhancementSliderInt("Time Extensions: %d minutes", "##ExtendTime", CVAR("ExtendTimer"), 1, 10, "",
5, true, true, false, isFeverDisabled);
UIWidgets::PaddedSeparator();
if (UIWidgets::EnhancementCheckbox("Boulder Blitz", CVAR("Blitz.Enabled"))) {
OnBlitzChange();
}
UIWidgets::Tooltip("Boulders will randomly be replaced with other boulder types.");
UIWidgets::PaddedSeparator();
if (UIWidgets::EnhancementCheckbox("Ornament Exchange", CVAR("OrnExch.Enabled"))) {
OnPresentChange();
bool toggle = CVarGetInteger(CVAR("OrnExch.Enabled"), 0);
CVarSetInteger("gHoliday.ItsHeckinPat.GiftsForNPCs", toggle);
OnConfigChanged();
}
UIWidgets::Tooltip("See Malon as Young Link in Lon Lon Ranch to exchange Gifts for Ornaments!\n"
"Note: Enabling this will set \"Gifts For NPCs\" to match.");
UIWidgets::PaddedEnhancementSliderInt("Gifts Required: %d Gifts", "##GiftsReq", CVAR("OrnExch.Amount"), 5, 30, "",
15, true, true, false, isExchangeDisabled);
UIWidgets::Tooltip("See Malon as Young Link in Lon Lon Ranch to exchange Gifts for Ornaments!");
if (CVarGetInteger(CVAR("OrnExch.Enabled"), 0)) {
UIWidgets::PaddedEnhancementSliderInt("Gifts Required: %d Gifts", "##GiftsReq", CVAR("OrnExch.Amount"),
5, 30, "", 15, true, true, false, isExchangeDisabled);
}
}
static void RegisterMod() {

View File

@@ -7,9 +7,15 @@
#include "soh/Enhancements/randomizer/3drando/random.hpp"
#include "soh/Enhancements/randomizer/3drando/location_access.hpp"
#include "soh/Enhancements/randomizer/entrance.h"
#include "soh/Enhancements/custom-collectible/CustomCollectible.h"
#include "soh/Notification/Notification.h"
#include "soh/Enhancements/nametag.h"
#include "objects/gameplay_field_keep/gameplay_field_keep.h"
#include "objects/gameplay_keep/gameplay_keep.h"
#include "objects/object_md/object_md.h"
#include "objects/object_trap/object_trap.h"
#include "objects/object_toryo/object_toryo.h"
#include "src/overlays/actors/ovl_Door_Ana/z_door_ana.h"
extern "C" {
#include "macros.h"
@@ -20,6 +26,7 @@ extern PlayState* gPlayState;
void DoorAna_SetupAction(DoorAna* doorAna, DoorAnaActionFunc actionFunc);
void DoorAna_GrabPlayer(DoorAna* doorAna, PlayState* play);
}
extern GetItemEntry vanillaQueuedItemEntry;
#define AUTHOR "Fredomato"
#define CVAR(v) "gHoliday." AUTHOR "." v
@@ -28,31 +35,38 @@ static CollisionPoly snowballPoly;
static f32 raycastResult;
const s16 entrances[] = {
0x0000, 0x0209, 0x0004, 0x0242, 0x0028, 0x0221, 0x0169, 0x0215, 0x0165, 0x024A, 0x0010, 0x021D, 0x0082, 0x01E1,
0x0037, 0x0205, 0x0098, 0x02A6, 0x0088, 0x03D4, 0x0008, 0x03A8, 0x0467, 0x023D, 0x0433, 0x0443, 0x0437, 0x0447,
0x009C, 0x033C, 0x00C9, 0x026A, 0x00C1, 0x0266, 0x0043, 0x03CC, 0x045F, 0x0309, 0x03A0, 0x03D0, 0x007E, 0x026E,
0x0530, 0x01D1, 0x0507, 0x03BC, 0x0388, 0x02A2, 0x0063, 0x01D5, 0x0528, 0x03C0, 0x043B, 0x0067, 0x02FD, 0x0349,
0x0550, 0x04EE, 0x039C, 0x0345, 0x05C8, 0x05DC, 0x0072, 0x034D, 0x030D, 0x0355, 0x037C, 0x03FC, 0x0380, 0x03C4,
0x004F, 0x0378, 0x02F9, 0x042F, 0x05D0, 0x05D4, 0x052C, 0x03B8, 0x016D, 0x01CD, 0x00B7, 0x0201, 0x003B, 0x0463,
0x0588, 0x057C, 0x0578, 0x0340, 0x04C2, 0x03E8, 0x04BE, 0x0482, 0x0315, 0x045B, 0x0371, 0x0394, 0x0272, 0x0211,
0x0053, 0x0472, 0x0453, 0x0351, 0x0384, 0x044B, 0x03EC, 0x04FF, 0x0700, 0x0800, 0x0701, 0x0801, 0x0702, 0x0802,
0x0703, 0x0803, 0x0704, 0x0804, 0x0705, 0x0805, 0x0706, 0x0806, 0x0707, 0x0807, 0x0708, 0x0808, 0x0709, 0x0809,
0x070A, 0x080A, 0x070B, 0x080B, 0x070C, 0x080C, 0x070D, 0x080D, 0x070E, 0x080E, 0x070F, 0x080F, 0x0710, 0x0711,
0x0811, 0x0712, 0x0812, 0x0713, 0x0813, 0x0714, 0x0814, 0x0715, 0x0815, 0x0716, 0x0816, 0x0717, 0x0817, 0x0718,
0x0818, 0x0719, 0x0819, 0x081A, 0x071B, 0x081B, 0x071C, 0x081C, 0x071D, 0x081D, 0x071E, 0x081E, 0x071F, 0x081F,
0x0720, 0x0820, 0x004B, 0x035D, 0x031C, 0x0361, 0x002D, 0x050B, 0x044F, 0x0359, 0x05E0, 0x020D, 0x011E, 0x0286,
0x04E2, 0x04D6, 0x01DD, 0x04DA, 0x00FC, 0x01A9, 0x0185, 0x04DE, 0x0102, 0x0189, 0x0117, 0x018D, 0x0276, 0x01FD,
0x00DB, 0x017D, 0x00EA, 0x0181, 0x0157, 0x01F9, 0x0328, 0x0560, 0x0129, 0x022D, 0x0130, 0x03AC, 0x0123, 0x0365,
0x00B1, 0x0033, 0x0138, 0x025A, 0x0171, 0x025E, 0x00E4, 0x0195, 0x013D, 0x0191, 0x014D, 0x01B9, 0x0246, 0x01C1,
0x0147, 0x01BD, 0x0108, 0x019D, 0x0225, 0x01A1, 0x0219, 0x027E, 0x0554, 0x00BB, 0x0282, 0x0600, 0x04F6, 0x0604,
0x01F1, 0x0568, 0x05F4, 0x040F, 0x0252, 0x040B, 0x00C5, 0x0301, 0x0407, 0x000C, 0x024E, 0x0305, 0x0175, 0x0417,
0x0423, 0x008D, 0x02F5, 0x0413, 0x02B2, 0x0457, 0x047A, 0x010E, 0x0608, 0x0564, 0x060C, 0x0610, 0x0580
0x0000, 0x0209, 0x0004, 0x0242, 0x0028, 0x0221, 0x0169, 0x0215, 0x0165, 0x024A, 0x0010, 0x021D, 0x0082, 0x01E1, 0x0037, 0x0205,
0x0098, 0x02A6, 0x0088, 0x03D4, 0x0008, 0x03A8, 0x0467, 0x023D, 0x0433, 0x0443, 0x0437, 0x0447, 0x009C, 0x033C, 0x00C9, 0x026A,
0x00C1, 0x0266, 0x0043, 0x03CC, 0x045F, 0x0309, 0x03A0, 0x03D0, 0x007E, 0x026E, 0x0530, 0x01D1, 0x0507, 0x03BC, 0x0388, 0x02A2,
0x0063, 0x01D5, 0x0528, 0x03C0, 0x043B, 0x0067, 0x02FD, 0x0349, 0x0550, 0x04EE, 0x039C, 0x0345, 0x05C8, 0x05DC, 0x0072, 0x034D,
0x030D, 0x0355, 0x037C, 0x03FC, 0x0380, 0x03C4, 0x004F, 0x0378, 0x02F9, 0x042F, 0x05D0, 0x05D4, 0x052C, 0x03B8, 0x016D, 0x01CD,
0x00B7, 0x0201, 0x003B, 0x0463, 0x0588, 0x057C, 0x0578, 0x0340, 0x04C2, 0x03E8, 0x04BE, 0x0482, 0x0315, 0x045B, 0x0371, 0x0394,
0x0272, 0x0211, 0x0053, 0x0472, 0x0453, 0x0351, 0x0384, 0x044B, 0x03EC, 0x04FF, 0x0700, 0x0800, 0x0701, 0x0801, 0x0702, 0x0802,
0x0703, 0x0803, 0x0704, 0x0804, 0x0705, 0x0805, 0x0706, 0x0806, 0x0707, 0x0807, 0x0708, 0x0808, 0x0709, 0x0809, 0x070A, 0x080A,
0x070B, 0x080B, 0x070C, 0x080C, 0x070D, 0x080D, 0x070E, 0x080E, 0x070F, 0x080F, 0x0710, 0x0711, 0x0811, 0x0712, 0x0812,
0x0713, 0x0813, 0x0714, 0x0814, 0x0715, 0x0815, 0x0716, 0x0816, 0x0717, 0x0817, 0x0718, 0x0818, 0x0719, 0x0819, 0x081A,
0x071B, 0x081B, 0x071C, 0x081C, 0x071D, 0x081D, 0x071E, 0x081E, 0x071F, 0x081F, 0x0720, 0x0820, 0x004B, 0x035D, 0x031C, 0x0361,
0x002D, 0x050B, 0x044F, 0x0359, 0x05E0, 0x020D, 0x011E, 0x0286, 0x04E2, 0x04D6, 0x01DD, 0x04DA, 0x00FC, 0x01A9, 0x0185, 0x04DE,
0x0102, 0x0189, 0x0117, 0x018D, 0x0276, 0x01FD, 0x00DB, 0x017D, 0x00EA, 0x0181, 0x0157, 0x01F9, 0x0328, 0x0560, 0x0129, 0x022D,
0x0130, 0x03AC, 0x0123, 0x0365, 0x00B1, 0x0033, 0x0138, 0x025A, 0x0171, 0x025E, 0x00E4, 0x0195, 0x013D, 0x0191, 0x014D, 0x01B9,
0x0246, 0x01C1, 0x0147, 0x01BD, 0x0108, 0x019D, 0x0225, 0x01A1, 0x0219, 0x027E, 0x0554, 0x00BB, 0x0282, 0x0600, 0x04F6, 0x0604,
0x01F1, 0x0568, 0x05F4, 0x040F, 0x0252, 0x040B, 0x00C5, 0x0301, 0x0407, 0x000C, 0x024E, 0x0305, 0x0175, 0x0417, 0x0423, 0x008D,
0x02F5, 0x0413, 0x02B2, 0x0457, 0x047A, 0x010E, 0x0608, 0x0564, 0x060C, 0x0610, 0x0580
};
static bool midoGrottoInit = false;
static SkelAnime midoSkelAnime;
static Vec3s midoJointTable[17];
static Vec3s midoMorphTable[17];
int FredsQuestWoodCollected = 0;
int FredsQuestWoodOnHand = 0;
static int lastDisplayedCount = -1;
static bool FredsQuestComplete = false;
static SkelAnime collectionPointSkelAnime;
static Vec3s collectionPointJointTable[17];
static Vec3s collectionPointMorphTable[17];
static std::string collectionPointNametag;
static void RandomGrotto_WaitOpen(DoorAna* doorAna, PlayState* play) {
if (!midoGrottoInit) {
@@ -64,9 +78,11 @@ static void RandomGrotto_WaitOpen(DoorAna* doorAna, PlayState* play) {
Actor* actor = &doorAna->actor;
Player* player = GET_PLAYER(play);
Math_SmoothStepToF(&actor->world.pos.x, player->actor.world.pos.x, 0.1f, 10.0f, 0.0f);
Math_SmoothStepToF(&actor->world.pos.z, player->actor.world.pos.z, 0.1f, 10.0f, 0.0f);
Math_SmoothStepToF(&actor->world.pos.y, player->actor.world.pos.y, 0.1f, 10.0f, 0.0f);
if (!Player_InCsMode(play)) {
Math_SmoothStepToF(&actor->world.pos.x, player->actor.world.pos.x, 0.1f, 10.0f, 0.0f);
Math_SmoothStepToF(&actor->world.pos.z, player->actor.world.pos.z, 0.1f, 10.0f, 0.0f);
Math_SmoothStepToF(&actor->world.pos.y, player->actor.world.pos.y, 0.1f, 10.0f, 0.0f);
}
Math_ApproachS(&doorAna->actor.shape.rot.y, doorAna->actor.yawTowardsPlayer, 5, 0xBB8);
@@ -108,11 +124,10 @@ static void RandomGrotto_Draw(Actor* actor, PlayState* play) {
CLOSE_DISPS(play->state.gfxCtx);
}
static void SpawnRandomGrotto() {
static Vec3f FindValidPos(f32 distance) {
Vec3f pos;
pos.y = 9999.0f;
int spawnAttempts = 0;
while (spawnAttempts < 50) {
while (true) {
if (GET_PLAYER(gPlayState) != nullptr) {
pos.x = GET_PLAYER(gPlayState)->actor.world.pos.x;
pos.z = GET_PLAYER(gPlayState)->actor.world.pos.z;
@@ -120,35 +135,355 @@ static void SpawnRandomGrotto() {
pos.x = 0;
pos.z = 0;
}
// X/Z anywhere from -1000.0 to +1000.0 from player
pos.x += (float)(Random(0, 2000)) - 1000.0f;
pos.z += (float)(Random(0, 2000)) - 1000.0f;
pos.x += (float)(Random(0, distance)) - distance / 2;
pos.z += (float)(Random(0, distance)) - distance / 2;
raycastResult = BgCheck_AnyRaycastFloor1(&gPlayState->colCtx, &snowballPoly, &pos);
if (raycastResult > BGCHECK_Y_MIN) {
Actor* grotto = Actor_Spawn(&gPlayState->actorCtx, gPlayState, ACTOR_DOOR_ANA, pos.x, raycastResult, pos.z,
0, 0, 0, 0, false);
midoGrottoInit = false;
DoorAna_SetupAction((DoorAna*)grotto, RandomGrotto_WaitOpen);
grotto->draw = RandomGrotto_Draw;
break;
pos.y = raycastResult;
return pos;
}
spawnAttempts++;
}
}
// TODO: If in hyrule field and treeChopper is on, teleport somewhere else in hyrule field
static void SpawnRandomGrotto() {
if (
gPlayState->sceneNum == SCENE_TEMPLE_OF_TIME_EXTERIOR_DAY ||
gPlayState->sceneNum == SCENE_TEMPLE_OF_TIME_EXTERIOR_NIGHT ||
gPlayState->sceneNum == SCENE_TEMPLE_OF_TIME_EXTERIOR_RUINS
) {
return;
}
Vec3f pos = FindValidPos(2000.0f);
Actor* grotto = Actor_Spawn(&gPlayState->actorCtx, gPlayState, ACTOR_DOOR_ANA, pos.x, pos.y, pos.z, 0, 0, 0, 0, false);
midoGrottoInit = false;
DoorAna_SetupAction((DoorAna*)grotto, RandomGrotto_WaitOpen);
grotto->draw = RandomGrotto_Draw;
}
void SpawnStick(Vec3f pos) {
CustomCollectible::Spawn(pos.x, pos.y + 150.0f, pos.z, 0, CustomCollectible::KILL_ON_TOUCH | CustomCollectible::TOSS_ON_SPAWN, 0, [](Actor* actor, PlayState* play) {
FredsQuestWoodOnHand++;
Audio_PlaySoundGeneral(NA_SE_SY_METRONOME, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale, &gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb);
}, [](Actor* actor, PlayState* play) {
Matrix_Scale(40.0f, 40.0f, 40.0f, MTXMODE_APPLY);
for (int i = 4; i < 7; i++) {
Matrix_RotateZYX(800 * i, 0, 800 * i, MTXMODE_APPLY);
GetItem_Draw(play, GID_STICK);
}
});
}
Actor* specialTree = nullptr;
void ChooseSpecialTree() {
Actor* actor = gPlayState->actorCtx.actorLists[ACTORCAT_PROP].head;
std::vector<Actor*> trees;
specialTree = nullptr;
while (actor != NULL) {
if (ACTOR_EN_WOOD02 == actor->id && actor->params < 10) {
trees.push_back(actor);
}
actor = actor->next;
}
if (trees.size() <= 1) {
return;
}
specialTree = trees[Random(0, trees.size() - 1)];
}
extern "C" bool HandleTreeBonk(Actor* actor) {
if (!CVarGetInteger(CVAR("FredsQuest.Enabled"), 0)) {
return false;
}
int damage = 2;
// random chance of doing a crit
if (Random(0, 100) < 30) {
damage = 4;
}
if (actor->colChkInfo.health - damage <= 0) {
if (specialTree == actor) {
ChooseSpecialTree();
for (int i = 0; i < CVarGetInteger(CVAR("FredsQuest.SpecialBreakDropRate"), 10); i++) {
SpawnStick(actor->world.pos);
}
} else {
for (int i = 0; i < CVarGetInteger(CVAR("FredsQuest.TreeBreakDropRate"), 3); i++) {
SpawnStick(actor->world.pos);
}
}
// Move tree (instead of killing and spawning another)
actor->colChkInfo.health = 8;
Vec3f pos = FindValidPos(5000.0f);
actor->world.pos.x = pos.x;
actor->world.pos.y = pos.y;
actor->world.pos.z = pos.z;
} else {
actor->colChkInfo.health -= damage;
for (int i = 0; i < CVarGetInteger(CVAR("FredsQuest.TreeBonkDropRate"), 1); i++) {
SpawnStick(actor->world.pos);
}
}
return true;
}
void DrawCrazyTaxiArrow(Actor* actor, PlayState* play) {
if (specialTree == nullptr || !CVarGetInteger(CVAR("FredsQuest.CrazyTaxiArrow"), 0)) {
return;
}
s16 yaw = Actor_WorldYawTowardActor(actor, specialTree);
Math_ApproachS(&actor->shape.rot.y, yaw, 5, 10000);
OPEN_DISPS(gPlayState->state.gfxCtx);
Gfx_SetupDL_4Xlu(gPlayState->state.gfxCtx);
Matrix_Scale(50.0f, 50.0f, 50.0f, MTXMODE_APPLY);
Matrix_Translate(0.0f, 70.0f, 0.0f, MTXMODE_APPLY);
Matrix_RotateY(5.86f, MTXMODE_APPLY);
gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(play->state.gfxCtx, (char*)__FILE__, __LINE__), G_MTX_MODELVIEW | G_MTX_LOAD);
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 0, 255, 0, 255);
gDPSetEnvColor(POLY_XLU_DISP++, 0, 255, 0, 255);
gSPDisplayList(POLY_XLU_DISP++, (Gfx*)gDebugArrowDL);
CLOSE_DISPS(gPlayState->state.gfxCtx);
}
void SpawnCrazyTaxiArrow() {
EnItem00* arrow = CustomCollectible::Spawn(0, 0, 0, 0, CustomCollectible::KEEP_ON_PLAYER, 0, NULL, NULL);
arrow->actor.draw = DrawCrazyTaxiArrow;
}
void CollectionPoint_Update(Actor* actor, PlayState* play) {
EnItem00* enItem00 = (EnItem00*)actor;
SkelAnime_Update(&collectionPointSkelAnime);
if (FredsQuestComplete) {
return;
}
if (lastDisplayedCount != FredsQuestWoodCollected) {
lastDisplayedCount = FredsQuestWoodCollected;
collectionPointNametag = "Bring me wood!";
if (FredsQuestWoodCollected > 0) {
collectionPointNametag += std::string(" (") + std::to_string(FredsQuestWoodCollected) + "/" + std::to_string(CVarGetInteger(CVAR("FredsQuest.WoodNeeded"), 300)) + ")";
}
NameTag_RemoveAllForActor(actor);
NameTag_RegisterForActorWithOptions(actor, collectionPointNametag.c_str(), { .yOffset = 100 });
}
if ((actor->xzDistToPlayer <= 200.0f) && (fabsf(actor->yDistToPlayer) <= fabsf(50.0f))) {
if (FredsQuestWoodOnHand) {
FredsQuestWoodCollected++;
FredsQuestWoodOnHand--;
Audio_PlaySoundGeneral(NA_SE_SY_METRONOME, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale, &gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb);
if (FredsQuestWoodCollected >= CVarGetInteger(CVAR("FredsQuest.WoodNeeded"), 300)) {
FredsQuestComplete = true;
collectionPointNametag = "You're a hero!";
NameTag_RemoveAllForActor(actor);
NameTag_RegisterForActorWithOptions(actor, collectionPointNametag.c_str(), { .yOffset = 100 });
if (IS_RANDO && Rando::Context::GetInstance()->GetOption(RSK_TRIFORCE_HUNT)) {
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_TRIFORCE_PIECE).GetGIEntry_Copy();
} else {
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_HEART_CONTAINER).GetGIEntry_Copy();
}
}
}
}
}
void CollectionPoint_Draw(Actor* actor, PlayState* play) {
OPEN_DISPS(play->state.gfxCtx);
Gfx_SetupDL_25Opa(play->state.gfxCtx);
SkelAnime_DrawSkeletonOpa(play, &collectionPointSkelAnime, NULL, NULL, actor);
// For every 2% of the goal, draw a stick at a different angle, building a tree
Matrix_Scale(40.0f, 40.0f, 40.0f, MTXMODE_APPLY);
Matrix_Translate(0, 0, -300.0f, MTXMODE_APPLY);
for (int i = 0; i < FredsQuestWoodCollected / (CVarGetInteger(CVAR("FredsQuest.WoodNeeded"), 300) / 50); i++) {
float angle = 10 * i;
float radius = (50 - i) * 0.5f; // Radius decreases as it goes up
float height = 10.0f; // Incremental height
Matrix_Translate(radius * cosf(angle), height, radius * sinf(angle), MTXMODE_APPLY);
Matrix_RotateY(angle, MTXMODE_APPLY);
GetItem_Draw(play, GID_STICK);
}
CLOSE_DISPS(play->state.gfxCtx);
}
void SpawnCollectionPoint() {
EnItem00* collectionPoint = CustomCollectible::Spawn(859.0f, 347.0f, 5185.0f, 0xB000, 0, 0, NULL, NULL);
collectionPoint->actor.update = CollectionPoint_Update;
collectionPoint->actor.draw = CollectionPoint_Draw;
collectionPoint->actor.flags |= ACTOR_FLAG_DRAW_WHILE_CULLED;
SkelAnime_InitFlex(gPlayState, &collectionPointSkelAnime, (FlexSkeletonHeader*)&object_toryo_Skel_007150,
(AnimationHeader*)&object_toryo_Anim_000E50, collectionPointJointTable, collectionPointMorphTable, 17);
}
void RandomTrap_Update(Actor* actor, PlayState* play) {
EnItem00* enItem00 = (EnItem00*)actor;
enItem00->unk_158--;
if (enItem00->unk_158 == 0) {
Actor_Kill(actor);
return;
}
Math_ApproachS(&actor->world.rot.y, actor->yawTowardsPlayer, 5, 0xBB8);
actor->speedXZ = 3.0f;
// TODO: CVar for speed
// Multiply speed by distance
actor->speedXZ += actor->xzDistToPlayer * 0.01f;
if (actor->xzDistToPlayer > 1000.0f && actor->velocity.y < -3.0f) {
actor->velocity.y += ABS(actor->yDistToPlayer) * 0.01f;
}
actor->shape.rot.y += 0x1000;
if ((actor->xzDistToPlayer <= 50.0f) && (fabsf(actor->yDistToPlayer) <= fabsf(20.0f))) {
// TODO: Random crowd control effect
GameInteractor::RawAction::KnockbackPlayer(5.0f);
Actor_Kill(actor);
}
if (actor->gravity != 0.0f) {
Actor_MoveXZGravity(actor);
Actor_UpdateBgCheckInfo(play, actor, 20.0f, 15.0f, 15.0f, 0x1D);
}
if (actor->bgCheckFlags & 0x0003) {
actor->speedXZ = 0.0f;
}
}
void RandomTrap_Draw(Actor* actor, PlayState* play) {
OPEN_DISPS(play->state.gfxCtx);
Matrix_Scale(4.0f, 4.0f, 4.0f, MTXMODE_APPLY);
Matrix_Translate(0, -200.0f, 0, MTXMODE_APPLY);
func_8002EBCC(actor, play, 1);
Gfx_DrawDListOpa(play, (Gfx*)gSlidingBladeTrapDL);
CLOSE_DISPS(play->state.gfxCtx);
}
void SpawnRandomTrap() {
Vec3f pos = FindValidPos(2000.0f);
EnItem00* randomTrap = CustomCollectible::Spawn(pos.x, pos.y, pos.z, 0, CustomCollectible::TOSS_ON_SPAWN, 0, NULL, NULL);
SoundSource_PlaySfxAtFixedWorldPos(gPlayState, &randomTrap->actor.world.pos, 20, NA_SE_EV_LIGHTNING);
randomTrap->actor.update = RandomTrap_Update;
randomTrap->actor.draw = RandomTrap_Draw;
randomTrap->unk_158 = 20 * CVarGetInteger(CVAR("RandomTraps.Lifetime"), 30);
}
void OnSceneInit() {
// Reset wood collected
FredsQuestWoodCollected = 0;
FredsQuestWoodOnHand = 0;
lastDisplayedCount = -1;
FredsQuestComplete = false;
if (gPlayState->sceneNum != SCENE_HYRULE_FIELD) {
return;
}
ChooseSpecialTree();
SpawnCrazyTaxiArrow();
SpawnCollectionPoint();
}
static void ConfigurationChanged() {
COND_HOOK(OnSceneSpawnActors, CVarGetInteger(CVAR("KrampusHole"), 0), SpawnRandomGrotto);
COND_HOOK(OnSceneSpawnActors, CVarGetInteger(CVAR("FredsQuest.Enabled"), 0), OnSceneInit);
COND_HOOK(OnPlayerUpdate, CVarGetInteger(CVAR("RandomTraps.Enabled"), 0), []() {
if (rand() % CVarGetInteger(CVAR("RandomTraps.SpawnChance"), 400) == 0) {
SpawnRandomTrap();
}
});
COND_HOOK(OnPlayerUpdate, CVarGetInteger(CVAR("FredsQuest.Enabled"), 0), []() {
if (CVarGetInteger(CVAR("FredsQuest.EncumberedThreshold"), 60) == 0 || FredsQuestWoodOnHand <= CVarGetInteger(CVAR("FredsQuest.EncumberedThreshold"), 60)) {
GameInteractor::State::RunSpeedModifier = 0;
} else {
GameInteractor::State::RunSpeedModifier = -2;
}
});
COND_VB_SHOULD(VB_PLAYER_ROLL, CVarGetInteger(CVAR("FredsQuest.Enabled"), 0), {
if (FredsQuestWoodOnHand > CVarGetInteger(CVAR("FredsQuest.EncumberedThreshold"), 0)) {
*should = false;
}
});
}
static void DrawMenu() {
ImGui::SeparatorText(AUTHOR);
if (UIWidgets::EnhancementCheckbox("The Krampus Hole", CVAR("KrampusHole"))) {
// UIWidgets::EnhancementSliderFloat("Xfloat", "Xfloat", CVAR("tmpxf"), 0.0f, 10.0f, "%.2f", 1.0f, false);
// UIWidgets::EnhancementSliderFloat("Yfloat", "Yfloat", CVAR("tmpyf"), 0.0f, 10.0f, "%.2f", 1.0f, false);
// UIWidgets::EnhancementSliderFloat("Zfloat", "Zfloat", CVAR("tmpzf"), 0.0f, 10.0f, "%.2f", 1.0f, false);
// UIWidgets::EnhancementSliderInt("Xs", "Xs", CVAR("tmpxs"), 0, UINT16_MAX, "%d", 1, false);
// UIWidgets::EnhancementSliderInt("Ys", "Ys", CVAR("tmpys"), 0, UINT16_MAX, "%d", 1, false);
// UIWidgets::EnhancementSliderInt("Zs", "Zs", CVAR("tmpzs"), 0, UINT16_MAX, "%d", 1, false);
if (UIWidgets::EnhancementCheckbox("Fred's Quest", CVAR("FredsQuest.Enabled"))) {
ConfigurationChanged();
}
UIWidgets::Tooltip("Collect wood and bring it to the collection point in Hyrule Field for a small reward.");
if (CVarGetInteger(CVAR("FredsQuest.Enabled"), 0)) {
if (UIWidgets::EnhancementCheckbox("Crazy Taxi Arrow", CVAR("FredsQuest.CrazyTaxiArrow"))) {
ConfigurationChanged();
}
if (UIWidgets::EnhancementSliderInt("Wood Needed", "##FredsQuest.WoodNeeded", CVAR("FredsQuest.WoodNeeded"), 0, 1000, "%d", 300, false)) {
ConfigurationChanged();
}
if (UIWidgets::EnhancementSliderInt("Tree Bonk Drop Rate", "##FredsQuest.TreeBonkDropRate", CVAR("FredsQuest.TreeBonkDropRate"), 0, 10, "%d", 1, false)) {
ConfigurationChanged();
}
if (UIWidgets::EnhancementSliderInt("Tree Break Drop Rate", "##FredsQuest.TreeBreakDropRate", CVAR("FredsQuest.TreeBreakDropRate"), 0, 50, "%d", 3, false)) {
ConfigurationChanged();
}
if (UIWidgets::EnhancementSliderInt("Special Break Drop Rate", "##FredsQuest.SpecialBreakDropRate", CVAR("FredsQuest.SpecialBreakDropRate"), 0, 50, "%d", 10, false)) {
ConfigurationChanged();
}
if (UIWidgets::EnhancementSliderInt("Encumbered Threshold", "##FredsQuest.EncumberedThreshold", CVAR("FredsQuest.EncumberedThreshold"), 0, 200, "%d", 60, false)) {
ConfigurationChanged();
}
UIWidgets::Tooltip("If you have more than this many sticks, you will be encumbered and run slower. 0 for disabled");
}
if (UIWidgets::EnhancementCheckbox("Random Traps", CVAR("RandomTraps.Enabled"))) {
ConfigurationChanged();
}
UIWidgets::Tooltip("Random traps will spawn around you at a configurable rate. (Currently only knockback)");
if (CVarGetInteger(CVAR("RandomTraps.Enabled"), 0)) {
if (UIWidgets::EnhancementSliderInt("Trap Lifetime (Seconds)", "##RandomTraps.Lifetime", CVAR("RandomTraps.Lifetime"), 0, 60, "%d", 30, false)) {
ConfigurationChanged();
}
if (UIWidgets::EnhancementSliderInt("Spawn Chance", "##RandomTraps.SpawnChance", CVAR("RandomTraps.SpawnChance"), 40, 2000, "%d", 1000, false)) {
ConfigurationChanged();
}
}
}
static void RegisterMod() {

View File

@@ -0,0 +1,18 @@
#ifndef FRED_H
#define FRED_H
#ifdef __cplusplus
extern int FredsQuestWoodCollected;
extern int FredsQuestWoodOnHand;
extern "C" {
#include "z64actor.h"
#endif
bool HandleTreeBonk(Actor* actor);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -103,13 +103,33 @@ void Penguin_Destroy(Actor* actor, PlayState* play) {
static void OnConfigurationChanged() {
COND_HOOK(OnPlayerUpdate, CVarGetInteger(CVAR("Hailstorm"), 0), []() {
// Every frame has a 1/300 chance of spawning hail
if (rand() % 300 == 0) {
// Every frame has a 1/500 chance of spawning close hail
if (rand() % 500 == 0) {
int spawned = 0;
while (spawned < 1) {
Vec3f pos = GET_PLAYER(gPlayState)->actor.world.pos;
pos.x += (float)Random(0, 100) - 50.0f;
pos.z += (float)Random(0, 100) - 50.0f;
pos.x += (float)Random(0, 50) - 25.0f;
pos.z += (float)Random(0, 50) - 25.0f;
pos.y += 200.0f;
Actor* actor = Actor_Spawn(&gPlayState->actorCtx, gPlayState, ACTOR_EN_NUTSBALL, pos.x, pos.y, pos.z, 0, 0, 0, 0, false);
EnNutsball* nut = (EnNutsball*)actor;
nut->actor.draw = EnNutsball_Draw;
nut->actor.shape.rot.y = 0;
nut->timer = 0;
nut->actionFunc = func_80ABBBA8;
nut->actor.speedXZ = 0.0f;
nut->actor.gravity = -2.0f;
spawned++;
}
}
// Every frame has a 1/50 chance of spawning far hail
if (rand() % 50 == 0) {
int spawned = 0;
while (spawned < 1) {
Vec3f pos = GET_PLAYER(gPlayState)->actor.world.pos;
pos.x += (float)Random(0, 500) - 250.0f;
pos.z += (float)Random(0, 500) - 250.0f;
pos.y += 200.0f;
Actor* actor = Actor_Spawn(&gPlayState->actorCtx, gPlayState, ACTOR_EN_NUTSBALL, pos.x, pos.y, pos.z, 0,
@@ -192,9 +212,11 @@ static void DrawMenu() {
if (UIWidgets::EnhancementCheckbox("Penguins", CVAR("Penguins"))) {
OnConfigurationChanged();
}
UIWidgets::Tooltip("Penguins will spawn in huddles throughout hyrule");
if (UIWidgets::EnhancementCheckbox("Hailstorm", CVAR("Hailstorm"))) {
OnConfigurationChanged();
}
UIWidgets::Tooltip("Ever persistent hailstorm throughout hyrule");
}
static void RegisterMod() {

View File

@@ -8,8 +8,6 @@
#include "soh/Enhancements/game-interactor/GameInteractor.h"
#include "soh/Enhancements/cosmetics/CosmeticsEditor.h"
void OnConfigChanged();
inline std::vector<std::function<void()>> holidayDrawFuncs = {};
inline std::vector<std::function<void()>> holidayRegisterFuncs = {};

View File

@@ -1,153 +0,0 @@
#include "Holiday.hpp"
#include "soh_assets.h"
#include "soh/Enhancements/randomizer/3drando/random.hpp"
#include "soh/frame_interpolation.h"
#include "soh/Notification/Notification.h"
#include "objects/gameplay_field_keep/gameplay_field_keep.h"
#include "soh/Enhancements/custom-message/CustomMessageManager.h"
#include "soh/util.h"
#include "soh/Enhancements/randomizer/randomizer.h"
extern "C" {
#include "macros.h"
#include "functions.h"
#include "variables.h"
extern PlayState* gPlayState;
}
extern GetItemEntry vanillaQueuedItemEntry;
#define AUTHOR "ItsHeckinPat"
#define CVAR(v) "gHoliday." AUTHOR "." v
bool spawningPresents = false;
int collectedPresent = 0;
struct Present {};
std::unordered_map<Actor*, Present> presents;
void Present_Init(Actor* actor, PlayState* play) {
Present present;
presents[actor] = present;
actor->gravity = -1;
Actor_MoveForward(actor);
actor->shape.rot.y = Random(0, 0xFFFF);
Actor_UpdateBgCheckInfo(play, actor, 10.0f, 10.0f, 0.0f, 0xFF);
}
void Present_Update(Actor* actor, PlayState* play) {
Present* present = &presents[actor];
if (actor->xzDistToPlayer < 50.0f && actor->yDistToPlayer < 50.0f) {
collectedPresent++;
Notification::Emit({
.itemIcon = "RG_TRIFORCE_PIECE",
.message = "You collected a present!",
.messageColor = ImVec4(1.0f, 1.0f, 1.0f, 1.0f),
});
Actor_Kill(actor);
}
}
void Present_Draw(Actor* actor, PlayState* play) {
OPEN_DISPS(play->state.gfxCtx);
Gfx_SetupDL_25Opa(play->state.gfxCtx);
Matrix_Scale(30.0f, 30.0f, 30.0f, MTXMODE_APPLY);
Matrix_Translate(49.20f, 0.0f, -106.60f, MTXMODE_APPLY);
gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(play->state.gfxCtx, (char*)__FILE__, __LINE__),
G_MTX_MODELVIEW | G_MTX_LOAD);
gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 255, 255, 255);
gSPDisplayList(POLY_OPA_DISP++, (Gfx*)gXmasDecor100DL);
CLOSE_DISPS(play->state.gfxCtx);
}
void Present_Destroy(Actor* actor, PlayState* play) {
presents.erase(actor);
}
static CollisionPoly presentPoly;
static f32 raycastResult;
void OnConfigChanged() {
COND_HOOK(OnSceneSpawnActors, CVarGetInteger(CVAR("GiftsForNPCs"), 0), []() {
presents.clear();
Vec3f pos;
pos.y = 9999.0f;
int spawnAttempts = 0;
while (spawnAttempts < 50) {
if (GET_PLAYER(gPlayState) != nullptr) {
pos.x = GET_PLAYER(gPlayState)->actor.world.pos.x;
pos.z = GET_PLAYER(gPlayState)->actor.world.pos.z;
} else {
pos.x = 0;
pos.z = 0;
}
// X/Z anywhere from -1000.0 to +1000.0 from player
pos.x += (float)(Random(0, 2000)) - 1000.0f;
pos.z += (float)(Random(0, 2000)) - 1000.0f;
raycastResult = BgCheck_AnyRaycastFloor1(&gPlayState->colCtx, &presentPoly, &pos);
if (raycastResult > BGCHECK_Y_MIN) {
spawningPresents = true;
Actor* actor = Actor_Spawn(&gPlayState->actorCtx, gPlayState, ACTOR_EN_OE2, pos.x, raycastResult, pos.z,
0, 0, 0, 0, false);
spawningPresents = false;
// break;
}
spawnAttempts++;
}
});
COND_ID_HOOK(ShouldActorInit, ACTOR_EN_OE2, CVarGetInteger(CVAR("GiftsForNPCs"), 0),
[](void* actorRef, bool* should) {
Actor* actor = (Actor*)actorRef;
if (spawningPresents) {
actor->init = Present_Init;
actor->update = Present_Update;
actor->draw = Present_Draw;
actor->destroy = Present_Destroy;
}
});
COND_ID_HOOK(OnOpenText, 0x1019, CVarGetInteger(CVAR("GiftsForNPCs"), 0),
[](u16* textId, bool* loadFromMessageTable) {
if (collectedPresent <= 0) {
return;
}
auto messageEntry = CustomMessage("A present??? FOR ME???");
messageEntry.Format();
messageEntry.LoadIntoFont();
*loadFromMessageTable = false;
vanillaQueuedItemEntry = Rando::StaticData::RetrieveItem(RG_PIECE_OF_HEART).GetGIEntry_Copy();
collectedPresent--;
});
}
static void DrawMenu() {
ImGui::SeparatorText(AUTHOR);
if (UIWidgets::EnhancementCheckbox("Gifts for NPCs", CVAR("GiftsForNPCs"))) {
OnConfigChanged();
}
}
static void RegisterMod() {
// #region Leave this alone unless you know what you are doing
OnConfigChanged();
// #endregion
// TODO: Anything you want to run once on startup
}
// TODO: Uncomment this line to enable the mod
static Holiday holiday(DrawMenu, RegisterMod);

View File

@@ -126,6 +126,7 @@ static void DrawMenu() {
if (UIWidgets::EnhancementCheckbox("Ganon Dating Sim", CVAR("GanonDatingSim"))) {
ConfigurationChanged();
}
UIWidgets::Tooltip("Prior to fighting him at the top of his Castle, you make an attempt to convince Ganon to join you instead.");
}
static void RegisterMod() {

View File

@@ -141,9 +141,10 @@ void ShinyDrawImGui() {
UIWidgets::Tooltip("Allows enemies to be shiny.\nShiny enemies are 25% bigger and have 4 times the health but drop "
"the equivalent of a gold rupee upon death");
UIWidgets::PaddedEnhancementSliderInt("Shiny Chance: %d", "##ShinyChance", CVAR("Shiny.Chance"), 1, 8192, "", 8192,
true, true, false, false, "");
UIWidgets::Tooltip("The chance for an enemy to be shiny is 1 / Shiny Chance");
if (CVarGetInteger(CVAR("Shiny.Enabled"), 0)) {
UIWidgets::PaddedEnhancementSliderInt("Shiny Chance: %d", "##ShinyChance", CVAR("Shiny.Chance"), 1, 8192, "", 8192, true, true, false, false, "");
UIWidgets::Tooltip("The chance for an enemy to be shiny is 1 / Shiny Chance");
}
}
#pragma endregion

View File

@@ -105,25 +105,23 @@ static void SpawnIcebergs() {
}
const s16 entrances[] = {
0x0000, 0x0209, 0x0004, 0x0242, 0x0028, 0x0221, 0x0169, 0x0215, 0x0165, 0x024A, 0x0010, 0x021D, 0x0082, 0x01E1,
0x0037, 0x0205, 0x0098, 0x02A6, 0x0088, 0x03D4, 0x0008, 0x03A8, 0x0467, 0x023D, 0x0433, 0x0443, 0x0437, 0x0447,
0x009C, 0x033C, 0x00C9, 0x026A, 0x00C1, 0x0266, 0x0043, 0x03CC, 0x045F, 0x0309, 0x03A0, 0x03D0, 0x007E, 0x026E,
0x0530, 0x01D1, 0x0507, 0x03BC, 0x0388, 0x02A2, 0x0063, 0x01D5, 0x0528, 0x03C0, 0x043B, 0x0067, 0x02FD, 0x0349,
0x0550, 0x04EE, 0x039C, 0x0345, 0x05C8, 0x05DC, 0x0072, 0x034D, 0x030D, 0x0355, 0x037C, 0x03FC, 0x0380, 0x03C4,
0x004F, 0x0378, 0x02F9, 0x042F, 0x05D0, 0x05D4, 0x052C, 0x03B8, 0x016D, 0x01CD, 0x00B7, 0x0201, 0x003B, 0x0463,
0x0588, 0x057C, 0x0578, 0x0340, 0x04C2, 0x03E8, 0x04BE, 0x0482, 0x0315, 0x045B, 0x0371, 0x0394, 0x0272, 0x0211,
0x0053, 0x0472, 0x0453, 0x0351, 0x0384, 0x044B, 0x03EC, 0x04FF, 0x0700, 0x0800, 0x0701, 0x0801, 0x0702, 0x0802,
0x0703, 0x0803, 0x0704, 0x0804, 0x0705, 0x0805, 0x0706, 0x0806, 0x0707, 0x0807, 0x0708, 0x0808, 0x0709, 0x0809,
0x070A, 0x080A, 0x070B, 0x080B, 0x070C, 0x080C, 0x070D, 0x080D, 0x070E, 0x080E, 0x070F, 0x080F, 0x0710, 0x0711,
0x0811, 0x0712, 0x0812, 0x0713, 0x0813, 0x0714, 0x0814, 0x0715, 0x0815, 0x0716, 0x0816, 0x0717, 0x0817, 0x0718,
0x0818, 0x0719, 0x0819, 0x081A, 0x071B, 0x081B, 0x071C, 0x081C, 0x071D, 0x081D, 0x071E, 0x081E, 0x071F, 0x081F,
0x0720, 0x0820, 0x004B, 0x035D, 0x031C, 0x0361, 0x002D, 0x050B, 0x044F, 0x0359, 0x05E0, 0x020D, 0x011E, 0x0286,
0x04E2, 0x04D6, 0x01DD, 0x04DA, 0x00FC, 0x01A9, 0x0185, 0x04DE, 0x0102, 0x0189, 0x0117, 0x018D, 0x0276, 0x01FD,
0x00DB, 0x017D, 0x00EA, 0x0181, 0x0157, 0x01F9, 0x0328, 0x0560, 0x0129, 0x022D, 0x0130, 0x03AC, 0x0123, 0x0365,
0x00B1, 0x0033, 0x0138, 0x025A, 0x0171, 0x025E, 0x00E4, 0x0195, 0x013D, 0x0191, 0x014D, 0x01B9, 0x0246, 0x01C1,
0x0147, 0x01BD, 0x0108, 0x019D, 0x0225, 0x01A1, 0x0219, 0x027E, 0x0554, 0x00BB, 0x0282, 0x0600, 0x04F6, 0x0604,
0x01F1, 0x0568, 0x05F4, 0x040F, 0x0252, 0x040B, 0x00C5, 0x0301, 0x0407, 0x000C, 0x024E, 0x0305, 0x0175, 0x0417,
0x0423, 0x008D, 0x02F5, 0x0413, 0x02B2, 0x0457, 0x047A, 0x010E, 0x0608, 0x0564, 0x060C, 0x0610, 0x0580
0x0000, 0x0209, 0x0004, 0x0242, 0x0028, 0x0221, 0x0169, 0x0215, 0x0165, 0x024A, 0x0010, 0x021D, 0x0082, 0x01E1, 0x0037, 0x0205,
0x0098, 0x02A6, 0x0088, 0x03D4, 0x0008, 0x03A8, 0x0467, 0x023D, 0x0433, 0x0443, 0x0437, 0x0447, 0x009C, 0x033C, 0x00C9, 0x026A,
0x00C1, 0x0266, 0x0043, 0x03CC, 0x045F, 0x0309, 0x03A0, 0x03D0, 0x007E, 0x026E, 0x0530, 0x01D1, 0x0507, 0x03BC, 0x0388, 0x02A2,
0x0063, 0x01D5, 0x0528, 0x03C0, 0x043B, 0x0067, 0x02FD, 0x0349, 0x0550, 0x04EE, 0x039C, 0x0345, 0x05C8, 0x05DC, 0x0072, 0x034D,
0x030D, 0x0355, 0x037C, 0x03FC, 0x0380, 0x03C4, 0x004F, 0x0378, 0x02F9, 0x042F, 0x05D0, 0x05D4, 0x052C, 0x03B8, 0x016D, 0x01CD,
0x00B7, 0x0201, 0x003B, 0x0463, 0x0588, 0x057C, 0x0578, 0x0340, 0x04C2, 0x03E8, 0x04BE, 0x0482, 0x0315, 0x045B, 0x0371, 0x0394,
0x0272, 0x0211, 0x0053, 0x0472, 0x0453, 0x0351, 0x0384, 0x044B, 0x03EC, 0x04FF, 0x0700, 0x0800, 0x0701, 0x0801, 0x0702, 0x0802,
0x0703, 0x0803, 0x0704, 0x0804, 0x0705, 0x0805, 0x0706, 0x0806, 0x0707, 0x0807, 0x0708, 0x0808, 0x0709, 0x0809, 0x070A, 0x080A,
0x070B, 0x080B, 0x070C, 0x080C, 0x070D, 0x080D, 0x070E, 0x080E, 0x070F, 0x080F, 0x0710, 0x0711, 0x0811, 0x0712, 0x0812,
0x0713, 0x0813, 0x0714, 0x0814, 0x0715, 0x0815, 0x0716, 0x0816, 0x0717, 0x0817, 0x0718, 0x0818, 0x0719, 0x0819, 0x081A,
0x071B, 0x081B, 0x071C, 0x081C, 0x071D, 0x081D, 0x071E, 0x081E, 0x071F, 0x081F, 0x0720, 0x0820, 0x004B, 0x035D, 0x031C, 0x0361,
0x002D, 0x050B, 0x044F, 0x0359, 0x05E0, 0x020D, 0x011E, 0x0286, 0x04E2, 0x04D6, 0x01DD, 0x04DA, 0x00FC, 0x01A9, 0x0185, 0x04DE,
0x0102, 0x0189, 0x0117, 0x018D, 0x0276, 0x01FD, 0x00DB, 0x017D, 0x00EA, 0x0181, 0x0157, 0x01F9, 0x0328, 0x0560, 0x0129, 0x022D,
0x0130, 0x03AC, 0x0123, 0x0365, 0x00B1, 0x0033, 0x0138, 0x025A, 0x0171, 0x025E, 0x00E4, 0x0195, 0x013D, 0x0191, 0x014D, 0x01B9,
0x0246, 0x01C1, 0x0147, 0x01BD, 0x0108, 0x019D, 0x0225, 0x01A1, 0x0219, 0x027E, 0x0554, 0x00BB, 0x0282, 0x0600, 0x04F6, 0x0604,
0x01F1, 0x0568, 0x05F4, 0x040F, 0x0252, 0x040B, 0x00C5, 0x0301, 0x0407, 0x000C, 0x024E, 0x0305, 0x0175, 0x0417, 0x0423, 0x008D,
0x02F5, 0x0413, 0x02B2, 0x0457, 0x047A, 0x010E, 0x0608, 0x0564, 0x060C, 0x0610, 0x0580
};
static void RandomGrotto_WaitOpen(DoorAna* doorAna, PlayState* play) {
@@ -148,6 +146,14 @@ static void RandomGrotto_WaitOpen(DoorAna* doorAna, PlayState* play) {
}
static void SpawnRandomGrotto() {
if (
gPlayState->sceneNum == SCENE_TEMPLE_OF_TIME_EXTERIOR_DAY ||
gPlayState->sceneNum == SCENE_TEMPLE_OF_TIME_EXTERIOR_NIGHT ||
gPlayState->sceneNum == SCENE_TEMPLE_OF_TIME_EXTERIOR_RUINS
) {
return;
}
Vec3f pos;
pos.y = 9999.0f;
int spawnAttempts = 0;
@@ -160,8 +166,8 @@ static void SpawnRandomGrotto() {
pos.z = 0;
}
// X/Z anywhere from -1000.0 to +1000.0 from player
pos.x += (float)(Random(0, 2000)) - 1000.0f;
pos.z += (float)(Random(0, 2000)) - 1000.0f;
pos.x += (float)(Random(0, 5000)) - 2500.0f;
pos.z += (float)(Random(0, 5000)) - 2500.0f;
raycastResult = BgCheck_AnyRaycastFloor1(&gPlayState->colCtx, &snowballPoly, &pos);
@@ -201,12 +207,15 @@ static void DrawMenu() {
if (UIWidgets::EnhancementCheckbox("Snowballs", CVAR("Snowballs"))) {
ConfigurationChanged();
}
UIWidgets::Tooltip("Rogue snowballs will spawn in Hyrule Field and Kakariko Village.");
if (UIWidgets::EnhancementCheckbox("Lake Hylia Icebergs", CVAR("Icebergs"))) {
ConfigurationChanged();
}
UIWidgets::Tooltip("Icebergs will spawn in Lake Hylia.");
if (UIWidgets::EnhancementCheckbox("Down the Rabbit Hole", CVAR("DownTheRabbitHole"))) {
ConfigurationChanged();
}
UIWidgets::Tooltip("Random grottos will spawn throughout Hyrule. Who knows where they will take you?");
if (UIWidgets::EnhancementCheckbox("Super Bonk", CVAR("SuperBonk"))) {
ConfigurationChanged();
}

View File

@@ -120,6 +120,7 @@ static void DrawMenu() {
if (UIWidgets::EnhancementCheckbox("Bomb Arrows", CVAR("BombArrows.Enabled"))) {
OnConfigurationChanged();
}
UIWidgets::Tooltip("Equip bombs over an already equipped Bow to shoot bomb arrows");
}
static void RegisterMod() {

View File

@@ -1,5 +1,6 @@
#include "TimeDisplay.h"
#include "soh/Enhancements/gameplaystats.h"
#include "soh/Enhancements/Holiday/Fredomato.h"
#include <global.h>
#include "assets/textures/parameter_static/parameter_static.h"
@@ -40,7 +41,8 @@ const std::vector<TimeObject> timeDisplayList = {
{ DISPLAY_IN_GAME_TIMER, "Display Gameplay Timer", CVAR_TIME_DISPLAY("Timers.InGameTimer") },
{ DISPLAY_TIME_OF_DAY, "Display Time of Day", CVAR_TIME_DISPLAY("Timers.TimeofDay") },
{ DISPLAY_CONDITIONAL_TIMER, "Display Conditional Timer", CVAR_TIME_DISPLAY("Timers.HotWater") },
{ DISPLAY_NAVI_TIMER, "Display Navi Timer", CVAR_TIME_DISPLAY("Timers.NaviTimer") }
{ DISPLAY_NAVI_TIMER, "Display Navi Timer", CVAR_TIME_DISPLAY("Timers.NaviTimer") },
{ DISPLAY_FRED_QUEST, "Display Fred's Quest", CVAR_TIME_DISPLAY("Timers.FredsQuest") }
};
static std::vector<TimeObject> activeTimers;
@@ -134,6 +136,10 @@ static void TimeDisplayGetTimer(uint32_t timeID) {
}
textureDisplay = Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName("NAVI_TIMER");
break;
case DISPLAY_FRED_QUEST:
timeDisplayTime = std::to_string(FredsQuestWoodOnHand) + "/" + std::to_string(FredsQuestWoodCollected) + "/" +
std::to_string(CVarGetInteger("gHoliday.Fredomato.FredsQuest.WoodNeeded", 300));
textureDisplay = Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName("ITEM_STICK");
default:
break;
}
@@ -189,6 +195,12 @@ void TimeDisplayWindow::Draw() {
ImGui::Image(textureDisplay, ImVec2(16.0f * fontScale, 16.0f * fontScale));
ImGui::TableNextColumn();
if (timers.timeID == DISPLAY_FRED_QUEST) {
ImGui::Text("%s", timeDisplayTime.c_str());
ImGui::PopID();
continue;
}
if (timeDisplayTime != "-:--") {
char* textToDecode = new char[timeDisplayTime.size() + 1];
textToDecode = std::strcpy(textToDecode, timeDisplayTime.c_str());

View File

@@ -18,6 +18,7 @@ typedef enum TimerDisplay {
DISPLAY_TIME_OF_DAY,
DISPLAY_CONDITIONAL_TIMER,
DISPLAY_NAVI_TIMER,
DISPLAY_FRED_QUEST,
} TimerDisplay;
typedef enum NaviTimerValues {

View File

@@ -43,6 +43,7 @@ DEFINE_HOOK(OnEnemyDefeat, (void* actor));
DEFINE_HOOK(OnBossDefeat, (void* actor));
DEFINE_HOOK(OnTimestamp, (u8 item));
DEFINE_HOOK(OnPlayerBonk, ());
DEFINE_HOOK(OnPlayerRoll, ());
DEFINE_HOOK(OnPlayerHealthChange, (int16_t amount));
DEFINE_HOOK(OnPlayerBottleUpdate, (int16_t contents));
DEFINE_HOOK(OnPlayerHoldUpShield, ());

View File

@@ -203,6 +203,10 @@ void GameInteractor_ExecuteOnPlayerBonk() {
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnPlayerBonk>();
}
void GameInteractor_ExecuteOnPlayerRoll() {
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnPlayerRoll>();
}
void GameInteractor_ExecuteOnPlayerHealthChange(int16_t amount) {
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnPlayerHealthChange>(amount);
}

View File

@@ -44,6 +44,7 @@ void GameInteractor_ExecuteOnEnemyDefeat(void* actor);
void GameInteractor_ExecuteOnBossDefeat(void* actor);
void GameInteractor_ExecuteOnTimestamp(u8 item);
void GameInteractor_ExecuteOnPlayerBonk();
void GameInteractor_ExecuteOnPlayerRoll();
void GameInteractor_ExecuteOnPlayerHealthChange(int16_t amount);
void GameInteractor_ExecuteOnPlayerBottleUpdate(int16_t contents);
void GameInteractor_ExecuteOnPlayerHoldUpShield();

View File

@@ -2351,6 +2351,10 @@ typedef enum {
// - `*int32_t` // ItemID
VB_USE_ITEM,
VB_DRAW_SKEL_LIMB,
VB_DRAW_SKEL_FLEX_LIMB,
VB_PLAYER_ROLL,
} GIVanillaBehavior;
#endif

View File

@@ -4,6 +4,8 @@
#include "soh/Enhancements/game-interactor/GameInteractor.h"
#include "soh/OTRGlobals.h"
static bool isResultOfHandling = false;
/**
* SET_CHECK_STATUS
*
@@ -11,7 +13,7 @@
*/
void Anchor::SendPacket_SetCheckStatus(RandomizerCheck rc) {
if (!IsSaveLoaded() || isProcessingIncomingPacket || !roomState.syncItemsAndFlags) {
if (!IsSaveLoaded() || isResultOfHandling) {
return;
}
@@ -39,6 +41,8 @@ void Anchor::HandlePacket_SetCheckStatus(nlohmann::json payload) {
RandomizerCheck rc = payload["rc"].get<RandomizerCheck>();
RandomizerCheckStatus status = payload["status"].get<RandomizerCheckStatus>();
bool skipped = payload["skipped"].get<bool>();
isResultOfHandling = true;
if (randoContext->GetItemLocation(rc)->GetCheckStatus() != status) {
randoContext->GetItemLocation(rc)->SetCheckStatus(status);
@@ -46,6 +50,8 @@ void Anchor::HandlePacket_SetCheckStatus(nlohmann::json payload) {
if (randoContext->GetItemLocation(rc)->GetIsSkipped() != skipped) {
randoContext->GetItemLocation(rc)->SetIsSkipped(skipped);
}
CheckTracker::RecalculateAllAreaTotals();
CheckTracker::RecalculateAvailableChecks();
isResultOfHandling = false;
}

View File

@@ -8,6 +8,7 @@
#include "soh/Enhancements/game-interactor/GameInteractor.h"
#include "soh/Enhancements/randomizer/draw.h"
#include "soh/Enhancements/Holiday/Fredomato.h"
#include "soh/ResourceManagerHelpers.h"
#include <stdlib.h>

View File

@@ -554,7 +554,9 @@ u16 func_80A6F810(PlayState* play, Actor* thisx) {
return 0x5058;
}
case ENHY_TYPE_BOB_18:
if (!LINK_IS_ADULT) {
if (CVarGetInteger("gHoliday.Fredomato.TreeChopper", 0)) {
return 0x505e;
} else if (!LINK_IS_ADULT) {
return (Flags_GetEventChkInf(EVENTCHKINF_ZELDA_FLED_HYRULE_CASTLE))
? 0x505F
: ((Flags_GetInfTable(INFTABLE_163)) ? 0x505E : 0x505D);

View File

@@ -7,6 +7,7 @@
#include "z_en_wood02.h"
#include "objects/object_wood02/object_wood02.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#include "soh/Enhancements/Holiday/Fredomato.h"
#define FLAGS 0
@@ -368,8 +369,17 @@ void EnWood02_Update(Actor* thisx, PlayState* play2) {
if (this->actor.home.rot.y != 0) {
dropsSpawnPt = this->actor.world.pos;
dropsSpawnPt.y += 200.0f;
if (HandleTreeBonk(&this->actor)) {
// no-op
} else
if (GameInteractor_Should(VB_TREE_DROP_ITEM, true, this)) {
if ((this->unk_14C >= 0) && (this->unk_14C < 0x64) && (CVarGetInteger(CVAR_ENHANCEMENT("TreesDropSticks"), 0)) && !(INV_CONTENT(ITEM_STICK) == ITEM_NONE)) {
(numDrops = (Rand_ZeroOne() * 4));
for (i = 0; i < numDrops; ++i) {
Item_DropCollectible(play, &dropsSpawnPt, ITEM00_STICK);
}
}
} else {
if ((this->unk_14C >= 0) && (this->unk_14C < 0x64)) {
if (GameInteractor_Should(VB_TREE_DROP_COLLECTIBLE, true, this)) {
Item_DropCollectibleRandom(play, &this->actor, &dropsSpawnPt, this->unk_14C << 4);

View File

@@ -6304,11 +6304,16 @@ s32 func_8083BBA0(Player* this, PlayState* play) {
}
void Player_SetupRoll(Player* this, PlayState* play) {
if (!GameInteractor_Should(VB_PLAYER_ROLL, true)) {
return;
}
Player_SetupAction(play, this, Player_Action_Roll, 0);
LinkAnimation_PlayOnceSetSpeed(play, &this->skelAnime,
GET_PLAYER_ANIM(PLAYER_ANIMGROUP_landing_roll, this->modelAnimType),
1.25f * sWaterSpeedFactor);
gSaveContext.ship.stats.count[COUNT_ROLLS]++;
GameInteractor_ExecuteOnPlayerRoll();
}
s32 Player_TryRoll(Player* this, PlayState* play) {
@@ -8570,6 +8575,16 @@ void Player_Action_808414F8(Player* this, PlayState* play) {
}
Player_GetMovementSpeedAndYaw(this, &speedTarget, &yawTarget, SPEED_MODE_LINEAR, play);
int32_t giSpeedModifier = GameInteractor_RunSpeedModifier();
if (giSpeedModifier != 0) {
if (giSpeedModifier > 0) {
speedTarget *= giSpeedModifier;
} else {
speedTarget /= abs(giSpeedModifier);
}
}
sp2C = func_8083FD78(this, &speedTarget, &yawTarget, play);
if (sp2C >= 0) {