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
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,14 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

### Under the hood

- **The builder's CONDITIONS page.** A new page between NPCS and TRY IT lists the ten standard
conditions with on/off switches and the system's own below them. Each can be renamed (the
standard name shows until you type one), given a short label for its chip, an icon (24 drawn for
CITY_NET that follow the theme, or a picture you upload), a description, an end (when removed, or
after a number of rounds) and modifiers on all rolls, a stat or a formula. + CONDITION adds one of
your own, up to 60 in all, and deleting one asks first. Putting a standard condition back as it
was saves nothing, as with WORDS. Conditions on tokens come next.

- **Conditions as part of a game system.** A custom system now carries its conditions: ten
standard ones every system starts with (Blinded, Bleeding, Poisoned, Prone, Stunned, Grappled,
Frightened, Unconscious, Restrained, Exhausted), each of which can be renamed, described, given
Expand Down
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -709,6 +709,8 @@ CITY_NET/
│ │ │ ├── SheetPage.tsx # CHARACTER SHEET: automatic until CUSTOMIZE copies the starter; a tree of tabs, sections and fields to move and edit, who sees and who changes each field, a tray of stats not placed yet, and a live preview. Also edits NPCS' stat block of its own (GM-only preview)
│ │ │ ├── SheetPreview.tsx # The sheet designer's live preview, drawn by the real SheetRenderer, as its owner, the GM and everyone else see it
│ │ │ ├── NpcsPage.tsx # NPCS: the stat block (the character sheet, or one of its own) and TIERS, each box a number, a formula with @level or dice, typed text kept as typed, a TRY IT roll at a level
│ │ │ ├── ConditionsPage.tsx # CONDITIONS: the standard set with switches and a system's own (+ CONDITION, DELETE after asking), up to 60; each one's name (the standard name as placeholder), chip label, icon (24 drawn, or UPLOAD), description, how it ends (when removed or after rounds) and modifiers on all rolls, a stat or a formula, typed numbers kept as typed
│ │ │ ├── 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
│ │ │ ├── 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 @@ -780,6 +782,7 @@ CITY_NET/
│ │ │ ├── StatsRulesPage.test.tsx # Stats in groups with their SAMPLE column, formulas with live values and mistakes, and tables with TRY IT
│ │ │ ├── SheetPage.test.tsx # Automatic or customized, the tree of tabs, sections and fields, moving and removing them (sections going to a tab you choose, the last tab making one page), and each field's settings
│ │ │ ├── NpcsPage.test.tsx # The stat block shared or its own, tiers whose boxes keep what was typed, and TRY IT rolling one, all through the server's own code
│ │ │ ├── ConditionsPage.test.tsx # Switches storing only "off", renaming and putting back, icons drawn or uploaded (a refusal said), ends and rounds, modifiers typed negative, a system's own added, named, deleted after asking, and + CONDITION stopping at 60
│ │ │ ├── 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 @@ -923,6 +926,8 @@ CITY_NET/
│ │ │ ├── sheetDesigner.ts # CHARACTER SHEET as logic: a system's own sheet read from its definition and written back - tabs, sections, fields, placement, the tray, and the problems that stop it saving
│ │ │ ├── publicLines.ts # What everyone else sees of a custom system's character: the sheet's public lines, for ID.EXE's INFO and the sheet preview's EVERYONE ELSE
│ │ │ ├── npcs.ts # NPCS as logic: the stat block (shared or its own), tiers and their boxes (number, formula with @level, or dice), the server's limits, and a rolled box as text
│ │ │ ├── conditions.ts # CONDITIONS as logic: the standard set and a system's own read from a definition and written back storing only what differs (a standard one put back leaves nothing), the count against 60, new and deleted ones, and what a modifier may name; mirrors the server's conditions.js
│ │ │ ├── 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)
│ │ │ ├── templates/
Expand All @@ -942,6 +947,8 @@ CITY_NET/
│ │ │ ├── statsRules.test.ts # Stats, samples, formulas and tables written back, ids from first names and kept after, held to the server's check and engine
│ │ │ ├── sheetDesigner.test.ts # The sheet designer's every step (customize, the tray, moving and removing tabs, sections and fields, attack numbers never EVERYONE), held to the server's check
│ │ │ ├── npcs.test.ts # The stat block and tiers written back, held to the server's check and rolled by its tierRolls
│ │ │ ├── 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
│ │ │ ├── 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
│ │ │ ├── examples.test.ts # The examples' and starters' card facts read off the server's own definitions, XP in the system's word, and locking a page but for browsing
│ │ │ ├── customTemplates.test.tsx # A published custom system's sheet drawn by the ordinary renderer from the server's render copy, the generic template standing in until it loads
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/components/BuilderScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import { FeaturesPage } from './FeaturesPage';
import { StatsRulesPage } from './StatsRulesPage';
import { SheetPage } from './SheetPage';
import { NpcsPage } from './NpcsPage';
import { ConditionsPage } from './ConditionsPage';
import { CONDITION_ICONS } from '../sheets/conditionIcons';
import { TryItPage } from './TryItPage';

