much better bot and ocr

This commit is contained in:
Boki 2026-02-22 14:21:32 -05:00
parent bb8f50116a
commit 6257bcf122
25 changed files with 583 additions and 101 deletions

View file

@ -8,6 +8,7 @@ using Poe2Trade.Game;
using Poe2Trade.GameLog;
using Poe2Trade.Inventory;
using Poe2Trade.Screen;
using Poe2Trade.Screen.Ocr;
using Poe2Trade.Trade;
using Poe2Trade.Ui.Overlay;
using Poe2Trade.Ui.ViewModels;
@ -33,6 +34,11 @@ public partial class App : Application
services.AddSingleton(sp => sp.GetRequiredService<ConfigStore>().Settings);
// Services
services.AddSingleton<IOcrEngine>(sp =>
{
var settings = sp.GetRequiredService<SavedSettings>();
return OcrEngineFactory.Create(settings.OcrEngine);
});
services.AddSingleton<IGameController, GameController>();
services.AddSingleton<IScreenReader, ScreenReader>();
services.AddSingleton<IClientLogWatcher>(sp =>

View file

@ -197,6 +197,8 @@ public sealed class D2dOverlay
IsExploring: _bot.Navigation.IsExploring,
ShowHudDebug: _bot.Store.Settings.ShowHudDebug,
ShowLootDebug: showLoot,
ShowYolo: _bot.ShowYoloOverlay,
ShowFightPosition: _bot.ShowFightPositionOverlay,
LootLabels: _bot.LootDebugDetector.Latest,
FightPosition: _bot.BossRunExecutor.FightPosition,
Fps: fps,

View file

@ -14,6 +14,8 @@ public record OverlayState(
bool IsExploring,
bool ShowHudDebug,
bool ShowLootDebug,
bool ShowYolo,
bool ShowFightPosition,
IReadOnlyList<LootLabel> LootLabels,
(double X, double Y)? FightPosition,
double Fps,

View file

@ -28,63 +28,68 @@ internal sealed class D2dEnemyBoxLayer : ID2dOverlayLayer, IDisposable
public void Draw(D2dRenderContext ctx, OverlayState state)
{
if (!state.ShowYolo && !state.ShowFightPosition) return;
var rt = ctx.RenderTarget;
foreach (var enemy in state.Enemies)
if (state.ShowYolo)
{
var confirmed = enemy.HealthBarConfirmed;
var boxBrush = confirmed ? ctx.Red : ctx.Yellow;
var rect = new RectangleF(enemy.X, enemy.Y, enemy.Width, enemy.Height);
rt.DrawRectangle(rect, boxBrush, 2f);
// Confidence label above the box
var pctIndex = Math.Clamp((int)(enemy.Confidence * 100), 0, 100);
var layout = confirmed ? _confirmedLabels[pctIndex] : _unconfirmedLabels[pctIndex];
var textBrush = confirmed ? ctx.Red : ctx.Yellow;
var m = layout.Metrics;
var labelX = enemy.X;
var labelY = enemy.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, textBrush);
}
// Boss bounding boxes (cyan) — extrapolate position to compensate for inference delay
var now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
var ageMs = (float)Math.Clamp(now - state.BossTimestampMs, 0, 60);
foreach (var boss in state.Bosses)
{
float dx = boss.VxPerMs * ageMs;
float dy = boss.VyPerMs * ageMs;
var rect = new RectangleF(boss.X + dx, boss.Y + dy, 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))
foreach (var enemy in state.Enemies)
{
layout = _ctx.CreateTextLayout(key, _ctx.LabelFormat);
_bossLabels[key] = layout;
var confirmed = enemy.HealthBarConfirmed;
var boxBrush = confirmed ? ctx.Red : ctx.Yellow;
var rect = new RectangleF(enemy.X, enemy.Y, enemy.Width, enemy.Height);
rt.DrawRectangle(rect, boxBrush, 2f);
// Confidence label above the box
var pctIndex = Math.Clamp((int)(enemy.Confidence * 100), 0, 100);
var layout = confirmed ? _confirmedLabels[pctIndex] : _unconfirmedLabels[pctIndex];
var textBrush = confirmed ? ctx.Red : ctx.Yellow;
var m = layout.Metrics;
var labelX = enemy.X;
var labelY = enemy.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, textBrush);
}
var m = layout.Metrics;
var labelX = boss.X + dx;
var labelY = boss.Y + dy - m.Height - 2;
// Boss bounding boxes (cyan) — extrapolate position to compensate for inference delay
var now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
var ageMs = (float)Math.Clamp(now - state.BossTimestampMs, 0, 60);
rt.FillRectangle(
new RectangleF(labelX - 1, labelY - 1, m.Width + 2, m.Height + 2),
ctx.LabelBgBrush);
foreach (var boss in state.Bosses)
{
float dx = boss.VxPerMs * ageMs;
float dy = boss.VyPerMs * ageMs;
var rect = new RectangleF(boss.X + dx, boss.Y + dy, boss.Width, boss.Height);
rt.DrawRectangle(rect, ctx.Cyan, 3f);
rt.DrawTextLayout(new System.Numerics.Vector2(labelX, labelY), layout, ctx.Cyan);
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 + dx;
var labelY = boss.Y + dy - 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);
}
}
// Fight position — red circle on screen where the fight area is
if (state.FightPosition is var (fx, fy))
if (state.ShowFightPosition && state.FightPosition is var (fx, fy))
{
const double worldToScreen = 835.0 / 97.0; // inverse of screenToWorld
const int screenCx = 1280, screenCy = 660; // player character screen position

View file

@ -23,12 +23,24 @@ public partial class DebugViewModel : ObservableObject
[ObservableProperty] private decimal? _clickX;
[ObservableProperty] private decimal? _clickY;
[ObservableProperty] private bool _showLootDebug;
[ObservableProperty] private bool _showYolo = true;
[ObservableProperty] private bool _showFightPosition = true;
partial void OnShowLootDebugChanged(bool value)
{
_bot.LootDebugDetector.Enabled = value;
}
partial void OnShowYoloChanged(bool value)
{
_bot.ShowYoloOverlay = value;
}
partial void OnShowFightPositionChanged(bool value)
{
_bot.ShowFightPositionOverlay = value;
}
public string[] GridLayoutNames { get; } =
[
"inventory", "stash12", "stash12_folder", "stash24",

View file

@ -20,11 +20,14 @@ public partial class SettingsViewModel : ObservableObject
[ObservableProperty] private decimal? _betweenTradesDelayMs = 5000;
[ObservableProperty] private bool _headless = true;
[ObservableProperty] private bool _showHudDebug;
[ObservableProperty] private string _ocrEngine = "WinOCR";
[ObservableProperty] private bool _isSaved;
[ObservableProperty] private string _calibrationStatus = "";
[ObservableProperty] private string _stashCalibratedAt = "";
[ObservableProperty] private string _shopCalibratedAt = "";
public static string[] OcrEngineOptions { get; } = ["WinOCR", "OneOCR", "EasyOCR"];
public ObservableCollection<StashTabViewModel> StashTabs { get; } = [];
public ObservableCollection<StashTabViewModel> ShopTabs { get; } = [];
@ -46,6 +49,7 @@ public partial class SettingsViewModel : ObservableObject
BetweenTradesDelayMs = s.BetweenTradesDelayMs;
Headless = s.Headless;
ShowHudDebug = s.ShowHudDebug;
OcrEngine = s.OcrEngine;
}
private void LoadTabs()
@ -97,6 +101,7 @@ public partial class SettingsViewModel : ObservableObject
s.BetweenTradesDelayMs = (int)(BetweenTradesDelayMs ?? 5000);
s.Headless = Headless;
s.ShowHudDebug = ShowHudDebug;
s.OcrEngine = OcrEngine;
});
IsSaved = true;
@ -210,4 +215,5 @@ public partial class SettingsViewModel : ObservableObject
partial void OnBetweenTradesDelayMsChanged(decimal? value) => IsSaved = false;
partial void OnHeadlessChanged(bool value) => IsSaved = false;
partial void OnShowHudDebugChanged(bool value) => IsSaved = false;
partial void OnOcrEngineChanged(string value) => IsSaved = false;
}

