diff --git a/.gitignore b/.gitignore index 95fdc4f..e84029e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ .direnv/ -result/ +result build/ .vscode/ diff --git a/CMakeLists.txt b/CMakeLists.txt index f4c0ec8..669068a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.16) -project(rmlui-template LANGUAGES CXX) +project(sessionzero LANGUAGES CXX) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) diff --git a/README.md b/README.md index 08f46e5..39e4bab 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,2 @@ -# RmlUi Application Project Template (Nix Flake) - -This is a template project for an RmlUi application, with Nix support. - -## Template features: -- Enabled plugins: SVG, Lua -- Hot reload RML/RCSS/Lua with F5 key -- Customizable "shell" rml with inner page loading and page state tracking -- application state manager -- Basic stylesheet for common html/rml controls, with a user-agent styleheet that takes priority -- Easily defined custom rml controls -- GLFW/GL3 backend, but all premade backends are included in the vendor folder -- Nix Flake included with a dev shell env and `nix build` capabilities +0 wrapped binary - -Other libraries included: -- Raylib - -Nix Flake features: -- Dev shell - - LSP for C++ and Lua -- Building & running -- RmlUi pinned to v6.2 \ No newline at end of file +# SessionZero +A no bs TTRPG companion toolkit diff --git a/assets/fonts/PTSerif-Regular.ttf b/assets/fonts/PTSerif-Regular.ttf new file mode 100644 index 0000000..f87c0f1 Binary files /dev/null and b/assets/fonts/PTSerif-Regular.ttf differ diff --git a/assets/images/icon.svg b/assets/images/icon.svg new file mode 100755 index 0000000..3534372 --- /dev/null +++ b/assets/images/icon.svg @@ -0,0 +1,53 @@ + + + + diff --git a/assets/images/logo-medium.png b/assets/images/logo-medium.png new file mode 100644 index 0000000..7945baa Binary files /dev/null and b/assets/images/logo-medium.png differ diff --git a/assets/main.rml b/assets/main.rml index 6983ece..7b2c7b5 100644 --- a/assets/main.rml +++ b/assets/main.rml @@ -1,15 +1,14 @@ - RmlUi Temaplate + SessionZero
diff --git a/assets/pages/home.rml b/assets/pages/home.rml index 032f7eb..2e733ea 100644 --- a/assets/pages/home.rml +++ b/assets/pages/home.rml @@ -1,14 +1,3 @@

Home

-

This is an RmlUi template project with some nice built in features such as:

-
\ No newline at end of file diff --git a/assets/pages/lua-test.lua b/assets/pages/lua-test.lua deleted file mode 100644 index 8320eaa..0000000 --- a/assets/pages/lua-test.lua +++ /dev/null @@ -1,17 +0,0 @@ -LuaTest = {} - -local counter = 0 - -function LuaTest.Test(event) - counter = counter + 1 - - local document = event.current_element.owner_document - - local target = document:GetElementById("lua-counter") - if target then - target.inner_rml = "Lua Counter: " .. counter - end - - print("Lua counter incremented to: " .. counter) - -end \ No newline at end of file diff --git a/assets/pages/lua-test.rcss b/assets/pages/lua-test.rcss deleted file mode 100644 index e69de29..0000000 diff --git a/assets/pages/lua-test.rml b/assets/pages/lua-test.rml deleted file mode 100644 index a89e916..0000000 --- a/assets/pages/lua-test.rml +++ /dev/null @@ -1,11 +0,0 @@ -
-

Lua test page

-

This button uses NavigateTo('about') lua code instead of being harcoded in a C++ PageBase class:

- - -

If you have a .lua file with the same name as the page in the pages directory, it will be loaded at runtime so you can add functionality to a page like this:

- -

This button calls LuaTest.Test() from the scoped `lua-test.lua` file

- -

-
diff --git a/assets/pages/test-page.lua b/assets/pages/test-page.lua deleted file mode 100644 index bda0ae6..0000000 --- a/assets/pages/test-page.lua +++ /dev/null @@ -1,28 +0,0 @@ -TestPage = {} - -local maxHp = 100 -local currentHp = 100 - -function TestPage.Damage(event) - - currentHp = currentHp - 5 - - local document = event.current_element.owner_document - local target = document:GetElementById("hp-bar") - - if target then - target:SetAttribute("current", currentHp) - end - -end - -function TestPage.Heal(event) - currentHp = currentHp + 5 - - local document = event.current_element.owner_document - local target = document:GetElementById("hp-bar") - - if target then - target:SetAttribute("current", currentHp) - end -end \ No newline at end of file diff --git a/assets/pages/test-page.rcss b/assets/pages/test-page.rcss deleted file mode 100644 index ed280c9..0000000 --- a/assets/pages/test-page.rcss +++ /dev/null @@ -1,17 +0,0 @@ -#page-root h1 { - color: red; -} - -.healthbar-track { - display: block; - width: 200px; - height: 16px; - background-color: #3c3c3c; - border: 1px #666666; -} - -.healthbar-fill { - display: block; - height: 100%; - background-color: #ffffff; -} \ No newline at end of file diff --git a/assets/pages/test-page.rml b/assets/pages/test-page.rml deleted file mode 100644 index 5e19a48..0000000 --- a/assets/pages/test-page.rml +++ /dev/null @@ -1,13 +0,0 @@ -
-

