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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,9 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

### Under the hood

- **Calling a rest from the GAME tab.** Under a custom game, TTRPG_SYSTEM has a RESTS panel for the
GM and granted editors: pick a rest, rest every player character, the NPCs on the map too, or
the ones ticked, see what each will get, and CALL it. Everyone sees a dice-log line each.
- **Conditions that end at a rest.** The builder's CONDITIONS page can end a condition AT A REST,
with the rests that end it ticked beside it; the RESTS page's WEARS OFF is the same setting.
- **The builder's RESTS page.** Between CONDITIONS and TRY IT: switch the standard rests on or off,
Expand Down
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -648,7 +648,7 @@ CITY_NET/
│ │ │ ├── bootSounds.test.ts # No audio at all being harmless, since there are no speakers to check the rest by
│ │ │ └── useAmbientHum.test.tsx # One hum per session: easing in once, the slider and mute adjusting it in place, and a start the browser holds back not taken for a start
│ │ ├── components/
│ │ │ ├── AdminPanel.tsx # GM dashboard — CITY / EXPORT / GAME / PLAYERS tabs; CITY_GENERATOR delegates to cityGen/ and exposes LAYOUT, DRAG_RECT/DRAW_AREA bounds, OVERPASS_DENSITY, WATER, PARK_PONDS, an optional SEED and REGENERATE; CUSTOM type integrates into NEXT_STYLE cycle using cross-map custom_structure_library; data-driven HouseRulesPanel for CP:R, CWN, and SR6; SR6 Edge replenishment (reset all / give 1 to player); a built-in game's own CONDITIONS for the main admin (TableConditionsPanel)
│ │ │ ├── AdminPanel.tsx # GM dashboard — CITY / EXPORT / GAME / PLAYERS tabs; CITY_GENERATOR delegates to cityGen/ and exposes LAYOUT, DRAG_RECT/DRAW_AREA bounds, OVERPASS_DENSITY, WATER, PARK_PONDS, an optional SEED and REGENERATE; CUSTOM type integrates into NEXT_STYLE cycle using cross-map custom_structure_library; data-driven HouseRulesPanel for CP:R, CWN, and SR6; SR6 Edge replenishment (reset all / give 1 to player); a built-in game's own CONDITIONS for the main admin (TableConditionsPanel); a custom game's RESTS for the GM and editors (GameRestsPanel), with the NPCs on the map being viewed
│ │ │ ├── SystemPicker.tsx # GAME tab's system picker: a searchable dropdown, BUILT-IN then YOUR SYSTEMS, portalled into the theme, asking before it switches the game for everyone
│ │ │ ├── systemPickerRules.ts # The picker's rules on their own: grouping and order, search, arrow-key movement, what the button says
│ │ │ ├── InventorySection.tsx # The inventory table, on every system. Its own file because SheetRenderer is long enough, and generic: which rows carry an extra button, and what pressing it does, is supplied from outside - so a drug offers CONSUME and a skillplug offers LOAD without either knowing about the other
Expand Down Expand Up @@ -728,6 +728,7 @@ CITY_NET/
│ │ │ ├── ConditionIcon.tsx # A condition's icon: one drawn for CITY_NET, stroked in the theme's color, or an uploaded picture through <img> only; anything unknown draws the target
│ │ │ ├── ConditionIconPicker.tsx # A condition's icon picked: the 24 drawn ones, or UPLOAD for a small PNG, WebP or SVG (a refusal said); the builder's CONDITIONS page and the GAME tab's both use it
│ │ │ ├── TableConditionsPanel.tsx # The GAME tab's CONDITIONS under a built-in game, main admin only: the standard set (four shown, the rest folded) and the table's own, added with a name, icon and description (no modifiers or rounds) or removed after asking; the server's refusal said, and every screen asking again once saved
│ │ │ ├── GameRestsPanel.tsx # The GAME tab's RESTS under a custom game, for the GM and granted editors: one of the game's rests, who rests (every player character, the NPCs on the map being viewed too, or ticked by hand), what each one will get (dice still to roll shown as such, conditions wearing off), and CALL; everyone previewed once so ticking asks nothing more, previewed again after a call; a refusal said
│ │ │ ├── TryItPage.tsx # TRY IT: the draft as a player would meet it, unsaved changes included. A made-up character on its own sheet with formulas worked out as you type, its health on a pretend token in the HEALTH folder's own panels with what others see, and an NPC rolled from a tier. Saves nothing but SAVE AS THE SAMPLE CHARACTER
│ │ │ ├── ControlledNpcSheetWindow.tsx # A friendly NPC's sheet for the player the GM gave it to: read-only, with rolls made for the NPC through the server
│ │ │ ├── UpdateModal.tsx # Draggable update notification modal (shown on admin login when update available; Update Now / Remind Me Later / Skip Version; docker-aware)
Expand Down Expand Up @@ -804,6 +805,7 @@ CITY_NET/
│ │ │ ├── ConditionsPage.test.tsx # Switches storing only "off", renaming and putting back, icons drawn or uploaded (a refusal said), ends and rounds, ending at the rests ticked (one turned off offered only to untick), modifiers typed negative, a system's own added, named, deleted after asking, and + CONDITION stopping at 60
│ │ │ ├── RestsPage.test.tsx # Switches storing only "off", own rests added, opened and deleted after asking, stopping at 12, names and their placeholders, counting as rests that are on and never in a loop, refills added and fitted to what they name, from the previewed sheet or health alone, tracks, a refill naming something gone, nothing to refill, and what wears off ticked from the rest's side; every change held to the server's checks
│ │ │ ├── TableConditionsPanel.test.tsx # The GAME tab's conditions against a fake server running the real checks: standard ones folded, the table's own listed, one added with a drawn or uploaded icon and sent whole with the GM's login, removed only once asked, a refusal said with the typing kept, + CONDITION stopping at 60, and the panel only for the main admin under a built-in game
│ │ │ ├── GameRestsPanel.test.tsx # The GAME tab's rests against a fake server: the game's rests offered, everyone previewed once with the map's NPCs, another rest previewed again, who rests (players, the map's NPCs too, ticked, starting with the players), CALL for each with the right request and a count, nobody chosen, a refusal, an answer that isn't a list, and the panel only under a custom game (a built-in one not even asked)
│ │ │ ├── TryItPage.test.tsx # The made-up character and its formulas, SAVE AS THE SAMPLE (absent with nowhere to save), RESET, health on a pretend token under each model, both maximums, and an NPC rolled from a tier
│ │ │ ├── ControlledNpcSheetWindow.test.tsx # A friendly NPC's sheet read-only for the player it was given to, fetched with their own login, rolling as the NPC
│ │ │ ├── SheetAttackPanel.attackAs.test.tsx # ATTACK AS: a player with a friendly NPC given to them, the GM with an NPC near the target, its weapons and LUCK, firing as it
Expand Down Expand Up @@ -953,6 +955,7 @@ CITY_NET/
│ │ │ ├── rests.ts # RESTS as logic: the standard four and a system's own read from a definition and written back storing only what differs, the count against 12, new and deleted ones (nothing left counting as or ending at a deleted one), what each counts as and loops, what a rest can refill under the health model and the sheet it plays with, refills added, fitted to what they name, changed and removed, and the conditions it wears off set from either side; mirrors the server's rests.js
│ │ │ ├── tokenConditions.ts # A token's conditions in the windows as logic: the column read as the server reads it, what to draw from the game's list (one it no longer has left out), rounds left and modifiers where sent, putting one on or taking one off without resetting another's rounds, and the map's four icons then +N
│ │ │ ├── tableConditions.ts # The GAME tab's CONDITIONS as logic: the table's own from the game's list, sent back whole, one added under an id no standard or other one has (name and description trimmed, a blank description not sent), one removed, the limit of 60 counting the standard set
│ │ │ ├── restCalls.ts # Calling a rest from the GAME tab: the two routes (GET /api/sheets/rests, POST /api/sheets/rest, preview or call), the NPC tokens on the map being viewed (a battle map's floor, or the city), who is chosen, what the server is asked for, and how a change reads ("HP 9 → 22", or "HP 9 + 1d8" before the dice)
│ │ │ ├── conditionIcons.ts # The 24 condition icons drawn for CITY_NET as path data on a 24-pixel grid, ours to use (the four first sketched too close to Feather's redrawn), and telling a drawn icon from an uploaded one
│ │ │ ├── tryIt.ts # TRY IT as logic: the made-up character from the sample, typed values, SAVE AS THE SAMPLE, the pretend token and its maximum
│ │ │ ├── examples.ts # The examples' and starters' cards (health, advancement in the system's own XP word, stats, formulas, distance), the copy's suggested name, and locking a page while one is looked at (every control but tabs, things opening out and marked views)
Expand All @@ -976,6 +979,7 @@ CITY_NET/
│ │ │ ├── conditions.test.ts # The standard set and what the game offers held to the server's own conditionsOf, each change storing only what differs, a system's own added, renamed and deleted, the limit of 60, and what a modifier may name
│ │ │ ├── rests.test.ts # RESTS logic held to the server's own rests.js, definition checks and conditionsOf: rests listed and offered, counting order and loops, edits stored as changes only, own ones added to 12 and deleted without leaving anything pointing at them, refill targets per health model and sheet, refills described, fitted and edited, and what wears off from either side
│ │ │ ├── tableConditions.test.ts # The GAME tab's conditions held to the server's own checks and conditionsOf: the table's own read and sent back, ids that never clash, blank descriptions not sent, removing, needing a name, and the limit of 60
│ │ │ ├── restCalls.test.ts # The NPCs on the map being viewed (city, a battle map's floor, a leftover battle map), who rests and what the server is asked, changes before and after the dice, the requests with the GM's login and refusals in the server's words
│ │ │ ├── tokenConditions.test.ts # Reading a token's conditions as the server does, what is drawn (in order, rounds and modifiers where sent, one the game lacks left out), putting on and taking off keeping the others' rounds, and the words for modifiers and rounds
│ │ │ ├── conditionIcons.test.tsx # The 24 icons are the server's, path data alone, none of Feather's paths, stroked in currentColor and never filled; an upload drawn through <img>; anything unknown drawn as the target
│ │ │ ├── tryIt.test.ts # The made-up character, typed values, SAVE AS THE SAMPLE and RESET, the pretend token and its maximum
Expand Down
14 changes: 11 additions & 3 deletions frontend/src/components/AdminPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ import { useParts, partOn, type PartLookup } from '../sheets/parts';
import { currenciesFor, isUploadedIcon, BUILT_IN_ICONS, type Currency } from '../sheets/currencies';
import { useCustomTemplate, refreshCustomTemplate, isCustomSystem } from '../sheets/customTemplates';
import { TableConditionsPanel } from './TableConditionsPanel';
import { GameRestsPanel } from './GameRestsPanel';
import { npcsOnMap } from '../sheets/restCalls';
import { shortfallRule } from '../sheets/moneyText';
import { SYSTEMS_CHANGED_EVENT } from '../sheets/systemsLibrary';

