mirror of
https://github.com/TwilitRealm/dusklight
synced 2026-08-20 13:24:40 -04:00
Merge remote-tracking branch 'origin/rando-mod' into rando-mod
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
cmake_minimum_required(VERSION 3.25)
|
cmake_minimum_required(VERSION 3.25)
|
||||||
project(randomizer CXX)
|
project(randomizer CXX)
|
||||||
|
set(CMAKE_CXX_STANDARD 23)
|
||||||
|
|
||||||
if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
|
if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
|
||||||
set(DUSK_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../.." CACHE PATH "Path to dusk source root")
|
set(DUSK_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../.." CACHE PATH "Path to dusk source root")
|
||||||
|
|||||||
@@ -1222,7 +1222,7 @@
|
|||||||
Forward:
|
Forward:
|
||||||
Connection: Kakariko Renados Sanctuary -> Kakariko Renados Sanctuary Basement
|
Connection: Kakariko Renados Sanctuary -> Kakariko Renados Sanctuary Basement
|
||||||
Stage: 75
|
Stage: 75
|
||||||
Room: 5
|
Room: 7
|
||||||
Spawn: "00"
|
Spawn: "00"
|
||||||
Spawn Type: "FF"
|
Spawn Type: "FF"
|
||||||
Parameters: "FFFF"
|
Parameters: "FFFF"
|
||||||
|
|||||||
@@ -356,4 +356,31 @@ namespace randomizer::logic::entrance
|
|||||||
|
|
||||||
return {parentAreaName, connectedAreaName};
|
return {parentAreaName, connectedAreaName};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Entrance::SetGameInfo(const YAML::Node& node) {
|
||||||
|
bool ret = true;
|
||||||
|
std::string stageIdString = node["Stage"].as<std::string>();
|
||||||
|
if (stageIdString == "null") {
|
||||||
|
stageIdString = "-1";
|
||||||
|
ret = false;
|
||||||
|
}
|
||||||
|
int stageId = std::stoi(stageIdString);
|
||||||
|
if (stageId == -1) {
|
||||||
|
stageId = 0xFF;
|
||||||
|
}
|
||||||
|
SetStageId(static_cast<uint8_t>(stageId));
|
||||||
|
std::string roomNoString = node["Room"].as<std::string>();
|
||||||
|
if (roomNoString == "null") {
|
||||||
|
roomNoString = "-1";
|
||||||
|
ret = false;
|
||||||
|
}
|
||||||
|
SetRoomNo(static_cast<int8_t>(std::stoi(roomNoString)));
|
||||||
|
SetPointNo(static_cast<int16_t>(std::stoi("0x0"+node["Spawn"].as<std::string>(),nullptr,16)));
|
||||||
|
int layer = std::stoi("0x0"+node["State"].as<std::string>(),nullptr,16);
|
||||||
|
if (layer == 0xFF) {
|
||||||
|
layer = -1;
|
||||||
|
}
|
||||||
|
SetLayerNo(static_cast<int8_t>(layer));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
} // namespace randomizer::logic::entrance
|
} // namespace randomizer::logic::entrance
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <cstdint>
|
||||||
|
#include "../utility/yaml.hpp"
|
||||||
|
|
||||||
// Forward Declarations
|
// Forward Declarations
|
||||||
namespace randomizer::logic::area
|
namespace randomizer::logic::area
|
||||||
@@ -148,6 +150,18 @@ namespace randomizer::logic::entrance
|
|||||||
*/
|
*/
|
||||||
Entrance* AssumeReachable();
|
Entrance* AssumeReachable();
|
||||||
|
|
||||||
|
void SetStageId(uint8_t stageId) { _stageId = stageId; }
|
||||||
|
void SetRoomNo(int8_t roomNo) { _roomNo = roomNo; }
|
||||||
|
void SetLayerNo(int8_t layerNo) { _layerNo = layerNo; }
|
||||||
|
void SetPointNo(int16_t pointNo) { _pointNo = pointNo; }
|
||||||
|
uint8_t GetStageId() const { return _stageId; }
|
||||||
|
int8_t GetRoomNo() const { return _roomNo; }
|
||||||
|
int8_t GetLayerNo() const { return _layerNo; }
|
||||||
|
int16_t GetPointNo() const { return _pointNo; }
|
||||||
|
bool SetGameInfo(const YAML::Node& node);
|
||||||
|
void SetCoupledDoor(int16_t entrance) { _coupledEntrance = entrance; }
|
||||||
|
int16_t getCoupledDoor() { return _coupledEntrance; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int _id = -1;
|
int _id = -1;
|
||||||
area::Area* _parentArea = nullptr;
|
area::Area* _parentArea = nullptr;
|
||||||
@@ -159,6 +173,11 @@ namespace randomizer::logic::entrance
|
|||||||
std::string _alias = "";
|
std::string _alias = "";
|
||||||
world::World* _world = nullptr;
|
world::World* _world = nullptr;
|
||||||
|
|
||||||
|
uint8_t _stageId = 0xFF;
|
||||||
|
int8_t _roomNo = -1;
|
||||||
|
int8_t _layerNo = -1;
|
||||||
|
int16_t _pointNo = -1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The local requirement for this entrance assuming we have access to its parent area.
|
* @brief The local requirement for this entrance assuming we have access to its parent area.
|
||||||
*/
|
*/
|
||||||
@@ -202,6 +221,9 @@ namespace randomizer::logic::entrance
|
|||||||
// to this one. So if the entrance is North Faron Woods -> Forest Temple Entrance,
|
// to this one. So if the entrance is North Faron Woods -> Forest Temple Entrance,
|
||||||
// then _assumed is the target entrance Root -> Forest Temple Entrance
|
// then _assumed is the target entrance Root -> Forest Temple Entrance
|
||||||
Entrance* _assumed = nullptr;
|
Entrance* _assumed = nullptr;
|
||||||
|
|
||||||
|
// If the entrance is a coupled door, this is the other door's point to override
|
||||||
|
int16_t _coupledEntrance = -1;
|
||||||
};
|
};
|
||||||
|
|
||||||
using EntrancePool = std::vector<Entrance*>;
|
using EntrancePool = std::vector<Entrance*>;
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ namespace randomizer::logic::entrance_shuffle
|
|||||||
|
|
||||||
auto forwardEntrance = world->GetEntrance(forwardEntry["Connection"].as<std::string>());
|
auto forwardEntrance = world->GetEntrance(forwardEntry["Connection"].as<std::string>());
|
||||||
forwardEntrance->SetType(type);
|
forwardEntrance->SetType(type);
|
||||||
// TODO: Set actual entrance data
|
forwardEntrance->SetGameInfo(forwardEntry);
|
||||||
forwardEntrance->SetID(world->GetNewEntranceID());
|
forwardEntrance->SetID(world->GetNewEntranceID());
|
||||||
forwardEntrance->SetPrimary(true);
|
forwardEntrance->SetPrimary(true);
|
||||||
forwardEntrance->SetAlias(
|
forwardEntrance->SetAlias(
|
||||||
@@ -78,7 +78,7 @@ namespace randomizer::logic::entrance_shuffle
|
|||||||
|
|
||||||
auto returnEntrance = world->GetEntrance(returnEntry["Connection"].as<std::string>());
|
auto returnEntrance = world->GetEntrance(returnEntry["Connection"].as<std::string>());
|
||||||
returnEntrance->SetType(type);
|
returnEntrance->SetType(type);
|
||||||
// TODO: Set actual entrance data
|
returnEntrance->SetGameInfo(returnEntry);
|
||||||
returnEntrance->SetID(world->GetNewEntranceID());
|
returnEntrance->SetID(world->GetNewEntranceID());
|
||||||
returnEntrance->SetAlias(
|
returnEntrance->SetAlias(
|
||||||
returnEntry["Alias"] ? returnEntry["Alias"].as<std::string>() : "");
|
returnEntry["Alias"] ? returnEntry["Alias"].as<std::string>() : "");
|
||||||
@@ -114,7 +114,7 @@ namespace randomizer::logic::entrance_shuffle
|
|||||||
[&](const auto& door) { return door->IsPrimary() == mainDoor->IsPrimary(); });
|
[&](const auto& door) { return door->IsPrimary() == mainDoor->IsPrimary(); });
|
||||||
auto coupledDoor = *coupledDoorItr;
|
auto coupledDoor = *coupledDoorItr;
|
||||||
|
|
||||||
// TODO: Add the coupled door's info to the main door
|
mainDoor->SetCoupledDoor(coupledDoor->GetPointNo());
|
||||||
|
|
||||||
// Completely remove the coupled door from the world graph
|
// Completely remove the coupled door from the world graph
|
||||||
doors.erase(coupledDoorItr);
|
doors.erase(coupledDoorItr);
|
||||||
@@ -471,7 +471,7 @@ namespace randomizer::logic::entrance_shuffle
|
|||||||
// plandomizer stuff going on. Currently, the only non-assumed entrance we're shuffling
|
// plandomizer stuff going on. Currently, the only non-assumed entrance we're shuffling
|
||||||
// is the randomized spawn, but if we ever shuffle warp portals, they'll go here too.
|
// is the randomized spawn, but if we ever shuffle warp portals, they'll go here too.
|
||||||
|
|
||||||
int retries = 20;
|
int retries = 10000;
|
||||||
while (retries > 0)
|
while (retries > 0)
|
||||||
{
|
{
|
||||||
std::unordered_map<Entrance*, Entrance*> rollbacks = {};
|
std::unordered_map<Entrance*, Entrance*> rollbacks = {};
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "../item.hpp"
|
#include "../item.hpp"
|
||||||
|
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
BitVector::BitVector(const std::list<int>& bits)
|
BitVector::BitVector(const std::list<int>& bits)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include <set>
|
#include <set>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
namespace randomizer::seedgen::settings
|
namespace randomizer::seedgen::settings
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -33,6 +33,9 @@
|
|||||||
#include "d/d_msg_object.h"
|
#include "d/d_msg_object.h"
|
||||||
#include "d/d_save.h"
|
#include "d/d_save.h"
|
||||||
#include "d/d_shop_system.h"
|
#include "d/d_shop_system.h"
|
||||||
|
#include "d/d_s_name.h"
|
||||||
|
#include "f_op/f_op_overlap_mng.h"
|
||||||
|
#include "m_Do/m_Do_Reset.h"
|
||||||
#include "c/c_damagereaction.h"
|
#include "c/c_damagereaction.h"
|
||||||
|
|
||||||
DEFINE_HOOK(&dFile_select_c::selectDataNameMove, dFile_select_c__selectDataNameMove);
|
DEFINE_HOOK(&dFile_select_c::selectDataNameMove, dFile_select_c__selectDataNameMove);
|
||||||
@@ -56,8 +59,12 @@ DEFINE_HOOK(&dSv_player_item_c::setLineUpItem, dSv_player_item_c__setLineUpItem)
|
|||||||
|
|
||||||
DEFINE_HOOK(&dSv_info_c::onSwitch, dSv_info_c__onSwitch);
|
DEFINE_HOOK(&dSv_info_c::onSwitch, dSv_info_c__onSwitch);
|
||||||
|
|
||||||
/*DEFINE_HOOK_SYMBOL("__Z21dComIfGp_setNextStagePKcsaafjiasii",
|
#ifdef _MSVC_LANG
|
||||||
void(char const*, s16, s8, s8, f32, u32, int, s8, s16, int, int), setNextStage);*/
|
#define setNextStage_sig "?dComIfGp_setNextStage@@YAXPEBDFCCMIHCFHH@Z"
|
||||||
|
#else
|
||||||
|
#define setNextStage_sig "_Z21dComIfGp_setNextStagePKcsaafjiasii"
|
||||||
|
#endif
|
||||||
|
DEFINE_HOOK_SYMBOL(setNextStage_sig, void(char const *, s16, s8, s8, f32, u32, int, s8, s16, int, int), setNextStage);
|
||||||
|
|
||||||
DEFINE_HOOK_SYMBOL("daObj_Gb_Create", int(fopAc_ac_c*), ObjGb_Create);
|
DEFINE_HOOK_SYMBOL("daObj_Gb_Create", int(fopAc_ac_c*), ObjGb_Create);
|
||||||
|
|
||||||
@@ -119,6 +126,8 @@ DEFINE_HOOK_SYMBOL("daE_MD_Create", int(fopAc_ac_c*), daE_MD_c__create);
|
|||||||
DEFINE_HOOK(&daObjMasterSword_c::executeWait, daObjMasterSword_c__executeWait);
|
DEFINE_HOOK(&daObjMasterSword_c::executeWait, daObjMasterSword_c__executeWait);
|
||||||
DEFINE_HOOK_SYMBOL("daObjMasterSword_Execute", int(daObjMasterSword_c*), daObjMasterSword_c__execute);
|
DEFINE_HOOK_SYMBOL("daObjMasterSword_Execute", int(daObjMasterSword_c*), daObjMasterSword_c__execute);
|
||||||
|
|
||||||
|
DEFINE_HOOK(&dScnName_c::changeGameScene, dScnName_c__changeGameScene);
|
||||||
|
|
||||||
namespace randomizer::ui {
|
namespace randomizer::ui {
|
||||||
dialogSelectModeState g_dialogSelectModeState = SelectReady;
|
dialogSelectModeState g_dialogSelectModeState = SelectReady;
|
||||||
}
|
}
|
||||||
@@ -1933,6 +1942,9 @@ HookAction hookPreNpcFOrderEvent(ModContext*, void* args, void* retval, void*) {
|
|||||||
u16 i_priority = mods::arg<u16>(args, 4);
|
u16 i_priority = mods::arg<u16>(args, 4);
|
||||||
|
|
||||||
// kinda hacky way to check for the state where Bo is trying to talk after getting Iron Boots
|
// kinda hacky way to check for the state where Bo is trying to talk after getting Iron Boots
|
||||||
|
if (i_this->eventInfo.getArchiveName() == nullptr) {
|
||||||
|
return HOOK_CONTINUE;
|
||||||
|
}
|
||||||
const std::string arcName = i_this->eventInfo.getArchiveName();
|
const std::string arcName = i_this->eventInfo.getArchiveName();
|
||||||
if (arcName == "Bou4" && i_priority == 40) {
|
if (arcName == "Bou4" && i_priority == 40) {
|
||||||
i_forceSpeak = FALSE;
|
i_forceSpeak = FALSE;
|
||||||
@@ -2106,6 +2118,12 @@ HookAction hookPreMasterSwordExecute(ModContext*, void* args, void* retval, void
|
|||||||
return HOOK_CONTINUE;
|
return HOOK_CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void hookPost_dScnName_c__changeGameScene(ModContext* ctx, void* args, void* retval, void* userdata) {
|
||||||
|
if (!mDoRst::isReset() && !fopOvlpM_IsPeek()) {
|
||||||
|
randomizer::session::registerStartingLocation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ModResult initialize() {
|
ModResult initialize() {
|
||||||
@@ -2148,7 +2166,7 @@ ModResult initialize() {
|
|||||||
|
|
||||||
ADD_HOOK_PRE(dSv_info_c__onSwitch, hookPreSaveInfoOnSwitch);
|
ADD_HOOK_PRE(dSv_info_c__onSwitch, hookPreSaveInfoOnSwitch);
|
||||||
|
|
||||||
//ADD_HOOK_PRE(setNextStage, hookPreSetNextStage);
|
ADD_HOOK_PRE(setNextStage, hookPreSetNextStage);
|
||||||
|
|
||||||
ADD_HOOK_PRE(ObjGb_Create, hookPreObjGbCreate);
|
ADD_HOOK_PRE(ObjGb_Create, hookPreObjGbCreate);
|
||||||
|
|
||||||
@@ -2211,6 +2229,8 @@ ModResult initialize() {
|
|||||||
ADD_HOOK_REPLACE(daObjMasterSword_c__executeWait, hookReplaceMasterSwordExecuteWait);
|
ADD_HOOK_REPLACE(daObjMasterSword_c__executeWait, hookReplaceMasterSwordExecuteWait);
|
||||||
ADD_HOOK_PRE(daObjMasterSword_c__execute, hookPreMasterSwordExecute);
|
ADD_HOOK_PRE(daObjMasterSword_c__execute, hookPreMasterSwordExecute);
|
||||||
|
|
||||||
|
ADD_HOOK_POST(dScnName_c__changeGameScene, hookPost_dScnName_c__changeGameScene);
|
||||||
|
|
||||||
return MOD_OK;
|
return MOD_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2237,6 +2257,7 @@ ModResult uninstall() {
|
|||||||
mods::hook::uninstall<dSv_player_item_c__setLineUpItem>(svc_hook);
|
mods::hook::uninstall<dSv_player_item_c__setLineUpItem>(svc_hook);
|
||||||
|
|
||||||
mods::hook::uninstall<dSv_info_c__onSwitch>(svc_hook);
|
mods::hook::uninstall<dSv_info_c__onSwitch>(svc_hook);
|
||||||
|
mods::hook::uninstall<setNextStage>();
|
||||||
|
|
||||||
mods::hook::uninstall<ObjGb_Create>(svc_hook);
|
mods::hook::uninstall<ObjGb_Create>(svc_hook);
|
||||||
|
|
||||||
@@ -2296,6 +2317,8 @@ ModResult uninstall() {
|
|||||||
mods::hook::uninstall<daObjMasterSword_c__executeWait>(svc_hook);
|
mods::hook::uninstall<daObjMasterSword_c__executeWait>(svc_hook);
|
||||||
mods::hook::uninstall<daObjMasterSword_c__execute>(svc_hook);
|
mods::hook::uninstall<daObjMasterSword_c__execute>(svc_hook);
|
||||||
|
|
||||||
|
mods::hook::uninstall<dScnName_c__changeGameScene>();
|
||||||
|
|
||||||
return MOD_OK;
|
return MOD_OK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
#include "../generator/randomizer.hpp"
|
#include "../generator/randomizer.hpp"
|
||||||
#include "../generator/utility/text.hpp"
|
#include "../generator/utility/text.hpp"
|
||||||
#include "../generator/utility/string.hpp"
|
#include "../generator/utility/string.hpp"
|
||||||
|
#include "../generator/logic/entrance_shuffle.hpp"
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <mods/svc/log.hpp>
|
#include <mods/svc/log.hpp>
|
||||||
@@ -136,11 +137,11 @@ std::optional<std::string> RandomizerContext::WriteToFile() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const auto& [key, override] : mEntranceOverrides) {
|
for (const auto& [key, override] : mEntranceOverrides) {
|
||||||
out["mEntranceOverrides"][key] = std::bit_cast<int>(override);
|
out["mEntranceOverrides"][std::bit_cast<uint64_t>(key)] = std::bit_cast<uint64_t>(override);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto& [key, override] : mReturnToPlaceOverrides) {
|
for (const auto& [key, override] : mReturnToPlaceOverrides) {
|
||||||
out["mReturnToPlaceOverrides"][key] = std::bit_cast<int>(override);
|
out["mReturnToPlaceOverrides"][key] = std::bit_cast<uint64_t>(override);
|
||||||
}
|
}
|
||||||
|
|
||||||
seedData << YAML::Dump(out);
|
seedData << YAML::Dump(out);
|
||||||
@@ -320,15 +321,15 @@ std::optional<std::string> RandomizerContext::LoadFromHash(const std::string& ha
|
|||||||
|
|
||||||
// Entrance Overrides
|
// Entrance Overrides
|
||||||
for (const auto& entranceNode : in["mEntranceOverrides"]) {
|
for (const auto& entranceNode : in["mEntranceOverrides"]) {
|
||||||
auto key = entranceNode.first.as<int>();
|
const auto key = std::bit_cast<EntranceOverride>(entranceNode.first.as<uint64_t>());;
|
||||||
auto override = std::bit_cast<EntranceOverride>(entranceNode.second.as<int>());
|
const auto override = std::bit_cast<EntranceOverride>(entranceNode.second.as<uint64_t>());
|
||||||
this->mEntranceOverrides[key] = override;
|
this->mEntranceOverrides[key] = override;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return to Place Overrides
|
// Return to Place Overrides
|
||||||
for (const auto& entranceNode : in["mReturnToPlaceOverrides"]) {
|
for (const auto& entranceNode : in["mReturnToPlaceOverrides"]) {
|
||||||
auto key = entranceNode.first.as<int>();
|
auto key = entranceNode.first.as<int>();
|
||||||
auto override = std::bit_cast<EntranceOverride>(entranceNode.second.as<int>());
|
const auto override = std::bit_cast<EntranceOverride>(entranceNode.second.as<uint64_t>());
|
||||||
this->mReturnToPlaceOverrides[key] = override;
|
this->mReturnToPlaceOverrides[key] = override;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -862,16 +863,21 @@ int randomizer_getItemAtLocation(const std::string& locationName) {
|
|||||||
|
|
||||||
void randomizer_checkAndOverrideEntranceData(const char*& stageName, s8& roomNo, s16& pointNo, s8& mapLayer) {
|
void randomizer_checkAndOverrideEntranceData(const char*& stageName, s8& roomNo, s16& pointNo, s8& mapLayer) {
|
||||||
RandomizerContext::EntranceOverride override = {
|
RandomizerContext::EntranceOverride override = {
|
||||||
static_cast<u8>(getStageID(stageName)), roomNo, static_cast<s8>(pointNo), mapLayer
|
.stageId = static_cast<u8>(getStageID(stageName)), .roomNo = roomNo, .mapLayer = mapLayer, .pointNo = static_cast<s16>(pointNo)};
|
||||||
};
|
|
||||||
|
|
||||||
int key = std::bit_cast<int>(override);
|
// Debugging: Dump mEntranceOverrides
|
||||||
if (randomizer_GetContext().mEntranceOverrides.contains(key)) {
|
// for (const auto& [key,val] : randomizer_GetContext().mEntranceOverrides) {
|
||||||
auto& newOverride = randomizer_GetContext().mEntranceOverrides[key];
|
// mods::log::debug("({},{},{},{}) -> ({},{},{},{})",key.stageId,key.roomNo,key.mapLayer,key.pointNo,val.stageId,val.roomNo,val.mapLayer,val.pointNo);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// mods::log::debug("Original Stage:{}, {}, {}, {}",stageName,roomNo,pointNo,mapLayer);
|
||||||
|
if (randomizer_GetContext().mEntranceOverrides.contains(override)) {
|
||||||
|
auto& newOverride = randomizer_GetContext().mEntranceOverrides[override];
|
||||||
stageName = allStages[newOverride.stageId];
|
stageName = allStages[newOverride.stageId];
|
||||||
pointNo = newOverride.pointNo;
|
pointNo = newOverride.pointNo;
|
||||||
roomNo = newOverride.roomNo;
|
roomNo = newOverride.roomNo;
|
||||||
mapLayer = newOverride.mapLayer;
|
mapLayer = newOverride.mapLayer;
|
||||||
|
// mods::log::debug("New Stage:{}, {}, {}, {}",stageName,roomNo,pointNo,mapLayer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1723,39 +1729,18 @@ RandomizerContext WriteSeedData(randomizer::logic::world::World* world) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Entrance Overrides
|
|
||||||
if (world->Setting("Mirror Chamber Access") == "Closed") {
|
|
||||||
// Set exiting the Arbiter's Grounds Boss Room to spawn at the Arbiter's Grounds entrance
|
|
||||||
// if mirror chamber access is closed
|
|
||||||
RandomizerContext::EntranceOverride original = {
|
|
||||||
StageIDs::Mirror_Chamber,
|
|
||||||
4,
|
|
||||||
0,
|
|
||||||
-1
|
|
||||||
};
|
|
||||||
|
|
||||||
RandomizerContext::EntranceOverride override = {
|
|
||||||
StageIDs::Bulblin_Camp,
|
|
||||||
3,
|
|
||||||
3,
|
|
||||||
-1
|
|
||||||
};
|
|
||||||
|
|
||||||
randoData.mEntranceOverrides[std::bit_cast<int>(original)] = override;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Vanilla Return to Place Overrides. Will need to change when boss/miniboss ER is implemented
|
// Vanilla Return to Place Overrides. Will need to change when boss/miniboss ER is implemented
|
||||||
static const std::list<std::pair<std::list<int>, RandomizerContext::EntranceOverride>> defaultPlaceOverrides{
|
static const std::vector<std::pair<std::vector<int>, RandomizerContext::EntranceOverride>> defaultPlaceOverrides{
|
||||||
{{Forest_Temple, Ook, Diababa}, {Forest_Temple, 22, 0, -1}},
|
{{Forest_Temple, Ook, Diababa}, {.stageId = Forest_Temple, .roomNo = 22, .mapLayer = -1, .pointNo = 0}},
|
||||||
{{Goron_Mines, Dangoro, Fyrus}, {Goron_Mines, 1, 0, -1}},
|
{{Goron_Mines, Dangoro, Fyrus}, {.stageId = Goron_Mines, .roomNo = 1, .mapLayer = -1, .pointNo = 0}},
|
||||||
{{Lakebed_Temple, Deku_Toad, Morpheel}, {Lakebed_Temple, 0, 0, -1}},
|
{{Lakebed_Temple, Deku_Toad, Morpheel}, {.stageId = Lakebed_Temple, .roomNo = 0, .mapLayer = -1, .pointNo = 0}},
|
||||||
{{Arbiters_Grounds, Death_Sword, Stallord}, {Arbiters_Grounds, 0, 0, -1}},
|
{{Arbiters_Grounds, Death_Sword, Stallord}, {.stageId = Arbiters_Grounds, .roomNo = 0, .mapLayer = -1, .pointNo = 0}},
|
||||||
{{Snowpeak_Ruins, Darkhammer, Blizzeta}, {Snowpeak_Ruins, 0, 0, -1}},
|
{{Snowpeak_Ruins, Darkhammer, Blizzeta}, {.stageId = Snowpeak_Ruins, .roomNo = 0, .mapLayer = -1, .pointNo = 0}},
|
||||||
{{Temple_of_Time, Darknut, Armogohma}, {Temple_of_Time, 0, 0, -1}},
|
{{Temple_of_Time, Darknut, Armogohma}, {.stageId = Temple_of_Time, .roomNo = 0, .mapLayer = -1, .pointNo = 0}},
|
||||||
{{City_in_the_Sky, Aeralfos, Argorok}, {City_in_the_Sky, 0, 3, -1}},
|
{{City_in_the_Sky, Aeralfos, Argorok}, {.stageId = City_in_the_Sky, .roomNo = 0, .mapLayer = -1, .pointNo = 3}},
|
||||||
{{Palace_of_Twilight, Phantom_Zant_1,
|
{{Palace_of_Twilight, Phantom_Zant_1,
|
||||||
Phantom_Zant_2, Zant_Main_Room, Zant_Fight}, {Palace_of_Twilight, 0, 0, -1}},
|
Phantom_Zant_2, Zant_Main_Room, Zant_Fight}, {.stageId = Palace_of_Twilight, .roomNo = 0, .mapLayer = -1, .pointNo = 0}},
|
||||||
{{Hyrule_Castle, Ganondorf_Castle, Ganondorf_Field}, {Hyrule_Castle, 11, 0, -1}},
|
{{Hyrule_Castle, Ganondorf_Castle, Ganondorf_Field}, {.stageId = Hyrule_Castle, .roomNo = 11, .mapLayer = -1, .pointNo = 0}},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Return to Place Overrides
|
// Return to Place Overrides
|
||||||
@@ -1765,6 +1750,48 @@ RandomizerContext WriteSeedData(randomizer::logic::world::World* world) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply entrance randomization
|
||||||
|
auto shuffleData = LOAD_EMBED_YAML(RANDO_DATA_PATH "entrance_shuffle_data.yaml");
|
||||||
|
if (world->AnyEntranceRandomizerEnabled()) {
|
||||||
|
for (const auto& entrance : world->GetShuffledEntrances()) {
|
||||||
|
randomizer::logic::entrance::Entrance* replacesEntrance = entrance->GetReplaces();
|
||||||
|
RandomizerContext::EntranceOverride forward = {.stageId = entrance->GetStageId(), .roomNo = entrance->GetRoomNo(), .mapLayer = entrance->GetLayerNo(), .pointNo = entrance->GetPointNo()};
|
||||||
|
RandomizerContext::EntranceOverride replaces = {.stageId = replacesEntrance->GetStageId(), .roomNo = replacesEntrance->GetRoomNo(), .mapLayer = replacesEntrance->GetLayerNo(), .pointNo = replacesEntrance->GetPointNo()};
|
||||||
|
if (forward.stageId == 0xFF || replaces.stageId == 0xFF || forward.roomNo == -1 || replaces.roomNo == -1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
randoData.mEntranceOverrides[forward] = replaces;
|
||||||
|
|
||||||
|
// Set an override for the second door, if the entrance is coupled
|
||||||
|
if (entrance->getCoupledDoor() != -1) {
|
||||||
|
RandomizerContext::EntranceOverride coupled = {.stageId = entrance->GetStageId(), .roomNo = entrance->GetRoomNo(), .mapLayer = entrance->GetLayerNo(), .pointNo = entrance->getCoupledDoor()};
|
||||||
|
randoData.mEntranceOverrides[coupled] = replaces;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (world->Setting("Mirror Chamber Access") == "Closed") {
|
||||||
|
// Set exiting the Arbiter's Grounds Boss Room to spawn at the Arbiter's Grounds entrance
|
||||||
|
// if mirror chamber access is closed
|
||||||
|
RandomizerContext::EntranceOverride original = {
|
||||||
|
.stageId = StageIDs::Mirror_Chamber,
|
||||||
|
.roomNo = 4,
|
||||||
|
.mapLayer = -1,
|
||||||
|
.pointNo = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
RandomizerContext::EntranceOverride override = {
|
||||||
|
.stageId = StageIDs::Bulblin_Camp,
|
||||||
|
.roomNo = 3,
|
||||||
|
.mapLayer = -1,
|
||||||
|
.pointNo = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check if we are already overriding the bulblin camp entrance, and correctly override the entrance
|
||||||
|
const auto& it = randoData.mEntranceOverrides.find(override);
|
||||||
|
randoData.mEntranceOverrides[original] = (it != randoData.mEntranceOverrides.end()) ? it->second : override;
|
||||||
|
}
|
||||||
|
|
||||||
return std::move(randoData);
|
return std::move(randoData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1807,4 +1834,4 @@ bool GenerateAndWriteSeed(std::string& generationStatusMsg) {
|
|||||||
|
|
||||||
generationStatusMsg = fmt::format("Seed generated! Hash: {}", randoData.mHash);
|
generationStatusMsg = fmt::format("Seed generated! Hash: {}", randoData.mHash);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
class RandomizerContext {
|
class RandomizerContext {
|
||||||
public:
|
public:
|
||||||
static constexpr size_t ACTR_CRC_SIZE = 32;
|
static constexpr size_t ACTR_CRC_SIZE = 32;
|
||||||
static constexpr size_t TGSC_CRC_SIZE = 35; // 3 extra bytes for scale x, y, z
|
static constexpr size_t TGSC_CRC_SIZE = 35; // 3 extra bytes for scale x, y, z
|
||||||
static constexpr size_t OBJ_DELETE_SIZE = 1;
|
static constexpr size_t OBJ_DELETE_SIZE = 1;
|
||||||
static constexpr u8 ROOM_STAGE = 0xFF;
|
static constexpr u8 ROOM_STAGE = 0xFF;
|
||||||
|
|
||||||
@@ -37,7 +37,7 @@ public:
|
|||||||
std::unordered_map<u8, std::list<u8>> mStartRegionFlags{};
|
std::unordered_map<u8, std::list<u8>> mStartRegionFlags{};
|
||||||
std::list<u8> mStartingInventory{};
|
std::list<u8> mStartingInventory{};
|
||||||
|
|
||||||
struct itemLocationData{
|
struct itemLocationData {
|
||||||
int itemId{0xFF};
|
int itemId{0xFF};
|
||||||
int stage{0xFF};
|
int stage{0xFF};
|
||||||
u16 flag{0xFFFF};
|
u16 flag{0xFFFF};
|
||||||
@@ -50,7 +50,7 @@ public:
|
|||||||
std::unordered_map<u16, u8> mSkyCharacterOverrides{};
|
std::unordered_map<u16, u8> mSkyCharacterOverrides{};
|
||||||
std::unordered_map<u16, u8> mGoldenWolfOverrides{};
|
std::unordered_map<u16, u8> mGoldenWolfOverrides{};
|
||||||
std::unordered_map<u16, u8> mShopOverrides{};
|
std::unordered_map<u16, u8> mShopOverrides{};
|
||||||
std::unordered_map<u16, u16> mTwilitInsectOverrides{}; // Just used in tracker for now
|
std::unordered_map<u16, u16> mTwilitInsectOverrides{}; // Just used in tracker for now
|
||||||
std::unordered_map<u32, itemLocationData> mFlowItemMessageOverrides{};
|
std::unordered_map<u32, itemLocationData> mFlowItemMessageOverrides{};
|
||||||
std::unordered_map<std::string, itemLocationData> mItemLocations{};
|
std::unordered_map<std::string, itemLocationData> mItemLocations{};
|
||||||
|
|
||||||
@@ -71,15 +71,26 @@ public:
|
|||||||
// Map of language -> map of key -> string
|
// Map of language -> map of key -> string
|
||||||
std::unordered_map<int, std::unordered_map<u32, std::string>> mTextOverrides{};
|
std::unordered_map<int, std::unordered_map<u32, std::string>> mTextOverrides{};
|
||||||
|
|
||||||
|
// Padded to 8 bytes so bit_cast can convert it to a uint64_t
|
||||||
struct EntranceOverride {
|
struct EntranceOverride {
|
||||||
u8 stageId = 0xFF;
|
u8 stageId = 0xFF;
|
||||||
s8 roomNo = -1;
|
s8 roomNo = -1;
|
||||||
s8 pointNo = -1;
|
|
||||||
s8 mapLayer = -1;
|
s8 mapLayer = -1;
|
||||||
|
u8 _pad0;
|
||||||
|
s16 pointNo = -1;
|
||||||
|
u16 _pad1;
|
||||||
|
bool operator==(const EntranceOverride& rhs) const = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
// keyed by stageId << 24 | pointNo << 16 | roomNo << 8 | mapLayer
|
// Packs an EntranceOverride into a uint64_t
|
||||||
std::unordered_map<int, EntranceOverride> mEntranceOverrides{};
|
struct EntranceOverrideHash {
|
||||||
|
size_t operator()(const EntranceOverride& x) const noexcept {
|
||||||
|
return std::hash<uint64_t>{}(std::bit_cast<uint64_t>(x));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unordered_map<EntranceOverride, EntranceOverride, EntranceOverrideHash>
|
||||||
|
mEntranceOverrides{};
|
||||||
|
|
||||||
// Overrides for returning to spawn. Keyed by stageId
|
// Overrides for returning to spawn. Keyed by stageId
|
||||||
std::unordered_map<int, EntranceOverride> mReturnToPlaceOverrides{};
|
std::unordered_map<int, EntranceOverride> mReturnToPlaceOverrides{};
|
||||||
@@ -153,7 +164,7 @@ public:
|
|||||||
|
|
||||||
static constexpr u8 EVENT_ITEM_QUEUE_SIZE = 10;
|
static constexpr u8 EVENT_ITEM_QUEUE_SIZE = 10;
|
||||||
|
|
||||||
RandomizerState() {mInitialized = false;}
|
RandomizerState() { mInitialized = false; }
|
||||||
|
|
||||||
int _create();
|
int _create();
|
||||||
int _delete();
|
int _delete();
|
||||||
@@ -161,19 +172,19 @@ public:
|
|||||||
int draw();
|
int draw();
|
||||||
void addItemToEventQueue(u8 item);
|
void addItemToEventQueue(u8 item);
|
||||||
void initGiveItemToPlayer();
|
void initGiveItemToPlayer();
|
||||||
//void handleBonkDamage();
|
// void handleBonkDamage();
|
||||||
void handleTimeOfDayChange();
|
void handleTimeOfDayChange();
|
||||||
void handleTimeSpeed();
|
void handleTimeSpeed();
|
||||||
void offLoad();
|
void offLoad();
|
||||||
|
|
||||||
void handlePoeItem(u8 bitSw);
|
void handlePoeItem(u8 bitSw);
|
||||||
|
|
||||||
u8 getGiveItemToPlayerStatus() const { return mEventItemStatus;}
|
u8 getGiveItemToPlayerStatus() const { return mEventItemStatus; }
|
||||||
u8 getTimeChange() const { return mTimeChange; }
|
u8 getTimeChange() const { return mTimeChange; }
|
||||||
bool getRoomReloadingState() const { return mRoomReloadingState; }
|
bool getRoomReloadingState() const { return mRoomReloadingState; }
|
||||||
bool getHasPendingToDChange() const { return mHasPendingToDChange; }
|
bool getHasPendingToDChange() const { return mHasPendingToDChange; }
|
||||||
|
|
||||||
void setGiveItemToPlayerStatus(u8 status) { mEventItemStatus = status;}
|
void setGiveItemToPlayerStatus(u8 status) { mEventItemStatus = status; }
|
||||||
void setHasPendingToDChange(bool hasPending) { mHasPendingToDChange = hasPending; }
|
void setHasPendingToDChange(bool hasPending) { mHasPendingToDChange = hasPending; }
|
||||||
void setTimeChange(u8 newTimeChange) { mTimeChange = newTimeChange; }
|
void setTimeChange(u8 newTimeChange) { mTimeChange = newTimeChange; }
|
||||||
void setRoomReloadingState(bool newState) { mRoomReloadingState = newState; }
|
void setRoomReloadingState(bool newState) { mRoomReloadingState = newState; }
|
||||||
@@ -217,7 +228,8 @@ int randomizer_getItemAtLocation(const std::string& locationName);
|
|||||||
/*
|
/*
|
||||||
* @brief Overrides the given entrance paramaters if an override exists for them
|
* @brief Overrides the given entrance paramaters if an override exists for them
|
||||||
*/
|
*/
|
||||||
void randomizer_checkAndOverrideEntranceData(const char*& i_Name, s8& i_RoomNo, s16& i_Point, s8& i_Layer);
|
void randomizer_checkAndOverrideEntranceData(
|
||||||
|
const char*& i_Name, s8& i_RoomNo, s16& i_Point, s8& i_Layer);
|
||||||
/*
|
/*
|
||||||
* @brief Puts the associated flag into the randomizer state's temporary flag
|
* @brief Puts the associated flag into the randomizer state's temporary flag
|
||||||
* variable. This allows the tracker/Archipelago to know a location has been checked
|
* variable. This allows the tracker/Archipelago to know a location has been checked
|
||||||
@@ -285,4 +297,4 @@ u32 getStageObjCRC32(u8* data, size_t size);
|
|||||||
*/
|
*/
|
||||||
bool GenerateAndWriteSeed(std::string& generationStatusMsg);
|
bool GenerateAndWriteSeed(std::string& generationStatusMsg);
|
||||||
|
|
||||||
#endif //DUSK_RANDOMIZER_CONTEXT_HPP
|
#endif // DUSK_RANDOMIZER_CONTEXT_HPP
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ bool activateSeed(const char* hash) {
|
|||||||
svc_mng.item->set_check_resolver(mod_ctx, nullptr, resolve_check, nullptr, &s_check_resolver);
|
svc_mng.item->set_check_resolver(mod_ctx, nullptr, resolve_check, nullptr, &s_check_resolver);
|
||||||
svc_mng.item->observe_gives(mod_ctx, observe_give, nullptr, &s_check_observer);
|
svc_mng.item->observe_gives(mod_ctx, observe_give, nullptr, &s_check_observer);
|
||||||
|
|
||||||
registerStageEdits();
|
registerStageEdits();
|
||||||
mods::log::info("activated seed {}", ctx.mHash);
|
mods::log::info("activated seed {}", ctx.mHash);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -292,6 +292,62 @@ void registerStageEdits() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set link to spawn outside of his house if we are playing with entrance rando and aren't already spawning in a dungeon
|
||||||
|
void registerStartingLocation() {
|
||||||
|
auto& ctx = randomizer_GetContext();
|
||||||
|
|
||||||
|
if (ctx.mEntranceOverrides.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that we aren't playing only with the setting "Mirror Chamber Access") == "Closed"
|
||||||
|
bool isExclusivelyMirrorChamber = false;
|
||||||
|
const auto& mirrorChamberIt = ctx.mEntranceOverrides.find(RandomizerContext::EntranceOverride{
|
||||||
|
.stageId = StageIDs::Mirror_Chamber,
|
||||||
|
.roomNo = 4,
|
||||||
|
.mapLayer = -1,
|
||||||
|
.pointNo = 0
|
||||||
|
});
|
||||||
|
if (ctx.mEntranceOverrides.size() == 1 && mirrorChamberIt != ctx.mEntranceOverrides.end()) {
|
||||||
|
if (mirrorChamberIt->second == RandomizerContext::EntranceOverride{
|
||||||
|
.stageId = StageIDs::Bulblin_Camp,
|
||||||
|
.roomNo = 3,
|
||||||
|
.mapLayer = -1,
|
||||||
|
.pointNo = 3,
|
||||||
|
}) {
|
||||||
|
isExclusivelyMirrorChamber = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isExclusivelyMirrorChamber) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr std::array<std::string_view,9> kDungeonNames = {
|
||||||
|
"D_MN01","D_MN04","D_MN05","D_MN06","D_MN07","D_MN08","D_MN09","D_MN10","D_MN11"
|
||||||
|
};
|
||||||
|
|
||||||
|
dSv_player_return_place_c& returnPlace = dComIfGs_getSaveData()->mPlayer.getPlayerReturnPlace();
|
||||||
|
bool isDungeon = false;
|
||||||
|
for (const auto& name : kDungeonNames) {
|
||||||
|
if (returnPlace.getName() == name) {
|
||||||
|
isDungeon = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isDungeon) {
|
||||||
|
// Force-set the entrance without the override
|
||||||
|
g_dComIfG_gameInfo.play.mNextStage.getStartStage()->set(returnPlace.getName(), returnPlace.getRoomNo(), returnPlace.mPlayerStatus, -1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the spawn to outside of link's house, which will get overrided later if we are replacing it
|
||||||
|
returnPlace.set("F_SP103",1,1);
|
||||||
|
// g_dComIfG_gameInfo.play.mNextStage.getStartStage()->set("F_SP103", 1, 1, -1);
|
||||||
|
dComIfGp_setNextStage("F_SP103", 1, 1, -1);
|
||||||
|
}
|
||||||
|
|
||||||
void onNewSave(ModContext*, uint32_t, void*) {
|
void onNewSave(ModContext*, uint32_t, void*) {
|
||||||
const std::string hash = g_pending_seed_hash;
|
const std::string hash = g_pending_seed_hash;
|
||||||
if (hash.empty())
|
if (hash.empty())
|
||||||
@@ -351,4 +407,4 @@ void update() {
|
|||||||
g_randomizerState.execute();
|
g_randomizerState.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,4 +35,5 @@ void update();
|
|||||||
void deactivateSeed();
|
void deactivateSeed();
|
||||||
void setupRandomizerFile();
|
void setupRandomizerFile();
|
||||||
void registerStageEdits();
|
void registerStageEdits();
|
||||||
}
|
void registerStartingLocation();
|
||||||
|
}
|
||||||
|
|||||||
@@ -420,6 +420,17 @@ ModResult buildSeedOptionsTab(ModContext* ctx, UiWindowHandle, UiElementHandle l
|
|||||||
add_select_setting(leftPane, "Lower Hyrule Castle Chandelier");
|
add_select_setting(leftPane, "Lower Hyrule Castle Chandelier");
|
||||||
add_select_setting(leftPane, "Skip Bridge Donation");
|
add_select_setting(leftPane, "Skip Bridge Donation");
|
||||||
|
|
||||||
|
add_section(leftPane, "Entrance Randomizer Settings");
|
||||||
|
add_select_setting(leftPane, "Randomize Starting Spawn");
|
||||||
|
add_select_setting(leftPane, "Randomize Dungeon Entrances");
|
||||||
|
add_select_setting(leftPane, "Randomize Boss Entrances");
|
||||||
|
add_select_setting(leftPane, "Randomize Grotto Entrances");
|
||||||
|
add_select_setting(leftPane, "Randomize Cave Entrances");
|
||||||
|
add_select_setting(leftPane, "Randomize Interior Entrances");
|
||||||
|
add_select_setting(leftPane, "Randomize Overworld Entrances");
|
||||||
|
add_select_setting(leftPane, "Decouple Double Door Entrances");
|
||||||
|
add_select_setting(leftPane, "Decouple Entrances");
|
||||||
|
|
||||||
add_section(leftPane, "Additional Settings");
|
add_section(leftPane, "Additional Settings");
|
||||||
add_select_setting(leftPane, "Starting Time of Day");
|
add_select_setting(leftPane, "Starting Time of Day");
|
||||||
add_select_setting(leftPane, "Logic Transform Anywhere");
|
add_select_setting(leftPane, "Logic Transform Anywhere");
|
||||||
|
|||||||
Reference in New Issue
Block a user