From 3087f9146e5514b35a48e733e072d9f32d08f6dd Mon Sep 17 00:00:00 2001 From: Boki Date: Mon, 16 Feb 2026 14:11:04 -0500 Subject: [PATCH] minimap quickness --- assets/toc_finish.png | Bin 0 -> 995 bytes src/Poe2Trade.Navigation/MinimapCapture.cs | 79 ++++++++++++++------- src/Poe2Trade.Navigation/WorldMap.cs | 12 +++- 3 files changed, 65 insertions(+), 26 deletions(-) create mode 100644 assets/toc_finish.png diff --git a/assets/toc_finish.png b/assets/toc_finish.png new file mode 100644 index 0000000000000000000000000000000000000000..e7ef7f3f3f212dc4a0c83b0e970510e273fdb063 GIT binary patch literal 995 zcmV<9104K`P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D19(Y9K~y+T9g}ZN zlyv}rfA9PL-n;jG@9w=j?v6VSZh$o>xoFAe2i8C5SaJb{gIFNMrY1R&jz5AFI)x81 zV2vdFvuK5d8cT%%3lxH32(68BEd5|^Y|g=IsaveAFHajEo{!J-?Gce!2Aia&6N|>! z7Ku_2O6I87Ly9IC%hza)*%Svt?1)6kh{lMfrV~laARNm?q{PxmON+5Jn#$|p2-Sfg zZ9Wg}rb@FZ7|)lqNR4j8q}CrGKNKN5C5`mdbW&m&h>X-2JHk=QLdhgTHhEr2L6XX$ zEgJLHI&--iO>TvQs={lUq*m(G2mI`eMA;fmLlgu<9I?H$m=^nohRQBOA_n4a2H&t)E|CnssnP$5gN5^gm1_$8WwLQi=hhjO zr{|nptFw14i_S`efd;`^ zuf=qk#l#+qkxCn}|7C-Fok^@5ck%ZX7d;JO`%QjQ@wo~yurCU?Fu*t<=@xW(N% zi>X43n+Xr%vtl<3O+J2aw^^!jQ=g;JU8llR56_!|KB2(6$?B{@JWFNiBNvN@UCdTl zOxC&)orMNNm2L(Oco=Q*alP5gZyj#dzxUJN5nNYQmc~uiu9^%TvzhPm@$~_dUryPa zddGpNe8I_!io)O#56hn>G1Bhm*>r%Bc9UUCa9dFsZE(PxkHxbAW;*>$b@@p$6k0Q# zi2YtaRhnew^8jDf+WdSrNPnY;%O`AlY!??(3<|bO9)9U#syo2l!7yV9o2;NlQ _detectedMode; @@ -31,23 +33,48 @@ public class MinimapCapture : IFrameConsumer, IDisposable /// public void Process(ScreenFrame screen) { - // Auto-detect minimap mode every 10th frame via single pixel probe + // Auto-detect minimap mode by checking orange player dot at both positions. + // null = not in gameplay (loading/menu) → skip frame entirely. + // Require 3 consecutive detections of a new mode to avoid transient flips. if (++_modeCheckCounter >= 10) { _modeCheckCounter = 0; var detected = DetectMinimapMode(screen); + if (detected == null) + { + _pendingModeCount = 0; + _lastFrame = null; + return; // not in gameplay — skip frame + } + if (detected != _detectedMode) { - var oldMode = _detectedMode; - var oldRegion = _detectedMode == MinimapMode.Overlay ? _config.OverlayRegion : _config.CornerRegion; - _detectedMode = detected; - var newRegion = _detectedMode == MinimapMode.Overlay ? _config.OverlayRegion : _config.CornerRegion; - Log.Information("MODE SWITCH: {Old} → {New} | oldRegion=({OX},{OY},{OW}x{OH}) newRegion=({NX},{NY},{NW}x{NH})", - oldMode, _detectedMode, - oldRegion.X, oldRegion.Y, oldRegion.Width, oldRegion.Height, - newRegion.X, newRegion.Y, newRegion.Width, newRegion.Height); - ResetAdaptation(); - ModeChanged?.Invoke(_detectedMode); + if (detected == _pendingMode) + _pendingModeCount++; + else + { + _pendingMode = detected.Value; + _pendingModeCount = 1; + } + + if (_pendingModeCount >= 3) + { + var oldMode = _detectedMode; + var oldRegion = _detectedMode == MinimapMode.Overlay ? _config.OverlayRegion : _config.CornerRegion; + _detectedMode = detected.Value; + var newRegion = _detectedMode == MinimapMode.Overlay ? _config.OverlayRegion : _config.CornerRegion; + Log.Information("MODE SWITCH: {Old} → {New} | oldRegion=({OX},{OY},{OW}x{OH}) newRegion=({NX},{NY},{NW}x{NH})", + oldMode, _detectedMode, + oldRegion.X, oldRegion.Y, oldRegion.Width, oldRegion.Height, + newRegion.X, newRegion.Y, newRegion.Width, newRegion.Height); + ResetAdaptation(); + _pendingModeCount = 0; + ModeChanged?.Invoke(_detectedMode); + } + } + else + { + _pendingModeCount = 0; } } @@ -137,19 +164,24 @@ public class MinimapCapture : IFrameConsumer, IDisposable } /// - /// Detect minimap mode by sampling pixels at the corner minimap center. - /// If the pixel is close to #DE581B (orange player dot), corner minimap is active. + /// Detect minimap mode by sampling the orange player dot (#DE581B) at both + /// the overlay center and corner center. Returns null if neither is found + /// (loading screen, menu, map transition). /// - private MinimapMode DetectMinimapMode(ScreenFrame screen) + private MinimapMode? DetectMinimapMode(ScreenFrame screen) { - var cx = _config.CornerCenterX; - var cy = _config.CornerCenterY; + if (IsOrangeDot(screen, _config.OverlayCenterX, _config.OverlayCenterY)) + return MinimapMode.Overlay; + if (IsOrangeDot(screen, _config.CornerCenterX, _config.CornerCenterY)) + return MinimapMode.Corner; + return null; + } - // Bounds check + private static bool IsOrangeDot(ScreenFrame screen, int cx, int cy) + { if (cx < 2 || cy < 2 || cx + 2 >= screen.Width || cy + 2 >= screen.Height) - return _detectedMode; + return false; - // Average a 5x5 patch worth of pixels double bSum = 0, gSum = 0, rSum = 0; var count = 0; for (var dy = -2; dy <= 2; dy++) @@ -162,16 +194,13 @@ public class MinimapCapture : IFrameConsumer, IDisposable count++; } - var b = bSum / count; - var g = gSum / count; var r = rSum / count; + var g = gSum / count; + var b = bSum / count; // #DE581B → R=222, G=88, B=27 const int tol = 60; - if (Math.Abs(r - 222) < tol && Math.Abs(g - 88) < tol && Math.Abs(b - 27) < tol) - return MinimapMode.Corner; - - return MinimapMode.Overlay; + return Math.Abs(r - 222) < tol && Math.Abs(g - 88) < tol && Math.Abs(b - 27) < tol; } private Mat BuildWallMask(Mat hsv, Mat playerMask, bool sample = false) diff --git a/src/Poe2Trade.Navigation/WorldMap.cs b/src/Poe2Trade.Navigation/WorldMap.cs index 3768cfb..1fe21ec 100644 --- a/src/Poe2Trade.Navigation/WorldMap.cs +++ b/src/Poe2Trade.Navigation/WorldMap.cs @@ -93,8 +93,17 @@ public class WorldMap : IDisposable // Warmup / re-bootstrap: stitch at current position to seed the canvas if (needsBootstrap) { + // Don't consume warmup slots on empty frames (game still loading minimap) + if (wallCountAfter < 50) + { + _frameCount--; + Log.Information("Warmup waiting for minimap ({Ms:F1}ms)", sw.Elapsed.TotalMilliseconds); + return _position; + } + StitchWithConfidence(classifiedMat, _position, boosted: true, mode: mode); PaintExploredCircle(_position); + LastMatchSucceeded = true; // signal caller to update viewport if (_consecutiveMatchFails >= 30) { Log.Information("Re-bootstrap: mode={Mode} pos=({X:F1},{Y:F1}) frameSize={FS} walls={W} stitch={Ms:F1}ms", @@ -471,6 +480,7 @@ public class WorldMap : IDisposable _position = new MapPosition(_canvasSize / 2.0, _canvasSize / 2.0); _frameCount = 0; _consecutiveMatchFails = 0; + LastMatchSucceeded = false; } /// @@ -520,7 +530,7 @@ public class WorldMap : IDisposable _prevWallMask?.Dispose(); _prevWallMask = null; - // Don't force re-bootstrap — let the match find the correct position first + _frameCount = 0; // force re-warmup with new mode's data } public void Dispose()