Document survival stats; lint the HUD loader in CI

Add docs/survival-stats.md (the stat model, no-code SurvivalStatsConfig tuning, the
SurvivorStatBar/Stat contract, the Rojo one-way-sync caveat) and cross-link it.
CHANGELOG Unreleased entry. CI + CONTRIBUTING now run stylua/luau-lsp over assets/
so the HUD loader is checked.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Samuel Lison
2026-06-19 13:30:12 +10:00
co-authored by Claude Opus 4.8
parent 649ff0c192
commit 772b555578
6 changed files with 133 additions and 5 deletions
+2 -2
View File
@@ -26,7 +26,7 @@ jobs:
uses: CompeyDev/setup-rokit@v0.1.2
- name: StyLua (format check)
run: stylua --check src demo
run: stylua --check src demo assets
- name: Selene (lint)
run: selene .
@@ -49,7 +49,7 @@ jobs:
--ignore "Packages/**" \
--ignore "DevPackages/**" \
--ignore "ServerPackages/**" \
src demo
src demo assets/client
# Prove both Rojo targets build: the drop-in engine model and the runnable demo place.
- name: Build engine model (default.project.json)
+10
View File
@@ -8,6 +8,16 @@ is promoted to the new version and `main` is tagged `vX.Y.Z`.
## Unreleased
### Added
- **Survival-stats engine + built-in HUD** (#8, #2) — a server tick simulates per-stat
drain/regen, stored as auto-replicating Player Attributes, and a reactive **top-left HUD**
renders them (no RemoteEvents). The HUD is a real, designer-editable `SurvivalHud` ScreenGui
in StarterGui — restyle it in Studio with zero code; bars bind by a `Stat` attribute and a
`Fill` child. Ships with seven default stats (health/energy/hunger/thirst/fatigue/blood/
poison), tunable **without code** via a `SurvivalStatsConfig` Configuration instance (or, for
developers, `Config.override("SurvivalStats", …)` / `Stats.defineStat`). Works out of the box
for every distribution — the demo/Rojo source mount it, the drop-in `.rbxm` auto-installs it
on `start()`, and a runtime fallback guarantees a HUD always appears. Adds the engine's first
client layer (`SurvivorCore.startClient()`). See [docs/survival-stats.md](docs/survival-stats.md).
- **Continuous integration** (`.github/workflows/ci.yml`) — every push to `main`/`dev` and
every PR runs `stylua --check`, `selene`, `luau-lsp analyze` (against a Rojo sourcemap +
Roblox type defs), then builds **both** `default.project.json` (the drop-in engine model)
+2 -2
View File
@@ -113,12 +113,12 @@ published (which auto-posts to Discussions → Announcements).
obvious. Keep the engine content-free.
4. **Test locally.** Play-test in Studio, then run the same checks CI does:
```bash
stylua --check src demo
stylua --check src demo assets
selene .
rojo sourcemap demo.project.json --output sourcemap.json
curl -fsSL -o globalTypes.d.luau https://raw.githubusercontent.com/JohnnyMorganz/luau-lsp/main/scripts/globalTypes.d.luau
luau-lsp analyze --sourcemap sourcemap.json --defs globalTypes.d.luau --no-strict-dm-types \
--ignore "Packages/**" --ignore "DevPackages/**" --ignore "ServerPackages/**" src demo
--ignore "Packages/**" --ignore "DevPackages/**" --ignore "ServerPackages/**" src demo assets/client
rojo build default.project.json --output SurvivorCore.rbxm
rojo build demo.project.json --output demo.rbxl
```
+2
View File
@@ -56,4 +56,6 @@ layer → harvesting/wildlife sub-engine.
- [Getting Started](getting-started.md) — install via Rojo + Wally or the drop-in `.rbxm`.
- [Extending SurvivorCore](extending.md) — the `register()` API, the component/attribute
model, and Hooks.
- [Survival Stats + HUD](survival-stats.md) — the stat simulation, no-code tuning, and the
designer-editable HUD.
- [CONTRIBUTING](../CONTRIBUTING.md) — local dev setup, code style, and the branching model.
+2 -1
View File
@@ -219,5 +219,6 @@ end)
SurvivorCore.start()
```
See [Getting Started](getting-started.md) to wire the engine into your place, and
See [Getting Started](getting-started.md) to wire the engine into your place,
[Survival Stats + HUD](survival-stats.md) for the built-in stats and HUD, and
[Architecture](architecture.md) for how the layers fit together.
+115
View File
@@ -0,0 +1,115 @@
# Survival Stats + HUD
SurvivorCore ships a survival-stat simulation and a reactive **top-left HUD**. It's batteries-
included — register nothing and you already get seven stats and a working HUD — but every rate
is tunable, and the HUD is a real ScreenGui you restyle in Studio with **zero code**.
## How it works
- Each stat value lives as a **Roblox Player Attribute** on the player (e.g. `Hunger`). Roblox
replicates attributes to that player's client automatically, so the HUD reads them directly —
**no RemoteEvents**.
- A server tick (10 Hz) moves each stat by its signed **rate per second** toward `0` or its
`Max`, clamped.
- The HUD binds each bar to its stat's attribute and re-renders on change.
`SurvivorCore.start()` (server) boots the simulation. The client HUD is booted by
`SurvivorCore.startClient()`, which the built-in HUD's loader does for you.
## The default stats
| Stat | Start | Rises to bad? | Behaviour in this version |
|---|---|---|---|
| Health | 100 | no (0 = bad) | display-only |
| Energy | 100 | no (0 = bad) | display-only |
| Hunger | 0 | **yes** (100 = starving) | rises over ~30 min |
| Thirst | 0 | **yes** (100 = dehydrated) | rises over ~20 min |
| Fatigue | 0 | **yes** (100 = exhausted) | rises over ~60 min |
| Poison | 0 | **yes** (100 = bad) | inert until a poison source |
| Blood | 100 | no (0 = death) | inert until a bleed source |
> Consequences (hunger/thirst draining energy, energy draining health, blood = 0 → death, …)
> and the energy/movement coupling arrive in later versions. This version is the stat
> simulation + HUD + tuning.
`Invert` (per stat) controls only the HUD: an inverted bar shows the *healthy* amount, so Hunger
(0 = fed) renders as a bar that **depletes** as you get hungry.
## Tuning — no code (recommended for creators)
The engine ships a **`SurvivalStatsConfig`** `Configuration` instance (in `ReplicatedStorage`).
It has one child `Configuration` per stat; edit their **Attributes** in Studio — no Lua:
| Attribute | Meaning |
|---|---|
| `RatePerSecond` | signed change per second (`+` rises toward `Max`, `` falls toward 0) |
| `Max` / `Start` | range and spawn value |
| `WarnAt` | warn when the displayed bar drops below this percent |
| `Invert` | bar full = healthy |
| `Display` | show this stat in the HUD |
Edits take effect **live** — drag `Thirst.RatePerSecond` up and thirst drains faster
immediately.
## Tuning — code (for developers)
Both work, and both lose to the Studio `SurvivalStatsConfig` instance if it's present:
```lua
-- tweak shipped rates
SurvivorCore.Config.override("SurvivalStats", {
Thirst = { ratePerSecond = 0.10 },
Hunger = { ratePerSecond = 0 },
})
-- or add your own stat (it gets a HUD bar if a bar binds it)
SurvivorCore.Stats.defineStat({
name = "Sanity", start = 100, max = 100, ratePerSecond = -0.05, invert = false, warnAt = 25, display = true,
})
```
**Precedence (last wins):** engine defaults → `Config.override` → the Studio `SurvivalStatsConfig`
instance.
## The HUD — restyle it freely
The HUD is the `SurvivalHud` ScreenGui in **StarterGui**. Restyle anything — colors, gradients,
textures, position, fonts, add or remove bars — in Studio. The engine only ever drives each
bar's fill, so your styling is untouched.
**A stat bar** is any `GuiObject` that:
- carries a **`Stat`** attribute (the player-attribute name, e.g. `"Hunger"`), and
- contains a child **`GuiObject` named `Fill`** (the part the engine resizes/recolors).
Optional per-bar attributes (each defaults from the stat's config, so usually you set only
`Stat`): `Max`, `Invert`, `WarnAt`, `FillColor`, `WarnColor`, `FillAxis` (`"X"` or `"Y"`).
To add a bar: duplicate an existing one, change its `Stat` attribute. To remove one: delete it.
To re-skin: edit the `Fill` and surrounding elements however you like. (Bars tagged
`SurvivorStatBar` work too — handy for the upcoming Builder UI.)
## How it ships (and always appears)
You never have to wire the HUD up:
1. **Rojo source / the demo** mount the `SurvivalHud` template into StarterGui directly.
2. **The drop-in `SurvivorCore.rbxm`** carries the templates inside the model; `start()`
installs them into StarterGui / StarterPlayerScripts / ReplicatedStorage if you haven't
supplied your own.
3. If somehow no HUD is present, the client builds a **minimal fallback** so a HUD always shows.
Supply your own HUD (a ScreenGui in StarterGui with `Stat`-bearing bars) and the engine uses it
instead of installing the default.
## Editing the bundled template (contributors)
The template is version-controlled as JSON (`assets/hud/SurvivalHud.model.json`,
`assets/config/SurvivalStatsConfig.model.json`) and mounted by Rojo. **Rojo syncs files → Studio,
not back** — so if you restyle the template live during a `rojo serve` session, save your changes
back to the `.model.json` (or re-author and overwrite it); a plain Studio edit under an active
sync is overwritten on the next sync. For your *own game*, just edit the `SurvivalHud` ScreenGui
in your place — it persists there normally.
---
See also: [Extending SurvivorCore](extending.md) · [Architecture](architecture.md).