feat(openworlds): create — subrace handling on the Creation Plane (#377, #315 AC5) - #669
Conversation
|
Warning Review limit reached
More reviews will be available in 44 minutes and 45 seconds. Learn how PR review limits work. Your organization has run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
Comment |
…, #315 AC5) The character creator offered no subrace choice. This adds subrace as an OPTIONAL second-tier lineage choice and threads it through the ability preview, the review/summary surfaces, the portrait filter, and the startProviderSession seam. Additive, render-only (viewer JSX, no build step). Validated by transforming screen-create.jsx through viewer/openworlds/vendor/babel-standalone-7.29.0.min.js. Changes (viewer/openworlds/screen-create.jsx): - RACES: optional `subraces` map on elf (high/wood), dwarf (mountain/hill), halfling (lightfoot/stout), gnome (forest/rock). Each subrace declares name/body and a `bonus` DELTA that stacks on the base race. Drow DECISION documented: drow stays a STANDALONE top-level race (its own RACES entry + Minthara gallery face), NOT folded into elf.subraces — matches the already-shipped data fork. (AC1) - Helpers: subracesForRace / hasSubraces / mergeBonus (additive, non-mutating) / effectiveRaceBonus (base + chosen subrace) / subraceLineageName. - StepRace: new SubracePicker renders brass/sketch radio chips, gated on the race having a non-empty subraces map; a synthesized "Standard" (subrace: null) option is first and is the default so base-race-only/homebrew heroes are never blocked. Switching race clears a stale subrace. (AC2) - Ability stacking: StepAbilities preview, StepReview summary, and the live right-rail summary all read effectiveRaceBonus(hero) — Mountain Dwarf shows STR +2 / CON +2. No surface left reading the bare base-race bonus. (AC3) - bindHero: spec.subrace serialized (empty string when none) across the startProviderSession seam. Engine-side wiring out of scope. (AC4) - Initial hero state gains `subrace: null`. - portraitChoicesForRace(race, subrace): ANDs in subrace when the gallery carries subrace-tagged faces, else degrades to the race-level pool (never an empty grid — the curated faces are race-only today). Preserves the existing #379/#390 race-level fallback. (#315 AC5) Test guard (viewer/tests/test_openworlds_static.py): - test_openworlds_create_subrace_handling_is_wired: parses RACES per-race, asserts the four subrace maps + bonus deltas, the drow-standalone decision, the SubracePicker/radiogroup/Standard-default UI, the single effectiveRaceBonus stacking path, the race-switch reset, the bindHero spec.subrace, and the subrace-aware portrait filter. Findings skipped: none — all four AC of #377 implemented.
c5facde to
5c5a47a
Compare
Closes #377. Implements #315 AC5 (subrace handling on the Creation Plane).
What changed
Subrace is now an optional second-tier lineage choice on the Creation Plane, threaded through the ability preview, the review/summary surfaces, the portrait filter, and the
startProviderSessionseam.Additive, render-only (viewer JSX, no build step). Validated by transforming
screen-create.jsxthroughviewer/openworlds/vendor/babel-standalone-7.29.0.min.js(62393 transformed bytes, plus a negative control proving the validator fails on broken JSX).viewer/openworlds/screen-create.jsxRACESentries gain an optionalsubracesmap — elf (high/wood), dwarf (mountain/hill), halfling (lightfoot/stout), gnome (forest/rock). Each subrace declaresname, optionalbody, and abonusdelta that stacks on the base race.RACESentry, its own CHA delta, and a Minthara gallery face taggedrace:"drow"), not folded intoelf.subraces. Only High/Wood live under elf so the gallery filter + race tags stay consistent with what's already shipped.subracesForRace/hasSubraces/mergeBonus(additive, non-mutating) /effectiveRaceBonus(base + chosen subrace) /subraceLineageName.SubracePickerrenders brass/sketch radio chips, gated on the selected race having a non-emptysubracesmap. A synthesized "Standard" option (subrace: null) is first and is the default, so base-race-only / homebrew heroes are never blocked. Switching race clears a stale subrace (a Wood Elf pick can't linger onto Dwarf).role="radiogroup"/role="radio"+data-worldos-testidhooks for agent driving.effectiveRaceBonus(hero). Mountain Dwarf shows STR +2 / CON +2; no surface is left reading the bare base-race bonus.spec.subraceis serialized (string slug, empty string when none chosen) across thestartProviderSessionseam alongsiderace. Engine-side wiring is out of scope (filed separately if needed).herostate gains asubrace: nullslot.portraitChoicesForRace(race, subrace)ANDs in the subrace when the gallery carries subrace-tagged faces, else degrades to the race-level pool (never an empty grid — the curated 12-face set is race-only today). Preserves the existing fix(openworlds): portrait gallery empty for 5 of 11 races (dwarf, halfling, gnome, dragonborn, half-orc) #379/fix(openworlds): portrait gallery empty-state fallback (Closes #379) #390 race-level fallback.viewer/tests/test_openworlds_static.pytest_openworlds_create_subrace_handling_is_wired: parsesRACESper-race and asserts the four subrace maps + bonus deltas, the drow-standalone decision, the SubracePicker / radiogroup / Standard-default UI, the singleeffectiveRaceBonusstacking path, the race-switch reset, the bindHerospec.subrace, and the subrace-aware portrait filter.Verification
mergeBonusnon-mutating, lineage name = "Wood Elf", and wood-elf portrait grid stays non-empty.viewer/tests/test_openworlds_static.py,test_portrait_art.py,test_building_universe.py, and the non-subprocesstest_portrait_gen.pyclasses: 96 passed.test_portrait_gen.py::PortraitGenRealSubprocessTests::test_null_default_returns_placeholder_no_networkfails withModuleNotFoundError: No module named 'pydantic'— it spawns the real engine subprocess viauv runand the engine venv in this environment lacks the dep. That test file is byte-identical to HEAD; this PR touches only the JSX screen + its static guard.Findings skipped
None — all four acceptance criteria of #377 are implemented.
Out of scope (untouched)
Parity lane, combat/table/character screens, the
server.pycombat/lockout area,scripts/play_party.sh,qa/run_duo.sh. Noviewer/server.pychange was needed (this is a wire-up + data change in the screen).