Test page

-

{{greeting}}

- - -

If you have a .rcss file with the same name as the page in the pages directory, it will be loaded at runtime so you can add custom styling to a page, which is why the header is red on this page only.

- -

This next section demonstates two things, the custom "healthbar" rml element, and scoped lua with the damage and heal buttons

- - - - -
\ No newline at end of file diff --git a/assets/styles/main.rcss b/assets/styles/main.rcss index bf1f080..fb2844b 100644 --- a/assets/styles/main.rcss +++ b/assets/styles/main.rcss @@ -1,25 +1,33 @@ -/* ========================================================================== - RCSS User Agent Defaults (Plain HTML Style) - ========================================================================== */ +* { + box-sizing: border-box; + font-family: "PT Serif"; +} -/* Core Application Window Foundation */ body { - display: block; + --background-color: #1a202e; /* Deep navy night */ + --primary-color: #3a4f6f; /* Rich medium blue */ + --secondary-color: #2d3f5c; /* Deeper blue */ + --accent-color: #5b89b3; /* Glowing blue highlight */ + --text-color: #ffffff; /* Pure white */ + --heading-color: #c5d5e6; /* Soft sky blue */ + + --form-background: #222837; /* Slightly lighter background */ + --text-on-primary: #ffffff; /* White text */ + --button-text: #ffffff; /* White text */ + + --neutral-light: #d8e1ea; /* Bright blue-white */ + --neutral-medium: #8494a7; /* Muted blue-gray */ + --neutral-dark: #2d364a; /* Deep blue shadow */ + + font-size: 24px; + color: var(--text-color); + background-color: var(--background-color); width: 100vw; height: 100vh; - font-family: "rmlui-debugger-font"; - font-size: 16px; - background-color: #ffffff; - color: #000000; - margin: 0px; - padding: 0px; + } /* Generic Structural Layout Defaults */ -* { - box-sizing: border-box; -} - div, section, article, header, footer, main, nav, aside { display: block; } @@ -38,6 +46,7 @@ h1, h2, h3, h4, h5, h6 { display: block; font-weight: bold; margin: 0.83em 0px; + color: var(--heading-color); } h1 { font-size: 2em; } @@ -137,7 +146,7 @@ thead, tbody, tfoot { ========================================================================== */ button, input, select, textarea { display: inline-block; - font-family: "rmlui-debugger-font"; + /* font-family: "rmlui-debugger-font"; */ font-size: 14px; margin: 2px; vertical-align: middle; @@ -267,7 +276,9 @@ progress-value { } - +/* ========================================================================== + Shell + ========================================================================== */ #navbar { @@ -277,13 +288,14 @@ progress-value { align-items: center; width: 100%; - height: 60px; + height: 70px; padding: 0px 20px; - background-color: #2b2baa; + background-color: transparent; + box-shadow: #000a 0px 6px 8px; } #navbar h1 { - color: #ffffff; + color: var(--heading-color); font-size: 24px; margin: 0px; } @@ -296,19 +308,41 @@ progress-value { #navbar button { padding: 8px 16px; - background-color: #3c3c3c; - color: #e0e0e0; - border: 1px #666666; } -#navbar button:hover { - background-color: #55555a; +#navbar svg.logo { + width: 50px; + height: 50px; } -/* Page Content Base */ + #page-content { display: block; - padding: 20px; - background-color: #1a1a1a; - color: #f0f0f0; + padding: 2rem; + background-color: var(--background-color); + color: var(--text-color); + margin-top: 15px; } + +/* Custom controls */ +button.btn-primary { + transition: background-color 0.2s exponential-in-out; + display: inline-block; + min-width: 100px; + min-height: 35px; + padding: 0.75rem 1.5rem; + text-decoration: none; + background-color: var(--primary-color); + border-radius: 4px; + border-color: transparent; + + color: var(--text-color); + font-size: 16px; +} button.btn-primary:hover { + background-color: var(--secondary-color); + cursor: pointer; +} +/* button.btn-primary:active { + background-color: #3e8e41; + border-color: #245325; +} */ diff --git a/compile_commands.json b/compile_commands.json deleted file mode 120000 index 25eb4b2..0000000 --- a/compile_commands.json +++ /dev/null @@ -1 +0,0 @@ -build/compile_commands.json \ No newline at end of file diff --git a/compile_commands.json b/compile_commands.json new file mode 100644 index 0000000..1a80dfc --- /dev/null +++ b/compile_commands.json @@ -0,0 +1,56 @@ +[ +{ + "directory": "/data/Projects/bellsworne/sessionzero/sz", + "command": "/nix/store/xcnqqnhw9hb4j5rjgds2yjryi8qki5f3-gcc-wrapper-15.2.0/bin/g++ -std=gnu++17 -o CMakeFiles/rmlui_backend.dir/vendor/rmlui_backend/RmlUi_Backend_GLFW_GL3.cpp.o -c /data/Projects/bellsworne/sessionzero/sz/vendor/rmlui_backend/RmlUi_Backend_GLFW_GL3.cpp", + "file": "/data/Projects/bellsworne/sessionzero/sz/vendor/rmlui_backend/RmlUi_Backend_GLFW_GL3.cpp", + "output": "CMakeFiles/rmlui_backend.dir/vendor/rmlui_backend/RmlUi_Backend_GLFW_GL3.cpp.o" +}, +{ + "directory": "/data/Projects/bellsworne/sessionzero/sz", + "command": "/nix/store/xcnqqnhw9hb4j5rjgds2yjryi8qki5f3-gcc-wrapper-15.2.0/bin/g++ -std=gnu++17 -o CMakeFiles/rmlui_backend.dir/vendor/rmlui_backend/RmlUi_Platform_GLFW.cpp.o -c /data/Projects/bellsworne/sessionzero/sz/vendor/rmlui_backend/RmlUi_Platform_GLFW.cpp", + "file": "/data/Projects/bellsworne/sessionzero/sz/vendor/rmlui_backend/RmlUi_Platform_GLFW.cpp", + "output": "CMakeFiles/rmlui_backend.dir/vendor/rmlui_backend/RmlUi_Platform_GLFW.cpp.o" +}, +{ + "directory": "/data/Projects/bellsworne/sessionzero/sz", + "command": "/nix/store/xcnqqnhw9hb4j5rjgds2yjryi8qki5f3-gcc-wrapper-15.2.0/bin/g++ -std=gnu++17 -o CMakeFiles/rmlui_backend.dir/vendor/rmlui_backend/RmlUi_Renderer_GL3.cpp.o -c /data/Projects/bellsworne/sessionzero/sz/vendor/rmlui_backend/RmlUi_Renderer_GL3.cpp", + "file": "/data/Projects/bellsworne/sessionzero/sz/vendor/rmlui_backend/RmlUi_Renderer_GL3.cpp", + "output": "CMakeFiles/rmlui_backend.dir/vendor/rmlui_backend/RmlUi_Renderer_GL3.cpp.o" +}, +{ + "directory": "/data/Projects/bellsworne/sessionzero/sz", + "command": "/nix/store/xcnqqnhw9hb4j5rjgds2yjryi8qki5f3-gcc-wrapper-15.2.0/bin/g++ -DASSETS_DIR=\\\"/data/Projects/bellsworne/sessionzero/sz/bin/assets\\\" -I/data/Projects/bellsworne/sessionzero/sz/src -I/data/Projects/bellsworne/sessionzero/sz/src/elements -I/data/Projects/bellsworne/sessionzero/sz/vendor/rmlui_backend -isystem /nix/store/nim1mngh64k267l8s1bpbr2ckfhvgrcn-freetype-2.14.3-dev/include/freetype2 -isystem /nix/store/nmydn4w4zjqkk4vwhh0qa76s1c6wg8hc-lunasvg-3.5.0/lib/pkgconfig/../../include/lunasvg -std=gnu++17 -o CMakeFiles/rmlui-template.dir/src/main.cpp.o -c /data/Projects/bellsworne/sessionzero/sz/src/main.cpp", + "file": "/data/Projects/bellsworne/sessionzero/sz/src/main.cpp", + "output": "CMakeFiles/rmlui-template.dir/src/main.cpp.o" +}, +{ + "directory": "/data/Projects/bellsworne/sessionzero/sz", + "command": "/nix/store/xcnqqnhw9hb4j5rjgds2yjryi8qki5f3-gcc-wrapper-15.2.0/bin/g++ -DASSETS_DIR=\\\"/data/Projects/bellsworne/sessionzero/sz/bin/assets\\\" -I/data/Projects/bellsworne/sessionzero/sz/src -I/data/Projects/bellsworne/sessionzero/sz/src/elements -I/data/Projects/bellsworne/sessionzero/sz/vendor/rmlui_backend -isystem /nix/store/nim1mngh64k267l8s1bpbr2ckfhvgrcn-freetype-2.14.3-dev/include/freetype2 -isystem /nix/store/nmydn4w4zjqkk4vwhh0qa76s1c6wg8hc-lunasvg-3.5.0/lib/pkgconfig/../../include/lunasvg -std=gnu++17 -o CMakeFiles/rmlui-template.dir/src/app.cpp.o -c /data/Projects/bellsworne/sessionzero/sz/src/app.cpp", + "file": "/data/Projects/bellsworne/sessionzero/sz/src/app.cpp", + "output": "CMakeFiles/rmlui-template.dir/src/app.cpp.o" +}, +{ + "directory": "/data/Projects/bellsworne/sessionzero/sz", + "command": "/nix/store/xcnqqnhw9hb4j5rjgds2yjryi8qki5f3-gcc-wrapper-15.2.0/bin/g++ -DASSETS_DIR=\\\"/data/Projects/bellsworne/sessionzero/sz/bin/assets\\\" -I/data/Projects/bellsworne/sessionzero/sz/src -I/data/Projects/bellsworne/sessionzero/sz/src/elements -I/data/Projects/bellsworne/sessionzero/sz/vendor/rmlui_backend -isystem /nix/store/nim1mngh64k267l8s1bpbr2ckfhvgrcn-freetype-2.14.3-dev/include/freetype2 -isystem /nix/store/nmydn4w4zjqkk4vwhh0qa76s1c6wg8hc-lunasvg-3.5.0/lib/pkgconfig/../../include/lunasvg -std=gnu++17 -o CMakeFiles/rmlui-template.dir/src/lua_app_api.cpp.o -c /data/Projects/bellsworne/sessionzero/sz/src/lua_app_api.cpp", + "file": "/data/Projects/bellsworne/sessionzero/sz/src/lua_app_api.cpp", + "output": "CMakeFiles/rmlui-template.dir/src/lua_app_api.cpp.o" +}, +{ + "directory": "/data/Projects/bellsworne/sessionzero/sz", + "command": "/nix/store/xcnqqnhw9hb4j5rjgds2yjryi8qki5f3-gcc-wrapper-15.2.0/bin/g++ -DASSETS_DIR=\\\"/data/Projects/bellsworne/sessionzero/sz/bin/assets\\\" -I/data/Projects/bellsworne/sessionzero/sz/src -I/data/Projects/bellsworne/sessionzero/sz/src/elements -I/data/Projects/bellsworne/sessionzero/sz/vendor/rmlui_backend -isystem /nix/store/nim1mngh64k267l8s1bpbr2ckfhvgrcn-freetype-2.14.3-dev/include/freetype2 -isystem /nix/store/nmydn4w4zjqkk4vwhh0qa76s1c6wg8hc-lunasvg-3.5.0/lib/pkgconfig/../../include/lunasvg -std=gnu++17 -o CMakeFiles/rmlui-template.dir/src/page.cpp.o -c /data/Projects/bellsworne/sessionzero/sz/src/page.cpp", + "file": "/data/Projects/bellsworne/sessionzero/sz/src/page.cpp", + "output": "CMakeFiles/rmlui-template.dir/src/page.cpp.o" +}, +{ + "directory": "/data/Projects/bellsworne/sessionzero/sz", + "command": "/nix/store/xcnqqnhw9hb4j5rjgds2yjryi8qki5f3-gcc-wrapper-15.2.0/bin/g++ -DASSETS_DIR=\\\"/data/Projects/bellsworne/sessionzero/sz/bin/assets\\\" -I/data/Projects/bellsworne/sessionzero/sz/src -I/data/Projects/bellsworne/sessionzero/sz/src/elements -I/data/Projects/bellsworne/sessionzero/sz/vendor/rmlui_backend -isystem /nix/store/nim1mngh64k267l8s1bpbr2ckfhvgrcn-freetype-2.14.3-dev/include/freetype2 -isystem /nix/store/nmydn4w4zjqkk4vwhh0qa76s1c6wg8hc-lunasvg-3.5.0/lib/pkgconfig/../../include/lunasvg -std=gnu++17 -o CMakeFiles/rmlui-template.dir/src/pages/test_page.cpp.o -c /data/Projects/bellsworne/sessionzero/sz/src/pages/test_page.cpp", + "file": "/data/Projects/bellsworne/sessionzero/sz/src/pages/test_page.cpp", + "output": "CMakeFiles/rmlui-template.dir/src/pages/test_page.cpp.o" +}, +{ + "directory": "/data/Projects/bellsworne/sessionzero/sz", + "command": "/nix/store/xcnqqnhw9hb4j5rjgds2yjryi8qki5f3-gcc-wrapper-15.2.0/bin/g++ -DASSETS_DIR=\\\"/data/Projects/bellsworne/sessionzero/sz/bin/assets\\\" -I/data/Projects/bellsworne/sessionzero/sz/src -I/data/Projects/bellsworne/sessionzero/sz/src/elements -I/data/Projects/bellsworne/sessionzero/sz/vendor/rmlui_backend -isystem /nix/store/nim1mngh64k267l8s1bpbr2ckfhvgrcn-freetype-2.14.3-dev/include/freetype2 -isystem /nix/store/nmydn4w4zjqkk4vwhh0qa76s1c6wg8hc-lunasvg-3.5.0/lib/pkgconfig/../../include/lunasvg -std=gnu++17 -o CMakeFiles/rmlui-template.dir/src/elements/health_bar_element.cpp.o -c /data/Projects/bellsworne/sessionzero/sz/src/elements/health_bar_element.cpp", + "file": "/data/Projects/bellsworne/sessionzero/sz/src/elements/health_bar_element.cpp", + "output": "CMakeFiles/rmlui-template.dir/src/elements/health_bar_element.cpp.o" +} +] \ No newline at end of file diff --git a/flake.nix b/flake.nix index 42d0e26..fec0ac2 100644 --- a/flake.nix +++ b/flake.nix @@ -85,7 +85,7 @@ }; packages.default = pkgs.stdenv.mkDerivation { - pname = "rmlui-template"; + pname = "sessionzero"; version = "0.1.0"; src = ./.; diff --git a/result b/result index a643bc2..b651164 120000 --- a/result +++ b/result @@ -1 +1 @@ -/nix/store/ff4m2wp3c6iyggyg0d1d0lmh8z777l3g-rmlui-template-0.1.0 \ No newline at end of file +/nix/store/qz2y746iqyp2k1baskbnhwklp3358gwd-sessionzero-0.1.0 \ No newline at end of file diff --git a/schema.md b/schema.md deleted file mode 100644 index 850a47a..0000000 --- a/schema.md +++ /dev/null @@ -1,330 +0,0 @@ -# sessionzero data format (szdf) - -This document defines the file format used for sessionzero data objects: schemas and instances. It is TOML-adjacent but not TOML. A dedicated parser is required; existing TOML libraries will not handle type annotations, reference literals, or computed fields. - -## 1. File kinds - -There are two kinds of files, distinguished by their header key. - -**Schema files** declare an sztype: its fields, their types, defaults, and computed values. A schema is the shape a data object can take. Header key is `schema`. - -**Instance files** are actual data objects that conform to (but are not locked to) a schema. Header key is `sztype`. An instance supplies values for the fields its schema declares, and may freely add fields the schema never mentioned. There is no strict validation against the schema beyond type checking on fields that exist in both. This is intentional: schemas describe a starting shape, not a locked contract, so a table can extend or override rulesets without editing shared package files. - -Schemas belong to a package, same as instances do. Two packages may each declare a schema for the same sztype name, and those two schemas are entirely independent of one another. `szcore.player` and `custom-package.player` are different schemas that happen to share a name. - -## 2. Addressing - -Every object, schema or instance, is identified by `package.sztype.id`, taken from its `package`, `sztype`/`schema`, and `id` keys. This triple is how objects reference each other and how the runtime resolves them. - -Fields within an object are accessed by dotting further into its table structure, e.g. `my-package.player.jeff.stats.maxHp`. - -## 3. Schema resolution - -An instance binds to a schema by its own `package.sztype`, exact match, not a search across packages. An instance with `package = "custom-package"` and `sztype = "player"` binds to `custom-package.player`, never to `szcore.player`, even if `szcore.player` exists and `custom-package.player` does not. - -If no schema exists for that exact `package.sztype`, the instance is treated as a fully freeform object: every field is untyped, nothing is readonly, and no computed fields resolve. This is a valid and supported state, not an error, for tables that don't want to bother declaring a schema at all. - -A package that wants to reuse or build on another package's schema does so explicitly, with `extends` in the schema header: - -``` -schema = "player" -package = "custom-package" -extends = "szcore.player" -``` - -`extends` pulls in every field the target schema declares (including computed fields and readonly flags). The extending schema may then add new fields or override existing ones by redeclaring them; a redeclared field replaces the inherited one entirely, it does not merge. `extends` may point to a schema in any package, not just the current one. - -The wildcard package type reference described in section 4 (`.sztype`, matching any loaded package) is unrelated to schema resolution. It only affects how a field's *value* is validated against a set of possible referenced objects, not which schema an instance itself is bound to. - -## 4. Scalars and tables - -Comments start with `#`. Sections are declared with `[section]` and nested sections with `[section.subsection]`, same as TOML. Supported scalar types are `string`, `int`, `number` (float), `bool`, and lists written with `[ ]`. Strings support the same triple-quoted multiline form as TOML (`"""`). - -``` -description = """ -A player named Jeff -""" -``` - -## 5. Type annotations - -Type annotations only appear in schema files, on the field's first (and only) declaration. Instance files never use type annotations; a field's type is fixed by its schema, or is untyped if the field is a freeform addition. - -``` -name: string -level: int = 1 -``` - -The annotation goes between the field name and the `=`. A default value is optional; if omitted, the field is unset until an instance supplies one. - -Custom object types (references to other sztypes) use the `package.sztype` form as the type: - -``` -class: szcore.class -``` - -A wildcard package, matching any loaded package's sztype of that name, uses a leading dot: - -``` -class: .class -``` - -A union of allowed sztypes uses `|`: - -``` -weapon: szcore.weapon | szcore.staff -``` - -## 6. Lists - -A list field wraps its element type in `[ ]`: - -``` -items: [szcore.item] = [] -``` - -Lists of unions are written `[Type1 | Type2]`. There is no separate comma-in-brackets form; commas are reserved for separating list elements, not types. - -## 7. Reference literals - -A reference to another object is a distinct literal, not a quoted string, so the parser can resolve it structurally rather than re-parsing string contents. References start with `$`: - -``` -class = $szcore.class.ranger -``` - -`$self` refers to the current object, used when one field's computed value depends on another field in the same object: - -``` -$self.stats.modifiers.dex -``` - -## 8. Computed fields - -A field whose value is derived from an expression, rather than stored directly, is declared with `:=` instead of `=`. This marks it at parse time as something to place in a dependency graph and evaluate, rather than a literal value to store. - -``` -ac := 10 + $self.stats.modifiers.dex -``` - -Computed fields may reference other computed fields. The runtime resolves these through a dependency graph and evaluates on read (with memoization invalidated by upstream changes), or once at load time, depending on how live the session needs to be. Circular dependencies are a load-time error, not a runtime crash. - -A computed field never takes a type annotation; its type is inferred from the expression. - -## 9. Readonly fields - -The `readonly` keyword prefixes a field declaration in a schema. A readonly field can be set once (at instance creation, or by hand in a text editor) but cannot be modified programmatically afterward. - -``` -readonly maxHp: int -``` - -Readonly is a schema-level property. It is not re-declared in instance files. - -## 10. Freeform fields - -Because schemas describe a starting shape rather than a strict contract, an instance may declare fields its schema never mentioned. These need no type annotation; their type is inferred from the value. This is how a table adds house rules, custom equipment slots, or homebrew stats without touching the shared schema file. - -``` -[background] -nickname = "Jeffrey the Bold" -``` - -An instance may also omit fields the schema declares. Their value stays unset, effectively leaving the field as a fill-in-the-blank template slot. - -## 11. Grammar summary - -``` -file := header table* -header := (schema_hdr | instance_hdr) pair* -schema_hdr := "schema" "=" string ; "package" "=" string ; ("extends" "=" string)? -instance_hdr:= "sztype" "=" string ; "package" "=" string ; "id" "=" string - -table := "[" dotted_key "]" pair* -pair := readonly? key (":" type)? ("=" value | ":=" expr) - -readonly := "readonly" -key := identifier -type := scalar_type | list_type | ref_type | union_type -scalar_type := "string" | "int" | "number" | "bool" -list_type := "[" type "]" -ref_type := (package "." sztype) | ("." sztype) -union_type := type ("|" type)+ - -value := string | int | number | bool | list | reference -list := "[" (value ("," value)*)? "]" -reference := "$" dotted_path -expr := arithmetic/logical expression over values, references, and literals -``` - -## 12. Examples - -### Schema: szcore player - -``` -schema = "player" -package = "szcore" - -[info] -name: string -level: int = 1 -class: szcore.class -race: szcore.race - -[stats] -readonly maxHp: int -currentHp: int -ac := 10 + $self.stats.modifiers.dex -str: int = 10 -dex: int = 10 -int: int = 10 -wis: int = 10 - -[stats.modifiers] -str := ($self.stats.str - 10) / 2 -dex := ($self.stats.dex - 10) / 2 -int := ($self.stats.int - 10) / 2 -wis := ($self.stats.wis - 10) / 2 - -[inventory] -items: [szcore.item] = [] -``` - -### Schema: custom-package player, extending szcore's - -A different package can declare its own `player` shape. Here it builds on szcore's version instead of starting over, adding a field szcore never had. - -``` -schema = "player" -package = "custom-package" -extends = "szcore.player" - -[info] -background: string = "" -``` - -A schema named `player` is declared in a package like any other schema; it's a template, not an instance. Building an actual player instance from one of these templates follows the special rules in section 15, not the ordinary `package.sztype.id` instance form shown for `rock` below. - -### Schema: item - -``` -schema = "item" -package = "szcore" - -[info] -name: string -value: number = 0 -weight: number = 0 - -[stats] -damage: int = 0 -skill: string -attack_dice: string -``` - -### Instance: rock - -``` -sztype = "item" -package = "szcore" -id = "rock" -equipable = true - -[info] -name = "Rock" -value = 1 -weight = 0.5 - -[stats] -damage = 1 -skill = "str" -attack_dice = "1d4" -``` - -`rock` binds to `szcore.item` since its own package is `szcore`. `equipable` is a freeform top-level field; the item schema never declares it, so it carries no type annotation and is simply a bool inferred from its literal value. - -## 13. Package manifest - -A package is a datapack. Every package directory contains one manifest file, `package.szdf`, using the same scalar/table syntax as everything else in this document, but with no `schema` or `sztype` header since a manifest is neither. - -``` -id = "szcore" -name = "SessionZero Core" -version = "1.2.0" -description = """ -Core ruleset objects for sessionzero. -""" -compatible_game_systems = ["dnd5e", "generic"] - -[dependencies] -some-other-package = ">=1.0.0" -``` - -`id` is the package identifier used everywhere else in this document as the leading segment of `package.sztype.id`. `version` is a plain semver string. `compatible_game_systems` is an arbitrary list of strings with no fixed vocabulary; it exists purely so sessions and the Library can filter packages by declared system, and two packages are free to agree or disagree on naming with no validation between them. - -`[dependencies]` lists other package ids this package's objects reference (via `$other-package.thing.id`), each mapped to a version constraint string. Constraints follow standard semver comparator syntax (`>=1.0.0`, `^1.2.0`, `~1.2.0`, or an exact `1.2.0`). A package with no external references omits the table or leaves it empty. Resolving these constraints against what's actually installed, and pulling missing dependencies from the Library, is a loader concern outside the file format itself. - -## 14. Sessions - -A session is a local directory (optionally synced online, per the four session modes) with its own manifest, `session.szdf`: - -``` -id = "curse-of-the-lost-mine" -name = "Curse of the Lost Mine" -description = """ -A short introductory campaign. -""" -compatible_game_systems = ["dnd5e"] -player_template = $custom-package.player - -[datapacks] -szcore = ">=1.0.0" -custom-package = ">=1.0.0" -``` - -`compatible_game_systems` filters which datapacks are offered as relevant when adding to the session; it is compared against each candidate package's own `compatible_game_systems`. `player_template` is a reference literal pointing at the one schema every player instance in this session must be created from. `[datapacks]` lists the packages included in the session, same constraint syntax as a package's own `[dependencies]`. - -A session's own data (config for future tools, chat state, turn order, whatever comes later) lives as further freeform fields and tables in this same manifest or in additional files under the session directory, per section 10. - -## 15. Player instances - -`player` is the one sztype that never belongs to a package. A player instance is not addressed as `package.sztype.id`; it has no package at all. Instead of a `package` field, its header carries `template`, a reference to whichever schema it's built from: - -``` -sztype = "player" -template = $custom-package.player -id = "jeff" -description = """ -A player named Jeff -""" - -[info] -name = "Jeff" -level = 10 -class = $szcore.class.ranger -race = $szcore.race.human -background = "Former blacksmith" - -[stats] -maxHp = 30 -currentHp = 30 -str = 20 -dex = 20 -int = 20 -wis = 20 - -[inventory] -items = [$szcore.item.rock] - -[background] -nickname = "Jeffrey the Bold" -``` - -`jeff` binds to `custom-package.player` because `template` says so explicitly, not because of any `package` field, since there isn't one. `jeff.stats.ac` still resolves through that schema's inherited computed field exactly as it would for an ordinary instance, reading `jeff.stats.modifiers.dex`, itself another inherited computed field reading `jeff.stats.dex`. `[background]` is a freeform table jeff added on top of everything the template declares. - -Where the file lives determines its scope, and scope determines its address: - -- Inside a session, under `players/`, it belongs to that session and is addressed through the session's own lookup: `players.jeff`, not a global `package.sztype.id` path. Two different sessions can each have a `players.jeff` with no collision, since the session itself is the namespace. -- Outside any session, under the application's `local/players/`, it's a personal roster entry not tied to a session yet, addressed as `local.players.jeff`. This is what the data management mode writes to when a player builds a character ahead of time, or a GM preps an NPC before a session exists to hold it. It can later be copied into a session's `players/` folder to actually be used at the table. - -Everything else about a player instance, its computed fields, readonly fields, freeform additions, resolves exactly as any other instance bound to a schema (section 3), just with `template` standing in for the `package` lookup that ordinary instances use. \ No newline at end of file diff --git a/schema.txt b/schema.txt deleted file mode 100644 index 6daaefd..0000000 --- a/schema.txt +++ /dev/null @@ -1,39 +0,0 @@ -Example of a player object - -```text -player { - name: "Player 1" - race: "Human" - class: "Fighter" - level: 1 - - stats { - maxHp: 100 - currentHp: 100 - ac: (10 + self.modifiers.dex) - int: 20 - wis: 15 - dex: 20 - str: 20 - - modifiers { - int: ((self.stats.int - 10) / 2) - dex: ((self.stats.dex - 10) / 2) - } - } - - -} -``` - -```text -item { - name: "Stick" - - stats { - damageMod: 1 - attack: d4 - skill: str - } -} -``` diff --git a/src/elements/health_bar_element.cpp b/src/elements/health_bar_element.cpp deleted file mode 100644 index 3691fd8..0000000 --- a/src/elements/health_bar_element.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include "health_bar_element.hpp" -#include -#include -#include -#include -#include -#include - -HealthBarElement::HealthBarElement(const Rml::String& tag) : Rml::Element(tag) { - Rml::XMLAttributes attributes; - - Rml::ElementPtr track = Rml::Factory::InstanceElement(this, "div", "div", attributes); - track->SetClass("healthbar-track", true); - - Rml::ElementPtr fillElement = Rml::Factory::InstanceElement(this, "div", "div", attributes); - fillElement->SetClass("healthbar-fill", true); - fill = fillElement.get(); - - track->AppendChild(std::move(fillElement)); - AppendChild(std::move(track)); - - UpdateFill(); -} - -void HealthBarElement::OnAttributeChange(const Rml::ElementAttributes &changed_attributes) { - Rml::Element::OnAttributeChange(changed_attributes); - if (changed_attributes.find("current") != changed_attributes.end() || - changed_attributes.find("max") != changed_attributes.end()) { - UpdateFill(); - } -} - -void HealthBarElement::UpdateFill() { - int current = GetAttribute("current", 0); - int max = GetAttribute("max", 100); - if (max <= 0) max = 1; - - float pct = Rml::Math::Clamp(static_cast(current) / static_cast(max), 0.0f, 1.0f) * 100.0f; - - if (fill) { - fill->SetProperty(Rml::PropertyId::Width, Rml::Property(pct, Rml::Unit::PERCENT)); - - if (current >= 90) { - fill->SetProperty(Rml::PropertyId::BackgroundColor, Rml::Property("#33cc33", Rml::Unit::COLOUR)); - } else if (current >= 50) { - fill->SetProperty(Rml::PropertyId::BackgroundColor, Rml::Property("#cccc33", Rml::Unit::COLOUR)); - } else if (current >= 0) { - fill->SetProperty(Rml::PropertyId::BackgroundColor, Rml::Property("#cc3333", Rml::Unit::COLOUR)); - } - } -} \ No newline at end of file diff --git a/src/elements/health_bar_element.hpp b/src/elements/health_bar_element.hpp deleted file mode 100644 index 6b51a50..0000000 --- a/src/elements/health_bar_element.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once -#include -#include - -class HealthBarElement : public Rml::Element { -public: - explicit HealthBarElement(const Rml::String& tag); - - void OnAttributeChange(const Rml::ElementAttributes &changed_attributes) override; - -private: - void UpdateFill(); - Rml::Element* fill = nullptr; -}; \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index a572ddc..712380e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -11,13 +11,10 @@ #include #include #include -#include #include "app.hpp" #include "element_registry.hpp" -#include "pages/test_page.hpp" #include "ButtonClickListener.hpp" #include "lui_app_api.hpp" -#include "elements/health_bar_element.hpp" static bool is_initalized = false; static bool is_running = false; @@ -26,7 +23,7 @@ static App *app; static ElementRegistry elementRegistry; void RegisterElements() { - elementRegistry.Register("healthbar"); + } bool InitalizeApplication() { @@ -58,6 +55,9 @@ bool InitalizeApplication() { return false; } + std::string assets_dir = App::GetAssetsPath(); + Rml::LoadFontFace(assets_dir + "/fonts/PTSerif-Regular.ttf"); + lua_State* L = Rml::Lua::Interpreter::GetLuaState(); AppLuaApi *lua_app_api = new AppLuaApi(app, L); lua_app_api->RegisterLuaBindings(); @@ -105,22 +105,6 @@ int main() { std::cout << "Loading UI...\n"; - app->RegisterPage("test-page"); - - app->SetupListeners([](Rml::ElementDocument *doc) { - if (auto *btn = doc->GetElementById("nav-test")) { - app->RegisterListener(btn, std::make_unique([](Rml::Event&){ - app->NavigateTo("test-page"); - })); - } - - if (auto *btn = doc->GetElementById("nav-home")) { - app->RegisterListener(btn, std::make_unique([](Rml::Event&){ - app->NavigateTo("home"); - })); - } - }); - app->LoadUi(); app->NavigateTo("home"); std::cout << "UI Loaded\n"; diff --git a/src/pages/test_page.cpp b/src/pages/test_page.cpp deleted file mode 100644 index 86eab3a..0000000 --- a/src/pages/test_page.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include "test_page.hpp" -#include -#include -#include -#include "ButtonClickListener.hpp" - -void TestPagePage::SetupDataModel(Rml::DataModelConstructor constructor) { - constructor.Bind("greeting", &data.greeting); - constructor.Bind("counter", &data.counter); -} - -void TestPagePage::OnEnter(Rml::Element *root) { - if (!root) { - std::cout << "Was not root\n"; - return; - } - - if (Rml::Element *btn = root->GetElementById("test-btn")) { - RegisterListener(btn, std::make_unique([this](Rml::Event&) { - data.counter++; - std::cout << "Button clicked, counter is now: " << data.counter << "\n"; - DirtyVariable("counter"); - })); - } -} - -void TestPagePage::OnExit() {} \ No newline at end of file diff --git a/src/pages/test_page.hpp b/src/pages/test_page.hpp deleted file mode 100644 index 00ff8d8..0000000 --- a/src/pages/test_page.hpp +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once -#include "page.hpp" -#include - -struct TestPageData { - std::string greeting = "Hello from the test page!"; - int counter = 0; -}; - -class TestPagePage : public PageBase { -public: - using PageBase::PageBase; - - void SetupDataModel(Rml::DataModelConstructor constructor) override; - void OnEnter(Rml::Element *root) override; - void OnExit() override; - -private: - TestPageData data; -}; \ No newline at end of file