├── .github └── FUNDING.yml ├── src └── core │ ├── FFXRando.cpp │ ├── BytesHelper.hpp │ ├── BytesHelper.cpp │ ├── Initializer.hpp │ ├── ShopRandomizer.cpp │ ├── ItemRandomizer.cpp │ ├── RandomEncounterRandomizer.cpp │ ├── Randomizer.hpp │ ├── Initializer.cpp │ ├── GearRandomizer.cpp │ ├── SphereGridRandomizer.cpp │ ├── DataEnums.hpp │ ├── PlayerCharacterRandomizer.cpp │ ├── EnemyDataRandomizer.cpp │ ├── GUI.cpp │ └── GUI.hpp ├── .gitignore ├── LICENSE ├── FFXRando.sln ├── README.md └── FFXRando.vcxproj /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [nyterage] 4 | -------------------------------------------------------------------------------- /src/core/FFXRando.cpp: -------------------------------------------------------------------------------- 1 | #include "Initializer.hpp" 2 | 3 | int main() 4 | { 5 | initializer_t init; 6 | return 0; 7 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | # Raw .bin files 35 | *.bin 36 | 37 | # directories 38 | input/ 39 | output/ 40 | Release/ 41 | Debug/ 42 | x64/ 43 | .vs/ 44 | .vscode/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 nyterage 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /FFXRando.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.12.35527.113 d17.12 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FFXRando", "FFXRando.vcxproj", "{DC968DC1-55BF-4501-BD9F-AE479258F391}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {DC968DC1-55BF-4501-BD9F-AE479258F391}.Debug|x64.ActiveCfg = Debug|x64 17 | {DC968DC1-55BF-4501-BD9F-AE479258F391}.Debug|x64.Build.0 = Debug|x64 18 | {DC968DC1-55BF-4501-BD9F-AE479258F391}.Debug|x86.ActiveCfg = Debug|Win32 19 | {DC968DC1-55BF-4501-BD9F-AE479258F391}.Debug|x86.Build.0 = Debug|Win32 20 | {DC968DC1-55BF-4501-BD9F-AE479258F391}.Release|x64.ActiveCfg = Release|x64 21 | {DC968DC1-55BF-4501-BD9F-AE479258F391}.Release|x64.Build.0 = Release|x64 22 | {DC968DC1-55BF-4501-BD9F-AE479258F391}.Release|x86.ActiveCfg = Release|Win32 23 | {DC968DC1-55BF-4501-BD9F-AE479258F391}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /src/core/BytesHelper.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | struct chunk_t 14 | { 15 | std::vector data{ 0, 0 }; 16 | size_t initial_offset = 0; 17 | int index = 0; 18 | 19 | chunk_t() = default; // Empty chunk constructor 20 | 21 | chunk_t( const std::vector& bytes, size_t from, size_t to, int index ) : data(), initial_offset(), index( index ) 22 | { 23 | if (from < to && from >= 0 && to <= bytes.size()) 24 | { 25 | initial_offset = from; 26 | data.assign( bytes.begin() + from, bytes.begin() + to ); 27 | } 28 | } 29 | }; 30 | 31 | struct bytes_mapper_t 32 | { 33 | std::vector bytes; 34 | 35 | bytes_mapper_t( const std::vector& bytes ) : bytes( bytes ) 36 | { 37 | } 38 | 39 | static std::vector fileToBytes( const std::string& filepath ); 40 | 41 | static std::vector bytesToChunks( const std::vector& bytes, uint16_t assumedChunkCount, size_t chunkOffset ); 42 | 43 | static char read1Byte( const std::vector& bytes, size_t offset ); 44 | 45 | static uint16_t read2Bytes( const std::vector& bytes, size_t offset ); 46 | 47 | static uint32_t read4Bytes( const std::vector& bytes, size_t offset ); 48 | 49 | static void write1Byte( std::vector& bytes, size_t offset, int value ); 50 | 51 | static void write2Bytes( std::vector& bytes, size_t offset, int value ); 52 | 53 | static void write4Bytes( std::vector& bytes, size_t offset, int value ); 54 | 55 | static void writeDataToBytes( std::vector& bytes, const std::vector& data, size_t offset ); 56 | 57 | static void writeBytesToNewFile( const std::vector& bytes, const std::string& filepath ); 58 | 59 | virtual void test() const; 60 | }; 61 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FFXRando 2 | Randomizer for many elements of the game Final Fantasy 10 3 | 4 | # How to use 5 | 1. Extract your Final Fantasy X game files from FFX_Data.vbf utilizing the VBF Browser application (can be found on Nexus Mods, under Final Fantasy 12 mods) 6 | 2. Place extracted files into the input folder. Currently the only directory that is needed is ffx_ps2. 7 | 3. Run the executeable, chose your options and hit Randomize. 8 | 4. import the files back into the game using the VBF Browser **OR** 9 | 5. If utilizing the the FFX/X2 External File Loader mod, you can place the ffx_ps2 directory from the "output" folder into the `/data/mods/` folder in your games directory. **OR** 10 | 6. If utilizing the Fahrenheit Mod Loarder, you can place the FFXRando directory (loacted in the "output" folder) into the `/fahrenheit/modules` folder. Dont forget to edit your loadorder file to include "FFXRando"! 11 | 7. Play and have fun... hopefully. 12 | 13 | # Currently Supports 14 | 1. Field Item Randomization 15 | 2. Enemy Reward Randomization, including Gil, AP, Steal, Drop, Bribe, and Equipment. 16 | 3. Shop Randomization 17 | 4. Equipment Randomization 18 | 5. Player Stat Randomization 19 | 6. Aeon Stat Randomization 20 | 7. Starting overdrive mode Randomization 21 | 8. Enemy Elemental Affinity Randomization 22 | 9. Sphere Grid Randomization 23 | 10. Random Encounter Randomization 24 | 11. Gear customization item Randomization 25 | 12. Aeon Stat increase/ability learning item randomization 26 | 27 | # Maybe Future Additions 28 | 1. Enemy Ability randomization?? 29 | 30 | # How to Build 31 | **Currently only supports MSVC compilation** 32 | 1. Setup wxWidgets following the directions on their website. 33 | 2. Open FFXRando.sln in Visual Studio, or run msbuild on the .sln 34 | 3. If running from the command line, you may need to edit the project settings (vcxproj) to point to your wxWidgets directory. 35 | 4. If running in Visual Studio, make sure you setup your wxwidgets.props file in the property manager. 36 | 5. Compile the application and enjoy! 37 | 38 | ____ 39 | **Big shoutout to the wonderful folks in the [Cid's Salvage Ship](https://discord.gg/vEu5wkjXGv) discord for answering all my questions and giving suggestions while i was making this.** 40 | 41 | **This would not have been possible without the data mapping already done by Karifean and their [Data Parser](https://github.com/Karifean/FFXDataParser) project , as well as the [Fahrenheit](https://github.com/peppy-enterprises/fahrenheit) team** 42 | 43 | ## External Libraries 44 | 45 | This program uses the following external libraries. 46 | 47 | json (https://github.com/nlohmann/json) 48 | 49 | Copyright © 2013-2025 Niels Lohmann 50 | MIT License (see LICENSE for more information). 51 | 52 | wxWidgets (https://wxwidgets.org/) 53 | 54 | Copyright (c) 1998-2005 Julian Smart, Robert Roebling et al 55 | [wxWindows Library Licence](https://github.com/wxWidgets/wxWidgets/blob/master/docs/licence.txt) 56 | -------------------------------------------------------------------------------- /src/core/BytesHelper.cpp: -------------------------------------------------------------------------------- 1 | #include "BytesHelper.hpp" 2 | 3 | std::vector bytes_mapper_t::fileToBytes( const std::string& filepath ) { 4 | std::ifstream file( filepath, std::ios::binary ); 5 | if (!file) 6 | { 7 | std::cerr << "Failed to open file: " << filepath << std::endl; 8 | std::cout << "Please be sure your extracted ffx_ps2 folder is in the input folder" << std::endl; 9 | return {}; 10 | } 11 | std::vector buffer; 12 | buffer.reserve( file.tellg() ); 13 | buffer.assign( std::istreambuf_iterator( file ), std::istreambuf_iterator() ); 14 | return buffer; 15 | } 16 | 17 | std::vector bytes_mapper_t::bytesToChunks( const std::vector& bytes, uint16_t assumedChunkCount, size_t chunkOffset ) { 18 | if (bytes.empty()) 19 | { 20 | return {}; 21 | } 22 | 23 | uint16_t chunkCount = assumedChunkCount; 24 | std::vector offsets( chunkCount + 1, 0 ); 25 | 26 | for (int i = 0; i <= chunkCount; i++) 27 | { 28 | size_t offset = read4Bytes( bytes, i * 4 + chunkOffset ); 29 | if (offset == 0xFFFFFFFF) 30 | chunkCount = i - 1; 31 | else 32 | offsets.at( i ) = offset; 33 | } 34 | 35 | std::vector chunks; 36 | 37 | for (size_t i = 0; i < chunkCount; i++) 38 | { 39 | size_t offset = offsets.at( i ); 40 | if (offset == 0) 41 | chunks.emplace_back(); 42 | else 43 | { 44 | size_t to = -1; 45 | for (size_t j = i + 1; j <= chunkCount; j++) 46 | { 47 | if (offsets[ j ] >= offset) 48 | { 49 | to = offsets[ j ]; 50 | break; 51 | } 52 | } 53 | if (to == -1) 54 | to = bytes.size(); 55 | chunks.emplace_back( bytes, offset, to, static_cast< int >( i ) ); 56 | } 57 | } 58 | 59 | return chunks; 60 | } 61 | 62 | // This is very redundant, but for consistency sake its here. 63 | char bytes_mapper_t::read1Byte( const std::vector& bytes, size_t offset ) { 64 | if (bytes.empty() || offset >= static_cast< int >( bytes.size() )) 65 | return 0; 66 | 67 | return bytes[ offset ]; 68 | } 69 | 70 | uint16_t bytes_mapper_t::read2Bytes( const std::vector& bytes, size_t offset ) { 71 | if (bytes.size() < offset + 2) 72 | return 0; 73 | 74 | return static_cast< uint8_t >( bytes[ offset ] ) | 75 | ( static_cast< uint8_t >( bytes[ offset + 1 ] ) << 8 ); 76 | } 77 | 78 | uint32_t bytes_mapper_t::read4Bytes( const std::vector& bytes, size_t offset ) { 79 | if (offset + 3 >= bytes.size()) 80 | return 0; 81 | 82 | return static_cast< uint8_t >( bytes[ offset ] ) | 83 | ( static_cast< uint8_t >( bytes[ offset + 1 ] ) << 8 ) | 84 | ( static_cast< uint8_t >( bytes[ offset + 2 ] ) << 16 ) | 85 | ( static_cast< uint8_t >( bytes[ offset + 3 ] ) << 24 ); 86 | } 87 | 88 | // same as read1Byte 89 | void bytes_mapper_t::write1Byte( std::vector& bytes, size_t offset, int value ) { 90 | if (offset >= bytes.size()) 91 | return; 92 | 93 | bytes[ offset ] = static_cast< char >( value & 0xFF ); 94 | } 95 | 96 | void bytes_mapper_t::write2Bytes( std::vector& bytes, size_t offset, int value ) { 97 | if (offset + 1 >= bytes.size()) 98 | return; 99 | 100 | bytes[ offset ] = static_cast< char >( value & 0xFF ); 101 | bytes[ offset + 1 ] = static_cast< char >( ( value >> 8 ) & 0xFF ); 102 | } 103 | 104 | void bytes_mapper_t::write4Bytes( std::vector& bytes, size_t offset, int value ) { 105 | if (offset + 3 >= bytes.size()) 106 | return; 107 | 108 | bytes[ offset ] = static_cast< char >( value & 0xFF ); 109 | bytes[ offset + 1 ] = static_cast< char >( ( value >> 8 ) & 0xFF ); 110 | bytes[ offset + 2 ] = static_cast< char >( ( value >> 16 ) & 0xFF ); 111 | bytes[ offset + 3 ] = static_cast< char >( ( value >> 24 ) & 0xFF ); 112 | } 113 | 114 | void bytes_mapper_t::writeDataToBytes( std::vector& bytes, const std::vector& data, size_t offset ) { 115 | if (offset < 0 || offset + static_cast< int >( data.size() ) > static_cast< int >( bytes.size() )) 116 | { 117 | return; 118 | } 119 | for (int i = 0; i < static_cast< int >( data.size() ); i++) 120 | { 121 | bytes[ offset + i ] = data[ i ]; 122 | } 123 | } 124 | 125 | void bytes_mapper_t::writeBytesToNewFile( const std::vector& bytes, const std::string& filepath ) { 126 | std::ofstream file( filepath, std::ios::binary ); 127 | if (!file) 128 | { 129 | return; 130 | } 131 | for (char byte : bytes) 132 | { 133 | file.put( byte ); 134 | } 135 | } 136 | 137 | void bytes_mapper_t::test() const { 138 | if (bytes.empty()) 139 | { 140 | printf( "Empty bytes\n" ); 141 | } 142 | } -------------------------------------------------------------------------------- /src/core/Initializer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "BytesHelper.hpp" 9 | #include "Data.hpp" 10 | #include "GUI.hpp" 11 | 12 | struct initializer_t 13 | { 14 | gui_t* gui; 15 | 16 | // Dynamic data 17 | std::unordered_map enemy_data; 18 | std::unordered_map unmodified_enemy_data; 19 | std::vector field_data; 20 | std::vector item_shop_data; 21 | std::vector gear_shop_data; 22 | std::vector buki_data; 23 | std::vector weapon_data; 24 | std::vector shop_arms_data; 25 | std::vector item_rate_data; 26 | std::vector arms_rate_data; 27 | std::vector player_stats_data; 28 | std::vector aeon_scaling_data; 29 | std::vector aeon_stat_data; 30 | std::vector sphere_grid_data; 31 | std::vector encounter_file_data; 32 | std::vector gear_customize_data; 33 | std::vector aeon_stat_customize_data; 34 | 35 | data_pack_t* data_pack; 36 | 37 | // Mutex to protect concurrent writes to enemy maps 38 | std::mutex enemy_data_mutex; 39 | 40 | initializer_t() : gui( nullptr ), 41 | enemy_data(), unmodified_enemy_data(), 42 | field_data(), item_shop_data(), gear_shop_data(), 43 | buki_data(), weapon_data(), shop_arms_data(), item_rate_data(), 44 | arms_rate_data(), player_stats_data(), aeon_scaling_data(), 45 | aeon_stat_data(), sphere_grid_data(), encounter_file_data(), 46 | gear_customize_data(), aeon_stat_customize_data(), data_pack( nullptr ) 47 | { 48 | if (!std::filesystem::exists( INPUT_FOLDER + JPPC_FOLDER )) 49 | { 50 | const WCHAR* message = L"Input files do not exist.\n Please be sure your extracted ffx_ps2 folder is in the input folder"; 51 | MessageBox( nullptr, message, L"Error", MB_OK | MB_ICONERROR ); 52 | return; 53 | } 54 | 55 | data_pack = new data_pack_t( 56 | enemy_data, 57 | unmodified_enemy_data, 58 | field_data, 59 | item_shop_data, 60 | gear_shop_data, 61 | buki_data, 62 | weapon_data, 63 | shop_arms_data, 64 | item_rate_data, 65 | arms_rate_data, 66 | player_stats_data, 67 | aeon_scaling_data, 68 | aeon_stat_data, 69 | sphere_grid_data, 70 | encounter_file_data, 71 | gear_customize_data, 72 | aeon_stat_customize_data 73 | ); 74 | 75 | std::thread data_thread( &initializer_t::initializeAllData, this ); 76 | std::thread gui_thread( &initializer_t::initializeGUI, this ); 77 | 78 | data_thread.join(); 79 | gui_thread.join(); 80 | } 81 | 82 | ~initializer_t(); 83 | 84 | std::vector chunkData( std::vector& bytes, size_t size ) const; 85 | std::vector getDataFromFile( const std::string& filepath, bool skip_header = false ) const; 86 | 87 | template 88 | inline void genericExcelReader( const std::string& filepath, std::vector& vector, size_t chunk_size, bool limit_chunks = false, int chunk_limit = 0 ) const 89 | { 90 | std::vector bytes = initializer_t::getDataFromFile( filepath, true ); 91 | std::vector chunks = initializer_t::chunkData( bytes, chunk_size ); 92 | 93 | // Pre-reserve expected number of elements to minimize reallocations 94 | if (chunk_size > 0) 95 | { 96 | size_t expected = bytes.size() / chunk_size; 97 | if (limit_chunks && expected > static_cast(chunk_limit)) 98 | expected = static_cast(chunk_limit); 99 | if (expected) 100 | vector.reserve( vector.size() + expected ); 101 | } 102 | 103 | for (auto& chunk : chunks) 104 | { 105 | if (limit_chunks && chunk.index >= chunk_limit) 106 | break; 107 | if (chunk.data.size() < chunk_size) 108 | continue; 109 | 110 | T* data = new T( chunk ); 111 | vector.push_back( data ); 112 | } 113 | } 114 | 115 | void initializeEnemyData( std::string monster_id, const std::string monster_file ); 116 | void initializeFieldData(); 117 | void initializeShopData( bool gear ); 118 | void initializeBukiData(); 119 | void initializeWeaponData(); 120 | void initializeShopArmsData(); 121 | void initializeItemRateData(); 122 | void initializeArmsRateData(); 123 | void initializePlayerStatData(); 124 | void initializeAeonScalingData(); 125 | void initializeAeonStatData(); 126 | void initializeSphereGridData(); 127 | void initializeBtlData(); 128 | void initializeGearCustomizeData(); 129 | void initializeAeonStatCustomizeData(); 130 | void initializeGUI(); 131 | void initializeAllData(); 132 | 133 | void runEnemyTests(); 134 | void runFieldTests(); 135 | void runShopTests(); 136 | void runBukiTests(); 137 | void runWeaponTests(); 138 | void runShopArmsTests(); 139 | void runEnemyLootTests(); 140 | void runItemRateTests(); 141 | void runSphereGridTests(); 142 | 143 | void runTests(); 144 | }; -------------------------------------------------------------------------------- /src/core/ShopRandomizer.cpp: -------------------------------------------------------------------------------- 1 | #include "Randomizer.hpp" 2 | 3 | void randomizer_t::reconstructItemShopData() 4 | { 5 | std::string name = "item_shop.bin"; 6 | genericReconstructor( btl_kernel_input, btl_kernel_output, name, data_pack.item_shop_data, true ); 7 | } 8 | 9 | void randomizer_t::reconstructArmsShopData() 10 | { 11 | std::string name = "arms_shop.bin"; 12 | genericReconstructor( btl_kernel_input, btl_kernel_output, name, data_pack.gear_shop_data, true ); 13 | } 14 | 15 | void randomizer_t::reconstructArmsRateData() 16 | { 17 | std::string name = "arms_rate.bin"; 18 | genericReconstructor( btl_kernel_input, btl_kernel_output, name, data_pack.arms_rate_data, true ); 19 | } 20 | 21 | void randomizer_t::reconstructItemRateData() 22 | { 23 | std::string name = "item_rate.bin"; 24 | genericReconstructor( btl_kernel_input, btl_kernel_output, name, data_pack.item_rate_data, true ); 25 | } 26 | 27 | void randomizer_t::randomizeArmsPrices() 28 | { 29 | for (auto& price : data_pack.arms_rate_data) 30 | { 31 | arms_rate_t& arms_rate = *price; 32 | if (arms_rate.rate > 1 && options_pack.keep_things_sane) 33 | arms_rate.rate = normal( arms_rate.rate, arms_rate.rate, 1, UINT16_MAX ); 34 | else 35 | arms_rate.rate = uniform( 1, UINT16_MAX ); 36 | arms_rate.writeToBytes(); 37 | } 38 | } 39 | 40 | void randomizer_t::randomizeItemPrices() 41 | { 42 | for (auto& price : data_pack.item_rate_data) 43 | { 44 | item_rate_t& item_rate = *price; 45 | if (item_rate.rate > 1 && item_rate.rate != 2 && options_pack.keep_things_sane) 46 | item_rate.rate = normal( item_rate.rate, item_rate.rate, 1, UINT16_MAX ); 47 | else if (item_rate.rate == 2 && options_pack.keep_things_sane) 48 | // Spheres normally cost 2 gil in data, since they are never sold by vendors. 49 | // Since this can happen with randomization, bump the price to something more reasonable. 50 | item_rate.rate = normal( 1000, 500, 250, UINT16_MAX ); 51 | else 52 | item_rate.rate = uniform( 1, UINT16_MAX ); 53 | item_rate.writeToBytes(); 54 | } 55 | } 56 | 57 | void randomizer_t::randomizeItemShops() 58 | { 59 | for (auto& shop : data_pack.item_shop_data) 60 | { 61 | shop_data_t* item_shop = shop; 62 | int n_items = uniform( options_pack.ensure_shops_sell_spheres ? 4 : 1, 16 ); 63 | for (int i = 0; i < 16; i++) 64 | { 65 | if (i >= n_items) 66 | { 67 | item_shop->item_indexes.at( i ) = 0; 68 | continue; 69 | } 70 | 71 | if (options_pack.ensure_shops_sell_spheres) 72 | { 73 | switch (i) 74 | { 75 | case 0: 76 | item_shop->item_indexes.at( i ) = ITEM_POWER_SPHERE; 77 | continue; 78 | case 1: 79 | item_shop->item_indexes.at( i ) = ITEM_MANA_SPHERE; 80 | continue; 81 | case 2: 82 | item_shop->item_indexes.at( i ) = ITEM_SPEED_SPHERE; 83 | continue; 84 | case 3: 85 | item_shop->item_indexes.at( i ) = ITEM_ABILITY_SPHERE; 86 | continue; 87 | default: 88 | break; 89 | } 90 | } 91 | 92 | bool found = true; 93 | item_t* potential_item; 94 | do 95 | potential_item = getRandomItem(); 96 | while (found = std::find( item_shop->item_indexes.begin(), item_shop->item_indexes.end(), potential_item->id ) != item_shop->item_indexes.end() && potential_item->id < 10000); 97 | 98 | item_shop->item_indexes.at( i ) = potential_item->id; 99 | } 100 | item_shop->writeToBytes(); 101 | } 102 | } 103 | 104 | int randomizer_t::getRandomShopArmsIndex() 105 | { 106 | std::uniform_int_distribution dist( 0, data_pack.shop_arms_data.size() - 1 ); 107 | return dist( rng ); 108 | } 109 | 110 | void randomizer_t::randomizeGearShops() 111 | { 112 | for (auto& shop : data_pack.gear_shop_data) 113 | { 114 | shop_data_t* gear_shop = shop; 115 | int n_items = uniform( 1, 16 ); 116 | for (int i = 0; i < 16; i++) 117 | { 118 | if (i >= n_items) 119 | { 120 | gear_shop->item_indexes.at( i ) = 0; 121 | continue; 122 | } 123 | gear_shop->item_indexes.at( i ) = getRandomShopArmsIndex(); 124 | } 125 | gear_shop->writeToBytes(); 126 | } 127 | } 128 | 129 | void randomizer_t::doShopRandomization() 130 | { 131 | if (!options_pack.randomize_item_shops && !options_pack.randomize_gear_shops) 132 | return; 133 | 134 | if (options_pack.randomize_item_shops) 135 | { 136 | printf( "Randomizing Item Shops...\n" ); 137 | randomizeItemShops(); 138 | reconstructItemShopData(); 139 | } 140 | 141 | if (options_pack.randomize_gear_shops) 142 | { 143 | printf( "Randomizing Gear Shops...\n" ); 144 | randomizeGearShops(); 145 | reconstructArmsShopData(); 146 | } 147 | } 148 | 149 | void randomizer_t::doPriceRandomization() 150 | { 151 | if (!options_pack.randomize_item_shop_prices && !options_pack.randomize_gear_shop_prices) 152 | return; 153 | 154 | if (options_pack.randomize_item_shop_prices) 155 | { 156 | printf( "Randomizing Item Prices...\n" ); 157 | randomizeItemPrices(); 158 | printf( "Reconstructing item_rate.bin...\n" ); 159 | reconstructItemRateData(); 160 | } 161 | 162 | if (options_pack.randomize_gear_shop_prices) 163 | { 164 | printf( "Randomizing Gear Prices...\n" ); 165 | randomizeArmsPrices(); 166 | printf( "Reconstructing arms_rate.bin...\n" ); 167 | reconstructArmsRateData(); 168 | } 169 | } -------------------------------------------------------------------------------- /src/core/ItemRandomizer.cpp: -------------------------------------------------------------------------------- 1 | #include "Randomizer.hpp" 2 | 3 | void randomizer_t::reconstructTakaraData() 4 | { 5 | std::string name = "takara.bin"; 6 | genericReconstructor( btl_kernel_input, btl_kernel_output, name, data_pack.field_data, true ); 7 | } 8 | 9 | void randomizer_t::checkItemList( uint16_t& id, uint8_t& quantity, bool key ) 10 | { 11 | bool key_item = key; 12 | if (id == 0) 13 | return; 14 | if (quantity == 0) 15 | quantity = 1; 16 | if (id > 9000) 17 | key_item = true; 18 | 19 | std::unordered_map::iterator it = all_items.find( id ); 20 | 21 | bool found = it != all_items.end(); 22 | if (!found) 23 | { 24 | item_t* item = new item_t( id, quantity, quantity ); 25 | item->addInstance( quantity ); 26 | all_items.insert( { id , item } ); 27 | if (!key_item) 28 | all_non_key_items.insert( { id , item } ); 29 | if (key_item) 30 | all_key_items.insert( { id , item } ); 31 | } 32 | else 33 | { 34 | item_t& item = *all_items.at( id ); 35 | if (item.getMinQuantity() > quantity) 36 | item.setMinQuantity( quantity ); 37 | if (item.getMaxQuantity() < quantity) 38 | item.setMaxQuantity( quantity ); 39 | item.addInstance( quantity ); 40 | } 41 | } 42 | 43 | void randomizer_t::getFieldItems() 44 | { 45 | for (auto& item : data_pack.field_data) 46 | { 47 | field_data_t& field = *item; 48 | if (( field.flag == 2 || field.flag == 10 ) && field.type != 203 && field.type != 177) 49 | checkItemList( field.type, field.quantity, field.flag == 10 ); 50 | } 51 | } 52 | 53 | void randomizer_t::getMonsterItems() 54 | { 55 | for (auto& enemy : data_pack.enemy_data) 56 | { 57 | enemy_loot_data_t& loot = *enemy.second->loot_data; 58 | checkItemList( loot.primary_normal_drop, loot.n_primary_normal_drop ); 59 | checkItemList( loot.primary_normal_drop_rare, loot.n_primary_normal_drop_rare ); 60 | checkItemList( loot.secondary_normal_drop, loot.n_secondary_normal_drop ); 61 | checkItemList( loot.secondary_normal_drop_rare, loot.n_secondary_normal_drop_rare ); 62 | checkItemList( loot.primary_normal_drop_overkill, loot.n_primary_normal_drop_overkill ); 63 | checkItemList( loot.primary_normal_drop_overkill_rare, loot.n_primary_normal_drop_overkill_rare ); 64 | checkItemList( loot.secondary_normal_drop_overkill, loot.n_secondary_normal_drop_overkill ); 65 | checkItemList( loot.secondary_normal_drop_overkill_rare, loot.n_secondary_normal_drop_overkill_rare ); 66 | checkItemList( loot.steal_item, loot.n_steal_item ); 67 | checkItemList( loot.steal_item_rare, loot.n_steal_item_rare ); 68 | checkItemList( loot.bribe_item, loot.n_bribe_item ); 69 | } 70 | } 71 | 72 | void randomizer_t::getShopItems() 73 | { 74 | uint8_t quantity = 1; 75 | for (auto& shop : data_pack.item_shop_data) 76 | { 77 | shop_data_t& item_shop = *shop; 78 | for (auto& item : item_shop.item_indexes) 79 | { 80 | checkItemList( item, quantity ); 81 | } 82 | } 83 | } 84 | 85 | item_t* randomizer_t::getRandomItem() 86 | { 87 | std::vector items; 88 | for (auto& item : all_non_key_items) 89 | items.push_back( item.second ); 90 | 91 | std::uniform_int_distribution dist( 0, items.size() - 1 ); 92 | int index = dist( rng ); 93 | return items[ index ]; 94 | } 95 | 96 | item_t* randomizer_t::getRandomKeyItem() 97 | { 98 | std::vector items; 99 | for (auto& item : all_key_items) 100 | items.push_back( item.second ); 101 | 102 | std::uniform_int_distribution dist( 0, items.size() - 1 ); 103 | int index = dist( rng ); 104 | 105 | all_key_items.erase( items[ index ]->id ); 106 | 107 | return items[ index ]; 108 | } 109 | 110 | int randomizer_t::getRandomItemQuantity( item_t* item, bool is_monster ) 111 | { 112 | if (options_pack.keep_things_sane) 113 | { 114 | if (is_monster) 115 | return uniform( 1, 4 ); 116 | if (item->getAverageQuantity() > 10) 117 | return normal( std::min( item->getMinQuantity() * 5, 7 ), std::min( item->getMinQuantity() * 5, 7 ) / 5, std::min( item->getMinQuantity(), 1 ), 99 ); 118 | if (item->getAverageQuantity() < 10) 119 | return normal( item->getAverageQuantity(), item->getStandardDeviation(), item->getMinQuantity(), item->getMaxQuantity() ); 120 | else 121 | return item->getMinQuantity(); 122 | } 123 | 124 | return uniform( 0, 99 ); 125 | } 126 | 127 | void randomizer_t::randomizeFieldItems() 128 | { 129 | std::vector blacklist = { 177, 203 }; 130 | if (!options_pack.randomize_celestials) 131 | { 132 | blacklist.push_back( 5 ); 133 | blacklist.push_back( 93 ); 134 | blacklist.push_back( 99 ); 135 | blacklist.push_back( 113 ); 136 | blacklist.push_back( 114 ); 137 | blacklist.push_back( 188 ); 138 | blacklist.push_back( 214 ); 139 | } 140 | if (!options_pack.randomize_brotherhood) 141 | { 142 | blacklist.push_back( 207 ); 143 | blacklist.push_back( 208 ); 144 | } 145 | for (auto& field : data_pack.field_data) 146 | { 147 | field_data_t& field_data = *field; 148 | if (std::find( blacklist.begin(), blacklist.end(), field_data.index ) != blacklist.end()) 149 | continue; 150 | 151 | if (field_data.flag != 10 || options_pack.randomize_key_items) 152 | { 153 | int roll; 154 | if( options_pack.randomize_key_items ) 155 | roll = std::discrete_distribution( { 0.4, 0.3, 0.2, 0.1 } )( rng ); 156 | else 157 | roll = std::discrete_distribution( { 0.5, 0.3, 0.2 } )( rng ); 158 | 159 | item_t* item = nullptr; 160 | switch (roll) 161 | { 162 | case 0: 163 | item = getRandomItem(); 164 | field_data.flag = 2; 165 | field_data.type = item->id; 166 | field_data.quantity = getRandomItemQuantity( item, false ); 167 | field_data.writeToBytes(); 168 | break; 169 | case 1: 170 | field_data.flag = 5; 171 | field_data.type = uniform( 0, data_pack.buki_data.size() - 1 ); 172 | field_data.quantity = 1; 173 | field_data.writeToBytes(); 174 | break; 175 | case 2: 176 | field_data.flag = 0; 177 | field_data.quantity = normal( 15, 5, 1, 255 ); 178 | field_data.writeToBytes(); 179 | break; 180 | case 3: 181 | if (all_key_items.size() == 0) 182 | { 183 | item = getRandomItem(); 184 | field_data.flag = 2; 185 | } 186 | else 187 | { 188 | item = getRandomKeyItem(); 189 | field_data.flag = 10; 190 | } 191 | field_data.type = item->id; 192 | field_data.quantity = getRandomItemQuantity( item, false ); 193 | field_data.writeToBytes(); 194 | break; 195 | default: 196 | break; 197 | } 198 | } 199 | } 200 | 201 | // Ensure all key items are in the pool 202 | while (all_key_items.size() > 0 && options_pack.randomize_key_items) 203 | { 204 | int random_field_index = uniform( 0, data_pack.field_data.size() - 1 ); 205 | field_data_t& field_data = *data_pack.field_data.at( random_field_index ); 206 | if (field_data.flag == 10) 207 | continue; 208 | if (std::find( blacklist.begin(), blacklist.end(), field_data.index ) != blacklist.end()) 209 | continue; 210 | 211 | item_t* item = getRandomKeyItem(); 212 | field_data.flag = 10; 213 | field_data.type = item->id; 214 | field_data.quantity = getRandomItemQuantity( item, false ); 215 | field_data.writeToBytes(); 216 | } 217 | } 218 | 219 | void randomizer_t::doFieldRandomization() 220 | { 221 | for (auto& item : all_non_key_items) 222 | { 223 | item.second->test(); 224 | } 225 | if (!options_pack.randomize_field_items) 226 | return; 227 | 228 | printf( "Randomizing Field Items...\n" ); 229 | randomizeFieldItems(); 230 | printf( "Reconstructing takara.bin...\n" ); 231 | reconstructTakaraData(); 232 | } -------------------------------------------------------------------------------- /src/core/RandomEncounterRandomizer.cpp: -------------------------------------------------------------------------------- 1 | #include "Randomizer.hpp" 2 | 3 | void randomizer_t::getRandomEncounterIDs() 4 | { 5 | for (auto& encounter : data_pack.encounter_files) 6 | { 7 | for (auto& monster : encounter->formation->monster_ids) 8 | { 9 | if (monster == ( 0xFFFF - 0x1000 ) || monster == MON_BELIAL_01 || monster == MON_SANDWORM_01 || monster == MON_SANDWORM_02 || 10 | monster == MON_S_SANDWORM_01 || monster == MON_MAKI_SANDWORM_02) 11 | continue; 12 | if (std::find( random_monster_encounter_ids.begin(), random_monster_encounter_ids.end(), monster ) != random_monster_encounter_ids.end()) 13 | continue; 14 | enemy_data_t& enemy = *data_pack.enemy_data.at( monster ); 15 | enemy_stat_data_t& stats = *enemy.stats_data; 16 | if (stats.arena_id != 255 && ( stats.arena_id != 0 || monster == 1 )) 17 | { 18 | random_monster_encounter_ids.push_back( monster ); 19 | continue; 20 | } 21 | bool in_boss_list = std::find( boss_id_whitelist.begin(), boss_id_whitelist.end(), monster ) != boss_id_whitelist.end(); 22 | if (in_boss_list) 23 | { 24 | random_monster_encounter_ids.push_back( monster ); 25 | continue; 26 | } 27 | bool in_whitelist = std::find( enemy_id_whitelist.begin(), enemy_id_whitelist.end(), monster ) != enemy_id_whitelist.end(); 28 | if (in_whitelist) 29 | { 30 | random_monster_encounter_ids.push_back( monster ); 31 | continue; 32 | } 33 | } 34 | } 35 | } 36 | 37 | void randomizer_t::adjustStats() 38 | { 39 | if (!options_pack.swap_random_stats && !options_pack.scale_encounter_stats) 40 | return; 41 | 42 | for (auto& pair : paired_monster_ids) 43 | { 44 | if (pair.second == 211) 45 | continue; 46 | const enemy_data_t& monster = *data_pack.unmodified_enemy_data.at(pair.first); 47 | enemy_data_t& new_monster = *data_pack.enemy_data.at(pair.second); 48 | const enemy_stat_data_t* stats = monster.stats_data; 49 | enemy_stat_data_t* new_stats = new_monster.stats_data; 50 | const enemy_loot_data_t* loot = monster.loot_data; 51 | enemy_loot_data_t* new_loot = new_monster.loot_data; 52 | 53 | if (options_pack.scale_encounter_stats) 54 | { 55 | float old_hp = stats->hp; 56 | float new_hp = new_stats->hp; 57 | float hp = std::ceil( old_hp * std::pow( std::pow( new_hp, 1 / 3.2 ) / std::pow( old_hp, 1 / 3.2 ), 1 / 3.2 ) / 25 ) * 25; 58 | new_stats->hp = std::clamp( static_cast< uint32_t >( hp ), std::min( stats->hp, new_stats->hp ), std::max( stats->hp, new_stats->hp ) ); 59 | 60 | float old_mp = stats->mp; 61 | float new_mp = new_stats->mp; 62 | float mp = std::ceil( stats->mp * std::sqrt( std::sqrt( new_stats->mp ) / std::sqrt( stats->mp ) ) / 5 ) * 5; 63 | new_stats->mp = std::clamp( static_cast< uint32_t >( mp ), std::min( stats->mp, new_stats->mp ), std::max( stats->mp, new_stats->mp ) ); 64 | 65 | float old_str = stats->str; 66 | float new_str = new_stats->str; 67 | float str = std::ceil( stats->str * std::sqrt( new_stats->str ) / std::sqrt( stats->str ) ); 68 | new_stats->str = std::clamp( static_cast< uint8_t >( str ), std::min( stats->str, new_stats->str ), std::max( stats->str, new_stats->str ) ); 69 | 70 | float old_mag = stats->mag; 71 | float new_mag = new_stats->mag; 72 | float mag = std::ceil( stats->mag * std::sqrt( new_stats->mag ) / std::sqrt( stats->mag ) ); 73 | new_stats->mag = std::clamp( static_cast< uint8_t >( mag ), std::min( stats->mag, new_stats->mag ), std::max( stats->mag, new_stats->mag ) ); 74 | 75 | float old_agi = stats->agi; 76 | float new_agi = new_stats->agi; 77 | float agi = std::ceil( stats->agi * std::sqrt( new_stats->agi ) / std::sqrt( stats->agi ) ); 78 | new_stats->agi = std::clamp( static_cast< uint8_t >( agi ), std::min( stats->agi, new_stats->agi ), std::max( stats->agi, new_stats->agi ) ); 79 | 80 | float old_acc = stats->acc; 81 | float new_acc = new_stats->acc; 82 | float acc = std::ceil( stats->acc * std::sqrt( new_stats->acc ) / std::sqrt( stats->acc ) ); 83 | new_stats->acc = std::clamp( static_cast< uint8_t >( acc ), std::min( stats->acc, new_stats->acc ), std::max( stats->acc, new_stats->acc ) ); 84 | 85 | float old_luck = stats->luck; 86 | float new_luck = new_stats->luck; 87 | float luck = std::ceil( stats->luck * std::sqrt( new_stats->luck ) / std::sqrt( stats->luck ) ); 88 | new_stats->luck = std::clamp( static_cast< uint8_t >( luck ), std::min( stats->luck, new_stats->luck ), std::max( stats->luck, new_stats->luck ) ); 89 | 90 | float old_ap = loot->ap; 91 | float new_ap = new_loot->ap; 92 | float ap = std::ceil( loot->ap * std::pow( std::pow( new_ap, 1 / 3.2 ) / std::pow( old_ap, 1 / 3.2 ), 1 / 3.2 ) ); 93 | new_loot->ap = std::clamp( static_cast< uint16_t >( ap ), std::min( loot->ap, new_loot->ap ), std::max( loot->ap, new_loot->ap ) ); 94 | 95 | float old_overkill_ap = loot->ap_overkill; 96 | float new_overkill_ap = new_loot->ap_overkill; 97 | float overkill_ap = std::ceil( loot->ap_overkill * std::pow( std::pow( new_overkill_ap, 1 / 3.2 ) / std::pow( old_overkill_ap, 1 / 3.2 ), 1 / 3.2 ) ); 98 | new_loot->ap_overkill = std::clamp( static_cast< uint16_t >( overkill_ap ), std::min( loot->ap_overkill, new_loot->ap_overkill ), std::max( loot->ap_overkill, new_loot->ap_overkill ) ); 99 | 100 | float old_gil = loot->gil; 101 | float new_gil = new_loot->gil; 102 | float gil = std::ceil( loot->gil * std::pow( std::pow( new_gil, 1 / 3.2 ) / std::pow( old_gil, 1 / 3.2 ), 1 / 3.2 ) ); 103 | new_loot->gil = std::clamp( static_cast< uint16_t >( gil ), std::min( loot->gil, new_loot->gil ), std::max( loot->gil, new_loot->gil ) ); 104 | } 105 | 106 | if (options_pack.swap_random_stats) 107 | { 108 | new_stats->hp = stats->hp; 109 | new_stats->mp = stats->mp; 110 | new_stats->str = stats->str; 111 | new_stats->def = stats->def; 112 | new_stats->mag = stats->mag; 113 | new_stats->mdef = stats->mdef; 114 | new_stats->agi = stats->agi; 115 | new_stats->acc = stats->acc; 116 | new_stats->eva = stats->eva; 117 | new_stats->luck = stats->luck; 118 | new_loot->ap = loot->ap; 119 | new_loot->ap_overkill = loot->ap_overkill; 120 | new_loot->gil = loot->gil; 121 | } 122 | 123 | if (options_pack.swap_random_stats || options_pack.scale_encounter_stats) 124 | { 125 | new_stats->writeToBytes(); 126 | new_monster.stats_data = new_stats; 127 | new_monster.writeStatsData( *new_stats ); 128 | 129 | new_loot->writeToBytes(); 130 | new_monster.loot_data = new_loot; 131 | new_monster.writeLootData( *new_loot ); 132 | } 133 | } 134 | } 135 | 136 | void randomizer_t::randomizeEncounters( encounter_file_t& encounter ) 137 | { 138 | formation_data_t& formation_data = *encounter.formation; 139 | for (auto& mon : formation_data.monster_ids) 140 | { 141 | std::unordered_map::iterator it = paired_monster_ids.find( mon ); 142 | bool found = it != paired_monster_ids.end(); 143 | if (!found) 144 | continue; 145 | 146 | mon = it->second; 147 | } 148 | } 149 | 150 | void randomizer_t::doRandomEcnounterRandomization() 151 | { 152 | if (!options_pack.randomize_encounters) 153 | return; 154 | 155 | shuffled_random_monster_encounter_ids.clear(); 156 | shuffled_random_monster_encounter_ids = random_monster_encounter_ids; 157 | std::shuffle( shuffled_random_monster_encounter_ids.begin(), shuffled_random_monster_encounter_ids.end(), rng ); 158 | 159 | for (int i = 0; i < random_monster_encounter_ids.size(); i++) 160 | { 161 | paired_monster_ids.insert( std::make_pair( random_monster_encounter_ids[ i ], shuffled_random_monster_encounter_ids[ i ] ) ); 162 | } 163 | printf( "Adjusting Random Ecnounter Stats...\n" ); 164 | adjustStats(); 165 | 166 | printf( "Randomizing Encounters...\n" ); 167 | for (auto& encounter : data_pack.encounter_files) 168 | { 169 | encounter_file_t& encounter_file = *encounter; 170 | 171 | randomizeEncounters( encounter_file ); 172 | 173 | encounter_file.writeFormationData(); 174 | std::string pathstr = OUTPUT_FOLDER + prefix + BTL_FOLDER + encounter_file.name; 175 | std::filesystem::path path = pathstr; 176 | std::filesystem::create_directories( path ); 177 | std::string filepath = pathstr + "/" + encounter_file.name + ".bin"; 178 | encounter_file.writeBytesToNewFile( encounter_file.bytes, filepath ); 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /FFXRando.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 17.0 23 | Win32Proj 24 | {dc968dc1-55bf-4501-bd9f-ae479258f391} 25 | FFXRando 26 | 10.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v143 33 | Unicode 34 | true 35 | false 36 | 37 | 38 | Application 39 | false 40 | v143 41 | true 42 | Unicode 43 | false 44 | 45 | 46 | Application 47 | true 48 | v143 49 | Unicode 50 | true 51 | false 52 | 53 | 54 | Application 55 | false 56 | v143 57 | true 58 | Unicode 59 | false 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | true 85 | false 86 | false 87 | true 88 | 89 | 90 | true 91 | false 92 | false 93 | true 94 | 95 | 96 | false 97 | 98 | 99 | false 100 | 101 | 102 | 103 | Level3 104 | true 105 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 106 | true 107 | 4996 108 | stdcpp23 109 | stdclatest 110 | StackFrameRuntimeCheck 111 | 112 | 113 | Console 114 | true 115 | wxbase33ud.lib;wxmsw33ud_aui.lib;wxmsw33ud_core.lib;%(AdditionalDependencies) 116 | 117 | 118 | 119 | 120 | Level3 121 | true 122 | true 123 | true 124 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 125 | true 126 | stdcpp23 127 | 4996 128 | stdclatest 129 | AnySuitable 130 | Speed 131 | true 132 | 133 | 134 | Windows 135 | true 136 | true 137 | true 138 | mainCRTStartup 139 | wxbase33u.lib;wxmsw33u_aui.lib;wxmsw33u_core.lib;%(AdditionalDependencies) 140 | 141 | 142 | 143 | 144 | Level3 145 | true 146 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 147 | true 148 | stdclatest 149 | true 150 | stdcpp23 151 | 4996 152 | StackFrameRuntimeCheck 153 | 154 | 155 | Console 156 | true 157 | wxbase33ud.lib;wxmsw33ud_aui.lib;wxmsw33ud_core.lib;%(AdditionalDependencies) 158 | 159 | 160 | 161 | 162 | Level3 163 | true 164 | true 165 | true 166 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 167 | true 168 | stdcpp23 169 | stdclatest 170 | 4996 171 | AnySuitable 172 | Speed 173 | true 174 | 175 | 176 | Windows 177 | true 178 | true 179 | true 180 | mainCRTStartup 181 | wxbase33u.lib;wxmsw33u_aui.lib;wxmsw33u_core.lib;%(AdditionalDependencies) 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | -------------------------------------------------------------------------------- /src/core/Randomizer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "BytesHelper.hpp" 3 | #include "Data.hpp" 4 | #include "json.hpp" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | using json = nlohmann::json; 14 | 15 | struct randomizer_t 16 | { 17 | private: 18 | std::mt19937 rng; 19 | mutable std::mutex rng_mutex; // protect rng across threads 20 | 21 | // Static data 22 | const data_pack_t& data_pack; 23 | const options_pack_t& options_pack; 24 | 25 | // Dynamically populated data 26 | std::unordered_map all_items; 27 | std::unordered_map all_non_key_items; 28 | std::unordered_map all_key_items; 29 | std::vector all_armor; 30 | std::vector all_weapons; 31 | std::vector abilities; 32 | std::vector def_pool; 33 | std::vector mdef_pool; 34 | std::vector eva_pool; 35 | std::vector original_sphere_grid_node_ids; 36 | std::vector standard_sphere_grid_node_ids; 37 | std::vector expert_sphere_grid_node_ids; 38 | std::vector random_monster_encounter_ids; 39 | 40 | // Shuffled vectors 41 | std::vector shuffled_player_stats_data; 42 | std::vector shuffled_aeon_scaling_data; 43 | std::vector shuffled_aeon_stat_data; 44 | std::vector shuffled_original_sphere_grid_node_ids; 45 | std::vector shuffled_standard_sphere_grid_node_ids; 46 | std::vector shuffled_expert_sphere_grid_node_ids; 47 | std::vector shuffled_random_monster_encounter_ids; 48 | std::unordered_map paired_monster_ids; 49 | 50 | // Lists 51 | const std::vector enemy_id_whitelist{ 52 | MON_BIPEDAL_MECH_01, 53 | MON_BIPEDAL_MECH_02, 54 | MON_BIPEDAL_MECH_02_BURNING, 55 | MON_BIPEDAL_MECH_03, 56 | MON_QUADREDAL_MECH_01, 57 | MON_QUADREDAL_MECH_02, 58 | MON_GOLEM_01, 59 | MON_GOLEM_02, 60 | MON_G_EBON_01, 61 | MON_G_EBON_02, 62 | MON_ARIMAN_05, 63 | MON_BOMB_04, 64 | MON_CHIMERA_03, 65 | MON_FOUR_HORN_04, 66 | MON_GUADO_02, 67 | MON_SAHAGIN_02_KAI, 68 | MON_PUDDING_07, 69 | MON_LIZARD_08, 70 | MON_KOURA_07, 71 | MON_ARIMAN_06, 72 | MON_SAHAGIN_02, 73 | MON_FLESIUS_01, 74 | MON_RISING_01, 75 | MON_OCTOPUS_01, 76 | MON_PIRANHA_11, 77 | MON_PIRANHA_12, 78 | MON_PIRANHA_13, 79 | MON_PIRANHA_21, 80 | MON_PIRANHA_22, 81 | MON_PIRANHA_23, 82 | MON_PIRANHA_31, 83 | MON_PIRANHA_32, 84 | MON_PIRANHA_33, 85 | MON_TREASURE_CHEST_01 86 | }; 87 | const std::vector boss_id_whitelist{ 88 | MON_OCHU_E01, 89 | MON_SINSPAWN_02, 90 | MON_GOLEM_03 91 | }; 92 | std::vector randomized_monsters{}; 93 | 94 | public: 95 | std::string prefix; 96 | std::string btl_kernel_input; 97 | std::string btl_kernel_output; 98 | 99 | json json_data; 100 | 101 | randomizer_t( const options_pack_t& options_pack, 102 | const data_pack_t& data_pack ) 103 | : 104 | rng(), 105 | data_pack( data_pack ), 106 | options_pack( options_pack ), 107 | all_items(), 108 | all_non_key_items(), 109 | all_key_items(), 110 | all_armor(), 111 | all_weapons(), 112 | abilities(), 113 | def_pool(), 114 | mdef_pool(), 115 | eva_pool(), 116 | original_sphere_grid_node_ids(), 117 | standard_sphere_grid_node_ids(), 118 | expert_sphere_grid_node_ids(), 119 | random_monster_encounter_ids(), 120 | shuffled_player_stats_data(), 121 | shuffled_aeon_scaling_data(), 122 | shuffled_aeon_stat_data(), 123 | shuffled_original_sphere_grid_node_ids(), 124 | shuffled_standard_sphere_grid_node_ids(), 125 | shuffled_expert_sphere_grid_node_ids(), 126 | shuffled_random_monster_encounter_ids(), 127 | paired_monster_ids(), 128 | randomized_monsters(), 129 | prefix( options_pack.fahrenheit ? FAHRENHEIT_PREFIX : "" ), 130 | btl_kernel_input( INPUT_FOLDER + BATTLE_KERNEL_FOLDER ), 131 | btl_kernel_output( OUTPUT_FOLDER + prefix + BATTLE_KERNEL_FOLDER ), 132 | json_data() 133 | { 134 | rng.seed( options_pack.seed ); 135 | 136 | json_data = { 137 | { "Name", NAME }, 138 | { "Desc", DESCRIPTION }, 139 | { "Authors", AUTHOR }, 140 | { "Link", "https://github.com/nyterage/FFXRando" }, 141 | { "DllList", json::array() }, 142 | { "Dependencies", json::array() }, 143 | { "LoadAfter", json::array() }, 144 | { "Version", VERSION }, 145 | { "Seed", options_pack.seed_text } 146 | }; 147 | 148 | getFieldItems(); 149 | getShopItems(); 150 | getMonsterItems(); 151 | getRandomEncounterIDs(); 152 | 153 | if (options_pack.shuffle_sphere_grid) 154 | getSphereGridNodeIds(); 155 | 156 | poplateGearLists(); 157 | populateAbilityData(); 158 | randomize(); 159 | } 160 | 161 | template 162 | T uniform( T min, T max ) { 163 | static_assert( std::is_integral::value, "Only integral types are supported" ); 164 | 165 | using DistType = typename std::conditional<(sizeof( T ) <= 2), uint32_t, T>::type; 166 | 167 | std::uniform_int_distribution dist( static_cast(min), static_cast(max) ); 168 | std::lock_guard lock(rng_mutex); 169 | return static_cast< T >( dist( rng ) ); 170 | } 171 | 172 | template 173 | T normal( T mean, T stddev, T min, T max ) { 174 | static_assert( std::is_integral::value, "Only integral types are supported" ); 175 | if (mean == 0) 176 | return 0; 177 | 178 | if (stddev == 0) 179 | return mean; 180 | 181 | std::normal_distribution dist( static_cast< double >( mean ), static_cast< double >( stddev ) ); 182 | std::lock_guard lock(rng_mutex); 183 | T number; 184 | do 185 | { 186 | double sampled = dist( rng ); 187 | number = static_cast< T >( std::round( sampled ) ); 188 | } 189 | while (number < min || number > max); 190 | 191 | return number; 192 | } 193 | 194 | // pick a random element from a vector 195 | template 196 | T randomElement( const std::vector& vec ) { 197 | 198 | if (vec.size() == 0) 199 | throw std::runtime_error( "Vector is empty" ); 200 | 201 | size_t index = uniform( 0, vec.size() - 1 ); 202 | return vec.at( index ); 203 | } 204 | 205 | void writeJson( json& j ) 206 | { 207 | if (!options_pack.fahrenheit) 208 | return; 209 | 210 | std::cout << std::setw( 4 ) << j << std::endl; 211 | std::ofstream file( OUTPUT_FOLDER + "FFXRando/FFXRando.manifest.json" ); 212 | file.write( j.dump( 4 ).c_str(), j.dump( 4 ).size() ); 213 | } 214 | 215 | template 216 | void genericReconstructor( const std::string& input_path, const std::string& output_path, const std::string& filename, std::vector& vector, bool has_header = false ) 217 | { 218 | std::vector reconstructed_bytes; 219 | std::string full_path = input_path + filename; 220 | std::vector bytes = bytes_mapper_t::fileToBytes( full_path ); 221 | 222 | if (has_header) 223 | bytes = std::vector( bytes.begin(), bytes.begin() + 20 ); 224 | 225 | // Reserve final size to avoid reallocations 226 | size_t final_size = bytes.size(); 227 | for (auto* element : vector) 228 | final_size += element->bytes.size(); 229 | reconstructed_bytes.reserve( final_size ); 230 | 231 | reconstructed_bytes.insert( reconstructed_bytes.end(), bytes.begin(), bytes.end() ); 232 | 233 | for (auto& element : vector) 234 | { 235 | std::vector element_bytes = element->bytes; 236 | reconstructed_bytes.insert( reconstructed_bytes.end(), element_bytes.begin(), element_bytes.end() ); 237 | } 238 | 239 | std::filesystem::create_directories( output_path ); 240 | std::string file = output_path + filename; 241 | bytes_mapper_t::writeBytesToNewFile( reconstructed_bytes, file ); 242 | } 243 | 244 | // Sphere Grid 245 | void reconstructSphereGridData(); 246 | void getSphereGridNodeIds(); 247 | void setRequiredAbilities(); 248 | void shuffleSphereGridNodes(); 249 | void randomizeSphereGridTrue(); 250 | void randomizeSphereGrid(); 251 | void emptySphereGrid(); 252 | void fillSphereGrid(); 253 | void removeSphereGridLocks(); 254 | void upgradeSphereGridNodes(); 255 | void downgradeSphereGridNodes(); 256 | void doSphereGridRandomization(); 257 | 258 | // Items 259 | void reconstructTakaraData(); 260 | void checkItemList( uint16_t& id, uint8_t& quantity, bool key = false ); 261 | void getFieldItems(); 262 | void getMonsterItems(); 263 | void getShopItems(); 264 | item_t* getRandomItem(); 265 | item_t* getRandomKeyItem(); 266 | int getRandomItemQuantity( item_t* item, bool is_monster = true ); 267 | void randomizeFieldItems(); 268 | void doFieldRandomization(); 269 | 270 | // Gear 271 | void reconstructBukiData(); 272 | void reconstructWeaponData(); 273 | void reconstructShopArmsData(); 274 | void reconstructKaizouData(); 275 | void poplateGearLists(); 276 | uint16_t getRandomAbility(); 277 | void populateAbilityData(); 278 | weapon_formula_e getRandomFormula(); 279 | void randomizeGearAbilities( gear_data_t& gear ); 280 | void randomizeShopArmsAbilities(); 281 | void randomizeBukiAbilities(); 282 | void randomizeWeaponsAbilities(); 283 | void randomizeWeaponCrit(); 284 | void randomizeWeaponAttackPower(); 285 | void randomizeWeaponDamageFormula(); 286 | void randomizeCustomizeItems(); 287 | void doGearRandomization(); 288 | 289 | // Enemy Data 290 | void randomizeEnemyDrops( enemy_data_t* enemy ); 291 | void randomizeEnemySteal( enemy_data_t* enemy ); 292 | void randomizeEnemyBribe( enemy_data_t* enemy ); 293 | void randomizeEnemyStatsNormal( enemy_data_t* enemy ); 294 | void addEnemyDefenses( enemy_data_t* enemy ); 295 | void randomizeEnemyStatsDefensiveNormalization( enemy_data_t* enemy ); 296 | void shuffleEnemyStats( enemy_data_t* enemy ); 297 | void randomizeEnemyElementalAffinities( enemy_data_t* enemy ); 298 | void randomizeEnemyGearDrops( enemy_data_t* enemy ); 299 | void doEnemyRandomization(); 300 | 301 | // Random Encounters 302 | void getRandomEncounterIDs(); 303 | void adjustStats(); 304 | void randomizeEncounters( encounter_file_t& encounter ); 305 | void doRandomEcnounterRandomization(); 306 | 307 | // Player Characters & Aeons 308 | void reconstructPlayerStatsData(); 309 | void reconstructAeonScalingData(); 310 | void reconstructAeonStatData(); 311 | void reconstructSumGrowData(); 312 | void randomizePlayerStats(); 313 | void shuffleCharacterStats(); 314 | void randomizeAeonStatScaling(); 315 | void randomizeAeonBaseStats(); 316 | void shuffleAeonStatScaling(); 317 | void shuffleAeonBaseStats(); 318 | void randomizeSumGrow(); 319 | void doPlayerStatRandomization(); 320 | 321 | // Shops 322 | void reconstructItemShopData(); 323 | void reconstructArmsShopData(); 324 | void reconstructArmsRateData(); 325 | void reconstructItemRateData(); 326 | void randomizeArmsPrices(); 327 | void randomizeItemPrices(); 328 | void randomizeItemShops(); 329 | int getRandomShopArmsIndex(); 330 | void randomizeGearShops(); 331 | void doShopRandomization(); 332 | void doPriceRandomization(); 333 | 334 | 335 | void randomize() 336 | { 337 | printf( "Starting Randomizer...\n" ); 338 | 339 | // Clean the output folder to prevent any issues 340 | std::filesystem::remove_all( OUTPUT_FOLDER ); 341 | 342 | std::thread enemy_thread( &randomizer_t::doEnemyRandomization, this ); 343 | std::thread shop_thread( &randomizer_t::doShopRandomization, this ); 344 | std::thread price_thread( &randomizer_t::doPriceRandomization, this ); 345 | std::thread field_thread( &randomizer_t::doFieldRandomization, this ); 346 | std::thread gear_thread( &randomizer_t::doGearRandomization, this ); 347 | // Dont Thread these to prevent a race condition. 348 | // TODO: Refactor so the race condition isnt possible 349 | // Since Player stats write the data to the file, make sure sphere grid is randomized first 350 | // as this sets the flags for Use and Lancet on Khimari and Riku if necessaray. 351 | doSphereGridRandomization(); 352 | doPlayerStatRandomization(); 353 | 354 | enemy_thread.join(); 355 | shop_thread.join(); 356 | price_thread.join(); 357 | field_thread.join(); 358 | gear_thread.join(); 359 | 360 | writeJson( json_data ); 361 | } 362 | }; -------------------------------------------------------------------------------- /src/core/Initializer.cpp: -------------------------------------------------------------------------------- 1 | #include "Initializer.hpp" 2 | #include 3 | #include 4 | 5 | std::vector initializer_t::chunkData( std::vector& bytes, size_t size ) const 6 | { 7 | std::vector chunks; 8 | if (size == 0) 9 | return chunks; 10 | chunks.reserve( bytes.size() / size + 1 ); 11 | int index = 0; 12 | for (size_t i = 0; i < bytes.size(); i += size) 13 | { 14 | chunks.emplace_back( bytes, i, i + size, index ); 15 | index++; 16 | } 17 | return chunks; 18 | } 19 | 20 | std::vector initializer_t::getDataFromFile( const std::string& filepath, bool skip_header ) const 21 | { 22 | std::vector bytes = bytes_mapper_t::fileToBytes( filepath ); 23 | 24 | if (skip_header) 25 | bytes = std::vector( bytes.begin() + 20, bytes.end() ); 26 | 27 | return bytes; 28 | } 29 | 30 | void initializer_t::initializeEnemyData( std::string monster_id, const std::string monster_file ) 31 | { 32 | std::vector bytes = bytes_mapper_t::fileToBytes( INPUT_FOLDER + MONSTER_FOLDER + monster_file ); 33 | // Erase the leading m 34 | monster_id.erase( 0, 1 ); 35 | enemy_data_t* enemy = new enemy_data_t( monster_id, bytes ); 36 | enemy_data_t* unmodified = new enemy_data_t( monster_id, bytes ); 37 | { 38 | std::lock_guard lock(enemy_data_mutex); 39 | enemy_data.emplace( std::stoi( monster_id ), enemy ); 40 | unmodified_enemy_data.emplace( std::stoi( monster_id ), unmodified ); 41 | } 42 | } 43 | 44 | void initializer_t::initializeFieldData() 45 | { 46 | field_data.reserve( 498 ); 47 | std::string path = INPUT_FOLDER + BATTLE_KERNEL_FOLDER + "takara.bin"; 48 | genericExcelReader( path, field_data, 4 ); 49 | } 50 | 51 | void initializer_t::initializeShopData( bool gear ) 52 | { 53 | gear_shop_data.reserve( 47 ); 54 | item_shop_data.reserve( 47 ); 55 | std::string file = gear ? "arms_shop.bin" : "item_shop.bin"; 56 | std::string path = INPUT_FOLDER + BATTLE_KERNEL_FOLDER + file; 57 | if (gear) 58 | genericExcelReader( path, gear_shop_data, 34 ); 59 | else 60 | genericExcelReader( path, item_shop_data, 34 ); 61 | } 62 | 63 | void initializer_t::initializeBukiData() 64 | { 65 | buki_data.reserve( 86 ); 66 | std::string path = INPUT_FOLDER + BATTLE_KERNEL_FOLDER + "buki_get.bin"; 67 | genericExcelReader( path, buki_data, 16 ); 68 | } 69 | 70 | void initializer_t::initializeWeaponData() 71 | { 72 | weapon_data.reserve( 153 ); 73 | std::string path = INPUT_FOLDER + BATTLE_KERNEL_FOLDER + "weapon.bin"; 74 | genericExcelReader( path, weapon_data, 22 ); 75 | } 76 | 77 | void initializer_t::initializeShopArmsData() 78 | { 79 | shop_arms_data.reserve( 428 ); 80 | std::string path = INPUT_FOLDER + BATTLE_KERNEL_FOLDER + "shop_arms.bin"; 81 | genericExcelReader( path, shop_arms_data, 22 ); 82 | } 83 | 84 | void initializer_t::initializeItemRateData() 85 | { 86 | item_rate_data.reserve( 253 ); 87 | std::string path = INPUT_FOLDER + BATTLE_KERNEL_FOLDER + "item_rate.bin"; 88 | genericExcelReader( path, item_rate_data, 4 ); 89 | } 90 | 91 | void initializer_t::initializeArmsRateData() 92 | { 93 | arms_rate_data.reserve( 134 ); 94 | std::string path = INPUT_FOLDER + BATTLE_KERNEL_FOLDER + "arms_rate.bin"; 95 | genericExcelReader( path, arms_rate_data, 4 ); 96 | } 97 | 98 | void initializer_t::initializePlayerStatData() 99 | { 100 | player_stats_data.reserve( 20 ); 101 | std::string path = INPUT_FOLDER + USPC_BTL_KERN_FOLDER + "ply_save.bin"; 102 | genericExcelReader( path, player_stats_data, 148 ); 103 | } 104 | 105 | void initializer_t::initializeAeonScalingData() 106 | { 107 | aeon_scaling_data.reserve( 8 ); 108 | std::string path = INPUT_FOLDER + USPC_BTL_KERN_FOLDER + "ply_rom.bin"; 109 | genericExcelReader( path, aeon_scaling_data, 44, true, 8 ); 110 | } 111 | 112 | void initializer_t::initializeAeonStatData() 113 | { 114 | aeon_stat_data.reserve( 20 ); 115 | std::string path = INPUT_FOLDER + BATTLE_KERNEL_FOLDER + "sum_assure.bin"; 116 | genericExcelReader( path, aeon_stat_data, 120 ); 117 | } 118 | 119 | void initializer_t::initializeSphereGridData() 120 | { 121 | sphere_grid_data.reserve( 3 ); 122 | for (int i = 0; i < 3; i++) 123 | { 124 | std::string name = "dat0" + std::to_string( i + 1 ) + ".dat"; 125 | std::string path = INPUT_FOLDER + ABMAP_FOLDER + name; 126 | std::vector bytes = bytes_mapper_t::fileToBytes( path ); 127 | sphere_grid_data_t* data = new sphere_grid_data_t( bytes, static_cast< sphere_grid_type_e >( i ) ); 128 | sphere_grid_data.push_back( data ); 129 | } 130 | } 131 | 132 | void initializer_t::initializeBtlData() 133 | { 134 | encounter_file_data.reserve( 414 ); 135 | std::string path = INPUT_FOLDER + BATTLE_KERNEL_FOLDER + "btl.bin"; 136 | btl_data_t btl_data = btl_data_t( getDataFromFile( path ) ); 137 | 138 | btl_data.getEncounterFiles( encounter_file_data ); 139 | } 140 | 141 | void initializer_t::initializeGearCustomizeData() 142 | { 143 | gear_customize_data.reserve( 20 ); 144 | std::string path = INPUT_FOLDER + BATTLE_KERNEL_FOLDER + "kaizou.bin"; 145 | genericExcelReader( path, gear_customize_data, 8 ); 146 | } 147 | 148 | void initializer_t::initializeAeonStatCustomizeData() 149 | { 150 | aeon_stat_customize_data.reserve( 20 ); 151 | std::string path = INPUT_FOLDER + BATTLE_KERNEL_FOLDER + "sum_grow.bin"; 152 | genericExcelReader( path, aeon_stat_customize_data, 8 ); 153 | } 154 | 155 | void initializer_t::initializeGUI() 156 | { 157 | gui = new gui_t( *data_pack ); 158 | wxApp::SetInstance( gui ); 159 | wxEntryStart( 0, nullptr ); 160 | wxTheApp->CallOnInit(); 161 | wxTheApp->OnRun(); 162 | wxTheApp->OnExit(); 163 | wxEntryCleanup(); 164 | } 165 | 166 | void initializer_t::initializeAllData() 167 | { 168 | enemy_data.reserve( ENEMY_COUNT ); 169 | unmodified_enemy_data.reserve( ENEMY_COUNT ); 170 | 171 | // Build list of enemy tasks 172 | std::vector> enemy_tasks; 173 | enemy_tasks.reserve( ENEMY_COUNT ); 174 | 175 | std::string monster_id; 176 | monster_id.reserve( 5 ); 177 | std::string filename; 178 | filename.reserve( 5 ); 179 | std::string monster_file; 180 | monster_file.reserve( 15 ); 181 | for (const auto& file : std::filesystem::directory_iterator( INPUT_FOLDER + MONSTER_FOLDER )) 182 | { 183 | filename = file.path().filename().string(); 184 | monster_id = filename; 185 | // Erase the leading underscore 186 | monster_id.erase( 0, 1 ); 187 | monster_file = filename + "/" + monster_id + ".bin"; 188 | enemy_tasks.emplace_back( monster_id, monster_file ); 189 | } 190 | 191 | // Thread pool for enemy loading 192 | std::vector enemy_workers; 193 | std::atomic next_task{0}; 194 | unsigned int worker_count = std::thread::hardware_concurrency(); 195 | if (worker_count == 0) 196 | worker_count = 4; // sensible default 197 | worker_count = std::min( worker_count, static_cast( enemy_tasks.size() == 0 ? 1 : enemy_tasks.size() ) ); 198 | 199 | enemy_workers.reserve( worker_count ); 200 | for (unsigned int i = 0; i < worker_count; ++i) 201 | { 202 | enemy_workers.emplace_back([this, &enemy_tasks, &next_task]() { 203 | for (;;) 204 | { 205 | size_t idx = next_task.fetch_add(1, std::memory_order_relaxed); 206 | if (idx >= enemy_tasks.size()) break; 207 | auto& task = enemy_tasks[idx]; 208 | this->initializeEnemyData( task.first, task.second ); 209 | } 210 | }); 211 | } 212 | 213 | std::thread btl_data_thread( &initializer_t::initializeBtlData, this ); 214 | std::thread field_thread( &initializer_t::initializeFieldData, this ); 215 | std::thread shop_item_thread( &initializer_t::initializeShopData, this, false ); 216 | std::thread shop_gear_thread( &initializer_t::initializeShopData, this, true ); 217 | std::thread buki_thread( &initializer_t::initializeBukiData, this ); 218 | std::thread weapon_thread( &initializer_t::initializeWeaponData, this ); 219 | std::thread shop_arms_thread( &initializer_t::initializeShopArmsData, this ); 220 | std::thread item_rate_thread( &initializer_t::initializeItemRateData, this ); 221 | std::thread arms_rate_thread( &initializer_t::initializeArmsRateData, this ); 222 | std::thread player_stats_thread( &initializer_t::initializePlayerStatData, this ); 223 | std::thread aeon_scaling_thread( &initializer_t::initializeAeonScalingData, this ); 224 | std::thread aeon_stat_thread( &initializer_t::initializeAeonStatData, this ); 225 | std::thread sphere_grid_thread( &initializer_t::initializeSphereGridData, this ); 226 | std::thread gear_customize_thread( &initializer_t::initializeGearCustomizeData, this ); 227 | std::thread aeon_stat_customize_thread( &initializer_t::initializeAeonStatCustomizeData, this ); 228 | 229 | field_thread.join(); 230 | shop_item_thread.join(); 231 | shop_gear_thread.join(); 232 | buki_thread.join(); 233 | weapon_thread.join(); 234 | shop_arms_thread.join(); 235 | item_rate_thread.join(); 236 | arms_rate_thread.join(); 237 | player_stats_thread.join(); 238 | aeon_scaling_thread.join(); 239 | aeon_stat_thread.join(); 240 | sphere_grid_thread.join(); 241 | gear_customize_thread.join(); 242 | aeon_stat_customize_thread.join(); 243 | btl_data_thread.join(); 244 | 245 | for (auto& t : enemy_workers) 246 | if (t.joinable()) 247 | t.join(); 248 | } 249 | 250 | void initializer_t::runEnemyTests() 251 | { 252 | for (auto& enemy : enemy_data) 253 | enemy.second->test(); 254 | } 255 | 256 | void initializer_t::runFieldTests() 257 | { 258 | for (auto& item : field_data) 259 | item->test(); 260 | } 261 | 262 | void initializer_t::runShopTests() 263 | { 264 | for (auto& shop : gear_shop_data) 265 | shop->test(); 266 | for (auto& shop : item_shop_data) 267 | shop->test(); 268 | } 269 | 270 | void initializer_t::runBukiTests() 271 | { 272 | for (auto& buki : buki_data) 273 | buki->test(); 274 | } 275 | 276 | void initializer_t::runWeaponTests() 277 | { 278 | for (auto& weapon : weapon_data) 279 | weapon->test(); 280 | } 281 | 282 | void initializer_t::runShopArmsTests() 283 | { 284 | for (auto& shop : shop_arms_data) 285 | shop->test(); 286 | } 287 | 288 | void initializer_t::runEnemyLootTests() 289 | { 290 | for (auto& loot : enemy_data) 291 | loot.second->loot_data->test(); 292 | } 293 | 294 | void initializer_t::runItemRateTests() 295 | { 296 | for (auto& item_rate : item_rate_data) 297 | item_rate->test(); 298 | } 299 | 300 | void initializer_t::runSphereGridTests() 301 | { 302 | for (auto& grid : sphere_grid_data) 303 | grid->test(); 304 | } 305 | 306 | void initializer_t::runTests() 307 | { 308 | std::thread enemy_thread( &initializer_t::runEnemyTests, this ); 309 | std::thread field_thread( &initializer_t::runFieldTests, this ); 310 | std::thread shop_thread( &initializer_t::runShopTests, this ); 311 | std::thread buki_thread( &initializer_t::runBukiTests, this ); 312 | std::thread weapon_thread( &initializer_t::runWeaponTests, this ); 313 | std::thread shop_arms_thread( &initializer_t::runShopArmsTests, this ); 314 | std::thread enemy_loot_thread( &initializer_t::runEnemyLootTests, this ); 315 | std::thread item_rate_thread( &initializer_t::runItemRateTests, this ); 316 | std::thread sphere_grid_thread( &initializer_t::runSphereGridTests, this ); 317 | 318 | enemy_thread.join(); 319 | field_thread.join(); 320 | shop_thread.join(); 321 | buki_thread.join(); 322 | weapon_thread.join(); 323 | shop_arms_thread.join(); 324 | enemy_loot_thread.join(); 325 | item_rate_thread.join(); 326 | sphere_grid_thread.join(); 327 | } 328 | 329 | // Destructor implementation: free owned pointers 330 | initializer_t::~initializer_t() 331 | { 332 | // Do not delete gui: wxEntryCleanup handles wxApp lifetime in initializeGUI. 333 | 334 | auto clearMap = [](auto& m) { 335 | for (auto& kv : m) delete kv.second; 336 | m.clear(); 337 | }; 338 | auto clearVec = [](auto& v) { 339 | for (auto* p : v) delete p; 340 | v.clear(); 341 | }; 342 | 343 | clearMap(enemy_data); 344 | clearMap(unmodified_enemy_data); 345 | 346 | clearVec(field_data); 347 | clearVec(item_shop_data); 348 | clearVec(gear_shop_data); 349 | clearVec(buki_data); 350 | clearVec(weapon_data); 351 | clearVec(shop_arms_data); 352 | clearVec(item_rate_data); 353 | clearVec(arms_rate_data); 354 | clearVec(player_stats_data); 355 | clearVec(aeon_scaling_data); 356 | clearVec(aeon_stat_data); 357 | clearVec(sphere_grid_data); 358 | clearVec(encounter_file_data); 359 | clearVec(gear_customize_data); 360 | clearVec(aeon_stat_customize_data); 361 | 362 | delete data_pack; 363 | data_pack = nullptr; 364 | } 365 | -------------------------------------------------------------------------------- /src/core/GearRandomizer.cpp: -------------------------------------------------------------------------------- 1 | #include "Randomizer.hpp" 2 | 3 | void randomizer_t::reconstructBukiData() 4 | { 5 | std::string name = "buki_get.bin"; 6 | genericReconstructor( btl_kernel_input, btl_kernel_output, name, data_pack.buki_data, true ); 7 | } 8 | 9 | void randomizer_t::reconstructWeaponData() 10 | { 11 | std::string name = "weapon.bin"; 12 | genericReconstructor( btl_kernel_input, btl_kernel_output, name, data_pack.weapon_data, true ); 13 | } 14 | 15 | void randomizer_t::reconstructShopArmsData() 16 | { 17 | std::string name = "shop_arms.bin"; 18 | genericReconstructor( btl_kernel_input, btl_kernel_output, name, data_pack.shop_arms_data, true ); 19 | } 20 | 21 | void randomizer_t::reconstructKaizouData() 22 | { 23 | std::string name = "kaizou.bin"; 24 | genericReconstructor( btl_kernel_input, btl_kernel_output, name, data_pack.customize_data, true ); 25 | } 26 | 27 | void randomizer_t::poplateGearLists() 28 | { 29 | for (auto& gear : data_pack.buki_data) 30 | { 31 | if (gear->is_brotherhood && !options_pack.randomize_brotherhood) 32 | continue; 33 | if (gear->is_celestial && !options_pack.randomize_celestials) 34 | continue; 35 | if (gear->is_armor) 36 | all_armor.push_back( gear ); 37 | else 38 | all_weapons.push_back( gear ); 39 | } 40 | for (auto& gear : data_pack.weapon_data) 41 | { 42 | if (gear->is_armor) 43 | all_armor.push_back( gear ); 44 | else 45 | all_weapons.push_back( gear ); 46 | } 47 | for (auto& gear : data_pack.shop_arms_data) 48 | { 49 | if (gear->is_armor) 50 | all_armor.push_back( gear ); 51 | else 52 | all_weapons.push_back( gear ); 53 | } 54 | } 55 | 56 | uint16_t randomizer_t::getRandomAbility() 57 | { 58 | std::uniform_int_distribution dist( 0, abilities.size() - 1 ); 59 | return abilities[ dist( rng ) ]; 60 | } 61 | 62 | void randomizer_t::populateAbilityData() 63 | { 64 | for (auto& gear : all_armor) 65 | gear->mapAbilities( abilities ); 66 | 67 | for (auto& gear : all_weapons) 68 | gear->mapAbilities( abilities ); 69 | 70 | for (auto& enemy : data_pack.enemy_data) 71 | { 72 | enemy_loot_data_t& loot = *enemy.second->loot_data; 73 | for (int chr = 0; chr < 7; chr++) 74 | { 75 | for (int i = 0; i < 8; i++) 76 | { 77 | bool found = std::find( abilities.begin(), abilities.end(), loot.weapon_abilities_by_char.at( chr ) ) != abilities.end(); 78 | if (!found && loot.weapon_abilities_by_char.at( chr ) != 255) 79 | abilities.push_back( loot.weapon_abilities_by_char.at( chr ) ); 80 | } 81 | } 82 | for (int chr = 0; chr < 7; chr++) 83 | { 84 | for (int i = 0; i < 8; i++) 85 | { 86 | bool found = std::find( abilities.begin(), abilities.end(), loot.gear_abilities_by_char.at( chr ) ) != abilities.end(); 87 | if (!found && loot.gear_abilities_by_char.at( chr ) != 255) 88 | abilities.push_back( loot.gear_abilities_by_char.at( chr ) ); 89 | } 90 | } 91 | } 92 | } 93 | 94 | weapon_formula_e randomizer_t::getRandomFormula() 95 | { 96 | static const std::vector weapon_formulas = { 97 | WEAPON_FORMULA_STR_VS_DEF, 98 | WEAPON_FORMULA_STR_IGNORE_DEF, 99 | WEAPON_FORMULA_MAG_VS_MDEF, 100 | WEAPON_FORMULA_MAG_IGNORE_MDEF, 101 | WEAPON_FORMULA_CURRENT_STATISTIC, 102 | WEAPON_FORMULA_MULTIPLE_OF_50, 103 | WEAPON_FORMULA_HEALING, 104 | WEAPON_FORMULA_TARGET_MAX_HP, 105 | WEAPON_FORMULA_MULTIPLE_OF_50_VARIABLE, 106 | WEAPON_FORMULA_TARGET_MAX_MP, 107 | WEAPON_FORMULA_TARGET_MAX_CTB, 108 | WEAPON_FORMULA_TARGET_CURRENT_MP, 109 | WEAPON_FORMULA_TARGET_CURRENT_CTB, 110 | WEAPON_FORMULA_STR_NO_CHEER, 111 | WEAPON_FORMULA_SPECIAL, 112 | WEAPON_FORMULA_PLAYER_MAX_HP_TENTHS, 113 | WEAPON_FORMULA_CELESTIAL_HIGH_HP, 114 | WEAPON_FORMULA_CELESTIAL_HIGH_MP, 115 | WEAPON_FORMULA_CELESTIAL_LOW_HP, 116 | WEAPON_FORMULA_MAG_NO_CHEER, 117 | WEAPON_FORMULA_KILLS, 118 | WEAPON_FORMULA_MULTIPLE_OF_9999 119 | }; 120 | weapon_formula_e formula = randomElement( weapon_formulas ); 121 | return formula; 122 | } 123 | 124 | void randomizer_t::randomizeGearAbilities( gear_data_t& gear ) 125 | { 126 | if (( gear.is_celestial && !options_pack.randomize_celestials ) || ( gear.is_brotherhood && !options_pack.randomize_brotherhood )) 127 | return; 128 | 129 | uint8_t n_slots = std::discrete_distribution( { 0.4, 0.3, 0.2, 0.1 } )( rng ); 130 | uint8_t n_abilities = uniform( 0, n_slots ); 131 | 132 | std::vector abilities; 133 | 134 | for (uint8_t i = 0; i < 4; i++) 135 | { 136 | if (i >= n_abilities) 137 | { 138 | abilities.push_back( 255 ); 139 | continue; 140 | } 141 | abilities.push_back( getRandomAbility() ); 142 | } 143 | 144 | gear.slots = n_slots; 145 | gear.ability_slot1 = abilities.at( 0 ); 146 | gear.ability_slot2 = abilities.at( 1 ); 147 | gear.ability_slot3 = abilities.at( 2 ); 148 | gear.ability_slot4 = abilities.at( 3 ); 149 | 150 | gear.writeToBytes(); 151 | } 152 | 153 | void randomizer_t::randomizeShopArmsAbilities() 154 | { 155 | for (auto& gear : data_pack.shop_arms_data) 156 | { 157 | gear_data_t& gear_data = *gear; 158 | randomizeGearAbilities( gear_data ); 159 | } 160 | } 161 | 162 | void randomizer_t::randomizeBukiAbilities() 163 | { 164 | for (auto& gear : data_pack.buki_data) 165 | { 166 | gear_data_t& gear_data = *gear; 167 | randomizeGearAbilities( gear_data ); 168 | } 169 | } 170 | 171 | void randomizer_t::randomizeWeaponsAbilities() 172 | { 173 | for (auto& gear : data_pack.weapon_data) 174 | { 175 | gear_data_t& gear_data = *gear; 176 | randomizeGearAbilities( gear_data ); 177 | } 178 | } 179 | 180 | void randomizer_t::randomizeWeaponCrit() 181 | { 182 | for (auto& gear : data_pack.shop_arms_data) 183 | { 184 | gear_data_t& gear_data = *gear; 185 | if (gear_data.is_armor || ( gear_data.is_celestial && !options_pack.randomize_celestials ) || ( gear_data.is_brotherhood && !options_pack.randomize_brotherhood )) 186 | continue; 187 | if (options_pack.keep_things_sane) 188 | gear_data.crit_bonus = normal( gear_data.crit_bonus, 1, 1, 8 ); 189 | else 190 | gear_data.crit_bonus = uniform( 0, 100 ); 191 | gear_data.writeToBytes(); 192 | } 193 | for (auto& gear : data_pack.buki_data) 194 | { 195 | gear_data_t& gear_data = *gear; 196 | if (gear_data.is_armor || ( gear_data.is_celestial && !options_pack.randomize_celestials ) || ( gear_data.is_brotherhood && !options_pack.randomize_brotherhood )) 197 | continue; 198 | if (options_pack.keep_things_sane) 199 | gear_data.crit_bonus = normal( gear_data.crit_bonus, 1, 1, 8 ); 200 | else 201 | gear_data.crit_bonus = uniform( 0, 100 ); 202 | gear_data.writeToBytes(); 203 | } 204 | for (auto& gear : data_pack.weapon_data) 205 | { 206 | gear_data_t& gear_data = *gear; 207 | if (gear_data.is_armor || ( gear_data.is_celestial && !options_pack.randomize_celestials ) || ( gear_data.is_brotherhood && !options_pack.randomize_brotherhood )) 208 | continue; 209 | if (options_pack.keep_things_sane) 210 | gear_data.crit_bonus = normal( gear_data.crit_bonus, 1, 1, 8 ); 211 | else 212 | gear_data.crit_bonus = uniform( 0, 100 ); 213 | gear_data.writeToBytes(); 214 | } 215 | } 216 | 217 | void randomizer_t::randomizeWeaponAttackPower() 218 | { 219 | for (auto& gear : data_pack.shop_arms_data) 220 | { 221 | gear_data_t& gear_data = *gear; 222 | if (gear_data.is_armor || ( gear_data.is_celestial && !options_pack.randomize_celestials ) || ( gear_data.is_brotherhood && !options_pack.randomize_brotherhood )) 223 | continue; 224 | if (options_pack.keep_things_sane) 225 | gear_data.attack_power = normal( 16, 3, 10, 24 ); 226 | else 227 | gear_data.attack_power = uniform( 1, 100 ); 228 | 229 | gear_data.writeToBytes(); 230 | } 231 | for (auto& gear : data_pack.buki_data) 232 | { 233 | gear_data_t& gear_data = *gear; 234 | if (gear_data.is_armor || ( gear_data.is_celestial && !options_pack.randomize_celestials ) || ( gear_data.is_brotherhood && !options_pack.randomize_brotherhood )) 235 | continue; 236 | if (options_pack.keep_things_sane) 237 | gear_data.attack_power = normal( 16, 3, 10, 24 ); 238 | else 239 | gear_data.attack_power = uniform( 1, 100 ); 240 | gear_data.writeToBytes(); 241 | } 242 | for (auto& gear : data_pack.weapon_data) 243 | { 244 | gear_data_t& gear_data = *gear; 245 | if (gear_data.is_armor || ( gear_data.is_celestial && !options_pack.randomize_celestials ) || ( gear_data.is_brotherhood && !options_pack.randomize_brotherhood )) 246 | continue; 247 | if (options_pack.keep_things_sane) 248 | gear_data.attack_power = normal( 16, 3, 10, 24 ); 249 | else 250 | gear_data.attack_power = uniform( 1, 100 ); 251 | gear_data.writeToBytes(); 252 | } 253 | } 254 | 255 | void randomizer_t::randomizeWeaponDamageFormula() 256 | { 257 | for (auto& gear : data_pack.shop_arms_data) 258 | { 259 | gear_data_t& gear_data = *gear; 260 | if (gear_data.is_armor || ( gear_data.is_celestial && !options_pack.randomize_celestials ) || ( gear_data.is_brotherhood && !options_pack.randomize_brotherhood )) 261 | continue; 262 | 263 | gear_data.damage_calc = getRandomFormula(); 264 | gear_data.writeToBytes(); 265 | } 266 | for (auto& gear : data_pack.buki_data) 267 | { 268 | gear_data_t& gear_data = *gear; 269 | if (gear_data.is_armor || ( gear_data.is_celestial && !options_pack.randomize_celestials ) || ( gear_data.is_brotherhood && !options_pack.randomize_brotherhood )) 270 | continue; 271 | 272 | gear_data.damage_calc = getRandomFormula(); 273 | gear_data.writeToBytes(); 274 | } 275 | for (auto& gear : data_pack.weapon_data) 276 | { 277 | gear_data_t& gear_data = *gear; 278 | if (gear_data.is_armor || ( gear_data.is_celestial && !options_pack.randomize_celestials ) || ( gear_data.is_brotherhood && !options_pack.randomize_brotherhood )) 279 | continue; 280 | 281 | gear_data.damage_calc = getRandomFormula(); 282 | gear_data.writeToBytes(); 283 | } 284 | } 285 | 286 | void randomizer_t::randomizeCustomizeItems() 287 | { 288 | for (auto& customization : data_pack.customize_data) 289 | { 290 | customize_data_t& customize = *customization; 291 | item_t* item = getRandomItem(); 292 | customize.item = item->id; 293 | uint8_t item_quantity = 1; 294 | if (options_pack.keep_things_sane) 295 | item_quantity = normal( 20, 10, 1, 99 ); 296 | else 297 | item_quantity = uniform( 1, 99 ); 298 | customize.item_quantity = item_quantity; 299 | customize.writeToBytes(); 300 | } 301 | } 302 | 303 | void randomizer_t::doGearRandomization() 304 | { 305 | if (!options_pack.randomize_gear_abilities && !options_pack.randomize_weapon_attack_power && !options_pack.randomize_weapon_crit && 306 | !options_pack.randomize_weapon_damage_formula && !options_pack.randomize_customization_items) 307 | return; 308 | 309 | if (options_pack.randomize_gear_abilities) 310 | { 311 | printf( "Randomizing shop_arms.bin Abilities...\n" ); 312 | std::thread shop_arms_thread( &randomizer_t::randomizeShopArmsAbilities, this ); 313 | printf( "Randomizing buki_get.bin Abilities...\n" ); 314 | std::thread buki_thread( &randomizer_t::randomizeBukiAbilities, this ); 315 | printf( "Randomizing weapon.bin Abilities...\n" ); 316 | std::thread weapon_thread( &randomizer_t::randomizeWeaponsAbilities, this ); 317 | 318 | shop_arms_thread.join(); 319 | buki_thread.join(); 320 | weapon_thread.join(); 321 | } 322 | 323 | if (options_pack.randomize_weapon_attack_power) 324 | { 325 | printf( "Randomizing weapon.bin Attack Power...\n" ); 326 | randomizeWeaponAttackPower(); 327 | } 328 | 329 | if (options_pack.randomize_weapon_crit) 330 | { 331 | printf( "Randomizing weapon.bin Crit Chance...\n" ); 332 | randomizeWeaponCrit(); 333 | } 334 | 335 | if (options_pack.randomize_weapon_damage_formula) 336 | { 337 | printf( "Randomizing weapon.bin Damage Formula...\n" ); 338 | randomizeWeaponDamageFormula(); 339 | } 340 | 341 | if (options_pack.randomize_customization_items) 342 | { 343 | printf( "Randomizing customization items...\n" ); 344 | randomizeCustomizeItems(); 345 | reconstructKaizouData(); 346 | } 347 | 348 | printf( "Reconstructing shop_arms.bin...\n" ); 349 | std::thread shop_arms_data_thread( &randomizer_t::reconstructShopArmsData, this ); 350 | printf( "Reconstructing buki_get.bin...\n" ); 351 | std::thread buki_data_thread( &randomizer_t::reconstructBukiData, this ); 352 | printf( "Reconstructing weapon.bin...\n" ); 353 | std::thread weapon_data_thread( &randomizer_t::reconstructWeaponData, this ); 354 | 355 | shop_arms_data_thread.join(); 356 | buki_data_thread.join(); 357 | weapon_data_thread.join(); 358 | } -------------------------------------------------------------------------------- /src/core/SphereGridRandomizer.cpp: -------------------------------------------------------------------------------- 1 | #include "Randomizer.hpp" 2 | 3 | void randomizer_t::reconstructSphereGridData() 4 | { 5 | for (auto& grid : data_pack.sphere_grid_data) 6 | { 7 | //std::string name = "dat0" + std::to_string( grid->type + 1 ) + ".dat"; 8 | //std::string string = "Reconstructing " + name + "..."; 9 | //std::cout << string << std::endl; 10 | //std::string path = INPUT_FOLDER + ABMAP_FOLDER + name; 11 | std::string output_path = OUTPUT_FOLDER + prefix + ABMAP_FOLDER; 12 | std::filesystem::create_directories( output_path ); 13 | //std::string file = output_path + name; 14 | //bytes_mapper_t::writeBytesToNewFile( grid->bytes, file ); 15 | 16 | std::string content_name; 17 | 18 | switch (grid->type) 19 | { 20 | case SPHERE_GRID_ORIGINAL: 21 | content_name = "dat09.dat"; 22 | break; 23 | case SPHERE_GRID_STANDARD: 24 | content_name = "dat10.dat"; 25 | break; 26 | case SPHERE_GRID_EXPERT: 27 | content_name = "dat11.dat"; 28 | break; 29 | default: 30 | break; 31 | } 32 | std::string content_string = "Reconstructing " + content_name + "..."; 33 | std::cout << content_string << std::endl; 34 | std::string content_path = INPUT_FOLDER + ABMAP_FOLDER + content_name; 35 | std::vector content_bytes; 36 | content_bytes.insert( content_bytes.end(), grid->full_content_bytes.begin(), grid->full_content_bytes.end() ); 37 | std::string content_file = output_path + content_name; 38 | bytes_mapper_t::writeBytesToNewFile( content_bytes, content_file ); 39 | } 40 | } 41 | 42 | 43 | void randomizer_t::getSphereGridNodeIds() 44 | { 45 | for (auto& grid : data_pack.sphere_grid_data) 46 | { 47 | for (auto& node : grid->nodes) 48 | { 49 | switch (grid->type) 50 | { 51 | case SPHERE_GRID_ORIGINAL: 52 | original_sphere_grid_node_ids.push_back( node->original_content ); 53 | break; 54 | case SPHERE_GRID_STANDARD: 55 | standard_sphere_grid_node_ids.push_back( node->original_content ); 56 | break; 57 | case SPHERE_GRID_EXPERT: 58 | expert_sphere_grid_node_ids.push_back( node->original_content ); 59 | break; 60 | default: 61 | std::cerr << "Unknown sphere grid type: " << grid->type << std::endl; 62 | break; 63 | } 64 | } 65 | } 66 | } 67 | 68 | void randomizer_t::setRequiredAbilities() 69 | { 70 | // Force abilities used by tutorials to be learned to prevent softlocks 71 | character_stats_t& riku_stats = *data_pack.player_stats_data[ ply_save_e::CHARACTER_RIKU ]; 72 | riku_stats.ability_flags1.bits.use = 1; 73 | riku_stats.ability_flags1.bits.steal = 1; 74 | riku_stats.writeToBytes(); 75 | 76 | character_stats_t& kimari_stats = *data_pack.player_stats_data[ ply_save_e::CHARACTER_KIMAHRI ]; 77 | kimari_stats.ability_flags2.bits.lancet = 1; 78 | kimari_stats.writeToBytes(); 79 | 80 | character_stats_t& wakka_stats = *data_pack.player_stats_data[ ply_save_e::CHARACTER_WAKKA ]; 81 | wakka_stats.ability_flags1.bits.dark_attack = 1; 82 | wakka_stats.writeToBytes(); 83 | 84 | character_stats_t& lulu_stats = *data_pack.player_stats_data[ ply_save_e::CHARACTER_LULU ]; 85 | lulu_stats.ability_flags3.bits.thunder = 1; 86 | lulu_stats.writeToBytes(); 87 | } 88 | 89 | void randomizer_t::shuffleSphereGridNodes() 90 | { 91 | setRequiredAbilities(); 92 | 93 | for (auto& grid : data_pack.sphere_grid_data) 94 | { 95 | switch (grid->type) 96 | { 97 | case sphere_grid_type_e::SPHERE_GRID_ORIGINAL: 98 | shuffled_original_sphere_grid_node_ids = original_sphere_grid_node_ids; 99 | std::shuffle( shuffled_original_sphere_grid_node_ids.begin(), shuffled_original_sphere_grid_node_ids.end(), rng ); 100 | break; 101 | case sphere_grid_type_e::SPHERE_GRID_STANDARD: 102 | shuffled_standard_sphere_grid_node_ids = standard_sphere_grid_node_ids; 103 | std::shuffle( shuffled_standard_sphere_grid_node_ids.begin(), shuffled_standard_sphere_grid_node_ids.end(), rng ); 104 | break; 105 | case sphere_grid_type_e::SPHERE_GRID_EXPERT: 106 | shuffled_expert_sphere_grid_node_ids = expert_sphere_grid_node_ids; 107 | std::shuffle( shuffled_expert_sphere_grid_node_ids.begin(), shuffled_expert_sphere_grid_node_ids.end(), rng ); 108 | break; 109 | default: 110 | break; 111 | } 112 | 113 | for (size_t i = 0; i < grid->nodes.size(); i++) 114 | { 115 | sphere_grid_node_data_t& node = *grid->nodes[ i ]; 116 | if (node.original_content == NODE_NULL) 117 | continue; // Skip null nodes 118 | 119 | switch (grid->type) 120 | { 121 | case sphere_grid_type_e::SPHERE_GRID_ORIGINAL: 122 | node.content = shuffled_original_sphere_grid_node_ids[ i ]; 123 | node.writeToBytes(); 124 | break; 125 | case sphere_grid_type_e::SPHERE_GRID_STANDARD: 126 | node.content = shuffled_standard_sphere_grid_node_ids[ i ]; 127 | node.writeToBytes(); 128 | break; 129 | case sphere_grid_type_e::SPHERE_GRID_EXPERT: 130 | node.content = shuffled_expert_sphere_grid_node_ids[ i ]; 131 | node.writeToBytes(); 132 | break; 133 | default: 134 | break; 135 | } 136 | } 137 | 138 | grid->writeToBytes(); 139 | } 140 | } 141 | 142 | void randomizer_t::randomizeSphereGridTrue() 143 | { 144 | setRequiredAbilities(); 145 | 146 | for (auto& grid : data_pack.sphere_grid_data) 147 | { 148 | sphere_grid_data_t& sphere_grid = *grid; 149 | for (auto& node_data : sphere_grid.nodes) 150 | { 151 | sphere_grid_node_data_t& node = *node_data; 152 | uint8_t new_content = uniform( 0, 0x7F ); 153 | node.content = new_content; 154 | node.writeToBytes(); 155 | } 156 | sphere_grid.writeToBytes(); 157 | } 158 | } 159 | 160 | void randomizer_t::randomizeSphereGrid() 161 | { 162 | setRequiredAbilities(); 163 | 164 | for (auto& grid : data_pack.sphere_grid_data) 165 | { 166 | std::vector abilities; 167 | abilities.reserve( 84 ); 168 | for (int i = 0x2A; i <= 0x7E; i++) 169 | abilities.push_back( i ); 170 | 171 | std::vector already_used_nodes; 172 | already_used_nodes.reserve( abilities.size() ); 173 | sphere_grid_data_t& sphere_grid = *grid; 174 | 175 | for (int i = 0; i < abilities.size(); i++) 176 | { 177 | uint8_t new_content = abilities[ i ]; 178 | int random_node_index = uniform( 0, sphere_grid.nodes.size() - 1 ); 179 | sphere_grid_node_data_t* node = sphere_grid.nodes[ random_node_index ]; 180 | node->content = new_content; 181 | node->writeToBytes(); 182 | already_used_nodes.push_back( node ); 183 | } 184 | 185 | for (auto& node_data : sphere_grid.nodes) 186 | { 187 | bool found = std::find( already_used_nodes.begin(), already_used_nodes.end(), node_data ) != already_used_nodes.end(); 188 | if (found) 189 | continue; 190 | sphere_grid_node_data_t& node = *node_data; 191 | uint8_t new_content = uniform( 0, options_pack.remove_sphere_grid_locks ? 0x26 : 0x29 ); 192 | node.content = new_content; 193 | node.writeToBytes(); 194 | } 195 | sphere_grid.writeToBytes(); 196 | } 197 | } 198 | 199 | void randomizer_t::emptySphereGrid() 200 | { 201 | for (auto& grid : data_pack.sphere_grid_data) 202 | { 203 | sphere_grid_data_t& sphere_grid = *grid; 204 | for (auto& node_data : sphere_grid.nodes) 205 | { 206 | sphere_grid_node_data_t& node = *node_data; 207 | std::vector blacklist = { 0xFF }; 208 | if (std::find( blacklist.begin(), blacklist.end(), node.original_content ) != blacklist.end()) 209 | continue; 210 | if (node.content <= 0x29) 211 | node.content = 0x01; 212 | node.writeToBytes(); 213 | } 214 | sphere_grid.writeToBytes(); 215 | } 216 | } 217 | 218 | void randomizer_t::fillSphereGrid() 219 | { 220 | for (auto& grid : data_pack.sphere_grid_data) 221 | { 222 | sphere_grid_data_t& sphere_grid = *grid; 223 | for (auto& node_data : sphere_grid.nodes) 224 | { 225 | sphere_grid_node_data_t& node = *node_data; 226 | if (node.content != 0x01) 227 | continue; 228 | uint8_t new_content = uniform( 1, 0x26 ); 229 | node.content = new_content; 230 | node.writeToBytes(); 231 | } 232 | sphere_grid.writeToBytes(); 233 | } 234 | } 235 | 236 | void randomizer_t::removeSphereGridLocks() 237 | { 238 | for (auto& grid : data_pack.sphere_grid_data) 239 | { 240 | sphere_grid_data_t& sphere_grid = *grid; 241 | for (auto& node_data : sphere_grid.nodes) 242 | { 243 | sphere_grid_node_data_t& node = *node_data; 244 | std::vector whitelist = { 0x00, 0x27, 0x28, 0x29 }; 245 | if (std::find( whitelist.begin(), whitelist.end(), node.content ) != whitelist.end()) 246 | node.content = 0x01; 247 | node.writeToBytes(); 248 | } 249 | sphere_grid.writeToBytes(); 250 | } 251 | } 252 | 253 | void randomizer_t::upgradeSphereGridNodes() 254 | { 255 | for (auto& grid : data_pack.sphere_grid_data) 256 | { 257 | sphere_grid_data_t& grid_data = *grid; 258 | for (auto& grid_node : grid_data.nodes) 259 | { 260 | sphere_grid_node_data_t& node = *grid_node; 261 | switch (node.content) 262 | { 263 | case NODE_STRENGTH_1: 264 | case NODE_STRENGTH_2: 265 | case NODE_STRENGTH_3: 266 | node.content = NODE_STRENGTH_4; 267 | break; 268 | case NODE_DEFENSE_1: 269 | case NODE_DEFENSE_2: 270 | case NODE_DEFENSE_3: 271 | node.content = NODE_DEFENSE_4; 272 | break; 273 | case NODE_MAGIC_1: 274 | case NODE_MAGIC_2: 275 | case NODE_MAGIC_3: 276 | node.content = NODE_MAGIC_4; 277 | break; 278 | case NODE_MAGIC_DEFENSE_1: 279 | case NODE_MAGIC_DEFENSE_2: 280 | case NODE_MAGIC_DEFENSE_3: 281 | node.content = NODE_MAGIC_DEFENSE_4; 282 | break; 283 | case NODE_AGILITY_1: 284 | case NODE_AGILITY_2: 285 | case NODE_AGILITY_3: 286 | node.content = NODE_AGILITY_4; 287 | break; 288 | case NODE_LUCK_1: 289 | case NODE_LUCK_2: 290 | case NODE_LUCK_3: 291 | node.content = NODE_LUCK_4; 292 | break; 293 | case NODE_EVASION_1: 294 | case NODE_EVASION_2: 295 | case NODE_EVASION_3: 296 | node.content = NODE_EVASION_4; 297 | break; 298 | case NODE_ACCURACY_1: 299 | case NODE_ACCURACY_2: 300 | case NODE_ACCURACY_3: 301 | node.content = NODE_ACCURACY_4; 302 | break; 303 | case NODE_HP_200: 304 | node.content = NODE_HP_300; 305 | break; 306 | case NODE_MP_10: 307 | case NODE_MP_20: 308 | node.content = NODE_MP_40; 309 | break; 310 | default: 311 | break; 312 | } 313 | node.writeToBytes(); 314 | } 315 | grid_data.writeToBytes(); 316 | } 317 | } 318 | 319 | void randomizer_t::downgradeSphereGridNodes() 320 | { 321 | for (auto& grid : data_pack.sphere_grid_data) 322 | { 323 | sphere_grid_data_t& grid_data = *grid; 324 | for (auto& grid_node : grid_data.nodes) 325 | { 326 | sphere_grid_node_data_t& node = *grid_node; 327 | switch (node.content) 328 | { 329 | case NODE_STRENGTH_2: 330 | case NODE_STRENGTH_3: 331 | case NODE_STRENGTH_4: 332 | node.content = NODE_STRENGTH_1; 333 | break; 334 | case NODE_DEFENSE_2: 335 | case NODE_DEFENSE_3: 336 | case NODE_DEFENSE_4: 337 | node.content = NODE_DEFENSE_1; 338 | break; 339 | case NODE_MAGIC_2: 340 | case NODE_MAGIC_3: 341 | case NODE_MAGIC_4: 342 | node.content = NODE_MAGIC_1; 343 | break; 344 | case NODE_MAGIC_DEFENSE_2: 345 | case NODE_MAGIC_DEFENSE_3: 346 | case NODE_MAGIC_DEFENSE_4: 347 | node.content = NODE_MAGIC_DEFENSE_1; 348 | break; 349 | case NODE_AGILITY_2: 350 | case NODE_AGILITY_3: 351 | case NODE_AGILITY_4: 352 | node.content = NODE_AGILITY_1; 353 | break; 354 | case NODE_LUCK_2: 355 | case NODE_LUCK_3: 356 | case NODE_LUCK_4: 357 | node.content = NODE_LUCK_1; 358 | break; 359 | case NODE_EVASION_2: 360 | case NODE_EVASION_3: 361 | case NODE_EVASION_4: 362 | node.content = NODE_EVASION_1; 363 | break; 364 | case NODE_ACCURACY_2: 365 | case NODE_ACCURACY_3: 366 | case NODE_ACCURACY_4: 367 | node.content = NODE_ACCURACY_1; 368 | break; 369 | case NODE_HP_300: 370 | node.content = NODE_HP_200; 371 | break; 372 | case NODE_MP_10: 373 | case NODE_MP_20: 374 | node.content = NODE_MP_40; 375 | break; 376 | default: 377 | break; 378 | } 379 | node.writeToBytes(); 380 | } 381 | grid_data.writeToBytes(); 382 | } 383 | } 384 | 385 | void randomizer_t::doSphereGridRandomization() 386 | { 387 | if (!options_pack.shuffle_sphere_grid && !options_pack.randomize_sphere_grid_true && !options_pack.randomize_sphere_grid && 388 | !options_pack.empty_sphere_grid && !options_pack.fill_sphere_grid && !options_pack.upgrade_sphere_nodes && 389 | !options_pack.downgrade_sphere_nodes && !options_pack.remove_sphere_grid_locks) 390 | return; 391 | 392 | if (options_pack.shuffle_sphere_grid) 393 | { 394 | printf( "Shuffling Sphere Grid...\n" ); 395 | shuffleSphereGridNodes(); 396 | } 397 | 398 | if (options_pack.randomize_sphere_grid_true) 399 | { 400 | printf( "Randomizing Sphere Grid...\n" ); 401 | randomizeSphereGridTrue(); 402 | } 403 | 404 | if (options_pack.randomize_sphere_grid) 405 | { 406 | printf( "Randomizing Sphere Grid...\n" ); 407 | randomizeSphereGrid(); 408 | } 409 | 410 | if (options_pack.remove_sphere_grid_locks) 411 | { 412 | printf( "Removing Sphere Grid Locks...\n" ); 413 | removeSphereGridLocks(); 414 | } 415 | 416 | if (options_pack.empty_sphere_grid) 417 | { 418 | printf( "Emptying Sphere Grid...\n" ); 419 | emptySphereGrid(); 420 | } 421 | 422 | if (options_pack.fill_sphere_grid) 423 | { 424 | printf( "Filling Sphere Grid...\n" ); 425 | fillSphereGrid(); 426 | } 427 | 428 | if (options_pack.upgrade_sphere_nodes) 429 | { 430 | printf( "Upgrading Sphere Grid Nodes...\n" ); 431 | upgradeSphereGridNodes(); 432 | } 433 | 434 | if (options_pack.downgrade_sphere_nodes) 435 | { 436 | printf( "Downgrading Sphere Grid Nodes...\n" ); 437 | downgradeSphereGridNodes(); 438 | } 439 | 440 | reconstructSphereGridData(); 441 | } -------------------------------------------------------------------------------- /src/core/DataEnums.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | enum ply_save_e 4 | { 5 | CHARACTER_TIDUS, 6 | CHARACTER_YUNA, 7 | CHARACTER_AURON, 8 | CHARACTER_KIMAHRI, 9 | CHARACTER_WAKKA, 10 | CHARACTER_LULU, 11 | CHARACTER_RIKU, 12 | CHARACTER_SEYMOUR, 13 | CHARACTER_VALEFOR, 14 | CHARACTER_IFRIT, 15 | CHARACTER_IXION, 16 | CHARACTER_SHIVA, 17 | CHARACTER_BAHAMUT, 18 | CHARACTER_ANIMA, 19 | CHARACTER_YOJIMBO, 20 | CHARACTER_CINDY, 21 | CHARACTER_SANDY, 22 | CHARACTER_MINDY, 23 | CHARACTER_DUMMY, 24 | CHARACTER_DUMMY1 25 | }; 26 | 27 | enum sphere_grid_type_e 28 | { 29 | SPHERE_GRID_ORIGINAL, 30 | SPHERE_GRID_STANDARD, 31 | SPHERE_GRID_EXPERT 32 | }; 33 | 34 | enum item_e 35 | { 36 | ITEM_POTION = 8192, 37 | ITEM_HI_POTION, 38 | ITEM_X_POTION, 39 | ITEM_MEGA_POTION, 40 | ITEM_ETHER, 41 | ITEM_TURBO_ETHER, 42 | ITEM_PHOENIX_DOWN, 43 | ITEM_MEGA_PHOENIX, 44 | ITEM_ELIXIR, 45 | ITEM_MEGALIXER, 46 | ITEM_ANTIDOTE, 47 | ITEM_SOFT, 48 | ITEM_EYE_DROPS, 49 | ITEM_ECHO_SCREEN, 50 | ITEM_HOLY_WATER, 51 | ITEM_REMEDY, 52 | ITEM_POWER_DISTILLER, 53 | ITEM_MANA_DISTILLER, 54 | ITEM_SPEED_DISTILLER, 55 | ITEM_ABILITY_DISTILLER, 56 | ITEM_ALBHED_POTION, 57 | ITEM_HEALING_WATER, 58 | ITEM_TETRA_ELEMENTAL, 59 | ITEM_ANTARCTIC_WIND, 60 | ITEM_ARCTIC_WIND, 61 | ITEM_ICE_GEM, 62 | ITEM_BOMB_FRAGMENT, 63 | ITEM_BOMB_CORE, 64 | ITEM_FIRE_GEM, 65 | ITEM_ELECTRO_MARBLE, 66 | ITEM_LIGHTNING_MARBLE, 67 | ITEM_LIGHTNING_GEM, 68 | ITEM_FISH_SCALE, 69 | ITEM_DRAGON_SCALE, 70 | ITEM_WATER_GEM, 71 | ITEM_GRENADE, 72 | ITEM_FRAG_GRENADE, 73 | ITEM_SLEEPING_POWDER, 74 | ITEM_DREAM_POWDER, 75 | ITEM_SILENCE_GRENADE, 76 | ITEM_SMOKE_BOMB, 77 | ITEM_SHADOW_GEM, 78 | ITEM_SHINING_GEM, 79 | ITEM_BLESSED_GEM, 80 | ITEM_SUPREME_GEM, 81 | ITEM_POISON_FANG, 82 | ITEM_SILVER_HOURGLASS, 83 | ITEM_GOLD_HOURGLASS, 84 | ITEM_CANDLE_OF_LIFE, 85 | ITEM_PETRIFY_GRENADE, 86 | ITEM_FARPLANE_SHADOW, 87 | ITEM_FARPLANE_WIND, 88 | ITEM_DESIGNER_WALLET, 89 | ITEM_DARK_MATTER, 90 | ITEM_CHOCOBO_FEATHER, 91 | ITEM_CHOCOBO_WING, 92 | ITEM_LUNAR_CURTAIN, 93 | ITEM_LIGHT_CURTAIN, 94 | ITEM_STAR_CURTAIN, 95 | ITEM_HEALING_SPRING, 96 | ITEM_MANA_SPRING, 97 | ITEM_STAMINA_SPRING, 98 | ITEM_SOUL_SPRING, 99 | ITEM_PETRIFYING_SALT, 100 | ITEM_STAMINA_TABLET, 101 | ITEM_MANA_TABLET, 102 | ITEM_TWIN_STARS, 103 | ITEM_STAMINA_TONIC, 104 | ITEM_MANA_TONIC, 105 | ITEM_THREE_STARS, 106 | ITEM_POWER_SPHERE, 107 | ITEM_MANA_SPHERE, 108 | ITEM_SPEED_SPHERE, 109 | ITEM_ABILITY_SPHERE, 110 | ITEM_FORTUNE_SPHERE, 111 | ITEM_ATTRIBUTE_SPHERE, 112 | ITEM_SPECIAL_SPHERE, 113 | ITEM_SKILL_SPHERE, 114 | ITEM_WHITE_MAGIC_SPHERE, 115 | ITEM_BLACK_MAGIC_SPHERE, 116 | ITEM_MASTER_SPHERE, 117 | ITEM_LEVEL1_KEY_SPHERE, 118 | ITEM_LEVEL2_KEY_SPHERE, 119 | ITEM_LEVEL3_KEY_SPHERE, 120 | ITEM_LEVEL4_KEY_SPHERE, 121 | ITEM_HP_SPHERE, 122 | ITEM_MP_SPHERE, 123 | ITEM_STRENGTH_SPHERE, 124 | ITEM_DEFENSE_SPHERE, 125 | ITEM_MAGIC_SPHERE, 126 | ITEM_MAGIC_DEFENSE_SPHERE, 127 | ITEM_AGILITY_SPHERE, 128 | ITEM_EVASION_SPHERE, 129 | ITEM_ACCURACY_SPHERE, 130 | ITEM_LUCK_SPHERE, 131 | ITEM_CLEAR_SPHERE, 132 | ITEM_RETURN_SPHERE, 133 | ITEM_FRIEND_SPHERE, 134 | ITEM_TELEPORT_SPHERE, 135 | ITEM_WARP_SPHERE, 136 | ITEM_MAP, 137 | ITEM_RENAME_CARD, 138 | ITEM_MUSK, 139 | ITEM_HYTPELLO_POTION, 140 | ITEM_SHINING_THORN, 141 | ITEM_PENDULIUM, 142 | ITEM_AMULET, 143 | ITEM_DOOR_TO_TOMORROW, 144 | ITEM_WINGS_TO_DISCOVERY, 145 | ITEM_GAMBLERS_SPIRIT, 146 | ITEM_UNDERDOGS_SECRET, 147 | ITEM_WINNING_FORMULA, 148 | ITEM_MAX 149 | }; 150 | 151 | enum weapon_formula_e 152 | { 153 | WEAPON_FORMULA_NONE, 154 | WEAPON_FORMULA_STR_VS_DEF, 155 | WEAPON_FORMULA_STR_IGNORE_DEF, 156 | WEAPON_FORMULA_MAG_VS_MDEF, 157 | WEAPON_FORMULA_MAG_IGNORE_MDEF, 158 | WEAPON_FORMULA_CURRENT_STATISTIC, 159 | WEAPON_FORMULA_MULTIPLE_OF_50, 160 | WEAPON_FORMULA_HEALING, 161 | WEAPON_FORMULA_TARGET_MAX_HP, 162 | WEAPON_FORMULA_MULTIPLE_OF_50_VARIABLE, 163 | WEAPON_FORMULA_TARGET_MAX_MP, 164 | WEAPON_FORMULA_TARGET_MAX_CTB, 165 | WEAPON_FORMULA_TARGET_CURRENT_MP, 166 | WEAPON_FORMULA_TARGET_CURRENT_CTB, 167 | WEAPON_FORMULA_STR_NO_CHEER, 168 | WEAPON_FORMULA_SPECIAL, 169 | WEAPON_FORMULA_PLAYER_MAX_HP_TENTHS, 170 | WEAPON_FORMULA_CELESTIAL_HIGH_HP, 171 | WEAPON_FORMULA_CELESTIAL_HIGH_MP, 172 | WEAPON_FORMULA_CELESTIAL_LOW_HP, 173 | WEAPON_FORMULA_MAG_NO_CHEER, 174 | WEAPON_FORMULA_GIL, 175 | WEAPON_FORMULA_KILLS, 176 | WEAPON_FORMULA_MULTIPLE_OF_9999 177 | }; 178 | 179 | enum monster_e 180 | { 181 | MON_DUMMY, 182 | MON_KOURA_01, 183 | MON_KOURA_02, 184 | MON_KOURA_03, 185 | MON_KOURA_04, 186 | MON_KOURA_05, 187 | MON_GARUKI_01, 188 | MON_GARUKI_02, 189 | MON_GARUKI_03, 190 | MON_WOLF_01, 191 | MON_WOLF_02, 192 | MON_WOLF_03, 193 | MON_WOLF_04, 194 | MON_WOLF_05, 195 | MON_WOLF_06, 196 | MON_WOLF_07, 197 | MON_PUDDING_01, 198 | MON_PUDDING_02, 199 | MON_PUDDING_03, 200 | MON_PUDDING_04, 201 | MON_PUDDING_05, 202 | MON_PUDDING_06, 203 | MON_LIZARD_01, 204 | MON_LIZARD_02, 205 | MON_LIZARD_03, 206 | MON_LIZARD_04, 207 | MON_LIZARD_05, 208 | MON_LIZARD_06, 209 | MON_BIRD_01, 210 | MON_BIRD_02, 211 | MON_BIRD_03, 212 | MON_INSECT_01, 213 | MON_INSECT_02, 214 | MON_INSECT_03, 215 | MON_INSECT_04, 216 | MON_ARIMAN_01, 217 | MON_ARIMAN_02, 218 | MON_ARIMAN_03, 219 | MON_ARIMAN_04, 220 | MON_PLANT_01, 221 | MON_PLANT_02, 222 | MON_SAHAGIN_01, 223 | MON_SAHAGIN_02, 224 | MON_SAHAGIN_02_KAI, 225 | MON_ZOO_01, 226 | MON_ZOO_02, 227 | MON_SANDWORM_01, 228 | MON_SANDWORM_02, 229 | MON_GOLEM_01, 230 | MON_GOLEM_02, 231 | MON_GHOST_01, 232 | MON_FLESIUS_01, 233 | MON_FLESIUS_02, 234 | MON_RISING_01, 235 | MON_RISING_02, 236 | MON_FOUR_HORN_01, 237 | MON_FOUR_HORN_02, 238 | MON_FOUR_HORN_03, 239 | MON_OCTOPUS_01, 240 | MON_DRAGON_01, 241 | MON_DRAGON_02, 242 | MON_DRAGON_03, 243 | MON_DRAGON_04, 244 | MON_DRAGON_05, 245 | MON_MORBOL_01, 246 | MON_MORBOL_02, 247 | MON_FIGHTER_01, 248 | MON_FIGHTER_02, 249 | MON_PIRANHA_11, 250 | MON_PIRANHA_12, 251 | MON_PIRANHA_13, 252 | MON_PIRANHA_21, 253 | MON_PIRANHA_22, 254 | MON_PIRANHA_23, 255 | MON_PIRANHA_31, 256 | MON_PIRANHA_32, 257 | MON_PIRANHA_33, 258 | MON_ELEMENTAL_01, 259 | MON_ELEMENTAL_02, 260 | MON_ELEMENTAL_03, 261 | MON_ELEMENTAL_04, 262 | MON_ELEMENTAL_05, 263 | MON_ELEMENTAL_06, 264 | MON_ELEMENTAL_07, 265 | MON_BLADE_01, 266 | MON_BEHEMOTH_01, 267 | MON_BEHEMOTH_02, 268 | MON_CHIMERA_01, 269 | MON_CHIMERA_02, 270 | MON_QUAL_01, 271 | MON_QUAL_02, 272 | MON_BIPEDAL_MECH_01, 273 | MON_BIPEDAL_MECH_02, 274 | MON_BIPEDAL_MECH_02_BURNING, 275 | MON_BIPEDAL_MECH_03, 276 | MON_MONOLITH_01, 277 | MON_QUADREDAL_MECH_01, 278 | MON_QUADREDAL_MECH_02, 279 | MON_QUADREDAL_MECH_03, 280 | MON_ULTIMA_WEAPON, 281 | MON_OMEGA_WEAPON, 282 | MON_OCTOPUS_BOSS, 283 | MON_SINSPAWN_01, 284 | MON_SINSPAWN_02, 285 | MON_CHOCOBO_EATER, 286 | MON_SNAIL_BOSS, 287 | MON_MAGICSQ_01, 288 | MON_SHINZ_L1, 289 | MON_SHINZ_L2, 290 | MON_BAJIBOSS, 291 | MON_KIKAI1, 292 | MON_KIKAI2, 293 | MON_HIMARI_B, 294 | MON_SINSPAWN_SE, 295 | MON_SINSPAWN1, 296 | MON_SINSPAWNZ_W1, 297 | MON_DOG_02, 298 | MON_SINSPAWN3, 299 | MON_MAGICSQ_02, 300 | MON_EF1, 301 | MON_EF2, 302 | MON_MAKA_BOSS, 303 | MON_COUNTDOWN, 304 | MON_COUNTDOWNB, 305 | MON_SEYMOUR, 306 | MON_ANIMA_B, 307 | MON_SEYMOUR_MONSTER1, 308 | MON_SEYMOUR_MONSTER1_W, 309 | MON_EBON1, 310 | MON_EBON3, 311 | MON_YUNALESCA, 312 | MON_SEYMOUR3, 313 | MON_JECHT, 314 | MON_CRANE, 315 | MON_BIRAN, 316 | MON_ENCHE, 317 | MON_SIN_ARM_LEFT, 318 | MON_SIN_ARM_RIGHT, 319 | MON_SIN_BACK, 320 | MON_SIN2_NEW, 321 | MON_SIN_HEAD, 322 | MON_GUADO_E, 323 | MON_SEYMOUR_MONSTER2, 324 | MON_SEYMOUR_MONSTER2_W, 325 | MON_KIMARI_W, 326 | MON_SINZ_L3, 327 | MON_IDO, 328 | MON_SINZ_L4, 329 | MON_SINZ_L5, 330 | MON_CID, 331 | MON_MAGICSQ_03, 332 | MON_DRAGON_E01, 333 | MON_ROBOT_E01, 334 | MON_OCHU_E01, 335 | MON_MAGICSQ_04, 336 | MON_SAHAGIN_E01, 337 | MON_SAHAGIN_E02, 338 | MON_ZOO_E01, 339 | MON_BLADE_E01, 340 | MON_SIN4, 341 | MON_SIN3_HEAD, 342 | MON_SIN3_ARM, 343 | MON_KUSABI, 344 | MON_VAELFORE_E, 345 | MON_IFRIT_E, 346 | MON_IXION_E, 347 | MON_SHIVA_E, 348 | MON_BAHAMUT_E, 349 | MON_ANIMA_E, 350 | MON_YOJIMBO_E, 351 | MON_TANKER_TRUCK, 352 | MON_KUSABI2, 353 | MON_JECHT_SWORD, 354 | MON_OPTION_LEFT, 355 | MON_OPTION_RIGHT, 356 | MON_YUNALESCA_ARM, 357 | MON_YU_YEVON, 358 | MON_MAGICSQ_05, 359 | MON_MAG_E, 360 | MON_DOG_E, 361 | MON_RAG_E, 362 | MON_IRON_GIANT_11, 363 | MON_IRON_GIANT_12, 364 | MON_IRON_GIANT_21, 365 | MON_IRON_GIANT_22, 366 | MON_BASILISK_01, 367 | MON_BASILISK_02, 368 | MON_ADAMANTOISE_01, 369 | MON_BELIAL_01, 370 | MON_OTYUGH_01, 371 | MON_OTYUGH_02, 372 | MON_F_EBON_01, 373 | MON_F_EBON_02, 374 | MON_BOMB_01, 375 | MON_BOMB_02, 376 | MON_G_EBON_01, 377 | MON_G_EBON_02, 378 | MON_S_EBON_11, 379 | MON_S_EBON_12, 380 | MON_S_EBON_21, 381 | MON_S_EBON_22, 382 | MON_KOYOKOYO_01, 383 | MON_MAGIC_POT_DX, 384 | MON_MAGIC_POT_02, 385 | MON_MAGIC_POT_03, 386 | MON_MAGIC_POT_04, 387 | MON_MAGIC_POT_05, 388 | MON_CACTUS_01, 389 | MON_CACTUS_02, 390 | MON_MIST_01, 391 | MON_LAST2_01, 392 | MON_TREASURE_CHEST_01, 393 | MON_FIGHTER_03, 394 | MON_GUADO_01, 395 | MON_KINOKO_01, 396 | MON_KINOKO_02, 397 | MON_KINOKO_03, 398 | MON_BLADE_02, 399 | MON_BOMB_03, 400 | MON_MIST_02, 401 | MON_GHOST_02, 402 | MON_PLANT_03, 403 | MON_GUADO_02, 404 | MON_TONBERRY_01, 405 | MON_TONBERRY_02, 406 | MON_ARIMAN_05, 407 | MON_BOMB_04, 408 | MON_CHIMERA_03, 409 | MON_FOUR_HORN_04, 410 | MON_GOLEM_03, 411 | MON_ZOO_01T, 412 | MON_WOLF_01T, 413 | MON_PUDDING_01T, 414 | MON_BIRD_01T, 415 | MON_PLANT_01T, 416 | MON_KOURA_01T, 417 | MON_KOURA_02T, 418 | MON_TREASURE_CHEST_TUTORIAL, 419 | MON_ZOO_02E, 420 | MON_LIZARD_07, 421 | MON_KOURA_06, 422 | MON_PUDDING_07, 423 | MON_ARIMAN_07, 424 | MON_MIST_03, 425 | MON_BLADE_03, 426 | MON_LIZARD_08, 427 | MON_KOURA_07, 428 | MON_ARIMAN_06, 429 | MON_ISAL_01, 430 | MON_GINNEM_01, 431 | MON_BERGEMINE_01, 432 | MON_MIMIC_01, 433 | MON_MIMIC_02, 434 | MON_MIMIC_03, 435 | MON_VALFOR_01, 436 | MON_INVISIBLE, 437 | MON_MIMIC_04, 438 | MON_MIMIC_PARTS_01, 439 | MON_VALFOR_02, 440 | MON_IFRIT_02, 441 | MON_MIMIC_PARTS_02, 442 | MON_MIMIC_PARTS_03, 443 | MON_MIMIC_PARTS_04, 444 | MON_RIFLE_01, 445 | MON_FLAMEGUN_01, 446 | MON_ANIMA_01, 447 | MON_DOG_01, 448 | MON_ANIMA_DOWN_01, 449 | MON_ANIMA_UP_01, 450 | MON_KATANA_01, 451 | MON_KODUKA_01, 452 | MON_SYURIKEN_01, 453 | MON_MAKISHIN_3_HEART, 454 | MON_MAKISHIN_3, 455 | MON_MIMIC_PARTS_18, 456 | MON_MIMIC_PARTS_19, 457 | MON_MAKI_ULTIMA_WEAPON, 458 | MON_MAKI_SINSPAWN_02, 459 | MON_MAKI_SINSPAWN_02_ARM, 460 | MON_MAKI_CHOCOBO_EATER, 461 | MON_MAKI_SINSPAWN_03, 462 | MON_MAKI_MAKA_BOSS, 463 | MON_MAKI_EBON1, 464 | MON_MAKI_EF2, 465 | MON_IFRIT_01, 466 | MON_IXION_01, 467 | MON_SHIVA_01, 468 | MON_BAHAMUT_01, 469 | MON_BODYGUARD_01, 470 | MON_DOG_03, 471 | MON_MAG_06, 472 | MON_RAG_01, 473 | MON_MAKI_KOURA_03, 474 | MON_MAKI_GARUIKI_03, 475 | MON_MAKI_WOLF_07, 476 | MON_MAKI_PUDDING_06, 477 | MON_MAKI_LIZARD_06, 478 | MON_MAKI_BIRD_02, 479 | MON_MAKI_INSECT_03, 480 | MON_MAKI_ARIMAN_03, 481 | MON_MAKI_ZOO_02, 482 | MON_MAKI_SANDWORM_02, 483 | MON_MAKI_FOUR_HORN_03, 484 | MON_MAKI_DRAGON_05, 485 | MON_MAKI_MORBOL_01, 486 | MON_MAKI_FIGHTER_02, 487 | MON_MAKI_ELEMENTAL_06, 488 | MON_MAKI_BEHEMOTH_01, 489 | MON_MAKI_CHIMERA_02, 490 | MON_MAKI_QUAL_02, 491 | MON_MAKI_IRON_GIANT_12, 492 | MON_MAKI_BASILISK_02, 493 | MON_MAKI_BELIAL_01, 494 | MON_MAKI_BOMB_01, 495 | MON_MAKI_SABO_01, 496 | MON_MAKI_LAST2_01, 497 | MON_MAKI_KINOKO_03, 498 | MON_MAKI_TONBERRY_02, 499 | MON_MAKI_BLADE_E01, 500 | MON_S_BOMB_01, 501 | MON_S_FOUR_HORN_01, 502 | MON_S_ZOO_01, 503 | MON_S_BASILISK_01, 504 | MON_S_OCHU_01, 505 | MON_S_MIST_01, 506 | MON_S_IRON_GIANT_11, 507 | MON_S_CHIMERA_01, 508 | MON_S_CLAW_01, 509 | MON_S_CACTUS_01, 510 | MON_S_ZOO_02, 511 | MON_S_SANDWORM_01, 512 | MON_S_GHOST_01, 513 | MON_S_MORBOL_01, 514 | MON_S_FIGHTER_01, 515 | MON_EVIL_VAELFORE_01, 516 | MON_EVIL_IFRIT_01, 517 | MON_EVIL_IXION_01, 518 | MON_EVIL_SHIVA_01, 519 | MON_EVIL_BAHAMUT_01, 520 | MON_EVIL_ANIMA_01, 521 | MON_EVIL_YOJIMBO_01, 522 | MON_EVIL_MAG_01, 523 | MON_EVIL_DOG_01, 524 | MON_EVIL_RAG_01, 525 | MON_ANGEL_B, 526 | MON_ANGEL_R, 527 | MON_ANGEL_L, 528 | MON_ZERO = 999 529 | }; 530 | 531 | enum sphere_node_type_e 532 | { 533 | NODE_LOCK_3, 534 | NODE_EMPTY, 535 | NODE_STRENGTH_1, 536 | NODE_STRENGTH_2, 537 | NODE_STRENGTH_3, 538 | NODE_STRENGTH_4, 539 | NODE_DEFENSE_1, 540 | NODE_DEFENSE_2, 541 | NODE_DEFENSE_3, 542 | NODE_DEFENSE_4, 543 | NODE_MAGIC_1, 544 | NODE_MAGIC_2, 545 | NODE_MAGIC_3, 546 | NODE_MAGIC_4, 547 | NODE_MAGIC_DEFENSE_1, 548 | NODE_MAGIC_DEFENSE_2, 549 | NODE_MAGIC_DEFENSE_3, 550 | NODE_MAGIC_DEFENSE_4, 551 | NODE_AGILITY_1, 552 | NODE_AGILITY_2, 553 | NODE_AGILITY_3, 554 | NODE_AGILITY_4, 555 | NODE_LUCK_1, 556 | NODE_LUCK_2, 557 | NODE_LUCK_3, 558 | NODE_LUCK_4, 559 | NODE_EVASION_1, 560 | NODE_EVASION_2, 561 | NODE_EVASION_3, 562 | NODE_EVASION_4, 563 | NODE_ACCURACY_1, 564 | NODE_ACCURACY_2, 565 | NODE_ACCURACY_3, 566 | NODE_ACCURACY_4, 567 | NODE_HP_200, 568 | NODE_HP_300, 569 | NODE_MP_40, 570 | NODE_MP_20, 571 | NODE_MP_10, 572 | NODE_LOCK_1, 573 | NODE_LOCK_2, 574 | NODE_LOCK_4, 575 | NODE_DELAY_ATTACK, 576 | NODE_DELAY_BUSTER, 577 | NODE_SLEEP_ATTACK, 578 | NODE_SILENCE_ATTACK, 579 | NODE_DARK_ATTACK, 580 | NODE_ZOMBIE_ATTACK, 581 | NODE_SLEEP_BUSTER, 582 | NODE_SILENCE_BUSTER, 583 | NODE_DARK_BUSTER, 584 | NODE_TRIPLE_FOUL, 585 | NODE_POWER_BREAK, 586 | NODE_MAGIC_BREAK, 587 | NODE_ARMOR_BREAK, 588 | NODE_MENTAL_BREAK, 589 | NODE_MUG, 590 | NODE_QUICK_HIT, 591 | NODE_STEAL, 592 | NODE_USE, 593 | NODE_FLEE, 594 | NODE_PRAY, 595 | NODE_CHEER, 596 | NODE_FOCUS, 597 | NODE_REFLEX, 598 | NODE_AIM, 599 | NODE_LUCK, 600 | NODE_JINX, 601 | NODE_LANCET, 602 | NODE_GUARD, 603 | NODE_SENTINEL, 604 | NODE_SPARE_CHANGE, 605 | NODE_THREATEN, 606 | NODE_PROVOKE, 607 | NODE_ENTRUST, 608 | NODE_COPYCAT, 609 | NODE_DOUBLECAST, 610 | NODE_BRIBE, 611 | NODE_CURE, 612 | NODE_CURA, 613 | NODE_CURAGA, 614 | NODE_NULL_FROST, 615 | NODE_NULL_BLAZE, 616 | NODE_NULL_SHOCK, 617 | NODE_NULL_TIDE, 618 | NODE_SCAN, 619 | NODE_ESUNA, 620 | NODE_LIFE, 621 | NODE_FULL_LIFE, 622 | NODE_HASTE, 623 | NODE_HASTEGA, 624 | NODE_SLOW, 625 | NODE_SLOWGA, 626 | NODE_SHELL, 627 | NODE_PROTECT, 628 | NODE_REFLECT, 629 | NODE_DISPEL, 630 | NODE_REGEN, 631 | NODE_HOLY, 632 | NODE_AUTO_LIFE, 633 | NODE_BLIZZARD, 634 | NODE_FIRE, 635 | NODE_THUNDER, 636 | NODE_WATER, 637 | NODE_FIRA, 638 | NODE_BLIZZARA, 639 | NODE_THUNDARA, 640 | NODE_WATERA, 641 | NODE_FIRAGA, 642 | NODE_BLIZZAGA, 643 | NODE_THUNDAGA, 644 | NODE_WATERGA, 645 | NODE_BIO, 646 | NODE_DEMI, 647 | NODE_DEATH, 648 | NODE_DRAIN, 649 | NODE_OSMOSE, 650 | NODE_FLARE, 651 | NODE_ULTIMA, 652 | NODE_PILFER_GIL, 653 | NODE_FULL_BREAK, 654 | NODE_EXTRACT_POWER, 655 | NODE_EXTRACT_MANA, 656 | NODE_EXTRACT_SPEED, 657 | NODE_EXTRACT_ABILITY, 658 | NODE_NAB_GIL, 659 | NODE_QUICK_POCKETS, 660 | NODE_NULL = 0XFF 661 | }; 662 | 663 | enum overdrive_mode_e 664 | { 665 | OVERDRIVE_WARRIOR, 666 | OVERDRIVE_COMRADE, 667 | OVERDRIVE_STOIC, 668 | OVERDRIVE_HEALER, 669 | OVERDRIVE_TACTICIAN, 670 | OVERDRIVE_VICTIM, 671 | OVERDRIVE_DANCER, 672 | OVERDRIVE_AVENGER, 673 | OVERDRIVE_SLAYER, 674 | OVERDRIVE_HERO, 675 | OVERDRIVE_ROOK, 676 | OVERDRIVE_VICTOR, 677 | OVERDRIVE_COWARD, 678 | OVERDRIVE_ALLY, 679 | OVERDRIVE_SUFFERER, 680 | OVERDRIVE_DAREDEVIL, 681 | OVERDRIVE_LONER, 682 | OVERDRIVE_UNUSED_1, 683 | OVERDRIVE_UNUSED_2, 684 | OVERDRIVE_AEON, 685 | }; -------------------------------------------------------------------------------- /src/core/PlayerCharacterRandomizer.cpp: -------------------------------------------------------------------------------- 1 | #include "Randomizer.hpp" 2 | 3 | void randomizer_t::reconstructPlayerStatsData() 4 | { 5 | for (auto& locale : LOCALIZATIONS) 6 | { 7 | std::string path = INPUT_FOLDER + locale.second + "ply_save.bin"; 8 | std::vector original_bytes = bytes_mapper_t::fileToBytes( path ); 9 | for (size_t j = 0; j < data_pack.player_stats_data.size(); j++) 10 | { 11 | character_stats_t& player_stats = *data_pack.player_stats_data[ j ]; 12 | std::vector player_stats_bytes = player_stats.bytes; 13 | for (size_t i = 0; i < player_stats_bytes.size(); i++) 14 | { 15 | original_bytes[ 20 + j * player_stats_bytes.size() + i ] = player_stats_bytes[ i ]; 16 | } 17 | } 18 | std::string output_path = OUTPUT_FOLDER + prefix + locale.second; 19 | std::filesystem::create_directories( output_path ); 20 | std::string file = output_path + "ply_save.bin"; 21 | bytes_mapper_t::writeBytesToNewFile( original_bytes, file ); 22 | } 23 | } 24 | 25 | void randomizer_t::reconstructAeonScalingData() 26 | { 27 | for (auto& locale : LOCALIZATIONS) 28 | { 29 | std::string path = INPUT_FOLDER + locale.second + "ply_rom.bin"; 30 | std::vector original_bytes = bytes_mapper_t::fileToBytes( path ); 31 | for (size_t j = 0; j < data_pack.aeon_scaling_data.size(); j++) 32 | { 33 | aeon_scaling_data_t& aeon_scaling = *data_pack.aeon_scaling_data[ j ]; 34 | std::vector aeon_scaling_bytes = aeon_scaling.bytes; 35 | for (size_t i = 0; i < aeon_scaling_bytes.size(); i++) 36 | { 37 | original_bytes[ 20 + j * aeon_scaling_bytes.size() + i ] = aeon_scaling_bytes[ i ]; 38 | } 39 | } 40 | std::string output_path = OUTPUT_FOLDER + prefix + locale.second; 41 | std::filesystem::create_directories( output_path ); 42 | std::string file = output_path + "ply_rom.bin"; 43 | bytes_mapper_t::writeBytesToNewFile( original_bytes, file ); 44 | } 45 | } 46 | 47 | void randomizer_t::reconstructAeonStatData() 48 | { 49 | std::string path = INPUT_FOLDER + BATTLE_KERNEL_FOLDER + "sum_assure.bin"; 50 | std::vector original_bytes = bytes_mapper_t::fileToBytes( path ); 51 | for (size_t j = 0; j < data_pack.aeon_stat_data.size(); j++) 52 | { 53 | aeon_stat_data_t& aeon_stats = *data_pack.aeon_stat_data[ j ]; 54 | std::vector aeon_stat_bytes = aeon_stats.bytes; 55 | for (size_t i = 0; i < aeon_stat_bytes.size(); i++) 56 | { 57 | original_bytes[ 20 + j * aeon_stat_bytes.size() + i ] = aeon_stat_bytes[ i ]; 58 | } 59 | } 60 | std::string output_path = OUTPUT_FOLDER + prefix + BATTLE_KERNEL_FOLDER; 61 | std::filesystem::create_directories( output_path ); 62 | std::string file = output_path + "sum_assure.bin"; 63 | bytes_mapper_t::writeBytesToNewFile( original_bytes, file ); 64 | } 65 | 66 | void randomizer_t::reconstructSumGrowData() 67 | { 68 | std::string name = "sum_grow.bin"; 69 | genericReconstructor( btl_kernel_input, btl_kernel_output, name, data_pack.aeon_customize_data, true ); 70 | } 71 | 72 | void randomizer_t::randomizePlayerStats() 73 | { 74 | for (int i = 0; i < 7; i++) 75 | { 76 | character_stats_t& stats = *data_pack.player_stats_data[ i ]; 77 | stats.base_hp = normal( stats.base_hp, stats.base_hp / 2, 50, 9999 ); 78 | if (stats.base_hp < 50) 79 | stats.base_hp = 50; 80 | stats.base_mp = normal( stats.base_mp, stats.base_mp / 2, 1, 999 ); 81 | if (i == CHARACTER_WAKKA && stats.base_mp < 5) 82 | stats.base_mp = 5; 83 | if (i == CHARACTER_LULU && stats.base_mp < 8) 84 | stats.base_mp = 8; 85 | bool tidus_or_auron = i == CHARACTER_TIDUS || i == CHARACTER_AURON; 86 | stats.base_str = normal( stats.base_str, stats.base_str / 2, tidus_or_auron ? 14 : 0, stats.base_str * 3 ); 87 | stats.base_def = normal( stats.base_def, stats.base_def / 2, 0, stats.base_def * 3 ); 88 | stats.base_mag = normal( stats.base_mag, stats.base_mag / 2, 0, stats.base_mag * 3 ); 89 | stats.base_mdef = normal( stats.base_mdef, stats.base_mdef / 2, 0, stats.base_mdef * 3 ); 90 | stats.base_agi = normal( stats.base_agi, stats.base_agi / 2, 0, stats.base_agi * 3 ); 91 | stats.base_acc = normal( stats.base_acc, stats.base_acc / 2, 0, stats.base_acc * 3 ); 92 | stats.base_luck = normal( stats.base_luck, stats.base_luck / 2, 0, stats.base_luck * 3 ); 93 | stats.current_ap = normal( stats.current_ap, stats.current_ap / 2, 0, UINT16_MAX ); 94 | stats.current_hp = normal( stats.current_hp, stats.current_hp / 2, 1, 9999 ); 95 | stats.current_mp = normal( stats.current_mp, stats.current_mp / 2, 1, 999 ); 96 | stats.max_hp = stats.base_hp; 97 | stats.max_mp = stats.base_mp; 98 | stats.str = normal( stats.str, stats.str / 2, 0, stats.str * 3 ); 99 | stats.def = normal( stats.def, stats.def / 2, 0, stats.def * 3 ); 100 | stats.mag = normal( stats.mag, stats.mag / 2, 0, stats.mag * 3 ); 101 | stats.mdef = normal( stats.mdef, stats.mdef / 2, 0, stats.mdef * 3 ); 102 | stats.agi = normal( stats.agi, stats.agi / 2, 0, stats.agi * 3 ); 103 | stats.acc = normal( stats.acc, stats.acc / 2, 0, stats.acc * 3 ); 104 | stats.luck = normal( stats.luck, stats.luck / 2, 0, stats.luck * 3 ); 105 | stats.writeToBytes(); 106 | } 107 | } 108 | 109 | void randomizer_t::shuffleCharacterStats() 110 | { 111 | for (int i = 0; i < 7; i++) 112 | { 113 | character_stats_t& stats = *data_pack.player_stats_data[ i ]; 114 | character_stats_t& new_stats = *shuffled_player_stats_data[ i ]; 115 | if (i == CHARACTER_WAKKA && new_stats.base_mp < 5) 116 | new_stats.base_mp = 5; 117 | if (i == CHARACTER_LULU && new_stats.base_mp < 8) 118 | new_stats.base_mp = 8; 119 | stats.base_hp = new_stats.base_hp; 120 | stats.base_mp = new_stats.base_mp; 121 | if (( i == CHARACTER_TIDUS || i == CHARACTER_AURON ) && new_stats.base_str < 14) 122 | new_stats.base_str = 14; 123 | stats.base_str = new_stats.base_str; 124 | stats.base_def = new_stats.base_def; 125 | stats.base_mag = new_stats.base_mag; 126 | stats.base_mdef = new_stats.base_mdef; 127 | stats.base_agi = new_stats.base_agi; 128 | stats.base_acc = new_stats.base_acc; 129 | stats.base_luck = new_stats.base_luck; 130 | stats.current_ap = new_stats.current_ap; 131 | stats.current_hp = new_stats.current_hp; 132 | stats.current_mp = new_stats.current_mp; 133 | stats.max_hp = new_stats.max_hp; 134 | stats.max_mp = new_stats.max_mp; 135 | stats.str = new_stats.str; 136 | stats.def = new_stats.def; 137 | stats.mag = new_stats.mag; 138 | stats.mdef = new_stats.mdef; 139 | stats.agi = new_stats.agi; 140 | stats.acc = new_stats.acc; 141 | stats.luck = new_stats.luck; 142 | stats.writeToBytes(); 143 | } 144 | } 145 | 146 | void randomizer_t::randomizeAeonStatScaling() 147 | { 148 | for (auto& aeon : data_pack.aeon_scaling_data) 149 | { 150 | if (!aeon->is_yuna_summon) 151 | continue; 152 | aeon->ap_req_coef1 = normal( aeon->ap_req_coef1, aeon->ap_req_coef1 / 2, 0, UINT16_MAX ); 153 | aeon->ap_req_coef2 = normal( aeon->ap_req_coef2, aeon->ap_req_coef2 / 2, 0, UINT16_MAX ); 154 | aeon->ap_req_coef3 = normal( aeon->ap_req_coef3, aeon->ap_req_coef3 / 2, 0, UINT16_MAX ); 155 | aeon->ap_req_max = normal( aeon->ap_req_max, aeon->ap_req_max / 2, 0, UINT16_MAX ); 156 | aeon->hp_coef1 = normal( aeon->hp_coef1, aeon->hp_coef1 / 2, 0, UINT16_MAX ); 157 | aeon->hp_coef2 = normal( aeon->hp_coef2, aeon->hp_coef2 / 2, 0, UINT16_MAX ); 158 | aeon->mp_coef1 = normal( aeon->mp_coef1, aeon->mp_coef1 / 2, 0, UINT16_MAX ); 159 | aeon->mp_coef2 = normal( aeon->mp_coef2, aeon->mp_coef2 / 2, 0, UINT16_MAX ); 160 | aeon->str_coef1 = normal( aeon->str_coef1, aeon->str_coef1 / 2, 0, UINT16_MAX ); 161 | aeon->str_coef2 = normal( aeon->str_coef2, aeon->str_coef2 / 2, 0, UINT16_MAX ); 162 | aeon->def_coef1 = normal( aeon->def_coef1, aeon->def_coef1 / 2, 0, UINT16_MAX ); 163 | aeon->def_coef2 = normal( aeon->def_coef2, aeon->def_coef2 / 2, 0, UINT16_MAX ); 164 | aeon->mag_coef1 = normal( aeon->mag_coef1, aeon->mag_coef1 / 2, 0, UINT16_MAX ); 165 | aeon->mag_coef2 = normal( aeon->mag_coef2, aeon->mag_coef2 / 2, 0, UINT16_MAX ); 166 | aeon->mdef_coef1 = normal( aeon->mdef_coef1, aeon->mdef_coef1 / 2, 0, UINT16_MAX ); 167 | aeon->mdef_coef2 = normal( aeon->mdef_coef2, aeon->mdef_coef2 / 2, 0, UINT16_MAX ); 168 | aeon->agi_coef1 = normal( aeon->agi_coef1, aeon->agi_coef1 / 2, 0, UINT16_MAX ); 169 | aeon->agi_coef2 = normal( aeon->agi_coef2, aeon->agi_coef2 / 2, 0, UINT16_MAX ); 170 | aeon->acc_coef1 = normal( aeon->acc_coef1, aeon->acc_coef1 / 2, 0, UINT16_MAX ); 171 | aeon->acc_coef2 = normal( aeon->acc_coef2, aeon->acc_coef2 / 2, 0, UINT16_MAX ); 172 | aeon->writeToBytes(); 173 | } 174 | } 175 | 176 | void randomizer_t::randomizeAeonBaseStats() 177 | { 178 | for (auto& aeon : data_pack.aeon_stat_data) 179 | { 180 | aeon->hp = normal( aeon->hp, aeon->hp / 2, 50, 99999 ); 181 | aeon->mp = normal( aeon->mp, aeon->mp / 2, 1, 9999 ); 182 | aeon->str = normal( aeon->str, aeon->str / 2, 0, UINT8_MAX ); 183 | aeon->def = normal( aeon->def, aeon->def / 2, 0, UINT8_MAX ); 184 | aeon->mag = normal( aeon->mag, aeon->mag / 2, 0, UINT8_MAX ); 185 | aeon->mdef = normal( aeon->mdef, aeon->mdef / 2, 0, UINT8_MAX ); 186 | aeon->agi = normal( aeon->agi, aeon->agi / 2, 0, UINT8_MAX ); 187 | aeon->acc = normal( aeon->acc, aeon->acc / 2, 0, UINT8_MAX ); 188 | aeon->writeToBytes(); 189 | } 190 | } 191 | 192 | void randomizer_t::shuffleAeonStatScaling() 193 | { 194 | int idx = 0; 195 | for (auto& aeon_sd : data_pack.aeon_scaling_data) 196 | { 197 | aeon_scaling_data_t& aeon = *aeon_sd; 198 | if (!aeon.is_yuna_summon) 199 | continue; 200 | aeon_scaling_data_t& shuffled_aeon = *shuffled_aeon_scaling_data[ idx ]; 201 | idx++; 202 | aeon.bytes = shuffled_aeon.bytes; 203 | } 204 | } 205 | 206 | void randomizer_t::shuffleAeonBaseStats() 207 | { 208 | std::shuffle( data_pack.aeon_stat_data.begin(), data_pack.aeon_stat_data.end(), rng ); 209 | for (size_t i = 0; i < data_pack.aeon_stat_data.size(); i++) 210 | { 211 | aeon_stat_data_t& aeon = *data_pack.aeon_stat_data[ i ]; 212 | aeon_stat_data_t& shuffled_aeon = *data_pack.aeon_stat_data[ i ]; 213 | aeon.bytes = shuffled_aeon.bytes; 214 | } 215 | } 216 | 217 | void randomizer_t::randomizeSumGrow() 218 | { 219 | for (auto& aeon : data_pack.aeon_customize_data) 220 | { 221 | customize_data_t& customize = *aeon; 222 | item_t* item = getRandomItem(); 223 | customize.item = item->id; 224 | uint8_t quantity = 1; 225 | if (customize.ability > 10) 226 | { 227 | if (options_pack.keep_things_sane) 228 | quantity = normal( 16, 10, 1, 99 ); 229 | else 230 | quantity = uniform( 1, 99 ); 231 | } 232 | 233 | customize.item_quantity = quantity; 234 | customize.writeToBytes(); 235 | } 236 | } 237 | 238 | void randomizer_t::doPlayerStatRandomization() 239 | { 240 | if (!options_pack.randomize_player_stats && !options_pack.randomize_aeon_stat_scaling && !options_pack.shuffle_player_stats && 241 | !options_pack.shuffle_aeon_stat_scaling && !options_pack.poison_is_deadly && !options_pack.randomize_starting_overdrive_mode && 242 | !options_pack.shuffle_sphere_grid && !options_pack.randomize_sphere_grid_true && !options_pack.randomize_sphere_grid && 243 | !options_pack.randomize_aeon_stat_items ) 244 | return; 245 | 246 | if (options_pack.randomize_player_stats) 247 | { 248 | printf( "Randomizing Party Stats...\n" ); 249 | randomizePlayerStats(); 250 | } 251 | 252 | if (options_pack.randomize_aeon_stat_scaling) 253 | { 254 | printf( "Randomizing Aeon Stat Scaling...\n" ); 255 | randomizeAeonStatScaling(); 256 | } 257 | 258 | if (options_pack.randomize_aeon_base_stats) 259 | { 260 | printf( "Randomizing Aeon Base Stats...\n" ); 261 | randomizeAeonBaseStats(); 262 | } 263 | 264 | if (options_pack.shuffle_player_stats) 265 | { 266 | for (int i = 0; i < 7; i++) 267 | { 268 | character_stats_t* stats = data_pack.player_stats_data[ i ]; 269 | shuffled_player_stats_data.push_back( stats ); 270 | } 271 | std::shuffle( shuffled_player_stats_data.begin(), shuffled_player_stats_data.end(), rng ); 272 | printf( "Shuffling Party Stats...\n" ); 273 | shuffleCharacterStats(); 274 | } 275 | 276 | if (options_pack.shuffle_aeon_stat_scaling) 277 | { 278 | printf( "Shuffling Aeon Stat Scaling...\n" ); 279 | for (auto& aeon_scaling_data : data_pack.aeon_scaling_data) 280 | { 281 | if (!aeon_scaling_data->is_yuna_summon) 282 | continue; 283 | shuffled_aeon_scaling_data.push_back( aeon_scaling_data ); 284 | std::shuffle( shuffled_aeon_scaling_data.begin(), shuffled_aeon_scaling_data.end(), rng ); 285 | } 286 | shuffleAeonStatScaling(); 287 | } 288 | 289 | if (options_pack.shuffle_aeon_base_stats) 290 | { 291 | printf( "Shuffling Aeon Base Stats...\n" ); 292 | shuffleAeonBaseStats(); 293 | } 294 | 295 | if (options_pack.poison_is_deadly) 296 | { 297 | printf( "Making Poison Deadly...\n" ); 298 | for (auto& stats : data_pack.player_stats_data) 299 | { 300 | stats->poison_damage = 50; 301 | stats->writeToBytes(); 302 | } 303 | } 304 | 305 | if (options_pack.randomize_starting_overdrive_mode) 306 | { 307 | printf( "Randomizing Starting Overdrive Mode...\n" ); 308 | for (int i = 0; i < 7; i++) 309 | { 310 | character_stats_t& stats = *data_pack.player_stats_data.at( i ); 311 | if (i == ply_save_e::CHARACTER_AURON) 312 | continue; // Auron's overdrive mode is always stoic for the tutorial 313 | uint8_t overdrive_mode = uniform( 0, 16 ); 314 | stats.overdrive_current = overdrive_mode; 315 | stats.overdrive_mode = overdrive_mode; 316 | if (overdrive_mode != OVERDRIVE_STOIC) 317 | stats.overdrive.bits.stoic = 0; 318 | 319 | switch (overdrive_mode) 320 | { 321 | case OVERDRIVE_WARRIOR: 322 | stats.overdrive.bits.warrior = 1; 323 | break; 324 | case OVERDRIVE_COMRADE: 325 | stats.overdrive.bits.comrade = 1; 326 | break; 327 | case OVERDRIVE_STOIC: 328 | stats.overdrive.bits.stoic = 1; 329 | break; 330 | case OVERDRIVE_HEALER: 331 | stats.overdrive.bits.healer = 1; 332 | break; 333 | case OVERDRIVE_TACTICIAN: 334 | stats.overdrive.bits.tactician = 1; 335 | break; 336 | case OVERDRIVE_VICTIM: 337 | stats.overdrive.bits.victim = 1; 338 | break; 339 | case OVERDRIVE_DANCER: 340 | stats.overdrive.bits.dancer = 1; 341 | break; 342 | case OVERDRIVE_AVENGER: 343 | stats.overdrive.bits.avenger = 1; 344 | break; 345 | case OVERDRIVE_SLAYER: 346 | stats.overdrive.bits.slayer = 1; 347 | break; 348 | case OVERDRIVE_HERO: 349 | stats.overdrive.bits.hero = 1; 350 | break; 351 | case OVERDRIVE_ROOK: 352 | stats.overdrive.bits.rook = 1; 353 | break; 354 | case OVERDRIVE_VICTOR: 355 | stats.overdrive.bits.victor = 1; 356 | break; 357 | case OVERDRIVE_COWARD: 358 | stats.overdrive.bits.coward = 1; 359 | break; 360 | case OVERDRIVE_ALLY: 361 | stats.overdrive.bits.ally = 1; 362 | break; 363 | case OVERDRIVE_SUFFERER: 364 | stats.overdrive.bits.sufferer = 1; 365 | break; 366 | case OVERDRIVE_DAREDEVIL: 367 | stats.overdrive.bits.daredevil = 1; 368 | break; 369 | case OVERDRIVE_LONER: 370 | stats.overdrive.bits.loner = 1; 371 | break; 372 | } 373 | stats.writeToBytes(); 374 | // stats.test(); 375 | } 376 | } 377 | 378 | if (options_pack.randomize_aeon_stat_items) 379 | { 380 | randomizeSumGrow(); 381 | reconstructSumGrowData(); 382 | } 383 | 384 | printf( "Reconstructing ply_save.bin...\n" ); 385 | reconstructPlayerStatsData(); 386 | 387 | if (options_pack.randomize_aeon_stat_scaling || options_pack.shuffle_aeon_stat_scaling) 388 | { 389 | printf( "Reconstructing ply_rom.bin...\n" ); 390 | reconstructAeonScalingData(); 391 | } 392 | 393 | if (options_pack.randomize_aeon_base_stats || options_pack.shuffle_aeon_base_stats) 394 | { 395 | printf( "Reconstructing sum_assure.bin...\n" ); 396 | reconstructAeonStatData(); 397 | } 398 | } -------------------------------------------------------------------------------- /src/core/EnemyDataRandomizer.cpp: -------------------------------------------------------------------------------- 1 | #include "Randomizer.hpp" 2 | 3 | 4 | void randomizer_t::randomizeEnemyDrops( enemy_data_t* enemy ) 5 | { 6 | enemy_loot_data_t& loot = *enemy->loot_data; 7 | if (options_pack.keep_things_sane) 8 | { 9 | if (loot.gil > 0) 10 | loot.gil = normal( loot.gil, loot.gil / 2, loot.gil * 0.05, static_cast< uint16_t >( std::min( loot.gil * 3, UINT16_MAX ) ) ); 11 | else 12 | loot.gil = 0; 13 | if (loot.ap > 0) 14 | loot.ap = normal( loot.ap, loot.ap / 2, loot.ap * 0.05, static_cast< uint16_t >( std::min( loot.ap * 3, UINT16_MAX ) ) ); 15 | else 16 | loot.ap = 0; 17 | if (loot.ap_overkill > 0) 18 | loot.ap_overkill = normal( loot.ap_overkill, loot.ap_overkill / 2, loot.ap_overkill * 0.05, static_cast< uint16_t >( std::min( loot.ap_overkill * 3, UINT16_MAX ) ) ); 19 | else 20 | loot.ap_overkill = 0; 21 | } 22 | else 23 | { 24 | loot.gil = uniform( 1, UINT16_MAX ); 25 | loot.ap = uniform( 1, UINT16_MAX ); 26 | loot.ap_overkill = uniform( 1, UINT16_MAX ); 27 | } 28 | 29 | // Normal Item Drop 30 | item_t* normal_drop = getRandomItem(); 31 | int normal_drop_quantity = getRandomItemQuantity( normal_drop ); 32 | loot.primary_normal_drop = normal_drop->id; 33 | loot.n_primary_normal_drop = normal_drop_quantity; 34 | 35 | // Rare Item Drop 36 | item_t* rare_drop = getRandomItem(); 37 | int rare_drop_quantity = getRandomItemQuantity( rare_drop ); 38 | loot.primary_normal_drop_rare = rare_drop->id; 39 | loot.n_primary_normal_drop_rare = rare_drop_quantity; 40 | 41 | // Secondary Item Drop 42 | item_t* secondary_normal_drop = getRandomItem(); 43 | int secondary_normal_drop_quantity = getRandomItemQuantity( secondary_normal_drop ); 44 | loot.secondary_normal_drop = secondary_normal_drop->id; 45 | loot.n_secondary_normal_drop = secondary_normal_drop_quantity; 46 | 47 | // Secondary Rare Item Drop 48 | item_t* secondary_rare_drop = getRandomItem(); 49 | int secondary_rare_drop_quantity = getRandomItemQuantity( secondary_rare_drop ); 50 | loot.secondary_normal_drop_rare = secondary_rare_drop->id; 51 | loot.n_secondary_normal_drop_rare = secondary_rare_drop_quantity; 52 | 53 | // Normal Overkill Drop 54 | item_t* normal_overkill_drop = getRandomItem(); 55 | int normal_overkill_drop_quantity = getRandomItemQuantity( normal_overkill_drop ); 56 | loot.primary_normal_drop_overkill = normal_overkill_drop->id; 57 | loot.n_primary_normal_drop_overkill = normal_overkill_drop_quantity; 58 | 59 | // Rare Overkill Drop 60 | item_t* rare_overkill_drop = getRandomItem(); 61 | int rare_overkill_drop_quantity = getRandomItemQuantity( rare_overkill_drop ); 62 | loot.primary_normal_drop_overkill_rare = rare_overkill_drop->id; 63 | loot.n_primary_normal_drop_overkill_rare = rare_overkill_drop_quantity; 64 | 65 | // Secondary Overkill Drop 66 | item_t* secondary_overkill_drop = getRandomItem(); 67 | int secondary_overkill_drop_quantity = getRandomItemQuantity( secondary_overkill_drop ); 68 | loot.secondary_normal_drop_overkill = secondary_overkill_drop->id; 69 | loot.n_secondary_normal_drop_overkill = secondary_overkill_drop_quantity; 70 | 71 | // Secondary Rare Overkill Drop 72 | item_t* secondary_rare_overkill_drop = getRandomItem(); 73 | int secondary_rare_overkill_drop_quantity = getRandomItemQuantity( secondary_rare_overkill_drop ); 74 | loot.secondary_normal_drop_overkill_rare = secondary_rare_overkill_drop->id; 75 | loot.n_secondary_normal_drop_overkill_rare = secondary_rare_overkill_drop_quantity; 76 | 77 | if (options_pack.keep_things_sane && loot.primary_drop_chance > 0) 78 | loot.primary_drop_chance = normal( loot.primary_drop_chance, loot.primary_drop_chance / 2, 0, 255 ); 79 | else 80 | loot.primary_drop_chance = uniform( 0, 255 ); 81 | 82 | if (options_pack.keep_things_sane && loot.secondary_drop_chance > 0) 83 | loot.secondary_drop_chance = normal( loot.secondary_drop_chance, loot.secondary_drop_chance / 2, 0, 255 ); 84 | else 85 | loot.secondary_drop_chance = uniform( 0, 255 ); 86 | 87 | loot.writeToBytes(); 88 | enemy->loot_data = &loot; 89 | enemy->writeLootData( loot ); 90 | }; 91 | 92 | void randomizer_t::randomizeEnemySteal( enemy_data_t* enemy ) 93 | { 94 | enemy_loot_data_t& loot = *enemy->loot_data; 95 | item_t* steal_item = getRandomItem(); 96 | int steal_item_quantity = getRandomItemQuantity( steal_item ); 97 | item_t* rare_steal_item = getRandomItem(); 98 | int rare_steal_item_quantity = getRandomItemQuantity( rare_steal_item ); 99 | loot.steal_chance = 255; 100 | loot.steal_item = steal_item->id; 101 | loot.steal_item_rare = rare_steal_item->id; 102 | loot.n_steal_item = steal_item_quantity; 103 | loot.n_steal_item_rare = rare_steal_item_quantity; 104 | if (loot.n_gil_steal > 0) 105 | { 106 | if (options_pack.keep_things_sane) 107 | loot.n_gil_steal = normal( loot.n_gil_steal, loot.n_gil_steal / 2, 1, UINT8_MAX ); 108 | else 109 | loot.n_gil_steal = uniform( 1, UINT8_MAX ); 110 | } 111 | loot.writeToBytes(); 112 | enemy->loot_data = &loot; 113 | enemy->writeLootData( loot ); 114 | } 115 | 116 | void randomizer_t::randomizeEnemyBribe( enemy_data_t* enemy ) 117 | { 118 | enemy_loot_data_t& loot = *enemy->loot_data; 119 | item_t* bribe_item = getRandomItem(); 120 | int bribe_item_quantity = getRandomItemQuantity( bribe_item ); 121 | loot.bribe_item = bribe_item->id; 122 | loot.n_bribe_item = bribe_item_quantity; 123 | loot.writeToBytes(); 124 | enemy->loot_data = &loot; 125 | enemy->writeLootData( loot ); 126 | } 127 | 128 | void randomizer_t::randomizeEnemyStatsNormal( enemy_data_t* enemy ) 129 | { 130 | int m_id = std::stoi( enemy->monster_id ); 131 | if (m_id == MON_TREASURE_CHEST_01 || m_id == MON_TREASURE_CHEST_TUTORIAL) 132 | return; 133 | enemy_stat_data_t& stats = *enemy->stats_data; 134 | if (stats.hp <= 1) 135 | return; 136 | if (stats.hp > 1) 137 | stats.hp = normal( stats.hp, stats.hp / 2, std::floor( stats.hp * 0.5 ), stats.hp * 3 ); 138 | if (stats.hp < 50) 139 | stats.hp = 50; 140 | stats.mp = normal( stats.mp, stats.mp / 2, 1, 999 ); 141 | stats.overkill_threshold = normal( stats.overkill_threshold / 2, stats.overkill_threshold, 1, 99999 ); 142 | if (options_pack.keep_things_sane) 143 | { 144 | double mult = 5 / 3; 145 | stats.str = normal( stats.str, stats.str / 3, stats.str / 3, std::clamp( static_cast< int >( std::round( stats.str * mult ) ), 0, 255 ) ); 146 | stats.def = normal( stats.def, stats.def / 3, stats.def / 3, std::clamp( static_cast< int >( std::round( stats.def * mult ) ), 0, 255 ) ); 147 | stats.mag = normal( stats.mag, stats.mag / 3, stats.mag / 3, std::clamp( static_cast< int >( std::round( stats.mag * mult ) ), 0, 255 ) ); 148 | stats.mdef = normal( stats.mdef, stats.mdef / 3, stats.mdef / 3, std::clamp( static_cast< int >( std::round( stats.mdef * mult ) ), 0, 255 ) ); 149 | stats.agi = normal( stats.agi, stats.agi / 3, stats.agi / 3, std::clamp( static_cast< int >( std::round( stats.agi * mult ) ), 0, 255 ) ); 150 | stats.acc = normal( stats.acc, stats.acc / 3, stats.acc / 3, std::clamp( static_cast< int >( std::round( stats.acc * mult ) ), 0, 255 ) ); 151 | } 152 | else 153 | { 154 | stats.str = normal( stats.str, stats.str / 2, 0, UINT8_MAX ); 155 | stats.def = normal( stats.def, stats.def / 2, 0, UINT8_MAX ); 156 | stats.mag = normal( stats.mag, stats.mag / 2, 0, UINT8_MAX ); 157 | stats.mdef = normal( stats.mdef, stats.mdef / 2, 0, UINT8_MAX ); 158 | stats.agi = normal( stats.agi, stats.agi / 2, 0, UINT8_MAX ); 159 | stats.acc = normal( stats.acc, stats.acc / 2, 0, UINT8_MAX ); 160 | } 161 | stats.flags1.bits.armored = rand() % 4 == 0; 162 | stats.writeToBytes(); 163 | enemy->stats_data = &stats; 164 | enemy->writeStatsData( stats ); 165 | } 166 | 167 | void randomizer_t::addEnemyDefenses( enemy_data_t* enemy ) 168 | { 169 | enemy_stat_data_t& stats = *enemy->stats_data; 170 | if (stats.hp <= 1) 171 | return; 172 | def_pool.push_back( stats.def ); 173 | mdef_pool.push_back( stats.mdef ); 174 | eva_pool.push_back( stats.eva ); 175 | } 176 | 177 | void randomizer_t::randomizeEnemyStatsDefensiveNormalization( enemy_data_t* enemy ) 178 | { 179 | int m_id = std::stoi( enemy->monster_id ); 180 | if (m_id == MON_TREASURE_CHEST_01 || m_id == MON_TREASURE_CHEST_TUTORIAL) 181 | return; 182 | enemy_stat_data_t& stats = *enemy->stats_data; 183 | if (stats.hp <= 1) 184 | return; 185 | 186 | if (stats.mp > 0) 187 | stats.mp = normal( stats.mp, stats.mp / 2, 1, 999 ); 188 | 189 | stats.overkill_threshold = normal( stats.overkill_threshold, stats.overkill_threshold / 2, 1, UINT32_MAX ); 190 | 191 | std::uniform_int_distribution dist( 0, def_pool.size() - 1 ); 192 | uint8_t def = def_pool[ dist( rng ) ]; 193 | stats.def = def; 194 | double def_factor = 1; 195 | if (def != 0) 196 | def_factor = ( 1.0 - ( def / 255.0 ) / 3 ); 197 | 198 | std::uniform_int_distribution dist2( 0, mdef_pool.size() - 1 ); 199 | uint8_t mdef = mdef_pool[ dist2( rng ) ]; 200 | stats.mdef = mdef; 201 | double mdef_factor = 1; 202 | if (mdef != 0) 203 | mdef_factor = ( 1.0 - ( mdef / 255.0 ) / 3 ); 204 | 205 | std::uniform_int_distribution dist3( 0, eva_pool.size() - 1 ); 206 | uint8_t eva = eva_pool[ dist3( rng ) ]; 207 | stats.eva = eva; 208 | double evasion_factor = 1; 209 | if (eva != 0) 210 | evasion_factor = ( 1.0 - ( eva / 255.0 ) / 3 ); 211 | 212 | double defensive_factor = ( def_factor + mdef_factor + evasion_factor ) * 2; 213 | if (defensive_factor < 0.33) 214 | stats.flags1.bits.armored = 0; 215 | else 216 | stats.flags1.bits.armored = rand() % 4 == 0; 217 | 218 | uint32_t base_hp = 1; 219 | if (stats.hp > 1) 220 | base_hp = stats.hp * defensive_factor; 221 | if (base_hp <= 1) 222 | base_hp = 50; 223 | uint32_t hp = normal( base_hp, base_hp / 2, 50, UINT32_MAX ); 224 | stats.hp = hp; 225 | if (stats.hp < 50) 226 | stats.hp = 50; 227 | double mult = 5 / 3; 228 | stats.str = normal( stats.str, stats.str / 3, stats.str / 3, std::clamp( static_cast< int >( std::round( stats.str * mult ) ), 0, 255 ) ); 229 | stats.mag = normal( stats.mag, stats.mag / 3, stats.mag / 3, std::clamp( static_cast< int >( std::round( stats.mag * mult ) ), 0, 255 ) ); 230 | stats.agi = normal( stats.agi, stats.agi / 3, stats.agi / 3, std::clamp( static_cast< int >( std::round( stats.agi * mult ) ), 0, 255 ) ); 231 | stats.acc = normal( stats.acc, stats.acc / 3, stats.acc / 3, std::clamp( static_cast< int >( std::round( stats.acc * mult ) ), 0, 255 ) ); 232 | enemy->loot_data->gil /= defensive_factor; 233 | enemy->loot_data->ap /= defensive_factor; 234 | stats.writeToBytes(); 235 | enemy->stats_data = &stats; 236 | enemy->writeStatsData( stats ); 237 | } 238 | 239 | void randomizer_t::shuffleEnemyStats( enemy_data_t* enemy ) 240 | { 241 | int m_id = std::stoi( enemy->monster_id ); 242 | if (m_id == MON_TREASURE_CHEST_01 || m_id == MON_TREASURE_CHEST_TUTORIAL) 243 | return; 244 | enemy_stat_data_t& stats = *enemy->stats_data; 245 | if (stats.hp <= 1) 246 | return; 247 | // Pick a random index to pull the stats from 248 | std::uniform_int_distribution dist( 0, data_pack.enemy_data.size() - 1 ); 249 | int index = dist( rng ); 250 | if (stats.hp > 1) 251 | stats.hp = normal( stats.hp, stats.hp / 2, 50, UINT32_MAX ); 252 | if (stats.hp < 50) 253 | stats.hp = 50; 254 | stats.mp = normal( stats.mp, stats.mp / 2, 1, 9999 ); 255 | stats.overkill_threshold = normal( stats.overkill_threshold / 2, stats.overkill_threshold, 1, UINT32_MAX ); 256 | stats.str = normal( stats.str, stats.str / 2, 0, UINT8_MAX ); 257 | stats.def = def_pool[ index ]; 258 | stats.mdef = mdef_pool[ index ]; 259 | stats.eva = eva_pool[ index ]; 260 | stats.flags1.bits.armored = rand() % 4 == 0; 261 | stats.agi = normal( stats.agi, stats.agi / 2, 0, UINT8_MAX ); 262 | stats.luck = normal( stats.luck, stats.luck / 2, 0, UINT8_MAX ); 263 | stats.acc = normal( stats.acc, stats.acc / 2, 0, UINT8_MAX ); 264 | stats.writeToBytes(); 265 | enemy->stats_data = &stats; 266 | enemy->writeStatsData( stats ); 267 | } 268 | 269 | void randomizer_t::randomizeEnemyElementalAffinities( enemy_data_t* enemy ) 270 | { 271 | enemy_stat_data_t& stats = *enemy->stats_data; 272 | if (stats.hp <= 1) 273 | return; 274 | 275 | int fire_roll = uniform( 0, 69 ); 276 | int ice_roll = uniform( 0, 69 ); 277 | int lightning_roll = uniform( 0, 69 ); 278 | int water_roll = uniform( 0, 69 ); 279 | int holy_roll = uniform( 0, 69 ); 280 | 281 | stats.element_weakness_flags.bits.fire = fire_roll < 20; 282 | stats.element_weakness_flags.bits.ice = ice_roll < 20; 283 | stats.element_weakness_flags.bits.lightning = lightning_roll < 20; 284 | stats.element_weakness_flags.bits.water = water_roll < 20; 285 | stats.element_weakness_flags.bits.holy = holy_roll < 20; 286 | 287 | stats.element_resist_flags.bits.fire = fire_roll < 25 && fire_roll >= 20; 288 | stats.element_resist_flags.bits.ice = ice_roll < 25 && ice_roll >= 20; 289 | stats.element_resist_flags.bits.lightning = lightning_roll < 25 && lightning_roll >= 20; 290 | stats.element_resist_flags.bits.water = water_roll < 25 && water_roll >= 20; 291 | stats.element_resist_flags.bits.holy = holy_roll < 25 && holy_roll >= 20; 292 | 293 | stats.element_immune_flags.bits.fire = fire_roll < 30 && fire_roll >= 25; 294 | stats.element_immune_flags.bits.ice = ice_roll < 30 && ice_roll >= 25; 295 | stats.element_immune_flags.bits.lightning = lightning_roll < 30 && lightning_roll >= 25; 296 | stats.element_immune_flags.bits.water = water_roll < 30 && water_roll >= 25; 297 | stats.element_immune_flags.bits.holy = holy_roll < 30 && holy_roll >= 25; 298 | 299 | stats.element_absorb_flags.bits.fire = fire_roll < 35 && fire_roll >= 30; 300 | stats.element_absorb_flags.bits.ice = ice_roll < 35 && ice_roll >= 30; 301 | stats.element_absorb_flags.bits.lightning = lightning_roll < 35 && lightning_roll >= 30; 302 | stats.element_absorb_flags.bits.water = water_roll < 35 && water_roll >= 30; 303 | stats.element_absorb_flags.bits.holy = holy_roll < 35 && holy_roll >= 30; 304 | 305 | stats.writeToBytes(); 306 | enemy->stats_data = &stats; 307 | enemy->writeStatsData( stats ); 308 | } 309 | 310 | void randomizer_t::randomizeEnemyGearDrops( enemy_data_t* enemy ) 311 | { 312 | enemy_loot_data_t& loot = *enemy->loot_data; 313 | if (options_pack.keep_things_sane) 314 | { 315 | loot.gear_slot_count_byte = uniform( 0, 16 ); 316 | loot.gear_ability_count_byte = uniform( 0, 16 ); 317 | } 318 | else 319 | { 320 | loot.gear_slot_count_byte = uniform( 0, 20 ); 321 | loot.gear_ability_count_byte = uniform( 0, 20 ); 322 | } 323 | 324 | if (options_pack.keep_things_sane && loot.gear_drop_chance > 0) 325 | loot.gear_drop_chance = normal( loot.gear_drop_chance, loot.gear_drop_chance / 2, 0, 101 ); 326 | else 327 | loot.gear_drop_chance = uniform( 0, 255 ); 328 | 329 | if (options_pack.randomize_weapon_attack_power && loot.gear_attack_power > 0) 330 | { 331 | if (options_pack.keep_things_sane) 332 | loot.gear_attack_power = normal( loot.gear_attack_power, loot.gear_attack_power / 2, 1, 24 ); 333 | else 334 | loot.gear_attack_power = uniform( 1, 100 ); 335 | } 336 | 337 | if (options_pack.randomize_weapon_crit && loot.gear_crit_bonus > 0) 338 | { 339 | if (options_pack.keep_things_sane) 340 | loot.gear_crit_bonus = normal( loot.gear_crit_bonus, loot.gear_crit_bonus / 2, 0, 100 ); 341 | else 342 | loot.gear_crit_bonus = uniform( 1, 100 ); 343 | } 344 | 345 | if (options_pack.randomize_weapon_damage_formula && loot.gear_damage_calc > 0) 346 | loot.gear_damage_calc = getRandomFormula(); 347 | 348 | if (loot.gear_damage_calc == WEAPON_FORMULA_NONE) 349 | loot.gear_damage_calc = WEAPON_FORMULA_STR_VS_DEF; 350 | 351 | for (int chr = 0; chr < 7; chr++) 352 | for (int i = 0; i < 8; i++) 353 | loot.weapon_abilities_by_char.at( chr ) = getRandomAbility(); 354 | 355 | for (int chr = 0; chr < 7; chr++) 356 | for (int i = 0; i < 8; i++) 357 | loot.gear_abilities_by_char.at( chr ) = getRandomAbility(); 358 | 359 | loot.writeToBytes(); 360 | enemy->loot_data = &loot; 361 | enemy->writeLootData( loot ); 362 | } 363 | 364 | void randomizer_t::doEnemyRandomization() 365 | { 366 | if (!options_pack.randomize_enemy_drops && !options_pack.randomize_enemy_steals && !options_pack.randomize_enemy_bribes && !options_pack.randomize_enemy_gear_drops && 367 | !options_pack.randomize_enemy_stats && !options_pack.randomize_enemy_stats_defensive && !options_pack.randomize_enemy_stats_shuffle && 368 | !options_pack.randomize_enemy_elemental_affinities && !options_pack.swap_random_stats && !options_pack.scale_encounter_stats) 369 | return; 370 | 371 | doRandomEcnounterRandomization(); 372 | 373 | // Generate the enemy defensive stats pool before going into the main loop 374 | if (options_pack.randomize_enemy_stats_defensive || options_pack.randomize_enemy_stats_shuffle) 375 | for (auto& enemy : data_pack.enemy_data) 376 | addEnemyDefenses( enemy.second ); 377 | 378 | for (auto& enemy : data_pack.enemy_data) 379 | { 380 | if (options_pack.randomize_enemy_drops) 381 | { 382 | printf( "Randomizing Enemy Drops for monster %s\n", enemy.second->monster_id.c_str() ); 383 | randomizeEnemyDrops( enemy.second ); 384 | } 385 | if (options_pack.randomize_enemy_steals) 386 | { 387 | printf( "Randomizing Enemy Steals for monster %s\n", enemy.second->monster_id.c_str() ); 388 | randomizeEnemySteal( enemy.second ); 389 | } 390 | if (options_pack.randomize_enemy_bribes) 391 | { 392 | printf( "Randomizing Enemy Bribes for monster %s\n", enemy.second->monster_id.c_str() ); 393 | randomizeEnemyBribe( enemy.second ); 394 | } 395 | if (options_pack.randomize_enemy_gear_drops) 396 | { 397 | printf( "Randomizing Enemy Gear Drops for monster %s\n", enemy.second->monster_id.c_str() ); 398 | randomizeEnemyGearDrops( enemy.second ); 399 | } 400 | if (options_pack.randomize_enemy_stats) 401 | { 402 | printf( "Randomizing Enemy Stats for monster %s\n", enemy.second->monster_id.c_str() ); 403 | randomizeEnemyStatsNormal( enemy.second ); 404 | } 405 | if (options_pack.randomize_enemy_stats_defensive) 406 | { 407 | printf( "Randomizing Enemy Defensive for monster %s\n", enemy.second->monster_id.c_str() ); 408 | randomizeEnemyStatsDefensiveNormalization( enemy.second ); 409 | } 410 | if (options_pack.randomize_enemy_stats_shuffle) 411 | { 412 | printf( "Shuffling Enemy Stats for monster %s\n", enemy.second->monster_id.c_str() ); 413 | shuffleEnemyStats( enemy.second ); 414 | } 415 | if (options_pack.randomize_enemy_elemental_affinities) 416 | { 417 | printf( "Randomizing Enemy Elemental Affinities for monster %s\n", enemy.second->monster_id.c_str() ); 418 | randomizeEnemyElementalAffinities( enemy.second ); 419 | } 420 | 421 | printf( "Reconstructing files for for monster %s\n", enemy.second->monster_id.c_str() ); 422 | std::string pathstr = OUTPUT_FOLDER + prefix + MONSTER_FOLDER + "_m" + enemy.second->monster_id; 423 | std::filesystem::path path = pathstr; 424 | std::filesystem::create_directories( path ); 425 | std::string filepath = pathstr + "/m" + enemy.second->monster_id + ".bin"; 426 | enemy.second->writeBytesToNewFile( enemy.second->bytes, filepath ); 427 | } 428 | } -------------------------------------------------------------------------------- /src/core/GUI.cpp: -------------------------------------------------------------------------------- 1 | #include "GUI.hpp" 2 | #include "Data.hpp" 3 | 4 | bool gui_t::OnInit() 5 | { 6 | SetAppearance( Appearance::System ); 7 | SetAppName( "FFX Randomizer" ); 8 | SetVendorName( "FFX Randomizer" ); 9 | SetUseBestVisual( true, true ); 10 | frame = new frame_t( dp ); 11 | frame->Show( true ); 12 | return true; 13 | } 14 | 15 | void frame_t::initialize() 16 | { 17 | wxBoxSizer* main_vertical_sizer = new wxBoxSizer( wxVERTICAL ); 18 | 19 | notebook = new wxAuiNotebook( this, ID_NOTEBOOK, wxDefaultPosition, wxDefaultSize, wxNB_FIXEDWIDTH ); 20 | notebook->SetAutoLayout( true ); 21 | notebook->SetThemeEnabled( true ); 22 | wxColor notebook_color = wxSystemSettings::GetColour( wxSYS_COLOUR_MENU ); 23 | notebook->SetBackgroundColour( notebook_color ); 24 | 25 | wxStaticText* header_text = new wxStaticText( this, wxID_ANY, _T( "FFX Randomizer" ), wxDefaultPosition, wxDefaultSize ); 26 | header_text->SetFont( wxFont( 18, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD ) ); 27 | 28 | wxStaticText* seed_header_text = new wxStaticText( this, wxID_ANY, _T( "RNG Seed" ), wxDefaultPosition, wxDefaultSize ); 29 | seed_text = new wxTextCtrl( this, ID_SEED, std::to_string( seed ), wxDefaultPosition, wxDefaultSize ); 30 | Bind( wxEVT_TEXT, &frame_t::onSeedChange, this, ID_SEED ); 31 | 32 | wxStaticText* extra_text = new wxStaticText( this, wxID_ANY, _T( "Options that affect other aspects of the randomizer:" ), wxDefaultPosition, wxDefaultSize ); 33 | extra_text->SetFont( wxFont( 10, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD ) ); 34 | header_panel_t* header_panel = new header_panel_t( this ); 35 | Bind( wxEVT_CHECKBOX, &frame_t::onRandomizeKeyItems, this, ID_ALLOW_RANDOMIZE_KEY_ITEMS ); 36 | 37 | Bind( wxEVT_CHECKBOX, &frame_t::onPoisonIsDeadly, this, ID_POISON_IS_DEADLY ); 38 | Bind( wxEVT_CHECKBOX, &frame_t::onRandomizeEnemyDrops, this, ID_RANDOMIZE_ENEMY_DROPS ); 39 | Bind( wxEVT_CHECKBOX, &frame_t::onRandomizeEnemySteals, this, ID_RANDOMIZE_ENEMY_STEALS ); 40 | Bind( wxEVT_CHECKBOX, &frame_t::onRandomizeEnemyBribes, this, ID_RANDOMIZE_ENEMY_BRIBES ); 41 | Bind( wxEVT_CHECKBOX, &frame_t::onRandomizeEnemyGearDrops, this, ID_RANDOMIZE_ENEMY_GEAR_DROPS ); 42 | Bind( wxEVT_RADIOBUTTON, &frame_t::onRandomizeEnemyStatsNone, this, ID_RANDOMIZE_ENEMY_STATS_NONE ); 43 | Bind( wxEVT_RADIOBUTTON, &frame_t::onRandomizeEnemyStats, this, ID_RANDOMIZE_ENEMY_STATS ); 44 | Bind( wxEVT_RADIOBUTTON, &frame_t::onRandomizeEnemyStatsDefensive, this, ID_RANDOMIZE_ENEMY_STATS_DEFENSIVE ); 45 | Bind( wxEVT_RADIOBUTTON, &frame_t::onRandomizeEnemyStatsShuffle, this, ID_RANDOMIZE_ENEMY_STATS_SHUFFLE ); 46 | 47 | Bind( wxEVT_CHECKBOX, &frame_t::onRandomizeItemShops, this, ID_RANDOMIZE_ITEM_SHOPS ); 48 | Bind( wxEVT_CHECKBOX, &frame_t::onRandomizeGearShops, this, ID_RANDOMIZE_GEAR_SHOPS ); 49 | Bind( wxEVT_CHECKBOX, &frame_t::onRandomizeItemShopPrices, this, ID_RANDOMIZE_ITEM_SHOP_PRICES ); 50 | Bind( wxEVT_CHECKBOX, &frame_t::onRandomizeGearShopPrices, this, ID_RANDOMIZE_GEAR_SHOP_PRICES ); 51 | Bind( wxEVT_CHECKBOX, &frame_t::onEnsureShopsSellSpheres, this, ID_ENSURE_SHOPS_SELL_SPHERES ); 52 | 53 | Bind( wxEVT_CHECKBOX, &frame_t::onRandomizeFieldItems, this, ID_RANDOMIZE_FIELD_ITEMS ); 54 | 55 | Bind( wxEVT_CHECKBOX, &frame_t::onRandomizeGearAbilities, this, ID_RANDOMIZE_GEAR_ABILITIES ); 56 | Bind( wxEVT_CHECKBOX, &frame_t::onRandomizeWeaponCrit, this, ID_RANDOMIZE_WEAPON_CRIT ); 57 | Bind( wxEVT_CHECKBOX, &frame_t::onRandomizeWeaponAttackPower, this, ID_RANDOMIZE_WEAPON_ATTACK_POWER ); 58 | Bind( wxEVT_CHECKBOX, &frame_t::onRandomizeWeaponDamageFormula, this, ID_RANDOMIZE_WEAPON_DAMAGE_FORMULA ); 59 | Bind( wxEVT_CHECKBOX, &frame_t::onRandomizeCustomizationItems, this, ID_RANDOMIZE_CUSTOMIZATION_ITEMS ); 60 | 61 | Bind( wxEVT_RADIOBUTTON, &frame_t::onRandomizePlayerStatsNone, this, ID_RANDOMIZE_PLAYER_STATS_NONE ); 62 | Bind( wxEVT_RADIOBUTTON, &frame_t::onRandomizePlayerStats, this, ID_RANDOMIZE_PLAYER_STATS ); 63 | Bind( wxEVT_RADIOBUTTON, &frame_t::onShufflePlayerStats, this, ID_SHUFFLE_PLAYER_STATS ); 64 | 65 | Bind( wxEVT_RADIOBUTTON, &frame_t::onRandomizeAeonStatScalingNone, this, ID_RANDOMIZE_AEON_STAT_SCALING_NONE ); 66 | Bind( wxEVT_RADIOBUTTON, &frame_t::onRandomizeAeonStatScaling, this, ID_RANDOMIZE_AEON_STAT_SCALING ); 67 | Bind( wxEVT_RADIOBUTTON, &frame_t::onShuffleAeonStatScaling, this, ID_SHUFFLE_AEON_STAT_SCALING ); 68 | 69 | Bind( wxEVT_RADIOBUTTON, &frame_t::onRandomizeAeonBaseStatsNone, this, ID_RANDOMIZE_AEON_BASE_STATS_NONE ); 70 | Bind( wxEVT_RADIOBUTTON, &frame_t::onRandomizeAeonBaseStats, this, ID_RANDOMIZE_AEON_BASE_STATS ); 71 | Bind( wxEVT_RADIOBUTTON, &frame_t::onShuffleAeonBaseStats, this, ID_SHUFFLE_AEON_BASE_STATS ); 72 | Bind( wxEVT_CHECKBOX, &frame_t::onRandomizeAeonStatItems, this, ID_RANDOMIZE_AEON_STAT_ITEMS ); 73 | 74 | Bind( wxEVT_CHECKBOX, &frame_t::onRandomizeStartingOverdriveMode, this, ID_RANDOMIZE_STARTING_OVERDRIVE_MODE ); 75 | Bind( wxEVT_CHECKBOX, &frame_t::onRandomizeEnemyElementalAffinities, this, ID_RANDOMIZE_ENEMY_ELEMENTAl_AFFINITIES ); 76 | Bind( wxEVT_CHECKBOX, &frame_t::onRandomizeEncounters, this, ID_RANDOMIZE_ENCOUNTERS ); 77 | 78 | Bind( wxEVT_CHECKBOX, &frame_t::onRemoveSphereGridLocks, this, ID_REMOVE_LOCKS ); 79 | Bind( wxEVT_RADIOBUTTON, &frame_t::onRandomizeSphereGridNone, this, ID_SPHERE_GRID_NONE ); 80 | Bind( wxEVT_RADIOBUTTON, &frame_t::onShuffleSphereGrid, this, ID_SHUFFLE_SPHERE_GRID ); 81 | Bind( wxEVT_RADIOBUTTON, &frame_t::onRandomizeSphereGridTrue, this, ID_RANDOMIZE_SPHERE_GRID_TRUE ); 82 | Bind( wxEVT_RADIOBUTTON, &frame_t::onRandomizeSphereGrid, this, ID_RANDOMIZE_SPHERE_GRID ); 83 | Bind( wxEVT_RADIOBUTTON, &frame_t::onSphereNodesNone, this, ID_SPHERE_NODES_NONE ); 84 | Bind( wxEVT_RADIOBUTTON, &frame_t::onEmptySphereGrid, this, ID_EMPTY_GRID ); 85 | Bind( wxEVT_RADIOBUTTON, &frame_t::onFillSphereGrid, this, ID_FULL_GRID ); 86 | Bind( wxEVT_RADIOBUTTON, &frame_t::onSphereGridNone, this, ID_NONE_GRID ); 87 | Bind( wxEVT_RADIOBUTTON, &frame_t::onUpgradeSphereNodes, this, ID_UPGRADE_SPHERE_NODES ); 88 | Bind( wxEVT_RADIOBUTTON, &frame_t::onDowngradeSphereNodes, this, ID_DOWNGRADE_SPHERE_NODES ); 89 | 90 | Bind( wxEVT_RADIOBUTTON, &frame_t::onRandizedEncountersStatsNone, this, ID_RANDOM_ENCOUNTER_STATS_NONE ); 91 | Bind( wxEVT_RADIOBUTTON, &frame_t::onSwapRandomEncounterStats, this, ID_SWAP_RANDOM_ENCOUNTER_STATS ); 92 | Bind( wxEVT_RADIOBUTTON, &frame_t::onScaleEncounterStats, this, ID_SCALE_ECOUNTER_STATS ); 93 | 94 | Bind( wxEVT_CHECKBOX, &frame_t::onKeepThingsSane, this, ID_KEEP_THINGS_SANE ); 95 | Bind( wxEVT_CHECKBOX, &frame_t::onFahrenheit, this, ID_FAHRENHEIT ); 96 | Bind( wxEVT_CHECKBOX, &frame_t::onRandomizeCelestials, this, ID_ALLOW_RANDOMIZE_CELESTIALS ); 97 | Bind( wxEVT_CHECKBOX, &frame_t::onRandomizeBrotherhood, this, ID_ALLOW_RANDOMIZE_BROTHERHOOD ); 98 | 99 | enemy_options_panel_t* panel = new enemy_options_panel_t( notebook ); 100 | notebook->AddPage( panel, _T( "Enemy" ), true ); 101 | 102 | gear_options_panel_t* gear_panel = new gear_options_panel_t( notebook ); 103 | notebook->AddPage( gear_panel, _T( "Gear" ), false ); 104 | 105 | item_options_panel_t* item_panel = new item_options_panel_t( notebook ); 106 | notebook->AddPage( item_panel, _T( "Items" ), false ); 107 | 108 | player_stats_panel_t* player_stats = new player_stats_panel_t( notebook ); 109 | notebook->AddPage( player_stats, _T( "Player" ), false ); 110 | 111 | aeon_stats_panel_t* aeon_stats = new aeon_stats_panel_t( notebook ); 112 | notebook->AddPage( aeon_stats, _T( "Aeon" ), false ); 113 | 114 | sphere_grid_panel_t* grid = new sphere_grid_panel_t( notebook ); 115 | notebook->AddPage( grid, _T( "Sphere Grid" ), false ); 116 | 117 | randomize_button = new wxButton( this, ID_RANDOMIZE, _T( "Randomize" ), wxDefaultPosition, wxDefaultSize, wxBU_ALIGN_MASK ); 118 | randomize_button->SetToolTip( "Click this button to randomize the game!" ); 119 | Bind( wxEVT_BUTTON, &frame_t::onRandomize, this, ID_RANDOMIZE ); 120 | 121 | main_vertical_sizer->Add( header_text, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT | wxTOP, FromDIP( 10 ) ); 122 | main_vertical_sizer->Add( seed_header_text, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT | wxTOP, FromDIP( 10 ) ); 123 | main_vertical_sizer->Add( seed_text, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT | wxTOP, FromDIP( 10 ) ); 124 | main_vertical_sizer->Add( extra_text, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT | wxTOP, FromDIP( 10 ) ); 125 | main_vertical_sizer->Add( header_panel, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT | wxTOP, FromDIP( 10 ) ); 126 | main_vertical_sizer->Add( notebook, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT | wxTOP, FromDIP( 10 ) ); 127 | //main_vertical_sizer->Add( enemy_stats_text, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT | wxTOP, FromDIP( 10 ) ); 128 | //main_vertical_sizer->Add( stats_panel, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT | wxTOP, FromDIP( 10 ) ); 129 | main_vertical_sizer->Add( randomize_button, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT | wxTOP, FromDIP( 10 ) ); 130 | SetSizer( main_vertical_sizer ); 131 | SetAutoLayout( true ); 132 | } 133 | 134 | void frame_t::onRandomize( wxCommandEvent& event ) 135 | { 136 | options = new options_pack_t( randomize_enemy_drops, 137 | randomize_enemy_steals, 138 | randomize_enemy_bribes, 139 | randomize_enemy_gear_drops, 140 | randomize_enemy_stats, 141 | randomize_enemy_stats_defensive, 142 | randomize_enemy_stats_shuffle, 143 | randomize_item_shops, 144 | randomize_gear_shops, 145 | randomize_item_shop_prices, 146 | randomize_gear_shop_prices, 147 | ensure_shops_sell_spheres, 148 | randomize_field_items, 149 | randomize_gear_abilities, 150 | randomize_weapon_crit, 151 | randomize_weapon_attack_power, 152 | randomize_weapon_damage_formula, 153 | randomize_player_stats, 154 | randomize_aeon_stat_scaling, 155 | randomize_aeon_base_stats, 156 | shuffle_player_stats, 157 | shuffle_aeon_stat_scaling, 158 | shuffle_aeon_base_stats, 159 | poison_is_deadly, 160 | randomize_starting_overdrive_mode, 161 | randomize_enemy_elemental_affinities, 162 | randomize_encounters, 163 | shuffle_sphere_grid, 164 | randomize_sphere_grid_true, 165 | randomize_sphere_grid, 166 | empty_sphere_grid, 167 | fill_sphere_grid, 168 | remove_sphere_grid_locks, 169 | upgrade_sphere_nodes, 170 | downgrade_sphere_nodes, 171 | swap_random_stats, 172 | scale_encounter_stats, 173 | randomize_key_items, 174 | randomize_celestials, 175 | randomize_brotherhood, 176 | randomize_customization_items, 177 | randomize_aeon_stat_items, 178 | keep_things_sane, 179 | seed, 180 | seed_text->GetValue().ToStdString(), 181 | fahrenheit ); 182 | 183 | randomizer = new randomizer_t( *options, dp ); 184 | wxLogMessage( "Finished Randomizing" ); 185 | } 186 | 187 | void frame_t::onPoisonIsDeadly( wxCommandEvent& event ) 188 | { 189 | poison_is_deadly = !poison_is_deadly; 190 | printf( "Poison is Deadly: %d\n", poison_is_deadly ); 191 | } 192 | 193 | void frame_t::onRandomizeEnemyDrops( wxCommandEvent& event ) 194 | { 195 | randomize_enemy_drops = !randomize_enemy_drops; 196 | printf( "Randomize Enemy Drops: %d\n", randomize_enemy_drops ); 197 | } 198 | 199 | void frame_t::onRandomizeEnemySteals( wxCommandEvent& event ) 200 | { 201 | randomize_enemy_steals = !randomize_enemy_steals; 202 | printf( "Randomize Enemy Steals: %d\n", randomize_enemy_steals ); 203 | } 204 | 205 | void frame_t::onRandomizeEnemyBribes( wxCommandEvent& event ) 206 | { 207 | randomize_enemy_bribes = !randomize_enemy_bribes; 208 | printf( "Randomize Enemy Bribes: %d\n", randomize_enemy_bribes ); 209 | } 210 | 211 | void frame_t::onRandomizeEnemyGearDrops( wxCommandEvent& event ) 212 | { 213 | randomize_enemy_gear_drops = !randomize_enemy_gear_drops; 214 | printf( "Randomize Enemy Gear Drops: %d\n", randomize_enemy_gear_drops ); 215 | } 216 | 217 | void frame_t::onRandomizeEnemyStatsNone( wxCommandEvent& event ) 218 | { 219 | randomize_enemy_stats = false; 220 | randomize_enemy_stats_defensive = false; 221 | randomize_enemy_stats_shuffle = false; 222 | printf( "Randomize Enemy Stats None: %d\n", randomize_enemy_stats ); 223 | } 224 | 225 | void frame_t::onRandomizeEnemyStats( wxCommandEvent& event ) 226 | { 227 | randomize_enemy_stats = !randomize_enemy_stats; 228 | if (randomize_enemy_stats) 229 | { 230 | randomize_enemy_stats_defensive = false; 231 | randomize_enemy_stats_shuffle = false; 232 | } 233 | printf( "Randomize Enemy Stats Normal Distribution: %d\n", randomize_enemy_stats ); 234 | } 235 | 236 | void frame_t::onRandomizeEnemyStatsDefensive( wxCommandEvent& event ) 237 | { 238 | randomize_enemy_stats_defensive = !randomize_enemy_stats_defensive; 239 | if (randomize_enemy_stats_defensive) 240 | { 241 | randomize_enemy_stats = false; 242 | randomize_enemy_stats_shuffle = false; 243 | } 244 | 245 | printf( "Randomize Enemy Stats Defensive: %d\n", randomize_enemy_stats_defensive ); 246 | } 247 | 248 | void frame_t::onRandomizeEnemyStatsShuffle( wxCommandEvent& event ) 249 | { 250 | randomize_enemy_stats_shuffle = !randomize_enemy_stats_shuffle; 251 | if (randomize_enemy_stats_shuffle) 252 | { 253 | randomize_enemy_stats = false; 254 | randomize_enemy_stats_defensive = false; 255 | } 256 | printf( "Randomize Enemy Stats Shuffle: %d\n", randomize_enemy_stats_shuffle ); 257 | } 258 | 259 | void frame_t::onRandomizeItemShops( wxCommandEvent& event ) 260 | { 261 | randomize_item_shops = !randomize_item_shops; 262 | printf( "Randomize Shops: %d\n", randomize_item_shops ); 263 | } 264 | 265 | void frame_t::onRandomizeGearShops( wxCommandEvent& event ) 266 | { 267 | randomize_gear_shops = !randomize_gear_shops; 268 | printf( "Randomize Gear Shops: %d\n", randomize_gear_shops ); 269 | } 270 | 271 | void frame_t::onRandomizeItemShopPrices( wxCommandEvent& event ) 272 | { 273 | randomize_item_shop_prices = !randomize_item_shop_prices; 274 | printf( "Randomize Shop Prices: %d\n", randomize_item_shop_prices ); 275 | } 276 | 277 | void frame_t::onRandomizeGearShopPrices( wxCommandEvent& event ) 278 | { 279 | randomize_gear_shop_prices = !randomize_gear_shop_prices; 280 | printf( "Randomize Gear Shop Prices: %d\n", randomize_gear_shop_prices ); 281 | } 282 | 283 | void frame_t::onEnsureShopsSellSpheres( wxCommandEvent& event ) 284 | { 285 | ensure_shops_sell_spheres = !ensure_shops_sell_spheres; 286 | printf( "Ensure Shops Sell Spheres: %d\n", ensure_shops_sell_spheres ); 287 | } 288 | 289 | void frame_t::onRandomizeFieldItems( wxCommandEvent& event ) 290 | { 291 | randomize_field_items = !randomize_field_items; 292 | printf( "Randomize Field Items: %d\n", randomize_field_items ); 293 | } 294 | 295 | void frame_t::onRandomizeGearAbilities( wxCommandEvent& event ) 296 | { 297 | randomize_gear_abilities = !randomize_gear_abilities; 298 | printf( "Randomize Gear Abilities: %d\n", randomize_gear_abilities ); 299 | } 300 | 301 | void frame_t::onRandomizeWeaponCrit( wxCommandEvent& event ) 302 | { 303 | randomize_weapon_crit = !randomize_weapon_crit; 304 | printf( "Randomize Weapon Crit Chance: %d\n", randomize_weapon_crit ); 305 | } 306 | 307 | void frame_t::onRandomizeWeaponAttackPower( wxCommandEvent& event ) 308 | { 309 | randomize_weapon_attack_power = !randomize_weapon_attack_power; 310 | printf( "Randomize Weapon Attack Power: %d\n", randomize_weapon_attack_power ); 311 | } 312 | 313 | void frame_t::onRandomizeWeaponDamageFormula( wxCommandEvent& event ) 314 | { 315 | randomize_weapon_damage_formula = !randomize_weapon_damage_formula; 316 | printf( "Randomize Weapon Damage Formula: %d\n", randomize_weapon_damage_formula ); 317 | } 318 | 319 | void frame_t::onRandomizeCustomizationItems( wxCommandEvent& event ) 320 | { 321 | randomize_customization_items = !randomize_customization_items; 322 | printf( "Randomize Customization Items: %d\n", randomize_customization_items ); 323 | } 324 | 325 | void frame_t::onRandomizePlayerStatsNone( wxCommandEvent& event ) 326 | { 327 | randomize_player_stats = false; 328 | shuffle_player_stats = false; 329 | printf( "Randomize Player Stats None: %d\n", randomize_player_stats ); 330 | } 331 | 332 | void frame_t::onRandomizePlayerStats( wxCommandEvent& event ) 333 | { 334 | randomize_player_stats = !randomize_player_stats; 335 | if (randomize_player_stats) 336 | shuffle_player_stats = false; 337 | printf( "Randomize Player Stats: %d\n", randomize_player_stats ); 338 | } 339 | 340 | void frame_t::onShufflePlayerStats( wxCommandEvent& event ) 341 | { 342 | shuffle_player_stats = !shuffle_player_stats; 343 | if (shuffle_player_stats) 344 | randomize_player_stats = false; 345 | printf( "Shuffle Player Stats: %d\n", shuffle_player_stats ); 346 | } 347 | 348 | void frame_t::onRandomizeAeonStatScalingNone( wxCommandEvent& event ) 349 | { 350 | randomize_aeon_stat_scaling = false; 351 | shuffle_aeon_stat_scaling = false; 352 | printf( "Randomize Aeon Stat Scaling None: %d\n", randomize_aeon_stat_scaling ); 353 | } 354 | 355 | void frame_t::onShuffleAeonStatScaling( wxCommandEvent& event ) 356 | { 357 | shuffle_aeon_stat_scaling = !shuffle_aeon_stat_scaling; 358 | if (shuffle_aeon_stat_scaling) 359 | randomize_aeon_stat_scaling = false; 360 | printf( "Shuffle Aeon Stats: %d\n", shuffle_aeon_stat_scaling ); 361 | } 362 | 363 | void frame_t::onRandomizeAeonStatScaling( wxCommandEvent& event ) 364 | { 365 | randomize_aeon_stat_scaling = !randomize_aeon_stat_scaling; 366 | if (randomize_aeon_stat_scaling) 367 | shuffle_aeon_stat_scaling = false; 368 | printf( "Randomize Aeon Stats: %d\n", randomize_aeon_stat_scaling ); 369 | } 370 | 371 | void frame_t::onRandomizeAeonBaseStatsNone( wxCommandEvent& event ) 372 | { 373 | randomize_aeon_base_stats = false; 374 | shuffle_aeon_base_stats = false; 375 | printf( "Randomize Aeon Base Stats None: %d\n", randomize_aeon_base_stats ); 376 | } 377 | 378 | void frame_t::onRandomizeAeonBaseStats( wxCommandEvent& event ) 379 | { 380 | randomize_aeon_base_stats = !randomize_aeon_base_stats; 381 | if (randomize_aeon_base_stats) 382 | shuffle_aeon_base_stats = false; 383 | printf( "Randomize Aeon Base Stats: %d\n", randomize_aeon_base_stats ); 384 | } 385 | 386 | void frame_t::onShuffleAeonBaseStats( wxCommandEvent& event ) 387 | { 388 | shuffle_aeon_base_stats = !shuffle_aeon_base_stats; 389 | if (shuffle_aeon_base_stats) 390 | randomize_aeon_base_stats = false; 391 | printf( "Shuffle Aeon Base Stats: %d\n", shuffle_aeon_base_stats ); 392 | } 393 | 394 | void frame_t::onRandomizeAeonStatItems( wxCommandEvent& event ) 395 | { 396 | randomize_aeon_stat_items = !randomize_aeon_stat_items; 397 | printf( "Randomize Aeon Stat Items: %d\n", randomize_aeon_stat_items ); 398 | } 399 | 400 | void frame_t::onRandomizeStartingOverdriveMode( wxCommandEvent& event ) 401 | { 402 | randomize_starting_overdrive_mode = !randomize_starting_overdrive_mode; 403 | printf( "Randomize Starting Overdrive Mode: %d\n", randomize_starting_overdrive_mode ); 404 | } 405 | 406 | void frame_t::onRandomizeEnemyElementalAffinities( wxCommandEvent& event ) 407 | { 408 | randomize_enemy_elemental_affinities = !randomize_enemy_elemental_affinities; 409 | printf( "Randomize Enemy Elemental Affinities: %d\n", randomize_enemy_elemental_affinities ); 410 | } 411 | 412 | void frame_t::onRandomizeEncounters( wxCommandEvent& event ) 413 | { 414 | randomize_encounters = !randomize_encounters; 415 | printf( "Randomize Encounters: %d\n", randomize_encounters ); 416 | } 417 | 418 | void frame_t::onRandomizeSphereGridNone( wxCommandEvent& event ) 419 | { 420 | randomize_sphere_grid_true = false; 421 | shuffle_sphere_grid = false; 422 | printf( "Randomize Sphere Grid None: %d\n", randomize_sphere_grid_true ); 423 | } 424 | 425 | void frame_t::onShuffleSphereGrid( wxCommandEvent& event ) 426 | { 427 | shuffle_sphere_grid = !shuffle_sphere_grid; 428 | if (shuffle_sphere_grid) 429 | { 430 | randomize_sphere_grid_true = false; 431 | randomize_sphere_grid = false; 432 | } 433 | printf( "Shuffle Sphere Grid: %d\n", shuffle_sphere_grid ); 434 | } 435 | 436 | void frame_t::onRandomizeSphereGridTrue( wxCommandEvent& event ) 437 | { 438 | randomize_sphere_grid_true = !randomize_sphere_grid_true; 439 | if (randomize_sphere_grid_true) 440 | { 441 | randomize_sphere_grid = false; 442 | shuffle_sphere_grid = false; 443 | } 444 | printf( "Truly Randomize Sphere Grid: %d\n", randomize_sphere_grid_true ); 445 | } 446 | 447 | void frame_t::onRandomizeSphereGrid( wxCommandEvent& event ) 448 | { 449 | randomize_sphere_grid = !randomize_sphere_grid; 450 | if (randomize_sphere_grid) 451 | { 452 | randomize_sphere_grid_true = false; 453 | shuffle_sphere_grid = false; 454 | } 455 | printf( "Randomize Sphere Grid: %d\n", randomize_sphere_grid ); 456 | } 457 | 458 | void frame_t::onEmptySphereGrid( wxCommandEvent& event ) 459 | { 460 | empty_sphere_grid = !empty_sphere_grid; 461 | if (empty_sphere_grid) 462 | fill_sphere_grid = false; 463 | printf( "Empty Sphere Grid: %d\n", empty_sphere_grid ); 464 | } 465 | 466 | void frame_t::onSphereGridNone( wxCommandEvent& event ) 467 | { 468 | empty_sphere_grid = false; 469 | fill_sphere_grid = false; 470 | printf( "Sphere Grid None: %d\n", empty_sphere_grid ); 471 | } 472 | 473 | void frame_t::onFillSphereGrid( wxCommandEvent& event ) 474 | { 475 | fill_sphere_grid = !fill_sphere_grid; 476 | if (fill_sphere_grid) 477 | empty_sphere_grid = false; 478 | printf( "Fill Sphere Grid: %d\n", fill_sphere_grid ); 479 | } 480 | 481 | void frame_t::onRemoveSphereGridLocks( wxCommandEvent& event ) 482 | { 483 | remove_sphere_grid_locks = !remove_sphere_grid_locks; 484 | printf( "Remove Sphere Grid Locks: %d\n", remove_sphere_grid_locks ); 485 | } 486 | 487 | void frame_t::onSphereNodesNone( wxCommandEvent& event ) 488 | { 489 | upgrade_sphere_nodes = false; 490 | downgrade_sphere_nodes = false; 491 | printf( "Sphere Nodes None: %d\n", upgrade_sphere_nodes ); 492 | } 493 | 494 | void frame_t::onUpgradeSphereNodes( wxCommandEvent& event ) 495 | { 496 | upgrade_sphere_nodes = !upgrade_sphere_nodes; 497 | if (upgrade_sphere_nodes) 498 | downgrade_sphere_nodes = false; 499 | printf( "Upgrade Sphere Nodes: %d\n", upgrade_sphere_nodes ); 500 | } 501 | 502 | void frame_t::onDowngradeSphereNodes( wxCommandEvent& event ) 503 | { 504 | downgrade_sphere_nodes = !downgrade_sphere_nodes; 505 | if (downgrade_sphere_nodes) 506 | upgrade_sphere_nodes = false; 507 | printf( "Downgrade Sphere Nodes: %d\n", downgrade_sphere_nodes ); 508 | } 509 | 510 | void frame_t::onRandizedEncountersStatsNone( wxCommandEvent& event ) 511 | { 512 | swap_random_stats = false; 513 | scale_encounter_stats = false; 514 | printf( "Randomized Encounters Stats None: %d\n", swap_random_stats ); 515 | } 516 | 517 | void frame_t::onSwapRandomEncounterStats( wxCommandEvent& event ) 518 | { 519 | swap_random_stats = !swap_random_stats; 520 | if (swap_random_stats) 521 | scale_encounter_stats = false; 522 | 523 | printf( "Swap Random Encounter Stats: %d\n", swap_random_stats ); 524 | } 525 | 526 | void frame_t::onScaleEncounterStats( wxCommandEvent& event ) 527 | { 528 | scale_encounter_stats = !scale_encounter_stats; 529 | if (scale_encounter_stats) 530 | swap_random_stats = false; 531 | printf( "Scale Encounter Stats: %d\n", scale_encounter_stats ); 532 | } 533 | 534 | void frame_t::onRandomizeKeyItems( wxCommandEvent& event ) 535 | { 536 | randomize_key_items = !randomize_key_items; 537 | printf( "Randomize Key Items: %d\n", randomize_key_items ); 538 | } 539 | 540 | void frame_t::onKeepThingsSane( wxCommandEvent& event ) 541 | { 542 | keep_things_sane = !keep_things_sane; 543 | printf( "Keep Things Sane: %d\n", keep_things_sane ); 544 | } 545 | 546 | void frame_t::onFahrenheit( wxCommandEvent& event ) 547 | { 548 | fahrenheit = !fahrenheit; 549 | printf( "Fahrenheit: %d\n", fahrenheit ); 550 | } 551 | 552 | void frame_t::onRandomizeCelestials( wxCommandEvent& event ) 553 | { 554 | randomize_celestials = !randomize_celestials; 555 | printf( "Randomize Celestial Weapons: %d\n", randomize_celestials ); 556 | } 557 | 558 | void frame_t::onRandomizeBrotherhood( wxCommandEvent& event ) 559 | { 560 | randomize_brotherhood = !randomize_brotherhood; 561 | printf( "Randomize Brotherhood: %d\n", randomize_brotherhood ); 562 | } 563 | 564 | int32_t frame_t::hash( char* str ) 565 | { 566 | unsigned int h; 567 | unsigned char* p; 568 | 569 | h = 0; 570 | for (p = ( unsigned char* ) str; *p != '\0'; p++) 571 | h = 37 * h + *p; 572 | return h; // or, h % ARRAY_SIZE; 573 | } 574 | 575 | void frame_t::onSeedChange( wxCommandEvent& event ) 576 | { 577 | if (seed_text->GetValue().IsEmpty()) 578 | return; 579 | 580 | std::string seed_str = seed_text->GetValue().ToStdString(); 581 | int32_t this_seed = 0; 582 | for (char& c : seed_str) 583 | { 584 | int32_t character = hash( &c ); 585 | this_seed += character; 586 | } 587 | 588 | seed = this_seed; 589 | printf( "Seed: %ld\n", seed ); 590 | } -------------------------------------------------------------------------------- /src/core/GUI.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "Randomizer.hpp" 8 | 9 | #ifndef WX_PRECOMP 10 | #include 11 | #endif 12 | 13 | enum 14 | { 15 | ID_SEED = 1, 16 | ID_KEEP_THINGS_SANE, 17 | ID_FAHRENHEIT, 18 | ID_RANDOMIZE, 19 | ID_ALLOW_RANDOMIZE_KEY_ITEMS, 20 | ID_ALLOW_RANDOMIZE_CELESTIALS, 21 | ID_ALLOW_RANDOMIZE_BROTHERHOOD, 22 | ID_POISON_IS_DEADLY, 23 | ID_RANDOMIZE_ENEMY_DROPS, 24 | ID_RANDOMIZE_ENEMY_STEALS, 25 | ID_RANDOMIZE_ENEMY_BRIBES, 26 | ID_RANDOMIZE_ENEMY_GEAR_DROPS, 27 | ID_RANDOMIZE_ENEMY_STATS_NONE, 28 | ID_RANDOMIZE_ENEMY_STATS, 29 | ID_RANDOMIZE_ENEMY_STATS_DEFENSIVE, 30 | ID_RANDOMIZE_ENEMY_STATS_SHUFFLE, 31 | ID_RANDOMIZE_ENEMY_ELEMENTAl_AFFINITIES, 32 | ID_RANDOMIZE_ENCOUNTERS, 33 | ID_RANDOMIZE_ITEM_SHOPS, 34 | ID_RANDOMIZE_ITEM_SHOP_PRICES, 35 | ID_ENSURE_SHOPS_SELL_SPHERES, 36 | ID_RANDOMIZE_FIELD_ITEMS, 37 | ID_RANDOMIZE_GEAR_SHOPS, 38 | ID_RANDOMIZE_GEAR_SHOP_PRICES, 39 | ID_RANDOMIZE_GEAR_ABILITIES, 40 | ID_RANDOMIZE_WEAPON_CRIT, 41 | ID_RANDOMIZE_WEAPON_ATTACK_POWER, 42 | ID_RANDOMIZE_WEAPON_DAMAGE_FORMULA, 43 | ID_RANDOMIZE_PLAYER_STATS_NONE, 44 | ID_RANDOMIZE_PLAYER_STATS, 45 | ID_SHUFFLE_PLAYER_STATS, 46 | ID_RANDOMIZE_STARTING_OVERDRIVE_MODE, 47 | ID_RANDOMIZE_AEON_STAT_SCALING_NONE, 48 | ID_SHUFFLE_AEON_STAT_SCALING, 49 | ID_RANDOMIZE_AEON_STAT_SCALING, 50 | ID_RANDOMIZE_AEON_BASE_STATS_NONE, 51 | ID_SHUFFLE_AEON_BASE_STATS, 52 | ID_RANDOMIZE_AEON_BASE_STATS, 53 | ID_SPHERE_GRID_NONE, 54 | ID_SHUFFLE_SPHERE_GRID, 55 | ID_RANDOMIZE_SPHERE_GRID, 56 | ID_RANDOMIZE_SPHERE_GRID_TRUE, 57 | ID_SPHERE_NODES_NONE, 58 | ID_UPGRADE_SPHERE_NODES, 59 | ID_DOWNGRADE_SPHERE_NODES, 60 | ID_NONE_GRID, 61 | ID_EMPTY_GRID, 62 | ID_FULL_GRID, 63 | ID_REMOVE_LOCKS, 64 | ID_RANDOM_ENCOUNTER_STATS_NONE, 65 | ID_SWAP_RANDOM_ENCOUNTER_STATS, 66 | ID_SCALE_ECOUNTER_STATS, 67 | ID_RANDOMIZE_CUSTOMIZATION_ITEMS, 68 | ID_RANDOMIZE_AEON_STAT_ITEMS, 69 | ID_BASE_WINDOW = 1000, 70 | ID_NOTEBOOK, 71 | }; 72 | 73 | struct gui_t : public wxApp 74 | { 75 | private: 76 | data_pack_t& dp; 77 | wxFrame* frame; 78 | 79 | public: 80 | gui_t( data_pack_t& data_pack ) : dp( data_pack ), frame( nullptr ) {} 81 | 82 | virtual bool OnInit(); 83 | }; 84 | 85 | class frame_t : public wxFrame 86 | { 87 | private: 88 | data_pack_t& dp; 89 | options_pack_t* options; 90 | 91 | randomizer_t* randomizer; 92 | 93 | bool randomize_enemy_drops; 94 | bool randomize_enemy_steals; 95 | bool randomize_enemy_bribes; 96 | bool randomize_enemy_gear_drops; 97 | bool randomize_enemy_stats; 98 | bool randomize_enemy_stats_defensive; 99 | bool randomize_enemy_stats_shuffle; 100 | bool randomize_item_shops; 101 | bool randomize_gear_shops; 102 | bool randomize_item_shop_prices; 103 | bool randomize_gear_shop_prices; 104 | bool ensure_shops_sell_spheres; 105 | bool randomize_field_items; 106 | bool randomize_gear_abilities; 107 | bool randomize_weapon_crit; 108 | bool randomize_weapon_attack_power; 109 | bool randomize_weapon_damage_formula; 110 | bool randomize_player_stats; 111 | bool randomize_aeon_stat_scaling; 112 | bool randomize_aeon_base_stats; 113 | bool shuffle_player_stats; 114 | bool shuffle_aeon_stat_scaling; 115 | bool shuffle_aeon_base_stats; 116 | bool poison_is_deadly; 117 | bool randomize_starting_overdrive_mode; 118 | bool randomize_enemy_elemental_affinities; 119 | bool randomize_encounters; 120 | 121 | bool shuffle_sphere_grid; 122 | bool randomize_sphere_grid_true; 123 | bool randomize_sphere_grid; 124 | bool empty_sphere_grid; 125 | bool fill_sphere_grid; 126 | bool remove_sphere_grid_locks; 127 | bool upgrade_sphere_nodes; 128 | bool downgrade_sphere_nodes; 129 | 130 | bool swap_random_stats; 131 | bool scale_encounter_stats; 132 | 133 | bool randomize_key_items; 134 | bool keep_things_sane; 135 | bool randomize_celestials; 136 | bool randomize_brotherhood; 137 | bool randomize_customization_items; 138 | bool randomize_aeon_stat_items; 139 | 140 | wxButton* randomize_button; 141 | int32_t seed; 142 | wxTextCtrl* seed_text; 143 | 144 | bool fahrenheit; 145 | 146 | wxAuiNotebook* notebook; 147 | 148 | public: 149 | frame_t( data_pack_t& data ) 150 | : wxFrame( NULL, ID_BASE_WINDOW, "FFX Randomizer", wxDefaultPosition, wxSize( 840, 720 ) ), 151 | dp( data ), 152 | options( nullptr ), 153 | randomizer( nullptr ), 154 | randomize_enemy_drops( false ), 155 | randomize_enemy_steals( false ), 156 | randomize_enemy_bribes( false ), 157 | randomize_enemy_gear_drops( false ), 158 | randomize_enemy_stats( false ), 159 | randomize_enemy_stats_defensive( false ), 160 | randomize_enemy_stats_shuffle( false ), 161 | randomize_item_shops( false ), 162 | randomize_gear_shops( false ), 163 | randomize_item_shop_prices( false ), 164 | randomize_gear_shop_prices( false ), 165 | ensure_shops_sell_spheres( false ), 166 | randomize_field_items( false ), 167 | randomize_gear_abilities( false ), 168 | randomize_weapon_crit( false ), 169 | randomize_weapon_attack_power( false ), 170 | randomize_weapon_damage_formula( false ), 171 | randomize_player_stats( false ), 172 | randomize_aeon_stat_scaling( false ), 173 | randomize_aeon_base_stats( false ), 174 | shuffle_player_stats( false ), 175 | shuffle_aeon_stat_scaling( false ), 176 | shuffle_aeon_base_stats( false ), 177 | poison_is_deadly( false ), 178 | randomize_starting_overdrive_mode( false ), 179 | randomize_enemy_elemental_affinities( false ), 180 | randomize_encounters( false ), 181 | shuffle_sphere_grid( false ), 182 | randomize_sphere_grid_true( false ), 183 | randomize_sphere_grid( false ), 184 | empty_sphere_grid( false ), 185 | fill_sphere_grid( false ), 186 | remove_sphere_grid_locks( false ), 187 | upgrade_sphere_nodes( false ), 188 | downgrade_sphere_nodes( false ), 189 | swap_random_stats( false ), 190 | scale_encounter_stats( false ), 191 | randomize_key_items( false ), 192 | keep_things_sane( true ), 193 | randomize_celestials( false ), 194 | randomize_brotherhood( false ), 195 | randomize_customization_items( false ), 196 | randomize_aeon_stat_items( false ), 197 | randomize_button( nullptr ), 198 | seed( std::chrono::system_clock::now().time_since_epoch().count() ), 199 | seed_text( nullptr ), 200 | fahrenheit( false ), 201 | notebook( nullptr ) 202 | { 203 | initialize(); 204 | SetSizeHints( GetBestSize(), GetMaxClientSize() ); 205 | SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); 206 | wxSize size = GetBestSize(); 207 | size.SetHeight( size.GetHeight() + 10 ); 208 | size.SetWidth( size.GetWidth() ); 209 | SetSize( size ); 210 | } 211 | 212 | private: 213 | void initialize(); 214 | void onSeedChange( wxCommandEvent& event ); 215 | void onRandomize( wxCommandEvent& event ); 216 | void onPoisonIsDeadly( wxCommandEvent& event ); 217 | void onRandomizeEnemyDrops( wxCommandEvent& event ); 218 | void onRandomizeEnemySteals( wxCommandEvent& event ); 219 | void onRandomizeEnemyBribes( wxCommandEvent& event ); 220 | void onRandomizeEnemyGearDrops( wxCommandEvent& event ); 221 | void onRandomizeEnemyStatsNone( wxCommandEvent& event ); 222 | void onRandomizeEnemyStats( wxCommandEvent& event ); 223 | void onRandomizeEnemyStatsDefensive( wxCommandEvent& event ); 224 | void onRandomizeEnemyStatsShuffle( wxCommandEvent& event ); 225 | void onRandomizeEnemyElementalAffinities( wxCommandEvent& event ); 226 | 227 | void onRandomizeItemShops( wxCommandEvent& event ); 228 | void onRandomizeGearShops( wxCommandEvent& event ); 229 | void onRandomizeItemShopPrices( wxCommandEvent& event ); 230 | void onRandomizeGearShopPrices( wxCommandEvent& event ); 231 | void onEnsureShopsSellSpheres( wxCommandEvent& event ); 232 | 233 | void onRandomizeFieldItems( wxCommandEvent& event ); 234 | 235 | void onRandomizeGearAbilities( wxCommandEvent& event ); 236 | void onRandomizeWeaponCrit( wxCommandEvent& event ); 237 | void onRandomizeWeaponAttackPower( wxCommandEvent& event ); 238 | void onRandomizeWeaponDamageFormula( wxCommandEvent& event ); 239 | void onRandomizeCustomizationItems( wxCommandEvent& event ); 240 | 241 | void onRandomizeStartingOverdriveMode( wxCommandEvent& event ); 242 | 243 | void onRandomizePlayerStatsNone( wxCommandEvent& event ); 244 | void onRandomizePlayerStats( wxCommandEvent& event ); 245 | void onShufflePlayerStats( wxCommandEvent& event ); 246 | 247 | void onRandomizeAeonStatScalingNone( wxCommandEvent& event ); 248 | void onRandomizeAeonStatScaling( wxCommandEvent& event ); 249 | void onShuffleAeonStatScaling( wxCommandEvent& event ); 250 | 251 | void onRandomizeAeonBaseStatsNone( wxCommandEvent& event ); 252 | void onRandomizeAeonBaseStats( wxCommandEvent& event ); 253 | void onShuffleAeonBaseStats( wxCommandEvent& event ); 254 | 255 | void onRandomizeAeonStatItems( wxCommandEvent& event ); 256 | 257 | void onRandomizeEncounters( wxCommandEvent& event ); 258 | void onRandizedEncountersStatsNone( wxCommandEvent& event ); 259 | void onSwapRandomEncounterStats( wxCommandEvent& event ); 260 | void onScaleEncounterStats( wxCommandEvent& event ); 261 | 262 | void onRemoveSphereGridLocks( wxCommandEvent& event ); 263 | void onRandomizeSphereGridNone( wxCommandEvent& event ); 264 | void onShuffleSphereGrid( wxCommandEvent& event ); 265 | void onRandomizeSphereGridTrue( wxCommandEvent& event ); 266 | void onRandomizeSphereGrid( wxCommandEvent& event ); 267 | void onSphereNodesNone( wxCommandEvent& event ); 268 | void onEmptySphereGrid( wxCommandEvent& event ); 269 | void onFillSphereGrid( wxCommandEvent& event ); 270 | void onSphereGridNone( wxCommandEvent& event ); 271 | void onUpgradeSphereNodes( wxCommandEvent& event ); 272 | void onDowngradeSphereNodes( wxCommandEvent& event ); 273 | 274 | void onRandomizeKeyItems( wxCommandEvent& event ); 275 | void onKeepThingsSane( wxCommandEvent& event ); 276 | void onFahrenheit( wxCommandEvent& event ); 277 | void onRandomizeCelestials( wxCommandEvent& event ); 278 | void onRandomizeBrotherhood( wxCommandEvent& event ); 279 | 280 | int32_t hash( char* str ); 281 | }; 282 | 283 | struct enemy_options_panel_t : public wxPanel 284 | { 285 | wxCheckBox* poisonIsDeadlyCheckbox; 286 | 287 | wxCheckBox* randomizeEnemyDropsCheckbox; 288 | wxCheckBox* randomizeEnemyStealsCheckbox; 289 | wxCheckBox* randomizeEnemyBribesCheckbox; 290 | wxCheckBox* randomizeEnemyGearDropsCheckbox; 291 | wxCheckBox* randomizeEnemyElementalAffinitiesCheckbox; 292 | wxCheckBox* randomizeEncountersCheckbox; 293 | 294 | enemy_options_panel_t( wxAuiNotebook* frame ) : wxPanel( frame, wxID_ANY, wxDefaultPosition, wxSize( 335, 350 ), wxBORDER_NONE), 295 | poisonIsDeadlyCheckbox( nullptr ), 296 | randomizeEnemyDropsCheckbox( nullptr ), 297 | randomizeEnemyStealsCheckbox( nullptr ), 298 | randomizeEnemyBribesCheckbox( nullptr ), 299 | randomizeEnemyGearDropsCheckbox( nullptr ), 300 | randomizeEnemyElementalAffinitiesCheckbox( nullptr ), 301 | randomizeEncountersCheckbox( nullptr ) 302 | { 303 | wxBoxSizer* sizer = new wxBoxSizer( wxVERTICAL ); 304 | 305 | poisonIsDeadlyCheckbox = new wxCheckBox( this, ID_POISON_IS_DEADLY, _T( "Enemy Poison is Deadly" ), wxDefaultPosition, wxDefaultSize, 0 ); 306 | poisonIsDeadlyCheckbox->SetToolTip( "If checked, poison will deal 50% of the player's HP per tick instead of the default 25%." ); 307 | 308 | randomizeEnemyDropsCheckbox = new wxCheckBox( this, ID_RANDOMIZE_ENEMY_DROPS, _T( "Randomize Enemy Drops" ), wxDefaultPosition, wxDefaultSize, 0 ); 309 | randomizeEnemyStealsCheckbox = new wxCheckBox( this, ID_RANDOMIZE_ENEMY_STEALS, _T( "Randomize Enemy Steals" ), wxDefaultPosition, wxDefaultSize, 0 ); 310 | randomizeEnemyBribesCheckbox = new wxCheckBox( this, ID_RANDOMIZE_ENEMY_BRIBES, _T( "Randomize Enemy Bribes" ), wxDefaultPosition, wxDefaultSize, 0 ); 311 | randomizeEnemyGearDropsCheckbox = new wxCheckBox( this, ID_RANDOMIZE_ENEMY_GEAR_DROPS, _T( "Randomize Enemy Gear Drops" ), wxDefaultPosition, wxDefaultSize, 0 ); 312 | randomizeEnemyElementalAffinitiesCheckbox = new wxCheckBox( this, ID_RANDOMIZE_ENEMY_ELEMENTAl_AFFINITIES, _T( "Randomize Enemy Elemental Affinities" ), wxDefaultPosition, wxDefaultSize, 0 ); 313 | randomizeEnemyElementalAffinitiesCheckbox->SetToolTip( "If checked, enemy elemental affinities will be randomized. This includes:\n- Weaknesss \n- Resists \n- Immunities \n- Absorbs\n\nThis is slightly skewed in favor of giving an enemy a weakness if it gets an affinity." ); 314 | randomizeEncountersCheckbox = new wxCheckBox( this, ID_RANDOMIZE_ENCOUNTERS, _T( "Randomize Random Encounters" ), wxDefaultPosition, wxDefaultSize, 0 ); 315 | randomizeEncountersCheckbox->SetToolTip( "If checked, random encounters will be randomized." ); 316 | 317 | wxStaticText* random_encounter_text = new wxStaticText( this, wxID_ANY, _T( "Random Encounter Stat Options" ), wxDefaultPosition, wxDefaultSize, 0 ); 318 | random_encounter_text->SetFont( wxFont( 9, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD ) ); 319 | 320 | wxRadioButton* swapRandomizedEncounterStatsNoneRadioBox = new wxRadioButton( this, ID_RANDOM_ENCOUNTER_STATS_NONE, _T( "None" ), wxDefaultPosition, wxDefaultSize, wxRB_GROUP ); 321 | swapRandomizedEncounterStatsNoneRadioBox->SetToolTip( "If checked, randomized encounters will have their original stats" ); 322 | wxRadioButton* swapRandomizedEncounterStatsRadioButton = new wxRadioButton( this, ID_SWAP_RANDOM_ENCOUNTER_STATS, _T( "Swap Randomized Encounter Stats" ), wxDefaultPosition, wxDefaultSize, 0 ); 323 | swapRandomizedEncounterStatsRadioButton->SetToolTip( "If checked, randomized encounters will get the stats of their original enemy." ); 324 | wxRadioButton* scaleEncounterStatsRadioButton = new wxRadioButton( this, ID_SCALE_ECOUNTER_STATS, _T( "Normalize Encounter Stats" ), wxDefaultPosition, wxDefaultSize, 0 ); 325 | scaleEncounterStatsRadioButton->SetToolTip( "If checked, randomized encounters will have their stats scaled based on the difference between the original and new enemies stats." ); 326 | 327 | wxStaticText* enemy_stats_text = new wxStaticText( this, wxID_ANY, _T( "Enemy Stat Randomization Options" ), wxDefaultPosition, wxDefaultSize, 0 ); 328 | enemy_stats_text->SetFont( wxFont( 9, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD ) ); 329 | 330 | wxRadioButton* noRandomizeEnemyStats = new wxRadioButton( this, ID_RANDOMIZE_ENEMY_STATS_NONE, _T( "No Randomization" ), wxDefaultPosition, wxDefaultSize, wxRB_GROUP ); 331 | noRandomizeEnemyStats->SetToolTip( "If checked, enemy stats will not be randomized." ); 332 | wxRadioButton* randomizeEnemyStatsRadioButton = new wxRadioButton( this, ID_RANDOMIZE_ENEMY_STATS, _T( "Randomize Enemy Stats" ), wxDefaultPosition, wxDefaultSize, 0 ); 333 | randomizeEnemyStatsRadioButton->SetToolTip( "If checked, enemy stats will be randomized using a normal distribution, centered on their original values. This keeps things close to vanilla, with some spice on occasion" ); 334 | wxRadioButton* randomizeEnemyStatsDefensiveRadioButton = new wxRadioButton( this, ID_RANDOMIZE_ENEMY_STATS_DEFENSIVE, _T( "Shuffle Enemy Stats + Normalize Defensives" ), wxDefaultPosition, wxDefaultSize, 0 ); 335 | randomizeEnemyStatsDefensiveRadioButton->SetToolTip( "If checked, enemy defensive stats (def/magic def/evasion) will be randomly assigned to another enemies values, with some math magic to try to keep them from ending up too tanky.\n Non defensive stats are randomized with the same method as the Normal Distribution option." ); 336 | wxRadioButton* randomizeEnemyStatsShuffleRadioButton = new wxRadioButton( this, ID_RANDOMIZE_ENEMY_STATS_SHUFFLE, _T( "Shuffle Enemy Stats" ), wxDefaultPosition, wxDefaultSize, 0 ); 337 | randomizeEnemyStatsShuffleRadioButton->SetToolTip( "If checked, enemy defensive stats (def/magic def/evasion) will be shuffled to a different enemies defensive stats at random.\nYou may encounter a flan with penance's defensive stats" ); 338 | 339 | sizer->Add( poisonIsDeadlyCheckbox, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 340 | 341 | sizer->Add( randomizeEnemyDropsCheckbox, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 342 | sizer->Add( randomizeEnemyStealsCheckbox, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 343 | sizer->Add( randomizeEnemyBribesCheckbox, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 344 | sizer->Add( randomizeEnemyGearDropsCheckbox, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 345 | sizer->Add( randomizeEnemyElementalAffinitiesCheckbox, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 346 | sizer->Add( randomizeEncountersCheckbox, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 347 | 348 | sizer->InsertSpacer( sizer->GetItemCount(), FromDIP( 10 ) ); 349 | sizer->Add( random_encounter_text, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 350 | sizer->Add( swapRandomizedEncounterStatsNoneRadioBox, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 351 | sizer->Add( swapRandomizedEncounterStatsRadioButton, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 352 | sizer->Add( scaleEncounterStatsRadioButton, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 353 | 354 | sizer->InsertSpacer( sizer->GetItemCount(), FromDIP( 10 ) ); 355 | 356 | sizer->Add( enemy_stats_text, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 357 | sizer->Add( noRandomizeEnemyStats, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 358 | sizer->Add( randomizeEnemyStatsRadioButton, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 359 | sizer->Add( randomizeEnemyStatsDefensiveRadioButton, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 360 | sizer->Add( randomizeEnemyStatsShuffleRadioButton, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 361 | 362 | sizer->InsertSpacer( sizer->GetItemCount(), FromDIP( 5 ) ); 363 | 364 | SetSizer( sizer ); 365 | } 366 | }; 367 | 368 | struct header_panel_t : public wxPanel 369 | { 370 | wxCheckBox* keepThingsSaneCheckbox; 371 | wxCheckBox* fahrenheitCheckbox; 372 | 373 | header_panel_t( frame_t* frame ) : wxPanel( frame, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBORDER_THEME ), 374 | 375 | keepThingsSaneCheckbox( nullptr ), 376 | fahrenheitCheckbox( nullptr ) 377 | { 378 | wxBoxSizer* sizer = new wxBoxSizer( wxVERTICAL ); 379 | fahrenheitCheckbox = new wxCheckBox( this, ID_FAHRENHEIT, _T( "Fahrenheit" ), wxDefaultPosition, wxDefaultSize, 0 ); 380 | fahrenheitCheckbox->SetToolTip( "Check this box to generate files in a format suitable for the Fahrenheit mod loader." ); 381 | keepThingsSaneCheckbox = new wxCheckBox( this, ID_KEEP_THINGS_SANE, _T( "Keep Things Sane" ), wxDefaultPosition, wxDefaultSize, 0 ); 382 | keepThingsSaneCheckbox->SetToolTip( "If checked, the randomizer will attempt to keep things close to vanilla values. IE, if you could ever only loot 1 of an item, the randomizer will keep it that way.\nAlso keeps constraints on gil, and AP to more reasonable values, within +-100% of their original values.\nThis will mean that some spheres will sell in shops for very little, as their data has them being sold for 2 gil." ); 383 | keepThingsSaneCheckbox->SetValue( true ); 384 | 385 | sizer->Add( fahrenheitCheckbox, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 386 | sizer->Add( keepThingsSaneCheckbox, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 387 | sizer->InsertSpacer( sizer->GetItemCount(), FromDIP( 5 ) ); 388 | SetSizer( sizer ); 389 | 390 | SetMinSize( GetBestVirtualSize() ); 391 | } 392 | }; 393 | 394 | struct gear_options_panel_t : public wxPanel 395 | { 396 | wxCheckBox* randomizeCelestialsCheckbox; 397 | wxCheckBox* randomizeBrotherhoodCheckbox; 398 | wxCheckBox* randomizeGearAbilitiesCheckbox; 399 | wxCheckBox* randomizeWeaponCritCheckbox; 400 | wxCheckBox* randomizeWeaponAttackPowerCheckbox; 401 | wxCheckBox* randomizeWeaponDamageFormulaCheckbox; 402 | wxCheckBox* randomizeGearShopsCheckbox; 403 | wxCheckBox* randomizeGearPricesCheckbox; 404 | wxCheckBox* randomizeCustomizationItemsCheckbox; 405 | 406 | gear_options_panel_t( wxAuiNotebook* book ) : wxPanel( book, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE ), 407 | randomizeCelestialsCheckbox( nullptr ), 408 | randomizeBrotherhoodCheckbox( nullptr ), 409 | randomizeGearAbilitiesCheckbox( nullptr ), 410 | randomizeWeaponCritCheckbox( nullptr ), 411 | randomizeWeaponAttackPowerCheckbox( nullptr ), 412 | randomizeWeaponDamageFormulaCheckbox( nullptr ), 413 | randomizeGearShopsCheckbox( nullptr ), 414 | randomizeGearPricesCheckbox( nullptr ), 415 | randomizeCustomizationItemsCheckbox( nullptr ) 416 | { 417 | wxBoxSizer* sizer = new wxBoxSizer( wxVERTICAL ); 418 | randomizeCelestialsCheckbox = new wxCheckBox( this, ID_ALLOW_RANDOMIZE_CELESTIALS, _T( "Include Celestial Items in Pool" ), wxDefaultPosition, wxDefaultSize, 0 ); 419 | randomizeCelestialsCheckbox->SetToolTip( "If checked, celestial weapons abilities will be able to be randomized." ); 420 | 421 | randomizeBrotherhoodCheckbox = new wxCheckBox( this, ID_ALLOW_RANDOMIZE_BROTHERHOOD, _T( "Include Brotherhood in Pool" ), wxDefaultPosition, wxDefaultSize, 0 ); 422 | randomizeBrotherhoodCheckbox->SetToolTip( "If checked, Brotherhood abilities will be able to be randomized." ); 423 | 424 | randomizeGearAbilitiesCheckbox = new wxCheckBox( this, ID_RANDOMIZE_GEAR_ABILITIES, _T( "Randomize Gear Abilities" ), wxDefaultPosition, wxDefaultSize, 0 ); 425 | randomizeGearAbilitiesCheckbox->SetToolTip( "If checked, gear abilities will be randomized. This includes:\n- Armor\n- Weapons" ); 426 | 427 | randomizeWeaponCritCheckbox = new wxCheckBox( this, ID_RANDOMIZE_WEAPON_CRIT, _T( "Randomize Weapon Crit Chance" ), wxDefaultPosition, wxDefaultSize, 0 ); 428 | randomizeWeaponCritCheckbox->SetToolTip( "If checked, weapon crit chance will be randomized." ); 429 | 430 | randomizeWeaponAttackPowerCheckbox = new wxCheckBox( this, ID_RANDOMIZE_WEAPON_ATTACK_POWER, _T( "Randomize Weapon Attack Power" ), wxDefaultPosition, wxDefaultSize, 0 ); 431 | randomizeWeaponAttackPowerCheckbox->SetToolTip( "If checked, weapon attack power will be randomized." ); 432 | 433 | randomizeWeaponDamageFormulaCheckbox = new wxCheckBox( this, ID_RANDOMIZE_WEAPON_DAMAGE_FORMULA, _T( "Randomize Weapon Damage Formula" ), wxDefaultPosition, wxDefaultSize, 0 ); 434 | randomizeWeaponDamageFormulaCheckbox->SetToolTip( "If checked, weapon damage formula will be randomized." ); 435 | 436 | randomizeGearShopsCheckbox = new wxCheckBox( this, ID_RANDOMIZE_GEAR_SHOPS, _T( "Randomize Gear Shops" ), wxDefaultPosition, wxDefaultSize, 0 ); 437 | randomizeGearShopsCheckbox->SetToolTip( "If checked, gear shops will be randomized. Including the number of items offered." ); 438 | 439 | randomizeGearPricesCheckbox = new wxCheckBox( this, ID_RANDOMIZE_GEAR_SHOP_PRICES, _T( "Randomize Gear Shop Prices" ), wxDefaultPosition, wxDefaultSize, 0 ); 440 | randomizeGearPricesCheckbox->SetToolTip( "If checked, gear shop prices will be randomized." ); 441 | 442 | randomizeCustomizationItemsCheckbox = new wxCheckBox( this, ID_RANDOMIZE_CUSTOMIZATION_ITEMS, _T( "Randomize Customization Items" ), wxDefaultPosition, wxDefaultSize, 0 ); 443 | randomizeCustomizationItemsCheckbox->SetToolTip( "If checked, customization items will be randomized. This includes:\n- The item required to apply an auto ability to an item\n- The number of items required" ); 444 | 445 | sizer->Add( randomizeCelestialsCheckbox, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 446 | sizer->Add( randomizeBrotherhoodCheckbox, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 447 | sizer->Add( randomizeGearAbilitiesCheckbox, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 448 | sizer->Add( randomizeWeaponCritCheckbox, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 449 | sizer->Add( randomizeWeaponAttackPowerCheckbox, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 450 | sizer->Add( randomizeWeaponDamageFormulaCheckbox, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 451 | sizer->Add( randomizeGearShopsCheckbox, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 452 | sizer->Add( randomizeGearPricesCheckbox, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 453 | sizer->Add( randomizeCustomizationItemsCheckbox, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 454 | sizer->InsertSpacer( sizer->GetItemCount(), FromDIP( 5 ) ); 455 | SetSizer( sizer ); 456 | 457 | SetMinSize( GetBestVirtualSize() ); 458 | } 459 | }; 460 | 461 | struct item_options_panel_t : wxPanel 462 | { 463 | wxCheckBox* randomizeKeyItemsCheckbox; 464 | wxCheckBox* randomizeShopsCheckbox; 465 | wxCheckBox* randomizeShopPricesCheckbox; 466 | wxCheckBox* randomizeFieldItemsCheckbox; 467 | wxCheckBox* ensureShopsSellSpheresCheckbox; 468 | 469 | item_options_panel_t( wxAuiNotebook* book ) : wxPanel( book, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE ), 470 | randomizeKeyItemsCheckbox( nullptr ), 471 | randomizeShopsCheckbox( nullptr ), 472 | randomizeShopPricesCheckbox( nullptr ), 473 | randomizeFieldItemsCheckbox( nullptr ) 474 | { 475 | wxBoxSizer* sizer = new wxBoxSizer( wxVERTICAL ); 476 | 477 | randomizeKeyItemsCheckbox = new wxCheckBox( this, ID_ALLOW_RANDOMIZE_KEY_ITEMS, _T( "Include Key Items in Pool" ), wxDefaultPosition, wxDefaultSize, 0 ); 478 | randomizeKeyItemsCheckbox->SetToolTip( "If checked, key items will be included in the randomization pool. This might break progression, use it with caution." ); 479 | 480 | 481 | randomizeShopsCheckbox = new wxCheckBox( this, ID_RANDOMIZE_ITEM_SHOPS, _T( "Randomize Shops" ), wxDefaultPosition, wxDefaultSize, 0 ); 482 | randomizeShopsCheckbox->SetToolTip( "If checked, shops will be randomized. This includes:\n- The number of items offered\n- The items offered" ); 483 | ensureShopsSellSpheresCheckbox = new wxCheckBox( this, ID_ENSURE_SHOPS_SELL_SPHERES, _T( "Ensure Randomized Shops Sell Spheres" ), wxDefaultPosition, wxDefaultSize, 0 ); 484 | ensureShopsSellSpheresCheckbox->SetToolTip( "If checked, randomized shops will always sell the basic types of spheres. This includes:\n- Power Spheres \n- Mana Spheres \n- Agility Spheres \n- Ability Spheres " ); 485 | randomizeShopPricesCheckbox = new wxCheckBox( this, ID_RANDOMIZE_ITEM_SHOP_PRICES, _T( "Randomize Shop Prices" ), wxDefaultPosition, wxDefaultSize, 0 ); 486 | randomizeShopPricesCheckbox->SetToolTip( "If checked, shop prices will be randomized. This includes:\n- The prices of the items" ); 487 | randomizeFieldItemsCheckbox = new wxCheckBox( this, ID_RANDOMIZE_FIELD_ITEMS, _T( "Randomize Field Items" ), wxDefaultPosition, wxDefaultSize, 0 ); 488 | randomizeFieldItemsCheckbox->SetToolTip( "If checked, field items will be randomized. This includes:\n- Treasure Chests \n- Items gifted \n- Event Rewards" ); 489 | 490 | sizer->Add( randomizeKeyItemsCheckbox, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 491 | sizer->Add( randomizeShopsCheckbox, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 492 | sizer->Add( ensureShopsSellSpheresCheckbox, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 493 | sizer->Add( randomizeShopPricesCheckbox, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 494 | sizer->Add( randomizeFieldItemsCheckbox, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 495 | sizer->InsertSpacer( sizer->GetItemCount(), FromDIP( 5 ) ); 496 | SetSizer( sizer ); 497 | SetMinSize( GetBestVirtualSize() ); 498 | } 499 | }; 500 | 501 | struct player_stats_panel_t : public wxPanel 502 | { 503 | wxCheckBox* randomizeStartingOverdriveModeCheckbox; 504 | 505 | player_stats_panel_t( wxAuiNotebook* panel ) : wxPanel( panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE ), 506 | randomizeStartingOverdriveModeCheckbox( nullptr ) 507 | { 508 | wxBoxSizer* sizer = new wxBoxSizer( wxVERTICAL ); 509 | 510 | wxStaticText* new_game_text = new wxStaticText( this, wxID_ANY, _T( "These Options only affect new save files!" ), wxDefaultPosition, wxDefaultSize, 0 ); 511 | new_game_text->SetFont( wxFont( 12, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD ) ); 512 | 513 | wxRadioButton* RandomizePlayerStatsNoneRadioButton = new wxRadioButton( this, ID_RANDOMIZE_PLAYER_STATS_NONE, _T( "No Randomization" ), wxDefaultPosition, wxDefaultSize, wxRB_GROUP ); 514 | RandomizePlayerStatsNoneRadioButton->SetToolTip( "If checked, player stats will not be randomized." ); 515 | wxRadioButton* RandomizePlayerStatsRadioButton = new wxRadioButton( this, ID_RANDOMIZE_PLAYER_STATS, _T( "Randomize Player Stats" ), wxDefaultPosition, wxDefaultSize, 0 ); 516 | RandomizePlayerStatsRadioButton->SetToolTip( "If checked, player stats will be randomized using a normal distribution, centered on their original values. This keeps things close to vanilla, with some spice on occasion" ); 517 | wxRadioButton* ShufflePlayerStatsRadioButton = new wxRadioButton( this, ID_SHUFFLE_PLAYER_STATS, _T( "Shuffle Player Stats" ), wxDefaultPosition, wxDefaultSize, 0 ); 518 | ShufflePlayerStatsRadioButton->SetToolTip( "If checked, player stats will be shuffled to another party members stats at random." ); 519 | 520 | randomizeStartingOverdriveModeCheckbox = new wxCheckBox( this, ID_RANDOMIZE_STARTING_OVERDRIVE_MODE, _T( "Randomize Starting Overdrive Mode" ), wxDefaultPosition, wxDefaultSize, 0 ); 521 | randomizeStartingOverdriveModeCheckbox->SetToolTip( "If checked, the starting overdrive mode for playable characters will be random." ); 522 | 523 | wxStaticText* exclusive_text = new wxStaticText( this, wxID_ANY, _T( "Player Stat Options" ), wxDefaultPosition, wxDefaultSize, 0 ); 524 | exclusive_text->SetFont( wxFont( 10, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD ) ); 525 | 526 | 527 | sizer->Add( new_game_text, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 528 | sizer->Add( randomizeStartingOverdriveModeCheckbox, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 529 | sizer->InsertSpacer( sizer->GetItemCount(), FromDIP( 10 ) ); 530 | 531 | sizer->Add( exclusive_text, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 532 | sizer->Add( RandomizePlayerStatsNoneRadioButton, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 533 | sizer->Add( RandomizePlayerStatsRadioButton, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 534 | sizer->Add( ShufflePlayerStatsRadioButton, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 535 | sizer->InsertSpacer( sizer->GetItemCount(), FromDIP( 5 ) ); 536 | SetSizer( sizer ); 537 | SetMinSize( GetBestVirtualSize() ); 538 | } 539 | }; 540 | 541 | struct aeon_stats_panel_t : public wxPanel 542 | { 543 | wxCheckBox* randomizeAeonStatCustomizationItemsCheckbox; 544 | 545 | aeon_stats_panel_t( wxAuiNotebook* panel ) : wxPanel( panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE ), 546 | randomizeAeonStatCustomizationItemsCheckbox( nullptr ) 547 | { 548 | wxBoxSizer* sizer = new wxBoxSizer( wxVERTICAL ); 549 | 550 | wxStaticText* new_game_text = new wxStaticText( this, wxID_ANY, _T( "These Options only affect new save files!" ), wxDefaultPosition, wxDefaultSize, 0 ); 551 | new_game_text->SetFont( wxFont( 12, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD ) ); 552 | 553 | randomizeAeonStatCustomizationItemsCheckbox = new wxCheckBox( this, ID_RANDOMIZE_AEON_STAT_ITEMS, _T( "Randomize Customization Items" ), wxDefaultPosition, wxDefaultSize, 0 ); 554 | randomizeAeonStatCustomizationItemsCheckbox->SetToolTip( "If checked, items to increase aeon stats, and teach new abilities will be randomized. This includes:\n- The item required \n- The number of items required" ); 555 | 556 | wxRadioButton* randomizeAeonBaseStatsNoneRadioButton = new wxRadioButton( this, ID_RANDOMIZE_AEON_BASE_STATS_NONE, _T( "None" ), wxDefaultPosition, wxDefaultSize, wxRB_GROUP ); 557 | randomizeAeonBaseStatsNoneRadioButton->SetToolTip( "If checked, aeon base stats will not be randomized." ); 558 | wxRadioButton* randomizeAeonBaseStatsRadioButton = new wxRadioButton( this, ID_RANDOMIZE_AEON_BASE_STATS, _T( "Randomize Aeon Base Stats" ), wxDefaultPosition, wxDefaultSize, 0 ); 559 | randomizeAeonBaseStatsRadioButton->SetToolTip( "If checked, aeon base stats will be randomized using a normal distribution, centered on their original values. This keeps things close to vanilla, with some spice on occasion" ); 560 | wxRadioButton* shuffleAeonBaseStatsRadioButton = new wxRadioButton( this, ID_SHUFFLE_AEON_BASE_STATS, _T( "Shuffle Aeon Base Stats" ), wxDefaultPosition, wxDefaultSize, 0 ); 561 | shuffleAeonBaseStatsRadioButton->SetToolTip( "If checked, aeon base stats will be shuffled to another aeons stats at random." ); 562 | 563 | wxRadioButton* randomizeAeonStatScalingNoneRadioButton = new wxRadioButton( this, ID_RANDOMIZE_AEON_STAT_SCALING_NONE, _T( "None" ), wxDefaultPosition, wxDefaultSize, wxRB_GROUP ); 564 | randomizeAeonStatScalingNoneRadioButton->SetToolTip( "If checked, aeon stat scaling will not be randomized." ); 565 | wxRadioButton* randomizeAeonStatScalingRadioButton = new wxRadioButton( this, ID_RANDOMIZE_AEON_STAT_SCALING, _T( "Randomize Aeon Stat Scaling" ), wxDefaultPosition, wxDefaultSize, 0 ); 566 | randomizeAeonStatScalingRadioButton->SetToolTip( "If checked, aeon stat scaling (with Yunas stats) will be randomized using a normal distribution, centered on their original values. This keeps things close to vanilla, with some spice on occasion" ); 567 | wxRadioButton* shuffleAeonStatScalingRadioButton = new wxRadioButton( this, ID_SHUFFLE_AEON_STAT_SCALING, _T( "Shuffle Aeon Stat Scaling" ), wxDefaultPosition, wxDefaultSize, 0 ); 568 | shuffleAeonStatScalingRadioButton->SetToolTip( "If checked, aeon stat scaling (with Yunas stats) will be shuffled to another aeons stats at random." ); 569 | 570 | wxStaticText* scaling_text = new wxStaticText( this, wxID_ANY, _T( "Aeon Stat Scaling Options" ), wxDefaultPosition, wxDefaultSize, 0 ); 571 | scaling_text->SetFont( wxFont( 9, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD ) ); 572 | 573 | wxStaticText* stats = new wxStaticText( this, wxID_ANY, _T( "Aeon Base Stat Options" ), wxDefaultPosition, wxDefaultSize, 0 ); 574 | stats->SetFont( wxFont( 9, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD ) ); 575 | 576 | sizer->Add( new_game_text, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 577 | sizer->Add( randomizeAeonStatCustomizationItemsCheckbox, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 578 | 579 | sizer->InsertSpacer( sizer->GetItemCount(), FromDIP( 10 ) ); 580 | 581 | sizer->Add( stats, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 582 | sizer->Add( randomizeAeonBaseStatsNoneRadioButton, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 583 | sizer->Add( randomizeAeonBaseStatsRadioButton, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 584 | sizer->Add( shuffleAeonBaseStatsRadioButton, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 585 | 586 | sizer->InsertSpacer( sizer->GetItemCount(), FromDIP( 10 ) ); 587 | 588 | sizer->Add( scaling_text, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 589 | sizer->Add( randomizeAeonStatScalingNoneRadioButton, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 590 | sizer->Add( randomizeAeonStatScalingRadioButton, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 591 | sizer->Add( shuffleAeonStatScalingRadioButton, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 592 | 593 | sizer->InsertSpacer( sizer->GetItemCount(), FromDIP( 5 ) ); 594 | SetSizer( sizer ); 595 | SetMinSize( GetBestVirtualSize() ); 596 | } 597 | }; 598 | 599 | struct sphere_grid_panel_t : public wxPanel 600 | { 601 | wxCheckBox* removeSphereGridLocksCheckbox; 602 | 603 | sphere_grid_panel_t( wxAuiNotebook* panel ) : wxPanel( panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE ), 604 | removeSphereGridLocksCheckbox( nullptr ) 605 | { 606 | wxBoxSizer* sizer = new wxBoxSizer( wxVERTICAL ); 607 | 608 | wxStaticText* new_game_text = new wxStaticText( this, wxID_ANY, _T( "These Options only affect new save files!" ), wxDefaultPosition, wxDefaultSize, 0 ); 609 | new_game_text->SetFont( wxFont( 12, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD ) ); 610 | 611 | wxRadioButton* RandomizeSphereGridNoneRadioButton = new wxRadioButton( this, ID_SPHERE_GRID_NONE, _T( "No Randomization" ), wxDefaultPosition, wxDefaultSize, wxRB_GROUP ); 612 | RandomizeSphereGridNoneRadioButton->SetToolTip( "If checked, sphere grid will not be randomized." ); 613 | wxRadioButton* ShuffleSphereGridRadioButton = new wxRadioButton( this, ID_SHUFFLE_SPHERE_GRID, _T( "Shuffle Sphere Grid" ), wxDefaultPosition, wxDefaultSize, 0 ); 614 | ShuffleSphereGridRadioButton->SetToolTip( "If checked, sphere grid will be shuffled. This means that the nodes will be in a different order, but the same nodes will be present." ); 615 | wxRadioButton* RandomizeSphereGridRadioButton = new wxRadioButton( this, ID_RANDOMIZE_SPHERE_GRID_TRUE, _T( "Randomize Sphere Grid - True Randomization" ), wxDefaultPosition, wxDefaultSize, 0 ); 616 | RandomizeSphereGridRadioButton->SetToolTip( "If checked, sphere grid will be truly randomized. This means that there is no guarentee you will get all ability nodes, and there may be duplicate ability nodes." ); 617 | wxRadioButton* RandomizeSphereGridRadioButton2 = new wxRadioButton( this, ID_RANDOMIZE_SPHERE_GRID, _T( "Randomize Sphere Grid - Controlled Randomization" ), wxDefaultPosition, wxDefaultSize, 0 ); 618 | RandomizeSphereGridRadioButton2->SetToolTip( "If checked, every node in the sphere grid will be replaced by a random node, not constrained by what was present originally, but will be guarenteed to have 1 of every ability node." ); 619 | 620 | wxRadioButton* sphereNodesNoneRadioButton = new wxRadioButton( this, ID_SPHERE_NODES_NONE, _T( "None" ), wxDefaultPosition, wxDefaultSize, wxRB_GROUP ); 621 | sphereNodesNoneRadioButton->SetToolTip( "If checked, sphere nodes will not be scaled up or down" ); 622 | wxRadioButton* upgradeSphereNodesRadioButton = new wxRadioButton( this, ID_UPGRADE_SPHERE_NODES, _T( "Upgrade Sphere Nodes" ), wxDefaultPosition, wxDefaultSize, 0 ); 623 | upgradeSphereNodesRadioButton->SetToolTip( "If checked, sphere nodes will be upgraded to their maximum value." ); 624 | wxRadioButton* downgradeSphereNodesRadioButton = new wxRadioButton( this, ID_DOWNGRADE_SPHERE_NODES, _T( "Downgrade Sphere Nodes" ), wxDefaultPosition, wxDefaultSize, 0 ); 625 | downgradeSphereNodesRadioButton->SetToolTip( "If checked, sphere nodes will be downgraded to their minimum value." ); 626 | 627 | wxRadioButton* sphereGridNoneRadioButton = new wxRadioButton( this, ID_NONE_GRID, _T( "None" ), wxDefaultPosition, wxDefaultSize, wxRB_GROUP ); 628 | sphereGridNoneRadioButton->SetToolTip( "If checked, the sphere grid nodes will be the same as normal." ); 629 | wxRadioButton* emptySphereGridRadioButton = new wxRadioButton( this, ID_EMPTY_GRID, _T( "Empty Sphere Grid" ), wxDefaultPosition, wxDefaultSize, 0 ); 630 | emptySphereGridRadioButton->SetToolTip( "If checked, the sphere grid will be empty aside from Ability nodes. You have to find all the spheres yourself." ); 631 | wxRadioButton* fillSphereGridRadioButton = new wxRadioButton( this, ID_FULL_GRID, _T( "Full Sphere Grid" ), wxDefaultPosition, wxDefaultSize, 0 ); 632 | fillSphereGridRadioButton->SetToolTip( "If checked, the sphere grid will have no empty nodes. Any slot that would be empty will be filled with a random stat node instead." ); 633 | 634 | removeSphereGridLocksCheckbox = new wxCheckBox( this, ID_REMOVE_LOCKS, _T( "Remove Sphere Grid Locks" ), wxDefaultPosition, wxDefaultSize, 0 ); 635 | removeSphereGridLocksCheckbox->SetToolTip( "If checked, the sphere grid will have no key nodes, giving you freedom to go anywhere and get anything." ); 636 | 637 | wxStaticText* sphere_grid_text = new wxStaticText( this, wxID_ANY, _T( "Sphere Grid Randomization Options" ), wxDefaultPosition, wxDefaultSize, 0 ); 638 | sphere_grid_text->SetFont( wxFont( 9, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD ) ); 639 | 640 | wxStaticText* node_text = new wxStaticText( this, wxID_ANY, _T( "Sphere Grid Node Options" ), wxDefaultPosition, wxDefaultSize, 0 ); 641 | node_text->SetFont( wxFont( 9, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD ) ); 642 | 643 | wxStaticText* grid_text = new wxStaticText( this, wxID_ANY, _T( "Sphere Grid Modifier Options" ), wxDefaultPosition, wxDefaultSize, 0 ); 644 | grid_text->SetFont( wxFont( 9, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD ) ); 645 | 646 | sizer->Add( new_game_text, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 647 | sizer->Add( removeSphereGridLocksCheckbox, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 648 | sizer->InsertSpacer( sizer->GetItemCount(), FromDIP( 5 ) ); 649 | 650 | sizer->Add( sphere_grid_text, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 651 | sizer->Add( RandomizeSphereGridNoneRadioButton, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 652 | sizer->Add( ShuffleSphereGridRadioButton, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 653 | sizer->Add( RandomizeSphereGridRadioButton, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 654 | sizer->Add( RandomizeSphereGridRadioButton2, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 655 | 656 | sizer->InsertSpacer( sizer->GetItemCount(), FromDIP( 10 ) ); 657 | 658 | sizer->Add( node_text, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 659 | sizer->Add( sphereNodesNoneRadioButton, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 660 | sizer->Add( upgradeSphereNodesRadioButton, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 661 | sizer->Add( downgradeSphereNodesRadioButton, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 662 | 663 | sizer->InsertSpacer( sizer->GetItemCount(), FromDIP( 10 ) ); 664 | 665 | sizer->Add( grid_text, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 666 | sizer->Add( sphereGridNoneRadioButton, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 667 | sizer->Add( emptySphereGridRadioButton, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 668 | sizer->Add( fillSphereGridRadioButton, 0, wxALIGN_LEFT | wxLEFT | wxRIGHT | wxTOP, FromDIP( 5 ) ); 669 | 670 | sizer->InsertSpacer( sizer->GetItemCount(), FromDIP( 5 ) ); 671 | SetSizer( sizer ); 672 | SetMinSize( GetBestVirtualSize() ); 673 | } 674 | }; 675 | 676 | wxDECLARE_APP( gui_t ); --------------------------------------------------------------------------------