Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/guide/openreliant.d.luau
Original file line number Diff line number Diff line change
Expand Up @@ -880,6 +880,8 @@ export type Hud = {
register_display: (name: string, definition: {frame: (seconds: number) -> (), replaces: { HudInstrument }?, layout: { [HudInstrument]: HudLayout }?, parts: { [HudPart]: HudPartLayout }?}) -> string,
--- Turns the display `name` on or off: the calling mod's by its own name, or any mod's by the qualified one. Returns whether it's registered.
set_display_enabled: (name: string, enabled: boolean) -> boolean,
--- Changes where the display `name` puts the game's instruments and their parts, after it registers: the calling mod's by its own name, or any mod's by the qualified one. `layout` and `parts` replace the display's, with the checks `register_display` makes; one left out stays as it was, and a mistake changes nothing. They go back as they were as soon as the display is turned off, fails or its mod stops. Returns whether it's registered.
set_display_layout: (name: string, placement: {layout: { [HudInstrument]: HudLayout }?, parts: { [HudPart]: HudPartLayout }?}) -> boolean,
--- Where the game's instrument `instrument` last drew, in the window's pixels, as the mods' displays place it, and even while one stands in for it; nil before it first draws, or outside a mission.
bounds: (instrument: HudInstrument) -> HudBounds?,
--- Where the part `part` of the game's instruments last drew, in the window's pixels, as the mods' displays place it, and even while one hides it; nil before it first draws, or outside a mission.
Expand Down Expand Up @@ -1161,6 +1163,7 @@ export type Interfaces = {
height: number,
register_display: (name: string, definition: {frame: (seconds: number) -> (), replaces: { HudInstrument }?, layout: { [HudInstrument]: HudLayout }?, parts: { [HudPart]: HudPartLayout }?}) -> string,
set_display_enabled: (name: string, enabled: boolean) -> boolean,
set_display_layout: (name: string, placement: {layout: { [HudInstrument]: HudLayout }?, parts: { [HudPart]: HudPartLayout }?}) -> boolean,
bounds: (instrument: HudInstrument) -> HudBounds?,
part_bounds: (part: HudPart) -> HudBounds?,
window_state: (instrument: HudInstrument) -> HudWindowState?,
Expand Down
2 changes: 2 additions & 0 deletions docs/guide/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,7 @@ Drawing over the flight display, while it's shown: text, lines and rectangles, i
| `height` | number | The window's height, in pixels. |
| `register_display(name: string, definition: {frame: (seconds: number) -> (), replaces: { HudInstrument }?, layout: { [HudInstrument]: HudLayout }?, parts: { [HudPart]: HudPartLayout }?})` | string | Registers a display, which `name` qualified with the mod's name names. While the flight display shows, `frame` draws it with this package's functions each frame, until it's turned off with `set_display_enabled`. A failed `frame` turns off that display only. `replaces` lists the game's instruments it stands in for, which aren't drawn while it's on, `layout` moves and scales the instruments it names, and `parts` moves, scales, aligns, rewords or hides the parts of them it names; they keep working, and go back as they were as soon as it's turned off, fails or its mod stops. Returns the qualified name. |
| `set_display_enabled(name: string, enabled: boolean)` | boolean | Turns the display `name` on or off: the calling mod's by its own name, or any mod's by the qualified one. Returns whether it's registered. |
| `set_display_layout(name: string, placement: {layout: { [HudInstrument]: HudLayout }?, parts: { [HudPart]: HudPartLayout }?})` | boolean | Changes where the display `name` puts the game's instruments and their parts, after it registers: the calling mod's by its own name, or any mod's by the qualified one. `layout` and `parts` replace the display's, with the checks `register_display` makes; one left out stays as it was, and a mistake changes nothing. They go back as they were as soon as the display is turned off, fails or its mod stops. Returns whether it's registered. |
| `bounds(instrument: HudInstrument)` | [HudBounds](#hudbounds), or nil | Where the game's instrument `instrument` last drew, in the window's pixels, as the mods' displays place it, and even while one stands in for it; nil before it first draws, or outside a mission. |
| `part_bounds(part: HudPart)` | [HudBounds](#hudbounds), or nil | Where the part `part` of the game's instruments last drew, in the window's pixels, as the mods' displays place it, and even while one hides it; nil before it first draws, or outside a mission. |
| `window_state(instrument: HudInstrument)` | [HudWindowState](#hudwindowstate), or nil | How far the game's window that holds `instrument` has opened, and whether it's opening or closing, as the display last moved it on, which it does in every view; for the comms, the further open of its two windows. Nil for an instrument outside the windows, and outside a mission. |
Expand Down Expand Up @@ -646,6 +647,7 @@ turret, and a turret's own scripts on their turret.
| `height` | number | The window's height, in pixels. |
| `register_display(name: string, definition: {frame: (seconds: number) -> (), replaces: { HudInstrument }?, layout: { [HudInstrument]: HudLayout }?, parts: { [HudPart]: HudPartLayout }?})` | string | Registers a display, which `name` qualified with the mod's name names. While the flight display shows, `frame` draws it with this package's functions each frame, until it's turned off with `set_display_enabled`. A failed `frame` turns off that display only. `replaces` lists the game's instruments it stands in for, which aren't drawn while it's on, `layout` moves and scales the instruments it names, and `parts` moves, scales, aligns, rewords or hides the parts of them it names; they keep working, and go back as they were as soon as it's turned off, fails or its mod stops. Returns the qualified name. |
| `set_display_enabled(name: string, enabled: boolean)` | boolean | Turns the display `name` on or off: the calling mod's by its own name, or any mod's by the qualified one. Returns whether it's registered. |
| `set_display_layout(name: string, placement: {layout: { [HudInstrument]: HudLayout }?, parts: { [HudPart]: HudPartLayout }?})` | boolean | Changes where the display `name` puts the game's instruments and their parts, after it registers: the calling mod's by its own name, or any mod's by the qualified one. `layout` and `parts` replace the display's, with the checks `register_display` makes; one left out stays as it was, and a mistake changes nothing. They go back as they were as soon as the display is turned off, fails or its mod stops. Returns whether it's registered. |
| `bounds(instrument: HudInstrument)` | [HudBounds](#hudbounds), or nil | Where the game's instrument `instrument` last drew, in the window's pixels, as the mods' displays place it, and even while one stands in for it; nil before it first draws, or outside a mission. |
| `part_bounds(part: HudPart)` | [HudBounds](#hudbounds), or nil | Where the part `part` of the game's instruments last drew, in the window's pixels, as the mods' displays place it, and even while one hides it; nil before it first draws, or outside a mission. |
| `window_state(instrument: HudInstrument)` | [HudWindowState](#hudwindowstate), or nil | How far the game's window that holds `instrument` has opened, and whether it's opening or closing, as the display last moved it on, which it does in every view; for the comms, the further open of its two windows. Nil for an instrument outside the windows, and outside a mission. |
Expand Down
4 changes: 4 additions & 0 deletions docs/guide/scripting.md
Original file line number Diff line number Diff line change
Expand Up @@ -1854,6 +1854,10 @@ hud.register_display("radar", {
growing from where it stands on the screen, as the whole display grows with the window.
- Everything goes back as it was as soon as the display is turned off, its `frame` fails, or its
mod stops. Where two displays place the same instrument, the one registered later does.
- `hud.set_display_layout(name, { layout = ..., parts = ... })` changes a display's `layout` and
`parts` after it registers, such as for a layout that follows the window's size, with the same
checks. Each one the table gives replaces the display's, one it leaves out stays as it was, and a
mistake changes nothing.
- `hud.bounds(instrument)` gives where an instrument last drew, in the window's pixels, as the
layouts place it, and also while a display stands in for it, so that a display can draw its own
in the game's instrument's place.
Expand Down
1 change: 1 addition & 0 deletions src/scripting/drawing.zig
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,7 @@ pub fn Package(comptime which: Which) type {
return @import("registries.zig").show(call, .display, name, enabled);
}
}.set) else {};
pub const set_display_layout = if (which == .hud) api.Native("Changes where the display `name` puts the game's instruments and their parts, after it registers: the calling mod's by its own name, or any mod's by the qualified one. `layout` and `parts` replace the display's, with the checks `register_display` makes; one left out stays as it was, and a mistake changes nothing. They go back as they were as soon as the display is turned off, fails or its mod stops. Returns whether it's registered.", "name: string, placement: {layout: { [HudInstrument]: HudLayout }?, parts: { [HudPart]: HudPartLayout }?}", "boolean", @import("registries.zig").setLayout) else {};
pub const replaced = if (which == .hud) instruments.replaced else {};
pub const bounds = if (which == .hud) instruments.bounds else {};
pub const part_bounds = if (which == .hud) instruments.part_bounds else {};
Expand Down
32 changes: 32 additions & 0 deletions src/scripting/presentation.zig
Original file line number Diff line number Diff line change
Expand Up @@ -753,6 +753,38 @@ test "a display stands in for the game's instruments and reads what they show" {
fixture.shown.endGame();
}

test "a display changes its layout after it registers" {
var fixture: Fixture = undefined;
try fixture.init(&.{.{
"a",
&.{
.{ "mod.ini", "[Scripts]\nPlayer=player.luau\n" },
.{
"player.luau",
\\local hud = require("openreliant.hud")
\\hud.register_display("radio", {layout = {clock = {scale = 2}}, parts = {radio_face = {scale = 2}}, frame = function() end})
\\-- The layout follows the window, and the parts stay, as the table leaves them out.
\\assert(hud.set_display_layout("radio", {layout = {clock = {offset = vector.create(5, 0, 0)}}}))
\\-- A mistake changes nothing, and a name not registered isn't one.
\\assert(not pcall(hud.set_display_layout, "radio", {layout = {clock = {scale = 0}}, parts = {}}))
\\assert(not pcall(hud.set_display_layout, "radio", {placement = {}}))
\\assert(not hud.set_display_layout("b:radio", {}) and not hud.set_display_layout("nothing", {}))
},
},
}});
defer fixture.deinit();
try fixture.shown.startGame(null, fixture.mission.objects, false);
defer fixture.shown.endGame();
// The displays place the instruments while the flight display's layer shows.
var view: engine.game.camera.Camera = .{};
var viewing: engine.game.camera.Viewing = .{};
var host: Host = .{ .seconds = 0.04, .devices = &fixture.devices, .window = .{ 640, 480 }, .camera = .{ .camera = &view, .viewing = &viewing, .now = 1, .player = 0 } };
host.views.set(.hud, .{ .font = &fixture.font, .gpa = std.testing.allocator, .screen = .{ 640, 480 }, .scale = 1 });
fixture.shown.frame(host);
try std.testing.expectEqual(hud.Placement{ .offset = .{ 5, 0 } }, fixture.shown.instrumentPlacements().get(.clock));
try std.testing.expectEqual(hud.Placement{ .scale = 2 }, fixture.shown.partPlacements().get(.radio_face).place);
}

test "a display reads the cockpit's instruments in any view" {
const gpa = std.testing.allocator;
var fixture: Fixture = undefined;
Expand Down
37 changes: 37 additions & 0 deletions src/scripting/registries.zig
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,43 @@ pub fn registration(comptime kind: Kind) fn (api.Call) i32 {
}.run;
}

/// `hud.set_display_layout(name, placement)`: replaces where the display `name` puts the
/// instruments, its `layout`, and their parts, its `parts`, with the checks `register` makes. A
/// field the table leaves out stays as it was, and a mistake changes nothing
/// ([#1028](https://github.com/OpenReliant/openreliant/issues/1028)).
pub fn setLayout(call: api.Call) i32 {
const state = call.state;
_ = presentation.Presentation.of(call);
const name = values.read(state, []const u8, 1, "name");
if (state.typeOf(2) != .table) call.raise("expected a table of layout and parts", .{});
var buffer: [runtime.max_name]u8 = undefined;
const registry = &call.runtime().registries;
const found = if (call.named(name, &buffer)) |qualified| registry.find(.display, qualified) else null;
const index = found orelse {
state.pushBoolean(false);
return 1;
};
var layout = registry.entries.items[index].layout;
var placed_parts = registry.entries.items[index].parts;
state.pushNil();
while (state.next(2)) {
const key = (if (state.typeOf(-2) == .string) state.toString(-2) else null) orelse call.raise("placement keys must be names", .{});
if (std.mem.eql(u8, key, "layout")) {
layout = .initFill(null);
readLayout(call, &layout);
} else if (std.mem.eql(u8, key, "parts")) {
placed_parts = .initFill(null);
readParts(call, &placed_parts);
} else call.raise("unknown placement field '{s}': a display's placement has layout and parts", .{key});
state.pop(1);
}
const entry = &registry.entries.items[index];
entry.layout = layout;
entry.parts = placed_parts;
state.pushBoolean(true);
return 1;
}

pub fn show(call: api.Call, kind: Kind, name: ?[]const u8, enabled: bool) bool {
_ = presentation.Presentation.of(call);
const registry = &call.runtime().registries;
Expand Down
Loading