minimap quickness
This commit is contained in:
parent
d80e723b94
commit
3087f9146e
3 changed files with 65 additions and 26 deletions
BIN
assets/toc_finish.png
Normal file
BIN
assets/toc_finish.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 995 B |
|
|
@ -13,6 +13,8 @@ public class MinimapCapture : IFrameConsumer, IDisposable
|
||||||
private readonly IScreenCapture _backend; // kept for debug capture paths
|
private readonly IScreenCapture _backend; // kept for debug capture paths
|
||||||
private int _modeCheckCounter = 9; // trigger mode detection on first frame
|
private int _modeCheckCounter = 9; // trigger mode detection on first frame
|
||||||
private MinimapMode _detectedMode = MinimapMode.Overlay;
|
private MinimapMode _detectedMode = MinimapMode.Overlay;
|
||||||
|
private int _pendingModeCount; // consecutive detections of a different mode
|
||||||
|
private MinimapMode _pendingMode;
|
||||||
private MinimapFrame? _lastFrame;
|
private MinimapFrame? _lastFrame;
|
||||||
|
|
||||||
public MinimapMode DetectedMode => _detectedMode;
|
public MinimapMode DetectedMode => _detectedMode;
|
||||||
|
|
@ -31,23 +33,48 @@ public class MinimapCapture : IFrameConsumer, IDisposable
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Process(ScreenFrame screen)
|
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)
|
if (++_modeCheckCounter >= 10)
|
||||||
{
|
{
|
||||||
_modeCheckCounter = 0;
|
_modeCheckCounter = 0;
|
||||||
var detected = DetectMinimapMode(screen);
|
var detected = DetectMinimapMode(screen);
|
||||||
|
if (detected == null)
|
||||||
|
{
|
||||||
|
_pendingModeCount = 0;
|
||||||
|
_lastFrame = null;
|
||||||
|
return; // not in gameplay — skip frame
|
||||||
|
}
|
||||||
|
|
||||||
if (detected != _detectedMode)
|
if (detected != _detectedMode)
|
||||||
{
|
{
|
||||||
var oldMode = _detectedMode;
|
if (detected == _pendingMode)
|
||||||
var oldRegion = _detectedMode == MinimapMode.Overlay ? _config.OverlayRegion : _config.CornerRegion;
|
_pendingModeCount++;
|
||||||
_detectedMode = detected;
|
else
|
||||||
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})",
|
_pendingMode = detected.Value;
|
||||||
oldMode, _detectedMode,
|
_pendingModeCount = 1;
|
||||||
oldRegion.X, oldRegion.Y, oldRegion.Width, oldRegion.Height,
|
}
|
||||||
newRegion.X, newRegion.Y, newRegion.Width, newRegion.Height);
|
|
||||||
ResetAdaptation();
|
if (_pendingModeCount >= 3)
|
||||||
ModeChanged?.Invoke(_detectedMode);
|
{
|
||||||
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Detect minimap mode by sampling pixels at the corner minimap center.
|
/// Detect minimap mode by sampling the orange player dot (#DE581B) at both
|
||||||
/// If the pixel is close to #DE581B (orange player dot), corner minimap is active.
|
/// the overlay center and corner center. Returns null if neither is found
|
||||||
|
/// (loading screen, menu, map transition).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private MinimapMode DetectMinimapMode(ScreenFrame screen)
|
private MinimapMode? DetectMinimapMode(ScreenFrame screen)
|
||||||
{
|
{
|
||||||
var cx = _config.CornerCenterX;
|
if (IsOrangeDot(screen, _config.OverlayCenterX, _config.OverlayCenterY))
|
||||||
var cy = _config.CornerCenterY;
|
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)
|
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;
|
double bSum = 0, gSum = 0, rSum = 0;
|
||||||
var count = 0;
|
var count = 0;
|
||||||
for (var dy = -2; dy <= 2; dy++)
|
for (var dy = -2; dy <= 2; dy++)
|
||||||
|
|
@ -162,16 +194,13 @@ public class MinimapCapture : IFrameConsumer, IDisposable
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
var b = bSum / count;
|
|
||||||
var g = gSum / count;
|
|
||||||
var r = rSum / count;
|
var r = rSum / count;
|
||||||
|
var g = gSum / count;
|
||||||
|
var b = bSum / count;
|
||||||
|
|
||||||
// #DE581B → R=222, G=88, B=27
|
// #DE581B → R=222, G=88, B=27
|
||||||
const int tol = 60;
|
const int tol = 60;
|
||||||
if (Math.Abs(r - 222) < tol && Math.Abs(g - 88) < tol && Math.Abs(b - 27) < tol)
|
return Math.Abs(r - 222) < tol && Math.Abs(g - 88) < tol && Math.Abs(b - 27) < tol;
|
||||||
return MinimapMode.Corner;
|
|
||||||
|
|
||||||
return MinimapMode.Overlay;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Mat BuildWallMask(Mat hsv, Mat playerMask, bool sample = false)
|
private Mat BuildWallMask(Mat hsv, Mat playerMask, bool sample = false)
|
||||||
|
|
|
||||||
|
|
@ -93,8 +93,17 @@ public class WorldMap : IDisposable
|
||||||
// Warmup / re-bootstrap: stitch at current position to seed the canvas
|
// Warmup / re-bootstrap: stitch at current position to seed the canvas
|
||||||
if (needsBootstrap)
|
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);
|
StitchWithConfidence(classifiedMat, _position, boosted: true, mode: mode);
|
||||||
PaintExploredCircle(_position);
|
PaintExploredCircle(_position);
|
||||||
|
LastMatchSucceeded = true; // signal caller to update viewport
|
||||||
if (_consecutiveMatchFails >= 30)
|
if (_consecutiveMatchFails >= 30)
|
||||||
{
|
{
|
||||||
Log.Information("Re-bootstrap: mode={Mode} pos=({X:F1},{Y:F1}) frameSize={FS} walls={W} stitch={Ms:F1}ms",
|
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);
|
_position = new MapPosition(_canvasSize / 2.0, _canvasSize / 2.0);
|
||||||
_frameCount = 0;
|
_frameCount = 0;
|
||||||
_consecutiveMatchFails = 0;
|
_consecutiveMatchFails = 0;
|
||||||
|
LastMatchSucceeded = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -520,7 +530,7 @@ public class WorldMap : IDisposable
|
||||||
|
|
||||||
_prevWallMask?.Dispose();
|
_prevWallMask?.Dispose();
|
||||||
_prevWallMask = null;
|
_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()
|
public void Dispose()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue