#ifndef DUSK_RANDOMIZER_CONTEXT_HPP #define DUSK_RANDOMIZER_CONTEXT_HPP #include #include #include #include #include #include #include #include #include #include /* * 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 mSettings{}; std::list mStartEventFlags{}; std::unordered_map> mStartRegionFlags{}; std::list mStartingInventory{}; struct itemLocationData { int itemId{0xFF}; int stage{0xFF}; u16 flag{0xFFFF}; }; std::unordered_map mTreasureChestOverrides{}; std::unordered_map mPoeOverrides{}; std::unordered_map mFreestandingItemOverrides{}; std::unordered_map mBugRewardOverrides{}; std::unordered_map mSkyCharacterOverrides{}; std::unordered_map mGoldenWolfOverrides{}; std::unordered_map mShopOverrides{}; std::unordered_map mTwilitInsectOverrides{}; // Just used in tracker for now std::unordered_map mItemLocations{}; u8 mStartHour{0}; u8 mMapBits{}; struct ActorData { std::vector bytes{}; std::string flow{}; }; std::unordered_map> mObjectPatches{}; std::unordered_map> mObjectAdditions{}; // std::unordered_map> mTgscDeletions{}; struct FlowReference { std::optional nativeId{}; std::string name{}; }; enum class FlowNodeType : u8 { MESSAGE, BRANCH, EVENT, }; struct FlowNode { FlowNodeType type{}; u8 group{}; std::optional patchIndex{}; std::string name{}; std::string operation{}; FlowReference message{}; FlowReference next{}; std::vector 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 text{}; }; std::vector mFlowNodes{}; std::vector mCustomMessages{}; // Map of language -> map of key -> string std::unordered_map> 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{}(std::bit_cast(x)); } }; std::unordered_map mEntranceOverrides{}; // Overrides for returning to spawn. Keyed by stageId std::unordered_map mReturnToPlaceOverrides{}; std::optional WriteToFile(); std::optional 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 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(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(rawBytes[i]); } return oss.str(); } /** * Helper function to convert hex string to raw bytes */ std::vector 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