91 lines
3.4 KiB
C#
91 lines
3.4 KiB
C#
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();
|
|
}
|
|
}
|