Files
dusklight/mods/randomizer/src/randomizer_context.hpp
T
2026-08-21 12:15:33 -06:00

320 lines
8.9 KiB
C++

#ifndef DUSK_RANDOMIZER_CONTEXT_HPP
#define DUSK_RANDOMIZER_CONTEXT_HPP
#include <dolphin/types.h>
#include <array>
#include <filesystem>
#include <iomanip>
#include <list>
#include <optional>
#include <sstream>
#include <string>
#include <unordered_map>
#include <vector>
/*
* Class holding all the information necessary for playing
* the current randomizer seed
*/
class RandomizerContext {
public:
static constexpr u32 FORMAT_VERSION = 3;
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 OBJ_DELETE_SIZE = 1;
static constexpr u8 ROOM_STAGE = 0xFF;
RandomizerContext() = default;
bool mCreatingSave{false};
u32 mSeedID{0};
std::string mHash{""};
// Maps enum of necessary setting to enum of value
std::unordered_map<int, int> mSettings{};
std::list<u16> mStartEventFlags{};
std::unordered_map<u8, std::list<u8>> mStartRegionFlags{};
std::list<u8> mStartingInventory{};
struct itemLocationData {
int itemId{0xFF};
int stage{0xFF};
u16 flag{0xFFFF};
};
std::unordered_map<u16, u8> mTreasureChestOverrides{};
std::unordered_map<u16, u8> mPoeOverrides{};
std::unordered_map<u16, u8> mFreestandingItemOverrides{};
std::unordered_map<u8, u8> mBugRewardOverrides{};
std::unordered_map<u16, u8> mSkyCharacterOverrides{};
std::unordered_map<u16, u8> mGoldenWolfOverrides{};
std::unordered_map<u16, u8> mShopOverrides{};
std::unordered_map<u16, u16> mTwilitInsectOverrides{}; // Just used in tracker for now
std::unordered_map<std::string, itemLocationData> mItemLocations{};
u8 mStartHour{0};
u8 mMapBits{};
struct ActorData {
std::vector<u8> bytes{};
std::string flow{};
};
std::unordered_map<u32, std::unordered_map<u32, ActorData>> mObjectPatches{};
std::unordered_map<u32, std::list<ActorData>> mObjectAdditions{};
// std::unordered_map<u32, std::unordered_set<u32>> mTgscDeletions{};
struct FlowReference {
std::optional<u16> nativeId{};
std::string name{};
};
enum class FlowNodeType : u8 {
MESSAGE,
BRANCH,
EVENT,
};
struct FlowNode {
FlowNodeType type{};
u8 group{};
std::optional<u16> patchIndex{};
std::string name{};
std::string operation{};
FlowReference message{};
FlowReference next{};
std::vector<FlowReference> results{};
u32 parameters{};
};
struct MessageStyleData {
u16 eventLabelId{};
u8 speaker{0x24};
u8 boxKind{};
u8 drawType{};
u8 boxPosition{};
u8 lineAlignment{};
u8 speakerMood{};
u8 cameraAttr{};
u8 talkAnim{0x02};
u8 faceAnim{0x03};
u16 trailingData{0x0400};
};
struct CustomMessage {
u8 group{};
std::string name{};
MessageStyleData style{};
std::unordered_map<int, std::string> text{};
};
std::vector<FlowNode> mFlowNodes{};
std::vector<CustomMessage> mCustomMessages{};
// Map of language -> map of key -> string
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 {
u8 stageId = 0xFF;
s8 roomNo = -1;
s8 mapLayer = -1;
u8 _pad0;
s16 pointNo = -1;
u16 _pad1;
bool operator==(const EntranceOverride& rhs) const = default;
};
// Packs an EntranceOverride into a uint64_t
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
std::unordered_map<int, EntranceOverride> mReturnToPlaceOverrides{};
std::optional<std::string> WriteToFile();
std::optional<std::string> LoadFromHash(const std::string& hash);
std::filesystem::path GetSeedDataPath() const;
enum Settings {
HYRULE_BARRIER_REQUIREMENTS,
HYRULE_BARRIER_FUSED_SHADOWS,
HYRULE_BARRIER_MIRROR_SHARDS,
HYRULE_BARRIER_POE_SOULS,
HYRULE_BARRIER_HEARTS,
HYRULE_BARRIER_DUNGEONS,
HYRULE_BIG_KEY_REQUIREMENTS,
HYRULE_BIG_KEY_FUSED_SHADOWS,
HYRULE_BIG_KEY_MIRROR_SHARDS,
HYRULE_BIG_KEY_POE_SOULS,
HYRULE_BIG_KEY_HEARTS,
HYRULE_BIG_KEY_DUNGEONS,
PALACE_OF_TWILIGHT_REQUIREMENTS,
TEMPLE_OF_TIME_SWORD_REQUIREMENT,
SKIP_MINOR_CUTSCENES,
SKIP_MAJOR_CUTSCENES,
SKIP_BRIDGE_DONATION,
MIRROR_CHAMBER_ACCESS,
};
enum Options {
ON,
OFF,
NONE,
VANILLA,
OPEN,
FUSED_SHADOWS,
MIRROR_SHARDS,
POE_SOULS,
HEARTS,
DUNGEONS,
WOODEN_SWORD,
ORDON_SWORD,
MASTER_SWORD,
LIGHT_SWORD,
BARRIER,
CLOSED,
};
static int SettingToEnum(const std::string& settingName);
static int OptionToEnum(const std::string& optionName);
};
/*
* Class holding seed-agnostic dynamic information about current randomizer play.
* This gets reset when resetting to the title screen.
*/
class RandomizerState {
public:
enum TimeChange {
NO_CHANGE = 0,
CHANGE_TO_NIGHT,
CHANGE_TO_DAY,
};
RandomizerState() { mInitialized = false; }
int _create();
int _delete();
int execute();
int draw();
// void handleBonkDamage();
void handleTimeOfDayChange();
void handleTimeSpeed();
void offLoad();
u8 getTimeChange() const { return mTimeChange; }
bool getRoomReloadingState() const { return mRoomReloadingState; }
bool getHasPendingToDChange() const { return mHasPendingToDChange; }
void setHasPendingToDChange(bool hasPending) { mHasPendingToDChange = hasPending; }
void setTimeChange(u8 newTimeChange) { mTimeChange = newTimeChange; }
void setRoomReloadingState(bool newState) { mRoomReloadingState = newState; }
bool mInitialized{false};
bool mHasPendingToDChange{false};
u8 mTimeChange{};
bool mRoomReloadingState{false};
int mFoolishItemCount{0};
bool mUpdateTracker{false};
bool mShowTracker{false};
u16 mTrackerTempEventFlag{0};
struct {
int stage{-1};
int flag{-1};
} mTrackerTempSwitchFlag;
struct {
int stage{-1};
int flag{-1};
} mTrackerTempItemFlag;
};
extern RandomizerState g_randomizerState;
RandomizerContext& randomizer_GetContext();
bool randomizer_IsActive();
int randomizer_getItemAtLocation(const std::string& locationName);
/*
* @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, u32& lastMode);
/*
* @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
* when the item is received instead of some indeterminate amount of time afterward.
*/
void randomizer_setTempFlag(const RandomizerContext::itemLocationData&);
bool randomizer_checkTempleOfTimeRequirement();
bool randomizer_mirrorChamberWallShouldExist();
void randomizer_returnToSpawn(bool tryDungeon);
u8 randomizer_getRandomFoolishItemModelID();
/**
* Helper function to convert raw bytes of a container to a hex string
*/
template <typename T>
std::string ContainerToHexString(const T& container, bool includePrefix = true) {
std::ostringstream oss;
if (includePrefix) {
oss << "0x";
}
// Get the raw byte pointer to the start of the data
const auto* rawBytes = reinterpret_cast<const u8*>(container.data());
// Calculate total byte size (number of elements * size of each element)
size_t totalBytes = container.size() * sizeof(typename T::value_type);
oss << std::hex << std::setfill('0') << std::uppercase;
for (size_t i = 0; i < totalBytes; ++i) {
// static_cast to not u8 is necessary so oss treats it as a number, not a char
oss << std::setw(2) << static_cast<u16>(rawBytes[i]);
}
return oss.str();
}
/**
* Helper function to convert hex string to raw bytes
*/
std::vector<u8> HexToBytes(std::string hex);
/*
* Gets the current stage id, room no, and layer no in the format for a key in mActorPatches
*/
u32 getActorPatchesCurrentStageKey(u8 roomNo);
/*
* Gets the CRC32 hash of an actors name, parameters, position, and angle
*/
u32 getStageObjCRC32(u8* data, size_t size);
/*
* Generates a seed and writes the necessary seed files to the players seed directory
* Returns true if generation was successful, false otherwise.
*/
bool GenerateAndWriteSeed(std::string& generationStatusMsg);
#endif // DUSK_RANDOMIZER_CONTEXT_HPP