├── .gitattributes ├── .gitignore ├── MM2RandoHost ├── App.config ├── App.xaml ├── App.xaml.cs ├── Converters │ ├── BoolToRedGreenBrushConverter.cs │ ├── BoolToVisibilityConverter.cs │ └── InverseBooleanConverter.cs ├── MM2RandoHost.csproj ├── MainWindow.xaml ├── MainWindow.xaml.cs ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Settings.Designer.cs │ └── Settings.settings ├── Resources │ ├── Brushes │ │ └── BrushesDark.xaml │ ├── SpriteImages │ │ ├── sprite_bass.png │ │ ├── sprite_proto.png │ │ ├── sprite_rock.png │ │ └── sprite_roll.png │ ├── Styles │ │ └── MainStyles.xaml │ └── header.png ├── ViewModels │ └── MainWindowViewModel.cs └── Views │ └── Primitives │ └── CustomWindow.cs ├── MM2RandoLib ├── Enums │ ├── EColorsHex.cs │ ├── EDmgVsBoss.cs │ ├── EDmgVsEnemy.cs │ ├── EEnemyID.cs │ ├── EItemNumber.cs │ ├── EItemStageAddress.cs │ ├── EMiscAddresses.cs │ ├── EMusicID.cs │ ├── ERMPortraitAddress.cs │ ├── ERMPortraitText.cs │ ├── ERMStageClearAddress.cs │ ├── ERMStageSelect.cs │ ├── ERMStageWeaponAddress.cs │ ├── ERMWeaponValue.cs │ ├── ESoundID.cs │ └── EStageID.cs ├── MM2RandoLib.csproj ├── ObservableBase.cs ├── Patcher │ ├── ByteChangeRecord.cs │ └── Patch.cs ├── Properties │ ├── Resources.Designer.cs │ └── Resources.resx ├── RandoSettings.cs ├── RandomMM2.cs ├── Randomizers │ ├── Colors │ │ ├── ColorSet.cs │ │ └── RColors.cs │ ├── Enemies │ │ ├── EnemyInstance.cs │ │ ├── EnemyType.cs │ │ ├── REnemies.cs │ │ ├── REnemyWeaknesses.cs │ │ ├── Room.cs │ │ └── SpriteBankRoomGroup.cs │ ├── IRandomizer.cs │ ├── RBossAI.cs │ ├── RBossRoom.cs │ ├── RItemGet.cs │ ├── RMusic.cs │ ├── RTeleporters.cs │ ├── RText.cs │ ├── RTilemap.cs │ ├── RWeaknesses.cs │ ├── RWeaponBehavior.cs │ ├── RWeaponGet.cs │ └── Stages │ │ ├── Components │ │ ├── EnemyComponent.cs │ │ └── LevelComponent.cs │ │ ├── RStages.cs │ │ └── StageFromSelect.cs ├── Resources │ ├── SpritePatches │ │ ├── SpriteSwap_Bass.ips │ │ ├── SpriteSwap_Proto.ips │ │ └── SpriteSwap_Roll.ips │ ├── bossnames.csv │ ├── companynames.csv │ ├── countrylist.csv │ ├── creditstext.csv │ ├── enemylist.csv │ ├── enemyweakness.csv │ ├── level_components.json │ ├── mm2rng_musicpatch.ips │ ├── mm2rng_prepatch.ips │ └── music.txt ├── Utilities │ ├── MiscHacks.cs │ └── SeedConvert.cs ├── WeaponTable.cs └── packages.config ├── MM2Randomizer.sln └── readme.txt /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | build/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | 28 | # MSTest test Results 29 | [Tt]est[Rr]esult*/ 30 | [Bb]uild[Ll]og.* 31 | 32 | # NUNIT 33 | *.VisualState.xml 34 | TestResult.xml 35 | 36 | # Build Results of an ATL Project 37 | [Dd]ebugPS/ 38 | [Rr]eleasePS/ 39 | dlldata.c 40 | 41 | # DNX 42 | project.lock.json 43 | artifacts/ 44 | 45 | *_i.c 46 | *_p.c 47 | *_i.h 48 | *.ilk 49 | *.meta 50 | *.obj 51 | *.pch 52 | *.pdb 53 | *.pgc 54 | *.pgd 55 | *.rsp 56 | *.sbr 57 | *.tlb 58 | *.tli 59 | *.tlh 60 | *.tmp 61 | *.tmp_proj 62 | *.log 63 | *.vspscc 64 | *.vssscc 65 | .builds 66 | *.pidb 67 | *.svclog 68 | *.scc 69 | 70 | # Chutzpah Test files 71 | _Chutzpah* 72 | 73 | # Visual C++ cache files 74 | ipch/ 75 | *.aps 76 | *.ncb 77 | *.opensdf 78 | *.sdf 79 | *.cachefile 80 | 81 | # Visual Studio profiler 82 | *.psess 83 | *.vsp 84 | *.vspx 85 | 86 | # TFS 2012 Local Workspace 87 | $tf/ 88 | 89 | # Guidance Automation Toolkit 90 | *.gpState 91 | 92 | # ReSharper is a .NET coding add-in 93 | _ReSharper*/ 94 | *.[Rr]e[Ss]harper 95 | *.DotSettings.user 96 | 97 | # JustCode is a .NET coding add-in 98 | .JustCode 99 | 100 | # TeamCity is a build add-in 101 | _TeamCity* 102 | 103 | # DotCover is a Code Coverage Tool 104 | *.dotCover 105 | 106 | # NCrunch 107 | _NCrunch_* 108 | .*crunch*.local.xml 109 | 110 | # MightyMoose 111 | *.mm.* 112 | AutoTest.Net/ 113 | 114 | # Web workbench (sass) 115 | .sass-cache/ 116 | 117 | # Installshield output folder 118 | [Ee]xpress/ 119 | 120 | # DocProject is a documentation generator add-in 121 | DocProject/buildhelp/ 122 | DocProject/Help/*.HxT 123 | DocProject/Help/*.HxC 124 | DocProject/Help/*.hhc 125 | DocProject/Help/*.hhk 126 | DocProject/Help/*.hhp 127 | DocProject/Help/Html2 128 | DocProject/Help/html 129 | 130 | # Click-Once directory 131 | publish/ 132 | 133 | # Publish Web Output 134 | *.[Pp]ublish.xml 135 | *.azurePubxml 136 | ## TODO: Comment the next line if you want to checkin your 137 | ## web deploy settings but do note that will include unencrypted 138 | ## passwords 139 | #*.pubxml 140 | 141 | *.publishproj 142 | 143 | # NuGet Packages 144 | *.nupkg 145 | # The packages folder can be ignored because of Package Restore 146 | **/packages/* 147 | # except build/, which is used as an MSBuild target. 148 | !**/packages/build/ 149 | # Uncomment if necessary however generally it will be regenerated when needed 150 | #!**/packages/repositories.config 151 | 152 | # Windows Azure Build Output 153 | csx/ 154 | *.build.csdef 155 | 156 | # Windows Store app package directory 157 | AppPackages/ 158 | 159 | # Visual Studio cache files 160 | # files ending in .cache can be ignored 161 | *.[Cc]ache 162 | # but keep track of directories ending in .cache 163 | !*.[Cc]ache/ 164 | 165 | # Others 166 | ClientBin/ 167 | [Ss]tyle[Cc]op.* 168 | ~$* 169 | *~ 170 | *.dbmdl 171 | *.dbproj.schemaview 172 | *.pfx 173 | *.publishsettings 174 | node_modules/ 175 | orleans.codegen.cs 176 | 177 | # RIA/Silverlight projects 178 | Generated_Code/ 179 | 180 | # Backup & report files from converting an old project file 181 | # to a newer Visual Studio version. Backup files are not needed, 182 | # because we have git ;-) 183 | _UpgradeReport_Files/ 184 | Backup*/ 185 | UpgradeLog*.XML 186 | UpgradeLog*.htm 187 | 188 | # SQL Server files 189 | *.mdf 190 | *.ldf 191 | 192 | # Business Intelligence projects 193 | *.rdl.data 194 | *.bim.layout 195 | *.bim_*.settings 196 | 197 | # Microsoft Fakes 198 | FakesAssemblies/ 199 | 200 | # Node.js Tools for Visual Studio 201 | .ntvs_analysis.dat 202 | 203 | # Visual Studio 6 build log 204 | *.plg 205 | 206 | # Visual Studio 6 workspace options file 207 | *.opt 208 | 209 | # LightSwitch generated files 210 | GeneratedArtifacts/ 211 | _Pvt_Extensions/ 212 | ModelManifest.xml 213 | /MM2Randomizer/u_backup.dat 214 | -------------------------------------------------------------------------------- /MM2RandoHost/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /MM2RandoHost/App.xaml: -------------------------------------------------------------------------------- 1 |  6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /MM2RandoHost/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | 3 | namespace MM2RandoHost 4 | { 5 | /// 6 | /// Interaction logic for App.xaml 7 | /// 8 | public partial class App : Application 9 | { 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /MM2RandoHost/Converters/BoolToRedGreenBrushConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | using System.Windows.Data; 9 | using System.Windows.Media; 10 | 11 | namespace MM2RandoHost.Converters 12 | { 13 | public class BoolToRedGreenBrushConverter : IValueConverter 14 | { 15 | private static readonly Brush red = new SolidColorBrush(Colors.Red); 16 | private static readonly Brush green = new SolidColorBrush(Color.FromArgb(255, 125, 255, 128)); 17 | 18 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 19 | { 20 | if (!(value is bool myBool)) 21 | { 22 | return red; 23 | } 24 | 25 | return myBool ? green : red; 26 | } 27 | 28 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 29 | { 30 | return false; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /MM2RandoHost/Converters/BoolToVisibilityConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | using System.Windows.Data; 9 | 10 | namespace MM2RandoHost.Converters 11 | { 12 | public class BoolToVisibilityConverter : IValueConverter 13 | { 14 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 15 | { 16 | return (bool)value ? Visibility.Visible : Visibility.Hidden; 17 | } 18 | 19 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 20 | { 21 | throw new NotImplementedException(); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /MM2RandoHost/Converters/InverseBooleanConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Data; 3 | 4 | namespace MM2RandoHost.Converters 5 | { 6 | [ValueConversion(typeof(bool), typeof(bool))] 7 | public class InverseBooleanConverter : IValueConverter 8 | { 9 | #region IValueConverter Members 10 | 11 | public object Convert(object value, Type targetType, object parameter, 12 | System.Globalization.CultureInfo culture) 13 | { 14 | if (targetType != typeof(bool)) 15 | throw new InvalidOperationException("The target must be a boolean"); 16 | 17 | return !(bool)value; 18 | } 19 | 20 | public object ConvertBack(object value, Type targetType, object parameter, 21 | System.Globalization.CultureInfo culture) 22 | { 23 | throw new NotSupportedException(); 24 | } 25 | 26 | #endregion 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /MM2RandoHost/MM2RandoHost.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {F12A49E8-C7A7-4FDE-BB7C-EA80451D5B96} 8 | WinExe 9 | Properties 10 | MM2RandoHost 11 | MM2RandoHost 12 | v4.7.2 13 | 512 14 | {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 15 | 4 16 | true 17 | 18 | false 19 | publish\ 20 | true 21 | Disk 22 | false 23 | Foreground 24 | 7 25 | Days 26 | false 27 | false 28 | true 29 | 0 30 | 0.1.2.%2a 31 | false 32 | true 33 | 34 | 35 | AnyCPU 36 | true 37 | full 38 | false 39 | bin\ 40 | TRACE;DEBUG 41 | prompt 42 | 4 43 | false 44 | 45 | 46 | AnyCPU 47 | pdbonly 48 | true 49 | bin\ 50 | TRACE 51 | prompt 52 | 4 53 | false 54 | 55 | 56 | true 57 | bin\x64\Debug\ 58 | DEBUG;TRACE 59 | full 60 | x64 61 | prompt 62 | MinimumRecommendedRules.ruleset 63 | false 64 | 65 | 66 | bin\x64\Release\ 67 | TRACE 68 | true 69 | pdbonly 70 | x64 71 | prompt 72 | MinimumRecommendedRules.ruleset 73 | false 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 4.0 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | MSBuild:Compile 91 | Designer 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | MSBuild:Compile 100 | Designer 101 | 102 | 103 | App.xaml 104 | Code 105 | 106 | 107 | MainWindow.xaml 108 | Code 109 | 110 | 111 | Designer 112 | MSBuild:Compile 113 | 114 | 115 | Designer 116 | MSBuild:Compile 117 | 118 | 119 | 120 | 121 | Code 122 | 123 | 124 | True 125 | True 126 | Resources.resx 127 | 128 | 129 | True 130 | Settings.settings 131 | True 132 | 133 | 134 | ResXFileCodeGenerator 135 | Resources.Designer.cs 136 | 137 | 138 | SettingsSingleFileGenerator 139 | Settings.Designer.cs 140 | 141 | 142 | 143 | 144 | 145 | Designer 146 | 147 | 148 | 149 | 150 | False 151 | Microsoft .NET Framework 4 %28x86 and x64%29 152 | true 153 | 154 | 155 | False 156 | .NET Framework 3.5 SP1 157 | false 158 | 159 | 160 | False 161 | Windows Installer 4.5 162 | true 163 | 164 | 165 | 166 | 167 | {2f96fe8d-29dc-46f3-9d26-fc85dc2225c8} 168 | MM2RandoLib 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 194 | -------------------------------------------------------------------------------- /MM2RandoHost/MainWindow.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows; 3 | using System.Diagnostics; 4 | using System.IO; 5 | using System.Windows.Media; 6 | using System.Windows.Input; 7 | using Microsoft.Win32; 8 | 9 | using MM2Randomizer; 10 | using MM2Randomizer.Utilities; 11 | 12 | using MM2RandoHost.ViewModels; 13 | using MM2RandoHost.Views.Primitives; 14 | 15 | namespace MM2RandoHost 16 | { 17 | /// 18 | /// Interaction logic for MainWindow.xaml 19 | /// 20 | public partial class MainWindow : CustomWindow 21 | { 22 | // TODO Remove 23 | MainWindowViewModel ViewModel; 24 | 25 | public MainWindow() 26 | { 27 | InitializeComponent(); 28 | 29 | // TODO Remove 30 | ViewModel = DataContext as MainWindowViewModel; 31 | } 32 | 33 | private void btnCreateROM_Click(object sender, RoutedEventArgs e) 34 | { 35 | int seed = -1; 36 | 37 | // Check if textbox contains a valid seed string 38 | if (!String.IsNullOrEmpty(tbxSeed.Text)) 39 | { 40 | try 41 | { 42 | // Use the provided seed so that a specific ROM may be generated. 43 | seed = SeedConvert.ConvertBase26To10(tbxSeed.Text); 44 | } 45 | catch (Exception ex) 46 | { 47 | Debug.WriteLine("Exception in parsing Seed. Using random seed. Message:/n" + ex.ToString()); 48 | seed = -1; 49 | } 50 | } 51 | 52 | // Perform randomization based on settings, then generate the ROM. 53 | ViewModel.PerformRandomization(seed, Keyboard.IsKeyDown(Key.LeftShift)); 54 | } 55 | 56 | private void btnCreateRandom_Click(object sender, RoutedEventArgs e) 57 | { 58 | ViewModel.PerformRandomization(-1, Keyboard.IsKeyDown(Key.LeftShift)); 59 | } 60 | 61 | private void btnOpenFolder_Click(object sender, RoutedEventArgs e) 62 | { 63 | if (RandomMM2.RecentlyCreatedFileName != "") 64 | { 65 | try 66 | { 67 | Process.Start("explorer.exe", string.Format("/select,\"{0}\"", RandomMM2.RecentlyCreatedFileName)); 68 | } 69 | catch (Exception ex) 70 | { 71 | Debug.WriteLine(ex.ToString()); 72 | Process.Start("explorer.exe", string.Format("/select,\"{0}\"", System.Reflection.Assembly.GetExecutingAssembly().Location)); 73 | } 74 | } 75 | else 76 | { 77 | Process.Start("explorer.exe", string.Format("/select,\"{0}\"", System.Reflection.Assembly.GetExecutingAssembly().Location)); 78 | } 79 | } 80 | 81 | private void btnBrowse_Click(object sender, RoutedEventArgs e) 82 | { 83 | OpenFileDialog dlg = new OpenFileDialog(); 84 | dlg.Filter = "ROM image (.nes)|*.nes"; 85 | dlg.Title = "Open Mega Man 2 (U) NES ROM File"; 86 | 87 | // Call the ShowDialog method to show the dialog box. 88 | string exePath = System.Reflection.Assembly.GetExecutingAssembly().Location; 89 | string exeDir = Path.GetDirectoryName(exePath); 90 | dlg.InitialDirectory = exeDir; 91 | 92 | bool? userClickedOK = dlg.ShowDialog(); 93 | 94 | // Process input if the user clicked OK. 95 | if (userClickedOK == true) 96 | { 97 | TryFile(dlg.FileName); 98 | SetTextBoxFocusToEnd(); 99 | } 100 | } 101 | 102 | private void tbxSource_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e) 103 | { 104 | if (ViewModel != null) 105 | TryFile(ViewModel.RandoSettings.SourcePath); 106 | } 107 | 108 | private void Window_Drop(object sender, DragEventArgs e) 109 | { 110 | if (e.Data.GetDataPresent(DataFormats.FileDrop)) 111 | { 112 | string[] files = (string[])e.Data.GetData(DataFormats.FileDrop); 113 | TryFile(files[0]); 114 | SetTextBoxFocusToEnd(); 115 | } 116 | BorderShowHandler(false, e); 117 | } 118 | 119 | private void TextBox_Drop(object sender, DragEventArgs e) 120 | { 121 | if (e.Data.GetDataPresent(DataFormats.FileDrop)) 122 | { 123 | string[] files = (string[])e.Data.GetData(DataFormats.FileDrop); 124 | TryFile(files[0]); 125 | SetTextBoxFocusToEnd(); 126 | } 127 | BorderShowHandler(false, e); 128 | } 129 | 130 | private void Window_DragEnter(object sender, DragEventArgs e) 131 | { 132 | BorderShowHandler(true, e); 133 | } 134 | 135 | private void Window_DragLeave(object sender, DragEventArgs e) 136 | { 137 | BorderShowHandler(false, e); 138 | } 139 | 140 | private void TextBox_PreviewDragOver(object sender, DragEventArgs e) 141 | { 142 | e.Handled = true; 143 | BorderShowHandler(true, e); 144 | } 145 | 146 | private void BorderShowHandler(bool show, DragEventArgs e) 147 | { 148 | if (!show) 149 | { 150 | border.BorderBrush = new SolidColorBrush(Colors.Transparent); 151 | return; 152 | } 153 | 154 | if (e.Data.GetDataPresent(DataFormats.FileDrop)) 155 | { 156 | border.BorderBrush = new SolidColorBrush(Colors.Red); 157 | } 158 | else 159 | { 160 | new SolidColorBrush(Colors.Transparent); 161 | } 162 | } 163 | 164 | private void SetTextBoxFocusToEnd() 165 | { 166 | tbxSource.Focus(); 167 | tbxSource.SelectionStart = tbxSource.Text.Length; 168 | } 169 | 170 | private void TryFile(string filename) 171 | { 172 | ViewModel.IsShowingHint = false; 173 | ViewModel.RandoSettings.ValidateFile(filename); 174 | } 175 | 176 | private void chkBurstChaser_Checked(object sender, RoutedEventArgs e) 177 | { 178 | MessageBox.Show("Caution: The randomizer is not balanced for Burst Chaser mode.\nIf it is your first time playing, consider using the default settings."); 179 | } 180 | 181 | private void CustonWindow_MouseDown(object sender, MouseButtonEventArgs e) 182 | { 183 | if (e.ChangedButton == System.Windows.Input.MouseButton.Left) 184 | this.DragMove(); 185 | } 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /MM2RandoHost/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Resources; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using System.Windows; 6 | 7 | // General Information about an assembly is controlled through the following 8 | // set of attributes. Change these attribute values to modify the information 9 | // associated with an assembly. 10 | [assembly: AssemblyTitle("MM2RandoHost")] 11 | [assembly: AssemblyDescription("")] 12 | [assembly: AssemblyConfiguration("")] 13 | [assembly: AssemblyCompany("duckfist")] 14 | [assembly: AssemblyProduct("MM2RandoHost")] 15 | [assembly: AssemblyCopyright("duckfist")] 16 | [assembly: AssemblyTrademark("")] 17 | [assembly: AssemblyCulture("")] 18 | 19 | // Setting ComVisible to false makes the types in this assembly not visible 20 | // to COM components. If you need to access a type in this assembly from 21 | // COM, set the ComVisible attribute to true on that type. 22 | [assembly: ComVisible(false)] 23 | 24 | //In order to begin building localizable applications, set 25 | //CultureYouAreCodingWith in your .csproj file 26 | //inside a . For example, if you are using US english 27 | //in your source files, set the to en-US. Then uncomment 28 | //the NeutralResourceLanguage attribute below. Update the "en-US" in 29 | //the line below to match the UICulture setting in the project file. 30 | 31 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] 32 | 33 | 34 | [assembly: ThemeInfo( 35 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located 36 | //(used if a resource is not found in the page, 37 | // or application resource dictionaries) 38 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located 39 | //(used if a resource is not found in the page, 40 | // app, or any theme specific resource dictionaries) 41 | )] 42 | 43 | 44 | // Version information for an assembly consists of the following four values: 45 | // 46 | // Major Version 47 | // Minor Version 48 | // Build Number 49 | // Revision 50 | // 51 | // You can specify all the values or you can default the Build and Revision Numbers 52 | // by using the '*' as shown below: 53 | // [assembly: AssemblyVersion("1.0.*")] 54 | [assembly: AssemblyVersion("0.5.0.*")] 55 | [assembly: AssemblyFileVersion("0.5.0.0")] 56 | -------------------------------------------------------------------------------- /MM2RandoHost/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace MM2RandoHost.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MM2RandoHost.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | 63 | /// 64 | /// Looks up a localized resource of type System.Drawing.Bitmap. 65 | /// 66 | internal static System.Drawing.Bitmap header { 67 | get { 68 | object obj = ResourceManager.GetObject("header", resourceCulture); 69 | return ((System.Drawing.Bitmap)(obj)); 70 | } 71 | } 72 | 73 | /// 74 | /// Looks up a localized resource of type System.Drawing.Bitmap. 75 | /// 76 | internal static System.Drawing.Bitmap sprite_bass { 77 | get { 78 | object obj = ResourceManager.GetObject("sprite_bass", resourceCulture); 79 | return ((System.Drawing.Bitmap)(obj)); 80 | } 81 | } 82 | 83 | /// 84 | /// Looks up a localized resource of type System.Drawing.Bitmap. 85 | /// 86 | internal static System.Drawing.Bitmap sprite_proto { 87 | get { 88 | object obj = ResourceManager.GetObject("sprite_proto", resourceCulture); 89 | return ((System.Drawing.Bitmap)(obj)); 90 | } 91 | } 92 | 93 | /// 94 | /// Looks up a localized resource of type System.Drawing.Bitmap. 95 | /// 96 | internal static System.Drawing.Bitmap sprite_rock { 97 | get { 98 | object obj = ResourceManager.GetObject("sprite_rock", resourceCulture); 99 | return ((System.Drawing.Bitmap)(obj)); 100 | } 101 | } 102 | 103 | /// 104 | /// Looks up a localized resource of type System.Drawing.Bitmap. 105 | /// 106 | internal static System.Drawing.Bitmap sprite_roll { 107 | get { 108 | object obj = ResourceManager.GetObject("sprite_roll", resourceCulture); 109 | return ((System.Drawing.Bitmap)(obj)); 110 | } 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /MM2RandoHost/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | 122 | ..\Resources\header.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 123 | 124 | 125 | ..\Resources\SpriteImages\sprite_bass.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 126 | 127 | 128 | ..\Resources\SpriteImages\sprite_proto.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 129 | 130 | 131 | ..\Resources\SpriteImages\sprite_rock.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 132 | 133 | 134 | ..\Resources\SpriteImages\sprite_roll.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 135 | 136 | -------------------------------------------------------------------------------- /MM2RandoHost/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace MM2RandoHost.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.0.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /MM2RandoHost/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /MM2RandoHost/Resources/Brushes/BrushesDark.xaml: -------------------------------------------------------------------------------- 1 |  4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | #FFE8EDF9 36 | #FFC5CBF9 37 | 38 | -------------------------------------------------------------------------------- /MM2RandoHost/Resources/SpriteImages/sprite_bass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duckfist/MM2Random/178a4cb7649b194782b94caf9b54379e52914c0d/MM2RandoHost/Resources/SpriteImages/sprite_bass.png -------------------------------------------------------------------------------- /MM2RandoHost/Resources/SpriteImages/sprite_proto.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duckfist/MM2Random/178a4cb7649b194782b94caf9b54379e52914c0d/MM2RandoHost/Resources/SpriteImages/sprite_proto.png -------------------------------------------------------------------------------- /MM2RandoHost/Resources/SpriteImages/sprite_rock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duckfist/MM2Random/178a4cb7649b194782b94caf9b54379e52914c0d/MM2RandoHost/Resources/SpriteImages/sprite_rock.png -------------------------------------------------------------------------------- /MM2RandoHost/Resources/SpriteImages/sprite_roll.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duckfist/MM2Random/178a4cb7649b194782b94caf9b54379e52914c0d/MM2RandoHost/Resources/SpriteImages/sprite_roll.png -------------------------------------------------------------------------------- /MM2RandoHost/Resources/header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duckfist/MM2Random/178a4cb7649b194782b94caf9b54379e52914c0d/MM2RandoHost/Resources/header.png -------------------------------------------------------------------------------- /MM2RandoHost/ViewModels/MainWindowViewModel.cs: -------------------------------------------------------------------------------- 1 | using MM2Randomizer; 2 | using MM2Randomizer.Utilities; 3 | 4 | using System.IO; 5 | using System.Reflection; 6 | using System.Diagnostics; 7 | 8 | namespace MM2RandoHost.ViewModels 9 | { 10 | public class MainWindowViewModel : ObservableBase 11 | { 12 | private RandoSettings _randoSettings; 13 | private bool _isShowingHint = true; 14 | private bool _hasGeneratedAROM = false; 15 | 16 | public MainWindowViewModel() 17 | { 18 | RandoSettings = new RandoSettings(); 19 | RandomMM2.Settings = RandoSettings; 20 | 21 | // Try to load "MM2.nes" if one is in the local directory already to save time 22 | string tryLocalpath = Path.Combine( 23 | Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), 24 | "MM2.nes"); 25 | 26 | if (File.Exists(tryLocalpath)) 27 | { 28 | RandoSettings.ValidateFile(tryLocalpath); 29 | IsShowingHint = false; 30 | } 31 | } 32 | 33 | public RandoSettings RandoSettings 34 | { 35 | get => _randoSettings; 36 | set => SetProperty(ref _randoSettings, value); 37 | } 38 | 39 | public bool IsShowingHint 40 | { 41 | get => _isShowingHint; 42 | set => SetProperty(ref _isShowingHint, value); 43 | } 44 | 45 | public bool HasGeneratedAROM 46 | { 47 | get => _hasGeneratedAROM; 48 | set => SetProperty(ref _hasGeneratedAROM, value); 49 | } 50 | 51 | public bool IsCoreModulesChecked 52 | { 53 | get => RandoSettings.Is8StagesRandom && 54 | RandoSettings.IsWeaponsRandom && 55 | RandoSettings.IsTeleportersRandom; 56 | } 57 | 58 | public void PerformRandomization(int seed, bool tryCreateLogFile) 59 | { 60 | // Perform randomization based on settings, then generate the ROM. 61 | RandomMM2.RandomizerCreate(true, seed); 62 | 63 | // Get A-Z representation of seed 64 | string seedAlpha = SeedConvert.ConvertBase10To26(RandomMM2.Seed); 65 | RandoSettings.SeedString = seedAlpha; 66 | Debug.WriteLine("\nSeed: " + seedAlpha + "\n"); 67 | 68 | // Create log file if left shift is pressed while clicking 69 | if (tryCreateLogFile && !RandoSettings.IsSpoilerFree) 70 | { 71 | string logFileName = $"MM2RNG-{seedAlpha}.log"; 72 | using (StreamWriter sw = new StreamWriter(logFileName, false)) 73 | { 74 | sw.WriteLine("Mega Man 2 Randomizer"); 75 | sw.WriteLine($"Version {RandoSettings.AssemblyVersion.ToString()}"); 76 | sw.WriteLine($"Seed {seedAlpha}\n"); 77 | sw.WriteLine(RandomMM2.randomStages.ToString()); 78 | sw.WriteLine(RandomMM2.randomWeaponBehavior.ToString()); 79 | sw.WriteLine(RandomMM2.randomEnemyWeakness.ToString()); 80 | sw.WriteLine(RandomMM2.randomWeaknesses.ToString()); 81 | sw.Write(RandomMM2.Patch.GetStringSortedByAddress()); 82 | } 83 | } 84 | 85 | // Flag UI as having created a ROM, enabling the "open folder" button 86 | HasGeneratedAROM = true; 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /MM2RandoHost/Views/Primitives/CustomWindow.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | using System.Windows.Controls; 8 | using System.Windows.Input; 9 | 10 | namespace MM2RandoHost.Views.Primitives 11 | { 12 | public class CustomWindow : Window 13 | { 14 | public Action OnMaximizeOrRestore { get; set; } 15 | 16 | protected void TitleBarClick(object sender, MouseButtonEventArgs e) 17 | { 18 | if (e.ClickCount == 2) 19 | { 20 | if (WindowState == WindowState.Normal) 21 | WindowMaximize(); 22 | else 23 | WindowRestore(); 24 | } 25 | } 26 | 27 | protected void MinimizeClick(object sender, RoutedEventArgs e) 28 | { 29 | WindowState = WindowState.Minimized; 30 | } 31 | 32 | protected void RestoreClick(object sender, RoutedEventArgs e) 33 | { 34 | if (WindowState == WindowState.Normal) 35 | WindowMaximize(); 36 | else 37 | WindowRestore(); 38 | } 39 | 40 | protected void CloseClick(object sender, RoutedEventArgs e) 41 | { 42 | Close(); 43 | } 44 | 45 | public override void OnApplyTemplate() 46 | { 47 | System.Windows.Shapes.Rectangle hitArea = GetTemplateChild("windowTitleBarClickRegion") as System.Windows.Shapes.Rectangle; 48 | if (hitArea != null) 49 | hitArea.MouseLeftButtonDown += TitleBarClick; 50 | 51 | Button restoreButton = GetTemplateChild("restoreButton") as Button; 52 | if (restoreButton != null) 53 | restoreButton.Click += MinimizeClick; 54 | 55 | Button closeButton = GetTemplateChild("closeButton") as Button; 56 | if (closeButton != null) 57 | closeButton.Click += CloseClick; 58 | 59 | base.OnApplyTemplate(); 60 | } 61 | 62 | public void WindowMaximize() 63 | { 64 | WindowState = WindowState.Maximized; 65 | if (OnMaximizeOrRestore != null) OnMaximizeOrRestore.Invoke(); 66 | } 67 | public void WindowRestore() 68 | { 69 | WindowState = WindowState.Normal; 70 | if (OnMaximizeOrRestore != null) OnMaximizeOrRestore.Invoke(); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /MM2RandoLib/Enums/EColorsHex.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace MM2Randomizer.Enums 7 | { 8 | public enum EColorsHex 9 | { 10 | //Attempted to translate colors to HTML like colors. This is off because nothing matches exactly. 11 | 12 | ///Section 0x0? 13 | Gray = 0x00, 14 | RoyalBlue = 0x01, 15 | DarkBlue= 0x02, 16 | RoyalPurple = 0x03, 17 | DarkMagenta = 0x04, 18 | Crimson= 0x05, 19 | Red = 0x06, 20 | DarkRed= 0x07, 21 | Brown = 0x08, 22 | Kelp= 0x09, 23 | ForestGreen= 0x0A, 24 | DarkGreen= 0x0B, 25 | DarkTeal= 0x0C, 26 | Black = 0x0D, 27 | Black1 = 0x0E, 28 | Black2 = 0x0F, 29 | 30 | //Section 0x1? 31 | LightGray= 0x10, 32 | MediumBlue= 0x11, 33 | Blue= 0x12, 34 | Purple= 0x13, 35 | Magenta= 0x14, 36 | VioletRed= 0x15, 37 | Orange= 0x16, 38 | Tangerine= 0x17, 39 | GoldenRod= 0x18, 40 | Grass= 0x19, 41 | Green= 0x1A, 42 | Moss= 0x1B, 43 | Teal= 0x1C, 44 | Black3= 0x1D, 45 | Black4= 0x1E, 46 | Black5= 0x1F, 47 | 48 | //Section 0x2? 49 | NearWhite= 0x20, 50 | LightBlue= 0x21, 51 | SoftBlue = 0x22, 52 | LightPurple= 0x23, 53 | LightPink= 0x24, 54 | LightVioletRed= 0x25, 55 | LightOrange= 0x26, 56 | YellowOrange= 0x27, 57 | Yellow= 0x28, 58 | Lemon= 0x29, 59 | LightGreen= 0x2A, 60 | Lime= 0x2B, 61 | LightCyan= 0x2C, 62 | MediumGray= 0x2D, 63 | Black6= 0x2E, 64 | Black7= 0x2F, 65 | 66 | //Section 0x3? 67 | White= 0x30, 68 | PastelBlue= 0x31, 69 | PaleBlue= 0x32, 70 | PastelPurple= 0x33, 71 | PastelPink= 0x34, 72 | PastelVioletRed= 0x35, 73 | Taupe= 0x36, 74 | Beige= 0x37, 75 | PastelYellow= 0x38, 76 | PastelLemon= 0x39, 77 | PastelGreen= 0x3A, 78 | PastelCyan= 0x3B, 79 | Cyan= 0x3C, 80 | BabyPink= 0x3D, 81 | Black8= 0x3E, 82 | Black9= 0x3F 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /MM2RandoLib/Enums/EDmgVsBoss.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace MM2Randomizer.Enums 4 | { 5 | public sealed class EDmgVsBoss 6 | { 7 | public string WeaponName 8 | { 9 | get; private set; 10 | } 11 | 12 | public int Address 13 | { 14 | get; private set; 15 | } 16 | 17 | public int Index 18 | { 19 | get; private set; 20 | } 21 | 22 | public static Dictionary Addresses; 23 | 24 | //Japanese 25 | public static readonly EDmgVsBoss Buster = new EDmgVsBoss(0, 0x02E933, "Buster"); 26 | public static readonly EDmgVsBoss AtomicFire = new EDmgVsBoss(1, 0x02E941, "Atomic Fire"); 27 | public static readonly EDmgVsBoss AirShooter = new EDmgVsBoss(2, 0x02E94F, "Air Shooter"); 28 | public static readonly EDmgVsBoss LeafShield = new EDmgVsBoss(3, 0x02E95D, "Leaf Shield"); 29 | public static readonly EDmgVsBoss BubbleLead = new EDmgVsBoss(4, 0x02E96B, "Bubble Lead"); 30 | public static readonly EDmgVsBoss QuickBoomerang = new EDmgVsBoss(5, 0x02E979, "Quick Boomerang"); 31 | public static readonly EDmgVsBoss TimeStopper = new EDmgVsBoss(6, 0x02C049, "Time Stopper"); 32 | public static readonly EDmgVsBoss MetalBlade = new EDmgVsBoss(7, 0x02E995, "Metal Blade"); 33 | public static readonly EDmgVsBoss ClashBomber = new EDmgVsBoss(8, 0x02E987, "Clash Bomber"); 34 | 35 | //English 36 | public static readonly EDmgVsBoss U_DamageP = new EDmgVsBoss(0, 0x2e952, "Buster"); 37 | public static readonly EDmgVsBoss U_DamageH = new EDmgVsBoss(1, 0x2e960, "Atomic Fire"); 38 | public static readonly EDmgVsBoss U_DamageA = new EDmgVsBoss(2, 0x2e96e, "Air Shooter"); 39 | public static readonly EDmgVsBoss U_DamageW = new EDmgVsBoss(3, 0x2e97c, "Leaf Shield"); 40 | public static readonly EDmgVsBoss U_DamageB = new EDmgVsBoss(4, 0x2e98a, "Bubble Lead"); 41 | public static readonly EDmgVsBoss U_DamageQ = new EDmgVsBoss(5, 0x2e998, "Quick Boomerang"); 42 | public static readonly EDmgVsBoss U_DamageF = new EDmgVsBoss(6, 0x2C049, "Time Stopper"); 43 | public static readonly EDmgVsBoss U_DamageM = new EDmgVsBoss(7, 0x2e9b4, "Metal Blade"); 44 | public static readonly EDmgVsBoss U_DamageC = new EDmgVsBoss(8, 0x2e9a6, "Clash Bomber"); 45 | 46 | static EDmgVsBoss() 47 | { 48 | Addresses = new Dictionary() 49 | { 50 | { U_DamageP.Address, U_DamageP }, 51 | { U_DamageH.Address, U_DamageH }, 52 | { U_DamageA.Address, U_DamageA }, 53 | { U_DamageW.Address, U_DamageW }, 54 | { U_DamageB.Address, U_DamageB }, 55 | { U_DamageQ.Address, U_DamageQ }, 56 | { U_DamageF.Address, U_DamageF }, 57 | { U_DamageM.Address, U_DamageM }, 58 | { U_DamageC.Address, U_DamageC }, 59 | }; 60 | } 61 | 62 | private EDmgVsBoss(int index, int address, string name) 63 | { 64 | this.Address = address; 65 | this.WeaponName = name; 66 | this.Index = index; 67 | } 68 | 69 | public static implicit operator int (EDmgVsBoss eDmgVsBoss) 70 | { 71 | return eDmgVsBoss.Address; 72 | } 73 | 74 | public static implicit operator EDmgVsBoss (int eDmgVsBoss) 75 | { 76 | return Addresses[eDmgVsBoss]; 77 | } 78 | 79 | public override string ToString() 80 | { 81 | return WeaponName; 82 | } 83 | 84 | /// 85 | /// Get a list of pointers to weapon damage tables against bosses, sorted by boss order 86 | /// 87 | /// 88 | /// 89 | /// 90 | public static List GetTables(bool includeBuster, bool includeTimeStopper) 91 | { 92 | List tables = new List(); 93 | if (includeBuster) tables.Add(U_DamageP); 94 | tables.Add(U_DamageH); 95 | tables.Add(U_DamageA); 96 | tables.Add(U_DamageW); 97 | tables.Add(U_DamageB); 98 | tables.Add(U_DamageQ); 99 | if (includeTimeStopper) tables.Add(U_DamageF); 100 | tables.Add(U_DamageM); 101 | tables.Add(U_DamageC); 102 | return tables; 103 | } 104 | 105 | /// 106 | /// 107 | /// 108 | public class Offset 109 | { 110 | public string Name 111 | { 112 | get; private set; 113 | } 114 | 115 | public int Value 116 | { 117 | get; private set; 118 | } 119 | 120 | public static Dictionary Offsets; 121 | 122 | public static readonly Offset Dragon = new Offset(0x08, "Dragon"); 123 | public static readonly Offset Guts = new Offset(0x0A, "Guts"); 124 | public static readonly Offset Machine = new Offset(0x0C, "Machine"); 125 | public static readonly Offset Alien = new Offset(0x0D, "Alien"); 126 | public static readonly Offset Heat = new Offset(0x00, "Heat"); 127 | public static readonly Offset Air = new Offset(0x01, "Air"); 128 | public static readonly Offset Wood = new Offset(0x02, "Wood"); 129 | public static readonly Offset Bubble = new Offset(0x03, "Bubble"); 130 | public static readonly Offset Quick = new Offset(0x04, "Quick"); 131 | public static readonly Offset Flash = new Offset(0x05, "Flash"); 132 | public static readonly Offset Metal = new Offset(0x06, "Metal"); 133 | public static readonly Offset Clash = new Offset(0x07, "Clash"); 134 | 135 | static Offset() 136 | { 137 | Offsets = new Dictionary() 138 | { 139 | { Dragon.Value , Dragon }, 140 | { Guts.Value , Guts }, 141 | { Machine.Value , Machine }, 142 | { Alien.Value , Alien }, 143 | { Heat.Value , Heat }, 144 | { Air.Value , Air }, 145 | { Wood.Value , Wood }, 146 | { Bubble.Value , Bubble }, 147 | { Quick.Value , Quick }, 148 | { Flash.Value , Flash }, 149 | { Metal.Value , Metal }, 150 | { Clash.Value , Clash }, 151 | }; 152 | } 153 | 154 | private Offset(int offset, string name) 155 | { 156 | this.Name = name; 157 | this.Value = offset; 158 | } 159 | 160 | public static implicit operator int (Offset offset) 161 | { 162 | return offset.Value; 163 | } 164 | 165 | public static implicit operator Offset(int offset) 166 | { 167 | return Offsets[offset]; 168 | } 169 | 170 | public override string ToString() 171 | { 172 | return Name; 173 | } 174 | } 175 | 176 | 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /MM2RandoLib/Enums/EDmgVsEnemy.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace MM2Randomizer.Enums 4 | { 5 | /// 6 | /// 7 | /// 8 | public sealed class EDmgVsEnemy 9 | { 10 | public int Address 11 | { 12 | get; private set; 13 | } 14 | 15 | public string WeaponName 16 | { 17 | get; private set; 18 | } 19 | 20 | public static Dictionary Addresses { get; set; } 21 | 22 | public static readonly EDmgVsEnemy DamageP = new EDmgVsEnemy(0x03E9A8, "Buster"); 23 | public static readonly EDmgVsEnemy DamageH = new EDmgVsEnemy(0x03EA24, "Atomic Fire"); 24 | public static readonly EDmgVsEnemy DamageA = new EDmgVsEnemy(0x03EA9C, "Air Shooter"); 25 | public static readonly EDmgVsEnemy DamageW = new EDmgVsEnemy(0x03EB14, "Leaf Shield"); 26 | public static readonly EDmgVsEnemy DamageB = new EDmgVsEnemy(0x03EB8C, "Bubble Lead"); 27 | public static readonly EDmgVsEnemy DamageQ = new EDmgVsEnemy(0x03EC04, "Quick Boomerang"); 28 | public static readonly EDmgVsEnemy DamageM = new EDmgVsEnemy(0x03ECF4, "Metal Blade"); 29 | public static readonly EDmgVsEnemy DamageC = new EDmgVsEnemy(0x03EC7C, "Clash Bomber"); 30 | 31 | static EDmgVsEnemy() 32 | { 33 | Addresses = new Dictionary() 34 | { 35 | { DamageP.Address, DamageP }, 36 | { DamageH.Address, DamageH }, 37 | { DamageA.Address, DamageA }, 38 | { DamageW.Address, DamageW }, 39 | { DamageB.Address, DamageB }, 40 | { DamageQ.Address, DamageQ }, 41 | { DamageM.Address, DamageM }, 42 | { DamageC.Address, DamageC }, 43 | }; 44 | } 45 | 46 | private EDmgVsEnemy(int address, string name) 47 | { 48 | this.Address = address; 49 | this.WeaponName = name; 50 | } 51 | 52 | public static implicit operator int (EDmgVsEnemy eDmgVsEnemy) 53 | { 54 | return eDmgVsEnemy.Address; 55 | } 56 | 57 | public static implicit operator EDmgVsEnemy(int eDmgVsEnemy) 58 | { 59 | return Addresses[eDmgVsEnemy]; 60 | } 61 | 62 | public static bool operator ==(EDmgVsEnemy a, EDmgVsEnemy b) 63 | { 64 | return (a.Address == b.Address); 65 | } 66 | 67 | public static bool operator !=(EDmgVsEnemy a, EDmgVsEnemy b) 68 | { 69 | return (a.Address != b.Address); 70 | } 71 | public static bool operator ==(int a, EDmgVsEnemy b) 72 | { 73 | return (a == b.Address); 74 | } 75 | 76 | public static bool operator !=(int a, EDmgVsEnemy b) 77 | { 78 | return (a != b.Address); 79 | } 80 | 81 | public override bool Equals(object obj) 82 | { 83 | return base.Equals(obj); 84 | } 85 | 86 | public override int GetHashCode() 87 | { 88 | return base.GetHashCode(); 89 | } 90 | 91 | /// 92 | /// 93 | /// 94 | /// 95 | public static List GetTables(bool includeBuster) 96 | { 97 | List list = new List() 98 | { 99 | DamageH, 100 | DamageA, 101 | DamageW, 102 | DamageB, 103 | DamageQ, 104 | DamageM, 105 | DamageC, 106 | }; 107 | 108 | if (includeBuster) 109 | { 110 | list.Insert(0, DamageP); 111 | } 112 | 113 | return list; 114 | } 115 | 116 | /// 117 | /// 118 | /// 119 | public class Offset 120 | { 121 | public int Value 122 | { 123 | get; private set; 124 | } 125 | 126 | public static readonly Offset PicopicoKun; 127 | public static readonly Offset Press; 128 | public static readonly Offset ClashBarrier_Other; 129 | public static readonly Offset ClashBarrier_W4; 130 | public static readonly Offset Buebeam; 131 | 132 | static Offset() 133 | { 134 | ClashBarrier_Other = new Offset(0x2D); 135 | Press = new Offset(0x30); 136 | ClashBarrier_W4 = new Offset(0x57); 137 | PicopicoKun = new Offset(0x6A); 138 | Buebeam = new Offset(0x6D); 139 | } 140 | 141 | private Offset(int offset) 142 | { 143 | this.Value = offset; 144 | } 145 | 146 | public static implicit operator int (Offset offset) 147 | { 148 | return offset.Value; 149 | } 150 | 151 | public static bool operator ==(Offset a, Offset b) 152 | { 153 | return (a.Value == b.Value); 154 | } 155 | 156 | public static bool operator !=(Offset a, Offset b) 157 | { 158 | return (a.Value != b.Value); 159 | } 160 | 161 | public static bool operator ==(int a, Offset b) 162 | { 163 | return (a == b.Value); 164 | } 165 | 166 | public static bool operator !=(int a, Offset b) 167 | { 168 | return (a != b.Value); 169 | } 170 | 171 | public override bool Equals(object obj) 172 | { 173 | return base.Equals(obj); 174 | } 175 | 176 | public override int GetHashCode() 177 | { 178 | return base.GetHashCode(); 179 | } 180 | } 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /MM2RandoLib/Enums/EEnemyID.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace MM2Randomizer.Enums 7 | { 8 | public enum EEnemyID 9 | { 10 | Shrink = 0x00, 11 | Shrink_Angler = 0x01, 12 | Shrink_Spawner = 0x02, 13 | M445_Activator = 0x03, 14 | M445_Instance = 0x04, 15 | M445_Deactivator = 0x05, 16 | Claw_Activator = 0x07, 17 | Claw_Instance = 0x08, 18 | Claw_Deactivator = 0x09, 19 | Tanishi = 0x0A, 20 | Kerog = 0x0C, 21 | Kerog_Small = 0x0D, 22 | QuickLaser = 0x14, 23 | Batton = 0x16, 24 | Robbit = 0x17, 25 | Monking = 0x1D, 26 | Kukku_Activator = 0x1E, 27 | Kukku_Deactivator = 0x20, 28 | Telly = 0x21, 29 | ChangkeyMaker = 0x23, 30 | QuickDarkPalette = 0x25, 31 | QuickNormalPalette = 0x27, 32 | Pierrobot = 0x29, 33 | FlyBoy = 0x2B, 34 | ClashBarrier = 0x2D, 35 | Press = 0x30, 36 | Blocky = 0x31, 37 | NeoMetall = 0x34, 38 | Matasaburo = 0x36, 39 | Pipi_Activator = 0x37, 40 | Pipi_Deactivator = 0x39, 41 | LightningGoro = 0x3E, 42 | Springer = 0x46, 43 | Mole_Activator = 0x47, 44 | Mole_Deactivator = 0x4A, 45 | Shotman_Left = 0x4B, 46 | Shotman_Right = 0x4C, 47 | SniperArmor = 0x4E, 48 | SniperJoe = 0x4F, 49 | Scworm = 0x50, 50 | BigFish = 0x71, 51 | Drip1 = 0x72, 52 | Drip2 = 0x73, 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /MM2RandoLib/Enums/EItemNumber.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace MM2Randomizer.Enums 7 | { 8 | public enum EItemNumber 9 | { 10 | None = 0, 11 | One = 1, 12 | Two = 2, 13 | Three = 4 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /MM2RandoLib/Enums/EItemStageAddress.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace MM2Randomizer.Enums 7 | { 8 | public enum EItemStageAddress 9 | { 10 | // 0x03C291 - Item # from Heat Man 11 | // 0x03C292 - Item # from Air Man 12 | // 0x03C293 - Item # from Wood Man 13 | // 0x03C294 - Item # from Bubble Man 14 | // 0x03C295 - Item # from Quick Man 15 | // 0x03C296 - Item # from Flash Man 16 | // 0x03C297 - Item # from Metal Man 17 | // 0x03C298 - Item # from Crash Man 18 | 19 | HeatMan = 0x03C291, 20 | AirMan = 0x03C292, 21 | WoodMan = 0x03C293, 22 | BubbleMan = 0x03C294, 23 | QuickMan = 0x03C295, 24 | FlashMan = 0x03C296, 25 | MetalMan = 0x03C297, 26 | CrashMan = 0x03C298 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /MM2RandoLib/Enums/EMiscAddresses.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace MM2Randomizer.Enums 7 | { 8 | public enum EMiscAddresses 9 | { 10 | WarpXCoordinateStartAddress = 0x038280, 11 | WarpYCoordinateStartAddress = 0x038278 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /MM2RandoLib/Enums/EMusicID.cs: -------------------------------------------------------------------------------- 1 | namespace MM2Randomizer.Enums 2 | { 3 | public enum EMusicID 4 | { 5 | Flash = 0x00, 6 | Wood = 0x01, 7 | Clash = 0x02, 8 | Heat = 0x03, 9 | Air = 0x04, 10 | Metal = 0x05, 11 | Quick = 0x06, 12 | Bubble = 0x07, 13 | Wily12 = 0x08, 14 | Wily345 = 0x09, 15 | Boss = 0x0B, 16 | StageSelectWily5 = 0x0C, 17 | Nothing = 0xFF, 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /MM2RandoLib/Enums/ERMPortraitAddress.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace MM2Randomizer.Enums 7 | { 8 | public enum ERMPortraitAddress 9 | { 10 | // StageSelect Address Value 11 | // ----------------------------- 12 | // Bubble Man 0x034670 3 13 | // Air Man 0x034671 1 14 | // Quick Man 0x034672 4 15 | // Wood Man 0x034673 2 16 | // Crash Man 0x034674 7 17 | // Flash Man 0x034675 5 18 | // Metal Man 0x034676 6 19 | // Heat Man 0x034677 0 20 | 21 | HeatMan = 0x034677, 22 | 23 | AirMan = 0x034671, 24 | 25 | WoodMan = 0x034673, 26 | 27 | BubbleMan = 0x034670, 28 | 29 | QuickMan = 0x034672, 30 | 31 | FlashMan = 0x034675, 32 | 33 | MetalMan = 0x034676, 34 | 35 | CrashMan = 0x034674 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /MM2RandoLib/Enums/ERMPortraitText.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace MM2Randomizer.Enums 7 | { 8 | public enum ERMPortraitText 9 | { 10 | HeatMan = 0x02F035, 11 | AirMan = 0x02EF3D, 12 | WoodMan = 0x02F045, 13 | BubbleMan = 0x02EF35, 14 | QuickMan = 0x02EF45, 15 | FlashMan = 0x02F13D, 16 | MetalMan = 0x02F135, 17 | CrashMan = 0x02F145 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /MM2RandoLib/Enums/ERMStageClearAddress.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace MM2Randomizer.Enums 7 | { 8 | public enum ERMStageClearAddress 9 | { 10 | HeatMan = 0x03C289, 11 | AirMan = 0x03C28A, 12 | WoodMan = 0x03C28B, 13 | BubbleMan = 0x03C28C, 14 | QuickMan = 0x03C28D, 15 | FlashMan = 0x03C28E, 16 | MetalMan = 0x03C28F, 17 | CrashMan = 0x03C290, 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /MM2RandoLib/Enums/ERMStageSelect.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace MM2Randomizer.Enums 7 | { 8 | public enum ERMStageSelect 9 | { 10 | //Not sure what the stage select order is but this is the starting Mem 11 | FirstStageInMemory = 0x0346E1 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /MM2RandoLib/Enums/ERMStageWeaponAddress.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace MM2Randomizer.Enums 7 | { 8 | public enum ERMStageWeaponAddress 9 | { 10 | // StageBeat Address Value 11 | // ----------------------------- 12 | // Heat Man 0x03C289 1 13 | // Air Man 0x03C28A 2 14 | // Wood Man 0x03C28B 4 15 | // Bubble Man 0x03C28C 8 16 | // Quick Man 0x03C28D 16 17 | // Flash Man 0x03C28E 32 18 | // Metal Man 0x03C28F 64 19 | // Crash Man 0x03C290 128 20 | 21 | HeatMan = 0x03C289, 22 | 23 | AirMan = 0x03C28A, 24 | 25 | WoodMan = 0x03C28B, 26 | 27 | BubbleMan = 0x03C28C, 28 | 29 | QuickMan = 0x03C28D, 30 | 31 | FlashMan = 0x03C28E, 32 | 33 | MetalMan = 0x03C28F, 34 | 35 | CrashMan = 0x03C290, 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /MM2RandoLib/Enums/ERMWeaponValue.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace MM2Randomizer.Enums 7 | { 8 | public enum ERMWeaponValueBit 9 | { 10 | // StageBeat Address Value 11 | // ----------------------------- 12 | // Heat Man 0x03C289 1 13 | // Air Man 0x03C28A 2 14 | // Wood Man 0x03C28B 4 15 | // Bubble Man 0x03C28C 8 16 | // Quick Man 0x03C28D 16 17 | // Flash Man 0x03C28E 32 18 | // Metal Man 0x03C28F 64 19 | // Crash Man 0x03C290 128 20 | HeatMan = 0x01, 21 | AirMan = 0x02, 22 | WoodMan = 0x04, 23 | BubbleMan = 0x08, 24 | QuickMan = 0x10, 25 | FlashMan = 0x20, 26 | MetalMan = 0x40, 27 | CrashMan = 0x80 28 | } 29 | 30 | public enum EWeaponIndex 31 | { 32 | Heat = 0x01, 33 | Air = 0x02, 34 | Wood = 0x03, 35 | Bubble = 0x04, 36 | Quick = 0x05, 37 | Clash = 0x06, 38 | Metal = 0x07, 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /MM2RandoLib/Enums/ESoundID.cs: -------------------------------------------------------------------------------- 1 | namespace MM2Randomizer.Enums 2 | { 3 | public enum ESoundID 4 | { 5 | WeaponF = 0x21, 6 | HeatmanUnused = 0x22, 7 | WeaponM = 0x23, 8 | WeaponP = 0x24, 9 | Shotman = 0x25, 10 | TakeDamage = 0x26, 11 | QuickBeam = 0x27, 12 | Refill = 0x28, 13 | MegaLand = 0x29, 14 | WilyDefeat = 0x2A, // no 15 | DamageEnemy = 0x2B, 16 | Dragon = 0x2C, 17 | Tink = 0x2D, 18 | ClashAttach = 0x2E, 19 | Cursor = 0x2F, 20 | TeleportIn = 0x30, 21 | WeaponW = 0x31, 22 | Pause = 0x32, 23 | Unknown0 = 0x33, // no 24 | BossDoor = 0x34, // no 25 | WeaponH_Charge0 = 0x35, // yes, but randomize the 3 26 | WeaponH_Charge1 = 0x36, 27 | WeaponH_Charge2 = 0x37, 28 | WeaponH_Shoot = 0x38, 29 | FlyBoy = 0x39, 30 | TeleportOut = 0x3A, 31 | Splash = 0x3B, 32 | Yoku = 0x3C, 33 | Droplet1 = 0x3D, 34 | Droplet2 = 0x3E, // no 35 | WeaponA = 0x3F, 36 | Unknown1 = 0x40, // yes. weird fuzz sound? 37 | Death = 0x41, 38 | OneUp = 0x42, 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /MM2RandoLib/Enums/EStageID.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace MM2Randomizer.Enums 7 | { 8 | public enum EStageID 9 | { 10 | HeatW1 = 0x00, 11 | AirW2 = 0x01, 12 | WoodW3 = 0x02, 13 | BubbleW4 = 0x03, 14 | QuickW5 = 0x04, 15 | FlashW6 = 0x05, 16 | Metal = 0x06, 17 | Clash = 0x07 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /MM2RandoLib/MM2RandoLib.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | netstandard2.0 4 | True 5 | MM2RandoLib 6 | 0.5.0 7 | Copyright 2019 duckfist 8 | .NET library that can generate a randomized ROM patch for Mega Man 2. 9 | duckfist 10 | https://github.com/duckfist/MM2Random/blob/master/LICENSE 11 | https://github.com/duckfist/MM2Random/ 12 | https://github.com/duckfist/MM2Random/ 13 | 14 | migrated to .netstandard 2.0 15 | 0.5.0.* 16 | 0.5.0.0 17 | false 18 | en-US 19 | AnyCPU;x64 20 | 21 | 22 | DEBUG;TRACE 23 | full 24 | true 25 | 26 | 27 | DEBUG;TRACE 28 | full 29 | true 30 | 31 | 32 | 33 | 34 | 35 | 36 | True 37 | True 38 | Resources.resx 39 | 40 | 41 | 42 | 43 | PublicResXFileCodeGenerator 44 | Resources.Designer.cs 45 | MM2Randomizer.Properties 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /MM2RandoLib/ObservableBase.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.ComponentModel; 3 | using System.Runtime.CompilerServices; 4 | 5 | namespace MM2Randomizer 6 | { 7 | /// Provides an implementation for the interface. 8 | public abstract class ObservableBase : INotifyPropertyChanged 9 | { 10 | /// Occurs when a property value cahnges. 11 | public event PropertyChangedEventHandler PropertyChanged; 12 | 13 | /// Raises the property changed event. 14 | /// The property name. 15 | protected void NotifyPropertyChanged([CallerMemberName]string propertyName = "") 16 | { 17 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 18 | } 19 | 20 | /// 21 | /// Set the value of a property, firing NotifyPropertyChanged if the value is new. This is used 22 | /// for a cleaner implementation of ViewModel properties, so they have fewer lines of code. 23 | /// 24 | /// The type of the property being changed. 25 | /// The property's backing field. 26 | /// The property's new value to set. 27 | /// The name of the property (leave blank to use the caller's name) 28 | /// True if the property has changed, false if it is still the same value. 29 | protected bool SetProperty( 30 | ref T privateField, 31 | T newValue, 32 | [CallerMemberName]string propertyName = null, 33 | params string[] additionalProperties) 34 | { 35 | // If value hasn't changed, just return false 36 | if (EqualityComparer.Default.Equals(privateField, newValue)) return false; 37 | 38 | // Value has changed, invoke propertychanged and return true 39 | privateField = newValue; 40 | NotifyPropertyChanged(propertyName); 41 | 42 | // Notify any additional properties that might depend on this one 43 | foreach (string additionalProperty in additionalProperties) 44 | NotifyPropertyChanged(additionalProperty); 45 | 46 | return true; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /MM2RandoLib/Patcher/ByteChangeRecord.cs: -------------------------------------------------------------------------------- 1 | namespace MM2Randomizer.Patcher 2 | { 3 | public class ChangeByteRecord 4 | { 5 | public int Address { get; set; } 6 | public byte Value { get; set; } 7 | public string Note { get; set; } 8 | 9 | public ChangeByteRecord(int address, byte value, string note = "") 10 | { 11 | Address = address; 12 | Value = value; 13 | Note = note; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /MM2RandoLib/Patcher/Patch.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | 7 | namespace MM2Randomizer.Patcher 8 | { 9 | public class Patch 10 | { 11 | public Dictionary Bytes { get; set; } 12 | 13 | public Patch() 14 | { 15 | Bytes = new Dictionary(); 16 | } 17 | 18 | /// 19 | /// TODO 20 | /// 21 | /// 22 | /// 23 | /// 24 | public void Add(int address, byte value, string note = "") 25 | { 26 | ChangeByteRecord newByte = new ChangeByteRecord(address, value, note); 27 | 28 | // Either replace the byte at the given address, or add it if it doesn't exist 29 | if (Bytes.ContainsKey(address)) 30 | { 31 | Bytes[address] = newByte; 32 | } 33 | else 34 | { 35 | Bytes.Add(address, newByte); 36 | } 37 | } 38 | 39 | /// 40 | /// TODO 41 | /// 42 | /// 43 | public void ApplyRandoPatch(string filename) 44 | { 45 | using (var stream = new FileStream(filename, FileMode.Open, FileAccess.ReadWrite)) 46 | { 47 | //GetStringSortedByAddress(); 48 | 49 | foreach (KeyValuePair kvp in Bytes) 50 | { 51 | stream.Position = kvp.Key; 52 | stream.WriteByte(kvp.Value.Value); 53 | } 54 | } 55 | } 56 | public string GetStringSortedByAddress() 57 | { 58 | var sortDict = from kvp in Bytes orderby kvp.Key ascending select kvp; 59 | return ConvertDictToString(sortDict); 60 | } 61 | 62 | public string GetString() 63 | { 64 | return ConvertDictToString((IOrderedEnumerable>)Bytes); 65 | } 66 | 67 | /// 68 | /// TODO 69 | /// 70 | /// 71 | /// 72 | private string ConvertDictToString(IOrderedEnumerable> dict) 73 | { 74 | StringBuilder sb = new StringBuilder(); 75 | foreach (KeyValuePair kvp in dict) 76 | { 77 | ChangeByteRecord b = kvp.Value; 78 | sb.Append($"0x{b.Address:X6}\t{b.Value:X2}\t{b.Note}"); 79 | sb.Append(Environment.NewLine); 80 | } 81 | return sb.ToString(); 82 | } 83 | 84 | /// 85 | /// TODO 86 | /// 87 | /// 88 | /// 89 | public void ApplyIPSPatch(string romname, byte[] patchBytes) 90 | { 91 | // Noobish Noobsicle wrote this IPS patching code 92 | // romname is the original ROM, patchname is the patch to apply 93 | FileStream romstream = new FileStream(romname, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite); 94 | int lint = patchBytes.Length; 95 | byte[] ipsbyte = patchBytes; 96 | byte[] rombyte = new byte[romstream.Length]; 97 | IAsyncResult romresult; 98 | int ipson = 5; 99 | int totalrepeats = 0; 100 | int offset = 0; 101 | bool keepgoing = true; 102 | while (keepgoing == true) 103 | { 104 | offset = ipsbyte[ipson] * 0x10000 + ipsbyte[ipson + 1] * 0x100 + ipsbyte[ipson + 2]; 105 | ipson++; 106 | ipson++; 107 | ipson++; 108 | if (ipsbyte[ipson] * 256 + ipsbyte[ipson + 1] == 0) 109 | { 110 | ipson++; 111 | ipson++; 112 | totalrepeats = ipsbyte[ipson] * 256 + ipsbyte[ipson + 1]; 113 | ipson++; 114 | ipson++; 115 | byte[] repeatbyte = new byte[totalrepeats]; 116 | for (int ontime = 0; ontime < totalrepeats; ontime++) 117 | repeatbyte[ontime] = ipsbyte[ipson]; 118 | romstream.Seek(offset, SeekOrigin.Begin); 119 | romresult = romstream.BeginWrite(repeatbyte, 0, totalrepeats, null, null); 120 | romstream.EndWrite(romresult); 121 | ipson++; 122 | } 123 | else 124 | { 125 | totalrepeats = ipsbyte[ipson] * 256 + ipsbyte[ipson + 1]; 126 | ipson++; 127 | ipson++; 128 | romstream.Seek(offset, SeekOrigin.Begin); 129 | romresult = romstream.BeginWrite(ipsbyte, ipson, totalrepeats, null, null); 130 | romstream.EndWrite(romresult); 131 | ipson = ipson + totalrepeats; 132 | } 133 | if (ipsbyte[ipson] == 69 && ipsbyte[ipson + 1] == 79 && ipsbyte[ipson + 2] == 70) 134 | keepgoing = false; 135 | } 136 | romstream.Close(); 137 | } 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /MM2RandoLib/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | 122 | ..\Resources\bossnames.csv;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252 123 | 124 | 125 | ..\Resources\companynames.csv;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252 126 | 127 | 128 | ..\Resources\countrylist.csv;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252 129 | 130 | 131 | ..\Resources\creditstext.csv;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 132 | 133 | 134 | ..\Resources\enemylist.csv;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252 135 | 136 | 137 | ..\Resources\enemyweakness.csv;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252 138 | 139 | 140 | ..\Resources\mm2rng_prepatch.ips;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 141 | 142 | 143 | ..\Resources\music.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 144 | 145 | 146 | ..\Resources\mm2rng_musicpatch.ips;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 147 | 148 | 149 | ..\Resources\level_components.json;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252 150 | 151 | 152 | ..\Resources\SpritePatches\SpriteSwap_Bass.ips;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 153 | 154 | 155 | ..\Resources\SpritePatches\SpriteSwap_Proto.ips;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 156 | 157 | 158 | ..\Resources\SpritePatches\SpriteSwap_Roll.ips;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 159 | 160 | -------------------------------------------------------------------------------- /MM2RandoLib/RandoSettings.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Text; 6 | 7 | namespace MM2Randomizer 8 | { 9 | public class RandoSettings : ObservableBase 10 | { 11 | private string seedString; 12 | private string sourcePath; 13 | private bool isSourcePathValid; 14 | private bool isSeedValid; 15 | private bool isSourcePathAndSeedValid; 16 | private string hashStringMD5; 17 | private string hashStringSHA256; 18 | private string hashValidationMessage; 19 | private bool isHashValid; 20 | private bool isSpoilerFree; 21 | 22 | private PlayerSprite _selectedPlayer; 23 | 24 | public readonly string[] ExpectedMD5s = new string[] 25 | { 26 | "caaeb9ee3b52839de261fd16f93103e6", // Mega Man 2 (U) 27 | "8e4bc5b03ffbd4ef91400e92e50dd294", // Mega Man 2 (USA) 28 | }; 29 | 30 | public readonly string[] ExpectedSHA256s = new string[] 31 | { 32 | "27b5a635df33ed57ed339dfc7fd62fc603b39c1d1603adb5cdc3562a0b0d555b", // Mega Man 2 (U) 33 | "49136b412ff61beac6e40d0bbcd8691a39a50cd2744fdcdde3401eed53d71edf", // Mega Man 2 (USA) 34 | }; 35 | 36 | public RandoSettings() 37 | { 38 | // Rando assembly state variables 39 | SeedString = ""; 40 | SourcePath = ""; 41 | IsSeedValid = false; 42 | IsSourcePathValid = false; 43 | IsSourcePathAndSeedValid = false; 44 | HashStringMD5 = ""; 45 | HashStringSHA256 = ""; 46 | HashValidationMessage = ""; 47 | IsHashValid = false; 48 | 49 | // Flags for Rando Core Modules (Interdependent, cannot be changed from UI) 50 | Is8StagesRandom = true; 51 | IsWeaponsRandom = true; 52 | IsTeleportersRandom = true; 53 | 54 | // Flags for Rando Gameplay Modules 55 | IsWeaponBehaviorRandom = true; 56 | IsWeaknessRandom = true; 57 | IsBossInBossRoomRandom = true; 58 | IsBossAIRandom = true; 59 | IsItemsRandom = true; 60 | IsEnemiesRandom = true; 61 | IsEnemyWeaknessRandom = true; 62 | IsTilemapChangesEnabled = true; 63 | 64 | // Flags for Rando Cosmetic Modules 65 | IsWeaponNamesRandom = true; 66 | IsColorsRandom = true; 67 | IsBGMRandom = true; 68 | SelectedPlayer = PlayerSprite.Rockman; 69 | 70 | // Flags for Optional Gameplay Modules 71 | FastText = true; 72 | BurstChaserMode = false; 73 | IsSpoilerFree = false; 74 | } 75 | 76 | #region Meta Properties 77 | 78 | /// 79 | /// Alphabetical string representation of the RandomMM2.Seed integer of the most 80 | /// recently generated ROM. 81 | /// 82 | public string SeedString 83 | { 84 | get => seedString; 85 | set 86 | { 87 | value = value.ToUpper(); 88 | if (seedString != value) 89 | { 90 | seedString = value; 91 | NotifyPropertyChanged(); 92 | 93 | // TODO: Check for better validity of seed 94 | IsSeedValid = (seedString == "") ? false : true; 95 | IsSourcePathAndSeedValid = IsSourcePathValid && IsSeedValid; 96 | } 97 | } 98 | } 99 | 100 | /// 101 | /// Full path to user-provided ROM to apply patch. 102 | /// 103 | public string SourcePath 104 | { 105 | get => sourcePath; 106 | set => SetProperty(ref sourcePath, value); 107 | } 108 | 109 | public bool IsSourcePathValid 110 | { 111 | get => isSourcePathValid; 112 | set => SetProperty(ref isSourcePathValid, value); 113 | } 114 | 115 | public bool IsSeedValid 116 | { 117 | get => isSeedValid; 118 | set => SetProperty(ref isSeedValid, value); 119 | } 120 | 121 | // TODO need this? 122 | public bool IsSourcePathAndSeedValid 123 | { 124 | get => isSourcePathAndSeedValid; 125 | set => SetProperty(ref isSourcePathAndSeedValid, value); 126 | } 127 | 128 | public bool IsSpoilerFree 129 | { 130 | get => isSpoilerFree; 131 | set => SetProperty(ref isSpoilerFree, value); 132 | } 133 | 134 | public string HashStringMD5 135 | { 136 | get => hashStringMD5; 137 | set => SetProperty(ref hashStringMD5, value); 138 | } 139 | 140 | public string HashStringSHA256 141 | { 142 | get => hashStringSHA256; 143 | set => SetProperty(ref hashStringSHA256, value); 144 | } 145 | 146 | public string HashValidationMessage 147 | { 148 | get => hashValidationMessage; 149 | set => SetProperty(ref hashValidationMessage, value); 150 | } 151 | 152 | public bool IsHashValid 153 | { 154 | get => isHashValid; 155 | set => SetProperty(ref isHashValid, value); 156 | } 157 | 158 | /// 159 | /// Get this assembly version as a bindable property. 160 | /// 161 | public Version AssemblyVersion 162 | { 163 | get 164 | { 165 | return Assembly.GetAssembly(typeof(RandomMM2)).GetName().Version; 166 | } 167 | } 168 | 169 | #endregion 170 | 171 | #region Randomizer Flags 172 | 173 | /// 174 | /// If True, the Robot Master stages will be shuffled and will not be indicated by the 175 | /// portraits on the Stage Select screen. 176 | /// 177 | public bool Is8StagesRandom { get; set; } 178 | 179 | /// 180 | /// If True, the weapons awarded from each Robot Master is shuffled. 181 | /// 182 | public bool IsWeaponsRandom { get; set; } 183 | 184 | /// 185 | /// If True, Items 1, 2, and 3 will be awarded from random Robot Masters. 186 | /// 187 | public bool IsItemsRandom { get; set; } 188 | 189 | /// 190 | /// If true, in Wily 5, the Robot Master locations in each teleporter is randomized. 191 | /// 192 | public bool IsTeleportersRandom { get; set; } 193 | 194 | /// 195 | /// If True, the damage each weapon does against each Robot Master is changed. The manner in 196 | /// which it is changed depends on if IsWeaknessEasy is True or if IsWeaknessHard is True. 197 | /// 198 | public bool IsWeaknessRandom { get; set; } 199 | 200 | /// 201 | /// 202 | /// 203 | public bool IsBossAIRandom { get; set; } 204 | 205 | /// 206 | /// TODO 207 | /// 208 | public bool IsColorsRandom { get; set; } 209 | 210 | /// 211 | /// TODO 212 | /// 213 | public bool IsEnemiesRandom { get; set; } 214 | 215 | public bool IsEnemyWeaknessRandom { get; set; } 216 | 217 | public bool IsBossInBossRoomRandom { get; set; } 218 | 219 | /// 220 | /// 221 | /// 222 | public bool IsTilemapChangesEnabled { get; set; } 223 | 224 | /// 225 | /// TODO 226 | /// 227 | public bool IsBGMRandom { get; set; } 228 | 229 | /// 230 | /// Change this value to set Mega Man's sprite graphic. 231 | /// 232 | public PlayerSprite SelectedPlayer 233 | { 234 | get => _selectedPlayer; 235 | set => SetProperty(ref _selectedPlayer, value); 236 | } 237 | 238 | /// 239 | /// 240 | /// 241 | public bool IsWeaponNamesRandom { get; set; } 242 | 243 | /// 244 | /// TODO 245 | /// 246 | public bool FastText { get; set; } 247 | 248 | public bool IsStageNameHidden { get; set; } 249 | 250 | /// 251 | /// TODO 252 | /// 253 | public bool BurstChaserMode { get; set; } 254 | 255 | public bool IsWeaponBehaviorRandom { get; set; } 256 | 257 | #endregion 258 | 259 | public string GetFlagsString() 260 | { 261 | StringBuilder sb = new StringBuilder(); 262 | if (Is8StagesRandom && IsWeaponsRandom && IsTeleportersRandom) 263 | sb.Append('!'); else sb.Append(' '); 264 | //if (Is8StagesRandom) sb.Append('A'); else sb.Append(' '); 265 | //if (IsWeaponsRandom) sb.Append('B'); else sb.Append(' '); 266 | //if (IsTeleportersRandom) sb.Append('C'); else sb.Append(' '); 267 | if (IsWeaponBehaviorRandom) sb.Append('A'); else sb.Append(' '); 268 | if (IsWeaknessRandom) sb.Append('B'); else sb.Append(' '); 269 | if (IsBossInBossRoomRandom) sb.Append('C'); else sb.Append(' '); 270 | if (IsBossAIRandom) sb.Append('D'); else sb.Append(' '); 271 | if (IsItemsRandom) sb.Append('E'); else sb.Append(' '); 272 | if (IsEnemiesRandom) sb.Append('F'); else sb.Append(' '); 273 | if (IsEnemyWeaknessRandom) sb.Append('G'); else sb.Append(' '); 274 | if (IsTilemapChangesEnabled) sb.Append('H'); else sb.Append(' '); 275 | 276 | if (IsWeaponNamesRandom) sb.Append('1'); else sb.Append(' '); 277 | if (IsColorsRandom) sb.Append('2'); else sb.Append(' '); 278 | if (IsBGMRandom) sb.Append('3'); else sb.Append(' '); 279 | 280 | if (FastText) sb.Append('t'); else sb.Append(' '); 281 | if (BurstChaserMode) sb.Append('©'); else sb.Append(' '); 282 | if (IsStageNameHidden) sb.Append('?'); else sb.Append(' '); 283 | return sb.ToString(); 284 | } 285 | 286 | /// 287 | /// This method checks that a file exists and then compares its checksum with known good Mega Man 2 ROMs. 288 | /// If it fails any of this, the method returns false. 289 | /// 290 | /// 291 | /// 292 | public bool ValidateFile(string path) 293 | { 294 | // Check if file even exists 295 | SourcePath = path; 296 | IsSourcePathValid = System.IO.File.Exists(SourcePath); 297 | IsSourcePathAndSeedValid = IsSourcePathValid && IsSeedValid; 298 | 299 | if (!IsSourcePathValid) 300 | { 301 | HashValidationMessage = "File does not exist."; 302 | IsHashValid = false; 303 | return false; 304 | } 305 | 306 | // Ensure file size is small so that we can take the hash 307 | var info = new System.IO.FileInfo(path); 308 | long size = info.Length; 309 | if (size > 2000000) 310 | { 311 | decimal MB = (size / (decimal)(1024d * 1024d)); 312 | HashValidationMessage = $"File is {MB:0.00} MB, clearly not a NES ROM. WTF are you doing?"; 313 | IsSourcePathValid = false; 314 | IsHashValid = false; 315 | return false; 316 | } 317 | 318 | // Calculate the file's hash 319 | string hashStrMd5 = ""; 320 | string hashStrSha256 = ""; 321 | 322 | // SHA256 323 | using (var sha = new System.Security.Cryptography.SHA256Managed()) 324 | { 325 | using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read)) 326 | { 327 | byte[] hashSha256 = sha.ComputeHash(fs); 328 | hashStrSha256 = BitConverter.ToString(hashSha256).Replace("-", String.Empty).ToLowerInvariant(); 329 | } 330 | } 331 | 332 | // MD5 333 | using (var md5 = System.Security.Cryptography.MD5.Create()) 334 | { 335 | using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read)) 336 | { 337 | var hashMd5 = md5.ComputeHash(fs); 338 | hashStrMd5 = BitConverter.ToString(hashMd5).Replace("-", "").ToLowerInvariant(); 339 | } 340 | } 341 | 342 | // Update hash strings 343 | HashStringSHA256 = hashStrSha256; 344 | HashStringMD5 = hashStrMd5; 345 | 346 | // Check that the hash matches a supported hash 347 | List md5s = new List(ExpectedMD5s); 348 | List sha256s = new List(ExpectedSHA256s); 349 | IsHashValid = (md5s.Contains(HashStringMD5) && sha256s.Contains(HashStringSHA256)); 350 | if (IsHashValid) 351 | { 352 | HashValidationMessage = "ROM checksum is valid, good to go!"; 353 | } 354 | else 355 | { 356 | HashValidationMessage = "Wrong file checksum. Please try another ROM, or it may not work."; 357 | return false; 358 | } 359 | 360 | // If we made it this far, the file looks good! 361 | return true; 362 | } 363 | } 364 | 365 | public enum PlayerSprite 366 | { 367 | Rockman, 368 | Protoman, 369 | Roll, 370 | Bass 371 | } 372 | } 373 | -------------------------------------------------------------------------------- /MM2RandoLib/RandomMM2.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Diagnostics; 5 | 6 | using MM2Randomizer.Patcher; 7 | using MM2Randomizer.Randomizers; 8 | using MM2Randomizer.Randomizers.Enemies; 9 | using MM2Randomizer.Randomizers.Colors; 10 | using MM2Randomizer.Randomizers.Stages; 11 | using MM2Randomizer.Randomizers.Stages.Components; 12 | using MM2Randomizer.Utilities; 13 | 14 | namespace MM2Randomizer 15 | { 16 | public static class RandomMM2 17 | { 18 | public static int Seed = -1; 19 | public static Random Random; 20 | public static Random RNGCosmetic; 21 | public static Patch Patch; 22 | public static RandoSettings Settings; 23 | public static readonly string TempFileName = "temp.nes"; 24 | public static string RecentlyCreatedFileName = ""; 25 | 26 | public static RStages randomStages; 27 | public static RWeaponGet randomWeaponGet; 28 | public static RWeaponBehavior randomWeaponBehavior; 29 | public static RWeaknesses randomWeaknesses; 30 | public static RBossAI randomBossAI; 31 | public static RItemGet randomItemGet; 32 | public static RTeleporters randomTeleporters; 33 | public static REnemies randomEnemies; 34 | public static REnemyWeaknesses randomEnemyWeakness; 35 | public static RBossRoom randomBossInBossRoom; 36 | public static RTilemap randomTilemap; 37 | public static RColors randomColors; 38 | public static RMusic randomMusic; 39 | public static RText rWeaponNames; 40 | public static List Randomizers; 41 | public static List CosmeticRandomizers; 42 | 43 | /// 44 | /// Perform the randomization based on the seed and user-provided settings, and then 45 | /// generate the new ROM. 46 | /// 47 | public static string RandomizerCreate(bool fromClientApp, int seed) 48 | { 49 | Seed = seed; 50 | 51 | // List of randomizer modules to use; will add modules based on checkbox states 52 | Randomizers = new List(); 53 | CosmeticRandomizers = new List(); 54 | 55 | 56 | ///========================== 57 | /// "CORE" MODULES 58 | ///========================== 59 | // NOTE: Just in case, link RStages, RWeaponGet, and RTeleporter into one "Core Randomizer" module 60 | // Their interdependencies are too risky to separate as options, and likely nobody will want to customize this part anyways. 61 | // Random portrait locations on stage select 62 | randomStages = new RStages(); 63 | // Random weapon awarded from each stage 64 | // WARNING: May be dependent on RTeleporters, verify? 65 | // WARNING: May be dependent on RStages 66 | randomWeaponGet = new RWeaponGet(); 67 | // Random teleporter destinations in Wily 5 68 | randomTeleporters = new RTeleporters(); 69 | 70 | 71 | ///========================== 72 | /// "GAMEPLAY SEED" MODULES 73 | ///========================== 74 | // Caution: RWeaknesses depends on this 75 | randomWeaponBehavior = new RWeaponBehavior(); 76 | 77 | // Depends on RWeaponBehavior (ammo), can use default values 78 | randomWeaknesses = new RWeaknesses(); 79 | 80 | // Caution: RText depends on this, but default values will be used if not enabled. 81 | randomBossInBossRoom = new RBossRoom(); 82 | 83 | // Independent 84 | randomBossAI = new RBossAI(); 85 | 86 | // Independent 87 | randomItemGet = new RItemGet(); 88 | 89 | // Independent 90 | randomEnemies = new REnemies(); 91 | 92 | // Independent 93 | randomEnemyWeakness = new REnemyWeaknesses(); 94 | 95 | // Independent 96 | randomTilemap = new RTilemap(); 97 | 98 | 99 | ///========================== 100 | /// "COSMETIC SEED" MODULES 101 | ///========================== 102 | // Caution: Depends on RBossRoom, but can use default values if its not enabled. 103 | rWeaponNames = new RText(); 104 | 105 | // Independent 106 | randomColors = new RColors(); 107 | 108 | // Independent 109 | randomMusic = new RMusic(); 110 | 111 | 112 | 113 | 114 | 115 | // Add randomizers according to each flag 116 | ///========================== 117 | /// "GAMEPLAY SEED" MODULES 118 | ///========================== 119 | if (Settings.Is8StagesRandom) 120 | { 121 | Randomizers.Add(randomStages); 122 | } 123 | if (Settings.IsWeaponsRandom) 124 | { 125 | Randomizers.Add(randomWeaponGet); 126 | } 127 | if (Settings.IsWeaponBehaviorRandom) 128 | { 129 | Randomizers.Add(randomWeaponBehavior); 130 | } 131 | if (Settings.IsWeaknessRandom) 132 | { 133 | Randomizers.Add(randomWeaknesses); 134 | } 135 | if (Settings.IsBossAIRandom) 136 | { 137 | Randomizers.Add(randomBossAI); 138 | } 139 | if (Settings.IsItemsRandom) 140 | { 141 | Randomizers.Add(randomItemGet); 142 | } 143 | if (Settings.IsTeleportersRandom) 144 | { 145 | Randomizers.Add(randomTeleporters); 146 | } 147 | if (Settings.IsEnemiesRandom) 148 | { 149 | Randomizers.Add(randomEnemies); 150 | } 151 | if (Settings.IsEnemyWeaknessRandom) 152 | { 153 | Randomizers.Add(randomEnemyWeakness); 154 | } 155 | if (Settings.IsBossInBossRoomRandom) 156 | { 157 | Randomizers.Add(randomBossInBossRoom); 158 | } 159 | if (Settings.IsTilemapChangesEnabled) 160 | { 161 | Randomizers.Add(randomTilemap); 162 | } 163 | 164 | ///========================== 165 | /// "COSMETIC SEED" MODULES 166 | ///========================== 167 | if (Settings.IsColorsRandom) 168 | { 169 | CosmeticRandomizers.Add(randomColors); 170 | } 171 | if (Settings.IsBGMRandom) 172 | { 173 | CosmeticRandomizers.Add(randomMusic); 174 | } 175 | if (Settings.IsWeaponNamesRandom) 176 | { 177 | CosmeticRandomizers.Add(rWeaponNames); 178 | } 179 | 180 | 181 | // Instantiate RNG object r based on RandomMM2.Seed 182 | InitializeSeed(); 183 | 184 | // Create randomization patch 185 | Patch = new Patch(); 186 | 187 | // In tournament mode, offset the seed by 1 call, making seeds mode-dependent 188 | if (Settings.IsSpoilerFree) 189 | { 190 | Random.Next(); 191 | RNGCosmetic.Next(); 192 | } 193 | 194 | // Conduct randomization of Gameplay Modules 195 | foreach (IRandomizer randomizer in Randomizers) 196 | { 197 | randomizer.Randomize(Patch, Random); 198 | Debug.WriteLine(randomizer); 199 | } 200 | 201 | // Conduct randomization of Cosmetic Modules 202 | foreach (IRandomizer cosmetic in CosmeticRandomizers) 203 | { 204 | cosmetic.Randomize(Patch, RNGCosmetic); 205 | Debug.WriteLine(cosmetic); 206 | } 207 | 208 | // Apply additional required incidental modifications 209 | if (Settings.Is8StagesRandom || Settings.IsWeaponsRandom) 210 | { 211 | MiscHacks.FixPortraits(Patch, Settings.Is8StagesRandom, randomStages, Settings.IsWeaponsRandom, randomWeaponGet); 212 | MiscHacks.FixWeaponLetters(Patch, randomWeaponGet, randomStages, rWeaponNames); 213 | } 214 | if (Settings.IsEnemiesRandom) 215 | { 216 | MiscHacks.FixM445PaletteGlitch(Patch); 217 | } 218 | 219 | // Apply final optional gameplay modifications 220 | if (Settings.FastText) 221 | { 222 | MiscHacks.SetFastWeaponGetText(Patch); 223 | MiscHacks.SetFastReadyText(Patch); 224 | MiscHacks.SetFastWilyMap(Patch); 225 | MiscHacks.SkipItemGetPages(Patch); 226 | } 227 | if (Settings.BurstChaserMode) 228 | { 229 | MiscHacks.SetBurstChaser(Patch); 230 | } 231 | MiscHacks.DrawTitleScreenChanges(Patch, Seed, Settings); 232 | MiscHacks.SetWily5NoMusicChange(Patch); 233 | MiscHacks.NerfDamageValues(Patch); 234 | MiscHacks.SetETankKeep(Patch); 235 | MiscHacks.PreventETankUseAtFullLife(Patch); 236 | MiscHacks.SetFastBossDefeatTeleport(Patch); 237 | 238 | // Create file name based on seed and game region 239 | string seedAlpha = SeedConvert.ConvertBase10To26(Seed); 240 | string newfilename = $"MM2-RNG-{seedAlpha}.nes"; 241 | 242 | // Apply patch and deliver the ROM; different routine for client vs. web app 243 | if (fromClientApp) 244 | { 245 | //File.Copy(Settings.SourcePath, TempFileName, true); 246 | //using (Stream stream = assembly.GetManifestResourceStream("MM2Randomizer.Resources.MM2.nes")) 247 | // Load user provided ROM 248 | using (Stream stream = new FileStream(Settings.SourcePath, FileMode.Open, FileAccess.Read)) 249 | { 250 | using (Stream output = File.OpenWrite(TempFileName)) 251 | { 252 | stream.CopyTo(output); 253 | } 254 | } 255 | 256 | // Apply pre-patch changes via IPS patch (manual title screen, stage select, stage changes, player sprite) 257 | Patch.ApplyIPSPatch(TempFileName, Properties.Resources.mm2rng_musicpatch); 258 | Patch.ApplyIPSPatch(TempFileName, Properties.Resources.mm2rng_prepatch); 259 | MiscHacks.SetNewMegaManSprite(Patch, TempFileName, Settings.SelectedPlayer); 260 | 261 | // Apply patch with randomized content 262 | Patch.ApplyRandoPatch(TempFileName); 263 | 264 | // If a file of the same seed already exists, delete it 265 | if (File.Exists(newfilename)) 266 | { 267 | File.Delete(newfilename); 268 | } 269 | 270 | // Finish the copy/rename and open Explorer at that location 271 | File.Move(TempFileName, newfilename); 272 | RecentlyCreatedFileName = newfilename; 273 | Settings.HashValidationMessage = "Successfully copied and patched! File: " + newfilename; 274 | return newfilename; 275 | } 276 | else 277 | { 278 | //File.Copy(Settings.SourcePath, TempFileName, true); 279 | string serverDir = $@"C:\mm2rng\{seedAlpha}"; 280 | Directory.CreateDirectory(serverDir); 281 | 282 | string serverPathTemp = Path.Combine(serverDir, TempFileName); 283 | string serverPathNew = Path.Combine(serverDir, newfilename); 284 | using (Stream stream = new FileStream("MM2.nes", FileMode.Open)) 285 | { 286 | using (Stream output = File.OpenWrite(serverPathTemp)) 287 | { 288 | stream.CopyTo(output); 289 | } 290 | } 291 | 292 | // Apply pre-patch changes via IPS patch (manual title screen, stage select, and stage changes) 293 | Patch.ApplyIPSPatch(serverPathTemp, Properties.Resources.mm2rng_musicpatch); 294 | Patch.ApplyIPSPatch(serverPathTemp, Properties.Resources.mm2rng_prepatch); 295 | 296 | // Apply patch with randomized content 297 | Patch.ApplyRandoPatch(serverPathTemp); 298 | 299 | // If a file of the same seed already exists, delete it 300 | if (File.Exists(serverPathNew)) 301 | { 302 | File.Delete(serverPathNew); 303 | } 304 | 305 | // Finish the copy/rename and open Explorer at that location 306 | File.Move(serverPathTemp, serverPathNew); 307 | RecentlyCreatedFileName = serverPathNew; 308 | return serverPathNew; 309 | } 310 | } 311 | 312 | /// 313 | /// Create a random seed or use the user-provided seed. 314 | /// 315 | private static void InitializeSeed() 316 | { 317 | if (Seed < 0) 318 | { 319 | Random rndSeed = new Random(); 320 | Seed = rndSeed.Next(int.MaxValue); 321 | } 322 | Random = new Random(Seed); 323 | RNGCosmetic = new Random(Seed); 324 | } 325 | 326 | /// 327 | /// Shuffle the elements of the provided list. 328 | /// 329 | /// The Type of the elements in the list. 330 | /// The object to be shuffled. 331 | /// The seed used to perform the shuffling. 332 | /// A reference to the shuffled list. 333 | public static IList Shuffle(this IList list, Random rng) 334 | { 335 | int n = list.Count; 336 | while (n > 1) 337 | { 338 | n--; 339 | int k = rng.Next(n + 1); 340 | T value = list[k]; 341 | list[k] = list[n]; 342 | list[n] = value; 343 | } 344 | return list; 345 | } 346 | } 347 | } 348 | -------------------------------------------------------------------------------- /MM2RandoLib/Randomizers/Colors/ColorSet.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | using MM2Randomizer.Enums; 5 | using MM2Randomizer.Patcher; 6 | 7 | namespace MM2Randomizer.Randomizers.Colors 8 | { 9 | public class ColorSet 10 | { 11 | public int[] addresses; 12 | public List ColorBytes; 13 | public int Index; 14 | 15 | public ColorSet() 16 | { 17 | ColorBytes = new List(); 18 | Index = 0; 19 | } 20 | 21 | public void RandomizeAndWrite(Patch patch, Random rand, int setNumber) 22 | { 23 | Index = rand.Next(ColorBytes.Count); 24 | 25 | for (int i = 0; i < addresses.Length; i++) 26 | { 27 | patch.Add(addresses[i], (byte)ColorBytes[Index][i], String.Format("Color Set {0} (Index Chosen: {1}) Value #{2}", setNumber, Index, i)); 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /MM2RandoLib/Randomizers/Enemies/EnemyInstance.cs: -------------------------------------------------------------------------------- 1 | using MM2Randomizer.Enums; 2 | 3 | namespace MM2Randomizer.Randomizers.Enemies 4 | { 5 | public class EnemyInstance 6 | { 7 | public bool HasIDChanged 8 | { 9 | get { return EnemyID != EnemyIDPrev; } 10 | } 11 | 12 | public int Offset { get; set; } 13 | public int StageNum { get; set; } 14 | public int RoomNum { get; set; } 15 | public int ScreenNum { get; set; } 16 | public bool IsActive { get; set; } 17 | public int EnemyID { get; set; } 18 | public int EnemyIDPrev { get; set; } // for reference only 19 | public int X { get; set; } 20 | public int YOriginal { get; set; } // for reference only 21 | public int YAir { get; set; } 22 | public int YGround { get; set; } 23 | public bool IsFaceRight { get; set; } 24 | 25 | //public bool IsRandomiziable { get; set; } 26 | //public List PatternTableAddresses { get; set; } 27 | //public List SpriteBankRows { get; set; } 28 | 29 | public EnemyInstance(int offset, int stage, int room, int screen, bool isActive, int id, int xOriginal, int yOriginal, int yAir, int yGround, bool faceRight) 30 | { 31 | Offset = offset; 32 | StageNum = stage; 33 | RoomNum = room; 34 | ScreenNum = screen; 35 | IsActive = isActive; 36 | EnemyID = id; 37 | EnemyIDPrev = id; 38 | X = xOriginal; 39 | YOriginal = yOriginal; 40 | YAir = yAir; 41 | YGround = yGround; 42 | IsFaceRight = faceRight; 43 | } 44 | 45 | public bool HasNewActivator() 46 | { 47 | if (!HasIDChanged) return false; 48 | 49 | switch ((EEnemyID)EnemyID) 50 | { 51 | case EEnemyID.Pipi_Activator: 52 | return true; 53 | case EEnemyID.Mole_Activator: 54 | return true; 55 | case EEnemyID.Claw_Activator: 56 | return true; 57 | case EEnemyID.Kukku_Activator: 58 | return true; 59 | case EEnemyID.M445_Activator: 60 | return true; 61 | default: return false; 62 | } 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /MM2RandoLib/Randomizers/Enemies/EnemyType.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | using MM2Randomizer.Enums; 4 | 5 | namespace MM2Randomizer.Randomizers.Enemies 6 | { 7 | public class EnemyType 8 | { 9 | public EEnemyID ID { get; set; } 10 | public List PatternTableAddresses { get; set; } 11 | public List SpriteBankRows { get; set; } 12 | public bool IsYPosAir { get; set; } 13 | public bool IsActivator { get; set; } 14 | 15 | /// 16 | /// Helps determine y-position adjustment for spawning. If height = 0, it can spawn at the top 17 | /// of the screen (pipis, deactivators, etc.) 18 | /// 19 | public int YAdjust { get; set; } 20 | 21 | public EnemyType(EEnemyID id, List patternTableAddresses, List spriteBankRows, bool isActivator, bool isYPosAir = false, int yAdjust = 0) 22 | { 23 | ID = id; 24 | PatternTableAddresses = patternTableAddresses; 25 | SpriteBankRows = spriteBankRows; 26 | IsActivator = isActivator; 27 | IsYPosAir = isYPosAir; 28 | YAdjust = yAdjust; 29 | } 30 | 31 | public static EEnemyID GetCorrespondingDeactivator(EEnemyID activator) 32 | { 33 | switch (activator) 34 | { 35 | case EEnemyID.Pipi_Activator: 36 | return EEnemyID.Pipi_Deactivator; 37 | case EEnemyID.Mole_Activator: 38 | return EEnemyID.Mole_Deactivator; 39 | case EEnemyID.Claw_Activator: 40 | return EEnemyID.Claw_Deactivator; 41 | case EEnemyID.Kukku_Activator: 42 | return EEnemyID.Kukku_Deactivator; 43 | case EEnemyID.M445_Activator: 44 | return EEnemyID.M445_Deactivator; 45 | default: return activator; 46 | } 47 | } 48 | 49 | public static EEnemyID GetCorrespondingDeactivator(int deactivatorID) 50 | { 51 | return GetCorrespondingDeactivator((EEnemyID)deactivatorID); 52 | } 53 | 54 | public static bool CheckIsActivator(int enemyID) 55 | { 56 | switch ((EEnemyID)enemyID) 57 | { 58 | case EEnemyID.Pipi_Activator: 59 | return true; 60 | case EEnemyID.Mole_Activator: 61 | return true; 62 | case EEnemyID.Claw_Activator: 63 | return true; 64 | case EEnemyID.Kukku_Activator: 65 | return true; 66 | case EEnemyID.M445_Activator: 67 | return true; 68 | default: return false; 69 | } 70 | } 71 | 72 | public static bool CheckIsDeactivator(int enemyID) 73 | { 74 | switch ((EEnemyID)enemyID) 75 | { 76 | case EEnemyID.Pipi_Deactivator: 77 | return true; 78 | case EEnemyID.Mole_Deactivator: 79 | return true; 80 | case EEnemyID.Claw_Deactivator: 81 | return true; 82 | case EEnemyID.Kukku_Deactivator: 83 | return true; 84 | case EEnemyID.M445_Deactivator: 85 | return true; 86 | default: return false; 87 | } 88 | } 89 | 90 | 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /MM2RandoLib/Randomizers/Enemies/REnemyWeaknesses.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using MM2Randomizer.Patcher; 6 | 7 | namespace MM2Randomizer.Randomizers.Enemies 8 | { 9 | public class REnemyWeaknesses : IRandomizer 10 | { 11 | private readonly static int EnemyDamageAddressP = 0x03E9A8; 12 | private readonly static int EnemyDamageAddressH = 0x03EA24; 13 | private readonly static int EnemyDamageAddressA = 0x03EA9C; 14 | private readonly static int EnemyDamageAddressW = 0x03EB14; 15 | private readonly static int EnemyDamageAddressB = 0x03EB8C; 16 | private readonly static int EnemyDamageAddressQ = 0x03EC04; 17 | private readonly static int EnemyDamageAddressC = 0x03EC7C; 18 | private readonly static int EnemyDamageAddressM = 0x03ECF4; 19 | 20 | // NOTE: Will have to change these indices if enemies are added/removed from enemyweaknesses.csv! 21 | private readonly static int EnemyIndexInShotArray_Friender = 8; 22 | 23 | private StringBuilder debug = new StringBuilder(); 24 | private List enemyNames = new List(); 25 | private List offsets = new List(); 26 | private List shotP = new List(); 27 | private List shotH = new List(); 28 | private List shotA = new List(); 29 | private List shotW = new List(); 30 | private List shotB = new List(); 31 | private List shotQ = new List(); 32 | private List shotC = new List(); 33 | private List shotM = new List(); 34 | 35 | public REnemyWeaknesses() { } 36 | 37 | public override string ToString() 38 | { 39 | return debug.ToString(); 40 | } 41 | 42 | public void Randomize(Patch p, Random r) 43 | { 44 | string[] lines = Properties.Resources.enemyweakness.Split(new string[] { Environment.NewLine }, StringSplitOptions.None); 45 | foreach (string line in lines) 46 | { 47 | if (line.StartsWith("#")) continue; // Ignore comment lines 48 | 49 | string[] cols = line.Split(new char[] { ',' }); 50 | 51 | enemyNames.Add(cols[0]); // Enemy name 52 | offsets.Add(Convert.ToInt32(cols[1], 16)); // Offset 53 | shotP.Add(byte.Parse(cols[2])); // Buster 54 | shotH.Add(byte.Parse(cols[3])); // Heat 55 | shotA.Add(byte.Parse(cols[4])); // Air 56 | shotW.Add(byte.Parse(cols[5])); // Wood 57 | shotB.Add(byte.Parse(cols[6])); // Bubble 58 | shotQ.Add(byte.Parse(cols[7])); // Quick 59 | shotC.Add(byte.Parse(cols[8])); // Clash 60 | shotM.Add(byte.Parse(cols[9])); // Metal 61 | } 62 | 63 | shotP.Shuffle(r); 64 | shotH.Shuffle(r); 65 | shotA.Shuffle(r); 66 | shotW.Shuffle(r); 67 | shotB.Shuffle(r); 68 | shotQ.Shuffle(r); 69 | shotC.Shuffle(r); 70 | shotM.Shuffle(r); 71 | 72 | // Force Buster to always do 1 damage to minibosses 73 | shotP[EnemyIndexInShotArray_Friender] = 0x01; 74 | 75 | // To each enemy... 76 | for (int i = 0; i < offsets.Count; i++) 77 | { 78 | // ...apply each weapon's damage 79 | p.Add(EnemyDamageAddressP + offsets[i], shotP[i], $"{enemyNames[i]} damage from P"); 80 | p.Add(EnemyDamageAddressH + offsets[i], shotH[i], $"{enemyNames[i]} damage from H"); 81 | p.Add(EnemyDamageAddressA + offsets[i], shotA[i], $"{enemyNames[i]} damage from A"); 82 | p.Add(EnemyDamageAddressW + offsets[i], shotW[i], $"{enemyNames[i]} damage from W"); 83 | p.Add(EnemyDamageAddressB + offsets[i], shotB[i], $"{enemyNames[i]} damage from B"); 84 | p.Add(EnemyDamageAddressQ + offsets[i], shotQ[i], $"{enemyNames[i]} damage from Q"); 85 | p.Add(EnemyDamageAddressC + offsets[i], shotC[i], $"{enemyNames[i]} damage from C"); 86 | p.Add(EnemyDamageAddressM + offsets[i], shotM[i], $"{enemyNames[i]} damage from M"); 87 | 88 | // Furthermore, there are 3 enemy types that need a second array of damage values 89 | // - Shrink (instance vs. spawner) 90 | // - Mole (moving up vs. moving down) 91 | // - Shotman (facing left vs. facing right) 92 | // The corresponding auxiliary types are omitted from the shuffle. 93 | // Instead, assign common weaknesses for a more consistent playing experience. 94 | // Each auxiliary type occurs at the next offset, offsets[i] + 1 95 | 96 | // Shrink 0x00 apply same damage to Shrink Spawner 0x01 97 | // Mole (Up) 0x48 apply same damage to Mole (Down) 0x49 98 | // Shotman (Left) 0x4B apply same damage to Shotman (Right) 0x4C 99 | if (offsets[i] == 0x00 || offsets[i] == 0x48 || offsets[i] == 0x4B) 100 | { 101 | p.Add(EnemyDamageAddressP + offsets[i] + 1, shotP[i], $"{enemyNames[i]} damage from P"); 102 | p.Add(EnemyDamageAddressH + offsets[i] + 1, shotH[i], $"{enemyNames[i]} damage from H"); 103 | p.Add(EnemyDamageAddressA + offsets[i] + 1, shotA[i], $"{enemyNames[i]} damage from A"); 104 | p.Add(EnemyDamageAddressW + offsets[i] + 1, shotW[i], $"{enemyNames[i]} damage from W"); 105 | p.Add(EnemyDamageAddressB + offsets[i] + 1, shotB[i], $"{enemyNames[i]} damage from B"); 106 | p.Add(EnemyDamageAddressQ + offsets[i] + 1, shotQ[i], $"{enemyNames[i]} damage from Q"); 107 | p.Add(EnemyDamageAddressC + offsets[i] + 1, shotC[i], $"{enemyNames[i]} damage from C"); 108 | p.Add(EnemyDamageAddressM + offsets[i] + 1, shotM[i], $"{enemyNames[i]} damage from M"); 109 | } 110 | } 111 | 112 | // Format nice debug table 113 | debug.AppendLine("Enemy Weaknesses:"); 114 | debug.AppendLine("\t\t\t\t\t\tP\tH\tA\tW\tB\tQ\tM\tC:"); 115 | debug.AppendLine("--------------------------------------------------------"); 116 | for (int i = 0; i < offsets.Count; i++) 117 | { 118 | debug.AppendLine($"{enemyNames[i]}\t{shotP[i]}\t{shotH[i]}\t{shotA[i]}\t{shotW[i]}\t{shotB[i]}\t{shotQ[i]}\t{shotC[i]}\t{shotM[i]}"); 119 | } 120 | debug.Append(Environment.NewLine); 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /MM2RandoLib/Randomizers/Enemies/Room.cs: -------------------------------------------------------------------------------- 1 | using MM2Randomizer.Enums; 2 | using System.Collections.Generic; 3 | 4 | namespace MM2Randomizer.Randomizers.Enemies 5 | { 6 | public class Room 7 | { 8 | public List EnemyInstances { get; set; } 9 | public int RoomNum { get; set; } 10 | 11 | public Room(int roomNum) 12 | { 13 | RoomNum = roomNum; 14 | EnemyInstances = new List(); 15 | } 16 | 17 | public EEnemyID? GetActivatorIfOneHasBeenAdded() 18 | { 19 | foreach (EnemyInstance enemy in EnemyInstances) 20 | { 21 | // This instance hasn't been replaced; keep old activator type if one is there 22 | if (!enemy.HasIDChanged) continue; 23 | 24 | EEnemyID id = (EEnemyID)enemy.EnemyID; 25 | if (id == EEnemyID.Pipi_Activator || 26 | id == EEnemyID.Mole_Activator || 27 | id == EEnemyID.Claw_Activator || 28 | id == EEnemyID.M445_Activator || 29 | id == EEnemyID.Kukku_Activator) 30 | { 31 | return id; 32 | } 33 | } 34 | return null; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /MM2RandoLib/Randomizers/Enemies/SpriteBankRoomGroup.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | using MM2Randomizer.Enums; 4 | 5 | namespace MM2Randomizer.Randomizers.Enemies 6 | { 7 | public class SpriteBankRoomGroup 8 | { 9 | public EStageID Stage { get; set; } 10 | 11 | /// 12 | /// Room numbers in the stage that this sprite bank applies to. Will always be sorted from least to greatest. 13 | /// 14 | public List Rooms { get; set; } 15 | public int PatternAddressStart { get; set; } 16 | public List NewEnemyTypes { get; set; } 17 | 18 | public bool IsSpriteRestricted { get; set; } 19 | public List SpriteBankRowsRestriction { get; set; } 20 | public List PatternTableAddressesRestriction { get; set; } 21 | 22 | public SpriteBankRoomGroup (EStageID stage, int patternAddressStart, int[] roomNums) 23 | { 24 | this.Stage = stage; 25 | this.PatternAddressStart = patternAddressStart; 26 | 27 | Rooms = new List(); 28 | for (int i = 0; i < roomNums.Length; i++) 29 | { 30 | Rooms.Add(new Room(roomNums[i])); 31 | } 32 | 33 | //EnemyInstances = new List(); 34 | NewEnemyTypes = new List(); 35 | IsSpriteRestricted = false; 36 | } 37 | 38 | public SpriteBankRoomGroup (EStageID stage, int patternAddressStart, int[] roomNums, int[] spriteBankRowsRestriction, byte[] patternTableAddressesRestriction/*, params EnemyInstance[] enemyInstances*/) 39 | : this(stage, patternAddressStart, roomNums) 40 | { 41 | SpriteBankRowsRestriction = new List(spriteBankRowsRestriction); 42 | PatternTableAddressesRestriction = new List(patternTableAddressesRestriction); 43 | IsSpriteRestricted = true; 44 | } 45 | 46 | public bool ContainsRoom(int roomNum) 47 | { 48 | foreach (Room room in Rooms) 49 | { 50 | if (room.RoomNum == roomNum) 51 | { 52 | return true; 53 | } 54 | } 55 | return false; 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /MM2RandoLib/Randomizers/IRandomizer.cs: -------------------------------------------------------------------------------- 1 | using MM2Randomizer.Patcher; 2 | using System; 3 | 4 | namespace MM2Randomizer.Randomizers 5 | { 6 | public interface IRandomizer 7 | { 8 | void Randomize(Patch p, Random r); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /MM2RandoLib/Randomizers/RBossRoom.cs: -------------------------------------------------------------------------------- 1 | using MM2Randomizer.Enums; 2 | using MM2Randomizer.Patcher; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | 8 | namespace MM2Randomizer.Randomizers 9 | { 10 | public class RBossRoom : IRandomizer 11 | { 12 | public class BossRoomRandomComponent 13 | { 14 | // This seems to be every table necessary to shuffle for getting a boss 15 | // to function and display properly in a different boss room. 16 | public byte IntroValue { get; set; } // 0F 0F 0B 05 09 07 05 03 17 | public byte AIPtrByte1 { get; set; } // C5 E3 FB 56 9E 56 20 C3 18 | public byte AIPtrByte2 { get; set; } // 80 82 84 86 87 89 8B 8C 19 | public byte GfxFix1 { get; set; } // 50 66 6C 60 54 5A 63 69 20 | public byte GfxFix2 { get; set; } // 51 67 6D 61 55 5C 64 6A 21 | public byte YPosFix1 { get; set; } // 09 0C 0F 0A 09 09 08 08 22 | public byte YPosFix2 { get; set; } // 0C 10 10 0C 0C 0C 0C 0C 23 | public byte[] SpriteBankSlotRowsBytes { get; set; } 24 | public int OriginalBossIndex { get; set; } 25 | // ...and maybe room layout??? 26 | 27 | public BossRoomRandomComponent(int original, byte introValue, byte aiPtr1, byte aiPtr2, byte gfxfix1, byte gfxfix2, byte yFix1, byte yFix2, byte[] spriteBankSlotRows) 28 | { 29 | this.IntroValue = introValue; 30 | this.AIPtrByte1 = aiPtr1; 31 | this.AIPtrByte2 = aiPtr2; 32 | this.GfxFix1 = gfxfix1; 33 | this.GfxFix2 = gfxfix2; 34 | this.YPosFix1 = yFix1; 35 | this.YPosFix2 = yFix2; 36 | this.SpriteBankSlotRowsBytes = spriteBankSlotRows; 37 | this.OriginalBossIndex = original; 38 | } 39 | } 40 | 41 | public RBossRoom() 42 | { 43 | // Initialize BossRoomRandomComponents, and add to "Components" list. 44 | // This list is still needed even if this module isn't enabled; it just won't get shuffled. 45 | BossRoomRandomComponent HeatManComponent = new BossRoomRandomComponent( 46 | original: 0, 47 | introValue: 0x0F, // 0x02C15E 48 | aiPtr1: 0xC5, // 0x02C057 49 | aiPtr2: 0x80, // 0x02C065 50 | gfxfix1: 0x50, // 0x02E4E9 51 | gfxfix2: 0x51, // 0x02C166 52 | yFix1: 0x09, // 0x02C14E 53 | yFix2: 0x0C, // 0x02C156 54 | spriteBankSlotRows: new byte[] { 55 | 0x98, 0x06, 56 | 0x99, 0x06, 57 | 0x9A, 0x06, 58 | 0x9B, 0x06, 59 | 0x9C, 0x06, 60 | 0x9D, 0x06, 61 | }); 62 | 63 | BossRoomRandomComponent AirManComponent = new BossRoomRandomComponent( 64 | original: 1, 65 | introValue: 0x0F, 66 | aiPtr1: 0xE3, 67 | aiPtr2: 0x82, 68 | gfxfix1: 0x66, 69 | gfxfix2: 0x67, 70 | yFix1: 0x0C, 71 | yFix2: 0x10, 72 | spriteBankSlotRows: new byte[] { 73 | 0xAB, 0x05, 74 | 0xAC, 0x05, 75 | 0xAD, 0x05, 76 | 0xAA, 0x06, 77 | 0xAB, 0x06, 78 | 0xAC, 0x06, 79 | }); 80 | 81 | BossRoomRandomComponent WoodManComponent = new BossRoomRandomComponent( 82 | original: 2, 83 | introValue: 0x0B, 84 | aiPtr1: 0xFB, 85 | aiPtr2: 0x84, 86 | gfxfix1: 0x6C, 87 | gfxfix2: 0x6D, 88 | yFix1: 0x0F, 89 | yFix2: 0x10, 90 | spriteBankSlotRows: new byte[] { 91 | 0xAC, 0x06, 92 | 0xAD, 0x06, 93 | 0xAE, 0x06, 94 | 0xAF, 0x06, 95 | 0xB0, 0x06, 96 | 0xB1, 0x06, 97 | }); 98 | 99 | BossRoomRandomComponent BubbleManComponent = new BossRoomRandomComponent( 100 | original: 3, 101 | introValue: 0x05, 102 | aiPtr1: 0x56, 103 | aiPtr2: 0x86, 104 | gfxfix1: 0x60, 105 | gfxfix2: 0x61, 106 | yFix1: 0x0A, 107 | yFix2: 0x0C, 108 | spriteBankSlotRows: new byte[] { 109 | 0x98, 0x07, 110 | 0x99, 0x07, 111 | 0x9A, 0x07, 112 | 0x9B, 0x07, 113 | 0x9C, 0x07, 114 | 0x9D, 0x07, 115 | }); 116 | 117 | BossRoomRandomComponent QuickManComponent = new BossRoomRandomComponent( 118 | original: 4, 119 | introValue: 0x09, 120 | aiPtr1: 0x9E, 121 | aiPtr2: 0x87, 122 | gfxfix1: 0x54, 123 | gfxfix2: 0x55, 124 | yFix1: 0x09, 125 | yFix2: 0x0C, 126 | spriteBankSlotRows: new byte[] { 127 | 0x90, 0x07, 128 | 0x91, 0x07, 129 | 0x92, 0x07, 130 | 0x93, 0x07, 131 | 0x94, 0x07, 132 | 0x95, 0x07, 133 | }); 134 | 135 | BossRoomRandomComponent FlashManComponent = new BossRoomRandomComponent( 136 | original: 5, 137 | introValue: 0x07, 138 | aiPtr1: 0x56, 139 | aiPtr2: 0x89, 140 | gfxfix1: 0x5A, 141 | gfxfix2: 0x5C, 142 | yFix1: 0x09, 143 | yFix2: 0x0C, 144 | spriteBankSlotRows: new byte[] { 145 | 0x9E, 0x06, 146 | 0x9F, 0x06, 147 | 0x96, 0x07, 148 | 0x97, 0x07, 149 | 0x9E, 0x07, 150 | 0x9F, 0x07, 151 | }); 152 | 153 | BossRoomRandomComponent MetalManComponent = new BossRoomRandomComponent( 154 | original: 6, 155 | introValue: 0x05, 156 | aiPtr1: 0x20, 157 | aiPtr2: 0x8B, 158 | gfxfix1: 0x63, 159 | gfxfix2: 0x64, 160 | yFix1: 0x08, 161 | yFix2: 0x0C, 162 | spriteBankSlotRows: new byte[] { 163 | 0xB0, 0x03, 164 | 0xB1, 0x03, 165 | 0xB2, 0x03, 166 | 0xB3, 0x03, 167 | 0xAA, 0x05, 168 | 0xAB, 0x05, 169 | }); 170 | 171 | BossRoomRandomComponent ClashManComponent = new BossRoomRandomComponent( 172 | original: 7, 173 | introValue: 0x03, 174 | aiPtr1: 0xC3, 175 | aiPtr2: 0x8C, 176 | gfxfix1: 0x69, 177 | gfxfix2: 0x6A, 178 | yFix1: 0x08, 179 | yFix2: 0x0C, 180 | spriteBankSlotRows: new byte[] { 181 | 0xAE, 0x05, 182 | 0xAF, 0x05, 183 | 0xB0, 0x05, 184 | 0xB1, 0x05, 185 | 0xB2, 0x05, 186 | 0xB3, 0x05, 187 | } 188 | ); 189 | 190 | Components = new List 191 | { 192 | HeatManComponent, 193 | AirManComponent, 194 | WoodManComponent, 195 | BubbleManComponent, 196 | QuickManComponent, 197 | FlashManComponent, 198 | MetalManComponent, 199 | ClashManComponent, 200 | }; 201 | } 202 | 203 | public List Components { get; set; } 204 | 205 | /// 206 | /// Shuffle which Robot Master awards which weapon. 207 | /// 208 | public void Randomize(Patch Patch, Random r) 209 | { 210 | Components.Shuffle(r); 211 | //DEBUG test a boss in a particular boss room, also comment out the corresponding boss from the Components list above 212 | //Components.Insert(3, BubbleManComponent); 213 | 214 | // Write in new boss positions 215 | for (int i = 0; i < 8; i++) 216 | { 217 | var bossroom = Components[i]; 218 | Patch.Add(0x02C15E + i, bossroom.IntroValue, $"Boss Intro Value for Boss Room {i}"); 219 | Patch.Add(0x02C057 + i, bossroom.AIPtrByte1, $"Boss AI Ptr Byte1 for Boss Room {i}"); 220 | Patch.Add(0x02C065 + i, bossroom.AIPtrByte2, $"Boss AI Ptr Byte2 for Boss Room {i}"); 221 | Patch.Add(0x02E4E9 + i, bossroom.GfxFix1, $"Boss GFX Fix 1 for Boss Room {i}"); 222 | Patch.Add(0x02C166 + i, bossroom.GfxFix1, $"Boss GFX Fix 2 for Boss Room {i}"); 223 | Patch.Add(0x02C14E + i, bossroom.YPosFix1, $"Boss Y-Pos Fix1 for Boss Room {i}"); 224 | Patch.Add(0x02C156 + i, bossroom.YPosFix2, $"Boss Y-Pos Fix2 for Boss Room {i}"); 225 | } 226 | 227 | // Adjust sprite banks for each boss room 228 | int[] spriteBankBossRoomAddresses = new int[] 229 | { 230 | 0x0034A6, // Heat room 231 | 0x0074A6, // Air room 232 | 0x00B4DC, // Wood room 233 | 0x00F4A6, // Bubble room 234 | 0x0134B8, // Quick room 235 | 0x0174A6, // Flash room 236 | 0x01B494, // Metal room 237 | 0x01F4DC, // Clash room 238 | }; 239 | for (int i = 0; i < spriteBankBossRoomAddresses.Length; i++) 240 | { 241 | for (int j = 0; j < Components[i].SpriteBankSlotRowsBytes.Length; j++) 242 | { 243 | Patch.Add(spriteBankBossRoomAddresses[i] + j, 244 | Components[i].SpriteBankSlotRowsBytes[j], 245 | $"Boss Room {i} Sprite Bank Swap {j}"); 246 | } 247 | } 248 | 249 | // Undo shuffling of damage values for each boss room 250 | int contactDmgTbl = 0x2E9C2; 251 | byte[] originalDmgVals = new byte[] { 08,08,08,04,04,04,06,04 }; 252 | byte[] newDmgVals = new byte[8]; 253 | for (int i = 0; i < Components.Count; i++) 254 | { 255 | newDmgVals[i] = originalDmgVals[Components[i].OriginalBossIndex]; 256 | Patch.Add(contactDmgTbl + i, newDmgVals[i]); 257 | } 258 | } 259 | } 260 | } 261 | -------------------------------------------------------------------------------- /MM2RandoLib/Randomizers/RItemGet.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | using MM2Randomizer.Patcher; 5 | using MM2Randomizer.Enums; 6 | 7 | namespace MM2Randomizer.Randomizers 8 | { 9 | public class RItemGet : IRandomizer 10 | { 11 | public RItemGet() { } 12 | 13 | /// 14 | /// Shuffle which Robot Master awards Items 1, 2, and 3. 15 | /// 16 | public void Randomize(Patch patch, Random r) 17 | { 18 | // 0x03C291 - Item # from Heat Man 19 | // 0x03C292 - Item # from Air Man 20 | // 0x03C293 - Item # from Wood Man 21 | // 0x03C294 - Item # from Bubble Man 22 | // 0x03C295 - Item # from Quick Man 23 | // 0x03C296 - Item # from Flash Man 24 | // 0x03C297 - Item # from Metal Man 25 | // 0x03C298 - Item # from Crash Man 26 | 27 | List newItemOrder = new List(); 28 | for (byte i = 0; i < 5; i++) newItemOrder.Add(EItemNumber.None); 29 | newItemOrder.Add(EItemNumber.One); 30 | newItemOrder.Add(EItemNumber.Two); 31 | newItemOrder.Add(EItemNumber.Three); 32 | newItemOrder.Shuffle(r); 33 | 34 | for (int i = 0; i < 8; i++) 35 | { 36 | patch.Add((int)EItemStageAddress.HeatMan + i, (byte)newItemOrder[i], String.Format("{0}man Item Get", ((EDmgVsBoss.Offset)i).ToString())); 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /MM2RandoLib/Randomizers/RTeleporters.cs: -------------------------------------------------------------------------------- 1 | using MM2Randomizer.Enums; 2 | using MM2Randomizer.Patcher; 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | namespace MM2Randomizer.Randomizers 7 | { 8 | public class RTeleporters : IRandomizer 9 | { 10 | public RTeleporters() { } 11 | 12 | public void Randomize(Patch patch, Random r) 13 | { 14 | // Create list of default teleporter position values 15 | List coords = new List 16 | { 17 | new byte[]{ 0x20, 0x3B }, // Teleporter X, Y (top-left) 18 | new byte[]{ 0x20, 0x7B }, 19 | new byte[]{ 0x20, 0xBB }, 20 | new byte[]{ 0x70, 0xBB }, 21 | new byte[]{ 0x90, 0xBB }, 22 | new byte[]{ 0xE0, 0x3B }, 23 | new byte[]{ 0xE0, 0x7B }, 24 | new byte[]{ 0xE0, 0xBB } 25 | }; 26 | 27 | // Randomize them 28 | coords.Shuffle(r); 29 | 30 | // Write the new x-coordinates 31 | for (int i = 0; i < coords.Count; i++) 32 | { 33 | byte[] location = coords[i]; 34 | patch.Add((int)(EMiscAddresses.WarpXCoordinateStartAddress + i), location[0], String.Format("Teleporter {0} X-Pos", i)); 35 | } 36 | 37 | // Write the new y-coordinates 38 | for (int i = 0; i < coords.Count; i++) 39 | { 40 | byte[] location = coords[i]; 41 | patch.Add((int)(EMiscAddresses.WarpYCoordinateStartAddress + i), location[1], String.Format("Teleporter {0} Y-Pos", i)); 42 | } 43 | 44 | // These values will be copied over to $04b0 (y) and $0470 (x), which will be checked 45 | // for in real time to determine where Mega will teleport to 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /MM2RandoLib/Randomizers/RTilemap.cs: -------------------------------------------------------------------------------- 1 | using MM2Randomizer.Patcher; 2 | using MM2Randomizer.Randomizers.Stages.Components; 3 | using Newtonsoft.Json; 4 | using System; 5 | 6 | namespace MM2Randomizer.Randomizers 7 | { 8 | public class RTilemap : IRandomizer 9 | { 10 | public RTilemap() { } 11 | 12 | 13 | public void Randomize(Patch p, Random r) 14 | { 15 | //ReadLevelComponentJSON(p, r); 16 | 17 | ChangeW4FloorsBeforeSpikes(p, r); 18 | ChangeW4FloorsSpikePit(p, r); 19 | } 20 | 21 | private void ReadLevelComponentJSON(Patch p, Random r) 22 | { 23 | ComponentManager component = JsonConvert.DeserializeObject(Properties.Resources.level_components); 24 | 25 | foreach (var levelComponent in component.LevelComponents) 26 | { 27 | // Get a random variation 28 | var variation = levelComponent.Variations[r.Next(levelComponent.Variations.Count)]; 29 | 30 | // Add patch for each element of the tsamap 31 | int startAddress = Convert.ToInt32(levelComponent.StartAddress, 16); 32 | for (int i = 0; i < variation.TsaMap.Length; i++) 33 | { 34 | // Parse hex string 35 | byte tsaVal = Convert.ToByte(variation.TsaMap[i], 16); 36 | 37 | p.Add(startAddress + i, tsaVal, $"Tilemap data for {levelComponent.Name} variation \"{variation.Name}\""); 38 | } 39 | } 40 | 41 | Console.WriteLine(component.LevelComponents); 42 | } 43 | 44 | private static void ChangeW4FloorsBeforeSpikes(Patch Patch, Random r) 45 | { 46 | // Choose 2 of the 5 32x32 tiles to be fake 47 | int tileA = r.Next(5); 48 | int tileB = r.Next(4); 49 | 50 | // Make sure 2nd tile chosen is different 51 | if (tileB == tileA) tileB++; 52 | 53 | for (int i = 0; i < 5; i++) 54 | { 55 | if (i == tileA || i == tileB) 56 | { 57 | Patch.Add(0x00CB5C + i * 8, 0x94, String.Format("Wily 4 Room 4 Tile {0} (fake)", i)); 58 | } 59 | else 60 | { 61 | Patch.Add(0x00CB5C + i * 8, 0x85, String.Format("Wily 4 Room 4 Tile {0} (solid)", i)); 62 | } 63 | } 64 | } 65 | 66 | private static void ChangeW4FloorsSpikePit(Patch Patch, Random r) 67 | { 68 | // 5 tiles, but since two adjacent must construct a gap, 4 possible gaps. Choose 1 random gap. 69 | int gap = r.Next(4); 70 | for (int i = 0; i < 4; i++) 71 | { 72 | if (i == gap) 73 | { 74 | Patch.Add(0x00CB9A + i * 8, 0x9B, String.Format("Wily 4 Room 5 Tile {0} (gap on right)", i)); 75 | Patch.Add(0x00CB9A + i * 8 + 8, 0x9C, String.Format("Wily 4 Room 5 Tile {0} (gap on left)", i)); 76 | ++i; // skip next tile since we just drew it 77 | } 78 | else 79 | { 80 | Patch.Add(0x00CB9A + i * 8, 0x9D, String.Format("Wily 4 Room 5 Tile {0} (solid)", i)); 81 | } 82 | } 83 | } 84 | 85 | 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /MM2RandoLib/Randomizers/RWeaponGet.cs: -------------------------------------------------------------------------------- 1 | using MM2Randomizer.Enums; 2 | using MM2Randomizer.Patcher; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | 7 | namespace MM2Randomizer.Randomizers 8 | { 9 | public class RWeaponGet : IRandomizer 10 | { 11 | private List NewWeaponOrder; 12 | 13 | public RWeaponGet() 14 | { 15 | NewWeaponOrder = new List() 16 | { 17 | ERMWeaponValueBit.HeatMan, 18 | ERMWeaponValueBit.AirMan, 19 | ERMWeaponValueBit.WoodMan, 20 | ERMWeaponValueBit.BubbleMan, 21 | ERMWeaponValueBit.QuickMan, 22 | ERMWeaponValueBit.FlashMan, 23 | ERMWeaponValueBit.MetalMan, 24 | ERMWeaponValueBit.CrashMan 25 | }.Select(s => s).ToList(); 26 | } 27 | 28 | /// 29 | /// Shuffle which Robot Master awards which weapon. 30 | /// 31 | public void Randomize(Patch Patch, Random r) 32 | { 33 | // StageBeat Address Value 34 | // ----------------------------- 35 | // Heat Man 0x03C289 1 36 | // Air Man 0x03C28A 2 37 | // Wood Man 0x03C28B 4 38 | // Bubble Man 0x03C28C 8 39 | // Quick Man 0x03C28D 16 40 | // Flash Man 0x03C28E 32 41 | // Metal Man 0x03C28F 64 42 | // Crash Man 0x03C290 128 43 | NewWeaponOrder.Shuffle(r); 44 | 45 | // Create table for which weapon is awarded by which robot master 46 | // This also affects which portrait is blacked out on the stage select 47 | // This also affects which teleporter deactivates after defeating a Wily 5 refight boss 48 | for (int i = 0; i < 8; i++) 49 | { 50 | Patch.Add((int)(ERMStageWeaponAddress.HeatMan + i), (byte)NewWeaponOrder[i], $"{(EDmgVsBoss.Offset)i} Weapon Get"); 51 | } 52 | 53 | // Create a copy of the default weapon order table to be used by teleporter function 54 | // This is needed to fix teleporters breaking from the new weapon order. 55 | // Unused space at end of bank 56 | Patch.Add(0x03f310, (byte)ERMWeaponValueBit.HeatMan, "Custom Array of Default Weapon Order"); 57 | Patch.Add(0x03f311, (byte)ERMWeaponValueBit.AirMan, "Custom Array of Default Weapon Order"); 58 | Patch.Add(0x03f312, (byte)ERMWeaponValueBit.WoodMan, "Custom Array of Default Weapon Order"); 59 | Patch.Add(0x03f313, (byte)ERMWeaponValueBit.BubbleMan, "Custom Array of Default Weapon Order"); 60 | Patch.Add(0x03f314, (byte)ERMWeaponValueBit.QuickMan, "Custom Array of Default Weapon Order"); 61 | Patch.Add(0x03f315, (byte)ERMWeaponValueBit.FlashMan, "Custom Array of Default Weapon Order"); 62 | Patch.Add(0x03f316, (byte)ERMWeaponValueBit.MetalMan, "Custom Array of Default Weapon Order"); 63 | Patch.Add(0x03f317, (byte)ERMWeaponValueBit.CrashMan, "Custom Array of Default Weapon Order"); 64 | 65 | // Change function to call $f300 instead of $c279 when looking up defeated refight boss to 66 | // get our default weapon table, fixing the teleporter softlock 67 | Patch.Add(0x03843b, 0x00, "Teleporter Fix Custom Function Call Byte 1"); 68 | Patch.Add(0x03843c, 0xf3, "Teleporter Fix Custom Function Call Byte 2"); 69 | 70 | // Create table for which stage is selectable on the stage select screen (independent of it being blacked out) 71 | for (int i = 0; i < 8; i++) 72 | { 73 | Patch.Add((int)(ERMStageSelect.FirstStageInMemory + i), (byte)NewWeaponOrder[i], "Selectable Stage Fix for Random Weapon Get"); 74 | } 75 | } 76 | 77 | public void FixPortraits(ref byte[] portraitBG_x, ref byte[] portraitBG_y) 78 | { 79 | // Since the acquired-weapons table's elements are powers of two, get a new array of their 0-7 index 80 | int[] newWeaponIndex = GetShuffleIndexPermutation(); 81 | 82 | // Permute portrait x/y values via the shuffled acquired-weapons array 83 | byte[] cpy = new byte[8]; 84 | for (int i = 0; i < 8; i++) 85 | cpy[newWeaponIndex[i]] = portraitBG_y[i]; 86 | Array.Copy(cpy, portraitBG_y, 8); 87 | 88 | for (int i = 0; i < 8; i++) 89 | cpy[newWeaponIndex[i]] = portraitBG_x[i]; 90 | Array.Copy(cpy, portraitBG_x, 8); 91 | } 92 | 93 | /// 94 | /// Get an array of the shuffled acquired-weapons' 0-7 index, since the original table's elements are bitwise/powers of 2. 95 | /// Uses the field . Must be called after . 96 | /// 97 | /// An array of the new locations of the 8 awarded weapons. The index represents the original robot master index, 98 | /// in the order H A W B Q F M C. The value represents the index of the new location. 99 | /// 100 | public int[] GetShuffleIndexPermutation() 101 | { 102 | int[] newWeaponIndex = new int[8]; 103 | for (int i = 0; i < 8; i++) 104 | { 105 | int j = 0; 106 | byte val = (byte)NewWeaponOrder[i]; 107 | while (val != 0) 108 | { 109 | val = (byte)(val >> 1); 110 | j++; 111 | } 112 | newWeaponIndex[i] = j - 1; 113 | } 114 | return newWeaponIndex; 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /MM2RandoLib/Randomizers/Stages/Components/EnemyComponent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace MM2Randomizer.Randomizers.Stages.Components 7 | { 8 | class EnemyComponent 9 | { 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /MM2RandoLib/Randomizers/Stages/Components/LevelComponent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace MM2Randomizer.Randomizers.Stages.Components 7 | { 8 | public class LevelComponent 9 | { 10 | public string Name { get; set; } 11 | public string StartAddress { get; set; } 12 | public string EndAddress { get; set; } 13 | public IList Variations { get; set; } 14 | } 15 | 16 | public class LevelComponentVariation 17 | { 18 | public string Name { get; set; } 19 | public string[] TsaMap { get; set; } 20 | public string Sprites { get; set; } 21 | } 22 | 23 | public class ComponentManager 24 | { 25 | public IList LevelComponents { get; set; } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /MM2RandoLib/Randomizers/Stages/RStages.cs: -------------------------------------------------------------------------------- 1 | using MM2Randomizer.Enums; 2 | using MM2Randomizer.Patcher; 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Text; 7 | 8 | namespace MM2Randomizer.Randomizers.Stages 9 | { 10 | public class RStages : IRandomizer 11 | { 12 | private List StageSelect; 13 | private StringBuilder debug = new StringBuilder(); 14 | 15 | public RStages() { } 16 | 17 | public override string ToString() 18 | { 19 | return debug.ToString(); 20 | } 21 | 22 | public void FixPortraits(ref byte[] portraitBG_x, ref byte[] portraitBG_y) 23 | { 24 | // Get the new stage order 25 | int[] newOrder = new int[8]; 26 | foreach (StageFromSelect stage in StageSelect) 27 | newOrder[stage.PortraitDestinationOriginal] = stage.PortraitDestinationNew; 28 | 29 | // Permute portrait x/y values via the shuffled stage-order array 30 | byte[] cpy = new byte[8]; 31 | for (int i = 0; i < 8; i++) 32 | cpy[newOrder[i]] = portraitBG_y[i]; 33 | Array.Copy(cpy, portraitBG_y, 8); 34 | 35 | for (int i = 0; i < 8; i++) 36 | cpy[newOrder[i]] = portraitBG_x[i]; 37 | Array.Copy(cpy, portraitBG_x, 8); 38 | } 39 | 40 | /// 41 | /// Shuffle the Robot Master stages. This shuffling will not be indicated by the Robot Master portraits. 42 | /// 43 | public void Randomize(Patch Patch, Random r) 44 | { 45 | // StageSelect Address Value 46 | // ----------------------------- 47 | // Bubble Man 0x034670 3 48 | // Air Man 0x034671 1 49 | // Quick Man 0x034672 4 50 | // Wood Man 0x034673 2 51 | // Crash Man 0x034674 7 52 | // Flash Man 0x034675 5 53 | // Metal Man 0x034676 6 54 | // Heat Man 0x034677 0 55 | 56 | StageSelect = new List(); 57 | 58 | StageSelect.Add(new StageFromSelect() 59 | { 60 | PortraitName = "Bubble Man", 61 | PortraitAddress = ERMPortraitAddress.BubbleMan, 62 | PortraitDestinationOriginal = 3, 63 | PortraitDestinationNew = 3, 64 | StageClearAddress = ERMStageClearAddress.BubbleMan, 65 | StageClearDestinationOriginal = 8, 66 | StageClearDestinationNew = 8, 67 | TextAddress = ERMPortraitText.BubbleMan, 68 | TextValues = "BUBBLE", 69 | }); 70 | StageSelect.Add(new StageFromSelect() 71 | { 72 | PortraitName = "Air Man", 73 | PortraitAddress = ERMPortraitAddress.AirMan, 74 | PortraitDestinationOriginal = 1, 75 | PortraitDestinationNew = 1, 76 | StageClearAddress = ERMStageClearAddress.AirMan, 77 | StageClearDestinationOriginal = 2, 78 | StageClearDestinationNew = 2, 79 | TextAddress = ERMPortraitText.AirMan, 80 | TextValues = "fAIRfff" 81 | }); 82 | StageSelect.Add(new StageFromSelect() 83 | { 84 | PortraitName = "Quick Man", 85 | PortraitAddress = ERMPortraitAddress.QuickMan, 86 | PortraitDestinationOriginal = 4, 87 | PortraitDestinationNew = 4, 88 | StageClearAddress = ERMStageClearAddress.QuickMan, 89 | StageClearDestinationOriginal = 16, 90 | StageClearDestinationNew = 16, 91 | TextAddress = ERMPortraitText.QuickMan, 92 | TextValues = "QUICKf" 93 | }); 94 | StageSelect.Add(new StageFromSelect() 95 | { 96 | PortraitName = "Wood Man", 97 | PortraitAddress = ERMPortraitAddress.WoodMan, 98 | PortraitDestinationOriginal = 2, 99 | PortraitDestinationNew = 2, 100 | StageClearAddress = ERMStageClearAddress.WoodMan, 101 | StageClearDestinationOriginal = 4, 102 | StageClearDestinationNew = 4, 103 | TextAddress = ERMPortraitText.WoodMan, 104 | TextValues = "WOODff", 105 | }); 106 | StageSelect.Add(new StageFromSelect() 107 | { 108 | PortraitName = "Clash Man", 109 | PortraitAddress = ERMPortraitAddress.CrashMan, 110 | PortraitDestinationOriginal = 7, 111 | PortraitDestinationNew = 7, 112 | StageClearAddress = ERMStageClearAddress.CrashMan, 113 | StageClearDestinationOriginal = 128, 114 | StageClearDestinationNew = 128, 115 | TextAddress = ERMPortraitText.CrashMan, 116 | TextValues = "CRASHf", 117 | }); 118 | StageSelect.Add(new StageFromSelect() 119 | { 120 | PortraitName = "Flash Man", 121 | PortraitAddress = ERMPortraitAddress.FlashMan, 122 | PortraitDestinationOriginal = 5, 123 | PortraitDestinationNew = 5, 124 | StageClearAddress = ERMStageClearAddress.FlashMan, 125 | StageClearDestinationOriginal = 32, 126 | StageClearDestinationNew = 32, 127 | TextAddress = ERMPortraitText.FlashMan, 128 | TextValues = "FLASHf", 129 | }); 130 | StageSelect.Add(new StageFromSelect() 131 | { 132 | PortraitName = "Metal Man", 133 | PortraitAddress = ERMPortraitAddress.MetalMan, 134 | PortraitDestinationOriginal = 6, 135 | PortraitDestinationNew = 6, 136 | StageClearAddress = ERMStageClearAddress.MetalMan, 137 | StageClearDestinationOriginal = 64, 138 | StageClearDestinationNew = 64, 139 | TextAddress = ERMPortraitText.MetalMan, 140 | TextValues = "METALf", 141 | }); 142 | StageSelect.Add(new StageFromSelect() 143 | { 144 | PortraitName = "Heat Man", 145 | PortraitAddress = ERMPortraitAddress.HeatMan, 146 | PortraitDestinationOriginal = 0, 147 | PortraitDestinationNew = 0, // 4 = quick 148 | StageClearAddress = ERMStageClearAddress.HeatMan, 149 | StageClearDestinationOriginal = 1, 150 | StageClearDestinationNew = 1, 151 | TextAddress = ERMPortraitText.HeatMan, 152 | TextValues = "HEATff", 153 | }); 154 | 155 | 156 | List newStageOrder = new List(); 157 | for (byte i = 0; i < 8; i++) newStageOrder.Add(i); 158 | 159 | newStageOrder.Shuffle(r); 160 | 161 | debug.AppendLine("Stage Select:"); 162 | for (int i = 0; i < 8; i++) 163 | { 164 | StageFromSelect stage = StageSelect[i]; 165 | 166 | // Change portrait destination 167 | stage.PortraitDestinationNew = StageSelect[newStageOrder[i]].PortraitDestinationOriginal; 168 | 169 | // Erase the portrait text if StageNameHidden flag is set 170 | if (RandomMM2.Settings.IsStageNameHidden) 171 | { 172 | for (int k = 0; k < 6; k++) 173 | { 174 | // Write in a blank space at each letter ('f' by my cipher) 175 | Patch.Add((int)stage.TextAddress + k, RText.CreditsCipher['f'], $"Hide Stage Select Portrait Text"); 176 | } 177 | 178 | for (int k = 0; k < 3; k++) 179 | { 180 | // Write in a blank space over "MAN"; 32 8-pixel tiles until the next row, 3 tiles until "MAN" text 181 | Patch.Add((int)stage.TextAddress + 32 + 3 + k, RText.CreditsCipher['f'], $"Hide Stage Select Portrait Text"); 182 | } 183 | } 184 | // Change portrait text to match new destination 185 | else 186 | { 187 | string newlabel = StageSelect[newStageOrder[i]].TextValues; 188 | for (int j = 0; j < newlabel.Length; j++) 189 | { 190 | char c = newlabel[j]; 191 | Patch.Add((int)stage.TextAddress + j, RText.CreditsCipher[c], $"Stage Select Portrait Text"); 192 | } 193 | } 194 | 195 | debug.AppendLine($"{Enum.GetName(typeof(EStageID), stage.PortraitDestinationOriginal)}'s portrait -> {Enum.GetName(typeof(EStageID), StageSelect[i].PortraitDestinationNew)} stage"); 196 | } 197 | 198 | foreach (StageFromSelect stage in StageSelect) 199 | { 200 | Patch.Add((int)stage.PortraitAddress, (byte)stage.PortraitDestinationNew, $"Stage Select {stage.PortraitName} Destination"); 201 | } 202 | } 203 | } 204 | 205 | } 206 | -------------------------------------------------------------------------------- /MM2RandoLib/Randomizers/Stages/StageFromSelect.cs: -------------------------------------------------------------------------------- 1 | using MM2Randomizer.Enums; 2 | 3 | namespace MM2Randomizer.Randomizers.Stages 4 | { 5 | /// 6 | /// This object encapsulates the relevant ROM offsets for properties of each 7 | /// selectable Robot Master portrait on the Stage Select screen. 8 | /// 9 | public class StageFromSelect 10 | { 11 | public string PortraitName; 12 | public ERMPortraitText TextAddress; 13 | public string TextValues; 14 | public ERMPortraitAddress PortraitAddress; 15 | public int PortraitDestinationOriginal; 16 | public int PortraitDestinationNew; 17 | public ERMStageClearAddress StageClearAddress; 18 | public int StageClearDestinationOriginal; 19 | public int StageClearDestinationNew; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /MM2RandoLib/Resources/SpritePatches/SpriteSwap_Bass.ips: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duckfist/MM2Random/178a4cb7649b194782b94caf9b54379e52914c0d/MM2RandoLib/Resources/SpritePatches/SpriteSwap_Bass.ips -------------------------------------------------------------------------------- /MM2RandoLib/Resources/SpritePatches/SpriteSwap_Proto.ips: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duckfist/MM2Random/178a4cb7649b194782b94caf9b54379e52914c0d/MM2RandoLib/Resources/SpritePatches/SpriteSwap_Proto.ips -------------------------------------------------------------------------------- /MM2RandoLib/Resources/SpritePatches/SpriteSwap_Roll.ips: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duckfist/MM2Random/178a4cb7649b194782b94caf9b54379e52914c0d/MM2RandoLib/Resources/SpritePatches/SpriteSwap_Roll.ips -------------------------------------------------------------------------------- /MM2RandoLib/Resources/bossnames.csv: -------------------------------------------------------------------------------- 1 | # Also randomize the "Dr. WILY"? 2 | # DR. COSSACK 3 | # DR. LIGHT 4 | # MR. X 5 | # BREAK MAN 6 | # DOC ROBOT 7 | # BASS 8 | # 9 | # Misc 10 | CLASH 11 | WILY 12 | PROTO 13 | BREAK 14 | ROCK 15 | DARK 16 | ENKER 17 | QUINT 18 | PUNK 19 | BALLADE 20 | # 21 | # Mega Man 22 | CUT 23 | ELEC 24 | ICE 25 | FIRE 26 | BOMB 27 | GUTS 28 | # 29 | # Mega Man 2 30 | BUBBLE 31 | HEAT 32 | METAL 33 | AIR 34 | FLASH 35 | QUICK 36 | WOOD 37 | CRASH 38 | # 39 | # MEGA MAN 3 40 | TOP 41 | GEMINI 42 | SHADOW 43 | NEEDLE 44 | HARD 45 | MAGNET 46 | SNAKE 47 | SPARK 48 | # 49 | # MEGA MAN 4 50 | PHARAOH 51 | BRIGHT 52 | RING 53 | DUST 54 | SKULL 55 | DIVE 56 | DRILL 57 | TOAD 58 | # 59 | # MEGA MAN 5 60 | STAR 61 | GRAVITY 62 | GYRO 63 | CRYSTAL 64 | NAPALM 65 | STONE 66 | CHARGE 67 | WAVE 68 | # 69 | # MEGA MAN 6 70 | WIND 71 | FLAME 72 | BLIZZARD 73 | PLANT 74 | TOMAHAWK 75 | YAMATO 76 | KNIGHT 77 | CENTAUR 78 | # MEGA MAN 7 79 | FREEZE 80 | CLOUD 81 | BURST 82 | JUNK 83 | SHADE 84 | TURBO 85 | SPRING 86 | SLASH 87 | # 88 | # MEGA MAN 8 89 | SEARCH 90 | FROST 91 | GRENADE 92 | AQUA 93 | SWORD 94 | CLOWN 95 | TENGU 96 | ASTRO 97 | # 98 | # ROCKMAN AND FORTE 99 | DYNAMO 100 | COLD 101 | GROUND 102 | PIRATE 103 | BURNER 104 | MAGIC 105 | #TENGU 106 | #ASTRO 107 | # 108 | # MEGA MAN 9 109 | CONCRETE 110 | MAGMA 111 | GALAXY 112 | HORNET 113 | JEWEL 114 | TORNADO 115 | PLUG 116 | SPLASH 117 | # 118 | # MEGA MAN 10 119 | PUMP 120 | SOLAR 121 | CHILL 122 | NITRO 123 | BLADE 124 | COMMANDO 125 | STRIKE 126 | SHEEP -------------------------------------------------------------------------------- /MM2RandoLib/Resources/companynames.csv: -------------------------------------------------------------------------------- 1 | HUDSON SOFT 2 | NINTENDO 3 | TREASURE 4 | KONAMI 5 | DATA EAST 6 | SNK PLAYMORE 7 | ACTIVISION 8 | JALECO 9 | TECMO 10 | SUNSOFT 11 | TAITO 12 | SONY 13 | SEGA CO.LTD 14 | IREM 15 | COMPILE 16 | KOEI 17 | TENGEN 18 | KEMCO 19 | RARE 20 | ATLUS 21 | HALKEN 22 | LUCASARTS 23 | WESTWOOD 24 | INTERPLAY 25 | TOSE CO.LTD 26 | NAMCO LTD. 27 | NATSUME 28 | SQUARE CO.LTD 29 | COMCEPT 30 | INTI CREATES 31 | RAINBOW ARTS 32 | OCEAN LTD. 33 | FACTOR 5 34 | VIC TOKAI 35 | ASCII 36 | THQ 37 | QUINTET 38 | ENIX 39 | MINDSCAPE -------------------------------------------------------------------------------- /MM2RandoLib/Resources/countrylist.csv: -------------------------------------------------------------------------------- 1 | AFGHANISTAN 2 | ALBANIA 3 | ALGERIA 4 | ANDORRA 5 | ANGOLA 6 | ANTIGUA AND BARBUDA 7 | ARGENTINA 8 | ARMENIA 9 | ARUBA 10 | AUSTRALIA 11 | AUSTRIA 12 | AZERBAIJAN 13 | ANTARCTICA 14 | THE BAHAMAS 15 | BAHRAIN 16 | BANGLADESH 17 | BARBADOS 18 | BELARUS 19 | BELGIUM 20 | BELIZE 21 | BENIN 22 | BHUTAN 23 | BOLIVIA 24 | BOSNIA AND HERZEGOVINA 25 | BOTSWANA 26 | BRAZIL 27 | BRUNEI 28 | BULGARIA 29 | BURKINA FASO 30 | BURMA 31 | BURUNDI 32 | CAMBODIA 33 | CAMEROON 34 | CANADA 35 | CABO VERDE 36 | CENTRAL AFRICAN REPUBLIC 37 | CHAD 38 | CHILE 39 | CHINA 40 | COLOMBIA 41 | THE CONGO 42 | COSTA RICA 43 | CROATIA 44 | CUBA 45 | CURACAO 46 | CYPRUS 47 | CZECH REPUBLIC 48 | DENMARK 49 | DJIBOUTI 50 | DOMINICA 51 | DOMINICAN REPUBLIC 52 | ECUADOR 53 | EGYPT 54 | EL SALVADOR 55 | EQUATORIAL GUINEA 56 | ERITREA 57 | ESTONIA 58 | ETHIOPIA 59 | FIJI 60 | FINLAND 61 | FRANCE 62 | GABON 63 | THE GAMBIA 64 | GEORGIA 65 | GERMANY 66 | GHANA 67 | GREECE 68 | GRENADA 69 | GUATEMALA 70 | GUINEA 71 | GUINEA BISSAU 72 | GUYANA 73 | HAITI 74 | HONDURAS 75 | HONG KONG 76 | HUNGARY 77 | ICELAND 78 | INDIA 79 | INDONESIA 80 | IRAN 81 | IRAQ 82 | IRELAND 83 | ISRAEL 84 | ITALY 85 | JAMAICA 86 | JAPAN 87 | JORDAN 88 | KAZAKHSTAN 89 | KENYA 90 | KIRIBATI 91 | KOSOVO 92 | KUWAIT 93 | KYRGYZSTAN 94 | LAOS 95 | LATVIA 96 | LEBANON 97 | LESOTHO 98 | LIBERIA 99 | LIBYA 100 | LIECHTENSTEIN 101 | LITHUANIA 102 | LUXEMBOURG 103 | MACAU 104 | MACEDONIA 105 | MADAGASCAR 106 | MALAWI 107 | MALAYSIA 108 | MALDIVES 109 | MALI 110 | MALTA 111 | MARSHALL ISLANDS 112 | MAURITANIA 113 | MAURITIUS 114 | MEXICO 115 | MICRONESIA 116 | MOLDOVA 117 | MONACO 118 | MONGOLIA 119 | MONTENEGRO 120 | MOZAMBIQUE 121 | NAMIBIA 122 | NAURU 123 | NEPAL 124 | NETHERLANDS 125 | NEW ZEALAND 126 | NICARAGUA 127 | NIGER 128 | NIGERIA 129 | NORTH KOREA 130 | NORWAY 131 | OMAN 132 | PAKISTAN 133 | PALAU 134 | PALESTINIAN TERRITORIES 135 | PANAMA 136 | PAPUA NEW GUINEA 137 | PARAGUAY 138 | PERU 139 | PHILIPPINES 140 | POLAND 141 | PORTUGAL 142 | QATAR 143 | ROMANIA 144 | RUSSIA 145 | RWANDA 146 | SAINT KITTS AND NEVIS 147 | SAINT LUCIA 148 | SAINT VINCENT 149 | SAMOA 150 | SAN MARINO 151 | SAO TOME AND PRINCIPE 152 | SAUDI ARABIA 153 | SENEGAL 154 | SERBIA 155 | SEYCHELLES 156 | SIERRA LEONE 157 | SINGAPORE 158 | SLOVAKIA 159 | SLOVENIA 160 | SOLOMON ISLANDS 161 | SOMALIA 162 | SOUTH AFRICA 163 | SOUTH KOREA 164 | SOUTH SUDAN 165 | SPAIN 166 | SRI LANKA 167 | SUDAN 168 | SURINAME 169 | SWAZILAND 170 | SWEDEN 171 | SWITZERLAND 172 | SYRIA 173 | TAIWAN 174 | TAJIKISTAN 175 | TANZANIA 176 | THAILAND 177 | TIMOR LESTE 178 | TOGO 179 | TONGA 180 | TRINIDAD AND TOBAGO 181 | TUNISIA 182 | TURKEY 183 | TURKMENISTAN 184 | TUVALU 185 | UGANDA 186 | UKRAINE 187 | UNITED ARAB EMIRATES 188 | UNITED KINGDOM 189 | UNITED STATES 190 | URUGUAY 191 | UZBEKISTAN 192 | VANUATU 193 | VENEZUELA 194 | VIETNAM 195 | YEMEN 196 | ZAMBIA 197 | ZIMBABWE -------------------------------------------------------------------------------- /MM2RandoLib/Resources/creditstext.csv: -------------------------------------------------------------------------------- 1 | 14|09|MM2 RANDOMIZER 2 | 13|2A|ROBOT MASTERS 3 | 01|61|f 4 | 17|AB|P H A W B Q F M C 5 | 25|E3|HEATMAN 2 3 1 2 6 | 24|24|AIRMAN 1 2 1 2 4 7 | 25|63|WOODMAN 1 1 1 3 2 8 | 27|A1|BUBBLEMAN 2 4 1 1 3 1 9 | 26|E2|QUICKMAN 1 5 1 1 1 2 10 | 26|22|FLASHMAN 1 2 1 1 3 1 11 | 26|62|METALMAN 1 2 1 2 1 1 1 12 | 26|A2|CLASHMAN 1 2 2 3 1 13 | 01|E1|f 14 | 01|21|f 15 | 11|6B|WILY BOSSES 16 | 01|A1|f 17 | 15|2C|P H A W B Q M C 18 | 22|65|DRAGON 19 | 24|A3|PICOPICO 20 | 20|E7|GUTS 21 | 23|24|BOOBEAM 22 | 23|64|MACHINE 23 | 21|A6|ALIEN X X X 2 X X X X 24 | 01|E1|f 25 | 01|21|f 26 | 01|61|f 27 | 01|A1|f 28 | 01|E1|f 29 | 01|21|f 30 | 01|61|f 31 | 01|A1|f 32 | 12|2A|RANDOMIZER BY 33 | 08|6C|DUCKFIST 34 | 01|A1|f 35 | 01|E1|f 36 | 01|21|f 37 | 01|61|f 38 | 01|A1|f 39 | 01|E1|f 40 | 01|21|f 41 | 21|66|SPECIAL THANKS TO THE 42 | 14|A9|ROCKMAN 2 TEAM 43 | 01|A1|f 44 | 12|8A|PRESENTED BY -------------------------------------------------------------------------------- /MM2RandoLib/Resources/enemylist.csv: -------------------------------------------------------------------------------- 1 | #Index,StageNum,RoomNum,ScreenNum,IsActive,EnemyID,XPosOriginal,YPosOriginal,YPosAir,YPosGround,FaceRight 2 | ### 3 | ### Heat Man / Wily 1 Stage 4 | ### 5 | 00,00,00,01,true,2B,70,10,24,A4,false 6 | 01,00,00,01,true,2B,F0,10,24,A4,false 7 | 02,00,00,02,true,2B,90,10,28,A4,false 8 | 03,00,00,02,true,21,A8,58,58,58,false 9 | 04,00,00,02,true,21,E8,78,78,78,false 10 | 05,00,00,03,true,21,48,48,48,48,false 11 | 06,00,00,03,true,21,88,68,68,68,false 12 | 07,00,00,03,true,21,D8,98,98,98,false 13 | 08,00,00,04,true,21,28,58,58,58,false 14 | 09,00,00,04,true,21,88,58,58,58,false 15 | 0A,00,00,04,true,21,E8,38,38,38,false 16 | 0B,00,00,05,true,21,48,58,58,58,false 17 | 0C,00,00,05,true,21,C8,58,58,58,false 18 | 0D,00,00,07,true,46,78,98,94,94,false 19 | 0E,00,00,07,true,46,98,58,94,94,false 20 | 0F,00,01,08,true,46,48,C8,C4,C4,true 21 | 10,00,01,08,false,53,68,37,0,0,false 22 | 11,00,01,08,false,55,78,57,0,0,false 23 | 12,00,01,08,false,54,A8,47,0,0,false 24 | 13,00,01,08,false,53,A8,77,0,0,false 25 | 14,00,02,09,true,21,B8,58,58,B4,false 26 | 15,00,02,09,true,21,E8,78,78,B4,false 27 | 16,00,02,0A,false,53,38,A7,0,0,false 28 | 17,00,02,0A,true,21,58,48,44,44,false 29 | 18,00,02,0A,false,54,A8,67,0,0,false 30 | 19,00,02,0A,false,53,A8,97,0,0,false 31 | 1A,00,02,0B,true,21,08,88,88,B4,false 32 | 1B,00,02,0B,true,21,38,78,78,B4,false 33 | 1C,00,02,0B,true,21,68,48,48,B4,false 34 | 1D,00,02,0B,false,53,A8,A7,0,0,false 35 | 1E,00,02,0B,true,21,D8,48,48,B4,false 36 | 1F,00,02,0C,false,55,48,77,0,0,false 37 | 20,00,02,0C,false,53,48,A7,0,0,false 38 | 21,00,02,0C,true,21,98,48,48,B4,false 39 | 22,00,02,0C,false,53,D8,B7,0,0,false 40 | 23,00,02,0E,true,46,68,B8,74,B4,false 41 | 24,00,02,0E,false,53,E8,77,0,0,false 42 | 25,00,02,0F,false,53,18,77,0,0,false 43 | 26,00,02,0F,false,54,48,47,0,0,false 44 | 27,00,02,0F,false,53,48,77,0,0,false 45 | 28,00,02,0F,false,53,78,47,0,0,false 46 | 29,00,02,0F,false,53,A8,77,0,0,false 47 | 2A,00,02,0F,false,54,D8,47,0,0,false 48 | 2B,00,02,0F,false,53,D8,77,0,0,false 49 | 2C,00,02,10,false,53,08,47,0,0,false 50 | 2D,00,02,10,false,53,38,47,0,0,false 51 | 2E,00,02,10,false,53,68,67,0,0,false 52 | 2F,00,02,10,false,53,98,67,0,0,false 53 | 30,00,02,10,false,53,C8,67,0,0,false 54 | 31,00,02,10,false,53,F8,67,0,0,false 55 | 32,00,02,11,false,53,28,67,0,0,false 56 | 33,00,02,11,false,53,58,77,0,0,false 57 | 34,00,02,11,false,53,88,67,0,0,false 58 | 35,00,02,11,false,53,B8,57,0,0,false 59 | 36,00,02,11,false,53,E8,47,0,0,false 60 | 37,00,02,12,false,53,18,97,0,0,false 61 | 38,00,02,12,false,53,48,97,0,0,false 62 | 39,00,02,12,false,54,78,67,0,0,false 63 | 3A,00,02,12,false,53,78,97,0,0,false 64 | 3B,00,02,12,false,53,A8,77,0,0,false 65 | 3C,00,02,12,false,53,D8,67,0,0,false 66 | 3D,00,03,14,true,4E,50,94,64,A4,true 67 | 3E,00,07,18,true,39,08,30,30,B4,true 68 | 3F,00,07,19,true,37,10,30,30,A4,false 69 | 40,00,07,19,true,00,A0,30,30,A4,false 70 | 41,00,07,1A,true,00,A0,30,30,A4,false 71 | 42,00,07,1B,true,37,20,30,30,A4,false 72 | 43,00,07,1C,true,39,30,30,30,B4,false 73 | 44,00,07,1D,true,50,48,BC,B4,B4,false 74 | 45,00,07,1D,true,50,B8,AC,A4,A4,false 75 | 46,00,07,1E,true,50,48,9C,94,94,false 76 | 47,00,07,1E,true,50,A8,AC,A4,A4,false 77 | 48,00,07,1E,true,50,E8,9C,94,94,false 78 | 49,00,07,1F,true,50,F0,BC,B4,B4,false 79 | 4A,00,07,20,true,50,6C,5C,44,54,true 80 | 4B,00,08,21,true,4F,4C,64,34,64,true 81 | 4C,00,09,22,true,4F,44,74,44,74,true 82 | 4D,00,0A,23,true,46,98,B8,54,B4,false 83 | 4E,00,0C,25,true,21,10,38,38,38,true 84 | 4F,00,0C,25,true,21,10,88,88,88,true 85 | 50,00,0D,2A,false,63,28,98,0,0,false 86 | 51,00,0E,2A,false,63,68,98,0,0,false 87 | 52,00,0E,2A,false,63,98,88,0,0,false 88 | 53,00,0E,2A,false,63,D8,88,0,0,false 89 | 54,00,0E,2A,false,64,FF,98,0,0,false 90 | ### 91 | ### Air Man / Wily 2 Stage 92 | ### 93 | #Index,StageNum,RoomNum,ScreenNum,IsActive,EnemyID,XPosOriginal,YPosOriginal,YPosAir,YPosGround,FaceRight 94 | 00,01,00,01,true,40,48,9B,10,64,false 95 | 01,01,00,01,true,41,F8,AB,10,74,false 96 | 02,01,00,02,true,40,98,6B,10,34,false 97 | 03,01,00,03,true,41,48,8B,10,54,false 98 | 04,01,00,03,true,40,E8,8B,10,54,false 99 | # Lightning Goros 100 | 05,01,00,04,true,3E,C0,20,10,54,false 101 | 06,01,00,05,true,3E,60,60,10,74,false 102 | 07,01,00,06,true,3E,00,38,10,94,false 103 | 08,01,00,06,true,3E,60,40,10,94,false 104 | 09,01,00,06,false,39,B0,08,10,74,false 105 | 0A,01,00,06,true,3E,E0,40,10,74,false 106 | # Pipi Spawner and Despawner 107 | 0B,01,00,07,true,37,70,30,20,74,false 108 | 0C,01,00,07,true,00,C0,20,20,74,false 109 | # Checkpoint room 110 | 0D,01,00,08,true,00,38,20,20,74,false 111 | 0E,01,00,08,true,00,B0,20,20,74,false 112 | 0F,01,00,09,true,00,60,20,20,74,false 113 | 10,01,01,0A,true,50,38,5C,20,54,true 114 | 11,01,01,0A,true,50,B8,9C,94,94,false 115 | 12,01,02,0C,true,40,48,9B,10,64,false 116 | 13,01,02,0C,true,41,B8,7B,10,44,false 117 | 14,01,02,0E,true,39,00,08,20,74,false 118 | 15,01,02,0E,true,36,80,50,54,54,false 119 | 16,01,02,0F,true,37,10,30,20,74,false 120 | 17,01,02,0F,true,00,E0,20,20,74,false 121 | 18,01,02,10,true,36,F0,50,54,54,false 122 | 19,01,02,11,true,36,60,30,34,34,false 123 | 1A,01,02,12,true,00,30,20,20,54,false 124 | 1B,01,02,12,true,37,E0,30,20,34,false 125 | 1C,01,02,13,true,39,C0,08,10,74,false 126 | 1D,01,05,17,true,2B,10,10,10,64,false 127 | 1E,01,05,17,true,2B,98,10,10,84,false 128 | 1F,01,05,18,true,2B,10,10,10,A4,false 129 | 20,01,07,1D,true,4C,74,24,24,24,true 130 | 21,01,07,1D,true,4B,9C,64,64,64,false 131 | 22,01,09,1F,true,4A,90,80,94,94,false 132 | 23,01,09,20,true,47,60,80,94,94,false 133 | 24,01,09,20,true,47,F0,80,94,94,false 134 | 25,01,09,22,true,4A,00,80,94,94,false 135 | 26,01,09,22,true,30,40,6C,6C,A4,false 136 | 27,01,09,22,true,30,90,0C,10,34,false 137 | 28,01,09,23,true,30,30,0C,10,34,false 138 | 29,01,09,23,true,30,B0,0C,10,34,false 139 | 2A,01,09,23,true,30,E0,0C,10,34,false 140 | ### 141 | ### Wood Man / Wily 3 Stage 142 | ### 143 | 00,02,00,01,true,16,08,48,48,A4,false 144 | 01,02,00,01,true,16,50,68,86,B4,false 145 | 02,02,00,01,true,16,98,48,48,A4,false 146 | 03,02,00,01,true,17,D0,90,48,A4,false 147 | 04,02,00,01,true,16,F8,18,18,94,false 148 | 05,02,00,02,true,16,38,18,18,94,false 149 | 06,02,00,02,true,16,88,28,28,94,false 150 | 07,02,00,02,true,17,F0,A0,A4,A4,false 151 | 08,02,00,03,true,16,50,68,68,B4,false 152 | 09,02,00,03,true,16,B8,48,48,B4,false 153 | 0A,02,00,04,true,16,18,48,48,B4,false 154 | 0B,02,00,04,true,17,80,B0,B4,B4,false 155 | 0C,02,00,04,true,16,98,48,48,B4,false 156 | 0D,02,01,05,true,16,38,78,78,B4,true 157 | 0E,02,01,05,true,16,60,88,88,A4,true 158 | 0F,02,01,05,true,16,80,68,68,A4,true 159 | 10,02,02,06,false,1C,AC,08,0,0,false 160 | 11,02,03,07,false,1C,AC,08,0,0,false 161 | 12,02,04,08,false,1C,CC,08,0,0,false 162 | 13,02,06,0A,true,16,50,48,48,A4,true 163 | 14,02,06,0A,true,16,78,68,68,B4,true 164 | 15,02,07,0C,true,39,30,08,48,74,false 165 | 16,02,07,0C,true,1D,70,E0,28,74,false 166 | 17,02,07,0C,true,1D,F0,E0,10,54,false 167 | 18,02,07,0D,true,37,40,30,20,54,false 168 | 19,02,07,0D,true,37,B0,30,20,54,false 169 | 1A,02,07,0D,true,1D,E0,E0,20,54,false 170 | 1B,02,07,0E,true,1D,70,E0,10,74,false 171 | 1C,02,07,0E,true,39,F0,08,10,34,false 172 | 1D,02,08,10,true,17,48,B0,68,B4,true 173 | 1E,02,09,11,true,17,C0,B0,68,B4,false 174 | 1F,02,0A,12,true,17,30,B0,68,B4,true 175 | 20,02,0B,13,true,20,80,08,20,B4,false 176 | 21,02,0B,14,true,1E,10,08,20,94,false 177 | 22,02,0B,14,true,1E,B0,08,10,74,false 178 | 23,02,0B,15,true,20,C0,08,10,74,false 179 | 24,02,10,1A,true,0A,BC,C4,C4,C4,false 180 | 25,02,11,1C,true,00,F0,10,10,94,false 181 | 26,02,11,1D,true,71,60,E0,10,94,false 182 | 27,02,11,1D,true,71,F0,E0,10,94,false 183 | 28,02,16,25,true,4B,2C,B4,88,B4,false 184 | 29,02,16,25,true,4B,CC,B4,88,B4,false 185 | 2A,02,16,26,true,4B,5C,B4,88,B4,false 186 | 2B,02,15,23,false,56,80,80,0,0,false 187 | 00,03,00,01,true,0C,50,70,20,74,false 188 | 01,03,00,01,true,0C,D0,50,20,54,false 189 | 02,03,00,02,true,0C,B0,50,20,54,false 190 | 03,03,00,02,false,13,F0,88,0,0,false 191 | 04,03,00,03,false,13,20,78,0,0,false 192 | 05,03,00,03,false,13,60,68,0,0,false 193 | 06,03,00,03,false,13,A0,78,0,0,false 194 | 07,03,00,03,false,13,D0,58,0,0,false 195 | 08,03,01,05,true,0A,5C,34,34,34,false 196 | 09,03,01,05,true,0A,6C,94,94,94,false 197 | 0A,03,01,05,true,0A,B4,64,64,64,false 198 | 0B,03,03,07,true,0A,44,34,34,34,false 199 | 0C,03,03,07,true,0A,7C,74,74,74,false 200 | 0D,03,03,07,true,0A,8C,A4,A4,A4,false 201 | 0E,03,04,09,true,00,08,58,58,B4,false 202 | 0F,03,04,09,true,00,50,70,70,B4,false 203 | 10,03,04,09,true,00,68,48,48,B4,false 204 | 11,03,04,09,true,00,C8,98,98,B4,false 205 | 12,03,04,09,true,05,38,08,40,B4,false 206 | 13,03,04,0A,true,03,D0,08,60,94,false 207 | 14,03,04,0B,true,00,D8,38,38,54,false 208 | 15,03,04,0C,true,03,70,08,60,74,false 209 | 16,03,04,0D,true,05,80,08,10,B4,false 210 | 17,03,04,0E,true,00,00,68,68,B4,false 211 | 18,03,04,0E,true,00,18,88,88,B4,false 212 | 19,03,04,0E,true,00,88,38,38,B4,false 213 | 1A,03,05,0F,true,0C,90,90,60,94,false 214 | 1B,03,05,10,true,0C,30,70,74,74,false 215 | 1C,03,05,10,true,09,A0,08,74,74,false 216 | 1D,03,05,11,true,07,C0,08,10,64,false 217 | 1E,03,05,12,true,07,90,08,10,64,false 218 | 1F,03,05,13,true,09,88,08,10,34,false 219 | 20,03,09,17,true,34,C8,98,94,94,false 220 | 21,03,0A,18,true,34,78,B8,74,B4,true 221 | 22,03,0D,1B,true,34,90,78,30,74,false 222 | 23,03,0D,1B,true,34,B8,B8,70,B4,false 223 | 24,03,0F,1E,true,21,10,58,54,54,true 224 | 25,03,0F,1E,true,21,10,98,94,94,true 225 | 26,03,0F,1E,false,12,D8,88,0,0,false 226 | 27,03,0F,1E,true,21,F0,A8,A4,A4,false 227 | 28,03,10,1F,false,12,48,28,0,0,false 228 | 29,03,11,20,true,21,10,38,34,34,true 229 | 2A,03,11,20,true,21,10,B8,B4,B4,true 230 | 2B,03,11,20,false,12,38,48,0,0,false 231 | 2C,03,11,20,true,21,F0,B8,B4,B4,false 232 | 2D,03,12,21,false,12,58,58,0,0,false 233 | 2E,03,13,22,true,4E,F0,A4,50,B4,false 234 | 2F,03,13,23,true,4F,9C,B4,B4,B4,false 235 | 30,03,13,24,true,4E,B0,A4,50,B4,false 236 | 31,03,13,25,true,4F,AC,B4,B4,B4,false 237 | 00,04,01,01,true,50,50,BC,50,B4,true 238 | 01,04,01,01,true,50,90,9C,40,94,true 239 | 02,04,03,03,true,46,68,B8,30,B4,true 240 | 03,04,03,03,false,14,80,80,0,0,false 241 | 04,04,04,04,false,14,80,80,0,0,false 242 | 05,04,05,05,false,14,80,80,0,0,false 243 | 06,04,05,05,true,46,98,A8,A4,A4,false 244 | 07,04,07,08,true,25,00,08,30,94,false 245 | 08,04,07,08,true,23,60,70,30,74,false 246 | 09,04,07,09,true,23,10,90,50,94,false 247 | 0A,04,07,09,true,00,60,50,50,74,false 248 | 0B,04,07,0A,true,23,10,90,70,94,false 249 | 0C,04,07,0A,true,25,60,08,70,94,false 250 | 0D,04,07,0B,true,27,80,08,70,74,false 251 | 0E,04,08,0C,false,14,80,80,0,0,false 252 | 0F,04,09,0D,false,14,80,80,0,0,false 253 | 10,04,0A,0E,false,14,80,80,0,0,false 254 | 11,04,0B,0F,false,14,80,80,0,0,false 255 | 12,04,0C,10,false,14,80,80,0,0,false 256 | 13,04,0D,11,false,14,80,80,0,0,false 257 | 14,04,0E,12,false,14,80,80,0,0,false 258 | 15,04,0F,13,true,00,A0,50,50,B4,false 259 | 16,04,0F,13,true,4E,F8,A4,50,B4,false 260 | 17,04,0F,14,true,4E,E8,A4,70,B4,false 261 | 00,05,00,02,true,4B,84,C4,C4,C4,false 262 | 01,05,00,03,true,4B,9C,94,C4,C4,false 263 | 02,05,00,03,true,4C,EC,94,94,94,false 264 | 03,05,00,04,true,4B,24,34,C4,C4,false 265 | 04,05,00,04,true,4B,CC,94,60,94,false 266 | 05,05,00,06,true,4C,44,94,60,94,true 267 | 06,05,01,07,true,4E,40,84,30,94,true 268 | 07,05,02,08,true,4B,68,84,60,84,false 269 | 08,05,04,0A,true,50,50,9C,60,94,false 270 | 09,05,04,0A,true,50,D8,6C,40,64,false 271 | 0A,05,05,0B,true,31,88,B0,90,C4,false 272 | 0B,05,06,0C,true,4E,68,B4,94,C4,true 273 | 0C,05,07,0D,true,4E,F8,B4,34,34,false 274 | 0D,05,07,0E,true,00,D8,20,20,34,false 275 | 0E,05,07,0F,true,4E,38,B4,60,C4,false 276 | 0F,05,07,0F,true,00,C0,20,20,C4,false 277 | 10,05,07,11,true,4E,08,84,60,94,false 278 | 11,05,0F,1A,false,72,18,34,0,0,false 279 | 12,05,0F,1A,false,73,58,44,0,0,false 280 | 13,05,0F,1A,false,72,B0,24,0,0,false 281 | 14,05,0F,1A,false,73,D0,44,0,0,false 282 | 15,05,0F,1B,false,72,28,24,0,0,false 283 | 16,05,0F,1B,false,73,48,34,0,0,false 284 | 17,05,0F,1B,false,72,70,44,0,0,false 285 | 18,05,0F,1B,false,73,B0,34,0,0,false 286 | 19,05,0F,1B,false,72,E8,54,0,0,false 287 | 1A,05,0F,1C,false,73,28,64,0,0,false 288 | 1B,05,0F,1C,false,72,78,64,0,0,false 289 | 1C,05,0F,1C,false,73,A0,64,0,0,false 290 | 1D,05,0F,1C,false,72,C8,64,0,0,false 291 | 1E,05,0F,1D,false,73,68,64,0,0,false 292 | 1F,05,0F,1D,false,72,90,64,0,0,false 293 | 20,05,0F,1D,false,73,B8,64,0,0,false 294 | 21,05,0F,1E,false,72,30,64,0,0,false 295 | 22,05,0F,1E,false,73,78,44,0,0,false 296 | 23,05,0F,1E,false,72,B0,44,0,0,false 297 | 00,06,00,02,true,30,70,38,40,84,false 298 | 01,06,00,02,true,30,D0,38,40,84,false 299 | 02,06,00,03,true,30,30,28,30,84,false 300 | 03,06,00,03,true,30,70,28,30,84,false 301 | 04,06,00,03,true,30,B0,38,40,84,false 302 | 05,06,00,04,true,4A,38,50,50,B4,false 303 | 06,06,00,04,true,47,E0,08,50,B4,false 304 | 07,06,00,05,true,47,F0,08,50,74,false 305 | 08,06,00,06,true,00,78,50,50,74,false 306 | 09,06,00,07,true,4A,18,08,50,A4,false 307 | 0A,06,02,0C,true,29,60,40,40,A4,false 308 | 0B,06,02,0C,true,29,C8,48,48,74,false 309 | 0C,06,02,0D,true,29,E0,58,58,A4,false 310 | 0D,06,02,0E,true,29,60,48,48,A4,false 311 | 0E,06,02,0F,true,29,10,38,38,A4,false 312 | 0F,06,02,0F,true,31,58,78,28,94,false 313 | 10,06,02,10,true,31,18,48,20,64,false 314 | 11,06,02,10,true,31,E8,18,10,34,false 315 | 12,06,02,11,true,46,B8,B8,20,B4,false 316 | 13,06,02,12,true,46,38,98,20,94,false 317 | 14,06,02,12,true,46,A8,98,20,94,false 318 | 15,06,02,13,true,46,38,88,20,74,false 319 | 16,06,02,13,true,46,98,88,20,74,false 320 | 00,07,00,00,true,21,38,78,80,B4,true 321 | 01,07,00,00,true,21,60,28,10,34,true 322 | 02,07,00,00,true,21,D0,48,60,74,false 323 | 03,07,01,01,true,21,30,68,10,54,true 324 | 04,07,01,01,true,21,A8,58,50,64,false 325 | 05,07,01,01,true,21,D0,B8,A4,A4,false 326 | 06,07,02,02,true,34,C8,98,60,94,false 327 | 07,07,02,03,true,34,48,88,40,84,false 328 | 08,07,02,03,true,34,88,78,40,74,false 329 | 09,07,03,04,true,21,10,38,38,38,true 330 | 0A,07,03,04,true,21,10,68,68,B4,true 331 | 0B,07,03,04,false,12,D8,48,0,0,false 332 | 0C,07,03,04,true,21,F0,68,68,B4,false 333 | 0D,07,04,05,true,21,10,78,78,B4,true 334 | 0E,07,04,05,false,12,D8,48,0,0,false 335 | 0F,07,04,05,true,21,F0,48,48,48,false 336 | 10,07,04,05,true,21,F0,70,70,B4,false 337 | 11,07,05,06,true,21,08,68,68,B4,true 338 | 12,07,05,06,false,12,D8,48,0,0,false 339 | 13,07,05,06,true,21,F0,38,38,38,false 340 | 14,07,05,06,true,21,F0,68,68,B4,false 341 | 15,07,06,08,true,31,18,90,40,B4,false 342 | 16,07,06,08,true,00,68,70,70,B4,false 343 | 17,07,08,0A,false,56,20,20,0,0,false 344 | 18,07,08,0A,true,34,48,58,54,54,true 345 | 19,07,09,0B,false,56,20,20,0,0,false 346 | 1A,07,09,0B,true,34,A8,B8,20,B4,false 347 | 1B,07,0A,0C,false,56,20,20,0,0,false 348 | 1C,07,0A,0C,true,37,A0,30,30,74,false 349 | 1D,07,0B,0D,true,37,30,30,20,54,true 350 | 1E,07,0C,0E,true,37,20,30,30,30,true 351 | 1F,07,0C,0E,true,4B,6C,64,30,64,true 352 | 20,07,0E,10,true,2B,E0,10,10,64,false 353 | 21,07,0E,11,true,2B,40,10,10,B4,false 354 | 22,07,0E,11,true,2B,A8,10,10,74,false -------------------------------------------------------------------------------- /MM2RandoLib/Resources/enemyweakness.csv: -------------------------------------------------------------------------------- 1 | #EnemyName,#OffsetHex,#P,#H,#A,#W,#B,#Q,#C,#M 2 | Shrink ,00,7,20,0,10,20,7,10,10 3 | #Shrink(From Angler) ,01,7,20,0,10,20,7,10,10 4 | M445(Instance) ,04,20,20,20,20,0,20,20,20 5 | Claw(Instance) ,08,20,20,10,10,20,7,10,10 6 | Tanishi ,0A,10,20,0,10,0,4,10,4 7 | Kerog ,0C,2,20,0,0,4,4,0,2 8 | Kerog(Small) ,0D,20,20,10,10,20,20,10,10 9 | Batton ,16,10,20,10,10,0,10,10,20 10 | Robbit ,17,2,20,0,20,7,4,20,4 11 | Friender(Instance) ,19,1,20,0,20,0,2,0,2 12 | Monking ,1D,7,20,0,20,0,7,20,7 13 | Cook(Instance) ,1F,2,20,0,20,0,2,20,0 14 | Telly(Instance) ,22,20,20,10,20,0,20,20,20 15 | Pierobot ,29,6,20,0,7,0,7,20,0 16 | Pierobot(Clown) ,2A,20,20,20,20,0,7,20,20 17 | Fly Boy(Instance) ,2C,4,20,20,7,0,4,20,20 18 | Press ,30,0,0,0,0,0,0,0,0 19 | Blocky ,31,10,20,0,0,0,10,20,10 20 | Neo Metall ,34,20,20,0,20,20,20,20,20 21 | Matasaburo ,36,4,0,0,7,7,4,7,4 22 | Pipi(Instance) ,38,20,20,20,20,0,20,20,20 23 | Lightning Goro ,3D,7,0,20,20,20,7,20,7 24 | Springer ,46,0,20,0,20,7,7,7,0 25 | Mole(Instance Up) ,48,4,20,7,20,0,4,4,7 26 | #Mole(Instance Down) ,49,4,20,7,20,0,4,4,7 27 | Shotman(Facing Left) ,4B,4,20,7,20,2,4,4,4 28 | #Shotman(Facing Right),4C,4,20,7,20,2,4,4,4 29 | Sniper Armor ,4E,1,20,7,7,0,4,4,0 30 | Sniper Joe ,4F,2,20,4,7,4,4,2,2 31 | Scworm ,50,4,20,20,20,7,4,4,20 -------------------------------------------------------------------------------- /MM2RandoLib/Resources/level_components.json: -------------------------------------------------------------------------------- 1 | { 2 | "LevelComponents": [ 3 | { 4 | "Name": "flash_room0_screen2", 5 | "StartAddress": "14590", 6 | "EndAddress": "145CF", 7 | "Variations": [ 8 | { 9 | "Name": "default", 10 | "TsaMap": [ 11 | "59","2A","28","01","01","23","58","59", 12 | "6B","33","25","1B","03","25","45","67", 13 | "59","60","30","09","03","75","78","03", 14 | "59","23","2D","10","03","17","09","71", 15 | "61","29","22","0D","03","33","0B","03", 16 | "0F","2B","30","1A","03","21","0D","38", 17 | "0F","31","2C","10","03","22","18","69", 18 | "2F","3A","31","0C","03","30","14","69"], 19 | "Sprites": "asdf" 20 | }, 21 | { 22 | "Name": "opened up", 23 | "TsaMap": [ 24 | "59","2A","28","01","01","75","78","69", 25 | "6B","33","25","0F","0F","0F","09","38", 26 | "59","60","30","0F","0F","0F","1B","03", 27 | "59","23","2D","0F","0F","0F","09","71", 28 | "61","29","22","0F","0F","0F","0B","03", 29 | "0F","2B","30","0F","0F","0F","0D","38", 30 | "0F","31","2C","10","03","22","18","69", 31 | "2F","3A","31","0C","03","30","14","69"], 32 | "Sprites": "asdf" 33 | }, 34 | { 35 | "Name": "1 tile platforms", 36 | "TsaMap": [ 37 | "59", "2A", "28","01","01","75","78","69", 38 | "6B", "33", "25","0F","0F","0F","09","38", 39 | "59", "60", "30","0F","0F","17","1B","03", 40 | "59", "23", "2D","0F","0F","0F","09","71", 41 | "61", "29", "0F","0F","27","06","0B","03", 42 | "0F", "2B", "0F","0F","0A","03","0D","38", 43 | "0F", "31", "0F","0F","0F","0F","18","69", 44 | "2F", "22", "0F","10","03","30","14","69"], 45 | "Sprites": "asdf" 46 | } 47 | ] 48 | }, 49 | ] 50 | } -------------------------------------------------------------------------------- /MM2RandoLib/Resources/mm2rng_musicpatch.ips: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duckfist/MM2Random/178a4cb7649b194782b94caf9b54379e52914c0d/MM2RandoLib/Resources/mm2rng_musicpatch.ips -------------------------------------------------------------------------------- /MM2RandoLib/Resources/mm2rng_prepatch.ips: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/duckfist/MM2Random/178a4cb7649b194782b94caf9b54379e52914c0d/MM2RandoLib/Resources/mm2rng_prepatch.ips -------------------------------------------------------------------------------- /MM2RandoLib/Utilities/SeedConvert.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace MM2Randomizer.Utilities 6 | { 7 | public static class SeedConvert 8 | { 9 | public static string ConvertBase10To26(int n) 10 | { 11 | string hexavigesimal = IntToString(n, 12 | Enumerable.Range('A', 26).Select(x => (char)x).ToArray()); 13 | return hexavigesimal; 14 | } 15 | 16 | public static int ConvertBase26To10(string n) 17 | { 18 | int base10 = StringToInt(n, 19 | Enumerable.Range('A', 26).Select(x => (char)x).ToArray()); 20 | return base10; 21 | } 22 | 23 | public static string IntToString(int value, char[] baseChars) 24 | { 25 | string result = string.Empty; 26 | int targetBase = baseChars.Length; 27 | 28 | do 29 | { 30 | result = baseChars[value % targetBase] + result; 31 | value = value / targetBase; 32 | } 33 | while (value > 0); 34 | 35 | return result; 36 | } 37 | 38 | public static int StringToInt(string value, char[] baseChars) 39 | { 40 | int result = 0; 41 | int targetBase = baseChars.Length; 42 | List baseCharsList = new List(baseChars); 43 | char[] valueChars = value.ToCharArray(); 44 | 45 | for (int i = 0; i < valueChars.Length; i++) 46 | { 47 | // Starting from the right of the string, get the index of the character 48 | int index = baseCharsList.IndexOf(valueChars[valueChars.Length - 1 - i]); 49 | 50 | // Add the product of each digit with its place value 51 | result = result + index * (int)Math.Pow(targetBase, i); 52 | } 53 | return result; 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /MM2RandoLib/WeaponTable.cs: -------------------------------------------------------------------------------- 1 | using MM2Randomizer.Enums; 2 | 3 | namespace MM2Randomizer 4 | { 5 | /// 6 | /// This object encapsulates 7 | /// 8 | public class WeaponTable 9 | { 10 | /// 11 | /// The name of the weapon. 12 | /// 13 | public string Name { get; set; } 14 | 15 | /// 16 | /// The unique ID of the weapon, which is referred to by the rest of the ROM. 17 | /// 18 | public int ID { get; set; } 19 | 20 | /// 21 | /// Pointer to the damage table for this weapon. This exact address refers to the 22 | /// damage done by this weapon against Heat Man, with the subsequent addresses 23 | /// referring to the next Robot Masters. 24 | /// Address + 0: Heat 25 | /// Address + 1: Air 26 | /// Address + 2: Wood 27 | /// Address + 3: Bubble 28 | /// Address + 4: Quick 29 | /// Address + 5: Flash 30 | /// Address + 6: Metal 31 | /// Address + 7: Clash 32 | /// 33 | public EDmgVsBoss Address { get; set; } 34 | 35 | /// 36 | /// The damage values used by this weapon against each Robot Master. They will be 37 | /// inserted into the offset provided by "Address". 38 | /// [0] = Heat 39 | /// [1] = Air 40 | /// [2] = Wood 41 | /// [3] = Bubble 42 | /// [4] = Quick 43 | /// [5] = Flash 44 | /// [6] = Metal 45 | /// [7] = Clash 46 | /// 47 | public int[] RobotMasters { get; set; } 48 | 49 | 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /MM2RandoLib/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /MM2Randomizer.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27004.2002 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MM2RandoHost", "MM2RandoHost\MM2RandoHost.csproj", "{F12A49E8-C7A7-4FDE-BB7C-EA80451D5B96}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MM2RandoLib", "MM2RandoLib\MM2RandoLib.csproj", "{2F96FE8D-29DC-46F3-9D26-FC85DC2225C8}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Debug|x64 = Debug|x64 14 | Release|Any CPU = Release|Any CPU 15 | Release|x64 = Release|x64 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {F12A49E8-C7A7-4FDE-BB7C-EA80451D5B96}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {F12A49E8-C7A7-4FDE-BB7C-EA80451D5B96}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {F12A49E8-C7A7-4FDE-BB7C-EA80451D5B96}.Debug|x64.ActiveCfg = Debug|x64 21 | {F12A49E8-C7A7-4FDE-BB7C-EA80451D5B96}.Debug|x64.Build.0 = Debug|x64 22 | {F12A49E8-C7A7-4FDE-BB7C-EA80451D5B96}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {F12A49E8-C7A7-4FDE-BB7C-EA80451D5B96}.Release|Any CPU.Build.0 = Release|Any CPU 24 | {F12A49E8-C7A7-4FDE-BB7C-EA80451D5B96}.Release|x64.ActiveCfg = Release|x64 25 | {F12A49E8-C7A7-4FDE-BB7C-EA80451D5B96}.Release|x64.Build.0 = Release|x64 26 | {2F96FE8D-29DC-46F3-9D26-FC85DC2225C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {2F96FE8D-29DC-46F3-9D26-FC85DC2225C8}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {2F96FE8D-29DC-46F3-9D26-FC85DC2225C8}.Debug|x64.ActiveCfg = Debug|x64 29 | {2F96FE8D-29DC-46F3-9D26-FC85DC2225C8}.Debug|x64.Build.0 = Debug|x64 30 | {2F96FE8D-29DC-46F3-9D26-FC85DC2225C8}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 | {2F96FE8D-29DC-46F3-9D26-FC85DC2225C8}.Release|Any CPU.Build.0 = Release|Any CPU 32 | {2F96FE8D-29DC-46F3-9D26-FC85DC2225C8}.Release|x64.ActiveCfg = Release|x64 33 | {2F96FE8D-29DC-46F3-9D26-FC85DC2225C8}.Release|x64.Build.0 = Release|x64 34 | EndGlobalSection 35 | GlobalSection(SolutionProperties) = preSolution 36 | HideSolutionNode = FALSE 37 | EndGlobalSection 38 | GlobalSection(ExtensibilityGlobals) = postSolution 39 | SolutionGuid = {0A433506-4D6A-42A1-AD39-F9AF55A48934} 40 | EndGlobalSection 41 | EndGlobal 42 | --------------------------------------------------------------------------------