Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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 tier
    • herbalism.toml: 5 tiers of herbs
    • logging.toml: 5 tiers of wood
    • skinning.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 armor
    • alchemy.toml: recipes for potions, elixirs, antidotes
    • enchanting.toml: recipes for enchantments (applied to gear)
    • cooking.toml: recipes for food buffs
    • jewelcrafting.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_critical
    • gathering_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) -> bool
    • calculate_expedition_duration(tier: u8, hours: u8, is_patron: bool) -> Duration
    • resolve_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) -> Duration
    • resolve_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
    • 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) -> CraftingJob
    • find_active_crafting_jobs(pool, character_id) -> Vec<CraftingJob>
    • complete_crafting_job(pool, job_id, result_item_id, is_critical)
    • create_gathering_expedition(pool, params) -> GatheringExpedition
    • find_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-craft queue
    • On job: load crafting job, resolve craft, create result item, update proficiency, complete job, create notification
  • Create src/gathering_worker.rs:
    • Register handler for resolve-gathering queue
    • On job: load expedition, resolve expedition, create material items in backpack, update proficiency, complete expedition, create notification

7. API Routes (crates/api)

  • Create src/routes/crafting.rs:
    • GET /api/characters/:id/crafting/proficiencies: all profession levels
    • GET /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 }
    • GET /api/characters/:id/crafting/active: list active crafting jobs with timers
    • POST /api/crafting/:job_id/collect: collect completed craft result
  • Create src/routes/gathering.rs:
    • GET /api/characters/:id/gathering/proficiencies: all gathering profession levels
    • POST /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 }
    • GET /api/characters/:id/gathering/active: list active expeditions with timers
    • POST /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 sufficient
  • can_start_expedition: rejects when skill level too low for tier
  • resolve_expedition: returns materials in correct quantity range
  • resolve_expedition: rare material chance scales with skill level
  • can_craft: accepts with correct materials and skill
  • can_craft: rejects missing materials
  • can_craft: rejects insufficient skill level
  • resolve_craft: generates item with correct rarity
  • resolve_craft: critical craft bumps rarity by one tier
  • resolve_craft: critical chance increases with skill level
  • calculate_craft_duration: Patron gets 2/3 duration
  • calculate_expedition_duration: Patron gets 2/3 duration

Integration Tests

  • POST /api/crafting/start deducts materials, creates job, returns completes_at
  • POST /api/crafting/start rejects when materials insufficient
  • Craft worker completes job and creates item in character inventory
  • Craft worker updates profession skill level
  • POST /api/crafting/:id/collect returns crafted item
  • POST /api/gathering/start creates expedition with correct duration
  • Gathering worker completes expedition and creates material items
  • POST /api/gathering/:id/collect returns expedition yields
  • Multiple activities run concurrently (run + craft + gather)
  • Crafting and gathering activities don’t block dungeon runs