Verifying R-255 on a live sheet found the rule working everywhere it was built and contradicted in the first place a GM looks. The header's ARMOUR field is the hide the creature HAS; on a grounded beast the hit-location table under it read 4 and the header still read 9. Nothing computes off that field, so nothing in the code was wrong. It was wrong at the table. The field stays editable and stays 9 — that is the creature's hide and what a GM types into. Under it, when system.grounded is set, the sheet now says what is in play: DOWN: 4 · CHEST 9, derived from exposedHideFor rather than from a second opinion, with the reason on the tooltip. Verified live: no badge in the air, and grounded the badge and the location table agree at 4 with the forequarters at 9. check-rules forbids redefining a rule by NAME, which misses how this goes wrong in practice — nobody redefines exposedHideFor, they write Math.floor(hide / 2) in the sheet because it is cheaper than an import. I wrote that version first and check-rules passed it. It now fails any file outside rules.mjs that halves a hide inline, naming file and line; check files are exempt because stating the expected value independently is what a test is for. That guard also turned up something I have not fixed, recorded in R-256: armourAgainst lives in ringbrp.mjs rather than rules.mjs, and tools/simulate.mjs has its own inline copy of it. They agree today so no published number is wrong, but every figure in the bestiary and the lethality baseline comes out of that simulator. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
144 lines
8.7 KiB
Handlebars
144 lines
8.7 KiB
Handlebars
<form class="ringbrp-sheet rb-npc" autocomplete="off">
|
|
<header class="rb-header">
|
|
<img class="rb-portrait" src="{{actor.img}}" data-edit="img" title="{{actor.name}}"/>
|
|
<div class="rb-headline">
|
|
<input class="rb-name" name="name" type="text" value="{{actor.name}}"/>
|
|
<input class="rb-role" name="system.role" type="text" value="{{sys.role}}" placeholder="{{localize 'RINGBRP.Sheet.Role'}}"/>
|
|
<div class="rb-origin">
|
|
<span class="rb-style-fixed">{{styleLabel}}</span>
|
|
</div>
|
|
</div>
|
|
<div class="rb-vitals">
|
|
<div class="rb-hp"><span class="rb-vital-label">{{localize "RINGBRP.Sheet.HP"}}</span>
|
|
<input type="number" name="system.hp.value" value="{{sys.hp.value}}"/><span class="rb-slash">/</span><span class="rb-max">{{sys.hp.max}}</span></div>
|
|
<div class="rb-mw"><span class="rb-vital-label">{{localize "RINGBRP.Sheet.MajorWound"}}</span><span class="rb-big">{{sys.majorWound.value}}</span></div>
|
|
<div class="rb-armour"><span class="rb-vital-label">{{localize "RINGBRP.Sheet.Armour"}}</span>
|
|
<input type="number" name="system.naturalArmour.value" value="{{sys.naturalArmour.value}}"/>
|
|
{{#if groundedHide}}<span class="rb-grounded-hide"
|
|
data-tooltip="{{localize 'RINGBRP.Sheet.GroundedHideHint'}}">{{localize "RINGBRP.Sheet.GroundedHide" body=groundedHide.body vital=groundedHide.vital}}</span>{{/if}}</div>
|
|
{{!-- The body plan. Every NPC has always HAD hit locations; nothing on this
|
|
sheet ever let a GM choose which body they belonged to, so every bear and
|
|
every dragon in the game was quietly a man with two arms. --}}
|
|
<div class="rb-species"><span class="rb-vital-label">{{localize "RINGBRP.Sheet.BodyPlan"}}</span>
|
|
<select name="system.speciesProfile" class="rb-species-select">
|
|
{{#each RINGBRP.locationTables as |t key|}}
|
|
<option value="{{key}}" {{#if (rbEq ../sys.speciesProfile key)}}selected{{/if}}>{{localize t.label}}</option>
|
|
{{/each}}
|
|
</select></div>
|
|
</div>
|
|
</header>
|
|
|
|
<section class="rb-characteristics">
|
|
{{#each sys.characteristics as |c key|}}
|
|
<div class="rb-char" data-characteristic="{{key}}">
|
|
<a class="rb-char-label" data-action="roll-characteristic">{{localize (rbConcat "RINGBRP.Char." key)}}</a>
|
|
<input type="number" name="system.characteristics.{{key}}.value" value="{{c.value}}"/>
|
|
<a class="rb-char-x5" data-action="roll-characteristic">{{rbTimes5 c.value}}</a></div>
|
|
{{/each}}
|
|
</section>
|
|
|
|
<section class="rb-derived">
|
|
<div class="rb-derived-group"><span class="rb-dl">{{localize "RINGBRP.Sheet.DamageMod"}}</span><span class="rb-dv">{{sys.damageModifier}}</span></div>
|
|
<div class="rb-derived-group"><span class="rb-dl">{{localize "RINGBRP.Sheet.MOV"}}</span><span class="rb-dv">{{sys.mov}}</span></div>
|
|
<div class="rb-derived-group"><span class="rb-dl">{{localize "RINGBRP.Sheet.Condition"}}</span><span class="rb-dv rb-cond-{{sys.condition}}">{{localize (rbConcat "RINGBRP.Condition." sys.condition)}}</span></div>
|
|
{{#if sys.wounded.flightLost}}<div class="rb-derived-group rb-flag-bad">{{localize "RINGBRP.Sheet.Grounded"}}</div>{{/if}}
|
|
</section>
|
|
|
|
<nav class="sheet-tabs rb-tabs" data-group="primary">
|
|
<a class="item" data-tab="skills">{{localize "RINGBRP.Tab.Skills"}}</a>
|
|
<a class="item" data-tab="combat">{{localize "RINGBRP.Tab.Combat"}}</a>
|
|
<a class="item" data-tab="bio">{{localize "RINGBRP.Tab.Tactics"}}</a>
|
|
</nav>
|
|
|
|
<section class="sheet-body rb-body">
|
|
<div class="tab" data-group="primary" data-tab="skills">
|
|
{{#each RINGBRP.categories as |cat key|}}
|
|
{{! Handlebars cannot read a property off a subexpression — `(lookup a b).length`
|
|
is a parse error, and a parse error in a template is a sheet that never opens.
|
|
#if already treats an empty array as falsy, so the length was never needed. }}
|
|
{{#if (lookup ../skillsByCategory key)}}
|
|
<div class="rb-cat">
|
|
<h3 class="rb-cat-head">{{localize cat.label}}<span class="rb-catbonus">+{{lookup ../sys.categoryBonus key}}</span>
|
|
<a class="rb-add" data-action="item-create" data-type="skill"><i class="fas fa-plus"></i></a></h3>
|
|
<ol class="rb-skills">
|
|
{{#each (lookup ../skillsByCategory key) as |sk|}}
|
|
<li class="rb-skill" data-item-id="{{sk.id}}">
|
|
<a class="rb-skill-name" data-action="roll-skill">{{sk.name}}</a>
|
|
<span class="rb-skill-val">{{sk.system.value}}%</span>
|
|
<a data-action="item-edit"><i class="fas fa-edit"></i></a>
|
|
<a data-action="item-delete"><i class="fas fa-trash"></i></a>
|
|
</li>
|
|
{{/each}}
|
|
</ol>
|
|
</div>
|
|
{{/if}}
|
|
{{/each}}
|
|
<a class="rb-add-wide" data-action="item-create" data-type="skill"><i class="fas fa-plus"></i> {{localize "RINGBRP.Sheet.AddSkill"}}</a>
|
|
</div>
|
|
|
|
<div class="tab" data-group="primary" data-tab="combat">
|
|
{{!-- Hit locations. These were derived for every NPC in the system and drawn
|
|
nowhere, so a GM running a bear could not see that its foreleg was gone. --}}
|
|
<section class="rb-npc-anatomy">
|
|
<div class="rb-npc-figure">{{> "systems/ringbrp/templates/partials/body-figure.hbs" map=bodyMap label=speciesLabel svgClass="rb-body-npc"}}</div>
|
|
<table class="rb-table rb-loctable rb-loctable-compact">
|
|
<thead><tr>
|
|
<th>{{localize "RINGBRP.Loc.Header"}}</th>
|
|
<th>{{localize "RINGBRP.Loc.Melee"}}</th>
|
|
<th>{{localize "RINGBRP.Loc.Ranged"}}</th>
|
|
<th>{{localize "RINGBRP.Sheet.Armour"}}</th>
|
|
<th>{{localize "RINGBRP.Sheet.HP"}}</th>
|
|
</tr></thead>
|
|
<tbody>
|
|
{{#each sys.locations as |l|}}
|
|
<tr class="rb-locrow {{#if l.destroyed}}rb-loc-row-destroyed{{else if l.disabled}}rb-loc-row-disabled{{/if}}" data-loc="{{l.id}}">
|
|
<td class="rb-loc-cell">{{localize l.label}}
|
|
{{#if l.destroyed}}<span class="rb-loc-flag rb-loc-flag-x">{{localize "RINGBRP.Loc.Destroyed"}}</span>
|
|
{{else if l.disabled}}<span class="rb-loc-flag">{{localize "RINGBRP.Loc.Disabled"}}</span>{{/if}}</td>
|
|
<td class="rb-dim">{{l.melee}}</td>
|
|
<td class="rb-dim">{{l.ranged}}</td>
|
|
<td>{{l.armour}}</td>
|
|
<td><input type="number" class="rb-loc-taken" name="system.locationDamage.{{l.id}}" value="{{l.taken}}"/><span class="rb-slash">/</span>{{l.max}}</td>
|
|
</tr>
|
|
{{/each}}
|
|
</tbody>
|
|
</table>
|
|
</section>
|
|
|
|
<h3 class="rb-cat-head">{{localize "RINGBRP.Tab.Weapons"}}
|
|
<a class="rb-add" data-action="item-create" data-type="weapon"><i class="fas fa-plus"></i></a></h3>
|
|
<table class="rb-table">
|
|
<thead><tr><th>{{localize "RINGBRP.Sheet.Weapon"}}</th><th>{{localize "RINGBRP.Sheet.Damage"}}</th><th>{{localize "RINGBRP.Sheet.Range"}}</th><th>{{localize "RINGBRP.Sheet.Ammo"}}</th><th></th></tr></thead>
|
|
<tbody>
|
|
{{#each weapons as |w|}}
|
|
<tr data-item-id="{{w.id}}">
|
|
<td><a data-action="roll-attack" class="rb-strong">{{w.name}}</a></td>
|
|
<td><a data-action="roll-damage">{{#if (rbEq w.system.damage.mode "byBand")}}{{w.system.damage.short}}/{{w.system.damage.medium}}/{{w.system.damage.long}}{{else}}{{w.system.damage.formula}}{{/if}}</a></td>
|
|
<td>{{w.system.rangeSummary}}</td>
|
|
<td>{{#if w.system.ammo.capacity}}{{w.system.ammo.loaded}}/{{w.system.ammo.capacity}}{{/if}}</td>
|
|
<td class="rb-actions"><a data-action="item-edit"><i class="fas fa-edit"></i></a><a data-action="item-delete"><i class="fas fa-trash"></i></a></td>
|
|
</tr>
|
|
{{/each}}
|
|
</tbody>
|
|
</table>
|
|
<ol class="rb-list">
|
|
{{#each armours as |a|}}
|
|
<li data-item-id="{{a.id}}">
|
|
<a class="rb-equip {{#if a.system.equipped}}on{{/if}}" data-action="toggle-equipped"><i class="fas fa-shield-halved"></i></a>
|
|
<span class="rb-strong">{{a.name}}</span><span class="rb-dim">{{a.system.points}}</span>
|
|
<a data-action="item-edit"><i class="fas fa-edit"></i></a><a data-action="item-delete"><i class="fas fa-trash"></i></a>
|
|
</li>
|
|
{{/each}}
|
|
</ol>
|
|
<a class="rb-add-wide" data-action="item-create" data-type="armour"><i class="fas fa-plus"></i> {{localize "RINGBRP.Tab.Armour"}}</a>
|
|
</div>
|
|
|
|
<div class="tab" data-group="primary" data-tab="bio">
|
|
<label class="rb-block">{{localize "RINGBRP.Sheet.Tactics"}}</label>
|
|
<textarea name="system.tactics" rows="6">{{sys.tactics}}</textarea>
|
|
<label class="rb-block">{{localize "RINGBRP.Sheet.Notes"}}</label>
|
|
{{editor notesHTML target="system.notes" button=true editable=editable engine="prosemirror"}}
|
|
</div>
|
|
</section>
|
|
</form>
|