feat(scanner): complete localized artifact quality checkpoint

This commit is contained in:
AzuTear
2026-07-11 15:59:19 +02:00
parent 639b0b7f59
commit 8b9f948c6b
215 changed files with 35440 additions and 7273 deletions
@@ -0,0 +1,90 @@
namespace GenshinAssistant.InputHelper;
internal enum NativeScannerInputGuardOutcome
{
Ready,
Stopped,
Blocked,
}
internal readonly record struct NativeScannerInputGuardDecision(
NativeScannerInputGuardOutcome Outcome,
string Message)
{
public bool CanContinue => Outcome == NativeScannerInputGuardOutcome.Ready;
public static NativeScannerInputGuardDecision Ready()
=> new(NativeScannerInputGuardOutcome.Ready, "");
public static NativeScannerInputGuardDecision Blocked(string message)
=> new(NativeScannerInputGuardOutcome.Blocked, message);
}
internal static class NativeScannerInputSafetyPolicy
{
// The live inventory capacity is 2,400 Artifacts. A full visible page
// contains 32 safe targets and the live-calibrated page transition uses 39
// wheel events. 3,200 upward events therefore cover the complete inventory
// with a deterministic safety margin while remaining explicitly bounded.
internal const int MaximumArtifactInventoryCapacity = 2400;
internal const int VisibleArtifactTargetsPerPage = 32;
internal const int PageScrollWheelEvents = 39;
internal const int InventoryTopResetWheelEvents = 3200;
internal const int InventoryTopResetStabilizationMs = 300;
internal const int PageScrollStabilizationMs = 120;
internal const int WheelPacingBatchEvents = PageScrollWheelEvents;
internal const int WheelPacingDelayMs = 1;
internal static bool ShouldPauseAfterWheelEvent(int completedEventCount)
=> completedEventCount > 0 && completedEventCount % WheelPacingBatchEvents == 0;
internal static NativeScannerInputGuardDecision Evaluate(
bool stopRequested,
bool escapePressed,
bool enterPressed,
bool f9Pressed,
long expectedGenshinHwnd,
long foregroundHwnd,
string foregroundProcess,
string action)
{
if (stopRequested)
{
return new NativeScannerInputGuardDecision(
NativeScannerInputGuardOutcome.Stopped,
$"stop requested before {action}");
}
if (escapePressed)
{
return new NativeScannerInputGuardDecision(
NativeScannerInputGuardOutcome.Stopped,
$"ESC held - native scan stopped before {action}");
}
if (enterPressed)
{
return new NativeScannerInputGuardDecision(
NativeScannerInputGuardOutcome.Stopped,
$"Enter held - native scan stopped before {action}");
}
if (f9Pressed)
{
return new NativeScannerInputGuardDecision(
NativeScannerInputGuardOutcome.Stopped,
$"F9 held - native scan stopped before {action}");
}
if (expectedGenshinHwnd == 0)
{
return NativeScannerInputGuardDecision.Blocked(
$"Genshin target window is unavailable before {action}; native scan stopped without refocusing.");
}
if (foregroundHwnd != expectedGenshinHwnd)
{
var actual = string.IsNullOrWhiteSpace(foregroundProcess) ? "unknown" : foregroundProcess;
return NativeScannerInputGuardDecision.Blocked(
$"Genshin lost foreground before {action}; native scan stopped without refocusing (foreground: {actual}).");
}
return NativeScannerInputGuardDecision.Ready();
}
}