work on well of souls and yolo detection
This commit is contained in:
parent
3456e0d62a
commit
40d30115bf
41 changed files with 3031 additions and 148 deletions
|
|
@ -103,6 +103,7 @@ public class MapRequirementsConverter : IValueConverter
|
|||
MapType.TrialOfChaos => "Trial Token x1",
|
||||
MapType.Temple => "Identity Scroll x20",
|
||||
MapType.Endgame => "Identity Scroll x20",
|
||||
MapType.Kulemak => "Invitation x1",
|
||||
_ => "",
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -181,13 +181,16 @@ public sealed class D2dOverlay
|
|||
private OverlayState BuildState(double fps, RenderTiming timing)
|
||||
{
|
||||
var detection = _bot.EnemyDetector.Latest;
|
||||
var bossDetection = _bot.BossDetector.Latest;
|
||||
return new OverlayState(
|
||||
Enemies: detection.Enemies,
|
||||
Bosses: bossDetection.Bosses,
|
||||
InferenceMs: detection.InferenceMs,
|
||||
Hud: _bot.HudReader.Current,
|
||||
NavState: _bot.Navigation.State,
|
||||
NavPosition: _bot.Navigation.Position,
|
||||
IsExploring: _bot.Navigation.IsExploring,
|
||||
ShowHudDebug: _bot.Store.Settings.ShowHudDebug,
|
||||
Fps: fps,
|
||||
Timing: timing);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,11 +24,13 @@ public sealed class D2dRenderContext : IDisposable
|
|||
// Pre-created brushes
|
||||
public ID2D1SolidColorBrush Red { get; private set; } = null!;
|
||||
public ID2D1SolidColorBrush Yellow { get; private set; } = null!;
|
||||
public ID2D1SolidColorBrush Cyan { get; private set; } = null!;
|
||||
public ID2D1SolidColorBrush Green { get; private set; } = null!;
|
||||
public ID2D1SolidColorBrush White { get; private set; } = null!;
|
||||
public ID2D1SolidColorBrush Gray { get; private set; } = null!;
|
||||
public ID2D1SolidColorBrush LifeBrush { get; private set; } = null!;
|
||||
public ID2D1SolidColorBrush ManaBrush { get; private set; } = null!;
|
||||
public ID2D1SolidColorBrush ShieldBrush { get; private set; } = null!;
|
||||
public ID2D1SolidColorBrush BarBgBrush { get; private set; } = null!;
|
||||
public ID2D1SolidColorBrush LabelBgBrush { get; private set; } = null!;
|
||||
public ID2D1SolidColorBrush DebugTextBrush { get; private set; } = null!;
|
||||
|
|
@ -79,11 +81,13 @@ public sealed class D2dRenderContext : IDisposable
|
|||
{
|
||||
Red = RenderTarget.CreateSolidColorBrush(new Color4(1f, 0f, 0f, 1f));
|
||||
Yellow = RenderTarget.CreateSolidColorBrush(new Color4(1f, 1f, 0f, 1f));
|
||||
Cyan = RenderTarget.CreateSolidColorBrush(new Color4(0f, 1f, 1f, 1f));
|
||||
Green = RenderTarget.CreateSolidColorBrush(new Color4(0.31f, 1f, 0.31f, 1f)); // 80,255,80
|
||||
White = RenderTarget.CreateSolidColorBrush(new Color4(1f, 1f, 1f, 1f));
|
||||
Gray = RenderTarget.CreateSolidColorBrush(new Color4(0.5f, 0.5f, 0.5f, 1f));
|
||||
LifeBrush = RenderTarget.CreateSolidColorBrush(new Color4(200 / 255f, 40 / 255f, 40 / 255f, 1f));
|
||||
ManaBrush = RenderTarget.CreateSolidColorBrush(new Color4(40 / 255f, 80 / 255f, 200 / 255f, 1f));
|
||||
ShieldBrush = RenderTarget.CreateSolidColorBrush(new Color4(100 / 255f, 180 / 255f, 220 / 255f, 1f));
|
||||
BarBgBrush = RenderTarget.CreateSolidColorBrush(new Color4(20 / 255f, 20 / 255f, 20 / 255f, 140 / 255f));
|
||||
LabelBgBrush = RenderTarget.CreateSolidColorBrush(new Color4(0f, 0f, 0f, 160 / 255f));
|
||||
DebugTextBrush = RenderTarget.CreateSolidColorBrush(new Color4(80 / 255f, 1f, 80 / 255f, 1f));
|
||||
|
|
@ -95,11 +99,13 @@ public sealed class D2dRenderContext : IDisposable
|
|||
{
|
||||
Red?.Dispose();
|
||||
Yellow?.Dispose();
|
||||
Cyan?.Dispose();
|
||||
Green?.Dispose();
|
||||
White?.Dispose();
|
||||
Gray?.Dispose();
|
||||
LifeBrush?.Dispose();
|
||||
ManaBrush?.Dispose();
|
||||
ShieldBrush?.Dispose();
|
||||
BarBgBrush?.Dispose();
|
||||
LabelBgBrush?.Dispose();
|
||||
DebugTextBrush?.Dispose();
|
||||
|
|
|
|||
|
|
@ -5,11 +5,13 @@ namespace Poe2Trade.Ui.Overlay;
|
|||
|
||||
public record OverlayState(
|
||||
IReadOnlyList<DetectedEnemy> Enemies,
|
||||
IReadOnlyList<DetectedBoss> Bosses,
|
||||
float InferenceMs,
|
||||
HudSnapshot? Hud,
|
||||
NavigationState NavState,
|
||||
MapPosition NavPosition,
|
||||
bool IsExploring,
|
||||
bool ShowHudDebug,
|
||||
double Fps,
|
||||
RenderTiming? Timing);
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ internal sealed class D2dDebugTextLayer : ID2dOverlayLayer, IDisposable
|
|||
UpdateCache(ctx, _left, ref lc, $"Pos: ({state.NavPosition.X:F0}, {state.NavPosition.Y:F0})", ctx.DebugTextBrush);
|
||||
UpdateCache(ctx, _left, ref lc, $"Enemies: {state.Enemies.Count} YOLO: {state.InferenceMs:F1}ms", ctx.DebugTextBrush);
|
||||
if (state.Hud is { Timestamp: > 0 } hud)
|
||||
UpdateCache(ctx, _left, ref lc, $"HP: {hud.LifePct:P0} MP: {hud.ManaPct:P0}", ctx.DebugTextBrush);
|
||||
UpdateCache(ctx, _left, ref lc, $"HP: {hud.LifePct:P0} ES: {hud.ShieldPct:P0} MP: {hud.ManaPct:P0}", ctx.DebugTextBrush);
|
||||
|
||||
// Right column: timing
|
||||
if (state.Timing != null)
|
||||
|
|
|
|||
|
|
@ -11,8 +11,13 @@ internal sealed class D2dEnemyBoxLayer : ID2dOverlayLayer, IDisposable
|
|||
private readonly IDWriteTextLayout[] _confirmedLabels = new IDWriteTextLayout[101];
|
||||
private readonly IDWriteTextLayout[] _unconfirmedLabels = new IDWriteTextLayout[101];
|
||||
|
||||
// Boss labels: cached by "classname NN%" string
|
||||
private readonly Dictionary<string, IDWriteTextLayout> _bossLabels = new();
|
||||
private readonly D2dRenderContext _ctx;
|
||||
|
||||
public D2dEnemyBoxLayer(D2dRenderContext ctx)
|
||||
{
|
||||
_ctx = ctx;
|
||||
for (int i = 0; i <= 100; i++)
|
||||
{
|
||||
var text = $"{i}%";
|
||||
|
|
@ -41,18 +46,43 @@ internal sealed class D2dEnemyBoxLayer : ID2dOverlayLayer, IDisposable
|
|||
var labelX = enemy.X;
|
||||
var labelY = enemy.Y - m.Height - 2;
|
||||
|
||||
// Background behind label
|
||||
rt.FillRectangle(
|
||||
new RectangleF(labelX - 1, labelY - 1, m.Width + 2, m.Height + 2),
|
||||
ctx.LabelBgBrush);
|
||||
|
||||
rt.DrawTextLayout(new System.Numerics.Vector2(labelX, labelY), layout, textBrush);
|
||||
}
|
||||
|
||||
// Boss bounding boxes (cyan)
|
||||
foreach (var boss in state.Bosses)
|
||||
{
|
||||
var rect = new RectangleF(boss.X, boss.Y, boss.Width, boss.Height);
|
||||
rt.DrawRectangle(rect, ctx.Cyan, 3f);
|
||||
|
||||
var pct = Math.Clamp((int)(boss.Confidence * 100), 0, 100);
|
||||
var key = $"{boss.ClassName} {pct}%";
|
||||
if (!_bossLabels.TryGetValue(key, out var layout))
|
||||
{
|
||||
layout = _ctx.CreateTextLayout(key, _ctx.LabelFormat);
|
||||
_bossLabels[key] = layout;
|
||||
}
|
||||
|
||||
var m = layout.Metrics;
|
||||
var labelX = boss.X;
|
||||
var labelY = boss.Y - m.Height - 2;
|
||||
|
||||
rt.FillRectangle(
|
||||
new RectangleF(labelX - 1, labelY - 1, m.Width + 2, m.Height + 2),
|
||||
ctx.LabelBgBrush);
|
||||
|
||||
rt.DrawTextLayout(new System.Numerics.Vector2(labelX, labelY), layout, ctx.Cyan);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var l in _confirmedLabels) l?.Dispose();
|
||||
foreach (var l in _unconfirmedLabels) l?.Dispose();
|
||||
foreach (var l in _bossLabels.Values) l?.Dispose();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,15 +7,20 @@ namespace Poe2Trade.Ui.Overlay.Layers;
|
|||
|
||||
internal sealed class D2dHudInfoLayer : ID2dOverlayLayer, IDisposable
|
||||
{
|
||||
private const float BarWidth = 200;
|
||||
private const float BarWidth = 160;
|
||||
private const float BarHeight = 16;
|
||||
private const float BarY = 1300;
|
||||
private const float LifeBarX = 1130;
|
||||
private const float ManaBarX = 1230;
|
||||
private const float BarGap = 8;
|
||||
private const float BarY = 1416; // near bottom of 1440
|
||||
|
||||
// 3 bars centered: total = 160*3 + 8*2 = 496, start = (2560-496)/2 = 1032
|
||||
private const float LifeBarX = 1032;
|
||||
private const float ShieldBarX = LifeBarX + BarWidth + BarGap;
|
||||
private const float ManaBarX = ShieldBarX + BarWidth + BarGap;
|
||||
|
||||
// Cached bar value layouts
|
||||
private string? _lifeLabel;
|
||||
private IDWriteTextLayout? _lifeLayout;
|
||||
private string? _shieldLabel;
|
||||
private IDWriteTextLayout? _shieldLayout;
|
||||
private string? _manaLabel;
|
||||
private IDWriteTextLayout? _manaLayout;
|
||||
|
||||
|
|
@ -23,14 +28,24 @@ internal sealed class D2dHudInfoLayer : ID2dOverlayLayer, IDisposable
|
|||
{
|
||||
if (state.Hud == null || state.Hud.Timestamp == 0) return;
|
||||
|
||||
DrawBar(ctx, LifeBarX, BarY, state.Hud.LifePct, ctx.LifeBrush, state.Hud.Life,
|
||||
DrawBar(ctx, LifeBarX, BarY, state.Hud.LifePct, ctx.LifeBrush,
|
||||
ref _lifeLabel, ref _lifeLayout);
|
||||
DrawBar(ctx, ManaBarX, BarY, state.Hud.ManaPct, ctx.ManaBrush, state.Hud.Mana,
|
||||
DrawBar(ctx, ShieldBarX, BarY, state.Hud.ShieldPct, ctx.ShieldBrush,
|
||||
ref _shieldLabel, ref _shieldLayout);
|
||||
DrawBar(ctx, ManaBarX, BarY, state.Hud.ManaPct, ctx.ManaBrush,
|
||||
ref _manaLabel, ref _manaLayout);
|
||||
|
||||
// DEBUG: draw sampling lines
|
||||
if (state.ShowHudDebug)
|
||||
{
|
||||
DrawShieldArc(ctx);
|
||||
DrawSampleLine(ctx, 167, 1185, 1411, ctx.LifeBrush); // life
|
||||
DrawSampleLine(ctx, 2394, 1185, 1411, ctx.ManaBrush); // mana
|
||||
}
|
||||
}
|
||||
|
||||
private static void DrawBar(D2dRenderContext ctx, float x, float y, float pct,
|
||||
ID2D1SolidColorBrush fillBrush, Screen.HudValues? values,
|
||||
ID2D1SolidColorBrush fillBrush,
|
||||
ref string? cachedLabel, ref IDWriteTextLayout? cachedLayout)
|
||||
{
|
||||
var rt = ctx.RenderTarget;
|
||||
|
|
@ -42,31 +57,57 @@ internal sealed class D2dHudInfoLayer : ID2dOverlayLayer, IDisposable
|
|||
rt.DrawRectangle(outer, ctx.Gray, 1f);
|
||||
|
||||
// Fill
|
||||
var fillWidth = BarWidth * Math.Clamp(pct, 0, 1);
|
||||
var clamped = Math.Clamp(pct, 0, 1);
|
||||
var fillWidth = BarWidth * clamped;
|
||||
if (fillWidth > 0)
|
||||
rt.FillRectangle(new RectangleF(x, y, fillWidth, BarHeight), fillBrush);
|
||||
|
||||
// Value text
|
||||
if (values != null)
|
||||
// Percentage text
|
||||
var label = $"{clamped:P0}";
|
||||
if (label != cachedLabel)
|
||||
{
|
||||
var label = $"{values.Current}/{values.Max}";
|
||||
if (label != cachedLabel)
|
||||
{
|
||||
cachedLayout?.Dispose();
|
||||
cachedLabel = label;
|
||||
cachedLayout = ctx.CreateTextLayout(label, ctx.BarValueFormat);
|
||||
}
|
||||
|
||||
var m = cachedLayout!.Metrics;
|
||||
var textX = x + (BarWidth - m.Width) / 2;
|
||||
var textY = y + (BarHeight - m.Height) / 2;
|
||||
rt.DrawTextLayout(new System.Numerics.Vector2(textX, textY), cachedLayout, ctx.White);
|
||||
cachedLayout?.Dispose();
|
||||
cachedLabel = label;
|
||||
cachedLayout = ctx.CreateTextLayout(label, ctx.BarValueFormat);
|
||||
}
|
||||
|
||||
var m = cachedLayout!.Metrics;
|
||||
var textX = x + (BarWidth - m.Width) / 2;
|
||||
var textY = y + (BarHeight - m.Height) / 2;
|
||||
rt.DrawTextLayout(new System.Numerics.Vector2(textX, textY), cachedLayout, ctx.White);
|
||||
}
|
||||
|
||||
private static void DrawShieldArc(D2dRenderContext ctx)
|
||||
{
|
||||
const float cx = 170, cy = 1298, r = 130;
|
||||
var rt = ctx.RenderTarget;
|
||||
|
||||
// Draw dots along the right semicircle (-90° to +90°)
|
||||
for (int deg = -90; deg <= 90; deg += 2)
|
||||
{
|
||||
var rad = deg * Math.PI / 180.0;
|
||||
var x = (float)(cx + r * Math.Cos(rad));
|
||||
var y = (float)(cy + r * Math.Sin(rad));
|
||||
rt.FillRectangle(new RectangleF(x - 1, y - 1, 3, 3), ctx.Yellow);
|
||||
}
|
||||
|
||||
// Draw center cross
|
||||
rt.FillRectangle(new RectangleF(cx - 3, cy - 1, 7, 3), ctx.Yellow);
|
||||
rt.FillRectangle(new RectangleF(cx - 1, cy - 3, 3, 7), ctx.Yellow);
|
||||
}
|
||||
|
||||
private static void DrawSampleLine(D2dRenderContext ctx, float x, float yTop, float yBot, ID2D1SolidColorBrush brush)
|
||||
{
|
||||
ctx.RenderTarget.DrawLine(
|
||||
new System.Numerics.Vector2(x, yTop),
|
||||
new System.Numerics.Vector2(x, yBot),
|
||||
brush, 2f);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_lifeLayout?.Dispose();
|
||||
_shieldLayout?.Dispose();
|
||||
_manaLayout?.Dispose();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,11 @@ public partial class DebugViewModel : ObservableObject
|
|||
|
||||
[ObservableProperty] private string _findText = "";
|
||||
[ObservableProperty] private string _debugResult = "";
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedFor(nameof(BurstCaptureLabel))]
|
||||
private bool _isBurstCapturing;
|
||||
|
||||
public string BurstCaptureLabel => IsBurstCapturing ? "Stop Capture" : "Burst Capture";
|
||||
[ObservableProperty] private string _selectedGridLayout = "inventory";
|
||||
[ObservableProperty] private decimal? _clickX;
|
||||
[ObservableProperty] private decimal? _clickY;
|
||||
|
|
@ -148,6 +153,15 @@ public partial class DebugViewModel : ObservableObject
|
|||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void DetectionStatus()
|
||||
{
|
||||
var enemy = _bot.EnemyDetector.Latest;
|
||||
var boss = _bot.BossDetector.Latest;
|
||||
DebugResult = $"Enemy: enabled={_bot.EnemyDetector.Enabled}, count={enemy.Enemies.Count}, ms={enemy.InferenceMs:F1}\n" +
|
||||
$"Boss: enabled={_bot.BossDetector.Enabled}, count={boss.Bosses.Count}, ms={boss.InferenceMs:F1}";
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void SaveMinimapDebug()
|
||||
{
|
||||
|
|
@ -187,6 +201,116 @@ public partial class DebugViewModel : ObservableObject
|
|||
catch (Exception ex) { DebugResult = $"Failed: {ex.Message}"; }
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task AttackTest()
|
||||
{
|
||||
const int VK_Q = 0x51;
|
||||
const int DurationMs = 30_000;
|
||||
const int PollMs = 100;
|
||||
const float ManaLow = 0.50f;
|
||||
const float ManaResume = 0.75f;
|
||||
const float ManaQThreshold = 0.60f;
|
||||
const int QPhaseStableMs = 2_000;
|
||||
const int QCooldownMs = 5_000;
|
||||
|
||||
var rng = new Random();
|
||||
try
|
||||
{
|
||||
DebugResult = "Attack test: focusing game...";
|
||||
await _bot.Game.FocusGame();
|
||||
await _bot.Game.MoveMouseTo(1280, 720);
|
||||
await Task.Delay(300);
|
||||
|
||||
var holding = true;
|
||||
_bot.Game.LeftMouseDown();
|
||||
_bot.Game.RightMouseDown();
|
||||
|
||||
var sw = System.Diagnostics.Stopwatch.StartNew();
|
||||
var manaStableStart = (long?)null;
|
||||
var qPhase = false;
|
||||
long lastQTime = -QCooldownMs;
|
||||
|
||||
while (sw.ElapsedMilliseconds < DurationMs)
|
||||
{
|
||||
var mana = _bot.HudReader.Current.ManaPct;
|
||||
var elapsed = sw.ElapsedMilliseconds;
|
||||
|
||||
// Mana management
|
||||
if (holding && mana < ManaLow)
|
||||
{
|
||||
_bot.Game.LeftMouseUp();
|
||||
_bot.Game.RightMouseUp();
|
||||
holding = false;
|
||||
DebugResult = $"Attack test: mana low ({mana:P0}), waiting...";
|
||||
await Task.Delay(50 + rng.Next(100));
|
||||
}
|
||||
else if (!holding && mana >= ManaResume)
|
||||
{
|
||||
await Task.Delay(50 + rng.Next(100));
|
||||
_bot.Game.LeftMouseDown();
|
||||
_bot.Game.RightMouseDown();
|
||||
holding = true;
|
||||
DebugResult = $"Attack test: mana recovered ({mana:P0}), attacking...";
|
||||
}
|
||||
|
||||
// Track Q phase activation
|
||||
if (!qPhase)
|
||||
{
|
||||
if (mana > ManaQThreshold)
|
||||
{
|
||||
manaStableStart ??= elapsed;
|
||||
if (elapsed - manaStableStart.Value >= QPhaseStableMs)
|
||||
{
|
||||
qPhase = true;
|
||||
DebugResult = "Attack test: Q phase activated";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
manaStableStart = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Press Q+E periodically
|
||||
if (qPhase && holding && elapsed - lastQTime >= QCooldownMs)
|
||||
{
|
||||
await _bot.Game.PressKey(VK_Q);
|
||||
await Task.Delay(100 + rng.Next(100));
|
||||
_bot.Game.LeftMouseUp();
|
||||
_bot.Game.RightMouseUp();
|
||||
await Task.Delay(200 + rng.Next(100));
|
||||
_bot.Game.LeftMouseDown();
|
||||
_bot.Game.RightMouseDown();
|
||||
lastQTime = elapsed;
|
||||
}
|
||||
|
||||
await Task.Delay(PollMs + rng.Next(100));
|
||||
}
|
||||
|
||||
DebugResult = "Attack test: completed (30s)";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
DebugResult = $"Attack test failed: {ex.Message}";
|
||||
Log.Error(ex, "Attack test failed");
|
||||
}
|
||||
finally
|
||||
{
|
||||
_bot.Game.LeftMouseUp();
|
||||
_bot.Game.RightMouseUp();
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void ToggleBurstCapture()
|
||||
{
|
||||
IsBurstCapturing = !IsBurstCapturing;
|
||||
_bot.FrameSaver.BurstMode = IsBurstCapturing;
|
||||
DebugResult = IsBurstCapturing
|
||||
? "Burst capture ON — saving every 200ms to training-data/raw/"
|
||||
: $"Burst capture OFF — {_bot.FrameSaver.SavedCount} frames saved";
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task ClickSalvage()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -194,11 +194,12 @@ public partial class MainWindowViewModel : ObservableObject
|
|||
{
|
||||
Log.Information("END pressed — emergency stop");
|
||||
await _bot.Navigation.Stop();
|
||||
_bot.BossRunExecutor.Stop();
|
||||
_bot.Pause();
|
||||
Avalonia.Threading.Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
IsPaused = true;
|
||||
State = "Stopped (F12)";
|
||||
State = "Stopped (END)";
|
||||
});
|
||||
}
|
||||
f12WasDown = endDown;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
using System.Collections.ObjectModel;
|
||||
using Timer = System.Timers.Timer;
|
||||
using Avalonia.Threading;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
|
@ -19,16 +20,33 @@ public partial class MappingViewModel : ObservableObject, IDisposable
|
|||
[ObservableProperty] private int _enemiesDetected;
|
||||
[ObservableProperty] private float _inferenceMs;
|
||||
[ObservableProperty] private bool _hasModel;
|
||||
[ObservableProperty] private bool _isKulemak;
|
||||
[ObservableProperty] private bool _kulemakEnabled;
|
||||
[ObservableProperty] private string _invitationTabPath = "";
|
||||
[ObservableProperty] private string _lootTabPath = "";
|
||||
[ObservableProperty] private decimal? _invitationCount = 15;
|
||||
|
||||
public static MapType[] MapTypes { get; } = [MapType.TrialOfChaos, MapType.Temple, MapType.Endgame];
|
||||
public static MapType[] MapTypes { get; } = [MapType.TrialOfChaos, MapType.Temple, MapType.Endgame, MapType.Kulemak];
|
||||
public ObservableCollection<string> StashTabPaths { get; } = [];
|
||||
|
||||
private static readonly string ModelPath = Path.GetFullPath("tools/python-detect/models/enemy-v1.pt");
|
||||
private static readonly string ModelsDir = Path.GetFullPath("tools/python-detect/models");
|
||||
|
||||
private static bool AnyModelExists() =>
|
||||
Directory.Exists(ModelsDir) && Directory.GetFiles(ModelsDir, "*.pt").Length > 0;
|
||||
|
||||
public MappingViewModel(BotOrchestrator bot)
|
||||
{
|
||||
_bot = bot;
|
||||
_selectedMapType = bot.Config.MapType;
|
||||
_hasModel = File.Exists(ModelPath);
|
||||
_isKulemak = _selectedMapType == MapType.Kulemak;
|
||||
_hasModel = AnyModelExists();
|
||||
|
||||
// Load Kulemak settings
|
||||
_kulemakEnabled = bot.Config.Kulemak.Enabled;
|
||||
_invitationTabPath = bot.Config.Kulemak.InvitationTabPath;
|
||||
_lootTabPath = bot.Config.Kulemak.LootTabPath;
|
||||
_invitationCount = bot.Config.Kulemak.InvitationCount;
|
||||
LoadStashTabPaths();
|
||||
|
||||
_bot.EnemyDetector.DetectionUpdated += OnDetectionUpdated;
|
||||
|
||||
|
|
@ -40,6 +58,47 @@ public partial class MappingViewModel : ObservableObject, IDisposable
|
|||
partial void OnSelectedMapTypeChanged(MapType value)
|
||||
{
|
||||
_bot.Store.UpdateSettings(s => s.MapType = value);
|
||||
IsKulemak = value == MapType.Kulemak;
|
||||
}
|
||||
|
||||
partial void OnKulemakEnabledChanged(bool value)
|
||||
{
|
||||
_bot.Store.UpdateSettings(s => s.Kulemak.Enabled = value);
|
||||
}
|
||||
|
||||
partial void OnInvitationTabPathChanged(string value)
|
||||
{
|
||||
_bot.Store.UpdateSettings(s => s.Kulemak.InvitationTabPath = value);
|
||||
}
|
||||
|
||||
partial void OnLootTabPathChanged(string value)
|
||||
{
|
||||
_bot.Store.UpdateSettings(s => s.Kulemak.LootTabPath = value);
|
||||
}
|
||||
|
||||
partial void OnInvitationCountChanged(decimal? value)
|
||||
{
|
||||
_bot.Store.UpdateSettings(s => s.Kulemak.InvitationCount = (int)(value ?? 15));
|
||||
}
|
||||
|
||||
private void LoadStashTabPaths()
|
||||
{
|
||||
StashTabPaths.Clear();
|
||||
StashTabPaths.Add(""); // empty = not configured
|
||||
var s = _bot.Store.Settings;
|
||||
if (s.StashCalibration == null) return;
|
||||
foreach (var tab in s.StashCalibration.Tabs)
|
||||
{
|
||||
if (tab.IsFolder)
|
||||
{
|
||||
foreach (var sub in tab.SubTabs)
|
||||
StashTabPaths.Add($"{tab.Name}/{sub.Name}");
|
||||
}
|
||||
else
|
||||
{
|
||||
StashTabPaths.Add(tab.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
partial void OnIsFrameSaverEnabledChanged(bool value)
|
||||
|
|
@ -50,6 +109,7 @@ public partial class MappingViewModel : ObservableObject, IDisposable
|
|||
partial void OnIsDetectionEnabledChanged(bool value)
|
||||
{
|
||||
_bot.EnemyDetector.Enabled = value;
|
||||
_bot.BossDetector.Enabled = value;
|
||||
}
|
||||
|
||||
private void OnDetectionUpdated(DetectionSnapshot snapshot)
|
||||
|
|
@ -64,7 +124,7 @@ public partial class MappingViewModel : ObservableObject, IDisposable
|
|||
private void RefreshStats()
|
||||
{
|
||||
FramesSaved = _bot.FrameSaver.SavedCount;
|
||||
HasModel = File.Exists(ModelPath);
|
||||
HasModel = AnyModelExists();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ public partial class SettingsViewModel : ObservableObject
|
|||
[ObservableProperty] private decimal? _waitForMoreItemsMs = 20000;
|
||||
[ObservableProperty] private decimal? _betweenTradesDelayMs = 5000;
|
||||
[ObservableProperty] private bool _headless = true;
|
||||
[ObservableProperty] private bool _showHudDebug;
|
||||
[ObservableProperty] private bool _isSaved;
|
||||
[ObservableProperty] private string _calibrationStatus = "";
|
||||
[ObservableProperty] private string _stashCalibratedAt = "";
|
||||
|
|
@ -44,6 +45,7 @@ public partial class SettingsViewModel : ObservableObject
|
|||
WaitForMoreItemsMs = s.WaitForMoreItemsMs;
|
||||
BetweenTradesDelayMs = s.BetweenTradesDelayMs;
|
||||
Headless = s.Headless;
|
||||
ShowHudDebug = s.ShowHudDebug;
|
||||
}
|
||||
|
||||
private void LoadTabs()
|
||||
|
|
@ -94,6 +96,7 @@ public partial class SettingsViewModel : ObservableObject
|
|||
s.WaitForMoreItemsMs = (int)(WaitForMoreItemsMs ?? 20000);
|
||||
s.BetweenTradesDelayMs = (int)(BetweenTradesDelayMs ?? 5000);
|
||||
s.Headless = Headless;
|
||||
s.ShowHudDebug = ShowHudDebug;
|
||||
});
|
||||
|
||||
IsSaved = true;
|
||||
|
|
@ -206,4 +209,5 @@ public partial class SettingsViewModel : ObservableObject
|
|||
partial void OnWaitForMoreItemsMsChanged(decimal? value) => IsSaved = false;
|
||||
partial void OnBetweenTradesDelayMsChanged(decimal? value) => IsSaved = false;
|
||||
partial void OnHeadlessChanged(bool value) => IsSaved = false;
|
||||
partial void OnShowHudDebugChanged(bool value) => IsSaved = false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -233,6 +233,36 @@
|
|||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- Kulemak Settings (visible when Kulemak selected) -->
|
||||
<Border IsVisible="{Binding IsKulemak}" Background="#161b22"
|
||||
BorderBrush="#30363d" BorderThickness="1"
|
||||
CornerRadius="8" Padding="10">
|
||||
<StackPanel Spacing="8">
|
||||
<TextBlock Text="KULEMAK" FontSize="11" FontWeight="SemiBold"
|
||||
Foreground="#8b949e" />
|
||||
<CheckBox IsChecked="{Binding KulemakEnabled}" Content="Enabled"
|
||||
Foreground="#e6edf3" />
|
||||
<DockPanel>
|
||||
<TextBlock Text="Invitation Tab" FontSize="11" Foreground="#8b949e"
|
||||
Width="140" VerticalAlignment="Center" />
|
||||
<ComboBox ItemsSource="{Binding StashTabPaths}"
|
||||
SelectedItem="{Binding InvitationTabPath}" />
|
||||
</DockPanel>
|
||||
<DockPanel>
|
||||
<TextBlock Text="Loot Tab" FontSize="11" Foreground="#8b949e"
|
||||
Width="140" VerticalAlignment="Center" />
|
||||
<ComboBox ItemsSource="{Binding StashTabPaths}"
|
||||
SelectedItem="{Binding LootTabPath}" />
|
||||
</DockPanel>
|
||||
<DockPanel>
|
||||
<TextBlock Text="Invitations per batch" FontSize="11" Foreground="#8b949e"
|
||||
Width="140" VerticalAlignment="Center" />
|
||||
<NumericUpDown Value="{Binding InvitationCount}"
|
||||
Minimum="1" Maximum="60" Increment="1" Width="100" />
|
||||
</DockPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- Training Data -->
|
||||
<Border Background="#161b22" BorderBrush="#30363d" BorderThickness="1"
|
||||
CornerRadius="8" Padding="10">
|
||||
|
|
@ -298,6 +328,10 @@
|
|||
<Button Content="ANGE" Command="{Binding ClickAngeCommand}" />
|
||||
<Button Content="STASH" Command="{Binding ClickStashCommand}" />
|
||||
<Button Content="SALVAGE" Command="{Binding ClickSalvageCommand}" />
|
||||
<Button Content="Attack Test" Command="{Binding AttackTestCommand}" />
|
||||
<Button Content="Detection?" Command="{Binding DetectionStatusCommand}" />
|
||||
<Button Content="{Binding BurstCaptureLabel}"
|
||||
Command="{Binding ToggleBurstCaptureCommand}" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
|
@ -408,6 +442,8 @@
|
|||
|
||||
<CheckBox IsChecked="{Binding Headless}" Content="Headless browser"
|
||||
Foreground="#e6edf3" Margin="0,4,0,0" />
|
||||
<CheckBox IsChecked="{Binding ShowHudDebug}" Content="Show HUD debug overlay"
|
||||
Foreground="#e6edf3" />
|
||||
|
||||
<StackPanel Orientation="Horizontal" Spacing="8" Margin="0,2,0,0">
|
||||
<Button Content="Save Settings" Command="{Binding SaveSettingsCommand}" />
|
||||
|
|
@ -564,6 +600,7 @@
|
|||
</ItemsControl>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</TabItem>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue