using Age.Engine.Model; using Age.Engine.Sys4; namespace Age.Engine.Hosting; public readonly record struct AdvWaitIndicatorConfig( int LayoutSlot, int X, int Y, int SurfaceSlot, int SourceX, int SourceY, int CellWidth, int CellHeight, int TerminalFrame, long FramePeriodMs) { /// Select the current atlas frame. TerminalFrame is the exclusive native upper bound, /// so SYSTEM4's value 12 addresses the twelve cells 0 through 11. public int FrameAt(long elapsedMs) { int frameCount = System.Math.Max(1, TerminalFrame); long period = System.Math.Max(1, FramePeriodMs); return (int)(System.Math.Max(0, elapsedMs) / period % frameCount); } } public readonly record struct AdvAutoWaitState( bool Enabled, bool VoicePending, long PostVoiceDelayMs, long UnvoicedDelayMs); public readonly record struct SurfaceRectFill( int SurfaceSlot, int X, int Y, int Width, int Height, int Alpha, long Rgb); public readonly record struct SurfaceRectCopy( int SourceSurface, int DestinationSurface, int SourceX, int SourceY, int Width, int Height, int DestinationX, int DestinationY); /// Opcode 0x24d's captured-range movie-mask transition. The movie's decoded green channel /// becomes a byte-per-pixel alpha mask over SourceRange in the scratch surface. public readonly record struct MovieMaskTransitionRequest( long CommandKey, int SurfaceSlot, long SourceRangeStart, int SourceRangeCount, int X, int Y, int Width, int Height, long Mode, long ResourceId, long StartDelayMs, long DurationMs); /// A synchronous AGE-owned diagnostic prompt after native body/context formatting. public readonly record struct DiagnosticMessage(string Caption, string Text); /// AGERc command 10's synchronous full-width text edit request and result. public readonly record struct FullwidthTextEditRequest(string CurrentText, string InitialText); public readonly record struct FullwidthTextEditResult(bool Accepted, string Text); public enum SurfaceBlackFadeDirection { FromBlack, ToBlack, } public interface IHost : IDiagnosticHost, ILifecycleHost, IAudioHost, IMovieHost { /// Present AGERc's modal full-width editor. Cancel preserves CurrentText. FullwidthTextEditResult EditFullwidthString(FullwidthTextEditRequest request) => new(false, request.CurrentText); void ShowText(int offset, string text); void ShowText(AdvLiveTextRun run, int glyphDelayMilliseconds) => ShowText(run.SourceOffset, run.Text); AdvRetainedTextRunResult? ShowText( GfxState gfx, AdvTextLayoutPresentationBinding binding, AdvLiveTextRun run, int glyphDelayMilliseconds) { ShowText(run, glyphDelayMilliseconds); return null; } int MessageGlyphDelayMilliseconds => 50; void SetMessageGlyphDelayMilliseconds(int milliseconds) { } // Native ADV text subsystem: op 0x7a updates the selected layout's last 20-byte cursor record; // op 0x204 rasterizes a string into a numbered surface before 0x1fb binds that surface. void SetAdvTextCursor(int layoutSlot, int x, int y) { } void DrawStringToSurface(int surfaceSlot, int x, int y, string text) { } void DrawStringToSurface(int surfaceSlot, int x, int y, string text, AdvTextStyle style) => DrawStringToSurface(surfaceSlot, x, y, text); void ClearRenderedAdvTextLayout(int layoutSlot) { } void ResetRenderedAdvTextLayout( GfxState gfx, AdvTextLayoutPresentationBinding binding) => ClearRenderedAdvTextLayout(binding.LayoutSlot); void RenderTextHistory(AdvTextHistoryRenderBatch batch) { } bool RenderTextHistory( GfxState gfx, AdvTextLayoutPresentationBinding binding, AdvTextHistoryRenderBatch batch) { RenderTextHistory(batch); return false; } // History render batches are transient bindings, unlike the retained backlog itself. HISTORY.BIN's // recording re-enable at exit ends that presentation and drops every bound target layout. void EndTextHistoryPresentation() { } void EndTextHistoryPresentation(GfxState gfx) => EndTextHistoryPresentation(); int MessageWindowAlphaSetting => 0; void SetMessageWindowAlphaSetting(int value) { } void FillSurfaceRect(SurfaceRectFill fill) { } void CopySurfaceRect(SurfaceRectCopy copy) { } void PresentObjectRange(GfxState gfx, long firstHandle, long count) { } void ConfigureAdvWaitIndicator(AdvWaitIndicatorConfig config) { } // Op 0x212 supplies the ordinary retained handle used to publish the configured atlas cell. void BindAdvWaitIndicator( AdvTextLayoutPresentationBinding binding, AdvTextLayoutSnapshot layout) { } // Op 0x1ce explicitly starts/stops the same animated marker that op 0x72 starts for an ADV wait. void SetAdvWaitIndicatorEnabled(bool enabled) { } // Op 0x20a republishes one retained ADV text layout and includes the current marker frame when active. void PublishAdvTextLayout(int layoutSlot) { } void PublishAdvTextLayout( GfxState gfx, AdvTextLayoutPresentationBinding binding) => PublishAdvTextLayout(binding.LayoutSlot); // Op 0x199 temporarily yields the active ADV page into its registered hide-window coroutine. // The retained scene continues to render, but the text layout and its wait marker are suspended // until op 0x7c restores the saved page PC. void SetAdvPagePresentationSuspended(bool suspended) { } void SetAdvPagePresentationSuspended(GfxState gfx, bool suspended) => SetAdvPagePresentationSuspended(suspended); 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 serviceInputCallback) { while (serviceInputCallback()) { } WaitForInput(layoutSlot); } void WaitForInput(int layoutSlot, Func serviceInputCallback, Func autoWaitState) => WaitForInput(layoutSlot, serviceInputCallback); void WakeInputCallbackService() { } void InputCallbackCompleted(GfxState gfx) { } // Generic AGE input-callback services (ops 0xcc/0xcd, 0xfb/0xff/0x100, 0x108). // Interactive hosts expose the same monotonic clock used by their frame scheduler. long InputClockMilliseconds => Environment.TickCount64; void SetCursorResource(long resourceId) { } void ClearCursorResource() { } // Native 0x1c7/0x1cc query two distinct ADV skip channels. Headless and non-interactive // hosts default to normal playback; the Godot host supplies the live interactive values. void SetMessageSkipActive(bool active) { } // Logical action 6 is the native hold-to-fast-forward channel. Keep it separate from the // persistent op-0x88 channel so releasing the key cannot turn off the user's Skip toggle. void SetPhysicalMessageSkipActive(bool active) { } bool IsMessageSkipActive => false; // Optional diagnostic override. Native ReadTextDB state is VM/profile-owned; interactive hosts // normally leave this false. bool IsAdvReadSkipActive => false; // Normal playback reaches op 0x21c and parks until a queued 0x223 transition completes. The // read/message-skip branch reaches op 0x20c and presents the completed endpoint immediately. void WaitForForegroundTransition(GfxState gfx) { } void PresentFrame(GfxState gfx) { } // Legacy SYS4 screen-transition family (ops 0x21, 0x22, and 0x25): scripts render complete // frames into numbered surfaces, then block while the engine alpha-composites an endpoint. // Native bypasses the timed service when ADV fast-forward is already active at opcode dispatch. void FadeSurfaceWithBlack( GfxState gfx, int surface, long intervalArgument, SurfaceBlackFadeDirection direction, bool forceEndpoint = false) { } void CrossfadeSurfaces( GfxState gfx, int sourceSurface, int targetSurface, long intervalArgument, bool forceEndpoint = false) { } void CreateTexture(int slot, int width, int height); /// Return a stable RGBA snapshot of one numbered surface, or null when unavailable. RgbaImage? CaptureSurfacePixels(int slot) => null; /// Replace one numbered surface from decoded RGBA pixels. False means unsupported. bool ReplaceSurfacePixels(int slot, RgbaImage image) => false; void SetTexture(long resourceId, int slot); void SetTexture(long resourceId, int slot, long colorKey) => SetTexture(resourceId, slot); void ReleaseSurface(int slot) { } /// Clear the selected target's pixels; -1 denotes the main backbuffer. void ClearRenderTarget(int surfaceSlot) { } void ReleaseSurfaceRange(int firstSlot, int count) { for (int slot = firstSlot; slot < firstSlot + count; slot++) ReleaseSurface(slot); } void DrawTexture(int slot, int srcX, int srcY, int width, int height, int dstX, int dstY); (int Width, int Height) GetTextureSize(int slot); }