minimap quickness

This commit is contained in:
Boki 2026-02-16 14:11:04 -05:00
parent d80e723b94
commit 3087f9146e
3 changed files with 65 additions and 26 deletions

BIN
assets/toc_finish.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 995 B

View file

@ -13,6 +13,8 @@ public class MinimapCapture : IFrameConsumer, IDisposable
private readonly IScreenCapture _backend; // kept for debug capture paths
private int _modeCheckCounter = 9; // trigger mode detection on first frame
private MinimapMode _detectedMode = MinimapMode.Overlay;
private int _pendingModeCount; // consecutive detections of a different mode
private MinimapMode _pendingMode;
private MinimapFrame? _lastFrame;
public MinimapMode DetectedMode => _detectedMode;
@ -31,25 +33,50 @@ public class MinimapCapture : IFrameConsumer, IDisposable
/// </summary>
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)
{
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;
_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;
}
}
var region = _detectedMode == MinimapMode.Overlay
? _config.OverlayRegion
@ -137,19 +164,24 @@ public class MinimapCapture : IFrameConsumer, IDisposable
}
/// <summary>
/// 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).
/// </summary>
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)

View file

@ -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;
}
/// <summary>
@ -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()