Added some of the settings, loaded from the server, implemented game win condition (pretty untested)
This commit is contained in:
2
APCpp
2
APCpp
Submodule APCpp updated: 38d5cf0798...505a174a3b
15
README.md
15
README.md
@@ -3,12 +3,21 @@
|
||||
|
||||
## Fork Overview
|
||||
|
||||
Currently playing around trying to see if we're able to hook the existing Archipelago Ocarina of time randomizer into Ship of Harkinian.
|
||||
This Repo serves as a proof of concept for SoH to connect to Archipellago, the current implementation tries to implement the existing OoT AP world.
|
||||
Mainly to check out the SoH Repo and to have something to develop against while the others on the discord are working on an AP world for SoH.
|
||||
|
||||
I'm not entierly happy with this implementation, but I'd like to create a bespoke Client to connect to AP with instead of relying on the current 3rd party clien't I've just thrown in to try out.
|
||||
|
||||
You can currently connect to the multiworld server, scout the items.
|
||||
If you have no randomizer genereted (the `Randomizer` folder is empty) and press the `Link up` button, you'll be able to start a randomizer save file
|
||||
If you have no randomizer genereted (the `Randomizer` folder is empty) `Connect` to the multiworld from the main screen, click the `Scout` button to load all of the item locations and finally press the `Link up` button, you'll be able to start a randomizer save file.
|
||||
with the items populated with the location from the server.
|
||||
|
||||
Sending and recieving is not implemented yet, Multiworld items are currently just recovery hearts
|
||||
Sending and Recieving should be implemented, Saving the game doesn't save the current item index localy (the library is a bit anoying with that).
|
||||
|
||||
Not all checks have been mapped, and some may be mapped incorrectly.
|
||||
The victory condition should be implemented but has largely gone untested.
|
||||
Results may varry
|
||||
|
||||
|
||||
## Website
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
#include "objects/object_link_child/object_link_child.h"
|
||||
#include "soh_assets.h"
|
||||
#include "kaleido.h"
|
||||
#include "soh/Enhancements/randomizer/archipelago.h"
|
||||
|
||||
extern "C" {
|
||||
#include <z64.h>
|
||||
@@ -876,6 +877,7 @@ void RegisterBossDefeatTimestamps() {
|
||||
case ACTOR_BOSS_GANON2:
|
||||
gSaveContext.ship.stats.itemTimestamp[TIMESTAMP_DEFEAT_GANON] = GAMEPLAYSTAT_TOTAL_TIME;
|
||||
gSaveContext.ship.stats.gameComplete = true;
|
||||
ArchipelagoClient::getInstance().send_game_won();
|
||||
break;
|
||||
case ACTOR_BOSS_GANONDROF:
|
||||
gSaveContext.ship.stats.itemTimestamp[TIMESTAMP_DEFEAT_PHANTOM_GANON] = GAMEPLAYSTAT_TOTAL_TIME;
|
||||
|
||||
@@ -36,6 +36,7 @@ auto SubscribeToSlotData() {
|
||||
|
||||
ArchipelagoClient::ArchipelagoClient() {
|
||||
ItemRecievedCallback = nullptr;
|
||||
game_won = false;
|
||||
|
||||
namespace apc = AP_Client_consts;
|
||||
CVarSetInteger("archipelago_connected", 0);
|
||||
@@ -64,71 +65,71 @@ void registerSlotCallbacks() {
|
||||
SubscribeToSlotData<"bridge_medallions">();
|
||||
SubscribeToSlotData<"bridge_rewards">();
|
||||
SubscribeToSlotData<"bridge_tokens">();
|
||||
SubscribeToSlotData<"bridge_hearts">();
|
||||
// SubscribeToSlotData<"bridge_hearts">();
|
||||
SubscribeToSlotData<"shuffle_ganon_bosskey">();
|
||||
SubscribeToSlotData<"ganon_bosskey_medallions">();
|
||||
SubscribeToSlotData<"ganon_bosskey_stones">();
|
||||
SubscribeToSlotData<"ganon_bosskey_rewards">();
|
||||
SubscribeToSlotData<"ganon_bosskey_tokens">();
|
||||
SubscribeToSlotData<"ganon_bosskey_hearts">();
|
||||
// SubscribeToSlotData<"ganon_bosskey_hearts">();
|
||||
SubscribeToSlotData<"trials">();
|
||||
SubscribeToSlotData<"triforce_hunt">();
|
||||
SubscribeToSlotData<"triforce_goal">();
|
||||
SubscribeToSlotData<"extra_triforce_percentage">();
|
||||
SubscribeToSlotData<"shopsanity">();
|
||||
SubscribeToSlotData<"shop_slots">();
|
||||
// SubscribeToSlotData<"extra_triforce_percentage">();
|
||||
// SubscribeToSlotData<"shopsanity">();
|
||||
// SubscribeToSlotData<"shop_slots">();
|
||||
SubscribeToSlotData<"shopsanity_prices">();
|
||||
SubscribeToSlotData<"tokensanity">();
|
||||
SubscribeToSlotData<"dungeon_shortcuts">();
|
||||
SubscribeToSlotData<"mq_dungeons_mode">();
|
||||
SubscribeToSlotData<"mq_dungeons_count">();
|
||||
SubscribeToSlotData<"shuffle_interior_entrances">();
|
||||
SubscribeToSlotData<"shuffle_grotto_entrances">();
|
||||
SubscribeToSlotData<"shuffle_dungeon_entrances">();
|
||||
SubscribeToSlotData<"shuffle_overworld_entrances">();
|
||||
SubscribeToSlotData<"shuffle_bosses">();
|
||||
SubscribeToSlotData<"key_rings">();
|
||||
SubscribeToSlotData<"enhance_map_compass">();
|
||||
SubscribeToSlotData<"shuffle_mapcompass">();
|
||||
SubscribeToSlotData<"shuffle_smallkeys">();
|
||||
SubscribeToSlotData<"shuffle_hideoutkeys">();
|
||||
SubscribeToSlotData<"shuffle_bosskeys">();
|
||||
SubscribeToSlotData<"logic_rules">();
|
||||
SubscribeToSlotData<"logic_no_night_tokens_without_suns_song">();
|
||||
SubscribeToSlotData<"warp_songs">();
|
||||
SubscribeToSlotData<"shuffle_song_items">();
|
||||
SubscribeToSlotData<"shuffle_medigoron_carpet_salesman">();
|
||||
SubscribeToSlotData<"shuffle_frog_song_rupees">();
|
||||
SubscribeToSlotData<"shuffle_scrubs">();
|
||||
SubscribeToSlotData<"shuffle_child_trade">();
|
||||
SubscribeToSlotData<"shuffle_freestanding_items">();
|
||||
SubscribeToSlotData<"shuffle_pots">();
|
||||
SubscribeToSlotData<"shuffle_crates">();
|
||||
SubscribeToSlotData<"shuffle_cows">();
|
||||
SubscribeToSlotData<"shuffle_beehives">();
|
||||
SubscribeToSlotData<"shuffle_kokiri_sword">();
|
||||
SubscribeToSlotData<"shuffle_ocarinas">();
|
||||
SubscribeToSlotData<"shuffle_gerudo_card">();
|
||||
SubscribeToSlotData<"shuffle_beans">();
|
||||
// SubscribeToSlotData<"tokensanity">();
|
||||
// SubscribeToSlotData<"dungeon_shortcuts">();
|
||||
// SubscribeToSlotData<"mq_dungeons_mode">();
|
||||
// SubscribeToSlotData<"mq_dungeons_count">();
|
||||
// SubscribeToSlotData<"shuffle_interior_entrances">();
|
||||
// SubscribeToSlotData<"shuffle_grotto_entrances">();
|
||||
// SubscribeToSlotData<"shuffle_dungeon_entrances">();
|
||||
// SubscribeToSlotData<"shuffle_overworld_entrances">();
|
||||
// SubscribeToSlotData<"shuffle_bosses">();
|
||||
// SubscribeToSlotData<"key_rings">();
|
||||
// SubscribeToSlotData<"enhance_map_compass">();
|
||||
// SubscribeToSlotData<"shuffle_mapcompass">();
|
||||
// SubscribeToSlotData<"shuffle_smallkeys">();
|
||||
// SubscribeToSlotData<"shuffle_hideoutkeys">();
|
||||
// SubscribeToSlotData<"shuffle_bosskeys">();
|
||||
// SubscribeToSlotData<"logic_rules">();
|
||||
// SubscribeToSlotData<"logic_no_night_tokens_without_suns_song">();
|
||||
// SubscribeToSlotData<"warp_songs">();
|
||||
// SubscribeToSlotData<"shuffle_song_items">();
|
||||
// SubscribeToSlotData<"shuffle_medigoron_carpet_salesman">();
|
||||
// SubscribeToSlotData<"shuffle_frog_song_rupees">();
|
||||
// SubscribeToSlotData<"shuffle_scrubs">();
|
||||
// SubscribeToSlotData<"shuffle_child_trade">();
|
||||
// SubscribeToSlotData<"shuffle_freestanding_items">();
|
||||
// SubscribeToSlotData<"shuffle_pots">();
|
||||
// SubscribeToSlotData<"shuffle_crates">();
|
||||
// SubscribeToSlotData<"shuffle_cows">();
|
||||
// SubscribeToSlotData<"shuffle_beehives">();
|
||||
// SubscribeToSlotData<"shuffle_kokiri_sword">();
|
||||
// SubscribeToSlotData<"shuffle_ocarinas">();
|
||||
// SubscribeToSlotData<"shuffle_gerudo_card">();
|
||||
// SubscribeToSlotData<"shuffle_beans">();
|
||||
SubscribeToSlotData<"starting_age">();
|
||||
SubscribeToSlotData<"bombchus_in_logic">();
|
||||
SubscribeToSlotData<"spawn_positions">();
|
||||
SubscribeToSlotData<"owl_drops">();
|
||||
// SubscribeToSlotData<"bombchus_in_logic">();
|
||||
// SubscribeToSlotData<"spawn_positions">();
|
||||
// SubscribeToSlotData<"owl_drops">();
|
||||
SubscribeToSlotData<"no_epona_race">();
|
||||
SubscribeToSlotData<"skip_some_minigame_phases">();
|
||||
// SubscribeToSlotData<"skip_some_minigame_phases">();
|
||||
SubscribeToSlotData<"complete_mask_quest">();
|
||||
SubscribeToSlotData<"free_scarecrow">();
|
||||
SubscribeToSlotData<"plant_beans">();
|
||||
// SubscribeToSlotData<"plant_beans">();
|
||||
SubscribeToSlotData<"chicken_count">();
|
||||
SubscribeToSlotData<"big_poe_count">();
|
||||
SubscribeToSlotData<"fae_torch_count">();
|
||||
// SubscribeToSlotData<"fae_torch_count">();
|
||||
SubscribeToSlotData<"blue_fire_arrows">();
|
||||
SubscribeToSlotData<"damage_multiplier">();
|
||||
SubscribeToSlotData<"deadly_bonks">();
|
||||
SubscribeToSlotData<"starting_tod">();
|
||||
SubscribeToSlotData<"junk_ice_traps">();
|
||||
// SubscribeToSlotData<"deadly_bonks">();
|
||||
// SubscribeToSlotData<"starting_tod">();
|
||||
// SubscribeToSlotData<"junk_ice_traps">();
|
||||
SubscribeToSlotData<"start_with_consumables">();
|
||||
SubscribeToSlotData<"adult_trade_start">();
|
||||
// SubscribeToSlotData<"adult_trade_start">();
|
||||
}
|
||||
|
||||
bool ArchipelagoClient::start_client() {
|
||||
@@ -193,6 +194,9 @@ bool ArchipelagoClient::isConnected() {
|
||||
|
||||
void ArchipelagoClient::check_location(RandomizerCheck SoH_check_id) {
|
||||
std::string_view ap_name = Rando::StaticData::SohCheckToAP[SoH_check_id];
|
||||
if(ap_name.empty()) {
|
||||
return;
|
||||
}
|
||||
int64_t ap_item_id = CheckNameToId(std::string(ap_name));
|
||||
SPDLOG_TRACE("Checked: {}({}), sending to AP server", ap_name, ap_item_id);
|
||||
|
||||
@@ -226,11 +230,10 @@ void ArchipelagoClient::on_clear_items() {
|
||||
|
||||
void ArchipelagoClient::on_item_recieved(int64_t recieved_item_id, bool notify_player) {
|
||||
// call each callback
|
||||
SPDLOG_TRACE("Trying to give rupie...");
|
||||
std::string item_name = getAPitemName(recieved_item_id);
|
||||
ArchipelagoClient& ap_client = ArchipelagoClient::getInstance();
|
||||
if(ap_client.ItemRecievedCallback) {
|
||||
SPDLOG_TRACE("Giving Rupie! {}", item_name);
|
||||
SPDLOG_TRACE("item recieved: {}, notify: {}", item_name, notify_player);
|
||||
ap_client.ItemRecievedCallback.operator()(item_name); // somehow passing it through the itemname breaks it????
|
||||
}
|
||||
}
|
||||
@@ -246,6 +249,13 @@ void ArchipelagoClient::on_location_scouted(std::vector<AP_NetworkItem> network_
|
||||
getInstance().scouted_items = network_items;
|
||||
}
|
||||
|
||||
void ArchipelagoClient::send_game_won() {
|
||||
if(!game_won) {
|
||||
AP_StoryComplete();
|
||||
game_won = true;
|
||||
}
|
||||
}
|
||||
|
||||
char* ArchipelagoClient::get_server_address_buff() {
|
||||
return server_address;
|
||||
}
|
||||
|
||||
@@ -49,6 +49,8 @@ class ArchipelagoClient {
|
||||
// todo move me back down when done testing
|
||||
static void on_item_recieved(int64_t recieved_item_id, bool notify_player);
|
||||
|
||||
void send_game_won();
|
||||
|
||||
protected:
|
||||
ArchipelagoClient();
|
||||
|
||||
@@ -62,6 +64,8 @@ class ArchipelagoClient {
|
||||
char slot_name[AP_Client_consts::MAX_PLAYER_NAME_LENGHT];
|
||||
char password[AP_Client_consts::MAX_PLAYER_NAME_LENGHT];
|
||||
|
||||
bool game_won;
|
||||
|
||||
std::map<std::string, int> slot_data;
|
||||
std::set<int64_t> locations;
|
||||
std::vector<AP_NetworkItem> scouted_items;
|
||||
|
||||
@@ -366,6 +366,7 @@ void Context::ParseArchipelago() {
|
||||
mSpoilerLoaded = false;
|
||||
|
||||
ArchipelagoClient& ap_client = ArchipelagoClient::getInstance();
|
||||
Rando::Settings::GetInstance()->ParseArchipelago(ap_client.get_slot_data());
|
||||
ParseArchipelagoItemsLocations(ap_client.get_scouted_items());
|
||||
|
||||
// lets see if counting AP_loaded as spoiler loaded does the trick
|
||||
@@ -383,6 +384,7 @@ void Context::ParseHashIconIndexesJson(nlohmann::json spoilerFileJson) {
|
||||
}
|
||||
|
||||
void Context::ParseItemLocationsJson(nlohmann::json spoilerFileJson) {
|
||||
// first fill all the items with their vanilla location
|
||||
nlohmann::json locationsJson = spoilerFileJson["locations"];
|
||||
for (auto it = locationsJson.begin(); it != locationsJson.end(); ++it) {
|
||||
RandomizerCheck rc = StaticData::locationNameToEnum[it.key()];
|
||||
@@ -405,10 +407,21 @@ void Context::ParseItemLocationsJson(nlohmann::json spoilerFileJson) {
|
||||
}
|
||||
}
|
||||
|
||||
void Context::ParseArchipelagoItemsLocations(std::vector<AP_NetworkItem> scouted_items) {
|
||||
void Context::ParseArchipelagoItemsLocations(const std::vector<AP_NetworkItem>& scouted_items) {
|
||||
int playerId = AP_GetPlayerID(); // todo change me when the client is developed further
|
||||
|
||||
// init the item table with regular items first
|
||||
for(int rc = 1; rc <= RC_MAX; rc++) {
|
||||
itemLocationTable[rc].SetPlacedItem(StaticData::GetLocation(static_cast<RandomizerCheck>(rc))->GetVanillaItem());
|
||||
}
|
||||
|
||||
for(const AP_NetworkItem& ap_item: scouted_items) {
|
||||
const RandomizerCheck rc = StaticData::APcheckToSoh.find(ap_item.locationName)->second;
|
||||
|
||||
if(rc == RC_KF_MIDOS_TOP_RIGHT_CHEST) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(playerId == ap_item.player) {
|
||||
// our item
|
||||
SPDLOG_TRACE("Populated item {} at location {}", ap_item.itemName, ap_item.locationName);
|
||||
|
||||
@@ -126,7 +126,8 @@ class Context {
|
||||
RandomizerArea GetAreaFromString(std::string str);
|
||||
|
||||
void ParseArchipelago();
|
||||
void ParseArchipelagoItemsLocations(const std::vector<AP_NetworkItem>);
|
||||
void ParseArchipelagoSettings(const std::map<std::string, int>& slot_data);
|
||||
void ParseArchipelagoItemsLocations(const std::vector<AP_NetworkItem>& slot_data);
|
||||
|
||||
/**
|
||||
* @brief Get the hash for the current seed.
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
#include "soh/util.h"
|
||||
#include "fishsanity.h"
|
||||
#include "randomizerTypes.h"
|
||||
#include "archipelago.h"
|
||||
|
||||
extern std::map<RandomizerCheckArea, std::string> rcAreaNames;
|
||||
|
||||
@@ -4109,6 +4110,7 @@ extern "C" u16 Randomizer_Item_Give(PlayState* play, GetItemEntry giEntry) {
|
||||
if (gSaveContext.ship.quest.data.randomizer.triforcePiecesCollected == (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_TRIFORCE_HUNT_PIECES_REQUIRED) + 1)) {
|
||||
gSaveContext.ship.stats.itemTimestamp[TIMESTAMP_TRIFORCE_COMPLETED] = GAMEPLAYSTAT_TOTAL_TIME;
|
||||
gSaveContext.ship.stats.gameComplete = 1;
|
||||
ArchipelagoClient::getInstance().send_game_won();
|
||||
Flags_SetRandomizerInf(RAND_INF_GRANT_GANONS_BOSSKEY);
|
||||
Play_PerformSave(play);
|
||||
GameInteractor_SetTriforceHuntCreditsWarpActive(true);
|
||||
|
||||
@@ -2226,6 +2226,96 @@ void Settings::ParseJson(nlohmann::json spoilerFileJson) {
|
||||
}
|
||||
}
|
||||
|
||||
void Settings::ParseArchipelago(const std::map<std::string, int>& slot_data) {
|
||||
for(const auto& slot_it : slot_data) {
|
||||
const std::string& APname = slot_it.first;
|
||||
int value = slot_it.second;
|
||||
|
||||
// remap value if needed
|
||||
if(APname == "open_forest") {
|
||||
if(value == 0) { // open and closed options swapped
|
||||
value = 2;
|
||||
} else if (value == 2) {
|
||||
value = 0;
|
||||
}
|
||||
}
|
||||
if(APname == "open_kakoriko") {
|
||||
if(value == 2) {
|
||||
value = 0; // closed
|
||||
} else {
|
||||
value = 1; // count the "zelda" option as open
|
||||
}
|
||||
}
|
||||
else if(APname == "open_door_of_time") {
|
||||
if(value == 1) {
|
||||
value = 2; // AP doesn't have the song only option
|
||||
}
|
||||
}
|
||||
else if(APname == "zora_fountain") {
|
||||
if(value == 0) { // open and closed options swapped
|
||||
value = 2;
|
||||
} else if (value == 2) {
|
||||
value = 0;
|
||||
}
|
||||
}
|
||||
else if(APname == "bridge") {
|
||||
if(value == 0) { // always open and vanilla are swapped
|
||||
value = 1;
|
||||
} else if (value == 1) {
|
||||
value = 0;
|
||||
} else if (value == 6) { // no support for ap hearts option
|
||||
value = 1; // revert to always open so its at least beatable
|
||||
} else if (value == 5) {
|
||||
value++; // Token option is one over, because AP doesn't have dungeon blue warp count as option
|
||||
}
|
||||
}
|
||||
else if(APname == "shopsanity_prices") {
|
||||
value = 1; // default shopsanity to cheap ballanced for now
|
||||
}
|
||||
else if(APname == "start_with_consumables") { // just setting this shouldn't magically work I think
|
||||
if(value == 1) {
|
||||
const RandomizerSettingKey stick_index = StaticData::optionNameToEnum["Start with Stick Ammo"];
|
||||
mContext->GetOption(stick_index).Set(mOptions[stick_index].GetValueFromText("Yes"));
|
||||
const RandomizerSettingKey nut_index = StaticData::optionNameToEnum["Start with Stick Ammo"];
|
||||
mContext->GetOption(nut_index).Set(mOptions[nut_index].GetValueFromText("Yes"));
|
||||
} else {
|
||||
const RandomizerSettingKey stick_index = StaticData::optionNameToEnum["Start with Stick Ammo"];
|
||||
mContext->GetOption(stick_index).Set(mOptions[stick_index].GetValueFromText("No"));
|
||||
const RandomizerSettingKey nut_index = StaticData::optionNameToEnum["Start with Stick Ammo"];
|
||||
mContext->GetOption(nut_index).Set(mOptions[nut_index].GetValueFromText("No"));
|
||||
}
|
||||
}
|
||||
const std::string& setting_name = std::string(StaticData::APsettingToHoSsetting[APname]);
|
||||
const RandomizerSettingKey index = StaticData::optionNameToEnum[setting_name];
|
||||
mContext->GetOption(index).Set(value);
|
||||
SPDLOG_INFO("Parsed Setting {}: ({}, {})", APname, (int)index, value);
|
||||
}
|
||||
|
||||
// maybe we have to set a couple of settings manually, if ap doesn't set them
|
||||
{
|
||||
//const RandomizerSettingKey index = StaticData::optionNameToEnum["Starting Age"];
|
||||
//mContext->GetOption(index).Set(mOptions[index].GetValueFromText("Random"));
|
||||
}
|
||||
{
|
||||
const RandomizerSettingKey index = StaticData::optionNameToEnum["Ganon's Trials"];
|
||||
mContext->GetOption(index).Set(mOptions[index].GetValueFromText("Set Number"));
|
||||
}
|
||||
{
|
||||
const RandomizerSettingKey index = StaticData::optionNameToEnum["Logic"];
|
||||
mContext->GetOption(index).Set(mOptions[index].GetValueFromText("Glitchless"));
|
||||
}
|
||||
{
|
||||
const RandomizerSettingKey index = StaticData::optionNameToEnum["Starting Hearts"];
|
||||
mContext->GetOption(index).Set(2);
|
||||
}
|
||||
{
|
||||
const RandomizerSettingKey index = StaticData::optionNameToEnum["Token Shuffle"];
|
||||
mContext->GetOption(index).Set(mOptions[index].GetValueFromText("Off"));
|
||||
}
|
||||
//const RandomizerSettingKey index = StaticData::optionNameToEnum["Sleeping Waterfall"];
|
||||
//mContext->GetOption(index).Set(0);
|
||||
}
|
||||
|
||||
void Settings::ReloadOptions() {
|
||||
for (int i = 0; i < RSK_MAX; i++) {
|
||||
mOptions[i].SetFromCVar();
|
||||
|
||||
@@ -112,6 +112,8 @@ class Settings {
|
||||
* @param spoilerFileJson
|
||||
*/
|
||||
void ParseJson(nlohmann::json spoilerFileJson);
|
||||
|
||||
void ParseArchipelago(const std::map<std::string, int>& slot_data);
|
||||
std::map<RandomizerArea, std::vector<RandomizerTrick>> mTricksByArea = {};
|
||||
void ReloadOptions();
|
||||
|
||||
|
||||
@@ -331,6 +331,82 @@ const std::unordered_map<RandomizerCheck, std::string_view>generate_SohcheckToAP
|
||||
std::unordered_map<std::string_view, RandomizerGet> StaticData::APitemToSoh = generate_APitemToSoh_mapping();
|
||||
std::unordered_map<std::string_view, RandomizerCheck> StaticData::APcheckToSoh = generate_APcheckToSoh_mapping();
|
||||
std::unordered_map<RandomizerCheck, std::string_view> StaticData::SohCheckToAP = generate_SohcheckToAP_mapping();
|
||||
|
||||
std::unordered_map<std::string_view, std::string_view> StaticData::APsettingToHoSsetting = {
|
||||
{ "open_forest", "Closed Forest" },
|
||||
{ "open_kakoriko", "Kakariko Gate" },
|
||||
{ "open_door_of_time", "Door of Time" },
|
||||
{ "zora_fountain", "Zora's Fountain" },
|
||||
{ "gerudo_fortress", "Fortress Carpenters" },
|
||||
{ "bridge", "Rainbow Bridge" }, // TODO underlying options may not overlap
|
||||
{ "bridge_stones", "Bridge Stone Count" },
|
||||
{ "bridge_medallions", "Bridge Medallion Count" },
|
||||
{ "bridge_rewards", "Bridge Reward Count" },
|
||||
{ "bridge_tokens", "Bridge Token Count" },
|
||||
{ "bridge_hearts", "NOT_SUPPORTED" },
|
||||
{ "shuffle_ganon_bosskey", "Ganon's Boss Key" },
|
||||
{ "ganon_bosskey_medallions", "GCBK Medallion Count" },
|
||||
{ "ganon_bosskey_stones", "GCBK Stone Count" },
|
||||
{ "ganon_bosskey_rewards", "GCBK Reward Count" },
|
||||
{ "ganon_bosskey_tokens", "GCBK Token Count" },
|
||||
{ "ganon_bosskey_hearts", "NOT_SUPPORTED" },
|
||||
{ "trials", "Ganon's Trials Count" },
|
||||
{ "triforce_hunt", "Triforce Hunt" },
|
||||
{ "triforce_goal", "Triforce Hunt Required Pieces" },
|
||||
{ "extra_triforce_percentage", "CUSTOM_IMPLEMENTATION" }, // TODO calc "Triforce Hunt Required Pieces" from percentage, Actually not really required to make the game run I think
|
||||
{ "shopsanity", "Shop Shuffle" },
|
||||
{ "shop_slots", "Shops Item Count" },
|
||||
{ "shopsanity_prices", "Shops Prices" }, // Item Prizes not in slot data, anything above starting wallet will be lowered to max 99
|
||||
{ "tokensanity", "Token Shuffle" },
|
||||
{ "dungeon_shortcuts", "NOT_SUPPORTED" }, // TODO could be implemented manually through
|
||||
{ "mq_dungeons_mode", "NOT_SUPORTED" }, // Not sure if we can figure this one out
|
||||
{ "mq_dungeons_count", "NOT_SUPPORTED" }, // Slot data doesn't expose the master quest dungeons used
|
||||
{ "shuffle_interior_entrances", "NOT_SUPPORTED" }, // Mapping not in Slot Data
|
||||
{ "shuffle_grotto_entrances", "NOT_SUPPORTED" }, // Mapping not in Slot Data
|
||||
{ "shuffle_dungeon_entrances", "NOT_SUPPORTED" }, // Mapping not in Slot Data
|
||||
{ "shuffle_overworld_entrances", "NOT_SUPPORTED" }, // Mapping not in Slot Data
|
||||
{ "shuffle_bosses", "NOT_SUPPORTED" }, // Mapping not in Slot Data
|
||||
{ "key_rings", "Key Rings" }, // slot data not exposed when set to random, however may not matter if you just can just recieve the key ring, may only be needed for logic
|
||||
{ "enhance_map_compass", "NOT_SUPPORTED" }, // Can't find it in rando settings, may be a qol setting
|
||||
{ "shuffle_mapcompass", "Maps/Compasses" }, // NOT REQUIRED
|
||||
{ "shuffle_smallkeys", "Small Key Shuffle" }, // NOT REQUIRED
|
||||
{ "shuffle_hideoutkeys", "Gerudo Fortress Keys" }, // NOT REQUIRED
|
||||
{ "shuffle_bosskeys", "Boss Key Shuffle" }, // NOT REQUIRED
|
||||
{ "logic_rules", "Logic" }, // NOT REQUIRED
|
||||
{ "logic_no_night_tokens_without_suns_song", "Night Skulltula's Expect Sun's Song" }, // NOT REQUIRED
|
||||
{ "warp_songs", "NOT_SUPORTED" }, // slot data not exposed
|
||||
{ "shuffle_song_items", "Shuffle Songs" }, // NOT REQUIRED
|
||||
{ "shuffle_medigoron_carpet_salesman", "NOT_SUPPORTED" }, // NOT REQURIED, , Should set "Shuffle Merchants" option (This option also sets granny)
|
||||
{ "shuffle_frog_song_rupees", "Shuffle Frog Song Rupees" }, // NOT REQUIRED
|
||||
{ "shuffle_scrubs", "Scrubs Shuffle" }, // NOT REQUIRED
|
||||
{ "shuffle_child_trade", "NOT_SUPPORTED" }, // NOT REQUIRED
|
||||
{ "shuffle_freestanding_items", "NOT_SUPPORTED" }, // NOT REQUIRED
|
||||
{ "shuffle_pots", "Shuffle Pots" }, // NOT REQUIRED
|
||||
{ "shuffle_crates", "MAYBE_SUPPORTED_TODO" }, // Maybe Requred, TODO TEST
|
||||
{ "shuffle_cows", "Shuffle Cows" }, // NOT REQUIRED
|
||||
{ "shuffle_beehives", "Shuffle Beehives" }, // NOT REQUIRED
|
||||
{ "shuffle_kokiri_sword", "Shuffle Kokiri Sword" }, // NOT REQUIRED
|
||||
{ "shuffle_ocarinas", "Shuffle Ocarinas" }, // NOT REQUIRED
|
||||
{ "shuffle_gerudo_card", "Shuffle Gerudo Membership Card" }, //NOT REQUIRED
|
||||
{ "shuffle_beans", "NOT_SUPPORTED" }, // NOT REQUIRED, Should set "Shuffle Merchants" option (This option also sets granny)
|
||||
{ "starting_age", "Selected Starting Age" }, // should this also set "Seelcted Starting Age"
|
||||
{ "bombchus_in_logic", "NOT_SUPPORTED" }, // NOT REQUIRED, Probably Implemented as a trick
|
||||
{ "spawn_positions", "NOT_SUPPORTED" },
|
||||
{ "owl_drops", "NOT_SUPPORTRED" },
|
||||
{ "no_epona_race", "Skip Epona Race" },
|
||||
{ "skip_some_minigame_phases", "NOT_IMPLEMENTED" }, // should be under quality of life options
|
||||
{ "complete_mask_quest", "Complete Mask Quest" },
|
||||
{ "free_scarecrow", "Skip Scarecrow's Song" },
|
||||
{ "plant_beans", "NOT_SUPPORTRED" },
|
||||
{ "chicken_count", "Cuccos to return" },
|
||||
{ "big_poe_count", "Big Poe Target Count" },
|
||||
{ "fae_torch_count", "NOT_SUPPORTED" },
|
||||
{ "blue_fire_arrows", "Blue Fire Arrows" },
|
||||
{ "damage_multiplier", "Damage Multiplier" },
|
||||
{ "deadly_bonks", "NOT_SUPPORTED" },
|
||||
{ "starting_tod", "NOT_SUPPORTED" },
|
||||
{ "junk_ice_traps", "Ice Traps" }, // NOT REQUIRED
|
||||
{ "start_with_consumables", "CUSTOM_IMPLEMENTATION" }, // might be able to just set "Start with Stick Ammo" and "Start with Nut Ammo", TODO check starting consumables
|
||||
{ "adult_trade_start", "NOT_SUPPORTED" }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ class StaticData {
|
||||
static std::unordered_map<std::string_view, RandomizerGet> APitemToSoh;
|
||||
static std::unordered_map<std::string_view, RandomizerCheck> APcheckToSoh;
|
||||
static std::unordered_map<RandomizerCheck, std::string_view> SohCheckToAP;
|
||||
|
||||
static std::unordered_map<std::string_view, std::string_view> APsettingToHoSsetting;
|
||||
StaticData();
|
||||
~StaticData();
|
||||
};
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "soh/Enhancements/debugger/debugSaveEditor.h"
|
||||
#include "soh_assets.h"
|
||||
#include "assets/textures/parameter_static/parameter_static.h"
|
||||
#include "soh/Enhancements/randomizer/archipelago.h"
|
||||
|
||||
extern "C" {
|
||||
extern SaveContext gSaveContext;
|
||||
@@ -347,6 +348,7 @@ void HandleDragAndDrop(std::vector<SplitObject>& objectList, int targetIndex, co
|
||||
void TimeSplitCompleteSplits() {
|
||||
gSaveContext.ship.stats.itemTimestamp[TIMESTAMP_DEFEAT_GANON] = GAMEPLAYSTAT_TOTAL_TIME;
|
||||
gSaveContext.ship.stats.gameComplete = true;
|
||||
ArchipelagoClient::getInstance().send_game_won();
|
||||
}
|
||||
|
||||
void TimeSplitsSkipSplit(uint32_t index) {
|
||||
|
||||
Reference in New Issue
Block a user