// The system builder (4a2b): it takes over the whole window, with no map. A sidebar down the left
Expand Down Expand Up @@ -115,6 +117,7 @@ const Icons: Record<BuilderPage | 'builder' | 'systems' | 'save' | 'publish', Re
rules: icon('M18 7V4H6l6 8-6 8h12v-3'),
sheet: icon('M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z', 'M14 2v6h6', 'M16 13H8', 'M16 17H8', 'M10 9H8'),
npcs: icon('M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2', 'M9 3a4 4 0 1 1 0 8 4 4 0 0 1 0-8z', 'M23 21v-2a4 4 0 0 0-3-3.87', 'M16 3.13a4 4 0 0 1 0 7.75'),
conditions: icon(...CONDITION_ICONS.skull),
try: icon('M5 3l14 9-14 9z'),
problems: icon('M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z', 'M12 9v4', 'M12 17h.01'),
save: icon('M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z', 'M17 21v-8H7v8', 'M7 3v5h8'),
Expand Down Expand Up @@ -416,6 +419,7 @@ export function BuilderScreen({ token, systemId, startPage = 'setup', running, o
{ready && page === 'rules' && <StatsRulesPage definition={definition!} edit={edit} api={api} />}
{ready && page === 'sheet' && <SheetPage definition={definition!} edit={edit} api={api} />}
{ready && page === 'npcs' && <NpcsPage definition={definition!} edit={edit} api={api} />}
{ready && page === 'conditions' && <ConditionsPage definition={definition!} edit={edit} api={looking ? undefined : api} />}
{ready && page === 'try' && <TryItPage definition={definition!} edit={looking ? undefined : edit} api={api} />}
</Locked>
</section>
Expand Down
20 changes: 20 additions & 0 deletions frontend/src/components/ConditionIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React from 'react';
import { CONDITION_ICONS, isDrawnIcon, isUploadedIcon } from '../sheets/conditionIcons';

// A condition's icon (4e1b): one drawn for CITY_NET, stroked in the theme's color, or a picture the
// GM uploaded, drawn through <img> only, which is what makes an uploaded SVG safe (as currency
// icons and battle maps). Anything else draws the target, so a broken reference still shows a mark.

export function ConditionIcon({ icon, size = 18, title }: { icon: string; size?: number; title?: string }) {
if (isUploadedIcon(icon)) {
return <img src={icon} alt={title ?? ''} width={size} height={size} style={{ objectFit: 'contain', display: 'block', flexShrink: 0 }} />;
}
const paths = CONDITION_ICONS[isDrawnIcon(icon) ? icon : 'target'];
return (
<svg viewBox="0 0 24 24" width={size} height={size} fill="none" stroke="currentColor" strokeWidth={2}
strokeLinecap="round" strokeLinejoin="round" role={title ? 'img' : undefined} aria-label={title} aria-hidden={title ? undefined : true}
style={{ display: 'block', flexShrink: 0 }}>
{paths.map((d) => <path key={d} d={d} />)}
</svg>
);
}
Loading
Loading