A Foundry VTT game system: Basic Roleplaying d100 in a drab British agency that deals with crossings, where reality keeps a record of what belongs where and can be made to get it wrong. Contents: - rules.mjs as the single authority for every rule formula, with 40 rules and 132 spot-checked values. Nothing may restate a formula it defines. - 25 postings, each with a key characteristic, trained skills, talents and its own kit; a 175-item catalogue across four eras; 231 generated icons. - Reaction Lanes initiative, re-rolled every round including the first. - Coherence, and the Quiet Arts, which only ever arrive from a crossing gone wrong. - Graded defences: only a critical stops a critical. - Borrowed Authority — access as a record rather than a hacking roll. - A case generator, and a rules journal generated from rules.mjs so the book cannot drift from the code. Built compendia are committed so a remote Foundry needs only the files. node_modules is not; `npm install` restores it for rebuilding content. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
256 lines
14 KiB
Handlebars
256 lines
14 KiB
Handlebars
<form class="ringbrp-sheet rb-expedition" autocomplete="off">
|
|
|
|
<header class="rb-header">
|
|
<img class="rb-portrait" src="{{actor.img}}" data-edit="img"/>
|
|
<div class="rb-headline">
|
|
<input class="rb-name" name="name" type="text" value="{{actor.name}}"/>
|
|
<div class="rb-origin">
|
|
<label>{{localize "RINGBRP.Expedition.Destination"}}
|
|
<input type="text" name="system.journey.destinationName" value="{{sys.journey.destinationName}}"/></label>
|
|
<label>{{localize "RINGBRP.Expedition.Knowledge"}}
|
|
<select name="system.journey.destinationKnowledge">
|
|
<option value="known" {{#if (rbEq sys.journey.destinationKnowledge "known")}}selected{{/if}}>{{localize "RINGBRP.Expedition.Known"}}</option>
|
|
<option value="rumoured" {{#if (rbEq sys.journey.destinationKnowledge "rumoured")}}selected{{/if}}>{{localize "RINGBRP.Expedition.Rumoured"}}</option>
|
|
<option value="deduced" {{#if (rbEq sys.journey.destinationKnowledge "deduced")}}selected{{/if}}>{{localize "RINGBRP.Expedition.Deduced"}}</option>
|
|
</select></label>
|
|
</div>
|
|
<div class="rb-origin">
|
|
<label>{{localize "RINGBRP.Expedition.Elapsed"}}
|
|
<input type="number" name="system.journey.elapsedDays" value="{{sys.journey.elapsedDays}}"/></label>
|
|
<span class="rb-progress">{{localize "RINGBRP.Expedition.Progress"}}
|
|
<strong>{{resolvedLegs}} / {{sys.journey.legs.length}}</strong></span>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
{{!-- ==========================================================
|
|
The three tracks are the whole game. They get the space.
|
|
========================================================== --}}
|
|
<section class="rb-tracks {{#if sys.inCrisis}}rb-crisis{{/if}}">
|
|
{{#each tracks as |t|}}
|
|
<div class="rb-track {{#if t.crisis}}rb-track-crisis{{/if}} {{#if t.low}}rb-track-low{{/if}}">
|
|
<span class="rb-track-label">{{localize t.label}}</span>
|
|
<div class="rb-track-row">
|
|
<a data-action="track-adjust" data-track="{{t.key}}" data-delta="-1" class="rb-track-btn">−</a>
|
|
<span class="rb-track-val">{{t.value}}</span>
|
|
<a data-action="track-adjust" data-track="{{t.key}}" data-delta="1" class="rb-track-btn">+</a>
|
|
</div>
|
|
<span class="rb-track-max">{{localize "RINGBRP.Expedition.Of"}} {{t.max}}</span>
|
|
<div class="rb-track-pips">
|
|
{{#each t.pips as |on|}}<span class="rb-pip {{#if on}}on{{/if}}"></span>{{/each}}
|
|
</div>
|
|
</div>
|
|
{{/each}}
|
|
</section>
|
|
{{#if sys.inCrisis}}<p class="rb-crisis-note">{{localize "RINGBRP.Expedition.Crisis"}}</p>{{/if}}
|
|
|
|
<div class="rb-caseline">
|
|
<a data-action="toggle-across" class="rb-across {{#if sys.across}}on{{/if}}">
|
|
{{#if sys.across}}{{localize "RINGBRP.Case.Across"}}{{else}}{{localize "RINGBRP.Case.Home"}}{{/if}}</a>
|
|
<a data-action="debrief" class="rb-debrief {{#if sys.across}}rb-disabled{{/if}}">{{localize "RINGBRP.Case.Debrief"}}</a>
|
|
<a data-action="close-case" class="rb-closecase {{#if sys.across}}rb-disabled{{/if}}"
|
|
title="{{localize 'RINGBRP.Case.CloseHint'}}">{{localize "RINGBRP.Case.Close"}}</a>
|
|
</div>
|
|
|
|
<section class="rb-crossbox {{#if crossing.across}}rb-crossbox-across{{/if}}">
|
|
<div class="rb-crossbox-head">
|
|
<span class="rb-crossbox-title">{{localize "RINGBRP.Cross.Panel"}}</span>
|
|
{{#if crossing.across}}
|
|
<a class="rb-cbtn rb-cross-all" data-action="cross-return-all"
|
|
data-tooltip="{{localize 'RINGBRP.Cross.AllHint'}}">{{localize "RINGBRP.Cross.BringHome"}}</a>
|
|
{{else}}
|
|
<a class="rb-cbtn" data-action="cross-out">{{localize "RINGBRP.Cross.GoAcross"}}</a>
|
|
{{/if}}
|
|
</div>
|
|
|
|
{{#if crossing.across}}
|
|
<p class="rb-hint">{{localize "RINGBRP.Cross.PanelHint"}}</p>
|
|
<table class="rb-table rb-crosstable">
|
|
<thead><tr>
|
|
<th class="rb-col-loc">{{localize "RINGBRP.Cross.Agent"}}</th>
|
|
<th class="rb-col-num">{{localize "RINGBRP.Sheet.Skill"}}</th>
|
|
<th class="rb-col-num">{{localize "RINGBRP.Coh.Short"}}</th>
|
|
<th class="rb-col-num">{{localize "RINGBRP.Cross.Aimed"}}</th>
|
|
<th></th>
|
|
</tr></thead>
|
|
<tbody>
|
|
{{#each crossing.members as |m|}}
|
|
<tr data-actor-id="{{m.id}}">
|
|
<td class="rb-col-loc">{{m.name}}{{#if m.arts}} <span class="rb-artmark" data-tooltip="{{localize 'RINGBRP.Cross.HasArts'}}">✦{{m.arts}}</span>{{/if}}</td>
|
|
<td class="rb-col-num">{{m.rating}}%</td>
|
|
<td class="rb-col-num" data-tooltip="{{m.band}}">{{m.coherence}}</td>
|
|
<td class="rb-col-num {{#if m.penalty}}rb-warn{{/if}}">{{m.effective}}%</td>
|
|
<td class="rb-actions">
|
|
<a class="rb-cbtn" data-action="cross-return">{{localize "RINGBRP.Cross.Return"}}</a>
|
|
{{#if ../crossing.aimer}}
|
|
<a class="rb-cbtn" data-action="cross-return-aided"
|
|
data-tooltip="{{localize 'RINGBRP.Cross.AidedBy'}} {{../crossing.aimer.name}} {{../crossing.aimer.rating}}%">{{localize "RINGBRP.Cross.Aided"}}</a>
|
|
{{/if}}
|
|
</td>
|
|
</tr>
|
|
{{/each}}
|
|
</tbody>
|
|
</table>
|
|
{{else}}
|
|
<p class="rb-hint">{{localize "RINGBRP.Cross.HomeHint"}}</p>
|
|
{{/if}}
|
|
</section>
|
|
|
|
<nav class="sheet-tabs rb-tabs" data-group="primary">
|
|
<a class="item" data-tab="journey">{{localize "RINGBRP.Expedition.Journey"}}</a>
|
|
<a class="item" data-tab="research">{{localize "RINGBRP.Expedition.Research"}}{{#if projects.length}} ({{projects.length}}){{/if}}</a>
|
|
<a class="item" data-tab="contacts">{{localize "RINGBRP.Expedition.Contacts"}}</a>
|
|
<a class="item" data-tab="notes">{{localize "RINGBRP.Tab.Bio"}}</a>
|
|
</nav>
|
|
|
|
<section class="sheet-body rb-body">
|
|
|
|
<div class="tab" data-group="primary" data-tab="journey">
|
|
|
|
{{!-- What happens next, stated plainly. This was the sheet's biggest gap. --}}
|
|
{{#if nextLeg}}
|
|
<section class="rb-nextleg">
|
|
<div class="rb-nextleg-head">
|
|
<span class="rb-nextleg-n">{{localize "RINGBRP.Expedition.Leg"}} {{rbAdd nextLeg.index 1}}</span>
|
|
<span class="rb-nextleg-terrain">{{nextLeg.terrain}}</span>
|
|
{{#unless nextLeg.explored}}<span class="rb-unexplored">{{localize "RINGBRP.Expedition.Unexplored"}}</span>{{/unless}}
|
|
</div>
|
|
<div class="rb-nextleg-stats">
|
|
<span><span class="rb-dl">{{localize "RINGBRP.Item.Difficulty"}}</span><span class="rb-dv">{{legDifficulty}}</span></span>
|
|
<span><span class="rb-dl">{{localize "RINGBRP.Expedition.Mod"}}</span><span class="rb-dv {{#if nextLegNegative}}rb-warn{{/if}}">{{rbSign nextLeg.modifier}}%</span></span>
|
|
<span><span class="rb-dl">{{localize "RINGBRP.Expedition.Days"}}</span><span class="rb-dv">{{nextLeg.days}}</span></span>
|
|
<span><span class="rb-dl">{{localize "RINGBRP.Expedition.Cost"}}</span><span class="rb-dv">−{{nextLeg.costSupply}} {{localize "RINGBRP.Expedition.Supply"}}{{#if nextLeg.costPower}} −{{nextLeg.costPower}} {{localize "RINGBRP.Expedition.Power"}}{{/if}}</span></span>
|
|
{{#if navigator}}
|
|
<span><span class="rb-dl">{{localize "RINGBRP.Expedition.Navigator"}}</span><span class="rb-dv">{{navigator.name}} {{navigator.rating}}%</span></span>
|
|
{{/if}}
|
|
</div>
|
|
{{#if nextLeg.forcedEncounterId}}
|
|
<p class="rb-nextleg-encounter">{{nextLeg.forcedEncounterId}}</p>
|
|
{{/if}}
|
|
{{#if navigator}}
|
|
<div class="rb-rolltarget">
|
|
<span class="rb-rolltarget-label">{{localize "RINGBRP.Expedition.RollTarget"}}</span>
|
|
<span class="rb-rolltarget-val">{{rollTarget}}%</span>
|
|
<span class="rb-rolltarget-note">{{navigator.name}} {{navigator.rating}}% · {{legDifficulty}} · {{rbSign nextLeg.modifier}}%</span>
|
|
</div>
|
|
{{/if}}
|
|
<button type="button" class="rb-bigbutton" data-action="resolve-leg" {{#if blocked}}disabled{{/if}}>
|
|
{{localize "RINGBRP.Expedition.ResolveLeg"}}</button>
|
|
{{#if sys.inCrisis}}<p class="rb-hint rb-hint-warn">{{localize "RINGBRP.Expedition.CrisisBlocks"}}</p>{{/if}}
|
|
{{#unless navigator}}<p class="rb-hint rb-hint-warn">{{localize "RINGBRP.Notify.NoNavigator"}}</p>{{/unless}}
|
|
</section>
|
|
{{else}}
|
|
<p class="rb-empty">{{localize "RINGBRP.Expedition.JourneyComplete"}}</p>
|
|
{{/if}}
|
|
|
|
<h3 class="rb-cat-head">{{localize "RINGBRP.Expedition.Itinerary"}}</h3>
|
|
<table class="rb-table rb-itinerary">
|
|
<thead><tr>
|
|
<th>#</th><th>{{localize "RINGBRP.Expedition.Terrain"}}</th>
|
|
<th>{{localize "RINGBRP.Expedition.Mod"}}</th><th>{{localize "RINGBRP.Expedition.Days"}}</th>
|
|
<th>{{localize "RINGBRP.Expedition.Explored"}}</th><th>{{localize "RINGBRP.Expedition.Event"}}</th>
|
|
</tr></thead>
|
|
<tbody>
|
|
{{#each sys.journey.legs as |leg i|}}
|
|
<tr class="{{#if leg.resolved}}rb-done{{/if}}{{#if (rbEq i ../sys.journey.currentLeg)}} rb-current{{/if}}">
|
|
<td class="rb-leg-n">{{rbAdd i 1}}</td>
|
|
<td>{{leg.terrain}}</td>
|
|
<td>{{rbSign leg.modifier}}%</td>
|
|
<td>{{leg.days}}</td>
|
|
<td>{{#if leg.explored}}<span class="rb-yes">✓</span>{{else}}<span class="rb-no">—</span>{{/if}}</td>
|
|
<td class="rb-leg-event">{{#if leg.forcedEncounterId}}{{leg.forcedEncounterId}}{{/if}}</td>
|
|
</tr>
|
|
{{/each}}
|
|
</tbody>
|
|
</table>
|
|
<p class="rb-hint">{{localize "RINGBRP.Expedition.TableNote"}}</p>
|
|
</div>
|
|
|
|
<div class="tab" data-group="primary" data-tab="research">
|
|
<h3 class="rb-cat-head">{{localize "RINGBRP.Expedition.Projects"}}</h3>
|
|
{{#each projects as |p|}}
|
|
<div class="rb-project" data-item-id="{{p.id}}">
|
|
<div class="rb-project-head">
|
|
<a class="rb-strong" data-action="item-edit">{{p.name}}</a>
|
|
<span class="rb-project-status">{{p.system.status}}</span>
|
|
</div>
|
|
<p class="rb-project-q">{{p.system.question}}</p>
|
|
<ol class="rb-steps rb-steps-compact">
|
|
{{#each p.system.steps as |s i|}}
|
|
<li class="rb-step {{#if (rbEq i ../p.system.currentStep)}}rb-step-current{{/if}} {{#if (rbEq s.state 'complete')}}rb-step-done{{/if}}">
|
|
<span class="rb-step-n">{{rbAdd i 1}}</span>
|
|
<span class="rb-step-type">{{s.stepType}}</span>
|
|
<span class="rb-dim">{{#if s.hours}}{{s.hours}} h{{else}}{{localize "RINGBRP.Expedition.InPlay"}}{{/if}} · {{s.difficulty}}</span>
|
|
{{#if s.objective}}<div class="rb-objective">{{s.objective}}</div>{{/if}}
|
|
</li>
|
|
{{/each}}
|
|
</ol>
|
|
</div>
|
|
{{else}}
|
|
<p class="rb-empty">{{localize "RINGBRP.Expedition.NoProjects"}}</p>
|
|
{{/each}}
|
|
|
|
<h3 class="rb-cat-head">{{localize "RINGBRP.Expedition.Discoveries"}}</h3>
|
|
<ol class="rb-list">
|
|
{{#each discoveries as |d|}}
|
|
<li data-item-id="{{d.id}}">
|
|
<span class="rb-disc-state {{#if d.system.published}}on{{/if}}"></span>
|
|
<a class="rb-strong" data-action="item-edit">{{d.name}}</a>
|
|
<span class="rb-dim">{{d.system.question}}</span>
|
|
</li>
|
|
{{else}}
|
|
<li><span class="rb-dim">{{localize "RINGBRP.Expedition.NoDiscoveries"}}</span></li>
|
|
{{/each}}
|
|
</ol>
|
|
</div>
|
|
|
|
<div class="tab" data-group="primary" data-tab="contacts">
|
|
<h3 class="rb-cat-head">{{localize "RINGBRP.Expedition.Standings"}}</h3>
|
|
<ol class="rb-list rb-standings">
|
|
{{#each standings as |s|}}
|
|
<li>
|
|
<span class="rb-strong">{{s.name}}</span>
|
|
<span class="rb-standing-value {{s.cls}}">{{s.display}}</span>
|
|
<span class="rb-dim">{{s.word}}</span>
|
|
<span class="rb-dim">{{localize "RINGBRP.Expedition.Encounters"}} {{s.encounters}}</span>
|
|
</li>
|
|
{{else}}
|
|
<li><span class="rb-dim">{{localize "RINGBRP.Expedition.NoContacts"}}</span></li>
|
|
{{/each}}
|
|
</ol>
|
|
|
|
<h3 class="rb-cat-head">{{localize "RINGBRP.Expedition.Groups"}}</h3>
|
|
{{#each groups as |g|}}
|
|
<div class="rb-group" data-item-id="{{g.id}}">
|
|
<div class="rb-project-head">
|
|
<a class="rb-strong" data-action="item-edit">{{g.name}}</a>
|
|
<span class="rb-dim">{{#if isGM}}{{g.population}}{{/if}}</span>
|
|
</div>
|
|
{{#each g.taboos as |t|}}<p class="rb-taboo{{#if t.secret}} rb-secret{{/if}}">{{#if t.secret}}<span class="rb-gmonly">{{localize "RINGBRP.Item.GMOnly"}}</span>{{/if}}{{localize "RINGBRP.Item.Taboo"}}: {{t.text}}</p>{{/each}}
|
|
{{#each g.desires as |d|}}<p class="rb-desire{{#if d.secret}} rb-secret{{/if}}">{{#if d.secret}}<span class="rb-gmonly">{{localize "RINGBRP.Item.GMOnly"}}</span>{{/if}}{{localize "RINGBRP.Item.Desire"}}: {{d.text}}</p>{{/each}}
|
|
{{#if g.offerForm}}<p class="rb-offerform rb-secret"><span class="rb-gmonly">{{localize "RINGBRP.Item.GMOnly"}}</span>{{localize "RINGBRP.Item.OfferForm"}}: {{g.offerForm}}</p>{{/if}}
|
|
{{#if g.nothingKnown}}<p class="rb-dim rb-nothingknown">{{localize "RINGBRP.Expedition.NothingLearned"}}</p>{{/if}}
|
|
<div class="rb-contact-bar" data-group-id="{{g.system.groupId}}">
|
|
<a class="rb-cbtn" data-action="contact" data-stage="read">{{localize "RINGBRP.Contact.Read"}}</a>
|
|
<a class="rb-cbtn" data-action="contact" data-stage="signal">{{localize "RINGBRP.Contact.Signal"}}</a>
|
|
<a class="rb-cbtn {{#if g.offerSpent}}rb-disabled{{/if}}" data-action="contact" data-stage="offer">{{localize "RINGBRP.Contact.Offer"}}</a>
|
|
<a class="rb-cbtn rb-cbtn-alt" data-action="new-encounter">{{localize "RINGBRP.Contact.NewEncounter"}}</a>
|
|
<span class="rb-cstate">
|
|
{{localize "RINGBRP.Contact.Impression"}} <strong>{{g.impression}}</strong> ·
|
|
{{localize "RINGBRP.Contact.Standing"}} <strong>{{g.standing}}</strong>
|
|
{{#if g.pidgin}} · {{localize "RINGBRP.Contact.Pidgin"}}{{/if}}
|
|
{{#if g.offerSpent}} · {{localize "RINGBRP.Contact.OfferSpentShort"}}{{/if}}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
{{else}}
|
|
<p class="rb-empty">{{localize "RINGBRP.Expedition.NoGroups"}}</p>
|
|
{{/each}}
|
|
</div>
|
|
|
|
<div class="tab" data-group="primary" data-tab="notes">
|
|
{{editor notesHTML target="system.notes" button=true editable=editable engine="prosemirror"}}
|
|
</div>
|
|
</section>
|
|
</form>
|