Give NPCs their own editor section and unify the world-state boxes
NPCs are still part of stat_schema, but editing them inside the World State panel meant a nested dashed sub-box among four flat stat lists. They now get a top-level "Cast & NPCs" section: one card per NPC (avatar, name, npc.<id> address, triggers, description, its own stats) in a responsive grid, saved through the same schema path via the new NpcEditor/addNpc exports. The panel's real inconsistency was CSS, not layout: the global field rule keys off input[type="..."], which never matches the editor's typeless key and description inputs, so those rendered with browser defaults beside properly styled number boxes. One scoped base rule now covers the whole editor and every entry is the same tile with a caption over each control. Two bugs fell out of that: .se-band-n (0,1,0) always lost to input[type="number"] (0,1,1), so band bounds rendered full-width; and the exclusion has to be :not(:where([type="checkbox"])) because a plain :not() inherits its argument's specificity and swallows every override. In Play, NPCs group under a single "Cast" heading as compact plates instead of each becoming another top-level group in the rail. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UeQVy5bEjLhfgWNc27Efet
This commit is contained in:
co-authored by
Claude Opus 5
parent
d66fd1d6a2
commit
be6dbb0f24
@@ -3,7 +3,7 @@ import { useNavigate, useParams } from 'react-router-dom'
|
||||
import { api } from '../api'
|
||||
import { Field, StoryCardRow, downloadJSON, pickJSONFile, useToast } from '../components'
|
||||
import ArtPicker from '../ArtPicker'
|
||||
import SchemaEditor from '../SchemaEditor'
|
||||
import SchemaEditor, { NpcEditor, addNpc } from '../SchemaEditor'
|
||||
|
||||
export default function ScenarioEditor() {
|
||||
const { id } = useParams()
|
||||
@@ -106,6 +106,15 @@ export default function ScenarioEditor() {
|
||||
debounceSave('stat_schema', () => saveSchema(cleaned))
|
||||
}
|
||||
|
||||
// NPCs live in the same stat_schema, but are edited in their own page
|
||||
// section — so this is just the `npcs` slice of the same save path.
|
||||
const setNpcs = (npcs) => {
|
||||
const next = { ...(parsedSchema || {}) }
|
||||
if (npcs && Object.keys(npcs).length) next.npcs = npcs
|
||||
else delete next.npcs
|
||||
applySchema(next)
|
||||
}
|
||||
|
||||
const addCard = async () => {
|
||||
const card = await api.createStoryCard({ scenario_id: Number(id) })
|
||||
setScenario({ ...scenario, story_cards: [...scenario.story_cards, card] })
|
||||
@@ -243,10 +252,10 @@ export default function ScenarioEditor() {
|
||||
</div>
|
||||
</div>
|
||||
<p className="dim" style={{ margin: '0 0 10px', fontSize: '0.85rem' }}>
|
||||
Optional. Define stats (with bands and rules), NPCs, flags, and milestones, and
|
||||
the AI will track them each turn — HP, mana, an NPC’s trust, quest objectives.
|
||||
Each NPC has its own name, trigger keys, description, and its own stats; a story
|
||||
card is created for it automatically. Leave blank for a plain narrative scenario.
|
||||
Optional. Define stats (with bands and rules), flags, and milestones, and the AI
|
||||
will track them each turn — HP, mana, a raised alarm, quest objectives. NPCs are
|
||||
part of this same schema but have their own section below. Leave blank for a plain
|
||||
narrative scenario.
|
||||
</p>
|
||||
{schemaView === 'form' ? (
|
||||
<SchemaEditor schema={parsedSchema} onChange={applySchema} />
|
||||
@@ -272,6 +281,27 @@ export default function ScenarioEditor() {
|
||||
</>
|
||||
)}
|
||||
|
||||
<div className="page-header" style={{ marginTop: 28 }}>
|
||||
<h2 style={{ margin: 0, fontFamily: 'Georgia, serif', fontSize: '1.2rem' }}>Cast & NPCs</h2>
|
||||
{schemaView === 'form' && (
|
||||
<button onClick={() => setNpcs(addNpc(parsedSchema?.npcs))}>+ Add NPC</button>
|
||||
)}
|
||||
</div>
|
||||
<p className="dim" style={{ margin: '0 0 10px', fontSize: '0.85rem' }}>
|
||||
Characters the AI tracks by name. Each has its own trigger words, description and
|
||||
stats — a dragon’s <em>ferocity</em>, a ranger’s <em>trust</em> — addressed as
|
||||
<code> npc.<id>.<stat></code>. Adding one here also creates a story card
|
||||
for it when an adventure starts.
|
||||
</p>
|
||||
{schemaView === 'form' ? (
|
||||
<NpcEditor npcs={parsedSchema?.npcs || {}} onChange={setNpcs} />
|
||||
) : (
|
||||
<div className="empty" style={{ padding: '20px 0' }}>
|
||||
NPCs are part of the schema JSON above — switch to <strong>Editor</strong> to edit
|
||||
them as cards.
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="page-header" style={{ marginTop: 28 }}>
|
||||
<h2 style={{ margin: 0, fontFamily: 'Georgia, serif', fontSize: '1.2rem' }}>Attached Scripts</h2>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user