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