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
|
## 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.
|
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.
|
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
|
## Website
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,7 @@
|
|||||||
#include "objects/object_link_child/object_link_child.h"
|
#include "objects/object_link_child/object_link_child.h"
|
||||||
#include "soh_assets.h"
|
#include "soh_assets.h"
|
||||||
#include "kaleido.h"
|
#include "kaleido.h"
|
||||||
|
#include "soh/Enhancements/randomizer/archipelago.h"
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include <z64.h>
|
#include <z64.h>
|
||||||
@@ -876,6 +877,7 @@ void RegisterBossDefeatTimestamps() {
|
|||||||
case ACTOR_BOSS_GANON2:
|
case ACTOR_BOSS_GANON2:
|
||||||
gSaveContext.ship.stats.itemTimestamp[TIMESTAMP_DEFEAT_GANON] = GAMEPLAYSTAT_TOTAL_TIME;
|
gSaveContext.ship.stats.itemTimestamp[TIMESTAMP_DEFEAT_GANON] = GAMEPLAYSTAT_TOTAL_TIME;
|
||||||
gSaveContext.ship.stats.gameComplete = true;
|
gSaveContext.ship.stats.gameComplete = true;
|
||||||
|
ArchipelagoClient::getInstance().send_game_won();
|
||||||
break;
|
break;
|
||||||
case ACTOR_BOSS_GANONDROF:
|
case ACTOR_BOSS_GANONDROF:
|
||||||
gSaveContext.ship.stats.itemTimestamp[TIMESTAMP_DEFEAT_PHANTOM_GANON] = GAMEPLAYSTAT_TOTAL_TIME;
|
gSaveContext.ship.stats.itemTimestamp[TIMESTAMP_DEFEAT_PHANTOM_GANON] = GAMEPLAYSTAT_TOTAL_TIME;
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ auto SubscribeToSlotData() {
|
|||||||
|
|
||||||
ArchipelagoClient::ArchipelagoClient() {
|
ArchipelagoClient::ArchipelagoClient() {
|
||||||
ItemRecievedCallback = nullptr;
|
ItemRecievedCallback = nullptr;
|
||||||
|
game_won = false;
|
||||||
|
|
||||||
namespace apc = AP_Client_consts;
|
namespace apc = AP_Client_consts;
|
||||||
CVarSetInteger("archipelago_connected", 0);
|
CVarSetInteger("archipelago_connected", 0);
|
||||||
@@ -64,71 +65,71 @@ void registerSlotCallbacks() {
|
|||||||
SubscribeToSlotData<"bridge_medallions">();
|
SubscribeToSlotData<"bridge_medallions">();
|
||||||
SubscribeToSlotData<"bridge_rewards">();
|
SubscribeToSlotData<"bridge_rewards">();
|
||||||
SubscribeToSlotData<"bridge_tokens">();
|
SubscribeToSlotData<"bridge_tokens">();
|
||||||
SubscribeToSlotData<"bridge_hearts">();
|
// SubscribeToSlotData<"bridge_hearts">();
|
||||||
SubscribeToSlotData<"shuffle_ganon_bosskey">();
|
SubscribeToSlotData<"shuffle_ganon_bosskey">();
|
||||||
SubscribeToSlotData<"ganon_bosskey_medallions">();
|
SubscribeToSlotData<"ganon_bosskey_medallions">();
|
||||||
SubscribeToSlotData<"ganon_bosskey_stones">();
|
SubscribeToSlotData<"ganon_bosskey_stones">();
|
||||||
SubscribeToSlotData<"ganon_bosskey_rewards">();
|
SubscribeToSlotData<"ganon_bosskey_rewards">();
|
||||||
SubscribeToSlotData<"ganon_bosskey_tokens">();
|
SubscribeToSlotData<"ganon_bosskey_tokens">();
|
||||||
SubscribeToSlotData<"ganon_bosskey_hearts">();
|
// SubscribeToSlotData<"ganon_bosskey_hearts">();
|
||||||
SubscribeToSlotData<"trials">();
|
SubscribeToSlotData<"trials">();
|
||||||
SubscribeToSlotData<"triforce_hunt">();
|
SubscribeToSlotData<"triforce_hunt">();
|
||||||
SubscribeToSlotData<"triforce_goal">();
|
SubscribeToSlotData<"triforce_goal">();
|
||||||
SubscribeToSlotData<"extra_triforce_percentage">();
|
// SubscribeToSlotData<"extra_triforce_percentage">();
|
||||||
SubscribeToSlotData<"shopsanity">();
|
// SubscribeToSlotData<"shopsanity">();
|
||||||
SubscribeToSlotData<"shop_slots">();
|
// SubscribeToSlotData<"shop_slots">();
|
||||||
SubscribeToSlotData<"shopsanity_prices">();
|
SubscribeToSlotData<"shopsanity_prices">();
|
||||||
SubscribeToSlotData<"tokensanity">();
|
// SubscribeToSlotData<"tokensanity">();
|
||||||
SubscribeToSlotData<"dungeon_shortcuts">();
|
// SubscribeToSlotData<"dungeon_shortcuts">();
|
||||||
SubscribeToSlotData<"mq_dungeons_mode">();
|
// SubscribeToSlotData<"mq_dungeons_mode">();
|
||||||
SubscribeToSlotData<"mq_dungeons_count">();
|
// SubscribeToSlotData<"mq_dungeons_count">();
|
||||||
SubscribeToSlotData<"shuffle_interior_entrances">();
|
// SubscribeToSlotData<"shuffle_interior_entrances">();
|
||||||
SubscribeToSlotData<"shuffle_grotto_entrances">();
|
// SubscribeToSlotData<"shuffle_grotto_entrances">();
|
||||||
SubscribeToSlotData<"shuffle_dungeon_entrances">();
|
// SubscribeToSlotData<"shuffle_dungeon_entrances">();
|
||||||
SubscribeToSlotData<"shuffle_overworld_entrances">();
|
// SubscribeToSlotData<"shuffle_overworld_entrances">();
|
||||||
SubscribeToSlotData<"shuffle_bosses">();
|
// SubscribeToSlotData<"shuffle_bosses">();
|
||||||
SubscribeToSlotData<"key_rings">();
|
// SubscribeToSlotData<"key_rings">();
|
||||||
SubscribeToSlotData<"enhance_map_compass">();
|
// SubscribeToSlotData<"enhance_map_compass">();
|
||||||
SubscribeToSlotData<"shuffle_mapcompass">();
|
// SubscribeToSlotData<"shuffle_mapcompass">();
|
||||||
SubscribeToSlotData<"shuffle_smallkeys">();
|
// SubscribeToSlotData<"shuffle_smallkeys">();
|
||||||
SubscribeToSlotData<"shuffle_hideoutkeys">();
|
// SubscribeToSlotData<"shuffle_hideoutkeys">();
|
||||||
SubscribeToSlotData<"shuffle_bosskeys">();
|
// SubscribeToSlotData<"shuffle_bosskeys">();
|
||||||
SubscribeToSlotData<"logic_rules">();
|
// SubscribeToSlotData<"logic_rules">();
|
||||||
SubscribeToSlotData<"logic_no_night_tokens_without_suns_song">();
|
// SubscribeToSlotData<"logic_no_night_tokens_without_suns_song">();
|
||||||
SubscribeToSlotData<"warp_songs">();
|
// SubscribeToSlotData<"warp_songs">();
|
||||||
SubscribeToSlotData<"shuffle_song_items">();
|
// SubscribeToSlotData<"shuffle_song_items">();
|
||||||
SubscribeToSlotData<"shuffle_medigoron_carpet_salesman">();
|
// SubscribeToSlotData<"shuffle_medigoron_carpet_salesman">();
|
||||||
SubscribeToSlotData<"shuffle_frog_song_rupees">();
|
// SubscribeToSlotData<"shuffle_frog_song_rupees">();
|
||||||
SubscribeToSlotData<"shuffle_scrubs">();
|
// SubscribeToSlotData<"shuffle_scrubs">();
|
||||||
SubscribeToSlotData<"shuffle_child_trade">();
|
// SubscribeToSlotData<"shuffle_child_trade">();
|
||||||
SubscribeToSlotData<"shuffle_freestanding_items">();
|
// SubscribeToSlotData<"shuffle_freestanding_items">();
|
||||||
SubscribeToSlotData<"shuffle_pots">();
|
// SubscribeToSlotData<"shuffle_pots">();
|
||||||
SubscribeToSlotData<"shuffle_crates">();
|
// SubscribeToSlotData<"shuffle_crates">();
|
||||||
SubscribeToSlotData<"shuffle_cows">();
|
// SubscribeToSlotData<"shuffle_cows">();
|
||||||
SubscribeToSlotData<"shuffle_beehives">();
|
// SubscribeToSlotData<"shuffle_beehives">();
|
||||||
SubscribeToSlotData<"shuffle_kokiri_sword">();
|
// SubscribeToSlotData<"shuffle_kokiri_sword">();
|
||||||
SubscribeToSlotData<"shuffle_ocarinas">();
|
// SubscribeToSlotData<"shuffle_ocarinas">();
|
||||||
SubscribeToSlotData<"shuffle_gerudo_card">();
|
// SubscribeToSlotData<"shuffle_gerudo_card">();
|
||||||
SubscribeToSlotData<"shuffle_beans">();
|
// SubscribeToSlotData<"shuffle_beans">();
|
||||||
SubscribeToSlotData<"starting_age">();
|
SubscribeToSlotData<"starting_age">();
|
||||||
SubscribeToSlotData<"bombchus_in_logic">();
|
// SubscribeToSlotData<"bombchus_in_logic">();
|
||||||
SubscribeToSlotData<"spawn_positions">();
|
// SubscribeToSlotData<"spawn_positions">();
|
||||||
SubscribeToSlotData<"owl_drops">();
|
// SubscribeToSlotData<"owl_drops">();
|
||||||
SubscribeToSlotData<"no_epona_race">();
|
SubscribeToSlotData<"no_epona_race">();
|
||||||
SubscribeToSlotData<"skip_some_minigame_phases">();
|
// SubscribeToSlotData<"skip_some_minigame_phases">();
|
||||||
SubscribeToSlotData<"complete_mask_quest">();
|
SubscribeToSlotData<"complete_mask_quest">();
|
||||||
SubscribeToSlotData<"free_scarecrow">();
|
SubscribeToSlotData<"free_scarecrow">();
|
||||||
SubscribeToSlotData<"plant_beans">();
|
// SubscribeToSlotData<"plant_beans">();
|
||||||
SubscribeToSlotData<"chicken_count">();
|
SubscribeToSlotData<"chicken_count">();
|
||||||
SubscribeToSlotData<"big_poe_count">();
|
SubscribeToSlotData<"big_poe_count">();
|
||||||
SubscribeToSlotData<"fae_torch_count">();
|
// SubscribeToSlotData<"fae_torch_count">();
|
||||||
SubscribeToSlotData<"blue_fire_arrows">();
|
SubscribeToSlotData<"blue_fire_arrows">();
|
||||||
SubscribeToSlotData<"damage_multiplier">();
|
SubscribeToSlotData<"damage_multiplier">();
|
||||||
SubscribeToSlotData<"deadly_bonks">();
|
// SubscribeToSlotData<"deadly_bonks">();
|
||||||
SubscribeToSlotData<"starting_tod">();
|
// SubscribeToSlotData<"starting_tod">();
|
||||||
SubscribeToSlotData<"junk_ice_traps">();
|
// SubscribeToSlotData<"junk_ice_traps">();
|
||||||
SubscribeToSlotData<"start_with_consumables">();
|
SubscribeToSlotData<"start_with_consumables">();
|
||||||
SubscribeToSlotData<"adult_trade_start">();
|
// SubscribeToSlotData<"adult_trade_start">();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ArchipelagoClient::start_client() {
|
bool ArchipelagoClient::start_client() {
|
||||||
@@ -193,6 +194,9 @@ bool ArchipelagoClient::isConnected() {
|
|||||||
|
|
||||||
void ArchipelagoClient::check_location(RandomizerCheck SoH_check_id) {
|
void ArchipelagoClient::check_location(RandomizerCheck SoH_check_id) {
|
||||||
std::string_view ap_name = Rando::StaticData::SohCheckToAP[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));
|
int64_t ap_item_id = CheckNameToId(std::string(ap_name));
|
||||||
SPDLOG_TRACE("Checked: {}({}), sending to AP server", ap_name, ap_item_id);
|
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) {
|
void ArchipelagoClient::on_item_recieved(int64_t recieved_item_id, bool notify_player) {
|
||||||
// call each callback
|
// call each callback
|
||||||
SPDLOG_TRACE("Trying to give rupie...");
|
|
||||||
std::string item_name = getAPitemName(recieved_item_id);
|
std::string item_name = getAPitemName(recieved_item_id);
|
||||||
ArchipelagoClient& ap_client = ArchipelagoClient::getInstance();
|
ArchipelagoClient& ap_client = ArchipelagoClient::getInstance();
|
||||||
if(ap_client.ItemRecievedCallback) {
|
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????
|
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;
|
getInstance().scouted_items = network_items;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ArchipelagoClient::send_game_won() {
|
||||||
|
if(!game_won) {
|
||||||
|
AP_StoryComplete();
|
||||||
|
game_won = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
char* ArchipelagoClient::get_server_address_buff() {
|
char* ArchipelagoClient::get_server_address_buff() {
|
||||||
return server_address;
|
return server_address;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,6 +49,8 @@ class ArchipelagoClient {
|
|||||||
// todo move me back down when done testing
|
// todo move me back down when done testing
|
||||||
static void on_item_recieved(int64_t recieved_item_id, bool notify_player);
|
static void on_item_recieved(int64_t recieved_item_id, bool notify_player);
|
||||||
|
|
||||||
|
void send_game_won();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
ArchipelagoClient();
|
ArchipelagoClient();
|
||||||
|
|
||||||
@@ -62,6 +64,8 @@ class ArchipelagoClient {
|
|||||||
char slot_name[AP_Client_consts::MAX_PLAYER_NAME_LENGHT];
|
char slot_name[AP_Client_consts::MAX_PLAYER_NAME_LENGHT];
|
||||||
char password[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::map<std::string, int> slot_data;
|
||||||
std::set<int64_t> locations;
|
std::set<int64_t> locations;
|
||||||
std::vector<AP_NetworkItem> scouted_items;
|
std::vector<AP_NetworkItem> scouted_items;
|
||||||
|
|||||||
@@ -366,6 +366,7 @@ void Context::ParseArchipelago() {
|
|||||||
mSpoilerLoaded = false;
|
mSpoilerLoaded = false;
|
||||||
|
|
||||||
ArchipelagoClient& ap_client = ArchipelagoClient::getInstance();
|
ArchipelagoClient& ap_client = ArchipelagoClient::getInstance();
|
||||||
|
Rando::Settings::GetInstance()->ParseArchipelago(ap_client.get_slot_data());
|
||||||
ParseArchipelagoItemsLocations(ap_client.get_scouted_items());
|
ParseArchipelagoItemsLocations(ap_client.get_scouted_items());
|
||||||
|
|
||||||
// lets see if counting AP_loaded as spoiler loaded does the trick
|
// 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) {
|
void Context::ParseItemLocationsJson(nlohmann::json spoilerFileJson) {
|
||||||
|
// first fill all the items with their vanilla location
|
||||||
nlohmann::json locationsJson = spoilerFileJson["locations"];
|
nlohmann::json locationsJson = spoilerFileJson["locations"];
|
||||||
for (auto it = locationsJson.begin(); it != locationsJson.end(); ++it) {
|
for (auto it = locationsJson.begin(); it != locationsJson.end(); ++it) {
|
||||||
RandomizerCheck rc = StaticData::locationNameToEnum[it.key()];
|
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
|
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) {
|
for(const AP_NetworkItem& ap_item: scouted_items) {
|
||||||
const RandomizerCheck rc = StaticData::APcheckToSoh.find(ap_item.locationName)->second;
|
const RandomizerCheck rc = StaticData::APcheckToSoh.find(ap_item.locationName)->second;
|
||||||
|
|
||||||
|
if(rc == RC_KF_MIDOS_TOP_RIGHT_CHEST) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if(playerId == ap_item.player) {
|
if(playerId == ap_item.player) {
|
||||||
// our item
|
// our item
|
||||||
SPDLOG_TRACE("Populated item {} at location {}", ap_item.itemName, ap_item.locationName);
|
SPDLOG_TRACE("Populated item {} at location {}", ap_item.itemName, ap_item.locationName);
|
||||||
|
|||||||
@@ -126,7 +126,8 @@ class Context {
|
|||||||
RandomizerArea GetAreaFromString(std::string str);
|
RandomizerArea GetAreaFromString(std::string str);
|
||||||
|
|
||||||
void ParseArchipelago();
|
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.
|
* @brief Get the hash for the current seed.
|
||||||
|
|||||||
@@ -39,6 +39,7 @@
|
|||||||
#include "soh/util.h"
|
#include "soh/util.h"
|
||||||
#include "fishsanity.h"
|
#include "fishsanity.h"
|
||||||
#include "randomizerTypes.h"
|
#include "randomizerTypes.h"
|
||||||
|
#include "archipelago.h"
|
||||||
|
|
||||||
extern std::map<RandomizerCheckArea, std::string> rcAreaNames;
|
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)) {
|
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.itemTimestamp[TIMESTAMP_TRIFORCE_COMPLETED] = GAMEPLAYSTAT_TOTAL_TIME;
|
||||||
gSaveContext.ship.stats.gameComplete = 1;
|
gSaveContext.ship.stats.gameComplete = 1;
|
||||||
|
ArchipelagoClient::getInstance().send_game_won();
|
||||||
Flags_SetRandomizerInf(RAND_INF_GRANT_GANONS_BOSSKEY);
|
Flags_SetRandomizerInf(RAND_INF_GRANT_GANONS_BOSSKEY);
|
||||||
Play_PerformSave(play);
|
Play_PerformSave(play);
|
||||||
GameInteractor_SetTriforceHuntCreditsWarpActive(true);
|
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() {
|
void Settings::ReloadOptions() {
|
||||||
for (int i = 0; i < RSK_MAX; i++) {
|
for (int i = 0; i < RSK_MAX; i++) {
|
||||||
mOptions[i].SetFromCVar();
|
mOptions[i].SetFromCVar();
|
||||||
|
|||||||
@@ -112,6 +112,8 @@ class Settings {
|
|||||||
* @param spoilerFileJson
|
* @param spoilerFileJson
|
||||||
*/
|
*/
|
||||||
void ParseJson(nlohmann::json spoilerFileJson);
|
void ParseJson(nlohmann::json spoilerFileJson);
|
||||||
|
|
||||||
|
void ParseArchipelago(const std::map<std::string, int>& slot_data);
|
||||||
std::map<RandomizerArea, std::vector<RandomizerTrick>> mTricksByArea = {};
|
std::map<RandomizerArea, std::vector<RandomizerTrick>> mTricksByArea = {};
|
||||||
void ReloadOptions();
|
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, RandomizerGet> StaticData::APitemToSoh = generate_APitemToSoh_mapping();
|
||||||
std::unordered_map<std::string_view, RandomizerCheck> StaticData::APcheckToSoh = generate_APcheckToSoh_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<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, RandomizerGet> APitemToSoh;
|
||||||
static std::unordered_map<std::string_view, RandomizerCheck> APcheckToSoh;
|
static std::unordered_map<std::string_view, RandomizerCheck> APcheckToSoh;
|
||||||
static std::unordered_map<RandomizerCheck, std::string_view> SohCheckToAP;
|
static std::unordered_map<RandomizerCheck, std::string_view> SohCheckToAP;
|
||||||
|
static std::unordered_map<std::string_view, std::string_view> APsettingToHoSsetting;
|
||||||
StaticData();
|
StaticData();
|
||||||
~StaticData();
|
~StaticData();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
#include "soh/Enhancements/debugger/debugSaveEditor.h"
|
#include "soh/Enhancements/debugger/debugSaveEditor.h"
|
||||||
#include "soh_assets.h"
|
#include "soh_assets.h"
|
||||||
#include "assets/textures/parameter_static/parameter_static.h"
|
#include "assets/textures/parameter_static/parameter_static.h"
|
||||||
|
#include "soh/Enhancements/randomizer/archipelago.h"
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
extern SaveContext gSaveContext;
|
extern SaveContext gSaveContext;
|
||||||
@@ -347,6 +348,7 @@ void HandleDragAndDrop(std::vector<SplitObject>& objectList, int targetIndex, co
|
|||||||
void TimeSplitCompleteSplits() {
|
void TimeSplitCompleteSplits() {
|
||||||
gSaveContext.ship.stats.itemTimestamp[TIMESTAMP_DEFEAT_GANON] = GAMEPLAYSTAT_TOTAL_TIME;
|
gSaveContext.ship.stats.itemTimestamp[TIMESTAMP_DEFEAT_GANON] = GAMEPLAYSTAT_TOTAL_TIME;
|
||||||
gSaveContext.ship.stats.gameComplete = true;
|
gSaveContext.ship.stats.gameComplete = true;
|
||||||
|
ArchipelagoClient::getInstance().send_game_won();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TimeSplitsSkipSplit(uint32_t index) {
|
void TimeSplitsSkipSplit(uint32_t index) {
|
||||||
|
|||||||
Reference in New Issue
Block a user