diff --git a/DDApp/DDApplication/MainWindow.axaml b/DDApp/DDApplication/MainWindow.axaml
index 5fa7007..20562e2 100644
--- a/DDApp/DDApplication/MainWindow.axaml
+++ b/DDApp/DDApplication/MainWindow.axaml
@@ -11,6 +11,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/DDApp/DDApplication/MainWindow.axaml.cs b/DDApp/DDApplication/MainWindow.axaml.cs
index 4e7ad8e..63b9a10 100644
--- a/DDApp/DDApplication/MainWindow.axaml.cs
+++ b/DDApp/DDApplication/MainWindow.axaml.cs
@@ -9,6 +9,7 @@ using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Layout;
using Avalonia.Media;
+using Avalonia.Controls.Primitives;
using Avalonia.Threading;
namespace DDApplication;
@@ -77,6 +78,9 @@ public partial class MainWindow : Window
private bool _lastUnderPressure;
private int _pressureStreak;
private LevelProfile _currentLevelProfile = LevelProfile.Unknown;
+ private bool _isMultiplayerMode;
+ private AggressivenessProfile _selectedAggressiveness = AggressivenessProfile.Aggressive;
+ private ModeOptimizationProfile _selectedModeOptimization = ModeOptimizationProfile.Auto;
public MainWindow()
{
@@ -87,6 +91,20 @@ public partial class MainWindow : Window
ApiKeyBox.Text = _configuredApiKey;
MultiplayerCheckBox.IsChecked = _multiplayerModeDefault;
+ AggressivenessProfileBox.SelectedIndex = 2;
+ ModeOptimizationProfileBox.SelectedIndex = 0;
+ SyncProfileSelectionState();
+
+ MultiplayerCheckBox.PropertyChanged += (_, e) =>
+ {
+ if (e.Property == ToggleButton.IsCheckedProperty)
+ {
+ SyncProfileSelectionState();
+ }
+ };
+ AggressivenessProfileBox.SelectionChanged += (_, _) => SyncProfileSelectionState();
+ ModeOptimizationProfileBox.SelectionChanged += (_, _) => SyncProfileSelectionState();
+
LogList.ItemsSource = _logEntries;
StartButton.Click += async (_, _) => await StartBotAsync();
@@ -125,6 +143,8 @@ public partial class MainWindow : Window
StopButton.IsEnabled = true;
ApiKeyBox.IsEnabled = false;
MultiplayerCheckBox.IsEnabled = false;
+ AggressivenessProfileBox.IsEnabled = false;
+ ModeOptimizationProfileBox.IsEnabled = false;
ResetKnowledgeState();
@@ -189,6 +209,8 @@ public partial class MainWindow : Window
StopButton.IsEnabled = false;
ApiKeyBox.IsEnabled = true;
MultiplayerCheckBox.IsEnabled = true;
+ AggressivenessProfileBox.IsEnabled = true;
+ ModeOptimizationProfileBox.IsEnabled = true;
SetStatus("Stopped.");
}
@@ -481,7 +503,7 @@ public partial class MainWindow : Window
_pressureStreak = Math.Max(_pressureStreak - 1, 0);
}
- var underPressure = hpRatio < PressureHpThreshold(profile) || _recentDamageTicks > 0 || closeThreats >= PressureThreatThreshold(profile) || _survivalTicks > 0 || _pressureStreak >= 2;
+ var underPressure = hpRatio < PressureHpThresholdAdjusted(profile) || _recentDamageTicks > 0 || closeThreats >= PressureThreatThresholdAdjusted(profile) || _survivalTicks > 0 || _pressureStreak >= 2;
_lastUnderPressure = underPressure;
var predictedEnemy = PredictEnemyRelative();
@@ -575,8 +597,8 @@ public partial class MainWindow : Window
});
}
- var specialThreatThreshold = SpecialThreatThreshold(profile);
- var specialHpFloor = SpecialHpFloor(profile);
+ var specialThreatThreshold = SpecialThreatThresholdAdjusted(profile);
+ var specialHpFloor = SpecialHpFloorAdjusted(profile);
if (Ready(BotAction.Special) && closeThreats >= specialThreatThreshold && (hpRatio >= specialHpFloor || closeThreats > specialThreatThreshold))
{
@@ -609,7 +631,7 @@ public partial class MainWindow : Window
{
var retreatDirection = Opposite(immediateDir.Value);
- if (Ready(BotAction.Dash) && (hpRatio < DashHpThreshold(profile) || _pressureStreak >= 3 || _survivalTicks > 0))
+ if (Ready(BotAction.Dash) && (hpRatio < DashHpThresholdAdjusted(profile) || _pressureStreak >= 3 || _survivalTicks > 0))
{
return new BotDecision(
"dash-retreat",
@@ -1001,23 +1023,143 @@ public partial class MainWindow : Window
_ => 2,
};
- private bool ShouldUseImmediateHit(LevelProfile profile, double hpRatio, int closeThreats, bool underPressure) => profile switch
+ private bool ShouldUseImmediateHit(LevelProfile profile, double hpRatio, int closeThreats, bool underPressure)
{
- LevelProfile.Default => hpRatio >= 0.05,
- LevelProfile.BiggerMap => hpRatio >= 0.05,
- LevelProfile.MoreBots => hpRatio >= 0.05,
- LevelProfile.NewBots => hpRatio >= 0.05,
- _ => hpRatio >= 0.05,
- };
+ var hpFloor = SelectedAggressiveness switch
+ {
+ AggressivenessProfile.Conservative => 0.30,
+ AggressivenessProfile.Balanced => 0.16,
+ AggressivenessProfile.Aggressive => 0.05,
+ AggressivenessProfile.Extreme => 0.01,
+ _ => 0.05,
+ };
- private bool ShouldRetreatOnImmediate(LevelProfile profile, double hpRatio, int closeThreats) => profile switch
+ if (IsMultiplayerOptimized)
+ {
+ hpFloor = Math.Max(hpFloor, 0.18);
+ }
+
+ return hpRatio >= hpFloor || (!underPressure && closeThreats <= 1);
+ }
+
+ private bool ShouldRetreatOnImmediate(LevelProfile profile, double hpRatio, int closeThreats)
{
- LevelProfile.Default => hpRatio < 0.02,
- LevelProfile.BiggerMap => hpRatio < 0.02,
- LevelProfile.MoreBots => hpRatio < 0.02,
- LevelProfile.NewBots => hpRatio < 0.02,
- _ => hpRatio < 0.02,
- };
+ var hpRetreat = SelectedAggressiveness switch
+ {
+ AggressivenessProfile.Conservative => 0.30,
+ AggressivenessProfile.Balanced => 0.18,
+ AggressivenessProfile.Aggressive => 0.02,
+ AggressivenessProfile.Extreme => 0.01,
+ _ => 0.02,
+ };
+
+ if (IsMultiplayerOptimized)
+ {
+ hpRetreat = Math.Max(hpRetreat, 0.20);
+ }
+
+ return hpRatio < hpRetreat || (IsMultiplayerOptimized && closeThreats >= 3);
+ }
+
+ private double PressureHpThresholdAdjusted(LevelProfile profile)
+ {
+ var value = PressureHpThreshold(profile);
+ value += SelectedAggressiveness switch
+ {
+ AggressivenessProfile.Conservative => 0.20,
+ AggressivenessProfile.Balanced => 0.08,
+ AggressivenessProfile.Aggressive => 0.00,
+ AggressivenessProfile.Extreme => -0.05,
+ _ => 0.00,
+ };
+
+ if (IsMultiplayerOptimized)
+ {
+ value += 0.08;
+ }
+
+ return Math.Clamp(value, 0.01, 0.95);
+ }
+
+ private int PressureThreatThresholdAdjusted(LevelProfile profile)
+ {
+ var value = PressureThreatThreshold(profile);
+ value += SelectedAggressiveness switch
+ {
+ AggressivenessProfile.Conservative => -2,
+ AggressivenessProfile.Balanced => -1,
+ AggressivenessProfile.Aggressive => 0,
+ AggressivenessProfile.Extreme => 1,
+ _ => 0,
+ };
+
+ if (IsMultiplayerOptimized)
+ {
+ value -= 1;
+ }
+
+ return Math.Clamp(value, 1, 8);
+ }
+
+ private int SpecialThreatThresholdAdjusted(LevelProfile profile)
+ {
+ var value = SpecialThreatThreshold(profile);
+ value += SelectedAggressiveness switch
+ {
+ AggressivenessProfile.Conservative => 1,
+ AggressivenessProfile.Balanced => 0,
+ AggressivenessProfile.Aggressive => 0,
+ AggressivenessProfile.Extreme => -1,
+ _ => 0,
+ };
+
+ if (IsMultiplayerOptimized)
+ {
+ value += 1;
+ }
+
+ return Math.Clamp(value, 1, 6);
+ }
+
+ private double SpecialHpFloorAdjusted(LevelProfile profile)
+ {
+ var value = SpecialHpFloor(profile);
+ value += SelectedAggressiveness switch
+ {
+ AggressivenessProfile.Conservative => 0.20,
+ AggressivenessProfile.Balanced => 0.10,
+ AggressivenessProfile.Aggressive => 0.00,
+ AggressivenessProfile.Extreme => -0.05,
+ _ => 0.00,
+ };
+
+ if (IsMultiplayerOptimized)
+ {
+ value += 0.10;
+ }
+
+ return Math.Clamp(value, 0.01, 0.95);
+ }
+
+ private double DashHpThresholdAdjusted(LevelProfile profile)
+ {
+ var value = DashHpThreshold(profile);
+ value += SelectedAggressiveness switch
+ {
+ AggressivenessProfile.Conservative => 0.18,
+ AggressivenessProfile.Balanced => 0.10,
+ AggressivenessProfile.Aggressive => 0.00,
+ AggressivenessProfile.Extreme => -0.03,
+ _ => 0.00,
+ };
+
+ if (IsMultiplayerOptimized)
+ {
+ value += 0.10;
+ }
+
+ return Math.Clamp(value, 0.01, 0.95);
+ }
private (int X, int Y)? PredictEnemyRelative()
{
@@ -1121,7 +1263,21 @@ public partial class MainWindow : Window
return true;
}
- return DeathHeatFor(direction) >= 4.0;
+ var deathHeatLimit = SelectedAggressiveness switch
+ {
+ AggressivenessProfile.Conservative => 2.5,
+ AggressivenessProfile.Balanced => 3.2,
+ AggressivenessProfile.Aggressive => 4.0,
+ AggressivenessProfile.Extreme => 5.0,
+ _ => 4.0,
+ };
+
+ if (IsMultiplayerOptimized)
+ {
+ deathHeatLimit -= 0.6;
+ }
+
+ return DeathHeatFor(direction) >= deathHeatLimit;
}
private void MarkVisit(int x, int y, double amount)
@@ -1235,7 +1391,7 @@ public partial class MainWindow : Window
$"Level: {_levelName} | Kills: {_kills} | Deaths: {_deaths} | K/D: {kdr} | " +
$"HP: {_currentHealth.ToString("0.0", CultureInfo.InvariantCulture)}/{_maxHealth.ToString("0.0", CultureInfo.InvariantCulture)} ({hpPercent.ToString("0", CultureInfo.InvariantCulture)}%) | " +
$"Progress: {_progress.ToString("0.0", CultureInfo.InvariantCulture)}% | Remaining: {_remainingTime.ToString("0.00", CultureInfo.InvariantCulture)} min | " +
- $"Pos (estimated): ({_x}, {_y}) | Profile: {_currentLevelProfile} | SurvivalMode: {(_survivalTicks > 0 ? "ON" : "OFF")}";
+ $"Pos (estimated): ({_x}, {_y}) | Profile: {_currentLevelProfile} | Aggro: {SelectedAggressiveness} | Mode: {SelectedModeOptimization} | SurvivalMode: {(_survivalTicks > 0 ? "ON" : "OFF")}";
var nearest = _nearestEnemy.HasValue ? $"({_nearestEnemy.Value.X}, {_nearestEnemy.Value.Y})" : "none";
var radar = _lastRadar.Count == 0
@@ -1276,7 +1432,38 @@ public partial class MainWindow : Window
Dispatcher.UIThread.Post(() => StatusText.Text = text);
}
- private bool IsMultiplayerMode => MultiplayerCheckBox.IsChecked == true;
+ private bool IsMultiplayerMode => _isMultiplayerMode;
+
+ private AggressivenessProfile SelectedAggressiveness => _selectedAggressiveness;
+
+ private ModeOptimizationProfile SelectedModeOptimization => _selectedModeOptimization;
+
+ private bool IsMultiplayerOptimized => SelectedModeOptimization switch
+ {
+ ModeOptimizationProfile.SingleplayerOptimized => false,
+ ModeOptimizationProfile.MultiplayerOptimized => true,
+ _ => _isMultiplayerMode,
+ };
+
+ private void SyncProfileSelectionState()
+ {
+ _isMultiplayerMode = MultiplayerCheckBox.IsChecked == true;
+ _selectedAggressiveness = AggressivenessProfileBox.SelectedIndex switch
+ {
+ 0 => AggressivenessProfile.Conservative,
+ 1 => AggressivenessProfile.Balanced,
+ 2 => AggressivenessProfile.Aggressive,
+ 3 => AggressivenessProfile.Extreme,
+ _ => AggressivenessProfile.Aggressive,
+ };
+
+ _selectedModeOptimization = ModeOptimizationProfileBox.SelectedIndex switch
+ {
+ 1 => ModeOptimizationProfile.SingleplayerOptimized,
+ 2 => ModeOptimizationProfile.MultiplayerOptimized,
+ _ => ModeOptimizationProfile.Auto,
+ };
+ }
private void AppendLog(string message)
{
@@ -1382,6 +1569,21 @@ public partial class MainWindow : Window
NewBots,
}
+ private enum AggressivenessProfile
+ {
+ Conservative,
+ Balanced,
+ Aggressive,
+ Extreme,
+ }
+
+ private enum ModeOptimizationProfile
+ {
+ Auto,
+ SingleplayerOptimized,
+ MultiplayerOptimized,
+ }
+
private enum Direction
{
North = 0,