adventure: let a player act from the web, not just read about it

The equip queue proved the reverse pipe works. This gives it verbs that
play the game: pull out of a run from the adventurer page, take today's
bout from the war room.

Its own table and its own poll, not more actions on equip_orders. Every
column of that table is equip vocabulary (item, slot, tier) and these
verbs act on the character rather than on something it is carrying.

Nothing in a request names an adventurer. The session maps to one
localpart and a localpart to one adventurer, so Pete resolves the
character itself and there is no id on the wire to forge.

The panel's copy is kept honest by the verdict: an applied action hides
the offer it has just spent, a refusal puts the button back. Watching it
run is what put that there, along with the strip's layout — gogobee
answers a bout with a whole sentence of damage, which the equip strip's
two-column row squeezed into a column and wrapped the verb.

Claude-Session: https://claude.ai/code/session_012bxpQQJDjC1mTtLN3VVtBQ
This commit is contained in:
prosolis
2026-07-24 18:55:42 -07:00
parent b19ab5eff0
commit 6b0aae9f4a
10 changed files with 1178 additions and 9 deletions
+38
View File
@@ -195,6 +195,42 @@
</a>
</section>
{{if .HasSelf}}
<!-- Your call. The panels above are a spectator view of a run going well or
badly; this is the one thing the watcher can do about it. Owner-only, and
shown whether or not the mark is currently on a run — the board is up to
two minutes stale, so hiding the button on a snapshot that says "in town"
would be the page refusing an action the game would have allowed. gogobee
answers rejected_not_running if it really has ended, and that answer shows
up in the strip below. -->
<section id="adv-actions" class="adv-actions mt-6 rounded-3xl bg-[color:var(--card)] border-2 border-[color:var(--ink)]/10 p-6 shadow-pete">
<h2 class="font-display text-xl font-bold mb-1">Your call</h2>
<p class="text-sm text-[color:var(--ink)]/60 mb-4">
Asked for here, done on the game box. It picks these up within a few seconds.
</p>
{{/* data-offer marks what stops being true once the extraction lands: there
is no run left to pull out of. Hidden by the script on an applied
verdict, restored on a refusal, which is the one case where the reader
still needs the retry. */}}
<div data-offer>
<button type="button"
class="adv-action-btn rounded-full border border-theme-adventure/40 text-theme-adventure hover:bg-theme-adventure/10 px-4 py-1.5 text-sm font-semibold transition-colors"
data-action="extract"
data-label="Pull out of the run"
data-confirm-label="Yes, pull out"
data-confirm="Pull out of the dungeon now? You keep the loot, XP and coins you're carrying, and the run waits where you left it: you have seven days to go back in. If you're leading a party, it ends the day for all of you.">Pull out of the run</button>
</div>
<!-- The queue is honest: an action lands on the game box's next poll, so a
fresh one reads "asked for", never "done". JS fills this from
/api/adventure/orders. -->
<div id="adv-action-orders-box" class="mt-5 hidden">
<h3 class="font-display text-base font-bold mb-2">What you've asked for</h3>
<ul id="adv-action-orders" class="space-y-1.5 text-xs"></ul>
</div>
</section>
{{end}}
{{if .HasHistory}}
<!-- The record. Public, like the dispatches it's counted from — this is the
same information the /adventure feed already printed, only as numbers
@@ -714,3 +750,5 @@
})();
</script>
{{end}}
{{define "scripts"}}<script src="/static/js/adventure-actions.js" defer></script>{{end}}