Implement ADV controls and native Auto timing
This commit is contained in:
50
engine/Age.Engine/Hosting/AdvAutoAdvanceTimer.cs
Normal file
50
engine/Age.Engine/Hosting/AdvAutoAdvanceTimer.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
namespace Age.Engine.Hosting;
|
||||
|
||||
/// <summary>
|
||||
/// Native ADV Auto timing policy. Unvoiced waits use AutoMessageTime1; voiced waits park until voice
|
||||
/// completion and then use AutoMessageTime0. The host supplies a monotonic clock and live voice state.
|
||||
/// </summary>
|
||||
public sealed class AdvAutoAdvanceTimer
|
||||
{
|
||||
private bool _armed;
|
||||
private bool _waitingForVoice;
|
||||
private long _deadlineMs;
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_armed = false;
|
||||
_waitingForVoice = false;
|
||||
_deadlineMs = 0;
|
||||
}
|
||||
|
||||
public bool Poll(AdvAutoWaitState state, bool voiceActive, long nowMs)
|
||||
{
|
||||
if (!state.Enabled)
|
||||
{
|
||||
Reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!_armed)
|
||||
{
|
||||
_armed = true;
|
||||
_waitingForVoice = state.VoicePending && voiceActive;
|
||||
if (!_waitingForVoice)
|
||||
_deadlineMs = nowMs + EffectiveDelay(
|
||||
state.VoicePending ? state.PostVoiceDelayMs : state.UnvoicedDelayMs);
|
||||
}
|
||||
|
||||
if (_waitingForVoice)
|
||||
{
|
||||
if (voiceActive) return false;
|
||||
_waitingForVoice = false;
|
||||
_deadlineMs = nowMs + EffectiveDelay(state.PostVoiceDelayMs);
|
||||
}
|
||||
|
||||
return nowMs >= _deadlineMs;
|
||||
}
|
||||
|
||||
// Native callers substitute 100 ms when either configuration getter returns zero.
|
||||
private static long EffectiveDelay(long configuredMs)
|
||||
=> configuredMs == 0 ? 100 : System.Math.Max(1, configuredMs);
|
||||
}
|
||||
@@ -7,6 +7,9 @@ public readonly record struct AdvWaitIndicatorConfig(
|
||||
int SourceX, int SourceY, int CellWidth, int CellHeight,
|
||||
int TerminalFrame, long FramePeriodMs);
|
||||
|
||||
public readonly record struct AdvAutoWaitState(
|
||||
bool Enabled, bool VoicePending, long PostVoiceDelayMs, long UnvoicedDelayMs);
|
||||
|
||||
public interface IHost
|
||||
{
|
||||
void ShowText(int offset, string text);
|
||||
@@ -17,6 +20,18 @@ public interface IHost
|
||||
void ConfigureAdvWaitIndicator(AdvWaitIndicatorConfig config) { }
|
||||
void WaitForInput();
|
||||
void WaitForInput(int layoutSlot) => WaitForInput();
|
||||
// Interactive hosts service script callbacks on the VM thread while the enclosing ADV page remains
|
||||
// parked. The callback returns true while another queued input callback is ready to run.
|
||||
void WaitForInput(int layoutSlot, Func<bool> serviceInputCallback)
|
||||
{
|
||||
while (serviceInputCallback()) { }
|
||||
WaitForInput(layoutSlot);
|
||||
}
|
||||
void WaitForInput(int layoutSlot, Func<bool> serviceInputCallback,
|
||||
Func<AdvAutoWaitState> autoWaitState)
|
||||
=> WaitForInput(layoutSlot, serviceInputCallback);
|
||||
void WakeInputCallbackService() { }
|
||||
void InputCallbackCompleted(GfxState gfx) { }
|
||||
void Sleep(long duration);
|
||||
void FrameYield();
|
||||
// Native 0x1c7/0x1cc query two distinct ADV skip channels. Headless and non-interactive
|
||||
|
||||
Reference in New Issue
Block a user