using System; using System.Collections.Generic; using System.Drawing; using System.IO; using System.Linq; using System.Text; using System.Windows.Forms; using BizHawk.Client.EmuHawk.ToolExtensions; using BizHawk.Emulation.Common; using BizHawk.Client.Common; namespace BizHawk.Client.EmuHawk { public partial class BasicBot : ToolFormBase , IToolFormAutoConfig { private const string DialogTitle = "Basic Bot"; private string _currentFileName = string.Empty; private string CurrentFileName { get { return _currentFileName; } set { _currentFileName = value; if (!string.IsNullOrWhiteSpace(_currentFileName)) { Text = DialogTitle + " - " + Path.GetFileNameWithoutExtension(_currentFileName); } else { Text = DialogTitle; } } } private bool _isBotting = false; private long _attempts = 1; private long _frames = 0; private int _targetFrame = 0; private bool _oldCountingSetting = false; private BotAttempt _currentBotAttempt = null; private BotAttempt _bestBotAttempt = null; private BotAttempt _comparisonBotAttempt = null; private bool _replayMode = false; private int _startFrame = 0; private string _lastRom = string.Empty; private bool _dontUpdateValues = false; private MemoryDomain _currentDomain; private bool _bigEndian; private int _dataSize; private Dictionary _cachedControlProbabilities; private ILogEntryGenerator _logGenerator; #region Services and Settings [RequiredService] private IEmulator Emulator { get; set; } // Unused, due to the use of MainForm to loadstate, but this needs to be kept here in order to establish an IStatable dependency [RequiredService] private IStatable StatableCore { get; set; } [RequiredService] private IMemoryDomains MemoryDomains { get; set; } [ConfigPersist] public BasicBotSettings Settings { get; set; } public class BasicBotSettings { public BasicBotSettings() { RecentBotFiles = new RecentFiles(); TurboWhenBotting = true; } public RecentFiles RecentBotFiles { get; set; } public bool TurboWhenBotting { get; set; } } #endregion #region Initialize public BasicBot() { InitializeComponent(); Text = DialogTitle; Settings = new BasicBotSettings(); _comparisonBotAttempt = new BotAttempt(); } private void BasicBot_Load(object sender, EventArgs e) { } #endregion #region UI Bindings private Dictionary ControlProbabilities { get { return ControlProbabilityPanel.Controls .OfType() .ToDictionary(tkey => tkey.ButtonName, tvalue => tvalue.Probability); } } private string SelectedSlot { get { char num = StartFromSlotBox.SelectedItem .ToString() .Last(); return "QuickSave" + num; } } private long Attempts { get { return _attempts; } set { _attempts = value; AttemptsLabel.Text = _attempts.ToString(); } } private long Frames { get { return _frames; } set { _frames = value; FramesLabel.Text = _frames.ToString(); } } private int FrameLength { get { return (int)FrameLengthNumeric.Value; } set { FrameLengthNumeric.Value = value; } } public int MaximizeAddress { get { int? addr = MaximizeAddressBox.ToRawInt(); if (addr.HasValue) { return addr.Value; } return 0; } set { MaximizeAddressBox.SetFromRawInt(value); } } public int MaximizeValue { get { int? addr = MaximizeAddressBox.ToRawInt(); if (addr.HasValue) { return GetRamvalue(addr.Value); } return 0; } } public int TieBreaker1Address { get { int? addr = TieBreaker1Box.ToRawInt(); if (addr.HasValue) { return addr.Value; } return 0; } set { TieBreaker1Box.SetFromRawInt(value); } } public int TieBreaker1Value { get { int? addr = TieBreaker1Box.ToRawInt(); if (addr.HasValue) { return GetRamvalue(addr.Value); } return 0; } } public int TieBreaker2Address { get { int? addr = TieBreaker2Box.ToRawInt(); if (addr.HasValue) { return addr.Value; } return 0; } set { TieBreaker2Box.SetFromRawInt(value); } } public int TieBreaker2Value { get { int? addr = TieBreaker2Box.ToRawInt(); if (addr.HasValue) { return GetRamvalue(addr.Value); } return 0; } } public int TieBreaker3Address { get { int? addr = TieBreaker3Box.ToRawInt(); if (addr.HasValue) { return addr.Value; } return 0; } set { TieBreaker3Box.SetFromRawInt(value); } } public int TieBreaker3Value { get { int? addr = TieBreaker3Box.ToRawInt(); if (addr.HasValue) { return GetRamvalue(addr.Value); } return 0; } } public byte MainComparisonType { get { return (byte)MainOperator.SelectedIndex; } set { if (value < 5) MainOperator.SelectedIndex = value; else MainOperator.SelectedIndex = 0; } } public byte Tie1ComparisonType { get { return (byte)Tiebreak1Operator.SelectedIndex; } set { if (value < 5) Tiebreak1Operator.SelectedIndex = value; else Tiebreak1Operator.SelectedIndex = 0; } } public byte Tie2ComparisonType { get { return (byte)Tiebreak2Operator.SelectedIndex; } set { if (value < 5) Tiebreak2Operator.SelectedIndex = value; else Tiebreak2Operator.SelectedIndex = 0; } } public byte Tie3ComparisonType { get { return (byte)Tiebreak3Operator.SelectedIndex; } set { if (value < 5) Tiebreak3Operator.SelectedIndex = value; else Tiebreak3Operator.SelectedIndex = 0; } } public string FromSlot { get { return StartFromSlotBox.SelectedItem != null ? StartFromSlotBox.SelectedItem.ToString() : string.Empty; } set { var item = StartFromSlotBox.Items. OfType() .FirstOrDefault(o => o.ToString() == value); if (item != null) { StartFromSlotBox.SelectedItem = item; } else { StartFromSlotBox.SelectedItem = null; } } } #endregion #region IToolForm Implementation public bool UpdateBefore { get { return true; } } public void NewUpdate(ToolFormUpdateType type) { } public void UpdateValues() { Update(fast: false); } public void FastUpdate() { Update(fast: true); } public void Restart() { if (_currentDomain == null || MemoryDomains.Contains(_currentDomain)) { _currentDomain = MemoryDomains.MainMemory; _bigEndian = _currentDomain.EndianType == MemoryDomain.Endian.Big; _dataSize = 1; } if (_isBotting) { StopBot(); } else if (_replayMode) { FinishReplay(); } if (_lastRom != GlobalWin.MainForm.CurrentlyOpenRom) { _lastRom = GlobalWin.MainForm.CurrentlyOpenRom; SetupControlsAndProperties(); } } public bool AskSaveChanges() { return true; } #endregion #region Control Events #region FileMenu private void FileSubMenu_DropDownOpened(object sender, EventArgs e) { SaveMenuItem.Enabled = !string.IsNullOrWhiteSpace(CurrentFileName); } private void RecentSubMenu_DropDownOpened(object sender, EventArgs e) { RecentSubMenu.DropDownItems.Clear(); RecentSubMenu.DropDownItems.AddRange( Settings.RecentBotFiles.RecentMenu(LoadFileFromRecent, true)); } private void NewMenuItem_Click(object sender, EventArgs e) { CurrentFileName = string.Empty; _bestBotAttempt = null; ControlProbabilityPanel.Controls .OfType() .ToList() .ForEach(cp => cp.Probability = 0); FrameLength = 0; MaximizeAddress = 0; TieBreaker1Address = 0; TieBreaker2Address = 0; TieBreaker3Address = 0; StartFromSlotBox.SelectedIndex = 0; MainOperator.SelectedIndex = 0; Tiebreak1Operator.SelectedIndex = 0; Tiebreak2Operator.SelectedIndex = 0; Tiebreak3Operator.SelectedIndex = 0; MainBestRadio.Checked = true; MainValueNumeric.Value = 0; TieBreak1Numeric.Value = 0; TieBreak2Numeric.Value = 0; TieBreak3Numeric.Value = 0; TieBreak1BestRadio.Checked = true; TieBreak2BestRadio.Checked = true; TieBreak3BestRadio.Checked = true; UpdateBestAttempt(); UpdateComparisonBotAttempt(); } private void OpenMenuItem_Click(object sender, EventArgs e) { var file = OpenFileDialog( CurrentFileName, PathManager.MakeAbsolutePath(Global.Config.PathEntries.ToolsPathFragment, null), "Bot files", "bot" ); if (file != null) { LoadBotFile(file.FullName); } } private void SaveMenuItem_Click(object sender, EventArgs e) { if (!string.IsNullOrWhiteSpace(CurrentFileName)) { SaveBotFile(CurrentFileName); } } private void SaveAsMenuItem_Click(object sender, EventArgs e) { var file = SaveFileDialog( CurrentFileName, PathManager.MakeAbsolutePath(Global.Config.PathEntries.ToolsPathFragment, null), "Bot files", "bot" ); if (file != null) { SaveBotFile(file.FullName); _currentFileName = file.FullName; } } private void ExitMenuItem_Click(object sender, EventArgs e) { Close(); } #endregion #region Options Menu private void OptionsSubMenu_DropDownOpened(object sender, EventArgs e) { TurboWhileBottingMenuItem.Checked = Settings.TurboWhenBotting; BigEndianMenuItem.Checked = _bigEndian; } private void MemoryDomainsMenuItem_DropDownOpened(object sender, EventArgs e) { MemoryDomainsMenuItem.DropDownItems.Clear(); MemoryDomainsMenuItem.DropDownItems.AddRange( MemoryDomains.MenuItems(SetMemoryDomain, _currentDomain.Name) .ToArray()); } private void BigEndianMenuItem_Click(object sender, EventArgs e) { _bigEndian ^= true; } private void DataSizeMenuItem_DropDownOpened(object sender, EventArgs e) { _1ByteMenuItem.Checked = _dataSize == 1; _2ByteMenuItem.Checked = _dataSize == 2; _4ByteMenuItem.Checked = _dataSize == 4; } private void _1ByteMenuItem_Click(object sender, EventArgs e) { _dataSize = 1; } private void _2ByteMenuItem_Click(object sender, EventArgs e) { _dataSize = 2; } private void _4ByteMenuItem_Click(object sender, EventArgs e) { _dataSize = 4; } private void TurboWhileBottingMenuItem_Click(object sender, EventArgs e) { Settings.TurboWhenBotting ^= true; } #endregion private void RunBtn_Click(object sender, EventArgs e) { StartBot(); } private void StopBtn_Click(object sender, EventArgs e) { StopBot(); } private void ClearBestButton_Click(object sender, EventArgs e) { _bestBotAttempt = null; Attempts = 0; Frames = 0; UpdateBestAttempt(); UpdateComparisonBotAttempt(); } private void PlayBestButton_Click(object sender, EventArgs e) { StopBot(); _replayMode = true; _dontUpdateValues = true; GlobalWin.MainForm.LoadQuickSave(SelectedSlot, false, true); // Triggers an UpdateValues call _dontUpdateValues = false; _startFrame = Emulator.Frame; SetNormalSpeed(); UpdateBotStatusIcon(); MessageLabel.Text = "Replaying"; GlobalWin.MainForm.UnpauseEmulator(); } private void FrameLengthNumeric_ValueChanged(object sender, EventArgs e) { AssessRunButtonStatus(); } private void ClearStatsContextMenuItem_Click(object sender, EventArgs e) { Attempts = 0; Frames = 0; } #endregion #region Classes private class BotAttempt { public BotAttempt() { Log = new List(); } public long Attempt { get; set; } public int Maximize { get; set; } public int TieBreak1 { get; set; } public int TieBreak2 { get; set; } public int TieBreak3 { get; set; } public byte ComparisonTypeMain { get; set; } public byte ComparisonTypeTie1 { get; set; } public byte ComparisonTypeTie2 { get; set; } public byte ComparisonTypeTie3 { get; set; } public List Log { get; set; } } private class BotData { public BotData() { MainCompareToBest = true; TieBreaker1CompareToBest = true; TieBreaker2CompareToBest = true; TieBreaker3CompareToBest = true; } public BotAttempt Best { get; set; } public Dictionary ControlProbabilities { get; set; } public int Maximize { get; set; } public int TieBreaker1 { get; set; } public int TieBreaker2 { get; set; } public int TieBreaker3 { get; set; } public byte ComparisonTypeMain { get; set; } public byte ComparisonTypeTie1 { get; set; } public byte ComparisonTypeTie2 { get; set; } public byte ComparisonTypeTie3 { get; set; } public bool MainCompareToBest { get; set; } public bool TieBreaker1CompareToBest { get; set; } public bool TieBreaker2CompareToBest { get; set; } public bool TieBreaker3CompareToBest { get; set; } public int MainCompareToValue { get; set; } public int TieBreaker1CompareToValue { get; set; } public int TieBreaker2CompareToValue { get; set; } public int TieBreaker3CompareToValue { get; set; } public int FrameLength { get; set; } public string FromSlot { get; set; } public long Attempts { get; set; } public long Frames { get; set; } public string MemoryDomain { get; set; } public bool BigEndian { get; set; } public int DataSize { get; set; } } #endregion #region File Handling private void LoadFileFromRecent(string path) { var result = LoadBotFile(path); if (!result) { Settings.RecentBotFiles.HandleLoadError(path); } } private bool LoadBotFile(string path) { var file = new FileInfo(path); if (!file.Exists) { return false; } var json = File.ReadAllText(path); var botData = (BotData)ConfigService.LoadWithType(json); _bestBotAttempt = botData.Best; var probabilityControls = ControlProbabilityPanel.Controls .OfType() .ToList(); foreach (var kvp in botData.ControlProbabilities) { var control = probabilityControls.Single(c => c.ButtonName == kvp.Key); control.Probability = kvp.Value; } MaximizeAddress = botData.Maximize; TieBreaker1Address = botData.TieBreaker1; TieBreaker2Address = botData.TieBreaker2; TieBreaker3Address = botData.TieBreaker3; try { MainComparisonType = botData.ComparisonTypeMain; Tie1ComparisonType = botData.ComparisonTypeTie1; Tie2ComparisonType = botData.ComparisonTypeTie2; Tie3ComparisonType = botData.ComparisonTypeTie3; MainBestRadio.Checked = botData.MainCompareToBest; TieBreak1BestRadio.Checked = botData.TieBreaker1CompareToBest; TieBreak2BestRadio.Checked = botData.TieBreaker2CompareToBest; TieBreak3BestRadio.Checked = botData.TieBreaker3CompareToBest; MainValueRadio.Checked = !botData.MainCompareToBest; TieBreak1ValueRadio.Checked = !botData.TieBreaker1CompareToBest; TieBreak2ValueRadio.Checked = !botData.TieBreaker2CompareToBest; TieBreak3ValueRadio.Checked = !botData.TieBreaker3CompareToBest; MainValueNumeric.Value = botData.MainCompareToValue; TieBreak1Numeric.Value = botData.TieBreaker1CompareToValue; TieBreak2Numeric.Value = botData.TieBreaker2CompareToValue; TieBreak3Numeric.Value = botData.TieBreaker3CompareToValue; } catch { MainComparisonType = 0; Tie1ComparisonType = 0; Tie2ComparisonType = 0; Tie3ComparisonType = 0; MainBestRadio.Checked = true; TieBreak1BestRadio.Checked = true; TieBreak2BestRadio.Checked = true; TieBreak3BestRadio.Checked = true; MainBestRadio.Checked = false; TieBreak1BestRadio.Checked = false; TieBreak2BestRadio.Checked = false; TieBreak3BestRadio.Checked = false; MainValueNumeric.Value = 0; TieBreak1Numeric.Value = 0; TieBreak2Numeric.Value = 0; TieBreak3Numeric.Value = 0; } FrameLength = botData.FrameLength; FromSlot = botData.FromSlot; Attempts = botData.Attempts; Frames = botData.Frames; _currentDomain = !string.IsNullOrWhiteSpace(botData.MemoryDomain) ? MemoryDomains[botData.MemoryDomain] : MemoryDomains.MainMemory; _bigEndian = botData.BigEndian; _dataSize = botData.DataSize > 0 ? botData.DataSize : 1; UpdateBestAttempt(); UpdateComparisonBotAttempt(); if (_bestBotAttempt != null) { PlayBestButton.Enabled = true; } CurrentFileName = path; Settings.RecentBotFiles.Add(CurrentFileName); MessageLabel.Text = Path.GetFileNameWithoutExtension(path) + " loaded"; AssessRunButtonStatus(); return true; } private void SaveBotFile(string path) { var data = new BotData { Best = _bestBotAttempt, ControlProbabilities = ControlProbabilities, Maximize = MaximizeAddress, TieBreaker1 = TieBreaker1Address, TieBreaker2 = TieBreaker2Address, TieBreaker3 = TieBreaker3Address, ComparisonTypeMain = MainComparisonType, ComparisonTypeTie1 = Tie1ComparisonType, ComparisonTypeTie2 = Tie2ComparisonType, ComparisonTypeTie3 = Tie3ComparisonType, MainCompareToBest = MainBestRadio.Checked, TieBreaker1CompareToBest = TieBreak1BestRadio.Checked, TieBreaker2CompareToBest = TieBreak2BestRadio.Checked, TieBreaker3CompareToBest = TieBreak3BestRadio.Checked, MainCompareToValue = (int)MainValueNumeric.Value, TieBreaker1CompareToValue = (int)TieBreak1Numeric.Value, TieBreaker2CompareToValue = (int)TieBreak2Numeric.Value, TieBreaker3CompareToValue = (int)TieBreak3Numeric.Value, FromSlot = FromSlot, FrameLength = FrameLength, Attempts = Attempts, Frames = Frames, MemoryDomain = _currentDomain.Name, BigEndian = _bigEndian, DataSize = _dataSize }; var json = ConfigService.SaveWithType(data); File.WriteAllText(path, json); CurrentFileName = path; Settings.RecentBotFiles.Add(CurrentFileName); MessageLabel.Text = Path.GetFileName(CurrentFileName) + " saved"; } #endregion private void SetupControlsAndProperties() { MaximizeAddressBox.SetHexProperties(MemoryDomains.MainMemory.Size); TieBreaker1Box.SetHexProperties(MemoryDomains.MainMemory.Size); TieBreaker2Box.SetHexProperties(MemoryDomains.MainMemory.Size); TieBreaker3Box.SetHexProperties(MemoryDomains.MainMemory.Size); StartFromSlotBox.SelectedIndex = 0; int starty = 0; int accumulatedy = 0; int lineHeight = 30; int marginLeft = 15; int count = 0; ControlProbabilityPanel.Controls.Clear(); foreach (var button in Emulator.ControllerDefinition.BoolButtons) { var control = new BotControlsRow { ButtonName = button, Probability = 0.0, Location = new Point(marginLeft, starty + accumulatedy), TabIndex = count + 1, ProbabilityChangedCallback = AssessRunButtonStatus }; ControlProbabilityPanel.Controls.Add(control); accumulatedy += lineHeight; count++; } if (Settings.RecentBotFiles.AutoLoad) { LoadFileFromRecent(Settings.RecentBotFiles.MostRecent); } UpdateBotStatusIcon(); } private void SetMemoryDomain(string name) { _currentDomain = MemoryDomains[name]; _bigEndian = MemoryDomains[name].EndianType == MemoryDomain.Endian.Big; } private int GetRamvalue(int addr) { int val; switch (_dataSize) { default: case 1: val = _currentDomain.PeekByte(addr); break; case 2: val = _currentDomain.PeekUshort(addr, _bigEndian); break; case 4: val = (int)_currentDomain.PeekUint(addr, _bigEndian); break; } return val; } private void Update(bool fast) { if (_dontUpdateValues) { return; } if (_replayMode) { int index = Emulator.Frame - _startFrame; if (index < _bestBotAttempt.Log.Count) { var logEntry = _bestBotAttempt.Log[index]; var lg = Global.MovieSession.MovieControllerInstance(); lg.SetControllersAsMnemonic(logEntry); foreach (var button in lg.Type.BoolButtons) { // TODO: make an input adapter specifically for the bot? Global.LuaAndAdaptor.SetButton(button, lg.IsPressed(button)); } } else { FinishReplay(); } } else if (_isBotting) { if (Global.Emulator.Frame >= _targetFrame) { Attempts++; Frames += FrameLength; _currentBotAttempt.Maximize = MaximizeValue; _currentBotAttempt.TieBreak1 = TieBreaker1Value; _currentBotAttempt.TieBreak2 = TieBreaker2Value; _currentBotAttempt.TieBreak3 = TieBreaker3Value; PlayBestButton.Enabled = true; if (IsBetter(_comparisonBotAttempt, _currentBotAttempt)) { _bestBotAttempt = _currentBotAttempt; UpdateBestAttempt(); } _currentBotAttempt = new BotAttempt { Attempt = Attempts }; GlobalWin.MainForm.LoadQuickSave(SelectedSlot, false, true); } PressButtons(); } } private void FinishReplay() { GlobalWin.MainForm.PauseEmulator(); _startFrame = 0; _replayMode = false; UpdateBotStatusIcon(); MessageLabel.Text = "Replay stopped"; } private bool IsBetter(BotAttempt comparison, BotAttempt current) { if (!TestValue(MainComparisonType, current.Maximize, comparison.Maximize)) { return false; } else if (current.Maximize == comparison.Maximize) { if (!TestValue(Tie1ComparisonType, current.TieBreak1, comparison.TieBreak1)) { return false; } else if (current.TieBreak1 == comparison.TieBreak1) { if (!TestValue(Tie2ComparisonType, current.TieBreak2, comparison.TieBreak2)) { return false; } else if (current.TieBreak2 == comparison.TieBreak2) { if (!TestValue(Tie3ComparisonType, current.TieBreak3, current.TieBreak3)) { return false; } } } } return true; } private bool TestValue(byte operation, int currentValue, int bestValue) { switch (operation) { case 0: return currentValue > bestValue; case 1: return currentValue >= bestValue; case 2: return currentValue == bestValue; case 3: return currentValue <= bestValue; case 4: return currentValue < bestValue; } return false; } private void UpdateBestAttempt() { if (_bestBotAttempt != null) { ClearBestButton.Enabled = true; BestAttemptNumberLabel.Text = _bestBotAttempt.Attempt.ToString(); BestMaximizeBox.Text = _bestBotAttempt.Maximize.ToString(); BestTieBreak1Box.Text = _bestBotAttempt.TieBreak1.ToString(); BestTieBreak2Box.Text = _bestBotAttempt.TieBreak2.ToString(); BestTieBreak3Box.Text = _bestBotAttempt.TieBreak3.ToString(); var sb = new StringBuilder(); foreach (var logEntry in _bestBotAttempt.Log) { sb.AppendLine(logEntry); } BestAttemptLogLabel.Text = sb.ToString(); PlayBestButton.Enabled = true; } else { ClearBestButton.Enabled = false; BestAttemptNumberLabel.Text = string.Empty; BestMaximizeBox.Text = string.Empty; BestTieBreak1Box.Text = string.Empty; BestTieBreak2Box.Text = string.Empty; BestTieBreak3Box.Text = string.Empty; BestAttemptLogLabel.Text = string.Empty; PlayBestButton.Enabled = false; } } private void PressButtons() { var rand = new Random((int)DateTime.Now.Ticks); var buttonLog = new Dictionary(); foreach (var button in Emulator.ControllerDefinition.BoolButtons) { double probability = _cachedControlProbabilities[button]; bool pressed = !(rand.Next(100) < probability); buttonLog.Add(button, pressed); Global.ClickyVirtualPadController.SetBool(button, pressed); } _currentBotAttempt.Log.Add(_logGenerator.GenerateLogEntry()); } private void StartBot() { if (!CanStart()) { MessageBox.Show("Unable to run with current settings"); return; } _isBotting = true; ControlsBox.Enabled = false; StartFromSlotBox.Enabled = false; RunBtn.Visible = false; StopBtn.Visible = true; GoalGroupBox.Enabled = false; _currentBotAttempt = new BotAttempt { Attempt = Attempts }; if (Global.MovieSession.Movie.IsRecording) { _oldCountingSetting = Global.MovieSession.Movie.IsCountingRerecords; Global.MovieSession.Movie.IsCountingRerecords = false; } _dontUpdateValues = true; GlobalWin.MainForm.LoadQuickSave(SelectedSlot, false, true); // Triggers an UpdateValues call _dontUpdateValues = false; _targetFrame = Global.Emulator.Frame + (int)FrameLengthNumeric.Value; GlobalWin.MainForm.UnpauseEmulator(); if (Settings.TurboWhenBotting) { SetMaxSpeed(); } UpdateBotStatusIcon(); MessageLabel.Text = "Running..."; _cachedControlProbabilities = ControlProbabilities; _logGenerator = Global.MovieSession.LogGeneratorInstance(); _logGenerator.SetSource(Global.ClickyVirtualPadController); } private bool CanStart() { if (!ControlProbabilities.Any(cp => cp.Value > 0)) { return false; } if (!MaximizeAddressBox.ToRawInt().HasValue) { return false; } if (FrameLengthNumeric.Value == 0) { return false; } return true; } private void StopBot() { RunBtn.Visible = true; StopBtn.Visible = false; _isBotting = false; _targetFrame = 0; ControlsBox.Enabled = true; StartFromSlotBox.Enabled = true; _targetFrame = 0; _currentBotAttempt = null; GoalGroupBox.Enabled = true; if (Global.MovieSession.Movie.IsRecording) { Global.MovieSession.Movie.IsCountingRerecords = _oldCountingSetting; } GlobalWin.MainForm.PauseEmulator(); SetNormalSpeed(); UpdateBotStatusIcon(); MessageLabel.Text = "Bot stopped"; } private void UpdateBotStatusIcon() { if (_replayMode) { BotStatusButton.Image = Properties.Resources.Play; BotStatusButton.ToolTipText = "Replaying best result"; } else if (_isBotting) { BotStatusButton.Image = Properties.Resources.RecordHS; BotStatusButton.ToolTipText = "Botting in progress"; } else { BotStatusButton.Image = Properties.Resources.Pause; BotStatusButton.ToolTipText = "Bot is currently not running"; } } private void SetMaxSpeed() { GlobalWin.MainForm.Unthrottle(); } private void SetNormalSpeed() { GlobalWin.MainForm.Throttle(); } private void AssessRunButtonStatus() { RunBtn.Enabled = FrameLength > 0 && !string.IsNullOrWhiteSpace(MaximizeAddressBox.Text) && ControlProbabilities.Any(kvp => kvp.Value > 0); } /// /// Updates comparison bot attempt with current best bot attempt values for values where the "best" radio button is selected /// private void UpdateComparisonBotAttempt() { if(_bestBotAttempt == null) { if (MainBestRadio.Checked) { _comparisonBotAttempt.Maximize = 0; } if (TieBreak1BestRadio.Checked) { _comparisonBotAttempt.TieBreak1 = 0; } if (TieBreak2BestRadio.Checked) { _comparisonBotAttempt.TieBreak2= 0; } if (TieBreak3BestRadio.Checked) { _comparisonBotAttempt.TieBreak3 = 0; } } else { if (MainBestRadio.Checked && _bestBotAttempt.Maximize != _comparisonBotAttempt.Maximize) { _comparisonBotAttempt.Maximize = _bestBotAttempt.Maximize; } if (TieBreak1BestRadio.Checked && _bestBotAttempt.TieBreak1 != _comparisonBotAttempt.TieBreak1) { _comparisonBotAttempt.TieBreak1 = _bestBotAttempt.TieBreak1; } if (TieBreak2BestRadio.Checked && _bestBotAttempt.TieBreak2 != _comparisonBotAttempt.TieBreak2) { _comparisonBotAttempt.TieBreak2 = _bestBotAttempt.TieBreak2; } if (TieBreak3BestRadio.Checked && _bestBotAttempt.TieBreak3 != _comparisonBotAttempt.TieBreak3) { _comparisonBotAttempt.TieBreak3 = _bestBotAttempt.TieBreak3; } } } private void MainBestRadio_CheckedChanged(object sender, EventArgs e) { RadioButton radioButton = (RadioButton)sender; if (radioButton.Checked) { this.MainValueNumeric.Enabled = false; _comparisonBotAttempt.Maximize = _bestBotAttempt == null ? 0 : _bestBotAttempt.Maximize; } } private void Tiebreak1BestRadio_CheckedChanged(object sender, EventArgs e) { RadioButton radioButton = (RadioButton)sender; if (radioButton.Checked) { this.TieBreak1Numeric.Enabled = false; _comparisonBotAttempt.TieBreak1 = _bestBotAttempt == null ? 0 : _bestBotAttempt.TieBreak1; } } private void Tiebreak2BestRadio_CheckedChanged(object sender, EventArgs e) { RadioButton radioButton = (RadioButton)sender; if (radioButton.Checked) { this.TieBreak2Numeric.Enabled = false; _comparisonBotAttempt.TieBreak2 = _bestBotAttempt == null ? 0 : _bestBotAttempt.TieBreak2; } } private void Tiebreak3BestRadio_CheckedChanged(object sender, EventArgs e) { RadioButton radioButton = (RadioButton)sender; if (radioButton.Checked) { this.TieBreak3Numeric.Enabled = false; _comparisonBotAttempt.TieBreak3 = _bestBotAttempt == null ? 0 : _bestBotAttempt.TieBreak3; } } private void MainValueRadio_CheckedChanged(object sender, EventArgs e) { RadioButton radioButton = (RadioButton)sender; if (radioButton.Checked) { this.MainValueNumeric.Enabled = true; _comparisonBotAttempt.Maximize = (int)this.MainValueNumeric.Value; } } private void TieBreak1ValueRadio_CheckedChanged(object sender, EventArgs e) { RadioButton radioButton = (RadioButton)sender; if (radioButton.Checked) { this.TieBreak1Numeric.Enabled = true; _comparisonBotAttempt.TieBreak1 = (int)this.TieBreak1Numeric.Value; } } private void TieBreak2ValueRadio_CheckedChanged(object sender, EventArgs e) { RadioButton radioButton = (RadioButton)sender; if (radioButton.Checked) { this.TieBreak2Numeric.Enabled = true; _comparisonBotAttempt.TieBreak2 = (int)this.TieBreak2Numeric.Value; } } private void TieBreak3ValueRadio_CheckedChanged(object sender, EventArgs e) { RadioButton radioButton = (RadioButton)sender; if (radioButton.Checked) { this.TieBreak3Numeric.Enabled = true; _comparisonBotAttempt.TieBreak3 = (int)this.TieBreak3Numeric.Value; } } private void MainValueNumeric_ValueChanged(object sender, EventArgs e) { NumericUpDown numericUpDown = (NumericUpDown)sender; this._comparisonBotAttempt.Maximize = (int)numericUpDown.Value; } private void TieBreak1Numeric_ValueChanged(object sender, EventArgs e) { NumericUpDown numericUpDown = (NumericUpDown)sender; this._comparisonBotAttempt.TieBreak1 = (int)numericUpDown.Value; } private void TieBreak2Numeric_ValueChanged(object sender, EventArgs e) { NumericUpDown numericUpDown = (NumericUpDown)sender; this._comparisonBotAttempt.TieBreak2 = (int)numericUpDown.Value; } private void TieBreak3Numeric_ValueChanged(object sender, EventArgs e) { NumericUpDown numericUpDown = (NumericUpDown)sender; this._comparisonBotAttempt.TieBreak3 = (int)numericUpDown.Value; } } }