Delve — Game Design Documents
Delve is an asynchronous idle MMO with synchronous multiplayer elements, an original fantasy RPG inspired by classic tabletop games. Players create characters, prepare them for dungeon runs, and send them into danger — reviewing detailed results when the run completes. The game is free-to-play with convenience monetization.
Document Index
| # | Document | Description | Status |
|---|---|---|---|
| 01 | Game Overview | Vision, core loop, design pillars, session flow, monetization philosophy | Draft |
| 02 | Character Creation | Species, classes, attributes, backgrounds, starting equipment | Draft |
| 03 | Character Progression | Leveling (1-50), subclasses, feats, skills, Paragon/prestige system | Draft |
| 04 | Combat System | d100 percentile mechanics, skill queue configuration, conditions, death, combat log format | Draft |
| 05 | Equipment and Loot | Gear slots, rarity tiers, weapons, armor, consumables, loot tables | Draft |
| 06 | Quests and Dungeons | Quest board, quest types, encounter types, run timeline, failure analysis, supplies | Draft |
| 07 | Crafting and Gathering | Gathering professions, crafting professions, recipes, material tiers | Draft |
| 08 | Economy and Trading | Currencies, marketplace, NPC vendors, gold sinks, inflation control | Draft |
| 09 | Social and MMO Systems | Guilds, timed lobby parties, raids, friends, chat, mail | Draft |
| 10 | PVP System | Arena PVP, async resolution, seasons, guild wars, faction warfare | Draft |
| 11 | Factions and Reputation | World factions, reputation tiers, faction conflicts, faction vendors | Draft |
| 12 | World and Setting | Aethermere setting, regions, lore (The Sundering), key NPCs | Draft |
| 13 | Idle and Time Mechanics | Offline progress, timers, notifications, anti-burnout design | Draft |
| 14 | Monetization | $5/mo Patron subscription, permanent purchases, no premium currency, ethical principles | Draft |
| 15 | Progression Hooks and Retention | Dailies, weeklies, achievements, seasons, leaderboards, collections | Draft |
Key Design Decisions
- Species & Classes: 9 original species and 9 classes with 6 custom attributes (Might, Logic, Speed, Presence, Fortitude, Luck)
- Combat: d100 percentile dice system with skill queue combat, optimized for idle/async auto-resolution
- Progression: Levels 1-50 with Paragon system beyond cap
- Monetization: F2P + optional $5/mo Patron subscription (50% faster time-based activities). No premium currency, no cosmetic sales, no loot boxes.
- Multiplayer: Timed lobby system — group content starts at scheduled times, players prepare async
- Scope: Full sandbox — combat, crafting, gathering, economy, PVP, factions, social
How to Read These Docs
- Start with 01-Game Overview for the big picture
- 02-04 cover the character and combat core
- 05-06 cover the loot and dungeon loop
- 07-08 cover crafting and economy
- 09-11 cover multiplayer and social systems
- 12 covers the game world
- 13-15 cover meta-systems (time, money, retention)
Each document is self-contained but cross-references related docs where systems interact.
Status
All documents are in Draft status — ready for review and iteration.
Game Overview
Game Pitch
Delve is an asynchronous idle MMO where players create characters inspired by classic tabletop RPG archetypes and send them into dangerous dungeons, questlines, and encounters that resolve over real-world time. Players don’t control combat in real-time — they prepare, strategize, and configure their characters’ behavior, then set them loose and review the results.
Think of it as managing an adventurer’s career. You’re the strategist behind the hero: picking quests, equipping gear, setting combat priorities, stocking supplies, and joining up with other players’ characters for raids. The dungeon happens while you’re at work. You come back, read the battle log, see where things went right or wrong, adapt, and send them out again.
Core Fantasy
You are the mind behind the adventurer. Every decision you make before the dungeon matters — what spells to prioritize, when to use potions, whether to bring extra rations or a second weapon. The dungeon run plays out like a story you get to read, with your preparation determining the outcome.
Core Gameplay Loop
LOGIN
│
▼
REVIEW RESULTS ─── Read the timeline of your last dungeon run.
│ See each encounter: combat rolls, trap checks,
│ loot found, resources consumed.
│ Understand exactly why you succeeded or failed.
│
▼
MANAGE INVENTORY ── Equip new gear, sell unwanted loot,
│ salvage materials, brew potions,
│ restock supplies (rations, torches, scrolls).
│
▼
BROWSE QUESTS ───── View the quest board: bounties, questlines,
│ dungeon crawls, guild raids.
│ Each shows difficulty, estimated duration,
│ potential rewards, and recommended level.
│
▼
PREPARE ─────────── Configure combat AI priorities.
│ Select spells to memorize.
│ Choose supplies to bring.
│ Set trap detection behavior.
│ Join a party lobby (if multiplayer quest).
│
▼
COMMIT ──────────── Lock in and send your character on the quest.
│ The run resolves over real-world time.
│
▼
IDLE ───────────── While waiting, you can:
│ • Manage other characters (if unlocked)
│ • Craft items or gather materials
│ • Browse the marketplace
│ • Chat with guild members
│ • Review leaderboards
│ • Log off entirely — the run continues
│
▼
NOTIFICATION ────── "Your dungeon run is complete!"
│
└──► Return to REVIEW RESULTS
Session Flow
Quick Session (5-10 minutes)
- Review last run results
- Sell junk loot, equip upgrades
- Pick a new quest from the board
- Use default combat settings or make minor tweaks
- Commit and log off
Active Session (30-60 minutes)
- Review results in detail — study the combat log
- Reorganize inventory, craft consumables
- Browse marketplace for upgrades
- Fine-tune combat AI configuration
- Coordinate with guild for an upcoming raid
- Commit to a quest
- While waiting: gather materials, manage a second character, chat
Check-in Session (1-2 minutes)
- See notification that a run completed
- Glance at results (success/fail, notable loot)
- Quick-commit to another quest of similar difficulty
- Log off
Target Audience
- Tabletop RPG fans who love character building and tactical decisions but don’t always have time for a full session
- Idle game enthusiasts who want deeper progression and strategy than typical idle/incremental games
- MMO players who want social features (guilds, raids, PVP) without demanding a fixed play schedule
- Strategy gamers who enjoy optimizing builds, min-maxing gear, and solving the puzzle of dungeon preparation
Platform Considerations
- Web-first — accessible from any browser, desktop or mobile
- Mobile-friendly — the async nature makes it perfect for phone check-ins
- No downloads required — low barrier to entry for F2P
- Push notifications — run complete, raid starting, auction sold
Monetization Philosophy
Delve is free to play with an optional $5/month subscription and small permanent purchases. No premium currency. No loot boxes. No cosmetic microtransactions.
The Patron Subscription ($5/month)
- 50% faster dungeon runs, crafting, and gathering
- That’s it. No bonus XP, no bonus loot, no combat advantages. Subscribers wait less — they don’t hit harder.
Permanent Purchases
- Additional character slots ($2 each, up to 5 extra)
- Expanded bank/storage space ($1 per upgrade)
- Name changes and appearance resets ($1 each)
What You CANNOT Buy
- Gear, weapons, or items with stats
- XP, gold, or loot boosts
- Premium currency (doesn’t exist)
- Loot boxes or randomized purchases (don’t exist)
- Combat advantages of any kind
- Cosmetics (earned through gameplay only)
- Content access (all gameplay is free)
Why This Model
Players should never feel like they lost a dungeon run because someone else spent money. A subscriber and a free player in the same dungeon have the same chance of success. The game makes money by being good enough that players want to support it — not by creating friction that drives impulse purchases.
See 14-monetization.md for full details.
What Makes Delve Different
| Feature | Typical Idle Game | Typical MMO | Delve |
|---|---|---|---|
| Time investment | Minutes/day | Hours/day | 5 min to 1 hour, your choice |
| Decision depth | Low (upgrade numbers) | High (real-time combat) | High (preparation & strategy) |
| Social features | Minimal | Extensive but time-demanding | Async-friendly guilds, raids, PVP |
| Failure feedback | “You died” | Real-time awareness | Detailed post-run analysis |
| Character building | Simple upgrades | Complex but real-time | Deep builds resolved automatically |
Design Pillars
-
Preparation is gameplay. The decisions you make before a dungeon run ARE the game. Choosing the right spells, configuring smart combat behavior, bringing enough supplies — this is where skill lives.
-
Failure is a teacher. When a run fails, the player should understand exactly why and what to change. The run timeline is the core feedback mechanism.
-
Respect the player’s time. Whether you have 5 minutes or an hour, there’s something meaningful to do. You should never feel punished for not logging in.
-
Social without synchronous. Guilds, raids, and PVP should be engaging without requiring everyone to be online at the same time. The timed lobby system makes group content accessible to all schedules.
-
Earn, don’t buy, your power. Every piece of gear, every level, every achievement is earned through play. Money buys time savings, never power.
Character Creation
Overview
Character creation is the player’s first major decision point. Every option — species, class, attribute spread, background — should feel distinct and have a clear gameplay impact. The goal is meaningful choice without overwhelming new players.
Players start with one character slot for free. Additional slots can be purchased from the in-game shop (see 14-monetization.md).
Character Identity
Name
- Must be unique server-wide
- 3-20 characters, letters and spaces only
- Filtered for inappropriate content
- Can be changed for a small one-time purchase ($1 — see 14-monetization.md)
Appearance
- Visual portrait selection from a curated set per species
- Cosmetic armor skins overlay the portrait
- Additional portraits earned through achievements, faction reputation, and seasonal events
Title
- Displayed alongside the character name (e.g., “Korrin the Unyielding”)
- Earned through achievements, faction reputation, PVP rank, or seasonal events
- Players choose which earned title to display
Attributes
Delve uses six core attributes that affect every system in the game — combat, dungeon exploration, crafting, and social interactions.
The Six Attributes
| Attribute | Abbrev | Combat Role | Dungeon Role |
|---|---|---|---|
| Might | MGT | Melee damage, heavy weapon accuracy, physical skill power | Force doors, carry more supplies, resist grapples, move heavy objects |
| Logic | LOG | Magical damage, spell accuracy, mana pool size | Solve puzzles, identify items, recall enemy lore, detect magical traps |
| Speed | SPD | Ranged accuracy, evasion rating, initiative tie-breaking | Dodge traps, avoid ambushes, act first, navigate hazards |
| Presence | PRS | Support/healing skill power, debuff potency, social skills | Negotiate with NPCs, intimidate enemies, inspire allies, avoid encounters through diplomacy |
| Fortitude | FRT | Max HP, stamina/devotion pool, condition resistance | Resist poison/disease, endure long dungeons, survive environmental hazards, recover more at rest |
| Luck | LCK | Critical hit threshold, critical miss reduction, loot quality | Find hidden treasure, better loot rolls, dodge random hazards, favorable decision outcomes |
How Attributes Affect Combat
Might:
- +2% melee skill accuracy per point above 10
- +1.5% melee skill damage per point above 10
- Determines Might-based resistance checks (grapples, knockdowns)
Logic:
- +2% magical skill accuracy per point above 10
- +1.5% magical skill damage per point above 10
- +3 mana per point
- Determines Logic-based resistance checks (illusions, puzzles)
Speed:
- +1.5% ranged skill accuracy per point above 10
- +1 evasion rating per point (makes you harder to hit)
- Tie-breaking value for skill initiative
- Determines Speed-based resistance checks (traps, AoE dodging)
Presence:
- +2% healing/buff skill effectiveness per point above 10
- +2% debuff potency per point above 10 (harder for enemies to resist)
- Determines Presence-based resistance checks (fear, charm, intimidation)
Fortitude:
- +5 max HP per point above 10
- +2 stamina/devotion per point
- +1.5% condition resistance per point above 10 (blanket bonus to resist poison, stun, etc.)
- Determines Fortitude-based resistance checks (poison, disease, exhaustion)
Luck:
- Critical hit threshold improved by 0.5% per point above 10 (base crit: ≤5 on d100; at 20 LCK: ≤10)
- Critical miss threshold reduced by 0.5% per point above 10 (base crit miss: 96-100; at 20 LCK: 98-100 only)
- +1% loot quality modifier per point above 10 (slightly better rolls on loot tables)
- Once per dungeon run per 5 points of LCK: automatically re-roll one failed d100 check
Attribute Scale
Attributes range from 1 to 30 (soft cap), with a theoretical maximum of 40 through endgame gear and buffs.
| Range | Description |
|---|---|
| 1-5 | Below average. Significant penalties in related activities. |
| 6-9 | Average. No notable bonus or penalty. |
| 10-14 | Above average. Noticeable edge in related activities. |
| 15-19 | Exceptional. Clear strength in this area. |
| 20-24 | Heroic. Among the best in the world. |
| 25-30 | Legendary. Near the peak of mortal capability. |
| 31-40 | Transcendent. Only achievable through endgame gear, buffs, and Paragon bonuses. |
Attribute Point-Buy
At character creation, players distribute 40 points across the six attributes. All attributes start at 5 (baseline), and points raise them from there.
| Attribute Value | Total Points Spent (from 5) |
|---|---|
| 5 | 0 (base) |
| 6 | 1 |
| 7 | 2 |
| 8 | 3 |
| 9 | 4 |
| 10 | 5 |
| 11 | 7 |
| 12 | 9 |
| 13 | 11 |
| 14 | 14 |
| 15 | 17 |
The escalating cost means players can build a well-rounded character (several 10-12s) or specialize heavily (one or two 14-15s with lower elsewhere). The maximum any single attribute can reach at creation is 15 before species bonuses.
Species bonus is applied AFTER point-buy and can push an attribute above 15 at creation.
Recommended Distributions
Each class has a one-click “Recommended” button that sets a sensible point-buy for new players. Advanced players can customize freely.
Species
Delve features 9 playable species at launch. Each species has a distinct visual identity, cultural flavor, a primary attribute bonus, a secondary attribute bonus, and a unique species skill usable in the skill queue (see 04-combat-system.md).
No species is mandatory for any class — bonuses nudge, they don’t dictate.
Ironborn
- Primary Attribute: +3 Fortitude
- Secondary Attribute: +1 Might
- Species Skill: Unyielding (Passive) — Once per dungeon run, when reduced below 20% HP, automatically gain Shielded (absorb damage equal to 15% max HP) and +10% damage for 2 rounds.
- Appearance: Tall, broad, stone-gray skin with metalite veins running through their body. Dense, almost sculpted features. Hair ranges from iron-black to rust-red.
- Lore: The Ironborn emerged from the deep mountains after the Sundering, their bodies permanently fused with the mineral-rich stone that sheltered them. They are naturally resilient and culturally stoic — they believe that to endure is to prevail. Ironborn communities are built around forges and mines, and they have a reputation as the finest blacksmiths in Aethermere.
- Playstyle nudge: Tanky frontline characters. Great with heavy armor and shields. The Fortitude bonus means more HP and better condition resistance.
Verdani
- Primary Attribute: +3 Logic
- Secondary Attribute: +1 Presence
- Species Skill: Aethersight (Active, Init 2) — Reveal all enemy weaknesses and resistances for 3 rounds. Allies gain +10% accuracy against revealed targets. 2 uses per dungeon run. Swift (doesn’t consume queue slot).
- Appearance: Slender, elongated limbs with faintly luminous skin in pale green or soft blue. Eyes without pupils that glow faintly. Intricate natural patterns on their skin resembling circuitry or veins of a leaf.
- Lore: The Verdani are an ancient species who claim to remember the world before the Sundering. They experience reality through a “second sense” — an innate ability to perceive magical currents. This makes them natural scholars and spellcasters, though their detached perspective sometimes puts them at odds with shorter-lived species. Their cities are grown from living crystal in the deep forests.
- Playstyle nudge: Spellcasters and support characters. The Logic bonus boosts magical damage and mana pools.
Kharren
- Primary Attribute: +3 Might
- Secondary Attribute: +1 Fortitude
- Species Skill: Blood Frenzy (Active, Init 1) — For 3 rounds, all melee skills deal +20% damage but you take 10% more damage from all sources. 2 uses per dungeon run.
- Appearance: Powerfully built with thick, layered skin ranging from deep crimson to dark bronze. Prominent lower canines (tusks). Scarification patterns are a cultural tradition — each scar tells a story.
- Lore: The Kharren are a proud warrior culture from the Ashlands, forged by generations of surviving volcanic wastelands and fighting for scarce resources. They value strength and honor in equal measure — a Kharren who wins through treachery is despised, but one who wins through might is celebrated. They were among the first to reclaim territory after the Sundering, and their holds are the most well-defended settlements in the harsh frontier.
- Playstyle nudge: Melee damage dealers. The Might bonus amplifies physical skill damage. Blood Frenzy is a classic risk/reward damage steroid.
Sylphari
- Primary Attribute: +3 Speed
- Secondary Attribute: +1 Logic
- Species Skill: Phase Step (Active, Init 0) — Become untargetable for 1 round. Your next offensive skill gains +15% accuracy and +15% damage. 2 uses per dungeon run.
- Appearance: Lean and angular with semi-translucent skin that shifts color subtly with their emotional state. Hair like spun glass or fiber optics. They move with an unnatural fluidity, as if slightly out of phase with reality.
- Lore: The Sylphari exist partially between dimensions — a side effect of the Sundering that affected their ancestors who lived near the epicenter. They flicker between planes of existence, making them appear to shimmer or blur at the edges. This gives them extraordinary reflexes and an uncanny ability to avoid danger. Their communities are nomadic, drifting along the ley lines that crisscross Aethermere.
- Playstyle nudge: Rogues, ranged damage, and evasion-focused builds. The Speed bonus boosts evasion and ranged accuracy. Phase Step is an incredible defensive/offensive hybrid.
Thornkin
- Primary Attribute: +3 Fortitude
- Secondary Attribute: +1 Logic
- Species Skill: Regenerative Bark (Active, Init 5) — Gain Regenerating condition (recover 5% max HP per round for 4 rounds). If already below 50% HP, the regeneration is doubled to 10%. 2 uses per dungeon run.
- Appearance: Bark-like skin in shades of brown, green, and amber. Leaves, moss, or small flowering vines grow naturally from their shoulders, head, and arms. Their eyes are deep amber or emerald. They are stocky and grounded.
- Lore: The Thornkin are a plant-humanoid species that arose when the Sundering’s magical fallout saturated the great forests. They are deeply connected to the natural world and grow stronger with age — elder Thornkin can be centuries old and massive in stature. They are patient, pragmatic, and difficult to kill. Thornkin communities grow their homes directly from living trees, and they have a symbiotic relationship with the forests they inhabit.
- Playstyle nudge: Tanks and healers. The Fortitude bonus stacks HP and condition resistance. Regenerative Bark is a powerful self-sustain tool that makes them excellent at enduring long dungeon runs.
Ashenmere (Ash Folk)
- Primary Attribute: +3 Presence
- Secondary Attribute: +1 Speed
- Species Skill: Soul Echo (Active, Init 3) — Target ally gains Inspired (+10% accuracy, +10% damage) for 3 rounds. If used on self, also gain +10% condition resistance. 3 uses per dungeon run.
- Appearance: Pale, almost translucent skin with faint ember-like markings that glow softly in dim light. Their hair is white, silver, or ashen blonde, and their eyes are striking — vivid amber, gold, or pale violet. They have an ethereal, otherworldly beauty.
- Lore: The Ashenmere are descendants of a civilization that was closest to the epicenter of the Sundering. Their ancestors should have been obliterated, but some survived by binding themselves to the lingering spirits of those who perished. Every Ashenmere carries an “echo” — a spiritual presence that enhances their force of personality and gives them an innate connection to others. They are natural diplomats, leaders, and healers. Their culture is built around community and memory — they honor the dead by living fully.
- Playstyle nudge: Support, healing, and leadership builds. The Presence bonus amplifies buff/debuff/heal skills. Soul Echo is a versatile party buff.
Glimkin
- Primary Attribute: +3 Luck
- Secondary Attribute: +1 Speed
- Species Skill: Fortune’s Favor (Passive) — Twice per dungeon run, when a skill would critically miss (96-100), downgrade it to a normal miss instead. Additionally, +5% to loot quality rolls on all encounters.
- Appearance: Small (about 4 feet tall), with large expressive eyes and slightly oversized ears. Their skin has a faint metallic sheen — copper, silver, or gold tones. Wild, unruly hair in improbable colors. They always seem to be grinning.
- Lore: No one is entirely sure where the Glimkin came from. They appeared shortly after the Sundering, cheerful and seemingly unbothered by the apocalypse. They have an inexplicable knack for being in the right place at the right time (and the wrong place at the wrong time, just as often). Glimkin claim this is “the Knack” — an innate connection to probability itself. Scholars suspect they were created by the Sundering’s chaotic magic. Glimkin communities are colorful, noisy, and organized only by accident.
- Playstyle nudge: Any class works. The Luck bonus makes crits more frequent, crit misses rarer, and loot better. Fortune’s Favor protects against catastrophic bad rolls. Great for players who enjoy the gambling aspect of the d100 system.
Vexari
- Primary Attribute: +3 Logic
- Secondary Attribute: +1 Luck
- Species Skill: Unravel (Active, Init 4) — Strip all buffs from target enemy. For each buff removed, deal moderate magical damage. If the target had no buffs, deal heavy magical damage instead. 2 uses per dungeon run.
- Appearance: Dark skin in shades of deep indigo or black, with glowing sigil-like markings that pulse faintly. Horns curl back from the forehead in various shapes. A thin, prehensile tail. Eyes are solid colors — red, violet, gold, or white with no visible pupil.
- Lore: The Vexari are touched by the chaotic energies that leaked through the Sundering’s dimensional tears. Their ancestors made pacts — willingly or not — with entities from beyond, and the mark persists in their bloodline. Other species are wary of them, but the Vexari have proven their loyalty to Aethermere time and again. They have an innate talent for manipulating magical energies, especially unweaving and disrupting spells. Vexari culture values self-determination above all — they refuse to be defined by their origins.
- Playstyle nudge: Spellcasters, especially disruptors and debuffers. Unravel is a powerful anti-mage tool. The Logic bonus feeds magical builds, and the Luck secondary helps with crits.
Ferathi
- Primary Attribute: +3 Speed
- Secondary Attribute: +1 Might
- Species Skill: Predator’s Instinct (Passive) — The first skill in your queue each encounter always executes before all enemies, regardless of initiative values. Additionally, if that first skill kills an enemy, your next skill gains +20% damage.
- Appearance: Lean, athletic builds with animalistic features — slit-pupil eyes, elongated canines, clawed fingertips, and fine fur covering their forearms, shoulders, and calves. Fur and eye colors vary widely (tawny, spotted, striped, solid). Ears are pointed and mobile.
- Lore: The Ferathi are beast-touched — their ancestors were hunters and trackers who bonded so deeply with the predators of the wild that the Sundering’s magic made the bond literal. They are fierce, territorial, and loyal to their pack (which, for adventuring Ferathi, means their party). Ferathi communities are clan-based, and each clan identifies with a different predator: wolf, hawk, panther, viper, bear. While they can seem feral to outsiders, Ferathi culture has a deep code of honor and surprisingly sophisticated oral traditions.
- Playstyle nudge: Aggressive builds — rogues, rangers, melee DPS. Predator’s Instinct guarantees you strike first, which synergizes beautifully with high-damage opening skills. The Speed + Might bonuses support fast, hard-hitting play.
Species Comparison Table
| Species | Primary (+3) | Secondary (+1) | Species Skill | Archetype |
|---|---|---|---|---|
| Ironborn | Fortitude | Might | Unyielding (auto-shield at low HP) | Immovable tank |
| Verdani | Logic | Presence | Aethersight (reveal weaknesses) | Analytical caster |
| Kharren | Might | Fortitude | Blood Frenzy (damage boost at cost) | Berserker |
| Sylphari | Speed | Logic | Phase Step (untargetable + burst) | Elusive striker |
| Thornkin | Fortitude | Logic | Regenerative Bark (self-heal over time) | Enduring survivor |
| Ashenmere | Presence | Speed | Soul Echo (ally buff) | Inspiring leader |
| Glimkin | Luck | Speed | Fortune’s Favor (prevent crit misses, loot bonus) | Lucky wildcard |
| Vexari | Logic | Luck | Unravel (strip buffs, deal damage) | Arcane disruptor |
| Ferathi | Speed | Might | Predator’s Instinct (guaranteed first strike) | Alpha predator |
Classes
Each class defines the character’s combat role, resource system, and skill progression. Classes determine which class-specific skills unlock at which levels and how the character scales through the 1-50 level range.
Classes are archetypes — original to Delve but inspired by classic fantasy roles. Each class has 3 subclasses chosen at level 10.
Vanguard (Martial Frontline)
- Role: Frontline tank and sustained damage
- Resource: Stamina
- Key Attributes: Might, Fortitude
- Identity: The disciplined warrior. Vanguards are the backbone of any party — durable, reliable, and deadly with any weapon. They excel at staying in the fight longer than anyone else.
- Skills per round: 2 (base), increases to 3 at level 25
- Unique Mechanic: Battle Surge — Once per dungeon run (twice at level 30), execute 3 skills in a single round instead of the normal amount. Configured in queue with a conditional.
- Starting Gear: Medium armor, martial weapon of choice, shield, explorer’s pack
- Subclasses (Level 10):
- Bulwark — Pure defense. Gains skills that redirect damage to self, buff party evasion, and create damage absorption shields. The immovable wall.
- Warblade — Offense focus. Gains skills with higher damage multipliers, bleed effects, and execute-style finishers. The relentless attacker.
- Tactician — Hybrid support. Gains skills that buff ally accuracy and damage, debuff enemy groups, and manipulate initiative order. The field commander.
Shade (Stealth & Precision)
- Role: Burst damage, trap specialist, evasion
- Resource: Momentum (starts at 0, builds +20 per round in combat. Retains 50% between encounters. Capped at 100. Spent on powerful skills.)
- Key Attributes: Speed, Luck
- Identity: The unseen blade. Shades strike from angles enemies don’t expect, exploit weaknesses, and specialize in ending fights before they become wars of attrition. Outside combat, they’re the best at detecting and disarming traps.
- Skills per round: 2 (base)
- Unique Mechanic: Exploit Weakness — When attacking an enemy affected by a debuff, all Shade skills deal +15% bonus damage. This makes debuffs from allies (or from the Shade’s own skills) extra valuable.
- Momentum Carry-Over: Momentum retains 50% between encounters. A Shade ending a fight at 80 momentum enters the next fight at 40 — enough to immediately use mid-tier skills. By encounter 4-5, the Shade is entering fights at full power from round 1.
- Trap Expertise: Shades have +25% to all trap detection and disarm checks. At level 20, traps that would deal damage deal 50% reduced damage even if triggered.
- Starting Gear: Light armor, two daggers, shortbow, lockpicks, infiltrator’s pack
- Subclasses (Level 10):
- Assassin — Maximum burst. First-strike bonus damage, execute skills, and +1% damage per 5 momentum banked (at 80 momentum: +16% damage). Rewards building and holding momentum.
- Phantom — Evasion focus. Skills that make you untargetable, redirect attacks, and let you skip past encounters entirely through stealth.
- Saboteur — Trap and debuff specialist. Gains skills that apply DoTs, weaken enemies, and set up traps that damage enemies at the start of the next encounter.
Arcanist (Arcane Spellcaster)
- Role: Versatile magical damage, AoE, and utility
- Resource: Mana
- Key Attributes: Logic, Fortitude (for concentration/durability)
- Identity: The scholar of destruction. Arcanists command raw magical energy — they can burn down groups of enemies or surgically dismantle a single target. Their weakness is durability; they need protection or distance.
- Skills per round: 2 (base)
- Unique Mechanic: Spell Mastery — The Arcanist’s mana cost for any skill decreases by 1% for each time that skill is used in the dungeon run (cumulative). By the 10th use of Arcane Bolt, it costs 10% less. Rewards consistent skill use.
- Starting Gear: Staff or wand, light armor, arcane focus, scholar’s pack
- Subclasses (Level 10):
- Evoker — Raw damage. Fire, ice, lightning AoE skills. Can shape AoE to avoid allies. The blaster.
- Chronomancer — Time manipulation. Skills that slow enemies, haste allies, and manipulate initiative values. The controller.
- Voidcaller — Dark magic. Drain effects, DoTs, and skills that get stronger the lower the Arcanist’s HP (risk/reward). The gambler.
Warden (Divine Healer & Protector)
- Role: Healer, buffer, anti-undead, off-tank
- Resource: Devotion
- Key Attributes: Presence, Fortitude
- Identity: The divine conduit. Wardens channel power from a higher source to mend wounds, shield allies, and purge corruption. In a party, they’re the difference between victory and a wipe. Solo, they outlast enemies through attrition.
- Skills per round: 2 (base)
- Unique Mechanic: Sanctify — Once per encounter, the Warden can Sanctify the battlefield (automatic, no queue slot). While Sanctified, all healing done by the Warden is increased by 20% and undead enemies take 10% more damage from all sources. Lasts 3 rounds.
- Starting Gear: Medium armor, shield, mace, holy symbol, healer’s pack
- Subclasses (Level 10):
- Lifebinder — Pure healing. Enhanced heal skills, resurrection ability, and skills that prevent damage rather than heal it after the fact.
- Crusader — Offensive healer. Radiant damage skills, smite-like burst damage, and heals that trigger when you deal damage. The fighting priest.
- Oathkeeper — Protection focus. Skills that redirect damage, grant damage immunity, and create healing zones. The guardian.
Pathfinder (Ranged & Scouting)
- Role: Ranged sustained damage, scouting, dungeon navigation
- Resource: Focus (regenerates faster than other resources — 40% between encounters)
- Key Attributes: Speed, Logic
- Identity: The wayfinder. Pathfinders are ranged specialists who read terrain, spot dangers, and strike from a distance. They bring a unique dungeon utility: scouting ahead to reveal traps, finding shortcuts, and identifying enemy composition before engagement.
- Skills per round: 2 (base)
- Unique Mechanic: Terrain Mastery — Before a dungeon run, the Pathfinder selects a terrain type (cave, forest, ruins, etc.). If the dungeon matches, they gain: +10% accuracy on all skills, +15% trap detection, and 10% chance per encounter to find a shortcut (skip the encounter entirely).
- Starting Gear: Light armor, bow, two short swords, explorer’s pack, scout’s kit
- Subclasses (Level 10):
- Marksman — Pure ranged damage. Gains skills with extreme accuracy, long-range bonuses, and multi-shot abilities.
- Beastcaller — Companion specialist. Gains an animal companion that acts as a separate combatant with its own mini-queue (2 skills). The companion persists across encounters but shares the Pathfinder’s HP pool.
- Trailblazer — Dungeon utility. Enhanced scouting, skills that reveal the entire dungeon layout, and abilities that let the party skip non-combat encounters. Reduces dungeon run duration by up to 15%.
Berserker (Aggressive Melee)
- Role: High-risk, high-reward melee damage
- Resource: Fury (starts at 0, builds from damage DEALT. Capped at 100. Retains 25% between encounters. Decays -5/round while Raging.)
- Key Attributes: Might, Fortitude
- Identity: The storm of violence. Berserkers get stronger the more damage they deal — hitting things fuels their rage. They trade defense for offense and thrive when they can stay aggressive.
- Skills per round: 2 (base), 3 while in Rage state
- Unique Mechanic: Rage — When Fury reaches 50+, the Berserker enters Rage (automatic). While Raging: +25% melee damage, -15% evasion, 3 skills per round instead of 2, and immune to Frightened. Fury decays -5/round while Raging — the Berserker must keep landing hits to sustain Rage. Rage ends if Fury drops below 25.
- Fury Generation: +1 Fury per ~3% of target’s max HP dealt. A big hit against a boss generates more Fury than chip damage against minions. Front-loading the queue with fast, reliable hits builds Fury fastest.
- Fury Carry-Over: Retains 25% between encounters. Ending a fight at 80 Fury means starting the next at 20 — not enough for Rage, but a head start.
- Starting Gear: No armor (uses Fortitude for base evasion), two-handed weapon, berserker’s pack
- Subclasses (Level 10):
- Ravager — Maximum damage. Skills that deal more damage the lower your HP. A crit-focused build that thrives at death’s door.
- Totemist — Spiritual warrior. Choose a spirit totem (Bear: +30% HP, Eagle: -1 initiative on all skills, Wolf: allies deal +10% damage). Provides party utility while raging.
- Bloodletter — Vampiric berserker. Skills that heal self based on damage dealt. The self-sustaining bruiser who gets harder to kill the harder they hit.
Songweaver (Support & Versatility)
- Role: Party buffer, debuffer, jack-of-all-trades
- Resource: Resonance (starts at max, depletes as abilities are used. Recovers 15% between encounters — the slowest pool recovery in the game. Rest points restore 50%.)
- Key Attributes: Presence, Speed
- Identity: The force multiplier. Songweavers don’t deal the most damage or have the most HP — they make everyone around them better. A good Songweaver in a party turns a mediocre group into an unstoppable force. Solo, they’re versatile enough to handle anything, just slower.
- Skills per round: 2 (base)
- Unique Mechanic: Anthem — The Songweaver maintains a persistent Anthem that provides a free passive bonus to all allies (including self) — Anthems cost NO Resonance. Choose one Anthem before the run:
- Anthem of Valor: +8% damage to all allies
- Anthem of Grace: +8% evasion to all allies
- Anthem of Mending: All allies regenerate 2% max HP per round
- Anthem of Haste: -1 initiative value on all ally skills (act faster)
- Resource Floor: Even at 0 Resonance, the Songweaver still provides value through the free Anthem and zero-cost basic weapon attacks. They decline over long dungeons but never become dead weight.
- Starting Gear: Light armor, rapier, musical instrument (cosmetic), entertainer’s pack
- Subclasses (Level 10):
- Virtuoso — Enhanced anthems. Can maintain TWO anthems simultaneously. Anthem effects increased to 12%.
- Dirge Singer — Offensive debuffer. Gains skills that weaken, slow, frighten, and disrupt enemy groups. The anti-support.
- War Chanter — Hybrid fighter/support. Gains melee combat skills that also buff allies when they hit. Each successful attack grants an ally a small buff.
Oathblade (Holy Warrior)
- Role: Durable melee fighter with healing and burst damage
- Resource: Zeal (30% recovery between encounters. Moderate pool, but Smite burns extra on top of normal skill costs, making effective burn rate ~1.5-2x faster when Smiting.)
- Key Attributes: Might, Presence
- Identity: The righteous warrior. Oathblades are melee fighters who channel divine power through their weapon strikes. They can heal, they can tank, and they can deal devastating burst damage — but they can’t do all three in the same dungeon run without running dry. Resource management is their core challenge.
- Skills per round: 2 (base)
- Unique Mechanic: Smite — Any melee weapon skill in the queue can be “empowered” with a Smite modifier (set during queue configuration). Smiting costs extra Zeal but adds 50% bonus radiant damage to the skill and an additional 25% if the target is undead. Players choose which skills to Smite and which to leave normal — Smiting everything means running dry fast; Smiting only bosses means sustained power.
- Starting Gear: Heavy armor, martial weapon, shield, holy symbol, crusader’s pack
- Subclasses (Level 10):
- Avenger — Offense focus. Smite costs reduced by 25%. Gains execute-style skills that deal bonus damage to low-HP enemies. The divine executioner.
- Sentinel — Defense focus. Gains aura skills that grant allies condition immunity, damage reduction, and healing. The party protector.
- Inquisitor — Anti-magic focus. Gains skills that silence enemy casters, strip magical buffs, and resist magical damage. The mage-hunter.
Hexbinder (Occult Caster)
- Role: Consistent magical damage, drain effects, dark utility
- Resource: Essence (unique: low base pool but recharges 50% between encounters — fast cycle, small bursts)
- Key Attributes: Logic, Presence
- Identity: The pact-maker. Hexbinders draw power from entities beyond the material world — not gods (that’s the Warden’s domain) but stranger, older things. Their magic is consistent and self-sustaining: many skills heal the caster or debuff enemies while dealing damage. They’re the magical equivalent of a war of attrition.
- Skills per round: 2 (base)
- Unique Mechanic: Soul Pact — Choose a pact entity at character creation. This determines the Hexbinder’s bonus skills and playstyle:
- Pact of the Flame: Fire-themed. Bonus fire damage on all skills. Burning condition applied frequently.
- Pact of the Void: Necrotic-themed. Drain effects heal the Hexbinder. Skills get stronger as the fight goes longer.
- Pact of the Eye: Psychic-themed. Powerful debuffs, mind control, and information-gathering. Less damage, more control.
- Starting Gear: Light armor, wand or rod, occult focus, hexer’s pack
- Subclasses (Level 10):
- Witch — Curse specialist. Gains powerful DoT and debuff skills that stack. Multiple curses on one enemy amplify each other.
- Soulreaper — Drain specialist. All damage skills heal for a percentage of damage dealt. Gets stronger the closer to death.
- Fateweaver — Luck manipulation. Gains skills that force enemies to re-roll successes, grant allies re-rolls on failures, and manipulate the d100 outcomes. The probability bender.
Class Comparison Table
| Class | Role | Resource | Recovery | Key Attributes | Skills/Round | Power Curve |
|---|---|---|---|---|---|---|
| Vanguard | Tank/Melee DPS | Stamina | 50% | Might, Fortitude | 2→3 | Flat (consistent) |
| Shade | Burst DPS/Traps | Momentum | 50% carry | Speed, Luck | 2 | Rising (ramps up) |
| Arcanist | Magic DPS/AoE | Mana | 25% | Logic, Fortitude | 2 | Declining (front-loaded) |
| Warden | Healer/Support | Devotion | 25% | Presence, Fortitude | 2 | Declining (must ration) |
| Pathfinder | Ranged DPS/Scout | Focus | 50% | Speed, Logic | 2 | Flat (sustainable) |
| Berserker | Melee DPS (risky) | Fury | 25% carry | Might, Fortitude | 2→3 (Rage) | Volatile (spikes) |
| Songweaver | Buffer/Debuffer | Resonance | 15% | Presence, Speed | 2 | Declining (Anthem floor) |
| Oathblade | Tank/Burst/Healer | Zeal | 30% | Might, Presence | 2 | Depends on Smite use |
| Hexbinder | Magic DPS/Drain | Essence | 50% | Logic, Presence | 2 | Flat (burst cycles) |
Backgrounds
Backgrounds represent what the character did before becoming an adventurer. Each provides a passive perk that affects dungeon runs and an attribute bonus (+1 to a specific attribute).
| Background | Attribute Bonus | Passive Perk |
|---|---|---|
| Soldier | +1 Fortitude | +10% HP recovery at rest points |
| Outlaw | +1 Speed | Chance to find hidden caches in dungeons |
| Scholar | +1 Logic | Automatically identify item properties on pickup |
| Pilgrim | +1 Presence | +15% healing received from all sources |
| Merchant | +1 Luck | 10% better prices at NPC vendors |
| Survivalist | +1 Fortitude | Consume 20% fewer rations during runs |
| Aristocrat | +1 Presence | Start with bonus gold; +10% gold from quest rewards |
| Performer | +1 Presence | 10% chance to avoid non-boss combat via performance |
| Artisan | +1 Logic | 15% faster crafting times |
| Wanderer | +1 Speed | +10% chance to find shortcuts in dungeons |
Starting Equipment
Beyond class-specific gear, all characters start with:
- 50 gold
- 5 rations
- 2 basic healing salves
- 1 torch bundle (5 torches)
- Basic adventurer’s clothing
Character Slots
- First character: Free
- Additional slots: Purchased from the in-game shop
- Each character is fully independent — separate inventory, separate quests, separate progression
- Characters on the same account can mail items and gold to each other (with a small gold transfer fee as a gold sink)
- Only one character can be on an active dungeon run at a time per slot (but crafting/gathering can run in parallel)
New Player Experience
- Name and appearance — Quick portrait selection per species
- Species selection — Each shown with a brief description, clear attribute bonus, and species skill preview
- Class selection — Presented with role description and playstyle summary
- Attribute points — Point-buy with one-click “Recommended” per class
- Background — Simple selection with clear perk description
- Tutorial quest — A short, guided dungeon run (2 encounters) that teaches the core loop: review results → equip → build queue → commit → wait → results
The entire creation process should take under 5 minutes. Players can respec attribute points later (for a gold cost) but species and class are permanent.
Character Progression
Overview
Progression in Delve is the long-term reward cycle that keeps players engaged. Characters grow stronger through leveling (1-50), weapon proficiency (0-100), subclass specialization, feat selection, and eventually Paragon ranks after the level cap. The curve is designed for an idle game — meaningful gains every session, but the cap should take months to reach.
Level System
Level Cap: 50
50 levels provides a long, satisfying progression arc with room for meaningful milestones and customization. Every level provides something — a stat boost, a new skill, a feat choice, or a subclass feature.
XP Sources
| Source | XP Amount | Notes |
|---|---|---|
| Dungeon run completion | High | Scales with difficulty tier |
| Quest completion (bonus) | Medium | One-time per quest |
| Per-encounter kills | Low-Medium | Per enemy defeated |
| Trap disarming | Low | Bonus for Shades |
| Puzzle solving | Low | Bonus for high-Logic characters |
| Hidden discoveries | Low | Exploration reward |
| PVP matches | Low-Medium | Win or lose, bonus for winning |
| First craft per recipe | Low | One-time per recipe |
| Daily first-run bonus | Medium | Encourages daily engagement |
XP on Failed Runs
Failed runs award partial XP based on progress. A character that cleared 8 of 10 encounters earns roughly 70-80% of the full run’s XP. Failure should sting but never feel like wasted time.
Leveling Curve
| Level Range | Phase | Approximate Time | Design Intent |
|---|---|---|---|
| 1-10 | Beginner | 1-2 weeks | Hook phase. Rapid progression, learn the core loop, unlock basic systems. |
| 11-20 | Intermediate | 2-4 weeks | Subclass chosen at 10, deeper content opens, multiplayer unlocks. |
| 21-30 | Advanced | 1-2 months | Build identity solidifies. Endgame content begins. Feats create meaningful choices. |
| 31-40 | Expert | 2-3 months | Power growth slows but each level feels significant. Raid content, faction endgame. |
| 41-50 | Endgame | 3-4 months | Each level is a major achievement. Capstone abilities. Preparing for Paragon. |
Total time to 50: Approximately 8-12 months of regular play (1-2 sessions per day). The journey IS the game.
What You Get Per Level
Every level provides:
- +Max HP (based on class + Fortitude)
- +Resource pool (Stamina, Mana, Devotion, etc. — class dependent)
- +1 attribute point every 5 levels (10, 15, 20, 25, 30, 35, 40, 45, 50 — total of 9 bonus attribute points)
Level Milestone Table
| Level | Milestone |
|---|---|
| 1 | Starting class skills and species skill |
| 3 | Class feature unlock |
| 5 | 1st Feat selection |
| 7 | Class skill unlock |
| 10 | Subclass selection — major branching point |
| 10 | +1 Attribute point |
| 12 | 2nd Feat selection |
| 15 | Subclass feature + skill unlock |
| 15 | +1 Attribute point |
| 18 | Class feature unlock |
| 20 | 3rd Feat selection |
| 20 | +1 Attribute point |
| 22 | Subclass feature |
| 25 | Class milestone: major power spike (e.g., Vanguard gets 3 skills/round) |
| 25 | +1 Attribute point |
| 28 | 4th Feat selection |
| 30 | Subclass feature + skill unlock |
| 30 | +1 Attribute point |
| 33 | Class feature unlock |
| 35 | 5th Feat selection + Attribute point |
| 38 | Subclass feature |
| 40 | Class milestone: second major power spike |
| 40 | +1 Attribute point |
| 42 | 6th Feat selection |
| 45 | Subclass capstone (ultimate subclass ability) |
| 45 | +1 Attribute point |
| 48 | 7th Feat selection |
| 50 | Class capstone — defining ultimate ability + Attribute point |
Attribute Growth Summary
- At creation: ~40 distributed points + species bonus (+3/+1) + background (+1)
- Through leveling: +9 attribute points (one every 5 levels from 10-50)
- From gear: Varies, typically +3-8 total across equipped items at endgame
- Theoretical endgame total: A primary attribute can reach 25-30 through optimization, 30-40 with top-tier gear and Paragon bonuses
Subclasses
Subclass selection at level 10 is the biggest single progression choice after class selection. It defines the character’s specialization for the rest of their career.
Each class has 3 subclasses (detailed in 02-character-creation.md). Subclasses provide:
- Level 10: Core subclass skill + passive
- Level 15: Subclass skill upgrade or new skill
- Level 22: Subclass feature (passive enhancement)
- Level 30: Advanced subclass skill
- Level 38: Subclass feature (passive enhancement)
- Level 45: Subclass capstone — a powerful ultimate ability
Subclass Cannot Be Changed
Subclass is a permanent choice. This makes it feel meaningful and encourages players to roll new characters to try different paths. Combined with weapon proficiency (which takes months to level), each character becomes a unique investment.
Feats
At levels 5, 12, 20, 28, 35, 42, and 48, players select a Feat — a permanent passive or activated bonus that customizes the character beyond class and species.
7 total feat slots over 50 levels gives players significant build variety. Two characters of the same class and subclass can play very differently based on feat choices.
Feat List (Launch Set)
Combat Feats
| Feat | Prerequisite | Effect |
|---|---|---|
| Iron Will | FRT 12+ | +15% resistance to all conditions. Your resistance checks are rolled twice, take the better result. |
| Lethal Precision | SPD 12+ | Critical hit threshold improved by an additional 3% (stacks with Luck). |
| Heavy Hitter | MGT 12+ | Power-type skills (init 6+) deal +15% damage. |
| Quick Reflexes | SPD 12+ | Quick-type skills (init 1-3) gain +10% accuracy. |
| Weapon Specialist | Any weapon prof 30+ | Choose one weapon type: +5% accuracy, +5% damage, and weapon proficiency gains 25% faster with that type. |
| Dual Focus | LOG 12+ | When casting a magical skill, 10% chance to cast it twice (second cast is free). |
| Relentless | FRT 14+ | When reduced to 0 HP, survive with 1 HP instead. Once per dungeon run. |
| Overwhelming Force | MGT 14+ | AoE skills hit one additional target beyond their normal range. |
| Evasive | SPD 14+ | +5 flat evasion rating. Dodging an attack (miss against you) grants +5% damage on your next skill. |
| Blood Magic | LOG 12+, FRT 12+ | You can spend HP instead of Mana/Devotion/Essence to fuel skills (at a 2:1 ratio — 2 HP per 1 resource). Dangerous but prevents running dry. |
Dungeon Feats
| Feat | Prerequisite | Effect |
|---|---|---|
| Trap Sense | None | +20% trap detection. Detected traps can be avoided automatically (no disarm roll needed). |
| Pack Rat | None | +5 inventory slots. Consumable stacks increased to 30 (from 20). |
| Efficient Traveler | None | Consume 25% fewer rations. Rest points restore 15% more HP. |
| Keen Eye | LOG 10+ | Automatically identify all item properties on pickup (normally Scholar background only). Reveal hidden rooms 15% more often. |
| Pathfinder’s Gift | None | +15% chance to find shortcuts. Dungeon run duration reduced by 5%. |
| Resilient Constitution | FRT 12+ | Between encounters, HP regeneration increased from 5-10% to 15-20%. Recover from one condition between encounters for free. |
Social/Economic Feats
| Feat | Prerequisite | Effect |
|---|---|---|
| Silver Tongue | PRS 12+ | +15% chance to avoid combat through diplomacy. NPC vendor prices improved by 10%. |
| Guild Commander | Level 20+ | While in a party, all allies gain +3% accuracy and +3% damage (stacks with other buffs). |
| Fortune Seeker | LCK 12+ | +10% gold from all sources. Loot quality modifier increased by an additional 5%. |
| Master Artisan | Any crafting skill 50+ | Crafting time reduced by 20%. Critical craft chance increased by 10%. |
Luck Feats
| Feat | Prerequisite | Effect |
|---|---|---|
| Lucky | None | 3 times per dungeon run, force a re-roll on any d100 result (yours or an enemy’s). Take whichever result you prefer. |
| Fate’s Chosen | LCK 14+ | Critical miss range eliminated entirely (96-100 becomes a normal miss, not a crit miss). Lucky feat re-rolls increased to 5. |
Feat Synergies
Feats are designed to create interesting build decisions when combined:
- Heavy Hitter + Weapon Specialist (axes) = Devastating power strikes with axes
- Quick Reflexes + Lethal Precision = Fast skills that crit frequently
- Blood Magic + Relentless = A desperation caster who trades HP for power with a safety net
- Lucky + Fate’s Chosen = Maximum dice manipulation — the “luck build”
Skills (Non-Combat)
Skills represent trained abilities that affect dungeon outcomes outside of combat. Each character has proficiency in skills based on class, background, and species.
Skill List
| Skill | Attribute | Dungeon Use |
|---|---|---|
| Athletics | Might | Climb, swim, force doors, resist grapples |
| Acrobatics | Speed | Balance, dodge physical hazards, squeeze through gaps |
| Stealth | Speed | Avoid encounters, ambush enemies, bypass patrols |
| Perception | Logic | Detect traps, spot ambushes, find hidden rooms |
| Investigation | Logic | Solve puzzles, find secret mechanisms, analyze clues |
| Arcana | Logic | Identify magical traps/items, interact with magical mechanisms |
| Survival | Fortitude | Navigate, forage for supplies, endure harsh environments |
| Medicine | Logic | Stabilize dying allies, identify poisons, improve rest recovery |
| Intimidation | Presence | Scare enemies into fleeing, prevent ambushes, force information |
| Persuasion | Presence | Negotiate with NPCs, recruit allies, avoid encounters |
| Deception | Presence | Bluff past enemies, mislead pursuers, create distractions |
| Craftsmanship | Logic | Repair items, improvise tools, interact with mechanical devices |
Skill Checks in Dungeons
d100 roll vs. success chance
Success Chance = Skill Base (from proficiency) + Attribute Modifier + Gear Bonuses
Proficient skills have a significantly higher base than non-proficient ones. This makes class choice matter for dungeon utility, not just combat.
Paragon System (Post-Level-50)
After hitting level 50, XP continues accumulating toward Paragon Ranks — an endgame horizontal progression system that provides incremental, uncapped growth.
Paragon Ranks
- Each Paragon Rank requires increasing XP (same curve as late levels)
- Each rank grants a Paragon Point
- Paragon Points are spent on a universal Paragon tree with minor but meaningful bonuses:
Paragon Tree Categories:
| Category | Bonuses Available |
|---|---|
| Combat | +0.5% accuracy, +0.5% damage, +0.5% crit chance (per point) |
| Durability | +3 max HP, +0.5% evasion, +0.5% condition resistance (per point) |
| Resources | +1% resource pool, +0.5% resource regeneration (per point) |
| Fortune | +0.3% loot quality, +0.3% gold find, +0.2% crit chance (per point) |
| Mastery | +0.5% weapon proficiency gain rate, +0.5% crafting/gathering speed (per point) |
Diminishing Returns
- Paragon Points are uncapped, but each subsequent point gives slightly less total impact
- The difference between Paragon 0 and Paragon 20 is significant
- The difference between Paragon 80 and Paragon 100 is barely noticeable
- This gives endgame players continuous progression without making them unreachable for newer max-level characters
Paragon Milestones
- Paragon 10: “Paragon” title + portrait frame
- Paragon 25: “Champion” title + enhanced portrait frame
- Paragon 50: “Legend” title + unique cosmetic armor effect
- Paragon 100: “Eternal” title + animated portrait frame + character statue in guild hall
Weapon Proficiency as Progression
See 04-combat-system.md for full details. Key points:
- Each weapon type (swords, axes, daggers, bows, etc.) has an independent 0-100 proficiency track
- Using a weapon in combat increases proficiency → unlocks new skills and passives
- This never ends. Even at level 50 with Paragon ranks, picking up a new weapon type means starting at proficiency 0 — a whole new progression journey
- A veteran player might have swords at 100 and axes at 60, but daggers at 15. Trying daggers gives them months of fresh progression.
Respec Rules
| What | Can Respec? | Cost |
|---|---|---|
| Attribute points | Yes | Gold (scales with level — more expensive at higher levels) |
| Feats | Yes (one at a time) | Gold (scales with level) |
| Subclass | No — permanent choice | Roll a new character |
| Species | No — permanent choice | Roll a new character |
| Class | No — permanent choice | Roll a new character |
| Weapon proficiency | Cannot be reset — always retained | N/A |
Respec costs act as a gold sink (see 08-economy-and-trading.md). The intent: attributes and feats are adaptable, but the core identity of the character (species, class, subclass) is permanent and meaningful.
Combat System
Overview
Combat in Delve is never witnessed in real-time. The player’s job is to build a skill queue — an ordered list of skills their character will execute — and the server resolves combat automatically. After the run, the player reads a detailed combat log showing every roll, every skill fired, and exactly what happened.
The core strategic loop: build your queue → run the dungeon → read the results → adjust your queue → run again.
Design Philosophy
- The player is the coach, not the athlete. You draft the game plan. The character executes it. Your preparation determines the outcome.
- Every failure is a lesson. The combat log must make it clear WHY something happened — which skill missed, which enemy acted first, where the queue fell apart.
- Progression never ends. Between character level, weapon proficiency, skill unlocks, and gear, there’s always something to improve.
Core Dice System: d100 Percentile
Delve uses a d100 (percentile) system for all combat resolution. Every skill, attack, and effect rolls a number from 1-100 against a success threshold.
How It Works
Roll d100 → Compare to success chance → Determine outcome
Every skill has a base accuracy (e.g., 75%). This is modified by the attacker’s stats, the defender’s stats, conditions, and buffs/debuffs to produce a final success chance.
Success Chance = Skill Base Accuracy + Attacker Modifier - Defender Modifier + Buffs/Debuffs
- Roll ≤ success chance → Hit
- Roll > success chance → Miss
- Roll ≤ 5 → Critical Hit (bonus damage, usually 1.5x)
- Roll 96-100 → Critical Miss (skill fails and may have a penalty — wasted resource, self-debuff, etc.)
Why d100?
- Percentages are intuitive — “you have a 73% chance to hit” is immediately understood
- Fine-grained balance — designers can tune in 1% increments
- Critical hits and misses are always possible, creating dramatic “clutch” and “heartbreak” moments
- Legally distinct from any existing tabletop system
Modifiers
Attacker Modifier is derived from:
- Relevant attribute (Might for melee, Speed for ranged, Logic/Presence for magic)
- Weapon proficiency level (0-100, grants +0 to +15)
- Gear bonuses
- Buff effects
Defender Modifier is derived from:
- Evasion rating (derived from armor, Speed, shields, and class features)
- Debuff effects
- Terrain/positional modifiers
Example
Skill: Crushing Blow (base accuracy 80%)
Attacker Might modifier: +8
Weapon proficiency bonus: +6 (sword mastery at level 40)
Target Evasion modifier: -12 (plate armor + shield)
Buff: Precision Stance +5
Final success chance: 80 + 8 + 6 - 12 + 5 = 87%
Roll: 34 → HIT (34 ≤ 87)
Saving Throws
When a skill inflicts a condition or effect (poison, stun, fear), the target makes a resistance check:
Roll d100 vs. Resistance Chance
Resistance Chance = Base Resistance + Relevant Ability Modifier + Gear/Buff Bonuses
- Roll ≤ resistance chance → effect is resisted (negated or halved)
- Roll > resistance chance → effect lands
This means high-Fortitude characters resist poison more often, high-Presence characters resist fear, etc.
The Skill Queue
The skill queue is the player’s primary interaction with combat. Before a dungeon run, the player arranges their available skills into an ordered queue. During combat, skills execute from this queue based on their initiative values, interleaved with enemy skills.
How the Queue Works
- The player arranges their skills in a queue (ordered list, typically 4-8 skills)
- Each skill has an initiative value — a fixed speed rating that determines when it fires relative to other actions in the round
- Each round, ALL skills from ALL combatants are sorted by initiative (lowest = fastest)
- Skills execute in initiative order
- When the player’s queue reaches the end, it loops back to the beginning
- The queue resets to position 1 at the start of each new encounter (but resources carry over — see Encounter Reset)
Initiative and Turn Order
Every skill has a fixed initiative value representing how fast the skill executes. Lower initiative = acts first.
Think of initiative as “wind-up time” — a quick dagger stab (init 2) fires before a heavy overhead swing (init 8).
Each round, the system collects the “next queued skill” from every combatant and sorts them:
ROUND 1 — Turn Order (sorted by initiative, lowest first):
Player Queue: Enemy (Dire Rat):
Slot 1: Crushing Blow (init 5) Slot 1: Bite (init 2)
Slot 2: Strengthening Shout (init 7) Slot 2: Squeal (init 6)
Execution order:
1. [Init 2] Dire Rat → Bite
2. [Init 5] Player → Crushing Blow
3. [Init 6] Dire Rat → Squeal
4. [Init 7] Player → Strengthening Shout
ROUND 2 — Queue loops:
Player Queue: Enemy (Dire Rat):
Slot 3: Quick Slash (init 3) Slot 3: Bite (init 2)
Slot 4: Healing Surge (init 9) Slot 4: Flee Check (init 10)
Execution order:
1. [Init 2] Dire Rat → Bite
2. [Init 3] Player → Quick Slash
3. [Init 9] Player → Healing Surge
4. [Init 10] Dire Rat → Flee Check
Skills Per Round
Each combatant executes 2 skills per round by default. This can be modified by:
- Class features (e.g., Fighter gets 3 skills per round at level 11)
- Haste-type buffs (+1 skill per round for duration)
- Exhaustion or debuffs (-1 skill per round)
- Specific skills that are “swift” and don’t consume a slot
This means a queue of 6 skills takes 3 rounds to cycle through before looping.
Tie-Breaking
When two skills have the same initiative value:
- Player character breaks ties using their character initiative stat (derived from DEX + class bonuses + gear). Higher character initiative goes first.
- If still tied (e.g., two enemies), resolve randomly.
Queue Slots
- Base queue size: 6 slots
- Certain class features expand this (e.g., Bard gets +1 slot for a support skill, Wizard gets +1 slot at level 10)
- Maximum queue size: 10 slots (with all bonuses)
- Minimum queue size: 3 slots (must have at least 3 skills queued)
Light Conditionals
Each skill slot in the queue can have one optional condition attached. If the condition is not met when the skill’s turn comes up, the skill is skipped and the queue advances to the next skill.
Available Conditions
Self Conditions:
- “Use if my HP is below X%” (e.g., only heal when hurt)
- “Use if my HP is above X%” (e.g., only use risky skills when healthy)
- “Skip if [condition] is active on me” (e.g., don’t buff if already buffed)
- “Use if I have [resource] remaining” (e.g., only use mana skill if mana available)
Target Conditions:
- “Use if enemy count is above/below X”
- “Use if target HP is above/below X%”
- “Use if target is [enemy type]” (undead, beast, humanoid, etc.)
- “Skip if target has [condition]” (e.g., don’t re-poison a poisoned target)
Ally Conditions (party play):
- “Use if any ally HP is below X%”
- “Use if ally count is below X” (e.g., emergency heal when party is dying)
Conditional Behavior
- A skill with no condition always fires when its turn comes
- A skill with a failed condition is skipped — the queue advances and that round’s action is lost (the character hesitates)
- This means poorly configured conditionals can waste turns — an intentional risk/reward for using them
- New players should leave most conditions empty and add them as they learn
Example Queue with Conditions
Slot 1: Quick Slash (init 3) ............. [no condition — always fires]
Slot 2: Crushing Blow (init 5) ........... [no condition — always fires]
Slot 3: Healing Surge (init 9) ........... [IF my HP < 50%]
Slot 4: Power Strike (init 6) ............ [no condition — always fires]
Slot 5: War Cry (init 7) ................. [IF enemy count ≥ 3]
Slot 6: Defensive Stance (init 4) ........ [IF my HP < 25%]
In this queue:
- Slots 1, 2, and 4 always fire — reliable damage core
- Slot 3 heals only when needed — skips if healthy (preserving the action for next cycle? No — it’s lost. The player accepts this trade-off for not wasting a heal at full HP)
- Slot 5 only War Cries against groups — skipped against single targets
- Slot 6 is an emergency defensive stance — almost always skipped, but catches emergencies
Skill Targeting
Each skill in the queue has a target rule that determines who it affects. The player configures this per slot.
Target Rules
Offensive Skills:
| Rule | Behavior |
|---|---|
| Nearest | Closest enemy (default for melee) |
| Lowest HP | Enemy with the fewest hit points (finish off wounded) |
| Highest HP | Enemy with the most hit points (focus the biggest threat) |
| Highest Threat | Enemy dealing the most damage (shut down their DPS) |
| Random | Random enemy (unpredictable but sometimes optimal for AoE setup) |
| Boss/Elite | Prioritize boss or elite enemies if present, otherwise fallback to nearest |
Defensive/Support Skills:
| Rule | Behavior |
|---|---|
| Self | Target self (default for self-buffs) |
| Lowest HP Ally | Ally with the fewest hit points (triage healing) |
| Highest Threat Ally | Ally drawing the most enemy attacks (keep the tank alive) |
| Random Ally | Random party member |
Area-of-Effect (AoE) Skills:
- AoE skills automatically target the cluster with the most enemies
- Player can set: “Prioritize damage” (hit max enemies) or “Prioritize safety” (avoid hitting allies in the blast)
Weapon Proficiency System
Every weapon type in the game has a proficiency track from 0 to 100. Using a weapon in combat increases proficiency with that weapon type, unlocking stronger skills and passive bonuses.
Weapon Types
Each weapon belongs to a type. Proficiency is tracked per type, not per individual weapon.
| Weapon Type | Examples | Primary Stat |
|---|---|---|
| Swords | Shortsword, Longsword, Greatsword | STR or DEX |
| Axes | Handaxe, Battleaxe, Greataxe | STR |
| Maces & Hammers | Mace, Warhammer, Maul | STR |
| Daggers | Dagger, Stiletto, Ritual Knife | DEX |
| Polearms | Spear, Halberd, Glaive | STR |
| Bows | Shortbow, Longbow, Composite Bow | DEX |
| Crossbows | Hand Crossbow, Light Crossbow, Heavy Crossbow | DEX |
| Staves | Quarterstaff, Battle Staff, Arcane Staff | STR or INT |
| Wands | Wand, Rod, Scepter | INT or WIS or CHA |
| Shields | Buckler, Kite Shield, Tower Shield | CON |
| Unarmed | Fists, Claws, Natural Weapons | STR or DEX |
Proficiency Progression
| Proficiency Level | Title | Accuracy Bonus | Skills Unlocked |
|---|---|---|---|
| 0-9 | Novice | +0 | Basic attack only |
| 10-19 | Apprentice | +2 | 1st weapon skill |
| 20-29 | Journeyman | +4 | 2nd weapon skill |
| 30-39 | Adept | +6 | 3rd weapon skill |
| 40-49 | Skilled | +8 | 4th weapon skill + passive bonus |
| 50-59 | Expert | +9 | 5th weapon skill |
| 60-69 | Master | +10 | 6th weapon skill + 2nd passive |
| 70-79 | Grandmaster | +11 | 7th weapon skill |
| 80-89 | Legend | +13 | 8th weapon skill + 3rd passive |
| 90-99 | Mythic | +14 | 9th weapon skill |
| 100 | Transcendent | +15 | Ultimate weapon skill + final passive |
Proficiency XP Gain
- Each combat encounter where the weapon is used grants weapon proficiency XP
- XP scales with encounter difficulty (harder fights = faster weapon growth)
- Proficiency slows down at higher levels (0→50 takes roughly the same time as 50→100)
- Estimated time to 100: 3-4 months of regular play with one weapon type
- Players can level multiple weapon types, but spreading focus means slower progress per type
- Weapon proficiency is character-bound — each character tracks their own
Passive Bonuses
At proficiency milestones (40, 60, 80, 100), the weapon type grants a passive bonus that’s always active when wielding that weapon type:
Example — Swords:
- Prof 40: +5% critical hit chance with swords
- Prof 60: Sword skills cost 10% less resource (mana/stamina)
- Prof 80: +10% damage with swords
- Prof 100: Sword attacks have a 10% chance to strike twice
These passives are unique per weapon type, encouraging players to specialize.
Skill Sources
Skills come from four sources. A character’s total available skill pool is the union of all skills they’ve unlocked from each source.
1. Weapon Skills
Unlocked by weapon proficiency (see above). These are the bulk of a character’s offensive skills.
Example — Sword Skills (unlocked at proficiency milestones):
| Prof | Skill | Init | Type | Description |
|---|---|---|---|---|
| 0 | Basic Slash | 5 | Physical | Simple sword attack. Base accuracy 80%. Deals weapon damage. |
| 10 | Quick Thrust | 3 | Physical | Fast stab. Base accuracy 75%. Low damage but acts early. |
| 20 | Crushing Blow | 7 | Physical | Heavy overhead strike. Base accuracy 70%. 1.5x damage. |
| 30 | Parrying Riposte | 4 | Physical/Defensive | Defensive strike. Base accuracy 85%. Reduces incoming damage from next attack by 30%, then counterattacks. |
| 40 | Whirlwind Slash | 6 | Physical/AoE | Spin attack hitting all adjacent enemies. Base accuracy 65%. 0.8x damage to each. |
| 50 | Bleeding Edge | 5 | Physical/DoT | Precise cut. Base accuracy 78%. Deals weapon damage + Bleeding condition (damage over 3 rounds). |
| 60 | Executioner’s Strike | 8 | Physical | Devastating blow. Base accuracy 60%. 2.5x damage. Best against low-HP targets. |
| 70 | Steel Tempest | 3 | Physical/AoE | Lightning-fast series of cuts. Hits 3 random enemies. Base accuracy 72%. 0.7x damage each. |
| 80 | Vorpal Edge | 6 | Physical | Empowered strike. Base accuracy 75%. Critical hit threshold expanded to ≤10 (double crit chance). |
| 90 | Blade Cascade | 4 | Physical | Rapid combo. Base accuracy 70%. Hits same target 3 times at 0.6x damage each. |
| 100 | Sword Saint’s Judgment | 9 | Physical/Ultimate | Massive single strike. Base accuracy 85%. 4x damage. Ignores 50% of target’s evasion. 5-encounter cooldown. |
2. Class Skills
Granted by character class at specific levels. These define the class’s unique combat identity.
Example — Fighter Class Skills:
| Level | Skill | Init | Description |
|---|---|---|---|
| 1 | Second Wind | 2 | Heal self for 15% max HP. 3 uses per dungeon run. |
| 2 | Tactical Assessment | 1 | Swift skill (doesn’t use a queue slot). Reveals enemy weaknesses for 3 rounds (+10% accuracy for party). |
| 3 | (Subclass skill) | Varies | Depends on subclass chosen |
| 5 | Action Surge | 0 | This round, execute 3 skills instead of 2. 1 use per dungeon run. |
| 7 | Intimidating Presence | 6 | Frightened condition on target (resistance check vs. CHA). |
| 10 | Indomitable | — | Passive: Once per encounter, automatically re-roll a failed resistance check. |
| 14 | Veteran’s Endurance | — | Passive: +15% max HP. |
| 20 | Legendary Commander | 1 | All allies execute +1 skill this round. 1 use per dungeon run. |
Example — Wizard Class Skills:
| Level | Skill | Init | Description |
|---|---|---|---|
| 1 | Arcane Bolt | 4 | Basic magical attack. Uses INT. Base accuracy 80%. Scales with level. |
| 1 | Mana Shield | 3 | Absorb next incoming hit, spending mana instead of HP. |
| 2 | Elemental Burst | 6 | AoE damage. Choose element at queue time (fire/cold/lightning). Targets resist with relevant save. |
| 3 | (Subclass skill) | Varies | |
| 5 | Arcane Missiles | 2 | 3 bolts that auto-hit (no accuracy roll) for moderate damage each. High mana cost. |
| 7 | Counterspell | 1 | Reactive: Negates next enemy magical skill. 2 uses per dungeon run. |
| 10 | Spell Weaving | — | Passive: 15% chance that a spell skill doesn’t consume mana. |
| 14 | Meteor Strike | 10 | Massive AoE. Very high damage. Very high mana cost. Long wind-up (init 10). |
| 20 | Time Stop | 0 | Execute your next 3 queued skills before anyone else acts. 1 use per dungeon run. |
3. Species Skills
One skill granted by species (race). Always available from level 1.
| Species | Skill | Init | Description |
|---|---|---|---|
| Human | Adaptability | — | Passive: Once per dungeon run, an action that misses by ≤5% automatically hits instead. |
| Elf | Keen Focus | 2 | +15% accuracy on next skill. Swift (doesn’t use queue slot). 3 uses per dungeon run. |
| Dwarf | Stone Endurance | 3 | Reduce incoming damage by 25% for 2 rounds. 2 uses per dungeon run. |
| Halfling | Lucky Dodge | — | Passive: When hit by a critical miss trigger (96-100), re-roll once. |
| Orc | Relentless Fury | 1 | When reduced below 20% HP, automatically triggers: gain 10% HP back and +20% damage for 2 rounds. Once per dungeon run. |
| Gnome | Arcane Resistance | — | Passive: +15% resistance to all magical effects. |
| Tiefling | Infernal Rebuke | 2 | When hit, automatically deal fire damage back to attacker. 3 uses per dungeon run. |
| Dragonborn | Breath Weapon | 7 | AoE elemental damage (type chosen at character creation). Base accuracy 75%. 2 uses per dungeon run. |
4. Gear Skills
Some equipment grants bonus skills that can be placed in the queue. These are a major part of the loot chase — finding a weapon with a powerful attached skill is exciting.
Examples:
- Flamebrand Longsword (Rare): Grants “Flame Burst” (init 5, AoE fire damage around the target)
- Shield of the Sentinel (Very Rare): Grants “Guardian’s Wall” (init 1, absorb damage for lowest-HP ally for 1 round)
- Cloak of Shadows (Uncommon): Grants “Vanish” (init 2, become untargetable for 1 round, next attack is a guaranteed critical)
- Ring of Mending (Rare): Grants “Pulse Heal” (init 8, heal self and all allies for 10% max HP)
Gear skills follow the same rules as all other skills — they occupy a queue slot, have initiative values, and can have conditionals.
Resource System
Skills aren’t free. Most non-basic skills cost resources to use. Resource management across a multi-encounter dungeon is a core strategic concern. Each class has a unique resource with different pool sizes, recovery rates, and behaviors — this is what makes classes feel fundamentally different even when using the same weapons.
The Zero-Cost Rule
Every class has access to zero-cost basic weapon attacks. The basic attack for each weapon type (Basic Slash, Stab, Shoot, etc. at proficiency 0) costs NO resources. This ensures a character is never completely helpless when their resource runs dry — they just lose access to their powerful skills and fall back on basics. Running on empty is weak, but it’s not dead in the water.
Resource Types
| Resource | Used By | Pool Size (Level 25) | Between-Encounter Recovery | In-Combat Recovery | Behavior |
|---|---|---|---|---|---|
| Stamina | Vanguard, Pathfinder | ~200 | 50% | None | Reliable workhorse. High pool, good recovery. Never flashy, never dry. |
| Momentum | Shade | 0-100 (capped) | Retains 50% of current value | +20 per round | Builds during combat, partially carries over. Rewards aggression across encounters. |
| Mana | Arcanist | ~150 | 25% | None | Powerful but limited. Must ration across dungeon. Spell Mastery reduces costs over time. |
| Devotion | Warden | ~140 | 25% | None | Precious healing fuel. Running dry means the party loses its healer. Careful rationing essential. |
| Focus | Pathfinder | ~160 | 50% | None | Most sustainable caster resource. Enables consistent ranged pressure across long dungeons. |
| Fury | Berserker | 0-100 (capped) | Retains 25% of current value | Gain based on damage dealt | Damage-driven. Hit things to power up. Partially carries over to reward aggressive play. |
| Resonance | Songweaver | ~180 | 15% | None | Starts high, depletes slowly. Lowest recovery rate among pool resources. Must be managed carefully over full dungeon. |
| Zeal | Oathblade | ~120 | 30% | None | Moderate pool. Smite burns extra on top of skill costs, so effective recovery is lower. The burst-vs-sustain tension. |
| Essence | Hexbinder | ~80 | 50% | None | Smallest pool but fastest recovery. Designed for short bursts each encounter. Never stockpiles, but never runs dry. |
| Charges | Species skills, gear skills, some class skills | Fixed (varies) | Do NOT recover | Do NOT recover | Powerful one-shots. Budget carefully across the whole dungeon run. |
Pool sizes are approximate at level 25 with moderate attributes. Actual values scale with level + relevant attribute.
Resource Pool Formulas
| Resource | Formula |
|---|---|
| Stamina | 60 + (Fortitude × 4) + (Level × 4) |
| Momentum | Fixed cap of 100 (not scaled by stats) |
| Mana | 30 + (Logic × 5) + (Level × 3) |
| Devotion | 30 + (Presence × 5) + (Level × 3) |
| Focus | 40 + (Logic × 4) + (Level × 3.5) |
| Fury | Fixed cap of 100 (not scaled by stats) |
| Resonance | 50 + (Presence × 4) + (Level × 4) |
| Zeal | 30 + (Presence × 3) + (Level × 3) |
| Essence | 20 + (Logic × 3) + (Level × 2) |
Detailed Resource Behaviors
Stamina (Vanguard, Pathfinder)
The baseline “reliable” resource. High pool, 50% recovery between encounters. A Vanguard or Pathfinder should be able to use powered skills in every encounter of a 10-encounter dungeon without running dry — as long as they don’t exclusively spam their most expensive skills.
- Dungeon curve: Flat. Consistent performance from encounter 1 to encounter 10.
- Strategic tension: Low. Stamina users focus more on queue optimization than resource management.
Momentum (Shade)
Starts at 0 each dungeon. Builds +20 per round of combat. Retains 50% between encounters (rounded down).
- Round 1: Momentum = 0 (or carry-over from last fight). Basic skills only.
- Round 2: Momentum = 20+. Cheap powered skills available.
- Round 3: Momentum = 40+. Mid-tier skills available.
- Round 4+: Momentum = 60+. Premium skills available.
- Between encounters: If ending a fight at 80 momentum → carry 40 into the next fight.
- Dungeon curve: Ramps up. Early encounters are weaker; by encounter 3-4 the Shade enters each fight with 30-40 banked momentum and hits their power spike faster.
- Strategic tension: Queue ordering matters enormously. Cheap skills early in the queue build momentum; expensive skills later in the queue spend it.
- Assassin subclass bonus: Skills deal +1% damage per 5 momentum currently banked (at 80 momentum: +16% damage). Rewards banking momentum rather than spending it freely.
Mana (Arcanist)
Classic caster resource. 25% recovery is the second-lowest among pool resources (only Resonance is lower). The Arcanist must choose: go nuclear on early encounters (Fireball everything) and scrape by later, or conserve mana and deal moderate damage throughout.
- Spell Mastery class feature reduces costs the more a skill is used in a dungeon, rewarding consistent skill selection over the full run.
- Dungeon curve: Declining. Peak power in encounters 1-3, then gradually weaker as mana depletes. Strategic play extends the peak.
- Strategic tension: High. Which encounters deserve your expensive spells? Can you clear this fight with cantrips and save mana for the boss?
Devotion (Warden)
Same 25% recovery as mana. But the stakes are higher: when the Warden runs dry, the party loses ALL healing. This makes Warden resource management the most consequential in the game for party play.
- Sanctify (free, once per encounter) provides a 20% healing boost window — timing Sanctify with your biggest heals maximizes devotion efficiency.
- Dungeon curve: Declining, with catastrophic failure state. Smart Wardens heal only when necessary and let natural regen handle minor damage.
- Strategic tension: Highest of any class. Over-healing wastes devotion. Under-healing risks party death. The Warden is always making resource calls.
Focus (Pathfinder)
50% recovery — tied with Stamina for the best among pool resources. Combined with a moderate pool size, the Pathfinder is the most sustainable ranged class.
- Dungeon curve: Nearly flat. Slight decline over very long dungeons but generally consistent.
- Strategic tension: Low-medium. Pathfinders can afford to use powered skills most rounds.
Fury (Berserker)
Starts at 0. Builds based on damage dealt only (not damage taken). Each skill that hits grants Fury proportional to the damage dealt (roughly +1 Fury per 3% of target’s max HP dealt). Retains 25% between encounters.
- Rage triggers at 50 Fury: +25% melee damage, -15% evasion, 3 skills per round, immune to Frightened.
- Rage ends if Fury drops below 25 (Fury naturally decays -5/round while Raging, creating a timer — must keep hitting to sustain it).
- Between encounters: If ending at 70 Fury → carry 17 into next fight. Not enough to start Raging, but a head start.
- Dungeon curve: Volatile. Short easy fights may never trigger Rage. Long hard fights trigger Rage quickly. Boss encounters are where Berserkers shine.
- Strategic tension: Medium. Queue should front-load fast, reliable hits to build Fury, then switch to heavy power skills once Raging.
Resonance (Songweaver)
Starts at max. 15% recovery between encounters — the lowest of any pool resource. The Songweaver’s arc is unique: they start as the strongest buffer in the game and gradually weaken.
- Anthem is free — the passive Anthem effect costs no Resonance and is always active. The Songweaver is never useless.
- Active skills (buffs, debuffs, heals) cost Resonance.
- Dungeon curve: Declining, but with a floor. Even at 0 Resonance, the Anthem provides value and basic weapon attacks are free.
- Strategic tension: Very high. Every active skill expenditure is a permanent investment. Is this encounter worth blowing a buff, or should you coast on the Anthem and basic attacks?
- Rest points provide a significant Resonance boost (50%), making rest point decisions critical for Songweavers.
Zeal (Oathblade)
Moderate pool, 30% recovery. The catch: Smite costs Zeal ON TOP OF the skill’s normal cost. A Smited Crushing Blow costs the base Crushing Blow stamina/zeal PLUS the Smite surcharge. This makes Oathblades burn resources roughly 1.5-2x faster than other classes when Smiting.
- Dungeon curve: Depends entirely on Smite usage. An Oathblade who Smites every skill is dry by encounter 5. One who only Smites bosses lasts the whole dungeon.
- Strategic tension: High. Which skills to Smite is the core Oathblade decision. Smite the opening power strike for burst? Or save Smites for the boss?
- Avenger subclass reduces Smite cost by 25%, making the burn rate much more sustainable.
Essence (Hexbinder)
Smallest pool but 50% recovery — the fast-cycle resource. A Hexbinder enters each encounter with a small burst window (3-4 powered skills), then falls back to basics or drain-style skills that are cheap/free.
- Pact of the Void subpath makes drain skills cost 0 Essence but deal 15% less damage — the sustained option.
- Dungeon curve: Flat but in a different way from Stamina. Each encounter is a mini-cycle: burst → conserve → burst → conserve.
- Strategic tension: Medium. The question isn’t “will I run dry over the dungeon” but “how do I get maximum value out of my 3-4 powered skills per encounter.”
Resource Recovery at Rest Points
Rest points (see 06-quests-and-dungeons.md) provide enhanced recovery:
| Resource | Rest Point Recovery |
|---|---|
| Stamina | 100% (full) |
| Momentum | Set to 50 (regardless of current value) |
| Mana | 50% |
| Devotion | 50% |
| Focus | 100% (full) |
| Fury | Set to 0 (rage subsides during rest) |
| Resonance | 50% (crucial for Songweavers) |
| Zeal | 50% |
| Essence | 100% (full) |
| Charges | Do NOT recover (even at rest) |
Running Out of Resources
When a skill in the queue would fire but the character doesn’t have enough resources:
- The skill is skipped (same as a failed conditional)
- The queue advances to the next slot
- The character does NOT lose the action — if the next slot has an affordable skill (or a zero-cost basic attack), it fires instead
- If NO skills in the queue are affordable, the character performs a zero-cost basic weapon attack as a fallback
This means even depleted characters contribute damage. They’re significantly weaker, but never dead weight.
10-Encounter Dungeon Performance Summary
How each class performs across a full 10-encounter dungeon (no rest points):
| Class | Enc 1-3 | Enc 4-6 | Enc 7-10 | Overall Arc |
|---|---|---|---|---|
| Vanguard | ★★★ | ★★★ | ★★★ | Flat — consistent all dungeon |
| Shade | ★★ | ★★★ | ★★★★ | Rising — builds momentum carry-over |
| Arcanist | ★★★★ | ★★★ | ★★ | Declining — front-loaded power |
| Warden | ★★★ | ★★★ | ★★ | Declining — must ration heals |
| Pathfinder | ★★★ | ★★★ | ★★★ | Flat — sustainable ranged |
| Berserker | ★★★ | ★★★ | ★★★ | Volatile — spikes on hard fights |
| Songweaver | ★★★★ | ★★★ | ★★ | Declining — Anthem sustains floor |
| Oathblade | ★★★★ | ★★★ | ★★ | Declining — if Smiting; Flat if conserving |
| Hexbinder | ★★★ | ★★★ | ★★★ | Flat — burst/recover cycle each encounter |
This diversity of power curves is intentional. Parties benefit from mixing classes with different arcs — an Arcanist who goes nuclear early paired with a Shade who comes online later creates sustained total DPS across the dungeon.
Encounter Flow
1. Surprise Check
Attacker Stealth Rating vs. Defender Perception Rating → d100 roll
- If the party’s scout (highest stealth) beats the enemies’ perception → party gets a surprise round (one full cycle of skills before enemies act)
- If enemies beat the party’s perception → enemies get a surprise round
- Shades and Pathfinders have class bonuses to stealth; Perception is a key defensive stat
2. Combat Rounds
Each round:
- Collect the next queued skill from each combatant
- Sort all skills by initiative value (lowest first)
- Execute skills in order, resolving hits/misses/effects
- Check for triggered reactive skills (species abilities, gear procs)
- Apply end-of-round effects (DoTs, condition timers, regeneration)
- Check for combat end conditions
3. Combat Ends When
- All enemies reach 0 HP (victory)
- The player character reaches 0 HP (defeat — see Death and Recovery)
- A special condition is met (survive X rounds, destroy an object, etc.)
- An enemy flees (some enemies flee at low HP)
4. Between Encounters (Partial Reset)
- Queue resets to slot 1
- HP does NOT fully recover — only natural regen (5-10% based on Fortitude) unless a healing skill or potion is available
- Resource recovery varies by type (see Resource System above):
- Stamina/Focus/Essence: 50%
- Zeal: 30%
- Mana/Devotion: 25%
- Resonance: 15%
- Momentum: retains 50% of current value
- Fury: retains 25% of current value
- Charges: do NOT recover
- Conditions persist (poison, bleeding, etc. carry to next encounter unless cured)
- Cooldowns tick down (a 5-encounter cooldown skill becomes available sooner)
This partial reset creates tension across a full dungeon run. A player who blows all their mana in encounter 1 will struggle by encounter 5.
Conditions and Effects
Conditions are status effects applied during combat by skills, traps, environmental hazards, and enemy abilities. They are central to strategy — applying the right debuffs and avoiding the wrong ones is as important as raw damage.
Conditions fall into three categories: Debuffs (harmful, applied to enemies), Buffs (beneficial, applied to self/allies), and Environmental (caused by dungeon hazards, affect everyone).
How Conditions Work
- Application: A skill that inflicts a condition triggers a resistance check on the target (unless the condition is irresistible or a buff)
- Resistance check:
d100 roll vs. target's Resistance Chancefor the relevant attribute. Roll ≤ resistance chance = resisted (condition doesn’t apply). Roll > resistance chance = condition lands. - Resistance Chance formula:
Base Resistance (30%) + (Relevant Attribute × 2%) + Gear/Buff Bonuses + Fortitude Blanket Bonus - Fortitude blanket bonus: Every point of Fortitude above 10 adds +1.5% to ALL resistance checks, regardless of type. This makes Fortitude the universal defensive attribute for conditions.
- Stacking: Most conditions do NOT stack with themselves. Reapplying a condition refreshes the duration instead. Exceptions are noted (Bleeding, Exhaustion).
- Condition cap: A character can be affected by a maximum of 5 debuffs simultaneously. If a 6th is applied, the one with the shortest remaining duration is removed. This prevents debuff avalanches from making a character completely nonfunctional.
- Cleansing: Conditions can be removed early by cleanse skills (Warden, Songweaver, some consumables), or by specific counters noted per condition.
Debuffs (Harmful Conditions)
Physical Debuffs
Bleeding
- Effect: Lose 4% of max HP at the end of each round. Does not trigger resistance checks to end — runs its full duration.
- Duration: 3 rounds
- Resisted By: Fortitude
- Stacking: YES — each application adds a separate Bleeding stack. 3 stacks of Bleeding = 12% max HP per round. Max 5 stacks.
- Cured By: Healing skill (removes 1 stack per heal), Healer’s Kit consumable (removes all stacks), rest point (removes all stacks)
- Strategic Notes: Bleeding is the primary sustained damage condition. Classes like the Shade (Saboteur) and weapons like daggers (Bleeding Edge) specialize in stacking bleeds. Against high-HP bosses, stacking bleeds can outdamage direct attacks. Fortitude-heavy builds resist the initial application but can’t stop it once it’s on.
- Combat Log Display:
⚠ BLEEDING (×2) — Losing 8% max HP/round (14 damage/round). 2 rounds remaining.
Poisoned
- Effect: -12% accuracy on all skills AND -12% damage dealt.
- Duration: 4 rounds OR until cured
- Resisted By: Fortitude
- Stacking: No (refreshes duration)
- Cured By: Antidote consumable (instant cure), Warden cleanse skill, rest point, or Fortitude resistance check at end of each round (separate roll — 50% of normal resistance chance, so it’s hard to shake off naturally)
- Strategic Notes: Poison is a comprehensive debuff that makes the target worse at everything. It’s most effective against damage dealers (reducing their output) and against enemies with high accuracy (making them miss). The per-round resistance check means high-Fortitude characters can shrug it off naturally; low-Fortitude characters are stuck for the full duration.
- Combat Log Display:
☠ POISONED — -12% accuracy, -12% damage. 3 rounds remaining. End-of-round Fortitude check: d100: 72 vs. 23% → FAILED to shake off.
Weakened
- Effect: -20% damage dealt on all skills.
- Duration: 3 rounds
- Resisted By: Might
- Stacking: No (refreshes duration)
- Cured By: Buff skills that grant Empowered (overrides Weakened), cleanse, rest point
- Strategic Notes: Pure damage reduction debuff. Most effective against enemies with few but powerful attacks (bosses). Less useful against swarm enemies. Resisted by Might, so casters and healers are more vulnerable to it.
Exhaustion
- Effect: Cumulative debuff with escalating levels. Each level stacks on the previous:
- Level 1: -5% accuracy, -5% damage, -5% evasion
- Level 2: -10% all stats, resource recovery between encounters halved
- Level 3: -20% all stats, only 1 skill per round
- Level 4: Unconscious (effectively dead for the encounter; in party play, can be revived)
- Duration: Until a rest point is reached OR the dungeon ends. Does NOT expire on its own.
- Resisted By: Cannot be resisted. Exhaustion is a dungeon management mechanic, not a combat mechanic.
- Stacking: YES — by levels. Each new source of exhaustion adds +1 level.
- Caused By: Running out of rations, failing certain environmental hazards, Berserker’s Frenzy subclass abilities, some boss mechanics, consecutive fights without rest
- Cured By: Rest points reduce exhaustion by 1 level. Some Warden skills can reduce by 1 level. Completing the dungeon fully removes exhaustion.
- Strategic Notes: Exhaustion is the penalty for poor preparation — not bringing enough rations, skipping rest points, or pushing through content that’s too hard. It’s also a design tool for making dungeon length matter. A 15-encounter dungeon without rest points will exhaust characters who aren’t prepared.
- Combat Log Display:
⚡ EXHAUSTION Level 2 — -10% all stats, resource recovery halved.
Elemental Debuffs
Burning
- Effect: Lose 6% of max HP at the start of each round. If another Burning creature is adjacent, 25% chance to spread Burning to non-burning adjacent targets (enemies AND allies — friendly fire risk for AoE fire skills).
- Duration: 2 rounds
- Resisted By: Speed (dodge the initial source)
- Stacking: No (refreshes duration)
- Cured By: Using an action to extinguish (costs a queue slot — the “Extinguish” fallback action), water-based environmental effects, cold damage (removes Burning), cleanse
- Strategic Notes: Burning is high damage-over-time but short duration and has a built-in counterplay (spend an action to extinguish). The spread mechanic makes Burning powerful against groups but dangerous in parties — an Arcanist (Evoker) who Burns an enemy adjacent to the party’s Vanguard may accidentally set the tank on fire. The Evoker subclass can shape AoE to avoid this.
Frozen
- Effect: Initiative values on ALL skills increased by +4 (act significantly slower). -10% evasion (harder to dodge while frozen in place).
- Duration: 2 rounds
- Resisted By: Fortitude
- Stacking: No (refreshes duration)
- Cured By: Fire damage (removes Frozen), Burning condition (removes Frozen — fire and ice cancel), cleanse, natural expiration
- Counter-Synergy with Burning: Frozen and Burning cancel each other. Applying Burning to a Frozen target removes Frozen (and vice versa). This creates tactical decisions — do you Freeze an enemy to slow them, knowing your Arcanist’s fire spells will unfreeze them?
- Strategic Notes: Frozen is the premier “slow you down” debuff. In the skill queue system, +4 initiative is devastating — a Quick Thrust (init 3) becomes init 7, acting after most enemy skills. Frozen is best used against fast enemies to neutralize their speed advantage.
Shocked (Lightning)
- Effect: 30% chance each round that the next queued skill fizzles (activates but automatically misses, still consuming resources). Additionally, -5% accuracy on all skills.
- Duration: 2 rounds
- Resisted By: Fortitude
- Stacking: No (refreshes duration)
- Cured By: Cleanse, natural expiration
- Strategic Notes: Shocked is the most RNG-heavy debuff. The 30% fizzle chance creates dramatic moments in the combat log — the enemy boss’s devastating power strike fizzles harmlessly due to being Shocked. Unreliable but potentially game-changing.
Corroded (Acid)
- Effect: -15% evasion (armor is dissolving) AND all incoming damage increased by 10%.
- Duration: 3 rounds
- Resisted By: Fortitude
- Stacking: No (refreshes duration)
- Cured By: Cleanse, natural expiration
- Strategic Notes: Corroded is a “damage amplifier” debuff. It doesn’t deal damage directly but makes the target dramatically more vulnerable to everything else. Apply Corroded first, THEN hit with power strikes. Excellent in party play where one character applies Corroded and others exploit it.
Mental Debuffs
Frightened
- Effect: -15% accuracy on all skills. Cannot target the source of fear (skills targeting the frightener are redirected to the next valid target per the target rule). If the source of fear is the ONLY enemy, -25% accuracy instead (nowhere to redirect).
- Duration: 2 rounds
- Resisted By: Presence
- End-of-round check: At the end of each round, the Frightened character makes a Presence resistance check at 50% of normal resistance chance. Success removes Frightened early.
- Cured By: Warden cleanse, Songweaver’s Anthem of Valor (provides immunity while active), Berserker’s Rage (grants immunity), natural end-of-round check
- Strategic Notes: Frightened is the premiere “keep away” condition. A tank who Frightens enemies protects squishier allies. An enemy that Frightens the party’s damage dealer can stall a fight. Presence-focused classes (Warden, Songweaver, Oathblade) resist it easily; Logic/Speed classes are more vulnerable.
Charmed
- Effect: Cannot target the charmer with any offensive skill. 25% chance each round that the Charmed character’s offensive skill targets a random ally instead of an enemy (in party play — solo characters target themselves for reduced damage). The Charmed character’s accuracy against non-charmer targets is unaffected.
- Duration: 3 rounds OR until the Charmed character takes damage from any source (damage breaks the charm)
- Resisted By: Presence
- Stacking: No (refreshes duration and source)
- Cured By: Taking any damage (including DoTs — a Bleeding character auto-breaks charm), cleanse, natural expiration
- Strategic Notes: Charm is terrifying in party play — a Charmed Berserker might Crushing Blow their own Warden. The counterplay is immediate: ANY damage breaks it. This means Burning or Bleeding actually serves as charm protection. Clever parties ensure their high-damage members always have a minor DoT ticking to prevent charm disasters.
- Combat Log Display:
💜 CHARMED by Siren — Cannot target Siren. Friendly fire risk: 25%. Damage will break charm.
Confused
- Effect: Each round, 40% chance that the queued skill targets a random combatant (could be an ally, an enemy, or self) instead of the configured target. Remaining 60% of the time, skills execute normally.
- Duration: 2 rounds
- Resisted By: Logic
- Stacking: No (refreshes duration)
- Cured By: Cleanse, natural expiration
- Strategic Notes: Confused is the “chaos” debuff. Unlike Charm (which has a clear source and clear counterplay), Confusion is just random disruption. It’s less severe than Charm (40% misdirection vs. guaranteed) but has no easy counterplay. Most dangerous on high-damage characters.
Silenced
- Effect: Cannot use magical skills (any skill tagged as Magical type). Physical and weapon skills are unaffected. Resource-based class skills that require verbal/magical components are also blocked.
- Duration: 2 rounds
- Resisted By: Logic
- Stacking: No (refreshes duration)
- Cured By: Cleanse, natural expiration
- Affected Classes: Arcanist (severely — most skills are magical), Warden (healing skills are magical), Hexbinder (most skills), Songweaver (active buffs are magical, but Anthem persists since it’s passive). Vanguard, Shade, Berserker, Pathfinder largely unaffected.
- Strategic Notes: Silence is an anti-caster condition. The Oathblade (Inquisitor) subclass specializes in applying Silence. Against a party, Silencing the Warden shuts down healing. Against enemy casters, Silence is the best counter available.
- Combat Log Display:
🔇 SILENCED — Cannot use magical skills. Physical skills unaffected. 2 rounds remaining.
Movement/Action Debuffs
Stunned
- Effect: Skip the next queued skill entirely (lose 1 action). If a character would execute 2 skills this round, they only execute 1. If they would execute 1 (already Slowed), they execute 0.
- Duration: 1 round (always — Stun is short but devastating)
- Resisted By: Fortitude
- Stacking: No. If already Stunned, reapplication has no effect (can’t double-stun).
- Cured By: Automatic after 1 round. Cannot be cleansed early (it’s too fast — cleanse happens on your turn, but the stun already consumed your action).
- Strategic Notes: Stun is the strongest single-round debuff in the game. Losing an entire action in a queue-based system is massive — it pushes back your entire queue by one slot, potentially desynchronizing combos. The Shield skill line (Shield Bash) is one of the most reliable Stun sources. Bosses typically have increased Stun resistance or are immune.
Slowed
- Effect: Only execute 1 skill per round instead of 2 (or instead of 3 for Raging Berserkers / Vanguards at level 25+).
- Duration: 2 rounds
- Resisted By: Speed
- Stacking: No (refreshes duration)
- Cured By: Haste buff (overrides Slow — they cancel each other), cleanse, natural expiration
- Counter-synergy with Haste: Slowed and Hasted cancel each other. Applying Haste to a Slowed character removes Slow. Applying Slow to a Hasted character removes Haste.
- Strategic Notes: Slowed is Stun’s longer, milder cousin. Losing 1 skill per round for 2 rounds means losing 2 total skills — same as being Stunned twice. But Slowed is resistable, cleansable, and counterable with Haste. Most effective against Berserkers (drops them from 3 skills/round while Raging to 1 — devastating).
Blinded
- Effect: -25% accuracy on all skills. Cannot detect traps (if Blinded during a trap encounter). Auto-fail sight-based skill checks.
- Duration: 2 rounds
- Resisted By: Speed (dodge the blinding effect)
- Stacking: No (refreshes duration)
- Cured By: Cleanse, Warden healing skill (some remove Blinded as a secondary effect), natural expiration
- Strategic Notes: Blinded is the heaviest single accuracy penalty in the game. A Blinded character with 80% base accuracy drops to 55% — coin-flip territory. Most impactful on power-strike builds where every miss is a massive DPS loss. Less impactful on DoT/condition builds that don’t rely on accuracy as much.
Rooted
- Effect: Cannot use skills tagged as “movement” or “repositioning.” Melee skills can only target adjacent enemies (can’t close distance). Ranged skills are unaffected. -10% evasion.
- Duration: 2 rounds
- Resisted By: Might (brute force to break free)
- Stacking: No (refreshes duration)
- Cured By: Might resistance check at end of each round (50% of normal chance), cleanse, fire damage (burns the roots)
- Strategic Notes: Rooted is a positioning debuff. In a system without physical movement, its primary effect is preventing certain skills that involve repositioning (Shadow Step, dash-type abilities) and reducing evasion. Most impactful against Shades and other mobile classes.
Buffs (Beneficial Conditions)
Buffs are applied by support skills, self-buffs, consumables, and some class mechanics. They cannot be resisted (you don’t resist your own buffs). They CAN be dispelled by enemy skills that strip buffs (Vexari’s Unravel, Hexbinder’s Witch subclass).
Inspired
- Effect: +10% accuracy and +10% damage on all skills.
- Duration: 2 rounds
- Source: Songweaver skills, Ashenmere species skill (Soul Echo), some gear
- Dispellable: Yes
- Strategic Notes: The bread-and-butter combat buff. Simple, universally useful, always worth applying. Best on high-damage allies (10% of a big number is a big bonus).
Empowered
- Effect: +20% damage on all skills. Overrides Weakened (removes it if present).
- Duration: 2 rounds
- Source: Berserker self-buffs, Oathblade aura skills, Songweaver War Chanter skills
- Dispellable: Yes
- Strategic Notes: Stronger than Inspired’s damage component but doesn’t include accuracy. Best on characters who already have high accuracy and want to push their damage ceiling.
Shielded
- Effect: Absorb the next X points of damage before HP is affected. Shield value varies by the skill that applied it (typically 10-30% of the caster’s max HP). Multiple Shielded effects stack their absorb values.
- Duration: Until depleted OR 5 rounds (whichever comes first)
- Source: Vanguard (Bulwark), Warden skills, Shield weapon skills, some gear
- Dispellable: Yes (dispel removes the entire shield)
- Strategic Notes: Shielded is the premier damage prevention buff. It’s more efficient than healing because it prevents damage before it happens (no HP lost, no healing needed). Stacking shields on a character before a boss encounter is a powerful preparation strategy.
Regenerating
- Effect: Recover 4% of max HP at the start of each round.
- Duration: 3 rounds
- Source: Warden heal-over-time skills, Thornkin species skill (Regenerative Bark — enhanced version), some potions, Songweaver Anthem of Mending (weaker but free and permanent)
- Dispellable: Yes
- Strategic Notes: Efficient healing over time. 4% per round × 3 rounds = 12% total — often more than a direct heal for the same resource cost. Best applied proactively before damage is taken.
Hasted
- Effect: Execute 3 skills per round instead of 2 (or 4 instead of 3 for Raging Berserkers). Overrides Slowed (removes it if present).
- Duration: 2 rounds
- Source: Arcanist (Chronomancer) skills, rare consumables (Potion of Speed), high-level Songweaver skills
- Dispellable: Yes
- Strategic Notes: Haste is one of the most powerful buffs in the game. An extra skill per round for 2 rounds means 2 extra actions total — effectively a free round of combat. Because the skill queue loops, Haste doesn’t just add actions, it accelerates your queue cycle, getting you back to your best skills faster. Extremely valuable on damage dealers.
Fortified
- Effect: +15% evasion and +15% resistance to all conditions.
- Duration: 3 rounds
- Source: Vanguard defensive skills, Shield skills (Shield Wall), Oathblade (Sentinel) aura, some potions
- Dispellable: Yes
- Strategic Notes: The defensive counterpart to Inspired. Makes a character harder to hit and harder to debuff. Best applied to the character most likely to be targeted (the tank) or most vulnerable to conditions (the Arcanist with low Fortitude).
Focused
- Effect: Critical hit threshold improved by +5% (e.g., base ≤5 becomes ≤10). Next critical hit deals 2x damage instead of the normal 1.5x.
- Duration: 3 rounds OR until a critical hit is scored (whichever comes first)
- Source: Shade self-buffs, Pathfinder (Eagle Eye), Assassin subclass skills, some gear
- Dispellable: Yes
- Strategic Notes: Focused turns the next critical hit into a devastating blow. Best applied before a high-accuracy, multi-hit skill (Viper’s Dance, Arrow Storm) to maximize the chance of triggering the enhanced crit.
Invisible
- Effect: Cannot be targeted by single-target enemy skills for the duration. AoE skills still affect Invisible characters. First offensive skill used while Invisible gains +20% accuracy and +20% damage (the Invisible character then becomes visible).
- Duration: 1 round OR until an offensive skill is used
- Source: Shade (Phase Step, Shadow Step), Potion of Invisibility, Phantom subclass skills
- Dispellable: No (it’s a physical state, not a magical effect)
- Strategic Notes: Invisibility is a powerful burst setup — become invisible, then unleash a devastating skill at +20%. It’s also defensive in that enemies can’t target you for a round. The AoE vulnerability prevents it from being a complete immunity.
Taunted (Forced Targeting)
- Effect: Affected enemy MUST target the Taunter with their next offensive skill. If the Taunter is untargetable (Invisible, dead), the Taunt breaks.
- Duration: 2 rounds
- Source: Vanguard skills (Guardian Stance, Bulwark subclass), Shield skills, Intimidation-based skills
- Applied to: Enemies (it’s technically a debuff on the enemy, but listed here because it’s a tank BUFF mechanic)
- Resisted By: Presence (the enemy resists being forced)
- Strategic Notes: Taunt is the core tanking mechanic. A Vanguard who can reliably Taunt enemies protects the party’s squishier members. Taunt interacts with the enemy’s queue — the enemy’s next skill is forced to target the Taunter, which may waste a heal or buff on an offensive action. Bosses may have Taunt resistance or immunity on certain phases.
Environmental Conditions
These are applied by dungeon hazards, traps, and environmental effects — not by skills. They affect any character in the encounter.
Darkness
- Effect: All characters without Darkvision: -15% accuracy, -25% trap detection, cannot detect hidden rooms. Characters WITH Darkvision (some species or gear) are unaffected.
- Duration: Entire encounter (or until a torch is used)
- Cured By: Using a torch (consumable), Light-producing skills, Darkvision gear
- Strategic Notes: This is why you bring torches. A dark encounter without torches is a serious handicap, especially for trap detection. Some dungeon types (caves, crypts) are primarily dark.
Wet
- Effect: +50% damage from Lightning/Shock effects. -50% damage from Fire effects. Burning condition is immediately removed.
- Duration: Entire encounter OR 3 rounds after the water source is passed
- Caused By: Flooded rooms, rain, water traps
- Strategic Notes: Wet dramatically changes elemental combat math. An Arcanist (Evoker) should switch from fire to lightning spells in wet encounters. Enemies in a flooded room are extremely vulnerable to Chain Lightning.
Extreme Heat
- Effect: All characters lose 2% max HP per round. Fire resistance reduces this to 1%. Fortitude resistance check each round — failure adds 1 Exhaustion level at the end of the encounter.
- Duration: Entire encounter
- Caused By: Volcanic areas, fire-themed dungeons, desert environments
- Strategic Notes: Extreme Heat makes long fights dangerous. Quick kills are rewarded. Fire Resistance Potions become essential in fire-themed dungeons.
Extreme Cold
- Effect: All characters have +2 initiative on all skills (act slower). Fortitude resistance check each round — failure adds 1 Exhaustion level at the end of the encounter. Fire-based skills deal +15% damage (the heat is welcome).
- Duration: Entire encounter
- Caused By: Frozen areas, ice-themed dungeons, mountain peaks
- Strategic Notes: Extreme Cold slows everyone down and punishes long fights. The fire damage bonus makes fire-based builds situationally excellent in cold dungeons.
Cursed Ground
- Effect: Healing effectiveness reduced by 50%. Regenerating condition ticks for half value. Necrotic damage increased by 25%.
- Duration: Entire encounter
- Caused By: Undead lairs, corrupted areas, necromantic rituals
- Strategic Notes: Cursed Ground makes healer-dependent parties struggle and is the signature hazard of undead dungeons. Parties should bring extra healing potions for cursed encounters and avoid relying solely on the Warden.
Arcane Interference
- Effect: All magical skills have -10% accuracy. Mana/Essence/Devotion costs increased by 25%. Physical skills are unaffected.
- Duration: Entire encounter
- Caused By: Anti-magic zones, Sundering scars, specific dungeon rooms
- Strategic Notes: Arcane Interference punishes magic-heavy parties. Fights in these zones favor physical damage dealers (Vanguard, Shade, Berserker, Pathfinder with bows). Arcanists and Hexbinders should switch to weapon skills if they have proficiency.
Condition Interactions
Certain conditions interact with each other in specific ways. These interactions create tactical depth and reward players who understand the system.
| Condition A | + Condition B | Interaction |
|---|---|---|
| Burning | + Frozen | Cancel each other (both removed) |
| Burning | + Wet | Burning is immediately removed |
| Frozen | + Fire damage | Frozen is removed; fire damage deals +25% |
| Wet | + Shocked | Shock duration doubled; fizzle chance increased to 50% |
| Wet | + Lightning damage | Lightning damage deals +50% |
| Hasted | + Slowed | Cancel each other (both removed) |
| Empowered | + Weakened | Empowered removes Weakened |
| Charmed | + Any damage | Charm is broken (removed) |
| Bleeding | + Charmed | Bleed tick breaks Charm immediately |
| Invisible | + Burning | The flames reveal the character — Invisible is removed |
| Rooted | + Fire damage | Roots burn away — Rooted is removed |
| Poisoned | + Regenerating | Both active simultaneously — regen heals, poison weakens. They don’t cancel. |
Condition Immunity
Some abilities grant immunity to specific conditions:
- Berserker Rage: Immune to Frightened
- Ironborn (Unyielding): Immune to Rooted while Shielded
- Fortified buff: Not immunity, but +15% resistance makes conditions much harder to land
- Boss mechanics: Many bosses are immune to Stunned and Charmed (noted in their stat block). No boss is immune to Bleeding or Burning (DoTs always work, ensuring damage-over-time builds remain viable against all content).
Condition Priority in the Combat Log
The combat log displays active conditions prominently:
YOUR STATUS: HP 142/200 (71%) | Stamina 64/180
Active: Inspired (1 round), Bleeding ×2 (2 rounds)
Losing 8% HP/round from Bleeding
ENEMY STATUS: Goblin Boss HP 89/150 (59%)
Active: Poisoned (3 rounds), Corroded (2 rounds)
-12% accuracy, -12% damage, -15% evasion, +10% incoming damage
After each encounter, the log summarizes condition impact:
📊 CONDITION IMPACT:
Your Bleeding stacks dealt 42 total damage to you (24% of damage taken)
Your Poisoned debuff on Goblin Boss reduced its damage output by ~18 points
Your Corroded debuff on Goblin Boss increased party damage to it by ~31 points
💡 TIP: Bringing Antidotes would have prevented 42 Bleeding damage.
The Goblin Shaman applied Bleeding twice — consider focusing it first.
Death and Recovery
Falling to 0 HP
When a character reaches 0 HP:
- Solo play: The character is defeated. The encounter (and the run, if no revival items) ends in failure.
- Party play: The character falls unconscious. Each round, roll d100:
- ≤30: Stabilize (stop dying, remain unconscious at 1 HP)
- 31-90: No change (still dying)
- 91-100: Worsen (one step closer to death — 3 worsens = eliminated from the run)
- Allies can use healing skills/items on unconscious characters to revive them
Revival Items
- Revive Scroll (consumable): Immediately revive with 25% HP. Rare and valuable.
- Certain class skills can revive (Warden’s “Breath of Life”, Oathblade’s “Lay on Hands” at high level)
Run Failure Penalties
- Character returns to town
- Partial XP awarded based on encounters completed (~70-80% of earned XP)
- Consumables used during the run are lost (potions, rations, scrolls, charges)
- Gear is NOT lost — no durability loss, no item destruction
- Recovery timer before next run: 15 minutes to 2 hours (scales with dungeon depth)
- Can be sped up with convenience purchase (see 14-monetization.md)
- Weapon proficiency XP is kept — even failed runs contribute to weapon mastery
Why No Permadeath
You can’t intervene in an idle game. Permadeath for something you couldn’t control would be rage-inducing. The penalty — lost time, lost consumables, recovery timer — is meaningful enough to make preparation matter without being devastating.
Skill Design Framework
Skills are categorized by role to ensure a wide variety of strategic options. Every weapon type and class should have skills across multiple categories.
Skill Categories
| Category | Purpose | Init Range | Resource Cost |
|---|---|---|---|
| Quick Strike | Fast, reliable damage. Low risk. | 1-3 | Low |
| Power Strike | Heavy damage. Slow but impactful. | 6-9 | Medium-High |
| AoE | Hit multiple targets. Good vs. groups. | 5-8 | High |
| DoT (Damage over Time) | Apply conditions that deal damage over rounds. Efficient vs. high-HP enemies. | 4-6 | Medium |
| Buff | Strengthen self or allies. Front-load for later payoff. | 2-5 | Medium |
| Debuff | Weaken enemies. Reduce their accuracy, damage, or speed. | 3-6 | Medium |
| Heal | Restore HP to self or allies. | 5-9 | Medium-High |
| Defensive | Reduce incoming damage, raise evasion, absorb hits. | 1-4 | Medium |
| Ultimate | Extremely powerful, very limited use (1-2 per dungeon run). Game-changing. | 0-10 | Charges (fixed uses) |
| Utility | Non-damage effects: cleanse conditions, reveal weaknesses, reposition. | 2-5 | Low-Medium |
Skill Design Rules
- Every skill must have a meaningful initiative value. Fast skills are weaker; slow skills are stronger. This is the core trade-off.
- No skill should be strictly better than another in all situations. Quick Thrust is faster but weaker than Crushing Blow — both have a place.
- Skills should combo. A debuff that lowers evasion makes the next power strike more likely to hit. A buff that increases damage makes the next AoE more devastating. Queue ordering matters.
- Resource costs create choices. A full queue of power strikes runs out of stamina fast. Mixing in basic attacks extends the run.
- Skills per weapon should feel thematically distinct. Sword skills feel different from axe skills — swords are precise and combo-oriented; axes are raw power and cleave.
Expanded Skill Lists by Weapon Type
Axes
| Prof | Skill | Init | Type | Description |
|---|---|---|---|---|
| 0 | Chop | 5 | Physical | Basic axe attack. 80% accuracy. Weapon damage. |
| 10 | Reckless Swing | 4 | Physical | Wild swing. 70% accuracy but 1.3x damage. If miss, self takes 5% HP. |
| 20 | Cleave | 6 | Physical/AoE | Swing through. Hits primary target and one adjacent. 75% accuracy. |
| 30 | Sunder Armor | 7 | Debuff | Heavy strike that reduces target evasion by 10 for 3 rounds. 80% accuracy. |
| 40 | Berserker Chop | 3 | Physical | Fast, frenzied attack. 75% accuracy. Damage increases by 10% for each missing 10% of max HP. |
| 50 | Maiming Strike | 6 | Physical/DoT | Deep wound. 72% accuracy. Weapon damage + heavy Bleeding (8% max HP/round, 3 rounds). |
| 60 | Skull Splitter | 8 | Physical | Devastating overhead. 65% accuracy. 2x damage. On crit: Stun. |
| 70 | Whirlwind | 5 | Physical/AoE | Spin attack hitting ALL enemies. 68% accuracy. 0.9x damage each. |
| 80 | Rampage | 4 | Physical | Each kill with this skill grants +1 additional hit (chain kills). 73% accuracy. |
| 90 | Deathblow | 7 | Physical | 60% accuracy. 3x damage. If this kills the target, fully refund stamina cost. |
| 100 | Worldsplitter | 10 | Ultimate | Massive AoE. 90% accuracy. 3x damage to all enemies. Sunder Armor effect on all hit. 5-encounter cooldown. |
Daggers
| Prof | Skill | Init | Type | Description |
|---|---|---|---|---|
| 0 | Stab | 3 | Physical | Quick dagger attack. 85% accuracy. Low damage but fast. |
| 10 | Poison Strike | 4 | Physical/DoT | Envenomed blade. 80% accuracy. Weapon damage + Poisoned condition. |
| 20 | Backstab | 2 | Physical | Strikes from the shadows. 75% accuracy. 2x damage if target hasn’t acted yet this round. |
| 30 | Fan of Knives | 5 | Physical/AoE | Throw daggers at up to 3 enemies. 70% accuracy each. 0.6x damage per target. |
| 40 | Eviscerate | 6 | Physical | Precise disemboweling. 78% accuracy. 1.5x damage + heavy Bleeding. |
| 50 | Shadow Step | 1 | Utility/Buff | Become untargetable until your next offensive skill. Swift (doesn’t consume queue slot). 2/dungeon. |
| 60 | Assassinate | 7 | Physical | Full power surprise attack. 70% accuracy. 3x damage. Double crit range (≤10). |
| 70 | Lacerate | 3 | Physical/DoT | Rapid cuts. 82% accuracy. Low immediate damage but applies Bleeding + Weakened. |
| 80 | Death Mark | 4 | Debuff | Mark a target: all attacks against them gain +15% accuracy and +15% damage for 3 rounds. |
| 90 | Viper’s Dance | 2 | Physical | Strike 4 times rapidly. 72% accuracy each. 0.5x damage per hit. Each hit applies a stack of poison. |
| 100 | Coup de Grace | 0 | Ultimate | Instant kill attempt: 50% base accuracy, but +2% per 1% of target’s missing HP. At 50% target HP, this is 100% accuracy instant kill. 5-encounter cooldown. |
Bows
| Prof | Skill | Init | Type | Description |
|---|---|---|---|---|
| 0 | Shoot | 4 | Physical/Ranged | Basic arrow shot. 80% accuracy. Weapon damage at range. |
| 10 | Aimed Shot | 7 | Physical/Ranged | Careful aim. 90% accuracy. 1.3x damage. |
| 20 | Rapid Shot | 2 | Physical/Ranged | Quick snap shot. 70% accuracy. 0.8x damage but very fast initiative. |
| 30 | Pinning Shot | 5 | Physical/Debuff | Arrow to the leg. 78% accuracy. Weapon damage + Slowed condition. |
| 40 | Volley | 8 | Physical/AoE/Ranged | Rain of arrows on an area. 65% accuracy. Hits all enemies. 0.7x damage each. |
| 50 | Piercing Arrow | 6 | Physical/Ranged | Armor-piercing shot. 75% accuracy. Ignores 50% of target evasion. |
| 60 | Explosive Arrow | 7 | Physical/AoE/Ranged | Arrow detonates on impact. 72% accuracy. 1.5x damage to target + 0.8x to adjacent. Burning condition. |
| 70 | Eagle Eye | 3 | Buff | Next 3 bow skills gain +20% accuracy. Swift. 2/dungeon. |
| 80 | Heart Seeker | 5 | Physical/Ranged | 80% accuracy. Crit threshold expanded to ≤15. On crit: 3x damage. |
| 90 | Arrow Storm | 6 | Physical/AoE/Ranged | 8 arrows at random enemies. 68% accuracy each. 0.5x damage. |
| 100 | Skyfall | 9 | Ultimate | Call down a massive arrow barrage. 85% accuracy. 4x damage AoE to all enemies. Pinning + Bleeding. 5-encounter cooldown. |
Staves (Melee/Hybrid)
| Prof | Skill | Init | Type | Description |
|---|---|---|---|---|
| 0 | Staff Strike | 5 | Physical | Basic staff whack. 78% accuracy. |
| 10 | Arcane Tap | 3 | Magical | Channel magic through the staff. 82% accuracy. INT-based damage. |
| 20 | Sweeping Strike | 6 | Physical/AoE | Wide sweep. 72% accuracy. Hits up to 3 adjacent enemies. Prone on crit. |
| 30 | Mana Siphon | 5 | Magical/Utility | 76% accuracy. Deals moderate damage and recovers 10% of damage dealt as mana. |
| 40 | Barrier Pulse | 4 | Defensive/AoE | Create a barrier granting Shielded to self and allies (absorb damage equal to 15% of your max HP). |
| 50 | Elemental Conduit | 6 | Magical | 80% accuracy. Damage type matches target’s vulnerability (auto-detect). If no vulnerability, defaults to force damage. |
| 60 | Thunderclap | 7 | Magical/AoE | Slam staff into ground. 70% accuracy. AoE damage + Stunned on targets that fail CON resistance. |
| 70 | Spell Amplifier | 2 | Buff | Next magical skill deals 50% more damage. Swift. 3/dungeon. |
| 80 | Leyline Surge | 8 | Magical | 78% accuracy. Deals massive magical damage. Cost: 25% of current mana (percentage-based, always usable). |
| 90 | Reality Fracture | 6 | Magical/Debuff | 72% accuracy. Deals damage and reduces target’s resistance to all conditions by 20% for 3 rounds. |
| 100 | Cataclysm | 10 | Ultimate | Channel devastating magical energy. 88% accuracy. AoE. 5x INT-based damage. All enemies: Stunned + Weakened. 5-encounter cooldown. |
Wands
| Prof | Skill | Init | Type | Description |
|---|---|---|---|---|
| 0 | Wand Bolt | 4 | Magical/Ranged | Basic magical bolt. 82% accuracy. |
| 10 | Hex Shot | 5 | Magical/Debuff | 78% accuracy. Moderate damage + Weakened. |
| 20 | Mana Dart | 2 | Magical/Ranged | Quick bolt. 80% accuracy. Low damage, low cost, very fast. |
| 30 | Chain Lightning | 6 | Magical/AoE | 75% accuracy. Hits target then bounces to 2 more. 100%/70%/50% damage on each bounce. |
| 40 | Life Drain | 5 | Magical | 76% accuracy. Deals necrotic damage. Heals self for 50% of damage dealt. |
| 50 | Dispel | 3 | Utility | Remove one buff from target enemy OR one debuff from self/ally. Always succeeds. |
| 60 | Soul Fire | 7 | Magical | 72% accuracy. Ignores evasion entirely (targets the soul). High damage. |
| 70 | Curse of Agony | 4 | Magical/DoT | 80% accuracy. Low initial damage but applies stacking DoT: 3%/5%/8%/12% max HP over 4 rounds. |
| 80 | Power Word: Pain | 6 | Magical/Debuff | 90% accuracy (hard to resist). Target’s next 2 skills have -30% accuracy. |
| 90 | Arcane Barrage | 3 | Magical/AoE | Fire 5 bolts at random enemies. 78% accuracy each. Each bolt that crits resets cooldown of one charge-based skill. |
| 100 | Oblivion Ray | 9 | Ultimate | Single-target annihilation beam. 92% accuracy. 6x damage. Ignores all evasion and resistances. 5-encounter cooldown. |
Shields (Off-Hand)
| Prof | Skill | Init | Type | Description |
|---|---|---|---|---|
| 0 | Block | 1 | Defensive | Reduce next incoming hit by 30%. |
| 10 | Shield Bash | 4 | Physical | Smash with shield. 75% accuracy. Low damage + Stunned (1 round). |
| 20 | Brace | 2 | Defensive | Reduce ALL incoming damage by 15% for this round. |
| 30 | Reflect | 3 | Defensive | On next incoming hit, reflect 50% of damage back to attacker. |
| 40 | Guardian Stance | 1 | Defensive/Buff | Redirect attacks from lowest-HP ally to self for 2 rounds. +20% evasion while active. |
| 50 | Shield Wall | 5 | Defensive/AoE | Grant all allies +10% evasion for 2 rounds. |
| 60 | Concussive Slam | 6 | Physical/AoE | Ground slam. 70% accuracy. AoE. Low damage + Slowed. |
| 70 | Aegis | 2 | Defensive | Absorb the next hit entirely (up to 30% of your max HP). 2/dungeon. |
| 80 | Rallying Defense | 3 | Buff/AoE | All allies: +10% accuracy and Shielded (absorb 10% of your max HP). |
| 90 | Fortress | 1 | Defensive | For 2 rounds: evasion doubled, immune to conditions. Cannot attack. |
| 100 | Immortal Bulwark | 0 | Ultimate | For 1 round: you and all allies cannot be reduced below 1 HP. 5-encounter cooldown. |
Party Combat
In multiplayer encounters, all party members’ queues execute simultaneously alongside enemies.
Turn Order (Party)
ROUND 1 — 4-Player Party vs. Orc Warband
All combatants' next queued skills, sorted by initiative:
[Init 1] Fighter → Block (shield skill)
[Init 2] Rogue → Backstab
[Init 2] Orc Grunt A → Slash (tie: resolved by character initiative stat)
[Init 3] Ranger → Rapid Shot
[Init 4] Orc Shaman → Hex Bolt
[Init 5] Wizard → Arcane Bolt
[Init 5] Orc Grunt B → Slash
[Init 6] Orc Chief → War Cry (buff)
[Init 7] Fighter → Crushing Blow
[Init 8] Rogue → Eviscerate
[Init 8] Ranger → Aimed Shot
[Init 9] Wizard → Elemental Burst
Healing and Support Targeting
- Heal skills target based on the player’s configured target rule (e.g., “lowest HP ally”)
- Buff skills target based on configured rule (e.g., “self” or “highest threat ally”)
- Party synergy: A well-coordinated party queues complementary skills:
- Tank leads with Block/Guardian Stance (fast initiative, protects party)
- Debuffer applies Weakened/Sunder Armor early
- DPS follows with Power Strikes against softened targets
- Healer places heals mid-queue with “if ally HP < 50%” conditionals
Combat Log / Replay
The combat log is how players experience combat after the fact. It must be detailed, readable, and instructional.
Log Format Example
═══ ENCOUNTER 4: Goblin Ambush ═══
Location: Collapsed Mine, Chamber 3
Enemies: Goblin Boss (Lv.5), Goblin Archer ×3 (Lv.3), Goblin Shaman (Lv.4)
⚠ SURPRISE: Perception check: d100 roll 67 vs. 42% detection chance → FAIL
Enemies get a surprise round!
── Surprise Round ──
[Init 2] Goblin Archer 1 → Snap Shot at YOU: d100: 31 vs. 68% → HIT → 7 piercing
[Init 2] Goblin Archer 2 → Snap Shot at YOU: d100: 82 vs. 68% → MISS
[Init 4] Goblin Shaman → Hex Bolt at YOU: d100: 28 vs. 71% → HIT → 5 necrotic + Weakened (2 rounds)
── Round 1 ──
[Init 2] Goblin Archer 3 → Snap Shot at YOU: d100: 55 vs. 68% → HIT → 7 piercing
[Init 3] YOU → Queue Slot 1: Quick Thrust → Goblin Shaman [Target: Highest Threat]
d100: 22 vs. 81% → HIT → 14 piercing damage (Shaman HP: 18/32)
[Init 5] YOU → Queue Slot 2: Crushing Blow → Goblin Shaman [Target: Highest Threat]
d100: 44 vs. 74% → HIT → 23 slashing damage (Shaman HP: 0/32) → DEAD
[Init 5] Goblin Boss → Power Swing at YOU: d100: 15 vs. 72% → HIT → 12 slashing
[Init 6] Goblin Archer 1 → Aimed Shot at YOU: d100: 91 vs. 65% → MISS
[Init 6] Goblin Archer 2 → Snap Shot at YOU: d100: 48 vs. 68% → HIT → 7 piercing
Your HP: 38/62 (61%) | Stamina: 74/100 | Conditions: Weakened (1 round remaining)
── Round 2 ──
[Init 3] YOU → Queue Slot 3: Whirlwind Slash → All Adjacent [AoE: max enemies]
Goblin Boss: d100: 38 vs. 69% → HIT → 11 slashing
Goblin Archer 1: d100: 71 vs. 69% → MISS
Goblin Archer 2: d100: 12 vs. 69% → HIT → 11 slashing (HP: 3/14)
[Init 4] Goblin Boss → Rallying Shout → All goblins gain Inspired (+10% acc, +10% dmg, 2 rounds)
[Init 5] YOU → Queue Slot 4: Healing Surge [IF HP < 50%]
Condition MET (HP at 61%... wait, 61% > 50%) → SKIPPED
↳ Queue advances to Slot 5: Power Strike → Goblin Boss [Target: Highest HP]
d100: 03 vs. 78% → ★ CRITICAL HIT ★ → 34 slashing (1.5× damage!) → Boss HP: 21/48
[Init 5] Goblin Archer 1 → Aimed Shot at YOU: d100: 29 vs. 75% (Inspired) → HIT → 9 piercing
[Init 5] Goblin Archer 2 → Snap Shot at YOU: d100: 88 vs. 78% (Inspired) → MISS
...
── Result ──
VICTORY — All enemies defeated in 4 rounds
HP remaining: 26/62 (42%)
Stamina remaining: 41/100
Consumables used: 0
📊 QUEUE PERFORMANCE:
Quick Thrust: 2 uses, 2 hits (100%)
Crushing Blow: 2 uses, 1 hit, 1 miss (50%)
Whirlwind Slash: 1 use, 2/3 targets hit (67%)
Healing Surge: 1 trigger, 1 skipped (condition not met)
Power Strike: 1 use, 1 crit! (100%)
💡 TIP: You entered this fight at 61% HP due to surprise round damage.
Higher Perception (currently 42%, need ~68%) would avoid ambushes here.
Consider gear with Perception bonuses or the Observant feat.
Loot: 15 gold, Goblin Scimitar +1, Minor Healing Potion
Log Features
- Color-coded outcomes: Hits highlighted, misses dimmed, crits in gold, heals in green
- Queue transparency: Shows which slot fired, the target rule used, and conditional evaluation
- Queue performance summary: After each encounter, shows hit rates per skill — helps the player evaluate their queue effectiveness
- Dice breakdown: Every d100 roll shown with the success threshold
- Tips: Context-sensitive suggestions after tough encounters
- Expandable detail: Summary view by default, expand for full roll-by-roll
- Run timeline: Visual progress bar showing all encounters, colored by outcome
Enemy Design and AI
Overview
Enemies use the same skill queue system as player characters. Every enemy has a stat block, a skill list, and a fixed skill queue that determines their behavior in combat. The key difference: enemy queues are pre-authored by designers, not configured by players. This makes enemy behavior predictable once learned — the first time you fight a Goblin Shaman, you don’t know what it does. The tenth time, you know exactly what’s coming and can build your queue to counter it.
Enemy Stat Blocks
Every enemy has:
GOBLIN SHAMAN (Level 12 Elite)
─────────────────────────────────
HP: 180
Evasion: 14
Attributes: MGT 8 | LOG 16 | SPD 10 | PRS 14 | FRT 10 | LCK 8
Resistances: +20% vs. Charmed, +10% vs. Silenced
Weaknesses: Vulnerable to fire (+25% fire damage taken)
Loot Table: Goblinoid Caster (Uncommon+)
Skill Queue (loops):
1. Hex Bolt (Init 4, magical, targets highest-threat player) — 78% accuracy, moderate necrotic damage + Weakened (2 rounds)
2. Healing Chant (Init 7, heal, targets lowest-HP ally) — Heal 15% of target's max HP [IF any ally below 50% HP, else skip]
3. Venom Cloud (Init 6, AoE magical, targets player cluster) — 70% accuracy, Poisoned condition (4 rounds)
4. Hex Bolt (Init 4, targets highest-threat player) — repeat damage
Skills per round: 2
Enemy Tiers
| Tier | Label | HP Multiplier | Skills | Behavior | Example |
|---|---|---|---|---|---|
| Minion | Trash mob | 0.5x | 2-3 simple skills | Targets nearest, no tactics | Goblin, Rat, Skeleton |
| Standard | Regular enemy | 1x | 3-4 skills with conditions | Uses debuffs, targets threats | Orc Warrior, Bandit Archer |
| Elite | Named/buffed enemy | 2x | 4-6 skills with conditionals | Uses healing, buffs allies, applies conditions | Goblin Shaman, Orc Warchief |
| Boss | Dungeon boss | 5-10x | 6-8 skills, multi-phase | Phase transitions, adds, special mechanics | Bugbear Chieftain, Wraith Lord |
| Raid Boss | Raid encounter | 20-50x | 8-12 skills, 3+ phases | Enrage, role-specific mechanics, coordination required | Flamelord, Lich King |
Enemy Queue Behavior
Enemies have the same queue mechanics as players — fixed order, looping, with light conditionals. The difference is that enemy queues are designed to create specific challenges:
Aggressive enemies (orcs, demons, berserkers):
- Front-load damage skills with low initiative (act fast, hit hard)
- Few or no defensive skills
- Designed to pressure the player’s HP and test their healing/defensive planning
Tactical enemies (shamans, commanders, mages):
- Mix of damage, debuffs, and support skills
- Conditionals like “heal ally if below 50% HP”
- Designed to create priority targets — “kill the healer first” moments
Defensive enemies (golems, shield-bearers, turtles):
- High evasion, Shielded buffs, damage reduction skills
- Slow but durable — punish players who rely only on burst damage
- Designed to test sustained DPS and condition application (DoTs bypass shields)
Swarm enemies (goblins, rats, spiders):
- Low individual HP, appear in groups of 3-6
- Simple queues but overwhelm through numbers
- Designed to test AoE skills and queue efficiency
Boss Mechanics
Bosses are the climax of dungeons. They use the same queue system but with phases — when a boss reaches an HP threshold, their queue changes.
Phase Transitions
WRAITH LORD (Level 25 Boss)
─────────────────────────────────
Total HP: 1,200
PHASE 1 (100% - 60% HP):
Queue: Shadow Bolt → Life Drain → Shadow Bolt → Fear Aura
Behavior: Standard ranged caster. Drains HP to self-heal.
PHASE 2 (60% - 30% HP): ← Queue changes here
Queue: Summon Wraiths → Necrotic Burst (AoE) → Shadow Bolt → Life Drain
Behavior: Summons 2 Wraith adds (minion tier). AoE pressure increases.
New Mechanic: Cursed Ground environmental condition activates (healing reduced 50%)
PHASE 3 (30% - 0% HP): ← Queue changes again
Queue: Death Coil (massive single-target) → Necrotic Burst → Death Coil → Soul Harvest (full heal if any adds alive)
Behavior: Desperate, high damage. Soul Harvest makes killing adds before Phase 3 critical.
New Mechanic: Boss gains Hasted (3 skills per round instead of 2)
Boss Design Principles
- Each phase changes the fight. A boss with one phase is just a big health pool. Phase transitions force the player to adapt mid-encounter (their queue loops, so the same skills hit different contexts).
- Bosses telegraph. The combat log should make phase transitions clear: “The Wraith Lord shrieks as spectral energy surrounds it — PHASE 2 BEGINS.” Players can study this from failed runs to prepare.
- At least one mechanic per boss must be counterable through preparation. Wraith Lord’s Soul Harvest is devastating if adds are alive — so the counter is AoE skills to kill adds before Phase 3. Flamelord’s fire damage is brutal — so the counter is Fire Resistance potions.
- Enrage timer on raid bosses. After X rounds (typically 20-30), raid bosses gain a stacking +10% damage buff per round. This prevents infinite stalling and creates a DPS check.
Enemy Targeting Logic
Enemies use target rules just like players, but their rules are fixed per enemy type:
| Enemy Type | Default Target Rule | Why |
|---|---|---|
| Melee minions | Nearest | Simple, predictable |
| Ranged minions | Lowest Evasion | Shoot the easy target |
| Standard melee | Highest Threat | Focus the damage dealer |
| Standard ranged | Lowest HP | Finish off wounded targets |
| Healer/support | Lowest HP ally (heal) / Highest Threat (debuff) | Keep allies alive, shut down threats |
| Boss (Phase 1) | Highest Threat | Focus the top damage dealer |
| Boss (Phase 2+) | Varies by mechanic | Phase-specific targeting creates new challenges |
Threat is calculated as: total damage dealt this encounter × 1.0 + healing done × 0.5 + debuffs applied × 0.3. Tanks can increase their threat through Taunt skills.
Enemy Condition Usage
Enemies apply conditions just like players. Each enemy type has thematic conditions:
| Enemy Theme | Conditions Applied | Counter Strategy |
|---|---|---|
| Undead | Frightened, Weakened, Exhaustion | High Presence, anti-undead gear |
| Fire | Burning, Corroded (heat melts armor) | Fire resistance, cold skills |
| Ice | Frozen, Slowed | Fire skills (break Frozen), Haste potions |
| Poison/Nature | Poisoned, Bleeding, Rooted | Antidotes, high Fortitude, fire (burns roots) |
| Arcane | Silenced, Confused, Shocked | High Logic, anti-magic gear (Inquisitor) |
| Beast | Bleeding, Stunned (charge attacks) | High Fortitude, Shielded buffs |
| Humanoid (martial) | Weakened, Stunned, Bleeding | Mixed defenses, control conditions back |
| Humanoid (magic) | Charmed, Frightened, Burning | High Presence, damage to break Charm |
Enemy Bestiary
When a player encounters an enemy for the first time, basic info is recorded in the Bestiary:
- Name, tier, and visual description
- HP range and general toughness
- “Known” conditions they apply (after being hit by them)
Subsequent encounters reveal more:
- Exact skill list (after seeing each skill fire)
- Resistances and weaknesses (after testing different damage types)
- Optimal counter-strategy tips (after multiple encounters)
The Verdani species skill Aethersight reveals ALL enemy info immediately for one encounter — a powerful scouting ability.
Enemy Scaling
Enemies don’t scale to the player’s level — they have fixed levels tied to the dungeon. A level 20 dungeon always has level 18-22 enemies. This means:
- Returning to low-level content makes you feel powerful (enemies are trivial)
- Attempting content above your level is genuinely dangerous
- The world has a consistent difficulty geography
Enemy Groups and Composition
Encounters don’t use random enemy packs. Each encounter is designed with a composition that creates a specific challenge:
| Composition | Example | Design Intent |
|---|---|---|
| Pure melee | 4 Orc Warriors | Test sustain and AoE |
| Ranged + melee | 2 Archers + 2 Warriors | Test target priority (kill archers first?) |
| Healer + DPS | 1 Shaman + 3 Goblins | Test focus fire (must kill healer or fight never ends) |
| Tank + DPS | 1 Shield-Bearer + 2 Assassins | Test ability to handle high-evasion target + burst damage |
| Swarm | 6 Rats | Test AoE efficiency |
| Solo elite | 1 Troll | Test sustained single-target DPS and condition management |
| Boss + adds | Boss + 2 Minions (respawning) | Test AoE + single-target + phase awareness |
Gear Durability
Gear degrades gradually through use. Durability is a gold sink, not a combat penalty — your gear always performs at full power regardless of durability. The only consequence of low durability is that you must repair before you can enhance or reforge the item.
- Max durability: 100 for all gear
- Loss per dungeon run: -5 durability on all equipped items (regardless of success or failure)
- At 0 durability: Gear still works at full power in combat. BUT it cannot be enhanced (+1 through +5) or reforged until repaired. A visual indicator marks gear as “worn” in the UI.
- Repair: Instant at any blacksmith vendor. Costs gold based on item rarity:
| Rarity | Repair Cost (0→100) |
|---|---|
| Common | 10 gold |
| Uncommon | 50 gold |
| Rare | 150 gold |
| Very Rare | 400 gold |
| Legendary | 1,000 gold |
- Partial repair costs proportionally less (repairing from 50→100 costs half)
- Repair all button repairs every equipped item in one click
- Players who run 10+ dungeons before repairing save time but pay the same total gold
This creates a steady, predictable gold drain that scales with how actively a player runs content — exactly the kind of friction-free gold sink the economy needs.
Combat Design Principles
- The queue IS the game. Arranging skills, choosing initiative trade-offs, setting conditionals, and managing resources across encounters — this is where all player skill lives.
- Speed vs. power is the core trade-off. Fast skills (low init) are weaker. Slow skills (high init) are stronger. Do you want to act first or hit harder?
- Combos reward mastery. A debuff that lowers evasion into a power strike into an execute creates a satisfying chain. Players who understand skill synergy outperform those who just queue their highest-damage skills.
- Weapon proficiency is the endless treadmill. Even at max character level, there are always more weapon types to master. Switching to a new weapon means starting at proficiency 0 — a new progression journey.
- Resources create meaningful scarcity. You can’t spam your best skills forever. Long dungeons demand efficiency. Short dungeons reward aggression. The player must read the situation and build their queue accordingly.
- Every run teaches something. The combat log, queue performance stats, and tips ensure the player always knows what to improve.
Equipment and Loot
Overview
Loot is the lifeblood of Delve. It’s the primary tangible reward for dungeon runs, the fuel for crafting, the currency of the marketplace, and the core of character power expression. The loot system is built on three principles:
- No loot is ever worthless. Every drop can be equipped, sold, or salvaged into useful crafting materials — regardless of level.
- Endgame loot is unique to YOU. Rare+ items roll randomized bonus properties, so no two legendaries are identical. The chase is finding (or reforging) the perfect combination.
- Transparency and fairness. All item properties are fully visible. No hidden stats, no loot boxes, no mystery. What you see is what you get — on the marketplace or in a dungeon.
Gear Slots
Each character has the following equipment slots:
| Slot | What Goes There | Combat Relevance |
|---|---|---|
| Main Hand | One-handed weapon, two-handed weapon, staff | Primary damage source. Determines weapon proficiency track and available weapon skills. |
| Off Hand | Shield, second weapon (dual-wield), focus, torch | Shield grants Evasion + shield skills. Dual-wield enables off-hand weapon skills. Focus boosts magical skill potency. |
| Head | Helms, circlets, hoods | Often provides condition resistance bonuses or Perception boosts. |
| Chest | Armor (light, medium, heavy) | Primary Evasion rating source. Defines armor class and stealth penalty. |
| Hands | Gloves, gauntlets, bracers | Often accuracy bonuses, melee damage bonuses, or crafting speed boosts. |
| Feet | Boots, greaves, sandals | Speed-related bonuses: initiative tie-breaking, evasion, trap avoidance. |
| Cloak | Cloaks, capes, mantles | Defensive or utility effects: condition resistance, stealth bonuses, elemental protection. |
| Ring 1 | Magical rings | Varied effects. Rings are the most likely slot to carry gear skills. |
| Ring 2 | Magical rings | Same as Ring 1. Two ring slots enable powerful combinations. |
| Amulet | Necklaces, pendants, holy symbols | Often resource pool bonuses (mana, devotion, etc.) or healing effectiveness. |
| Belt | Belts, sashes | Attribute bonuses (often Might or Fortitude). Provides quick slots. |
Quick Slots (Belt)
The belt provides 3 quick slots for consumable items (potions, scrolls) that the skill queue can access during encounters without consuming a queue action. Items not in quick slots require spending a queue action to use.
Rarity Tiers
| Tier | Color | Properties | Where Found | Tradeable? |
|---|---|---|---|---|
| Common | White | Fixed stats only. No magical properties. No random rolls. | All content, NPC vendors | Yes |
| Uncommon | Green | Fixed base + 1 fixed magical property. No random rolls. | Levels 5+ content, crafting | Yes |
| Rare | Blue | Fixed base + signature effect + 1 random bonus with random potency. | Levels 15+ content, crafting, bosses | Yes |
| Very Rare | Purple | Fixed base + signature effect + 2 random bonuses with random potency. | Levels 25+ content, raids, crafting | Yes |
| Legendary | Orange | Fixed base + powerful signature effect + 2 random bonuses with random potency. May include a gear skill. | Levels 35+ content, major raids, world bosses | Yes |
| Artifact | Gold | Fixed base + unique signature effect + 3 random bonuses + guaranteed gear skill. Server-unique. | Special events, ultimate challenges | No (soulbound) |
Key Design: Fixed Base + Random Bonuses
This is the core of the loot system for Rare+ items:
- Fixed base: The item always has specific stats (Evasion for armor, damage for weapons, etc.) determined by its type and level.
- Signature effect: A defining property that makes this specific named item what it is. A “Flamebrand Longsword” ALWAYS has fire damage. A “Frostweave Robe” ALWAYS has cold resistance. The signature is part of the item’s identity.
- Random bonuses: 1-3 additional properties rolled from a pool when the item drops. Both the property type AND the potency are randomized. This is what makes every drop unique.
Example: Two Flamebrand Longswords
FLAMEBRAND LONGSWORD (Legendary)
─────────────────────────────────
Type: Sword | Level 40 | Weapon Damage: 45-62
Signature: Fire Blade — All sword skills deal +15% bonus fire damage.
Hits have a 20% chance to apply Burning (2 rounds).
Player A's drop: Player B's drop:
Random Bonus 1: Random Bonus 1:
+8% critical hit chance +12% accuracy
Random Bonus 2: Random Bonus 2:
+4 Speed +6 Might
Gear Skill: Flame Burst Gear Skill: Flame Burst
(Init 5, AoE fire around target) (Init 5, AoE fire around target)
Both are Flamebrand Longswords with the same signature effect and gear skill. But Player A’s is a crit-focused speed build weapon, while Player B’s is a raw accuracy/power weapon. Both are excellent — just different.
Random Bonus Pools
When a Rare+ item drops, its random bonuses are rolled from this pool:
Offensive Bonuses:
| Bonus | Potency Range |
|---|---|
| +X% accuracy (all skills) | 3-12% |
| +X% damage (all skills) | 3-10% |
| +X% critical hit chance | 2-8% |
| +X% damage to [condition] targets | 5-15% (e.g., “+12% damage to Poisoned targets”) |
| +X% accuracy with [weapon type] | 5-15% |
| +X% [element] damage | 5-15% |
Defensive Bonuses:
| Bonus | Potency Range |
|---|---|
| +X Evasion | 2-8 |
| +X% condition resistance (all) | 3-10% |
| +X% resistance to [specific condition] | 8-20% |
| +X% max HP | 3-10% |
| +X% healing received | 5-15% |
| Reduce critical hit damage taken by X% | 10-25% |
Attribute Bonuses:
| Bonus | Potency Range |
|---|---|
| +X Might | 1-6 |
| +X Logic | 1-6 |
| +X Speed | 1-6 |
| +X Presence | 1-6 |
| +X Fortitude | 1-6 |
| +X Luck | 1-6 |
Resource Bonuses:
| Bonus | Potency Range |
|---|---|
| +X% resource pool (Stamina/Mana/etc.) | 3-10% |
| +X% resource recovery between encounters | 3-8% |
| X% chance skill doesn’t consume resources | 2-6% |
Utility Bonuses:
| Bonus | Potency Range |
|---|---|
| +X% gold find | 5-15% |
| +X% loot quality | 2-8% |
| +X% trap detection | 5-15% |
| +X% crafting speed | 5-12% |
| +X% weapon proficiency gain | 5-15% |
Potency Tiers
When a bonus rolls, its potency is determined by a quality roll (d100):
| Roll | Quality | Potency | Rarity |
|---|---|---|---|
| 1-50 | Standard | Low end of range | Common outcome |
| 51-80 | Superior | Mid range | Decent |
| 81-95 | Exceptional | High end of range | Noteworthy |
| 96-100 | Perfect | Maximum value | Very rare — “god roll” on this bonus |
A fully “god-rolled” legendary (all bonuses at perfect potency) is extremely rare — roughly 1 in 8,000 chance for a 2-bonus item. This creates the long-term chase.
Artifact Rarity
Artifacts are server-unique — only one copy of each exists at a time on the server.
- If the holder goes inactive for 30+ days, the artifact returns to the loot pool
- Artifacts are soulbound (cannot be traded) — you must earn them
- Artifacts have 3 random bonuses + a guaranteed powerful gear skill
- Artifacts are designed to be prestigious but not required — a well-rolled legendary can compete
Weapons
Weapon Types and Base Stats
Weapons are categorized by type, which determines which proficiency track they use (see 04-combat-system.md for the full proficiency system). Base damage scales with item level.
| Weapon Type | Examples | Damage Style | Primary Attribute |
|---|---|---|---|
| Swords | Shortsword, Longsword, Greatsword | Balanced, combo-oriented | Might or Speed (Finesse) |
| Axes | Handaxe, Battleaxe, Greataxe | High burst, cleave | Might |
| Maces & Hammers | Mace, Warhammer, Maul | Anti-armor, stun potential | Might |
| Daggers | Dagger, Stiletto, Ritual Knife | Fast, DoT-focused | Speed |
| Polearms | Spear, Halberd, Glaive | Reach, area control | Might |
| Bows | Shortbow, Longbow, Composite Bow | Ranged sustained DPS | Speed |
| Crossbows | Hand Crossbow, Light Crossbow, Heavy Crossbow | Ranged burst, slow | Speed |
| Staves | Quarterstaff, Battle Staff, Arcane Staff | Hybrid melee/magic | Might or Logic |
| Wands | Wand, Rod, Scepter | Pure magic, ranged | Logic, Presence |
| Shields | Buckler, Kite Shield, Tower Shield | Defensive skills, evasion | Fortitude |
Base Damage Scaling
Weapon base damage scales with item level, not character level. A level 30 longsword does more base damage than a level 10 longsword, period.
| Item Level Range | Base Damage Range (example: Longsword) |
|---|---|
| 1-10 | 8-15 |
| 11-20 | 16-28 |
| 21-30 | 29-45 |
| 31-40 | 46-65 |
| 41-50 | 66-85 |
Weapon Properties
- Finesse: Use Might or Speed for accuracy/damage calculation (whichever is higher)
- Light: Can dual-wield with another light weapon (enables off-hand skill slot)
- Heavy: Small species (Glimkin) have -15% accuracy. Deals +10% damage as compensation for the restriction.
- Two-Handed: Occupies both Main Hand and Off Hand. Higher base damage than one-handed equivalents.
- Ammunition: Requires arrows/bolts (tracked as a supply, not per-arrow)
Weapon Proficiency
Each character has a weapon proficiency score (0-100) per weapon type. Using a weapon in combat increases proficiency, unlocking new weapon skills for the skill queue at milestones (every 10 levels). Full details in 04-combat-system.md.
Gear Skills on Weapons
Legendary and Artifact weapons can grant a gear skill — a bonus skill that can be placed in the skill queue while the weapon is equipped. Gear skills are part of the item’s signature (not randomized — every Flamebrand has Flame Burst).
Examples:
| Weapon | Rarity | Gear Skill | Init | Effect |
|---|---|---|---|---|
| Flamebrand Longsword | Legendary | Flame Burst | 5 | AoE fire damage around the target. 75% accuracy. Applies Burning. |
| Frostfang Dagger | Legendary | Ice Shiv | 2 | Fast cold damage strike. 80% accuracy. Applies Frozen on crit. |
| Thundercaller Bow | Legendary | Lightning Arrow | 6 | Ranged lightning attack that chains to 2 adjacent enemies. Applies Shocked. |
| Doomhammer | Legendary | Seismic Slam | 8 | Massive AoE. 65% accuracy. Applies Stunned to all targets hit. |
| Lifedrinker Rod | Legendary | Soul Siphon | 4 | Necrotic damage. Heals caster for 40% of damage dealt. |
| Aegis of the Dawn | Legendary (Shield) | Divine Barrier | 1 | Grant Shielded (25% of your max HP) to self and lowest-HP ally. |
Armor
Armor Types and Evasion
| Armor | Type | Base Evasion | MGT Req | Stealth Penalty | Notes |
|---|---|---|---|---|---|
| Padded | Light | 11 + Speed | — | -10% | Cheap, basic |
| Leather | Light | 11 + Speed | — | — | No penalty, default light |
| Studded Leather | Light | 12 + Speed | — | — | Best light for most builds |
| Hide | Medium | 12 + Speed (max +2) | — | — | Basic medium |
| Chain Shirt | Medium | 13 + Speed (max +2) | — | — | Common medium |
| Scale Mail | Medium | 14 + Speed (max +2) | — | -10% | Heavier medium |
| Breastplate | Medium | 14 + Speed (max +2) | — | — | Best medium, no penalty |
| Half Plate | Medium | 15 + Speed (max +2) | — | -10% | Highest medium evasion |
| Ring Mail | Heavy | 14 | — | -15% | Entry heavy |
| Chain Mail | Heavy | 16 | MGT 13 | -15% | Standard heavy |
| Splint | Heavy | 17 | MGT 15 | -15% | Strong heavy |
| Plate | Heavy | 18 | MGT 15 | -20% | Maximum evasion, maximum penalty |
| Shield | — | +5 | — | — | Off-hand, enables shield skills |
Base Evasion values are for common versions. Higher rarity armors of the same type get +1 (Uncommon), +2 (Rare), +3 (Very Rare), +4 (Legendary) to base Evasion, plus their signature effects and random bonuses.
Armor and Class Synergy
- Light armor: Full Speed bonus to Evasion. No stealth penalty (usually). Best for Shades, Pathfinders, Hexbinders.
- Medium armor: Speed bonus capped at +2. Balanced. Best for Songweavers, Wardens, Pathfinders.
- Heavy armor: No Speed bonus. Highest flat Evasion. Best for Vanguards, Oathblades, Berserkers (though Berserkers can opt for unarmored — Fortitude replaces Evasion from armor).
Item Level and Why Old Loot Still Matters
The Problem with Traditional MMO Loot
In most games, a Rare sword from level 10 is vendored the moment you hit level 15. Hours of dungeon running produce gear that’s worthless within days. This feels bad.
Delve’s Solution: Rarity-Scaled Salvage
Every item, regardless of level, salvages into the same materials based on its rarity. A level 5 Rare longsword and a level 45 Rare longsword both produce the same Rare-tier crafting materials.
This means:
- A level 5 Rare drop is never worthless — it salvages into materials you need for endgame crafting
- Revisiting low-level dungeons to farm specific enemies or materials is always viable
- New players can sell Rare drops on the marketplace to endgame crafters who want the salvage materials
- The economy stays healthy because supply comes from all level ranges, not just endgame
Salvage Table
| Item Rarity | Salvage Output |
|---|---|
| Common | 2-4 Common Fragments + small gold value |
| Uncommon | 3-5 Common Fragments + 1-2 Uncommon Shards |
| Rare | 2-4 Uncommon Shards + 1-2 Rare Cores |
| Very Rare | 2-3 Rare Cores + 1 Prismatic Essence |
| Legendary | 2-4 Rare Cores + 1-2 Prismatic Essences + 1 Legendary Spark |
| Artifact | Cannot be salvaged (soulbound) |
Material uses:
- Common Fragments: Basic crafting, leveling crafting professions
- Uncommon Shards: Mid-tier crafting, gear enhancement (+1)
- Rare Cores: Advanced crafting, gear enhancement (+2), reforging
- Prismatic Essence: High-tier crafting, enchanting, gear enhancement (+3)
- Legendary Spark: Reforging legendary items, crafting legendary recipes
Disenchanting (Magical Items Only)
Uncommon+ items can be disenchanted instead of salvaged. Disenchanting extracts Magical Essence — a separate material used specifically for enchanting and adding magical properties to crafted gear.
| Item Rarity | Magical Essence Yield |
|---|---|
| Uncommon | 1-2 Magical Essence |
| Rare | 2-4 Magical Essence |
| Very Rare | 4-6 Magical Essence |
| Legendary | 6-10 Magical Essence |
Salvage vs. Disenchant is a meaningful choice — you can do one or the other, not both. Do you need crafting materials or enchanting materials?
The Reforging System
Reforging is the primary endgame resource sink and the answer to “I got a Legendary drop but the random bonuses are bad.”
How Reforging Works
- Take a Rare+ item to a Reforger NPC (available in major hub cities)
- Select one random bonus to reforge (you choose which bonus slot to re-roll)
- Pay the reforging cost (materials + gold)
- The selected bonus is replaced with a new random bonus from the full pool, with a new random potency
Reforging Costs
| Item Rarity | Materials Required | Gold Cost |
|---|---|---|
| Rare | 3 Rare Cores + 2 Uncommon Shards | 500 |
| Very Rare | 5 Rare Cores + 2 Prismatic Essences | 2,000 |
| Legendary | 3 Prismatic Essences + 1 Legendary Spark | 5,000 |
Reforging Rules
- You choose WHICH bonus to reforge. The other bonuses remain untouched.
- The signature effect is never reforged — it’s permanent.
- The new bonus is fully random — it could be the same type you just had, or completely different.
- There is no guarantee of improvement. You might reforge a +8% accuracy into a +3% gold find. This is the gamble.
- Cooldown: Each item can only be reforged once per day (prevents mindless spam-reforging in a single session)
- No destruction risk. The item is never damaged or destroyed by reforging.
Why Reforging Works
- Resource sink: Rare Cores and Prismatic Essences are consumed in large quantities, keeping salvage materials in demand at all times
- Gold sink: 500-5,000 gold per attempt adds up fast when chasing perfect rolls
- Endless endgame: A player with a “good” legendary can always chase a “perfect” one through reforging
- Marketplace interaction: Players who don’t want to gamble on reforging can buy pre-rolled items on the marketplace instead. Players who enjoy the chase can reforge and sell the results.
- Salvage demand: Since reforging consumes materials from salvaging, EVERY item in the game has value as salvage fuel — even level 1 drops
Location-Specific and Dungeon-Specific Loot
Philosophy
Regular loot (Common through Rare) drops everywhere and has no location restrictions. But Very Rare and Legendary items are often tied to specific dungeons, raids, or regions. This gives endgame players a reason to target specific content.
How It Works
Generic loot (Common-Rare): Drops from any content of appropriate level. Generic swords, armor, accessories with random names and properties.
Named loot (Very Rare-Legendary): Specific named items that only drop from specific sources. These are the items with signature effects and gear skills.
| Source Type | Named Loot Examples |
|---|---|
| Regional bosses | “Thornvale Bramble Shield” (Thornvale region only) |
| Dungeon bosses | “Undercrypt Wraith Blade” (Undercrypt dungeon final boss) |
| Raid bosses | “Flamelord’s Crown” (Burning Citadel raid) |
| World bosses | “Earthshaker Maul” (world boss event) |
| Faction vendors | “Iron Compact Greatsword” (Exalted reputation purchase) |
| Crafted | “Masterwork Adamantine Plate” (Level 50 Blacksmithing recipe) |
Named Item Registry
The game maintains a Named Item Registry — a catalog of all named Very Rare+ items showing:
- Where they drop
- Their signature effect
- Their gear skill (if any)
- What random bonus slots they have
- Player-reported “best rolls” for each item
This registry is viewable in-game and helps players plan which dungeons to target.
Consumables
Consumables are single-use items critical to dungeon preparation. They plug directly into the skill queue and condition systems.
Potions
| Potion | Effect | Rarity | Condition Interaction |
|---|---|---|---|
| Healing Salve | Restore 15% max HP | Common | — |
| Greater Healing Salve | Restore 30% max HP | Uncommon | — |
| Superior Healing Salve | Restore 50% max HP | Rare | Also removes 1 stack of Bleeding |
| Supreme Healing Salve | Restore 75% max HP | Very Rare | Removes all Bleeding stacks + Poisoned |
| Antidote | Cure Poisoned condition | Common | Specifically targets Poisoned |
| Burn Salve | Cure Burning condition | Common | Specifically targets Burning |
| Fire Resistance Draught | +30% resistance to Burning for 3 encounters | Uncommon | Reduces Burning damage taken by 50% |
| Cold Resistance Draught | +30% resistance to Frozen for 3 encounters | Uncommon | Reduces Frozen duration by 1 round |
| Potion of Speed | Grants Hasted for 2 rounds (one combat) | Rare | Haste: 3 skills/round, cancels Slowed |
| Potion of Invisibility | Grants Invisible for 1 encounter | Rare | See Invisible condition in combat doc |
| Elixir of Titanic Might | +8 Might for 1 encounter | Rare | Affects all Might-based calculations |
| Elixir of Clarity | +8 Logic for 1 encounter | Rare | Affects all Logic-based calculations |
| Cleansing Tonic | Remove all debuffs | Very Rare | Nuclear option — removes everything |
| Elixir of Fortune | +5 Luck for 1 encounter | Rare | Better crits, fewer crit misses, better loot for that encounter |
Scrolls
- Scrolls grant a one-time skill usable in the skill queue for one encounter
- Any class can use any scroll, but off-class scrolls have -15% accuracy
- Scrolls are consumed when the encounter starts (whether the skill fires or not)
- Can be crafted by Arcanists (see 07-crafting-and-gathering.md)
- Examples: Scroll of Fireball (AoE fire), Scroll of Healing Wave (party heal), Scroll of Haste (grant Hasted)
Supplies
| Supply | Weight (slots) | Consumption Rate | Running Out |
|---|---|---|---|
| Rations | 1 slot per 5 | 1 per encounter | Exhaustion accumulates |
| Torches | 1 slot per 5 | 1 per dark area | Darkness condition applies (see combat doc) |
| Ammunition | 1 slot per 20 | Per ranged skill use | Must switch to melee skills |
| Lockpicks | 1 slot per 3 | 1 per lock attempt | Cannot pick locks (must force with Might) |
| Rope | 1 slot | Per climbing encounter | Harder Might checks, or blocked paths |
| Healer’s Kit | 1 slot per 3 | 1 per Medicine check | Cannot stabilize unconscious allies without magic |
| Antidotes | 1 slot per 3 | Per Poisoned encounter | Must endure full Poisoned duration |
Gear Enhancement
Enhancement Levels (+1 to +5)
Any weapon or armor can be enhanced at a blacksmith NPC, increasing its base stats.
| Enhancement | Effect (Weapons) | Effect (Armor) | Success Rate | Materials |
|---|---|---|---|---|
| +1 | +5% base damage | +1 Evasion | 100% | 3 Uncommon Shards + 200g |
| +2 | +10% base damage | +2 Evasion | 85% | 5 Uncommon Shards + 2 Rare Cores + 500g |
| +3 | +15% base damage | +3 Evasion | 70% | 3 Rare Cores + 1 Prismatic Essence + 1,500g |
| +4 | +20% base damage | +4 Evasion | 55% | 5 Rare Cores + 2 Prismatic Essences + 3,000g |
| +5 | +25% base damage | +5 Evasion | 40% | 3 Prismatic Essences + 1 Legendary Spark + 5,000g |
Failure does NOT destroy or downgrade the item. It just consumes the materials and gold. You can try again immediately.
Enhancement level is preserved when reforging — a +3 Flamebrand stays +3 after reforging a random bonus.
Enchanting
Enchanting adds or modifies magical properties on gear. This is a crafting profession (see 07-crafting-and-gathering.md) — player Enchanters perform this service.
- Common/Uncommon items: Can have 1 enchantment added (effectively giving them a signature-like effect)
- Rare+ items: Cannot be enchanted (they already have signature effects and random bonuses)
- Enchanting uses Magical Essence (from disenchanting unwanted magic items)
Loot Tables and Drops
How Loot Works
Each dungeon encounter has a loot table influenced by:
- Encounter difficulty — Harder enemies roll on better loot tables
- Dungeon tier — Higher-level dungeons have better base pools
- Enemy type — Specific enemies drop thematic items (fire enemies → fire gear, undead → necrotic gear)
- Luck attribute — Higher LCK improves the quality roll on drops (see Potency Tiers above)
- Bad luck protection — Hidden counter that boosts drop rates after dry streaks
Drop Rate Guidelines
| Content Level | Common | Uncommon | Rare | Very Rare | Legendary |
|---|---|---|---|---|---|
| Levels 1-10 | 90% | 10% | <1% | — | — |
| Levels 11-20 | 70% | 25% | 5% | <1% | — |
| Levels 21-30 | 50% | 35% | 12% | 3% | <1% |
| Levels 31-40 | 30% | 35% | 25% | 8% | 2% |
| Levels 41-50 | 20% | 30% | 30% | 15% | 5% |
| Raid bosses | — | 10% | 40% | 35% | 15% |
| World bosses | — | — | 30% | 40% | 30% |
These are per-encounter rates for the “bonus drop” slot (beyond guaranteed gold + common item).
Bad Luck Protection
- A hidden counter tracks encounters since your last Rare+ drop
- Every encounter without a Rare+ drop increases the next encounter’s drop rate by +1%
- Counter resets when a Rare+ item drops
- This ensures even the unluckiest player will eventually get upgrades
- LCK attribute makes this counter increase slightly faster
Boss Drops
- Dungeon bosses always drop at least one item of the dungeon’s highest tier
- Raid bosses always drop 2-3 items, with at least one Very Rare+
- World bosses always drop 1-2 items for EACH participant, at least Rare quality
Loot Distribution (Party Play)
After a party run completes, shared loot enters a Need/Greed window:
- Need — “I want this for my build.” Only available if the item is usable by your class/weapon proficiency.
- Greed — “I want this for selling/salvage.” Always available.
- Pass — “I don’t want it.”
- Need rolls take priority over Greed rolls (d100 roll-off for ties)
- Personal loot (gold, consumables, materials) is NOT shared — each player gets their own
- Loot window: 24 hours after run completion. Unclaimed items are distributed randomly among Greed rollers.
- Transparent: All players can see who rolled Need on what. No hidden rolls.
Trading and the Marketplace
What’s Tradeable
| Rarity | Tradeable? | Notes |
|---|---|---|
| Common | Yes | Low value, mostly vendor/salvage |
| Uncommon | Yes | Moderate marketplace activity |
| Rare | Yes | Active marketplace — random bonuses create varied values |
| Very Rare | Yes | High marketplace value — location-specific, good rolls command premium |
| Legendary | Yes | Premium marketplace items. “God-rolled” legendaries are the most valuable items in the game. |
| Artifact | No | Soulbound on pickup |
Marketplace Value Factors
A Rare+ item’s marketplace value depends on:
- Signature effect — Is it from a desirable dungeon? Is the signature useful?
- Random bonus types — Are the bonuses synergistic? (e.g., +accuracy AND +crit on a weapon = high value)
- Random bonus potency — How close to “perfect” are the rolls?
- Enhancement level — A +3 item is worth significantly more than a +0
- Demand — Is this weapon type or armor type popular for current meta builds?
See 08-economy-and-trading.md for full marketplace details.
Inventory Management
Carrying Capacity
- Base capacity: 20 item slots (not weight-based — simplicity over simulation)
- Equipped gear does NOT count against inventory
- Stackable items (potions, rations, materials) stack up to 20 per slot
- Belt quick slots are separate (3 slots for combat consumables)
Bank Storage
- Characters have a bank in town with 50 slots (base)
- Additional bank space available as a permanent purchase ($1 per +25 slots — see 14-monetization.md)
- Bank is accessible between runs, not during them
- Material bank is separate — 100 slots for crafting materials only (see 07-crafting-and-gathering.md)
Inventory Overflow
- If a dungeon run yields more loot than inventory can hold, excess goes to temporary overflow (persists 48 hours)
- Players must clear overflow before starting a new run
- After 48 hours, overflow items are auto-sold at vendor price (with notification)
- Overflow items CAN be salvaged/disenchanted directly from the overflow screen
Item Comparison UI
When viewing any item, the UI shows a side-by-side comparison with currently equipped gear:
┌─ CURRENTLY EQUIPPED ──────────┐ ┌─ NEW DROP ─────────────────────┐
│ Steel Longsword +1 (Uncommon) │ │ Flamebrand Longsword (Legend.) │
│ Damage: 22-35 │ │ Damage: 48-65 │
│ +5% accuracy (fixed) │ │ Signature: Fire Blade │
│ │ │ +15% fire damage on skills │
│ │ │ 20% chance to Burn │
│ │ │ Bonus: +8% crit chance ▲ NEW │
│ │ │ Bonus: +4 Speed ▲ NEW │
│ │ │ Gear Skill: Flame Burst ▲ NEW│
├───────────────────────────────┤ ├────────────────────────────────┤
│ Overall: ▼ SIGNIFICANT │ │ Overall: ▲ SIGNIFICANT │
│ DOWNGRADE │ │ UPGRADE │
└───────────────────────────────┘ └────────────────────────────────┘
Estimated DPS change: +45% ▲
Evasion change: 0 (same armor)
New conditions available: Burning (via Fire Blade + Flame Burst)
Key features:
- Green ▲ for improvements, Red ▼ for downgrades
- DPS estimate calculated from accuracy × damage × crit chance × skills-per-round
- Condition availability highlighted when new gear enables applying or resisting conditions
- Gear skill preview showing what the skill does and its initiative value
- One-click “Equip” or “Salvage” or “Send to Bank” from the comparison screen
Loot Design Principles
- Every drop has value. Equip it, sell it, or salvage it. Nothing goes to waste.
- The signature makes the item. Named items have a clear identity. You know what a Flamebrand does before you see its random rolls.
- Random bonuses create the chase. Two players with the same named item will have different builds because their bonuses rolled differently.
- Reforging is the endgame. Once you find the item you want, the long-term goal is perfecting its random bonuses through reforging.
- Transparency builds trust. All stats visible, all rolls visible, no hidden mechanics. The marketplace works because buyers know exactly what they’re getting.
- Level doesn’t kill value. A level 10 Rare is worth the same salvage as a level 40 Rare. Low-level content stays relevant.
Quests and Dungeons
Overview
Quests and dungeons are the primary content in Delve — where characters spend their time, earn XP, and find loot. The quest system is built around a quest board where players select from available challenges, prepare their character, and commit to a run that resolves over real-world time.
The key design goal: every run tells a story. Win or lose, the player should be able to read through the run timeline and understand exactly what happened, learn from mistakes, and plan better for next time.
Quest Board
The quest board is the central hub for selecting content. It’s presented as a bulletin board in the character’s current town.
Board Refresh
- The quest board refreshes every 6 hours with a new selection of quests
- Some quests persist across refreshes (major questlines, recurring bounties)
- Players can pin up to 3 quests to save them across refreshes
- Higher-level characters see more quests and harder options
Quest Information Display
Each quest on the board shows:
- Name and flavor text — “Clear the Goblin Warren,” “Investigate the Haunted Manor”
- Type — Bounty, Questline, Dungeon Crawl, Raid (see below)
- Difficulty rating — Skull icons (1-5) plus a recommended level range
- Estimated duration — Real-world time to complete (e.g., “~45 minutes”, “~3 hours”)
- Encounter count — How many encounters to expect
- Environment type — Cave, forest, undead crypt, etc. (affects Pathfinder favored terrain, elemental resistance needs)
- Known enemy types — General info (e.g., “Undead heavy”, “Goblinoid raiders”) — not full roster
- Reward preview — XP range, gold range, potential loot tier (e.g., “Rare+ drops possible”)
- Supply recommendation — Suggested rations, potions, torches based on dungeon length
- Party size — Solo, 2-player, 4-player, or raid (8+ player)
Quest Types
Bounties
- Length: 1-3 encounters
- Duration: 15-45 minutes real-time
- Purpose: Quick content for short play sessions or grinding specific resources
- Structure: Travel to location → fight target → return
- Examples: “Slay the Dire Wolf terrorizing the eastern road,” “Eliminate the bandit captain at Crow’s Perch”
- Rewards: Modest XP and gold, targeted loot (bounty targets drop specific item types)
Questlines
- Length: 5-8 encounters
- Duration: 1-3 hours real-time
- Purpose: The bread-and-butter content. Narrative-driven multi-encounter sequences.
- Structure: A series of encounters connected by a narrative thread. Includes combat, traps, decisions, and sometimes branching paths.
- Branching decisions: At certain points, the character faces a choice (e.g., “Left passage or right passage?”). Players configure these choices before the run or set decision-making rules (e.g., “always take the safer path,” “always take the path with more treasure potential”).
- Examples: “Investigate the disappearances in Millhaven,” “Recover the Sunstone from the Forgotten Temple”
- Rewards: Good XP, solid loot, occasional unique quest rewards (named items)
- Narrative continuity: Some questlines are multi-part — completing part 1 unlocks part 2 on a future board refresh
Dungeon Crawls
- Length: 10-20 encounters
- Duration: 3-8 hours real-time
- Purpose: Major undertakings. The flagship content for dedicated players.
- Structure: A full dungeon with multiple rooms, branching paths, rest points, traps, puzzles, minibosses, and a final boss.
- Rest points: Locations within the dungeon where the character can rest (recover some HP and resource pools). The skill queue can be configured for rest behavior (rest immediately, rest only if below X% HP, skip rest to save time).
- Supply management: Long dungeons require careful supply planning. Running out of rations means exhaustion. Running out of torches means missed traps.
- Examples: “The Undercrypt of Ashenmoor (Level 15-20, 15 encounters),” “The Dragon’s Hoard of Frostpeak (Level 35-40, 20 encounters)”
- Rewards: High XP, best loot, dungeon completion achievements, rare/very rare drops guaranteed from final boss
Raids
- Length: 15-30 encounters
- Duration: 4-12 hours real-time
- Purpose: Multiplayer endgame content. Requires a full party (4-8 players).
- Structure: Large-scale dungeon with encounters designed for party composition. Some encounters require specific roles (tank to hold aggro, healer to keep party alive, DPS to meet damage thresholds).
- Timed lobby: Raids start at a scheduled time (see 09-social-and-mmo-systems.md). Players join the raid lobby and configure their character before the start time.
- Examples: “The Siege of Ironhold (8-player, Level 35+),” “Abyssal Rift (4-player, Level 50)”
- Rewards: Best loot in the game, raid-exclusive legendary items, guild reputation, seasonal leaderboard ranking
Encounter Types
Dungeons are composed of a sequence of encounters. Each encounter type tests different aspects of the character’s build.
Combat Encounters
- Standard fight against one or more enemies
- Enemies have stat blocks (HP, Evasion rating, attacks, abilities, resistances)
- Resolved using the combat system (see 04-combat-system.md)
- Variants:
- Standard: Fight a group of enemies
- Ambush: Enemies get a surprise round (failed Perception check — Logic-based)
- Boss: Powerful single enemy or enemy with minions. Higher HP, special abilities, multiple phases.
- Waves: Multiple rounds of enemies with short breaks between
- Timed: Must defeat enemies within X rounds or face consequences (reinforcements, collapsing room)
Trap Encounters
- The character encounters a trap (pit, poison darts, falling rocks, magical ward)
- Resolution:
- Detection: Perception (Logic) or Investigation (Logic) check to notice the trap
- Disarming: Sleight of Hand, Arcana, or Thieves’ Tools check to disarm (if detected)
- Avoidance: Speed resistance check to dodge (if triggered)
- Endurance: Take the damage and move on
- Shades excel here — higher detection rates, better disarm chances
- Configurable behavior: “attempt to disarm” vs. “just dodge it” vs. “trigger and tank it” (useful for high-HP characters in low-danger traps)
- Traps may inflict conditions: Poisoned, Burning, Bleeding, etc.
Decision Points
- A narrative fork where the character must choose between options
- Examples:
- “Two passages: one smells of sulfur, the other echoes with dripping water”
- “A wounded NPC asks for help. Stop to aid them, or press forward?”
- “The treasure chest has a strange aura. Open it, or leave it?”
- Pre-configured decisions: Players set decision-making heuristics:
- “Prioritize: Safety / Treasure / Speed / Exploration”
- Specific overrides: “Always help NPCs,” “Never open suspicious chests,” “Always take the path with potential treasure”
- Decision outcomes affect:
- Which encounters come next (branching paths)
- Bonus loot or bonus encounters
- NPC relationships (faction reputation)
- Sometimes avoiding a harder fight entirely
Environmental Hazards
- Non-trap obstacles that test specific abilities
- Examples:
- Flooded room (Athletics/Might to swim, or lose rations)
- Collapsed passage (Might check to clear, or find alternate route)
- Magical darkness (torches useless — need Darkvision or Light spell)
- Extreme cold (Fortitude resistance check or take damage each round, fire resistance helps)
- Rickety bridge (Speed check or fall, taking damage)
- Skills and supplies matter: rope for climbing, torches for dark areas, rations for long detours
Rest Points
- Safe areas within longer dungeons where the character can recover
- Rest provides:
- HP recovery (50% of max HP, or 100% with Soldier background bonus)
- Resource pool recovery (half of expended Mana/Stamina/Devotion/etc., rounded down)
- Rations consumed (1 ration)
- Condition removal (Poisoned, exhaustion level 1)
- Players configure rest behavior in the skill queue:
- “Rest whenever available”
- “Rest only if below X% HP”
- “Rest only if resource pool below X”
- “Skip rest” (for speed-running)
Puzzle Encounters
- Logic or Presence-based challenges
- Resolution is a skill check (d100 percentile roll) with bonuses from relevant proficiencies
- Examples:
- Magical lock requiring Arcana check
- Ancient riddle requiring History check
- Pattern recognition requiring Investigation check
- Failure consequence: miss bonus loot, trigger a trap, or face an extra combat encounter
- Not all characters need to solve puzzles — failure is an alternate path, not a dead end
Run Timeline
The run timeline is the complete record of a dungeon run. It’s the primary thing the player reviews after a run.
Timeline View
A visual representation of the dungeon run showing:
DUNGEON: The Goblin Warren (Level 10, Questline)
Duration: 1 hour 23 minutes | Encounters: 7 | Result: SUCCESS
[1]──[2]──[3]──[4]──[5]──[6]──[7]
⚔ ⚠ 🔀 ⚔ 🏕 ⚔ ⚔★
1. ⚔ Combat: Goblin Patrol (3 goblins) ─── VICTORY, clean [HP: 95%]
2. ⚠ Trap: Pit Trap ─── DETECTED & DISARMED [Perception: 72 vs DC 45]
3. 🔀 Decision: Left (sulfur) or Right (water) ─── Chose LEFT [Treasure priority]
4. ⚔ Combat: Fire Beetles (5) ─── VICTORY, took damage [HP: 62%]
5. 🏕 Rest Point ─── Rested, consumed 1 ration [HP: 62% → 85%]
6. ⚔ Combat: Goblin Boss + Shaman ─── VICTORY, hard fight [HP: 34%]
7. ⚔★ Boss: Bugbear Chieftain ─── VICTORY [HP: 12%]
LOOT SUMMARY:
Gold: 127
Items: Bugbear's Morningstar +1 (Uncommon), Shaman's Robe (Common), 3x Minor Healing Potion
Materials: 4x Leather Scraps, 2x Goblin Teeth (alchemy reagent)
SUPPLIES CONSUMED:
Rations: 4 of 6 brought (2 remaining)
Torches: 2 of 4 brought (2 remaining)
Healing Potions: 2 used during combat
XP EARNED: 450 (Level 8 → 72% to Level 9)
Expanding Encounters
Each encounter in the timeline can be expanded to show the full combat log (see 04-combat-system.md) or the full skill check breakdown for traps and hazards.
Failure Analysis
When a run fails, the timeline includes a failure summary that highlights exactly what went wrong:
DUNGEON: The Undercrypt (Level 20, Dungeon Crawl)
Duration: 2 hours 47 minutes | Encounters: 12 of 15 | Result: FAILED at Encounter 12
FAILURE SUMMARY:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Cause of Death: Reduced to 0 HP by Wraith Lord's Necrotic Burst
Contributing Factors:
⚠ Entered encounter 12 with only 28% HP (entered rest point at encounter 9
with 71% HP but skill queue was set to "rest only below 30%")
⚠ No healing potions remaining (last used at encounter 8)
⚠ Wraith Lord has necrotic resistance — your primary damage type is necrotic
(consider radiant damage weapon or spell)
⚠ Failed Presence resistance check against Wraith's fear aura (PRS check: 23
vs DC 55) — spent 2 rounds frightened, unable to close distance
Recommendations:
→ Bring more healing potions (you brought 3, recommend 5+ for this dungeon length)
→ Lower rest threshold to 50% HP for dungeons with 15+ encounters
→ Consider equipping a radiant damage option for undead-heavy dungeons
→ Increasing Presence (currently 10) or taking Resilient (PRS) would help against
fear effects common in undead encounters
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Difficulty Tiers
Each quest has a difficulty rating relative to the player’s level:
| Difficulty | Skull Rating | Relative Level | Completion Rate | XP/Loot Bonus |
|---|---|---|---|---|
| Trivial | ☠ | 10+ below | ~99% | 50% penalty |
| Easy | ☠☠ | 5-9 below | ~90% | None |
| Normal | ☠☠☠ | At level | ~70% | None |
| Hard | ☠☠☠☠ | 1-5 above | ~50% | +25% XP and gold |
| Deadly | ☠☠☠☠☠ | 6+ above | ~25% | +50% XP and gold, better loot table |
The completion rates are rough targets assuming appropriate gear and average skill queue configuration. Well-prepared characters with good builds and smart skill queue configuration can push above these rates. Poorly prepared characters will fall below them.
Level Scaling
- Quests have fixed levels — they don’t scale to the character
- A level 25 character returning to a level 10 dungeon will find it trivial
- This is intentional — it provides a sense of power progression and allows farming lower content for specific drops or materials
Time Durations
Real-world time for dungeon runs is a core idle mechanic. Durations are based on quest type and difficulty:
| Quest Type | Encounter Count | Duration |
|---|---|---|
| Bounty (easy) | 1-2 | 15-30 minutes |
| Bounty (hard) | 2-3 | 30-60 minutes |
| Questline (easy) | 4-6 | 1-2 hours |
| Questline (hard) | 6-8 | 2-4 hours |
| Dungeon Crawl (short) | 8-12 | 3-5 hours |
| Dungeon Crawl (long) | 12-20 | 5-8 hours |
| Raid (small) | 10-15 | 4-6 hours |
| Raid (large) | 15-30 | 6-12 hours |
Duration Philosophy
- Short sessions (bounties) should be completable during a lunch break
- Medium sessions (questlines) should complete while at work or overnight
- Long sessions (dungeon crawls) should be overnight or full-day affairs
- Raids are events — they happen at scheduled times and run for a significant period
- Dungeon run durations cannot be sped up with real money. This is a core design principle. Everyone waits the same amount.
Supplies System
Preparation is a key part of the gameplay loop. Players must stock their character with supplies before committing to a run.
Supply Planning
The quest board shows recommended supplies. Players choose what to bring, trading off between safety and inventory space.
| Supply | Weight (slots) | Consumption Rate | Running Out |
|---|---|---|---|
| Rations | 1 slot per 5 | 1 per encounter | Exhaustion accumulates |
| Torches | 1 slot per 5 | 1 per dark area (varies by dungeon) | Can’t see traps, -20% to Perception checks |
| Healing Potions | 1 slot each | As needed in combat | No emergency healing |
| Ammunition | 1 slot per 20 | Per ranged attack | Must switch to melee/cantrips |
| Lockpicks | 1 slot per 3 | 1 per lock attempt | Can’t pick locks |
| Rope | 1 slot | Per climbing/traversal encounter | Harder Athletics (Might) checks, or blocked paths |
| Antidotes | 1 slot per 3 | Per poison encounter | Must endure Poisoned condition |
Supply Strategy
- A 10-encounter dungeon needs at least 10 rations (2 slots)
- Cave dungeons need more torches; outdoor quests need few or none
- Undead dungeons benefit from extra healing potions (no short rests in crypts?)
- Over-packing supplies means less room for loot on the way out
- Under-packing means risking failure
This tension — preparation vs. capacity — is the core strategic decision before every run.
Recurring and Seasonal Content
Daily Bounties
- 3 bounties refresh daily with bonus XP and gold
- Completing all 3 grants a daily completion bonus (bonus loot chest)
Weekly Challenges
- Special modifier dungeons that rotate weekly
- Examples: “All enemies have double HP,” “No healing potions allowed,” “Timed run: complete in fewer encounters for bonus rewards”
- Higher risk, higher reward
Seasonal Dungeons
- Time-limited dungeons available during seasonal events (see 15-progression-hooks-and-retention.md)
- Unique enemies, unique loot, unique environmental themes
- Available for the duration of the season (typically 2-3 months)
Economy and Trading
Overview
The economy is the connective tissue of Delve. Gold and materials flow between dungeon runs, crafting, the marketplace, and NPC vendors. A healthy economy requires careful balance: enough currency entering the system to feel rewarding, enough leaving (gold sinks) to prevent inflation, and enough player-to-player trading to create a real marketplace.
Currencies
Gold (Primary Currency)
- Earned from: dungeon runs, quest rewards, NPC vendor sales, daily bonuses
- Spent on: NPC vendor purchases, crafting station fees, respec costs, auction house fees, guild creation
- Gold is tradeable between players via the marketplace and mail
Faction Tokens (Reputation Currency)
- Earned by completing faction-specific quests and dailies
- Spent at faction vendors for exclusive recipes, gear, and cosmetics
- Each faction has its own token type
- NOT tradeable — must be earned personally
- See 11-factions-and-reputation.md
Raid Tokens
- Earned from completing raid content
- Spent on raid-exclusive gear at raid vendors
- Weekly cap on how many can be earned (prevents no-lifing the system)
- NOT tradeable
Gold Economy
Gold Sources (Faucets)
| Source | Gold/Instance | Frequency |
|---|---|---|
| Dungeon run (bounty) | 50-200 | Multiple per day |
| Dungeon run (questline) | 200-800 | 1-3 per day |
| Dungeon run (crawl) | 500-2000 | 1 per day |
| Raid completion | 1000-5000 | 1-2 per week |
| NPC vendor sales | Varies | Constant |
| Daily first-run bonus | 100 flat | 1 per day |
| Quest completion bonus | 50-500 | Per quest |
Gold Sinks (Drains)
| Sink | Cost | Purpose |
|---|---|---|
| Respec (attributes) | 500-5000 (scales with level) | Discourages constant respec, provides meaningful choice |
| Respec (feats) | 1000-10000 | Same as above |
| Crafting station fees | 10-500 per craft | Small per-transaction drain |
| Auction house listing fee | 5% of listing price | Discourages low-value listings |
| Auction house sale tax | 10% of sale price | Primary marketplace gold sink |
| Gear upgrade attempts | 100-5000 per attempt | Escalating with enhancement level |
| Gear repair | 10-1000 | Per item, scales with rarity. Steady drain from active play. |
| Reforging fees | 500-5000 | Per reforge attempt (scales with rarity) |
| Guild creation | 10,000 | One-time cost |
| Guild bank expansion | 5,000 per slot | Ongoing guild gold sink |
| Cross-character mail transfer fee | 5% of gold sent | Prevents zero-cost gold shuffling |
| NPC vendor purchases (consumables) | Varies | Rations, basic potions, etc. |
Inflation Control Philosophy
- Gold sinks should feel like natural costs of doing business, not arbitrary taxes
- The auction house tax is the primary inflation control — it removes gold proportional to economic activity
- If inflation becomes an issue, seasonal events can introduce additional gold sinks (cosmetic vendors, limited-time items for gold)
- Monitoring: track median gold holdings by level bracket and adjust faucets/sinks accordingly
Player Marketplace (Auction House)
The marketplace is where players trade items for gold. It’s the central economic hub.
Listing Items
- Any non-bound item can be listed (most loot is tradeable)
- Set a buyout price (instant purchase)
- Set an optional auction starting price (bidding over 24/48 hours)
- Listing fee: 5% of buyout price (non-refundable, acts as gold sink)
- Listings last 48 hours by default
- Max active listings: 20 (base), expandable via permanent purchase ($1 per +5 slots)
Buying Items
- Browse by category, rarity, level range, stats, or keyword search
- Sort by price, time remaining, or relevance
- “Buy Now” at the buyout price
- Bid on auction listings (outbid notification)
- Purchase history for reference
Sale Tax
- When an item sells, 10% of the sale price is taken as tax (gold sink)
- Seller receives 90% of the sale price
- Merchant background reduces tax to 8% (Merchant background grants +1 Luck)
Item Binding
Most items are freely tradeable, but some are bind-on-pickup (BoP):
- Raid-exclusive legendary gear
- Quest reward items with unique effects
- Artifact-rarity items
- Achievement rewards
Some items are bind-on-equip (BoE):
- Can be traded until first equipped
- Encourages marketplace activity for high-end gear
- Creates the “equip or sell?” dilemma — a valuable gear piece might be worth more on the market than on your character
Market Manipulation Prevention
- Price floor: Items cannot be listed below NPC vendor price
- No buyout sniping bots: Rate limiting on purchases, CAPTCHA-like verification for rapid purchases
- Price history: Visible graph of average sale prices over the last 30 days for any item type
- Report system: Players can flag suspicious trading patterns for review
NPC Vendors
General Goods Vendor
- Sells: Rations, torches, basic potions, rope, lockpicks, ammunition, healer’s kits
- Prices are fixed and do not fluctuate
- Always available
Equipment Vendor
- Sells: Common-quality weapons and armor
- Rotating stock of uncommon items (refreshes daily)
- Prices are fixed
Specialty Vendors (Unlocked by Progression)
- Alchemist: Sells potion recipes and rare reagents (limited stock, refreshes weekly)
- Blacksmith: Sells gear upgrade and enhancement services
- Enchanter: Sells enchanting materials and basic enchantments
- Exotic Trader: Appears randomly with unusual items (rare recipes, unique consumables, cosmetics for gold)
Vendor Buy Prices
- Vendors buy items at 50% of their sell price (base)
- Merchant background: 60%
- High faction reputation with Merchant Consortium: up to 70%
- This makes the marketplace almost always a better option for valuable items, driving player-to-player trade
Trading Between Players
Marketplace (Primary)
- The auction house is the primary trading mechanism
- Anonymous — buyers and sellers don’t know each other
- Safe — no scam risk
Mail System
- Players can mail items and gold to specific players
- Gold transfer fee: 5% (gold sink)
- Item mail: Free, but items are held for 1 hour before delivery (prevents impulse scam trades)
- Mail between characters on the same account uses the same system
No Direct Trade
- There is no direct trade window between players
- All trades go through the marketplace or mail
- This prevents social engineering scams (“put your item up first, I promise I’ll pay”)
- It also ensures all trades are taxed appropriately
Real-Money Purchases and the Economy
See 14-monetization.md for the full monetization model. Key points relevant to the economy:
- There is no premium currency. No gems, no crystals, no tokens. All purchases are in real dollars at transparent prices.
- Nothing that affects combat stats is sold for real money. No gear, no XP, no gold, no loot boosts.
- Patron subscribers get 50% faster crafting and dungeon runs — this means subscribers generate materials and loot at a faster rate, which increases marketplace supply. This is healthy for the economy (more supply = lower prices = better for free players).
- Permanent purchases (character slots, storage, marketplace listing slots) are small one-time buys that don’t distort the economy.
Economic Health Indicators
The game should track and monitor:
- Median gold by level bracket — Are new players too poor? Are endgame players hoarding?
- Marketplace transaction volume — Is the market healthy and active?
- Average item prices over time — Is inflation occurring?
- Gold faucet/sink ratio — Is more gold entering or leaving the system?
- Subscriber vs. free player economic participation — Are subscribers dominating the marketplace unfairly?
These metrics inform tuning decisions — adjusting drop rates, vendor prices, tax rates, and gold rewards to keep the economy balanced.
Social and MMO Systems
Overview
Delve is an MMO, not a single-player idle game. Social systems are what transform individual dungeon runs into a shared world. The core challenge: making multiplayer meaningful and engaging in an asynchronous game where players are rarely online at the same time.
The solution is the timed lobby system — group content starts at scheduled times, and players prepare their characters before the deadline. You don’t need to be online at the same time as your party; you just need to be ready before kickoff.
Guilds
What Is a Guild?
A guild is a persistent group of players who share resources, coordinate on raids, and compete together. Guilds are the primary social structure in Delve.
Guild Creation
- Cost: 10,000 gold
- Requirements: Level 10+ character
- Guild name: Unique server-wide, 3-24 characters
- Guild tag: 2-4 character abbreviation displayed next to member names
Guild Size
- Base capacity: 50 members
- Expandable via guild upgrades (gold cost): up to 200 members
- No hard cap on inactive members, but guilds can set auto-kick thresholds
Guild Ranks and Permissions
| Rank | Permissions |
|---|---|
| Guild Master | All permissions, transfer ownership, disband guild |
| Officer | Invite/kick members, schedule raids, manage guild bank, edit MOTD |
| Veteran | Access guild bank (deposit + limited withdrawal), join raid lobbies |
| Member | Access guild bank (deposit only), join raid lobbies, guild chat |
| Recruit | Guild chat only, 7-day probation period |
Guild masters can create custom ranks with granular permissions.
Guild Bank
- Shared storage for items and gold
- Members deposit items/gold; withdrawal permissions are rank-based
- Withdrawal logs visible to officers (prevents theft without accountability)
- Bank size: 50 slots base, expandable with guild gold
- Acts as a gold sink (expansion costs) and a social coordination tool
Guild Buffs
Guilds can invest gold into temporary server-wide buffs for all members:
| Buff | Cost | Duration | Effect |
|---|---|---|---|
| Fortune’s Favor | 5,000 gold | 24 hours | +10% gold from dungeon runs |
| Scholar’s Blessing | 5,000 gold | 24 hours | +10% XP from dungeon runs |
| Artisan’s Focus | 3,000 gold | 24 hours | 15% faster crafting |
| Gatherer’s Bounty | 3,000 gold | 24 hours | 15% more gathering yield |
| Endurance | 4,000 gold | 24 hours | +5% max HP during runs |
- Only one buff active at a time per guild
- Acts as a meaningful gold sink
- Encourages active guild management
Guild Leveling
- Guilds earn XP from member activities (dungeon completions, raid completions, PVP wins)
- Guild levels unlock:
- Higher member cap
- More guild bank slots
- Access to guild-exclusive crafting stations (10% crafting time reduction)
- Guild hall cosmetic upgrades
- Guild achievements and titles
Guild Hall
- A visual representation of the guild’s home base
- Upgradeable with gold and materials
- Contains: guild bank, crafting stations, quest board (guild-specific bounties), trophy room (displays raid achievements)
- Primarily a social and cosmetic space — a “home” for the guild
Party System
Timed Lobby System
This is the core multiplayer mechanic. Here’s how it works:
- A player (or guild officer) creates a party lobby for a specific quest/dungeon/raid
- A start time is set — e.g., “This raid starts at 8:00 PM server time on Saturday”
- Players join the lobby before the start time, slotting their configured character into a party role
- At the start time, the run begins automatically — regardless of whether players are online at that moment
- The run resolves over real-world time as usual
- All party members can review results when the run completes
Lobby Details
- Party size: 2-player (duo), 4-player (standard), 8-player (raid)
- Role slots: Lobbies can specify role requirements:
- Tank (1-2): Vanguard, Berserker, Oathblade
- Healer (1-2): Warden, Songweaver, Oathblade
- DPS (2-4): Any damage-focused build
- Support (0-2): Songweaver, Arcanist (utility), Pathfinder (scout)
- Minimum level: Set by the quest requirements
- Ready check: Players must mark “Ready” after configuring their character
- Auto-fill option: If the lobby isn’t full by start time, the system can optionally auto-fill from a public queue (configurable by lobby creator)
- Late join window: Players can join up to 30 minutes before start time
Lobby Scheduling
- Lobbies can be created up to 7 days in advance
- Guild officers can create recurring lobbies (e.g., “Every Saturday at 8 PM: Weekly Raid”)
- Notification system: Players receive notifications:
- When a lobby they might be interested in is created (guild lobbies, friend lobbies)
- 1 hour before a lobby starts
- 15 minutes before a lobby starts (final reminder)
- When the run is complete
Public Queue
For players without a guild or for casual grouping:
- Queue for a quest — the system matches you with other queued players when enough are available
- Matching prioritizes: level range, role balance, queue time
- Once matched, a lobby is created with a 1-hour countdown — giving all matched players time to configure
- Less control than a private lobby, but zero coordination required
Party Combat
- Party members act in initiative order alongside enemies
- Each player’s skill queue operates independently based on their configuration
- Allied skill queue awareness: Skill queues can reference ally conditions:
- “Heal the ally with the lowest HP”
- “Use Inspiring Melody on the Shade when they attack the boss”
- “Taunt enemies attacking the Arcanist”
- Party composition matters — a party of 4 DPS will struggle without a healer
Raids
Raids are the pinnacle multiplayer content. See 06-quests-and-dungeons.md for encounter structure.
Raid-Specific Features
- 8-player parties with strict role requirements
- Multi-phase boss fights with unique mechanics:
- Phase transitions at HP thresholds
- Environmental hazards (stand in fire = take damage)
- Add spawns (boss summons minions periodically)
- Enrage timers (boss gets stronger if fight goes too long — DPS check)
- Role-specific mechanics (tank must hold aggro, healer must dispel debuff, DPS must focus specific target)
- Raid lockouts: Characters can only complete a specific raid once per week for full rewards
- Additional completions in the same week give reduced rewards (partial XP, no bonus loot)
- Lockout resets every Monday at server reset time
- Raid difficulty tiers: Normal / Heroic / Mythic
- Each tier increases enemy stats, adds mechanics, and improves loot quality
- Mythic raids drop the best gear in the game
Raid Scheduling
- Guilds schedule raids via the lobby system
- Raid lobbies appear on a server-wide raid calendar (for public raids)
- Raid start times are prominently displayed with countdown timers
Friends List
- Add players by character name
- See friends’ online status, current activity, and level
- Quick-invite to party lobbies
- Friend activity feed: See when friends complete notable dungeons, earn achievements, or find legendary loot
- Max friends: 100
Messaging and Mail
In-Game Mail
- Send items, gold, and text messages to any player
- Item mail: 1-hour delivery delay (anti-scam)
- Gold mail: 5% transfer fee (gold sink)
- Text mail: Instant delivery
- Mail expires after 30 days if unclaimed
- Used for: auction house payments, guild communications, friend-to-friend trading
Chat System
Channels:
- Global: Server-wide chat (rate-limited to prevent spam)
- Guild: Guild members only
- Party: Current party/lobby members
- Whisper: Private 1-on-1 messages
- Trade: Dedicated channel for trade offers and requests
- LFG (Looking for Group): Dedicated channel for finding parties
Chat is persistent — messages are stored and visible when you log in (last 100 messages per channel). This is crucial for async play — guild coordination happens over hours, not minutes.
Chat Moderation
- Profanity filter (configurable: on/off/custom)
- Report system for harassment
- Mute/block individual players
- Rate limiting on global/trade channels
- Automated spam detection
Player Inspection
- Click on any player’s name to view their public profile:
- Character name, title, guild
- Level and class
- Equipped gear (visible to all)
- Achievement showcase (player-selected highlights)
- PVP rank
- Dungeon completion statistics
- Players can set their profile to private (hides gear and stats, shows only name/level/guild)
Social Features Summary
| Feature | Solo Benefit | Multiplayer Benefit |
|---|---|---|
| Guilds | Guild buffs, bank storage | Raid access, coordination, community |
| Party Lobby | N/A | Group content, harder dungeons, better loot |
| Friends | Activity feed | Quick party invites, social connection |
| Cross-character transfers | Trading, communication | |
| Chat | Trade channel, LFG | Guild coordination, socializing |
| Marketplace | Buy/sell | Player-driven economy |
PVP System
Overview
PVP in Delve is fully asynchronous. You configure your character’s skill queue, queue for a match, and the system resolves the fight. You can review the combat log afterward, just like a dungeon run. This makes PVP accessible to all time zones and schedules while preserving the strategic depth of build optimization and skill queue configuration.
Arena PVP
1v1 Arena
The core PVP mode. Your character fights another player’s character in a controlled environment.
How It Works
- Queue for 1v1 — Your character enters the matchmaking queue
- Matchmaking — System finds an opponent near your rating
- Fight resolves immediately — No waiting period (unlike dungeon runs)
- Review the log — Full combat log shows every roll, decision, and outcome
The fight resolves instantly because there’s no dungeon exploration — just two configured characters fighting. This makes PVP feel snappy and repeatable.
Matchmaking
- ELO-based rating system — Win against higher-rated opponents = more rating gain
- New characters start at 1000 ELO
- Rating range for matchmaking: +/- 150 ELO (widens over time if no match found)
- Level-based brackets to prevent level 50s from fighting level 5s:
- Bracket 1: Levels 1-12
- Bracket 2: Levels 13-25
- Bracket 3: Levels 26-37
- Bracket 4: Levels 38-50
- Within a bracket, gear and build differences create the competitive space
Arena Rules
- Standardized arena — Flat terrain, no environmental factors. Pure build vs. build.
- No consumables — Potions, scrolls, and supplies are not used in arena PVP. Prevents pay-to-consume advantages.
- Full HP/resources — Both fighters start at full HP, full ability charges, all abilities available
- 10-round time limit — If neither fighter is dead after 10 rounds, the one with higher HP percentage wins
- Death is not permanent — Losing an arena match has zero character consequences (no gear loss, no recovery timer)
Team Arena (3v3)
- Same matchmaking system but team-based
- Premade teams (guild members, friends) or solo queue (auto-matched)
- Team rating is separate from 1v1 rating
- Party composition matters heavily — tank/healer/DPS vs. triple DPS is a real strategic choice
- Skill queues can reference teammate conditions (same as party dungeon runs)
Arena Rewards
| Reward | Source |
|---|---|
| PVP Rating | Win/loss affects ELO |
| Honor Points | Earned per match (more for wins), spent at PVP vendor |
| Season Rating | Best rating achieved during the current season |
| Titles | Earned at rating thresholds |
PVP Vendor
Spends Honor Points on:
- Cosmetic gear skins unique to PVP
- Titles at various honor point thresholds
- PVP-specific consumables (usable only in guild war content, not arena)
- Portraits and profile frames showing PVP rank
No stat gear from PVP vendor. PVP rewards are cosmetic and prestige-based. Gear comes from PVE (dungeons, crafting, raids). This prevents a mandatory PVP grind for PVE players and vice versa.
PVP Seasons
Season Structure
- Seasons last 3 months (aligned with real-world quarters)
- At the start of each season, arena ratings soft reset (compressed toward 1000, not fully wiped)
- Season rewards are distributed at the end based on peak rating achieved during the season
Season Rewards
| Rating Tier | Title | Reward |
|---|---|---|
| Bronze (1000-1199) | Combatant | Bronze portrait frame |
| Silver (1200-1399) | Duelist | Silver portrait frame, exclusive silver armor skin |
| Gold (1400-1599) | Gladiator | Gold portrait frame, exclusive gold armor skin, cosmetic pet |
| Platinum (1600-1799) | Champion | Platinum portrait frame, exclusive platinum armor skin, unique title |
| Diamond (1800+) | Legend | Diamond portrait frame, legendary armor skin, unique animated title, statue in PVP hall |
- All season rewards are permanent — earned once, kept forever
- Previous season rewards are no longer earnable (creates exclusivity and bragging rights)
Leaderboard
- Top 100 players per bracket displayed on a public leaderboard
- Shows: rank, character name, guild, class, win/loss record, peak rating
- Updated in real-time as matches resolve
PVP Balance
The Balance Challenge
PVE gear in PVP creates inherent imbalance — a player with legendary raid gear will outperform someone in quest greens at the same skill level. Delve addresses this with stat normalization:
Stat Normalization in Arena
- All gear in arena PVP is scaled to a baseline item level
- A player in common gear fights at the same effective stat level as a player in legendary gear
- Gear properties still matter — enchantments, set bonuses, and special effects function normally, but raw stats (Evasion rating, damage dice, attribute bonuses from MGT/LOG/SPD/PRS/FRT/LCK) are equalized
- This means: your choice of enchantments, class build, feat selection, and skill queue configuration determine PVP outcomes — not how many raid clears you have
Why Normalization?
- Makes PVP accessible to all players regardless of PVE progression
- Prevents “you need raid gear to compete” gatekeeping
- Keeps the competitive focus on strategy and build decisions
- Players who don’t enjoy PVE can still compete in PVP
What’s NOT Normalized
- Level — Higher level characters have more abilities, feats, and base stats. This is why PVP has level brackets.
- Class and subclass — Some matchups are inherently favored (rock-paper-scissors element). Over many games, this evens out.
- Skill queue configuration — The primary competitive differentiator. A well-configured skill queue beats a poorly configured one.
Guild Wars
Guild wars are opt-in, ongoing conflicts between two guilds that add PVP stakes to normal play.
Declaring War
- A guild officer challenges another guild to war
- The challenged guild has 48 hours to accept or decline
- Both guilds set a war wager (gold from the guild bank)
- War lasts 1 week from acceptance
War Mechanics
- During the war period, members of opposing guilds are automatically matched for war arena matches when both queue for PVP
- War matches award war points to the winning guild
- At the end of the week, the guild with more war points wins the wager
- War matches use the same normalized arena rules
War Standings
- Tracked on a war scoreboard visible to both guilds
- Individual performance tracked (top contributors earn bonus honor points)
- Guild chat gets automated updates (“Your guild is winning the war 45-38!”)
War Rewards
- Winning guild receives the loser’s wager
- Winning guild members receive bonus honor points
- “War Victor” title available for the winning guild during the following week
Faction Warfare
Faction warfare is a server-wide, persistent PVP system tied to the faction system (see 11-factions-and-reputation.md).
How It Works
- Certain world zones are contested territory controlled by factions
- Players aligned with a faction can queue for faction battles to capture or defend territory
- Faction battles are larger-scale (8v8 or 16v16), using the timed lobby system
- Territory control grants server-wide buffs to all members of the controlling faction
Territory Control Benefits
| Territory | Controlling Faction Bonus |
|---|---|
| Silver Mines | +5% gold from all sources |
| Ancient Library | +5% XP from all sources |
| Sacred Grove | +5% gathering yield |
| Forge of Ages | +5% crafting speed |
- Territories can be challenged once per day by an opposing faction
- The defending faction’s battle happens at a set time (timed lobby — same async-friendly system)
- Control changes are announced server-wide
Faction PVP Rating
- Separate from arena rating
- Affects faction reputation gain rate
- Top faction PVP contributors earn exclusive faction titles and cosmetics
PVP Design Principles
- PVP is always optional. No PVE content requires PVP participation. No PVP rewards give PVE advantages (beyond cosmetics).
- Skill > gear. Stat normalization ensures PVP outcomes depend on build choices and skill queue configuration, not gear farming.
- Async-friendly. All PVP resolves without requiring both players online simultaneously.
- Meaningful rewards without mandatory participation. PVP rewards are cosmetic prestige items. FOMO is limited to seasonal titles (which are purely visual).
- Anti-griefing. No open-world PVP, no ganking, no corpse camping. All PVP is consensual and structured.
Factions and Reputation
Overview
Factions are persistent organizations in the game world that players align with to unlock exclusive rewards, recipes, enchantments, and consumables. Reputation is earned over time through quests and activities — it’s a long-term progression system that complements the level and gear treadmills.
Factions create meaningful choices. Some factions are in conflict, so raising reputation with one may lower it with another. This encourages players to specialize or plan carefully.
Design Principle: No Class Funneling
Every faction’s rewards are usable by all 9 classes. Factions do not map to class archetypes. There is no “correct” faction for a Shade, Arcanist, or Vanguard. Instead, each faction optimizes a different gameplay strategy dimension — durability, burst damage, loot discovery, gear enhancement, consumable power, risk/reward, or economic advantage. Any class benefits from any faction.
A Shade might choose Iron Compact (shore up squishiness with durability), Covenant of Dusk (maximize burst damage at the cost of fragility), or Shadow Court (optimize loot per run). All three are valid builds that produce meaningfully different characters.
Faction List
The Iron Compact
- Theme: Military order, discipline, endurance
- Headquarters: Ironhold Citadel (The Ironmarch)
- Conflict with: The Covenant of Dusk
- Gameplay Dimension: Durability & Endurance
- Rewards focus on surviving longer, recovering more between encounters, and shrugging off conditions. For players who want to go deeper into dungeons and never die.
- Rewards:
- Enchantments (all gear types): +max HP, +condition resistance, +HP recovery between encounters, +rest point effectiveness
- Consumables: Extended-duration rations (last 2 encounters instead of 1), fortification elixirs (+Fortitude for full dungeon run), condition immunity tonics
- Recipes: Reinforced armor upgrades (any weight class), durable weapon coatings (reduce gear degradation), bulk supply crafting (craft supplies in larger batches)
- Passive (Exalted): +10% HP recovery at rest points, +10% condition resistance in all content
- Cosmetics: Heavy-plated armor skins, “Iron Warden” title, fortress-themed portrait frames
The Shadow Court
- Theme: Intelligence network, information brokers, treasure hunters
- Headquarters: The Undercity (The Shallows)
- Conflict with: The Order of the Dawn
- Gameplay Dimension: Loot & Discovery
- Rewards focus on finding more treasure, detecting traps and hidden rooms, and increasing loot quality. For players who want to maximize the value of every dungeon run.
- Rewards:
- Enchantments (all gear types): +loot quality, +trap detection, +hidden room discovery chance, +gold find
- Consumables: Treasure-finding scrolls (reveal all hidden caches in current dungeon), trap-sight potions (+50% trap detection for a full run), appraisal dust (identify items on pickup)
- Recipes: Lockpick crafting (all tiers), loot-enhancing weapon oils (bonus drop chance from kills), salvage kits (bonus materials from salvaging)
- Passive (Exalted): +5% loot quality on all dungeon runs, +15% trap detection in all content
- Cosmetics: Dark leather armor skins, “Shadow Blade” title, mysterious portrait frames
The Arcane Conclave
- Theme: Academy of magic, research, craftsmanship
- Headquarters: The Spire Atrium (The Spire)
- Conflict with: The Primal Circle
- Gameplay Dimension: Gear Enhancement & Reforging
- Rewards focus on making gear better — reforging, enchanting, and enhancement. For players who want to min-max their equipment and chase perfect rolls.
- Rewards:
- Enchantments (all gear types): +enchantment potency (existing enchantments are stronger), +reforge quality floor (minimum potency on reforge rolls), weapon/armor enhancement success rate bonus
- Consumables: Reforging catalysts (guarantee the new bonus is different from the old one), enhancement boosters (+15% success rate on next enhancement attempt), essence amplifiers (double magical essence from next disenchant)
- Recipes: Advanced enchanting formulas (unique enchantments not available elsewhere), gem cutting techniques (higher quality cuts), enhancement protection charms (prevent material loss on failed enhancement)
- Passive (Exalted): Reforging costs reduced by 15%, +5% gear enhancement success rate
- Cosmetics: Arcane-effect armor skins, “Archmage” title, glowing rune portrait frames
The Primal Circle
- Theme: Harmony with nature, self-sufficiency, the land provides
- Headquarters: The Eldergrove (The Verdant Deep)
- Conflict with: The Arcane Conclave
- Gameplay Dimension: Gathering & Consumable Power
- Rewards focus on gathering more materials, crafting better consumables, and getting more out of alchemy and cooking. For players who want the best supply chain and the strongest potions/food.
- Rewards:
- Enchantments (all gear types): +gathering yield, +consumable effectiveness (potions heal more, elixirs last longer), +crafting speed, +critical craft chance
- Consumables: Gathering boosters (double yield on next gathering expedition), potency enhancers (next consumable used has +50% effect), nature’s bounty scrolls (bonus materials from dungeon node encounters)
- Recipes: Upgraded versions of all alchemy recipes (greater potency), exclusive cooking recipes (strongest food buffs), bulk crafting formulas (craft in larger batches), rare herb cultivation (grow rare herbs with a personal garden system)
- Passive (Exalted): +10% gathering yield from all sources, +10% consumable effectiveness
- Cosmetics: Nature-themed armor skins, “Warden of the Wild” title, living-vine portrait frames
The Order of the Dawn
- Theme: Zealous order, strength through conviction, overwhelming force
- Headquarters: The Sunspire Cathedral (The Pale Reaches)
- Conflict with: The Shadow Court
- Gameplay Dimension: Burst Power & Boss Killing
- Rewards focus on dealing more damage, especially to bosses and elites. For players who want to optimize kill speed and tackle the hardest combat content.
- Rewards:
- Enchantments (all gear types): +damage vs. bosses, +damage vs. elites, +critical hit damage, +skill damage (flat bonus to all damage skills)
- Consumables: Wrath elixirs (+20% damage for 3 encounters, -10% max HP for duration), boss-slayer oils (weapon coating: +25% damage to boss-type enemies for one fight), fury tonics (+15% critical hit chance for one encounter)
- Recipes: High-damage weapon coatings for all weapon types, boss-tuned ammunition (bonus damage to large enemies), sharpening kits (temporary flat damage boost to any weapon)
- Passive (Exalted): +8% damage to boss and elite enemies, +5% critical hit damage
- Cosmetics: Radiant-themed armor skins, “Dawnbringer” title, sunrise portrait frames
The Covenant of Dusk
- Theme: Forbidden knowledge, power at a price, risk and reward
- Headquarters: The Duskhollow (The Ashlands)
- Conflict with: The Iron Compact
- Gameplay Dimension: Risk/Reward & Condition Exploitation
- Rewards focus on powerful effects with trade-offs, condition manipulation, and getting stronger as things get more dangerous. For players who like glass cannon builds and gambling on big payoffs.
- Rewards:
- Enchantments (all gear types): +damage when below 50% HP, +damage per debuff on target, chance to apply random condition on hit, +damage but -max HP (tunable trade-off enchantments)
- Consumables: Blood elixirs (spend 20% HP to gain +30% damage for 3 rounds), dusk vials (apply a random debuff to all enemies at encounter start, but also apply one to yourself), desperation draughts (all stats +15% when below 25% HP)
- Recipes: Condition-enhancing weapon coatings (conditions you apply last 1 extra round), volatile potions (random potency — could be weak or incredibly strong), cursed gear upgrades (powerful enhancement with a drawback)
- Passive (Exalted): +10% damage when below 50% HP, conditions you apply last 1 additional round
- Cosmetics: Shadow-effect armor skins, “Duskwalker” title, dark-flame portrait frames
The Merchant Consortium
- Theme: Trade guild, commerce, mutual profit
- Headquarters: The Grand Exchange (The Shattered Coast)
- Conflict with: None (neutral faction)
- Gameplay Dimension: Economy & Trade
- Rewards focus on making more gold, spending less gold, and having better marketplace access. For players who want to build wealth and dominate the economy.
- Rewards:
- Enchantments (all gear types): +gold find, +vendor sell prices, +material yield from salvage, reduced enhancement costs
- Consumables: Merchant’s eye (identify full value of all loot for one dungeon — helps decide equip vs. sell vs. salvage), golden touch scrolls (+50% gold from next dungeon run), trade route maps (bonus gathering yield from one zone for 24 hours)
- Recipes: Efficient salvage techniques (bonus materials from salvaging), market-ready crafting (crafted items sell for 20% more to vendors), luxury goods crafting (high gold value items crafted from common materials)
- Passive (Exalted): Auction house sale tax reduced from 10% to 7%, NPC vendor buy prices increased by 15%
- Cosmetics: Wealthy-themed armor skins, “Grand Merchant” title, gold-coin portrait frames
Conflict Pairs
Factions exist in three conflict pairs, each representing a genuine gameplay trade-off that every class must consider.
Iron Compact vs. Covenant of Dusk — Safety vs. Power
- The trade-off: Do you want to survive longer and go deeper into dungeons, or hit harder at the cost of fragility?
- Why it matters for everyone:
- A Vanguard might go Iron Compact (unkillable tank) or Covenant of Dusk (offensive Vanguard that hits like a truck when hurt)
- A Shade might go Iron Compact (shore up low HP to survive longer) or Covenant of Dusk (maximize burst damage with risk/reward synergy)
- An Arcanist might go Iron Compact (survive long enough to cast) or Covenant of Dusk (glass cannon blaster)
- Gaining reputation with one causes 50% of that gain as a loss to the other
Order of the Dawn vs. Shadow Court — Kill Speed vs. Loot Efficiency
- The trade-off: Do you want to kill bosses faster and deal more damage, or find more loot and hidden treasure per run?
- Why it matters for everyone:
- A Berserker might go Order of the Dawn (maximize already-high damage against bosses) or Shadow Court (offset Berserker’s poor trap detection and find more loot)
- A Warden might go Order of the Dawn (add damage to a support class) or Shadow Court (lean into utility and treasure finding)
- A Pathfinder might go Order of the Dawn (add boss-killing power to ranged DPS) or Shadow Court (double down on scouting and exploration strengths)
- Gaining reputation with one causes 50% of that gain as a loss to the other
Arcane Conclave vs. Primal Circle — Gear Investment vs. Consumable Economy
- The trade-off: Do you want to make your permanent gear as powerful as possible, or have the best consumables and gathering yields?
- Why it matters for everyone:
- A min-maxer who reforges obsessively wants Arcane Conclave
- A player who runs long dungeons and burns through supplies wants Primal Circle
- A crafter-focused player might want Primal Circle for gathering, or Arcane Conclave for enhancement recipes — depends on what they craft
- A casual player who doesn’t reforge much gets more from Primal Circle’s consumable bonuses
- Gaining reputation with one causes 50% of that gain as a loss to the other
Merchant Consortium — Neutral
- Gaining reputation with the Merchant Consortium does not affect any other faction
- Every player benefits from economic advantages, but the Consortium competes with other factions for the player’s limited daily quest time
Why This Works for Build Diversity
The faction system creates 9 classes x 3 subclasses x 7 faction choices = hundreds of distinct character identities. Two Shade Assassins with different faction alignments play very differently:
- Shade Assassin + Iron Compact: Tanky assassin who survives long dungeons. Uses durability enchantments to offset low HP. Takes on attrition content that normally kills Shades.
- Shade Assassin + Covenant of Dusk: Glass cannon who gets stronger as HP drops. Uses risk/reward consumables to spike damage. Dominant in short dungeons and boss fights but fragile.
- Shade Assassin + Shadow Court: Loot-focused treasure hunter. Uses discovery enchantments to find hidden caches. Runs exploration dungeons in The Shattered Coast and The Sunken Kingdoms for maximum haul.
- Shade Assassin + Order of the Dawn: Boss killer. Stacks damage bonuses to delete bosses in The Pale Reaches. Less versatile but devastating in boss-focused content.
- Shade Assassin + Arcane Conclave: Min-maxer who reforges obsessively. Gear is immaculate. Less situational power but the best possible base stats.
- Shade Assassin + Primal Circle: Consumable-focused. Burns through high-potency potions every run. Great at sustained play and always has the best supplies.
- Shade Assassin + Merchant Consortium: Economy player. Makes gold efficiently, plays the marketplace, funds alts. Less combat power but wealthiest on the server.
Reputation System
Reputation Tiers
| Tier | Rep Required | Benefits |
|---|---|---|
| Hostile | -3000 to -1001 | Faction NPCs refuse service, faction quests unavailable |
| Unfriendly | -1000 to -1 | No access to faction vendors or quests |
| Neutral | 0 | Starting state for all factions. Basic faction quests available |
| Friendly | 1 to 2999 | Access to Tier 1 faction vendor items and more quests |
| Honored | 3000 to 8999 | Access to Tier 2 vendor items, faction daily quests |
| Revered | 9000 to 20999 | Access to Tier 3 vendor items, faction-exclusive recipes |
| Exalted | 21000+ | Access to all faction rewards, faction passive bonus, unique title, cosmetic rewards |
Reputation Gains
| Activity | Rep Gained | Frequency |
|---|---|---|
| Faction daily quest | 100-250 | 3 per day per faction |
| Dungeon run in faction-presence zone | 50-100 | Per completion |
| Faction bounty (repeatable task) | 25-75 | No daily limit |
| Faction-aligned crafting | 25-50 | Per craft using faction recipes |
| Faction warfare victory | 200-500 | Per battle |
| Turning in faction-requested materials | 10-50 | Repeatable |
Faction Conflicts and Rep Loss
- Factions exist in pairs of conflict: Iron Compact vs. Covenant of Dusk, Order of the Dawn vs. Shadow Court, Arcane Conclave vs. Primal Circle
- Gaining reputation with one faction in a pair causes 50% of that gain as a loss to the opposing faction
- Example: Earning 200 rep with Iron Compact loses 100 rep with Covenant of Dusk
- The Merchant Consortium is neutral — gaining rep with them doesn’t affect other factions
- This means a character can realistically be Exalted with 2-3 factions but must choose which rival factions to support
Recovering Lost Reputation
- A character can never fall below Hostile (-3000) with any faction
- Lost reputation can be recovered by doing quests and activities for the faction
- There are rare “diplomacy” bounties that grant rep with BOTH factions in a conflicting pair (limited, don’t fully resolve the tension)
Faction Quests
Daily Quests
- 3 daily quests per faction (available at Friendly+)
- Quick procedurally generated bounty-style tasks taking 15-30 minutes
- Grant faction reputation, gold, and faction tokens
Faction Bounties
- Repeatable procedural tasks: gather specific materials, defeat specific enemy types, craft specific items
- Lower rep reward than daily quests but no daily limit
- Good for players who want to grind reputation
Faction Tokens
- Earned alongside reputation from faction activities
- Spent at faction vendors for exclusive rewards
- Cannot be traded
- Different from reputation — you might be Exalted but still saving up tokens for that legendary recipe
Faction Vendor Tiers
| Tier | Rep Required | Token Cost Range | Items Available |
|---|---|---|---|
| Tier 1 | Friendly | 50-200 tokens | Basic consumables, common recipes, faction tabard cosmetic |
| Tier 2 | Honored | 200-500 tokens | Uncommon enchantment formulas, rare consumable recipes, faction cosmetic |
| Tier 3 | Revered | 500-1500 tokens | Rare enchantment formulas, exclusive recipes, faction cosmetic set piece |
| Tier 4 | Exalted | 1500-5000 tokens | Legendary recipes, unique enchantment formulas, faction passive unlocked, faction title, full cosmetic set |
Strategic Considerations
For Build Optimization
- Players should evaluate which faction dimension matters most for their content focus and playstyle, not their class
- A player who runs boss-focused content in The Pale Reaches benefits from Order of the Dawn regardless of class
- A player who runs long attrition dungeons in The Bonewood benefits from Iron Compact regardless of class
- A player who focuses on crafting and marketplace play benefits from Primal Circle or Arcane Conclave
For Guild Strategy
- Guilds benefit from having members across different factions — covering all vendor inventories
- A guild with members Exalted across all factions has access to every recipe and enchantment between them
- Guild coordination on faction warfare can control territory bonuses for all members
For Alts
- Since faction choice is per-character (not account-wide), players can align different characters with different factions
- A player’s main might be Iron Compact for durability, while their alt is Covenant of Dusk for a different playstyle
- Faction tokens are per-character, encouraging long-term play across multiple characters
For Faction Warfare
- Faction warfare provides team-based PVP content with real stakes (territory control — see 10-pvp-system.md)
- Territory bonuses affect all members of the controlling faction server-wide
- Top faction PVP contributors earn exclusive faction titles and cosmetics
World and Setting
Overview
Delve takes place in Aethermere, a classic high-fantasy world fragmented by a magical catastrophe. The world exists to serve gameplay — every zone is designed around the game’s systems (procedural quests, factions, gathering, crafting, progression). There is no central storyline. Quests are procedurally generated tasks that players repeat to gain experience, loot, and materials. Lore exists as flavor text and environmental context, never as required reading.
The World of Aethermere
The Premise
Aethermere was once a unified empire, but a catastrophic magical event — The Sundering — shattered the world into fragmented regions connected by dangerous wilderness. Civilization survives in pockets: walled cities, fortified towns, and scattered settlements. Between them lie dungeons, ruins, monster-infested wilds, and forgotten places filled with treasure.
Adventurers exist because someone has to explore the dangerous places, clear the monsters, recover the artifacts, and keep the roads open. That someone is the player’s character.
Tone
- Hopeful but dangerous. The world is recovering, not dying. There are safe havens, friendly NPCs, and reasons to fight. But the wilds are genuinely lethal.
- Classic fantasy with texture. Dungeons, dragons, ancient ruins — but the world has depth. Factions have real conflicts. Player choices have consequences.
- Discovery over exposition. The Sundering left gaps in history. Ancient ruins hold secrets. Players discover lore fragments through play.
Zone Design Philosophy
Level-Scaling Content
Every zone has content for every level. Zones are not level-locked. A level 1 character and a level 50 character can both adventure in the same zone — the quest board generates level-appropriate dungeons, the gathering nodes yield tier-appropriate materials based on gathering skill, and enemies scale to the content selected, not the zone.
This means players never have to leave a zone they enjoy. A player who loves the dungeon style of the Verdant Deep can quest there from level 1 to 50 and into Paragon. They’ll just be running harder dungeons with tougher enemies and better loot as they progress.
Why Players Spread Out
Every zone offers content for all character types and all levels, but zones differ in what resources, enemy types, dungeon styles, and faction reputation they emphasize. Two players of the same class and level may choose completely different zones based on:
- What gathering materials they need — ores in the mountains, herbs in the forests, hides in the wilds
- What enemy types they want to farm — undead drop different salvage than elementals or beasts
- What dungeon styles they prefer — trap-heavy vs. combat-heavy vs. environmental hazard focus
- Which factions they’re building reputation with — each zone has different faction presence
- What crafting recipes require — zone-specific rare materials drive players to specific regions
How Level-Scaling Works
- Quest boards in every zone generate bounties, dungeon crawls, and raids appropriate to the character’s level. A level 5 in the Ashlands gets level 5 volcanic dungeon runs. A level 40 in Thornvale gets level 40 farmland dungeon runs.
- Gathering nodes yield materials based on the character’s gathering skill level, not the zone. A skilled miner in any zone with mining nodes extracts tier-appropriate ore.
- Enemy families are zone-specific (goblins in Thornvale, demons in the Ashlands), but their stat blocks scale to the dungeon’s difficulty tier. A level 5 goblin warband and a level 45 goblin warband are different fights.
- Zone-specific rare materials drop at all levels but in quantities and qualities that scale with character level and gathering skill.
- Faction dailies are available at all levels in every zone where the faction has presence.
Zone Structure
Each zone provides:
- Hub town with quest board, vendors, crafting stations, and gathering dispatch
- Multiple dungeon entrances generating content at the character’s level
- Gathering nodes for 2-3 gathering professions (no zone has all four at high yield)
- Faction presence from 2-3 factions (with at least one faction vendor)
- Environmental type (relevant to Pathfinder’s Terrain Mastery and elemental resistances)
- Dominant enemy families (determines loot tables, salvage types, and which damage types/resistances matter)
- Zone-specific rare materials (unique to this zone, used in specific crafting recipes)
Zone Unlocking
Zones unlock as the player explores the world. New characters start with access to 3 zones and unlock additional zones by completing a threshold number of dungeon runs (in any zone). This is purely an exploration gate, not a level gate — a level 1 character who runs enough dungeons unlocks zones just as fast as a level 30.
| Unlock Stage | Zones Available | Requirement |
|---|---|---|
| Starting | Thornvale, The Shallows, Dusthollow | Character creation |
| First Expansion | The Ironmarch, The Verdant Deep, The Drowned Tunnels | Complete 10 dungeon runs (any zone) |
| Second Expansion | The Ashlands, The Shattered Coast, The Bonewood | Complete 30 dungeon runs (any zone) |
| Third Expansion | Stormhaven Plateau, The Tangled Warren, The Pale Reaches | Complete 60 dungeon runs (any zone) |
| Fourth Expansion | The Spire, The Sunken Kingdoms, The Blightmoor | Complete 100 dungeon runs (any zone) |
| Endgame | The Rift, The Crucible | Complete 150 dungeon runs (any zone) |
Players unlock zones in batches. The unlocking is account-wide — once unlocked, all characters on the account can access the zone. The threshold counts are cumulative across all characters.
Zones
Thornvale
- Environment: Pastoral farmlands, light forests, rolling hills
- Hub Town: Millhaven — a market town with all basic services
- Gathering Emphasis: Herbalism (herbs, roots, wildflowers), Logging (softwood, hardwood, heartwood)
- Secondary Gathering: Mining (low yield — scattered surface deposits)
- Dungeon Style: Balanced — even mix of combat, traps, environmental encounters, and decision points. No single mechanic dominates. Good for generalist builds and new players learning the systems.
- Enemy Families: Goblins, wolves, bandits, giant spiders, boars, treants
- Salvage Focus: Leather scraps, crude weapons (Blacksmithing materials), beast parts (Alchemy reagents)
- Faction Presence: Iron Compact (full hub), Merchant Consortium (trade post)
- Zone-Specific Rare: Thornvale Sap — used in Alchemy and Cooking recipes (healing and buff consumables)
- Why Go Here: Best Herbalism and Logging yields in the game. Balanced dungeons suit any build. Good starting zone but remains efficient at all levels for gathering-focused play.
The Shallows
- Environment: Coastal wetlands, tidal caves, river deltas, mangrove mazes
- Hub Town: Reedhaven — a stilted fishing village turned adventurer outpost
- Gathering Emphasis: Skinning (fish leather, scale scraps, chitin, crustacean shell), Mining (river gems, bog iron, clay deposits)
- Secondary Gathering: Herbalism (swamp herbs, mushrooms, waterlogged roots)
- Dungeon Style: Hazard-heavy — flooded passages, visibility issues, poisonous gas pockets, quicksand, tidal surges. Rewards Fortitude, good supply management, and preparation over raw combat power.
- Enemy Families: Crabs, bog trolls, swamp serpents, mud elementals, pirates, leeches
- Salvage Focus: Chitin, scales, monster parts (Alchemy materials), rough gems (Jewelcrafting)
- Faction Presence: Shadow Court (full hub), Merchant Consortium (trade post)
- Zone-Specific Rare: Tidestone — used in Jewelcrafting and water-resistance Enchanting recipes
- Why Go Here: Best Skinning yields plus early gem access for Jewelcrafting. Hazard-heavy dungeons reward tanky builds and players who invest in supply planning. Shadow Court reputation.
Dusthollow
- Environment: Arid badlands, mesas, sandstone ruins, desert canyons, oasis camps
- Hub Town: Sandspire — a cliffside settlement built into canyon walls
- Gathering Emphasis: Mining (sandstone, ore veins, rough gems, crystal formations), Skinning (lizard hides, scorpion chitin, vulture feathers)
- Secondary Gathering: Herbalism (desert succulents, cactus extract, dry sage)
- Dungeon Style: Combat-dense — wave-style encounters and timed challenges. Enemies come in numbers. AoE damage, sustained resource management, and offensive builds are rewarded. Fastest XP per hour for players who can handle the pace.
- Enemy Families: Scorpions, sand elementals, desert bandits, hyenas, dust wraiths, sand wurms
- Salvage Focus: Chitin, venom sacs (Alchemy poisons), raw gems, sand-scoured metal (Blacksmithing)
- Faction Presence: Merchant Consortium (trade outpost), Covenant of Dusk (hidden shrine)
- Zone-Specific Rare: Sunite Glass — used in Jewelcrafting and Enchanting recipes (light/fire themed)
- Why Go Here: Combat-dense dungeons are the fastest XP grind for well-geared offensive characters. Good Mining and Skinning. Merchant Consortium and Covenant of Dusk reputation.
The Ironmarch
- Environment: Mountain passes, ancient ruins, underground caverns, crumbling fortresses
- Hub Town: Hammerfall — a fortified trading post at the mountain’s base
- Gathering Emphasis: Mining (all ore types, gems, stone), Logging (hardwood, petrified wood, mountain oak)
- Secondary Gathering: Skinning (cave beast hides, mountain goat pelts)
- Dungeon Style: Trap-heavy — mechanical traps, collapsing passages, locked doors, pressure plates, poison dart walls, pit traps. Logic and Speed checks are frequent. Every class can succeed with the right supplies (lockpicks, trap detection potions), but Shades and high-Logic characters have a natural edge.
- Enemy Families: Orcs, trolls, cave beasts, undead miners, stone golems, mountain drakes
- Salvage Focus: Metal scraps, stone components (Blacksmithing, Jewelcrafting), enchanting dust from golems
- Faction Presence: Iron Compact (full hub), Shadow Court (hidden outpost)
- Zone-Specific Rare: Deepvein Crystal — used in Jewelcrafting and Enchanting recipes
- Why Go Here: Best Mining yields in the game. Trap-heavy dungeons drop lockpick recipes and trap-disarm tools useful everywhere. Iron Compact daily quests. Strong Logging secondary.
The Verdant Deep
- Environment: Dense ancient forest, druidic circles, fey-touched groves, overgrown ruins
- Hub Town: Sylvanus — a treetop settlement
- Gathering Emphasis: Herbalism (rare herbs, fungi, ironwood bark, mosses), Logging (ironwood, living wood, ancient timber)
- Secondary Gathering: Skinning (beast hides, insect chitin, feathers)
- Dungeon Style: Decision-heavy — branching paths with many encounters offering alternative resolutions. Diplomacy, stealth, and brute force are all viable paths through the same dungeon. Presence and Speed checks appear alongside standard combat. Rewards diverse attribute spreads and adaptable builds.
- Enemy Families: Corrupted beasts, fey creatures, plant monsters, giant insects, dryads, forest spirits
- Salvage Focus: Organic components (Alchemy, Cooking), rare wood (Blacksmithing weapon hafts), magical essence from fey
- Faction Presence: Primal Circle (full hub), Arcane Conclave (research outpost)
- Zone-Specific Rare: Feybloom Pollen — used in Alchemy and Cooking recipes (powerful buff consumables)
- Why Go Here: Best Herbalism yields alongside strong Logging. Decision-heavy dungeons mean multiple valid approaches per run — high replay value. Primal Circle reputation.
The Drowned Tunnels
- Environment: Flooded underground river systems, bioluminescent caverns, subterranean lakes, crystal grottoes
- Hub Town: Glowport — a cavern settlement lit by fungal growths
- Gathering Emphasis: Mining (phosphite crystals, cave gems, deep iron), Herbalism (glowing fungi, cave moss, mineral-fed roots)
- Secondary Gathering: Skinning (cave fish, amphibious beasts, blind crawlers)
- Dungeon Style: Endurance/Hazard — environmental hazards (flooding, underwater passages, toxic gas, darkness) combined with long dungeon crawls and infrequent rest points. Supply management is critical. Characters with high Fortitude and good preparation thrive. Running out of torches or rations here is punishing.
- Enemy Families: Cave fish, amphibians, myconids, oozes, blind cave trolls, crystal elementals
- Salvage Focus: Ooze residue (Alchemy), crystal shards (Enchanting), bioluminescent material (Jewelcrafting)
- Faction Presence: Arcane Conclave (research team), Shadow Court (smuggler route)
- Zone-Specific Rare: Luminous Spore — used in Alchemy and Enchanting recipes (light and detection themed)
- Why Go Here: Unique gathering materials found nowhere else. Long dungeon crawls reward endurance builds and heavy supply preparation. Two strong gathering professions. Arcane Conclave reputation.
The Ashlands
- Environment: Volcanic wasteland, lava fields, obsidian formations, ash-choked ruins, magma vents
- Hub Town: Cinderport — a heat-resistant fortress city
- Gathering Emphasis: Mining (obsidian, fire gems, volcanic ore, sulphur), Skinning (salamander hides, demon chitin, drake scales)
- Secondary Gathering: Herbalism (ash bloom, fire lichen, magma root)
- Dungeon Style: High-damage — fire hazards, lava flows, heat exhaustion, and enemies that hit exceptionally hard. Encounters test raw durability and burst damage output. Fire resistance gear and healing consumables are near-mandatory. Short, intense dungeons.
- Enemy Families: Fire elementals, demons, salamanders, ash golems, flame drakes, magma crawlers
- Salvage Focus: Fire essence (Enchanting), demon parts (Alchemy), obsidian shards (Blacksmithing, Jewelcrafting)
- Faction Presence: Covenant of Dusk (full hub), Order of the Dawn (crusader outpost)
- Zone-Specific Rare: Molten Core Fragment — used in fire-themed Blacksmithing and Enchanting recipes
- Why Go Here: Best source of fire gems and volcanic crafting materials. High-damage dungeons reward burst-oriented builds. Covenant of Dusk reputation. Short dungeon durations make it efficient for quick sessions.
The Shattered Coast
- Environment: Coastal ruins, underwater caves, shipwreck reefs, pirate coves, cliff fortresses
- Hub Town: Tidegate — a port city and major trading hub
- Gathering Emphasis: Skinning (sea creature hides, coral, pearl oysters, shark teeth), Herbalism (sea kelp, tide bloom, salt crystals, coastal herbs)
- Secondary Gathering: Mining (sea-floor deposits, coral gems)
- Dungeon Style: Exploration — branching paths, hidden rooms, secret caches, optional boss encounters. Dungeons are large with many off-the-beaten-path discoveries. Perception, Investigation, and Luck characters find significantly more loot per run. Any build can complete the main path, but thorough explorers are heavily rewarded.
- Enemy Families: Pirates, sea serpents, merfolk raiders, water elementals, drowned undead, kraklings
- Salvage Focus: Nautical components, pearl dust (Jewelcrafting), sea salt (Cooking, Alchemy), waterlogged enchanting materials
- Faction Presence: Merchant Consortium (full hub — Grand Exchange), Shadow Court (smuggler network)
- Zone-Specific Rare: Abyssal Pearl — used in Jewelcrafting and Enchanting recipes (water and luck themed)
- Why Go Here: Merchant Consortium headquarters means best trade access and lowest auction fees while here. Exploration-heavy dungeons yield the most total loot per run. Strong Skinning and Herbalism.
The Bonewood
- Environment: Dead forest, necromantic corruption, bone-white trees, haunted ruins, crypts
- Hub Town: Ashwick — a walled town at the forest’s edge
- Gathering Emphasis: Logging (bonewood, petrified timber, ghost oak), Herbalism (grave bloom, spirit moss, decay fungi)
- Secondary Gathering: Skinning (undead beast remains, spectral residue)
- Dungeon Style: Attrition — long dungeons with many encounters and scarce rest points. Enemies apply debuffs and conditions (poison, fear, life drain, curse). Sustained healing, condition removal, and careful resource rationing are critical. Punishes front-loaded builds that burn resources early.
- Enemy Families: Undead (skeletons, zombies, wraiths, ghouls), necromancers, death knights, bone constructs, spectral beasts
- Salvage Focus: Necrotic essence (Enchanting), bone dust (Alchemy), ectoplasm (Alchemy, Enchanting), grave iron (Blacksmithing)
- Faction Presence: Order of the Dawn (full hub), Primal Circle (restoration outpost)
- Zone-Specific Rare: Soulstone Shard — used in necrotic and radiant Enchanting recipes
- Why Go Here: Best source of necrotic crafting materials. Attrition dungeons reward sustain-focused builds. All classes can bring radiant consumables for the undead-heavy enemies. Order of the Dawn reputation. Strong Logging and Herbalism.
Stormhaven Plateau
- Environment: Windswept highlands, perpetual thunderstorms, lightning-struck ruins, sky temples, floating rock formations
- Hub Town: Tempest Hold — a fortress built to withstand constant storms
- Gathering Emphasis: Mining (storm iron, charged crystals, sky gems), Logging (stormwood, lightning-split timber)
- Secondary Gathering: Herbalism (storm thistle, wind sage, charged petals)
- Dungeon Style: Speed/Initiative — timed encounters, fast enemies, ambush-heavy. Initiative order matters more here than anywhere else. Enemies strike first if you’re slow. Quick skill queues and Speed-focused builds dominate, but any class can invest in initiative gear and consumables.
- Enemy Families: Air elementals, storm harpies, lightning drakes, thunder bears, sky raiders, wind wraiths
- Salvage Focus: Charged crystals (Enchanting), storm iron (Blacksmithing), wind essence (Alchemy), lightning shards (Jewelcrafting)
- Faction Presence: Arcane Conclave (observatory), Iron Compact (garrison)
- Zone-Specific Rare: Stormheart Crystal — used in lightning-themed Enchanting and Blacksmithing recipes
- Why Go Here: Speed-focused dungeons are ideal for initiative-optimized builds. Unique storm-themed crafting materials found nowhere else. Best source of charged crystals. Arcane Conclave and Iron Compact reputation.
The Tangled Warren
- Environment: Massive underground insect hive, tunnels of chitin and webbing, fungal gardens, brood chambers
- Hub Town: Burrower’s Rest — a settlement carved from abandoned hive chambers
- Gathering Emphasis: Skinning (chitin plates, spider silk, hive wax, insect wings), Herbalism (hive fungi, royal jelly, spore pods)
- Secondary Gathering: Mining (hive-encrusted ores, amber deposits)
- Dungeon Style: Wave/AoE — swarms of smaller enemies with occasional massive boss creatures. Encounters regularly feature 8-15+ enemies at once. AoE damage and crowd control are heavily rewarded. Single-target specialists can still succeed but runs take longer.
- Enemy Families: Giant spiders, hive beetles, wasp swarms, centipedes, brood mothers, myconids, hive queens
- Salvage Focus: Chitin (Blacksmithing armor), silk thread (light armor crafting), venom (Alchemy poisons), amber (Jewelcrafting)
- Faction Presence: Primal Circle (research team), Covenant of Dusk (harvesters)
- Zone-Specific Rare: Royal Chitin — used in unique light and medium armor Blacksmithing recipes
- Why Go Here: Swarm encounters make this the best zone for AoE-focused builds. Best chitin and silk source for armor crafting. Royal Chitin armor recipes are highly sought-after. Primal Circle and Covenant of Dusk reputation.
The Pale Reaches
- Environment: Frozen tundra, glacial caverns, ancient ice fortresses, aurora-lit wastelands, permafrost ruins
- Hub Town: Last Light — humanity’s northernmost settlement
- Gathering Emphasis: Mining (adamantine, frost gems, glacial iron), Logging (frostwood, petrified ancient wood)
- Secondary Gathering: Skinning (frost beast pelts, ice drake scales, mammoth hide)
- Dungeon Style: Boss-focused — fewer encounters per dungeon but each is a significant challenge. Multi-phase boss fights, powerful elites, and raid-caliber encounters even in solo/party content. Rewards single-target damage optimization, tank/healer coordination, and learning boss patterns over multiple runs.
- Enemy Families: Frost giants, white dragons, ice elementals, revenants, undead armies, liches, frost wolves
- Salvage Focus: Adamantine scraps (Blacksmithing), frost essence (Enchanting), dragon parts (all professions), lich dust (Alchemy)
- Faction Presence: Order of the Dawn (full hub — Sunspire Cathedral), Covenant of Dusk (hidden presence)
- Zone-Specific Rare: Frozen Aether — used in legendary Enchanting and Alchemy recipes
- Why Go Here: Adamantine mining — the premier Blacksmithing material. Boss-heavy dungeons drop the best individual item rewards. Cold resistance gear and consumables are useful here. Order of the Dawn reputation.
The Spire
- Environment: Massive magical tower, reality-warping floors, planar rifts, arcane laboratories, impossible geometry
- Hub Town: The Spire Atrium (Arcane Conclave headquarters)
- Gathering Emphasis: Herbalism (planar herbs, aether bloom, void spores), Mining (arcane crystals, planar ore)
- Secondary Gathering: Skinning (construct components, extraplanar hide)
- Dungeon Style: Puzzle/Mechanic — each floor has unique rules (reversed gravity, phase-shifting walls, mana-draining fields, temporal loops). Adaptability and diverse skill queues are rewarded. Procedurally varied floors mean no two runs are identical. Logic checks are common but not mandatory — brute force is always an option, just less efficient.
- Enemy Families: Arcane constructs, displaced elementals, rogue mages, reality aberrations, planar beings, clockwork guardians
- Salvage Focus: Magical essence (premium grade — Enchanting), arcane components (all magical crafting), construct parts (Blacksmithing, Jewelcrafting)
- Faction Presence: Arcane Conclave (full hub), Primal Circle (opposition research team)
- Zone-Specific Rare: Aether Core — used in legendary Enchanting and Jewelcrafting recipes
- Why Go Here: Best source of magical essence and arcane crafting materials. Puzzle-heavy dungeons reward adaptable builds. Procedural floor variation keeps runs fresh forever. Arcane Conclave reputation.
The Sunken Kingdoms
- Environment: Submerged ruins of a pre-Sundering civilization, air-pocket cities, deep-sea trenches, coral palaces, abyssal vents
- Hub Town: Depthgate — an air-domed settlement at the ocean floor
- Gathering Emphasis: Skinning (deep-sea leviathan hide, abyssal chitin, kraken ink), Mining (deep-sea gems, pressure-forged ore, abyssal metal)
- Secondary Gathering: Herbalism (abyssal kelp, deep coral, pressure bloom)
- Dungeon Style: Exploration (deep) — massive non-linear dungeon maps with multiple paths, hidden areas, secret boss encounters, and environmental storytelling. Similar to The Shattered Coast’s exploration style but larger-scale and more dangerous. High Perception, Investigation, and Luck characters find significantly more content per run.
- Enemy Families: Deep-sea leviathans, abyssal horrors, merfolk warlords, pressure elementals, ancient guardians, coral golems
- Salvage Focus: Abyssal materials (all professions — unique tier), leviathan parts (Alchemy, Cooking), deep gems (Jewelcrafting premium)
- Faction Presence: Merchant Consortium (deep trade route), Shadow Court (treasure hunters)
- Zone-Specific Rare: Depthstone — used in legendary Blacksmithing and Jewelcrafting recipes
- Why Go Here: Unique deep-sea materials used in crafting recipes found nowhere else. Exploration dungeons yield the most total loot per run for thorough players. Merchant Consortium and Shadow Court reputation.
The Blightmoor
- Environment: Corrupted swampland, toxic fog, mutated landscapes, ruined laboratories, quarantine walls
- Hub Town: Holdfast — a quarantined fortress on the moor’s edge
- Gathering Emphasis: Herbalism (blight herbs, toxic bloom, mutation spores, purified extracts), Skinning (mutant hides, toxic chitin, aberrant tissue)
- Secondary Gathering: Mining (corrupted ore, blight crystals)
- Dungeon Style: Attrition/Condition — enemies apply stacking debuffs, environmental conditions are constant (poison, disease, exhaustion, mutation). Resource management and condition resistance define success. The longest average dungeon runs in the game. Similar to The Bonewood’s attrition style but with environmental hazards layered on top.
- Enemy Families: Mutant beasts, plague spreaders, aberrations, toxic elementals, hive minds, blight cultists
- Salvage Focus: Toxic components (Alchemy — powerful poisons and antidotes), mutation catalysts (Enchanting), blight-resistant materials (Blacksmithing specialty armor)
- Faction Presence: Primal Circle (restoration effort), Iron Compact (containment force)
- Zone-Specific Rare: Purified Blight Essence — used in the most powerful Alchemy recipes and condition-resistance Enchanting
- Why Go Here: Best source of Alchemy materials for poisons and antidotes. Condition-heavy dungeons reward Fortitude-stacked builds and condition-resistance gear. Longest dungeons mean highest total XP per run for characters who can survive. Primal Circle and Iron Compact reputation.
The Rift
- Environment: Fractured reality, shifting planar landscapes, fragments of the Sundering’s epicenter
- Hub Town: Riftwatch — a stabilized platform at the Rift’s edge, maintained by all factions
- Gathering Emphasis: All four gathering professions available at all tiers. Nodes are randomized per visit.
- Dungeon Style: Fully procedural — every run generates a unique dungeon with randomized environments, enemy combinations, trap types, and boss mechanics. Players can add optional difficulty modifiers for better rewards. No two runs are ever the same.
- Enemy Families: Everything — the Rift pulls enemies from all zones. Any enemy family, any damage type, any mechanic.
- Salvage Focus: All material types, plus Rift-exclusive components used in legendary crafting
- Faction Presence: All factions maintain a presence. Rift runs grant reputation with a faction of the player’s choice.
- Zone-Specific Rare: Aethershard — the universal legendary crafting material, used in every top-tier recipe
- Why Go Here: The ultimate variety zone. Procedural generation means infinite replayability. All gathering and all faction reputation available. Optional difficulty modifiers for competitive play. Leaderboards track deepest Rift clears.
The Crucible
- Environment: An ancient arena complex, built before the Sundering, reactivated by unknown forces
- Hub Town: Uses Riftwatch as hub (shared with The Rift)
- Gathering Emphasis: None — combat trophies only
- Dungeon Style: Pure combat gauntlet — no traps, no puzzles, no environmental hazards. Just increasingly difficult fights. Wave-based with leaderboard rankings. Curated encounters designed to test specific combat scenarios — single boss, swarm, mixed, anti-magic, anti-melee, etc.
- Enemy Families: Curated from all zones, arranged in escalating difficulty tiers
- Salvage Focus: High-value combat trophies tradeable for faction tokens or rare crafting materials
- Faction Presence: All factions. Crucible rankings affect faction warfare standings.
- Why Go Here: Pure combat optimization testing. Best way to benchmark builds against other players. Leaderboard competition. Efficient faction token farming through combat trophies.
Zone Summary Table
| Zone | Environment | Primary Gathering | Dungeon Style | Factions |
|---|---|---|---|---|
| Thornvale | Farmland/Forest | Herbalism, Logging | Balanced | Iron Compact, Merchant Consortium |
| The Shallows | Coastal Wetlands | Skinning, Mining | Hazard-heavy | Shadow Court, Merchant Consortium |
| Dusthollow | Desert Badlands | Mining, Skinning | Combat-dense | Merchant Consortium, Covenant of Dusk |
| The Ironmarch | Mountains/Caverns | Mining, Logging | Trap-heavy | Iron Compact, Shadow Court |
| The Verdant Deep | Ancient Forest | Herbalism, Logging | Decision-heavy | Primal Circle, Arcane Conclave |
| The Drowned Tunnels | Underground Rivers | Mining, Herbalism | Endurance/Hazard | Arcane Conclave, Shadow Court |
| The Ashlands | Volcanic Wasteland | Mining, Skinning | High-damage | Covenant of Dusk, Order of the Dawn |
| The Shattered Coast | Coastal Ruins | Skinning, Herbalism | Exploration | Merchant Consortium, Shadow Court |
| The Bonewood | Dead Forest | Logging, Herbalism | Attrition | Order of the Dawn, Primal Circle |
| Stormhaven Plateau | Thunderstorm Highlands | Mining, Logging | Speed/Initiative | Arcane Conclave, Iron Compact |
| The Tangled Warren | Underground Hive | Skinning, Herbalism | Wave/AoE | Primal Circle, Covenant of Dusk |
| The Pale Reaches | Frozen Tundra | Mining, Logging | Boss-focused | Order of the Dawn, Covenant of Dusk |
| The Spire | Magical Tower | Herbalism, Mining | Puzzle/Mechanic | Arcane Conclave, Primal Circle |
| The Sunken Kingdoms | Submerged Ruins | Skinning, Mining | Exploration (deep) | Merchant Consortium, Shadow Court |
| The Blightmoor | Toxic Swampland | Herbalism, Skinning | Attrition/Condition | Primal Circle, Iron Compact |
| The Rift | Fractured Reality | All | Procedural | All (player choice) |
| The Crucible | Ancient Arena | None (trophies) | Pure Combat | All (rankings) |
Gathering Distribution
Each gathering profession has high-yield nodes in specific zones. Since all zones scale to all levels, this drives player movement based on what materials they need, not what level they are.
| Profession | Best Zones |
|---|---|
| Mining | The Ironmarch (best overall), Dusthollow, The Ashlands, Stormhaven Plateau, The Pale Reaches, The Sunken Kingdoms |
| Herbalism | The Verdant Deep (best overall), Thornvale, The Drowned Tunnels, The Bonewood, The Spire, The Blightmoor |
| Logging | Thornvale (best overall), The Ironmarch, The Verdant Deep, The Bonewood, Stormhaven Plateau, The Pale Reaches |
| Skinning | The Shallows (best overall), Dusthollow, The Shattered Coast, The Tangled Warren, The Sunken Kingdoms, The Blightmoor |
Zone-Specific Materials
Every zone produces a unique rare material that cannot be found elsewhere. These materials are used in specific crafting recipes, guaranteeing that every zone stays relevant and populated regardless of player level.
| Zone | Rare Material | Primary Crafting Use |
|---|---|---|
| Thornvale | Thornvale Sap | Alchemy, Cooking (healing/buff consumables) |
| The Shallows | Tidestone | Jewelcrafting, Enchanting (water resistance) |
| Dusthollow | Sunite Glass | Jewelcrafting, Enchanting (light/fire) |
| The Ironmarch | Deepvein Crystal | Jewelcrafting, Enchanting |
| The Verdant Deep | Feybloom Pollen | Alchemy, Cooking (powerful buffs) |
| The Drowned Tunnels | Luminous Spore | Alchemy, Enchanting (light/detection) |
| The Ashlands | Molten Core Fragment | Blacksmithing, Enchanting (fire) |
| The Shattered Coast | Abyssal Pearl | Jewelcrafting, Enchanting (water/luck) |
| The Bonewood | Soulstone Shard | Enchanting (necrotic/radiant) |
| Stormhaven Plateau | Stormheart Crystal | Enchanting, Blacksmithing (lightning) |
| The Tangled Warren | Royal Chitin | Blacksmithing (light/medium armor) |
| The Pale Reaches | Frozen Aether | Enchanting, Alchemy (legendary) |
| The Spire | Aether Core | Enchanting, Jewelcrafting (legendary) |
| The Sunken Kingdoms | Depthstone | Blacksmithing, Jewelcrafting (legendary) |
| The Blightmoor | Purified Blight Essence | Alchemy, Enchanting (condition resistance) |
| The Rift | Aethershard | Universal legendary crafting material |
Faction Distribution
Each faction has presence across multiple zones, ensuring players can build reputation with their chosen faction regardless of where they prefer to play.
| Faction | Zone Presence |
|---|---|
| Iron Compact | Thornvale, The Ironmarch, Stormhaven Plateau, The Blightmoor, The Rift |
| Shadow Court | The Shallows, The Ironmarch, The Drowned Tunnels, The Shattered Coast, The Sunken Kingdoms, The Rift |
| Arcane Conclave | The Verdant Deep, The Drowned Tunnels, Stormhaven Plateau, The Spire, The Rift |
| Primal Circle | The Verdant Deep, The Bonewood, The Tangled Warren, The Spire, The Blightmoor, The Rift |
| Order of the Dawn | The Ashlands, The Bonewood, The Pale Reaches, The Rift |
| Covenant of Dusk | Dusthollow, The Ashlands, The Tangled Warren, The Pale Reaches, The Rift |
| Merchant Consortium | Thornvale, The Shallows, Dusthollow, The Shattered Coast, The Sunken Kingdoms, The Rift |
Dungeon Style Distribution
Different dungeon styles reward different playstyles, ensuring no single build dominates all content. Players are incentivized to either specialize in zones that match their build or diversify their character to handle multiple styles.
| Dungeon Style | Zones | What It Rewards |
|---|---|---|
| Balanced | Thornvale | All builds equally — good for generalists |
| Hazard-heavy | The Shallows, The Drowned Tunnels | High Fortitude, supply management, preparation |
| Combat-dense | Dusthollow, The Tangled Warren | AoE damage, sustained resources, offensive builds |
| Trap-heavy | The Ironmarch | Logic/Speed, Shade class bonus, trap gear |
| Decision-heavy | The Verdant Deep | Diverse attributes, Presence, Speed, adaptability |
| Endurance/Hazard | The Drowned Tunnels | Fortitude, supplies, endurance builds |
| High-damage | The Ashlands | Durability, fire resistance, burst healing |
| Exploration | The Shattered Coast, The Sunken Kingdoms | Perception, Investigation, Luck — bonus loot |
| Attrition | The Bonewood, The Blightmoor | Sustain, condition resistance, healing, rationing |
| Speed/Initiative | Stormhaven Plateau | Speed, initiative optimization, fast queues |
| Wave/AoE | The Tangled Warren | AoE damage, crowd control |
| Boss-focused | The Pale Reaches | Single-target DPS, tank/healer coordination |
| Puzzle/Mechanic | The Spire | Logic, adaptability, diverse skill queues |
| Procedural | The Rift | Broad preparation, adaptability |
| Pure Combat | The Crucible | Optimized combat builds, raw DPS/survivability |
The Sundering (Core Lore)
The Sundering happened approximately 200 years before the game’s present day. Key facts:
- The old empire attempted a massive magical working — a spell to grant the emperor godhood
- The spell failed catastrophically, tearing reality apart
- The physical world was fractured — mountains rose, coastlines shifted, underground caverns opened
- The magical fallout created dungeons: places where reality is thin, monsters spawn, and treasures accumulate
- The factions emerged in the aftermath, each with a different philosophy about how to rebuild
- No one fully understands what happened — fragments of truth are scattered across the world’s dungeons
Why Dungeons Exist
Dungeons in Aethermere aren’t just underground caves. They’re Sundering scars — places where the magical catastrophe left permanent damage to reality. This is why they:
- Generate monsters (reality is thin, things slip through)
- Contain treasure (the Sundering displaced objects across time and space)
- Reset and repopulate (they’re self-healing wounds in reality)
- Scale in danger (the deeper the scar, the worse the damage to reality)
This provides a lore justification for the game’s repeatable, procedurally generated, level-scaling dungeon content. A level 5 dungeon and a level 50 dungeon in the same zone are simply different depths of the same Sundering scar.
Key NPCs
NPCs serve as quest board operators, vendors, faction representatives, and crafting trainers. They are functional game interfaces with personality flavor. They don’t have personal storylines — they exist to make each hub town feel distinct.
Recurring NPC Roles (Per Hub Town)
- Quest Board Operator — manages the local quest board, provides context on available runs
- General Vendor — buys/sells common supplies (rations, torches, potions)
- Faction Representative(s) — one per faction present in the zone; offers faction dailies and sells faction vendor items
- Crafting Trainer — teaches recipes appropriate to the zone’s gathering focus
- Gathering Dispatcher — manages gathering expeditions to the zone’s nodes
- Gear Vendor — sells level-appropriate equipment
World Design Principles
- Gameplay first. Every zone exists to serve a game system. No scenery-only areas.
- All zones, all levels. Every zone has content for every character level. Quest boards generate level-appropriate content. Gathering scales with skill. No zone is ever “too low” or “too high.”
- Player choice, not class funneling. All zones are useful for all classes. Players choose zones based on what resources and activities they want, not what class they play.
- Spread the population. Gathering emphasis, faction presence, dungeon style, and zone-specific rare materials ensure no single zone is universally “best.” Different goals send players to different places.
- Unlock through play, not level. Zone unlocking is based on total dungeon runs completed, not character level. Active players unlock zones faster regardless of their character’s level.
- No dead zones. Zone-specific rare materials, scaling content, and faction dailies keep every zone populated and relevant at all stages of the game.
- Procedural content. Quests are generated tasks — kill targets, gather items, clear dungeons, complete bounties. Names and flavor text are procedural. Players engage with the systems, not a plot.
- Room to expand. The Sundering fractured the world into many pieces — new zones can always be added without disrupting existing progression.
Idle and Time Mechanics
Overview
Delve is fundamentally an idle game — the core activities (dungeon runs, crafting, gathering) happen on real-world timers. The design challenge is making the passage of time feel rewarding, not punishing. Players should look forward to checking their results, not feel stressed about falling behind.
Core Idle Philosophy
Time is the universal currency. Everyone — free players and paying players alike — waits for dungeon runs to resolve. This creates a level playing field where the differentiator is how well you prepare, not how much you play.
Key Principles
- Offline progress is real progress. Your character advances whether you’re watching or not.
- No penalty for absence. Missing a day (or a week) should never feel punishing. Daily bonuses are additive, not restorative.
- Active play is a bonus, not a requirement. Players who log in frequently can optimize more, but the game doesn’t punish those who check in once a day.
- Notifications inform, not nag. Alerts are helpful (“Your run is done!”), not guilt-inducing (“You haven’t logged in today!”).
Time-Gated Activities
Dungeon Runs
- Duration: 15 minutes to 12 hours depending on quest type
- Cannot be sped up with real money (core design principle)
- One active run per character at a time
- Character is unavailable for other activities while on a run
- Run completes regardless of whether the player is online
Crafting
- Duration: 15 minutes to 12 hours depending on recipe tier
- 50% faster for Patron subscribers (see 14-monetization.md)
- One active craft per profession at a time
- Runs in parallel with dungeon runs — character can craft while dungeon running
- Completes offline
Gathering
- Duration: 1 to 12 hours
- 50% faster for Patron subscribers
- One active task per gathering profession at a time
- Runs in parallel with everything else
- Completes offline
Marketplace Listings
- Duration: 48 hours per listing
- Auto-expires if not sold
- Revenue collected via mail upon sale (happens offline)
Raid Lobbies
- Countdown timer to scheduled start (set by lobby creator)
- Players must configure and “Ready” before the start time
- Raid starts at the scheduled time whether all slots are filled or not (unfilled slots can optionally be auto-filled from public queue)
Activity Parallelism
At any given time, a character can have ALL of the following running simultaneously:
| Activity | Slots | Runs in Parallel? |
|---|---|---|
| Dungeon run | 1 | Yes (but character is “away”) |
| Crafting (per profession) | 1 each | Yes |
| Gathering (per profession) | 1 each | Yes |
| Marketplace listings | 20 | Yes |
| Raid lobby (waiting) | 1 | Yes |
This means even during a long dungeon run, players can:
- Queue crafting recipes
- Start gathering expeditions
- Manage marketplace listings
- Join a raid lobby for a future run
- Chat with guild members
- Manage a second character (if unlocked)
Offline Progress
What Happens When You Log Off
Everything continues. Specifically:
- Active dungeon run resolves on schedule
- Active crafting completes on schedule
- Active gathering completes on schedule
- Marketplace items can sell (revenue held in mail)
- Raid lobbies count down (notifications sent if applicable)
What Doesn’t Happen Offline
- No automatic re-queuing (the player must choose the next activity)
- No automatic selling or salvaging
- No automatic quest selection
- No passive XP or gold generation (you must be doing something)
This means the game requires brief check-ins to remain active — but those check-ins can be as short as 2 minutes.
Idle Efficiency
The game tracks how efficiently you use your character’s time:
- Time spent “idle” (no active task) is wasted potential
- The UI shows a gentle indicator: “Your character has been idle for 3 hours. Ready for a new adventure?”
- This is informational, not punitive — no XP decay, no penalty
Active Play Bonuses
Players who are online and actively playing get minor bonuses that reward engagement without punishing absence:
Active Player Benefits
- Real-time quest board browsing — Spot high-value quests as they appear on refresh
- Marketplace sniping — Buy underpriced items before others notice
- Immediate re-queuing — Start a new run the moment the previous one finishes (no idle time)
- Social interaction — Chat, guild coordination, party formation
- Manual optimization — Review combat logs between runs and fine-tune skill queue configuration
- Daily bonus collection — First-run bonus, daily bounties (available for 24 hours, so very flexible)
What Active Play Does NOT Provide
- No bonus XP for being online
- No bonus loot for being online
- No faster dungeon completion for being online
- No advantages in combat for being online
- No “energy” system that requires regular play to maintain
Notification System
Notifications are how the game communicates with idle players. They should be helpful and respectful.
Notification Types
| Notification | Trigger | Priority |
|---|---|---|
| “Dungeon run complete!” | Run finishes | High |
| “Crafting complete!” | Craft finishes | Medium |
| “Gathering complete!” | Gathering finishes | Medium |
| “Item sold on marketplace!” | Auction sells | Medium |
| “Raid starting in 1 hour” | Lobby countdown | High |
| “Raid starting in 15 minutes” | Lobby countdown | High |
| “Your raid is complete!” | Raid finishes | High |
| “Quest board has refreshed” | 6-hour refresh | Low |
| “New mail received” | Mail arrival | Low |
| “Guild war declared!” | Guild event | Medium |
Notification Settings
Players can customize:
- Which notifications to receive
- Push notifications (mobile/browser) vs. in-game only
- “Do not disturb” hours
- Batched notifications (send a summary instead of individual alerts)
What We Never Notify
- “You haven’t logged in today!” — No guilt notifications
- “Your energy is full!” — No energy system
- “Limited time offer!” — No predatory urgency
- “Your character is idle!” — Informational in-game only, never a push notification
Anti-Burnout Design
The Problem with Idle Games
Many idle games create a treadmill where players feel compelled to check in constantly or fall behind. Delve explicitly designs against this.
Anti-Burnout Mechanisms
No Stamina/Energy System
- There is no “energy” that regenerates over time and caps out
- There is no “daily limit” on dungeon runs (limited by real-time duration, not artificial caps)
- A player who checks in once per day and a player who checks in ten times get the same dungeon run quality
No Streak Bonuses
- Daily first-run bonus is available every day but there is no “consecutive day” multiplier
- Missing a day doesn’t break a streak or lose accumulated bonus
- This prevents the psychology of “I have to log in or lose my 30-day streak”
Catch-Up Friendly
- A player who takes a week off can jump right back in
- No accumulated disadvantage beyond time not spent progressing
- Rested bonus: After 24+ hours offline, the first dungeon run gives +25% XP (caps at 3 days of rest for +50%). Rewards returning, not punishes leaving.
Reasonable Time Gates
- The longest activity in the game is a 12-hour raid
- Most content is 1-4 hours
- Players are never asked to wait more than half a day for a single result
Multiple Progression Paths
- If a player is tired of dungeon runs, they can focus on crafting, gathering, PVP, faction reputation, or social activities
- Burnout on one system doesn’t mean burnout on the whole game
Time Zone Considerations
Server Time
- All timed events (raid schedules, quest board refreshes, daily resets) use server time
- Server time is prominently displayed in the UI
- Daily reset: midnight server time
Raid Scheduling
- Raid lobbies show start times in both server time AND the player’s local time
- Guilds with members across time zones can schedule multiple raid windows
- The timed lobby system inherently accommodates time zones — you prepare whenever you’re online, the raid starts at the set time
Monetization
Overview
Delve uses a Free-to-Play with Optional Subscription model. There is no premium currency. There are no loot boxes. There are no cosmetic microtransactions. The game makes money two ways:
- A cheap monthly subscription that makes time-based activities 50% faster
- Small permanent purchases for account upgrades (character slots, bank space)
That’s it. The game is designed to be fully playable — and fully competitive — without spending a cent. Subscribers don’t hit harder, find better loot, or gain more XP. They wait less.
Design Philosophy
Why No Premium Currency
Premium currencies (gems, crystals, coins) exist to obscure the real cost of purchases. When you buy 1,100 gems for $9.99 and a skin costs 950 gems, it’s deliberately hard to calculate the real price — and you’re left with 150 gems that are useless without buying more. This is manipulative by design.
Delve charges real money for real things at real prices. $5/month for a subscription. $2 for a character slot. No conversion rates, no leftover currency, no “just 50 more gems” psychology.
Why No Cosmetics
Delve is an async idle game. You experience combat through text logs, not 3D animations. Your character is a portrait and a stat sheet, not a rendered model walking through a dungeon. Selling armor skins or weapon effects for a character you never watch fight would be dishonest — we’d be charging for something that has almost no visible impact on the player experience.
Cosmetics in Delve (titles, portrait frames, profile badges) are earned through gameplay — achievements, faction reputation, PVP seasons, and seasonal events. They represent accomplishment, not spending.
The Core Principle
A subscriber and a free player in the same dungeon run have the same chance of success, the same loot tables, and the same XP. The subscriber just got there faster.
Delve Patron Subscription
Price: $5.00 / month
(or $48/year — 2 months free with annual plan)
What Subscribers Get
50% faster time-based activities:
| Activity | Free Player | Subscriber |
|---|---|---|
| Bounty (15-45 min) | 15-45 min | 10-30 min |
| Questline (1-3 hours) | 1-3 hours | 40 min - 2 hours |
| Dungeon Crawl (3-8 hours) | 3-8 hours | 2-5.5 hours |
| Raid (4-12 hours) | 4-12 hours | 2.5-8 hours |
| Crafting (15 min - 12 hours) | 15 min - 12 hours | 10 min - 8 hours |
| Gathering (1-12 hours) | 1-12 hours | 40 min - 8 hours |
| Post-death recovery timer | 15 min - 2 hours | 10 min - 1.5 hours |
Seasonal Pass (Premium Track):
During active seasons (~3 months each, 4 per year), Patron subscribers automatically unlock the Premium Season Track — bonus seasonal content and a reward progression track. See the Seasonal Pass section below for full details.
That’s it. No bonus XP, no bonus gold, no bonus loot, no better drop rates, no combat advantages, no extra skill queue slots, no bonus stats.
What 50% Faster Means in Practice
- A free player who checks in twice a day (morning and evening) can complete roughly 2 questlines or 1 dungeon crawl per day
- A subscriber on the same schedule can complete 3 questlines or 1.5 dungeon crawls per day
- Over a week, the subscriber has done roughly 50% more content — not because they’re stronger, but because each run finishes sooner
- Over months, subscribers reach endgame faster. But a free player who reaches endgame is exactly as powerful as a subscriber who reached it faster.
Subscription is NOT Required For:
- Any gameplay content (all dungeons, raids, quests, PVP — free)
- Competitive PVP (arena, guild wars, faction warfare — all free, no time advantage in PVP)
- Trading on the marketplace
- Joining guilds, parties, raids
- Crafting any recipe or gathering any material
- Any feature in the game
PVP and Subscription Fairness
Arena PVP matches resolve instantly (no time duration — see 10-pvp-system.md), so the subscription speed bonus has zero impact on PVP. A subscriber and a free player in the arena are on perfectly equal footing.
Subscriber Status Display
- A small, subtle Patron badge next to the subscriber’s name in chat and profile
- This is the ONLY visual indicator. No glowing effects, no special colors, no “premium” aesthetic
- The badge is informational, not aspirational — it doesn’t say “I’m better,” it says “I support the game”
Seasonal Pass
How Seasons Work
Delve runs 4 seasons per year (~3 months each), each with a theme, unique seasonal dungeons, new enemies, and seasonal mechanics. See 15-progression-hooks-and-retention.md for full seasonal content details.
Two Tracks: Free and Premium
Every season has two parallel reward tracks:
Free Track (all players):
- Access to all seasonal dungeons and content (gameplay is never gated)
- Basic seasonal reward track with milestones: gold, common/uncommon crafting materials, 1-2 seasonal titles
- Seasonal leaderboard participation
- 15 tiers of rewards earned through seasonal play
Premium Track (Patron subscribers only):
- Everything in the Free Track, PLUS:
- Bonus seasonal challenge dungeons — harder encounters with unique modifiers that only appear during the season
- Extended reward track — 30 additional tiers beyond the free track’s 15
- Premium track rewards include:
- Rare and Very Rare crafting materials
- Exclusive seasonal portrait and profile frame (earned, not bought — you must play through the track)
- Exclusive seasonal title
- Bonus gold at milestones
- Rare salvage materials (Rare Cores, Prismatic Essences)
- A guaranteed Named Item at the final tier (seasonal-exclusive, with the usual random bonus rolls)
Seasonal Challenge Dungeons (Premium Track)
These are the premium track’s main content offering — unique dungeons that test different builds and strategies:
| Season | Challenge Dungeon | Unique Mechanic |
|---|---|---|
| Season of Flame | The Erupting Caldera | Heat meter — the longer you’re in, the more fire damage you take. Speed is survival. |
| Season of Frost | The Frozen Abyss | Frost accumulation — characters gradually slow (initiative penalties) unless they find warmth sources. |
| Season of Shadow | The Harvest of Souls | Soul economy — defeated enemies drop souls spent on temporary buffs OR saved for tier rewards. |
| Season of Growth | The Awakening Grove | Growth — plant seeds at rest points that bloom into useful effects in later encounters. |
Challenge dungeons award Seasonal Tokens — a currency spent to progress through the premium reward track. Regular seasonal dungeons (free track) also award Seasonal Tokens, but challenge dungeons award more.
Track Progression
| Tier | Free Track Reward | Premium Track Reward |
|---|---|---|
| 1-5 | Gold, Common Fragments | Gold, Uncommon Shards |
| 6-10 | Uncommon Shards, seasonal title | Rare Cores, seasonal portrait |
| 11-15 | Rare Core, seasonal profile frame | Prismatic Essence, seasonal profile frame (animated) |
| 16-20 | — (Free track ends at 15) | Rare Cores, bonus gold, seasonal title (exclusive) |
| 21-25 | — | Prismatic Essences, Legendary Spark |
| 26-30 | — | Named Seasonal Weapon or Armor (Legendary, random rolls) |
Seasonal Pass Principles
- No separate purchase. The premium track is included in the Patron subscription. If you’re a subscriber, you have it.
- All gameplay content is still free. Non-subscribers can play the seasonal dungeons and earn free track rewards. The premium track adds BONUS challenges and better rewards, not access gates.
- Rewards are earned, not given. Having the premium track doesn’t hand you anything — you must play through the tiers. A subscriber who doesn’t play the season gets nothing.
- Seasonal exclusivity. Premium track rewards (the exclusive portrait, title, and named item) are only available during that season. This creates healthy FOMO tied to gameplay effort, not spending.
- No catch-up purchases. You can’t buy tiers. If you didn’t finish the track, you didn’t finish it. This prevents the “I can just buy my way to tier 30” problem.
Why This Works
- Subscribers get quarterly fresh content that justifies ongoing subscription — not just speed, but actual new challenges and rewards
- Free players get the seasonal dungeons and basic rewards — they’re not excluded from the fun
- The premium track gives subscribers something to work TOWARD, increasing engagement and retention during seasons
- No additional purchase required — the subscription covers everything
Permanent Purchases
Small, one-time purchases for account-level upgrades. Priced in real currency. No bundles, no “deals,” no urgency tactics.
Character Slots
| Purchase | Price | Notes |
|---|---|---|
| 2nd character slot | $2.00 | First slot is free |
| 3rd character slot | $2.00 | |
| 4th character slot | $2.00 | |
| 5th character slot | $2.00 | |
| 6th character slot (max) | $2.00 | Hard cap — no one needs more than 6 |
Total for all slots: $10.00. One-time cost, permanent, account-wide.
Each character is fully independent (own inventory, quests, progression). Characters on the same account can mail items/gold to each other (with the standard transfer fee as a gold sink).
Storage Upgrades
| Purchase | Price | Notes |
|---|---|---|
| +25 bank slots | $1.00 | Per character, stackable up to +100 (4 purchases max) |
| +50 material bank slots | $1.00 | Per character, stackable up to +200 (4 purchases max) |
| +5 marketplace listing slots | $1.00 | Per account, stackable up to +20 (4 purchases max) |
Maximum storage spend per character: $8.00 (4 bank + 4 material bank). This is a convenience upgrade — the base storage is sufficient for normal play but tight for serious crafters and traders.
Quality of Life
| Purchase | Price | Notes |
|---|---|---|
| Name change | $1.00 | Change character name |
| Appearance reset | $1.00 | Change character portrait |
What is NEVER Sold
This list defines the boundary between “acceptable monetization” and “pay-to-win.” These items will never be available for real money under any circumstances:
- Gear, weapons, or items with combat stats
- XP boosts or level skips
- Gold or gold boosts
- Loot or loot quality boosts
- Drop rate boosts
- Combat advantages of any kind (bonus stats, bonus skills, bonus queue slots)
- Dungeon or quest unlocks (all content is free)
- Reforging materials or shortcuts
- Crafting materials
- Premium currency (doesn’t exist)
- Loot boxes or randomized purchases (don’t exist)
- Cosmetic items (earned through gameplay only)
- Season pass as a separate purchase (premium track is included in Patron subscription)
- “Convenience” items that are actually power (no stat potions, no temporary buffs)
Revenue Model Analysis
Revenue Sources
| Source | Type | Est. Revenue Per User |
|---|---|---|
| Patron Subscription | Recurring ($5/mo or $48/yr) | Primary revenue. Includes seasonal premium track. Target: 15-25% of active players subscribe. |
| Character Slots | One-time ($2 each) | Secondary. Most players buy 1-2 extra slots over their lifetime ($2-4). |
| Storage Upgrades | One-time ($1 each) | Tertiary. Serious players buy most; casual players buy few. |
| Name/Appearance | One-time ($1 each) | Minimal. Rare purchases. |
Target ARPU (Average Revenue Per User)
- Free players (75-85% of users): $0/month — perfectly fine, they populate the world and marketplace
- Occasional spenders (10-15%): $2-10 one-time for slots/storage, no recurring
- Subscribers (10-20%): $5/month ongoing
Why This Works Financially
- Low barrier to subscription: $5/month is impulse-purchase territory. A cup of coffee. Low enough that players don’t agonize over it.
- Clear value proposition: “Your stuff finishes 50% faster” is easy to understand and easy to justify.
- No whale dependency: The maximum a player can spend is ~$5/month + ~$20 lifetime for all upgrades. There are no whales. Revenue comes from a broad base of modest subscribers, not a tiny number of big spenders. This is healthier and more sustainable.
- Retention-driven: Since revenue comes from subscriptions, the game is incentivized to keep players engaged and happy long-term — not to create friction that drives impulse purchases.
- Word of mouth: Fair monetization generates positive word of mouth. “This game doesn’t try to screw you” is a marketing advantage.
Break-Even Considerations
- Server costs, development, and maintenance must be covered by subscription + purchase revenue
- The game needs a critical mass of subscribers (exact number depends on infrastructure costs)
- If the game is good and retention is high, the subscription model is more sustainable than a whale-dependent model
- If the game needs more revenue, the answer is more content that drives retention, not more things to sell
Anti-Predatory Design
No Dark Patterns
- No “limited time offers” that create urgency
- No “first purchase discount” that normalizes spending
- No “are you sure?” popups that guilt-trip players who try to unsubscribe
- No “daily login streak” that punishes absence (see 13-idle-and-time-mechanics.md)
- No notifications about what subscribers are getting that you’re not (“FOMO notifications”)
No Hidden Costs
- The subscription page shows exactly what you get and what you don’t get
- One-time purchases show the exact price in real currency
- No auto-renewal without clear consent and easy cancellation
- Subscription can be cancelled instantly from the account page — no “talk to support” hoops
Spending Transparency
- Account settings show total lifetime spend
- Optional monthly spending cap (self-imposed, can be changed)
- Parental controls: require password for any purchase, set spending limits
- No purchases in the middle of gameplay flow (no “buy now to finish faster” popups during a dungeon run)
Player Trust Commitment
- If we ever break these principles — if we ever add power for sale, premium currency, or predatory mechanics — we will have failed. The monetization model is a promise to players, not a suggestion. It is as much a part of the game design as the combat system.
Frequently Anticipated Objections
“$5/month isn’t enough revenue.” It is if the game retains players. A game with 100,000 active players at 15% subscription rate generates $75,000/month from subscriptions alone, plus one-time purchases. The key is retention, not extraction.
“50% faster is too strong. It’s pay-to-win.” It’s pay-to-wait-less. In an idle game, time IS the mechanic — so this is the only lever that makes sense. But the subscriber doesn’t get better loot, more XP, or stronger stats. They just get their results sooner. In PVP (which resolves instantly), there is zero subscriber advantage.
“Without cosmetics, where’s the long-term revenue?” Subscriptions ARE long-term revenue. A subscriber paying $5/month for 2 years generates $120 — more than most players spend on cosmetics in other games. And the subscription model incentivizes the developer to keep the game good, not to keep adding cosmetic churn.
“What if you need more money later?” More content. New regions, new dungeons, new raids, new classes, new species — content that retains players and drives subscriptions. The answer to “we need more revenue” is always “make the game better,” never “sell more stuff.”
Progression Hooks and Retention
Overview
Retention in an idle game means giving players a reason to come back — not through guilt or punishment, but through anticipation and reward. Every time a player logs in, something good should be waiting for them, and something exciting should be ahead.
This document covers the systems that keep players engaged beyond the core dungeon loop.
Daily Engagement
Daily First-Run Bonus
- First dungeon run completed each day grants +50% XP and +50% gold for that run
- No streak requirement — available fresh every day regardless of when you last played
- Visible on the quest board: “First Run Bonus Available!”
Daily Bounties
- 3 short bounties refresh every day (separate from the main quest board)
- Completing all 3 grants a Daily Bounty Chest containing:
- Bonus gold (scaling with level)
- Random crafting materials
- Small chance of uncommon+ gear
- Bounties are designed to be completable in 30-60 minutes total
- If not completed, they simply refresh the next day (no loss, no guilt)
Daily Faction Quests
- Each faction offers 3 daily quests (see 11-factions-and-reputation.md)
- Available at Friendly reputation or higher
- Provides faction tokens and reputation
Weekly Engagement
Weekly Challenge Dungeon
- A special dungeon with a modifier that changes each week
- Modifiers create unique gameplay situations:
| Week | Modifier | Effect |
|---|---|---|
| 1 | Fortified | All enemies have +25% HP |
| 2 | Volatile | All enemies explode on death (fire damage in AOE) |
| 3 | Ascetic | No potion use allowed |
| 4 | Speedrun | Bonus rewards for completing under a time threshold |
| 5 | Tyrannical | Boss encounters are significantly harder |
| 6 | Fog of War | Perception checks are harder |
| 7 | Bountiful | All loot drops are +1 rarity tier |
| 8 | Nemesis | A powerful stalker enemy appears at random encounters |
- Completing the weekly challenge grants a Weekly Chest with guaranteed rare+ loot
- Leaderboard for fastest completion times (bragging rights)
Weekly Raid Reset
- Raid lockouts reset every Monday
- Creates a natural weekly rhythm: plan the raid, execute the raid, gear up, repeat
Weekly PVP Cap
- Raid tokens and honor points have a weekly earning cap
- Prevents no-lifing while ensuring consistent rewards for regular play
Achievement System
Achievements are permanent milestones that recognize player accomplishments. They provide long-term goals and showcase dedication.
Achievement Categories
Combat Achievements
- “First Blood” — Complete your first dungeon run
- “Flawless Victory” — Complete a dungeon without taking damage
- “Giant Slayer” — Defeat a boss 5 levels above you
- “Unstoppable” — Complete 100 dungeon runs
- “Living Legend” — Complete 1,000 dungeon runs
- “Death’s Door” — Survive a dungeon with less than 5% HP remaining
- “One Shot” — Kill a boss in a single round
Progression Achievements
- “Level 25 / 50” — Reach level milestones
- “Master of Arms” — Equip a full set of rare or better gear
- “Walking Arsenal” — Own 50 unique weapons
- “Paragon 10 / 25 / 50 / 100” — Reach Paragon milestones
Crafting Achievements
- “First Craft” — Craft your first item
- “Master [Profession]” — Reach skill 100 in a crafting profession
- “Grand Master” — Reach skill 100 in all crafting professions
- “Critical Crafter” — Get 10 critical crafts
Social Achievements
- “Guilded” — Join a guild
- “Guild Leader” — Create a guild
- “Raid Ready” — Complete your first raid
- “Social Butterfly” — Add 10 friends
Exploration Achievements
- “Explorer” — Visit all regions
- “Dungeon Crawler” — Complete every dungeon at least once
- “Lore Hunter” — Find all lore fragments in a region
- “Cartographer” — Unlock all hidden rooms in a dungeon
PVP Achievements
- “First Duel” — Complete your first PVP match
- “Gladiator” — Reach Gold rating in arena
- “Legend” — Reach Diamond rating in arena
- “War Hero” — Win a guild war
Collection Achievements
- “Bestiary Complete” — Encounter every enemy type
- “Fashionista” — Own 25 cosmetic items
- “Title Collector” — Earn 10 titles
Achievement Rewards
Each achievement grants one or more of:
- Achievement points (displayed on profile, total is a prestige indicator)
- Title (selected achievements grant unique titles)
- Portrait or profile frame (for exceptional achievements)
- Gold (modest amounts)
- Crafting materials (for high-difficulty achievements)
Achievement Showcase
- Players select up to 5 achievements to display on their public profile
- Creates a “badge” system that communicates playstyle and dedication at a glance
Seasonal Events
Seasonal events are time-limited content that creates excitement and community engagement. Each season has a Free Track (all players) and a Premium Track (Patron subscribers). See 14-monetization.md for full monetization details.
Season Structure
- 4 seasons per year aligned with real-world seasons (~3 months each)
- Each season introduces:
- New seasonal dungeon(s) with unique enemies and mechanics (free for all players)
- Free reward track (15 tiers) — gold, materials, 1-2 seasonal titles
- Premium reward track (30 tiers, Patron subscribers) — rare materials, exclusive portraits, exclusive titles, Named seasonal item at tier 30
- Bonus challenge dungeons (Patron subscribers) — harder seasonal content with unique modifiers
- Seasonal leaderboard
Example Seasons
Season of Flame (Summer)
- Theme: Fire and volcanic activity
- Seasonal dungeon: The Erupting Caldera — fire-themed enemies, lava hazards
- Challenge dungeon (Premium): Inferno Core — heat meter increases fire damage the longer you stay. Speed is survival.
- Unique mechanic: Heat meter
- Free rewards: “Firewalker” title, gold, crafting materials
- Premium rewards: “Flamewarden” exclusive title, ember portrait frame, Named seasonal weapon (fire-themed Legendary)
Season of Frost (Winter)
- Theme: Ice and cold
- Seasonal dungeon: The Frozen Abyss — ice enemies, slippery terrain, visibility hazards
- Challenge dungeon (Premium): The Deep Freeze — frost accumulation gradually slows all skills. Warmth sources are scarce.
- Unique mechanic: Frost accumulation
- Free rewards: “Frostborn” title, gold, crafting materials
- Premium rewards: “Frostkeeper” exclusive title, ice crystal portrait frame, Named seasonal armor (cold resistance Legendary)
Season of Shadow (Autumn)
- Theme: Undead and darkness
- Seasonal dungeon: The Harvest of Souls — undead hordes, necromantic puzzles
- Challenge dungeon (Premium): The Soul Gauntlet — defeated enemies drop souls spent on temporary buffs OR saved for bonus track progress
- Unique mechanic: Soul economy
- Free rewards: “Soulreaper” title, gold, crafting materials
- Premium rewards: “Deathless” exclusive title, spectral portrait frame, Named seasonal weapon (necrotic Legendary)
Season of Growth (Spring)
- Theme: Nature and renewal
- Seasonal dungeon: The Awakening Grove — fey creatures, plant-based hazards, transformation encounters
- Challenge dungeon (Premium): The Verdant Trial — plant seeds at rest points that bloom into effects in later encounters. Strategic planting is key.
- Unique mechanic: Growth and planting
- Free rewards: “Bloomkeeper” title, gold, crafting materials
- Premium rewards: “Lifebringer” exclusive title, verdant portrait frame, Named seasonal armor (regeneration Legendary)
Seasonal Content Vault
- After a season ends, its dungeons and content are removed
- Seasonal rewards (titles, portraits, named items) are exclusive — they cannot be earned after the season
- This creates anticipation for each new season and gives seasonal rewards lasting value
- If a season is extremely popular, it may return in a modified form in a future year (with new rewards)
Leaderboards
Leaderboard Categories
| Leaderboard | Metric | Reset |
|---|---|---|
| PVP Arena | ELO rating | Per season |
| Dungeon Speed | Fastest completion time per dungeon | Permanent (seasonal records too) |
| Weekly Challenge | Fastest/best completion of weekly dungeon | Weekly |
| Achievement Points | Total achievement score | Permanent |
| Crafting | Total items crafted / highest skill | Permanent |
| Wealth | Total gold (liquid + estimated assets) | Permanent |
| Raid | Fastest raid completion by guild | Per season |
Leaderboard Display
- Top 100 per category
- Player can see their own rank even if outside top 100
- Guild leaderboards (aggregate of member rankings)
- Server-wide and (eventually) cross-server
Collection Systems
Collections provide long-term completionist goals.
Bestiary
- Every enemy encountered is logged in the bestiary
- Entry includes: enemy name, stat overview, weaknesses, lore snippet
- Completing regional bestiaries grants exploration achievements
- Bestiary percentage visible on profile
Item Catalog
- Every unique item discovered is logged
- Includes items seen on the marketplace (not just personally owned)
- “Collect all items of X type” achievements
- Useful reference for build planning
Title Collection
- All earned titles visible in a collection
- Sources shown (achievement, PVP, faction, seasonal)
- Titles are purely cosmetic but socially valuable
Recipe Collection
- All learned recipes across all crafting professions
- Shows undiscovered recipes as “???” to create discovery goals
- Completion percentage per profession
Prestige and Long-Term Goals
For Level-Capped Characters
See 03-character-progression.md for the Paragon system.
For Completionists
- Region completion (all quests, all dungeons, all gathering nodes, all lore)
- Faction Exalted status (time-gated but achievable)
- Full crafting mastery (skill 100 in all professions)
- Bestiary completion
- Achievement hunting (many achievements are designed to take months)
For Competitive Players
- PVP rating climbing
- Speed run records
- Guild raid progression (first guild to clear new content)
- Seasonal leaderboard competition
For Social Players
- Guild leveling and hall upgrades
- Helping newer players (mentoring achievements)
- Organizing raids and events
- Marketplace mastery (economic gameplay)
Retention Philosophy
- Respect the player’s time. Every login should feel valuable, not obligatory.
- Layer short and long-term goals. Daily bounties for today, achievements for this month, collections for this year.
- Variety prevents burnout. Multiple systems (combat, crafting, PVP, social, collecting) mean there’s always something fresh to do.
- Celebrate accomplishment. Achievements, titles, leaderboards, and cosmetics make progress visible and socially rewarding.
- No dead ends. Even max-level characters have Paragon levels, seasonal content, PVP seasons, and collections to pursue.
Delve — Technical Architecture
Table of Contents
- Architecture Overview
- Technology Stack
- Client Architecture
- Server Architecture
- Database Design
- Game Systems — Server
- Game Systems — Client
- Polling & Notifications
- Chat — Discord Integration
- Mobile Wrapping
- Infrastructure & DevOps
- Security
- Performance Targets
1. Architecture Overview
Delve is a server-authoritative async idle MMO rendered as a web application and wrapped for mobile distribution. Because combat is never real-time (players configure a skill queue, the server resolves it), the architecture optimizes for throughput of simulation ticks and low-frequency but reliable client updates rather than sub-100ms latency.
┌─────────────────────────────────────────────────────┐
│ CLIENTS │
│ Browser (SPA) · iOS (Capacitor) · Android │
└──────────┬──────────────────────────────┬───────────┘
│ HTTPS (REST + polling) │ Push
▼ ▼
┌─────────────────────┐ ┌────────────────────────┐
│ API Gateway / │ │ Push Notification │
│ Load Balancer │ │ Service (FCM / APNs) │
│ (Caddy) │ └────────────────────────┘
└──────────┬──────────┘
│ ┌────────────────────────┐
▼ │ Discord │
┌─────────────────────┐ │ (Chat, community, │
│ REST API Servers │ │ LFG, guild comms) │
└──────────┬──────────┘ └────────────────────────┘
│
┌──────────┴──────────────────────────────────────────┐
│ BullMQ Job Queue (Redis-backed) │
└──────┬────────────┬────────────┬────────────────────┘
▼ ▼ ▼
┌────────────┐ ┌──────────┐ ┌──────────────┐
│ Simulation│ │ Economy │ │ PVP Match │
│ Workers │ │ Workers │ │ Workers │
└────────────┘ └──────────┘ └──────────────┘
│ │ │
▼ ▼ ▼
┌─────────────────────────────────────────────────────┐
│ Data Layer │
│ PostgreSQL · Redis · Backblaze B2 │
└─────────────────────────────────────────────────────┘
Design Principles
- Server-authoritative: All game state mutations happen server-side. The client is a view layer.
- Async-first: Most gameplay resolves on the server without a connected client.
- REST + polling, no WebSockets: The game is inherently async — players wait minutes to hours for results. Polling every 30–60s is perfectly adequate and dramatically simplifies the server (no persistent connections, no connection state, no reconnect logic).
- Discord for chat: Community, guild coordination, LFG, and trade chat all happen on Discord. This is where the community already lives, and it eliminates an entire real-time system from the codebase.
- Horizontally scalable: Stateless API servers; sharded workers keyed by character/guild ID.
- Offline-tolerant: Runs, crafting, and gathering proceed whether the player is online or not.
2. Technology Stack
Client
| Layer | Technology | Rationale |
|---|---|---|
| UI Framework | SvelteKit (SPA mode) | Small bundle size (~30KB framework), fast reactivity, excellent mobile perf. SPA mode since all logic is server-side. |
| Rendering | HTML/CSS + PixiJS (optional) | Most of Delve is UI-driven (queues, inventories, logs). PixiJS available for animated run replays and map rendering. |
| State Management | Svelte stores + TanStack Query | Stores for local UI state; TanStack Query for server state caching, deduplication, and background refetching. |
| Styling | Tailwind CSS | Utility-first, tree-shakes to small bundle. Fantasy theme via design tokens. |
| Mobile Wrapper | Capacitor | Web-to-native bridge for iOS/Android. Access to push notifications, haptics, secure storage. |
| Build | Vite | Fast HMR in dev, optimized production builds with code splitting. |
Server
| Layer | Technology | Rationale |
|---|---|---|
| Language | Rust | High performance for the simulation engine. Strong type system catches bugs at compile time. Single binary deploys. Memory safety without GC pauses. |
| API Framework | Axum | Tokio-based, ergonomic extractors, tower middleware ecosystem. The standard choice for Rust web services. |
| Task Scheduling | Custom Redis-backed job queue (via redis crate) | Delayed jobs for run completion, crafting timers, gathering expeditions, raid scheduling, auction expiry. Rust doesn’t have a BullMQ equivalent — a simple custom queue on Redis ZADD/ZPOPMIN with timestamps is sufficient and avoids a heavy dependency. |
| ORM / Query | SQLx | Compile-time checked SQL queries against the real database. No ORM overhead — write SQL directly with type-safe results. Async Postgres driver built-in. |
| Serialization | serde + serde_json | Industry-standard Rust serialization. Used for API request/response bodies, JSONB fields, and game data definitions. |
| Validation | validator crate + custom types | Derive-based validation on request structs. Newtype pattern for domain-specific constraints (e.g., AttributeValue(u8) that enforces 1–99 range). |
| Auth | argon2 crate + custom session middleware | Session-based auth with Argon2id password hashing. Sessions stored in Redis. OAuth2 via oauth2 crate for social logins. |
Data
| Layer | Technology | Rationale |
|---|---|---|
| Primary DB | PostgreSQL 16 | JSONB for flexible item properties, strong indexing, reliable ACID transactions for economy. |
| Cache / Jobs | Redis 7 (Valkey) | Session store, leaderboard sorted sets, rate limiting, job queue backing store. |
| Object Storage | S3-compatible (Backblaze B2) | Run replay logs, seasonal assets, user avatars. Accessed via aws-sdk-s3 crate. |
| Search (future) | Meilisearch | Marketplace full-text search, bestiary/recipe lookup. |
Infrastructure
| Layer | Technology | Rationale |
|---|---|---|
| Containers | Docker + Docker Compose (dev), Kubernetes (prod) | Single binary per service → tiny Docker images (~10–20MB with FROM scratch or Alpine). |
| Reverse Proxy | Caddy | Automatic HTTPS, HTTP/2, simple config. |
| CI/CD | GitHub Actions | Build, test, deploy pipeline. Rust builds cached via sccache or cargo-chef Docker layer. |
| Monitoring | Prometheus + Grafana | Metrics exported via metrics + metrics-exporter-prometheus crates. |
| Logging | tracing + tracing-subscriber → JSON → Loki | Structured logging with spans. The tracing ecosystem is Rust’s standard for observability. |
| Error Tracking | Sentry (sentry-rust crate) | Server-side panic/error capture. Client errors via Sentry JS SDK. |
3. Client Architecture
3.1 Application Structure
src/
├── lib/
│ ├── api/ # API client, polling, query hooks
│ │ ├── client.ts # Hono RPC typed client (end-to-end type safety)
│ │ └── queries/ # TanStack Query definitions per domain (with polling intervals)
│ ├── stores/ # Svelte stores for client-side state
│ │ ├── auth.ts # Current user session
│ │ ├── notifications.ts
│ │ └── ui.ts # Theme, sidebar state, modals
│ ├── components/ # Reusable UI components
│ │ ├── character/ # Character sheet, skill queue builder
│ │ ├── combat/ # Run replay viewer, encounter log
│ │ ├── inventory/ # Gear grid, item tooltips, drag-and-drop
│ │ ├── marketplace/ # Listings, search, buy/sell flows
│ │ ├── social/ # Guild panel, friends list, Discord links
│ │ └── ui/ # Buttons, modals, toasts, progress bars
│ ├── game/ # Client-side game logic
│ │ ├── tooltips.ts # Stat calculation for item/skill tooltips
│ │ ├── timers.ts # Countdown display for active runs/crafts
│ │ └── constants.ts # Shared enums, rarity colors, etc.
│ └── types/ # TypeScript types matching the Rust API contract
│ ├── character.ts # Character, Attributes, Species, Class
│ ├── item.ts # Item, Rarity, ItemLocation, BonusProperty
│ ├── skill.ts # SkillQueueSlot, QueueCondition
│ ├── combat.ts # RunLog, EncounterLog, ActionLog
│ └── api.ts # Request/response types per endpoint
├── routes/
│ ├── (auth)/ # Login, register, password reset
│ ├── (game)/ # Main game layout wrapper
│ │ ├── character/ # Character sheet, progression, skill queue
│ │ ├── quest-board/ # Available quests, active runs
│ │ ├── inventory/ # Gear, backpack, bank
│ │ ├── crafting/ # Profession UIs, recipe browser
│ │ ├── marketplace/ # Auction house
│ │ ├── guild/ # Guild management, lobby scheduling
│ │ ├── pvp/ # Arena queue, leaderboards
│ │ ├── world/ # Map, factions, bestiary
│ │ └── settings/ # Account, notifications, appearance
│ └── +layout.svelte # Root layout with nav, polling init
└── app.html
3.2 Key Client Patterns
Server State vs. Client State: All game data (character stats, inventory, active runs) is server state managed via TanStack Query. The client never computes authoritative game values — it only displays them. Client state is limited to UI concerns (which tab is open, tooltip position, theme preference).
Optimistic Updates: For low-risk actions (equipping gear, reordering skill queue), the client optimistically updates the UI and rolls back on server rejection. For economy actions (marketplace buy, gold transfer), the client waits for server confirmation.
Polling Strategy: TanStack Query handles all server state polling. Different data types poll at different intervals based on how time-sensitive they are:
| Data | Poll Interval | Rationale |
|---|---|---|
| Active runs/crafts/gathering | 60s | Client shows countdown from completesAt — only needs to poll to detect completion |
| Notifications (in-app) | 30s | GET /api/characters/:id/notifications — new completions, mail, PVP results |
| Marketplace listings | 60s | Not urgent — player checks when ready |
| Party/raid lobby status | 15s | More time-sensitive when coordinating group content |
| PVP queue status | 10s | Needs faster feedback when waiting for a match |
| Everything else | On demand | Fetched when the player navigates to that screen |
When a countdown timer reaches zero, the client immediately refetches that resource (rather than waiting for the next poll interval) to show the result as soon as it’s available.
Timer Display: Active runs, crafts, and gathering expeditions show countdown timers. The client calculates display time from startedAt + duration timestamps provided by the server. No client-side simulation of progress — just a countdown to the known completion time. On timer expiry, TanStack Query refetches the resource immediately.
Run Replay Viewer: When a dungeon run completes, the server stores a structured log of every encounter, roll, and outcome. The client renders this as a scrollable timeline with expandable encounter cards. Optional PixiJS layer for animated combat playback.
3.3 Responsive Design
The game targets three breakpoints:
| Breakpoint | Width | Layout |
|---|---|---|
| Mobile | < 640px | Single column, bottom tab navigation, stacked panels |
| Tablet | 640–1024px | Two-column with collapsible sidebar |
| Desktop | > 1024px | Three-column with persistent sidebar and detail panel |
Touch-first interaction design: all drag-and-drop (inventory, skill queue) uses pointer events with touch support. No hover-dependent interactions — tooltips trigger on tap-and-hold on mobile.
4. Server Architecture
4.1 Service Topology
The server is a Rust workspace (Cargo monorepo) deployed as multiple binary targets from a single codebase. This avoids microservice complexity while allowing independent scaling of compute-heavy workers. All binaries share common crates for game logic, database access, and types.
delve-server/
├── Cargo.toml # Workspace root
├── crates/
│ ├── api/ # REST API server binary
│ │ ├── src/
│ │ │ ├── main.rs # Axum server entry point
│ │ │ ├── routes/ # Route handlers organized by domain
│ │ │ │ ├── auth.rs
│ │ │ │ ├── characters.rs
│ │ │ │ ├── quests.rs
│ │ │ │ ├── inventory.rs
│ │ │ │ ├── crafting.rs
│ │ │ │ ├── marketplace.rs
│ │ │ │ ├── guilds.rs
│ │ │ │ ├── pvp.rs
│ │ │ │ ├── factions.rs
│ │ │ │ ├── social.rs
│ │ │ │ ├── notifications.rs
│ │ │ │ └── admin.rs
│ │ │ ├── middleware/ # Auth, rate limiting, validation, tracing
│ │ │ └── extractors.rs # Custom Axum extractors (AuthUser, ValidatedJson, etc.)
│ │ └── Cargo.toml
│ ├── workers/ # Background job processor binary
│ │ ├── src/
│ │ │ ├── main.rs # Worker entry point — registers job handlers, polls Redis queue
│ │ │ ├── simulation/ # Dungeon run resolver
│ │ │ │ ├── engine.rs # Core d100 combat engine
│ │ │ │ ├── encounters.rs
│ │ │ │ ├── skill_queue.rs
│ │ │ │ ├── conditions.rs
│ │ │ │ └── loot.rs
│ │ │ ├── economy.rs # Marketplace matching, auction expiry
│ │ │ ├── crafting.rs # Craft completion, critical craft rolls
│ │ │ ├── gathering.rs # Expedition completion, yield calculation
│ │ │ ├── pvp.rs # Arena match resolution, ELO updates
│ │ │ ├── guild.rs # Guild XP tallying, buff expiry
│ │ │ └── scheduled.rs # Daily reset, weekly rotation, season transitions
│ │ └── Cargo.toml
│ ├── game/ # Core game logic library (shared by api + workers)
│ │ ├── src/
│ │ │ ├── combat.rs # Damage formulas, hit chance, crit calculation
│ │ │ ├── progression.rs # XP curves, level thresholds, feat unlocks
│ │ │ ├── economy.rs # Tax rates, vendor prices, inflation formulas
│ │ │ ├── loot_tables.rs # Drop rates, rarity weights per content tier
│ │ │ ├── time.rs # Duration calculations for runs, crafts, gathering
│ │ │ └── rng.rs # Seeded deterministic RNG (ChaCha8Rng)
│ │ └── Cargo.toml
│ ├── db/ # Database layer (shared)
│ │ ├── src/
│ │ │ ├── lib.rs # Connection pool (sqlx::PgPool), migrations
│ │ │ ├── models/ # Row types (FromRow derives)
│ │ │ ├── queries/ # SQLx query functions per domain
│ │ │ └── migrations/ # SQL migration files (sqlx migrate)
│ │ └── Cargo.toml
│ ├── types/ # Shared types, enums, constants
│ │ ├── src/
│ │ │ ├── character.rs
│ │ │ ├── item.rs
│ │ │ ├── skill.rs
│ │ │ ├── quest.rs
│ │ │ ├── combat.rs
│ │ │ └── ids.rs # Typed ID wrappers (CharacterId, ItemId, etc.)
│ │ └── Cargo.toml
│ └── jobs/ # Job queue abstraction (shared)
│ ├── src/
│ │ ├── lib.rs # Redis-backed delayed job queue
│ │ ├── enqueue.rs # Enqueue jobs with optional delay
│ │ └── process.rs # Poll and process jobs
│ └── Cargo.toml
├── data/ # Static game data (TOML/RON files, compiled into binary)
│ ├── items/ # Item template definitions
│ ├── quests/ # Quest and dungeon definitions
│ ├── creatures/ # Enemy stat blocks
│ ├── skills/ # Skill definitions
│ ├── recipes/ # Crafting recipes
│ └── loot_tables/ # Loot table definitions
└── Dockerfile # Multi-stage: build with rust image, run with scratch/alpine
Why a Cargo Workspace
- Single compilation unit for shared code: The
game,db,types, andjobscrates are compiled once and shared by both theapiandworkersbinaries. - Compile-time guarantees: SQLx checks queries against the real database schema at compile time. Type mismatches between API routes and database models are caught before deployment.
- Two binaries, one repo:
cargo build --bin apiandcargo build --bin workersproduce independent binaries that can be deployed and scaled separately. - Static game data: Item templates, quests, creatures, and loot tables are defined as TOML or RON files in the
data/directory and loaded at startup (or compiled in viainclude_str!). Balance changes are code changes — reviewed in PRs, versioned in git.
4.2 Process Types
| Process | Scaling | Role |
|---|---|---|
delve-api | Horizontal (2+ instances behind load balancer) | REST endpoints, request validation, auth, notification polling. Axum binary. |
delve-workers | Horizontal (scale by queue depth) | Polls Redis job queue, dispatches to simulation/economy/pvp/crafting handlers. Single binary handles all job types — scaling is just running more instances. |
delve-workers --scheduler | Single instance | Same binary with a flag to also run cron-like triggers: daily resets, weekly rotations, season transitions. Only one instance runs scheduled jobs (Redis-based leader election). |
Workers handle all background job types in a single binary. The economy queue is processed serially (single consumer) to prevent race conditions, while simulation/pvp/crafting jobs are processed concurrently across all worker instances.
4.3 Request Flow Example — Start a Dungeon Run
1. Client POST /api/quests/start
Body: { characterId, questId, skillQueue, loadout, supplies }
2. API Server:
a. Validate session → get userId
b. Validate character belongs to user, is not already in a run
c. Validate skill queue (skills owned, correct order, conditionals valid)
d. Validate loadout (gear owned, correct slots)
e. Validate supplies (owned, within slot limits)
f. Calculate run duration based on quest + Patron status
g. Deduct supplies from inventory
h. Create run record in DB (status: "in_progress", completesAt: now + duration)
i. Enqueue BullMQ delayed job: "resolve-run" with delay = duration
j. Return { runId, completesAt } to client
3. [Time passes — player may be offline]
4. BullMQ triggers "resolve-run" job at completesAt:
a. Simulation Worker picks up job
b. Load run record, character snapshot, quest definition
c. For each encounter in quest:
- Roll initiative for all participants
- Execute skill queues round-by-round (d100 rolls, conditional checks)
- Apply damage, healing, conditions
- Check for death/completion
- If rest point: restore resources per rules
- Log every action and roll to run_log JSONB
d. Calculate loot drops from completed encounters
e. Calculate XP and gold earned
f. Write results: run status, loot, XP, gold, run_log
g. Insert notification record: { characterId, type: "run_complete", runId }
h. If character has push notifications enabled and is offline:
- Send push notification via FCM/APNs: "Your dungeon run is complete!"
5. Client discovers result via one of:
- Poll: GET /api/characters/:id/notifications returns the "run_complete" event
- Timer: client countdown hits zero → immediate refetch of run status
- Push: mobile notification tapped → app opens to run result screen
5. Database Design
5.1 PostgreSQL Schema (Key Tables)
Data-driven convention: All game content references (species, class, weapon type, rarity, faction, etc.) are stored as TEXT columns containing data IDs validated by the application against the GameData registry. No Postgres ENUM types are used for game content — this means adding new content never requires a database migration.
-- ==================== ACCOUNTS ====================
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email TEXT UNIQUE NOT NULL,
password_hash TEXT NOT NULL,
patron_tier SMALLINT DEFAULT 0, -- 0=free, 1=patron
patron_expires TIMESTAMPTZ,
character_slots SMALLINT DEFAULT 2,
bank_slots SMALLINT DEFAULT 50,
marketplace_slots SMALLINT DEFAULT 10,
created_at TIMESTAMPTZ DEFAULT now(),
last_login TIMESTAMPTZ
);
CREATE TABLE sessions (
id TEXT PRIMARY KEY,
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
expires_at TIMESTAMPTZ NOT NULL
);
-- ==================== CHARACTERS ====================
CREATE TABLE characters (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
name TEXT UNIQUE NOT NULL,
species TEXT NOT NULL, -- data ID, validated by app against GameData registry
class TEXT NOT NULL, -- data ID, validated by app against GameData registry
subclass TEXT, -- data ID, NULL until level 10
background TEXT NOT NULL,
level SMALLINT DEFAULT 1,
xp INTEGER DEFAULT 0,
paragon_level INTEGER DEFAULT 0, -- post-50 progression
paragon_xp BIGINT DEFAULT 0,
-- Core attributes (base values before gear/buffs)
might SMALLINT NOT NULL,
logic SMALLINT NOT NULL,
speed SMALLINT NOT NULL,
presence SMALLINT NOT NULL,
fortitude SMALLINT NOT NULL,
luck SMALLINT NOT NULL,
-- Non-combat skills (0-100)
skill_athletics SMALLINT DEFAULT 0,
skill_acrobatics SMALLINT DEFAULT 0,
skill_stealth SMALLINT DEFAULT 0,
skill_perception SMALLINT DEFAULT 0,
skill_arcana SMALLINT DEFAULT 0,
skill_nature SMALLINT DEFAULT 0,
skill_religion SMALLINT DEFAULT 0,
skill_persuasion SMALLINT DEFAULT 0,
skill_deception SMALLINT DEFAULT 0,
skill_intimidation SMALLINT DEFAULT 0,
skill_medicine SMALLINT DEFAULT 0,
skill_survival SMALLINT DEFAULT 0,
-- Currency
gold BIGINT DEFAULT 0,
-- Rested bonus (accumulated offline XP multiplier)
rested_bonus REAL DEFAULT 0.0, -- 0.0 to 0.5
last_active TIMESTAMPTZ DEFAULT now(),
-- Chosen feats (JSONB array of feat IDs)
feats JSONB DEFAULT '[]',
created_at TIMESTAMPTZ DEFAULT now(),
CONSTRAINT valid_attributes CHECK (
might BETWEEN 1 AND 99 AND logic BETWEEN 1 AND 99 AND
speed BETWEEN 1 AND 99 AND presence BETWEEN 1 AND 99 AND
fortitude BETWEEN 1 AND 99 AND luck BETWEEN 1 AND 99
)
);
CREATE INDEX idx_characters_user ON characters(user_id);
CREATE INDEX idx_characters_name ON characters(name);
-- ==================== EQUIPMENT & INVENTORY ====================
CREATE TABLE items (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
owner_id UUID REFERENCES characters(id) ON DELETE CASCADE,
template_id TEXT NOT NULL, -- data ID, references item template in GameData
rarity TEXT NOT NULL, -- data ID, references rarity tier in GameData
enhancement SMALLINT DEFAULT 0, -- +0 to +5
bonus_properties JSONB DEFAULT '[]', -- random rolled properties for rare+
location TEXT NOT NULL, -- 'equipped:main_hand', 'backpack', 'bank', 'mail'
slot_index SMALLINT, -- position within location
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE INDEX idx_items_owner ON items(owner_id);
CREATE INDEX idx_items_owner_location ON items(owner_id, location);
-- Artifact tracking (server-unique items)
CREATE TABLE artifacts (
template_id TEXT PRIMARY KEY,
held_by UUID REFERENCES characters(id),
acquired_at TIMESTAMPTZ
);
-- ==================== SKILL QUEUE ====================
CREATE TABLE skill_queues (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
character_id UUID REFERENCES characters(id) ON DELETE CASCADE,
name TEXT DEFAULT 'Default',
is_active BOOLEAN DEFAULT false,
slots JSONB NOT NULL, -- ordered array of { skillId, condition? }
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE INDEX idx_skill_queues_char ON skill_queues(character_id);
-- ==================== WEAPON PROFICIENCY ====================
CREATE TABLE weapon_proficiencies (
character_id UUID REFERENCES characters(id) ON DELETE CASCADE,
weapon_type TEXT NOT NULL, -- sword, axe, bow, staff, etc.
proficiency SMALLINT DEFAULT 0, -- 0-100
PRIMARY KEY (character_id, weapon_type)
);
-- ==================== RUNS & QUESTS ====================
CREATE TABLE runs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
character_id UUID REFERENCES characters(id) ON DELETE CASCADE,
quest_id TEXT NOT NULL, -- references static quest/dungeon definition
difficulty TEXT NOT NULL,
status TEXT DEFAULT 'in_progress', -- in_progress, completed, failed, abandoned
skill_queue JSONB NOT NULL, -- snapshot of queue at run start
loadout JSONB NOT NULL, -- snapshot of equipped gear at run start
supplies JSONB NOT NULL, -- snapshot of supplies consumed
party_id UUID, -- NULL for solo, references party for group content
started_at TIMESTAMPTZ DEFAULT now(),
completes_at TIMESTAMPTZ NOT NULL,
completed_at TIMESTAMPTZ,
run_log JSONB, -- full encounter-by-encounter replay log
rewards JSONB, -- { xp, gold, items[], proficiencyGains }
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE INDEX idx_runs_character ON runs(character_id);
CREATE INDEX idx_runs_status ON runs(status) WHERE status = 'in_progress';
CREATE INDEX idx_runs_completes ON runs(completes_at) WHERE status = 'in_progress';
-- ==================== CRAFTING & GATHERING ====================
CREATE TABLE crafting_proficiencies (
character_id UUID REFERENCES characters(id) ON DELETE CASCADE,
profession TEXT NOT NULL, -- blacksmithing, alchemy, etc.
skill_level SMALLINT DEFAULT 1, -- 1-100
PRIMARY KEY (character_id, profession)
);
CREATE TABLE crafting_jobs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
character_id UUID REFERENCES characters(id) ON DELETE CASCADE,
recipe_id TEXT NOT NULL,
status TEXT DEFAULT 'in_progress',
started_at TIMESTAMPTZ DEFAULT now(),
completes_at TIMESTAMPTZ NOT NULL,
result_item_id UUID, -- set on completion
is_critical BOOLEAN
);
CREATE INDEX idx_crafting_jobs_char ON crafting_jobs(character_id);
CREATE TABLE gathering_expeditions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
character_id UUID REFERENCES characters(id) ON DELETE CASCADE,
profession TEXT NOT NULL, -- mining, herbalism, logging, skinning
zone TEXT NOT NULL,
tier SMALLINT NOT NULL, -- 1-5
status TEXT DEFAULT 'in_progress',
started_at TIMESTAMPTZ DEFAULT now(),
completes_at TIMESTAMPTZ NOT NULL,
yields JSONB -- set on completion: [{ materialId, quantity }]
);
CREATE TABLE known_recipes (
character_id UUID REFERENCES characters(id) ON DELETE CASCADE,
recipe_id TEXT NOT NULL,
learned_at TIMESTAMPTZ DEFAULT now(),
PRIMARY KEY (character_id, recipe_id)
);
-- ==================== MARKETPLACE ====================
CREATE TABLE marketplace_listings (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
seller_id UUID REFERENCES characters(id) ON DELETE CASCADE,
item_id UUID REFERENCES items(id),
price BIGINT NOT NULL,
listing_fee BIGINT NOT NULL, -- 5% deducted at listing time
status TEXT DEFAULT 'active', -- active, sold, expired, cancelled
listed_at TIMESTAMPTZ DEFAULT now(),
expires_at TIMESTAMPTZ NOT NULL, -- listed_at + 48 hours
sold_to UUID REFERENCES characters(id),
sold_at TIMESTAMPTZ
);
CREATE INDEX idx_marketplace_status ON marketplace_listings(status) WHERE status = 'active';
CREATE INDEX idx_marketplace_expires ON marketplace_listings(expires_at) WHERE status = 'active';
CREATE INDEX idx_marketplace_seller ON marketplace_listings(seller_id);
-- ==================== MAIL ====================
CREATE TABLE mail (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
sender_id UUID REFERENCES characters(id),
recipient_id UUID REFERENCES characters(id) ON DELETE CASCADE,
subject TEXT,
body TEXT,
gold_amount BIGINT DEFAULT 0,
item_ids UUID[], -- items attached
is_read BOOLEAN DEFAULT false,
deliverable_at TIMESTAMPTZ NOT NULL, -- sent_at + 1 hour
sent_at TIMESTAMPTZ DEFAULT now(),
expires_at TIMESTAMPTZ -- auto-delete after 30 days
);
CREATE INDEX idx_mail_recipient ON mail(recipient_id);
-- ==================== GUILDS ====================
CREATE TABLE guilds (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT UNIQUE NOT NULL,
tag TEXT UNIQUE NOT NULL, -- 2-4 char tag
leader_id UUID REFERENCES characters(id),
level SMALLINT DEFAULT 1,
xp BIGINT DEFAULT 0,
bank_gold BIGINT DEFAULT 0,
active_buff TEXT, -- current guild buff ID
buff_expires TIMESTAMPTZ,
max_members SMALLINT DEFAULT 50, -- scales with guild level, max 200
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE guild_members (
guild_id UUID REFERENCES guilds(id) ON DELETE CASCADE,
character_id UUID REFERENCES characters(id) ON DELETE CASCADE,
rank TEXT DEFAULT 'member', -- leader, officer, member, recruit
joined_at TIMESTAMPTZ DEFAULT now(),
PRIMARY KEY (guild_id, character_id)
);
CREATE INDEX idx_guild_members_char ON guild_members(character_id);
-- ==================== FACTIONS & REPUTATION ====================
CREATE TABLE character_reputation (
character_id UUID REFERENCES characters(id) ON DELETE CASCADE,
faction_id TEXT NOT NULL, -- iron_compact, shadow_court, etc.
reputation INTEGER DEFAULT 0, -- -3000 to 21000+
PRIMARY KEY (character_id, faction_id)
);
-- ==================== PVP ====================
CREATE TABLE pvp_ratings (
character_id UUID REFERENCES characters(id) ON DELETE CASCADE,
bracket TEXT NOT NULL, -- '1v1', '3v3'
rating INTEGER DEFAULT 1000, -- ELO
season SMALLINT NOT NULL,
wins INTEGER DEFAULT 0,
losses INTEGER DEFAULT 0,
PRIMARY KEY (character_id, bracket, season)
);
CREATE TABLE pvp_matches (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
bracket TEXT NOT NULL,
season SMALLINT NOT NULL,
team_a UUID[] NOT NULL, -- character IDs
team_b UUID[] NOT NULL,
winner TEXT, -- 'a', 'b', 'draw'
match_log JSONB,
rating_changes JSONB, -- { charId: delta }
resolved_at TIMESTAMPTZ DEFAULT now()
);
-- ==================== SOCIAL ====================
CREATE TABLE friends (
character_id UUID REFERENCES characters(id) ON DELETE CASCADE,
friend_id UUID REFERENCES characters(id) ON DELETE CASCADE,
status TEXT DEFAULT 'pending', -- pending, accepted
created_at TIMESTAMPTZ DEFAULT now(),
PRIMARY KEY (character_id, friend_id)
);
-- ==================== PARTIES & RAIDS ====================
CREATE TABLE parties (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
leader_id UUID REFERENCES characters(id),
type TEXT NOT NULL, -- duo, standard, raid
max_size SMALLINT NOT NULL, -- 2, 4, 8
status TEXT DEFAULT 'forming', -- forming, ready, in_run, completed
quest_id TEXT,
scheduled_at TIMESTAMPTZ, -- for timed lobbies
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE party_members (
party_id UUID REFERENCES parties(id) ON DELETE CASCADE,
character_id UUID REFERENCES characters(id) ON DELETE CASCADE,
role TEXT, -- tank, healer, dps, support
ready BOOLEAN DEFAULT false,
PRIMARY KEY (party_id, character_id)
);
-- ==================== ACHIEVEMENTS & COLLECTIONS ====================
CREATE TABLE character_achievements (
character_id UUID REFERENCES characters(id) ON DELETE CASCADE,
achievement_id TEXT NOT NULL,
earned_at TIMESTAMPTZ DEFAULT now(),
PRIMARY KEY (character_id, achievement_id)
);
CREATE TABLE character_bestiary (
character_id UUID REFERENCES characters(id) ON DELETE CASCADE,
creature_id TEXT NOT NULL,
kills INTEGER DEFAULT 0,
first_killed TIMESTAMPTZ DEFAULT now(),
PRIMARY KEY (character_id, creature_id)
);
-- ==================== DAILY/WEEKLY TRACKING ====================
CREATE TABLE daily_tracking (
character_id UUID REFERENCES characters(id) ON DELETE CASCADE,
date DATE NOT NULL DEFAULT CURRENT_DATE,
first_run_bonus BOOLEAN DEFAULT false,
bounties_completed SMALLINT DEFAULT 0,
bounty_chest_claimed BOOLEAN DEFAULT false,
faction_quests JSONB DEFAULT '{}', -- { factionId: count }
PRIMARY KEY (character_id, date)
);
CREATE TABLE weekly_tracking (
character_id UUID REFERENCES characters(id) ON DELETE CASCADE,
week_start DATE NOT NULL, -- Monday of the week
challenge_completed BOOLEAN DEFAULT false,
raid_tokens_earned INTEGER DEFAULT 0,
PRIMARY KEY (character_id, week_start)
);
-- ==================== SEASONAL ====================
CREATE TABLE season_progress (
character_id UUID REFERENCES characters(id) ON DELETE CASCADE,
season_id TEXT NOT NULL,
track TEXT NOT NULL, -- 'free' or 'premium'
tier_reached SMALLINT DEFAULT 0,
xp INTEGER DEFAULT 0,
PRIMARY KEY (character_id, season_id)
);
5.2 Static Game Data
Item templates, quest definitions, skill data, creature stats, recipes, and loot tables are not stored in PostgreSQL. They are defined as TOML files in the data/ directory, deserialized into Rust structs at startup via serde, and versioned with the codebase. This keeps game balance changes in source control and avoids DB migrations for tuning.
# data/items/iron_longsword.toml
[iron_longsword]
name = "Iron Longsword"
type = "weapon"
subtype = "sword"
slot = "main_hand"
damage = [8, 14]
speed = 1.0
level_req = 1
rarity = "common"
# data/quests/goblin_warren.toml
[goblin_warren]
name = "Goblin Warren"
type = "bounty"
level_range = [1, 5]
base_duration_secs = 1800 # 30 minutes
loot_table = "goblin_bounty_t1"
[[goblin_warren.encounters]]
type = "combat"
enemies = ["goblin_scout", "goblin_scout"]
# ...
#![allow(unused)]
fn main() {
// crates/game/src/data.rs
use once_cell::sync::Lazy;
use std::collections::HashMap;
pub static ITEMS: Lazy<HashMap<String, ItemTemplate>> = Lazy::new(|| {
load_toml_dir("data/items")
});
pub static QUESTS: Lazy<HashMap<String, QuestDefinition>> = Lazy::new(|| {
load_toml_dir("data/quests")
});
}
5.3 Redis Data Structures
# Session store
session:{sessionId} → JSON { userId, expiresAt } TTL: 30 days
# Leaderboards (sorted sets)
leaderboard:pvp:1v1:season:{n} → ZADD score=rating member=charId
leaderboard:pvp:3v3:season:{n} → ZADD score=rating member=charId
leaderboard:achievements → ZADD score=points member=charId
leaderboard:wealth → ZADD score=gold member=charId
# Rate limiting
ratelimit:{userId}:{endpoint} → counter TTL: window duration
# Active PVP queue
pvp:queue:1v1 → sorted set (score=rating, member=charId)
pvp:queue:3v3 → sorted set (score=rating, member=charId+teamId)
# BullMQ job queues (managed by BullMQ internally)
bull:resolve-run:*
bull:marketplace-buy:*
bull:pvp-resolve:*
bull:resolve-craft:*
bull:resolve-gathering:*
6. Game Systems — Server
6.1 Simulation Engine (Combat Resolution)
The simulation engine is the core of Delve. It runs entirely on the server with no real-time client involvement.
#![allow(unused)]
fn main() {
// crates/workers/src/simulation/engine.rs
use rand::SeedableRng;
use rand_chacha::ChaCha8Rng;
pub struct SimulationContext {
pub run: Run,
pub party: Vec<CharacterSnapshot>, // character stats + gear + queue at run start
pub quest: QuestDefinition,
pub rng: ChaCha8Rng, // deterministic seeded RNG
pub log: RunLogBuilder,
}
pub fn resolve_run(ctx: &mut SimulationContext) -> RunResult {
for encounter in &ctx.quest.encounters {
let result = resolve_encounter(ctx, encounter);
ctx.log.add_encounter(&result);
if result.outcome == EncounterOutcome::PartyWipe {
return RunResult {
status: RunStatus::Failed,
log: ctx.log.build(),
failed_at: Some(encounter.index),
rewards: None,
};
}
}
let loot = roll_loot(ctx);
let xp = calculate_xp(ctx);
RunResult {
status: RunStatus::Completed,
log: ctx.log.build(),
failed_at: None,
rewards: Some(RunRewards { loot, xp }),
}
}
}
Deterministic RNG: Each run is seeded with a ChaCha8Rng initialized from a seed stored in the run record. This means any run can be replayed identically for debugging or dispute resolution. The seed is derived from blake3::hash(run_id + started_at). ChaCha8 is fast and produces identical output across platforms — critical for deterministic simulation.
Encounter Resolution Loop:
- Sort all participants by initiative (Speed + weapon speed modifier + d100 roll)
- For each round (max 50 rounds per encounter):
- For each participant in initiative order:
- Evaluate skill queue: find first skill whose conditions are met and resources available
- Roll d100 against success chance (derived from attacker stats vs. defender stats)
- Apply effects: damage, healing, conditions, resource cost
- Check for death/incapacitation
- Tick conditions (poison damage, buff/debuff duration)
- Check encounter end conditions
- For each participant in initiative order:
- Log all rolls and outcomes
6.2 Economy Worker
The economy worker handles marketplace transactions with serialized processing to prevent race conditions.
Marketplace Buy Flow:
1. API validates buyer has enough gold
2. API enqueues "marketplace.buy" job (NOT direct DB update)
3. Economy worker (single instance) processes:
a. BEGIN TRANSACTION
b. Verify listing still active (SELECT FOR UPDATE)
c. Verify buyer gold >= price (SELECT FOR UPDATE on buyer character)
d. Transfer gold: buyer -= price, seller += (price - 10% tax)
e. Transfer item: update item.owner_id, item.location = 'mail'
f. Create mail record for seller (gold received notification)
g. Update listing status = 'sold'
h. COMMIT
4. Insert notification records for buyer ("item purchased") and seller ("item sold")
6.3 PVP Match Resolution
Arena matches resolve instantly (no wait timer) using the same simulation engine as PvE, but with stat normalization applied.
PVP Flow:
1. Player enters arena queue → added to Redis sorted set by rating
2. Matchmaker (runs every 5 seconds):
a. Scan queue for viable matches (rating within ±150, expanding over time)
b. Pop matched players from queue
c. Enqueue "pvp.resolve" job (immediate, no delay)
3. PVP Worker resolves match:
a. Snapshot both characters with PVP stat normalization
b. Run simulation engine (same as PvE but PVP-specific encounter rules)
c. Calculate ELO changes
d. Write match result + rating updates
e. Insert notification records for both players
4. Both players discover result via polling (PVP poll interval: 10s) or push notification
6.4 Scheduled Jobs
| Job | Schedule | Description |
|---|---|---|
daily-reset | 00:00 UTC | Reset daily bounties, first-run bonus, faction quest counts |
weekly-reset | Monday 00:00 UTC | Rotate weekly challenge, reset raid token caps |
auction-expiry | Every 5 min | Expire stale marketplace listings, return items via mail |
rested-bonus-tick | Every 1 hour | Increment rested bonus for offline characters |
season-transition | Manual trigger | End current season, archive ratings, distribute rewards |
guild-buff-expiry | Every 1 min | Expire guild buffs past their duration |
mail-cleanup | Daily | Delete read mail older than 30 days |
pvp-matchmaker | Every 5 sec | Scan PVP queue and create matches |
7. Game Systems — Client
7.1 Skill Queue Builder
The skill queue builder is the most complex client-side UI. Players drag-and-drop skills into an ordered list and configure optional conditions.
┌─────────────────────────────────────────────┐
│ Skill Queue: "Boss Rush Build" [Save]│
├─────────────────────────────────────────────┤
│ 1. [🗡️ Power Strike] always │
│ 2. [🛡️ Shield Wall] if HP < 50% │
│ 3. [⚡ Cleave] if enemies > 2 │
│ 4. [❤️ Second Wind] if HP < 30% │
│ 5. [🗡️ Execute] if target HP < 20%│
│ 6. [🗡️ Basic Attack] always (fallback) │
├─────────────────────────────────────────────┤
│ Available Skills: [Drag to add] │
│ [Whirlwind] [Taunt] [Parry] [Charge] ... │
└─────────────────────────────────────────────┘
Condition types (predefined, not free-form):
always— default, no conditionif_hp_below(%)— self HP thresholdif_hp_above(%)if_target_hp_below(%)if_enemy_count_above(n)if_enemy_count_below(n)if_ally_hp_below(%)— any ally below thresholdif_resource_above(type, n)— e.g., “if Fury > 50”if_resource_below(type, n)if_has_condition(condition)— if affected by specific statusif_target_has_condition(condition)
The client validates the queue locally (correct number of slots, skills owned, conditions valid) and sends it to the server for authoritative validation on run start.
7.2 Run Replay Viewer
After a run completes, the client renders the server-generated run_log as a detailed timeline.
┌─────────────────────────────────────────────┐
│ Run Complete: Goblin Warren (Normal) │
│ Result: ✅ Success | Duration: 32 min │
│ XP: +450 | Gold: +120 | Items: 3 │
├─────────────────────────────────────────────┤
│ │
│ ▼ Encounter 1/5: Goblin Scouts (Combat) │
│ Round 1: │
│ You use Power Strike → Hit (rolled 34 │
│ vs 72% chance) → 18 damage to Goblin A │
│ Goblin A attacks → Miss (rolled 88 vs │
│ 45% chance) │
│ Round 2: ... │
│ Result: Victory (2 rounds) │
│ │
│ ▶ Encounter 2/5: Trapped Hallway (Trap) │
│ ▶ Encounter 3/5: Fork in the Road (Choice) │
│ ▶ Encounter 4/5: Goblin Chieftain (Combat) │
│ ▶ Encounter 5/5: Treasure Room (Loot) │
│ │
├─────────────────────────────────────────────┤
│ Loot: [Goblin Blade (Uncommon)] [Health │
│ Potion x3] [12 Rough Leather] │
│ [Collect All]│
└─────────────────────────────────────────────┘
7.3 Activity Dashboard
The main game screen shows all concurrent activities at a glance:
┌─────────────────────────────────────────────┐
│ 🏰 Active Activities │
├─────────────────────────────────────────────┤
│ ⚔️ Dungeon Run: Goblin Warren 02:15:33 │
│ 🔨 Crafting: Iron Longsword 00:12:45│
│ ⛏️ Mining: Copper Vein (Tier 1) 01:45:00│
│ 📦 Marketplace: 3 active listings │
│ 🎯 Arena Queue: Searching... │
├─────────────────────────────────────────────┤
│ 📬 1 new mail | 🔔 2 new notifications │
└─────────────────────────────────────────────┘
8. Polling & Notifications
8.1 Why Polling, Not WebSockets
Delve is an async idle game. The fastest meaningful game event is a PVP match (resolved in seconds), and the slowest is a raid (12 hours). There is no real-time combat, no live player movement, no twitch gameplay. The client needs updates, not a live stream.
WebSockets add significant complexity for negligible benefit in this context:
- Persistent connection state to manage on the server (memory per connection, reconnect logic, heartbeats)
- Dedicated WebSocket server processes to scale independently
- Connection lifecycle management on mobile (background/foreground transitions)
- Missed-event recovery when connections drop
Polling via TanStack Query is simpler, stateless, and works identically whether the player is on desktop, mobile, or just opened the app after 8 hours offline. The server remains a pure REST API with no connection state.
8.2 Notification System
Workers write notification records to the database when events complete. The client polls for them.
CREATE TABLE notifications (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
character_id UUID REFERENCES characters(id) ON DELETE CASCADE,
type TEXT NOT NULL, -- run_complete, craft_complete, item_sold, etc.
title TEXT NOT NULL,
body TEXT,
data JSONB, -- { runId, itemId, etc. } for deep linking
is_read BOOLEAN DEFAULT false,
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE INDEX idx_notifications_unread
ON notifications(character_id, created_at DESC)
WHERE is_read = false;
GET /api/characters/:id/notifications?since={timestamp}
→ Returns unread notifications since last poll
POST /api/characters/:id/notifications/read
Body: { ids: [...] }
→ Marks notifications as read
8.3 Push Notifications (Mobile)
When a worker creates a notification record, it also checks whether to send a push notification. Push is sent when:
- The player has push enabled for that notification type
- The player’s last API request was > 5 minutes ago (likely offline/backgrounded)
Push notifications are delivered via FCM (Android) and APNs (iOS) through Capacitor’s push plugin.
Notification events (user-configurable):
- Run completed
- Crafting completed
- Gathering expedition completed
- Marketplace item sold
- Raid lobby starting in 15 min / 5 min / now
- Mail received
- Guild war declared
Never sent (per design doc anti-burnout philosophy):
- “Come back!” re-engagement nags
- Daily streak reminders
- “Your friends are playing” social pressure
- Limited-time urgency notifications
8.4 Polling Impact on Server Load
At 10,000 concurrent players polling every 30s, that’s ~333 requests/second to the notification endpoint. This is a trivial load for an API server — it’s a single indexed query (WHERE character_id = $1 AND is_read = false AND created_at > $2). For comparison, a typical Node.js server handles 5,000–10,000 simple requests/second.
At 100,000 concurrent players: ~3,333 req/s. Still manageable with 2-3 API server instances. The notification query hits an index and returns a handful of rows — it’s one of the cheapest possible database operations.
9. Chat — Discord Integration
9.1 Why Discord, Not In-Game Chat
Building a real-time chat system requires WebSockets, persistent connections, message storage, moderation tools, spam filtering, mute/block systems, and mobile notification integration. Discord already does all of this better than we could build, and the Delve community will already be on Discord.
9.2 Integration Approach
Discord is the primary social layer. The game links to it but does not embed it.
In-game integration points:
- Guild creation flow prompts the guild leader to link a Discord server
- Guild detail page shows a “Join Discord” button that opens the linked invite
- Party/raid lobby page shows a “Coordinate on Discord” link
- LFG (looking for group) happens in a dedicated Discord channel, not in-game
- Trade chat happens in a Discord channel
Server-side integration (Discord bot, optional):
- Bot posts to a
#notificationschannel when server events happen (season starts, maintenance, patch notes) - Bot can optionally post run results or achievements to a guild’s Discord channel (if guild links their server and enables it)
- Account linking: players can link their Discord account via OAuth2 for bot features
9.3 What This Eliminates
| Removed Component | Complexity Saved |
|---|---|
| WebSocket server process | No persistent connections, no connection state management |
| Chat message storage | No chat tables, no message history, no Redis pub/sub |
| Chat moderation | No profanity filter, no mute/ban system, no reporting UI |
| Presence system | No online/offline tracking, no heartbeats |
| NATS message bus | Workers write to DB directly, no pub/sub needed |
| Chat UI components | No message input, no channel switching, no emoji picker |
This reduces the server to a pure stateless REST API, which is dramatically simpler to build, deploy, debug, and scale.
10. Mobile Wrapping
10.1 Capacitor Configuration
Capacitor wraps the SvelteKit SPA as a native iOS and Android app.
Native plugins used:
├── @capacitor/push-notifications — FCM/APNs integration
├── @capacitor/haptics — tactile feedback for loot drops, crits
├── @capacitor/app — app state (foreground/background detection)
├── @capacitor/status-bar — immersive game UI
├── @capacitor/splash-screen — branded loading screen
├── @capacitor/browser — external links (terms, support, Discord)
└── @capacitor/preferences — local key-value storage (settings, cached auth)
10.2 Mobile-Specific Adaptations
| Concern | Implementation |
|---|---|
| App lifecycle | Detect foreground → resume polling + refetch stale queries. Background → pause polling (push notifications still arrive). |
| Offline | Show cached data via TanStack Query persistence. Queue-able actions (queue edits) stored locally, synced on reconnect. |
| Deep links | delve://character/{id}, delve://guild/{id}, delve://quest/{id} for sharing |
| App Store compliance | All purchases routed through Stripe web checkout (linked from app). No in-app purchase SDK to avoid 30% platform cut. Patron status synced via server. |
| Performance | Lazy-load routes. Limit PixiJS animations on low-end devices (detect via navigator.deviceMemory). |
| Safe areas | CSS env(safe-area-inset-*) for notch/dynamic island handling |
| Splash + icons | Generated via @capacitor/assets from a single source SVG |
10.3 Build Pipeline
Web Build:
pnpm build → SvelteKit static adapter → dist/
iOS:
npx cap sync ios → Xcode project updated
xcodebuild → .ipa
Distribute via App Store Connect (or TestFlight for beta)
Android:
npx cap sync android → Gradle project updated
./gradlew assembleRelease → .aab
Distribute via Google Play Console (or internal testing track)
CI/CD:
GitHub Actions:
- On push to main: build web, run tests, deploy to staging
- On tag (v*): build web + iOS + Android, deploy to production
- iOS builds via Xcode Cloud or self-hosted Mac runner
- Android builds via standard Linux runner
11. Infrastructure & DevOps
11.1 Deployment Architecture
Production Environment:
┌─────────────────────────────────────────────┐
│ CDN (Cloudflare Pages) │
│ Static SPA assets │
└─────────────────────────────────────────────┘
┌─────────────────────────────────────────────┐
│ Caddy (Reverse Proxy) │
│ TLS termination, /api → API │
└──────────────────┬──────────────────────────┘
│
┌────────┴────────┐
│ API Servers │
│ (2 replicas) │
└────────┬────────┘
│
┌─────────────┼─────────────┐
▼ ▼ ▼
┌─────────┐ ┌──────────┐ ┌──────────────┐
│Simulation│ │ Economy │ │ PVP/Crafting │
│Workers(N)│ │Worker(1) │ │ Workers │
└─────────┘ └──────────┘ └──────────────┘
│ │ │
└─────────────┼─────────────┘
│
┌────────┼────────┐
▼ ▼ ▼
PostgreSQL Redis Backblaze B2
11.2 Environment Strategy
| Environment | Purpose | Infrastructure |
|---|---|---|
| Local | Development | Docker Compose (all services) |
| Staging | Pre-production testing | Single node, real DB, seeded test data |
| Production | Live game | Multi-node, replicated DB, Redis cluster |
11.3 Database Operations
- Migrations: Drizzle-kit generates SQL migration files, applied via CI before deployment.
- Backups: PostgreSQL WAL archiving to S3 for point-in-time recovery. Daily full backups retained 30 days.
- Connection pooling: PgBouncer in transaction mode between API/workers and PostgreSQL.
11.4 Monitoring & Alerting
| Metric | Alert Threshold |
|---|---|
| API response time p95 | > 500ms |
| Simulation worker queue depth | > 1000 pending jobs |
| Error rate | > 1% of requests |
| Database connections | > 80% pool utilization |
| Redis memory | > 80% capacity |
| Disk space | > 85% utilization |
| Notification poll latency p95 | > 100ms |
12. Security
12.1 Authentication & Authorization
- Password hashing: Argon2id with recommended parameters
- Sessions: Opaque session tokens stored in Redis, 30-day expiry, refreshed on use
- CSRF: SameSite=Strict cookies + origin header validation
- OAuth2: Optional social login (Google, Discord) via standard OAuth2 flow
- Authorization: Every API call validates that the authenticated user owns the character being acted upon. No cross-user access.
12.2 Anti-Cheat
Because the game is server-authoritative, traditional client-side cheats are not possible. The main attack vectors are:
| Vector | Mitigation |
|---|---|
| API manipulation | All inputs validated server-side. Skill queue validated against owned skills. Gear validated against owned items. Gold amounts verified in transactions. |
| Race conditions | Economy worker serialized. SELECT FOR UPDATE on critical resources. Idempotency keys on purchase/trade endpoints. |
| Automation / botting | Rate limiting on all endpoints. CAPTCHA on account creation. Behavioral analysis on marketplace patterns (future). |
| Multi-accounting | Single email per account. IP-based rate limiting on account creation. Marketplace patterns flagged for review. |
| Time manipulation | All timers are server-side. completesAt timestamps are authoritative. Client countdown is display-only. |
12.3 Data Protection
- Encryption at rest: PostgreSQL with disk encryption. Redis with TLS.
- Encryption in transit: TLS everywhere (HTTPS, internal service communication).
- PII minimization: Only email stored. No real names, addresses, or payment details (payments handled by Stripe).
- GDPR/CCPA: Account deletion endpoint that cascades to all character data. Data export endpoint for portability.
- Input sanitization: All user-provided strings (character names, guild names, mail messages) sanitized against XSS.
13. Performance Targets
13.1 Client
| Metric | Target |
|---|---|
| Initial load (LCP) | < 2s on 4G |
| Bundle size (gzipped) | < 150KB initial, < 500KB total with lazy routes |
| Time to interactive | < 3s on mid-range mobile |
| Frame rate (UI transitions) | 60fps |
| Memory usage | < 100MB on mobile |
13.2 Server
| Metric | Target |
|---|---|
| API response time p50 | < 50ms |
| API response time p95 | < 200ms |
| Notification poll response | < 30ms (indexed query, tiny result set) |
| Simulation throughput | 100+ concurrent run resolutions per worker |
| Marketplace transaction throughput | 1000+ transactions/minute |
| Target concurrent players (initial) | 10,000 |
| Target concurrent players (scaled) | 100,000+ |
13.3 Scalability Path
Phase 1 (Launch): Single PostgreSQL instance, 2 API servers, 2 simulation workers, 1 economy worker. Supports ~10K concurrent players.
Phase 2 (Growth): Read replicas for PostgreSQL, Redis cluster, 4+ simulation workers, horizontal API scaling. Supports ~50K concurrent players.
Phase 3 (Scale): Database sharding by character ID range (if needed), dedicated PVP cluster, CDN for all static game data, regional API deployments for latency. Supports 100K+ concurrent players.
Without WebSocket servers, scaling is much simpler — every API server is stateless and interchangeable. No sticky sessions, no connection affinity, no need to coordinate which server holds which player’s connection.
Appendix A: Data-Driven Design
All game content (species, classes, items, skills, conditions, factions, etc.) is defined in TOML data files and referenced by string ID. See features/data-driven-architecture.md for the full design. Only engine-structural concepts are Rust enums.
Game Data Registry
#![allow(unused)]
fn main() {
// crates/game/src/registry.rs
// Loaded once at startup, shared via Axum state. All game content lives here.
pub struct GameData {
pub species: HashMap<String, SpeciesDef>,
pub classes: HashMap<String, ClassDef>,
pub backgrounds: HashMap<String, BackgroundDef>,
pub weapon_types: HashMap<String, WeaponTypeDef>,
pub equipment_slots: IndexMap<String, EquipmentSlotDef>, // ordered
pub rarities: Vec<RarityDef>, // ordered by tier
pub resources: HashMap<String, ResourceDef>,
pub conditions: HashMap<String, ConditionDef>,
pub skills: HashMap<String, SkillDef>,
pub item_templates: HashMap<String, ItemTemplateDef>,
pub creatures: HashMap<String, CreatureDef>,
pub quests: HashMap<String, QuestDef>,
pub factions: HashMap<String, FactionDef>,
pub reputation_tiers: Vec<ReputationTierDef>,
pub loot_tables: HashMap<String, LootTableDef>,
pub recipes: HashMap<String, RecipeDef>,
pub achievements: HashMap<String, AchievementDef>,
}
impl GameData {
pub fn load(data_dir: &Path) -> Result<Self, GameDataError>;
pub fn validate(&self) -> Result<(), GameDataError>; // cross-reference check
}
}
Data-Driven IDs (game content — stored as TEXT in Postgres)
#![allow(unused)]
fn main() {
// crates/types/src/ids.rs
// Validated newtype wrappers over String. The inner value is a key into GameData.
macro_rules! data_id {
($name:ident) => {
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, sqlx::Type)]
#[sqlx(transparent)]
pub struct $name(String);
impl $name {
pub fn as_str(&self) -> &str { &self.0 }
/// Unchecked construction — only use when loading from DB (already validated)
pub fn from_db(s: String) -> Self { Self(s) }
}
};
}
data_id!(SpeciesId);
data_id!(ClassId);
data_id!(SubclassId);
data_id!(BackgroundId);
data_id!(WeaponTypeId);
data_id!(SlotId);
data_id!(RarityId);
data_id!(ResourceId);
data_id!(ConditionId);
data_id!(SkillId);
data_id!(ItemTemplateId);
data_id!(CreatureId);
data_id!(QuestId);
data_id!(FactionId);
data_id!(RecipeId);
data_id!(AchievementId);
data_id!(LootTableId);
}
Character (uses data IDs, not enums)
#![allow(unused)]
fn main() {
// crates/types/src/character.rs
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
pub struct Character {
pub id: Uuid,
pub user_id: Uuid,
pub name: String,
pub species: SpeciesId, // TEXT column — e.g., "ironborn"
pub class: ClassId, // TEXT column — e.g., "vanguard"
pub subclass: Option<SubclassId>,
pub background: BackgroundId,
pub level: i16,
pub xp: i32,
pub gold: i64,
pub might: i16,
pub logic: i16,
pub speed: i16,
pub presence: i16,
pub fortitude: i16,
pub luck: i16,
}
}
Engine-Structural Enums (code, not data)
#![allow(unused)]
fn main() {
// These remain as Rust enums — they control code branching, not content.
/// Where an item is stored — each variant has different rules and UI.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum ItemLocation {
Equipped { slot: String }, // slot is a data ID (e.g., "main_hand")
Backpack { index: u16 },
Bank { index: u16 },
Mail { mail_id: Uuid },
Listed { listing_id: Uuid },
}
/// Run state machine — finite set of states with enforced transitions.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum RunStatus { InProgress, Completed, Failed }
/// Encounter dispatch — each variant calls a different resolver function.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum EncounterType { Combat, Trap, Decision, Hazard, Rest, Puzzle }
/// Queue condition evaluation — each variant has different evaluation logic.
/// The parameters (resource ID, condition ID) are data references.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum QueueCondition {
Always,
IfHpBelow { threshold: u8 },
IfHpAbove { threshold: u8 },
IfTargetHpBelow { threshold: u8 },
IfEnemyCountAbove { count: u8 },
IfEnemyCountBelow { count: u8 },
IfAllyHpBelow { threshold: u8 },
IfResourceAbove { resource: String, amount: u16 }, // resource is a data ID
IfResourceBelow { resource: String, amount: u16 },
IfHasCondition { condition: String }, // condition is a data ID
IfTargetHasCondition { condition: String },
}
}
Client/Server Type Sharing
Since the backend is Rust and the frontend is TypeScript, types are not shared at the language level. The API contract is the source of truth:
- OpenAPI spec: The API server exports an OpenAPI schema (via
utoipacrate), and the client generates TS types from it (viaopenapi-typescript). - Or: A
types.tsfile in the client is manually maintained to match the API. - Game data: The client fetches data definitions (species list, class list, rarity colors, etc.) via
GET /api/game-datawhich returns the full registry for UI rendering.
Appendix B: API Route Summary
| Method | Path | Description |
|---|---|---|
| Auth | ||
| POST | /api/auth/register | Create account |
| POST | /api/auth/login | Login, returns session |
| POST | /api/auth/logout | Destroy session |
| Characters | ||
| GET | /api/characters | List user’s characters |
| POST | /api/characters | Create character |
| GET | /api/characters/:id | Get character details |
| DELETE | /api/characters/:id | Delete character |
| PUT | /api/characters/:id/skill-queue | Save skill queue |
| GET | /api/characters/:id/skill-queues | List saved queues |
| Equipment | ||
| GET | /api/characters/:id/inventory | Get backpack + equipped |
| POST | /api/characters/:id/equip | Equip item |
| POST | /api/characters/:id/unequip | Unequip item |
| POST | /api/characters/:id/salvage | Salvage items |
| Quests & Runs | ||
| GET | /api/quest-board | Get available quests for character |
| POST | /api/runs | Start a dungeon run |
| GET | /api/runs/:id | Get run status + results |
| GET | /api/characters/:id/runs | Run history |
| Crafting | ||
| GET | /api/characters/:id/recipes | Known recipes |
| POST | /api/crafting/start | Start crafting job |
| GET | /api/characters/:id/crafting | Active crafting jobs |
| Gathering | ||
| POST | /api/gathering/start | Start expedition |
| GET | /api/characters/:id/gathering | Active expeditions |
| Marketplace | ||
| GET | /api/marketplace | Search listings |
| POST | /api/marketplace/list | Create listing |
| POST | /api/marketplace/buy/:id | Buy listing |
| DELETE | /api/marketplace/:id | Cancel listing |
| GET | /api/characters/:id/mail | Get mailbox |
| POST | /api/mail/send | Send mail |
| POST | /api/mail/:id/collect | Collect attachments |
| Guilds | ||
| POST | /api/guilds | Create guild |
| GET | /api/guilds/:id | Guild details |
| POST | /api/guilds/:id/join | Request to join |
| POST | /api/guilds/:id/invite | Invite player |
| PUT | /api/guilds/:id/settings | Update guild settings |
| POST | /api/guilds/:id/buff | Activate guild buff |
| PVP | ||
| POST | /api/pvp/queue | Enter arena queue |
| DELETE | /api/pvp/queue | Leave arena queue |
| GET | /api/pvp/history | Match history |
| GET | /api/pvp/leaderboard/:bracket | Leaderboard |
| Factions | ||
| GET | /api/characters/:id/reputation | All faction standings |
| GET | /api/factions/:id/vendor | Faction vendor inventory |
| Social | ||
| GET | /api/characters/:id/friends | Friends list |
| POST | /api/friends/add | Send friend request |
| POST | /api/friends/accept/:id | Accept request |
| Parties | ||
| POST | /api/parties | Create party |
| POST | /api/parties/:id/join | Join party |
| POST | /api/parties/:id/ready | Mark ready |
| GET | /api/parties/:id | Party details |
| Seasons & Dailies | ||
| GET | /api/characters/:id/dailies | Daily progress |
| GET | /api/characters/:id/weeklies | Weekly progress |
| GET | /api/seasons/current | Current season info |
| GET | /api/characters/:id/season-progress | Season track progress |
| Notifications | ||
| GET | /api/characters/:id/notifications | Unread notifications (polled) |
| POST | /api/characters/:id/notifications/read | Mark notifications as read |
| Achievements | ||
| GET | /api/characters/:id/achievements | Earned achievements |
| GET | /api/characters/:id/bestiary | Bestiary progress |
Delve — Hosting & Infrastructure Plan
Table of Contents
- Hosting Philosophy
- Phased Infrastructure
- Phase 1 — Solo Dev / Alpha
- Phase 2 — Closed Beta (500–2,000 players)
- Phase 3 — Launch (2,000–10,000 players)
- Phase 4 — Growth (10,000–50,000 players)
- Phase 5 — Scale (50,000+ players)
- Service-by-Service Breakdown
- Cost vs. Revenue Analysis
- Domain, DNS & CDN
- Backups & Disaster Recovery
- Local Development
- CI/CD Pipeline
- Monitoring Stack
- Decision Log
1. Hosting Philosophy
Delve is an indie project with an ethical monetization model ($3/mo subscriptions, no whales). Infrastructure costs must stay well below revenue at every stage. This means:
- No Kubernetes until it’s actually needed. K8s adds operational overhead that doesn’t pay off until you have multiple engineers and dozens of services. A single VPS running Docker Compose can handle thousands of concurrent players for Delve’s async workload.
- No managed cloud databases at small scale. A self-hosted PostgreSQL on a dedicated VPS is 5-10x cheaper than AWS RDS or equivalent, and fine when you’re the only operator.
- Graduate infrastructure with player count. Every upgrade should be a response to measured bottlenecks, not anticipated ones.
- Prefer value VPS providers. Hetzner, OVH, and Vultr offer 3-5x the compute-per-dollar compared to AWS/GCP/Azure for baseline infrastructure.
- Use managed services only where the operational cost of self-hosting exceeds the price difference. Email delivery, push notifications, and payment processing are always managed. Databases and app servers are self-hosted until scale demands otherwise.
2. Phased Infrastructure
| Phase | Players | Monthly Cost | Infrastructure |
|---|---|---|---|
| 1. Solo Dev / Alpha | 1–50 | ~$10–25 | Single VPS |
| 2. Closed Beta | 500–2,000 | ~$50–100 | 2 VPS + managed DB option |
| 3. Launch | 2,000–10,000 | ~$150–350 | 3-4 VPS, dedicated DB server |
| 4. Growth | 10,000–50,000 | ~$500–1,500 | Multi-server, read replicas, Redis cluster |
| 5. Scale | 50,000+ | ~$2,000–5,000+ | Managed K8s, multi-region |
3. Phase 1 — Solo Dev / Alpha
Goal: Get the game running, playable, testable. You and a handful of testers.
Infrastructure
Single VPS (Hetzner CX32 or equivalent)
├── 4 vCPU, 8 GB RAM, 80 GB NVMe
├── Docker Compose runs everything:
│ ├── Caddy (reverse proxy + auto TLS)
│ ├── delve-api (Rust binary)
│ ├── delve-workers (Rust binary — simulation, economy, crafting, pvp)
│ ├── PostgreSQL 16
│ └── Redis 7 (Valkey)
├── SvelteKit SPA served by Caddy as static files
└── Cost: ~€8/mo ($9/mo) on Hetzner Cloud
Provider: Hetzner Cloud
| Spec | Value |
|---|---|
| Plan | CX32 (shared vCPU) |
| CPU | 4 vCPU |
| RAM | 8 GB |
| Disk | 80 GB NVMe |
| Transfer | 20 TB/mo |
| Location | Falkenstein, DE (or Ashburn, VA for US) |
| Cost | €7.59/mo (~$8.50) |
Why Hetzner: Best price-to-performance for European/US hosting. Their ARM options (CAX line) are even cheaper if the stack supports it (Rust cross-compiles to aarch64 trivially, PostgreSQL + Redis run fine on ARM).
What Runs Where
Everything on one box. Docker Compose with a single docker-compose.yml. PostgreSQL data on a persistent volume. Caddy handles TLS via Let’s Encrypt.
Backups
- PostgreSQL: Daily
pg_dumpto Hetzner’s 20GB backup space (free with server) or to a Backblaze B2 bucket ($0.005/GB/mo) - Schedule: cron job at 04:00 UTC
Total Phase 1 Cost
| Service | Monthly Cost |
|---|---|
| Hetzner CX32 | $9 |
| Domain (.com) | ~$1 (amortized) |
| Backblaze B2 (backups) | $0.10 |
| Email (Resend free tier) | $0 |
| Total | ~$10/mo |
4. Phase 2 — Closed Beta (500–2,000 players)
Goal: Real players, real load. Validate the game systems, economy, and multiplayer features. Start collecting subscription revenue.
Infrastructure
VPS 1 — Application (Hetzner CX42)
├── 8 vCPU, 16 GB RAM, 160 GB NVMe
├── Docker Compose:
│ ├── Caddy
│ ├── delve-api
│ ├── delve-workers (×2 containers)
│ ├── delve-workers --scheduler (×1)
│ └── Redis 7
└── Cost: ~€16/mo ($18/mo)
VPS 2 — Database (Hetzner CX32)
├── 4 vCPU, 8 GB RAM, 80 GB NVMe
├── PostgreSQL 16 (dedicated, not sharing CPU with app)
├── Automated WAL backups to B2
└── Cost: ~€8/mo ($9/mo)
Why Split the Database
PostgreSQL performance degrades when it competes for CPU and I/O with the application. Isolating it on a dedicated VPS is the highest-impact scaling move at this stage, and it costs only $9/mo.
Push Notifications
At this phase, mobile testers need push notifications.
| Service | Free Tier | Paid |
|---|---|---|
| Firebase Cloud Messaging (FCM) | Unlimited Android + web push | Free |
| APNs (via Firebase) | Unlimited iOS push | Free (Apple Developer Program $99/yr already required) |
FCM is free at any scale. The only cost is the Apple Developer Program membership ($99/yr) required to publish to iOS.
Payments
Stripe for subscription billing and one-time purchases. Stripe processes payment on the web (not via in-app purchase), so no App Store / Play Store commission on subscriptions.
| Service | Cost |
|---|---|
| Stripe | 2.9% + $0.30 per transaction |
At $3/mo subscription: Stripe takes ~$0.39, you keep ~$2.61 per subscriber.
Transactional email for account verification, password reset, subscription receipts.
| Service | Free Tier | After Free |
|---|---|---|
| Resend | 3,000 emails/mo | $20/mo for 50K |
| Postmark | 100 emails/mo | $15/mo for 10K |
Resend’s free tier covers beta easily. Upgrade to paid at launch.
Total Phase 2 Cost
| Service | Monthly Cost |
|---|---|
| Hetzner CX42 (app) | $18 |
| Hetzner CX32 (db) | $9 |
| Backblaze B2 | $0.50 |
| Domain + DNS | $1 |
| Apple Developer | $8 (amortized) |
| Stripe fees | Variable |
| Email (Resend free) | $0 |
| Total | ~$37/mo (before Stripe) |
Revenue at This Phase
If 500 beta players, 15% subscribe: 75 × $2.61 net = ~$196/mo. Comfortably profitable on infrastructure.
5. Phase 3 — Launch (2,000–10,000 players)
Goal: Public launch. Stable, performant, ready for organic growth.
Infrastructure
VPS 1 — API (Hetzner CX42)
├── 8 vCPU, 16 GB RAM, 160 GB NVMe
├── Caddy (reverse proxy)
├── delve-api (×2 containers, load balanced by Caddy)
└── Cost: ~€16/mo ($18/mo)
VPS 2 — Workers (Hetzner CX42)
├── 8 vCPU, 16 GB RAM, 160 GB NVMe
├── delve-workers (×4 containers)
├── delve-workers --scheduler (×1)
└── Cost: ~€16/mo ($18/mo)
VPS 3 — Database (Hetzner CX42)
├── 8 vCPU, 16 GB RAM, 160 GB NVMe
├── PostgreSQL 16 (primary)
├── PgBouncer (connection pooling)
├── Automated WAL archiving to B2
└── Cost: ~€16/mo ($18/mo)
VPS 4 — Redis + Monitoring (Hetzner CX32)
├── 4 vCPU, 8 GB RAM, 80 GB NVMe
├── Redis 7 (dedicated, persistent)
├── Prometheus + Grafana + Loki (monitoring stack)
└── Cost: ~€8/mo ($9/mo)
CDN — Cloudflare (Free plan)
├── Static SPA assets, game data JSON
├── DDoS protection
└── Cost: $0 (free plan is sufficient)
Why 4 Servers
| Server | Bottleneck it addresses |
|---|---|
| API | Handles all REST polling traffic. Isolated so poll load doesn’t compete with simulation CPU. Rust’s efficiency means a CX42 handles this easily. |
| Workers | Simulation is CPU-intensive. Isolated so a spike in dungeon completions doesn’t lag the API. |
| Database | PostgreSQL needs dedicated I/O. Shared CPU causes query latency spikes. |
| Redis + Monitoring | Redis needs stable memory. Monitoring (Prometheus, Grafana) is a nice-to-have that shouldn’t compete with game systems. |
Object Storage
Run replay logs (JSONB stored in DB for now, but if they get large):
| Service | Cost |
|---|---|
| Backblaze B2 | $0.005/GB/mo storage, $0.01/GB egress |
| Hetzner Object Storage | €0.0065/GB/mo |
At 10K players with ~50KB per run log and 3 runs/day average: ~1.5 GB/day = ~45 GB/mo. Cost: ~$0.25/mo on B2. Negligible.
Total Phase 3 Cost
| Service | Monthly Cost |
|---|---|
| Hetzner CX42 (API) | $18 |
| Hetzner CX42 (workers) | $18 |
| Hetzner CX42 (database) | $18 |
| Hetzner CX32 (Redis + monitoring) | $9 |
| Cloudflare (CDN) | $0 |
| Backblaze B2 | $2 |
| Resend (email) | $20 |
| Apple Developer | $8 |
| Domain + DNS | $1 |
| Sentry (error tracking, free tier) | $0 |
| Total | ~$94/mo |
Revenue at This Phase
If 5,000 active players, 15% subscribe: 750 × $2.61 = ~$1,958/mo. Plus one-time purchases (~$0.50 ARPU across all players): +$2,500 cumulative.
Infrastructure is ~6% of subscription revenue. Very healthy margin.
6. Phase 4 — Growth (10,000–50,000 players)
Goal: Handle sustained growth. Start introducing redundancy for uptime guarantees.
Infrastructure
Load Balancer — Hetzner Load Balancer
├── Routes /api/* to API pool
├── Health checks, automatic failover
└── Cost: €6/mo
API Pool (2× Hetzner CX42)
├── 8 vCPU, 16 GB RAM each
├── delve-api + Caddy per node
└── Cost: 2 × €16 = €32/mo
Worker Pool (3× Hetzner CX42)
├── delve-workers (distributed via Redis job queue)
├── Economy queue consumed serially by one instance
├── delve-workers --scheduler on one designated node
└── Cost: 3 × €16 = €48/mo
Database — Primary + Read Replica
├── Primary: Hetzner CX52 (16 vCPU, 32 GB RAM)
│ ├── PostgreSQL 16 + PgBouncer
│ └── Cost: €36/mo
├── Read Replica: Hetzner CX42 (8 vCPU, 16 GB RAM)
│ ├── Streaming replication, serves read-heavy queries
│ │ (leaderboards, marketplace search, profile lookups)
│ └── Cost: €16/mo
└── Total: €52/mo
Redis — Hetzner CX42
├── 16 GB RAM, persistent, Sentinel for failover (or Valkey cluster)
└── Cost: €16/mo
Monitoring — Hetzner CX32
├── Prometheus, Grafana, Loki, Alertmanager
├── Sentry (cloud, Team plan for higher limits)
└── Cost: €8/mo + $26/mo Sentry
Search
At this scale, marketplace search benefits from a dedicated search engine:
| Service | Hosting | Cost |
|---|---|---|
| Meilisearch | Self-hosted on worker VPS | $0 (already have spare capacity) |
| Meilisearch Cloud | Managed | $30/mo (if self-hosting is too much operational burden) |
Total Phase 4 Cost
| Service | Monthly Cost |
|---|---|
| Hetzner Load Balancer | $7 |
| API servers (2×) | $36 |
| Worker servers (3×) | $54 |
| Database primary | $40 |
| Database replica | $18 |
| Redis | $18 |
| Monitoring VPS | $9 |
| Sentry Team | $26 |
| Cloudflare Pro | $20 |
| Backblaze B2 | $5 |
| Resend | $20 |
| Apple Developer | $8 |
| Domain | $1 |
| Total | ~$262/mo |
Revenue at This Phase
If 25,000 active players, 15% subscribe: 3,750 × $2.61 = ~$9,788/mo. Infrastructure is ~3% of subscription revenue.
At this point you’re making real money and could afford managed services or an ops hire if needed.
7. Phase 5 — Scale (50,000+ players)
Goal: Professional-grade infrastructure. Consider managed Kubernetes, multi-region, and a dedicated ops approach.
When to Move to Kubernetes
Move to K8s when at least two of these are true:
- More than one person is deploying and operating infrastructure
- You need auto-scaling (traffic is spiky, not steady)
- You’re managing 15+ containers across 10+ servers and Docker Compose is unwieldy
- You need zero-downtime rolling deployments across multiple server pools
Infrastructure
Kubernetes Cluster (Hetzner Cloud or CIVO)
├── Control plane (managed by provider)
├── Node pool — API: 3× CX42 (auto-scaling 2-5)
├── Node pool — Workers: 4× CX42 (auto-scaling 2-8)
└── Estimated: €150-350/mo for nodes
Managed PostgreSQL (Hetzner Managed DB or Neon)
├── Primary: 16 vCPU, 64 GB RAM
├── 2 read replicas
├── Automated backups, point-in-time recovery
├── Connection pooling (PgBouncer built-in)
└── Estimated: €150-300/mo
Managed Redis (Upstash or self-hosted Valkey cluster)
├── 3-node cluster for HA
├── 32 GB total memory
└── Estimated: €50-100/mo
Multi-Region Consideration:
├── If majority US players: US primary + EU edge CDN
├── If global: US primary + EU secondary with DB replication
└── Add ~50-100% to compute costs for second region
Total Phase 5 Cost (Estimated)
| Service | Monthly Cost |
|---|---|
| Kubernetes nodes | $300–500 |
| Managed PostgreSQL | $200–350 |
| Managed Redis | $60–120 |
| Monitoring (Grafana Cloud or self-hosted) | $50–100 |
| Cloudflare Pro | $20 |
| Object storage | $15 |
| Email (Resend or Postmark) | $40 |
| Sentry Business | $80 |
| Push notifications | $0 (FCM free) |
| Total | ~$800–1,300/mo |
Revenue at This Phase
If 50,000 active players, 15% subscribe: 7,500 × $2.61 = ~$19,575/mo. If 100,000 active players: ~$39,150/mo.
Infrastructure at 2-5% of revenue. Very healthy.
8. Service-by-Service Breakdown
PostgreSQL
| Phase | Setup | Cost |
|---|---|---|
| 1–2 | Single instance on shared/dedicated VPS | $0–9 |
| 3 | Dedicated VPS, PgBouncer, WAL backups | $18 |
| 4 | Primary + read replica, PgBouncer | $58 |
| 5 | Managed, primary + 2 replicas | $200–350 |
Key configuration:
shared_buffers: 25% of RAMeffective_cache_size: 75% of RAMwork_mem: 64MB (for marketplace queries, leaderboard aggregation)max_connections: 200 (with PgBouncer in front, actual app connections pool at ~20 per API instance)- WAL level:
replica(for streaming replication readiness from day 1)
Redis
| Phase | Setup | Cost |
|---|---|---|
| 1–2 | Shared VPS, appendonly persistence | $0 |
| 3 | Dedicated VPS | $9 |
| 4 | Dedicated VPS, Sentinel | $18 |
| 5 | Cluster or managed | $60–120 |
Memory estimation at 50K players:
- Sessions: ~50K × 0.5KB = 25MB
- Leaderboards: ~10 boards × 50K entries × 0.1KB = 50MB
- Job queue: ~10K pending × 1KB = 10MB
- PVP queues: negligible
- Rate limiting: ~50K counters × 0.1KB = 5MB
- Total: ~90MB — Redis memory is not a concern until extreme scale
Caddy / Load Balancer
| Phase | Setup |
|---|---|
| 1–3 | Caddy on the API VPS (reverse proxy + auto TLS) |
| 4+ | Hetzner Load Balancer ($7/mo) in front of Caddy instances |
Caddy handles:
- Automatic HTTPS via Let’s Encrypt
- HTTP/2
- Static file serving (SPA bundle)
- Gzip/brotli compression
Job Queue
The custom Redis-backed job queue runs in-process on worker binaries. No separate infrastructure. Job types and their expected volumes:
| Job Type | Trigger | Volume (10K players) |
|---|---|---|
resolve-run | Run timer completes | ~30K/day |
resolve-craft | Craft timer completes | ~15K/day |
resolve-gathering | Expedition completes | ~10K/day |
marketplace-buy | Player purchases listing | ~5K/day |
auction-expiry | Every 5 min (batch) | 288/day |
pvp-resolve | Match found | ~3K/day |
daily-reset | 00:00 UTC | 1/day |
weekly-reset | Monday 00:00 UTC | 1/week |
mail-delivery | 1 hour after send | ~2K/day |
guild-buff-expiry | Every 1 min (batch) | 1,440/day |
9. Cost vs. Revenue Analysis
Revenue Model (from monetization doc)
- Subscription: $3/mo, ~$2.61 net after Stripe fees
- Target subscription rate: 15% of active players
- One-time purchases: ~$0.50 ARPU lifetime average
Break-Even Table
| Active Players | Subscribers (15%) | Subscription Revenue | Infra Cost | Margin |
|---|---|---|---|---|
| 500 | 75 | $196/mo | $37/mo | $159 |
| 2,000 | 300 | $783/mo | $60/mo | $723 |
| 5,000 | 750 | $1,958/mo | $94/mo | $1,864 |
| 10,000 | 1,500 | $3,915/mo | $150/mo | $3,765 |
| 25,000 | 3,750 | $9,788/mo | $262/mo | $9,526 |
| 50,000 | 7,500 | $19,575/mo | $1,000/mo | $18,575 |
| 100,000 | 15,000 | $39,150/mo | $2,500/mo | $36,650 |
Infrastructure stays at 2-6% of revenue across all phases. The async, server-resolved nature of Delve means the compute cost per player is very low compared to real-time multiplayer games.
Break-Even Point
At $10/mo infrastructure (Phase 1), you need 4 subscribers to break even on hosting. That’s ~27 active players at 15% subscription rate. The game is profitable on infrastructure almost immediately once anyone is paying.
The real costs are development time (your time), not infrastructure.
10. Domain, DNS & CDN
Domain
Register delve.game or similar via Cloudflare Registrar (at-cost pricing, no markup).
DNS
Cloudflare DNS (free):
delve.game→ Cloudflare CDN → origin server (static SPA)api.delve.game→ origin server (REST API, proxied through Cloudflare is fine — no WebSocket concerns)
CDN
Cloudflare free plan:
- Cache the SPA shell (
index.html, JS, CSS, images) - Cache static game data files (item templates, skill definitions exported as JSON)
- DDoS protection (useful once the game has any visibility)
- Edge compression (Brotli)
Since there are no WebSockets, all traffic can be proxied through Cloudflare from day one — free DDoS protection and caching for the SPA assets. API traffic proxied through Cloudflare adds ~10-20ms latency but gains DDoS protection, which is worth it.
At Phase 4+, Cloudflare Pro ($20/mo) adds:
- Better DDoS mitigation
- WAF rules
- Cache analytics
11. Backups & Disaster Recovery
Backup Strategy
| Data | Method | Frequency | Retention | Storage |
|---|---|---|---|---|
| PostgreSQL | pg_dump (Phase 1–2), WAL archiving (Phase 3+) | Daily full + continuous WAL | 30 days full, 7 days WAL | Backblaze B2 |
| Redis | RDB snapshots | Every 6 hours | 7 days | Local + B2 |
| Run logs | Stored in PostgreSQL (JSONB) | Covered by DB backup | Same as DB | Same as DB |
| Application code | Git repository | Every push | Infinite | GitHub |
| Docker images | Container registry | Every deploy | 30 versions | GitHub Container Registry (free for public, 500MB free for private) |
| Secrets/config | Encrypted in repo or secrets manager | Every change | Infinite | Git (encrypted) or Doppler/Infisical |
Disaster Recovery
| Scenario | Recovery |
|---|---|
| App server dies | Deploy new VPS from Docker image (10 min). Stateless — no data loss. |
| Database server dies | Provision new VPS, restore from latest B2 backup + WAL replay. RPO: minutes. RTO: 30–60 min. |
| Redis dies | Provision new VPS, restore from RDB snapshot. Sessions regenerate on next login. Leaderboards rebuild from DB. RPO: 6 hours. RTO: 15 min. |
| Complete datacenter failure | Restore all from B2 backups to a different Hetzner datacenter (or different provider entirely). RTO: 2-4 hours. |
| Corrupt database (bad migration, bug) | Point-in-time recovery from WAL archive. Restore to any moment before corruption. |
Backup Testing
Monthly: restore the latest PostgreSQL backup to a temporary VPS and run a basic health check query. Automate this in CI. Untested backups are not backups.
12. Local Development
Docker Compose (dev)
# docker-compose.yml (development)
services:
postgres:
image: postgres:16-alpine
ports: ["5432:5432"]
environment:
POSTGRES_DB: delve
POSTGRES_USER: delve
POSTGRES_PASSWORD: devpassword
volumes:
- pgdata:/var/lib/postgresql/data
redis:
image: valkey/valkey:7-alpine
ports: ["6379:6379"]
mailpit:
image: axllent/mailpit
ports: ["8025:8025", "1025:1025"]
# Catches all outgoing email in dev — accessible at localhost:8025
volumes:
pgdata:
The Rust API server, workers, and SvelteKit dev server run directly on the host (not in containers) for fast iteration. They connect to the containerized dependencies.
# Terminal 1: Start dependencies
docker compose up -d
# Terminal 2: API server (with cargo-watch for auto-rebuild)
cargo watch -x 'run --bin api'
# Terminal 3: Workers (with cargo-watch)
cargo watch -x 'run --bin workers'
# Terminal 4: Client (SvelteKit dev server)
pnpm --filter client dev
Rust build times: Initial full build will take 1-3 minutes. Incremental rebuilds via cargo-watch are typically 5-15 seconds. Use cargo-chef in the Docker multi-stage build to cache dependency compilation separately from application code.
Seed Data
A seed binary (cargo run --bin seed) populates the dev database with:
- Test user accounts (free, patron, admin)
- Characters at various levels with gear
- Active marketplace listings
- Guild with members
- In-progress runs, crafts, and expeditions
13. CI/CD Pipeline
GitHub Actions
On Pull Request:
├── cargo fmt --check (formatting)
├── cargo clippy (linting)
├── cargo test (unit + integration tests against Docker Compose services)
├── cargo sqlx prepare --check (verify query cache is up to date)
├── Client: pnpm lint + pnpm check + pnpm build
└── Build check: cargo build --release (ensure it compiles)
On Push to main:
├── All PR checks
├── Build Docker images (delve-api, delve-workers) via cargo-chef multi-stage
├── Build SvelteKit SPA
├── Push images to GitHub Container Registry
├── Deploy to staging (auto)
└── Smoke test against staging
On Git Tag (v*):
├── All main checks
├── Build + push production Docker images
├── Deploy to production (manual approval gate)
├── Build iOS (Xcode Cloud or self-hosted Mac runner)
├── Build Android (.aab)
├── Upload to TestFlight / Play Console internal track
└── Tag container images with version
Deployment (Phase 1–4)
Simple SSH-based deployment. No need for fancy orchestration:
# deploy.sh (run from CI or manually)
ssh deploy@app-server "
cd /opt/delve &&
docker compose pull &&
docker compose up -d --remove-orphans
"
ssh deploy@worker-server "
cd /opt/delve &&
docker compose pull &&
docker compose up -d --remove-orphans
"
Database migrations run as a separate step before app deployment:
ssh deploy@app-server "
cd /opt/delve &&
docker compose run --rm api sqlx migrate run
"
Deployment (Phase 5 — Kubernetes)
Helm charts or Kustomize manifests. Rolling deployments with readiness probes. Migrations as init containers or pre-deploy jobs.
14. Monitoring Stack
Phase 1–2: Minimal
- Uptime: UptimeRobot (free, 50 monitors) — ping API endpoint every 5 min
- Errors: Sentry free tier (5K events/mo)
- Logs:
docker compose logs— review manually
Phase 3+: Full Stack
All self-hosted on the monitoring VPS:
Prometheus
├── Scrapes API server metrics (request rate, latency, error rate)
├── Scrapes worker metrics (queue depth, job duration, failure rate)
├── Scrapes PostgreSQL (pg_exporter: connections, query time, replication lag)
├── Scrapes Redis (redis_exporter: memory, commands/sec, keyspace)
├── Scrapes Node exporter (CPU, RAM, disk, network per VPS)
└── Retention: 30 days local
Grafana
├── Dashboards:
│ ├── Game Overview: active players, runs/hour, marketplace volume
│ ├── API Performance: request rate, p50/p95/p99 latency, error rate
│ ├── Worker Health: queue depths, processing time, failure rate
│ ├── Database: query latency, connections, replication lag
│ ├── Redis: memory usage, commands/sec, hit rate
│ └── Infrastructure: CPU, RAM, disk, network per server
└── Alerts → Discord webhook (or email)
Loki
├── Aggregates structured JSON logs from all services (via tracing + tracing-subscriber)
├── Queryable from Grafana
└── Retention: 14 days
Alertmanager
├── Routes alerts to Discord channel
├── Key alerts:
│ ├── API p95 > 500ms for 5 min
│ ├── Any worker queue > 1000 pending for 10 min
│ ├── PostgreSQL replication lag > 30s
│ ├── Any server disk > 85%
│ ├── Any server CPU > 90% sustained 10 min
│ └── Error rate > 1% for 5 min
└── Silence/snooze via Grafana UI
Application Metrics (exposed via Prometheus client)
#![allow(unused)]
fn main() {
// Key metrics (via metrics + metrics-exporter-prometheus crates):
// API server:
http_request_duration_seconds // histogram, labeled by route + method
http_requests_total // counter, labeled by route + status
notification_poll_duration_ms // histogram — track polling endpoint performance
// Workers:
simulation_runs_resolved_total // counter
simulation_run_duration_seconds // histogram
job_queue_depth // gauge, labeled by queue name
job_processing_duration_seconds // histogram, labeled by job type
job_failures_total // counter, labeled by job type
// Business metrics:
runs_started_total // counter
marketplace_transactions_total // counter
subscriptions_active // gauge (query from DB, cached)
}
15. Decision Log
Key hosting decisions and the reasoning behind them. Update this as decisions change.
| Decision | Chosen | Alternatives Considered | Why |
|---|---|---|---|
| Backend language | Rust | TypeScript/Node.js, Go, Python, C# | Best performance for CPU-bound simulation engine. Strong type system. Single binary deploys (~10-20MB Docker images). No GC pauses. |
| API framework | Axum | Actix-web, Rocket, Warp | Tokio-native, ergonomic extractors, tower middleware ecosystem. Most active Rust web framework. |
| Client-server communication | REST + polling | WebSockets, SSE, long polling | Game is async — players wait minutes to hours. Polling every 30-60s is adequate. Eliminates persistent connection management entirely. Server stays stateless. |
| Chat | Discord (external) | In-game WebSocket chat | Community already lives on Discord. Eliminates real-time messaging system, chat storage, moderation tools, presence tracking. Massive complexity reduction. |
| Primary hosting provider | Hetzner Cloud | AWS, DigitalOcean, Vultr, OVH | 3-5x cheaper than AWS for equivalent specs. Reliable. EU + US datacenters. ARM options for future savings. |
| Container orchestration (Phase 1–4) | Docker Compose | Kubernetes, Nomad, bare metal | K8s is overkill for <10 containers on <10 servers. Docker Compose is simple, well-understood, and sufficient. |
| Database | Self-hosted PostgreSQL | Neon, Supabase, PlanetScale, AWS RDS | Self-hosted is $9–40/mo vs. $50–200/mo managed for equivalent specs. Acceptable risk for a single operator. Move to managed at Phase 5. |
| CDN | Cloudflare (free) | Bunny CDN, Fastly, AWS CloudFront | Free tier is generous. DDoS protection included. Upgrade to Pro at Phase 4 ($20/mo). |
| Object storage | Backblaze B2 | AWS S3, Hetzner Object Storage, Cloudflare R2 | Cheapest. S3-compatible API. Free egress via Cloudflare bandwidth alliance. |
| Resend | Postmark, SendGrid, SES | Good free tier (3K/mo). Simple API. Scales cheaply. | |
| Push notifications | Firebase (FCM) | OneSignal, Pusher | Free at any scale. Direct integration with Capacitor. |
| Payments | Stripe (web checkout) | In-app purchase (Apple/Google) | Avoids 30% platform commission. Web checkout is compliant if not selling digital goods consumed within the app (subscriptions for server-side speed are defensible). |
| Monitoring | Self-hosted Prometheus/Grafana | Datadog, New Relic, Grafana Cloud | Free. Full control. Datadog would cost $100+/mo for equivalent coverage. |
| Error tracking | Sentry | Bugsnag, self-hosted | Best free tier. Rust + JS SDKs. Essential for client + server error visibility. |
| Secrets management | Environment variables (Phase 1–3), Infisical (Phase 4+) | Doppler, HashiCorp Vault, AWS Secrets Manager | Env vars are fine while it’s one person deploying. Move to a proper secrets manager when there are multiple operators. |
| App Store payments strategy | Web checkout via Stripe | Native in-app purchase | Apple/Google take 30% (or 15% for small business). At $3/mo, that’s $0.45–0.90 per sub lost. Web checkout keeps the full margin minus Stripe’s 2.9%+$0.30. Requires careful compliance with store policies. |
Delve — Feature Implementation Plan
Features are ordered sequentially — each builds on the ones before it. Implement in order.
Foundation
| # | Feature | Description | Key Deps |
|---|---|---|---|
| 00 | Project Foundation | Cargo workspace, DB, Docker, CI, SvelteKit skeleton | — |
| 01 | Authentication | Registration, login, sessions, rate limiting | 00 |
| 02 | Character Creation | Species, classes, backgrounds, attribute point-buy | 01 |
Core Game Loop
| # | Feature | Description | Key Deps |
|---|---|---|---|
| 03 | Items & Inventory | Gear templates, random bonuses, equip/unequip, salvage | 02 |
| 04 | Character Progression | Leveling, feats, subclasses, weapon proficiency, Paragon | 02, 03 |
| 05 | Skill Queue | Queue builder, conditions, validation, multiple saved queues | 04 |
| 06 | Combat Engine | d100 system, simulation engine, encounters, loot resolution | 05, 03 |
| 07 | Quests & Runs | Quest board, run lifecycle, worker resolution, rewards | 06 |
Secondary Systems
| # | Feature | Description | Key Deps |
|---|---|---|---|
| 08 | Crafting & Gathering | 5 crafting + 4 gathering professions, idle timers | 03 |
| 09 | Economy & Marketplace | Auction house, mail, NPC vendors, gold sinks | 03 |
| 10 | Guilds & Social | Guilds, friends, parties, raid lobbies | 02, 09 |
| 11 | PVP System | Arena, matchmaking, stat normalization, ELO, seasons | 06, 05 |
| 12 | Factions & Reputation | 7 factions, rep tiers, conflict pairs, vendors | 07 |
Engagement & Polish
| # | Feature | Description | Key Deps |
|---|---|---|---|
| 13 | Notifications | Polling notifications, push (FCM/APNs), preferences | 00, 01 |
| 14 | Dailies, Weeklies & Seasons | Daily bounties, weekly challenges, seasonal events | 07, 04 |
| 15 | Achievements & Collections | Achievements, bestiary, leaderboards, titles | 07+ |
| 16 | Monetization | Patron subscription, purchases, Stripe integration | 01, 07, 08 |
| 17 | Gear Enhancement & Reforging | +1 to +5 upgrades, re-roll random bonuses | 03, 08 |
Distribution
| # | Feature | Description | Key Deps |
|---|---|---|---|
| 18 | Mobile Wrapping | Capacitor iOS/Android, push, deep links, app store | 13, all UI |
Implementation Notes
- Features 00-07 constitute the MVP — a playable game loop. Prioritize these.
- Features 08-12 add depth. Can be built in parallel by multiple contributors.
- Features 13-17 are engagement/monetization layers. Feature 13 (notifications) should be wired in early even if other engagement features come later.
- Feature 18 (mobile) can begin as soon as the client UI is stable, but app store submission should wait until the core loop is solid.
- Each feature file lists its own technical tasks and tests. Tasks within a feature are ordered but can sometimes be parallelized where noted.
- Data-Driven Architecture — READ THIS FIRST. All game content (species, classes, items, conditions, etc.) is defined in TOML data files and referenced by string ID. No Rust enums for game content. No Postgres enums. Adding new content = add a TOML file, restart server.
Data-Driven Architecture
Principle
Adding new game content should never require recompilation. Species, classes, items, skills, factions, status effects — all of it is defined in data files (TOML), loaded at server startup, and validated at load time. The database stores string IDs that reference data definitions, not Rust enums.
This means:
- Adding a 10th species = add a TOML file, restart server
- Adding a new status effect = add it to conditions.toml, restart server
- Adding a new weapon type = add a TOML file, restart server
- No code changes, no recompilation, no database migration
What Is Data (String IDs)
Everything that describes “what exists in the game world” is defined in TOML files under data/ and referenced by string ID throughout the codebase and database.
| Category | Example IDs | Defined In |
|---|---|---|
| Species | "ironborn", "verdani", "kharren" | data/species/*.toml |
| Classes | "vanguard", "shade", "arcanist" | data/classes/*.toml |
| Subclasses | "bulwark", "assassin", "evoker" | data/classes/*.toml (nested) |
| Backgrounds | "soldier", "scholar", "merchant" | data/backgrounds/*.toml |
| Weapon Types | "sword", "axe", "bow", "staff" | data/weapon_types/*.toml |
| Equipment Slots | "main_hand", "head", "ring_1" | data/equipment_slots.toml |
| Rarity Tiers | "common", "rare", "legendary" | data/rarities.toml |
| Resource Types | "stamina", "mana", "fury" | data/resources.toml |
| Status Effects | "poisoned", "stunned", "hasted" | data/conditions/*.toml |
| Skills | "power_strike", "fireball" | data/skills/*.toml |
| Item Templates | "iron_longsword", "leather_cap" | data/items/**/*.toml |
| Creatures | "goblin_scout", "cave_troll" | data/creatures/*.toml |
| Quests | "goblin_warren", "deep_mines" | data/quests/**/*.toml |
| Factions | "iron_compact", "shadow_court" | data/factions/*.toml |
| Reputation Tiers | "hostile", "friendly", "exalted" | data/factions/reputation_tiers.toml |
| Crafting Professions | "blacksmithing", "alchemy" | data/crafting/professions.toml |
| Gathering Professions | "mining", "herbalism" | data/gathering/professions.toml |
| Recipes | "iron_longsword_recipe" | data/crafting/recipes/*.toml |
| Achievements | "first_blood", "dungeon_crawler" | data/achievements/*.toml |
| Loot Tables | "goblin_bounty_t1" | data/loot_tables/*.toml |
| Bonus Property Pools | "offensive", "defensive" | data/bonus_pools.toml |
Database Columns
All of these are stored as TEXT in PostgreSQL. The column species on the characters table is TEXT NOT NULL, not a Postgres enum. This means adding a new species never requires a migration.
-- YES: plain text, validated by the application
CREATE TABLE characters (
species TEXT NOT NULL, -- validated against data/species/ at runtime
class TEXT NOT NULL, -- validated against data/classes/ at runtime
background TEXT NOT NULL, -- validated against data/backgrounds/ at runtime
...
);
-- NO: Postgres enums require ALTER TYPE migrations to add values
-- CREATE TYPE species AS ENUM ('ironborn', 'verdani', ...);
Rust Types
Instead of Rust enums with hardcoded variants, game content IDs are wrapped in validated newtypes:
#![allow(unused)]
fn main() {
/// A validated reference to a species defined in data/species/*.toml
/// The inner String is guaranteed to be a valid species ID at construction time.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, sqlx::Type)]
#[sqlx(transparent)]
pub struct SpeciesId(String);
impl SpeciesId {
/// Create a SpeciesId, validating it exists in the game data registry.
pub fn new(id: &str, registry: &GameData) -> Result<Self, GameDataError> {
if registry.species.contains_key(id) {
Ok(Self(id.to_string()))
} else {
Err(GameDataError::UnknownSpecies(id.to_string()))
}
}
pub fn as_str(&self) -> &str { &self.0 }
}
// Same pattern for ClassId, BackgroundId, WeaponTypeId, SlotId,
// RarityId, ResourceTypeId, ConditionId, FactionId, SkillId, etc.
}
Data Registry
All game data is loaded into a GameData struct at startup and shared via Axum state:
#![allow(unused)]
fn main() {
pub struct GameData {
pub species: HashMap<String, SpeciesDef>,
pub classes: HashMap<String, ClassDef>,
pub backgrounds: HashMap<String, BackgroundDef>,
pub weapon_types: HashMap<String, WeaponTypeDef>,
pub equipment_slots: HashMap<String, EquipmentSlotDef>,
pub rarities: Vec<RarityDef>, // ordered by tier (common first)
pub resources: HashMap<String, ResourceDef>,
pub conditions: HashMap<String, ConditionDef>,
pub skills: HashMap<String, SkillDef>,
pub item_templates: HashMap<String, ItemTemplateDef>,
pub creatures: HashMap<String, CreatureDef>,
pub quests: HashMap<String, QuestDef>,
pub factions: HashMap<String, FactionDef>,
pub reputation_tiers: Vec<ReputationTierDef>, // ordered by threshold
pub loot_tables: HashMap<String, LootTableDef>,
pub recipes: HashMap<String, RecipeDef>,
pub achievements: HashMap<String, AchievementDef>,
pub bonus_pools: HashMap<String, BonusPoolDef>,
}
impl GameData {
/// Load all data files from the data/ directory and validate cross-references.
pub fn load(data_dir: &Path) -> Result<Self, GameDataError> {
let data = Self { /* load each category */ };
data.validate()?; // check all cross-references
Ok(data)
}
/// Validate that all cross-references between data files are valid.
/// e.g., a class's `primary_resource` must reference a valid resource ID,
/// a quest's `encounters.enemy_ids` must reference valid creature IDs, etc.
fn validate(&self) -> Result<(), GameDataError> { /* ... */ }
}
}
Cross-Reference Validation
At startup, the GameData::validate() method checks every cross-reference:
- Every class’s
primary_resourceis a valid resource ID - Every class’s
starting_skillsare valid skill IDs - Every species’s
attribute_bonusesreference valid attribute names - Every item template’s
slotis a valid equipment slot ID - Every item template’s
weapon_type(if weapon) is a valid weapon type ID - Every skill’s
resource_cost.typeis a valid resource ID - Every skill’s
conditions_appliedare valid condition IDs - Every quest encounter’s
enemy_idsare valid creature IDs - Every quest’s
loot_tableis a valid loot table ID - Every recipe’s
materialsreference valid item template IDs - Every recipe’s
result_item_template_idis a valid item template ID - Every faction’s
conflict_pair(if set) is a valid faction ID - Every loot table entry’s
template_idis a valid item template ID
If any validation fails, the server refuses to start with a clear error message naming the broken reference. This catches data errors at deploy time, not at player-action time.
What Is Code (Rust Enums)
Things that define how the engine works — not what content exists — remain as Rust enums. These change only when the engine’s behavior changes, which is a code change anyway.
| Category | Enum | Why It’s Code |
|---|---|---|
| Item storage locations | ItemLocation (Equipped, Backpack, Bank, Mail, Listed) | Each variant has different capacity rules, UI sections, transfer logic |
| Run state machine | RunStatus (InProgress, Completed, Failed) | Finite states with transitions enforced by the worker |
| Encounter dispatch | EncounterType (Combat, Trap, Decision, Hazard, Rest, Puzzle) | Each dispatches to a completely different resolver function |
| Queue condition evaluation | QueueConditionKind (IfHpBelow, IfEnemyCountAbove, etc.) | Each has different evaluation logic in the combat engine |
| Achievement criteria kinds | CriteriaKind (KillCount, ReachLevel, CraftCount, etc.) | Each has different progress tracking and check logic |
| Notification types | NotificationType (RunComplete, ItemSold, etc.) | Each has different payload structure and routing |
Important: Condition Definitions vs. Condition Evaluation
The definitions of status effects (Poisoned does 5 damage/round, lasts 3 rounds) are data-driven — they live in data/conditions/*.toml. But the evaluation logic for queue conditions (“if my HP is below X”) is code, because each condition type requires different logic to evaluate:
#![allow(unused)]
fn main() {
// CODE: how to evaluate a queue condition (engine logic)
pub enum QueueConditionKind {
Always,
IfHpBelow,
IfHpAbove,
IfTargetHpBelow,
IfEnemyCountAbove,
IfEnemyCountBelow,
IfAllyHpBelow,
IfResourceAbove,
IfResourceBelow,
IfHasCondition, // the condition ID is a data reference
IfTargetHasCondition, // the condition ID is a data reference
}
// DATA: what "poisoned" actually does (loaded from TOML)
// data/conditions/poisoned.toml
// [poisoned]
// name = "Poisoned"
// damage_per_round = 5
// duration_rounds = 3
// damage_type = "poison"
// stackable = false
}
Data File Format Examples
Species Definition
# data/species/ironborn.toml
[ironborn]
name = "Ironborn"
description = "Stout mountain-dwelling folk..."
attribute_bonuses = { fortitude = 2, might = 1 }
[ironborn.species_skill]
id = "ironborn_resilience"
name = "Ironborn Resilience"
description = "Reduce incoming physical damage by 5%"
effect = { type = "damage_reduction", subtype = "physical", value = 0.05 }
Condition Definition
# data/conditions/poisoned.toml
[poisoned]
name = "Poisoned"
description = "Taking poison damage each round"
category = "debuff"
damage_per_round = 5
damage_type = "poison"
default_duration = 3
stackable = false
icon = "poison"
[stunned]
name = "Stunned"
description = "Cannot act this round"
category = "debuff"
skip_turn = true
default_duration = 1
stackable = false
icon = "stun"
Equipment Slot Definition
# data/equipment_slots.toml
[[slots]]
id = "main_hand"
name = "Main Hand"
accepts = ["weapon"]
required = false
[[slots]]
id = "off_hand"
name = "Off Hand"
accepts = ["weapon", "shield"]
required = false
blocked_by = "two_handed" # if main_hand has a two-handed weapon
[[slots]]
id = "head"
name = "Head"
accepts = ["armor_head"]
required = false
# ... etc for all 11 slots
Rarity Definition
# data/rarities.toml
[[rarities]]
id = "common"
name = "Common"
color = "#ffffff"
tier = 0
bonus_count = 0
has_signature = false
salvage_material = "common_fragment"
salvage_quantity = 2
[[rarities]]
id = "rare"
name = "Rare"
color = "#0070dd"
tier = 2
bonus_count = [1, 2] # 1-2 random bonuses
has_signature = true
salvage_material = "rare_core"
salvage_quantity = 1
[[rarities]]
id = "legendary"
name = "Legendary"
color = "#ff8000"
tier = 4
bonus_count = 3
has_signature = true
has_gear_skill = true
salvage_material = "legendary_spark"
salvage_quantity = 1
Resource Definition
# data/resources.toml
[[resources]]
id = "stamina"
name = "Stamina"
max_base = 100
regen_per_round = 10
regen_on_rest = 50
color = "#4CAF50"
used_by_classes = ["vanguard", "pathfinder", "berserker"]
[[resources]]
id = "mana"
name = "Mana"
max_base = 80
regen_per_round = 5
regen_on_rest = 30
color = "#2196F3"
used_by_classes = ["arcanist", "hexbinder"]
[[resources]]
id = "fury"
name = "Fury"
max_base = 0 # starts at 0, builds during combat
regen_per_round = 0
gain_on_hit = 15
gain_on_take_damage = 10
color = "#F44336"
used_by_classes = ["berserker"]
Migration Strategy for Existing Feature Docs
The feature docs (00-18) reference hardcoded enums in several places. The principle going forward:
- Never define a Rust enum for game content. Use
String/ newtype wrapper validated against theGameDataregistry. - Never use
sqlx::Typeon game content. UseTEXTcolumns in Postgres. The#[sqlx(transparent)]derive on the newtype wrapper handles serialization. - Define all game content in
data/TOML files. Validate cross-references at startup. - Feature 00 now includes the
GameDataregistry as part of the foundation — it’s loaded before the API server starts accepting requests. - Tests use a test
GameDatafixture loaded from atest_data/directory with minimal content (1-2 of each type) rather than the full game data set.
Feature 00: Project Foundation
Overview
Set up the Rust Cargo workspace, database infrastructure, Docker Compose development environment, and CI pipeline. This is the skeleton that every subsequent feature builds on — no game logic yet, just the scaffolding to build, run, test, and deploy.
Technical Tasks
1. Initialize Cargo Workspace
- Create root
Cargo.tomlwith workspace members - Create crate stubs:
crates/api— binary, depends ondb,types,game,jobscrates/workers— binary, depends ondb,types,game,jobscrates/game— librarycrates/db— librarycrates/types— librarycrates/jobs— library
- Add shared dependencies to workspace
Cargo.toml:tokio(async runtime)serde,serde_json(serialization)sqlx(database, withpostgres,runtime-tokio,tls-rustlsfeatures)redis(withtokio-compfeature)uuid(withv4,serdefeatures)chrono(timestamps, withserdefeature)tracing,tracing-subscriber(logging)thiserror,anyhow(error handling)
- Verify
cargo buildsucceeds with empty crates - Verify
cargo testruns (no tests yet, but harness works)
2. API Server Skeleton (crates/api)
- Add Axum dependencies:
axum,tower,tower-http(cors, tracing, compression) - Create
main.rswith:- Tokio runtime initialization
- Tracing subscriber setup (JSON format for structured logging)
- Axum router with a health check endpoint:
GET /api/health→{ "status": "ok" } - Graceful shutdown on SIGTERM/SIGINT
- Create
AppStatestruct holding:PgPool(SQLx connection pool)redis::Client(Redis connection)
- Wire AppState into Axum via
ExtensionorState - Create
src/error.rs— unified error type that converts to Axum responses with appropriate HTTP status codes - Create
src/middleware/directory with placeholder modules
3. Worker Binary Skeleton (crates/workers)
- Create
main.rswith:- Tokio runtime initialization
- Tracing subscriber setup
- CLI argument parsing (e.g.,
--schedulerflag) viaclap - Placeholder job processing loop: poll Redis, log “no jobs”, sleep
- Graceful shutdown
4. Database Setup (crates/db)
- Add SQLx CLI as a dev dependency or document
cargo install sqlx-cli - Create
migrations/directory - Create initial migration
0001_create_extensions.sql:CREATE EXTENSION IF NOT EXISTS "pgcrypto";(forgen_random_uuid())
- Create
src/lib.rs:create_pool(database_url: &str) -> PgPoolfunctionrun_migrations(pool: &PgPool)function
- Create
src/models/mod.rs— empty, will hold row types - Create
src/queries/mod.rs— empty, will hold query functions
5. Job Queue Foundation (crates/jobs)
- Create Redis-backed delayed job queue:
enqueue(queue: &str, payload: &[u8], delay: Duration)— adds job to Redis sorted set withscore = now + delayenqueue_immediate(queue: &str, payload: &[u8])— adds withscore = nowpoll(queue: &str) -> Option<Job>—ZPOPMINwhere score <= nowack(job_id: &str)— remove from processing setfail(job_id: &str, error: &str)— move to dead letter queue
- Job struct:
{ id: String, queue: String, payload: Vec<u8>, enqueued_at: DateTime, attempts: u32 } - Use Redis
ZADD/ZPOPMINwith timestamps as scores for delayed execution - Add retry logic: on failure, re-enqueue with exponential backoff (max 3 retries)
6. Docker Compose Development Environment
- Create
docker-compose.yml:- PostgreSQL 16 Alpine (port 5432, volume for persistence)
- Valkey 7 Alpine (port 6379)
- Mailpit (ports 8025 web UI, 1025 SMTP)
- Create
.env.examplewith:DATABASE_URL=postgres://delve:devpassword@localhost:5432/delveREDIS_URL=redis://localhost:6379SMTP_HOST=localhostSMTP_PORT=1025
- Add
.envto.gitignore
7. Configuration Management
- Create
crates/api/src/config.rs:- Load config from environment variables (with
dotenvyfor .env files in dev) - Struct:
Config { database_url, redis_url, host, port, smtp_host, smtp_port } - Validate required fields on startup, panic with clear error if missing
- Load config from environment variables (with
8. CI Pipeline
- Create
.github/workflows/ci.yml:- Trigger on PR and push to main
- Steps:
cargo fmt --check,cargo clippy,cargo test,cargo build --release - Use
actions-rs/toolchainordtolnay/rust-toolchain - Cache cargo registry and target directory via
Swatinem/rust-cache - Start PostgreSQL and Redis services for integration tests
9. Dockerfile
- Create multi-stage
Dockerfile:- Stage 1:
cargo-chefto cache dependency compilation - Stage 2: Build release binaries
- Stage 3:
FROM debian:bookworm-slim(or Alpine) — copy binaries, set entrypoint
- Stage 1:
- Produce two targets:
delve-apianddelve-workers - Verify
docker buildsucceeds and images are < 50MB
10. SvelteKit Client Skeleton
- Initialize SvelteKit project in
client/directory:pnpm create svelte@latest client(skeleton project, TypeScript)- Install:
tailwindcss,@tanstack/svelte-query,@tanstack/query-sync-storage-persister - Configure Tailwind with design token stubs (colors for rarity tiers, etc.)
- Configure SvelteKit static adapter (SPA mode)
- Configure Vite proxy:
/api→http://localhost:3000in dev
- Create root layout (
+layout.svelte):- QueryClientProvider wrapper
- Placeholder navigation shell
- Create
GET /api/healthquery to verify client↔server connectivity - Create
lib/api/client.ts— base fetch wrapper with auth header injection
Tests
Unit Tests
crates/jobs: Testenqueue+pollreturns job after delay has elapsedcrates/jobs: TestpollreturnsNonewhen delay hasn’t elapsedcrates/jobs: Testackremoves job from processing setcrates/jobs: Testfailwith retry re-enqueues with backoffcrates/jobs: Testfailafter max retries moves to dead letter queuecrates/db: Testcreate_poolconnects to test databasecrates/db: Testrun_migrationsapplies migrations without errorcrates/api/config: Test config loads from env varscrates/api/config: Test config panics on missing required fields
Integration Tests
- API server starts and
GET /api/healthreturns 200 with{ "status": "ok" } - API server connects to PostgreSQL (pool creation succeeds)
- API server connects to Redis (ping succeeds)
- Worker binary starts and exits cleanly on SIGTERM
- Job queue round-trip: enqueue → poll → ack (against real Redis)
- Migrations run cleanly on a fresh database
Client Tests
- SvelteKit builds without errors (
pnpm build) - Health check query hits the API and receives a response (dev proxy test)
Feature 01: Authentication
Overview
User registration, login, logout, and session management. This is the gateway to every other feature — no game actions are possible without an authenticated user. Sessions are stored in Redis with opaque tokens. Passwords are hashed with Argon2id.
Dependencies
- Feature 00 (Project Foundation)
Technical Tasks
1. Users Table Migration
- Create migration
0002_create_users.sql:CREATE TABLE users ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), email TEXT UNIQUE NOT NULL, password_hash TEXT NOT NULL, patron_tier SMALLINT DEFAULT 0, patron_expires TIMESTAMPTZ, character_slots SMALLINT DEFAULT 2, bank_slots SMALLINT DEFAULT 50, marketplace_slots SMALLINT DEFAULT 10, created_at TIMESTAMPTZ DEFAULT now(), last_login TIMESTAMPTZ );
2. User Model & Queries (crates/db)
- Create
src/models/user.rs—Userstruct withFromRowderive - Create
src/queries/users.rs:create_user(pool, email, password_hash) -> Userfind_user_by_email(pool, email) -> Option<User>find_user_by_id(pool, id) -> Option<User>update_last_login(pool, id)
3. Password Hashing (crates/api)
- Add
argon2crate dependency - Create
src/auth/password.rs:hash_password(password: &str) -> String— Argon2id with recommended paramsverify_password(password: &str, hash: &str) -> bool
4. Session Management
- Create
src/auth/session.rs:create_session(redis, user_id) -> String— generate random 32-byte token (hex-encoded), store in Redis with 30-day TTL assession:{token} → { user_id, expires_at }validate_session(redis, token) -> Option<UserId>— lookup in Redis, return user_id if validdestroy_session(redis, token)— delete from Redisrefresh_session(redis, token)— reset TTL to 30 days on each use
5. Auth Middleware
- Create
src/middleware/auth.rs:- Axum extractor
AuthUserthat readsAuthorization: Bearer {token}header (or cookie) - Validates session via Redis
- Returns
401 Unauthorizedif missing or invalid - Populates
AuthUser { user_id: Uuid }for downstream handlers
- Axum extractor
6. Auth Routes (crates/api)
- Create
src/routes/auth.rs:POST /api/auth/register:- Validate: email format, password length (8-128 chars)
- Check email not already taken (409 Conflict)
- Hash password
- Create user record
- Create session
- Return
{ token, user: { id, email } }
POST /api/auth/login:- Find user by email (401 if not found)
- Verify password (401 if wrong)
- Update last_login
- Create session
- Return
{ token, user: { id, email } }
POST /api/auth/logout:- Requires auth middleware
- Destroy session
- Return 204 No Content
GET /api/auth/me:- Requires auth middleware
- Return current user info
{ id, email, patron_tier, character_slots }
7. Rate Limiting
- Create
src/middleware/rate_limit.rs:- Redis-based rate limiter using
INCR+EXPIRE - Per-IP rate limiting for auth endpoints (10 req/min for register, 20 req/min for login)
- Per-user rate limiting for authenticated endpoints (100 req/min default)
- Return
429 Too Many RequestswithRetry-Afterheader when exceeded
- Redis-based rate limiter using
8. Input Validation
- Add
validatorcrate - Create request structs with validation derives:
RegisterRequest { email: String, password: String }— email format, password lengthLoginRequest { email: String, password: String }
- Create
ValidatedJson<T>Axum extractor that validates before passing to handler
9. Client Auth (client/)
- Create
lib/api/auth.ts:register(email, password)— POST to/api/auth/registerlogin(email, password)— POST to/api/auth/loginlogout()— POST to/api/auth/logoutgetMe()— GET to/api/auth/me
- Create
lib/stores/auth.ts:- Svelte store holding current user + token
- Persist token to
localStorage - Auto-inject token into all API requests via the base fetch wrapper
- Create auth pages:
routes/(auth)/login/+page.svelte— login formroutes/(auth)/register/+page.svelte— registration form
- Create auth guard: redirect to
/loginif not authenticated when accessing(game)routes
Tests
Unit Tests
password::hash_passwordproduces a valid Argon2id hashpassword::verify_passwordreturns true for correct passwordpassword::verify_passwordreturns false for wrong passwordsession::create_sessionstores a key in Redis with correct TTLsession::validate_sessionreturns user_id for valid tokensession::validate_sessionreturns None for expired/invalid tokensession::destroy_sessionremoves the key from RedisRegisterRequestvalidation rejects invalid email formatsRegisterRequestvalidation rejects passwords shorter than 8 charsRegisterRequestvalidation accepts valid input
Integration Tests
POST /api/auth/registerwith valid data → 200, returns token + userPOST /api/auth/registerwith duplicate email → 409POST /api/auth/registerwith invalid email → 422POST /api/auth/registerwith short password → 422POST /api/auth/loginwith correct credentials → 200, returns tokenPOST /api/auth/loginwith wrong password → 401POST /api/auth/loginwith non-existent email → 401GET /api/auth/mewith valid token → 200, returns userGET /api/auth/mewithout token → 401POST /api/auth/logoutdestroys session (subsequent/mereturns 401)- Rate limiter returns 429 after exceeding limit
- Session persists across server restart (stored in Redis, not memory)
Feature 02: Character Creation
Overview
Players create characters by choosing a species, class, background, and distributing attribute points. Each user can have up to 2 characters for free (up to 6 with purchased slots). Characters are the core identity — all subsequent features operate on a selected character.
Dependencies
- Feature 01 (Authentication)
Technical Tasks
1. Static Game Data — Species, Classes, Backgrounds
- Create
data/species/directory with TOML files defining all 9 species:- Each species: name, description, attribute_bonuses (e.g.,
{ might: 2, fortitude: 1 }), species_skill (name, description, passive effect) - Species: Ironborn, Verdani, Kharren, Sylphari, Thornkin, Ashenmere, Glimkin, Vexari, Ferathi
- Each species: name, description, attribute_bonuses (e.g.,
- Create
data/classes/directory with TOML files defining all 9 classes:- Each class: name, description, primary_resource (stamina/mana/fury/etc.), starting_skills (list of skill IDs), subclasses (3 per class, with name + description — details deferred to Feature 04)
- Classes: Vanguard, Shade, Arcanist, Warden, Pathfinder, Berserker, Songweaver, Oathblade, Hexbinder
- Create
data/backgrounds/directory with TOML files defining all 10 backgrounds:- Each background: name, description, attribute_bonus (+1 to one attribute), passive_perk (name, effect description)
- Create
crates/game/src/data.rs:- Load functions:
load_species(),load_classes(),load_backgrounds() - Use
once_cell::sync::Lazyfor global static access - Validate data on load (no missing references, attribute bonuses sum correctly)
- Load functions:
2. Type Definitions (crates/types)
- Create
src/character.rs:Speciesenum (9 variants) with Serialize/Deserialize/sqlx::TypeClassenum (9 variants)Backgroundenum (10 variants)Attributesstruct:{ might, logic, speed, presence, fortitude, luck }— allu8Characterstruct with all fields matching the DB schema
- Create
src/ids.rs:CharacterId(Uuid)newtype with Serialize/Deserialize/FromRow supportUserId(Uuid)newtype
3. Characters Table Migration
- Create migration
0003_create_characters.sql:- Full characters table with all columns from the technical architecture schema
- Indexes on
user_idandname - Check constraint: attributes between 1 and 99
- Unique constraint on
name
4. Character Model & Queries (crates/db)
- Create
src/models/character.rs—Characterstruct with FromRow - Create
src/queries/characters.rs:create_character(pool, params) -> Characterfind_characters_by_user(pool, user_id) -> Vec<Character>find_character_by_id(pool, id) -> Option<Character>find_character_by_name(pool, name) -> Option<Character>delete_character(pool, id)count_characters_by_user(pool, user_id) -> i64
5. Character Creation Validation (crates/game)
- Create
src/character_creation.rs:validate_attributes(attributes: &Attributes, species: Species) -> Result<()>:- Base points: each attribute starts at 5, player distributes 40 points
- Range: each base attribute 5–15 (before species bonuses)
- Total distributed must equal 40
- After species bonuses, final values must be 1–99
validate_name(name: &str) -> Result<()>:- Length: 2–24 characters
- Allowed: alphanumeric + spaces + hyphens + apostrophes
- No leading/trailing whitespace
- No consecutive spaces
- Profanity filter (basic word list — can be expanded later)
calculate_starting_attributes(base: &Attributes, species: Species, background: Background) -> Attributes:- Apply species bonuses
- Apply background bonus (+1 to one attribute)
get_starting_equipment(class: Class) -> Vec<String>:- Return list of item template IDs for starting gear (deferred to Feature 05, return empty for now)
6. Character Routes (crates/api)
- Create
src/routes/characters.rs:GET /api/characters:- Auth required
- Return all characters owned by the authenticated user
- Include: id, name, species, class, level, gold (summary view)
POST /api/characters:- Auth required
- Body:
{ name, species, class, background, attributes: { might, logic, speed, presence, fortitude, luck } } - Validate user hasn’t exceeded character slot limit
- Validate name uniqueness
- Validate name format
- Validate attribute distribution
- Calculate final attributes (base + species + background bonuses)
- Create character record
- Return full character object
GET /api/characters/:id:- Auth required
- Validate character belongs to authenticated user
- Return full character details including all attributes, skills, gold, level, etc.
DELETE /api/characters/:id:- Auth required
- Validate character belongs to authenticated user
- Soft delete or hard delete (hard delete for now — cascade will handle related data)
- Return 204
7. Character Ownership Middleware
- Create
src/middleware/character.rs:- Axum extractor
CharacterOwner(Character):- Reads
:idpath parameter - Loads character from DB
- Verifies
character.user_id == auth_user.user_id - Returns 404 if character not found or not owned by user
- Reads
- This extractor will be reused by every endpoint that operates on a character
- Axum extractor
8. Client — Character Selection & Creation
- Create
routes/(game)/+page.svelte:- Character selection screen (list of user’s characters)
- “Create New Character” button (disabled if at slot limit)
- Selecting a character sets it as active in the Svelte store
- Create
routes/(game)/create-character/+page.svelte:- Step 1: Choose species (cards showing name, description, bonuses, species skill)
- Step 2: Choose class (cards showing name, description, resource type, starting skills)
- Step 3: Choose background (cards showing name, bonus, perk)
- Step 4: Distribute attributes (point-buy UI with +/- buttons, live preview of final stats with bonuses)
- Step 5: Choose name (text input with validation feedback)
- Step 6: Review & confirm
- Create
lib/types/character.ts— TypeScript types matching the Rust character types - Create
lib/api/queries/characters.ts— TanStack Query hooks for character CRUD
Tests
Unit Tests
validate_attributes: accepts valid 40-point distributionvalidate_attributes: rejects total != 40validate_attributes: rejects individual attribute < 5 or > 15validate_attributes: correctly applies species bonusesvalidate_name: accepts “Aldric the Bold”validate_name: rejects empty stringvalidate_name: rejects names > 24 charsvalidate_name: rejects names with special characters (e.g.,<script>)validate_name: rejects names with consecutive spacescalculate_starting_attributes: correctly sums base + species + backgroundcalculate_starting_attributes: Ironborn gets +2 Fortitude, +1 Might- Game data loading: all 9 species load without error
- Game data loading: all 9 classes load without error
- Game data loading: all 10 backgrounds load without error
Integration Tests
POST /api/characterswith valid data → 201, character created with correct attributesPOST /api/characterswith duplicate name → 409POST /api/characterswhen at slot limit → 403 with clear error messagePOST /api/characterswith invalid attribute total → 422POST /api/characterswith invalid species → 422GET /api/charactersreturns only characters owned by authenticated userGET /api/characters/:idfor own character → 200GET /api/characters/:idfor another user’s character → 404DELETE /api/characters/:idfor own character → 204, character goneDELETE /api/characters/:idfor another user’s character → 404- Character count correctly limits creation per user
Feature 03: Items & Inventory
Overview
The item system: gear templates, item instances with random properties, inventory management (backpack, equipped slots, bank), equipping/unequipping, and salvaging. This must be in place before combat or quests, since characters need gear to do anything.
Dependencies
- Feature 02 (Character Creation)
Technical Tasks
1. Static Game Data — Item Templates
- Create
data/items/directory with TOML files organized by category:weapons/swords.toml,weapons/axes.toml,weapons/daggers.toml,weapons/bows.toml,weapons/staves.toml,weapons/wands.toml,weapons/shields.tomlarmor/heavy.toml,armor/medium.toml,armor/light.tomlaccessories/rings.toml,accessories/amulets.toml,accessories/cloaks.toml,accessories/belts.tomlconsumables/potions.toml,consumables/scrolls.toml,consumables/supplies.tomlmaterials/crafting.toml(placeholder — filled in Feature 09)
- Define item template struct:
name, item_type, subtype, slot, rarity, level_req, base_stats: { damage?, armor?, evasion?, speed? }, signature_effect? (for Rare+), skill_granted? (for Legendary/Artifact weapons) - Start with Tier 1 (level 1-10) items — enough to test the full system. Higher tiers added later.
- Create
crates/game/src/items.rs:load_item_templates()→HashMap<String, ItemTemplate>ItemTemplatestructEquipmentSlotenum: MainHand, OffHand, Head, Chest, Hands, Feet, Cloak, Ring1, Ring2, Amulet, Belt
2. Random Bonus Property System (crates/game)
- Create
src/item_generation.rs:- Define bonus property pools:
- Offensive: bonus damage, crit chance, attack speed, armor penetration
- Defensive: bonus armor, evasion, HP, damage reduction
- Attribute: +might, +logic, +speed, +presence, +fortitude, +luck
- Resource: +max resource, +resource regen
- Utility: +gold find, +XP gain, +crafting speed
- Define potency tiers: Minor, Standard, Major, Grand
generate_item(template_id: &str, rng: &mut impl Rng) -> Item:- Common: base stats only
- Uncommon: base stats + 1 random bonus
- Rare: base + signature effect + 1-2 random bonuses
- Very Rare: base + signature + 2-3 random bonuses
- Legendary: base + signature + 3 random bonuses + gear skill
- Artifact: hand-defined (no random generation)
- Bonus selection uses weighted random from the appropriate pool
- Define bonus property pools:
3. Item & Inventory Tables Migration
- Create migration
0004_create_items.sql:itemstable: id, owner_id, template_id, rarity, enhancement, bonus_properties (JSONB), location, slot_index, created_at- Index on (owner_id), (owner_id, location)
artifactstable: template_id (PK), held_by, acquired_at
4. Item Model & Queries (crates/db)
- Create
src/models/item.rs:Itemstruct with FromRowItemLocationenum serialized as textBonusPropertystruct (for JSONB deserialization)
- Create
src/queries/items.rs:create_item(pool, owner_id, template_id, rarity, bonuses, location) -> Itemfind_items_by_owner(pool, owner_id) -> Vec<Item>find_items_by_location(pool, owner_id, location) -> Vec<Item>find_equipped_items(pool, owner_id) -> Vec<Item>find_item_by_id(pool, id) -> Option<Item>update_item_location(pool, item_id, new_location, slot_index)delete_item(pool, item_id)count_items_in_location(pool, owner_id, location) -> i64
5. Equip/Unequip Logic (crates/game)
- Create
src/equipment.rs:can_equip(character: &Character, item: &Item, slot: EquipmentSlot) -> Result<()>:- Check level requirement
- Check class restrictions (if any)
- Check slot compatibility (e.g., can’t put a sword in head slot)
- Check two-handed weapon rules (main hand + off hand)
get_equipped_stats(items: &[Item]) -> EquippedStats:- Sum all base stats + bonus properties from equipped items
- Return aggregated damage, armor, evasion, attribute bonuses, etc.
6. Salvage System (crates/game)
- Create
src/salvage.rs:salvage_item(item: &Item) -> Vec<SalvageMaterial>:- Returns materials based on rarity (NOT level — per design doc)
- Common → Common Fragments
- Uncommon → Uncommon Shards
- Rare → Rare Cores
- Very Rare → Prismatic Essences
- Legendary → Legendary Sparks
- Material quantities scale with rarity, not item level
7. Inventory Routes (crates/api)
- Create
src/routes/inventory.rs:GET /api/characters/:id/inventory:- Return all items grouped by location:
{ equipped: [], backpack: [], bank: [] } - Each item includes resolved template data (name, base stats) merged with instance data (bonuses, enhancement)
- Return all items grouped by location:
POST /api/characters/:id/equip:- Body:
{ item_id, slot } - Validate ownership, slot compatibility, level requirement
- If slot already occupied, swap to backpack
- Update item locations in DB
- Body:
POST /api/characters/:id/unequip:- Body:
{ slot } - Move equipped item to backpack
- Check backpack isn’t full
- Body:
POST /api/characters/:id/salvage:- Body:
{ item_ids: [uuid] } - Validate all items owned and in backpack/bank
- Cannot salvage equipped items
- Delete items, grant materials (materials are items in backpack with
type: material) - Return list of materials gained
- Body:
POST /api/characters/:id/move-item:- Body:
{ item_id, to_location, to_index? } - Move between backpack ↔ bank
- Validate capacity limits
- Body:
8. Starting Equipment
- Update character creation (Feature 02) to grant starting gear:
- Each class gets a weapon + basic armor appropriate to their archetype
- Items are Common rarity, level 1
- Created as item instances and placed in equipped slots
9. Client — Inventory UI
- Create
routes/(game)/inventory/+page.svelte:- Equipment panel: visual grid of 11 gear slots (character paper doll layout)
- Backpack panel: grid of items with drag-and-drop support
- Bank panel: accessible tab, grid layout
- Item tooltip on hover/tap: name, rarity color, base stats, bonus properties, level req, slot
- Equip: drag from backpack to slot, or right-click → “Equip”
- Unequip: click equipped item → “Unequip”
- Salvage: select multiple items → “Salvage” button with confirmation
- Create
lib/types/item.ts— TypeScript types for items - Create
lib/components/inventory/ItemTooltip.svelte— reusable tooltip component - Create
lib/components/inventory/ItemGrid.svelte— reusable grid component
Tests
Unit Tests
generate_item: Common items have no bonus propertiesgenerate_item: Rare items have signature effect + 1-2 bonusesgenerate_item: Legendary items have 3 bonuses + gear skill referencegenerate_item: deterministic with seeded RNG (same seed → same item)can_equip: accepts valid weapon in main handcan_equip: rejects item below level requirementcan_equip: rejects sword in head slotcan_equip: handles two-handed weapon (clears off-hand)get_equipped_stats: correctly sums stats from multiple itemsget_equipped_stats: includes bonus propertiessalvage_item: Common → correct Common Fragments countsalvage_item: Rare → Rare Cores (not affected by item level)- Item template loading: all templates load without error, no missing references
Integration Tests
GET /api/characters/:id/inventoryreturns equipped + backpack + bank itemsPOST /api/characters/:id/equipmoves item from backpack to slotPOST /api/characters/:id/equipswaps existing equipped item to backpackPOST /api/characters/:id/equiprejects item not owned by characterPOST /api/characters/:id/unequipmoves item to backpackPOST /api/characters/:id/unequipfails when backpack is fullPOST /api/characters/:id/salvagedeletes items and returns materialsPOST /api/characters/:id/salvagerejects equipped itemsPOST /api/characters/:id/move-itemmoves item between backpack and bank- Character creation grants starting equipment in correct slots
- Inventory respects backpack capacity limit (default 30 slots)
- Bank respects bank capacity limit (default 50 slots, upgradeable)
Feature 04: Character Progression
Overview
Leveling (1-50), XP curves, attribute growth per level, subclass selection at level 10, feat selection at milestone levels, non-combat skill advancement, weapon proficiency tracking, and the Paragon system for post-50 horizontal progression.
Dependencies
- Feature 02 (Character Creation)
- Feature 03 (Items & Inventory) — for weapon proficiency tracking by weapon type
Technical Tasks
1. Static Game Data — Progression Tables
- Create
data/progression/:xp_table.toml: XP required per level (1-50), designed for 8-12 months of regular playattribute_growth.toml: attribute points gained per level (per design doc milestones)feats.toml: all selectable feats with name, description, prerequisites, effectssubclasses.toml: 3 subclasses per class (27 total) with name, description, bonus_skills, passive_effects
- Create
data/skills/:class_skills.toml: starting skills per class + skills unlocked at levels 5, 10, 15, 20, 25, 30species_skills.toml: one unique passive skill per speciesweapon_skills.toml: 10 skills per weapon type, unlocked at proficiency milestones (10, 20, 30, 40, 50, 60, 70, 80, 90, 100)
2. Progression Logic (crates/game)
- Create
src/progression.rs:xp_for_level(level: u16) -> u64: lookup from XP tablecheck_level_up(current_level: u16, current_xp: u64) -> Option<LevelUpResult>:- If XP >= threshold, return new level + attribute points earned + any unlocks
- Handle multi-level-up (if enough XP for several levels at once)
LevelUpResult: new_level, attribute_points, feat_slot_unlocked (bool), skills_unlocked, subclass_eligible (bool)apply_level_up(character: &mut Character, result: &LevelUpResult):- Increment level, reduce XP by threshold, apply attribute points
- Create
src/feats.rs:- Feat selection rules: slots at levels 5, 12, 20, 28, 35, 42, 48
can_select_feat(character: &Character, feat_id: &str) -> Result<()>:- Check character has an open feat slot
- Check prerequisites met (level, class, attribute minimums)
- Check feat not already selected
apply_feat(character: &mut Character, feat_id: &str):- Add to character’s feat list
3. Weapon Proficiency (crates/game)
- Create
src/weapon_proficiency.rs:WeaponTypeenum: Sword, Axe, Dagger, Bow, Staff, Wand, Shield, Mace, Speargain_proficiency(current: u16, amount: u16) -> u16: capped at 100skills_unlocked_at(proficiency: u16) -> Vec<String>: return skill IDs unlocked at current proficiency- Proficiency gained from using the weapon in combat (calculated during simulation)
4. Non-Combat Skills (crates/game)
- Create
src/noncombat_skills.rs:- 12 skills: Athletics, Acrobatics, Stealth, Perception, Arcana, Nature, Religion, Persuasion, Deception, Intimidation, Medicine, Survival
- Skills are 0-100, improved through encounter checks
check_skill(skill_value: u16, difficulty: u16, rng: &mut impl Rng) -> SkillCheckResult:- d100 roll vs. (skill_value + attribute_modifier)
- Returns success/failure + margin
5. Subclass System
- Create
src/subclass.rs:can_choose_subclass(character: &Character) -> bool: level >= 10 and subclass is Nonevalid_subclasses(class: Class) -> Vec<SubclassInfo>: returns 3 options for the classapply_subclass(character: &mut Character, subclass_id: &str) -> Result<()>:- Validate character is eligible
- Validate subclass belongs to character’s class
- Set subclass (permanent, cannot be changed)
- Grant subclass starting skills
6. Paragon System (crates/game)
- Create
src/paragon.rs:- Post-level-50 horizontal progression
- Paragon XP required per level (uncapped, escalating)
- Each paragon level grants a small bonus (e.g., +1 to a chosen attribute, +1% to a stat)
gain_paragon_xp(character: &mut Character, xp: u64): check for paragon level up- Paragon bonuses are marginal — depth, not power
7. Database Migrations
- Create migration
0005_create_progression.sql:weapon_proficienciestable: character_id, weapon_type, proficiency (PK: character_id + weapon_type)skill_queuestable: id, character_id, name, is_active, slots (JSONB), created_at (index on character_id)
8. Progression Queries (crates/db)
- Create
src/queries/progression.rs:get_weapon_proficiencies(pool, character_id) -> Vec<WeaponProficiency>update_weapon_proficiency(pool, character_id, weapon_type, new_value)update_character_level(pool, character_id, new_level, new_xp, attribute_updates)set_subclass(pool, character_id, subclass)add_feat(pool, character_id, feat_id)
9. Progression Routes (crates/api)
- Create
src/routes/progression.rs(or extendcharacters.rs):GET /api/characters/:id/progression:- Return: level, xp, xp_to_next, feats, subclass, weapon_proficiencies, non-combat skills, paragon_level
POST /api/characters/:id/choose-subclass:- Body:
{ subclass_id } - Validate eligibility, apply permanently
- Body:
POST /api/characters/:id/choose-feat:- Body:
{ feat_id } - Validate feat slot available, prerequisites met
- Body:
POST /api/characters/:id/allocate-attributes:- Body:
{ might: 1, speed: 1 }(unspent points from leveling) - Validate character has unspent points
- Body:
10. Client — Character Sheet
- Create
routes/(game)/character/+page.svelte:- Character overview: name, species, class, subclass, level, XP bar
- Attributes panel: all 6 attributes with base + bonus breakdown
- Feats panel: selected feats + available slots with “Choose Feat” UI
- Weapon proficiencies: bar chart per weapon type (0-100)
- Non-combat skills: list with values
- Subclass selection modal (appears at level 10 if unchosen)
Tests
Unit Tests
xp_for_level: returns correct XP for levels 1, 10, 25, 50check_level_up: returns None when XP insufficientcheck_level_up: returns correct result for single level upcheck_level_up: handles multi-level-up correctlycan_select_feat: accepts valid feat selectioncan_select_feat: rejects feat already selectedcan_select_feat: rejects when no feat slot availablecan_select_feat: rejects when prerequisites not metcan_choose_subclass: true at level 10 with no subclasscan_choose_subclass: false at level 9can_choose_subclass: false when subclass already chosenvalid_subclasses: returns exactly 3 for each classgain_proficiency: caps at 100skills_unlocked_at: returns correct skills at proficiency milestonescheck_skill: d100 against skill value produces expected pass/fail distribution (statistical test with seeded RNG)- Paragon XP escalation: each level requires more than the last
Integration Tests
GET /api/characters/:id/progressionreturns all progression dataPOST /api/characters/:id/choose-subclasspermanently sets subclassPOST /api/characters/:id/choose-subclasssecond call → 409 (already chosen)POST /api/characters/:id/choose-featadds feat to characterPOST /api/characters/:id/choose-featat limit → 409POST /api/characters/:id/allocate-attributesdistributes unspent pointsPOST /api/characters/:id/allocate-attributesrejects if no unspent points- Weapon proficiency persists across requests
Feature 05: Skill Queue
Overview
The skill queue is Delve’s core strategic system. Players build an ordered list of 6-10 combat skills with optional conditions. The server executes this queue during combat simulation. This feature covers building, saving, validating, and managing multiple named queues.
Dependencies
- Feature 04 (Character Progression) — for skill unlocks and weapon proficiency
Technical Tasks
1. Static Game Data — Skills
- Flesh out
data/skills/TOML files (started in Feature 04) with full combat skill details:- Per skill: id, name, description, type (attack/heal/buff/debuff/utility), resource_cost (type + amount), cooldown (rounds), base_damage/healing, success_modifier, initiative_modifier, target_type (single/aoe/self/ally), conditions_applied, weapon_type_required
- Weapon skills: 10 per weapon type (sword, axe, dagger, bow, staff, wand, shield, mace, spear)
- Class skills: 8-12 per class, unlocked at level milestones
- Species skills: 1 passive per species (not queued — applied automatically)
- Gear skills: defined on Legendary/Artifact weapon templates (not in skills data — on item templates)
2. Skill Queue Types (crates/types)
- Create
src/skill.rs:SkillQueueSlot { skill_id: String, condition: QueueCondition }QueueConditionenum with all condition variants:AlwaysIfHpBelow { threshold: u8 }IfHpAbove { threshold: u8 }IfTargetHpBelow { threshold: u8 }IfEnemyCountAbove { count: u8 }IfEnemyCountBelow { count: u8 }IfAllyHpBelow { threshold: u8 }IfResourceAbove { resource: ResourceType, amount: u16 }IfResourceBelow { resource: ResourceType, amount: u16 }IfHasCondition { condition: StatusEffect }IfTargetHasCondition { condition: StatusEffect }
ResourceTypeenum: Stamina, Momentum, Mana, Devotion, Focus, Fury, Resonance, Zeal, Essence
3. Skill Queue Validation (crates/game)
- Create
src/skill_queue.rs:validate_queue(character: &Character, slots: &[SkillQueueSlot], proficiencies: &[WeaponProficiency], equipped: &[Item]) -> Result<()>:- Queue length: 1-10 slots (minimum 1, maximum 10)
- Every skill_id must be known by the character (from class, weapon proficiency, or equipped gear)
- Weapon skills require the matching weapon type equipped
- Conditions must reference valid resource types for the character’s class
- Thresholds must be in valid ranges (0-100 for HP%, valid amounts for resources)
- Must have at least one
Alwayscondition or the queue may never fire
get_available_skills(character: &Character, proficiencies: &[WeaponProficiency], equipped: &[Item]) -> Vec<SkillInfo>:- Return all skills the character can use based on class, weapon proficiency, and gear
- Used by the client to populate the skill picker
4. Skill Queue Database Operations
- Already created
skill_queuestable in Feature 04 migration - Create
src/queries/skill_queues.rs:create_skill_queue(pool, character_id, name, slots_json) -> SkillQueuefind_skill_queues(pool, character_id) -> Vec<SkillQueue>find_active_queue(pool, character_id) -> Option<SkillQueue>update_skill_queue(pool, id, name?, slots_json?)set_active_queue(pool, character_id, queue_id)— deactivates all others, activates this onedelete_skill_queue(pool, id)
5. Skill Queue Routes (crates/api)
- Create
src/routes/skill_queues.rs:GET /api/characters/:id/skill-queues:- Return all saved queues for the character (each with name, is_active, slots)
POST /api/characters/:id/skill-queues:- Body:
{ name, slots: [{ skill_id, condition }] } - Validate queue (all skills owned, conditions valid)
- Save to DB
- Return created queue
- Body:
PUT /api/characters/:id/skill-queues/:queue_id:- Update name and/or slots
- Re-validate if slots changed
DELETE /api/characters/:id/skill-queues/:queue_id:- Cannot delete the active queue (must activate another first)
POST /api/characters/:id/skill-queues/:queue_id/activate:- Set as active queue (used for the next run)
GET /api/characters/:id/available-skills:- Return all skills the character can currently use (for the queue builder UI)
6. Client — Skill Queue Builder
- Create
routes/(game)/character/skill-queue/+page.svelte:- Queue selector: dropdown of saved queues, “New Queue” button
- Queue builder:
- Ordered list of skill slots (drag-and-drop reordering)
- Each slot shows: skill icon/name + condition dropdown
- “Add Skill” button opens skill picker
- Skill picker: filterable list of available skills grouped by source (class, weapon, gear)
- Condition editor: dropdown per slot with type + threshold input
- Save/rename/delete queue buttons
- “Set Active” button
- Validation feedback: highlight invalid slots, show error messages
- Create
lib/components/character/SkillQueueBuilder.svelte - Create
lib/components/character/SkillPicker.svelte - Create
lib/components/character/ConditionEditor.svelte
Tests
Unit Tests
validate_queue: accepts valid queue with all skills ownedvalidate_queue: rejects empty queuevalidate_queue: rejects queue > 10 slotsvalidate_queue: rejects skill not owned by charactervalidate_queue: rejects weapon skill without matching weapon equippedvalidate_queue: rejects invalid condition threshold (e.g., HP > 100%)validate_queue: warns (but allows) queue with noAlwaysfallbackget_available_skills: returns class skills for character’s classget_available_skills: returns weapon skills up to current proficiencyget_available_skills: returns gear skills from equipped Legendary weaponget_available_skills: doesn’t return skills for unequipped weapon typesQueueConditionserialization round-trips correctly (serde JSON)
Integration Tests
GET /api/characters/:id/skill-queuesreturns empty list for new characterPOST /api/characters/:id/skill-queuescreates queue and returns itPOST /api/characters/:id/skill-queueswith invalid skill → 422PUT /api/characters/:id/skill-queues/:idupdates name and slotsPOST /api/characters/:id/skill-queues/:id/activatesets active flag- Only one queue is active at a time (activating one deactivates others)
DELETEon active queue → 409GET /api/characters/:id/available-skillsreturns skills based on class + proficiency + gear- Queue slots JSONB round-trips correctly (stored and retrieved identically)
Feature 06: Combat Engine
Overview
The heart of Delve: the server-side simulation engine that resolves combat encounters. Uses d100 percentile dice, processes skill queues round-by-round in initiative order, applies damage/healing/conditions, and produces a detailed log of every action and roll. This is a pure game logic feature — no API routes yet (that’s Feature 07).
Dependencies
- Feature 05 (Skill Queue) — for skill definitions and queue validation
- Feature 03 (Items & Inventory) — for equipped gear stats
Technical Tasks
1. Static Game Data — Creatures
- Create
data/creatures/directory with TOML files:- Organized by zone/theme:
goblins.toml,undead.toml,beasts.toml, etc. - Per creature: id, name, level, hp, attributes (might/logic/speed/presence/fortitude/luck), armor, evasion, skills (ordered list — creature’s own “queue”), loot_table_id, xp_reward
- Start with 15-20 creatures for levels 1-10 (enough to test the engine)
- Boss creatures: higher stats, multi-phase (phase 2 activates at HP threshold with new skill list)
- Organized by zone/theme:
2. Combat Types (crates/types)
- Create
src/combat.rs:CombatState: tracks all participants, HP, resources, active conditions, round numberParticipant: id, name, is_player, hp, max_hp, resources, attributes, armor, evasion, skill_queue, active_conditions, initiative_rollCombatAction: the result of a single skill execution (actor, target, skill, roll, hit, damage, healing, conditions applied)RoundLog: list of actions + condition tick results for one roundEncounterLog: list of rounds + outcome for one encounterRunLog: list of encounter logs + final statusEncounterOutcome: Victory, Survived, PartyWipe, FledRunStatus: Completed, FailedStatusEffectenum: Poisoned, Bleeding, Burning, Stunned, Blinded, Slowed, Weakened, Shielded, Regenerating, Empowered, Hasted, etc.Condition: effect type, duration (rounds remaining), source_id, potency
3. d100 Roll System (crates/game)
- Create
src/combat/rolls.rs:roll_d100(rng: &mut impl Rng) -> u8: returns 1-100calculate_hit_chance(attacker: &Participant, defender: &Participant, skill: &SkillDef) -> u8:- Base: 50% + (attacker relevant attribute - defender relevant attribute) + skill success_modifier
- Clamp to 5-95 (before crit/fail rules)
is_critical(roll: u8, luck: u8) -> bool: roll <= 5 (modified by luck)is_critical_fail(roll: u8, luck: u8) -> bool: roll >= 96 (modified by luck)calculate_damage(base_damage: (u16, u16), attacker: &Participant, defender: &Participant, is_crit: bool, rng: &mut impl Rng) -> u32:- Roll between base damage range
- Add might modifier (for physical) or logic modifier (for magic)
- Subtract defender armor (physical) or resistance (magic)
- Critical: double damage
- Floor at 1 (always deal at least 1 damage)
4. Initiative System (crates/game)
- Create
src/combat/initiative.rs:roll_initiative(participant: &Participant, rng: &mut impl Rng) -> u16:- Base: Speed attribute + weapon speed modifier
- Add d100 roll
- Lower total = faster (acts first)
sort_by_initiative(participants: &mut [Participant]):- Sort ascending (lowest initiative acts first)
5. Condition System (crates/game)
- Create
src/combat/conditions.rs:apply_condition(target: &mut Participant, condition: Condition):- Add to active conditions (or refresh duration if already present)
tick_conditions(participant: &mut Participant, log: &mut Vec<ConditionTick>):- For each active condition:
- Apply per-round effect (poison damage, regen healing, etc.)
- Decrement duration
- Remove if expired
- Log all effects
- For each active condition:
has_condition(participant: &Participant, effect: StatusEffect) -> bool- Condition effects:
- Poisoned: X damage per round
- Bleeding: X damage per round, reduced healing
- Burning: X fire damage per round
- Stunned: skip next action
- Blinded: -30% hit chance
- Slowed: +50 initiative (acts later)
- Weakened: -25% damage
- Shielded: absorb X damage before HP loss
- Regenerating: heal X per round
- Empowered: +25% damage
- Hasted: -25 initiative (acts sooner)
6. Skill Queue Execution (crates/game)
- Create
src/combat/execution.rs:evaluate_queue(participant: &Participant, combat_state: &CombatState) -> Option<&SkillQueueSlot>:- Iterate queue in order
- For each slot, evaluate condition against current combat state
- Return first skill whose condition is met AND resource cost can be paid
- Return None if no valid skill found (participant passes turn)
execute_skill(actor: &mut Participant, target: &mut Participant, skill: &SkillDef, rng: &mut impl Rng) -> CombatAction:- Deduct resource cost from actor
- Roll d100 for hit
- Calculate damage/healing if hit
- Apply conditions if hit
- Handle AoE (apply to multiple targets)
- Return CombatAction log entry
select_target(actor: &Participant, skill: &SkillDef, combat_state: &CombatState) -> Option<ParticipantId>:- Single target attack: target enemy with lowest HP
- AoE: all enemies
- Self: the actor
- Ally heal: ally with lowest HP
7. Encounter Resolution (crates/game)
- Create
src/combat/encounter.rs:resolve_combat_encounter(players: Vec<Participant>, enemies: Vec<Participant>, rng: &mut impl Rng) -> EncounterLog:- Roll initiative for all participants
- Loop rounds (max 50 per encounter):
- For each participant in initiative order:
- If dead/stunned, skip
- Evaluate skill queue → get skill
- Select target
- Execute skill
- Log action
- Tick conditions on all participants
- Check end conditions:
- All enemies dead → Victory
- All players dead → PartyWipe
- Round limit reached → draw (treated as Victory with reduced rewards)
- For each participant in initiative order:
- Return EncounterLog with all rounds and outcome
8. Non-Combat Encounter Handlers (crates/game)
- Create
src/combat/encounters_noncombat.rs:resolve_trap(party: &[CharacterSnapshot], trap: &TrapDef, rng: &mut impl Rng) -> EncounterLog:- Skill check (Perception to detect, Acrobatics/Athletics to avoid)
- Failure: party takes damage
resolve_decision(party: &[CharacterSnapshot], decision: &DecisionDef, rng: &mut impl Rng) -> EncounterLog:- Auto-pick based on highest relevant skill in party
- Each choice leads to different outcomes (bonus loot, skip encounters, etc.)
resolve_hazard(party: &[CharacterSnapshot], hazard: &HazardDef, rng: &mut impl Rng) -> EncounterLog:- Environmental damage, skill checks to mitigate
resolve_rest_point(party: &mut [Participant]) -> EncounterLog:- Restore HP by 25%, restore some resources
resolve_puzzle(party: &[CharacterSnapshot], puzzle: &PuzzleDef, rng: &mut impl Rng) -> EncounterLog:- Logic/Arcana/Perception checks, success grants bonus
9. Full Run Simulation (crates/game)
- Create
src/simulation.rs:SimulationContext: run record, party snapshots, quest definition, seeded RNG, log builderresolve_run(ctx: &mut SimulationContext) -> RunResult:- For each encounter in quest:
- Create participants from party snapshots + encounter enemies
- Dispatch to appropriate resolver (combat, trap, decision, etc.)
- Record encounter log
- If party wipe → return Failed
- Carry forward HP/resource state between encounters
- All encounters survived → calculate rewards (XP, gold, loot)
- Return RunResult with status, full log, and rewards
- For each encounter in quest:
ChaCha8Rngseeded fromblake3::hash(run_id + started_at)for deterministic replay
10. Loot Resolution (crates/game)
- Create
src/loot.rs:roll_loot(quest: &QuestDefinition, difficulty: Difficulty, encounters_completed: usize, rng: &mut impl Rng) -> Vec<GeneratedItem>:- Reference quest’s loot table
- Roll for each encounter’s loot drop (chance based on difficulty)
- Generate items via
item_generation::generate_item - Bonus chest for completing all encounters
- Loot tables defined in
data/loot_tables/TOML files:- Each table: list of
{ template_id, weight, rarity_weights: { common: 60, uncommon: 30, rare: 10 } }
- Each table: list of
Tests
Unit Tests
roll_d100: always returns 1-100 (fuzz with 10,000 rolls)calculate_hit_chance: 50% base when attacker/defender attributes equalcalculate_hit_chance: higher attacker stat → higher chancecalculate_hit_chance: clamped to 5-95 rangeis_critical: roll of 3 is critical, roll of 50 is notis_critical_fail: roll of 98 is critical failcalculate_damage: critical doubles damagecalculate_damage: armor reduces damagecalculate_damage: minimum 1 damageroll_initiative: lower speed → higher initiative value (slower)sort_by_initiative: sorts fastest firstapply_condition: adds condition to participantapply_condition: refreshes duration if already presenttick_conditions: poison deals damage each roundtick_conditions: removes expired conditionstick_conditions: stunned participant is flaggedevaluate_queue: returns first matching skillevaluate_queue: skips skills whose conditions aren’t metevaluate_queue: skips skills when resource cost can’t be paidevaluate_queue: returns None when nothing matchesexecute_skill: deducts resource costexecute_skill: applies conditions on hitexecute_skill: AoE hits all enemiesresolve_combat_encounter: party wins when all enemies dieresolve_combat_encounter: party wipe when all players dieresolve_combat_encounter: respects 50-round limitresolve_combat_encounter: initiative order is respectedresolve_combat_encounter: deterministic with same seed (run twice → identical log)resolve_trap: Perception check succeeds/fails correctlyresolve_rest_point: restores HP by 25%resolve_run: processes all encounters in sequenceresolve_run: stops on party wiperesolve_run: carries HP state between encountersroll_loot: higher difficulty → better rarity distributionroll_loot: deterministic with seeded RNG
Integration Tests
- (None — this is pure game logic with no DB or API. All tests are unit tests.)
- Full simulation test: create a party with known stats vs. known enemies, run simulation with fixed seed, assert exact sequence of events in the log
Feature 07: Quests & Dungeon Runs
Overview
The core gameplay loop: browse the quest board, start a run (which consumes real time), and review the results when it completes. This feature connects the combat engine (Feature 06) to the API and job queue, making the game actually playable.
Dependencies
- Feature 06 (Combat Engine)
- Feature 05 (Skill Queue) — active queue used for the run
- Feature 03 (Items & Inventory) — equipped gear snapshot, loot rewards
Technical Tasks
1. Static Game Data — Quests
- Create
data/quests/directory:bounties/— short quests (1-3 encounters, 15-45 min)questlines/— medium quests (5-8 encounters, 1-3 hours)dungeon_crawls/— long quests (10-20 encounters, 3-8 hours)- Per quest: id, name, type, level_range, base_duration_secs, encounters (ordered list), loot_table, difficulty_options, description, zone
- Each encounter: type (combat/trap/decision/hazard/rest/puzzle), enemy_ids (for combat), difficulty_class (for skill checks), description
- Start with 10-15 quests covering levels 1-10
2. Quest Board Logic (crates/game)
- Create
src/quest_board.rs:generate_quest_board(character_level: u16, zone: Option<&str>, rng: &mut impl Rng) -> Vec<QuestBoardEntry>:- Return 6-10 level-appropriate quests
- Mix of bounties, questlines, and dungeon crawls
- Refreshes every 6 hours (timestamp-seeded for consistency)
QuestBoardEntry: quest_id, name, type, difficulty, estimated_duration, encounter_count, level_range, recommended_attributescalculate_run_duration(quest: &QuestDefinition, difficulty: Difficulty, is_patron: bool) -> Duration:- Base duration from quest data
- Multiply by difficulty modifier (Trivial 0.5x, Normal 1x, Hard 1.5x, Deadly 2x)
- Patron: multiply by 0.667 (50% faster = 2/3 time)
3. Run Management — Database
- Create migration
0006_create_runs.sql:runstable: id, character_id, quest_id, difficulty, status (in_progress/completed/failed), skill_queue (JSONB snapshot), loadout (JSONB snapshot), supplies (JSONB snapshot), party_id, started_at, completes_at, completed_at, run_log (JSONB), rewards (JSONB)- Indexes: character_id, status WHERE in_progress, completes_at WHERE in_progress
4. Run Queries (crates/db)
- Create
src/queries/runs.rs:create_run(pool, params) -> Runfind_active_run(pool, character_id) -> Option<Run>find_run_by_id(pool, id) -> Option<Run>find_runs_by_character(pool, character_id, limit, offset) -> Vec<Run>complete_run(pool, run_id, status, run_log, rewards)
5. Run Start Flow (crates/api)
- Create
src/routes/runs.rs:GET /api/quest-board?character_id=:- Generate quest board for character’s level
- Return list of available quests with details
POST /api/runs:- Body:
{ character_id, quest_id, difficulty } - Validations:
- Character not already in a run
- Character meets quest level requirements
- Character has an active skill queue
- Character has gear equipped (at minimum a weapon)
- Snapshot the character’s current state:
- Skill queue (copied, not referenced — changes after start don’t affect the run)
- Equipped gear (stats frozen at start time)
- Supplies (deducted from inventory at start)
- Calculate completes_at timestamp
- Create run record (status: in_progress)
- Enqueue delayed job:
resolve-runwith delay = duration - Return
{ run_id, completes_at }
- Body:
6. Run Resolution Worker (crates/workers)
- Create
src/simulation_worker.rs:- Register job handler for
resolve-runqueue - On job received:
- Load run record from DB
- Load character snapshot + quest definition
- Build SimulationContext with seeded RNG
- Call
resolve_run()from the game crate - Write results to DB:
- Update run: status, completed_at, run_log (JSONB), rewards
- If success: grant XP, gold, loot items to character
- Update weapon proficiency (based on weapon used)
- Check for level up, apply if needed
- Create notification record (Feature 14)
- If character has push enabled, send push notification
- Register job handler for
7. Run Results & History (crates/api)
- Extend
src/routes/runs.rs:GET /api/runs/:id:- Return run details: status, quest info, timestamps
- If completed: include run_log, rewards, failure analysis (if failed)
GET /api/characters/:id/runs:- Paginated run history
- Summary view: quest name, difficulty, status, rewards, date
POST /api/runs/:id/collect:- Collect loot rewards from a completed run
- Move items from run rewards into character’s backpack
- Grant XP and gold to character
- Idempotent (can’t collect twice)
8. Supplies System
- Define supply items in
data/items/consumables/supplies.toml:- Rations (restore HP during rest encounters)
- Torches (improve Perception in dark encounters)
- Healing Potions (emergency heal — auto-used when HP < 20%)
- Antidotes (cure poison)
- Repair Kits (prevent gear durability loss)
POST /api/runsdeducts selected supplies from inventory at run start- Supplies affect encounter resolution (checked during simulation)
9. Client — Quest Board & Runs
- Create
routes/(game)/quest-board/+page.svelte:- Quest board: list of available quests as cards
- Each card: quest name, type badge, difficulty selector, encounter count, estimated duration, level range, reward preview
- “Start Run” button per quest → confirmation modal showing:
- Active skill queue summary
- Equipped gear summary
- Supply selection (optional)
- Estimated completion time
- Create
routes/(game)/runs/+page.svelte:- Active run panel (if any): quest name, progress bar to completesAt, countdown timer
- Run history: paginated list of past runs
- Create
routes/(game)/runs/[id]/+page.svelte:- Run result detail page
- Encounter-by-encounter timeline (collapsible)
- Each encounter: rounds, actions, rolls, outcomes (the Run Replay Viewer from the tech architecture)
- Loot display with “Collect All” button
- If failed: failure analysis (which encounter, what went wrong)
- Wire polling: active run status polls every 60s; on timer expiry, immediate refetch
Tests
Unit Tests
generate_quest_board: returns level-appropriate questsgenerate_quest_board: includes mix of quest typesgenerate_quest_board: deterministic for same time window + seedcalculate_run_duration: Patron gets 2/3 timecalculate_run_duration: difficulty modifier applied correctly
Integration Tests
GET /api/quest-boardreturns quests appropriate to character levelPOST /api/runscreates run with correct completes_at timestampPOST /api/runssnapshots skill queue and loadoutPOST /api/runsdeducts supplies from inventoryPOST /api/runsrejects if character already in a runPOST /api/runsrejects if no active skill queuePOST /api/runsrejects if quest level too highGET /api/runs/:idreturns in_progress run with completes_at- Worker resolves run and updates DB with results and log
- Worker grants XP and gold to character on successful run
- Worker creates loot items in character’s rewards
- Worker updates weapon proficiency after run
- Worker triggers level-up if XP threshold reached
POST /api/runs/:id/collectmoves loot to backpack and grants XP/goldPOST /api/runs/:id/collectsecond call → 409 (already collected)GET /api/characters/:id/runsreturns paginated history- Full end-to-end: create character → equip gear → set queue → start run → worker resolves → collect loot → verify character state updated
Feature 08: Crafting & Gathering
Overview
Two parallel idle activity systems: gathering expeditions (send character to mine/herb/log/skin for 1-12 hours) and crafting (combine materials into items over 15 min-12 hours). Both run concurrently with dungeon runs. Crafting has 5 professions, gathering has 4. Each has a skill level (1-100) that unlocks higher-tier content.
Dependencies
- Feature 03 (Items & Inventory) — materials are items, crafted items go to backpack
- Feature 00 (Job Queue) — delayed jobs for completion timers
Technical Tasks
1. Static Game Data
- Create
data/gathering/:mining.toml: 5 tiers of mining nodes (Copper→Iron→Steel→Mithril→Adamantine), zones where they’re found, yield per tier, skill requirement per tierherbalism.toml: 5 tiers of herbslogging.toml: 5 tiers of woodskinning.toml: 5 tiers of leather/hide- Each tier: required skill level, expedition durations (1/3/6/12 hours), yield range, rare material chance
- Create
data/crafting/:blacksmithing.toml: recipes for weapons and heavy armoralchemy.toml: recipes for potions, elixirs, antidotesenchanting.toml: recipes for enchantments (applied to gear)cooking.toml: recipes for food buffsjewelcrafting.toml: recipes for rings and amulets- Per recipe: id, name, profession, skill_req, materials (list of material_id + quantity), result_item_template_id, result_rarity, crafting_time_secs, critical_bonus (what happens on critical craft)
2. Database Migrations
- Create migration
0007_create_crafting.sql:crafting_proficiencies: character_id, profession, skill_level (PK: character_id + profession)crafting_jobs: id, character_id, recipe_id, status, started_at, completes_at, result_item_id, is_criticalgathering_expeditions: id, character_id, profession, zone, tier, status, started_at, completes_at, yields (JSONB)known_recipes: character_id, recipe_id, learned_at (PK: character_id + recipe_id)- Indexes on character_id for all tables
3. Gathering Logic (crates/game)
- Create
src/gathering.rs:can_start_expedition(character_proficiency: u16, tier: u8) -> boolcalculate_expedition_duration(tier: u8, hours: u8, is_patron: bool) -> Durationresolve_expedition(profession: &str, tier: u8, skill_level: u16, rng: &mut impl Rng) -> ExpeditionResult:- Calculate material yields (base + skill bonus)
- Roll for rare materials (5% base, +0.1% per skill level)
- Calculate skill XP gained
ExpeditionResult: materials (vec of id + quantity), skill_xp, rare_finds
4. Crafting Logic (crates/game)
- Create
src/crafting.rs:can_craft(character: &Character, recipe: &Recipe, inventory: &[Item]) -> Result<()>:- Check profession skill level meets requirement
- Check all materials available in inventory
- Check recipe is known
calculate_craft_duration(recipe: &Recipe, is_patron: bool) -> Durationresolve_craft(recipe: &Recipe, skill_level: u16, rng: &mut impl Rng) -> CraftResult:- Generate result item via
item_generation::generate_item - Roll for critical craft: 5% base + 0.15% per skill level (max 20%)
- Critical: bump rarity by one tier (e.g., Rare → Very Rare)
- Calculate skill XP gained
- Generate result item via
CraftResult: item (GeneratedItem), is_critical, skill_xp
5. Queries (crates/db)
- Create
src/queries/crafting.rs:get_crafting_proficiencies(pool, character_id) -> Vec<CraftingProficiency>update_crafting_proficiency(pool, character_id, profession, new_level)get_known_recipes(pool, character_id) -> Vec<String>(recipe IDs)learn_recipe(pool, character_id, recipe_id)create_crafting_job(pool, params) -> CraftingJobfind_active_crafting_jobs(pool, character_id) -> Vec<CraftingJob>complete_crafting_job(pool, job_id, result_item_id, is_critical)create_gathering_expedition(pool, params) -> GatheringExpeditionfind_active_expeditions(pool, character_id) -> Vec<GatheringExpedition>complete_expedition(pool, expedition_id, yields_json)
6. Crafting & Gathering Workers (crates/workers)
- Create
src/crafting_worker.rs:- Register handler for
resolve-craftqueue - On job: load crafting job, resolve craft, create result item, update proficiency, complete job, create notification
- Register handler for
- Create
src/gathering_worker.rs:- Register handler for
resolve-gatheringqueue - On job: load expedition, resolve expedition, create material items in backpack, update proficiency, complete expedition, create notification
- Register handler for
7. API Routes (crates/api)
- Create
src/routes/crafting.rs:GET /api/characters/:id/crafting/proficiencies: all profession levelsGET /api/characters/:id/crafting/recipes: known recipes with craftability status (has materials?)POST /api/crafting/start:- Body:
{ character_id, recipe_id } - Validate: known recipe, has materials, meets skill level
- Deduct materials from inventory
- Create crafting job, enqueue delayed job
- Return
{ job_id, completes_at }
- Body:
GET /api/characters/:id/crafting/active: list active crafting jobs with timersPOST /api/crafting/:job_id/collect: collect completed craft result
- Create
src/routes/gathering.rs:GET /api/characters/:id/gathering/proficiencies: all gathering profession levelsPOST /api/gathering/start:- Body:
{ character_id, profession, zone, tier, duration_hours } - Validate: meets skill level for tier
- Create expedition, enqueue delayed job
- Return
{ expedition_id, completes_at }
- Body:
GET /api/characters/:id/gathering/active: list active expeditions with timersPOST /api/gathering/:id/collect: collect expedition yields
8. Client — Crafting & Gathering UI
- Create
routes/(game)/crafting/+page.svelte:- Profession tabs (Blacksmithing, Alchemy, Enchanting, Cooking, Jewelcrafting)
- Recipe browser: filterable list, shows materials needed, craftable indicator
- Active crafting jobs with countdown timers
- “Craft” button → confirmation with material cost
- Create
routes/(game)/gathering/+page.svelte:- Profession tabs (Mining, Herbalism, Logging, Skinning)
- Tier selection per profession with skill level gates
- Duration selector (1h, 3h, 6h, 12h)
- Active expeditions with countdown timers
- “Collect” button when done
Tests
Unit Tests
can_start_expedition: accepts when skill level sufficientcan_start_expedition: rejects when skill level too low for tierresolve_expedition: returns materials in correct quantity rangeresolve_expedition: rare material chance scales with skill levelcan_craft: accepts with correct materials and skillcan_craft: rejects missing materialscan_craft: rejects insufficient skill levelresolve_craft: generates item with correct rarityresolve_craft: critical craft bumps rarity by one tierresolve_craft: critical chance increases with skill levelcalculate_craft_duration: Patron gets 2/3 durationcalculate_expedition_duration: Patron gets 2/3 duration
Integration Tests
POST /api/crafting/startdeducts materials, creates job, returns completes_atPOST /api/crafting/startrejects when materials insufficient- Craft worker completes job and creates item in character inventory
- Craft worker updates profession skill level
POST /api/crafting/:id/collectreturns crafted itemPOST /api/gathering/startcreates expedition with correct duration- Gathering worker completes expedition and creates material items
POST /api/gathering/:id/collectreturns expedition yields- Multiple activities run concurrently (run + craft + gather)
- Crafting and gathering activities don’t block dungeon runs
Feature 09: Economy & Marketplace
Overview
The player economy: gold as primary currency, the auction house marketplace for player-to-player trading, NPC vendors, the mail system for item/gold transfers, and gold sinks to prevent inflation. All marketplace transactions are serialized through the economy worker to prevent race conditions.
Dependencies
- Feature 03 (Items & Inventory) — items to trade
- Feature 01 (Authentication) — user accounts for mail
Technical Tasks
1. Database Migrations
- Create migration
0008_create_economy.sql:marketplace_listings: id, seller_id, item_id, price, listing_fee, status (active/sold/expired/cancelled), listed_at, expires_at (48h), sold_to, sold_at- Indexes: status WHERE active, expires_at WHERE active, seller_id
mail: id, sender_id (nullable — system mail), recipient_id, subject, body, gold_amount, item_ids (UUID[]), is_read, deliverable_at, sent_at, expires_at- Index: recipient_id
2. Economy Rules (crates/game)
- Create
src/economy.rs:- Constants:
LISTING_FEE_RATE: f64 = 0.05(5% of list price, deducted upfront)SALE_TAX_RATE: f64 = 0.10(10% of sale price, deducted from proceeds)MAIL_GOLD_FEE_RATE: f64 = 0.05(5% fee on gold transfers via mail)LISTING_DURATION: Duration = 48 hoursMAIL_DELIVERY_DELAY: Duration = 1 hourMAIL_EXPIRY: Duration = 30 days
calculate_listing_fee(price: u64) -> u64calculate_sale_proceeds(price: u64) -> u64: price - sale taxcalculate_mail_gold_fee(amount: u64) -> u64- NPC vendor pricing:
npc_sell_price(item: &Item) -> u64: base value by rarity tiernpc_buy_price(item: &Item, background: Background) -> u64: 50-70% of sell value (some backgrounds get better rates)
- Constants:
3. Marketplace Queries (crates/db)
- Create
src/queries/marketplace.rs:create_listing(pool, seller_id, item_id, price, listing_fee) -> Listingfind_active_listings(pool, filters: MarketplaceFilters, limit, offset) -> Vec<Listing>:- Filters: item_type, rarity, level_range, price_range, name search
find_listing_by_id(pool, id) -> Option<Listing>find_listings_by_seller(pool, seller_id) -> Vec<Listing>complete_sale(pool, listing_id, buyer_id)— used by economy workercancel_listing(pool, listing_id)— return item to sellerexpire_listings(pool) -> Vec<Listing>— find all WHERE expires_at < now AND status = active
4. Mail Queries (crates/db)
- Create
src/queries/mail.rs:send_mail(pool, sender_id, recipient_id, subject, body, gold, item_ids) -> Mailfind_mail_for_character(pool, character_id) -> Vec<Mail>: WHERE deliverable_at <= nowfind_mail_by_id(pool, id) -> Option<Mail>mark_read(pool, mail_id)collect_mail_attachments(pool, mail_id)— move items/gold to characterdelete_expired_mail(pool) -> u64— delete WHERE expires_at < now AND is_read
5. Economy Worker (crates/workers)
- Create
src/economy_worker.rs:- Single-consumer queue — only one instance processes the
marketplace-buyqueue to prevent race conditions handle_marketplace_buy(job):- BEGIN TRANSACTION
- Load listing (SELECT FOR UPDATE) — verify still active
- Load buyer character (SELECT FOR UPDATE) — verify gold >= price
- Deduct gold from buyer
- Add sale proceeds (price - 10% tax) to seller’s gold
- Transfer item ownership to buyer (set location to ‘mail’)
- Create mail to buyer with purchased item
- Create mail to seller with gold notification
- Update listing status to ‘sold’
- COMMIT
handle_auction_expiry():- Run every 5 minutes via scheduler
- Find all expired active listings
- Return items to sellers via mail
- Update listing status to ‘expired’
- Single-consumer queue — only one instance processes the
6. Marketplace Routes (crates/api)
- Create
src/routes/marketplace.rs:GET /api/marketplace:- Query params: type, rarity, level_min, level_max, price_min, price_max, search, page
- Return paginated listings with item details
POST /api/marketplace/list:- Body:
{ character_id, item_id, price } - Validate: item owned, in backpack/bank, not equipped, price > 0
- Check seller has available listing slots (default 10, up to 30 with purchases)
- Deduct listing fee (5% of price) from seller’s gold
- Move item to ‘listed’ location
- Create listing record
- Body:
POST /api/marketplace/buy/:listing_id:- Body:
{ character_id } - Validate: buyer has enough gold, listing is active, buyer != seller
- Do NOT directly modify gold — enqueue
marketplace-buyjob - Return
{ status: "processing" }(buyer sees result on next poll)
- Body:
DELETE /api/marketplace/:listing_id:- Cancel own listing, return item to backpack
- Listing fee is NOT refunded
7. Mail Routes (crates/api)
- Create
src/routes/mail.rs:GET /api/characters/:id/mail:- Return delivered mail (deliverable_at <= now)
- Include: sender name, subject, body, gold amount, item summaries, is_read, sent_at
POST /api/mail/send:- Body:
{ sender_id, recipient_name, subject, body, gold_amount?, item_ids? } - Validate: sender owns items, has enough gold + transfer fee
- Deduct gold + fee from sender, remove items from inventory
- Create mail with deliverable_at = now + 1 hour
- Body:
POST /api/mail/:id/collect:- Move attached items to character’s backpack
- Add gold to character’s balance
- Mark as collected (prevent double collection)
8. NPC Vendor Routes (crates/api)
- Add to
src/routes/inventory.rs(or new file):POST /api/characters/:id/sell-to-vendor:- Body:
{ item_ids: [uuid] } - Calculate NPC price per item, add gold to character, delete items
- Body:
- NPC buy is handled via faction vendors (Feature 13)
9. Client — Marketplace & Mail
- Create
routes/(game)/marketplace/+page.svelte:- Search/filter bar: item type, rarity, level, price range, text search
- Listing grid: item card with name, rarity color, stats preview, price, time remaining
- Buy flow: click listing → confirmation modal → POST buy → show “Processing…” → poll for result
- Sell flow: select item from inventory → set price → list
- My Listings tab: active listings with cancel button
- Create
routes/(game)/mail/+page.svelte(or sidebar panel):- Inbox list: sender, subject, gold/item indicators, read status
- Mail detail: body, attachments, “Collect” button
- Compose: recipient name autocomplete, subject, body, attach gold/items
Tests
Unit Tests
calculate_listing_fee: 5% of 1000 = 50calculate_sale_proceeds: 1000 - 10% = 900calculate_mail_gold_fee: 5% of 500 = 25npc_sell_price: scales by rarity tiernpc_buy_price: returns 50-70% of sell price
Integration Tests
POST /api/marketplace/listcreates listing, deducts fee, moves itemPOST /api/marketplace/listrejects equipped itemPOST /api/marketplace/listrejects when insufficient gold for feePOST /api/marketplace/listrejects when at listing slot limitGET /api/marketplacereturns active listings with filtersPOST /api/marketplace/buy/:idenqueues job, economy worker transfers gold/item- Economy worker: buyer gold decreases, seller gold increases by correct amounts
- Economy worker: item transferred to buyer via mail
POST /api/marketplace/buy/:idon already-sold listing → 409DELETE /api/marketplace/:idreturns item to seller, no fee refund- Auction expiry: expired listings return items to seller via mail
POST /api/mail/sendwith gold deducts gold + fee, creates deliverable mailPOST /api/mail/sendwith items removes items from sender inventoryGET /api/characters/:id/mailonly returns mail where deliverable_at <= nowPOST /api/mail/:id/collectmoves items/gold to characterPOST /api/mail/:id/collecttwice → 409- Concurrent buy attempts on same listing: only one succeeds (economy worker serialization)
POST /api/characters/:id/sell-to-vendorgrants gold and deletes items
Feature 10: Guilds & Social
Overview
Guilds (persistent groups up to 200 members with ranks, bank, leveling, and buffs), friends list, party formation for group content, and timed raid lobbies. Chat is handled externally via Discord — this feature covers the in-game social structures only.
Dependencies
- Feature 02 (Character Creation)
- Feature 09 (Economy) — guild bank, gold costs
Technical Tasks
1. Database Migrations
- Create migration
0009_create_social.sql:guilds: id, name (unique), tag (unique, 2-4 chars), leader_id, level, xp, bank_gold, active_buff, buff_expires, max_members, discord_invite_url, created_atguild_members: guild_id, character_id (PK), rank (leader/officer/member/recruit), joined_at- Index: guild_members(character_id)
friends: character_id, friend_id (PK), status (pending/accepted), created_atparties: id, leader_id, type (duo/standard/raid), max_size, status (forming/ready/in_run/completed), quest_id, scheduled_at, created_atparty_members: party_id, character_id (PK), role (tank/healer/dps/support), ready (bool)
2. Guild Logic (crates/game)
- Create
src/guilds.rs:GUILD_CREATION_COST: u64 = 10_000(gold)guild_max_members(guild_level: u16) -> u16: starts at 50, increases with levelguild_xp_for_level(level: u16) -> u64: XP required for next guild levelguild_buff_options() -> Vec<GuildBuff>:- +10% XP for 24 hours
- +10% gold for 24 hours
- -10% crafting time for 24 hours
- Only one buff active at a time
guild_buff_cost(buff: &GuildBuff) -> u64: gold cost from guild bank
3. Guild Queries (crates/db)
- Create
src/queries/guilds.rs:create_guild(pool, name, tag, leader_id) -> Guildfind_guild_by_id(pool, id) -> Option<Guild>find_guild_by_name(pool, name) -> Option<Guild>find_guild_members(pool, guild_id) -> Vec<GuildMember>find_character_guild(pool, character_id) -> Option<(Guild, GuildMember)>add_member(pool, guild_id, character_id, rank)remove_member(pool, guild_id, character_id)update_member_rank(pool, guild_id, character_id, new_rank)update_guild_settings(pool, guild_id, discord_url?)activate_guild_buff(pool, guild_id, buff, expires_at, cost)add_guild_xp(pool, guild_id, xp)
4. Party Queries (crates/db)
- Create
src/queries/parties.rs:create_party(pool, leader_id, type, quest_id?, scheduled_at?) -> Partyfind_party_by_id(pool, id) -> Option<Party>find_character_party(pool, character_id) -> Option<Party>add_party_member(pool, party_id, character_id, role)remove_party_member(pool, party_id, character_id)set_member_ready(pool, party_id, character_id, ready)check_all_ready(pool, party_id) -> boolupdate_party_status(pool, party_id, status)
5. Guild Routes (crates/api)
- Create
src/routes/guilds.rs:POST /api/guilds:- Body:
{ character_id, name, tag } - Validate: name/tag unique, character not in guild, has 10K gold
- Deduct gold, create guild, add creator as leader
- Body:
GET /api/guilds/:id:- Public info: name, tag, level, member count, leader name
GET /api/guilds/:id/members:- Member list with names, ranks, last active
POST /api/guilds/:id/join:- Body:
{ character_id } - Add as recruit (open join — no invite required for now)
- Validate: not already in a guild, guild not full
- Body:
POST /api/guilds/:id/leave:- Remove from guild. Leader cannot leave (must transfer leadership first)
POST /api/guilds/:id/invite:- Body:
{ character_name }— officer+ only
- Body:
PUT /api/guilds/:id/settings:- Body:
{ discord_invite_url? }— leader/officer only
- Body:
POST /api/guilds/:id/promote:- Body:
{ character_id, new_rank }— leader only
- Body:
POST /api/guilds/:id/kick:- Body:
{ character_id }— officer+ only, cannot kick higher rank
- Body:
POST /api/guilds/:id/buff:- Body:
{ buff_id }— officer+ only, deducts from guild bank
- Body:
POST /api/guilds/:id/bank/deposit:- Body:
{ character_id, amount }— deposit gold to guild bank
- Body:
POST /api/guilds/:id/transfer-leadership:- Body:
{ new_leader_id }— leader only
- Body:
6. Friends Routes (crates/api)
- Create
src/routes/social.rs:GET /api/characters/:id/friends:- Return friends list with status (pending/accepted), names, levels
POST /api/friends/add:- Body:
{ character_id, friend_name } - Create pending friend request
- Body:
POST /api/friends/accept/:friend_character_id:- Accept pending request (creates reciprocal entry)
DELETE /api/friends/:friend_character_id:- Remove friend (both directions)
7. Party Routes (crates/api)
- Create
src/routes/parties.rs:POST /api/parties:- Body:
{ leader_id, type, quest_id?, scheduled_at? } - Create party, add leader
- Body:
GET /api/parties/:id:- Party info: members, roles, ready status, quest, schedule
POST /api/parties/:id/join:- Body:
{ character_id, role } - Validate: party not full, correct type limits (duo=2, standard=4, raid=8)
- Body:
POST /api/parties/:id/leave:- Remove from party. If leader leaves, disband.
POST /api/parties/:id/ready:- Body:
{ character_id }— toggle ready status
- Body:
POST /api/parties/:id/start:- Leader only, all members must be ready
- Start group run (uses Feature 07 run system with party_id)
8. Client — Guild & Social UI
- Create
routes/(game)/guild/+page.svelte:- Guild overview: name, tag, level, XP bar, member count
- Member list: name, rank, last active, promote/kick actions
- Guild bank: current gold, deposit button
- Active buff display with “Activate Buff” selector
- Discord link button
- Settings (for officers+): Discord URL
- Create
routes/(game)/social/+page.svelte:- Friends tab: friends list, add friend input, pending requests
- Parties tab: current party, create party, join party
- Create party lobby view for group content coordination
Tests
Unit Tests
guild_max_members: 50 at level 1, scales correctlyguild_buff_cost: returns correct costs per buff type- Guild rank hierarchy: leader > officer > member > recruit
Integration Tests
POST /api/guildscreates guild, deducts 10K goldPOST /api/guildsrejects when already in a guildPOST /api/guildsrejects duplicate name/tagPOST /api/guilds/:id/joinadds as recruitPOST /api/guilds/:id/joinrejects when guild fullPOST /api/guilds/:id/leaveremoves memberPOST /api/guilds/:id/leaveby leader → 409POST /api/guilds/:id/promotechanges rankPOST /api/guilds/:id/kickremoves member (officer+)POST /api/guilds/:id/kickrejects kicking higher rankPOST /api/guilds/:id/buffactivates buff, deducts from bankPOST /api/guilds/:id/buffrejects when buff already activePOST /api/guilds/:id/bank/depositadds gold to guild bankPOST /api/guilds/:id/transfer-leadershipchanges leaderPOST /api/friends/addcreates pending requestPOST /api/friends/acceptcreates mutual friendshipDELETE /api/friends/:idremoves both directionsPOST /api/partiescreates party with leaderPOST /api/parties/:id/joinadds member up to maxPOST /api/parties/:id/startstarts group run when all readyPOST /api/parties/:id/startrejects when not all ready
Feature 11: PVP System
Overview
Arena PVP with instant resolution (no wait timer), ELO-based matchmaking, stat normalization for fairness, 1v1 and 3v3 brackets, seasonal ratings with soft resets, and leaderboards. PVP matches use the same combat engine as PvE but with normalized stats.
Dependencies
- Feature 06 (Combat Engine) — same simulation engine with PVP rules
- Feature 05 (Skill Queue) — players use their configured queues
Technical Tasks
1. Database Migrations
- Create migration
0010_create_pvp.sql:pvp_ratings: character_id, bracket (‘1v1’/‘3v3’), rating (default 1000), season, wins, losses (PK: character_id + bracket + season)pvp_matches: id, bracket, season, team_a (UUID[]), team_b (UUID[]), winner (‘a’/‘b’/‘draw’), match_log (JSONB), rating_changes (JSONB), resolved_at
2. PVP Stat Normalization (crates/game)
- Create
src/pvp.rs:normalize_character(character: &CharacterSnapshot) -> PvpParticipant:- Normalize all base attributes to a baseline (e.g., 30 per attribute)
- Keep weapon type and skill queue (strategy matters)
- Keep gear enchantments/effects (effects matter, raw stats don’t)
- Strip raw stat bonuses from gear
- Set all participants to same HP pool
- This ensures Patron speed bonus has zero PVP impact (arena resolves instantly)
- Higher-level characters have more skills available (real advantage) but not stat advantage
3. ELO Matchmaking (crates/game)
- Create
src/pvp_matchmaking.rs:calculate_elo_change(winner_rating: i32, loser_rating: i32) -> (i32, i32):- Standard ELO formula with K-factor 32
- Winner gains, loser loses (different amounts based on rating gap)
is_viable_match(rating_a: i32, rating_b: i32, wait_seconds: u64) -> bool:- Start with ±150 rating window
- Expand by 50 per 30 seconds of waiting
- Max window: ±500
4. PVP Queue (Redis)
- PVP queue stored in Redis sorted set:
pvp:queue:{bracket}with score = rating join_queue(redis, character_id, bracket, rating): ZADDleave_queue(redis, character_id, bracket): ZREMfind_match(redis, bracket) -> Option<(CharId, CharId)>:- Scan sorted set for viable matches
- Pop both matched players atomically
5. PVP Worker (crates/workers)
- Create
src/pvp_worker.rs:- Matchmaker (runs every 5 seconds via scheduler):
- Call
find_matchfor each bracket - For each match found: enqueue
pvp-resolvejob (immediate)
- Call
- Match resolver (handles
pvp-resolvequeue):- Load both characters’ snapshots
- Normalize stats
- Run combat simulation (same engine, PVP encounter rules)
- Determine winner
- Calculate ELO changes
- Write match result + rating updates to DB
- Create notifications for both players
- Matchmaker (runs every 5 seconds via scheduler):
6. PVP Seasons (crates/game)
- Create
src/pvp_seasons.rs:- Season duration: ~3 months
soft_reset_rating(current: i32) -> i32: compress toward 1000 (e.g., new = 1000 + (current - 1000) / 2)- Season rewards based on final rating tier:
- Bronze (< 1200): title
- Silver (1200-1499): title + portrait frame
- Gold (1500-1799): title + portrait + materials
- Diamond (1800+): title + portrait + exclusive seasonal item
7. PVP Routes (crates/api)
- Create
src/routes/pvp.rs:POST /api/pvp/queue:- Body:
{ character_id, bracket } - Validate: character has active skill queue, has gear, not already in queue
- Add to Redis PVP queue
- Return
{ status: "queued" }
- Body:
DELETE /api/pvp/queue:- Body:
{ character_id, bracket } - Remove from queue
- Body:
GET /api/pvp/queue/status?character_id=:- Return: in_queue (bool), match_found (bool), match_id (if found)
- Polled every 10 seconds by client
GET /api/pvp/history?character_id=:- Paginated match history: opponent, result, rating change, date
GET /api/pvp/leaderboard/:bracket:- Top 100 by rating from Redis sorted set
- Include: rank, character name, rating, wins, losses
GET /api/pvp/ratings?character_id=:- Current ratings for all brackets + season
8. Client — PVP UI
- Create
routes/(game)/pvp/+page.svelte:- Bracket selector (1v1, 3v3)
- Current rating, rank, W/L record
- “Enter Queue” / “Leave Queue” button
- Queue status indicator with polling (10s interval)
- Match result modal when match completes
- Create
routes/(game)/pvp/history/+page.svelte:- Match history list with replay viewer (same as PvE run viewer)
- Create
routes/(game)/pvp/leaderboard/+page.svelte:- Top 100 table per bracket
- Highlight current player’s rank
Tests
Unit Tests
normalize_character: all attributes set to baselinenormalize_character: gear effects preserved, raw stats strippednormalize_character: HP pool equalizedcalculate_elo_change: higher rated player gains less from beating lower ratedcalculate_elo_change: lower rated player gains more from upsetcalculate_elo_change: sum of changes is approximately zerois_viable_match: accepts ±150 at 0 seconds waitis_viable_match: rejects ±200 at 0 seconds, accepts at 30 secondssoft_reset_rating: compresses 1500 → 1250, 500 → 750- PVP combat simulation: deterministic with same seed
- PVP combat simulation: stat normalization means level 10 vs level 50 is fair on raw stats
Integration Tests
POST /api/pvp/queueadds to Redis sorted setDELETE /api/pvp/queueremoves from RedisPOST /api/pvp/queuerejects without active skill queue- Matchmaker finds viable match and enqueues resolution
- PVP worker resolves match, writes result, updates ratings
GET /api/pvp/queue/statusshows match_found after resolutionGET /api/pvp/historyreturns match resultsGET /api/pvp/leaderboard/1v1returns top 100 sorted by ratingGET /api/pvp/ratingsreturns current season ratings- Concurrent queue joins: two players with close ratings → matched within 10 seconds
Feature 12: Factions & Reputation
Overview
Seven factions with a reputation system ranging from Hostile (-3000) to Exalted (21000+). Three conflict pairs where gaining reputation with one faction loses 50% with its opposite. Faction vendors sell exclusive recipes, consumables, and enchantments at reputation tiers. Daily faction quests provide the primary rep grind.
Dependencies
- Feature 07 (Quests & Runs) — faction quests use the run system
- Feature 03 (Items & Inventory) — faction vendor items
Technical Tasks
1. Static Game Data — Factions
- Create
data/factions/:factions.toml: all 7 factions with:- id, name, description, zone, conflict_pair (opposing faction ID or null)
- Factions: Iron Compact, Shadow Court, Arcane Conclave, Primal Circle, Order of the Dawn, Covenant of Dusk, Merchant Consortium
- Conflict pairs: Iron Compact ↔ Shadow Court, Arcane Conclave ↔ Primal Circle, Order of the Dawn ↔ Covenant of Dusk. Merchant Consortium has no conflict.
reputation_tiers.toml: tier thresholds- Hostile: < -1000
- Unfriendly: -1000 to -1
- Neutral: 0 to 2999
- Friendly: 3000 to 5999
- Honored: 6000 to 11999
- Revered: 12000 to 20999
- Exalted: 21000+
faction_vendors/— per faction TOML with items available at each rep tierfaction_quests/— 3 daily quests per faction (templates, generated dynamically)exalted_passives.toml: one unique passive ability per faction at Exalted
2. Reputation Logic (crates/game)
- Create
src/factions.rs:gain_reputation(character_rep: &mut HashMap<String, i32>, faction_id: &str, amount: i32):- Add amount to faction
- If faction has a conflict pair, subtract 50% from opposing faction
- Clamp: minimum -3000, no maximum
get_reputation_tier(rep: i32) -> ReputationTiercan_access_vendor_tier(rep: i32, required_tier: ReputationTier) -> boolget_exalted_passive(faction_id: &str) -> Option<PassiveEffect>: only if Exalted
3. Database — Already Exists
character_reputationtable was defined in the base schema (Feature 02 migration area)- If not yet created, add migration
0011_create_reputation.sql:character_reputation: character_id, faction_id, reputation (default 0), PK: character_id + faction_id
4. Reputation Queries (crates/db)
- Create
src/queries/reputation.rs:get_all_reputation(pool, character_id) -> Vec<(String, i32)>get_faction_reputation(pool, character_id, faction_id) -> i32update_reputation(pool, character_id, faction_id, new_value)batch_update_reputation(pool, character_id, changes: Vec<(String, i32)>)— for conflict pair updates
5. Faction Quest Generation (crates/game)
- Create
src/faction_quests.rs:generate_daily_faction_quests(character: &Character, faction_id: &str, date: NaiveDate) -> Vec<FactionQuest>:- 3 quests per faction per day
- Seeded by character_id + faction_id + date (deterministic per day)
- Quest types: bounties themed to the faction’s identity
- Requires Friendly rep to access faction quests
- Each quest grants: reputation (150-250 per quest), gold, XP, chance at faction-specific materials
6. Faction Routes (crates/api)
- Create
src/routes/factions.rs:GET /api/characters/:id/reputation:- Return all 7 faction standings with tier names
GET /api/factions/:faction_id/vendor?character_id=:- Return items available to buy based on character’s rep tier
- Each item: name, cost (gold or faction tokens), rep requirement
POST /api/factions/:faction_id/vendor/buy:- Body:
{ character_id, item_template_id } - Validate rep tier, deduct cost, create item
- Body:
GET /api/factions/:faction_id/quests?character_id=:- Return today’s 3 faction quests (if Friendly+)
- Include completion status
POST /api/factions/:faction_id/quests/:quest_id/start:- Start faction quest as a run (uses Feature 07 run system)
- On completion, grant rep (plus conflict pair penalty)
7. Update Run Completion for Reputation
- In the simulation worker (Feature 07), after resolving a faction quest run:
- Call
gain_reputationwith appropriate amounts - Handle conflict pair rep loss
- Check for tier-up events (Friendly → Honored, etc.)
- Create notification on tier change
- Call
8. Client — Factions UI
- Create
routes/(game)/factions/+page.svelte:- Faction list: 7 factions with current rep bar, tier badge, conflict pair indicator
- Faction detail panel: description, current tier perks, progress to next tier
- Vendor tab: items purchasable at current tier
- Daily quests tab: 3 quests per day (if Friendly+)
- Exalted passive display (if earned)
Tests
Unit Tests
gain_reputation: adds correct amount to factiongain_reputation: subtracts 50% from conflict pairgain_reputation: Merchant Consortium has no conflict penaltygain_reputation: clamps at -3000 minimumget_reputation_tier: correct tier at boundaries (2999 = Neutral, 3000 = Friendly)generate_daily_faction_quests: returns 3 questsgenerate_daily_faction_quests: same character + date → same quests (deterministic)generate_daily_faction_quests: different date → different quests
Integration Tests
GET /api/characters/:id/reputationreturns all 7 factions with rep valuesGET /api/factions/:id/vendorreturns items appropriate to character’s rep tierPOST /api/factions/:id/vendor/buyat sufficient rep → creates item, deducts costPOST /api/factions/:id/vendor/buyat insufficient rep → 403GET /api/factions/:id/questsreturns daily quests when Friendly+GET /api/factions/:id/questsreturns empty when below Friendly- Completing a faction quest grants reputation and applies conflict penalty
- Reputation tier change creates a notification
Feature 13: Notifications & Push
Overview
The in-game notification system that replaces WebSockets. Workers write notification records to the database when events complete. The client polls for them. Mobile push notifications are sent via FCM/APNs when the player appears to be offline.
Dependencies
- Feature 00 (Project Foundation) — Redis, database
- Feature 01 (Authentication) — user sessions
Technical Tasks
1. Database Migration
- Create migration
0012_create_notifications.sql:notifications: id, character_id, type, title, body, data (JSONB for deep linking), is_read, created_at- Index: (character_id, created_at DESC) WHERE is_read = false
push_tokens: user_id, platform (‘ios’/‘android’/‘web’), token, created_at, updated_at- Index: user_id
notification_preferences: character_id, notification_type, in_app (bool), push (bool)- PK: character_id + notification_type
2. Notification Types (crates/types)
- Create
src/notification.rs:NotificationTypeenum:- RunComplete
- CraftComplete
- GatheringComplete
- ItemSold
- MailReceived
- PvpMatchComplete
- RaidLobbyStarting
- GuildWarDeclared
- ReputationTierUp
- LevelUp
- AchievementEarned
Notificationstruct: id, character_id, notification_type, title, body, data (serde_json::Value), is_read, created_at
3. Notification Service (crates/db or shared)
- Create
src/queries/notifications.rs:create_notification(pool, character_id, type, title, body, data) -> Notificationget_unread_notifications(pool, character_id, since: Option<DateTime>) -> Vec<Notification>mark_read(pool, notification_ids: &[Uuid])mark_all_read(pool, character_id)delete_old_notifications(pool, older_than: DateTime)— cleanup, 30-day retentionget_notification_preferences(pool, character_id) -> Vec<NotificationPreference>update_notification_preference(pool, character_id, type, in_app, push)
4. Push Notification Service
- Create
crates/api/src/push.rs(or shared crate):should_send_push(pool, redis, character_id, notification_type) -> bool:- Check push preference enabled for this type
- Check last API request from this user was > 5 minutes ago (likely offline)
send_push(token: &str, platform: &str, title: &str, body: &str, data: &Value):- FCM for Android (via HTTP v1 API)
- APNs for iOS (via
a]pns2crate or HTTP/2 directly)
register_push_token(pool, user_id, platform, token)unregister_push_token(pool, user_id, platform)
5. Integrate Notifications into Workers
- Update all workers to create notifications on event completion:
- Simulation worker (Feature 07):
RunComplete+LevelUpif applicable - Crafting worker (Feature 08):
CraftComplete - Gathering worker (Feature 08):
GatheringComplete - Economy worker (Feature 09):
ItemSold,MailReceived - PVP worker (Feature 11):
PvpMatchComplete - After creating notification, check if push should be sent
- Simulation worker (Feature 07):
6. Notification Routes (crates/api)
- Create
src/routes/notifications.rs:GET /api/characters/:id/notifications?since=:- Return unread notifications since timestamp
- Ordered by created_at DESC
- Limit 50 per request
POST /api/characters/:id/notifications/read:- Body:
{ ids: [uuid] } - Mark specified notifications as read
- Body:
POST /api/characters/:id/notifications/read-all:- Mark all as read
GET /api/characters/:id/notification-preferences:- Return per-type in_app + push settings
PUT /api/characters/:id/notification-preferences:- Body:
{ type, in_app, push } - Update preference
- Body:
7. Push Token Registration Routes
- Add to auth or settings routes:
POST /api/push/register:- Body:
{ platform, token } - Register push token for authenticated user
- Body:
DELETE /api/push/unregister:- Body:
{ platform } - Remove push token
- Body:
8. Scheduled Cleanup
- Add to scheduler worker:
cleanup-notifications: daily, delete notifications older than 30 dayscleanup-read-notifications: daily, delete read notifications older than 7 days
9. Client — Notifications
- Create notification bell component in the main nav:
- Badge showing unread count
- Dropdown panel listing recent notifications
- Click notification → navigate to relevant screen (run result, mail, PVP result)
- Polling:
GET /api/characters/:id/notificationsevery 30 seconds - Create
routes/(game)/settings/notifications/+page.svelte:- Per-type toggles for in-app and push notifications
- Mobile (Capacitor):
- Register push token on app start via
@capacitor/push-notifications - Handle push tap → deep link to relevant content
- Register push token on app start via
Tests
Unit Tests
should_send_push: returns true when push enabled and user offline > 5 minshould_send_push: returns false when push disabled for typeshould_send_push: returns false when user recently activeNotificationTypeserializes/deserializes correctly
Integration Tests
GET /api/characters/:id/notificationsreturns unread notificationsGET /api/characters/:id/notifications?since=filters by timestampPOST /api/characters/:id/notifications/readmarks as read- Subsequent GET excludes read notifications
POST /api/characters/:id/notifications/read-allclears all- Run completion creates RunComplete notification
- Craft completion creates CraftComplete notification
- Item sold creates ItemSold notification
- Notification preferences are respected (disabled type → no notification created for in-app)
- Push token registration and unregistration work
- Cleanup job deletes old notifications
Feature 14: Dailies, Weeklies & Seasons
Overview
Recurring engagement systems: daily first-run bonus, 3 daily bounties with bonus chest, daily faction quests (Feature 12), weekly challenge dungeons, rotating seasonal events with free/premium reward tracks, and the rested XP bonus for offline players. Designed to reward regular play without punishing absence.
Dependencies
- Feature 07 (Quests & Runs) — daily bounties and weekly challenges are run types
- Feature 04 (Character Progression) — XP bonuses, rested bonus
- Feature 12 (Factions) — daily faction quests
Technical Tasks
1. Database Migrations
- Create migration
0013_create_dailies.sql:daily_tracking: character_id, date, first_run_bonus (bool), bounties_completed (smallint), bounty_chest_claimed (bool), faction_quests (JSONB) — PK: character_id + dateweekly_tracking: character_id, week_start (date), challenge_completed (bool), raid_tokens_earned (int) — PK: character_id + week_startseason_progress: character_id, season_id, track (‘free’/‘premium’), tier_reached (smallint), xp (int) — PK: character_id + season_idseasons: id, name, starts_at, ends_at, theme, is_active
2. Daily Reset Logic (crates/game)
- Create
src/dailies.rs:DAILY_FIRST_RUN_XP_BONUS: f64 = 0.50(50% bonus XP)DAILY_FIRST_RUN_GOLD_BONUS: f64 = 0.50(50% bonus gold)DAILY_BOUNTY_COUNT: u8 = 3generate_daily_bounties(character_level: u16, date: NaiveDate) -> Vec<DailyBounty>:- 3 short bounties, seeded by date for server-wide consistency
- Different content than the quest board
check_bounty_chest_eligible(bounties_completed: u8) -> bool: all 3 doneroll_bounty_chest(character_level: u16, rng: &mut impl Rng) -> Vec<GeneratedItem>:- Guaranteed Uncommon+ item, bonus materials, gold
3. Weekly Challenge Logic (crates/game)
- Create
src/weeklies.rs:generate_weekly_challenge(week_start: NaiveDate) -> WeeklyChallenge:- Rotating modifier dungeon (harder than normal, unique mechanic)
- Same challenge for all players (seeded by week)
- Duration: 1-2 hours
roll_weekly_chest(character_level: u16, rng: &mut impl Rng) -> Vec<GeneratedItem>:- Guaranteed Rare+ item, premium materials
4. Rested Bonus Logic (crates/game)
- Create
src/rested.rs:calculate_rested_bonus(last_active: DateTime, now: DateTime) -> f64:- +25% XP after 24 hours offline
- Caps at +50% after 3+ days
- Linear ramp: 0% at 0h, 25% at 24h, 50% at 72h
consume_rested_bonus(current: f64, xp_earned: u64) -> (f64, u64):- Apply bonus to earned XP
- Deplete rested pool proportionally
- Returns (remaining_bonus, bonus_xp_amount)
5. Seasonal Content Logic (crates/game)
- Create
src/seasons.rs:Season: id, name, starts_at, ends_at, theme, free_track (Vec), premium_track (Vec ) SeasonTier: tier_number, xp_required, rewards (Vec) - Free track: 15 tiers
- Premium track: 30 additional tiers (Patron only)
calculate_season_xp(activity: &str, difficulty: Difficulty) -> u32:- XP from seasonal dungeons, regular runs, PVP, daily completion
check_tier_up(current_xp: u32, current_tier: u16, track: &[SeasonTier]) -> Option<TierUpResult>- Seasonal challenge dungeons: unique encounter modifiers per season theme
6. Queries (crates/db)
- Create
src/queries/dailies.rs:get_or_create_daily_tracking(pool, character_id, date) -> DailyTrackingupdate_daily_tracking(pool, character_id, date, updates)get_or_create_weekly_tracking(pool, character_id, week_start) -> WeeklyTrackingupdate_weekly_tracking(pool, character_id, week_start, updates)get_season_progress(pool, character_id, season_id) -> Option<SeasonProgress>update_season_progress(pool, character_id, season_id, new_xp, new_tier)get_active_season(pool) -> Option<Season>
7. Scheduled Workers
- Update scheduler in
crates/workers:daily-reset(00:00 UTC): no DB mutation needed —daily_trackingrows are created per-day on first accessweekly-reset(Monday 00:00 UTC): same pattern forweekly_trackingrested-bonus-tick(every 1 hour): update rested_bonus for characters inactive > 24hseason-transition(manual or scheduled): end current season, archive progress, activate next season
8. Update Run Completion for Dailies
- In simulation worker, after resolving a run:
- If this is the character’s first run today: apply first-run XP/gold bonus
- If this was a daily bounty: increment bounties_completed, check for chest
- If this was the weekly challenge: mark challenge_completed, grant chest
- If season is active: add season XP, check for tier up
- Apply rested bonus to XP earned (if any rested bonus available)
9. API Routes (crates/api)
- Create
src/routes/dailies.rs:GET /api/characters/:id/dailies:- Return: first_run_bonus_available, daily_bounties (3 with completion status), bounty_chest_available, rested_bonus_percentage
GET /api/characters/:id/weeklies:- Return: weekly_challenge (details + completion status), raid_tokens (earned / cap)
GET /api/seasons/current:- Return: active season info, tracks, rewards per tier
GET /api/characters/:id/season-progress:- Return: current tier, XP, rewards claimed, next tier preview
10. Client — Dailies & Season UI
- Add daily/weekly panel to main game dashboard:
- Daily bounties: 3 checkboxes, bonus chest indicator
- First run bonus indicator
- Weekly challenge: name, completion status
- Rested bonus display
- Create
routes/(game)/season/+page.svelte:- Season theme banner
- Reward track visualization: horizontal progress bar with tier markers
- Free track on top, premium track below (greyed if not Patron)
- Claimed/unclaimed reward indicators
Tests
Unit Tests
generate_daily_bounties: returns 3 bountiesgenerate_daily_bounties: deterministic per datecheck_bounty_chest_eligible: true when 3/3, false otherwisegenerate_weekly_challenge: deterministic per weekgenerate_weekly_challenge: different each weekcalculate_rested_bonus: 0% at 0 hourscalculate_rested_bonus: 25% at 24 hourscalculate_rested_bonus: 50% at 72+ hourscalculate_rested_bonus: linear interpolation between thresholdsconsume_rested_bonus: depletes bonus proportionallycalculate_season_xp: returns correct XP per activity typecheck_tier_up: returns tier-up when XP sufficientcheck_tier_up: returns None when insufficient
Integration Tests
GET /api/characters/:id/dailiesreturns correct state for today- First run of the day applies 50% XP and gold bonus
- Second run of the day does NOT apply bonus
- Completing 3 daily bounties enables bonus chest claim
- Weekly challenge completion grants weekly chest
- Season XP accumulates across activities
- Season tier-up triggers notification
- Rested bonus applies to run XP after offline period
- Daily tracking resets per calendar day (UTC)
- Weekly tracking resets on Monday
Feature 15: Achievements & Collections
Overview
Long-term completionist goals: achievements across all game systems (combat, crafting, social, exploration, PVP, collection), the bestiary (creature catalog), item catalog, recipe collection, titles, and leaderboards. Achievements grant points, titles, and cosmetic rewards.
Dependencies
- Feature 07 (Quests & Runs) — combat and exploration achievements
- Feature 08 (Crafting & Gathering) — crafting achievements
- Feature 11 (PVP) — PVP achievements and leaderboards
- Feature 10 (Guilds) — social achievements
Technical Tasks
1. Static Game Data — Achievements
- Create
data/achievements/:combat.toml: First Blood, Dungeon Crawler (all dungeons), Deadly Survivor (complete Deadly), etc.progression.toml: level milestones, Paragon milestones, full feat selectioncrafting.toml: First Craft, Master Blacksmith (skill 100), Grand Master (all 100), Critical Crafter (10 crits)social.toml: Guilded (join guild), Raid Ready (first raid), Social Butterfly (10 friends)exploration.toml: Explorer (all regions), Lore Hunter, Cartographerpvp.toml: First Duel, Gladiator (Gold rating), Legend (Diamond)collection.toml: Bestiary Complete, Fashionista (25 cosmetics), Title Collector (10 titles)- Per achievement: id, name, description, category, points, reward (title?, portrait_frame?, gold?, materials?), criteria (structured conditions)
2. Achievement Tracking Types (crates/types)
- Create
src/achievement.rs:AchievementCriteriaenum:KillCount { creature_id: Option<String>, count: u32 }— kill N creatures (or specific type)DungeonComplete { dungeon_id: Option<String>, difficulty: Option<Difficulty> }CraftCount { profession: Option<String>, count: u32 }ReachLevel { level: u16 }ReachProficiency { weapon_type: String, level: u16 }ReachReputation { faction_id: String, tier: ReputationTier }PvpRating { bracket: String, rating: i32 }FriendCount { count: u32 }GuildAction { action: String }— join, create, etc.CollectionCount { collection: String, count: u32 }
Achievement: id, name, description, category, points, reward, criteria
3. Database Migration
- Create migration
0014_create_achievements.sql:character_achievements: character_id, achievement_id, earned_at — PK: character_id + achievement_idcharacter_bestiary: character_id, creature_id, kills, first_killed — PK: character_id + creature_idcharacter_titles: character_id, title_id, earned_at, is_active — PK: character_id + title_id
4. Achievement Engine (crates/game)
- Create
src/achievements.rs:check_achievements(character: &Character, event: &GameEvent, state: &AchievementState) -> Vec<String>:- Given a game event (kill, craft, level up, etc.), check all relevant achievements
- Return IDs of newly earned achievements
GameEventenum:CreatureKilled { creature_id: String }RunCompleted { quest_id: String, difficulty: Difficulty }ItemCrafted { profession: String }LevelReached { level: u16 }ProficiencyReached { weapon_type: String, level: u16 }ReputationChanged { faction_id: String, new_rep: i32 }PvpMatchCompleted { bracket: String, new_rating: i32 }FriendAdded { total_friends: u32 }GuildJoined/GuildCreated
AchievementState: set of already-earned achievement IDs (to avoid re-checking)
5. Bestiary Tracking
- Create
src/bestiary.rs:record_kill(character_id: Uuid, creature_id: &str):- Increment kill count
- If first kill, record timestamp
get_bestiary_completion(character_id: Uuid) -> BestiaryProgress:- Total creatures encountered / total creatures in game
- Per-region completion percentage
6. Queries (crates/db)
- Create
src/queries/achievements.rs:get_earned_achievements(pool, character_id) -> Vec<CharacterAchievement>grant_achievement(pool, character_id, achievement_id)get_achievement_points(pool, character_id) -> u32get_bestiary(pool, character_id) -> Vec<BestiaryEntry>upsert_bestiary_entry(pool, character_id, creature_id, kills_delta)get_active_title(pool, character_id) -> Option<String>set_active_title(pool, character_id, title_id)get_earned_titles(pool, character_id) -> Vec<Title>
7. Integrate Achievement Checks into Workers
- After every significant game event, call
check_achievements:- Simulation worker: after each encounter (CreatureKilled), after run (RunCompleted, LevelReached)
- Crafting worker: after craft (ItemCrafted)
- PVP worker: after match (PvpMatchCompleted)
- Reputation updates: after rep change (ReputationChanged)
- Social actions: on friend add, guild join/create
- For each newly earned achievement:
- Grant achievement record
- Grant reward (title, gold, materials)
- Create notification (AchievementEarned)
- Update bestiary after combat encounters (record creature kills)
8. Leaderboard System
- Redis sorted sets for leaderboards (already defined in tech architecture):
leaderboard:pvp:1v1:season:{n}— ratingleaderboard:pvp:3v3:season:{n}— ratingleaderboard:achievements— total pointsleaderboard:wealth— gold
- Update leaderboards when relevant data changes:
- PVP: after match resolution
- Achievements: after achievement granted
- Wealth: after gold change (debounced — update every 5 minutes via scheduler)
9. API Routes (crates/api)
- Create
src/routes/achievements.rs:GET /api/characters/:id/achievements:- Return earned achievements with timestamps + total points
- Include unearned achievements as “locked” entries with descriptions (progression visibility)
GET /api/characters/:id/bestiary:- Return creature entries: name, kills, first killed, completion percentage
GET /api/characters/:id/titles:- Return earned titles, active title
POST /api/characters/:id/titles/set:- Body:
{ title_id } - Set active title (displayed on profile)
- Body:
GET /api/leaderboards/:type:- Query params: page, bracket (for PVP)
- Return top 100 + requesting player’s rank
GET /api/characters/:id/showcase:- Return selected 5 achievements for profile display
PUT /api/characters/:id/showcase:- Body:
{ achievement_ids: [5 max] }
- Body:
10. Client — Achievements UI
- Create
routes/(game)/achievements/+page.svelte:- Category tabs: Combat, Progression, Crafting, Social, Exploration, PVP, Collection
- Achievement cards: name, description, progress bar (if trackable), earned/locked indicator, points, reward preview
- Total achievement points prominently displayed
- Create
routes/(game)/bestiary/+page.svelte:- Grid of creature entries (unlocked show art + stats, locked show silhouette)
- Regional filters
- Completion percentage
- Create
routes/(game)/leaderboards/+page.svelte:- Leaderboard tabs: PVP, Achievements, Wealth, Dungeon Speed, Weekly
- Top 100 table with rank, name, score
- “Your Rank” indicator
- Create
routes/(game)/profile/+page.svelte:- Character card: name, level, class, active title
- Achievement showcase (5 selected)
- Key stats: total achievements, bestiary %, PVP rating
Tests
Unit Tests
check_achievements: CreatureKilled event triggers “First Blood” when first killcheck_achievements: doesn’t re-trigger already-earned achievementscheck_achievements: LevelReached(10) triggers level 10 achievementcheck_achievements: multiple achievements can trigger from one eventrecord_kill: increments count, first kill sets timestampget_bestiary_completion: correct percentage calculation
Integration Tests
- Run completion triggers relevant combat achievements
- Achievement grant creates notification
- Achievement grant adds points to leaderboard
GET /api/characters/:id/achievementsreturns earned + locked achievementsGET /api/characters/:id/bestiaryreturns entries after combatGET /api/leaderboards/achievementsreturns sorted by pointsPOST /api/characters/:id/titles/setchanges active titlePUT /api/characters/:id/showcasesaves 5 achievement selections- Bestiary entry created after first encounter with creature
- Kill count increments across multiple runs
Feature 16: Monetization
Overview
Patron subscription ($3/mo or $30/yr) for 50% faster time-based activities and premium seasonal track, plus small permanent purchases (character slots, storage upgrades, name changes). All payments via Stripe web checkout. No in-app purchase SDK — avoids the 30% platform cut.
Dependencies
- Feature 01 (Authentication) — user accounts with patron_tier
- Feature 07 (Quests & Runs) — patron speed bonus applied to run duration
- Feature 08 (Crafting & Gathering) — patron speed bonus on crafts/expeditions
- Feature 14 (Seasons) — premium track access
Technical Tasks
1. Stripe Integration
- Add Stripe Rust SDK or use HTTP API directly via
reqwest - Create
crates/api/src/payments.rs:create_checkout_session(user_id, product: ProductType) -> String (checkout URL):- Products:
- Patron Monthly ($3.00)
- Patron Annual ($30.00)
- Character Slot ($2.00)
- Bank Space +25 ($1.00)
- Material Bank +50 ($1.00)
- Marketplace Slots +5 ($1.00)
- Name Change ($1.00)
- Appearance Reset ($1.00)
- Create Stripe Checkout Session with success/cancel URLs
- For subscriptions: create Stripe Subscription with appropriate price ID
- Products:
handle_webhook(payload, signature) -> Result<()>:- Verify Stripe signature
- Handle events:
checkout.session.completed→ fulfill purchaseinvoice.paid→ renew subscriptioncustomer.subscription.deleted→ expire subscriptioncustomer.subscription.updated→ handle plan changes
2. Purchase Fulfillment
- Create
src/payments/fulfill.rs:fulfill_patron_subscription(user_id, plan, expires_at):- Set
patron_tier = 1,patron_expires = expires_aton user - All time calculations now check patron status
- Set
fulfill_character_slot(user_id):- Increment
character_slots(max 6)
- Increment
fulfill_bank_space(user_id):- Increment
bank_slotsby 25 (max 150 = 50 base + 4 purchases)
- Increment
fulfill_marketplace_slots(user_id):- Increment
marketplace_slotsby 5 (max 30 = 10 base + 4 purchases)
- Increment
fulfill_name_change(character_id, new_name):- Validate new name, update character
fulfill_appearance_reset(character_id):- Reset appearance fields (future — appearance is just a portrait for now)
3. Patron Speed Bonus Integration
- Ensure all duration calculations check
user.patron_tier:calculate_run_duration(Feature 07): ×0.667 if patroncalculate_craft_duration(Feature 08): ×0.667 if patroncalculate_expedition_duration(Feature 08): ×0.667 if patron- Death recovery timer: ×0.667 if patron
- These are already parameterized — just need to pass
is_patroncorrectly
4. Subscription Expiry Check
- Add to scheduler worker:
check-patron-expiry(daily): find users wherepatron_expires < now()andpatron_tier > 0- Set
patron_tier = 0for expired subscriptions - Active runs/crafts continue at original speed (snapshot at start) — only new activities use free-tier timing
5. Spending Transparency
- Create migration
0015_create_purchases.sql:purchase_history: id, user_id, product_type, amount_cents, stripe_session_id, created_at- Index: user_id
6. API Routes (crates/api)
- Create
src/routes/payments.rs:POST /api/payments/checkout:- Body:
{ product }(e.g., “patron_monthly”, “character_slot”) - Validate: user eligible (e.g., not already at max slots)
- Create Stripe checkout session
- Return
{ checkout_url }
- Body:
POST /api/payments/webhook:- No auth (Stripe calls this directly)
- Verify webhook signature
- Fulfill purchase
GET /api/payments/history:- Return user’s purchase history (amount, product, date)
GET /api/payments/patron-status:- Return: is_patron, expires_at, plan_type
POST /api/payments/cancel-subscription:- Cancel Stripe subscription (takes effect at period end)
- No “are you sure?” hoops — instant cancellation
7. Client — Subscription & Purchases
- Create
routes/(game)/settings/subscription/+page.svelte:- Current status: Free / Patron (with expiry date)
- Subscribe button → opens Stripe checkout in browser
- Cancel button → instant cancellation with confirmation
- What you get: clear comparison table (free vs patron)
- Create
routes/(game)/settings/purchases/+page.svelte:- Available purchases with current limits (e.g., “3/6 character slots”)
- Buy buttons → Stripe checkout
- Purchase history
- Total lifetime spend display (transparency per design doc)
- Stripe checkout opens via
@capacitor/browseron mobile (external browser, not in-app webview)
Tests
Unit Tests
- Duration calculations: patron gets 2/3 duration (parameterized across all activity types)
- Duration calculations: free player gets full duration
fulfill_character_slot: increments correctly, respects max of 6fulfill_bank_space: increments by 25, respects max of 150fulfill_marketplace_slots: increments by 5, respects max of 30
Integration Tests
POST /api/payments/checkoutreturns valid Stripe checkout URL- Stripe webhook
checkout.session.completedfor subscription → user becomes patron - Stripe webhook
invoice.paidfor renewal → patron_expires extended - Stripe webhook
customer.subscription.deleted→ patron expires at period end - Patron expiry check sets patron_tier = 0 after expiration
POST /api/payments/checkoutfor character slot → slot count increases after webhookPOST /api/payments/checkoutfor character slot at max → 409GET /api/payments/historyreturns purchase recordsPOST /api/payments/cancel-subscriptioncancels in Stripe- New run started after becoming patron uses reduced duration
- Run started before patron expires continues at patron speed
Feature 17: Gear Enhancement & Reforging
Overview
Endgame item progression: enhancement (+1 to +5 upgrades that increase base stats) and reforging (re-roll random bonus properties on Rare+ items). Both are material and gold sinks that give max-level players long-term goals without adding power creep.
Dependencies
- Feature 03 (Items & Inventory) — gear with bonus properties
- Feature 08 (Crafting) — crafting materials used as enhancement/reforge inputs
- Feature 09 (Economy) — gold sinks
Technical Tasks
1. Enhancement Logic (crates/game)
- Create
src/enhancement.rs:- Enhancement levels: +1 through +5
- Per level: material cost, gold cost, success chance
- +1: 5 Uncommon Shards, 500 gold, 100% success
- +2: 10 Uncommon Shards + 2 Rare Cores, 2000 gold, 80% success
- +3: 5 Rare Cores, 5000 gold, 60% success
- +4: 10 Rare Cores + 2 Prismatic Essences, 15000 gold, 40% success
- +5: 5 Prismatic Essences + 1 Legendary Spark, 50000 gold, 20% success
- Failure: item stays at current level (no downgrade), materials consumed
enhance_item(item: &Item, target_level: u8, rng: &mut impl Rng) -> EnhanceResult:- Check current enhancement < target
- Roll success chance
- Return Success(new_level) or Failure(current_level)
enhancement_stat_bonus(base_stat: u16, level: u8) -> u16:- +5% per level to base damage/armor/evasion
2. Reforging Logic (crates/game)
- Create
src/reforging.rs:- Only available for Rare+ items (they have random bonus properties)
reforge_cost(rarity: Rarity) -> ReforgeCost:- Rare: 3 Rare Cores + 1000 gold
- Very Rare: 5 Rare Cores + 2 Prismatic Essences + 5000 gold
- Legendary: 3 Prismatic Essences + 1 Legendary Spark + 20000 gold
reforge_item(item: &mut Item, rng: &mut impl Rng) -> Vec<BonusProperty>:- Re-roll ALL random bonus properties (keep count, re-roll values and types)
- Signature effect is NOT re-rolled (it’s fixed per template)
- Return new bonus properties
3. API Routes (crates/api)
- Add to
src/routes/inventory.rs:POST /api/characters/:id/enhance:- Body:
{ item_id } - Validate: item owned, enhancement < 5, has materials + gold
- Deduct materials and gold
- Roll enhancement
- Return
{ success, new_enhancement_level }
- Body:
POST /api/characters/:id/reforge:- Body:
{ item_id } - Validate: item owned, Rare+ rarity, has materials + gold
- Deduct materials and gold
- Re-roll bonuses
- Return
{ new_bonus_properties }
- Body:
4. Client — Enhancement & Reforging UI
- Add to inventory screen:
- “Enhance” button on eligible items → modal showing cost, success chance, current level
- “Reforge” button on Rare+ items → modal showing cost, current bonuses, warning about random results
- Enhancement level displayed on item: “+3 Iron Longsword”
- Visual indicator of enhancement level (glow effect or badge)
Tests
Unit Tests
enhance_item: +1 always succeeds (100%)enhance_item: +5 has 20% success rate (statistical test with seeded RNG)enhance_item: failure doesn’t downgradeenhancement_stat_bonus: +5 at level 3 = +15% bonusreforge_item: keeps same number of bonus propertiesreforge_item: can produce different properties than originalreforge_item: deterministic with seeded RNGreforge_cost: correct per rarity tier
Integration Tests
POST /api/characters/:id/enhancededucts materials and goldPOST /api/characters/:id/enhanceon max (+5) → 409POST /api/characters/:id/enhancewith insufficient materials → 422POST /api/characters/:id/reforgere-rolls bonuses, deducts costPOST /api/characters/:id/reforgeon Common item → 422- Enhanced item stats correctly increased in equipment calculations
Feature 18: Mobile Wrapping & Deployment
Overview
Wrap the SvelteKit SPA in Capacitor for iOS and Android distribution. Configure push notifications, deep links, app lifecycle handling, safe area layout, and the build/release pipeline for app stores.
Dependencies
- Feature 13 (Notifications) — push notification integration
- All client UI features complete (or sufficiently built)
Technical Tasks
1. Capacitor Setup
- Install Capacitor in the client project:
npm install @capacitor/core @capacitor/clinpx cap init delve com.delve.gamenpx cap add iosnpx cap add android
- Configure
capacitor.config.ts:server.urlpointing to production APIpluginsconfiguration for each native plugin
2. Native Plugin Integration
- Install and configure plugins:
@capacitor/push-notifications— FCM (Android) + APNs (iOS)@capacitor/haptics— tactile feedback for loot drops, crits, level ups@capacitor/app— foreground/background detection@capacitor/status-bar— immersive UI (dark status bar, game theme)@capacitor/splash-screen— branded loading screen@capacitor/browser— external links (Stripe checkout, Discord, support)@capacitor/preferences— local key-value storage (cached auth token, settings)
3. Push Notification Flow
- Create
lib/push.ts:- On app start: request push permission
- On token received:
POST /api/push/register { platform, token } - On token refresh: re-register
- On notification received (foreground): show in-app toast
- On notification tapped (background): deep link to relevant screen
- Configure FCM:
- Create Firebase project, add Android app
- Download
google-services.json→android/app/
- Configure APNs:
- Enable Push Notifications capability in Xcode
- Create APNs key in Apple Developer portal
- Upload key to Firebase for FCM→APNs bridging
4. App Lifecycle Handling
- Create
lib/lifecycle.ts:- On
appStateChangeto background:- Pause TanStack Query polling (no network requests while backgrounded)
- Save current state to Preferences (cached auth, last viewed screen)
- On
appStateChangeto foreground:- Resume polling
- Invalidate stale queries (refetch character state, notifications)
- Check for session validity
- On app resume after long background:
- Full state refresh
- On
5. Deep Linking
- Configure URL scheme:
delve:// - Routes:
delve://character/{id}→ character sheetdelve://run/{id}→ run resultdelve://guild/{id}→ guild pagedelve://marketplace→ marketplacedelve://pvp→ PVP arena
- Configure iOS Universal Links and Android App Links for web→app handoff
- Push notification payloads include deep link URLs
6. Responsive & Mobile Polish
- Audit all screens for mobile breakpoint (< 640px):
- Bottom tab navigation instead of sidebar
- Stacked panels instead of side-by-side
- Touch-friendly tap targets (minimum 44px)
- Drag-and-drop (skill queue, inventory) works with touch via pointer events
- Item tooltips trigger on tap-and-hold, dismiss on tap elsewhere
- Safe area handling:
env(safe-area-inset-top)for status barenv(safe-area-inset-bottom)for home indicator / gesture bar
- Keyboard handling:
- Search inputs in marketplace, friend name, mail compose
- Viewport resize on keyboard open (prevent content hidden behind keyboard)
7. App Store Assets
- Generate app icons and splash screens via
@capacitor/assets:- Source: single 1024x1024 icon SVG/PNG
- Outputs: all required iOS and Android icon sizes
- Splash screen: centered logo on brand color background
- Prepare store metadata:
- App name: “Delve”
- Short description (30 chars)
- Full description (4000 chars)
- Keywords
- Screenshots (6.7“ iPhone, 12.9“ iPad, phone + tablet Android)
- Privacy policy URL
- Support URL
8. Build Pipeline
- iOS:
pnpm build→npx cap sync ios- Build via Xcode Cloud or self-hosted Mac runner
- Signing: automatic with App Store Connect API key
- Distribute: TestFlight (beta), App Store (production)
- Android:
pnpm build→npx cap sync android- Build via Gradle:
./gradlew bundleRelease→.aab - Signing: upload key in Play Console
- Distribute: internal testing (beta), production track
- CI integration:
- Add mobile build steps to the
v*tag pipeline - Cache Gradle and CocoaPods dependencies
- Add mobile build steps to the
9. App Store Compliance
- Payment handling:
- All purchases via Stripe web checkout (opens in external browser)
- No in-app purchase SDK
- App description clearly states “subscription managed via web”
- Monitor Apple/Google policy changes on external payment links
- Privacy:
- App Privacy Details (Apple): email address collected for account
- Data Safety (Google Play): email collected, no tracking
- Age rating: fantasy violence (text-based, no graphic content), in-app purchases
Tests
Manual Tests (mobile builds require manual QA)
- App launches on iOS simulator and Android emulator
- Push notification permission prompt appears on first launch
- Push notification received when run completes (app backgrounded)
- Tapping push notification opens correct screen
- App resumes from background and refreshes data
- All screens render correctly at mobile breakpoint
- Safe areas respected on notch/dynamic island devices
- Drag-and-drop works with touch (skill queue, inventory)
- Stripe checkout opens in external browser and returns to app
- Deep links open correct screens
- App works offline (shows cached data, queues actions)
Automated Tests
- Capacitor build succeeds for iOS (
npx cap sync ios→ Xcode build) - Capacitor build succeeds for Android (
npx cap sync android→ Gradle build) - All client unit tests pass in the Capacitor web view context
- Deep link routing resolves to correct pages