View file

@ -341,6 +341,10 @@
<StackPanel Spacing="6">
<TextBlock Text="DEBUG OVERLAYS" FontSize="11" FontWeight="SemiBold"
Foreground="#8b949e" />
<ToggleSwitch IsChecked="{Binding ShowYolo}"
Content="YOLO Boxes" Foreground="#e6edf3" />
<ToggleSwitch IsChecked="{Binding ShowFightPosition}"
Content="Fight Position" Foreground="#e6edf3" />
<ToggleSwitch IsChecked="{Binding ShowLootDebug}"
Content="Loot Labels" Foreground="#e6edf3" />
</StackPanel>
@ -455,6 +459,12 @@
<CheckBox IsChecked="{Binding ShowHudDebug}" Content="Show HUD debug overlay"
Foreground="#e6edf3" />
<StackPanel Spacing="4" Margin="0,4,0,0">
<TextBlock Text="OCR Engine (restart required)" FontSize="11" Foreground="#8b949e" />
<ComboBox ItemsSource="{Binding OcrEngineOptions}"
SelectedItem="{Binding OcrEngine}" MinWidth="200" />
</StackPanel>
<StackPanel Orientation="Horizontal" Spacing="8" Margin="0,2,0,0">
<Button Content="Save Settings" Command="{Binding SaveSettingsCommand}" />
<TextBlock Text="Saved!" Foreground="#3fb950" VerticalAlignment="Center"