diff --git a/debug_loot_capture.png b/debug_loot_capture.png index 65bdaf5..dfca7f9 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 9ec04e0..62c371c 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 baafe39..2593477 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 9b7ccb2..ed01db6 100644 --- a/src/Poe2Trade.Bot/BossRunExecutor.cs +++ b/src/Poe2Trade.Bot/BossRunExecutor.cs @@ -58,6 +58,8 @@ public class BossRunExecutor : GameExecutor public override void Stop() { base.Stop(); + _nav.Frozen = false; + FightPosition = null; Log.Information("Boss run executor stop requested"); } @@ -358,106 +360,113 @@ public class BossRunExecutor : GameExecutor FightPosition = (fightWorldX, fightWorldY); await WalkToWorldPosition(fightWorldX, fightWorldY, cancelWhen: IsBossAlive); _nav.Frozen = true; // Lock canvas — position tracking only - if (_stopped) { _nav.Frozen = false; return; } - // 3x fight-then-well loop - for (var phase = 1; phase <= 3; phase++) + try { if (_stopped) return; - var preWp = _nav.WorldPosition; - Log.Information("=== Boss phase {Phase}/4 === fightArea=({FX:F0},{FY:F0}) charPos=({CX:F1},{CY:F1})", - phase, fightWorldX, fightWorldY, preWp.X, preWp.Y); - var lastBossPos = await AttackBossUntilGone(fightWorldX, fightWorldY); - if (_stopped) return; - - // Update fight area to where the boss was last seen - var postWp = _nav.WorldPosition; - Log.Information("Phase {Phase} ended: charPos=({CX:F1},{CY:F1}) lastBossPos={Boss}", - phase, postWp.X, postWp.Y, - lastBossPos != null ? $"({lastBossPos.Value.X:F1},{lastBossPos.Value.Y:F1})" : "null"); - if (lastBossPos != null) + // 3x fight-then-well loop + for (var phase = 1; phase <= 3; phase++) { - fightWorldX = lastBossPos.Value.X; - fightWorldY = lastBossPos.Value.Y; - FightPosition = (fightWorldX, fightWorldY); - Log.Information("Fight area updated to ({X:F0},{Y:F0})", fightWorldX, fightWorldY); + if (_stopped) return; + var preWp = _nav.WorldPosition; + Log.Information("=== Boss phase {Phase}/4 === fightArea=({FX:F0},{FY:F0}) charPos=({CX:F1},{CY:F1})", + phase, fightWorldX, fightWorldY, preWp.X, preWp.Y); + + var lastBossPos = await AttackBossUntilGone(fightWorldX, fightWorldY); + if (_stopped) return; + + // Update fight area to where the boss was last seen + var postWp = _nav.WorldPosition; + Log.Information("Phase {Phase} ended: charPos=({CX:F1},{CY:F1}) lastBossPos={Boss}", + phase, postWp.X, postWp.Y, + lastBossPos != null ? $"({lastBossPos.Value.X:F1},{lastBossPos.Value.Y:F1})" : "null"); + if (lastBossPos != null) + { + fightWorldX = lastBossPos.Value.X; + fightWorldY = lastBossPos.Value.Y; + FightPosition = (fightWorldX, fightWorldY); + Log.Information("Fight area updated to ({X:F0},{Y:F0})", fightWorldX, fightWorldY); + } + + // Wait for death animation before looking for well + await Sleep(3000); + + // 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 Sleep(500); + await ClickClosestTemplateToCenter(CathedralWellTemplate); + await Sleep(200); + + // Walk back to fight position for next phase + await WalkToWorldPosition(fightWorldX, fightWorldY, cancelWhen: IsBossAlive); } - // Wait for death animation before looking for well - await Sleep(3000); + // 4th fight - no well after + if (_stopped) return; + { + var p4wp = _nav.WorldPosition; + Log.Information("=== Boss phase 4/4 === fightArea=({FX:F0},{FY:F0}) charPos=({CX:F1},{CY:F1})", + fightWorldX, fightWorldY, p4wp.X, p4wp.Y); + } + var finalBossPos = await AttackBossUntilGone(fightWorldX, fightWorldY); + if (_stopped) return; - // 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 Sleep(500); - await ClickClosestTemplateToCenter(CathedralWellTemplate); - await Sleep(200); + // Update fight area from phase 4 if we got detections + { + var p4postWp = _nav.WorldPosition; + Log.Information("Phase 4 ended: charPos=({CX:F1},{CY:F1}) finalBossPos={Boss}", + p4postWp.X, p4postWp.Y, + finalBossPos != null ? $"({finalBossPos.Value.X:F1},{finalBossPos.Value.Y:F1})" : "null"); + } + if (finalBossPos != null) + { + fightWorldX = finalBossPos.Value.X; + fightWorldY = finalBossPos.Value.Y; + FightPosition = (fightWorldX, fightWorldY); + } + Log.Information("Ring phase: using fightArea=({FX:F0},{FY:F0})", fightWorldX, fightWorldY); - // Walk back to fight position for next phase - await WalkToWorldPosition(fightWorldX, fightWorldY, cancelWhen: IsBossAlive); + // Walk to known ring position and look for the template + await WalkToWorldPosition(-440, -330); + await Sleep(1000); + if (_stopped) return; + + Log.Information("Looking for Return the Ring..."); + var ring = await _screen.TemplateMatch(ReturnTheRingTemplate); + if (ring == null) + { + ring = await _screen.TemplateMatch(ReturnTheRingTemplate); + } + if (ring != null) + { + Log.Information("Found Return the Ring at ({X},{Y}), clicking", ring.X, ring.Y); + await _game.LeftClickAt(ring.X, ring.Y); + await Sleep(500); + } + else + { + Log.Error("Could not find Return the Ring template"); + } + if (_stopped) return; + + // Walk back to fight area — fightWorldX/Y carries position from all phases + Log.Information("Walking to fight position ({X:F0},{Y:F0})", fightWorldX, fightWorldY); + await WalkToWorldPosition(fightWorldX, fightWorldY); + await Sleep(300); + Log.Information("Attacking at ring fight position"); + await AttackBossUntilGone(fightWorldX, fightWorldY); + if (_stopped) return; + + StopBossDetection(); + Log.Information("Fight complete"); } - - // 4th fight - no well after - if (_stopped) return; + finally { - var p4wp = _nav.WorldPosition; - Log.Information("=== Boss phase 4/4 === fightArea=({FX:F0},{FY:F0}) charPos=({CX:F1},{CY:F1})", - fightWorldX, fightWorldY, p4wp.X, p4wp.Y); + _nav.Frozen = false; + FightPosition = null; } - var finalBossPos = await AttackBossUntilGone(fightWorldX, fightWorldY); - if (_stopped) return; - - // Update fight area from phase 4 if we got detections - { - var p4postWp = _nav.WorldPosition; - Log.Information("Phase 4 ended: charPos=({CX:F1},{CY:F1}) finalBossPos={Boss}", - p4postWp.X, p4postWp.Y, - finalBossPos != null ? $"({finalBossPos.Value.X:F1},{finalBossPos.Value.Y:F1})" : "null"); - } - if (finalBossPos != null) - { - fightWorldX = finalBossPos.Value.X; - fightWorldY = finalBossPos.Value.Y; - FightPosition = (fightWorldX, fightWorldY); - } - Log.Information("Ring phase: using fightArea=({FX:F0},{FY:F0})", fightWorldX, fightWorldY); - - // Walk to known ring position and look for the template - await WalkToWorldPosition(-440, -330); - await Sleep(1000); - if (_stopped) return; - - Log.Information("Looking for Return the Ring..."); - var ring = await _screen.TemplateMatch(ReturnTheRingTemplate); - if (ring == null) - { - ring = await _screen.TemplateMatch(ReturnTheRingTemplate); - } - if (ring != null) - { - Log.Information("Found Return the Ring at ({X},{Y}), clicking", ring.X, ring.Y); - await _game.LeftClickAt(ring.X, ring.Y); - await Sleep(500); - } - else - { - Log.Error("Could not find Return the Ring template"); - } - if (_stopped) return; - - // Walk back to fight area — fightWorldX/Y carries position from all phases - Log.Information("Walking to fight position ({X:F0},{Y:F0})", fightWorldX, fightWorldY); - await WalkToWorldPosition(fightWorldX, fightWorldY); - await Sleep(300); - Log.Information("Attacking at ring fight position"); - await AttackBossUntilGone(fightWorldX, fightWorldY); - if (_stopped) return; - - StopBossDetection(); - _nav.Frozen = false; - FightPosition = null; - Log.Information("Fight complete"); } /// diff --git a/src/Poe2Trade.Ui/Overlay/Layers/D2dEnemyBoxLayer.cs b/src/Poe2Trade.Ui/Overlay/Layers/D2dEnemyBoxLayer.cs index 1084c4d..15f1424 100644 --- a/src/Poe2Trade.Ui/Overlay/Layers/D2dEnemyBoxLayer.cs +++ b/src/Poe2Trade.Ui/Overlay/Layers/D2dEnemyBoxLayer.cs @@ -87,7 +87,7 @@ internal sealed class D2dEnemyBoxLayer : ID2dOverlayLayer, IDisposable if (state.FightPosition is var (fx, fy)) { const double worldToScreen = 835.0 / 97.0; // inverse of screenToWorld - const int screenCx = 1280, screenCy = 720; + const int screenCx = 1280, screenCy = 660; // player character screen position var wp = state.NavPosition; var sx = (float)(screenCx + (fx - wp.X) * worldToScreen); var sy = (float)(screenCy + (fy - wp.Y) * worldToScreen);