Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 27 additions & 5 deletions code/include/z3D/actors/z_en_dns.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,41 @@

#include "z3D/z3D.h"

typedef enum EnDnsType {
DNS_TYPE_DEKU_NUTS_5,
DNS_TYPE_DEKU_STICKS_1,
DNS_TYPE_HEART_PIECE,
DNS_TYPE_DEKU_SEEDS_30,
DNS_TYPE_DEKU_SHIELD,
DNS_TYPE_BOMBS_5,
DNS_TYPE_ARROWS_30,
DNS_TYPE_RED_POTION,
DNS_TYPE_GREEN_POTION,
DNS_TYPE_DEKU_STICK_UPGRADE,
DNS_TYPE_DEKU_NUT_UPGRADE,
DNS_TYPE_MAX,
} EnDnsType;

typedef enum EnDnsCanBuyResult {
DNS_CANBUY_RESULT_NEED_RUPEES,
DNS_CANBUY_RESULT_CAPACITY_FULL,
DNS_CANBUY_RESULT_SUCCESS_NEW_ITEM,
DNS_CANBUY_RESULT_CANT_GET_NOW,
DNS_CANBUY_RESULT_SUCCESS,
} EnDnsCanBuyResult;

struct EnDns;

typedef void (*EnDnsActionFunc)(struct EnDns* this, GlobalContext* globalCtx);

typedef u32 (*EnDnsPurchaseableCheck)(struct EnDns*);
typedef void (*EnDnsSetRupeesAndFlags)(struct EnDns*);
typedef u32 (*EnDnsCanBuyFunc)(struct EnDns*);
typedef void (*EnDnsPaymentFunc)(struct EnDns*);

typedef struct {
/* 0x00 */ s16 itemPrice;
/* 0x02 */ u16 itemAmount;
/* 0x04 */ s32 getItemID;
/* 0x08 */ EnDnsPurchaseableCheck purchaseableCheck;
/* 0x0C */ EnDnsSetRupeesAndFlags setRupeesAndFlags;
/* 0x08 */ EnDnsCanBuyFunc canBuy;
/* 0x0C */ EnDnsPaymentFunc payment;
} DnsItemEntry; // size = 0x10

