Potsanity proof of concept

This commit is contained in:
aMannus 2023-12-14 12:19:39 +01:00
parent dad4ae0095
commit fe7476a377
29 changed files with 158 additions and 30 deletions

View File

@ -457,7 +457,7 @@ u32 Actor_HasParent(Actor* actor, PlayState* play);
// TODO: Rename the follwing 3 functions using whatever scheme we use when we rename func_8002F434 and func_8002F554.
s32 GiveItemEntryWithoutActor(PlayState* play, GetItemEntry getItemEntry);
s32 GiveItemEntryFromActor(Actor* actor, PlayState* play, GetItemEntry getItemEntry, f32 xzRange, f32 yRange);
void GiveItemEntryFromActorWithFixedRange(Actor* actor, PlayState* play, GetItemEntry getItemEntry);
s32 GiveItemEntryFromActorWithFixedRange(Actor* actor, PlayState* play, GetItemEntry getItemEntry);
s32 func_8002F434(Actor* actor, PlayState* play, s32 getItemId, f32 xzRange, f32 yRange);
void func_8002F554(Actor* actor, PlayState* play, s32 getItemId);
void func_8002F580(Actor* actor, PlayState* play);

View File

@ -7,6 +7,7 @@
#include "z64collision_check.h"
#include "z64bgcheck.h"
#include "soh/Enhancements/item-tables/ItemTableTypes.h"
#include "soh/Enhancements/randomizer/randomizerTypes.h"
#include "z64actor_enum.h"
#define ACTOR_NUMBER_MAX 2000
@ -285,6 +286,8 @@ typedef struct EnItem00 {
/* 0x160 */ ColliderCylinder collider;
s16 ogParams;
GetItemEntry randoGiEntry;
RandomizerCheck randoCheck;
RandomizerInf randoInf;
} EnItem00; // size = 0x1AC
// Only A_OBJ_SIGNPOST_OBLONG and A_OBJ_SIGNPOST_ARROW are used in room files.

View File

@ -515,6 +515,8 @@ const std::vector<FlagTable> flagTables = {
{ RAND_INF_BONGO_BONGO_SOUL, "RAND_INF_BONGO_BONGO_SOUL" },
{ RAND_INF_TWINROVA_SOUL, "RAND_INF_TWINROVA_SOUL" },
{ RAND_INF_GANON_SOUL, "RAND_INF_GANON_SOUL" },
{ RAND_INF_SHUFFLE_POTS_MARKET_GUARD_HOUSE_CHILD_1, "RAND_INF_SHUFFLE_POTS_MARKET_GUARD_HOUSE_CHILD_1" },
} },
};

View File

@ -17,6 +17,7 @@ enum class Category {
cVanillaMap,
cVanillaCompass,
cAdultTrade,
cPot,
};
enum class OptionCategory {

View File

@ -1356,4 +1356,9 @@ void HintTable_Init_Exclude_Overworld() {
//obscure text
Text{"a #cow in a luxurious hole# offers", /*french*/"la #vache dans une grotte luxueuse# donne", /*spanish*/"una #vaca de un lujoso hoyo# brinda"},
});
hintTable[RHT_SHUFFLE_POTS_MARKET_GUARD_HOUSE_1] = HintText::Exclude({
//obscure text
Text{"a #pot in a room# holds", /*french*/"a #pot in a room# holds", /*spanish*/"a #pot in a room# holds"},
});
}

View File

