Merge pull request #1264 from garrettjoecox/scrubsanity

Scrubsanity
This commit is contained in:
briaguya 2022-08-25 16:48:28 -04:00 committed by GitHub
commit 1cfa0a9623
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 536 additions and 58 deletions

View File

@ -183,7 +183,7 @@ typedef struct {
char ganonHintText[150];
char ganonText[250];
u8 seedIcons[5];
u16 randomizerInf[2];
u16 randomizerInf[4];
u8 temporaryWeapon;
u16 adultTradeItems;
} SaveContext; // size = 0x1428

View File

@ -1039,6 +1039,19 @@ int Fill() {
//Fast fill for the rest of the pool
std::vector<uint32_t> remainingPool = FilterAndEraseFromPool(ItemPool, [](const auto i) { return true; });
FastFill(remainingPool, GetAllEmptyLocations(), false);
//Add prices for scrubsanity, this is unique to SoH because we write/read scrub prices to/from the spoilerfile.
if (Scrubsanity.Is(SCRUBSANITY_AFFORDABLE)) {
for (size_t i = 0; i < ScrubLocations.size(); i++) {
Location(ScrubLocations[i])->SetScrubsanityPrice(10);
}
} else if (Scrubsanity.Is(SCRUBSANITY_RANDOM_PRICES)) {
for (size_t i = 0; i < ScrubLocations.size(); i++) {
int randomPrice = GetRandomScrubPrice();
Location(ScrubLocations[i])->SetScrubsanityPrice(randomPrice);
}
}
GeneratePlaythrough();
//Successful placement, produced beatable result
if(playthroughBeatable && !placementFailure) {

View File

@ -1011,6 +1011,56 @@ std::vector<std::vector<uint32_t>> ShopLocationLists = {
GC_ShopLocations,
};
//List of scrubs, used for pricing the scrubs
std::vector<uint32_t> ScrubLocations = {
LW_DEKU_SCRUB_NEAR_DEKU_THEATER_RIGHT,
LW_DEKU_SCRUB_NEAR_DEKU_THEATER_LEFT,
LW_DEKU_SCRUB_NEAR_BRIDGE,
LW_DEKU_SCRUB_GROTTO_REAR,
LW_DEKU_SCRUB_GROTTO_FRONT,
SFM_DEKU_SCRUB_GROTTO_REAR,
SFM_DEKU_SCRUB_GROTTO_FRONT,
HF_DEKU_SCRUB_GROTTO,
LH_DEKU_SCRUB_GROTTO_LEFT,
LH_DEKU_SCRUB_GROTTO_RIGHT,
LH_DEKU_SCRUB_GROTTO_CENTER,
GV_DEKU_SCRUB_GROTTO_REAR,
GV_DEKU_SCRUB_GROTTO_FRONT,
COLOSSUS_DEKU_SCRUB_GROTTO_REAR,
COLOSSUS_DEKU_SCRUB_GROTTO_FRONT,
GC_DEKU_SCRUB_GROTTO_LEFT,
GC_DEKU_SCRUB_GROTTO_RIGHT,
GC_DEKU_SCRUB_GROTTO_CENTER,
DMC_DEKU_SCRUB,
DMC_DEKU_SCRUB_GROTTO_LEFT,
DMC_DEKU_SCRUB_GROTTO_RIGHT,
DMC_DEKU_SCRUB_GROTTO_CENTER,
ZR_DEKU_SCRUB_GROTTO_REAR,
ZR_DEKU_SCRUB_GROTTO_FRONT,
LLR_DEKU_SCRUB_GROTTO_LEFT,
LLR_DEKU_SCRUB_GROTTO_RIGHT,
LLR_DEKU_SCRUB_GROTTO_CENTER,
DEKU_TREE_MQ_DEKU_SCRUB,
DODONGOS_CAVERN_DEKU_SCRUB_NEAR_BOMB_BAG_LEFT,
DODONGOS_CAVERN_DEKU_SCRUB_SIDE_ROOM_NEAR_DODONGOS,
DODONGOS_CAVERN_DEKU_SCRUB_NEAR_BOMB_BAG_RIGHT,
DODONGOS_CAVERN_DEKU_SCRUB_LOBBY,
DODONGOS_CAVERN_MQ_DEKU_SCRUB_LOBBY_REAR,
DODONGOS_CAVERN_MQ_DEKU_SCRUB_LOBBY_FRONT,
DODONGOS_CAVERN_MQ_DEKU_SCRUB_STAIRCASE,
DODONGOS_CAVERN_MQ_DEKU_SCRUB_SIDE_ROOM_NEAR_LOWER_LIZALFOS,
JABU_JABUS_BELLY_DEKU_SCRUB,
GANONS_CASTLE_DEKU_SCRUB_CENTER_LEFT,
GANONS_CASTLE_DEKU_SCRUB_CENTER_RIGHT,
GANONS_CASTLE_DEKU_SCRUB_RIGHT,
GANONS_CASTLE_DEKU_SCRUB_LEFT,
GANONS_CASTLE_MQ_DEKU_SCRUB_RIGHT,
GANONS_CASTLE_MQ_DEKU_SCRUB_CENTER_LEFT,
GANONS_CASTLE_MQ_DEKU_SCRUB_CENTER,
GANONS_CASTLE_MQ_DEKU_SCRUB_CENTER_RIGHT,
GANONS_CASTLE_MQ_DEKU_SCRUB_LEFT,
};
//List of gossip stone locations for hints
std::vector<uint32_t> gossipStoneLocations = {
DMC_GOSSIP_STONE,

View File

@ -257,8 +257,8 @@ public:
}
void SetPrice(uint16_t price_) {
//don't override price if the price was set for shopsanity
if (hasShopsanityPrice) {
//don't override price if the price was set for shopsanity/scrubsanity
if (hasShopsanityPrice || hasScrubsanityPrice) {
return;
}
price = price_;
@ -269,10 +269,19 @@ public:
hasShopsanityPrice = true;
}
void SetScrubsanityPrice(uint16_t price_) {
price = price_;
hasScrubsanityPrice = true;
}
bool HasShopsanityPrice() const {
return hasShopsanityPrice;
}
bool HasScrubsanityPrice() const {
return hasScrubsanityPrice;
}
bool IsExcluded() const {
return excludedOption.Value<bool>();
}
@ -426,6 +435,7 @@ public:
isHintable = false;
price = 0;
hasShopsanityPrice = false;
hasScrubsanityPrice = false;
hidden = false;
}
@ -451,6 +461,7 @@ private:
bool isHintable = false;
uint32_t parentRegion = NONE;
bool hasShopsanityPrice = false;
bool hasScrubsanityPrice = false;
bool hidden = false;
};
@ -467,6 +478,8 @@ ItemLocation* Location(uint32_t locKey);
extern std::vector<std::vector<uint32_t>> ShopLocationLists;
extern std::vector<uint32_t> ScrubLocations;
extern std::vector<uint32_t> gossipStoneLocations;
extern std::vector<uint32_t> dungeonRewardLocations;

View File

@ -2532,6 +2532,7 @@ namespace Settings {
ShuffleRewards.SetSelectedIndex(cvarSettings[RSK_SHUFFLE_DUNGEON_REWARDS]);
ShuffleSongs.SetSelectedIndex(cvarSettings[RSK_SHUFFLE_SONGS]);
Tokensanity.SetSelectedIndex(cvarSettings[RSK_SHUFFLE_TOKENS]);
Scrubsanity.SetSelectedIndex(cvarSettings[RSK_SHUFFLE_SCRUBS]);
ShuffleCows.SetSelectedIndex(cvarSettings[RSK_SHUFFLE_COWS]);
ShuffleKokiriSword.SetSelectedIndex(cvarSettings[RSK_SHUFFLE_KOKIRI_SWORD]);
ShuffleOcarinas.SetSelectedIndex(cvarSettings[RSK_SHUFFLE_OCARINA]);

View File

@ -673,15 +673,24 @@ static void WriteHints(int language) {
static void WriteAllLocations(int language) {
for (const uint32_t key : allLocations) {
ItemLocation* location = Location(key);
std::string placedItemName;
switch (language) {
case 0:
default:
jsonData["locations"][location->GetName()] = location->GetPlacedItemName().english;
break;
case 2:
jsonData["locations"][location->GetName()] = location->GetPlacedItemName().french;
break;
case 0:
default:
location->GetPlacedItemName().english;
break;
case 2:
location->GetPlacedItemName().french;
break;
}
// Eventually check for other things here like fake name
if (location->HasScrubsanityPrice() || location->HasShopsanityPrice()) {
jsonData["locations"][location->GetName()]["item"] = placedItemName;
jsonData["locations"][location->GetName()]["price"] = location->GetPrice();
} else {
jsonData["locations"][location->GetName()] = placedItemName;
}
}
}

View File

@ -104,6 +104,7 @@ Sprite* Randomizer::GetSeedTexture(uint8_t index) {
Randomizer::~Randomizer() {
this->randoSettings.clear();
this->itemLocations.clear();
this->randomizerMerchantPrices.clear();
}
std::unordered_map<s16, s16> getItemIdToItemId = {
@ -546,6 +547,7 @@ std::unordered_map<std::string, RandomizerSettingKey> SpoilerfileSettingNameToEn
{ "Open Settings:Random Ganon's Trials", RSK_RANDOM_TRIALS },
{ "Open Settings:Trial Count", RSK_TRIAL_COUNT },
{ "Shuffle Settings:Shuffle Gerudo Card", RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD },
{ "Shuffle Settings:Scrub Shuffle", RSK_SHUFFLE_SCRUBS },
{ "Shuffle Settings:Shuffle Cows", RSK_SHUFFLE_COWS },
{ "Shuffle Settings:Tokensanity", RSK_SHUFFLE_TOKENS },
{ "Shuffle Settings:Shuffle Adult Trade", RSK_SHUFFLE_ADULT_TRADE },
@ -772,6 +774,17 @@ void Randomizer::ParseRandomizerSettingsFile(const char* spoilerFileName) {
numericValueString = it.value();
gSaveContext.randoSettings[index].value = std::stoi(numericValueString);
break;
case RSK_SHUFFLE_SCRUBS:
if(it.value() == "Off") {
gSaveContext.randoSettings[index].value = 0;
} else if(it.value() == "Affordable") {
gSaveContext.randoSettings[index].value = 1;
} else if(it.value() == "Expensive") {
gSaveContext.randoSettings[index].value = 2;
} else if(it.value() == "Random Prices") {
gSaveContext.randoSettings[index].value = 3;
}
break;
case RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD:
case RSK_SHUFFLE_COWS:
case RSK_SHUFFLE_ADULT_TRADE:
@ -1102,9 +1115,10 @@ void Randomizer::ParseItemLocationsFile(const char* spoilerFileName, bool silent
for (auto itemit = itemJson.begin(); itemit != itemJson.end(); ++itemit) {
// todo handle prices
if (itemit.key() == "item") {
gSaveContext.itemLocations[index].check = SpoilerfileCheckNameToEnum[it.key()];
gSaveContext.itemLocations[index].get = SpoilerfileGetNameToEnum[itemit.value()];
} else if (itemit.key() == "price") {
randomizerMerchantPrices[gSaveContext.itemLocations[index].check] = itemit.value();
}
}
} else {
@ -1588,6 +1602,280 @@ std::string Randomizer::GetGanonHintText() const {
return ganonHintText;
}
ScrubIdentity Randomizer::IdentifyScrub(s32 sceneNum, s32 actorParams, s32 respawnData) {
struct ScrubIdentity scrubIdentity;
scrubIdentity.randomizerCheck = RC_UNKNOWN_CHECK;
scrubIdentity.getItemId = GI_NONE;
scrubIdentity.itemPrice = -1;
scrubIdentity.isShuffled = GetRandoSettingValue(RSK_SHUFFLE_SCRUBS) > 0;
// Based on z_en_dns.c 93-113
switch (actorParams) {
case 0x00:
scrubIdentity.getItemId = GI_NUTS_5_2;
break;
case 0x01:
scrubIdentity.getItemId = GI_STICKS_1;
break;
case 0x02:
scrubIdentity.getItemId = GI_HEART_PIECE;
break;
case 0x03:
scrubIdentity.getItemId = GI_SEEDS_30;
break;
case 0x04:
scrubIdentity.getItemId = GI_SHIELD_DEKU;
break;
case 0x05:
scrubIdentity.getItemId = GI_BOMBS_5;
break;
case 0x06:
scrubIdentity.getItemId = GI_ARROWS_LARGE;
break;
case 0x07:
scrubIdentity.getItemId = GI_POTION_RED;
break;
case 0x08:
scrubIdentity.getItemId = GI_POTION_GREEN;
break;
case 0x09:
scrubIdentity.getItemId = GI_STICK_UPGRADE_20;
break;
case 0x0A:
scrubIdentity.getItemId = GI_NUT_UPGRADE_30;
break;
}
// TODO: Handle MQ scrubs
switch (sceneNum) {
case SCENE_DDAN: // Dodongo's Cavern
switch (actorParams) {
case 0x00:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_DODONGOS_CAVERN_DEKU_SCRUB_NEAR_BOMB_BAG_LEFT;
scrubIdentity.randomizerCheck = RC_DODONGOS_CAVERN_DEKU_SCRUB_NEAR_BOMB_BAG_LEFT;
break;
case 0x01:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_DODONGOS_CAVERN_DEKU_SCRUB_SIDE_ROOM_NEAR_DODONGOS;
scrubIdentity.randomizerCheck = RC_DODONGOS_CAVERN_DEKU_SCRUB_SIDE_ROOM_NEAR_DODONGOS;
break;
case 0x03:
case 0x06:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_DODONGOS_CAVERN_DEKU_SCRUB_NEAR_BOMB_BAG_RIGHT;
scrubIdentity.randomizerCheck = RC_DODONGOS_CAVERN_DEKU_SCRUB_NEAR_BOMB_BAG_RIGHT;
break;
case 0x04:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_DODONGOS_CAVERN_DEKU_SCRUB_LOBBY;
scrubIdentity.randomizerCheck = RC_DODONGOS_CAVERN_DEKU_SCRUB_LOBBY;
break;
}
break;
case SCENE_BDAN: // Jabu Jabu's Belly
switch (actorParams) {
case 0x00:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_JABU_JABUS_BELLY_DEKU_SCRUB;
scrubIdentity.randomizerCheck = RC_JABU_JABUS_BELLY_DEKU_SCRUB;
break;
}
break;
case SCENE_GANONTIKA: // Ganon's Castle
switch (actorParams) {
case 0x05:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_GANONS_CASTLE_DEKU_SCRUB_CENTER_LEFT;
scrubIdentity.randomizerCheck = RC_GANONS_CASTLE_DEKU_SCRUB_CENTER_LEFT;
break;
case 0x03:
case 0x06:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_GANONS_CASTLE_DEKU_SCRUB_CENTER_RIGHT;
scrubIdentity.randomizerCheck = RC_GANONS_CASTLE_DEKU_SCRUB_CENTER_RIGHT;
break;
case 0x07:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_GANONS_CASTLE_DEKU_SCRUB_RIGHT;
scrubIdentity.randomizerCheck = RC_GANONS_CASTLE_DEKU_SCRUB_RIGHT;
break;
case 0x08:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_GANONS_CASTLE_DEKU_SCRUB_LEFT;
scrubIdentity.randomizerCheck = RC_GANONS_CASTLE_DEKU_SCRUB_LEFT;
break;
}
break;
case SCENE_KAKUSIANA: // Grotto
switch (respawnData) {
case 0xE6: // Hyrule Field Scrub Grotto
switch (actorParams) {
case 0x02:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_HF_DEKU_SCRUB_GROTTO;
scrubIdentity.randomizerCheck = RC_HF_DEKU_SCRUB_GROTTO;
scrubIdentity.isShuffled = true;
break;
}
break;
case 0xEB: // ZR Scrub Grotto
switch (actorParams) {
case 0x07:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_ZR_DEKU_SCRUB_GROTTO_REAR;
scrubIdentity.randomizerCheck = RC_ZR_DEKU_SCRUB_GROTTO_REAR;
break;
case 0x08:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_ZR_DEKU_SCRUB_GROTTO_FRONT;
scrubIdentity.randomizerCheck = RC_ZR_DEKU_SCRUB_GROTTO_FRONT;
break;
}
break;
case 0xEE: // Sacred Forest Meadow Scrub Grotto
switch (actorParams) {
case 0x07:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_SFM_DEKU_SCRUB_GROTTO_REAR;
scrubIdentity.randomizerCheck = RC_SFM_DEKU_SCRUB_GROTTO_REAR;
break;
case 0x08:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_SFM_DEKU_SCRUB_GROTTO_FRONT;
scrubIdentity.randomizerCheck = RC_SFM_DEKU_SCRUB_GROTTO_FRONT;
break;
}
break;
case 0xEF: // Lake Hylia Scrub Grotto
switch (actorParams) {
case 0x00:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_LH_DEKU_SCRUB_GROTTO_LEFT;
scrubIdentity.randomizerCheck = RC_LH_DEKU_SCRUB_GROTTO_LEFT;
break;
case 0x05:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_LH_DEKU_SCRUB_GROTTO_RIGHT;
scrubIdentity.randomizerCheck = RC_LH_DEKU_SCRUB_GROTTO_RIGHT;
break;
case 0x03:
case 0x06:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_LH_DEKU_SCRUB_GROTTO_CENTER;
scrubIdentity.randomizerCheck = RC_LH_DEKU_SCRUB_GROTTO_CENTER;
break;
}
break;
case 0xF0: // Gerudo Valley Scrub Grotto
switch (actorParams) {
case 0x07:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_GV_DEKU_SCRUB_GROTTO_REAR;
scrubIdentity.randomizerCheck = RC_GV_DEKU_SCRUB_GROTTO_REAR;
break;
case 0x08:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_GV_DEKU_SCRUB_GROTTO_FRONT;
scrubIdentity.randomizerCheck = RC_GV_DEKU_SCRUB_GROTTO_FRONT;
break;
}
break;
case 0xF5: // Lost Woods Scrub Grotto
switch (actorParams) {
case 0x03:
case 0x06:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_LW_DEKU_SCRUB_GROTTO_REAR;
scrubIdentity.randomizerCheck = RC_LW_DEKU_SCRUB_GROTTO_REAR;
break;
case 0x0A:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_LW_DEKU_SCRUB_GROTTO_FRONT;
scrubIdentity.randomizerCheck = RC_LW_DEKU_SCRUB_GROTTO_FRONT;
scrubIdentity.isShuffled = true;
break;
}
break;
case 0xF9: // Death Mountain Crater Scrub Grotto
switch (actorParams) {
case 0x00:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_DMC_DEKU_SCRUB_GROTTO_LEFT;
scrubIdentity.randomizerCheck = RC_DMC_DEKU_SCRUB_GROTTO_LEFT;
break;
case 0x05:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_DMC_DEKU_SCRUB_GROTTO_RIGHT;
scrubIdentity.randomizerCheck = RC_DMC_DEKU_SCRUB_GROTTO_RIGHT;
break;
case 0x03:
case 0x06:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_DMC_DEKU_SCRUB_GROTTO_CENTER;
scrubIdentity.randomizerCheck = RC_DMC_DEKU_SCRUB_GROTTO_CENTER;
break;
}
break;
case 0xFB: // Gerudo City Scrub Grotto
switch (actorParams) {
case 0x00:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_GC_DEKU_SCRUB_GROTTO_LEFT;
scrubIdentity.randomizerCheck = RC_GC_DEKU_SCRUB_GROTTO_LEFT;
break;
case 0x05:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_GC_DEKU_SCRUB_GROTTO_RIGHT;
scrubIdentity.randomizerCheck = RC_GC_DEKU_SCRUB_GROTTO_RIGHT;
break;
case 0x03:
case 0x06:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_GC_DEKU_SCRUB_GROTTO_CENTER;
scrubIdentity.randomizerCheck = RC_GC_DEKU_SCRUB_GROTTO_CENTER;
break;
}
break;
case 0xFC: // Lon Lon Ranch Scrub Grotto
switch (actorParams) {
case 0x00:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_LLR_DEKU_SCRUB_GROTTO_LEFT;
scrubIdentity.randomizerCheck = RC_LLR_DEKU_SCRUB_GROTTO_LEFT;
break;
case 0x05:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_LLR_DEKU_SCRUB_GROTTO_RIGHT;
scrubIdentity.randomizerCheck = RC_LLR_DEKU_SCRUB_GROTTO_RIGHT;
break;
case 0x03:
case 0x06:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_LLR_DEKU_SCRUB_GROTTO_CENTER;
scrubIdentity.randomizerCheck = RC_LLR_DEKU_SCRUB_GROTTO_CENTER;
break;
}
break;
case 0xFD: // Desert Colossus Scrub Grotto
switch (actorParams) {
case 0x07:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_COLOSSUS_DEKU_SCRUB_GROTTO_REAR;
scrubIdentity.randomizerCheck = RC_COLOSSUS_DEKU_SCRUB_GROTTO_REAR;
break;
case 0x08:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_COLOSSUS_DEKU_SCRUB_GROTTO_FRONT;
scrubIdentity.randomizerCheck = RC_COLOSSUS_DEKU_SCRUB_GROTTO_FRONT;
break;
}
break;
}
break;
case SCENE_SPOT10: // Lost woods
switch (actorParams) {
case 0x00:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_LW_DEKU_SCRUB_NEAR_DEKU_THEATER_RIGHT;
scrubIdentity.randomizerCheck = RC_LW_DEKU_SCRUB_NEAR_DEKU_THEATER_RIGHT;
break;
case 0x01:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_LW_DEKU_SCRUB_NEAR_DEKU_THEATER_LEFT;
scrubIdentity.randomizerCheck = RC_LW_DEKU_SCRUB_NEAR_DEKU_THEATER_LEFT;
break;
case 0x09:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_LW_DEKU_SCRUB_NEAR_BRIDGE;
scrubIdentity.randomizerCheck = RC_LW_DEKU_SCRUB_NEAR_BRIDGE;
scrubIdentity.isShuffled = true;
break;
}
break;
case SCENE_SPOT17: // Death Mountain Crater
switch (actorParams) {
case 0x05:
scrubIdentity.randomizerInf = RAND_INF_SCRUBS_PURCHASED_DMC_DEKU_SCRUB;
scrubIdentity.randomizerCheck = RC_DMC_DEKU_SCRUB;
break;
}
break;
}
if (randomizerMerchantPrices.find(scrubIdentity.randomizerCheck) != randomizerMerchantPrices.end()) {
scrubIdentity.itemPrice = randomizerMerchantPrices[scrubIdentity.randomizerCheck];
}
return scrubIdentity;
}
u8 Randomizer::GetRandoSettingValue(RandomizerSettingKey randoSettingKey) {
return this->randoSettings[randoSettingKey];
}
@ -2181,10 +2469,6 @@ RandomizerCheck Randomizer::GetCheckFromActor(s16 sceneNum, s16 actorId, s16 act
break;
case 62:
switch (actorParams) {
case 2:
return RC_HF_DEKU_SCRUB_GROTTO;
case 10:
return RC_LW_DEKU_SCRUB_GROTTO_FRONT;
case 22988:
return RC_KF_STORMS_GROTTO_CHEST;
case -22988:
@ -2449,8 +2733,6 @@ RandomizerCheck Randomizer::GetCheckFromActor(s16 sceneNum, s16 actorId, s16 act
break;
case 91:
switch (actorParams) {
case 9:
return RC_LW_DEKU_SCRUB_NEAR_BRIDGE;
case 14365:
return RC_LW_GOSSIP_STONE;
case 27905:
@ -2615,6 +2897,7 @@ void GenerateRandomizerImgui() {
cvarSettings[RSK_SHUFFLE_DUNGEON_REWARDS] = CVar_GetS32("gRandomizeShuffleDungeonReward", 0);
cvarSettings[RSK_SHUFFLE_SONGS] = CVar_GetS32("gRandomizeShuffleSongs", 0);
cvarSettings[RSK_SHUFFLE_TOKENS] = CVar_GetS32("gRandomizeShuffleTokens", 0);
cvarSettings[RSK_SHUFFLE_SCRUBS] = CVar_GetS32("gRandomizeShuffleScrubs", 0);
cvarSettings[RSK_SHUFFLE_COWS] = CVar_GetS32("gRandomizeShuffleCows", 0);
cvarSettings[RSK_SHUFFLE_ADULT_TRADE] = CVar_GetS32("gRandomizeShuffleAdultTrade", 0);
cvarSettings[RSK_SKIP_CHILD_ZELDA] = CVar_GetS32("gRandomizeSkipChildZelda", 0);
@ -3111,6 +3394,20 @@ void DrawRandoEditor(bool& open) {
SohImGui::EnhancementCombobox("gRandomizeShuffleSongs", randoShuffleSongs, 3, 0);
PaddedSeparator();
// Shuffle Scrubs
ImGui::Text(Settings::Scrubsanity.GetName().c_str());
InsertHelpHoverText(
"Off - Scrubs will not be shuffled. The 3 Scrubs that give one-time items in the vanilla game (PoH, Deku Nut capacity, and Deku Stick capacity) will have random items.\n"
"\n"
"Affordable - Scrubs will be shuffled and their item will cost 10 rupees.\n"
"\n"
"Expensive - Scrubs will be shuffled and their item will cost the vanilla price.\n"
"\n"
"Random - Scrubs will be shuffled and their item will cost will be between 0-95 rupees.\n"
);
SohImGui::EnhancementCombobox("gRandomizeShuffleScrubs", randoShuffleScrubs, 4, 0);
PaddedSeparator();
// Shuffle Tokens
ImGui::Text(Settings::Tokensanity.GetName().c_str());
InsertHelpHoverText("Shuffles Golden Skulltula Tokens into the item pool. This means "
@ -3131,10 +3428,10 @@ void DrawRandoEditor(bool& open) {
"expected to be collected after getting Sun's Song.");
PaddedSeparator();
// Shuffle Cows
SohImGui::EnhancementCheckbox(Settings::ShuffleCows.GetName().c_str(), "gRandomizeShuffleCows");
InsertHelpHoverText(
"Cows give a randomized item from the pool upon performing Epona's Song in front of them.");
InsertHelpHoverText("Cows give a randomized item from the pool upon performing Epona's Song in front of them.");
PaddedSeparator();
// Shuffle Adult Trade Quest
@ -3725,11 +4022,19 @@ void CreateGetItemMessages(std::vector<GetItemMessage> messageEntries) {
}
}
// Currently these are generated at runtime, one for each price between 0-95. We're soon going to migrate this
// to being generated at save load, with only messages specific to each scrub.
void CreateScrubMessages() {
CustomMessageManager* customMessageManager = CustomMessageManager::Instance;
customMessageManager->AddCustomMessageTable(Randomizer::scrubMessageTableID);
const std::vector<u8> prices = { 10, 40 };
for (u8 price : prices) {
customMessageManager->CreateMessage(Randomizer::scrubMessageTableID, 0,
{ TEXTBOX_TYPE_BLACK, TEXTBOX_POS_BOTTOM,
"\x12\x38\x82\All right! You win! In return for&sparing me, I will give you a&%gmysterious item%w!&Please, take it!\x07\x10\xA3",
"\x12\x38\x82\In Ordnung! Du gewinnst! Im Austausch&dafür, dass du mich verschont hast,&werde ich dir einen %gmysteriösen&Gegenstand%w geben! Bitte nimm ihn!\x07\x10\xA3",
"\x12\x38\x82\D'accord! Vous avez gagné! En échange&de m'épargner, je vous donnerai un &%gobjet mystérieux%w! S'il vous plaît,&prenez-le!\x07\x10\xA3",
});
for (u32 price = 5; price <= 95; price += 5) {
customMessageManager->CreateMessage(Randomizer::scrubMessageTableID, price,
{ TEXTBOX_TYPE_BLACK, TEXTBOX_POS_BOTTOM,
"\x12\x38\x82\All right! You win! In return for&sparing me, I will sell you a&%gmysterious item%w!&%r" +

View File

@ -19,6 +19,7 @@ class Randomizer {
std::string ganonHintText;
std::string ganonText;
std::unordered_map<RandomizerSettingKey, u8> randoSettings;
std::unordered_map<RandomizerCheck, u16> randomizerMerchantPrices;
s16 GetItemFromGet(RandomizerGet randoGet, GetItemID ogItemId);
s16 GetItemFromActor(s16 actorId, s16 actorParams, s16 sceneNum, GetItemID ogItemId);
void ParseRandomizerSettingsFile(const char* spoilerFileName);
@ -50,6 +51,7 @@ class Randomizer {
std::string GetAdultAltarText() const;
std::string GetGanonText() const;
std::string GetGanonHintText() const;
ScrubIdentity IdentifyScrub(s32 sceneNum, s32 actorParams, s32 respawnData);
s16 GetRandomizedItemIdFromKnownCheck(RandomizerCheck randomizerCheck, GetItemID ogId);
s16 GetRandomizedItemId(GetItemID ogId, s16 actorId, s16 actorParams, s16 sceneNum);
static void CreateCustomMessages();

View File

@ -1,6 +1,8 @@
#pragma once
#include <stdint.h>
#include "z64item.h"
#include "randomizer_inf.h"
// This should probably go in a less rando-specific location
// but the best location will probably be in the modding engine
@ -987,6 +989,7 @@ typedef enum {
RSK_SHUFFLE_DUNGEON_REWARDS,
RSK_SHUFFLE_SONGS,
RSK_SHUFFLE_TOKENS,
RSK_SHUFFLE_SCRUBS,
RSK_SHUFFLE_COWS,
RSK_SHUFFLE_WEIRD_EGG,
RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD,
@ -1015,3 +1018,11 @@ typedef enum {
RSK_SKULLS_SUNS_SONG,
RSK_SHUFFLE_ADULT_TRADE
} RandomizerSettingKey;
typedef struct ScrubIdentity {
RandomizerInf randomizerInf;
RandomizerCheck randomizerCheck;
GetItemID getItemId;
int32_t itemPrice;
bool isShuffled;
} ScrubIdentity;

View File

@ -29,6 +29,43 @@ typedef enum {
RAND_INF_COWS_MILKED_JABU_JABUS_BELLY_MQ_COW,
RAND_INF_COWS_MILKED_HF_COW_GROTTO_GOSSIP_STONE,
RAND_INF_SCRUBS_PURCHASED_DODONGOS_CAVERN_DEKU_SCRUB_NEAR_BOMB_BAG_LEFT,
RAND_INF_SCRUBS_PURCHASED_DODONGOS_CAVERN_DEKU_SCRUB_SIDE_ROOM_NEAR_DODONGOS,
RAND_INF_SCRUBS_PURCHASED_DODONGOS_CAVERN_DEKU_SCRUB_NEAR_BOMB_BAG_RIGHT,
RAND_INF_SCRUBS_PURCHASED_DODONGOS_CAVERN_DEKU_SCRUB_LOBBY,
RAND_INF_SCRUBS_PURCHASED_JABU_JABUS_BELLY_DEKU_SCRUB,
RAND_INF_SCRUBS_PURCHASED_GANONS_CASTLE_DEKU_SCRUB_CENTER_LEFT,
RAND_INF_SCRUBS_PURCHASED_GANONS_CASTLE_DEKU_SCRUB_CENTER_RIGHT,
RAND_INF_SCRUBS_PURCHASED_GANONS_CASTLE_DEKU_SCRUB_RIGHT,
RAND_INF_SCRUBS_PURCHASED_GANONS_CASTLE_DEKU_SCRUB_LEFT,
RAND_INF_SCRUBS_PURCHASED_HF_DEKU_SCRUB_GROTTO,
RAND_INF_SCRUBS_PURCHASED_ZR_DEKU_SCRUB_GROTTO_REAR,
RAND_INF_SCRUBS_PURCHASED_ZR_DEKU_SCRUB_GROTTO_FRONT,
RAND_INF_SCRUBS_PURCHASED_SFM_DEKU_SCRUB_GROTTO_REAR,
RAND_INF_SCRUBS_PURCHASED_SFM_DEKU_SCRUB_GROTTO_FRONT,
RAND_INF_SCRUBS_PURCHASED_LH_DEKU_SCRUB_GROTTO_LEFT,
RAND_INF_SCRUBS_PURCHASED_LH_DEKU_SCRUB_GROTTO_RIGHT,
RAND_INF_SCRUBS_PURCHASED_LH_DEKU_SCRUB_GROTTO_CENTER,
RAND_INF_SCRUBS_PURCHASED_GV_DEKU_SCRUB_GROTTO_REAR,
RAND_INF_SCRUBS_PURCHASED_GV_DEKU_SCRUB_GROTTO_FRONT,
RAND_INF_SCRUBS_PURCHASED_LW_DEKU_SCRUB_GROTTO_REAR,
RAND_INF_SCRUBS_PURCHASED_LW_DEKU_SCRUB_GROTTO_FRONT,
RAND_INF_SCRUBS_PURCHASED_DMC_DEKU_SCRUB_GROTTO_LEFT,
RAND_INF_SCRUBS_PURCHASED_DMC_DEKU_SCRUB_GROTTO_RIGHT,
RAND_INF_SCRUBS_PURCHASED_DMC_DEKU_SCRUB_GROTTO_CENTER,
RAND_INF_SCRUBS_PURCHASED_GC_DEKU_SCRUB_GROTTO_LEFT,
RAND_INF_SCRUBS_PURCHASED_GC_DEKU_SCRUB_GROTTO_RIGHT,
RAND_INF_SCRUBS_PURCHASED_GC_DEKU_SCRUB_GROTTO_CENTER,
RAND_INF_SCRUBS_PURCHASED_LLR_DEKU_SCRUB_GROTTO_LEFT,
RAND_INF_SCRUBS_PURCHASED_LLR_DEKU_SCRUB_GROTTO_RIGHT,
RAND_INF_SCRUBS_PURCHASED_LLR_DEKU_SCRUB_GROTTO_CENTER,
RAND_INF_SCRUBS_PURCHASED_COLOSSUS_DEKU_SCRUB_GROTTO_REAR,
RAND_INF_SCRUBS_PURCHASED_COLOSSUS_DEKU_SCRUB_GROTTO_FRONT,
RAND_INF_SCRUBS_PURCHASED_LW_DEKU_SCRUB_NEAR_DEKU_THEATER_RIGHT,
RAND_INF_SCRUBS_PURCHASED_LW_DEKU_SCRUB_NEAR_DEKU_THEATER_LEFT,
RAND_INF_SCRUBS_PURCHASED_LW_DEKU_SCRUB_NEAR_BRIDGE,
RAND_INF_SCRUBS_PURCHASED_DMC_DEKU_SCRUB,
// 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,

View File

@ -1578,18 +1578,12 @@ extern "C" RandomizerCheck Randomizer_GetCheckFromActor(s16 sceneNum, s16 actorI
return OTRGlobals::Instance->gRandomizer->GetCheckFromActor(sceneNum, actorId, actorParams);
}
extern "C" CustomMessageEntry Randomizer_GetScrubMessage(u16 scrubTextId) {
int price = 0;
switch (scrubTextId) {
case TEXT_SCRUB_POH:
price = 10;
break;
case TEXT_SCRUB_STICK_UPGRADE:
case TEXT_SCRUB_NUT_UPGRADE:
price = 40;
break;
}
return CustomMessageManager::Instance->RetrieveMessage(Randomizer::scrubMessageTableID, price);
extern "C" ScrubIdentity Randomizer_IdentifyScrub(s32 sceneNum, s32 actorParams, s32 respawnData) {
return OTRGlobals::Instance->gRandomizer->IdentifyScrub(sceneNum, actorParams, respawnData);
}
extern "C" CustomMessageEntry Randomizer_GetScrubMessage(s16 itemPrice) {
return CustomMessageManager::Instance->RetrieveMessage(Randomizer::scrubMessageTableID, itemPrice);
}
extern "C" CustomMessageEntry Randomizer_GetNaviMessage() {
@ -1718,8 +1712,8 @@ extern "C" int CustomMessage_RetrieveIfExists(GlobalContext* globalCtx) {
} else {
messageEntry = Randomizer_GetGanonHintText();
}
} else if (textId == TEXT_SCRUB_POH || textId == TEXT_SCRUB_STICK_UPGRADE || textId == TEXT_SCRUB_NUT_UPGRADE) {
messageEntry = Randomizer_GetScrubMessage(textId);
} else if (textId >= 0x9000 && textId <= 0x905F) {
messageEntry = Randomizer_GetScrubMessage((textId & ((1 << 8) - 1)));
} else if (CVar_GetS32("gRandomizeRupeeNames", 0) &&
(textId == TEXT_BLUE_RUPEE || textId == TEXT_RED_RUPEE || textId == TEXT_PURPLE_RUPEE ||
textId == TEXT_HUGE_RUPEE)) {

View File

@ -96,6 +96,7 @@ Sprite* GetSeedTexture(uint8_t index);
void Randomizer_LoadSettings(const char* spoilerFileName);
u8 Randomizer_GetSettingValue(RandomizerSettingKey randoSettingKey);
RandomizerCheck Randomizer_GetCheckFromActor(s16 actorId, s16 actorParams, s16 sceneNum);
ScrubIdentity Randomizer_IdentifyScrub(s32 sceneNum, s32 actorParams, s32 respawnData);
void Randomizer_LoadHintLocations(const char* spoilerFileName);
void Randomizer_LoadItemLocations(const char* spoilerFileName, bool silent);
GetItemEntry Randomizer_GetRandomizedItem(GetItemID ogId, s16 actorId, s16 actorParams, s16 sceneNum);

View File

@ -8,6 +8,7 @@
#define NUM_DUNGEONS 8
#define NUM_TRIALS 6
#define NUM_COWS 10
#define NUM_SCRUBS 35
/**
* Initialize new save.

View File

@ -15,6 +15,7 @@ void EnDns_Destroy(Actor* thisx, GlobalContext* globalCtx);
void EnDns_Update(Actor* thisx, GlobalContext* globalCtx);
void EnDns_Draw(Actor* thisx, GlobalContext* globalCtx);
u32 EnDns_RandomizerPurchaseableCheck(EnDns* this);
u32 func_809EF5A4(EnDns* this);
u32 func_809EF658(EnDns* this);
u32 func_809EF70C(EnDns* this);
@ -24,6 +25,7 @@ u32 func_809EF854(EnDns* this);
u32 func_809EF8F4(EnDns* this);
u32 func_809EF9A4(EnDns* this);
void EnDns_RandomizerPurchase(EnDns* this);
void func_809EF9F8(EnDns* this);
void func_809EFA28(EnDns* this);
void func_809EFA58(EnDns* this);
@ -155,7 +157,6 @@ void EnDns_Init(Actor* thisx, GlobalContext* globalCtx) {
Collider_InitCylinder(globalCtx, &this->collider);
Collider_SetCylinderType1(globalCtx, &this->collider, &this->actor, &sCylinderInit);
ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 35.0f);
this->actor.textId = D_809F040C[this->actor.params];
Actor_SetScale(&this->actor, 0.01f);
this->actor.colChkInfo.mass = MASS_IMMOVABLE;
this->maintainCollider = 1;
@ -164,7 +165,27 @@ void EnDns_Init(Actor* thisx, GlobalContext* globalCtx) {
this->actor.speedXZ = 0.0f;
this->actor.velocity.y = 0.0f;
this->actor.gravity = -1.0f;
this->actor.textId = D_809F040C[this->actor.params];
this->dnsItemEntry = sItemEntries[this->actor.params];
if (gSaveContext.n64ddFlag) {
// 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(globalCtx->sceneNum, this->actor.params, respawnData);
if (Randomizer_GetSettingValue(RSK_SHUFFLE_SCRUBS) == 1 || Randomizer_GetSettingValue(RSK_SHUFFLE_SCRUBS) == 3 && this->scrubIdentity.itemPrice != -1) {
this->dnsItemEntry->itemPrice = this->scrubIdentity.itemPrice;
}
if (this->scrubIdentity.isShuffled) {
this->dnsItemEntry->getItemId = this->scrubIdentity.getItemId;
this->dnsItemEntry->purchaseableCheck = EnDns_RandomizerPurchaseableCheck;
this->dnsItemEntry->setRupeesAndFlags = EnDns_RandomizerPurchase;
this->dnsItemEntry->itemAmount = 1;
// Currently the textID is simply identified by the item price since that is the only thing
// unique to it, later on this will change to identifying by scrubIdentity.randomizerInf
this->actor.textId = 0x9000 + this->dnsItemEntry->itemPrice;
}
}
this->actionFunc = EnDns_SetupWait;
}
@ -185,6 +206,13 @@ void EnDns_ChangeAnim(EnDns* this, u8 index) {
/* 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) {
if ((CUR_CAPACITY(UPG_NUTS) != 0) && (AMMO(ITEM_NUT) >= CUR_CAPACITY(UPG_NUTS))) {
return 1;
@ -281,6 +309,10 @@ u32 func_809EF9A4(EnDns* this) {
}
/* Paying and flagging functions */
void EnDns_RandomizerPurchase(EnDns* this) {
Rupees_ChangeBy(-this->dnsItemEntry->itemPrice);
Flags_SetRandomizerInf(this->scrubIdentity.randomizerInf);
}
void func_809EF9F8(EnDns* this) {
Rupees_ChangeBy(-this->dnsItemEntry->itemPrice);
@ -369,31 +401,25 @@ void EnDns_Talk(EnDns* this, GlobalContext* globalCtx) {
}
void func_809EFDD0(EnDns* this, GlobalContext* globalCtx) {
if (this->actor.params == 0x9) {
if (gSaveContext.n64ddFlag) {
GetItemEntry getItemEntry = Randomizer_GetRandomizedItem(GI_STICK_UPGRADE_30, this->actor.id, this->actor.params, globalCtx->sceneNum);
GiveItemEntryFromActor(&this->actor, globalCtx, getItemEntry, 130.0f, 100.0f);
} else if (CUR_UPG_VALUE(UPG_STICKS) < 2) {
func_8002F434(&this->actor, globalCtx, GI_STICK_UPGRADE_20, 130.0f, 100.0f);
if (!gSaveContext.n64ddFlag || !this->scrubIdentity.isShuffled) {
if (this->actor.params == 0x9) {
if (CUR_UPG_VALUE(UPG_STICKS) < 2) {
func_8002F434(&this->actor, globalCtx, GI_STICK_UPGRADE_20, 130.0f, 100.0f);
} else {
func_8002F434(&this->actor, globalCtx, GI_STICK_UPGRADE_30, 130.0f, 100.0f);
}
} else if (this->actor.params == 0xA) {
if (CUR_UPG_VALUE(UPG_NUTS) < 2) {
func_8002F434(&this->actor, globalCtx, GI_NUT_UPGRADE_30, 130.0f, 100.0f);
} else {
func_8002F434(&this->actor, globalCtx, GI_NUT_UPGRADE_40, 130.0f, 100.0f);
}
} else {
func_8002F434(&this->actor, globalCtx, GI_STICK_UPGRADE_30, 130.0f, 100.0f);
}
} else if (this->actor.params == 0xA) {
if (gSaveContext.n64ddFlag) {
GetItemEntry getItemEntry = Randomizer_GetRandomizedItem(GI_NUT_UPGRADE_40, this->actor.id, this->actor.params, globalCtx->sceneNum);
GiveItemEntryFromActor(&this->actor, globalCtx, getItemEntry, 130.0f, 100.0f);
} else if (CUR_UPG_VALUE(UPG_NUTS) < 2) {
func_8002F434(&this->actor, globalCtx, GI_NUT_UPGRADE_30, 130.0f, 100.0f);
} else {
func_8002F434(&this->actor, globalCtx, GI_NUT_UPGRADE_40, 130.0f, 100.0f);
func_8002F434(&this->actor, globalCtx, this->dnsItemEntry->getItemId, 130.0f, 100.0f);
}
} else {
if (!gSaveContext.n64ddFlag) {
func_8002F434(&this->actor, globalCtx, this->dnsItemEntry->getItemId, 130.0f, 100.0f);
} else {
GetItemEntry getItemEntry = Randomizer_GetRandomizedItem(this->dnsItemEntry->getItemId, this->actor.id, this->actor.params, globalCtx->sceneNum);
GiveItemEntryFromActor(&this->actor, globalCtx, getItemEntry, 130.0f, 100.0f);
}
GetItemEntry itemEntry = Randomizer_GetItemFromKnownCheck(this->scrubIdentity.randomizerCheck, this->scrubIdentity.getItemId);
GiveItemEntryFromActor(&this->actor, globalCtx, itemEntry, 130.0f, 100.0f);
}
}
@ -489,6 +515,11 @@ void EnDns_Update(Actor* thisx, GlobalContext* globalCtx) {
this->dustTimer++;
this->actor.textId = D_809F040C[this->actor.params];
if (gSaveContext.n64ddFlag && this->scrubIdentity.isShuffled) {
// Currently the textID is simply identified by the item price since that is the only thing
// unique to it, later on this will change to identifying by scrubIdentity.randomizerInf
this->actor.textId = 0x9000 + this->dnsItemEntry->itemPrice;
}
Actor_SetFocus(&this->actor, 60.0f);
Actor_SetScale(&this->actor, 0.01f);
SkelAnime_Update(&this->skelAnime);

View File

@ -32,6 +32,7 @@ typedef struct EnDns {
/* 0x02BD */ u8 dropCollectible;
/* 0x02C0 */ DnsItemEntry* dnsItemEntry;
/* 0x02C4 */ f32 yInitPos;
/* */ ScrubIdentity scrubIdentity;
} EnDns; // size = 0x02C8
#endif

View File

@ -69,6 +69,15 @@ void EnShopnuts_Init(Actor* thisx, GlobalContext* globalCtx) {
CollisionCheck_SetInfo(&this->actor.colChkInfo, NULL, &sColChkInfoInit);
Collider_UpdateCylinder(&this->actor, &this->collider);
if (gSaveContext.n64ddFlag) {
s16 respawnData = gSaveContext.respawn[RESPAWN_MODE_RETURN].data & ((1 << 8) - 1);
ScrubIdentity scrubIdentity = Randomizer_IdentifyScrub(globalCtx->sceneNum, this->actor.params, respawnData);
if (scrubIdentity.isShuffled && Flags_GetRandomizerInf(scrubIdentity.randomizerInf)) {
Actor_Kill(&this->actor);
}
}
if (((this->actor.params == 0x0002) && (gSaveContext.itemGetInf[0] & 0x800)) ||
((this->actor.params == 0x0009) && (gSaveContext.infTable[25] & 4)) ||
((this->actor.params == 0x000A) && (gSaveContext.infTable[25] & 8))) {