diff --git a/docs/guide/scripting.md b/docs/guide/scripting.md index d00f4a8b..bff7ea68 100644 --- a/docs/guide/scripting.md +++ b/docs/guide/scripting.md @@ -2170,7 +2170,8 @@ return { } ``` -The rooms draw their pointer over what scripts draw, as the front end does over a mod's screen. +The pointer goes over what scripts draw, in the rooms and their places, on the front end's screens +and a mod's, and on the menus over the rooms and over a mission. [`examples/mods/cd-player-pages`](../../examples/mods/cd-player-pages) gives the CD player its pages, with the front end's arrows under its screen. diff --git a/src/engine/game/hudoptions.zig b/src/engine/game/hudoptions.zig index 2347a426..200dbdb2 100644 --- a/src/engine/game/hudoptions.zig +++ b/src/engine/game/hudoptions.zig @@ -140,8 +140,14 @@ pub const PauseMenu = struct { view_setting: camera.CockpitSetting = .cockpit, /// The archive the fonts came from, which the settings screen's shapes are read from. archive: ?bigfile.Hog = null, - /// The settings screen, while it is shown. + /// The settings screen, while it is shown, and on the frame that leaves it. settings: ?SettingsScreen = null, + /// The pointer that this frame's drawing ends with, which `drawPointer` draws: the main + /// screen's or the settings screen's; none once the pause has an outcome, or where the + /// settings screen's shapes are left out. + pointer_shown: ?PointerShown = null, + + const PointerShown = enum { main, settings }; /// `optfnt.fnt` and `smlfnt2.fnt`, as `hog_load` read them and `font_open` opened them /// (`menu_font_large`, `menu_font_small`), and what holds them and their glyphs' images. @@ -195,24 +201,23 @@ pub const PauseMenu = struct { /// `pause_menu_draw` (`0x004906F0`), the overlay while paused, with the pointer brought up to /// date first as `mission_paused_frame` does (`menu_mouse_update`). A screen shown for the /// first time runs its enter routine; if its choice goes elsewhere, it runs its leave routine. - /// The pointer is drawn last. + /// The game draws the pointer last, which OpenReliant draws after what scripts draw over the + /// menu (`drawPointer`). pub fn draw(pause_menu: *PauseMenu, frame: Frame) menu.Error!void { + pause_menu.pointer_shown = null; const fonts = if (pause_menu.fonts) |*open_fonts| open_fonts else return; pause_menu.pointer.update(frame.devices.mouse, frame.screen); - const ui: menu.Ui = .{ - .gpa = fonts.gpa, - .target = frame.target, - .screen = frame.screen, - .scale = frame.ui_scale.of(frame.screen), - .art = frame.art, - .fonts = .{ .display = frame.font, .small = &fonts.small.font, .large = &fonts.large.font }, - .strings = frame.strings, - }; + const ui = uiOf(frame, fonts); const screen = switch (pause_menu.at) { .screen => |shown| shown, .outcome => return, }; const tab = SettingsScreen.tabOf(screen); + // The settings screen the frame before left goes, now that its pointer has been drawn. + if (tab == null) if (pause_menu.settings) |*shown| { + shown.close(fonts.gpa); + pause_menu.settings = null; + }; if (pause_menu.entered != screen) { if (tab) |shown| pause_menu.enterSettings(frame, shown); pause_menu.entered = screen; @@ -222,14 +227,51 @@ pub const PauseMenu = struct { .pointer = pause_menu.pointer, .escaped = frame.devices.keyboard.pressed(input.scan.escape, .none, true), }); - if (next) |going| if (!std.meta.eql(going, pause_menu.at)) { - if (pause_menu.settings) |*shown| shown.close(fonts.gpa); - pause_menu.settings = null; - pause_menu.at = going; - }; + if (next) |going| pause_menu.at = going; if (tab != null) return; if (frame.version) |version| try hud.drawVersion(ui.fonts.small, ui.gpa, ui.target, ui.screen, version, ui.scale); - try ui.drawPointer(pause_menu.pointer); + pause_menu.pointer_shown = .main; + } + + /// The end of `pause_menu_draw`: the pointer of the screen drawn, which OpenReliant draws after + /// what the scripts draw over the menu, so that it stays on top + /// ([#1171](https://github.com/OpenReliant/openreliant/issues/1171)). + pub fn drawPointer(pause_menu: *PauseMenu, frame: Frame) menu.Error!void { + const fonts = if (pause_menu.fonts) |*open_fonts| open_fonts else return; + switch (pause_menu.pointer_shown orelse return) { + .main => try uiOf(frame, fonts).drawPointer(pause_menu.pointer), + .settings => { + const shown = if (pause_menu.settings) |*kept| kept else return; + const shapes = if (shown.shapes) |*read| &read.art else return; + try settingsCanvas(frame, fonts).drawPointer(shapes, shown.pointer); + }, + } + } + + /// What the main screen draws with. + fn uiOf(frame: Frame, fonts: *Fonts) menu.Ui { + return .{ + .gpa = fonts.gpa, + .target = frame.target, + .screen = frame.screen, + .scale = frame.ui_scale.of(frame.screen), + .art = frame.art, + .fonts = .{ .display = frame.font, .small = &fonts.small.font, .large = &fonts.large.font }, + .strings = frame.strings, + }; + } + + /// What the settings screen draws on. + fn settingsCanvas(frame: Frame, fonts: *Fonts) canvas.Canvas { + return .{ + .gpa = fonts.gpa, + .target = frame.target, + .window = frame.screen, + .share = frame.ui_scale.share(), + .fonts = .{ .large = &fonts.large.font, .small = &fonts.small.font }, + .strings = frame.strings, + .version = frame.version, + }; } /// Opens the settings screen on `tab`: the front end's shapes and its dialog's read, and the @@ -261,16 +303,8 @@ pub const PauseMenu = struct { hud.drawFilled(frame.target, .{ .left = 0, .top = 0, .right = @floatFromInt(frame.screen[0]), .bottom = @floatFromInt(frame.screen[1]) }, shade); const shapes = if (shown.shapes) |*read| &read.art else return .{ .screen = .main }; const dialog_art = if (shown.dialog) |*read| &read.art else return .{ .screen = .main }; - const drawn: canvas.Canvas = .{ - .gpa = fonts.gpa, - .target = frame.target, - .window = frame.screen, - .share = frame.ui_scale.share(), - .fonts = .{ .large = &fonts.large.font, .small = &fonts.small.font }, - .strings = frame.strings, - .version = frame.version, - }; - try shown.screen.draw(drawn, shapes, dialog_art, .{ .devices = frame.devices, .sound = frame.settings.sound, .video = frame.settings.video }, shown.pointer); + try shown.screen.draw(settingsCanvas(frame, fonts), shapes, dialog_art, .{ .devices = frame.devices, .sound = frame.settings.sound, .video = frame.settings.video }); + pause_menu.pointer_shown = .settings; return switch (end orelse return null) { .back, .main_menu => .{ .screen = .main }, .continue_mission => .{ .outcome = .continue_mission }, diff --git a/src/engine/game/interface/canvas.zig b/src/engine/game/interface/canvas.zig index 9f12e70f..7b6ae988 100644 --- a/src/engine/game/interface/canvas.zig +++ b/src/engine/game/interface/canvas.zig @@ -216,6 +216,13 @@ pub const Canvas = struct { } }; + /// Draws `pointer` with its shape in `art`. The screens of the front end and the menus draw + /// their pointer last, so OpenReliant draws it after what the scripts draw over a screen, which + /// keeps it on top ([#1171](https://github.com/OpenReliant/openreliant/issues/1171)). + pub fn drawPointer(canvas: Canvas, art: *hud.Art, pointer: Pointer) Error!void { + try canvas.onScreen().shape(art, pointer.shape(), pointer.at); + } + /// **Improvement:** OpenReliant's version, where the canvas has one, in the window's bottom /// right corner as the pause menu writes it (`hud.drawVersion`), as large as the screen's text. /// A screen writes it before its pointer. diff --git a/src/engine/game/interface/game_modes.zig b/src/engine/game/interface/game_modes.zig index 262a02a5..c6b82702 100644 --- a/src/engine/game/interface/game_modes.zig +++ b/src/engine/game/interface/game_modes.zig @@ -181,8 +181,8 @@ pub const GameModes = struct { } /// The screen's drawing, over the background: the title, the list and the panel, the buttons, - /// the one under the pointer lit, OpenReliant's version, then the pointer. - pub fn draw(screen: GameModes, canvas: Canvas, art: *hud.Art, pointer: Pointer) canvas_module.Error!void { + /// the one under the pointer lit, and OpenReliant's version. The pointer goes over it all, after what scripts draw (`canvas.Canvas.drawPointer`). + pub fn draw(screen: GameModes, canvas: Canvas, art: *hud.Art) canvas_module.Error!void { const modes = screen.modes; try title.write(canvas, canvas.fonts.large, canvas_module.white); mod_manager.list_frame.draw(canvas); @@ -195,7 +195,6 @@ pub const GameModes = struct { try shown.draw(canvas.dimmedUnless(playable), art, settings.button_shapes, std.meta.eql(screen.lit, Item{ .button = button })); } try canvas.drawVersion(); - try canvas.onScreen().shape(art, pointer.shape(), pointer.at); } /// The rows shown, each a mode's label, the chosen one white, and the arrows, the one under the diff --git a/src/engine/game/interface/game_options.zig b/src/engine/game/interface/game_options.zig index 3327d97e..c21d2713 100644 --- a/src/engine/game/interface/game_options.zig +++ b/src/engine/game/interface/game_options.zig @@ -131,9 +131,9 @@ pub const GameOptions = struct { /// `game_options_draw` (`0x0042AFB0`), over the menu's background: the buttons, the item under /// the pointer lit, the title and the labels, ABOUT OPENRELIANT's box or QUIT's question where - /// either is up, OpenReliant's version, then the pointer. The game writes the label under the + /// either is up, and OpenReliant's version. The pointer goes over it all, after what scripts draw (`canvas.Canvas.drawPointer`). The game writes the label under the /// pointer white before the labels, which write over it in blue, so it stays blue. - pub fn draw(menu: GameOptions, canvas: Canvas, art: *hud.Art, dialog_art: *hud.Art, about_art: *hud.Art, pointer: Pointer) canvas_module.Error!void { + pub fn draw(menu: GameOptions, canvas: Canvas, art: *hud.Art, dialog_art: *hud.Art, about_art: *hud.Art) canvas_module.Error!void { for ([_]Item{ .about, .mods, .main_menu, .quit }) |item| { const rect = rects.get(item); try canvas.shape(art, button_shape, .{ rect.x, rect.y }); @@ -155,7 +155,6 @@ pub const GameOptions = struct { if (menu.about) |about| try about.draw(canvas, about_art); if (menu.confirm) |confirm| try confirm.draw(canvas, dialog_art); try canvas.drawVersion(); - try canvas.onScreen().shape(art, pointer.shape(), pointer.at); } }; diff --git a/src/engine/game/interface/in_game_options.zig b/src/engine/game/interface/in_game_options.zig index 15b30cc6..6278a85d 100644 --- a/src/engine/game/interface/in_game_options.zig +++ b/src/engine/game/interface/in_game_options.zig @@ -243,8 +243,8 @@ pub const InGameOptions = struct { /// `in_game_options_draw` (`0x004398B0`), over the menu's background: the buttons and their /// labels, the item under the pointer lit, the panels' labels, ABOUT OPENRELIANT's box or - /// QUIT's question where either is up, OpenReliant's version, then the pointer. - pub fn draw(menu: InGameOptions, canvas: Canvas, art: *hud.Art, dialog_art: *hud.Art, about_art: ?*hud.Art, pointer: Pointer) canvas_module.Error!void { + /// QUIT's question where either is up, and OpenReliant's version. The pointer goes over it all, after what scripts draw (`canvas.Canvas.drawPointer`). + pub fn draw(menu: InGameOptions, canvas: Canvas, art: *hud.Art, dialog_art: *hud.Art, about_art: ?*hud.Art) canvas_module.Error!void { for (std.enums.values(Item)) |item| { const shown = button(item) orelse continue; try shown.draw(canvas, art, button_shapes, menu.under == item); @@ -257,7 +257,6 @@ pub const InGameOptions = struct { if (menu.about) |about| if (about_art) |shapes| try about.draw(canvas, shapes); if (menu.confirm) |confirm| try confirm.draw(canvas, dialog_art); try canvas.drawVersion(); - try canvas.onScreen().shape(art, pointer.shape(), pointer.at); } }; diff --git a/src/engine/game/interface/main_menu.zig b/src/engine/game/interface/main_menu.zig index 0b9121a3..68f04f31 100644 --- a/src/engine/game/interface/main_menu.zig +++ b/src/engine/game/interface/main_menu.zig @@ -341,9 +341,14 @@ pub const MainMenu = struct { /// `main_menu_draw`: the panels' labels in blue, QUIT's and INSTANT ACTION's buttons and labels, /// the item under the pointer lit and a panel's labels in gold, QUIT's question or the notice - /// of a newer release where it is up, then the pointer, and with the developers' keys on the - /// mission's number. The menu's background is drawn behind it all (`background_set`). - pub fn draw(menu: MainMenu, canvas: Canvas, art: *hud.Art, dialog_art: *hud.Art, pointer: Pointer, developer_font: ?*hud.Opened) canvas_module.Error!void { + /// of a newer release where it is up, and with the developers' keys on the mission's number. + /// The menu's background is drawn behind it all (`background_set`), and the pointer over it + /// all, after what scripts draw (`canvas.Canvas.drawPointer`). + /// + /// **Improvement:** the game draws the mission's number over the pointer. OpenReliant draws + /// the pointer last, over what scripts draw too + /// ([#1171](https://github.com/OpenReliant/openreliant/issues/1171)). + pub fn draw(menu: MainMenu, canvas: Canvas, art: *hud.Art, dialog_art: *hud.Art, developer_font: ?*hud.Opened) canvas_module.Error!void { const large = canvas.fonts.large; const small = canvas.fonts.small; for (std.enums.values(Item)) |item| { @@ -376,7 +381,6 @@ pub const MainMenu = struct { try asked.draw(canvas, dialog_art); } try canvas.drawVersion(); - try canvas.onScreen().shape(art, pointer.shape(), pointer.at); if (menu.developer) if (developer_font) |font| { var buffer: [16]u8 = undefined; const number = std.mem.print(&buffer, "M{d}", .{menu.mission}) catch return; diff --git a/src/engine/game/interface/mod_catalogue.zig b/src/engine/game/interface/mod_catalogue.zig index 832f51f6..88b98572 100644 --- a/src/engine/game/interface/mod_catalogue.zig +++ b/src/engine/game/interface/mod_catalogue.zig @@ -789,9 +789,9 @@ pub const ModCatalogue = struct { } /// Draws the screen over the background: the title, the list, the panel, the buttons with the one - /// under the pointer highlighted and the ones that do nothing dimmed, OpenReliant's version, and - /// the pointer. - pub fn draw(screen: ModCatalogue, canvas: Canvas, art: *hud.Art, pointer: Pointer) canvas_module.Error!void { + /// under the pointer highlighted and the ones that do nothing dimmed, and OpenReliant's version. + /// The pointer goes over it all, after what scripts draw (`canvas.Canvas.drawPointer`). + pub fn draw(screen: ModCatalogue, canvas: Canvas, art: *hud.Art) canvas_module.Error!void { try title.write(canvas, canvas.fonts.large, canvas_module.white); list_frame.draw(canvas); details_frame.draw(canvas); @@ -802,7 +802,6 @@ pub const ModCatalogue = struct { try button.shown(doing).draw(canvas.dimmedUnless(screen.usable(button)), art, settings.button_shapes, std.meta.eql(screen.lit, Item{ .button = button })); } try canvas.drawVersion(); - try canvas.onScreen().shape(art, pointer.shape(), pointer.at); } /// Draws the visible rows: a group's heading in the large font, in blue, like the controls tab's diff --git a/src/engine/game/interface/mod_manager.zig b/src/engine/game/interface/mod_manager.zig index 5a77f3de..77c63082 100644 --- a/src/engine/game/interface/mod_manager.zig +++ b/src/engine/game/interface/mod_manager.zig @@ -630,9 +630,9 @@ pub const ModManager = struct { } /// The screen's drawing, over the background: the title, the note while the mods wait for the - /// next start, the list and the panel, the buttons, the one under the pointer lit, OpenReliant's - /// version, then the pointer. - pub fn draw(screen: ModManager, canvas: Canvas, art: *hud.Art, pointer: Pointer) canvas_module.Error!void { + /// next start, the list and the panel, the buttons, the one under the pointer lit, and + /// OpenReliant's version. The pointer goes over it all, after what scripts draw (`canvas.Canvas.drawPointer`). + pub fn draw(screen: ModManager, canvas: Canvas, art: *hud.Art) canvas_module.Error!void { try title.write(canvas, canvas.fonts.large, canvas_module.white); if (screen.waits()) try restart_note.write(canvas, canvas.fonts.small, canvas_module.gold); list_frame.draw(canvas); @@ -646,7 +646,6 @@ pub const ModManager = struct { } try movers.draw(canvas, art, screen.litArrow(.move)); try canvas.drawVersion(); - try canvas.onScreen().shape(art, pointer.shape(), pointer.at); } /// The rows whose mods load, as the rows stand: the mods that are on, aren't damaged, and have diff --git a/src/engine/game/interface/mod_options.zig b/src/engine/game/interface/mod_options.zig index e3124560..2368dfdf 100644 --- a/src/engine/game/interface/mod_options.zig +++ b/src/engine/game/interface/mod_options.zig @@ -486,8 +486,8 @@ pub const ModOptions = struct { } /// The screen's drawing, over the background: the title, the list, the buttons, the one under the - /// pointer lit, the text of the option under it, OpenReliant's version, then the pointer. - pub fn draw(screen: *const ModOptions, canvas: Canvas, art: *hud.Art, pointer: Pointer) canvas_module.Error!void { + /// pointer lit, the text of the option under it, and OpenReliant's version. The pointer goes over it all, after what scripts draw (`canvas.Canvas.drawPointer`). + pub fn draw(screen: *const ModOptions, canvas: Canvas, art: *hud.Art) canvas_module.Error!void { try (Label{ .text = .{ .words = screen.page.title }, .at = title_at, .alignment = .centre }).write(canvas, canvas.fonts.large, canvas_module.white); var buffer: [max_options]Line.Shown = undefined; var numbers: [max_options][number_words]u8 = undefined; @@ -505,7 +505,6 @@ pub const ModOptions = struct { try canvas.wrapped(canvas.fonts.small, descriptionAt(screen.pane), description, canvas_module.blue, .centre, description_lines); }; try canvas.drawVersion(); - try canvas.onScreen().shape(art, pointer.shape(), pointer.at); } }; diff --git a/src/engine/game/interface/pilot_roster.zig b/src/engine/game/interface/pilot_roster.zig index a7657645..ed446295 100644 --- a/src/engine/game/interface/pilot_roster.zig +++ b/src/engine/game/interface/pilot_roster.zig @@ -468,9 +468,10 @@ pub const Roster = struct { /// `pilot_roster_draw`: the pilot chosen, lit; the labels in blue; the call sign's frame and /// the call sign, white while it is typed, with its cursor; the buttons and the list's arrow, /// and the button under the pointer lit with its label in white; the list, where it is open; - /// SET GAME DIFFICULTY or QUIT's question, where either is up; then the pointer. The roster's - /// background is drawn behind it all (`background_set`). - pub fn draw(roster: Roster, canvas: Canvas, art: *hud.Art, dialog_art: *hud.Art, pointer: Pointer, pilot: Pilot) canvas_module.Error!void { + /// SET GAME DIFFICULTY or QUIT's question, where either is up. The roster's background is drawn + /// behind it all (`background_set`), and the pointer over it all, after what scripts draw + /// (`canvas.Canvas.drawPointer`). + pub fn draw(roster: Roster, canvas: Canvas, art: *hud.Art, dialog_art: *hud.Art, pilot: Pilot) canvas_module.Error!void { const small = canvas.fonts.small; if (pilot.female) try canvas.shape(art, female_shape, female_at) else try canvas.shape(art, male_shape, male_at); for (labels.values) |held| if (held) |label| try writeLabel(canvas, label, canvas_module.blue); @@ -497,7 +498,6 @@ pub const Roster = struct { if (roster.difficulty) |box| try box.draw(canvas, art, pilot.difficulty); if (roster.confirm) |confirm| try confirm.draw(canvas, dialog_art); try canvas.drawVersion(); - try canvas.onScreen().shape(art, pointer.shape(), pointer.at); } }; diff --git a/src/engine/game/interface/restart.zig b/src/engine/game/interface/restart.zig index 9726978c..755f0662 100644 --- a/src/engine/game/interface/restart.zig +++ b/src/engine/game/interface/restart.zig @@ -119,15 +119,25 @@ pub const Restart = struct { } /// `restart_screen_draw` (`0x0043ED90`), at the screen's brightness over a cleared frame: its - /// picture, the button under the pointer lit, the three labels in the small font, and the - /// pointer until a choice is made. - pub fn draw(screen: Restart, canvas: Canvas, art: *hud.Art, pointer: Pointer) canvas_module.Error!void { - var faded = canvas; - faded.brightness = screen.brightness; - try faded.shape(art, picture_shape, picture_at); - if (screen.under) |under| try faded.shape(art, lit_shape, lit_at.get(under)); - for (labels.values) |label| try label.write(faded, faded.fonts.small, label_colour); - if (screen.chosen == null) try faded.onScreen().shape(art, pointer.shape(), pointer.at); + /// picture, the button under the pointer lit, and the three labels in the small font. The + /// pointer goes over it, after what scripts draw (`drawPointer`). + pub fn draw(screen: Restart, canvas: Canvas, art: *hud.Art) canvas_module.Error!void { + try screen.faded(canvas).shape(art, picture_shape, picture_at); + if (screen.under) |under| try screen.faded(canvas).shape(art, lit_shape, lit_at.get(under)); + for (labels.values) |label| try label.write(screen.faded(canvas), canvas.fonts.small, label_colour); + } + + /// The end of `restart_screen_draw`: the pointer, at the screen's brightness, until a choice is + /// made (`canvas.Canvas.drawPointer`). + pub fn drawPointer(screen: Restart, canvas: Canvas, art: *hud.Art, pointer: Pointer) canvas_module.Error!void { + if (screen.chosen == null) try screen.faded(canvas).drawPointer(art, pointer); + } + + /// `canvas` at the screen's brightness. + fn faded(screen: Restart, canvas: Canvas) Canvas { + var at_brightness = canvas; + at_brightness.brightness = screen.brightness; + return at_brightness; } }; @@ -151,3 +161,27 @@ test Restart { _ = screen.frame(.{ .at = .{ 250, 380 }, .down = true }, &keyboard, 1); try std.testing.expectEqual(null, screen.chosen); } + +test "Restart.drawPointer" { + const gpa = std.testing.allocator; + const device = @import("../../surrender/srd3d/device.zig"); + const language = @import("../language.zig"); + const spr = @import("../../../formats/spr.zig"); + var recorder: device.testing.Recorder = .{ .gpa = gpa }; + defer recorder.deinit(); + const strings: language.Language = .{ .strings = &.{} }; + var font: hud.Opened = undefined; + const drawn: Canvas = .{ .gpa = gpa, .target = recorder.interface(), .window = .{ 640, 480 }, .fonts = .{ .large = &font, .small = &font }, .strings = &strings }; + // A set with a shape for each of the pointer's. + const bytes = try spr.testing.paletteAndShapes(gpa, 17); + defer gpa.free(bytes); + var art: hud.Art = try .init(gpa, try .parse(bytes), null, null); + defer art.deinit(gpa); + // The pointer shows until a choice is made. + var screen: Restart = .{}; + try screen.drawPointer(drawn, &art, .{ .at = .{ 100, 100 } }); + try std.testing.expectEqual(1, recorder.draws.items.len); + screen.chosen = .main_menu; + try screen.drawPointer(drawn, &art, .{ .at = .{ 100, 100 } }); + try std.testing.expectEqual(1, recorder.draws.items.len); +} diff --git a/src/engine/game/interface/saved_games.zig b/src/engine/game/interface/saved_games.zig index 6fd53e81..272d22de 100644 --- a/src/engine/game/interface/saved_games.zig +++ b/src/engine/game/interface/saved_games.zig @@ -560,8 +560,8 @@ pub const SavedGames = struct { /// title; saving, the name's frame and, while it is typed, the name with its cursor and OK; /// the labels and the date; the buttons, the one under the pointer lit with its label in white; /// the rows shown, the selected one white over a bar; the selected save's details; the arrows; - /// QUIT's question or the box saying a save failed; OpenReliant's version; then the pointer. - pub fn draw(screen: SavedGames, canvas: Canvas, art: *hud.Art, dialog_art: *hud.Art, pointer: Pointer, call_sign: []const u8) canvas_module.Error!void { + /// QUIT's question or the box saying a save failed; and OpenReliant's version. The pointer goes over it all, after what scripts draw (`canvas.Canvas.drawPointer`). + pub fn draw(screen: SavedGames, canvas: Canvas, art: *hud.Art, dialog_art: *hud.Art, call_sign: []const u8) canvas_module.Error!void { const large = canvas.fonts.large; const small = canvas.fonts.small; canvas.box(list_box_at, list_box_size); @@ -617,7 +617,6 @@ pub const SavedGames = struct { if (screen.confirm) |confirm| try confirm.draw(canvas, dialog_art); if (screen.save_error) |box| try box.draw(canvas, dialog_art); try canvas.drawVersion(); - try canvas.onScreen().shape(art, pointer.shape(), pointer.at); } }; diff --git a/src/engine/game/interface/settings.zig b/src/engine/game/interface/settings.zig index 6707ae7d..80e45219 100644 --- a/src/engine/game/interface/settings.zig +++ b/src/engine/game/interface/settings.zig @@ -566,8 +566,8 @@ pub const Settings = struct { /// The screen's drawing (`controls_screen_draw`, `0x0042CD30`, `audio_screen_draw`, /// `0x0042E2E0`, `video_screen_draw`, `0x0042F440`): the tabs' labels, the shown one white and /// one under the pointer gold, the tab, the buttons, the one under the pointer lit, Escape's - /// question where it is up, OpenReliant's version, then the pointer. - pub fn draw(screen: Settings, canvas: Canvas, art: *hud.Art, dialog_art: *hud.Art, shown: Shown, pointer: Pointer) canvas_module.Error!void { + /// question where it is up, and OpenReliant's version. The pointer goes over it all, after what scripts draw (`canvas.Canvas.drawPointer`). + pub fn draw(screen: Settings, canvas: Canvas, art: *hud.Art, dialog_art: *hud.Art, shown: Shown) canvas_module.Error!void { for (std.enums.values(Tab)) |tab| { const colour = if (tab == screen.tab) canvas_module.white else if (screen.lit_tab == tab) canvas_module.gold else canvas_module.blue; try tab.label().write(canvas, canvas.fonts.large, colour); @@ -580,7 +580,6 @@ pub const Settings = struct { } if (screen.question) |question| try question.draw(canvas, dialog_art); try canvas.drawVersion(); - try canvas.onScreen().shape(art, pointer.shape(), pointer.at); } }; diff --git a/src/engine/genilib/interf.zig b/src/engine/genilib/interf.zig index 7e3c5523..a69a62cd 100644 --- a/src/engine/genilib/interf.zig +++ b/src/engine/genilib/interf.zig @@ -763,23 +763,33 @@ pub const Interface = struct { // A mod's screen stands in for the screen's own, drawn by its scripts over this. if (front.scripted_shown != null) return; switch (front.screen) { - .main_menu => try front.main_menu.draw(drawn, art, &resources.dialog, front.pointer, &resources.developer.font), - .game_options => try front.game_options.draw(drawn, art, &resources.dialog, &resources.about, front.pointer), - .audio, .controls, .video => try front.settings.draw(drawn, art, &resources.dialog, shown, front.pointer), - .mods => try front.mod_manager.draw(drawn, art, front.pointer), - .mod_options => try front.mod_options.draw(drawn, art, front.pointer), - .mod_catalog => try front.mod_catalogue.draw(drawn, art, front.pointer), - .game_modes => try front.game_modes.draw(drawn, art, front.pointer), - .pilot_roster => try front.pilot_roster.draw(drawn, art, &resources.dialog, front.pointer, front.pilot), - .saved_games => try front.saved_games.draw(drawn, art, &resources.dialog, front.pointer, front.pilot.call_sign.slice()), + .main_menu => try front.main_menu.draw(drawn, art, &resources.dialog, &resources.developer.font), + .game_options => try front.game_options.draw(drawn, art, &resources.dialog, &resources.about), + .audio, .controls, .video => try front.settings.draw(drawn, art, &resources.dialog, shown), + .mods => try front.mod_manager.draw(drawn, art), + .mod_options => try front.mod_options.draw(drawn, art), + .mod_catalog => try front.mod_catalogue.draw(drawn, art), + .game_modes => try front.game_modes.draw(drawn, art), + .pilot_roster => try front.pilot_roster.draw(drawn, art, &resources.dialog, front.pilot), + .saved_games => try front.saved_games.draw(drawn, art, &resources.dialog, front.pilot.call_sign.slice()), else => {}, } } - /// The pointer over a mod's screen that stands in for the front end's own, which the front end - /// draws after the scripts' drawing, as each of its own screens draws the pointer last. + /// Whether the front end shows a screen, which `draw` draws, or a mod's screen in its place. + fn showsScreen(front: Interface) bool { + if (front.scripted_shown != null) return true; + return switch (front.screen) { + .main_menu, .game_options, .audio, .controls, .video, .mods, .mod_options, .mod_catalog, .game_modes, .pilot_roster, .saved_games => true, + else => false, + }; + } + + /// The pointer over the screen shown, its own or a mod's, which each of the front end's screens + /// draws last. The front end draws it after the scripts' drawing, so that it stays on top + /// (`canvas.Canvas.drawPointer`). pub fn drawPointer(front: Interface, resources: *Resources, target: device.Device, window: [2]u32, strings: *const language.Language) canvas.Error!void { - if (front.scripted_shown == null) return; + if (!front.showsScreen()) return; const art = if (resources.shapes) |*shapes| shapes else return; const drawn: canvas.Canvas = .{ .gpa = resources.gpa, @@ -788,7 +798,7 @@ pub const Interface = struct { .fonts = .{ .large = &resources.large.font, .small = &resources.small.font }, .strings = strings, }; - try drawn.onScreen().shape(art, front.pointer.shape(), front.pointer.at); + try drawn.drawPointer(art, front.pointer); } }; diff --git a/src/openreliant/main.zig b/src/openreliant/main.zig index 1c04cf3d..62f77811 100644 --- a/src/openreliant/main.zig +++ b/src/openreliant/main.zig @@ -2240,7 +2240,7 @@ const FrontEndDisplay = struct { const shown: *FrontEndDisplay = @ptrCast(@alignCast(context)); try shown.front.draw(shown.resources, shown.target, shown.window, shown.strings, shown.settings, version.string); if (shown.presentation) |scripts| try scripts.draw(.ui, shown.target, null); - // The pointer over a mod's screen that stands in for the front end's own. + // The pointer, over what the scripts draw. try shown.front.drawPointer(shown.resources, shown.target, shown.window, shown.strings); if (shown.console) |console| try console.draw(shown.target, shown.window, shown.strings, .menus); } @@ -2577,9 +2577,9 @@ const Display = struct { fn drawOverlay(display: *Display) !void { if (display.console) |console| if (console.isUp()) return console.draw(display.device, display.screen, display.strings, .mission); if (display.pause_menu.isOpen()) { - try display.drawPauseMenu(); + try display.pause_menu.draw(display.pauseFrame()); if (display.presentation) |scripts| try scripts.draw(.ui, display.device, null); - return; + return display.pause_menu.drawPointer(display.pauseFrame()); } try display.drawDisplay(); if (display.presentation) |scripts| { @@ -2588,8 +2588,9 @@ const Display = struct { } } - fn drawPauseMenu(display: *Display) !void { - return display.pause_menu.draw(.{ + /// What the pause menu draws with this frame. + fn pauseFrame(display: *Display) game.hudoptions.Frame { + return .{ .target = display.device, .screen = display.screen, .ui_scale = display.ui_scale, @@ -2600,7 +2601,7 @@ const Display = struct { .settings = display.settings, .version = version.string, .timer = platform.window.ticks(), - }); + }; } /// Draws the display from the drawn frame's copy of the game, then hands the game what the diff --git a/src/openreliant/rooms.zig b/src/openreliant/rooms.zig index c86ce8d2..9831dfd1 100644 --- a/src/openreliant/rooms.zig +++ b/src/openreliant/rooms.zig @@ -992,8 +992,18 @@ const Backdrop = struct { /// left out. fn draw(backdrop: *Backdrop, target: canvas.Canvas) ?*game.hud.Art { if (backdrop.background.image) |*shown| target.fill(shown); + return backdrop.art(); + } + + /// The shapes the screen draws with; null where they are left out. + fn art(backdrop: *Backdrop) ?*game.hud.Art { return if (backdrop.shapes) |*loaded| &loaded.art else null; } + + /// The screen's pointer, with its shapes (`canvas.Canvas.drawPointer`). + fn drawPointer(backdrop: *Backdrop, target: canvas.Canvas, pointer: canvas.Pointer) canvas.Error!void { + if (backdrop.art()) |shapes| try target.drawPointer(shapes, pointer); + } }; /// The in-game options, and what they draw with, ABOUT STARLANCER's shapes among it. @@ -1007,10 +1017,10 @@ const Menu = struct { if (menu.about_shapes) |*shapes| shapes.deinit(gpa); } - fn draw(menu: *Menu, target: canvas.Canvas, dialog: *game.hud.Art, pointer: canvas.Pointer) canvas.Error!void { + fn draw(menu: *Menu, target: canvas.Canvas, dialog: *game.hud.Art) canvas.Error!void { const shapes = menu.backdrop.draw(target) orelse return; const about = if (menu.about_shapes) |*loaded| &loaded.art else null; - try menu.state.draw(target, shapes, dialog, about, pointer); + try menu.state.draw(target, shapes, dialog, about); } }; @@ -1023,9 +1033,9 @@ const SavesScreen = struct { screen.backdrop.close(gpa); } - fn draw(screen: *SavesScreen, target: canvas.Canvas, dialog: *game.hud.Art, pointer: canvas.Pointer, call_sign: []const u8) canvas.Error!void { + fn draw(screen: *SavesScreen, target: canvas.Canvas, dialog: *game.hud.Art, call_sign: []const u8) canvas.Error!void { const shapes = screen.backdrop.draw(target) orelse return; - try screen.state.draw(target, shapes, dialog, pointer, call_sign); + try screen.state.draw(target, shapes, dialog, call_sign); } }; @@ -1038,9 +1048,9 @@ const SettingsScreen = struct { screen.backdrop.close(gpa); } - fn draw(screen: *SettingsScreen, target: canvas.Canvas, dialog: *game.hud.Art, shown: settings.Shown, pointer: canvas.Pointer) canvas.Error!void { + fn draw(screen: *SettingsScreen, target: canvas.Canvas, dialog: *game.hud.Art, shown: settings.Shown) canvas.Error!void { const shapes = screen.backdrop.draw(target) orelse return; - try screen.state.draw(target, shapes, dialog, shown, pointer); + try screen.state.draw(target, shapes, dialog, shown); } }; @@ -1049,9 +1059,14 @@ const Restarting = struct { state: restart_screen.Restart = .{}, shapes: ?canvas.Shapes = null, - fn draw(screen: *Restarting, target: canvas.Canvas, pointer: canvas.Pointer) canvas.Error!void { + fn draw(screen: *Restarting, target: canvas.Canvas) canvas.Error!void { + const shapes = if (screen.shapes) |*loaded| &loaded.art else return; + try screen.state.draw(target, shapes); + } + + fn drawPointer(screen: *Restarting, target: canvas.Canvas, pointer: canvas.Pointer) canvas.Error!void { const shapes = if (screen.shapes) |*loaded| &loaded.art else return; - try screen.state.draw(target, shapes, pointer); + try screen.state.drawPointer(target, shapes, pointer); } }; @@ -1124,11 +1139,11 @@ const Shown = struct { switch (shown.screen) { .induction => |tour| tour.draw(target), .rooms => |inside| try inside.draw(target, driver.clock.game_ticks), - .options => |menu| try menu.draw(target, &driver.front.dialog, driver.pointer), - .saved_games => |screen| try screen.draw(target, &driver.front.dialog, driver.pointer, driver.pilot.call_sign.slice()), - .settings => |screen| try screen.draw(target, &driver.front.dialog, .{ .devices = driver.movies.devices, .sound = driver.sound, .video = driver.video }, driver.pointer), + .options => |menu| try menu.draw(target, &driver.front.dialog), + .saved_games => |screen| try screen.draw(target, &driver.front.dialog, driver.pilot.call_sign.slice()), + .settings => |screen| try screen.draw(target, &driver.front.dialog, .{ .devices = driver.movies.devices, .sound = driver.sound, .video = driver.video }), .briefing => |meeting| try meeting.draw(target), - .restart => |screen| try screen.draw(target, driver.pointer), + .restart => |screen| try screen.draw(target), .credits => |screen| try screen.draw(target), .itac => |terminal| try terminal.draw(target), .pod => |pod| try pod.draw(target), @@ -1136,15 +1151,19 @@ const Shown = struct { .cd_player => |player| try player.draw(target), } if (driver.movies.scripts) |scripts| try scripts.drawUi(target.target); - // The pointer of the rooms and their places goes over what scripts draw, as the front - // end's does over a mod's screen. The menus draw theirs as the front end's own screens do. + // The pointer of the rooms, their places and the menus goes over what scripts draw, as the + // front end's does. switch (shown.screen) { .rooms => |inside| try inside.drawPointer(target), .itac => |terminal| try terminal.drawPointer(target), .pod => |pod| try pod.drawPointer(target), .locker => |case| try case.drawPointer(target), .cd_player => |player| try player.drawPointer(target), - .induction, .options, .saved_games, .settings, .briefing, .restart, .credits => {}, + .options => |menu| try menu.backdrop.drawPointer(target, driver.pointer), + .saved_games => |screen| try screen.backdrop.drawPointer(target, driver.pointer), + .settings => |screen| try screen.backdrop.drawPointer(target, driver.pointer), + .restart => |screen| try screen.drawPointer(target, driver.pointer), + .induction, .briefing, .credits => {}, } if (driver.console) |console| try console.shown.draw(target.target, shown.window, driver.strings, .menus); }