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(); } }