This commit is contained in:
Boki 2026-02-21 21:48:58 -05:00
parent aee3a7f22c
commit 152c74fa15
5 changed files with 122 additions and 26 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 MiB

After

Width:  |  Height:  |  Size: 7.2 MiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 MiB

After

Width:  |  Height:  |  Size: 5.9 MiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 338 KiB

After

Width:  |  Height:  |  Size: 332 KiB

Before After
Before After

View file

@ -351,12 +351,18 @@ public class BossRunExecutor : GameExecutor
for (var phase = 1; phase <= 3; phase++)
{
if (_stopped) return;
Log.Information("=== Boss phase {Phase}/4 ===", phase);
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;
@ -380,16 +386,27 @@ public class BossRunExecutor : GameExecutor
// 4th fight - no well after
if (_stopped) return;
Log.Information("=== Boss phase 4/4 ===");
{
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;
// 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;
}
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);
@ -606,6 +623,19 @@ public class BossRunExecutor : GameExecutor
const int screenCy = 720;
const double screenToWorld = 97.0 / 835.0;
(double X, double Y)? lastBossWorldPos = null;
var yoloLogCount = 0;
// Subscribe to YOLO events for real-time chase updates
// (main loop is too slow due to template matching to effectively track boss)
void OnBossDetected(BossSnapshot snapshot)
{
if (snapshot.Bosses.Count == 0) return;
var boss = snapshot.Bosses[0];
_combatTargetX = boss.Cx;
_combatTargetY = boss.Cy;
_combat.SetChaseTarget(boss.Cx, boss.Cy);
}
_bossDetector.BossDetected += OnBossDetected;
Log.Information("Boss is alive, engaging");
var (combatTask, cts) = StartCombatLoop(screenCx, screenCy);
@ -620,39 +650,22 @@ public class BossRunExecutor : GameExecutor
{
if (_stopped) return lastBossWorldPos;
// Update attack target from YOLO (fast, no capture)
// Update lastBossWorldPos from latest YOLO (for phase tracking)
var snapshot = _bossDetector.Latest;
if (snapshot.Bosses.Count > 0)
{
var boss = snapshot.Bosses[0];
_combatTargetX = boss.Cx;
_combatTargetY = boss.Cy;
var wp = _nav.WorldPosition;
lastBossWorldPos = (
wp.X + (boss.Cx - screenCx) * screenToWorld,
wp.Y + (boss.Cy - screenCy) * screenToWorld);
// Walk toward boss to stay as close as possible
var bossDx = boss.Cx - screenCx;
var bossDy = boss.Cy - screenCy;
var bossDist = Math.Sqrt(bossDx * bossDx + bossDy * bossDy);
if (bossDist > 50)
{
var dirX = bossDx / bossDist;
var dirY = bossDy / bossDist;
var keys = new List<int>();
if (dirY < -0.3) keys.Add(InputSender.VK.W);
if (dirY > 0.3) keys.Add(InputSender.VK.S);
if (dirX < -0.3) keys.Add(InputSender.VK.A);
if (dirX > 0.3) keys.Add(InputSender.VK.D);
foreach (var k in keys) await _game.KeyDown(k);
await Helpers.Sleep(150);
foreach (var k in keys) await _game.KeyUp(k);
}
yoloLogCount++;
if (yoloLogCount % 5 == 1) // log every 5th detection
Log.Information("YOLO boss: screen=({Sx},{Sy}) charWorld=({Cx:F1},{Cy:F1}) bossWorld=({Bx:F1},{By:F1}) conf={Conf:F2}",
boss.Cx, boss.Cy, wp.X, wp.Y,
lastBossWorldPos.Value.X, lastBossWorldPos.Value.Y, boss.Confidence);
}
// Check death + healthbar (combat keeps running in background)
@ -686,6 +699,8 @@ public class BossRunExecutor : GameExecutor
}
finally
{
_bossDetector.BossDetected -= OnBossDetected;
_combat.ClearChaseTarget();
await StopCombatLoop(combatTask, cts);
}
}

View file

@ -32,6 +32,11 @@ public class CombatManager
private long _lastOrbitMs;
private int _nextOrbitMs = OrbitStepMinMs;
// Chase — walks toward a screen position instead of orbiting
private volatile int _chaseX = -1;
private volatile int _chaseY = -1;
private readonly HashSet<int> _chaseKeys = new();
// Smoothed mouse position — lerps toward target to avoid jitter
private double _smoothX = 1280;
private double _smoothY = 720;
@ -39,6 +44,18 @@ public class CombatManager
public bool IsHolding => _holding;
public void SetChaseTarget(int screenX, int screenY)
{
_chaseX = screenX;
_chaseY = screenY;
}
public void ClearChaseTarget()
{
_chaseX = -1;
_chaseY = -1;
}
public CombatManager(IGameController game, HudReader hudReader, FlaskManager flasks)
{
_game = game;
@ -52,7 +69,16 @@ public class CombatManager
public async Task Tick(int x, int y, int jitter = 30)
{
await _flasks.Tick();
if (_chaseX >= 0)
{
await UpdateChase();
}
else
{
if (_chaseKeys.Count > 0) await ReleaseChaseKeys();
await UpdateOrbit();
}
// Lerp smoothed position toward target
_smoothX += (x - _smoothX) * SmoothFactor;
@ -106,6 +132,55 @@ public class CombatManager
}
}
/// <summary>
/// Walk toward chase target using held WASD keys. Replaces orbit when active.
/// </summary>
private async Task UpdateChase()
{
// Release orbit key when entering chase mode
if (_orbitIndex >= 0)
{
await _game.KeyUp(OrbitKeys[_orbitIndex]);
_orbitIndex = -1;
}
const int screenCx = 1280, screenCy = 720;
var cx = _chaseX;
var cy = _chaseY;
var dx = cx - screenCx;
var dy = cy - screenCy;
var dist = Math.Sqrt(dx * dx + dy * dy);
var wanted = new HashSet<int>();
if (dist > 100)
{
var dirX = dx / dist;
var dirY = dy / dist;
if (dirY < -0.3) wanted.Add(InputSender.VK.W);
if (dirY > 0.3) wanted.Add(InputSender.VK.S);
if (dirX < -0.3) wanted.Add(InputSender.VK.A);
if (dirX > 0.3) wanted.Add(InputSender.VK.D);
}
foreach (var k in _chaseKeys.Except(wanted).ToList())
{
await _game.KeyUp(k);
_chaseKeys.Remove(k);
}
foreach (var k in wanted.Except(_chaseKeys).ToList())
{
await _game.KeyDown(k);
_chaseKeys.Add(k);
}
}
private async Task ReleaseChaseKeys()
{
foreach (var k in _chaseKeys)
await _game.KeyUp(k);
_chaseKeys.Clear();
}
/// <summary>
/// Cycle WASD directions to orbit in a small circle while attacking.
/// </summary>
@ -150,6 +225,9 @@ public class CombatManager
_smoothX = 1280;
_smoothY = 720;
await ReleaseOrbit();
await ReleaseChaseKeys();
_chaseX = -1;
_chaseY = -1;
}
/// <summary>
@ -164,5 +242,8 @@ public class CombatManager
_holding = false;
}
await ReleaseOrbit();
await ReleaseChaseKeys();
_chaseX = -1;
_chaseY = -1;
}
}