diff --git a/README.md b/README.md index bd25aec8..a3a6df1d 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,8 @@ through the inventory title and HUD, and generates a typed Kotlin view for each server. If you rename a button in the UI, the server code stops compiling instead of breaking at runtime. Inventory UIs support real items, scrolling collections, paging, toggles, choices, tooltips, and native anvil text -input. HUDs are positioned with core shaders and rendered by the server whenever it wants to update them. +input. HUDs are positioned with core shaders and rendered by the server whenever it wants to update them. Full-screen +windows are drawn over the whole screen with no screen open, and the player aims a cursor by turning. > [!WARNING] > @@ -191,6 +192,63 @@ You own the rest: - Per-player state and cleanup, such as stopping the resend task when the player leaves. - Threading: a `HudView` or `HudStack` is not thread-safe, so render it from one thread at a time. +## Full-screen windows + +A full-screen window is authored with [``](docs/AUTHORING.md#full-screen-windows) and generates a typed +view, such as `LobbyFullscreen`, that extends `FullscreenView`. With the `minestom` target it takes a `Player`; with +other targets it takes a `FullscreenHost`, which only `window-minestom` implements (`MinestomFullscreenHost`). The +player aims by turning, clicks with the attack key, and leaves by sneaking. An excerpt of the example's +[`MyLobby`](jvm/example/src/main/kotlin/com/chunkzero/window/example/MyLobby.kt): + +```kotlin +class MyLobby(player: Player) : LobbyFullscreen(player) { + private var name by state("${player.username}'s Foundry") + private val worlds = list(cells = 3, key = { it: String -> it }, step = 1) { WORLDS } + + override fun foundryName(): Component = Component.text(name) + + override fun worldName(index: Int): Component = Component.text(worlds.at(index).orEmpty()) + + // A click opens a native dialog; the canvas is back once the player confirms or cancels. + override fun onRename(click: FullscreenClick) { + promptText(Component.text("Name your foundry"), name) { text -> text?.let { name = it } } + } + + override fun onScroll(steps: Int) = repeat(abs(steps)) { if (steps > 0) worlds.next() else worlds.previous() } + + // The other generated bindings and handlers are omitted. +} + +MyLobby(player).open() +``` + +- Handlers receive a `FullscreenClick` with the cursor's canvas position. `onScroll(steps)` gets the scroll wheel as + signed steps, and `onRightClick(click)` gets right clicks anywhere on the canvas. +- `list(...)` pages a list through a ``'s cells, like `WindowView.list(...)`. +- `promptText(title, initial) { text -> }` shows a native dialog with a text field and Confirm and Cancel buttons. The + callback gets the text, or `null` on Cancel or when the window closes. Escape does not dismiss it. +- Views are single-use, as with windows, and a player has at most one full-screen window open. + +While the window is open, the Minestom host: + +- keeps the player in spectator mode on the server and tells only their client it is in survival, so clicks, the scroll + wheel, and number keys reach the server. Game mode, ability, hotbar, time, and max health updates the server sends + meanwhile still reach the client, followed by packets that hide them again. Closing restores the player's game mode + (or the one the server set meanwhile), abilities, position, and held slot. +- hides the HUD by holding the client's world age negative, which the pack's shaders read. The hidden strip at the + bottom of the screen can clip the player's own inventory (E) at small GUI heights, since the server is not told it + opened. +- closes the window when the player dies or spawns into another instance. Opening one while the player is dead or + respawning throws, so a respawn listener should open it on the next tick. + +Limitations: + +- First person only: third person (F5) mirrors the camera, so the drawn cursor and the server's hit-test disagree. +- Shader packs that replace world rendering, such as Iris, are not supported. +- The client sends at most one held-slot change per tick, so `onScroll` gets one update per tick of at most 4 steps + either way, more than 4 notches in one tick read as a scroll the other way, and number keys look like scrolling. +- Chat and other GUI overlays draw over the canvas, and there are no real items. + ## Supported versions Window targets Minecraft 26.2 (resource pack format 88). The hosts and example server use Minestom builds for 26.2, and diff --git a/docs/AUTHORING.md b/docs/AUTHORING.md index a57c91d4..43c0bf40 100644 --- a/docs/AUTHORING.md +++ b/docs/AUTHORING.md @@ -564,10 +564,11 @@ const raised = (fill: Token) => ### Roots -| root | `raw` equivalent | notes | -| ---------------------------------------------------- | ---------------- | ---------------------------------------------------------------- | -| `` | `ui(...)` | `frame` is drawn first over the GUI grown by `bleed` | -| `` | `hud(...)` | children lay out as a column; without a size it fits its content | +| root | `raw` equivalent | notes | +| ---------------------------------------------------- | ----------------- | ---------------------------------------------------------------- | +| `` | `ui(...)` | `frame` is drawn first over the GUI grown by `bleed` | +| `` | `hud(...)` | children lay out as a column; without a size it fits its content | +| `` | `fullscreen(...)` | see [Full-screen windows](#full-screen-windows) | `text={{ ... }}` on a window, HUD, section, box, or switch sets default text style (`color`, `shadow`, `bold`, `italic`, `underlined`, `strikethrough`, `obfuscated`, `font`, `smallCaps`, `align`) for the text inside it; the nearest setting @@ -612,8 +613,25 @@ which the client still draws once the shader has moved them. ### Full-screen windows -`` lays its children out on a 480x270 canvas drawn over the whole screen with no screen -open; the player aims a cursor by turning. It takes the same elements as a window, except those that need a container: +`` (`raw.fullscreen(...)`) lays its children out on a 480x270 canvas drawn over the +whole screen with no screen open; the player aims a cursor by turning. List it in `defineWindows({ fullscreens })`. The +canvas is letterboxed or pillarboxed to the screen and does not move with FOV. Its children lay out as a column in a +root box that takes box props such as `align`, `justify`, `text`, and `theme`. A pack with a full-screen window emits +the core shaders that draw it, with no `coreShaders` option, and needs pack format 85 through 88. + +```tsx + + + +``` + +- `mode`: `"unlocked"`, the default, keeps the camera on the player, so the world turns behind the canvas as they aim. + `"horse"` locks the view in place and draws the cursor about a tick behind the mouse. +- `backdrop`: a hex color filling the screen behind the canvas, letterbox included. Omit it to show the world. + +It takes the same elements as a window, except those that need a container: - **Supported**: art, static and bound text, switches and conditions, tints, bars, regions and the industrial controls built on them (with hover outlines), `toggle` and `selection` state, and indexed handles. A ``'s cells are @@ -629,6 +647,10 @@ open; the player aims a cursor by turning. It takes the same elements as a windo region's `itemModel`, and text `shadow` are also rejected. - `span` and `at` count grid tracks, which are slots only in a section. A `` sizes its `cell`s at 18px per slot; give other industrial controls a size or a grid to fill on a full-screen window. +- Text costs a [text-row font](#what-things-cost) per distinct row, as in windows, and full-screen rows rarely line up + with container rows. Each tooltip line counts as a row. + +The server side is in the README's [Full-screen windows](../README.md#full-screen-windows). ## Industrial diff --git a/docs/VALIDATION.md b/docs/VALIDATION.md index c10faf39..380dac15 100644 --- a/docs/VALIDATION.md +++ b/docs/VALIDATION.md @@ -60,6 +60,10 @@ sprites, container/hotbar item sync, cards that render a real item in one of the routes clicks to the card's region, left/right/shift/hotbar clicks, reactive title invalidation, anvil input, HUD diagnostics, GUI scale 2, and clean disconnect. +It does not cover full-screen windows. Their compiler output, cursor mapping, session, and host behavior are covered by +the deterministic checks and the `fullscreen` golden case; check how they draw and take input in a real client with the +example's `/lobby` and `/lookout`, in first person, at a few window sizes and GUI scales. + The `just mc-validation` recipe defaults to the sibling `../mc-validation` checkout and passes its absolute path to both Gradle builds. Override it with: diff --git a/example/README.md b/example/README.md index 22d6d344..90096e14 100644 --- a/example/README.md +++ b/example/README.md @@ -10,12 +10,14 @@ example/ │ ├── rpp.config.ts # pack metadata, build, and Window plugin configuration │ ├── src/ │ │ └── window/ # UI sources — stripped from the built pack -│ │ ├── index.ts # defineWindows entry: the sprite catalog, windows, HUDs, and previews +│ │ ├── index.ts # defineWindows entry: the sprite catalog, windows, HUDs, full-screen windows, and previews │ │ ├── handles.ts # typed bindings shared by the windows │ │ ├── art.ts # inline art: icons, coin, vents, bars, and the rank sprites │ │ ├── shop.tsx # paged catalog, selections, toggles, and buttons │ │ ├── search.tsx # linked native-anvil catalog search │ │ ├── upgrades.tsx # themed workshop: styles, vars, cards with a face per stage, choices, and a hotspot +│ │ ├── lobby.tsx # full-screen main menu: paged worlds, a rename prompt, tooltips, and a ready switch +│ │ ├── lookout.tsx # horse-mode full-screen window with a signal bar │ │ ├── status_hud.tsx # the shader-relocated actionbar HUDs │ │ └── clock_hud.ts # the clock HUD, written with the function-style raw API │ ├── build/previews/ # preview PNGs of the windows and HUDs, rewritten by every build @@ -24,13 +26,14 @@ example/ ``` The Minestom server is the Gradle module `jvm/example`, which depends on `window-minestom`. `Main.kt` boots the server, -`Market.kt` and `Workshop.kt` are a throwaway domain model, and `MyShop.kt` and `MyUpgrades.kt` implement the -**generated** `ShopView` and `UpgradesView`. `MyStatusHud.kt` implements the **generated** HUD views. The pack uses -`target: "minestom"`, so a window view takes the `Player` it is shown to: `Main.kt` opens the shop with -`MyShop(player, market).open()`. Generated HUD views take no player; the handwritten ones that need it take it -themselves. `Main.kt` joins the status HUDs into one action-bar `HudStack` and resends it with `player.sendHud(stack)` -every tick, which keeps the wave bar moving smoothly, re-renders the clocks, and stays ahead of the action bar fading -out. The bindings live under `generated/`, produced by the rpp plugin — committed, do not hand-edit. +`Market.kt` and `Workshop.kt` are a throwaway domain model, `MyShop.kt` and `MyUpgrades.kt` implement the **generated** +`ShopView` and `UpgradesView`, and `MyLobby.kt` and `MyLookout.kt` the full-screen `LobbyFullscreen` and +`LookoutFullscreen`. `MyStatusHud.kt` implements the **generated** HUD views. The pack uses `target: "minestom"`, so a +window view takes the `Player` it is shown to: `Main.kt` opens the shop with `MyShop(player, market).open()`. Generated +HUD views take no player; the handwritten ones that need it take it themselves. `Main.kt` joins the status HUDs into one +action-bar `HudStack` and resends it with `player.sendHud(stack)` every tick, which keeps the wave bar moving smoothly, +re-renders the clocks, and stays ahead of the action bar fading out. The bindings live under `generated/`, produced by +the rpp plugin — committed, do not hand-edit. ## Run it @@ -51,6 +54,12 @@ levels, each card shows its level as text and is marked new, shows a bar, or is the shared tone. A maxed card takes no clicks, so hovering it shows "Fully upgraded" and no outline. Its levels last until the server stops. +`/lobby` opens a full-screen main menu over the world; aim by turning and sneak to leave. Click the foundry name to +rename it in a native dialog, which returns to the lobby on Confirm or Cancel. The arrows or the scroll wheel move the +world list, a click selects a world, and right clicking anywhere toggles ready, which switches the line under the list +and retints the status. Play leaves once you are ready. `/lookout` opens a horse-mode window, which locks the view while +the cursor follows the mouse; Charge fills its signal bar. + The actionbar-backed HUD demo is disabled by default so the inventory examples stay visually focused. Enable it with `-Dwindow.hud.enabled=true`; use `-Dwindow.hud.spriteDebug=true` to print each HUD's static art to chat. The pack blanks vanilla's experience bar, and one of the HUDs draws its wave bar there. @@ -59,6 +68,8 @@ vanilla's experience bar, and one of the HUDs draws its wave bar there. | -------------------------------- | ---------------------------------------------------------------------- | | `/shop` | opens the catalog, like the hotbar emerald | | `/upgrades` | opens the workshop, like the catalog's Workshop button | +| `/lobby` | opens the full-screen lobby | +| `/lookout` | opens the horse-mode lookout | | `/tone ` | pins the tone the HUDs and newly opened windows draw in; `auto` unpins | | `/bar ` | pins how full the wave bar and a newly opened shop's bar are | @@ -71,12 +82,12 @@ when testing from another machine. ## The typed contract The rpp plugin emits `internal` pack data (`WindowPackData`, `WindowFonts`, `WindowSpacers`, and friends), the public -`WindowDefinitions` and `WindowHudDefinitions`, and one public abstract class for each window/HUD. Each handle in -`handles.ts` becomes a member of the views that use it: `text` handles such as `balance` and `status` are functions -returning a `Component`, `flag` handles such as `canBuy` are Boolean functions, `action` handles such as `buy` are -`onBuy(click)` handlers, `products` is a `WindowCollection` that `MyShop` backs with a `WindowList`, and the -`category`/`sort` selections and `favorites`/`affordable` toggles are UI-owned `var`s with `on…Changed` hooks. In -`UpgradesView` the indexed handles take the card's index (`upgradeLevel(index)`, `onUpgrade(click)` with `click.index`), -the `upgradeStage` value and the `rank` sprite return generated enums, and `upgradeProgress` and `tone` return a bar's -fraction and a tint's palette entry. `MyStatusHud` implements each HUD's handles. Omit one, rebuild, and the breakage -surfaces at compile time. +`WindowDefinitions`, `WindowHudDefinitions`, and `WindowFullscreenDefinitions`, and one public abstract class for each +window, HUD, and full-screen window. Each handle in `handles.ts` becomes a member of the views that use it: `text` +handles such as `balance` and `status` are functions returning a `Component`, `flag` handles such as `canBuy` are +Boolean functions, `action` handles such as `buy` are `onBuy(click)` handlers, `products` is a `WindowCollection` that +`MyShop` backs with a `WindowList`, and the `category`/`sort` selections and `favorites`/`affordable` toggles are +UI-owned `var`s with `on…Changed` hooks. In `UpgradesView` the indexed handles take the card's index +(`upgradeLevel(index)`, `onUpgrade(click)` with `click.index`), the `upgradeStage` value and the `rank` sprite return +generated enums, and `upgradeProgress` and `tone` return a bar's fraction and a tint's palette entry. `MyStatusHud` +implements each HUD's handles. Omit one, rebuild, and the breakage surfaces at compile time. diff --git a/example/pack/rpp.config.ts b/example/pack/rpp.config.ts index 262c3856..dce560bc 100644 --- a/example/pack/rpp.config.ts +++ b/example/pack/rpp.config.ts @@ -18,7 +18,7 @@ export default defineConfig({ namespace: "window", coreShaders: { hud: true, hover: true }, vanilla: { anvil: { field: "search_field" }, hud: { experience: false }, slotHighlight: false }, - size: { report: true, budget: 1_000_000 }, + size: { report: true, budget: 1_150_000 }, previews: { output: "build/previews" }, kotlin: { package: "com.chunkzero.window.example.generated", diff --git a/example/pack/src/window/handles.ts b/example/pack/src/window/handles.ts index c4986abe..7cf5e6d8 100644 --- a/example/pack/src/window/handles.ts +++ b/example/pack/src/window/handles.ts @@ -71,3 +71,20 @@ export const amount = selection("amount", ["one", "five", "max"]); export const rank = sprite("rank", { only: ["rank_1", "rank_2", "rank_3"] }); export const rankName = text("rank_name"); export const openShop = action("open_shop"); + +// Lobby and lookout, the full-screen windows; `tone` is shared +export const WORLDS = 3; +const worlds = { shape: [WORLDS] } as const; +export const worldName = text("world_name", worlds); +export const pickWorld = action("pick_world", worlds); +export const previousWorld = action("previous_world"); +export const nextWorld = action("next_world"); +export const foundryName = text("foundry_name"); +export const rename = action("rename"); +export const lobbyStatus = text("lobby_status"); +export const ready = flag("ready"); +export const play = action("play"); + +/** How far the lookout's signal has charged. */ +export const signal = fraction("signal"); +export const charge = action("charge"); diff --git a/example/pack/src/window/index.ts b/example/pack/src/window/index.ts index 3e5219fb..9e8400a1 100644 --- a/example/pack/src/window/index.ts +++ b/example/pack/src/window/index.ts @@ -5,6 +5,8 @@ import { art } from "plugin:window/theme/industrial"; import { ranks } from "./art.ts"; import clock, { runtime } from "./clock_hud.ts"; import * as handles from "./handles.ts"; +import lobby from "./lobby.tsx"; +import lookout from "./lookout.tsx"; import search from "./search.tsx"; import shop from "./shop.tsx"; import statusHuds, { bottomCenter, coins, hint, power, rate, topLeft, waveBar, waveCharge } from "./status_hud.tsx"; @@ -33,6 +35,7 @@ export default defineWindows({ sprites: { searchField: art.inputField, ...ranks }, windows: [shop, search, upgrades], huds: statusHuds, + fullscreens: [lobby, lookout], // `rpp build` writes these to build/previews/, one PNG per preview. previews: [ preview(shop, [ @@ -76,5 +79,11 @@ export default defineWindows({ { name: "status_bottom_center_danger", scale: 3 }, ), preview(waveBar, [[waveCharge, 0.6]], { scale: 3 }), + preview(lobby, [ + [handles.foundryName, "Steve's Foundry"], + [handles.lobbyStatus, "Ready for Emberly"], + [handles.worldName, ["Ironvale", "Emberly", "Ashmoor"]], + [handles.ready, true], + ]), ], }); diff --git a/example/pack/src/window/lobby.tsx b/example/pack/src/window/lobby.tsx new file mode 100644 index 00000000..56bc1e0d --- /dev/null +++ b/example/pack/src/window/lobby.tsx @@ -0,0 +1,74 @@ +import { Case, Fullscreen, Image, Region, Switch, Text, create } from "plugin:window/ui"; +import { Button, Column, Repeater, Row, art } from "plugin:window/theme/industrial"; + +import { icons } from "./art.ts"; +import { + WORLDS, + foundryName, + lobbyStatus, + nextWorld, + pickWorld, + play, + previousWorld, + ready, + rename, + tone, + worldName, +} from "./handles.ts"; + +const s = create({ + title: { color: "#ffe2b8" }, + plate: { frame: art.recess, width: 160, height: 16, justify: "center", align: "center" }, + arrow: { width: 16, height: 18 }, + action: { width: 60, height: 18 }, +}); + +/** + * A main menu drawn over the whole screen while the world turns behind it. Click the name to rename the foundry in a + * native dialog, pick a world from the paged list (the arrows or the scroll wheel move it), right click anywhere to + * ready up, then play. + */ +export default ( + + + Foundry Lobby + + + + + + + + + {(i) => } + + + + + + + Ready + + + Right click when ready + + + + + + + + +); diff --git a/example/pack/src/window/lookout.tsx b/example/pack/src/window/lookout.tsx new file mode 100644 index 00000000..1bf9a4b7 --- /dev/null +++ b/example/pack/src/window/lookout.tsx @@ -0,0 +1,30 @@ +import { Bar, Fullscreen, Text } from "plugin:window/ui"; +import { Button, Column, Row, art } from "plugin:window/theme/industrial"; + +import { barTrack, toneFill } from "./art.ts"; +import { charge, signal } from "./handles.ts"; + +/** A horse-mode window: the view stays locked while the player aims the cursor, and charging fills the signal bar. */ +export default ( + + + Lookout + + + + + + + +); diff --git a/jvm/example/src/main/kotlin/com/chunkzero/window/example/Main.kt b/jvm/example/src/main/kotlin/com/chunkzero/window/example/Main.kt index b139f2aa..3d884641 100644 --- a/jvm/example/src/main/kotlin/com/chunkzero/window/example/Main.kt +++ b/jvm/example/src/main/kotlin/com/chunkzero/window/example/Main.kt @@ -33,8 +33,8 @@ import java.nio.file.Path * jvm/gradlew -p jvm :example:run * ``` * - * Then connect a Minecraft 26.2 client and the shop opens on spawn. Reopen it with `/shop` or the hotbar emerald, and - * open the workshop with `/upgrades` or the shop's Workshop button. + * Then connect a Minecraft 26.2 client and the shop opens on spawn. Reopen it with `/shop` or the hotbar emerald, open + * the workshop with `/upgrades` or the shop's Workshop button, and the full-screen windows with `/lobby` and `/lookout`. */ fun main() { val server = MinecraftServer.init() @@ -50,6 +50,7 @@ fun main() { registerShopAccess(market) registerToneCommand() registerBarCommand() + registerFullscreens() server.start("0.0.0.0", serverPort) println("Window example server listening on $serverPort — pack served at $packUrl.") @@ -128,6 +129,16 @@ private fun registerShopAccess(market: Market) { } } +/** `/lobby` opens the full-screen lobby, and `/lookout` the horse-mode lookout; sneak to leave either. */ +private fun registerFullscreens() { + val lobby = Command("lobby") + lobby.setDefaultExecutor { sender, _ -> (sender as? Player)?.let { MyLobby(it).open() } } + MinecraftServer.getCommandManager().register(lobby) + val lookout = Command("lookout") + lookout.setDefaultExecutor { sender, _ -> (sender as? Player)?.let { MyLookout(it).open() } } + MinecraftServer.getCommandManager().register(lookout) +} + // A fresh view per open: each carries its own reactive state. private fun openShop( player: Player, diff --git a/jvm/example/src/main/kotlin/com/chunkzero/window/example/MyLobby.kt b/jvm/example/src/main/kotlin/com/chunkzero/window/example/MyLobby.kt new file mode 100644 index 00000000..2ad52bb4 --- /dev/null +++ b/jvm/example/src/main/kotlin/com/chunkzero/window/example/MyLobby.kt @@ -0,0 +1,66 @@ +package com.chunkzero.window.example + +import com.chunkzero.window.FullscreenClick +import com.chunkzero.window.example.generated.LobbyFullscreen +import net.kyori.adventure.text.Component +import net.minestom.server.entity.Player +import kotlin.math.abs + +/** + * The full-screen lobby: a paged world list moved by the arrows or the scroll wheel, a foundry name renamed through a + * native dialog, and a ready flag flipped by right clicking anywhere. + */ +class MyLobby( + player: Player, +) : LobbyFullscreen(player) { + private var name by state("${player.username}'s Foundry") + private var isReady by state(false) + private val worlds = list(cells = WORLD_NAME_SIZE, key = { it: String -> it }, step = 1) { WORLDS } + + override fun foundryName(): Component = fit("foundry_name", Component.text(name)) + + override fun lobbyStatus(): Component = + Component.text(if (isReady) "Ready for ${worlds.selected}" else "${worlds.selected} selected") + + override fun tone(): Tone { + val pinned = pinnedTone(player) + if (pinned != null) return Tone.entries.first { it.value == pinned } + return if (isReady) Tone.OK else Tone.WARN + } + + override fun ready(): Boolean = isReady + + override fun worldName(index: Int): Component = Component.text(worlds.at(index).orEmpty()) + + override fun onPickWorld( + index: Int, + click: FullscreenClick, + ) { + worlds.selectAt(index) + } + + override fun onPreviousWorld(click: FullscreenClick) = worlds.previous() + + override fun onNextWorld(click: FullscreenClick) = worlds.next() + + override fun onScroll(steps: Int) = repeat(abs(steps)) { if (steps > 0) worlds.next() else worlds.previous() } + + override fun onRightClick(click: FullscreenClick) { + isReady = !isReady + } + + /** Asks for a new name in a native dialog; the lobby is back once the player confirms or cancels. */ + override fun onRename(click: FullscreenClick) { + promptText(Component.text("Name your foundry"), name) { text -> + text?.trim()?.takeIf(String::isNotEmpty)?.let { name = it } + } + } + + override fun onPlay(click: FullscreenClick) { + if (!isReady) return + close() + player.sendMessage(Component.text("$name joins ${worlds.selected}.")) + } +} + +private val WORLDS = listOf("Ironvale", "Emberly", "Ashmoor", "Slagport", "Rustwood", "Kilnmere", "Coalcrag") diff --git a/jvm/example/src/main/kotlin/com/chunkzero/window/example/MyLookout.kt b/jvm/example/src/main/kotlin/com/chunkzero/window/example/MyLookout.kt new file mode 100644 index 00000000..70bc86ce --- /dev/null +++ b/jvm/example/src/main/kotlin/com/chunkzero/window/example/MyLookout.kt @@ -0,0 +1,18 @@ +package com.chunkzero.window.example + +import com.chunkzero.window.FullscreenClick +import com.chunkzero.window.example.generated.LookoutFullscreen +import net.minestom.server.entity.Player + +/** The horse-mode lookout: each Charge click fills a quarter of the signal bar, and a full bar starts over. */ +class MyLookout( + player: Player, +) : LookoutFullscreen(player) { + private var charges by state(0) + + override fun signal(): Float = charges / 4f + + override fun onCharge(click: FullscreenClick) { + charges = (charges + 1) % 5 + } +} diff --git a/jvm/example/src/main/kotlin/com/chunkzero/window/example/generated/LobbyFullscreen.kt b/jvm/example/src/main/kotlin/com/chunkzero/window/example/generated/LobbyFullscreen.kt new file mode 100644 index 00000000..c8d099d9 --- /dev/null +++ b/jvm/example/src/main/kotlin/com/chunkzero/window/example/generated/LobbyFullscreen.kt @@ -0,0 +1,83 @@ +// Generated by window-codegen — do not edit. +package com.chunkzero.window.example.generated + +import com.chunkzero.window.FullscreenClick +import com.chunkzero.window.FullscreenScope +import com.chunkzero.window.FullscreenView +import com.chunkzero.window.minestom.MinestomFullscreenHost +import net.kyori.adventure.text.Component +import net.minestom.server.entity.Player + +/** Typed view for the `lobby` full-screen window. Implement the abstract members. */ +public abstract class LobbyFullscreen(protected val player: Player) : FullscreenView(WindowFullscreenDefinitions.lobby, MinestomFullscreenHost(player)) { + /** Render the `foundry_name` text. */ + protected abstract fun foundryName(): Component + + /** Render the `lobby_status` text. */ + protected abstract fun lobbyStatus(): Component + + /** Handle a `next_world` click. */ + protected abstract fun onNextWorld(click: FullscreenClick) + + /** Handle a `pick_world` click; `index` is the clicked entry, row-major. */ + protected abstract fun onPickWorld(index: Int, click: FullscreenClick) + + /** Handle a `play` click. */ + protected abstract fun onPlay(click: FullscreenClick) + + /** Handle a `previous_world` click. */ + protected abstract fun onPreviousWorld(click: FullscreenClick) + + /** The `ready` flag. */ + protected abstract fun ready(): Boolean + + /** Handle a `rename` click. */ + protected abstract fun onRename(click: FullscreenClick) + + /** The values of `tone`. */ + public enum class Tone(public val value: String) { + OK("ok"), + WARN("warn"), + DANGER("danger"), + } + + /** The palette entry the `tone` tint draws in. */ + protected abstract fun tone(): Tone + + /** Render the `world_name` text. */ + protected abstract fun worldName(index: Int): Component + + /** Handle a scroll of the wheel, as the signed steps the hotbar selection moved. */ + protected open fun onScroll(steps: Int) {} + + /** Handle a right click anywhere on the canvas. */ + protected open fun onRightClick(click: FullscreenClick) {} + + final override fun FullscreenScope.bind() { + slot("foundry_name") { foundryName() } + slot("lobby_status") { lobbyStatus() } + button("next_world", ::onNextWorld) + for (index in 0 until PICK_WORLD_SIZE) { + button("pick_world[$index]") { click -> + onPickWorld(index, click) + } + } + button("play", ::onPlay) + button("previous_world", ::onPreviousWorld) + switch("ready") { ready().toString() } + button("rename", ::onRename) + tint("tone") { tone().value } + for (index in 0 until WORLD_NAME_SIZE) { + slot("world_name[$index]") { worldName(index) } + } + scroll(::onScroll) + rightClick(::onRightClick) + } + + public companion object { + /** A shape dimension of `pick_world`. */ + public const val PICK_WORLD_SIZE: Int = 3 + /** A shape dimension of `world_name`. */ + public const val WORLD_NAME_SIZE: Int = 3 + } +} diff --git a/jvm/example/src/main/kotlin/com/chunkzero/window/example/generated/LookoutFullscreen.kt b/jvm/example/src/main/kotlin/com/chunkzero/window/example/generated/LookoutFullscreen.kt new file mode 100644 index 00000000..9ed24de0 --- /dev/null +++ b/jvm/example/src/main/kotlin/com/chunkzero/window/example/generated/LookoutFullscreen.kt @@ -0,0 +1,30 @@ +// Generated by window-codegen — do not edit. +package com.chunkzero.window.example.generated + +import com.chunkzero.window.FullscreenClick +import com.chunkzero.window.FullscreenScope +import com.chunkzero.window.FullscreenView +import com.chunkzero.window.minestom.MinestomFullscreenHost +import net.minestom.server.entity.Player + +/** Typed view for the `lookout` full-screen window. Implement the abstract members. */ +public abstract class LookoutFullscreen(protected val player: Player) : FullscreenView(WindowFullscreenDefinitions.lookout, MinestomFullscreenHost(player)) { + /** Handle a `charge` click. */ + protected abstract fun onCharge(click: FullscreenClick) + + /** How much of the `signal` bar is filled, from 0 to 1. */ + protected abstract fun signal(): Float + + /** Handle a scroll of the wheel, as the signed steps the hotbar selection moved. */ + protected open fun onScroll(steps: Int) {} + + /** Handle a right click anywhere on the canvas. */ + protected open fun onRightClick(click: FullscreenClick) {} + + final override fun FullscreenScope.bind() { + button("charge", ::onCharge) + bar("signal") { signal() } + scroll(::onScroll) + rightClick(::onRightClick) + } +} diff --git a/jvm/example/src/main/kotlin/com/chunkzero/window/example/generated/WindowFonts.kt b/jvm/example/src/main/kotlin/com/chunkzero/window/example/generated/WindowFonts.kt index 0bb5f0e3..793c4b00 100644 --- a/jvm/example/src/main/kotlin/com/chunkzero/window/example/generated/WindowFonts.kt +++ b/jvm/example/src/main/kotlin/com/chunkzero/window/example/generated/WindowFonts.kt @@ -842,5 +842,17 @@ internal object WindowFonts { "window:small_caps/y88" to fontMetrics0, "window:small_caps/y96" to fontMetrics0, "window:small_caps/ym1" to fontMetrics0, + "window:y113" to vanillaMetrics, + "window:y116" to vanillaMetrics, + "window:y136" to vanillaMetrics, + "window:y144" to vanillaMetrics, + "window:y154" to vanillaMetrics, + "window:y156" to vanillaMetrics, + "window:y162" to vanillaMetrics, + "window:y176" to vanillaMetrics, + "window:y195" to vanillaMetrics, + "window:y207" to vanillaMetrics, + "window:y80" to vanillaMetrics, + "window:y98" to vanillaMetrics, ) } diff --git a/jvm/example/src/main/kotlin/com/chunkzero/window/example/generated/WindowFullscreenDefinitions.kt b/jvm/example/src/main/kotlin/com/chunkzero/window/example/generated/WindowFullscreenDefinitions.kt new file mode 100644 index 00000000..45ae4b0e --- /dev/null +++ b/jvm/example/src/main/kotlin/com/chunkzero/window/example/generated/WindowFullscreenDefinitions.kt @@ -0,0 +1,10 @@ +// Generated by window-codegen — do not edit. +package com.chunkzero.window.example.generated + +import com.chunkzero.window.FullscreenDefinition + +/** Typed full-screen window definitions of this Window pack. */ +public object WindowFullscreenDefinitions { + public val lobby: FullscreenDefinition = FullscreenDefinition(WindowPackData.manifest, "lobby") + public val lookout: FullscreenDefinition = FullscreenDefinition(WindowPackData.manifest, "lookout") +} diff --git a/jvm/example/src/main/kotlin/com/chunkzero/window/example/generated/WindowFullscreenEntries.kt b/jvm/example/src/main/kotlin/com/chunkzero/window/example/generated/WindowFullscreenEntries.kt new file mode 100644 index 00000000..c9516ddb --- /dev/null +++ b/jvm/example/src/main/kotlin/com/chunkzero/window/example/generated/WindowFullscreenEntries.kt @@ -0,0 +1,717 @@ +// Generated by window-codegen — do not edit. +package com.chunkzero.window.example.generated + +import com.chunkzero.window.manifest.Align +import com.chunkzero.window.manifest.ArtEntry +import com.chunkzero.window.manifest.BarEntry +import com.chunkzero.window.manifest.ControlEntry +import com.chunkzero.window.manifest.ControlTooltipEntry +import com.chunkzero.window.manifest.CursorEntry +import com.chunkzero.window.manifest.FullscreenCanvasEntry +import com.chunkzero.window.manifest.FullscreenEntry +import com.chunkzero.window.manifest.FullscreenMode +import com.chunkzero.window.manifest.FullscreenSurfaceEntry +import com.chunkzero.window.manifest.HorseEntry +import com.chunkzero.window.manifest.LayerEntry +import com.chunkzero.window.manifest.LayerKind +import com.chunkzero.window.manifest.SlotEntry +import com.chunkzero.window.manifest.SwitchCaseEntry +import com.chunkzero.window.manifest.SwitchEntry + +/** Compiled full-screen entries and canvas contract of this Window pack. */ +internal object WindowFullscreenEntries { + val all: Map = + mapOf( + "lobby" to FullscreenEntry( + surface = FullscreenSurfaceEntry( + kind = "fullscreen", + mode = FullscreenMode.UNLOCKED, + size = listOf(480, 270), + ), + static = "\uDB80\uDC11\uDB80\uDC10\uDB80\uDC0F\uDB80\uDC0E\uDB80\uDC0D\uF7CD\uDB80\uDC02\uDB80\uDC04\uDB80\uDC05\uDB80\uDC08\uDB80\uDC0A", + staticMarker = "#a00400", + slots = mapOf( + "foundry_name" to SlotEntry( + x = 165, + y = 98, + width = 150, + align = Align.CENTER, + font = "window:y98", + color = "#ffe2b8", + shadow = false, + bold = false, + italic = false, + underlined = false, + strikethrough = false, + obfuscated = false, + text = null, + shaderMarker = null, + shaderColor = null, + ), + "label_0" to SlotEntry( + x = 203, + y = 80, + width = 75, + align = Align.LEFT, + font = "window:y80", + color = "#ffe2b8", + shadow = false, + bold = false, + italic = false, + underlined = false, + strikethrough = false, + obfuscated = false, + text = "Foundry Lobby", + shaderMarker = null, + shaderColor = null, + ), + "label_1" to SlotEntry( + x = 146, + y = 136, + width = 4, + align = Align.CENTER, + font = "window:y136", + color = "#ffffff", + shadow = false, + bold = false, + italic = false, + underlined = false, + strikethrough = false, + obfuscated = false, + text = "<", + shaderMarker = null, + shaderColor = null, + ), + "label_2" to SlotEntry( + x = 330, + y = 136, + width = 4, + align = Align.CENTER, + font = "window:y136", + color = "#ffffff", + shadow = false, + bold = false, + italic = false, + underlined = false, + strikethrough = false, + obfuscated = false, + text = ">", + shaderMarker = null, + shaderColor = null, + ), + "label_3" to SlotEntry( + x = 196, + y = 156, + width = 29, + align = Align.LEFT, + font = "window:y156", + color = "#3ddc84", + shadow = false, + bold = false, + italic = false, + underlined = false, + strikethrough = false, + obfuscated = false, + text = "Ready", + shaderMarker = null, + shaderColor = null, + ), + "label_4" to SlotEntry( + x = 185, + y = 156, + width = 111, + align = Align.LEFT, + font = "window:y156", + color = "#f2c9a0", + shadow = false, + bold = false, + italic = false, + underlined = false, + strikethrough = false, + obfuscated = false, + text = "Right click when ready", + shaderMarker = null, + shaderColor = null, + ), + "label_5" to SlotEntry( + x = 197, + y = 176, + width = 20, + align = Align.CENTER, + font = "window:y176", + color = "#ffffff", + shadow = false, + bold = false, + italic = false, + underlined = false, + strikethrough = false, + obfuscated = false, + text = "Play", + shaderMarker = null, + shaderColor = null, + ), + "label_6" to SlotEntry( + x = 265, + y = 176, + width = 17, + align = Align.CENTER, + font = "window:y176", + color = "#ffffff", + shadow = false, + bold = false, + italic = false, + underlined = false, + strikethrough = false, + obfuscated = false, + text = "Quit", + shaderMarker = null, + shaderColor = null, + ), + "lobby_status" to SlotEntry( + x = 132, + y = 116, + width = 216, + align = Align.CENTER, + font = "window:y116", + color = "#3ddc84", + shadow = false, + bold = false, + italic = false, + underlined = false, + strikethrough = false, + obfuscated = false, + text = null, + shaderMarker = null, + shaderColor = null, + tint = "tone", + tintMarkers = mapOf( + "danger" to "#a01004", + "ok" to "#a01002", + "warn" to "#a01003", + ), + ), + "world_name[0]" to SlotEntry( + x = 162, + y = 136, + width = 48, + align = Align.CENTER, + font = "window:y136", + color = "#ffffff", + shadow = false, + bold = false, + italic = false, + underlined = false, + strikethrough = false, + obfuscated = false, + text = null, + shaderMarker = null, + shaderColor = null, + ), + "world_name[1]" to SlotEntry( + x = 216, + y = 136, + width = 48, + align = Align.CENTER, + font = "window:y136", + color = "#ffffff", + shadow = false, + bold = false, + italic = false, + underlined = false, + strikethrough = false, + obfuscated = false, + text = null, + shaderMarker = null, + shaderColor = null, + ), + "world_name[2]" to SlotEntry( + x = 270, + y = 136, + width = 48, + align = Align.CENTER, + font = "window:y136", + color = "#ffffff", + shadow = false, + bold = false, + italic = false, + underlined = false, + strikethrough = false, + obfuscated = false, + text = null, + shaderMarker = null, + shaderColor = null, + ), + ), + controls = mapOf( + "next_world" to ControlEntry( + index = 5, + x = 324, + y = 131, + width = 16, + height = 18, + action = "next_world", + defaultAction = null, + hover = true, + outline = "\uDB80\uDC13\uDB80\uDC11\uDB80\uDC0D\uE3F4\uDB80\uDC02\uDB80\uDC04\uDB80\uDC06\uDB80\uDC08\uDB80\uDC0A", + source = "region `next_world`", + ), + "pick_world[0]" to ControlEntry( + index = 2, + x = 160, + y = 132, + width = 52, + height = 16, + action = "pick_world[0]", + defaultAction = null, + hover = true, + outline = "\uDB80\uDC12\uDB80\uDC10\uED3C\uDB80\uDC03\uDB80\uDC04\uDB80\uDC06\uDB80\uDC08\uDB80\uDC0A", + tooltip = ControlTooltipEntry( + panel = "\uDB80\uDC12\uDB80\uDC0F\uDB80\uDC0C\uDB80\uDC0B\uED20\uDB80\uDC03\uDB80\uDC04\uDB80\uDC05\uDB80\uDC09", + marker = "#a3f000", + lines = listOf( + SlotEntry( + x = 151, + y = 154, + width = 70, + align = Align.LEFT, + font = "window:y154", + color = "#ffffff", + shadow = false, + bold = false, + italic = false, + underlined = false, + strikethrough = false, + obfuscated = false, + text = "Click to select", + shaderMarker = "#a3f400", + shaderColor = null, + source = "region `pick_world[0]`", + ), + ), + ), + source = "region `pick_world[0]`", + ), + "pick_world[1]" to ControlEntry( + index = 3, + x = 214, + y = 132, + width = 52, + height = 16, + action = "pick_world[1]", + defaultAction = null, + hover = true, + outline = "\uDB80\uDC12\uDB80\uDC11\uDB80\uDC0F\uDB80\uDC0D\uDB80\uDC0C\uF465\uDB80\uDC02\uDB80\uDC07\uDB80\uDC09\uDB80\uDC0A", + tooltip = ControlTooltipEntry( + panel = "\uDB80\uDC12\uDB80\uDC11\uDB80\uDC0E\uDB80\uDC0B\uF030\uDB80\uDC02\uDB80\uDC06\uDB80\uDC07", + marker = "#a3f000", + lines = listOf( + SlotEntry( + x = 205, + y = 154, + width = 70, + align = Align.LEFT, + font = "window:y154", + color = "#ffffff", + shadow = false, + bold = false, + italic = false, + underlined = false, + strikethrough = false, + obfuscated = false, + text = "Click to select", + shaderMarker = "#a3f400", + shaderColor = null, + source = "region `pick_world[1]`", + ), + ), + ), + source = "region `pick_world[1]`", + ), + "pick_world[2]" to ControlEntry( + index = 4, + x = 268, + y = 132, + width = 52, + height = 16, + action = "pick_world[2]", + defaultAction = null, + hover = true, + outline = "\uDB80\uDC13\uDB80\uDC0E\uDB80\uDC0D\uE3AB\uDB80\uDC02\uDB80\uDC04\uDB80\uDC0A", + tooltip = ControlTooltipEntry( + panel = "\uDB80\uDC12\uDB80\uDC11\uDB80\uDC10\uDB80\uDC0F\uDB80\uDC0E\uDB80\uDC0D\uDB80\uDC0C\uDB80\uDC0B\uECBA\uDB80\uDC02\uDB80\uDC04\uDB80\uDC07\uDB80\uDC08\uDB80\uDC09", + marker = "#a3f000", + lines = listOf( + SlotEntry( + x = 259, + y = 154, + width = 70, + align = Align.LEFT, + font = "window:y154", + color = "#ffffff", + shadow = false, + bold = false, + italic = false, + underlined = false, + strikethrough = false, + obfuscated = false, + text = "Click to select", + shaderMarker = "#a3f400", + shaderColor = null, + source = "region `pick_world[2]`", + ), + ), + ), + source = "region `pick_world[2]`", + ), + "play" to ControlEntry( + index = 6, + x = 177, + y = 171, + width = 60, + height = 18, + action = "play", + defaultAction = null, + hover = true, + outline = "\uDB80\uDC12\uDB80\uDC10\uDB80\uDC0F\uDB80\uDC0B\uE224\uDB80\uDC03\uDB80\uDC04\uDB80\uDC05\uDB80\uDC07\uDB80\uDC08\uDB80\uDC09", + tooltip = ControlTooltipEntry( + panel = "\uDB80\uDC11\uDB80\uDC0F\uDB80\uDC0E\uDB80\uDC0B\uE157\uDB80\uDC02\uDB80\uDC04\uDB80\uDC08\uDB80\uDC09\uDB80\uDC0A", + marker = "#a3f000", + lines = listOf( + SlotEntry( + x = 93, + y = 195, + width = 20, + align = Align.LEFT, + font = "window:y195", + color = "#ffffff", + shadow = false, + bold = false, + italic = false, + underlined = false, + strikethrough = false, + obfuscated = false, + text = "Play", + shaderMarker = "#a3f400", + shaderColor = null, + source = "region `play`", + ), + SlotEntry( + x = 93, + y = 207, + width = 229, + align = Align.LEFT, + font = "window:y207", + color = "#aaaaaa", + shadow = false, + bold = false, + italic = false, + underlined = false, + strikethrough = false, + obfuscated = false, + text = "Joins the selected world once you are ready", + shaderMarker = "#a3f406", + shaderColor = null, + source = "region `play`", + ), + ), + ), + source = "region `play`", + ), + "previous_world" to ControlEntry( + index = 1, + x = 140, + y = 131, + width = 16, + height = 18, + action = "previous_world", + defaultAction = null, + hover = true, + outline = "\uDB80\uDC12\uDB80\uDC0E\uDB80\uDC0D\uF27E\uDB80\uDC03\uDB80\uDC06\uDB80\uDC07\uDB80\uDC08\uDB80\uDC0A", + source = "region `previous_world`", + ), + "rename" to ControlEntry( + index = 0, + x = 160, + y = 94, + width = 160, + height = 16, + action = "rename", + defaultAction = null, + hover = true, + outline = "\uDB80\uDC12\uDB80\uDC10\uE911\uDB80\uDC02\uDB80\uDC04\uDB80\uDC0A", + tooltip = ControlTooltipEntry( + panel = "\uDB80\uDC12\uDB80\uDC11\uDB80\uDC0D\uDB80\uDC0C\uEEC4\uDB80\uDC02\uDB80\uDC06\uDB80\uDC07\uDB80\uDC09", + marker = "#a3f000", + lines = listOf( + SlotEntry( + x = 202, + y = 116, + width = 75, + align = Align.LEFT, + font = "window:y116", + color = "#ffffff", + shadow = false, + bold = false, + italic = false, + underlined = false, + strikethrough = false, + obfuscated = false, + text = "Click to rename", + shaderMarker = "#a3f400", + shaderColor = null, + source = "region `rename`", + ), + ), + ), + source = "region `rename`", + ), + "window:close" to ControlEntry( + index = 7, + x = 243, + y = 171, + width = 60, + height = 18, + action = "window:close", + defaultAction = "window:close", + hover = true, + outline = "\uDB80\uDC12\uDB80\uDC11\uDB80\uDC10\uDB80\uDC0F\uDB80\uDC0C\uDB80\uDC0B\uF59E\uDB80\uDC02\uDB80\uDC05\uDB80\uDC06", + source = "region `window:close`", + ), + ), + switches = mapOf( + "ready" to SwitchEntry( + cases = listOf( + SwitchCaseEntry( + value = "true", + static = "\uDB80\uDC12\uDB80\uDC10\uDB80\uDC0F\uDB80\uDC0E\uDB80\uDC0B\uF0CB\uDB80\uDC03\uDB80\uDC04\uDB80\uDC09\uDB80\uDC0A", + slots = listOf("label_3"), + spriteSlots = emptyList(), + regions = emptyList(), + switches = emptyList(), + ), + SwitchCaseEntry( + value = "false", + static = "", + slots = listOf("label_4"), + spriteSlots = emptyList(), + regions = emptyList(), + switches = emptyList(), + ), + ), + source = null, + ), + ), + tints = mapOf( + "tone" to mapOf( + "danger" to "#ff4d4d", + "ok" to "#3ddc84", + "warn" to "#ffb020", + ), + ), + layers = listOf( + LayerEntry(LayerKind.SLOT, "label_0"), + LayerEntry(LayerKind.SLOT, "foundry_name"), + LayerEntry(LayerKind.SLOT, "lobby_status"), + LayerEntry(LayerKind.SLOT, "label_1"), + LayerEntry(LayerKind.SLOT, "world_name[0]"), + LayerEntry(LayerKind.SLOT, "world_name[1]"), + LayerEntry(LayerKind.SLOT, "world_name[2]"), + LayerEntry(LayerKind.SLOT, "label_2"), + LayerEntry(LayerKind.SWITCH, "ready"), + LayerEntry(LayerKind.SLOT, "label_3"), + LayerEntry(LayerKind.SLOT, "label_4"), + LayerEntry(LayerKind.SLOT, "label_5"), + LayerEntry(LayerKind.SLOT, "label_6"), + ), + markers = mapOf( + "foundry_name" to "#a00c01", + "label_0" to "#a00801", + "label_1" to "#a01400", + "label_2" to "#a02400", + "label_3" to "#a02c02", + "label_4" to "#a03005", + "label_5" to "#a03400", + "label_6" to "#a03800", + "lobby_status" to "#a01002", + "ready" to "#a02800", + "world_name[0]" to "#a01800", + "world_name[1]" to "#a01c00", + "world_name[2]" to "#a02000", + ), + backdropMarker = "#a80007", + ), + "lookout" to FullscreenEntry( + surface = FullscreenSurfaceEntry( + kind = "fullscreen", + mode = FullscreenMode.HORSE, + size = listOf(480, 270), + ), + static = "\uDB80\uDC12\uDB80\uDC10\uE905\uDB80\uDC02\uDB80\uDC04\uDB80\uDC0A", + staticMarker = "#a00400", + slots = mapOf( + "label_0" to SlotEntry( + x = 221, + y = 113, + width = 38, + align = Align.LEFT, + font = "window:y113", + color = "#ffe2b8", + shadow = false, + bold = false, + italic = false, + underlined = false, + strikethrough = false, + obfuscated = false, + text = "Lookout", + shaderMarker = null, + shaderColor = null, + ), + "label_1" to SlotEntry( + x = 191, + y = 144, + width = 35, + align = Align.CENTER, + font = "window:y144", + color = "#ffffff", + shadow = false, + bold = false, + italic = false, + underlined = false, + strikethrough = false, + obfuscated = false, + text = "Charge", + shaderMarker = null, + shaderColor = null, + ), + "label_2" to SlotEntry( + x = 258, + y = 144, + width = 29, + align = Align.CENTER, + font = "window:y144", + color = "#ffffff", + shadow = false, + bold = false, + italic = false, + underlined = false, + strikethrough = false, + obfuscated = false, + text = "Leave", + shaderMarker = null, + shaderColor = null, + ), + ), + controls = mapOf( + "charge" to ControlEntry( + index = 8, + x = 179, + y = 140, + width = 58, + height = 16, + action = "charge", + defaultAction = null, + hover = true, + outline = "\uDB80\uDC12\uDB80\uDC10\uDB80\uDC0F\uDB80\uDC0C\uDB80\uDC0B\uEFAF\uDB80\uDC03\uDB80\uDC04\uDB80\uDC05\uDB80\uDC07\uDB80\uDC08\uDB80\uDC09", + tooltip = ControlTooltipEntry( + panel = "\uDB80\uDC12\uDB80\uDC0F\uDB80\uDC0E\uDB80\uDC0D\uDB80\uDC0B\uF545\uDB80\uDC02\uDB80\uDC08", + marker = "#a3f000", + lines = listOf( + SlotEntry( + x = 161, + y = 162, + width = 94, + align = Align.LEFT, + font = "window:y162", + color = "#ffffff", + shadow = false, + bold = false, + italic = false, + underlined = false, + strikethrough = false, + obfuscated = false, + text = "Charges the signal", + shaderMarker = "#a3f400", + shaderColor = null, + source = "region `charge`", + ), + ), + ), + source = "region `charge`", + ), + "window:close" to ControlEntry( + index = 9, + x = 243, + y = 140, + width = 58, + height = 16, + action = "window:close", + defaultAction = "window:close", + hover = true, + outline = "\uDB80\uDC12\uDB80\uDC11\uDB80\uDC10\uDB80\uDC0F\uDB80\uDC0C\uDB80\uDC0B\uE09E\uDB80\uDC02\uDB80\uDC05\uDB80\uDC07\uDB80\uDC08\uDB80\uDC09", + source = "region `window:close`", + ), + ), + art = mapOf( + "art/0" to ArtEntry( + static = "\uDB80\uDC12\uDB80\uDC10\uDB80\uDC0F\uDB80\uDC0D\uEBE7\uDB80\uDC0A\uEF82\uDB80\uDC0A\uE686\uDB80\uDC0A\uE6CD\uDB80\uDC02\uDB80\uDC05\uDB80\uDC07\uDB80\uDC08\uDB80\uDC0A", + bar = BarEntry( + name = "signal", + x = 180, + width = 120, + levels = listOf("\uEDDD\uEDDD\uEDDD\uEDDD\uEDDD\uEDDD\uEDDD\uEDDD\uEDDD\uEDDD\uEDDD\uEDDD\uEDDD\uEDDD\uEDDD\uEDDD\uEDDD\uEDDD\uEDDD\uEDDD\uEDDD\uEDDD\uEDDD\uEDDD\uEDDD\uEDDD\uEDDD\uEDDD\uEDDD\uEDDD\uEDDD\uEDDD\uEDDD\uEDDD\uEDDD\uEDDD\uEDDD\uEDDD\uEDDD\uEDDD\uEDDD\uEDDD\uEDDD\uEDDD\uEDDD\uEDDD\uEDDD\uEDDD\uEDDD\uEDDD\uEDDD\uEDDD\uEDDD\uEDDD\uEDDD\uEDDD\uEDDD\uEDDD\uEDDD\uEDDD", "\uE548\uE548\uE548\uE548\uE548\uE548\uE548\uE548\uE548\uE548\uE548\uE548\uE548\uE548\uE548\uE548\uE548\uE548\uE548\uE548\uE548\uE548\uE548\uE548\uE548\uE548\uE548\uE548\uE548\uE548", "\uF819\uF819\uF819\uF819\uF819\uF819\uF819\uF819\uF819\uF819\uF819\uF819\uF819\uF819\uF819", "\uE6CD\uE6CD\uE6CD\uE6CD\uE6CD\uE6CD\uE6CD\uE6CD", "\uE686\uE686\uE686\uE686", "\uEF82\uEF82", "\uEBE7"), + advances = mapOf( + "\uE548" to 3, + "\uE686" to 17, + "\uE6CD" to 9, + "\uEBE7" to 65, + "\uEDDD" to 2, + "\uEF82" to 33, + "\uF819" to 5, + ), + ), + ), + ), + layers = listOf( + LayerEntry(LayerKind.SLOT, "label_0"), + LayerEntry(LayerKind.ART, "art/0"), + LayerEntry(LayerKind.SLOT, "label_1"), + LayerEntry(LayerKind.SLOT, "label_2"), + ), + markers = mapOf( + "art/0" to "#a00c00", + "label_0" to "#a00801", + "label_1" to "#a01000", + "label_2" to "#a01400", + ), + backdropMarker = "#a80008", + ), + ) + val canvas: FullscreenCanvasEntry = + FullscreenCanvasEntry( + width = 480, + height = 270, + degreesPerPixel = 0.1875, + edgeYaw = 45.0, + edgePitch = 25.3125, + snapEpsilon = 0.01, + hitMargin = 1, + displayScale = 256.0, + markerAlpha = 253, + cursor = CursorEntry( + text = "\uE0EB\uDB80\uDC07\uDB80\uDC0A", + marker = "#a7fc00", + width = 8, + height = 12, + ), + backdrop = "\uE6AF\uDB80\uDC07\uDB80\uDC0A", + overlayMarker = "#a3f800", + hudHiddenWorldAge = -1000000001L, + hudStripHeight = 56, + hudStripHalfWidth = 96, + hudMaxHealth = 20, + horse = HorseEntry( + offset = listOf(0.0, -1.75, 3.0), + armor = "window:fullscreen/horse_cursor", + ), + ) +} diff --git a/jvm/example/src/main/kotlin/com/chunkzero/window/example/generated/WindowPackData.kt b/jvm/example/src/main/kotlin/com/chunkzero/window/example/generated/WindowPackData.kt index 18f81b7a..27985440 100644 --- a/jvm/example/src/main/kotlin/com/chunkzero/window/example/generated/WindowPackData.kt +++ b/jvm/example/src/main/kotlin/com/chunkzero/window/example/generated/WindowPackData.kt @@ -17,5 +17,7 @@ internal object WindowPackData { sprites = WindowSprites.all, windows = WindowEntries.all, huds = WindowHudEntries.all, + fullscreens = WindowFullscreenEntries.all, + fullscreenCanvas = WindowFullscreenEntries.canvas, ) }