Randomized Ice trap models (#1648)

This commit is contained in:
Christopher Leggett 2022-10-03 19:15:36 -04:00 committed by GitHub
parent db2e6164cb
commit 0720c37656
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 156 additions and 79 deletions

View File

@ -65,7 +65,7 @@ typedef struct {
typedef struct {
RandomizerCheck check;
RandomizerGet get;
RandomizerGetData get;
} ItemLocationRando;
typedef struct {

View File

@ -278,6 +278,16 @@ Item& ItemTable(const uint32_t itemKey) {
return itemTable[itemKey];
}
Item& ItemFromGIID(const int giid) {
uint32_t index = 0;
while (index < KEY_ENUM_MAX) {
if (itemTable[index].GetItemID() == giid) {
return itemTable[index];
}
index++;
}
}
//This function should only be used to place items containing hint text
//at gossip stone locations.
void NewItem(const uint32_t itemKey, const Item item) {

View File

@ -5,5 +5,6 @@
void ItemTable_Init();
Item& ItemTable(uint32_t itemKey);
Item& ItemFromGIID(int giid);
void NewItem(uint32_t itemKey, Item item);
std::array<Item, KEY_ENUM_MAX>* GetFullItemTable_();

View File

@ -1489,6 +1489,7 @@ std::vector<uint32_t> everyPossibleLocation = {};
//set of overrides to write to the patch
std::set<ItemOverride, ItemOverride_Compare> overrides = {};
std::unordered_map<RandomizerCheck, uint8_t> iceTrapModels = {};
std::vector<std::vector<uint32_t>> playthroughLocations;
std::vector<uint32_t> wothLocations;
@ -1616,9 +1617,13 @@ void CreateItemOverrides() {
for (uint32_t locKey : allLocations) {
auto loc = Location(locKey);
ItemOverride_Value val = ItemTable(loc->GetPlaceduint32_t()).Value();
// If this is an ice trap in a shop, change the name based on what the model will look like
if (loc->GetPlaceduint32_t() == ICE_TRAP && loc->IsCategory(Category::cShop)) {
NonShopItems[TransformShopIndex(GetShopIndex(locKey))].Name = GetIceTrapName(val.looksLikeItemId);
// If this is an ice trap, store the disguise model in iceTrapModels
if (loc->GetPlaceduint32_t() == ICE_TRAP) {
iceTrapModels[loc->GetRandomizerCheck()] = val.looksLikeItemId;
// If this is ice trap is in a shop, change the name based on what the model will look like
if (loc->IsCategory(Category::cShop)) {
NonShopItems[TransformShopIndex(GetShopIndex(locKey))].Name = GetIceTrapName(val.looksLikeItemId);
}
}
overrides.insert({
.key = loc->Key(),

View File

@ -489,6 +489,7 @@ extern std::vector<uint32_t> everyPossibleLocation;
//set of overrides to write to the patch
extern std::set<ItemOverride, ItemOverride_Compare> overrides;
extern std::unordered_map<RandomizerCheck, uint8_t> iceTrapModels;
extern std::vector<std::vector<uint32_t>> playthroughLocations;
extern std::vector<uint32_t> wothLocations;

View File

@ -239,7 +239,7 @@ void InitTrickNames() {
Text{"Skull Hammer", "Maillet Ressort", "Martillo de hierro"}};
trickNameTable[GI_STONE_OF_AGONY] = {
Text{"Shard of Agahnim", "Fragment d'Agahnim", "Piedra de Agahnim"},
Text{"Stone of Agony", "Pierre de Souffrance", "Fragmento de la Agonía"},
Text{"Shard of Agony", "Fragment de Souffrance", "Piedra de la Agonía"},
Text{"Pirate's Charm", "Pierre de Pirate", "Amuleto Pirata"}};
trickNameTable[GI_DINS_FIRE] = {
Text{"Eldin's Fire", "Feu d'Eldin", "Fuego de Eldin"},

View File

@ -677,8 +677,38 @@ static void WriteAllLocations(int language) {
// 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();
jsonData["locations"][location->GetName()]["item"] = placedItemName;
if (location->GetPlacedItemKey() == ICE_TRAP && location->IsCategory(Category::cShop)) {
switch (language) {
case 0:
default:
jsonData["locations"][location->GetName()]["model"] =
ItemFromGIID(iceTrapModels[location->GetRandomizerCheck()]).GetName().english;
jsonData["locations"][location->GetName()]["trickName"] =
NonShopItems[TransformShopIndex(GetShopIndex(key))].Name.english;
break;
case 2:
jsonData["locations"][location->GetName()]["model"] =
ItemFromGIID(iceTrapModels[location->GetRandomizerCheck()]).GetName().french;
jsonData["locations"][location->GetName()]["trickName"] =
NonShopItems[TransformShopIndex(GetShopIndex(key))].Name.french;
break;
}
}
jsonData["locations"][location->GetName()]["price"] = location->GetPrice();
} else if (location->GetPlacedItemKey() == ICE_TRAP && iceTrapModels.contains(location->GetRandomizerCheck())) {
jsonData["locations"][location->GetName()]["item"] = placedItemName;
switch (language) {
case 0:
default:
jsonData["locations"][location->GetName()]["model"] =
ItemFromGIID(iceTrapModels[location->GetRandomizerCheck()]).GetName().english;
break;
case 2:
jsonData["locations"][location->GetName()]["model"] =
ItemFromGIID(iceTrapModels[location->GetRandomizerCheck()]).GetName().french;
break;
}
} else {
jsonData["locations"][location->GetName()] = placedItemName;
}

View File

@ -441,12 +441,19 @@ void Randomizer::LoadMerchantMessages(const char* spoilerFileName) {
for (int index = 0; index < NUM_SHOP_ITEMS; index++) {
RandomizerCheck shopItemCheck = shopItemRandomizerChecks[index];
RandomizerGet shopItemGet = this->itemLocations[shopItemCheck];
RandomizerGet shopItemGet = this->itemLocations[shopItemCheck].rgID;
std::vector<std::string> shopItemName;
// TODO: This should eventually be replaced with a full fledged trick model & trick name system
if (shopItemGet == RG_ICE_TRAP) {
shopItemGet = RG_HUGE_RUPEE;
shopItemGet = this->itemLocations[shopItemCheck].fakeRgID;
shopItemName = {
std::string(this->itemLocations[shopItemCheck].trickName),
std::string(this->itemLocations[shopItemCheck].trickName),
std::string(this->itemLocations[shopItemCheck].trickName)
};
} else {
shopItemName = EnumToSpoilerfileGetName[shopItemGet];
}
std::vector<std::string> shopItemName = EnumToSpoilerfileGetName[shopItemGet];
u16 shopItemPrice = merchantPrices[shopItemCheck];
// Each shop item has two messages, one for when the cursor is over it, and one for when you select it and are
// prompted buy/don't buy, so we're adding the first at {index}, and the second at {index + NUM_SHOP_ITEMS}
@ -474,7 +481,7 @@ void Randomizer::LoadItemLocations(const char* spoilerFileName, bool silent) {
this->itemLocations[itemLocation.check] = itemLocation.get;
}
itemLocations[RC_UNKNOWN_CHECK] = RG_NONE;
itemLocations[RC_UNKNOWN_CHECK].rgID = itemLocations[RC_UNKNOWN_CHECK].fakeRgID = RG_NONE;
}
void Randomizer::LoadRequiredTrials(const char* spoilerFileName) {
@ -999,14 +1006,21 @@ void Randomizer::ParseItemLocationsFile(const char* spoilerFileName, bool silent
// todo handle prices
if (itemit.key() == "item") {
gSaveContext.itemLocations[randomizerCheck].check = randomizerCheck;
gSaveContext.itemLocations[randomizerCheck].get = SpoilerfileGetNameToEnum[itemit.value()];
gSaveContext.itemLocations[randomizerCheck].get.rgID = SpoilerfileGetNameToEnum[itemit.value()];
} else if (itemit.key() == "price") {
merchantPrices[gSaveContext.itemLocations[randomizerCheck].check] = itemit.value();
} else if (itemit.key() == "model") {
gSaveContext.itemLocations[randomizerCheck].get.fakeRgID =
SpoilerfileGetNameToEnum[itemit.value()];
} else if (itemit.key() == "trickName") {
strncpy(gSaveContext.itemLocations[randomizerCheck].get.trickName,
std::string(itemit.value()).c_str(), MAX_TRICK_NAME_SIZE);
}
}
} else {
gSaveContext.itemLocations[randomizerCheck].check = randomizerCheck;
gSaveContext.itemLocations[randomizerCheck].get = SpoilerfileGetNameToEnum[it.value()];
gSaveContext.itemLocations[randomizerCheck].check = SpoilerfileCheckNameToEnum[it.key()];
gSaveContext.itemLocations[randomizerCheck].get.rgID = SpoilerfileGetNameToEnum[it.value()];
gSaveContext.itemLocations[randomizerCheck].get.fakeRgID = RG_NONE;
}
}
@ -1024,20 +1038,21 @@ bool Randomizer::IsTrialRequired(RandomizerInf trial) {
return this->trialsRequired.contains(trial);
}
RandomizerGet Randomizer::GetRandomizerGetFromActor(s16 actorId, s16 sceneNum, s16 actorParams) {
RandomizerGetData Randomizer::GetRandomizerGetDataFromActor(s16 actorId, s16 sceneNum, s16 actorParams) {
return this->itemLocations[GetCheckFromActor(actorId, sceneNum, actorParams)];
}
RandomizerGet Randomizer::GetRandomizerGetFromKnownCheck(RandomizerCheck randomizerCheck) {
RandomizerGetData Randomizer::GetRandomizerGetDataFromKnownCheck(RandomizerCheck randomizerCheck) {
return this->itemLocations[randomizerCheck];
}
GetItemID Randomizer::GetItemIdFromActor(s16 actorId, s16 sceneNum, s16 actorParams, GetItemID ogItemId) {
return GetItemIdFromRandomizerGet(GetRandomizerGetFromActor(actorId, sceneNum, actorParams), ogItemId);
GetItemEntry Randomizer::GetItemFromActor(s16 actorId, s16 sceneNum, s16 actorParams, GetItemID ogItemId) {
RandomizerGetData rgData = this->itemLocations[GetCheckFromActor(actorId, sceneNum, actorParams)];
return GetItemEntryFromRGData(rgData, ogItemId);
}
ItemObtainability Randomizer::GetItemObtainabilityFromRandomizerCheck(RandomizerCheck randomizerCheck) {
return GetItemObtainabilityFromRandomizerGet(GetRandomizerGetFromKnownCheck(randomizerCheck));
return GetItemObtainabilityFromRandomizerGet(GetRandomizerGetDataFromKnownCheck(randomizerCheck).rgID);
}
ItemObtainability Randomizer::GetItemObtainabilityFromRandomizerGet(RandomizerGet randoGet) {
@ -1932,7 +1947,7 @@ bool Randomizer::IsItemVanilla(RandomizerGet randoGet) {
}
bool Randomizer::CheckContainsVanillaItem(RandomizerCheck randoCheck) {
RandomizerGet randoGet = this->itemLocations[randoCheck];
RandomizerGet randoGet = this->itemLocations[randoCheck].rgID;
return IsItemVanilla(randoGet);
}
@ -2591,9 +2606,9 @@ ShopItemIdentity Randomizer::IdentifyShopItem(s32 sceneNum, u8 slotIndex) {
break;
}
RandomizerGet randoGet = GetRandomizerGetFromKnownCheck(shopItemIdentity.randomizerCheck);
if (randomizerGetToEnGirlShopItem.find(randoGet) != randomizerGetToEnGirlShopItem.end()) {
shopItemIdentity.enGirlAShopItem = randomizerGetToEnGirlShopItem[randoGet];
RandomizerGetData randoGet = GetRandomizerGetDataFromKnownCheck(shopItemIdentity.randomizerCheck);
if (randomizerGetToEnGirlShopItem.find(randoGet.rgID) != randomizerGetToEnGirlShopItem.end()) {
shopItemIdentity.enGirlAShopItem = randomizerGetToEnGirlShopItem[randoGet.rgID];
}
if (merchantPrices.find(shopItemIdentity.randomizerCheck) != merchantPrices.end()) {
@ -2607,8 +2622,40 @@ u8 Randomizer::GetRandoSettingValue(RandomizerSettingKey randoSettingKey) {
return this->randoSettings[randoSettingKey];
}
GetItemID Randomizer::GetItemIdFromKnownCheck(RandomizerCheck randomizerCheck, GetItemID ogItemId) {
return GetItemIdFromRandomizerGet(this->itemLocations[randomizerCheck], ogItemId);
GetItemEntry Randomizer::GetItemEntryFromRGData(RandomizerGetData rgData, GetItemID ogItemId, bool checkObtainability) {
// Go ahead and early return the ogItemId's entry if we somehow get RG_NONE.
if (rgData.rgID == RG_NONE) {
return ItemTableManager::Instance->RetrieveItemEntry(MOD_NONE, ogItemId);
}
if (checkObtainability && OTRGlobals::Instance->gRandomizer->GetItemObtainabilityFromRandomizerGet(rgData.rgID) != CAN_OBTAIN) {
return ItemTableManager::Instance->RetrieveItemEntry(MOD_NONE, GI_RUPEE_BLUE);
}
// Can't get RG_ICE_TRAP if the rgID corresponds to a vanilla item
if (IsItemVanilla(rgData.rgID)) {
return ItemTableManager::Instance->RetrieveItemEntry(MOD_NONE, GetItemIdFromRandomizerGet(rgData.rgID, ogItemId));
}
// After this point we can assume we are dealing with a randomizer exclusive item.
GetItemEntry giEntry = ItemTableManager::Instance->RetrieveItemEntry(
MOD_RANDOMIZER, GetItemIdFromRandomizerGet(rgData.rgID, ogItemId));
// If we have an ice trap, we want to change the GID and DrawFunc to the fakeRgID's values.
if (rgData.rgID == RG_ICE_TRAP) {
ModIndex modIndex;
if (IsItemVanilla(rgData.fakeRgID)) {
modIndex = MOD_NONE;
} else {
modIndex = MOD_RANDOMIZER;
}
GetItemEntry fakeGiEntry = ItemTableManager::Instance->RetrieveItemEntry(modIndex, GetItemIdFromRandomizerGet(rgData.fakeRgID, ogItemId));
giEntry.gid = fakeGiEntry.gid;
giEntry.gi = fakeGiEntry.gi;
giEntry.drawFunc = fakeGiEntry.drawFunc;
}
return giEntry;
}
GetItemEntry Randomizer::GetItemFromKnownCheck(RandomizerCheck randomizerCheck, GetItemID ogItemId, bool checkObtainability) {
RandomizerGetData rgData = this->itemLocations[randomizerCheck];
return GetItemEntryFromRGData(rgData, ogItemId, checkObtainability);
}
RandomizerCheck Randomizer::GetCheckFromActor(s16 actorId, s16 sceneNum, s16 actorParams) {

View File

@ -7,13 +7,14 @@
#include <memory>
#include <soh/Enhancements/randomizer/randomizerTypes.h>
#include <soh/Enhancements/custom-message/CustomMessageManager.h>
#include "soh/Enhancements/item-tables/ItemTableTypes.h"
#define NUM_NAVI_MESSAGES 19
#define NUM_ICE_TRAP_MESSAGES 23
class Randomizer {
private:
std::unordered_map<RandomizerCheck, RandomizerGet> itemLocations;
std::unordered_map<RandomizerCheck, RandomizerGetData> itemLocations;
std::unordered_map<RandomizerCheck, std::string> hintLocations;
std::string childAltarText;
std::string adultAltarText;
@ -25,7 +26,7 @@ class Randomizer {
void ParseRequiredTrialsFile(const char* spoilerFileName);
void ParseItemLocationsFile(const char* spoilerFileName, bool silent);
bool IsItemVanilla(RandomizerGet randoGet);
GetItemEntry GetItemEntryFromRGData(RandomizerGetData rgData, GetItemID ogItemId, bool checkObtainability = true);
public:
Randomizer();
@ -54,16 +55,16 @@ class Randomizer {
bool IsTrialRequired(RandomizerInf trial);
u8 GetRandoSettingValue(RandomizerSettingKey randoSettingKey);
RandomizerCheck GetCheckFromActor(s16 actorId, s16 sceneNum, s16 actorParams);
RandomizerGet GetRandomizerGetFromActor(s16 actorId, s16 sceneNum, s16 actorParams);
RandomizerGet GetRandomizerGetFromKnownCheck(RandomizerCheck randomizerCheck);
RandomizerGetData GetRandomizerGetDataFromActor(s16 actorId, s16 sceneNum, s16 actorParams);
RandomizerGetData GetRandomizerGetDataFromKnownCheck(RandomizerCheck randomizerCheck);
std::string GetChildAltarText() const;
std::string GetAdultAltarText() const;
std::string GetGanonText() const;
std::string GetGanonHintText() const;
ScrubIdentity IdentifyScrub(s32 sceneNum, s32 actorParams, s32 respawnData);
ShopItemIdentity IdentifyShopItem(s32 sceneNum, u8 slotIndex);
GetItemID GetItemIdFromKnownCheck(RandomizerCheck randomizerCheck, GetItemID ogItemId);
GetItemID GetItemIdFromActor(s16 actorId, s16 sceneNum, s16 actorParams, GetItemID ogItemId);
GetItemEntry GetItemFromKnownCheck(RandomizerCheck randomizerCheck, GetItemID ogItemId, bool checkObtainability = true);
GetItemEntry GetItemFromActor(s16 actorId, s16 sceneNum, s16 actorParams, GetItemID ogItemId);
GetItemID GetItemIdFromRandomizerGet(RandomizerGet randoGet, GetItemID ogItemId);
ItemObtainability GetItemObtainabilityFromRandomizerCheck(RandomizerCheck randomizerCheck);
ItemObtainability GetItemObtainabilityFromRandomizerGet(RandomizerGet randomizerCheck);

View File

@ -4,6 +4,8 @@
#include "z64item.h"
#include "randomizer_inf.h"
#define MAX_TRICK_NAME_SIZE 50
// This should probably go in a less rando-specific location
// but the best location will probably be in the modding engine
// which doesn't exist yet.
@ -964,6 +966,12 @@ typedef enum {
RG_MAX
} RandomizerGet;
typedef struct {
RandomizerGet rgID;
RandomizerGet fakeRgID;
char trickName[MAX_TRICK_NAME_SIZE];
} RandomizerGetData;
typedef enum {
RSK_NONE,
RSK_LOGIC_RULES,

View File

@ -1696,57 +1696,15 @@ extern "C" GetItemEntry ItemTable_RetrieveEntry(s16 tableID, s16 getItemID) {
}
extern "C" GetItemEntry Randomizer_GetItemFromActor(s16 actorId, s16 sceneNum, s16 actorParams, GetItemID ogId) {
s16 getItemModIndex;
RandomizerCheck randomizerCheck = OTRGlobals::Instance->gRandomizer->GetCheckFromActor(actorId, sceneNum, actorParams);
// if we got unknown check here, we don't need to do anything else, just return the ogId.
if (randomizerCheck == RC_UNKNOWN_CHECK) {
return ItemTable_RetrieveEntry(MOD_NONE, ogId);
}
if (OTRGlobals::Instance->gRandomizer->CheckContainsVanillaItem(randomizerCheck)) {
getItemModIndex = MOD_NONE;
} else {
getItemModIndex = MOD_RANDOMIZER;
}
s16 itemID = OTRGlobals::Instance->gRandomizer->GetItemIdFromActor(actorId, sceneNum, actorParams, ogId);
if (OTRGlobals::Instance->gRandomizer->GetItemObtainabilityFromRandomizerCheck(randomizerCheck) != CAN_OBTAIN) {
return ItemTable_RetrieveEntry(MOD_NONE, GI_RUPEE_BLUE);
}
return ItemTable_RetrieveEntry(getItemModIndex, itemID);
return OTRGlobals::Instance->gRandomizer->GetItemFromActor(actorId, sceneNum, actorParams, ogId);
}
extern "C" GetItemEntry Randomizer_GetItemFromKnownCheck(RandomizerCheck randomizerCheck, GetItemID ogId) {
s16 getItemModIndex;
// if we got unknown check here, we don't need to do anything else, just return the ogId.
if (randomizerCheck == RC_UNKNOWN_CHECK) {
return ItemTable_RetrieveEntry(MOD_NONE, ogId);
}
if (OTRGlobals::Instance->gRandomizer->CheckContainsVanillaItem(randomizerCheck)) {
getItemModIndex = MOD_NONE;
} else {
getItemModIndex = MOD_RANDOMIZER;
}
s16 itemID = OTRGlobals::Instance->gRandomizer->GetItemIdFromKnownCheck(randomizerCheck, ogId);
if (OTRGlobals::Instance->gRandomizer->GetItemObtainabilityFromRandomizerCheck(randomizerCheck) != CAN_OBTAIN) {
return ItemTable_RetrieveEntry(MOD_NONE, GI_RUPEE_BLUE);
}
return ItemTable_RetrieveEntry(getItemModIndex, itemID);
return OTRGlobals::Instance->gRandomizer->GetItemFromKnownCheck(randomizerCheck, ogId);
}
extern "C" GetItemEntry Randomizer_GetItemFromKnownCheckWithoutObtainabilityCheck(RandomizerCheck randomizerCheck, GetItemID ogId) {
s16 getItemModIndex;
if (OTRGlobals::Instance->gRandomizer->CheckContainsVanillaItem(randomizerCheck)) {
getItemModIndex = MOD_NONE;
} else {
getItemModIndex = MOD_RANDOMIZER;
}
s16 itemID = OTRGlobals::Instance->gRandomizer->GetItemIdFromKnownCheck(randomizerCheck, ogId);
return ItemTable_RetrieveEntry(getItemModIndex, itemID);
return OTRGlobals::Instance->gRandomizer->GetItemFromKnownCheck(randomizerCheck, ogId, false);
}
extern "C" ItemObtainability Randomizer_GetItemObtainabilityFromRandomizerCheck(RandomizerCheck randomizerCheck) {

View File

@ -58,7 +58,13 @@ void SaveManager::LoadRandomizerVersion1() {
if(!CVar_GetS32("gRandomizer", 0)) return;
for (int i = 0; i < ARRAY_COUNT(gSaveContext.itemLocations); i++) {
SaveManager::Instance->LoadData("get" + std::to_string(i), gSaveContext.itemLocations[i].get);
SaveManager::Instance->LoadStruct("get" + std::to_string(i), [&]() {
SaveManager::Instance->LoadData("rgID", gSaveContext.itemLocations[i].get.rgID);
SaveManager::Instance->LoadData("fakeRgID", gSaveContext.itemLocations[i].get.fakeRgID);
std::string trickName;
SaveManager::Instance->LoadData("trickName", trickName);
strncpy(gSaveContext.itemLocations[i].get.trickName, trickName.c_str(), MAX_TRICK_NAME_SIZE);
});
SaveManager::Instance->LoadData("check" + std::to_string(i), gSaveContext.itemLocations[i].check);
}
@ -124,7 +130,13 @@ void SaveManager::LoadRandomizerVersion2() {
SaveManager::Instance->LoadArray("itemLocations", RC_MAX, [&](size_t i) {
gSaveContext.itemLocations[i].check = RandomizerCheck(i);
SaveManager::Instance->LoadData("", gSaveContext.itemLocations[i].get);
SaveManager::Instance->LoadStruct("", [&]() {
SaveManager::Instance->LoadData("rgID", gSaveContext.itemLocations[i].get.rgID);
SaveManager::Instance->LoadData("fakeRgID", gSaveContext.itemLocations[i].get.fakeRgID);
std::string trickName;
SaveManager::Instance->LoadData("trickName", trickName);
strncpy(gSaveContext.itemLocations[i].get.trickName, trickName.c_str(), MAX_TRICK_NAME_SIZE);
});
});
SaveManager::Instance->LoadArray("seed", ARRAY_COUNT(gSaveContext.seedIcons), [&](size_t i) {
@ -188,7 +200,11 @@ void SaveManager::SaveRandomizer() {
if(!gSaveContext.n64ddFlag) return;
SaveManager::Instance->SaveArray("itemLocations", RC_MAX, [&](size_t i) {
SaveManager::Instance->SaveData("", gSaveContext.itemLocations[i].get);
SaveManager::Instance->SaveStruct("", [&]() {
SaveManager::Instance->SaveData("rgID", gSaveContext.itemLocations[i].get.rgID);
SaveManager::Instance->SaveData("fakeRgID", gSaveContext.itemLocations[i].get.fakeRgID);
SaveManager::Instance->SaveData("trickName", gSaveContext.itemLocations[i].get.trickName);
});
});
SaveManager::Instance->SaveArray("seed", ARRAY_COUNT(gSaveContext.seedIcons), [&](size_t i) {