mirror of
https://github.com/TwilitRealm/dusklight
synced 2026-06-24 15:43:13 -04:00
add inventory resetting on server connect, properly handle getting new items from locations, integrate some of archi into new rando ui stuff
For the most part, solo archipelago runs seem to work fine now using the original TP apworld, but im sure there are plenty of weird issues still that need to be handled.
This commit is contained in:
@@ -1128,6 +1128,15 @@ void dComIfGs_setWarpItemData(char const* stage, cXyz pos, s16 angle, s8 roomNo,
|
||||
u8 param_5);
|
||||
BOOL dComIfGs_isStageSwitch(int i_stageNo, int i_no);
|
||||
BOOL dComIfGs_isStageTbox(int i_stageNo, int i_no);
|
||||
#if TARGET_PC
|
||||
void dComIfGs_onStageTbox(int i_stageNo, int i_no);
|
||||
void dComIfGs_offStageTbox(int i_stageNo, int i_no);
|
||||
|
||||
void dComIfGs_onStageItem(int i_stageNo, int i_no);
|
||||
void dComIfGs_offStageItem(int i_stageNo, int i_no);
|
||||
|
||||
#endif
|
||||
|
||||
void dComIfGs_onStageSwitch(int i_stageNo, int i_no);
|
||||
void dComIfGs_offStageSwitch(int i_stageNo, int i_no);
|
||||
BOOL dComIfGs_isStageSwitch(int i_stageNo, int i_no);
|
||||
@@ -1990,6 +1999,23 @@ inline void dComIfGs_onRegionFlag(int i_stageNo, int i_no) {
|
||||
const int shift = i_no % 8;
|
||||
regionFlags[offset] |= (0x80 >> shift);
|
||||
}
|
||||
|
||||
inline void dComIfGs_onSaveTbox(int i_stageNo, int i_no) {
|
||||
g_dComIfG_gameInfo.info.getSavedata().getSave(i_stageNo).getBit().onTbox(i_no);
|
||||
}
|
||||
|
||||
inline void dComIfGs_offSaveTbox(int i_stageNo, int i_no) {
|
||||
g_dComIfG_gameInfo.info.getSavedata().getSave(i_stageNo).getBit().offTbox(i_no);
|
||||
}
|
||||
|
||||
inline void dComIfGs_onSaveItem(int i_no) {
|
||||
g_dComIfG_gameInfo.info.getMemory().getBit().onItem(i_no);
|
||||
}
|
||||
|
||||
inline void dComIfGs_offSaveItem(int i_no) {
|
||||
g_dComIfG_gameInfo.info.getMemory().getBit().offItem(i_no);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
inline BOOL dComIfGs_isSaveTbox(int i_stageNo, int i_no) {
|
||||
@@ -2456,6 +2482,12 @@ inline void dComIfGs_onItem(int i_bitNo, int i_roomNo) {
|
||||
g_dComIfG_gameInfo.info.onItem(i_bitNo, i_roomNo);
|
||||
}
|
||||
|
||||
#if TARGET_PC
|
||||
inline void dComIfGs_offItem(int i_bitNo, int i_roomNo) {
|
||||
g_dComIfG_gameInfo.info.offItem(i_bitNo, i_roomNo);
|
||||
}
|
||||
#endif
|
||||
|
||||
inline bool dComIfGs_isItem(int i_bitNo, int i_roomNo) {
|
||||
return g_dComIfG_gameInfo.info.isItem(i_bitNo, i_roomNo);
|
||||
}
|
||||
|
||||
@@ -993,6 +993,9 @@ public:
|
||||
BOOL isSwitch(int i_no, int i_roomNo) const;
|
||||
BOOL revSwitch(int i_no, int i_roomNo);
|
||||
void onItem(int i_no, int i_roomNo);
|
||||
#if TARGET_PC
|
||||
void offItem(int i_no, int i_roomNo);
|
||||
#endif
|
||||
BOOL isItem(int i_no, int i_roomNo) const;
|
||||
void onActor(int i_no, int i_roomNo);
|
||||
void offActor(int i_no, int i_roomNo);
|
||||
|
||||
@@ -2093,6 +2093,41 @@ BOOL dComIfGs_isStageTbox(int i_stageNo, int i_no) {
|
||||
}
|
||||
}
|
||||
|
||||
#if TARGET_PC
|
||||
void dComIfGs_onStageTbox(int i_stageNo, int i_no) {
|
||||
if (dComIfGp_getStageStagInfo() && i_stageNo == dStage_stagInfo_GetSaveTbl(dComIfGp_getStageStagInfo())) {
|
||||
dComIfGs_onTbox(i_no);
|
||||
} else {
|
||||
dComIfGs_onSaveTbox(i_stageNo, i_no);
|
||||
}
|
||||
}
|
||||
void dComIfGs_offStageTbox(int i_stageNo, int i_no) {
|
||||
if (dComIfGp_getStageStagInfo() && i_stageNo == dStage_stagInfo_GetSaveTbl(dComIfGp_getStageStagInfo())) {
|
||||
dComIfGs_offTbox(i_no);
|
||||
} else {
|
||||
dComIfGs_offSaveTbox(i_stageNo, i_no);
|
||||
}
|
||||
}
|
||||
|
||||
void dComIfGs_onStageItem(int i_stageNo, int i_no) {
|
||||
if (dComIfGp_getStageStagInfo() && i_stageNo == dStage_stagInfo_GetSaveTbl(dComIfGp_getStageStagInfo())) {
|
||||
dComIfGs_onItem(i_no, -1);
|
||||
} else {
|
||||
dComIfGs_onSaveItem(i_no);
|
||||
}
|
||||
}
|
||||
|
||||
void dComIfGs_offStageItem(int i_stageNo, int i_no) {
|
||||
if (dComIfGp_getStageStagInfo() && i_stageNo == dStage_stagInfo_GetSaveTbl(dComIfGp_getStageStagInfo())) {
|
||||
// Need to subtract 0x80 (MEMORY_ITEM constant in d_save.cpp) because the above function does it
|
||||
dComIfGs_offItem(i_stageNo, i_no - 0x80);
|
||||
} else {
|
||||
dComIfGs_offSaveItem(i_no);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void dComIfGs_onStageSwitch(int i_stageNo, int i_no) {
|
||||
#if TARGET_PC
|
||||
// Avoid trying to get the save table if stag info is NULL
|
||||
@@ -2107,7 +2142,12 @@ void dComIfGs_onStageSwitch(int i_stageNo, int i_no) {
|
||||
}
|
||||
|
||||
void dComIfGs_offStageSwitch(int i_stageNo, int i_no) {
|
||||
#if TARGET_PC
|
||||
// Avoid trying to get the save table if stag info is NULL
|
||||
if (dComIfGp_getStageStagInfo() && i_stageNo == dStage_stagInfo_GetSaveTbl(dComIfGp_getStageStagInfo())) {
|
||||
#else
|
||||
if (i_stageNo == dStage_stagInfo_GetSaveTbl(dComIfGp_getStageStagInfo())) {
|
||||
#endif
|
||||
dComIfGs_offSwitch(i_no, -1);
|
||||
}
|
||||
|
||||
|
||||
+28
-12
@@ -23,6 +23,8 @@
|
||||
#include "m_Do/m_Do_graphic.h"
|
||||
#include <cstring>
|
||||
|
||||
#include "dusk/archipelago/archipelago_context.hpp"
|
||||
|
||||
#if TARGET_PC
|
||||
#include "dusk/config.hpp"
|
||||
#include "dusk/menu_pointer.h"
|
||||
@@ -1037,18 +1039,23 @@ void dFile_select_c::dataSelectStart() {
|
||||
makeRecInfo(mSelectNum);
|
||||
|
||||
#if TARGET_PC
|
||||
// Load the randomizer seed if one is tied to this file
|
||||
auto curFileSeedHash = dusk::getSettings().randomizer.seedHashes.at(mSelectNum).getValue();
|
||||
// If this is a vanilla file, clear rando data structures
|
||||
if (curFileSeedHash.empty()) {
|
||||
g_randomizerState = RandomizerState();
|
||||
randomizer_GetContext() = RandomizerContext();
|
||||
}
|
||||
// Reset randomizer state if we're switching to a different file
|
||||
else if (curFileSeedHash != randomizer_GetContext().mHash || g_randomizerState.mFileNum != mSelectNum) {
|
||||
g_randomizerState = RandomizerState();
|
||||
randomizer_GetContext() = RandomizerContext();
|
||||
randomizer_GetContext().LoadFromHash(curFileSeedHash);
|
||||
// TODO: not this please D:
|
||||
if (dusk::archi::ArchipelagoContext::IsConnected()) {
|
||||
dusk::archi::ArchipelagoContext::GenerateLocalWorldData();
|
||||
}else {
|
||||
// Load the randomizer seed if one is tied to this file
|
||||
auto curFileSeedHash = dusk::getSettings().randomizer.seedHashes.at(mSelectNum).getValue();
|
||||
// If this is a vanilla file, clear rando data structures
|
||||
if (curFileSeedHash.empty()) {
|
||||
g_randomizerState = RandomizerState();
|
||||
randomizer_GetContext() = RandomizerContext();
|
||||
}
|
||||
// Reset randomizer state if we're switching to a different file
|
||||
else if (curFileSeedHash != randomizer_GetContext().mHash || g_randomizerState.mFileNum != mSelectNum) {
|
||||
g_randomizerState = RandomizerState();
|
||||
randomizer_GetContext() = RandomizerContext();
|
||||
randomizer_GetContext().LoadFromHash(curFileSeedHash);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1401,6 +1408,15 @@ void dFile_select_c::selectDataPlayTypeMove() {
|
||||
modal.hide(true);
|
||||
dusk::ui::push_document(std::make_unique<dusk::ui::FileSelectRandomizerWindow>(this));
|
||||
}},
|
||||
// If Archipelago is selected, open archipelago settings window
|
||||
dusk::ui::ModalAction{
|
||||
.label = "Archipelago",
|
||||
.onPressed = [this](dusk::ui::Modal& modal) {
|
||||
mDusk.mBackToFileSelect = false;
|
||||
mDoAud_seStartMenu(Z2SE_SY_CURSOR_OK);
|
||||
modal.hide(true);
|
||||
dusk::archi::ArchipelagoContext::GenerateLocalWorldData();
|
||||
}},
|
||||
},
|
||||
// If we dismiss this modal, go back to file selection
|
||||
.onDismiss = [this](dusk::ui::Modal& modal) {
|
||||
|
||||
+2
-3
@@ -553,6 +553,7 @@ void execItemGet(u8 i_itemNo) {
|
||||
if (randomizer_IsActive()) {
|
||||
i_itemNo = verifyProgressiveItem(i_itemNo);
|
||||
g_randomizerState.mUpdateTracker = true;
|
||||
dusk::archi::ArchipelagoContext::SetNeedUpdateLocations(true);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -2184,9 +2185,7 @@ void item_func_MIRROR_PIECE_1() {
|
||||
}
|
||||
|
||||
void item_func_ARCHIPELAGO_ITEM() {
|
||||
if (randomizer_IsActive()) {
|
||||
dusk::archi::ArchipelagoContext::UpdateCheckedLocations();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -2068,6 +2068,32 @@ void dSv_info_c::onItem(int i_no, int i_roomNo) {
|
||||
}
|
||||
}
|
||||
|
||||
#if TARGET_PC
|
||||
void dSv_info_c::offItem(int i_no, int i_roomNo) {
|
||||
JUT_ASSERT(4398, (0 <= i_no && i_no < (MEMORY_ITEM+ DAN_ITEM+ ZONE_ITEM+ ONEZONE_ITEM)) || i_no == -1 || i_no == 255);
|
||||
|
||||
if (i_no == -1 || i_no == 255) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (i_no < MEMORY_ITEM) {
|
||||
mDan.offItem(i_no);
|
||||
} else if (i_no < (MEMORY_ITEM + DAN_ITEM)) {
|
||||
mMemory.getBit().offItem(i_no - MEMORY_ITEM);
|
||||
} else {
|
||||
JUT_ASSERT(4414, 0 <= i_roomNo && i_roomNo < 64);
|
||||
int zoneNo = dComIfGp_roomControl_getZoneNo(i_roomNo);
|
||||
JUT_ASSERT(4416, 0 <= zoneNo && zoneNo < ZONE_MAX);
|
||||
|
||||
if (i_no < (MEMORY_ITEM + DAN_ITEM + ZONE_ITEM)) {
|
||||
mZone[zoneNo].getBit().offItem(i_no - (MEMORY_ITEM + DAN_ITEM));
|
||||
} else {
|
||||
mZone[zoneNo].getBit().offOneItem(i_no - (MEMORY_ITEM + DAN_ITEM + ZONE_ITEM));
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
BOOL dSv_info_c::isItem(int i_no, int i_roomNo) const {
|
||||
JUT_ASSERT(4488, (0 <= i_no && i_no < (MEMORY_ITEM+ DAN_ITEM+ ZONE_ITEM+ ONEZONE_ITEM)) || i_no == -1 || i_no == 255);
|
||||
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
#include "dusk/config.hpp"
|
||||
#include "dusk/logging.h"
|
||||
#include "dusk/randomizer/game/tools.h"
|
||||
#include "dusk/randomizer/game/verify_item_functions.h"
|
||||
#include "dusk/randomizer/generator/logic/hints.hpp"
|
||||
#include "dusk/ui/rando_config.hpp"
|
||||
#include "dusk/ui/ui.hpp"
|
||||
|
||||
namespace dusk::archi
|
||||
@@ -39,7 +42,7 @@ static auto sArchiSettingToDusklight = std::to_array<SettingsNameConvert>({
|
||||
{"lanayru_twilight_cleared", "Lanayru Twilight Cleared"},
|
||||
{"skip_mdh", "Skip Midna's Desparate Hour"},
|
||||
{"open_map", "Unlock Map Regions"},
|
||||
{"increase_wallet", "Increase Wallet Capacity"},
|
||||
{"increase_wallet", "Logic Increase Wallet Capacity"},
|
||||
{"transform_anywhere", "Logic Transform Anywhere"},
|
||||
{"bonks_do_damage", "Bonks Do Damage"},
|
||||
{"skip_lakebed_entrance", "Lakebed Does Not Require Water Bombs"},
|
||||
@@ -128,6 +131,20 @@ void ArchipelagoContext::LoadTempItemInfo() {
|
||||
itemName
|
||||
};
|
||||
}
|
||||
|
||||
// add temporary replacement IDs for items not included in the base rando
|
||||
|
||||
m_apItemToGameItem[16] = { // Water Bombs (3)
|
||||
0x16,
|
||||
randomizer::logic::item::Importance::JUNK,
|
||||
"Water Bombs 5"
|
||||
};
|
||||
|
||||
m_apItemToGameItem[20] = { // Bomblings (3)
|
||||
0x1A,
|
||||
randomizer::logic::item::Importance::JUNK,
|
||||
"Bomblings 5"
|
||||
};
|
||||
}
|
||||
|
||||
void ArchipelagoContext::LoadTempLocationInfo() {
|
||||
@@ -164,15 +181,19 @@ void ArchipelagoContext::itemRecvImpl(int id, bool notify) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_isAllowUpdateLocations = true; // guards against triggering UpdateCheckedLocations
|
||||
|
||||
auto& item = m_apItemToGameItem[id];
|
||||
|
||||
DuskLog.info("[AP] Adding Item: {}", item.itemName);
|
||||
|
||||
if (item.importance == randomizer::logic::item::Importance::MAJOR) {
|
||||
g_randomizerState.addItemToEventQueue(item.itemId);
|
||||
if (notify && item.importance == randomizer::logic::item::Importance::MAJOR) {
|
||||
DuskLog.info("[AP] Adding Item: {}", item.itemName);
|
||||
g_randomizerState.addItemToEventQueue(verifyProgressiveItem(item.itemId));
|
||||
}else {
|
||||
DuskLog.info("[AP] Silently Adding Item: {}", item.itemName);
|
||||
execItemGet(item.itemId);
|
||||
}
|
||||
|
||||
m_isAllowUpdateLocations = false;
|
||||
}
|
||||
|
||||
int ArchipelagoContext::getItemIdFromApId(int apId) {
|
||||
@@ -194,9 +215,7 @@ std::string ArchipelagoContext::getLocationNameFromApId(int apId) const {
|
||||
return "";
|
||||
}
|
||||
|
||||
ArchipelagoContext::ArchipelagoContext() {
|
||||
|
||||
}
|
||||
ArchipelagoContext::ArchipelagoContext() = default;
|
||||
|
||||
void ArchipelagoContext::SetServerIp(const std::string_view& ip) {
|
||||
getSettings().archipelago.serverIP.setValue(std::string(ip));
|
||||
@@ -222,6 +241,22 @@ const std::string& ArchipelagoContext::GetPassword() {
|
||||
return getSettings().archipelago.serverPass.getValue();
|
||||
}
|
||||
|
||||
std::string ArchipelagoContext::GetArchipelagoSeedName() {
|
||||
if (IsConnected()) {
|
||||
auto& roomInfo = instance().m_roomInfo;
|
||||
return fmt::format("AP_{}_{}", GetSlotName(), roomInfo.seed_name);
|
||||
}else {
|
||||
DuskLog.fatal("Archipelago was not connected when attempting to get seed name!");
|
||||
}
|
||||
}
|
||||
|
||||
void ArchipelagoContext::GetSeedDirectoryPath(std::filesystem::path& outPath) {
|
||||
if (IsConnected()) {
|
||||
auto& roomInfo = instance().m_roomInfo;
|
||||
outPath = ui::GetRandomizerPath() / "archipelago" / GetArchipelagoSeedName();
|
||||
}
|
||||
}
|
||||
|
||||
void ArchipelagoContext::ConnectToServer() {
|
||||
config::Save();
|
||||
|
||||
@@ -236,16 +271,17 @@ void ArchipelagoContext::ConnectToServer() {
|
||||
|
||||
AP_SetItemClearCallback([]() {
|
||||
DuskLog.info("Item Clear Callback Called!");
|
||||
// HandleResetInventory();
|
||||
instance().m_isNeedResetInv = true;
|
||||
});
|
||||
|
||||
AP_SetItemRecvCallback([](int id, bool notify) {
|
||||
DuskLog.info("Item Receive Callback Called! Item: {} Notify: {}", id, notify);
|
||||
HandleItemReceived(id, notify);
|
||||
AP_SetItemRecvCallback([](AP_NetworkItem& item, bool notify) {
|
||||
DuskLog.info("Item Receive Callback Called! Item: {} Notify: {}", item.item, notify);
|
||||
HandleItemReceived(item, notify);
|
||||
});
|
||||
|
||||
AP_SetLocationCheckedCallback([](int loc) {
|
||||
DuskLog.info("Location Checked Callback Called! Location: {}", loc);
|
||||
SetLocationChecked(loc, true);
|
||||
});
|
||||
|
||||
AP_SetLocationInfoCallback([](std::vector<AP_NetworkItem> items) {
|
||||
@@ -260,7 +296,7 @@ void ArchipelagoContext::ConnectToServer() {
|
||||
return;
|
||||
}
|
||||
|
||||
std::thread messageThread = std::thread(MainThreadFunc);
|
||||
std::thread messageThread = std::thread(MessageThreadFunc);
|
||||
messageThread.detach();
|
||||
}
|
||||
|
||||
@@ -278,62 +314,82 @@ bool ArchipelagoContext::IsConnected() {
|
||||
return status == AP_ConnectionStatus::Connected || status == AP_ConnectionStatus::Authenticated;
|
||||
}
|
||||
|
||||
void ArchipelagoContext::MainThreadFunc() {
|
||||
void ArchipelagoContext::MessageThreadFunc() {
|
||||
// wait a bit before checking connection state, as websocket is probably not connected yet
|
||||
// (i really am not liking APCpp, why cant I check if the websocket is in the process of connecting???)
|
||||
std::this_thread::sleep_for(std::chrono::seconds(2));
|
||||
|
||||
DuskLog.info("AP Thread started.");
|
||||
|
||||
if (IsConnected())
|
||||
if (IsConnected()) {
|
||||
AP_GetRoomInfo(&instance().m_roomInfo);
|
||||
RequestAllLocationScout();
|
||||
}
|
||||
|
||||
while (IsConnected()) {
|
||||
if (AP_IsMessagePending())
|
||||
ParseMessageData();
|
||||
|
||||
instance().m_queueMutex.lock();
|
||||
if (randomizer_IsActive() && !instance().m_inactiveItemsQueue.empty()) {
|
||||
for (auto item : instance().m_inactiveItemsQueue) {
|
||||
instance().itemRecvImpl(item, false);
|
||||
}
|
||||
|
||||
instance().m_inactiveItemsQueue.clear();
|
||||
}
|
||||
instance().m_queueMutex.unlock();
|
||||
}
|
||||
|
||||
DuskLog.info("AP Thread ended.");
|
||||
}
|
||||
|
||||
void ArchipelagoContext::HandleItemReceived(int id, bool notify) {
|
||||
int relativeId = id - ARCHI_ITEM_OFFSET;
|
||||
void ArchipelagoContext::Execute() {
|
||||
if (!IsConnected())
|
||||
return;
|
||||
|
||||
// && ((relativeId >= 0 && relativeId <= 6) || relativeId == 7)
|
||||
if (!notify) {
|
||||
// reset player inventory if server requested it
|
||||
if (instance().m_isNeedResetInv) {
|
||||
HandleResetInventory();
|
||||
instance().m_isNeedResetInv = false;
|
||||
return; // end execution early so next frame can re-add inventory if needed
|
||||
}
|
||||
|
||||
// drain pending item queue here
|
||||
instance().m_queueMutex.lock();
|
||||
if (!instance().m_receivedItemsQueue.empty()) {
|
||||
for (auto item : instance().m_receivedItemsQueue) {
|
||||
instance().itemRecvImpl(item.first, item.second);
|
||||
}
|
||||
|
||||
instance().m_receivedItemsQueue.clear();
|
||||
}
|
||||
instance().m_queueMutex.unlock();
|
||||
|
||||
// update location checks here if we need to
|
||||
if (instance().m_isUpdateLocations) {
|
||||
UpdateCheckedLocations();
|
||||
instance().m_isUpdateLocations = false;
|
||||
}
|
||||
}
|
||||
|
||||
void ArchipelagoContext::HandleItemReceived(AP_NetworkItem& netItem, bool notify) {
|
||||
int relativeId = netItem.item - ARCHI_ITEM_OFFSET;
|
||||
|
||||
if (!notify && ((relativeId >= 0 && relativeId <= 6) || relativeId == 7)) {
|
||||
// skip rupee refills so players cant abuse disconnect/reconnect
|
||||
return;
|
||||
}
|
||||
|
||||
if (!randomizer_IsActive()) {
|
||||
DuskLog.info("Randomizer not active, adding item to queue.");
|
||||
|
||||
instance().m_queueMutex.lock();
|
||||
instance().m_inactiveItemsQueue.push_back(relativeId);
|
||||
instance().m_queueMutex.unlock();
|
||||
if (netItem.location != -1 && IsLocationChecked(netItem.location)) {
|
||||
// no need to handle item if its location has already been checked
|
||||
return;
|
||||
}
|
||||
|
||||
instance().itemRecvImpl(relativeId, notify);
|
||||
instance().m_queueMutex.lock();
|
||||
instance().m_receivedItemsQueue.push_back({relativeId, notify});
|
||||
instance().m_queueMutex.unlock();
|
||||
}
|
||||
|
||||
void ArchipelagoContext::HandleResetInventory() {
|
||||
DuskLog.info("Resetting Inventory.");
|
||||
// NOTE: this does not clear ALL things from save, so if a player managed to do something while disconnected from the archi, it might mess with things
|
||||
|
||||
auto& playerInfo = g_dComIfG_gameInfo.info.getPlayer();
|
||||
|
||||
// reset items
|
||||
playerInfo.getItem().init();
|
||||
playerInfo.getGetItem().init();
|
||||
|
||||
// reset collect (poes, shards, swords)
|
||||
playerInfo.getCollect().init();
|
||||
@@ -342,13 +398,46 @@ void ArchipelagoContext::HandleResetInventory() {
|
||||
playerInfo.getPlayerStatusA().setWalletSize(WALLET);
|
||||
// dont reset rupees, and instead reject rupee updates while refilling inv
|
||||
|
||||
// sync all location collect flags with current collection status obtained from initial room connection
|
||||
UpdateAllLocationState();
|
||||
|
||||
// clear all item-related flags
|
||||
|
||||
dComIfGs_offEventBit(0x2580); // Power up dominion rod
|
||||
|
||||
// shadow crystal
|
||||
dComIfGs_offEventBit(0xD04); // Can transform at will
|
||||
dComIfGs_offEventBit(0x501); // Midna Charge Unlocked
|
||||
|
||||
// hidden skills
|
||||
dComIfGs_offEventBit(0x2904); // ENDING BLOW
|
||||
dComIfGs_offEventBit(0x2908); // SHIELD ATTACK
|
||||
dComIfGs_offEventBit(0x2902); // BACK SLICE
|
||||
dComIfGs_offEventBit(0x2901); // HELM SPLITTER
|
||||
dComIfGs_offEventBit(0x2A80); // MORTAL DRAW
|
||||
dComIfGs_offEventBit(0x2A40); // JUMP STRIKE
|
||||
dComIfGs_offEventBit(0x2A20); // GREAT SPIN
|
||||
|
||||
}
|
||||
|
||||
void ArchipelagoContext::HandleReceiveLocationScout(const std::vector<AP_NetworkItem>& items) {
|
||||
for (const auto& item : items) {
|
||||
int parsedItemId = dItemNo_Randomizer_ARCHIPELAGO_ITEM_e;
|
||||
int parsedItemId;
|
||||
std::string parsedItemName;
|
||||
if (item.player == AP_GetPlayerID()) {
|
||||
parsedItemId = instance().getItemIdFromApId(item.item - ARCHI_ITEM_OFFSET);
|
||||
int adjustedId = item.item - ARCHI_ITEM_OFFSET;
|
||||
|
||||
if (instance().m_apItemToGameItem.contains(adjustedId)) {
|
||||
auto& itemInfo = instance().m_apItemToGameItem[adjustedId];
|
||||
parsedItemId = itemInfo.itemId;
|
||||
parsedItemName = itemInfo.itemName;
|
||||
}else {
|
||||
parsedItemId = -1;
|
||||
parsedItemName = "Unknown";
|
||||
}
|
||||
}else {
|
||||
parsedItemId = dItemNo_Randomizer_ARCHIPELAGO_ITEM_e;
|
||||
parsedItemName = "Archipelago Item";
|
||||
}
|
||||
int locationId = item.location - ARCHI_ITEM_OFFSET;
|
||||
|
||||
@@ -359,26 +448,31 @@ void ArchipelagoContext::HandleReceiveLocationScout(const std::vector<AP_Network
|
||||
continue;
|
||||
}
|
||||
|
||||
bool collected = false;
|
||||
if (instance().m_initLocationCollectState.contains(item.location))
|
||||
collected = instance().m_initLocationCollectState[item.location];
|
||||
|
||||
instance().m_locationItemInfo[locName] = {
|
||||
parsedItemId,
|
||||
parsedItemName,
|
||||
locName,
|
||||
item.location,
|
||||
false
|
||||
collected
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
void ArchipelagoContext::UpdateCheckedLocations() {
|
||||
// TODO: we need a randomizer world to be able to check all valid locations and compare collection
|
||||
// state against a previously cached value, and if collected we can send a location update packet.
|
||||
auto& world = instance().m_archiWorld;
|
||||
|
||||
return;
|
||||
|
||||
// non-usable example code
|
||||
|
||||
// replace this with however we'll be getting our world
|
||||
std::unique_ptr<randomizer::logic::world::World> world = std::make_unique<randomizer::logic::world::World>(1, nullptr);
|
||||
bool changed = false;
|
||||
|
||||
for (auto location : world->GetAllLocations()) {
|
||||
// skip locations that aren't progression, which are locations that just aren't randomized
|
||||
if (!location->IsProgression()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto locName = location->GetName();
|
||||
|
||||
if (!instance().m_locationItemInfo.contains(locName)) {
|
||||
@@ -393,8 +487,92 @@ void ArchipelagoContext::UpdateCheckedLocations() {
|
||||
if (isCollected && !cachedLocData.collected) {
|
||||
cachedLocData.collected = true;
|
||||
AP_SendItem(cachedLocData.apLocationId);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!changed) {
|
||||
DuskLog.warn("No locations had any changes! this might not be normal.");
|
||||
}
|
||||
}
|
||||
|
||||
void ArchipelagoContext::SetNeedUpdateLocations(bool update) {
|
||||
if (!instance().m_isAllowUpdateLocations)
|
||||
instance().m_isUpdateLocations = update;
|
||||
}
|
||||
|
||||
bool ArchipelagoContext::IsLocationChecked(int locId) {
|
||||
auto& world = instance().m_archiWorld;
|
||||
|
||||
for (const auto& [locName, locInfo] : instance().m_locationItemInfo) {
|
||||
if (locInfo.apLocationId == locId) {
|
||||
if (locInfo.collected)
|
||||
return true;
|
||||
|
||||
if (auto location = world->GetLocation(locInfo.locationName, true)) {
|
||||
return isLocationObtained(location);
|
||||
}
|
||||
|
||||
DuskLog.error("Failed to obtain location: {}", locName);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ArchipelagoContext::SetLocationChecked(int locId, bool collected) {
|
||||
// func was ran before location scouts could be sent out, cache result until scouts return.
|
||||
if (instance().m_locationItemInfo.empty()) {
|
||||
instance().m_initLocationCollectState[locId] = collected;
|
||||
return;
|
||||
}
|
||||
|
||||
auto& world = instance().m_archiWorld;
|
||||
|
||||
for (auto& [locName, locInfo] : instance().m_locationItemInfo) {
|
||||
if (locInfo.apLocationId == locId) {
|
||||
locInfo.collected = collected;
|
||||
|
||||
// update location flags if possible
|
||||
auto location = world->GetLocation(locInfo.locationName, true);
|
||||
if (!location || !location->IsProgression())
|
||||
return;
|
||||
|
||||
setLocationCollected(location, collected);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
DuskLog.warn("No location found for locId {}.", locId);
|
||||
}
|
||||
|
||||
void ArchipelagoContext::UpdateLocationState(int locId, bool collected) {
|
||||
auto& world = instance().m_archiWorld;
|
||||
|
||||
for (const auto& [locName, locInfo] : instance().m_locationItemInfo) {
|
||||
if (locInfo.apLocationId == locId) {
|
||||
auto location = world->GetLocation(locInfo.locationName, true);
|
||||
if (!location || !location->IsProgression())
|
||||
continue;
|
||||
|
||||
setLocationCollected(location, collected);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
DuskLog.warn("No location found for locId {}.", locId);
|
||||
}
|
||||
|
||||
void ArchipelagoContext::UpdateAllLocationState() {
|
||||
auto& world = instance().m_archiWorld;
|
||||
|
||||
for (const auto& [locName, locInfo] : instance().m_locationItemInfo) {
|
||||
auto location = world->GetLocation(locInfo.locationName, true);
|
||||
if (!location || !location->IsProgression())
|
||||
continue;
|
||||
|
||||
setLocationCollected(location, locInfo.collected);
|
||||
}
|
||||
}
|
||||
|
||||
void ArchipelagoContext::RequestAllLocationScout(bool isHint) {
|
||||
@@ -412,7 +590,7 @@ void ArchipelagoContext::SetAPConfigYamlPath(const std::string_view& path) {
|
||||
instance().m_apConfigPath = path;
|
||||
}
|
||||
|
||||
bool ArchipelagoContext::GenerateConfigFromAP(randomizer::seedgen::config::Config& outConfig) {
|
||||
bool ArchipelagoContext::GenerateConfigFromAP(randomizer::seedgen::config::Config& config) {
|
||||
if (instance().m_apConfigPath.empty()) {
|
||||
DuskLog.warn("AP Config Path Empty!");
|
||||
return false;
|
||||
@@ -431,17 +609,8 @@ bool ArchipelagoContext::GenerateConfigFromAP(randomizer::seedgen::config::Confi
|
||||
return false;
|
||||
}
|
||||
|
||||
outConfig.SetSeed("Archipelago");
|
||||
|
||||
randomizer::seedgen::settings::Settings settings;
|
||||
|
||||
auto defaultSettings = randomizer::seedgen::settings::GetAllSettingsInfo();
|
||||
|
||||
// add default settings to config first
|
||||
for (const auto& [name, info] : *defaultSettings) {
|
||||
if (info->GetType() == randomizer::seedgen::settings::Type::STANDARD)
|
||||
settings.InsertSetting(name, randomizer::seedgen::settings::Setting(info.get(), info->GetDefaultOption()));
|
||||
}
|
||||
config.SetSeed("Archipelago");
|
||||
randomizer::seedgen::settings::Settings& settings = config.GetSettings();
|
||||
|
||||
// update settings using ap config
|
||||
for (const auto& apSettingEntry : apConfigYaml["Twilight Princess"]) {
|
||||
@@ -470,11 +639,16 @@ bool ArchipelagoContext::GenerateConfigFromAP(randomizer::seedgen::config::Confi
|
||||
|
||||
auto& setting = settings.GetMap().at(settingConvert.dusklightName);
|
||||
|
||||
if (apSettingValue) {
|
||||
setting.SetCurrentOption("On");
|
||||
}else {
|
||||
setting.SetCurrentOption("Off");
|
||||
}
|
||||
setting.SetCurrentOption(apSettingValue ? "On" : "Off");
|
||||
|
||||
continue;
|
||||
}
|
||||
if (apSettingName == "poe_shuffled") {
|
||||
auto& setting = settings.GetMap().at("Poe Souls");
|
||||
bool apSettingValue = apSettingEntry.second.as<bool>();
|
||||
|
||||
// this setting has more options, but the current apworld only has off or on for now.
|
||||
setting.SetCurrentOption(apSettingValue ? "All" : "Vanilla");
|
||||
|
||||
continue;
|
||||
}
|
||||
@@ -482,16 +656,152 @@ bool ArchipelagoContext::GenerateConfigFromAP(randomizer::seedgen::config::Confi
|
||||
|
||||
auto apSettingValue = apSettingEntry.second.as<std::string>();
|
||||
|
||||
// TODO: the rest of the translations
|
||||
// TODO: clean up this if-else hellscape
|
||||
|
||||
if (apSettingName == "castle_requirements") {
|
||||
auto& setting = settings.GetMap().at("Hyrule Barrier Requirements");
|
||||
|
||||
// ap assumes max mirror shards/fused shadows/dungeons, so update those settings as well
|
||||
|
||||
if(apSettingValue == "open")
|
||||
setting.SetCurrentOption("Open");
|
||||
else if(apSettingValue == "vanilla")
|
||||
setting.SetCurrentOption("Vanilla");
|
||||
else if(apSettingValue == "fused_shadows") {
|
||||
setting.SetCurrentOption("Fused Shadows");
|
||||
settings.GetMap().at("Hyrule Barrier Fused Shadows").SetCurrentOption("3");
|
||||
}else if(apSettingValue == "mirror_shards") {
|
||||
setting.SetCurrentOption("Mirror Shards");
|
||||
settings.GetMap().at("Hyrule Barrier Mirror Shards").SetCurrentOption("4");
|
||||
}else if(apSettingValue == "all_dungeons") {
|
||||
setting.SetCurrentOption("Dungeons");
|
||||
settings.GetMap().at("Hyrule Barrier Dungeons").SetCurrentOption("8");
|
||||
}
|
||||
}else if (apSettingName == "palace_requirements") {
|
||||
auto& setting = settings.GetMap().at("Palace of Twilight Requirements");
|
||||
|
||||
if(apSettingValue == "open")
|
||||
setting.SetCurrentOption("Open");
|
||||
else if(apSettingValue == "vanilla")
|
||||
setting.SetCurrentOption("Vanilla");
|
||||
else if(apSettingValue == "fused_shadows")
|
||||
setting.SetCurrentOption("Fused Shadows");
|
||||
else if(apSettingValue == "mirror_shards")
|
||||
setting.SetCurrentOption("Mirror Shards");
|
||||
|
||||
}else if (apSettingName == "faron_woods_logic") {
|
||||
auto& setting = settings.GetMap().at("Faron Woods Logic");
|
||||
|
||||
if(apSettingValue == "open")
|
||||
setting.SetCurrentOption("Open");
|
||||
else if(apSettingValue == "closed")
|
||||
setting.SetCurrentOption("Closed");
|
||||
}else if (apSettingName == "small_key_settings") {
|
||||
auto& setting = settings.GetMap().at("Small Keys");
|
||||
|
||||
if(apSettingValue == "vanilla")
|
||||
setting.SetCurrentOption("Vanilla");
|
||||
else if(apSettingValue == "own_dungeon")
|
||||
setting.SetCurrentOption("Own Dungeon");
|
||||
else if(apSettingValue == "any_dungeon")
|
||||
setting.SetCurrentOption("Any Dungeon");
|
||||
else if(apSettingValue == "anywhere")
|
||||
setting.SetCurrentOption("Anywhere");
|
||||
else if(apSettingValue == "startwith")
|
||||
setting.SetCurrentOption("Keysy");
|
||||
|
||||
}else if (apSettingName == "big_key_settings") {
|
||||
auto& setting = settings.GetMap().at("Big Keys");
|
||||
|
||||
if(apSettingValue == "vanilla")
|
||||
setting.SetCurrentOption("Vanilla");
|
||||
else if(apSettingValue == "own_dungeon")
|
||||
setting.SetCurrentOption("Own Dungeon");
|
||||
else if(apSettingValue == "any_dungeon")
|
||||
setting.SetCurrentOption("Any Dungeon");
|
||||
else if(apSettingValue == "anywhere")
|
||||
setting.SetCurrentOption("Anywhere");
|
||||
else if(apSettingValue == "startwith")
|
||||
setting.SetCurrentOption("Keysy");
|
||||
|
||||
}else if (apSettingName == "map_and_compass_settings") {
|
||||
auto& setting = settings.GetMap().at("Maps and Compasses");
|
||||
|
||||
if(apSettingValue == "vanilla")
|
||||
setting.SetCurrentOption("Vanilla");
|
||||
else if(apSettingValue == "own_dungeon")
|
||||
setting.SetCurrentOption("Own Dungeon");
|
||||
else if(apSettingValue == "any_dungeon")
|
||||
setting.SetCurrentOption("Any Dungeon");
|
||||
else if(apSettingValue == "anywhere")
|
||||
setting.SetCurrentOption("Anywhere");
|
||||
else if(apSettingValue == "startwith")
|
||||
setting.SetCurrentOption("Keysy");
|
||||
|
||||
}else if (apSettingName == "trap_frequency") {
|
||||
auto& setting = settings.GetMap().at("Trap Item Frequency");
|
||||
|
||||
if(apSettingValue == "no_traps")
|
||||
setting.SetCurrentOption("None");
|
||||
else if(apSettingValue == "few")
|
||||
setting.SetCurrentOption("Few");
|
||||
else if(apSettingValue == "many")
|
||||
setting.SetCurrentOption("Many");
|
||||
else if(apSettingValue == "mayhem")
|
||||
setting.SetCurrentOption("Mayhem");
|
||||
else if(apSettingValue == "nightmare")
|
||||
setting.SetCurrentOption("Nightmare");
|
||||
|
||||
}else if (apSettingName == "damage_magnification") {
|
||||
auto& setting = settings.GetMap().at("Logic Damage Multiplier");
|
||||
|
||||
if(apSettingValue == "vanilla")
|
||||
setting.SetCurrentOption("Vanilla");
|
||||
else if(apSettingValue == "double")
|
||||
setting.SetCurrentOption("Double");
|
||||
else if(apSettingValue == "triple")
|
||||
setting.SetCurrentOption("Triple");
|
||||
else if(apSettingValue == "quadruple")
|
||||
setting.SetCurrentOption("Quadruple");
|
||||
else if(apSettingValue == "ohko")
|
||||
setting.SetCurrentOption("OHKO");
|
||||
|
||||
}else if (apSettingName == "goron_mines_entrance") {
|
||||
auto& setting = settings.GetMap().at("Goron Mines Entrance");
|
||||
|
||||
if(apSettingValue == "closed")
|
||||
setting.SetCurrentOption("Closed");
|
||||
else if(apSettingValue == "no_wrestling")
|
||||
setting.SetCurrentOption("No Wrestling");
|
||||
else if(apSettingValue == "open")
|
||||
setting.SetCurrentOption("Open");
|
||||
|
||||
}else if (apSettingName == "tot_entrance") {
|
||||
auto& setting = settings.GetMap().at("Sacred Grove Does Not Require Skull Kid");
|
||||
auto& setting2 = settings.GetMap().at("Temple of Time Sword Requirement");
|
||||
|
||||
if(apSettingValue == "closed") {
|
||||
setting.SetCurrentOption("Off");
|
||||
setting2.SetCurrentOption("Master Sword");
|
||||
}else if (apSettingValue == "open_grove") {
|
||||
setting.SetCurrentOption("On");
|
||||
setting2.SetCurrentOption("Master Sword");
|
||||
}else if (apSettingValue == "open") {
|
||||
setting.SetCurrentOption("On");
|
||||
setting2.SetCurrentOption("None");
|
||||
}
|
||||
|
||||
}else if (apSettingName == "logic_rules") {
|
||||
auto& setting = settings.GetMap().at("Logic Rules");
|
||||
|
||||
if(apSettingValue == "glitchless") {
|
||||
setting.SetCurrentOption("All Locations Reachable");
|
||||
}else if (apSettingValue == "glitched") { // this might not be the most direct translation
|
||||
setting.SetCurrentOption("Beatable Only");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
outConfig.GetSettingsList().push_back(settings);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -502,4 +812,133 @@ int ArchipelagoContext::GetItemAtLocation(const std::string& locName) {
|
||||
}
|
||||
return instance().m_locationItemInfo[locName].itemId;
|
||||
}
|
||||
|
||||
int ArchipelagoContext::GetItemAtLocation(int locId) {
|
||||
for (const auto& [locName, locInfo] : instance().m_locationItemInfo) {
|
||||
if (locInfo.apLocationId == locId) {
|
||||
return locInfo.itemId;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ArchipelagoContext::CreateArchipelagoWorld() {
|
||||
std::filesystem::path workingDir;
|
||||
GetSeedDirectoryPath(workingDir);
|
||||
|
||||
auto trackerRando = randomizer::Randomizer(workingDir);
|
||||
trackerRando.GenerateTrackerWorld(false);
|
||||
|
||||
instance().m_archiWorld = std::move(trackerRando.GetWorlds().front());
|
||||
}
|
||||
|
||||
void ArchipelagoContext::FillArchipelagoWorld() {
|
||||
auto& world = instance().m_archiWorld;
|
||||
|
||||
if (world == nullptr) {
|
||||
DuskLog.error("Archipelago world was not created!");
|
||||
return;
|
||||
}
|
||||
|
||||
auto& locationInfo = instance().m_locationItemInfo;
|
||||
|
||||
// fill all locations with data pulled from archi session
|
||||
for (auto location : world->GetAllLocations()) {
|
||||
// skip locations that aren't progression, which are locations that just aren't randomized
|
||||
if (!location->IsProgression()) {
|
||||
location->SetCurrentItem(location->GetOriginalItem());
|
||||
continue;
|
||||
}
|
||||
|
||||
auto locName = location->GetName();
|
||||
if (!locationInfo.contains(locName)) {
|
||||
if (!location->HasCategories("Warp Portal") &&
|
||||
!location->HasCategories("Placeholder") &&
|
||||
!location->HasCategories("Hint Sign"))
|
||||
DuskLog.warn("Missing archipelago location data for: {}", locName);
|
||||
auto origItem = location->GetOriginalItem();
|
||||
|
||||
// set location to original item
|
||||
|
||||
if (origItem->GetID() != -1) // ensure item is not nothing
|
||||
location->SetCurrentItem(origItem);
|
||||
else
|
||||
DuskLog.info("Location ({}) does not have an original item!", locName);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
auto& locInfo = locationInfo[locName];
|
||||
if (locInfo.itemId != -1) {
|
||||
location->SetCurrentItem(world->GetItem(locInfo.itemId));
|
||||
}else {
|
||||
DuskLog.info("Skipping location ({}) as item is -1.", locName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ArchipelagoContext::CreateRandomizerContext() {
|
||||
auto& world = instance().m_archiWorld;
|
||||
|
||||
// Set hint texts before writing context
|
||||
randomizer::logic::hints::GenerateAllHints(world);
|
||||
|
||||
// TODO: generate archipelago item get text replacements
|
||||
|
||||
auto randoData = WriteSeedData(world.get());
|
||||
randoData.mHash = GetArchipelagoSeedName();
|
||||
|
||||
randomizer_GetContext() = randoData;
|
||||
|
||||
std::filesystem::path workingDir;
|
||||
GetSeedDirectoryPath(workingDir);
|
||||
|
||||
auto writeToFileResult = randoData.WriteToFile(workingDir / "seed.dat");
|
||||
|
||||
if (writeToFileResult.has_value()) {
|
||||
DuskLog.error("Failed to create Rando Data. Reason: {}", writeToFileResult.value());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void ArchipelagoContext::LoadRandomizerContext() {
|
||||
randomizer_GetContext() = RandomizerContext();
|
||||
|
||||
std::filesystem::path workingDir;
|
||||
GetSeedDirectoryPath(workingDir);
|
||||
|
||||
randomizer_GetContext().LoadFromPath(workingDir / "seed.dat");
|
||||
randomizer_GetContext().mHash = GetArchipelagoSeedName();
|
||||
}
|
||||
|
||||
void ArchipelagoContext::GenerateLocalWorldData() {
|
||||
bool createContext = false;
|
||||
std::filesystem::path workingDir;
|
||||
|
||||
GetSeedDirectoryPath(workingDir);
|
||||
|
||||
if (std::filesystem::exists(workingDir)) {
|
||||
instance().m_config.LoadFromFile(workingDir / "settings.yaml", workingDir / "preferences.yaml");
|
||||
}else {
|
||||
std::filesystem::create_directories(workingDir);
|
||||
// creates base yamls at directory if they dont exist yet
|
||||
instance().m_config.LoadFromFile(workingDir / "settings.yaml", workingDir / "preferences.yaml");
|
||||
|
||||
GenerateConfigFromAP(instance().m_config);
|
||||
|
||||
instance().m_config.WriteToFile(workingDir / "settings.yaml", workingDir / "preferences.yaml");
|
||||
|
||||
createContext = true;
|
||||
}
|
||||
|
||||
CreateArchipelagoWorld();
|
||||
|
||||
FillArchipelagoWorld();
|
||||
|
||||
if (createContext) {
|
||||
CreateRandomizerContext();
|
||||
}else {
|
||||
LoadRandomizerContext();
|
||||
}
|
||||
}
|
||||
} // dusk::archi
|
||||
@@ -10,26 +10,38 @@ namespace dusk::archi
|
||||
class ArchipelagoContext {
|
||||
private:
|
||||
struct TEMP_GameItemInfo {
|
||||
int itemId;
|
||||
randomizer::logic::item::Importance importance;
|
||||
int itemId = -1;
|
||||
randomizer::logic::item::Importance importance = randomizer::logic::item::Importance::INVALID;
|
||||
std::string itemName;
|
||||
};
|
||||
|
||||
struct TEMP_GameLocationInfo {
|
||||
int apId;
|
||||
int apId = -1;
|
||||
std::string locName;
|
||||
};
|
||||
|
||||
struct GameLocationInfo {
|
||||
int itemId;
|
||||
int64_t apLocationId;
|
||||
bool collected;
|
||||
int itemId = -1;
|
||||
std::string itemName;
|
||||
std::string locationName;
|
||||
int64_t apLocationId = -1;
|
||||
bool collected = false;
|
||||
};
|
||||
|
||||
std::vector<int> m_inactiveItemsQueue;
|
||||
std::vector<std::pair<int, bool>> m_receivedItemsQueue;
|
||||
std::mutex m_queueMutex;
|
||||
|
||||
// Rando Data
|
||||
randomizer::seedgen::config::Config m_config;
|
||||
std::unique_ptr<randomizer::logic::world::World> m_archiWorld = nullptr;
|
||||
bool m_isUpdateLocations = false;
|
||||
bool m_isNeedResetInv = false;
|
||||
bool m_isAllowUpdateLocations = false;
|
||||
|
||||
// AP Data
|
||||
std::unordered_map<std::string, GameLocationInfo> m_locationItemInfo;
|
||||
std::map<int, bool> m_initLocationCollectState;
|
||||
AP_RoomInfo m_roomInfo;
|
||||
|
||||
// TEMP
|
||||
std::map<int, TEMP_GameItemInfo> m_apItemToGameItem;
|
||||
@@ -58,6 +70,10 @@ namespace dusk::archi
|
||||
static const std::string& GetSlotName();
|
||||
static const std::string& GetPassword();
|
||||
|
||||
static std::string GetArchipelagoSeedName();
|
||||
|
||||
static void GetSeedDirectoryPath(std::filesystem::path& outPath);
|
||||
|
||||
// Connection Handlers
|
||||
|
||||
static void ConnectToServer();
|
||||
@@ -68,9 +84,11 @@ namespace dusk::archi
|
||||
|
||||
// State Handlers
|
||||
|
||||
static void MainThreadFunc();
|
||||
static void MessageThreadFunc();
|
||||
|
||||
static void HandleItemReceived(int id, bool notify);
|
||||
static void Execute();
|
||||
|
||||
static void HandleItemReceived(AP_NetworkItem& id, bool notify);
|
||||
|
||||
static void HandleResetInventory();
|
||||
|
||||
@@ -78,6 +96,16 @@ namespace dusk::archi
|
||||
|
||||
static void UpdateCheckedLocations();
|
||||
|
||||
static void SetNeedUpdateLocations(bool update);
|
||||
|
||||
static bool IsLocationChecked(int locId);
|
||||
|
||||
static void SetLocationChecked(int locId, bool collected);
|
||||
|
||||
static void UpdateLocationState(int locId, bool collected);
|
||||
|
||||
static void UpdateAllLocationState();
|
||||
|
||||
// State Requesters
|
||||
|
||||
static void RequestAllLocationScout(bool isHint = false);
|
||||
@@ -86,9 +114,21 @@ namespace dusk::archi
|
||||
|
||||
static void SetAPConfigYamlPath(const std::string_view& path);
|
||||
|
||||
static bool GenerateConfigFromAP(randomizer::seedgen::config::Config& outConfig);
|
||||
static bool GenerateConfigFromAP(randomizer::seedgen::config::Config& config);
|
||||
|
||||
static int GetItemAtLocation(const std::string& locName);
|
||||
|
||||
static int GetItemAtLocation(int locId);
|
||||
|
||||
static void CreateArchipelagoWorld();
|
||||
|
||||
static void FillArchipelagoWorld();
|
||||
|
||||
static void CreateRandomizerContext();
|
||||
|
||||
static void LoadRandomizerContext();
|
||||
|
||||
static void GenerateLocalWorldData();
|
||||
|
||||
};
|
||||
} // dusk::archi
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "aurora/lib/window.hpp"
|
||||
#include "dusk/file_select.hpp"
|
||||
#include "dusk/archipelago/archipelago_context.hpp"
|
||||
#include "dusk/ui/rando_seed_generation.hpp"
|
||||
|
||||
namespace dusk {
|
||||
|
||||
@@ -69,12 +70,11 @@ void ImGuiArchipelagoDebug::drawWindow() {
|
||||
OpenApFilePicker();
|
||||
}
|
||||
|
||||
if (ImGui::Button("Test Config Convert")) {
|
||||
randomizer::seedgen::config::Config config;
|
||||
archi::ArchipelagoContext::GenerateConfigFromAP(config);
|
||||
}
|
||||
|
||||
if (archi::ArchipelagoContext::IsConnected()) {
|
||||
if (ImGui::Button("Test Create World Data")) {
|
||||
archi::ArchipelagoContext::GenerateLocalWorldData();
|
||||
}
|
||||
|
||||
if (ImGui::Button("Disconnect")) {
|
||||
archi::ArchipelagoContext::DisconnectFromServer();
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
#include <numeric>
|
||||
#include <ranges>
|
||||
|
||||
#include "dusk/archipelago/archipelago_context.hpp"
|
||||
|
||||
|
||||
namespace dusk {
|
||||
|
||||
@@ -254,8 +256,16 @@ namespace dusk {
|
||||
auto trackerHash = trackerRando->GetConfig().GetHash(false);
|
||||
// If no hash, or seeds switched, try to create tracker world from currently active seed
|
||||
if (trackerHash.empty() || (trackerHash != contextHash && !contextHash.empty())) {
|
||||
*trackerRando = randomizer::Randomizer(ui::GetRandomizerPath());
|
||||
trackerRando->GenerateTrackerWorld();
|
||||
if (archi::ArchipelagoContext::IsConnected()) {
|
||||
std::filesystem::path workingDir;
|
||||
archi::ArchipelagoContext::GetSeedDirectoryPath(workingDir);
|
||||
|
||||
*trackerRando = randomizer::Randomizer(workingDir);
|
||||
trackerRando->GenerateTrackerWorld(false);
|
||||
}else {
|
||||
*trackerRando = randomizer::Randomizer(ui::GetRandomizerPath());
|
||||
trackerRando->GenerateTrackerWorld();
|
||||
}
|
||||
}
|
||||
|
||||
if (randomizer_IsActive()) {
|
||||
|
||||
@@ -22,10 +22,14 @@
|
||||
#include "d/d_meter2_draw.h"
|
||||
#include "d/d_meter2_info.h"
|
||||
#include "d/d_msg_flow.h"
|
||||
#include "dusk/archipelago/archipelago_context.hpp"
|
||||
|
||||
std::optional<std::string> RandomizerContext::WriteToFile() {
|
||||
return WriteToFile(this->GetSeedDataPath());
|
||||
}
|
||||
|
||||
std::ofstream seedData(this->GetSeedDataPath());
|
||||
std::optional<std::string> RandomizerContext::WriteToFile(const fspath& path) {
|
||||
std::ofstream seedData(path);
|
||||
if (!seedData.is_open()) {
|
||||
return "Could not open seed data file";
|
||||
}
|
||||
@@ -137,7 +141,11 @@ std::optional<std::string> RandomizerContext::LoadFromHash(const std::string& ha
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto in = LoadYAML(this->GetSeedDataPath());
|
||||
return LoadFromPath(this->GetSeedDataPath());
|
||||
}
|
||||
|
||||
std::optional<std::string> RandomizerContext::LoadFromPath(const fspath& path) {
|
||||
auto in = LoadYAML(path);
|
||||
|
||||
// Necessary settings
|
||||
for (const auto& settingNode : in["mSettings"] ) {
|
||||
@@ -587,6 +595,8 @@ int RandomizerState::execute() {
|
||||
handleFoolishItem();
|
||||
}
|
||||
|
||||
dusk::archi::ArchipelagoContext::Execute();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1001,6 +1011,11 @@ RandomizerContext WriteSeedData(randomizer::logic::world::World* world) {
|
||||
|
||||
// Set data for all locations
|
||||
for (const auto& location : world->GetAllLocations()) {
|
||||
// skip locations with nothing
|
||||
if (location->GetCurrentItem()->GetID() == -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto& metaData = location->GetMetadata();
|
||||
|
||||
// Chest Overrides
|
||||
|
||||
@@ -80,7 +80,9 @@ public:
|
||||
} mStartLocation;
|
||||
|
||||
std::optional<std::string> WriteToFile();
|
||||
std::optional<std::string> WriteToFile(const fspath& path);;
|
||||
std::optional<std::string> LoadFromHash(const std::string& hash);
|
||||
std::optional<std::string> LoadFromPath(const fspath& path);
|
||||
std::filesystem::path GetSeedDataPath() const;
|
||||
|
||||
enum Settings {
|
||||
@@ -267,4 +269,9 @@ u32 getStageObjCRC32(u8* data, size_t size);
|
||||
*/
|
||||
bool GenerateAndWriteSeed(std::string& generationStatusMsg);
|
||||
|
||||
/*
|
||||
* Creates RandomizerContext that contains all needed data for the seed.
|
||||
*/
|
||||
RandomizerContext WriteSeedData(randomizer::logic::world::World* world);
|
||||
|
||||
#endif //DUSK_RANDOMIZER_CONTEXT_HPP
|
||||
|
||||
@@ -495,8 +495,84 @@ randomizer::logic::item_pool::ItemPool getSaveItemPool(randomizer::logic::world:
|
||||
return pool;
|
||||
}
|
||||
|
||||
void setLocationCollected(randomizer::logic::location::Location* location, bool collect) {
|
||||
auto& locationMeta = location->GetMetadata();
|
||||
|
||||
if (auto& chestNode = locationMeta["Chest"]) {
|
||||
auto tboxId = chestNode[0]["Tbox Id"].as<u8>();
|
||||
auto stageId = getStageSaveId(chestNode[0]["Stage"].as<u8>());
|
||||
if (collect)
|
||||
dComIfGs_onStageTbox(stageId, tboxId);
|
||||
else
|
||||
dComIfGs_offStageTbox(stageId, tboxId);
|
||||
}
|
||||
if (auto& poeNode = locationMeta["Poe"]) {
|
||||
auto flag = poeNode[0]["Flag"].as<u8>();
|
||||
auto stageId = getStageSaveId(poeNode[0]["Stage"].as<u8>());
|
||||
if (collect)
|
||||
dComIfGs_onStageSwitch(stageId, flag);
|
||||
else
|
||||
dComIfGs_offStageSwitch(stageId, flag);
|
||||
}
|
||||
if (auto& freeStandingItemNode = locationMeta["Freestanding Item"]) {
|
||||
auto flag = freeStandingItemNode[0]["Flag"].as<u8>();
|
||||
auto stageId = getStageSaveId(freeStandingItemNode[0]["Stage"].as<u8>());
|
||||
// big baba uses tbox, hardcode this edge case
|
||||
if (location->GetName() == "Forest Temple Big Baba Key") {
|
||||
if (collect)
|
||||
dComIfGs_onStageTbox(stageId, flag);
|
||||
else
|
||||
dComIfGs_offStageTbox(stageId, flag);
|
||||
}else {
|
||||
if (collect)
|
||||
dComIfGs_onStageItem(stageId, flag);
|
||||
else
|
||||
dComIfGs_offStageItem(stageId, flag);
|
||||
}
|
||||
}
|
||||
if (auto& eventFlagNode = locationMeta["Event Flag"]) {
|
||||
auto flag = eventFlagNode.as<u16>();
|
||||
if (collect)
|
||||
dComIfGs_onEventBit(flag);
|
||||
else
|
||||
dComIfGs_offEventBit(flag);
|
||||
}
|
||||
if (auto& wolfNode = locationMeta["Golden Wolf"]) {
|
||||
auto flag = wolfNode[0]["Flag"].as<u16>();
|
||||
if (collect)
|
||||
dComIfGs_onEventBit(flag);
|
||||
else
|
||||
dComIfGs_offEventBit(flag);
|
||||
}
|
||||
if (auto& switchFlagNode = locationMeta["Switch Flag"]) {
|
||||
auto flag = switchFlagNode["Flag"].as<u8>();
|
||||
auto stageId = getStageSaveId(switchFlagNode["Stage"].as<u8>());
|
||||
if (collect)
|
||||
dComIfGs_onStageSwitch(stageId, flag);
|
||||
else
|
||||
dComIfGs_offStageSwitch(stageId, flag);
|
||||
}
|
||||
if (auto& itemFlagNode = locationMeta["Item Flag"]) {
|
||||
auto flag = itemFlagNode["Flag"].as<u8>();
|
||||
auto stageId = getStageSaveId(itemFlagNode["Stage"].as<u8>());
|
||||
if (collect)
|
||||
dComIfGs_onStageItem(stageId, flag);
|
||||
else
|
||||
dComIfGs_offStageItem(stageId, flag);
|
||||
}
|
||||
if (auto& twilitInsectNode = locationMeta["Twilit Insect"]) {
|
||||
auto flag = twilitInsectNode[0]["Flag"].as<u8>();
|
||||
auto stageId = getStageSaveId(twilitInsectNode[0]["Stage"].as<u8>());
|
||||
if (collect)
|
||||
dComIfGs_onStageTbox(stageId, flag);
|
||||
else
|
||||
dComIfGs_offStageTbox(stageId, flag);
|
||||
}
|
||||
}
|
||||
|
||||
bool isLocationObtained(randomizer::logic::location::Location* location) {
|
||||
auto& locationMeta = location->GetMetadata();
|
||||
|
||||
if (auto& chestNode = locationMeta["Chest"]) {
|
||||
auto tboxId = chestNode[0]["Tbox Id"].as<u8>();
|
||||
auto stageId = getStageSaveId(chestNode[0]["Stage"].as<u8>());
|
||||
|
||||
@@ -25,6 +25,11 @@ int getTempleKeysFound(int saveId);
|
||||
*/
|
||||
randomizer::logic::item_pool::ItemPool getSaveItemPool(randomizer::logic::world::World* world);
|
||||
|
||||
/*
|
||||
* Updates locations relevant flag in save to whatever state is supplied.
|
||||
*/
|
||||
void setLocationCollected(randomizer::logic::location::Location* location, bool collect);
|
||||
|
||||
/*
|
||||
* Finds locations relevant flag in save (using its metadata) and checks if it's been set.
|
||||
*/
|
||||
|
||||
@@ -1055,9 +1055,10 @@
|
||||
# Importance: Junk
|
||||
# Id: 0xDB
|
||||
|
||||
#- Name: Unused
|
||||
# Importance: Junk
|
||||
# Id: 0xDC
|
||||
- Name: Archipelago Item
|
||||
Importance: Minor
|
||||
Id: 0xDC
|
||||
APItemId: -1
|
||||
|
||||
#- Name: Unused
|
||||
# Importance: Junk
|
||||
|
||||
@@ -232,7 +232,7 @@ namespace randomizer::logic::entrance
|
||||
return this->_decoupled;
|
||||
}
|
||||
|
||||
void Entrance::SetDisbled(const bool& disabled)
|
||||
void Entrance::SetDisabled(const bool& disabled)
|
||||
{
|
||||
this->_disabled = disabled;
|
||||
LOG_TO_DEBUG(this->GetOriginalName() + " disabled status set to " + (disabled ? "True" : "False"));
|
||||
|
||||
@@ -102,7 +102,7 @@ namespace randomizer::logic::entrance
|
||||
bool IsShuffled() const;
|
||||
void SetDecoupled(const bool& decoupled);
|
||||
bool IsDecoupled() const;
|
||||
void SetDisbled(const bool& disabled);
|
||||
void SetDisabled(const bool& disabled);
|
||||
bool IsDisabled() const;
|
||||
void SetPrimary(const bool& primary);
|
||||
bool IsPrimary() const;
|
||||
|
||||
@@ -4,36 +4,33 @@
|
||||
#include "world.hpp"
|
||||
|
||||
namespace randomizer::logic::hints {
|
||||
static const std::unordered_map<std::string, std::string> dungeonColors = {
|
||||
{"Forest Temple", "<green>"},
|
||||
{"Goron Mines", "<red>"},
|
||||
{"Lakebed Temple", "<blue>"},
|
||||
{"Arbiters Grounds", "<orange>"},
|
||||
{"Snowpeak Ruins", "<light blue>"},
|
||||
{"Temple of Time", "<dark green>"},
|
||||
{"City in the Sky", "<yellow>"},
|
||||
{"Palace of Twilight", "<purple>"},
|
||||
// {"Hyrule Castle", "<silver>"}
|
||||
};
|
||||
|
||||
// Tell the player which dungeons are required on the sign in front of Link's House
|
||||
static void GenerateRequiredDungeonsHint(world::WorldPool& worlds) {
|
||||
static const std::unordered_map<std::string, std::string> dungeonColors = {
|
||||
{"Forest Temple", "<green>"},
|
||||
{"Goron Mines", "<red>"},
|
||||
{"Lakebed Temple", "<blue>"},
|
||||
{"Arbiters Grounds", "<orange>"},
|
||||
{"Snowpeak Ruins", "<light blue>"},
|
||||
{"Temple of Time", "<dark green>"},
|
||||
{"City in the Sky", "<yellow>"},
|
||||
{"Palace of Twilight", "<purple>"},
|
||||
// {"Hyrule Castle", "<silver>"}
|
||||
};
|
||||
|
||||
for (const auto& world : worlds) {
|
||||
auto& requiredDungeonText = world->AddNewText("Links House Sign");
|
||||
for (const auto& [dungeonName, dungeon] : world->GetDungeonTable()) {
|
||||
if (dungeon->IsRequired()) {
|
||||
requiredDungeonText += dungeonColors.at(dungeonName) + getTextObject(dungeonName) + "\n";
|
||||
}
|
||||
static void GenerateRequiredDungeonsHint(world::World* world) {
|
||||
auto& requiredDungeonText = world->AddNewText("Links House Sign");
|
||||
for (const auto& [dungeonName, dungeon] : world->GetDungeonTable()) {
|
||||
if (dungeon->IsRequired()) {
|
||||
requiredDungeonText += dungeonColors.at(dungeonName) + getTextObject(dungeonName) + "\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (requiredDungeonText.Empty()) {
|
||||
requiredDungeonText += getTextObject("No Required Dungeons Text");
|
||||
}
|
||||
if (requiredDungeonText.Empty()) {
|
||||
requiredDungeonText += getTextObject("No Required Dungeons Text");
|
||||
}
|
||||
}
|
||||
|
||||
static void doItemTextReplacement(const std::unique_ptr<world::World>& world,
|
||||
static void doItemTextReplacement(world::World* world,
|
||||
const std::string& locationName,
|
||||
const std::list<std::string>& textNames,
|
||||
Text::Color color) {
|
||||
@@ -48,40 +45,44 @@ namespace randomizer::logic::hints {
|
||||
}
|
||||
}
|
||||
|
||||
static void GenerateItemTextReplacements(world::WorldPool& worlds) {
|
||||
for (const auto& world : worlds) {
|
||||
doItemTextReplacement(world, "Fishing Hole Bottle", {"Fishing Hole Sign Text"}, Text::GREEN);
|
||||
doItemTextReplacement(world, "Charlo Donation Blessing", {"Charlo Donation Ask Text"}, Text::GREEN);
|
||||
doItemTextReplacement(world, "Sera Shop Slingshot", {"Slingshot Shop Text",
|
||||
"Slingshot Shop Too Expensive Text", "Slingshot Shop Purchase Confirmation Text",
|
||||
"Slingshot Shop After Purchase Text"}, Text::ORANGE);
|
||||
static void GenerateItemTextReplacements(world::World* world) {
|
||||
doItemTextReplacement(world, "Fishing Hole Bottle", {"Fishing Hole Sign Text"}, Text::GREEN);
|
||||
doItemTextReplacement(world, "Charlo Donation Blessing", {"Charlo Donation Ask Text"}, Text::GREEN);
|
||||
doItemTextReplacement(world, "Sera Shop Slingshot", {"Slingshot Shop Text",
|
||||
"Slingshot Shop Too Expensive Text", "Slingshot Shop Purchase Confirmation Text",
|
||||
"Slingshot Shop After Purchase Text"}, Text::ORANGE);
|
||||
|
||||
doItemTextReplacement(world, "Barnes Bomb Bag", {"Barnes Special Offer Text"}, Text::ORANGE);
|
||||
doItemTextReplacement(world, "Kakariko Village Malo Mart Wooden Shield", {"Kakariko Malo Mart Wooden Shield Purchase Confirmation Text",
|
||||
"Kakariko Malo Mart Wooden Shield Too Expensive Text", "Kakariko Malo Mart Wooden Shield Text"}, Text::ORANGE);
|
||||
doItemTextReplacement(world, "Barnes Bomb Bag", {"Barnes Special Offer Text"}, Text::ORANGE);
|
||||
doItemTextReplacement(world, "Kakariko Village Malo Mart Wooden Shield", {"Kakariko Malo Mart Wooden Shield Purchase Confirmation Text",
|
||||
"Kakariko Malo Mart Wooden Shield Too Expensive Text", "Kakariko Malo Mart Wooden Shield Text"}, Text::ORANGE);
|
||||
|
||||
doItemTextReplacement(world, "Kakariko Village Malo Mart Hylian Shield", {"Kakariko Malo Mart Hylian Shield Purchase Confirmation Text",
|
||||
"Kakariko Malo Mart Hylian Shield Too Expensive Text", "Kakariko Malo Mart Hylian Shield After Purchase Text",
|
||||
"Kakariko Malo Mart Hylian Shield Text"}, Text::ORANGE);
|
||||
doItemTextReplacement(world, "Kakariko Village Malo Mart Hylian Shield", {"Kakariko Malo Mart Hylian Shield Purchase Confirmation Text",
|
||||
"Kakariko Malo Mart Hylian Shield Too Expensive Text", "Kakariko Malo Mart Hylian Shield After Purchase Text",
|
||||
"Kakariko Malo Mart Hylian Shield Text"}, Text::ORANGE);
|
||||
|
||||
doItemTextReplacement(world, "Kakariko Village Malo Mart Red Potion", {"Kakariko Malo Mart Red Potion Too Expensive Text",
|
||||
"Kakariko Malo Mart Red Potion Purchase Confirmation Text", "Kakariko Malo Mart Red Potion Text"}, Text::ORANGE);
|
||||
doItemTextReplacement(world, "Kakariko Village Malo Mart Red Potion", {"Kakariko Malo Mart Red Potion Too Expensive Text",
|
||||
"Kakariko Malo Mart Red Potion Purchase Confirmation Text", "Kakariko Malo Mart Red Potion Text"}, Text::ORANGE);
|
||||
|
||||
doItemTextReplacement(world, "Kakariko Village Malo Mart Hawkeye", {"Kakariko Malo Mart Hawkeye Purchase Confirmation Text",
|
||||
"Kakariko Malo Mart Hawkeye Too Expensive Text", "Kakariko Malo Mart Hawkeye After Purchase Text",
|
||||
"Kakariko Malo Mart Hawkeye Coming Soon Text", "Kakariko Malo Mart Hawkeye Text"}, Text::ORANGE);
|
||||
doItemTextReplacement(world, "Kakariko Village Malo Mart Hawkeye", {"Kakariko Malo Mart Hawkeye Purchase Confirmation Text",
|
||||
"Kakariko Malo Mart Hawkeye Too Expensive Text", "Kakariko Malo Mart Hawkeye After Purchase Text",
|
||||
"Kakariko Malo Mart Hawkeye Coming Soon Text", "Kakariko Malo Mart Hawkeye Text"}, Text::ORANGE);
|
||||
|
||||
doItemTextReplacement(world, "Castle Town Malo Mart Magic Armor", {"Chudleys Shop Magic Armor Text",
|
||||
"Castle Town Malo Mart Magic Armor After Purchase Text", "Castle Town Malo Mart Magic Armor Text",
|
||||
"Castle Town Malo Mart Magic Armor Sold Out Text"}, Text::ORANGE);
|
||||
doItemTextReplacement(world, "Castle Town Malo Mart Magic Armor", {"Chudleys Shop Magic Armor Text",
|
||||
"Castle Town Malo Mart Magic Armor After Purchase Text", "Castle Town Malo Mart Magic Armor Text",
|
||||
"Castle Town Malo Mart Magic Armor Sold Out Text"}, Text::ORANGE);
|
||||
|
||||
doItemTextReplacement(world, "Coro Bottle", {"Coro Bottle Offer 1 Text",
|
||||
"Coro Bottle Offer 2 Text", "Coro Bottle Offer 3 Text", "Coro Bottle Offer 4 Text"}, Text::ORANGE);
|
||||
}
|
||||
doItemTextReplacement(world, "Coro Bottle", {"Coro Bottle Offer 1 Text",
|
||||
"Coro Bottle Offer 2 Text", "Coro Bottle Offer 3 Text", "Coro Bottle Offer 4 Text"}, Text::ORANGE);
|
||||
}
|
||||
|
||||
void GenerateAllHints(world::WorldPool& worlds) {
|
||||
GenerateRequiredDungeonsHint(worlds);
|
||||
GenerateItemTextReplacements(worlds);
|
||||
for (const auto& world : worlds) {
|
||||
GenerateAllHints(world);
|
||||
}
|
||||
}
|
||||
|
||||
void GenerateAllHints(const std::unique_ptr<world::World>& world) {
|
||||
GenerateRequiredDungeonsHint(world.get());
|
||||
GenerateItemTextReplacements(world.get());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,5 +3,6 @@
|
||||
namespace randomizer::logic::hints {
|
||||
|
||||
void GenerateAllHints(world::WorldPool& worldPool);
|
||||
void GenerateAllHints(const std::unique_ptr<world::World>& world);
|
||||
|
||||
}
|
||||
@@ -842,7 +842,7 @@ namespace randomizer::logic::world
|
||||
// Disable the dungeon's starting entrances
|
||||
for (auto& entrance : dungeon->GetStartingEntrances())
|
||||
{
|
||||
entrance->SetDisbled(true);
|
||||
entrance->SetDisabled(true);
|
||||
}
|
||||
|
||||
// Run an accessibility search to see which locations inherently require accessing this dungeon
|
||||
@@ -864,7 +864,7 @@ namespace randomizer::logic::world
|
||||
// Re-enable the dungeon's entrances
|
||||
for (auto& entrance : dungeon->GetStartingEntrances())
|
||||
{
|
||||
entrance->SetDisbled(false);
|
||||
entrance->SetDisabled(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -916,7 +916,7 @@ namespace randomizer::logic::world
|
||||
// Disable the dungeon's starting entrances
|
||||
for (auto& entrance : dungeon->GetStartingEntrances())
|
||||
{
|
||||
entrance->SetDisbled(true);
|
||||
entrance->SetDisabled(true);
|
||||
}
|
||||
|
||||
// Check if the game is beatable, set dungeon as required if so. If the dungeon is not required and barren
|
||||
@@ -942,7 +942,7 @@ namespace randomizer::logic::world
|
||||
// Re-enable the dungeon's entrances
|
||||
for (auto& entrance : dungeon->GetStartingEntrances())
|
||||
{
|
||||
entrance->SetDisbled(false);
|
||||
entrance->SetDisabled(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1169,11 +1169,13 @@ namespace randomizer::logic::world
|
||||
return this->_startingItemPool;
|
||||
}
|
||||
|
||||
location::Location* World::GetLocation(const std::string& name)
|
||||
location::Location* World::GetLocation(const std::string& name, const bool& ignoreError)
|
||||
{
|
||||
if (!this->_locationTable.contains(name))
|
||||
{
|
||||
throw std::runtime_error("Unknown location name \"" + name + "\"");
|
||||
if (!ignoreError)
|
||||
throw std::runtime_error("Unknown location name \"" + name + "\"");
|
||||
return nullptr;
|
||||
}
|
||||
return this->_locationTable.at(name).get();
|
||||
}
|
||||
|
||||
@@ -131,7 +131,7 @@ namespace randomizer::logic::world
|
||||
item::Item* GetGameWinningItem() const;
|
||||
item_pool::ItemPool& GetItemPool();
|
||||
item_pool::ItemPool& GetStartingItemPool();
|
||||
location::Location* GetLocation(const std::string& name);
|
||||
location::Location* GetLocation(const std::string& name, const bool& ignoreError = false);
|
||||
location::LocationPool GetAllLocations(const bool& includeNonItemLocations = false);
|
||||
area::Area* GetArea(const std::string& name, const bool& createIfNotFound = false);
|
||||
area::Area* GetRootArea() const;
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "dusk/logging.h"
|
||||
#include "dusk/archipelago/archipelago_context.hpp"
|
||||
#include "dusk/ui/rando_config.hpp"
|
||||
#include "dusk/randomizer/game/randomizer_context.hpp"
|
||||
|
||||
@@ -45,19 +46,24 @@ namespace randomizer
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void Randomizer::GenerateTrackerWorld() {
|
||||
void Randomizer::GenerateTrackerWorld(bool useAntiSpoilerLog) {
|
||||
auto contextHash = randomizer_GetContext().mHash;
|
||||
|
||||
if (contextHash.empty()) {
|
||||
return;
|
||||
if (!useAntiSpoilerLog) {
|
||||
this->_config.LoadFromFile(GetConfigPath(), GetPrefPath());
|
||||
this->_config.SetHash(contextHash);
|
||||
}else {
|
||||
if (contextHash.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::filesystem::path seedSettings = dusk::ui::GetRandomizerSeedsPath() /
|
||||
contextHash / (contextHash + " Anti-Spoiler Log.txt");
|
||||
|
||||
this->_config.LoadFromFile(seedSettings, GetPrefPath());
|
||||
this->_config.SetHash(contextHash);
|
||||
}
|
||||
|
||||
std::filesystem::path seedSettings = dusk::ui::GetRandomizerSeedsPath() /
|
||||
contextHash / (contextHash + " Anti-Spoiler Log.txt");
|
||||
|
||||
this->_config.LoadFromFile(seedSettings, GetPrefPath());
|
||||
this->_config.SetHash(contextHash);
|
||||
|
||||
std::unique_ptr<logic::world::World> world = std::make_unique<logic::world::World>(1, this);
|
||||
world->SetSettings(this->_config.GetSettingsList().front());
|
||||
// Always use logic when building a tracker world
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace randomizer
|
||||
*/
|
||||
std::optional<std::string> Generate();
|
||||
void GenerateWorlds();
|
||||
void GenerateTrackerWorld();
|
||||
void GenerateTrackerWorld(bool useAntiSpoilerLog = true);
|
||||
|
||||
auto& GetConfig() { return this->_config; }
|
||||
auto& GetWorlds() { return this->_worlds; }
|
||||
|
||||
@@ -55,8 +55,8 @@ FetchContent_Declare(
|
||||
message(STATUS "randomizer: Fetching APCpp")
|
||||
FetchContent_Declare(
|
||||
APCpp
|
||||
GIT_REPOSITORY https://github.com/N00byKing/APCpp.git
|
||||
GIT_TAG 9194179
|
||||
GIT_REPOSITORY https://github.com/CraftyBoss/APCpp.git
|
||||
GIT_TAG 5091686
|
||||
)
|
||||
|
||||
FetchContent_MakeAvailable(yaml-cpp base64pp battery-embed APCpp)
|
||||
|
||||
Reference in New Issue
Block a user