diff --git a/debug_loot_capture.png b/debug_loot_capture.png
index 8fac337..1fb1a3e 100644
Binary files a/debug_loot_capture.png and b/debug_loot_capture.png differ
diff --git a/debug_loot_detected.png b/debug_loot_detected.png
index 15f766c..3199c9e 100644
Binary files a/debug_loot_detected.png and b/debug_loot_detected.png differ
diff --git a/debug_loot_edges.png b/debug_loot_edges.png
index 2639463..cc4da60 100644
Binary files a/debug_loot_edges.png and b/debug_loot_edges.png differ
diff --git a/src/Poe2Trade.Bot/BossRunExecutor.cs b/src/Poe2Trade.Bot/BossRunExecutor.cs
index f97b103..c78b804 100644
--- a/src/Poe2Trade.Bot/BossRunExecutor.cs
+++ b/src/Poe2Trade.Bot/BossRunExecutor.cs
@@ -368,6 +368,7 @@ public class BossRunExecutor : GameExecutor
// Walk to well and click the closest match to screen center
Log.Information("Phase {Phase} done, walking to well", phase);
await WalkToWorldPosition(wellWorldX, wellWorldY);
+ await Helpers.Sleep(1000);
await ClickClosestTemplateToCenter(CathedralWellTemplate);
await Helpers.Sleep(200);
@@ -378,11 +379,14 @@ public class BossRunExecutor : GameExecutor
// 4th fight - no well after
if (_stopped) return;
Log.Information("=== Boss phase 4/4 ===");
- await AttackBossUntilGone();
+ var phase4BossPos = await AttackBossUntilGone();
if (_stopped) return;
- // Walk to ring area and return it
- await WalkToWorldPosition(-440, -330);
+ // Walk toward where the boss died (ring spawns there)
+ var ringX = phase4BossPos?.X ?? fightWorldX;
+ var ringY = phase4BossPos?.Y ?? fightWorldY;
+ Log.Information("Walking to ring area ({X:F0},{Y:F0})", ringX, ringY);
+ await WalkToWorldPosition(ringX, ringY);
if (_stopped) return;
Log.Information("Looking for Return the Ring...");
@@ -438,13 +442,13 @@ public class BossRunExecutor : GameExecutor
var topRegion = new Region(750, 16, 1068, 96);
// Check all three healthbar templates — boss is alive if ANY matches
- var m1 = await _screen.TemplateMatchAll(BossHealthbarTemplate, topRegion, threshold: 0.5);
+ var m1 = await _screen.TemplateMatchAll(BossHealthbarTemplate, topRegion, threshold: 0.5, silent: true);
if (m1.Count > 0) return true;
- var m2 = await _screen.TemplateMatchAll(BossHealthbarTemplate2, topRegion, threshold: 0.5);
+ var m2 = await _screen.TemplateMatchAll(BossHealthbarTemplate2, topRegion, threshold: 0.5, silent: true);
if (m2.Count > 0) return true;
- var m3 = await _screen.TemplateMatchAll(BossHealthbarTemplate3, topRegion, threshold: 0.5);
+ var m3 = await _screen.TemplateMatchAll(BossHealthbarTemplate3, topRegion, threshold: 0.5, silent: true);
if (m3.Count > 0) return true;
return false;
@@ -452,10 +456,18 @@ public class BossRunExecutor : GameExecutor
///
/// Check for the "Resurrect at Checkpoint" button — means we died.
+ /// Self-throttled: only actually checks every 2s to avoid slowing combat.
/// If found, click it, wait for respawn, and return true.
///
+ private long _lastDeathCheckMs;
+ private readonly Stopwatch _deathCheckSw = Stopwatch.StartNew();
+
private async Task CheckDeath()
{
+ var now = _deathCheckSw.ElapsedMilliseconds;
+ if (now - _lastDeathCheckMs < 2000) return false;
+ _lastDeathCheckMs = now;
+
var match = await _screen.TemplateMatch(ResurrectTemplate);
if (match == null) return false;
@@ -475,16 +487,23 @@ public class BossRunExecutor : GameExecutor
var sw = Stopwatch.StartNew();
var atkX = 1280;
var atkY = 720;
+ var lastCheckMs = 0L;
while (sw.ElapsedMilliseconds < timeoutMs)
{
if (_stopped) return false;
- if (await CheckDeath()) continue;
- if (await IsBossAlive())
+ // Only check healthbar + death every ~500ms to avoid blocking combat
+ if (sw.ElapsedMilliseconds - lastCheckMs > 500)
{
- Log.Information("Boss healthbar detected after {Ms}ms", sw.ElapsedMilliseconds);
- return true;
+ lastCheckMs = sw.ElapsedMilliseconds;
+ if (await CheckDeath()) continue;
+
+ if (await IsBossAlive())
+ {
+ Log.Information("Boss healthbar detected after {Ms}ms", sw.ElapsedMilliseconds);
+ return true;
+ }
}
// Attack toward YOLO-detected boss if available, otherwise last known position
diff --git a/src/Poe2Trade.Screen/IScreenReader.cs b/src/Poe2Trade.Screen/IScreenReader.cs
index 76a3c6e..a5e371a 100644
--- a/src/Poe2Trade.Screen/IScreenReader.cs
+++ b/src/Poe2Trade.Screen/IScreenReader.cs
@@ -17,7 +17,7 @@ public interface IScreenReader : IDisposable
Task Snapshot();
Task DiffOcr(string? savePath = null, Region? region = null);
Task TemplateMatch(string templatePath, Region? region = null);
- Task> TemplateMatchAll(string templatePath, Region? region = null, double threshold = 0.7);
+ Task> TemplateMatchAll(string templatePath, Region? region = null, double threshold = 0.7, bool silent = false);
Task NameplateDiffOcr(System.Drawing.Bitmap reference, System.Drawing.Bitmap current);
void SetLootBaseline(System.Drawing.Bitmap frame);
List DetectLootLabels(System.Drawing.Bitmap reference, System.Drawing.Bitmap current);
diff --git a/src/Poe2Trade.Screen/ScreenReader.cs b/src/Poe2Trade.Screen/ScreenReader.cs
index 1647133..b4c64ef 100644
--- a/src/Poe2Trade.Screen/ScreenReader.cs
+++ b/src/Poe2Trade.Screen/ScreenReader.cs
@@ -177,10 +177,11 @@ public class ScreenReader : IScreenReader
return Task.FromResult(result);
}
- public Task> TemplateMatchAll(string templatePath, Region? region = null, double threshold = 0.7)
+ public Task> TemplateMatchAll(string templatePath, Region? region = null, double threshold = 0.7, bool silent = false)
{
var results = _templateMatch.MatchAll(templatePath, region, threshold);
- Log.Information("TemplateMatchAll: {Count} matches for {Template}", results.Count, Path.GetFileName(templatePath));
+ if (!silent)
+ Log.Information("TemplateMatchAll: {Count} matches for {Template}", results.Count, Path.GetFileName(templatePath));
return Task.FromResult(results);
}