overlay and calibration

This commit is contained in:
Boki 2026-02-19 20:00:23 -05:00
parent 3062993f7c
commit 3456e0d62a
24 changed files with 1193 additions and 439 deletions

View file

@ -1,6 +1,10 @@
using System.Collections.ObjectModel;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Poe2Trade.Bot;
using Poe2Trade.Core;
using Poe2Trade.Inventory;
using Serilog;
namespace Poe2Trade.Ui.ViewModels;
@ -16,11 +20,18 @@ public partial class SettingsViewModel : ObservableObject
[ObservableProperty] private decimal? _betweenTradesDelayMs = 5000;
[ObservableProperty] private bool _headless = true;
[ObservableProperty] private bool _isSaved;
[ObservableProperty] private string _calibrationStatus = "";
[ObservableProperty] private string _stashCalibratedAt = "";
[ObservableProperty] private string _shopCalibratedAt = "";
public ObservableCollection<StashTabViewModel> StashTabs { get; } = [];
public ObservableCollection<StashTabViewModel> ShopTabs { get; } = [];
public SettingsViewModel(BotOrchestrator bot)
{
_bot = bot;
LoadFromConfig();
LoadTabs();
}
private void LoadFromConfig()
@ -35,6 +46,42 @@ public partial class SettingsViewModel : ObservableObject
Headless = s.Headless;
}
private void LoadTabs()
{
var s = _bot.Store.Settings;
StashTabs.Clear();
if (s.StashCalibration != null)
{
foreach (var tab in s.StashCalibration.Tabs)
StashTabs.Add(new StashTabViewModel(tab));
StashCalibratedAt = FormatTimestamp(s.StashCalibration.CalibratedAt);
}
else
{
StashCalibratedAt = "Not calibrated";
}
ShopTabs.Clear();
if (s.ShopCalibration != null)
{
foreach (var tab in s.ShopCalibration.Tabs)
ShopTabs.Add(new StashTabViewModel(tab));
ShopCalibratedAt = FormatTimestamp(s.ShopCalibration.CalibratedAt);
}
else
{
ShopCalibratedAt = "Not calibrated";
}
}
private static string FormatTimestamp(long unixMs)
{
if (unixMs == 0) return "Not calibrated";
var dt = DateTimeOffset.FromUnixTimeMilliseconds(unixMs).LocalDateTime;
return dt.ToString("yyyy-MM-dd HH:mm");
}
[RelayCommand]
private void SaveSettings()
{
@ -52,6 +99,106 @@ public partial class SettingsViewModel : ObservableObject
IsSaved = true;
}
[RelayCommand]
private void SaveTabs()
{
_bot.Store.UpdateSettings(s =>
{
// Models are already updated via write-through in StashTabViewModel
// Just trigger a save
if (s.StashCalibration != null)
s.StashCalibration = s.StashCalibration;
if (s.ShopCalibration != null)
s.ShopCalibration = s.ShopCalibration;
});
CalibrationStatus = "Tabs saved!";
}
[RelayCommand]
private async Task CalibrateStash()
{
try
{
var calibrator = new StashCalibrator(_bot.Screen, _bot.Game);
CalibrationStatus = "Calibrating stash...";
await _bot.Game.FocusGame();
await Helpers.RandomDelay(150, 300);
var pos = await _bot.Inventory.FindAndClickNameplate("STASH");
if (!pos.HasValue)
{
CalibrationStatus = "STASH not found. Stand near your stash.";
return;
}
await Helpers.RandomDelay(300, 500);
var cal = await calibrator.CalibrateOpenPanel();
await _bot.Game.PressEscape();
await Helpers.RandomDelay(200, 400);
_bot.Store.UpdateSettings(s => s.StashCalibration = cal);
LoadTabs();
CalibrationStatus = $"Stash calibrated — {cal.Tabs.Count} tabs found.";
}
catch (Exception ex)
{
CalibrationStatus = $"Stash calibration failed: {ex.Message}";
Log.Error(ex, "Stash calibration failed");
}
}
[RelayCommand]
private async Task CalibrateShop()
{
try
{
var calibrator = new StashCalibrator(_bot.Screen, _bot.Game);
CalibrationStatus = "Calibrating shop...";
await _bot.Game.FocusGame();
await Helpers.RandomDelay(150, 300);
var pos = await _bot.Inventory.FindAndClickNameplate("ANGE");
if (!pos.HasValue)
{
CalibrationStatus = "ANGE not found. Stand near the vendor.";
return;
}
await Helpers.RandomDelay(800, 1200);
// ANGE opens a dialog — click "Manage Shop"
var dialogRegion = new Region(1080, 600, 400, 300);
var managePos = await _bot.Screen.FindTextInRegion(dialogRegion, "Manage");
if (managePos.HasValue)
{
await _bot.Game.LeftClickAt(managePos.Value.X, managePos.Value.Y);
await Helpers.RandomDelay(300, 500);
}
else
{
Log.Warning("'Manage Shop' not found in dialog region, saving debug capture");
await _bot.Screen.SaveRegion(dialogRegion, "debug/calibrate-dialog.png");
}
var cal = await calibrator.CalibrateOpenPanel(firstFolderOnly: true);
await _bot.Game.PressEscape();
await Helpers.RandomDelay(200, 400);
_bot.Store.UpdateSettings(s => s.ShopCalibration = cal);
LoadTabs();
CalibrationStatus = $"Shop calibrated — {cal.Tabs.Count} tabs found.";
}
catch (Exception ex)
{
CalibrationStatus = $"Shop calibration failed: {ex.Message}";
Log.Error(ex, "Shop calibration failed");
}
}
partial void OnPoe2LogPathChanged(string value) => IsSaved = false;
partial void OnWindowTitleChanged(string value) => IsSaved = false;
partial void OnTravelTimeoutMsChanged(decimal? value) => IsSaved = false;