- Documents recent infrastructure cleanup (8 CTs destroyed, 35 DNS records removed, Headscale cleanup) - Adds 24 new runbooks covering Authentik, PeerTube, Meshtastic, RECON, Proxmox, Mailcow, Internet Archive, GPU routing - Adds project documentation for headscale, vaultwarden, peertube, matrix, mmud, advbbs, arr stack - Updates services.md, environment.md, caddy.md, authentik.md to match live infrastructure - Removes 4 deprecated runbook duplicates (canonical versions live in projects/) - Adds .gitignore for binary archives and editor temp files Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
15 KiB
Task: MMUD Phase 5 — Endgame Modes
Before Writing Any Code
Re-read these sections of /home/zvx/projects/mmud/docs/planned.md:
- Endgame: Three Rotating Modes (all of it — mode selection, R&E with Pursuer + support roles, Raid Boss with mechanic tables + phases, Hold the Line with regen + checkpoints + floor bosses)
- The Darkcragg Depths (dungeon name context)
- Floor Boss Mechanic Tables (all 4 floor tables)
- Bounties During Hold the Line
Also re-read config.py for: HTL_REGEN_ROOMS_PER_DAY, FLOOR_BOSS_MECHANICS, WARDEN_HP/REGEN, RAID_BOSS_HP_PER_PLAYER/CAP/REGEN/MECHANIC_TABLE/PHASES, PURSUER_ADVANCE_RATE/SPAWN_DISTANCE/RELAY_RESET_DISTANCE, WARD/LURE constants, ENDGAME_MODES.
Phase 4 already generates floor bosses, raid boss pre-config, and bounties. This phase wires the runtime game logic for all three modes.
Phase 5 Deliverables
All three endgame modes are playable. The epoch vote selects the mode. Each mode has its own win condition, progression mechanics, and broadcasts.
1. Epoch Vote System
src/systems/vote.py (new file)
- Day 30 trigger (already in daytick.py — wire it to the vote system)
vote <mode>command at barkeep — free action. Modes:retrieve,raid,hold(or numbers 1/2/3).- Votes are public — broadcast on cast: "🗳 {name} voted {mode}."
- Votes can be changed up until epoch ends. UPSERT into epoch_votes table.
- Tally on epoch end: most votes wins. Tiebreak: longest-unplayed mode. No quorum — 1 vote decides if only 1 player votes. Zero votes → longest-unplayed auto-selected.
epoch_generate.pyalready accepts endgame mode — wire the vote result into the next epoch's generation.
2. Hold the Line — Runtime Logic
src/systems/endgame_htl.py (new file)
Room clearing:
- All dungeon rooms start hostile (
htl_cleared = 0) - Killing all monsters in a room sets
htl_cleared = 1with timestamp - Regen ticks (already in daytick.py) revert rooms: pick N random cleared non-checkpoint rooms per floor per day, set
htl_cleared = 0, respawn monsters. Spread ticks across the day (floor 2 at 5/day = 1 room every ~5 hours). - Cleared rooms are safe — no random encounters. Reverted rooms respawn monsters.
Checkpoints:
- Checkpoint rooms are defined during worldgen (is_checkpoint = 1 in rooms table). 3 per floor (hub, midpoint, stairway), 1 on floor 4 (Warden).
- Establishment condition: all rooms in the checkpoint cluster (checkpoint room + all adjacent rooms) must be cleared within one regen window. Check on each room clear — if the cluster is complete, spawn the floor boss.
- Once the floor boss dies, checkpoint locks permanently.
htl_checkpoints.established = 1. Regen can never revert rooms behind an established checkpoint. - Final checkpoint on each floor (stairway) unlocks the next floor for all players.
Floor bosses:
-
Already generated with rolled mechanics (from Phase 4 bossgen).
-
Floor boss spawns in the checkpoint room when cluster is cleared. Uses the same shared HP pool / chip-and-run combat as bounties.
-
Boss mechanic implementation — each mechanic modifies combat behavior:
armored— damage halved until boss below 50% HPenraged— boss deals double damage below 50% HP, takes 25% moreregenerator— boss heals 10% HP between sessions (check on engagement, apply since last fight)stalwart— first flee attempt per engagement always failswarded— boss has +50% DEF until a discovery secret on the same floor is found (check secret_progress)phasing— boss is immune to damage on even-numbered epoch days (check epoch.day_number)draining— boss steals 10% of damage dealt as HP from the attackersplitting— at 50% HP, boss splits into two half-HP monsters in adjacent rooms. Both must die.rotating_resistance— immune to the highest stat used by the last player who fought it. Track in DB.retaliator— reflects 20% of damage back to attackersummoner— spawns 1 add at start of each engagement. Add must die before boss can be damaged.cursed— player who dealt most damage last session gets -2 to a random stat next login
-
Floor 4 Warden: shared HP pool 300-500, regen at 3%/8h, rolls 2 mechanics from the full table.
-
Warden kill = epoch win. Broadcast: "🏆 The Warden has fallen! The Darkcragg Depths are conquered!"
Broadcasts from DCRG:
- "🏰 Floor {n} Checkpoint {name} established!"
- "⚠ Floor {n} lost {x} rooms. Frontline at {room}."
- "🏰 Floor {n} unlocked! The descent continues."
- "💀 Floor {n} frontline collapsed to Checkpoint {name}. Rally!"
- Floor boss spawned, floor boss killed, Warden progress.
Barkeep integration:
- Grist's recap includes HtL status: floors cleared percentage, checkpoint status, frontline position.
3. Raid Boss — Runtime Logic
src/systems/endgame_raid.py (new file)
Activation:
- On epoch start (if mode is raid_boss), calculate active player count (anyone who entered the dungeon in first 3 days).
- Set raid_boss.hp = 300 × active players, cap 6000. Set raid_boss.hp_max to same value.
- Place boss in a room on floor 3-4 (from pre-generated config in Phase 4).
Combat:
- Same chip-and-run as bounties — shared HP pool, engage/damage/flee.
- Regen: 3%/8h (lazy-evaluated like bounties — calculate on engagement).
- Track contributions in raid_boss_contributors.
Mechanic implementation (2-3 rolled):
windup_strike— every 3rd combat round, next round deals triple damage unless player usesdefendordodgeaction. Adddefendanddodgeas combat commands (cost 1 action, negate the windup).flat_damage_boost— boss damage multiplied by 1.5xretribution— at 75%/50%/25% HP thresholds, burst damage (2x normal) to the player who pushed it past the thresholdaura_damage— player takes 5% max HP unavoidable damage each combat round regardless of DEFextra_regen— regen rate becomes 5%/8h instead of 3%/8harmor_phase— boss takes half damage until: a discovery secret on the floor is found, OR 5+ unique players have contributed damageboss_flees— at 75%/50%/25% HP, boss relocates to random room on same floor. Broadcast from DCRG: "🐉 The {boss} has fled to somewhere on Floor {n}!" Players must find it.regen_burst— once per day at a random hour, boss heals 15% max HP in one tick. Trackable through observation.no_escape— below 25% HP, all flee attempts fail. Fight to the death.summoner— 1-2 adds spawn per engagement, must be killed before boss takes damagelockout— after engaging, player can't reengage for 24 hours. Store lockout_until in raid_boss_contributors.enrage_timer— after 5 combat rounds in a single engagement, boss damage doubles each subsequent round
Phases:
- Phase transitions at 66% and 33% HP (
RAID_BOSS_PHASES). - At each threshold, rolled mechanics intensify. Implementation: each mechanic has a
phase_modifier(phase_num)that scales its effect. E.g., summoner spawns 1 add in phase 1, 2 in phase 2, 3 in phase 3. Windup goes from every 3rd round to every 2nd. - Phase transition broadcasts from DCRG: "🐉 The {boss} enters its second phase!"
Win condition: Boss HP reaches 0. All contributors rewarded. Killing blow gets bonus. Broadcast: "🏆 The {boss} has been slain! Victory belongs to the Darkcragg!"
4. Retrieve and Escape — Runtime Logic
src/systems/endgame_rne.py (new file)
Setup:
- Guardian monster placed on floor 4 during epoch generation (add to bossgen if not already there — a strong but non-boss monster guarding the objective).
escape_runtable tracks run state.
Claiming the objective:
- Player defeats the guardian on floor 4 → objective claimed.
escape_run.active = 1, carrier set, pursuer spawns. - Broadcast from DCRG: "👑 {name} claimed the {objective}! The Pursuer stirs."
- Monster spawn rates double on all floors (multiply spawn chance by
ESCAPE_SPAWN_RATE_MULTIPLIER).
Pursuer:
- Tracks carrier. Advances 1 room toward carrier every 2 carrier actions (
PURSUER_ADVANCE_RATE). - Spawns 3 rooms behind carrier (
PURSUER_SPAWN_DISTANCE). - Track pursuer position in
escape_run.pursuer_room_id. Track fractional ticks inpursuer_ticks. - On every carrier action: increment pursuer_ticks. When pursuer_ticks >= PURSUER_ADVANCE_RATE, advance pursuer 1 room toward carrier (pathfind shortest route), reset ticks.
- When pursuer enters carrier's room: forced combat. Pursuer is invulnerable (takes no damage). Hits hard. Carrier can only flee. Flee uses normal SPD check. Success = carrier moves 1 room. Failure = take damage + try again next action.
Carrier death and relay:
- Carrier dies → objective drops at death room. Broadcast from DCRG: "💀 The carrier has fallen on Floor {n}. The {objective} lies unguarded."
escape_run.objective_dropped = 1,dropped_room_idset.- Any player can
pickupthe objective in that room. - On pickup: pursuer resets to 5 rooms behind new carrier (
PURSUER_RELAY_RESET_DISTANCE). Broadcast: "👑 {name} picks up the {objective}! The Pursuer resets." - Death penalty still applies to the dead carrier (gold loss, respawn in town).
Three support roles:
Blockers:
- Non-carrier in a room between pursuer and carrier. When pursuer reaches a blocker's room, forced combat with the blocker instead of advancing.
- Blocker can't kill pursuer (invulnerable). Each round blocker survives = 1 round pursuer isn't moving.
- Blocker can flee (normal SPD check). Blocker can die.
- Broadcast: "🛡 {name} is blocking the Pursuer on Floor {n}!" and "💀 {name} fell holding the line. The Pursuer advances."
- Implementation: on pursuer advance, check if any player is in the target room. If yes, pursuer enters combat with them instead of continuing.
Warders:
wardcommand in a cleared dungeon room (1 extra action after clearing =WARD_ACTION_COST). Setsrooms.ward_active = 1.- Warded room slows pursuer — takes 2 advance ticks to pass through instead of 1 (
WARD_PURSUER_SLOWDOWN). - Ward breaks after one use (reset to 0 when pursuer passes through).
- No broadcast on warding — silent preparation.
Lures:
lurecommand when on same floor as pursuer. Costs 2 actions (LURE_ACTION_COST).- Pursuer diverts toward lure player for 3 ticks (
LURE_DIVERT_TICKS), then snaps back to carrier tracking. Total delay ~6 ticks (LURE_TOTAL_DELAY_TICKS) including backtrack. - Broadcast: "🎯 {name} lured the Pursuer into {room}! It diverts."
- After divert expires: "👁 The Pursuer has reacquired the carrier."
Pursuer distance broadcasts (from DCRG):
- "👁 The Pursuer is {n} rooms behind the carrier." (every 5 carrier actions)
- "👁 The Pursuer is 3 rooms behind. It's closing."
- "⚠ The Pursuer has reached the carrier!"
Win condition: Any player delivers objective to town (The Last Ember). Broadcast: "🏆 The {objective} has reached the surface! Victory belongs to the Darkcragg!"
- All participants get epoch win credit (tracked in escape_participants by role).
5. Mode Activation in Engine
Update src/core/engine.py and src/core/actions.py:
- On game start, check epoch.endgame_mode. Load the appropriate endgame system.
- Mode-specific commands only available when that mode is active:
- HtL: checkpoint status command, floor control display
- Raid: raid boss status command (
boss— show HP, phase, mechanics discovered so far) - R&E:
pickup,ward,lure,blockcommands. Carrier status. Pursuer distance.
- Combat system needs to dispatch to endgame boss combat (floor boss, raid boss, pursuer) when the target is a special entity. Same chip-and-run framework but with mechanic overlays.
- Endgame status integrated into barkeep recap and stats display.
6. New Combat Commands
For raid boss mechanics:
defend/def— defensive stance. Negates windup strike. Costs 1 dungeon action. Does no damage that round.dodge/dge— evasion. Negates windup strike. Costs 1 dungeon action. Does no damage that round.
For R&E:
pickup— pick up dropped objective in current room. Free action.ward— ward current room after clearing it. 1 dungeon action.lure— lure the Pursuer. 2 dungeon actions.block— (passive) just being in the pursuer's path triggers blocking. No explicit command needed — the system detects it. But add ablockinfo command that shows: "Stand in the Pursuer's path to block. It will fight you instead of advancing."
Rules
- All responses under 150 chars. Test this.
- All broadcasts route through DCRG node, not EMBR.
- Endgame mode commands are only available when that mode is active. Other mode commands return: "That doesn't apply this epoch."
- Boss combat uses the same chip-and-run framework as bounties — shared HP pool, damage persists, flee to disengage.
- Floor boss and raid boss regen is lazy-evaluated (calculate accumulated regen on engagement).
- Use constants from
config.py. - Raw parameterized SQL, no ORM.
- Commit after each mode is working (3 major commits minimum).
Testing
Add to tests/:
tests/test_vote.py— vote casting, changing, public broadcast, tally, tiebreak, zero-vote fallbacktests/test_htl.py— room clearing, regen ticks, checkpoint cluster detection, checkpoint establishment, floor boss spawn on cluster clear, floor unlock, Warden kill = win, rooms behind checkpoint immune to regentests/test_boss_mechanics.py— test each of the 12 mechanic implementations: armored, enraged, regenerator, stalwart, warded, phasing, draining, splitting, rotating_resistance, retaliator, summoner, cursed. Test phase scaling for raid boss.tests/test_raid.py— HP scaling from active players, cap at 6000, regen, phase transitions at 66%/33%, contribution tracking, lockout mechanic, completion + rewardstests/test_rne.py— objective claim, pursuer advancement (2:1 ratio), pursuer in carrier room triggers combat, carrier death drops objective, relay pickup resets pursuer, ward slows pursuer, lure diverts pursuer, blocker intercepts pursuer, win condition on town deliverytests/test_rne_broadcasts.py— all R&E broadcasts fire correctly (claim, distance, blocker, lure, death, relay, victory)
Use in-memory SQLite for tests. All endgame tests should generate a proper epoch first (use epoch_generate with DummyBackend).
Done When
All three endgame modes are playable end-to-end. A Hold the Line epoch can be won by clearing all floors and killing the Warden. A Raid Boss epoch can be won by depleting the boss HP pool through coordinated chip-and-run combat with mechanic discovery. A Retrieve and Escape epoch can be won through a relay of carriers with blockers, warders, and lures supporting. The epoch vote selects the next mode. All broadcasts route through DCRG. All responses under 150 chars, all tests passing. Commit and report.