Expand Down Expand Up @@ -143,7 +145,8 @@ function BattleAdminPanel({
</>}

{tab === 'game' && <>
<TTRPGSystemPanel token={token} mainAdmin={isPrimaryAdmin} onOpenNpcLibrary={onOpenNpcLibrary} onOpenSystems={onOpenSystems} activeUsers={activeUsers} />
<TTRPGSystemPanel token={token} mainAdmin={isPrimaryAdmin} onOpenNpcLibrary={onOpenNpcLibrary} onOpenSystems={onOpenSystems} activeUsers={activeUsers}
npcs={npcsOnMap(locations || [], 'battle_map', activeBattleMapData)} />
{/* Money, which a custom system with the bank off does not have (3b2b). */}
{bankOn && <>
<CurrencyIconPanel token={token} globalSettings={globalSettings} fetchGlobalSettings={fetchGlobalSettings} system={globalSettings?.game_system} />
Expand Down Expand Up @@ -1047,7 +1050,8 @@ export function AdminPanel({

{adminTab === 'game' && (
<>
<TTRPGSystemPanel token={token} mainAdmin={isPrimaryAdmin} onOpenNpcLibrary={onOpenNpcLibrary} onOpenSystems={onOpenSystems} activeUsers={activeUsers} />
<TTRPGSystemPanel token={token} mainAdmin={isPrimaryAdmin} onOpenNpcLibrary={onOpenNpcLibrary} onOpenSystems={onOpenSystems} activeUsers={activeUsers}
npcs={npcsOnMap(locations || [], view, activeBattleMapData)} />
{/* Money, which a custom system with the bank off does not have (3b2b). */}
{bankOn && <>
<CurrencyIconPanel token={token} globalSettings={globalSettings} fetchGlobalSettings={fetchGlobalSettings} system={gameSystem} />
Expand Down Expand Up @@ -2299,8 +2303,10 @@ const SR6_HOUSE_RULES: HouseRuleDef[] = [
},
];

function TTRPGSystemPanel({ token, mainAdmin, onOpenNpcLibrary, onOpenSystems, activeUsers }: {
function TTRPGSystemPanel({ token, mainAdmin, onOpenNpcLibrary, onOpenSystems, activeUsers, npcs }: {
token: string; onOpenNpcLibrary?: () => void; activeUsers?: any[];
/** The NPC tokens on the map being viewed, for a custom game's rests (4f5). */
npcs?: { id: number; name: string }[];
/** The main admin: changes a built-in game's own conditions (the server's route is theirs alone). */
mainAdmin?: boolean;
/** Opens the system builder on MY SYSTEMS; the main admin's alone, so absent for a granted editor. */
Expand Down Expand Up @@ -2367,6 +2373,8 @@ function TTRPGSystemPanel({ token, mainAdmin, onOpenNpcLibrary, onOpenSystems, a
]} />
{/* A built-in game's own conditions (4e2c2); a custom system's are edited in the builder. */}
{mainAdmin && !isCustomSystem(system) && <TableConditionsPanel token={token} system={system} />}
{/* A custom game's rests (4f5); a built-in game's are its own buttons below. */}
{isCustomSystem(system) && <GameRestsPanel token={token} system={system} npcs={npcs ?? []} />}
{system === 'shadowrun_6e' && (() => {
const onlinePlayers = (activeUsers || []).filter((u: any) => !u.isAdmin && !u.isNPC).map((u: any) => u.userName);
return (
Expand Down
Loading
Loading