mirror of
https://github.com/TwilitRealm/dusklight
synced 2026-08-25 06:41:30 -04:00
320 lines
8.9 KiB
C++
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
|