attacking fixed
This commit is contained in:
parent
109b1b4059
commit
f914443d86
5 changed files with 96 additions and 66 deletions
Binary file not shown.
|
Before Width: | Height: | Size: 7.5 MiB After Width: | Height: | Size: 7.1 MiB |
Binary file not shown.
|
Before Width: | Height: | Size: 6.3 MiB After Width: | Height: | Size: 5.9 MiB |
Binary file not shown.
|
Before Width: | Height: | Size: 354 KiB After Width: | Height: | Size: 331 KiB |
|
|
@ -481,22 +481,70 @@ public class BossRunExecutor : GameExecutor
|
|||
/// <summary>
|
||||
/// Wait for the boss healthbar to appear (boss spawns/becomes active).
|
||||
/// </summary>
|
||||
// -- Async combat: combat runs in background, checks run on main thread --
|
||||
|
||||
private volatile int _combatTargetX = 1280;
|
||||
private volatile int _combatTargetY = 720;
|
||||
private volatile int _combatJitter = 30;
|
||||
|
||||
/// <summary>
|
||||
/// Background combat loop — calls Tick continuously until cancelled.
|
||||
/// Target position is updated via volatile fields from the main thread.
|
||||
/// </summary>
|
||||
private async Task RunCombatLoop(CancellationToken ct)
|
||||
{
|
||||
try
|
||||
{
|
||||
while (!ct.IsCancellationRequested)
|
||||
await _combat.Tick(_combatTargetX, _combatTargetY, _combatJitter);
|
||||
}
|
||||
catch (OperationCanceledException) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start background combat loop, returning the task and CTS.
|
||||
/// Caller must cancel + await + ReleaseAll when done.
|
||||
/// </summary>
|
||||
private (Task combatTask, CancellationTokenSource cts) StartCombatLoop(int x = 1280, int y = 720, int jitter = 30)
|
||||
{
|
||||
_combatTargetX = x;
|
||||
_combatTargetY = y;
|
||||
_combatJitter = jitter;
|
||||
_combat.Reset().GetAwaiter().GetResult();
|
||||
var cts = new CancellationTokenSource();
|
||||
var task = Task.Run(() => RunCombatLoop(cts.Token));
|
||||
return (task, cts);
|
||||
}
|
||||
|
||||
private async Task StopCombatLoop(Task combatTask, CancellationTokenSource cts)
|
||||
{
|
||||
cts.Cancel();
|
||||
try { await combatTask; } catch { /* expected */ }
|
||||
await _combat.ReleaseAll();
|
||||
cts.Dispose();
|
||||
}
|
||||
|
||||
private async Task<bool> WaitForBossSpawn(int timeoutMs = 30_000)
|
||||
{
|
||||
Log.Information("Waiting for boss healthbar to appear...");
|
||||
var sw = Stopwatch.StartNew();
|
||||
var atkX = 1280;
|
||||
var atkY = 720;
|
||||
var lastCheckMs = 0L;
|
||||
|
||||
while (sw.ElapsedMilliseconds < timeoutMs)
|
||||
var (combatTask, cts) = StartCombatLoop();
|
||||
try
|
||||
{
|
||||
if (_stopped) return false;
|
||||
|
||||
// Only check healthbar + death every ~500ms to avoid blocking combat
|
||||
if (sw.ElapsedMilliseconds - lastCheckMs > 500)
|
||||
while (sw.ElapsedMilliseconds < timeoutMs)
|
||||
{
|
||||
lastCheckMs = sw.ElapsedMilliseconds;
|
||||
if (_stopped) return false;
|
||||
|
||||
// Update target from YOLO (fast, no capture)
|
||||
var snapshot = _bossDetector.Latest;
|
||||
if (snapshot.Bosses.Count > 0)
|
||||
{
|
||||
_combatTargetX = snapshot.Bosses[0].Cx;
|
||||
_combatTargetY = snapshot.Bosses[0].Cy;
|
||||
}
|
||||
|
||||
// Check death + healthbar (blocks main thread, combat continues in background)
|
||||
if (await CheckDeath()) continue;
|
||||
|
||||
if (await IsBossAlive())
|
||||
|
|
@ -506,18 +554,13 @@ public class BossRunExecutor : GameExecutor
|
|||
}
|
||||
}
|
||||
|
||||
// Attack toward YOLO-detected boss if available, otherwise last known position
|
||||
var snapshot = _bossDetector.Latest;
|
||||
if (snapshot.Bosses.Count > 0)
|
||||
{
|
||||
atkX = snapshot.Bosses[0].Cx;
|
||||
atkY = snapshot.Bosses[0].Cy;
|
||||
}
|
||||
await _combat.Tick(atkX, atkY);
|
||||
Log.Warning("WaitForBossSpawn timed out after {Ms}ms", timeoutMs);
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
await StopCombatLoop(combatTask, cts);
|
||||
}
|
||||
|
||||
Log.Warning("WaitForBossSpawn timed out after {Ms}ms", timeoutMs);
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -532,60 +575,41 @@ public class BossRunExecutor : GameExecutor
|
|||
|
||||
const int screenCx = 1280;
|
||||
const int screenCy = 720;
|
||||
|
||||
// Attack target — defaults to screen center, updated by YOLO
|
||||
var atkX = screenCx;
|
||||
var atkY = screenCy;
|
||||
(double X, double Y)? lastBossWorldPos = null;
|
||||
|
||||
await _game.MoveMouseFast(atkX, atkY);
|
||||
await Helpers.Sleep(200);
|
||||
|
||||
var sw = Stopwatch.StartNew();
|
||||
var lastCheckMs = 0L;
|
||||
|
||||
await _combat.Reset();
|
||||
Log.Information("Boss is alive, engaging");
|
||||
var (combatTask, cts) = StartCombatLoop(screenCx, screenCy);
|
||||
|
||||
try
|
||||
{
|
||||
var sw = Stopwatch.StartNew();
|
||||
while (sw.ElapsedMilliseconds < timeoutMs)
|
||||
{
|
||||
if (_stopped) return lastBossWorldPos;
|
||||
|
||||
// Update attack target from YOLO when available
|
||||
// Update attack target from YOLO (fast, no capture)
|
||||
var snapshot = _bossDetector.Latest;
|
||||
if (snapshot.Bosses.Count > 0)
|
||||
{
|
||||
var boss = snapshot.Bosses[0];
|
||||
atkX = boss.Cx;
|
||||
atkY = boss.Cy;
|
||||
_combatTargetX = boss.Cx;
|
||||
_combatTargetY = boss.Cy;
|
||||
|
||||
// Convert boss screen offset to world position
|
||||
// Scale: sqrt(72²+65²)/835 ≈ 0.116 minimap px per screen px
|
||||
const double screenToWorld = 97.0 / 835.0;
|
||||
var wp = _nav.WorldPosition;
|
||||
var bossWorldX = wp.X + (boss.Cx - screenCx) * screenToWorld;
|
||||
var bossWorldY = wp.Y + (boss.Cy - screenCy) * screenToWorld;
|
||||
lastBossWorldPos = (bossWorldX, bossWorldY);
|
||||
lastBossWorldPos = (
|
||||
wp.X + (boss.Cx - screenCx) * screenToWorld,
|
||||
wp.Y + (boss.Cy - screenCy) * screenToWorld);
|
||||
}
|
||||
|
||||
// Re-check healthbar every ~0.5s, first miss = phase over
|
||||
if (sw.ElapsedMilliseconds - lastCheckMs > 500)
|
||||
// Check death + healthbar (combat keeps running in background)
|
||||
if (await CheckDeath()) continue;
|
||||
|
||||
if (!await IsBossAlive())
|
||||
{
|
||||
if (await CheckDeath()) continue;
|
||||
|
||||
var bossAlive = await IsBossAlive();
|
||||
lastCheckMs = sw.ElapsedMilliseconds;
|
||||
|
||||
if (!bossAlive)
|
||||
{
|
||||
Log.Information("Healthbar not found, boss phase over after {Ms}ms", sw.ElapsedMilliseconds);
|
||||
return lastBossWorldPos;
|
||||
}
|
||||
Log.Information("Healthbar not found, boss phase over after {Ms}ms", sw.ElapsedMilliseconds);
|
||||
return lastBossWorldPos;
|
||||
}
|
||||
|
||||
await _combat.Tick(atkX, atkY);
|
||||
}
|
||||
|
||||
Log.Warning("AttackBossUntilGone timed out after {Ms}ms", timeoutMs);
|
||||
|
|
@ -593,26 +617,26 @@ public class BossRunExecutor : GameExecutor
|
|||
}
|
||||
finally
|
||||
{
|
||||
await _combat.ReleaseAll();
|
||||
await StopCombatLoop(combatTask, cts);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task AttackAtPosition(int x, int y, int durationMs)
|
||||
{
|
||||
await _combat.Reset();
|
||||
var sw = Stopwatch.StartNew();
|
||||
var (combatTask, cts) = StartCombatLoop(x, y, jitter: 20);
|
||||
try
|
||||
{
|
||||
var sw = Stopwatch.StartNew();
|
||||
while (sw.ElapsedMilliseconds < durationMs)
|
||||
{
|
||||
if (_stopped) return;
|
||||
if (await CheckDeath()) continue;
|
||||
await _combat.Tick(x, y, jitter: 20);
|
||||
await Helpers.Sleep(500);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
await _combat.ReleaseAll();
|
||||
await StopCombatLoop(combatTask, cts);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -171,18 +171,24 @@ public class ScreenReader : IScreenReader
|
|||
|
||||
public Task<TemplateMatchResult?> TemplateMatch(string templatePath, Region? region = null)
|
||||
{
|
||||
var result = _templateMatch.Match(templatePath, region);
|
||||
if (result != null)
|
||||
Log.Information("Template match found: ({X},{Y}) confidence={Conf:F3}", result.X, result.Y, result.Confidence);
|
||||
return Task.FromResult(result);
|
||||
return Task.Run(() =>
|
||||
{
|
||||
var result = _templateMatch.Match(templatePath, region);
|
||||
if (result != null)
|
||||
Log.Information("Template match found: ({X},{Y}) confidence={Conf:F3}", result.X, result.Y, result.Confidence);
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
public Task<List<TemplateMatchResult>> TemplateMatchAll(string templatePath, Region? region = null, double threshold = 0.7, bool silent = false)
|
||||
{
|
||||
var results = _templateMatch.MatchAll(templatePath, region, threshold);
|
||||
if (!silent)
|
||||
Log.Information("TemplateMatchAll: {Count} matches for {Template}", results.Count, Path.GetFileName(templatePath));
|
||||
return Task.FromResult(results);
|
||||
return Task.Run(() =>
|
||||
{
|
||||
var results = _templateMatch.MatchAll(templatePath, region, threshold);
|
||||
if (!silent)
|
||||
Log.Information("TemplateMatchAll: {Count} matches for {Template}", results.Count, Path.GetFileName(templatePath));
|
||||
return results;
|
||||
});
|
||||
}
|
||||
|
||||
// -- Save --
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue