adventure: give the Siege a war room instead of a Matrix-only rumour
The Siege is the one mechanic where the whole town works on a single object, and it existed exclusively in Matrix — so anybody not in the room at the time never knew it happened. A communal event nobody can see is a communal event that fails. gogobee now pushes the war room on the roster tick and Pete replaces its copy, the same snapshot contract the board has and for the same reason: the shared pool is state, not history, and a retried snapshot would be a lie about how much HP is left. /adventure/siege draws it. The load-bearing detail is one CSS line — the health bar has a width transition, so a poll that lands a lower pool slides the bar down instead of snapping it. That is the difference between watching the town chip a boss down and reading a report about it. Alongside: a countdown to the window's close, past sieges with the bar each one ended on, and the muster split into who took today's bout and who still has one going spare — the mechanic is one fight per person per day, so that second column is a hit the town hasn't taken yet. The opt-out rule here deliberately differs from the board's. The board omits an opted-out player outright, because class + level + zone re-identifies them. A Siege contributor is anonymised instead: their damage is part of what the town did to the boss, and deleting it would understate the shared effort and stop the totals adding up. They keep their rank, lose their name, and carry no token — so there is no link back to a page that names them. An opted-out player who never fought is still omitted; there is nothing to account for. siege_start / siege_win / siege_loss are wired on the gogobee side; the templates for all three have been sitting unused in renderAdventure since the section shipped.
This commit is contained in:
@@ -94,6 +94,56 @@ CREATE INDEX IF NOT EXISTS idx_adv_events_subject ON adventure_events(subject, o
|
||||
CREATE INDEX IF NOT EXISTS idx_adv_events_opponent ON adventure_events(opponent, occurred_at DESC) WHERE opponent IS NOT NULL AND opponent <> '';
|
||||
CREATE INDEX IF NOT EXISTS idx_adv_events_type ON adventure_events(event_type, occurred_at DESC);
|
||||
|
||||
-- The Siege. Three tables, all fed by one gogobee push and all replaced whole,
|
||||
-- because the Siege is state, not history — the same contract as the roster.
|
||||
--
|
||||
-- The split is by lifetime, not by convenience. adventure_siege is the single
|
||||
-- live boss (a CHECK-pinned one-row table, like adventure_roster_meta, so "no
|
||||
-- Siege camped" is a row saying active=0 rather than an ambiguous empty table).
|
||||
-- adventure_siege_defenders is the muster for that one boss and dies with it.
|
||||
-- adventure_siege_history outlives both, and is the reason the current Siege
|
||||
-- feels like it counts: a health bar with nothing behind it is a progress bar.
|
||||
CREATE TABLE IF NOT EXISTS adventure_siege (
|
||||
id INTEGER PRIMARY KEY CHECK (id = 1),
|
||||
active INTEGER NOT NULL DEFAULT 0,
|
||||
boss_id INTEGER NOT NULL DEFAULT 0,
|
||||
boss_name TEXT NOT NULL DEFAULT '',
|
||||
tier INTEGER NOT NULL DEFAULT 0,
|
||||
hp_current INTEGER NOT NULL DEFAULT 0,
|
||||
hp_max INTEGER NOT NULL DEFAULT 0,
|
||||
starts_at INTEGER NOT NULL DEFAULT 0,
|
||||
ends_at INTEGER NOT NULL DEFAULT 0,
|
||||
bouts_today INTEGER NOT NULL DEFAULT 0,
|
||||
snapshot_at INTEGER NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
-- pos is the push order, which is gogobee's ranking (damage desc). Kept as the
|
||||
-- key rather than the token because an opted-out defender carries NO token — the
|
||||
-- board shows their rank and their damage as "an adventurer" and offers no link,
|
||||
-- so several rows can legitimately be tokenless and they must not collide.
|
||||
CREATE TABLE IF NOT EXISTS adventure_siege_defenders (
|
||||
pos INTEGER PRIMARY KEY,
|
||||
token TEXT NOT NULL DEFAULT '',
|
||||
name TEXT NOT NULL,
|
||||
level INTEGER NOT NULL DEFAULT 0,
|
||||
fights INTEGER NOT NULL DEFAULT 0,
|
||||
damage INTEGER NOT NULL DEFAULT 0,
|
||||
fought_today INTEGER NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS adventure_siege_history (
|
||||
boss_id INTEGER PRIMARY KEY,
|
||||
boss_name TEXT NOT NULL,
|
||||
tier INTEGER NOT NULL DEFAULT 0,
|
||||
outcome TEXT NOT NULL, -- "defeated" | "survived"
|
||||
hp_remaining INTEGER NOT NULL DEFAULT 0,
|
||||
hp_max INTEGER NOT NULL DEFAULT 0,
|
||||
defenders INTEGER NOT NULL DEFAULT 0,
|
||||
mvp TEXT NOT NULL DEFAULT '',
|
||||
mvp_fights INTEGER NOT NULL DEFAULT 0,
|
||||
ended_at INTEGER NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
-- A signed-in buyer's own euro balance, as of the last snapshot gogobee pushed.
|
||||
-- Keyed by localpart (== Authentik preferred_username == the session's Username),
|
||||
-- a *separate keyspace* from the anonymous roster tokens on purpose: it is only
|
||||
|
||||
Reference in New Issue
Block a user