typedef struct EnDns {
Expand Down
5 changes: 2 additions & 3 deletions code/oot.ld
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,6 @@ SECTIONS
EnBox_Init = 0x1899EC + _LD_OFF;
EnCow_Init = 0x189FD4 + _LD_OFF;
EnCow_Destroy = 0x18A3E4 + _LD_OFF;
.patch_BusinessScrubTable 0x18A634 + _LD_OFF : { *(.patch_BusinessScrubTable) }
EnGe1_Init = 0x18B218 + _LD_OFF;
.patch_CheckGerudoToken_18B368 0x18B368 + _LD_OFF : { *(.patch_CheckGerudoToken_18B368) }
.patch_CheckGerudoToken_18B3A0 0x18B3A0 + _LD_OFF : { *(.patch_CheckGerudoToken_18B3A0) }
Expand Down Expand Up @@ -285,7 +284,7 @@ SECTIONS
.patch_patch_ItemGiveBombchuDropTwo 0x22BAE4 + _LD_OFF : { *(.patch_ItemGiveBombchuDropTwo) }
.patch_GetObjectEntry_EnNutsballInit 0x22DEC4 + _LD_OFF : { *(.patch_GetObjectEntry_EnNutsballInit) }
EnShopnuts_Init = 0x22ED2C + _LD_OFF;
.patch_BusinessScrubCheckFlags 0x22EE64 + _LD_OFF : { *(.patch_BusinessScrubCheckFlags) }
.patch_BusinessScrubCheckFlags 0x22EE60 + _LD_OFF : { *(.patch_BusinessScrubCheckFlags) }
EnTorch2_Update = 0x22F0C8 + _LD_OFF;
.patch_DarkLinkPlayerRecoil 0x22F838 + _LD_OFF : { *(.patch_DarkLinkPlayerRecoil) }
ObjMure3_Update = 0x2318AC + _LD_OFF;
Expand Down Expand Up @@ -853,7 +852,7 @@ SECTIONS
gGanondorfBlood = 0x514F78 + _LD_OFF;
gGanonBlood = 0x515194 + _LD_OFF;
EnChanger_LoserGetItemIds = 0x521774 + _LD_OFF;
VanillaScrubTable = 0x522384 + _LD_OFF;
EnDns_ItemEntries = 0x522384 + _LD_OFF;
EnGirlA_ShopItemEntries = 0x524F50 + _LD_OFF;
.patch_BombchuCheapestPriceOne 0x525348 + _LD_OFF : { *(.patch_BombchuCheapestPriceOne) }
.patch_BombchuCheapestPriceTwo 0x5253D8 + _LD_OFF : { *(.patch_BombchuCheapestPriceTwo) }
Expand Down
77 changes: 31 additions & 46 deletions code/src/actors/business_scrubs.c
Original file line number Diff line number Diff line change
@@ -1,63 +1,45 @@
#include "business_scrubs.h"
#include "settings.h"
#include "multiplayer.h"
#include "common.h"

void EnDns_Update(Actor* thisx, GlobalContext* globalCtx);
void EnShopnuts_Init(Actor* thisx, GlobalContext* globalCtx);

u32 EnDns_rPurchaseableCheck(EnDns* scrub);
void EnDns_rSetRupeesAndFlags(EnDns* scrub);
void EnDns_rSetRupeesAndFlagsIfScrubsanity(EnDns* scrub);
static u32 EnDns_rCanBuy(EnDns* scrub);
static void EnDns_rPay(EnDns* scrub);

static const DnsItemEntry Scrub_0 = { 20, 5, 0x30, EnDns_rPurchaseableCheck, EnDns_rSetRupeesAndFlagsIfScrubsanity };
static const DnsItemEntry Scrub_1 = { 15, 1, 0x31, EnDns_rPurchaseableCheck, EnDns_rSetRupeesAndFlagsIfScrubsanity };
static const DnsItemEntry Scrub_2 = { 10, 1, 0x3E, EnDns_rPurchaseableCheck, EnDns_rSetRupeesAndFlags };
static const DnsItemEntry Scrub_3 = { 40, 30, 0x33, EnDns_rPurchaseableCheck, EnDns_rSetRupeesAndFlagsIfScrubsanity };
static const DnsItemEntry Scrub_4 = { 50, 1, 0x34, EnDns_rPurchaseableCheck, EnDns_rSetRupeesAndFlagsIfScrubsanity };
static const DnsItemEntry Scrub_5 = { 40, 5, 0x37, EnDns_rPurchaseableCheck, EnDns_rSetRupeesAndFlagsIfScrubsanity };
static const DnsItemEntry Scrub_6 = { 70, 20, 0x38, EnDns_rPurchaseableCheck, EnDns_rSetRupeesAndFlagsIfScrubsanity };
static const DnsItemEntry Scrub_7 = { 40, 1, 0x39, EnDns_rPurchaseableCheck, EnDns_rSetRupeesAndFlagsIfScrubsanity };
static const DnsItemEntry Scrub_8 = { 40, 1, 0x3A, EnDns_rPurchaseableCheck, EnDns_rSetRupeesAndFlagsIfScrubsanity };
static const DnsItemEntry Scrub_9 = { 40, 1, 0x77, EnDns_rPurchaseableCheck, EnDns_rSetRupeesAndFlags };
static const DnsItemEntry Scrub_A = { 40, 1, 0x79, EnDns_rPurchaseableCheck, EnDns_rSetRupeesAndFlags };

const DnsItemEntry* rScrubTable[] = { &Scrub_0, &Scrub_1, &Scrub_2, &Scrub_3, &Scrub_4, &Scrub_5,
&Scrub_6, &Scrub_7, &Scrub_8, &Scrub_9, &Scrub_A };

extern DnsItemEntry* VanillaScrubTable[];
extern DnsItemEntry* EnDns_ItemEntries[];

s16 rScrubRandomItemPrices[11] = { 0 };

u32 EnDns_rPurchaseableCheck(EnDns* scrub) {
void BusinessScrubs_Init(void) {
if (gSettingsContext.scrubsanity != SCRUBSANITY_OFF) {
for (s32 scrubType = 0; scrubType < DNS_TYPE_MAX; scrubType++) {
EnDns_ItemEntries[scrubType]->canBuy = EnDns_rCanBuy;
EnDns_ItemEntries[scrubType]->payment = EnDns_rPay;
}
}
}

static u32 EnDns_rCanBuy(EnDns* scrub) {
s16 price;

if (gSettingsContext.scrubsanity == SCRUBSANITY_OFF) {
return VanillaScrubTable[scrub->actor.params]->purchaseableCheck(scrub);
} else if (gSettingsContext.scrubsanity == SCRUBSANITY_AFFORDABLE) {
if (gSettingsContext.scrubsanity == SCRUBSANITY_AFFORDABLE) {
price = 10;
} else if (gSettingsContext.scrubsanity == SCRUBSANITY_RANDOM_PRICES) {
price = rScrubRandomItemPrices[scrub->actor.params];
} else {
} else { // SCRUBSANITY_EXPENSIVE
price = scrub->dnsItemEntry->itemPrice;
}

if (gSaveContext.rupees < price) {
return 0;
return DNS_CANBUY_RESULT_NEED_RUPEES;
}
return 4;
return DNS_CANBUY_RESULT_SUCCESS;
}

void EnDns_rSetRupees(EnDns* scrub) {
if (gSettingsContext.scrubsanity == SCRUBSANITY_AFFORDABLE) {
Rupees_ChangeBy(-10);
} else if (gSettingsContext.scrubsanity == SCRUBSANITY_RANDOM_PRICES) {
Rupees_ChangeBy(-rScrubRandomItemPrices[scrub->actor.params]);
} else {
Rupees_ChangeBy(-scrub->dnsItemEntry->itemPrice);
}
}

void EnDns_rSetRupeesAndFlags(EnDns* scrub) {
static void EnDns_rPay(EnDns* scrub) {
u32 sceneNum = gGlobalContext->sceneNum;
u32 bitMask = (0x1 << (scrub->actor.params + 1));

Expand All @@ -68,18 +50,21 @@ void EnDns_rSetRupeesAndFlags(EnDns* scrub) {
}

gSaveContext.sceneFlags[sceneNum].unk |= bitMask;
EnDns_rSetRupees(scrub);
}

void EnDns_rSetRupeesAndFlagsIfScrubsanity(EnDns* scrub) {
if (gSettingsContext.scrubsanity == SCRUBSANITY_OFF) {
EnDns_rSetRupees(scrub);
if (gSettingsContext.scrubsanity == SCRUBSANITY_AFFORDABLE) {
Rupees_ChangeBy(-10);
} else if (gSettingsContext.scrubsanity == SCRUBSANITY_RANDOM_PRICES) {
Rupees_ChangeBy(-rScrubRandomItemPrices[scrub->actor.params]);
} else {
EnDns_rSetRupeesAndFlags(scrub);
Rupees_ChangeBy(-scrub->dnsItemEntry->itemPrice);
}
}

u32 EnShopnnuts_rCheckFlags(EnShopnuts* scrub) {
s32 EnShopnnuts_rCheckFlags(EnShopnuts* scrub) {
if (gSettingsContext.scrubsanity == SCRUBSANITY_OFF) {
return -1; // Use vanilla checks.
}

u32 sceneNum = gGlobalContext->sceneNum;
u32 bitMask = (0x1 << (scrub->actor.params + 1));

Expand Down Expand Up @@ -118,8 +103,8 @@ void EnDns_rUpdate(Actor* thisx, GlobalContext* globalCtx) {
EnDns_Update(&scrub->actor, globalCtx);

// Skip scrubs with repeatable purchases
if (gSettingsContext.scrubsanity == SCRUBSANITY_OFF && scrub->actor.params != 0x2 && scrub->actor.params != 0x9 &&
scrub->actor.params != 0xA) {
if (gSettingsContext.scrubsanity == SCRUBSANITY_OFF && scrub->actor.params != DNS_TYPE_HEART_PIECE &&
scrub->actor.params != DNS_TYPE_DEKU_STICK_UPGRADE && scrub->actor.params != DNS_TYPE_DEKU_NUT_UPGRADE) {
return;
}

Expand Down
2 changes: 2 additions & 0 deletions code/src/actors/business_scrubs.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
#include "z3D/actors/z_en_dns.h"
#include "z3D/actors/z_en_shopnuts.h"

void BusinessScrubs_Init(void);

void EnDns_rUpdate(Actor* thisx, GlobalContext* globalCtx);
void EnDns_StartBurrow(EnDns* this);

Expand Down
11 changes: 11 additions & 0 deletions code/src/asm/hooks.s
Original file line number Diff line number Diff line change
Expand Up @@ -2181,3 +2181,14 @@ HOOK PlayerBonk
bl Player_OnBonk
pop {r0-r12, lr}
bx lr

HOOK BusinessScrubCheckFlags
push {r0-r12, lr}
cpy r0,r4 @ scrub actor
bl EnShopnnuts_rCheckFlags
cmp r0,#0x0
pop {r0-r12, lr}
addeq lr,lr,#0x0C @ 0: skip checks, don't kill actor
addgt lr,lr,#0x10 @ 1: kill actor
cmplt r1,#0x2 @ -1: resume vanilla checks
bx lr
7 changes: 1 addition & 6 deletions code/src/asm/patches.s
Original file line number Diff line number Diff line change
Expand Up @@ -410,12 +410,7 @@ PATCH FireArrowRequirement
tst r2,#0x400

PATCH BusinessScrubCheckFlags
cpy r0,r4
bl EnShopnnuts_rCheckFlags
cmp r0,#0x0

PATCH BusinessScrubTable
.word rScrubTable
bl hook_BusinessScrubCheckFlags

PATCH KakarikoGateCheck
bl hook_KakarikoGateCheck
Expand Down
36 changes: 18 additions & 18 deletions code/src/cutscenes.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,20 +57,20 @@ void Cutscene_OverrideLACS(void) {
break;
}
if (conditionMet) {
ItemOverride_PushDelayedOverride(0x01);
ItemOverride_PushDelayedOverride(DLYOVR_LACS);
EventSet(0xC4);
gSaveContext.entranceIndex = 0x58C;
}
}

void Cutscene_OverrideMinuet(void) {
gSaveContext.eventChkInf[5] |= 0x1;
ItemOverride_PushDelayedOverride(0x20);
ItemOverride_PushDelayedOverride(DLYOVR_SONG_MINUET);
}

void Cutscene_OverrideBolero(void) {
gSaveContext.eventChkInf[5] |= 0x2;
ItemOverride_PushDelayedOverride(0x21);
ItemOverride_PushDelayedOverride(DLYOVR_SONG_BOLERO);
}

u32 Cutscene_SerenadeCheckChestFlag(void) {
Expand All @@ -79,13 +79,13 @@ u32 Cutscene_SerenadeCheckChestFlag(void) {

void Cutscene_OverrideSerenade(void) {
gSaveContext.eventChkInf[5] |= 0x4;
ItemOverride_PushDelayedOverride(0x22);
ItemOverride_PushDelayedOverride(DLYOVR_SONG_SERENADE);
}

u32 Cutscene_OverrideRequiem(void) {
if (!EventCheck(0xAC) &&
Entrance_SceneAndSpawnAre(0x5C, 0x01)) { // Spirit Temple -> Desert Colossus, index 01E1 in the entrance table
ItemOverride_PushDelayedOverride(0x23);
ItemOverride_PushDelayedOverride(DLYOVR_SONG_REQUIEM);
EventSet(0xAC);
gSaveContext.entranceIndex = 0x1ED;
}
Expand All @@ -96,7 +96,7 @@ void Cutscene_OverrideNocturne(void) {
if ((gEntranceTable[gSaveContext.entranceIndex].scene == 0x52) && (gSaveContext.linkAge == AGE_ADULT)) {
if ((gSaveContext.questItems & 0x1) && (gSaveContext.questItems & 0x2) && (gSaveContext.questItems & 0x4)) {
if (!EventCheck(0xAA)) {
ItemOverride_PushDelayedOverride(0x24);
ItemOverride_PushDelayedOverride(DLYOVR_SONG_NOCTURNE);
EventSet(0xAA);
gSaveContext.entranceIndex = 0x513;
}
Expand All @@ -106,7 +106,7 @@ void Cutscene_OverrideNocturne(void) {

u32 Cutscene_OverridePrelude(void) {
if (gSaveContext.questItems & 0x1) {
ItemOverride_PushDelayedOverride(0x25);
ItemOverride_PushDelayedOverride(DLYOVR_SONG_PRELUDE);
gSaveContext.eventChkInf[5] |= 0x20;
return 1;
}
Expand All @@ -118,11 +118,11 @@ u32 Cutscene_CheckLullabyFlag(void) {
}

void Cutscene_OverrideLullaby(void) {
ItemOverride_PushDelayedOverride(DLYOVR_SONG_LULLABY);
gSaveContext.eventChkInf[5] |= 0x200;
ItemOverride_PushDelayedOverride(0x26);
gGlobalContext->nextEntranceIndex = 0x594;
gGlobalContext->sceneLoadFlag = 0x14;
PLAYER->actor.draw = NULL;
PLAYER->stateFlags1 |= 1; // Loading area
}

u32 Cutscene_CheckEponasSongFlag(void) {
Expand All @@ -131,7 +131,7 @@ u32 Cutscene_CheckEponasSongFlag(void) {

void Cutscene_OverrideEponasSong(void) {
gSaveContext.eventChkInf[5] |= 0x100;
ItemOverride_PushDelayedOverride(0x27);
ItemOverride_PushDelayedOverride(DLYOVR_SONG_EPONA);
gGlobalContext->unk_2B7E = 4;
}

Expand All @@ -141,43 +141,43 @@ u32 Cutscene_CheckSariasSongFlag(void) {

void Cutscene_OverrideSariasSong(void) {
if (!(gSaveContext.eventChkInf[5] & 0x80)) {
ItemOverride_PushDelayedOverride(0x28);
ItemOverride_PushDelayedOverride(DLYOVR_SONG_SARIA);
gSaveContext.eventChkInf[5] |= 0x80;
}
}

void Cutscene_OverrideSunsSong(void) {
if (!(gSaveContext.eventChkInf[5] & 0x400)) {
ItemOverride_PushDelayedOverride(0x29);
ItemOverride_PushDelayedOverride(DLYOVR_SONG_SUN);
gSaveContext.eventChkInf[5] |= 0x400;
}
}

void Cutscene_OverrideSongOfTime(Actor* ocarina) {
gSaveContext.eventChkInf[10] |= 0x200;
ItemOverride_PushDelayedOverride(0x2A);
ItemOverride_PushDelayedOverride(DLYOVR_SONG_TIME);
gGlobalContext->nextEntranceIndex = 0x50F;
gGlobalContext->sceneLoadFlag = 0x14;
}

void Cutscene_OverrideSongOfStorms(void) {
gSaveContext.eventChkInf[6] |= 0x20;
gSaveContext.eventChkInf[5] |= 0x800;
ItemOverride_PushDelayedOverride(0x2B);
ItemOverride_PushDelayedOverride(DLYOVR_SONG_STORMS);
gGlobalContext->unk_2B7E = 4;
}

void Cutscene_OverrideFairyReward(BgDyYoseizo* fairy) {
s16 fairyIdx = fairy->unk_D2C;

if (gGlobalContext->sceneNum == 0x3D) {
if (gGlobalContext->sceneNum == SCENE_GREAT_FAIRYS_FOUNTAIN_SPELLS) {
if (!(gSaveContext.itemGetInf[1] & (0x100 << fairyIdx))) {
ItemOverride_PushDelayedOverride(0x10 + fairyIdx);
ItemOverride_PushDelayedOverride(DLYOVR_FAIRY_ZF + fairyIdx);
gSaveContext.itemGetInf[1] |= (0x100 << fairyIdx);
}
} else if (gGlobalContext->sceneNum == 0x3B) {
} else if (gGlobalContext->sceneNum == SCENE_GREAT_FAIRYS_FOUNTAIN_MAGIC) {
if (!(gGlobalContext->actorCtx.flags.chest & (0x1 << fairyIdx))) {
ItemOverride_PushDelayedOverride(0x13 + fairyIdx);
ItemOverride_PushDelayedOverride(DLYOVR_FAIRY_DMT + fairyIdx);
gGlobalContext->actorCtx.flags.chest |= (0x1 << fairyIdx);
}
}
Expand Down
Loading