@ -574,6 +574,11 @@ static void PlaceVanillaCowMilk() {
}
}
static void PlaceVanillaPotContents() {
auto ctx = Rando::Context::GetInstance();
ctx->PlaceItemInLocation(RC_MARKET_GUARD_HOUSE_CHILD_POT_1, RG_GREEN_RUPEE, false, true);
}
static void SetScarceItemPool() {
ReplaceMaxItem(RG_PROGRESSIVE_BOMBCHUS, 3);
ReplaceMaxItem(RG_BOMBCHU_5, 1);
@ -726,6 +731,12 @@ void GenerateItemPool() {
PlaceVanillaCowMilk();
}
if (ctx->GetOption(RSK_SHUFFLE_POTS)) {
AddItemToMainPool(RG_GREEN_RUPEE);
} else {
PlaceVanillaPotContents();
}
if (ctx->GetOption(RSK_SHUFFLE_MAGIC_BEANS)) {
AddItemToMainPool(RG_MAGIC_BEAN_PACK);
if (ctx->GetOption(RSK_ITEM_POOL).Is(RO_ITEM_POOL_PLENTIFUL)) {

View File

@ -168,8 +168,9 @@ void AreaTable_Init_CastleTown() {
areaTable[RR_MARKET_GUARD_HOUSE] = Area("Market Guard House", "Market Guard House", RA_NONE, NO_DAY_NIGHT_CYCLE, {}, {
//Locations
LocationAccess(RC_MARKET_10_BIG_POES, {[]{return IsAdult && BigPoeKill;}}),
LocationAccess(RC_MARKET_GS_GUARD_HOUSE, {[]{return IsChild;}}),
LocationAccess(RC_MARKET_10_BIG_POES, {[]{return IsAdult && BigPoeKill;}}),
LocationAccess(RC_MARKET_GS_GUARD_HOUSE, {[]{return IsChild;}}),
LocationAccess(RC_MARKET_GUARD_HOUSE_CHILD_POT_1, {[]{return CanBreakPots();}}),
}, {
//Exits
Entrance(RR_MARKET_ENTRANCE, {[]{return true;}}),

View File

@ -503,6 +503,10 @@ namespace Logic {
}
}
bool CanBreakPots() {
return true;
}
uint8_t GetDifficultyValueFromString(Rando::Option& glitchOption) {
return 0;
}

View File

@ -380,6 +380,7 @@ bool CanPlay(bool song);
bool CanUse(RandomizerGet itemName);
bool HasProjectile(HasProjectileAge age);
bool HasBossSoul(RandomizerGet itemName);
bool CanBreakPots();
bool SmallKeys(RandomizerRegion dungeon, uint8_t requiredAmount);
bool SmallKeys(RandomizerRegion dungeon, uint8_t requiredAmountGlitchless, uint8_t requiredAmountGlitched);
bool CanDoGlitch(GlitchType glitch);

View File

@ -118,6 +118,10 @@ void WriteIngameSpoilerLog() {
if (!ctx->GetOption(RSK_SHUFFLE_COWS) && loc->IsCategory(Category::cCow)) {
continue;
}
// Shuffle Pots
else if (!ctx->GetOption(RSK_SHUFFLE_POTS) && loc->IsCategory(Category::cPot)) {
continue;
}
// Merchants
else if (ctx->GetOption(RSK_SHUFFLE_MERCHANTS).Is(RO_SHUFFLE_MERCHANTS_OFF) && loc->IsCategory(Category::cMerchant)) {
continue;

View File

@ -28,6 +28,7 @@ typedef enum {
SPOILER_CHK_MERCHANT,
SPOILER_CHK_GRAVEDIGGER,
SPOILER_CHK_RANDOMIZER_INF,
SPOILER_CHK_POT,
} SpoilerCollectionCheckType;
// GetLocation groups for checks, used to group the checks by logical location

View File

@ -94,6 +94,10 @@ class SpoilerCollectionCheck {
static auto RandomizerInf(const int8_t scene, const uint8_t flag) {
return SpoilerCollectionCheck(SPOILER_CHK_RANDOMIZER_INF, scene, flag);
}
static auto Pot(const uint8_t scene, const uint8_t flag) {
return SpoilerCollectionCheck(SPOILER_CHK_POT, scene, flag);
}
};
enum class LocationType {

View File

@ -1,6 +1,7 @@
#include "static_data.h"
#define TWO_ACTOR_PARAMS(a, b) (abs(a) << 16) | abs(b)
#define THREE_ACTOR_PARAMS(a, b, c) (abs(a) << 10) | (abs(a) << 20) | abs(c)
std::array<Rando::Location, RC_MAX> Rando::StaticData::locationTable;
@ -207,6 +208,7 @@ std::vector<RandomizerCheck> Rando::StaticData::overworldLocations = {
RC_MARKET_TREASURE_CHEST_GAME_ITEM_3,
RC_MARKET_TREASURE_CHEST_GAME_ITEM_4,
RC_MARKET_TREASURE_CHEST_GAME_ITEM_5,
RC_MARKET_GUARD_HOUSE_CHILD_POT_1,
// Market Shops
RC_MARKET_BOMBCHU_SHOP_ITEM_1,
@ -1291,6 +1293,9 @@ void Rando::StaticData::InitLocationTable() {
locationTable[RC_GV_COW] = Location::Base(RC_GV_COW, RCQUEST_BOTH, RCTYPE_COW, RCAREA_GERUDO_VALLEY, ACTOR_EN_COW, SCENE_GERUDO_VALLEY, 0x00, 0x15, "Cow", "GV Cow", RHT_GV_COW, RG_MILK, { Category::cCow }, SpoilerCollectionCheck::Cow(0x5A, 0x15), SpoilerCollectionCheckGroup::GROUP_GERUDO_VALLEY);
locationTable[RC_JABU_JABUS_BELLY_MQ_COW] = Location::Base(RC_JABU_JABUS_BELLY_MQ_COW, RCQUEST_MQ, RCTYPE_COW, RCAREA_JABU_JABUS_BELLY, ACTOR_EN_COW, SCENE_JABU_JABU, 0x00, 0x15, "MQ Cow", "Jabu Jabus Belly MQ Cow", RHT_JABU_JABUS_BELLY_MQ_COW, RG_MILK, { Category::cCow }, SpoilerCollectionCheck::Cow(0x02, 0x15), SpoilerCollectionCheckGroup::GROUP_DUNGEON_JABUJABUS_BELLY);
// Pots Randomizer Check Randomizer Check Quest Type Area Actor ID Scene ID Params Flags Short Name Spoiler Name Hint Text Key Vanilla Item Categories Spoiler Collection Check Collection Check Group
locationTable[RC_MARKET_GUARD_HOUSE_CHILD_POT_1] = Location::Base(RC_MARKET_GUARD_HOUSE_CHILD_POT_1, RCQUEST_BOTH, RCTYPE_POT, RCAREA_MARKET, ACTOR_OBJ_TSUBO, SCENE_MARKET_GUARD_HOUSE, THREE_ACTOR_PARAMS(-80, 0, -7), 0x00, "Guard House Child Pot 1", "MK Guard House Child Pot 1", RHT_SHUFFLE_POTS_MARKET_GUARD_HOUSE_1, RG_GREEN_RUPEE, { Category::cPot }, SpoilerCollectionCheck::Pot(0x4D, 0x00), SpoilerCollectionCheckGroup::GROUP_HYRULE_CASTLE);
/*-------------------------------
--- SHOPS ---
8 6 2 4

View File

@ -414,7 +414,7 @@ bool OptionGroup::RenderImGui() const { // NOLINT(*-no-recursion)
ImGui::Unindent();
}
if (option->HasFlag(IMFLAG_SEPARATOR_BOTTOM)) {
UIWidgets::PaddedSeparator();
UIWidgets::PaddedSeparator(false, true);
}
}
}

View File

@ -229,6 +229,8 @@ void Settings::CreateOptionDescriptions() {
"\n"
"The Gerudo Card is required to enter the Gerudo Training Grounds, opening "
"the gate to Haunted Wasteland and the Horseback Archery minigame.";
mOptionDescriptions[RSK_SHUFFLE_POTS] =
"Freestanding pots will drop a randomized item the first time they're broken and collected. Pots will have a different appearance when they hold a randomized item.";
mOptionDescriptions[RSK_SHOPSANITY] = "Off - All shop items will be the same as vanilla.\n"
"\n"
"0 Items - Vanilla shop items will be shuffled among different shops.\n"

View File

@ -1402,6 +1402,7 @@ std::map<RandomizerCheck, RandomizerInf> rcToRandomizerInf = {
{ RC_LH_ADULT_FISHING, RAND_INF_ADULT_FISHING },
{ RC_MARKET_10_BIG_POES, RAND_INF_10_BIG_POES },
{ RC_KAK_100_GOLD_SKULLTULA_REWARD, RAND_INF_KAK_100_GOLD_SKULLTULA_REWARD },
{ RC_MARKET_GUARD_HOUSE_CHILD_POT_1, RAND_INF_SHUFFLE_POTS_MARKET_GUARD_HOUSE_CHILD_1 },
};
Rando::Location* Randomizer::GetCheckObjectFromActor(s16 actorId, s16 sceneNum, s32 actorParams = 0x00) {
@ -1609,6 +1610,24 @@ CowIdentity Randomizer::IdentifyCow(s32 sceneNum, s32 posX, s32 posZ) {
return cowIdentity;
}
PotIdentity Randomizer::IdentifyPot(s32 sceneNum, s32 posX, s32 posY, s32 posZ) {
struct PotIdentity potIdentity;
potIdentity.randomizerInf = RAND_INF_MAX;
potIdentity.randomizerCheck = RC_UNKNOWN_CHECK;
s32 actorParams = THREE_ACTOR_PARAMS(posX, posY, posZ);
Rando::Location* location = GetCheckObjectFromActor(ACTOR_OBJ_TSUBO, sceneNum, actorParams);
if (location->GetRandomizerCheck() != RC_UNKNOWN_CHECK) {
potIdentity.randomizerInf = rcToRandomizerInf[location->GetRandomizerCheck()];
potIdentity.randomizerCheck = location->GetRandomizerCheck();
}
return potIdentity;
}
u8 Randomizer::GetRandoSettingValue(RandomizerSettingKey randoSettingKey) {
return Rando::Context::GetInstance()->GetOption(randoSettingKey).GetSelectedOptionIndex();
}

View File

@ -54,6 +54,7 @@ class Randomizer {
ScrubIdentity IdentifyScrub(s32 sceneNum, s32 actorParams, s32 respawnData);
ShopItemIdentity IdentifyShopItem(s32 sceneNum, u8 slotIndex);
CowIdentity IdentifyCow(s32 sceneNum, s32 posX, s32 posZ);
PotIdentity IdentifyPot(s32 sceneNum, s32 posX, s32 potY, s32 posZ);
GetItemEntry GetItemFromKnownCheck(RandomizerCheck randomizerCheck, GetItemID ogItemId, bool checkObtainability = true);
GetItemEntry GetItemFromActor(s16 actorId, s16 sceneNum, s16 actorParams, GetItemID ogItemId, bool checkObtainability = true);
GetItemID GetItemIdFromRandomizerGet(RandomizerGet randoGet, GetItemID ogItemId);

View File

@ -101,10 +101,11 @@ typedef enum {
RCTYPE_CHEST_GAME, // todo replace this once we implement it, just using it to exclude for now
RCTYPE_LINKS_POCKET, // todo this feels hacky
RCTYPE_GOSSIP_STONE,
RCTYPE_SONG_LOCATION, // Song locations
RCTYPE_SONG_LOCATION, // Song locations
RCTYPE_BOSS_HEART_OR_OTHER_REWARD, // Boss heart container or lesser dungeon rewards (lens, ice arrow)
RCTYPE_DUNGEON_REWARD, // Dungeon rewards (blue warps)
RCTYPE_OCARINA, // Ocarina locations
RCTYPE_DUNGEON_REWARD, // Dungeon rewards (blue warps)
RCTYPE_OCARINA, // Ocarina locations
RCTYPE_POT, // Shuffle Pots
} RandomizerCheckType;
typedef enum { RCQUEST_VANILLA, RCQUEST_MQ, RCQUEST_BOTH } RandomizerCheckQuest;
@ -746,6 +747,7 @@ typedef enum {
RC_MARKET_BOMBCHU_SHOP_ITEM_6,
RC_MARKET_BOMBCHU_SHOP_ITEM_7,
RC_MARKET_BOMBCHU_SHOP_ITEM_8,
RC_MARKET_GUARD_HOUSE_CHILD_POT_1,
RC_TOT_LIGHT_ARROWS_CUTSCENE,
RC_TOT_MASTER_SWORD,
RC_HC_MALON_EGG,
@ -3277,6 +3279,8 @@ typedef enum {
RHT_CARPET_SALESMAN_DIALOG_THIRD,
RHT_CARPET_SALESMAN_DIALOG_FOURTH,
RHT_GRANNY_DIALOG,
// Shuffle Pots
RHT_SHUFFLE_POTS_MARKET_GUARD_HOUSE_1,
RHT_MAX
} RandomizerHintTextKey;
@ -3400,6 +3404,7 @@ typedef enum {
RSK_SHUFFLE_COWS,
RSK_SHUFFLE_WEIRD_EGG,
RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD,
RSK_SHUFFLE_POTS,
RSK_SHUFFLE_FROG_SONG_RUPEES,
RSK_ITEM_POOL,
RSK_ICE_TRAPS,
@ -3881,6 +3886,11 @@ typedef struct CowIdentity {
RandomizerCheck randomizerCheck;
} CowIdentity;
typedef struct PotIdentity {
RandomizerInf randomizerInf;
RandomizerCheck randomizerCheck;
} PotIdentity;
typedef enum {
TRACKER_WINDOW_FLOATING,
TRACKER_WINDOW_WINDOW

View File

@ -153,6 +153,7 @@ void RandomizerCheckObjects::UpdateImGuiVisibility() {
((CVarGetInteger("gRandomizeShuffleTokens", RO_TOKENSANITY_OFF) == RO_TOKENSANITY_DUNGEONS) &&
RandomizerCheckObjects::AreaIsDungeon(location.GetArea()))) &&
(location.GetRCType() != RCTYPE_COW || CVarGetInteger("gRandomizeShuffleCows", RO_GENERIC_NO)) &&
(location.GetRCType() != RCTYPE_POT || CVarGetInteger("gRandomizeShufflePotContents", RO_GENERIC_NO)) &&
(location.GetRCType() != RCTYPE_ADULT_TRADE ||
CVarGetInteger("gRandomizeShuffleAdultTrade", RO_GENERIC_NO)) &&
(location.GetRandomizerCheck() != RC_KF_KOKIRI_SWORD_CHEST ||

View File

@ -7,6 +7,7 @@
#include <map>
#define TWO_ACTOR_PARAMS(a, b) (abs(a) << 16) | abs(b)
#define THREE_ACTOR_PARAMS(a, b, c) (abs(a) << 10) | (abs(a) << 20) | abs(c)
namespace RandomizerCheckObjects {
bool AreaIsDungeon(RandomizerCheckArea area);

View File

@ -64,6 +64,7 @@ bool showKokiriSword;
bool showMasterSword;
bool showWeirdEgg;
bool showGerudoCard;
bool showPots;
bool showFrogSongRupees;
bool showStartingMapsCompasses;
bool showKeysanity;
@ -426,6 +427,7 @@ bool HasItemBeenCollected(RandomizerCheck rc) {
case SpoilerCollectionCheckType::SPOILER_CHK_SCRUB:
case SpoilerCollectionCheckType::SPOILER_CHK_RANDOMIZER_INF:
case SpoilerCollectionCheckType::SPOILER_CHK_MASTER_SWORD:
case SpoilerCollectionCheckType::SPOILER_CHK_POT:
return Flags_GetRandomizerInf(OTRGlobals::Instance->gRandomizer->GetRandomizerInfFromCheck(rc));
case SpoilerCollectionCheckType::SPOILER_CHK_EVENT_CHK_INF:
return gSaveContext.eventChkInf[flag / 16] & (0x01 << flag % 16);
@ -1030,6 +1032,9 @@ void LoadSettings() {
showGerudoCard = IS_RANDO ?
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD) == RO_GENERIC_YES
: true;
showPots = IS_RANDO ?
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_POTS) == RO_GENERIC_YES
: true;
showFrogSongRupees = IS_RANDO ?
OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_FROG_SONG_RUPEES) == RO_GENERIC_YES
: false;
@ -1132,6 +1137,7 @@ bool IsVisibleInCheckTracker(RandomizerCheck rc) {
(showDungeonTokens && RandomizerCheckObjects::AreaIsDungeon(loc->GetArea()))
) &&
(loc->GetRCType() != RCTYPE_COW || showCows) &&
(loc->GetRCType() != RCTYPE_POT || showPots) &&
(loc->GetRCType() != RCTYPE_ADULT_TRADE ||
showAdultTrade ||
rc == RC_KAK_ANJU_AS_ADULT || // adult trade checks that are always shuffled

View File

@ -170,6 +170,8 @@ typedef enum {
RAND_INF_TWINROVA_SOUL,
RAND_INF_GANON_SOUL,
RAND_INF_SHUFFLE_POTS_MARKET_GUARD_HOUSE_CHILD_1,
// 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

@ -105,6 +105,7 @@ void Settings::CreateOptions() {
mOptions[RSK_SHUFFLE_OCARINA] = Option::Bool("Shuffle Ocarinas", "gRandomizeShuffleOcarinas", mOptionDescriptions[RSK_SHUFFLE_OCARINA]);
mOptions[RSK_SHUFFLE_WEIRD_EGG] = Option::Bool("Shuffle Weird Egg", "gRandomizeShuffleWeirdEgg", mOptionDescriptions[RSK_SHUFFLE_WEIRD_EGG]);
mOptions[RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD] = Option::Bool("Shuffle Gerudo Membership Card", "gRandomizeShuffleGerudoToken", mOptionDescriptions[RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD]);
mOptions[RSK_SHUFFLE_POTS] = Option::Bool("Shuffle Pot Contents", "gRandomizeShufflePotContents", mOptionDescriptions[RSK_SHUFFLE_POTS]);
mOptions[RSK_SHUFFLE_MAGIC_BEANS] = Option::Bool("Shuffle Magic Beans", "gRandomizeShuffleBeans", mOptionDescriptions[RSK_SHUFFLE_MAGIC_BEANS]);
mOptions[RSK_SHUFFLE_MERCHANTS] = Option::U8("Shuffle Merchants", {"Off", "On (No Hints)", "On (With Hints)"}, OptionCategory::Setting, "gRandomizeShuffleMerchants", mOptionDescriptions[RSK_SHUFFLE_MERCHANTS], WidgetType::Combobox, RO_SHUFFLE_MERCHANTS_OFF);
mOptions[RSK_SHUFFLE_FROG_SONG_RUPEES] = Option::Bool("Shuffle Frog Song Rupees", "gRandomizeShuffleFrogSongRupees", mOptionDescriptions[RSK_SHUFFLE_FROG_SONG_RUPEES]);
@ -614,7 +615,8 @@ void Settings::CreateOptions() {
&mOptions[RSK_SHUFFLE_MASTER_SWORD],
&mOptions[RSK_SHUFFLE_OCARINA],
&mOptions[RSK_SHUFFLE_WEIRD_EGG],
&mOptions[RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD]
&mOptions[RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD],
&mOptions[RSK_SHUFFLE_POTS]
}, false, WidgetContainerType::COLUMN);
mOptionGroups[RSG_SHUFFLE_NPCS_IMGUI] = OptionGroup::SubGroup("Shuffle NPCs & Merchants", {
&mOptions[RSK_SHOPSANITY],
@ -813,6 +815,7 @@ void Settings::CreateOptions() {
&mOptions[RSK_SHUFFLE_OCARINA],
&mOptions[RSK_SHUFFLE_WEIRD_EGG],
&mOptions[RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD],
&mOptions[RSK_SHUFFLE_POTS],
&mOptions[RSK_SHUFFLE_MAGIC_BEANS],
&mOptions[RSK_SHUFFLE_MERCHANTS],
&mOptions[RSK_SHUFFLE_FROG_SONG_RUPEES],
@ -1009,6 +1012,7 @@ void Settings::CreateOptions() {
{ "Shuffle Settings:Link's Pocket", RSK_LINKS_POCKET },
{ "Shuffle Settings:Shuffle Songs", RSK_SHUFFLE_SONGS },
{ "Shuffle Settings:Shuffle Gerudo Membership Card", RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD },
{ "Shuffle Settings:Shuffle Pot Contents", RSK_SHUFFLE_POTS },
{ "Shuffle Settings:Shopsanity", RSK_SHOPSANITY },
{ "Shuffle Settings:Shopsanity Prices", RSK_SHOPSANITY_PRICES },
{ "Shuffle Settings:Affordable Prices", RSK_SHOPSANITY_PRICES_AFFORDABLE },
@ -2060,6 +2064,7 @@ void Settings::ParseJson(nlohmann::json spoilerFileJson) {
}
break;
case RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD:
case RSK_SHUFFLE_POTS:
case RSK_SHUFFLE_COWS:
case RSK_SHUFFLE_ADULT_TRADE:
case RSK_SHUFFLE_MAGIC_BEANS:

View File

@ -1987,6 +1987,10 @@ extern "C" CowIdentity Randomizer_IdentifyCow(s32 sceneNum, s32 posX, s32 posZ)
return OTRGlobals::Instance->gRandomizer->IdentifyCow(sceneNum, posX, posZ);
}
extern "C" PotIdentity Randomizer_IdentifyPot(s32 sceneNum, s32 posX, s32 posY, s32 posZ) {
return OTRGlobals::Instance->gRandomizer->IdentifyPot(sceneNum, posX, posY, posZ);
}
extern "C" GetItemEntry ItemTable_Retrieve(int16_t getItemID) {
GetItemEntry giEntry = ItemTableManager::Instance->RetrieveItemEntry(MOD_NONE, getItemID);
return giEntry;

View File

@ -133,6 +133,7 @@ RandomizerCheck Randomizer_GetCheckFromActor(s16 actorId, s16 sceneNum, s16 acto
ScrubIdentity Randomizer_IdentifyScrub(s32 sceneNum, s32 actorParams, s32 respawnData);
ShopItemIdentity Randomizer_IdentifyShopItem(s32 sceneNum, u8 slotIndex);
CowIdentity Randomizer_IdentifyCow(s32 sceneNum, s32 posX, s32 posZ);
PotIdentity Randomizer_IdentifyPot(s32 sceneNum, s32 posX, s32 posY, s32 posZ);
void Randomizer_ParseSpoiler(const char* fileLoc);
void Randomizer_LoadHintMessages();
void Randomizer_LoadMerchantMessages();

View File

@ -2055,8 +2055,8 @@ s32 GiveItemEntryFromActor(Actor* actor, PlayState* play, GetItemEntry getItemEn
* \param play the Global Context
* \param getItemEntry the GetItemEntry for the item you want the player to receive.
*/
void GiveItemEntryFromActorWithFixedRange(Actor* actor, PlayState* play, GetItemEntry getItemEntry) {
GiveItemEntryFromActor(actor, play, getItemEntry, 50.0f, 10.0f);
s32 GiveItemEntryFromActorWithFixedRange(Actor* actor, PlayState* play, GetItemEntry getItemEntry) {
return GiveItemEntryFromActor(actor, play, getItemEntry, 50.0f, 10.0f);
}
// TODO: Rename to GiveItemIdFromActor or similar

View File

@ -348,6 +348,7 @@ void EnItem00_Init(Actor* thisx, PlayState* play) {
f32 shadowScale = 6.0f;
s32 getItemId = GI_NONE;
this->randoGiEntry = (GetItemEntry)GET_ITEM_NONE;
this->randoCheck = (RandomizerCheck)RC_UNKNOWN_CHECK;
s16 spawnParam8000 = this->actor.params & 0x8000;
s32 pad1;
@ -485,12 +486,11 @@ void EnItem00_Init(Actor* thisx, PlayState* play) {
this->actor.shape.shadowAlpha = 180;
this->actor.focus.pos = this->actor.world.pos;
this->getItemId = GI_NONE;
RandomizerCheck randoCheck =
Randomizer_GetCheckFromActor(this->actor.id, play->sceneNum, this->ogParams);
this->randoCheck = Randomizer_GetCheckFromActor(this->actor.id, play->sceneNum, this->ogParams);
this->randoInf = RAND_INF_MAX;
if (IS_RANDO && randoCheck != RC_UNKNOWN_CHECK) {
this->randoGiEntry =
Randomizer_GetItemFromKnownCheck(randoCheck, getItemId);
if (IS_RANDO && this->randoCheck != RC_UNKNOWN_CHECK) {
this->randoGiEntry = Randomizer_GetItemFromKnownCheck(this->randoCheck, getItemId);
this->randoGiEntry.getItemFrom = ITEM_FROM_FREESTANDING;
}
@ -581,7 +581,10 @@ void EnItem00_Init(Actor* thisx, PlayState* play) {
if (!IS_RANDO || this->randoGiEntry.getItemId == GI_NONE) {
func_8002F554(&this->actor, play, getItemId);
} else {
GiveItemEntryFromActorWithFixedRange(&this->actor, play, this->randoGiEntry);
if (GiveItemEntryFromActorWithFixedRange(&this->actor, play, this->randoGiEntry) &&
this->randoInf != RAND_INF_MAX) {
Flags_SetRandomizerInf(this->randoInf);
}
}
}
}
@ -734,7 +737,10 @@ void func_8001E5C8(EnItem00* this, PlayState* play) {
if (!IS_RANDO) {
func_8002F434(&this->actor, play, this->getItemId, 50.0f, 80.0f);
} else {
GiveItemEntryFromActor(&this->actor, play, this->randoGiEntry, 50.0f, 80.0f);
if (GiveItemEntryFromActor(&this->actor, play, this->randoGiEntry, 50.0f, 80.0f) &&
this->randoInf != RAND_INF_MAX) {
Flags_SetRandomizerInf(this->randoInf);
}
}
this->unk_15A++;
} else {
@ -955,7 +961,10 @@ void EnItem00_Update(Actor* thisx, PlayState* play) {
func_8002F554(&this->actor, play, getItemId);
} else {
getItemId = this->randoGiEntry.getItemId;
GiveItemEntryFromActorWithFixedRange(&this->actor, play, this->randoGiEntry);
if (GiveItemEntryFromActorWithFixedRange(&this->actor, play, this->randoGiEntry) &&
this->randoInf != RAND_INF_MAX) {
Flags_SetRandomizerInf(this->randoInf);
}
}
}
@ -1363,12 +1372,8 @@ static const Vtx customDropVtx[] = {
*/
void EnItem00_DrawCollectible(EnItem00* this, PlayState* play) {
if (IS_RANDO && (this->getItemId != GI_NONE || this->actor.params == ITEM00_SMALL_KEY)) {
RandomizerCheck randoCheck =
Randomizer_GetCheckFromActor(this->actor.id, play->sceneNum, this->ogParams);
if (randoCheck != RC_UNKNOWN_CHECK) {
this->randoGiEntry =
Randomizer_GetItemFromKnownCheck(randoCheck, GI_NONE);
if (this->randoCheck != RC_UNKNOWN_CHECK) {
this->randoGiEntry = Randomizer_GetItemFromKnownCheck(this->randoCheck, GI_NONE);
this->randoGiEntry.getItemFrom = ITEM_FROM_FREESTANDING;
}
@ -1457,12 +1462,8 @@ void EnItem00_DrawHeartContainer(EnItem00* this, PlayState* play) {
*/
void EnItem00_DrawHeartPiece(EnItem00* this, PlayState* play) {
if (IS_RANDO) {
RandomizerCheck randoCheck =
Randomizer_GetCheckFromActor(this->actor.id, play->sceneNum, this->ogParams);
if (randoCheck != RC_UNKNOWN_CHECK) {
this->randoGiEntry =
Randomizer_GetItemFromKnownCheck(randoCheck, GI_NONE);
if (this->randoCheck != RC_UNKNOWN_CHECK) {
this->randoGiEntry = Randomizer_GetItemFromKnownCheck(this->randoCheck, GI_NONE);
this->randoGiEntry.getItemFrom = ITEM_FROM_FREESTANDING;
}

View File

@ -83,7 +83,29 @@ static InitChainEntry sInitChain[] = {
ICHAIN_F32(uncullZoneScale, 100, ICHAIN_CONTINUE), ICHAIN_F32(uncullZoneDownward, 800, ICHAIN_STOP),
};
s8 ObjTsubo_HoldsRandomizedItem(ObjTsubo* this, PlayState* play) {
return Randomizer_GetSettingValue(RSK_SHUFFLE_POTS) &&
!Flags_GetRandomizerInf(this->potIdentity.randomizerInf) &&
this->potIdentity.randomizerCheck != RC_UNKNOWN_CHECK;
}
void ObjTsubo_SpawnCollectible(ObjTsubo* this, PlayState* play) {
if (IS_RANDO && ObjTsubo_HoldsRandomizedItem(this, play)) {
GetItemEntry getItemEntry = Randomizer_GetItemFromKnownCheck(this->potIdentity.randomizerCheck, GI_NONE);
EnItem00* actor = (EnItem00*)Item_DropCollectible2(play, &this->actor.world.pos, ITEM00_SMALL_KEY);
actor->randoCheck = this->potIdentity.randomizerCheck;
actor->randoGiEntry = getItemEntry;
actor->randoGiEntry.getItemFrom = ITEM_FROM_FREESTANDING;
actor->randoInf = this->potIdentity.randomizerInf;
actor->actor.velocity.y = 8.0f;
actor->actor.speedXZ = 2.0f;
actor->actor.gravity = -0.9f;
actor->actor.world.rot.y = Rand_CenteredFloat(65536.0f);
return;
}
s16 dropParams = this->actor.params & 0x1F;
if ((dropParams >= ITEM00_RUPEE_GREEN) && (dropParams <= ITEM00_BOMBS_SPECIAL)) {
@ -145,6 +167,16 @@ void ObjTsubo_Init(Actor* thisx, PlayState* play) {
ObjTsubo_SetupWaitForObject(this);
osSyncPrintf("(dungeon keep 壷)(arg_data 0x%04x)\n", this->actor.params);
}
if (IS_RANDO) {
this->potIdentity = Randomizer_IdentifyPot(play->sceneNum, (s16)this->actor.world.pos.x,
(s16)this->actor.world.pos.y, (s16)this->actor.world.pos.z);
if (ObjTsubo_HoldsRandomizedItem(this, play)) {
this->actor.scale.x = 0.1f;
this->actor.scale.y = 0.1f;
this->actor.scale.z = 0.1f;
}
}
}
void ObjTsubo_Destroy(Actor* thisx, PlayState* play2) {

View File

@ -13,6 +13,7 @@ typedef struct ObjTsubo {
/* 0x014C */ ObjTsuboActionFunc actionFunc;
/* 0x0150 */ ColliderCylinder collider;
/* 0x019C */ s8 objTsuboBankIndex;
/* */ PotIdentity potIdentity;
} ObjTsubo; // size = 0x01A0
#endif