Crafting System Architecture Analysis - elanthia-online/dr-scripts GitHub Wiki
Version: 1.3 Date: December 2025 Target Audience: Script Contributors Last Updated: Added spin.lic (fiber spinning utility)
- Executive Summary
- Terminology Reference
- System Architecture Overview
- Data Layer
- Script Layer
- Common Library (DRCC)
- Workflow Analysis
- Known Issues and Pain Points
- Improvement Opportunities
- Feature Gap Analysis
- Refactoring Recommendations
The Lich crafting system is a comprehensive automation framework for DragonRealms crafting disciplines. It consists of:
-
3 Core Data Files:
base-crafting.yaml,base-recipes.yaml,base-ingredients.yaml -
15 Scripts: Discipline-specific automation scripts (
.licfiles) -
1 Orchestrator:
workorders.lic- coordinates work order completion -
1 Training Script:
craft.lic- skill training automation -
1 Common Library:
common-crafting.rb(DRCC module) -
1 User Profile:
base.yaml- user configuration
- Modular discipline-specific scripts
- Centralized data-driven recipe management
- Comprehensive work order automation
- Flexible tool belt support
- Skill training automation with tier-based item selection
- Inconsistent error handling across scripts
- Complex material management with edge cases
- Adjustable tongs logic is fragile
- Limited observability and debugging support
- Inconsistent terminology between scripts and official DR names
This section maps official DragonRealms terminology to script/data file usage. Reference: Elanthipedia Crafting
DragonRealms crafting is organized into 5 Skills, each containing Sub-disciplines:
| Skill | Sub-disciplines | Status |
|---|---|---|
| Forging | Blacksmithing, Armorsmithing, Weaponsmithing | Implemented |
| Engineering | Shaping, Carving, Tinkering | Implemented |
| Outfitting | Tailoring, Spinning, Jewelry Making, Artistry | Partial (Tailoring, Spinning) |
| Alchemy | Remedies, Reactants, Cooking | Partial (Remedies only) |
| Enchanting | Artificing, Binding, Invoking | Partial (Artificing only) |
| Official Term | Script Arg | Data File Key | Script File | Notes |
|---|---|---|---|---|
| Forging (skill) | forging |
forging_tools |
- | Parent skill |
| Blacksmithing | blacksmithing |
blacksmithing |
forge.lic |
Tools, containers |
| Armorsmithing | armorsmithing |
blacksmithing |
forge.lic |
Shares data with Blacksmithing |
| Weaponsmithing | weaponsmithing |
blacksmithing |
forge.lic |
Shares data with Blacksmithing |
| Engineering (skill) | engineering |
engineering_tools |
- | Parent skill |
| Shaping | shaping |
shaping |
shape.lic |
Wood items, bows |
| Carving | carving |
shaping |
carve.lic |
Uses shaping data |
| Tinkering | - | shaping |
tinker.lic |
Uses shaping data |
| Outfitting (skill) | outfitting |
outfitting_tools |
- | Parent skill |
| Tailoring | tailoring |
tailoring |
sew.lic |
Cloth, leather, knitting |
| Spinning | - | - | spin.lic |
Fiber → thread/yarn (utility) |
| Alchemy (skill) | alchemy |
alchemy_tools |
- | Parent skill |
| Remedies | remedies |
remedies |
remedy.lic |
Potions, salves |
| Enchanting (skill) | enchanting |
enchanting_tools |
- | Parent skill |
| Artificing | artificing |
artificing |
enchant.lic |
Founts, foci, runestones |
| Script | Uses Term | Should Be | Context |
|---|---|---|---|
workorders.lic |
blacksmithing |
Blacksmithing | Correct |
workorders.lic |
weaponsmithing |
Weaponsmithing | Correct |
workorders.lic |
tailoring |
Tailoring | Correct |
workorders.lic |
shaping |
Shaping | Correct |
workorders.lic |
carving |
Carving | Correct |
workorders.lic |
remedies |
Remedies | Correct |
workorders.lic |
artificing |
Artificing | Correct |
craft.lic |
forging |
Forging (skill) | Training the skill |
craft.lic |
outfitting |
Outfitting (skill) | Training the skill |
craft.lic |
engineering |
Engineering (skill) | Training the skill |
craft.lic |
alchemy |
Alchemy (skill) | Training the skill |
craft.lic |
enchanting |
Enchanting (skill) | Training the skill |
smith.lic |
type param |
Blacksmithing type |
armorsmithing/weaponsmithing/blacksmithing
|
forge.lic |
book_type |
- | Uses blacksmithing, armorsmithing, weaponsmithing
|
enchant.lic |
book_type |
- | Uses artificing
|
sew.lic |
book_type |
- | Uses outfitting
|
| Book Type | Official Sub-discipline | Skill |
|---|---|---|
blacksmithing |
Blacksmithing | Forging |
armorsmithing |
Armorsmithing | Forging |
weaponsmithing |
Weaponsmithing | Forging |
shaping |
Shaping | Engineering |
carving |
Carving | Engineering |
tinkering |
Tinkering | Engineering |
outfitting |
Tailoring | Outfitting |
remedies |
Remedies | Alchemy |
artificing |
Artificing | Enchanting |
User Invocation
|
+--------------------+--------------------+
| |
+---------v---------+ +----------v----------+
| workorders.lic | | craft.lic |
| (Work Orders) | | (Skill Training) |
+---------+---------+ +----------+----------+
| |
+--------------------+--------------------+
|
+---------------+-----------+-----------+---------------+
| | | | |
+---v---+ +---v---+ +---v---+ +---v---+ +---v---+
|Forging| |Outfit.| | Engin.| |Alchemy| |Enchant|
+-------+ +-------+ +-------+ +-------+ +-------+
|forge | |sew | |shape | |remedy | |enchant|
|smith | |spin | |carve | |alchemy| | |
|smelt | | | |tinker | | | | |
+---+---+ +---+---+ +---+---+ +---+---+ +---+---+
| | | | |
+---------------+-----------+-----------+---------------+
|
+-----------v-----------+
| common-crafting.rb |
| (DRCC Module) |
+-----------+-----------+
|
+-----------v-----------+
| Data Files (YAML) |
| base-crafting.yaml |
| base-recipes.yaml |
| base-ingredients.yaml |
+-----------------------+
| Level | Component | Purpose |
|---|---|---|
| 0 | craft.lic |
Skill training automation (calls other scripts) |
| 1 | workorders.lic |
Work order orchestration, NPC interaction, logistics |
| 2 | smith.lic |
Mid-level wrapper for Forging with enhancements |
| 3 |
forge.lic, sew.lic, shape.lic, carve.lic, tinker.lic, remedy.lic, enchant.lic
|
Low-level craft execution engines |
| 4 |
smelt.lic, alchemy.lic, spin.lic
|
Utility scripts (metal combining, ingredient prep, fiber spinning) |
| Lib | common-crafting.rb |
Shared functions (DRCC module) |
| Discipline | Primary Script | Skill | Notes |
|---|---|---|---|
| Blacksmithing | forge.lic |
Forging | General metal items |
| Weaponsmithing | forge.lic |
Forging | Weapons from metal |
| Armorsmithing | forge.lic |
Forging | Armor from metal |
| Tailoring | sew.lic |
Outfitting | Cloth/leather/knitting |
| Spinning | spin.lic |
Outfitting | Fiber → thread/yarn |
| Shaping | shape.lic |
Engineering | Wood items, bows |
| Carving | carve.lic |
Engineering | Stone/bone items |
| Tinkering | tinker.lic |
Engineering | Crossbows, mechanisms |
| Remedies | remedy.lic |
Alchemy | Potions, salves |
| Artificing | enchant.lic |
Enchanting | Magical items |
Location: scripts/data/base-crafting.yaml
Purpose: Contains town-specific crafting infrastructure data.
Key Sections:
blacksmithing:
<hometown>:
stock-room: <room_id> # Where to buy ingots
stock-number: <order_num> # Order number for ingots
stock-volume: <int> # Volume per stock purchase
anvils: [room_ids] # Available anvil rooms
crucibles: [room_ids] # Available crucible rooms
grindstones: [room_ids] # Available grindstone rooms
npc-rooms: [room_ids] # Work order NPC locations
npc: <name> # NPC first name
npc_last_name: <name> # NPC last name for finding
repair-room: <room_id> # Tool repair NPC room
repair-npc: <name> # Repair NPC name
finisher-room: <room_id> # Consumables (oil) room
finisher-number: <order_num> # Oil order number
logbook: <type> # Logbook type (forging/outfitting/etc)Disciplines Covered:
-
blacksmithing(also used for weaponsmithing/armorsmithing) tailoringshapingremediesartificing-
stock(material definitions) -
recipe_parts(component locations) -
deeds(deed packet locations)
Issues Identified:
- Some hometowns have incomplete data
- Room IDs may become stale with game updates
- No validation layer for data integrity
Location: scripts/data/base-recipes.yaml
Purpose: Complete recipe catalog with metadata.
Recipe Structure:
- name: "a metal longsword" # Full recipe name (must match exactly)
noun: longsword # Item noun for commands
volume: 7 # Material volume required
type: weaponsmithing # Discipline type
work_order: true # Can be used for work orders
chapter: 2 # Recipe book chapter
part: # Required assembly parts
- hiltExtended Fields (Alchemy/Enchanting):
# Alchemy recipes
- name: "blister cream"
herb1: "red flower"
herb1_stock: 1 # Stock number (null = forage)
herb2: "nemoih"
herb2_stock: null
catalyst: bar
container: mortar
noun: cream
# Enchanting recipes
- enchant_stock1_name: "abolition"
enchant_stock1: 6
item: 17 # Base item order numberStatistics (Approximate):
- 500+ recipes total
- Covers: blacksmithing, armorsmithing, weaponsmithing, tailoring, shaping, carving, remedies, artificing
Location: scripts/data/base-ingredients.yaml
Purpose: Foraging and ingredient processing data.
Structure:
ingredients:
- name: "red flower"
output: "dried flower" # Result after processing
ranks: 50 # Outdoorsmanship skill required
Crossing: 123 # Room ID per hometown
Riverhaven: 456
stackable: trueLocation: scripts/profiles/base.yaml
Purpose: User configuration template.
Key Settings:
hometown: Crossing
crafting_container: backpack
crafting_items_in_container: [tool1, tool2]
# Discipline-specific
forging_belt:
name: "forging belt"
items: [tongs, hammer, bellows]
forging_tools: [tongs, hammer, bellows, shovel]
adjustable_tongs: true
# Work order settings
workorder_diff: challenging
workorder_min_items: 1
workorder_max_items: 6
craft_max_mindstate: 32
# Material preferences
workorders_materials:
metal_type: bronze
fabric_type: burlap
wood_type: balsaPurpose: End-to-end work order automation.
Responsibilities:
- Request work orders from NPCs
- Validate recipe availability
- Calculate material requirements
- Dispatch to appropriate craft script
- Bundle completed items
- Turn in work order
Workflow:
request_work_order() -> find_recipe() -> [discipline]_items() -> bundle_item() -> complete_work_order()
Discipline Method Mapping:
| Discipline | Method | Called Script |
|---|---|---|
| blacksmithing/weaponsmithing | forge_items[_with_own_ingot] | smith.lic -> forge.lic |
| tailoring (ch 2-4) | sew_items | sew.lic |
| tailoring (ch 5) | knit_items | sew.lic |
| shaping | shape_items | shape.lic |
| carving | carve_items | carve.lic |
| remedies | remedy_items | remedy.lic |
| artificing | enchanting_items | enchant.lic |
Purpose: Core forging state machine.
Modes:
- Standard forging (from recipe book)
- Instructions-based forging
- Enhancements (temper, hone, balance, lighten, reinforce)
- Resume interrupted work
State Machine Pattern:
def work(settings)
loop do
result = DRC.bput(@command, *PATTERNS)
case result
when 'pattern1' then swap_tool('tool1'); @command = 'new_command'
when 'pattern2' then assemble_part
when 'Roundtime' then finish if Flags['work-done']
end
end
endFlag System:
-
forge-assembly: Triggers part assembly -
work-done: Signals craft completion -
ingot-restow: Handles leftover ingot material
Tool Flow:
anvil work: hammer + tongs
grindstone: wire brush
slack tub: empty hands
oil finishing: oil bottle
Purpose: Higher-level forging with enhancements.
Responsibilities:
- Look up recipe in base-recipes.yaml
- Ensure copper on hand
- Buy required parts
- Buy ingot (if buy flag)
- Find anvil
- Call forge.lic
- Apply enhancements (temper/hone/balance/lighten/reinforce)
- Dispose of scrap
Enhancement Flow:
[temper, hone, balance, lighten, reinforce].compact.each do |enhancement|
DRC.wait_for_script_to_complete('forge', [enhancement, recipe['noun']])
endPurpose: Sewing, knitting, and leather work.
Modes:
- Standard sewing (cloth/leather)
- Knitting (yarn)
- Enhancements (seal, reinforce, lighten)
- Resume interrupted work
Material Detection:
@cloth = %w[silk wool burlap cotton felt linen electroweave steelsilk ...]
if @cloth.include?(@mat_type)
# cloth workflow
else
# leather workflow
endTool Flow:
cutting: scissors
sewing: sewing needles + thread
pinning: pins
smoothing: slickstone
measuring: yardstick
hole punching: awl
sealing: sealing wax
knitting: knitting needles + yarn
Purpose: Remedy/potion creation.
Flag System:
-
remedy-water: Add water -
remedy-alcohol: Add alcohol -
remedy-catalyst: Add catalyst -
remedy-herb: Add second herb -
remedy-turn: Turn container -
remedy-smell: Smell mixture -
remedy-sieve: Sieve mixture
Container Handling:
@container = mortar (external) or bowl/cauldron (internal)
@pestle = pestle (external) or mixing stick (internal)
@verb = crush (external) or mix (internal)Special Container Support:
# Water/alcohol containers that generate contents
if @settings.water_container
fput("rub my #{@settings.water_container}") # Generate water
endPurpose: Lumber shaping and bow crafting.
Modes:
- Standard shaping (from lumber)
- Arrow fletching (from shafts)
- Bow enhancements (laminate, lighten, cable)
- Resume interrupted work
Tool Flow:
initial: drawknife
shaping: shaper
carving: carving knife
smoothing: rasp
clamping: clamps
gluing: glue
staining: stain
Purpose: Stone and bone carving for Engineering discipline.
Modes:
- Standard carving (from stone/bone)
- Resume interrupted work
Material Types:
| Type Arg | Material | Main Tool | Notes |
|---|---|---|---|
stack |
Bone | Saw | Stackable bone material |
bone |
Bone | Saw | Alias for stack |
rock |
Stone | Chisel | Small stone |
stone |
Stone | Chisel | Medium stone |
pebble |
Stone | Chisel | Tiny stone |
boulder |
Stone | Chisel | Large stone (can't pick up) |
deed |
Stone | Chisel | Deed-based stone on sled |
Tool Selection Logic:
@main_tool = if @type == 'stack' || @type == 'bone'
@settings.carving_tools.find { |item| /\bsaw/i =~ item }
else
@settings.carving_tools.find { |item| /\bchisel/i =~ item }
endTool Flow:
cutting: saw (bone) or chisel (stone)
smoothing: rifflers, rasp
polishing: polish
Flag System:
-
carve-assembly: Triggers part assembly (hilt, haft, pole, cord)
Ownership Handling:
# Tracks whether item needs "my" prefix
@my = 'my ' # If we picked it up
@my = '' # If it's too heavy (boulder) or on sledResume Flow:
def resume
case DRC.bput("analyze my #{@noun}", ...)
when /prevent|obstruct carving/ then cut
when /riffler set/ then rub with rifflers
when /rasp/ then rub with rasp
when /polish/ then apply polish
when /finished/ then exit
end
endPurpose: Crossbow crafting and mechanism creation for Engineering discipline.
Modes:
- Standard tinkering (crossbows from lumber)
- Bolt fletching (from shafts)
- Crossbow enhancements (laminate, lighten, cable)
- Mechanism creation (from metal ingots)
- Resume interrupted work
Unique Feature - Mechanism Creation:
# Direct invocation for mechanism making
# ;tinker mechanisms bronze 3
def mechanisms(args)
DRCC.find_shaping_room(@hometown)
fput("turn press to 4")
count.times do
DRCC.get_crafting_item("#{args.material} ingot", ...)
swap_tool("shovel") # Fuel the press
swap_tool("pliers")
work('push my ingot with press')
# Combine stacks if making multiple
DRCC.stow_crafting_item('mechanisms', @bag, nil)
end
endTool Flow:
initial: drawknife (lumber) or shaper (bolts)
shaping: shaper
carving: carving knife
smoothing: rasp
clamping: clamps
gluing: glue
staining: stain
adjusting: tinker tools
mechanism work: pliers + gear press
fuel: shovel (or adjustable tongs)
Flag System:
-
tinkering-assembly: Triggers part assembly (backer, string, pole, cord, strip, lenses, mechanism, boltheads, bolt flights) -
tinker-done: Signals craft completion
Adjustable Tongs Support:
# Uses same pattern as forge.lic
if next_tool.include?('shovel') && @adjustable_tongs
DRCC.get_adjust_tongs?('shovel', @bag, @bag_items, @belt, @adjustable_tongs)
endMechanism Assembly:
# Special handling for mechanisms vs other parts
if part.include?("mechanism")
until /is not required/ =~ DRC.bput("assemble my mechanism with my #{@noun}", ...)
pause 0.05
end
else
DRC.bput("assemble my #{@noun} with my #{part}", ...)
endmagic_cleanup Variation:
# Tinker has unique symbiosis handling
def magic_cleanup
DRC.bput('prepare symbiosis', ...) # Prepare then release
DRC.bput('release symbiosis', ...)
DRC.bput('release spell', ...)
DRC.bput('release mana', ...)
endPurpose: Magical item creation.
Flag System:
-
enchant-focus: Focus on item -
enchant-meditate: Meditate on fount -
enchant-imbue: Cast imbue spell -
enchant-push: Push with loop -
enchant-sigil: Trace next sigil -
enchant-complete: Finished -
imbue-failed/backlash: Error states
Sigil Tracing:
def trace_sigil(sigil)
DRCI.get_item?("#{sigil} sigil")
DRC.bput("study my #{sigil} sigil", /commit the design to memory/)
DRC.bput("trace #{@item} on #{@brazier}", /trace its form/)
endImbue Methods:
- Spell-based (from waggle_sets)
- Wand-based (imbue wand/rod)
Purpose: Forage and prepare ingredients for Remedies crafting.
Note: This is a utility script, not a crafting engine. It prepares materials for remedy.lic.
Modes:
-
forage: Gather herbs from the world -
prepare: Process herbs (dry/crush) at press/grinder
Invocation:
;alchemy <ingredient> forage [quantity] # Forage ingredient
;alchemy <ingredient> prepare # Process ingredient
;alchemy <ingredient> forage prepare 25 # Forage 25 then processData Source: Uses base-ingredients.yaml for:
- Ingredient locations per hometown
- Output product names (e.g., "red flower" → "dried flower")
- Required Outdoorsmanship ranks
- Stackability information
Key Settings:
alchemy_herb_quantity: 25 # Default forage quantity
alchemy_herb_storage: haversack # Where to store raw herbs
herb_container: mortar # Where processed herbs go
alchemy_prep_quantity: 75 # Max stack size after prep
alchemy_forage_type: careful # Forage type (careful/precise)
forage_override_room: null # Override forage location
forage_override_town: null # Override processing townWorkflow:
def forage_ingredient(ingredient)
DRCT.walk_to(@forage_location) # Go to forage room
while quantity_needed >= 0
DRC.bput("forage #{ingredient['name']} #{@alchemy_forage_type}")
DRCI.put_away_item?(ingredient['name'], @alchemy_herb_storage)
quantity_needed -= 6 # Forage yields 6 units
end
end
def prepare_ingredient(ingredient)
DRCC.find_press_grinder_room(@press_location)
while count(ingredient['name'], @alchemy_herb_storage) > 0
if ingredient['output'].include?("dried")
fput("put my #{ingredient['name']} in press") # Drying
else
fput("put my #{ingredient['name']} in grinder") # Crushing
end
# Combine stacks up to 75
end
endCount Helper:
# Counts stacked items using ordinals (first, second, etc.)
def count(item, container)
$ORDINALS.each do |ordinal|
# "count my first dried flower in my haversack"
# Stops when "I could not find" is returned
end
endPurpose: Automated skill training through crafting disposable items.
Skill Coverage:
- Forging (via
smith.lic) - Outfitting (via
sew.lic- knitting and sewing) - Engineering (via
shape.lic) - Alchemy (via
remedy.lic+alchemy.lic) - Enchanting (via
enchant.lic)
Invocation:
;craft forging # Train Forging skill
;craft outfitting # Train Outfitting skill
;craft engineering # Train Engineering skill
;craft alchemy # Train Alchemy skill
;craft enchanting # Train Enchanting skillTier-Based Item Selection:
The script selects items based on current skill rank to maximize learning:
| Skill | Tier 1 (0-25) | Tier 6 (300-425) | Tier 12 (1400+) |
|---|---|---|---|
| Forging | shallow metal cup | metal armband | wire sieve |
| Outfitting | knitted socks | knitted cloak | artisan's belt |
| Engineering | wood band | wood armband | ornate burin |
| Alchemy | blister cream | chest unguent | vigor poultices |
| Enchanting | radiant trinket | strange arrow runestone | electric focus |
Tier Thresholds:
# Rank-based tier selection (12 tiers)
Tier 1: 0-25 # Extremely Easy
Tier 2: 26-50 # Very Easy
Tier 3: 51-100 # Easy
Tier 4: 101-175 # Simple
Tier 5: 176-300 # Basic
Tier 6: 301-425 # Somewhat Challenging
Tier 7: 426-550 # Challenging
Tier 8: 551-700 # Complicated
Tier 9: 701-850 # Intricate
Tier 10: 851-1175 # Difficult
Tier 11: 1176-1400 # Very Difficult
Tier 12: 1401+ # Extremely DifficultKey Settings:
craft_max_mindstate: 32 # Max XP before exiting
craft_overrides: # Override tier item selection
Forging:
item: "custom item name"
Outfitting:
type: sew # or knit
chapter: 3
item: "item name"
volumes: 10 # fabric volume needed
yarn_quantity: 100 # Minimum yarn before restock
worn_trashcan: "bucket" # Where to dispose items
worn_trashcan_verb: "put" # Disposal verbTraining Methods:
| Skill | Method | Sub-script Called |
|---|---|---|
| Forging | train_forging() |
smith.lic with buy flag |
| Outfitting | train_outfitting() |
sew.lic (knit or sew mode) |
| Engineering | train_engineering() |
shape.lic with trash flag |
| Alchemy | train_alchemy() |
alchemy.lic + remedy.lic
|
| Enchanting | train_enchanting() |
enchant.lic |
Material Handling:
def check_yarn
buy_yarn if count < @yarn_quantity
end
def check_fabric(fabric_volumes)
existing = count_fabric_in_bag
stock_needed = ((fabric_volumes - existing) / 10.0).ceil
order_fabric(stock_room, stock_needed, ...)
end
def check_wood
buy_wood if count < 5
endClassroom Integration:
# Teaching support
def check_teaching
@settings.classes_to_teach.each { |class| teach_to_pcs }
end
# Learning support
def check_listening
DRC.listen?(@last_teacher, @settings.listen_observe)
DRC.assess_teach # Find classes to join
endDisposal:
# All crafted items are disposed after completion
DRCI.dispose_trash(item_noun, @worn_trashcan, @worn_trashcan_verb)Purpose: Combine ingots in crucible.
State Machine:
case result
when 'clumps of molten metal' then turn crucible
when 'flickers' then push bellows
when 'needs more fuel' then push fuel with shovel/tongs
endPurpose: Spin raw fibers into thread/yarn for Tailoring.
Note: This is a material preparation utility for Outfitting, similar to how alchemy.lic prepares ingredients for Remedies.
Invocation:
;spin <material> [weight]
# Examples:
;spin silk heavy # Spin silk fibers into heavy thread
;spin wool yarn # Spin wool fibers into yarn
;spin cotton # Defaults to heavy weightSupported Materials:
| Material | Stock Index | Cost | Count | Notes |
|---|---|---|---|---|
| silk | 1 | 187 | 14 | Requires cutting at 10 yards |
| cotton | 2 | 185 | 8 | Standard processing |
| linen | 3 | 167 | 8 | Standard processing |
| wool | 4 | 133 | 8 | Standard processing |
| burlap | 5 | 201 | 4 | Standard processing |
Weight Options:
-
fine- Finest thread -
thin- Thin thread -
average- Medium weight -
thick- Thick thread -
heavy- Heavy thread (default) -
yarn- Yarn weight
Workflow:
def initialize
# 1. Calculate cost and buy fibers
DRCM.ensure_copper_on_hand(data[:cost] * data[:count], @settings, @hometown)
# 2. Order and combine fibers
data[:count].times do |index|
DRCT.order_item(stock_room, data[:index])
if index == 0 && data[:name] == 'silk'
# Silk requires special handling - cut at 10 yards
DRC.bput('mark my fibers at 10', /mark it for cutting/)
# Cut with scissors, drop excess
else
fput('combine my fiber')
end
end
# 3. Find spinning wheel
until DRCC.find_wheel(@hometown)
move('go door')
pause 30
end
# 4. Spin the fiber
fput('put fiber on wheel')
fput("adjust wheel to #{args.weight || 'heavy'}")
# 5. State machine loop
until DRC.left_hand || DRC.right_hand
case DRC.bput('spin wheel', ...)
when 'shade' then fput('clean wheel')
when 'twist' then fput('turn wheel')
when 'bunch' then fput('push wheel')
when 'slide' then fput('push wheel')
end
end
# 6. Stow result
fput("stow my #{DRC.right_hand || DRC.left_hand}")
endState Machine:
| Game Output | Action | Purpose |
|---|---|---|
shade |
clean wheel |
Remove debris |
twist |
turn wheel |
Adjust tension |
Individual strands...twisting |
turn wheel |
Adjust tension |
bunch |
push wheel |
Even out fiber |
slide |
push wheel |
Even out fiber |
| (other/roundtime) | wait | Continue spinning |
Dependencies:
- Uses
DRCC.find_wheel()from common-crafting.rb - Reads
tailoringstock room frombase-crafting.yaml - Uses
outfitting_beltfor scissors
Location: lib/dragonrealms/commons/common-crafting.rb
Module: Lich::DragonRealms::DRCC
DRCC.find_anvil(hometown) # Find empty anvil room
DRCC.find_empty_crucible(hometown)
DRCC.find_grindstone(hometown)
DRCC.find_sewing_room(hometown, override)
DRCC.find_shaping_room(hometown, override)
DRCC.find_enchanting_room(hometown, override)
DRCC.find_wheel(hometown) # Spinning wheel
DRCC.find_press_grinder_room(hometown)DRCC.get_crafting_item(name, bag, bag_items, belt, skip_exit = false)
DRCC.stow_crafting_item(name, bag, belt)
DRCC.get_adjust_tongs?(usage, bag, bag_items, belt, adjustable_tongs = false)DRCC.find_recipe(chapter, match_string, book = 'book')
DRCC.find_recipe2(chapter, match_string, book = 'book', discipline = nil)
DRCC.recipe_lookup(recipes, item_name)DRCC.check_consumables(name, room, number, bag, bag_items, belt, count = 3)
DRCC.repair_own_tools(info, tools, bag, bag_items, belt)DRCC.empty_crucible?
DRCC.clean_anvil?
DRCC.clean_brazier?
DRCC.logbook_item(logbook, noun, container)
DRCC.crafting_cost(recipe, hometown, parts, quantity, material)
DRCC.count_raw_metal(container, type = nil)1. workorders.lic: request_work_order()
- Walk to NPC rooms
- Ask for work order
- Filter by recipe availability
2. workorders.lic: forge_items() / forge_items_with_own_ingot()
- Calculate materials needed
- Loop for each item:
a. Order/get ingot
b. Call smith.lic
c. Bundle item to logbook
3. smith.lic:
- Buy parts (hilt/haft)
- Find anvil (DRCC.find_anvil)
- Call forge.lic
- Apply enhancements
- Dispose scrap
4. forge.lic:
- Study recipe
- Main work loop:
- Match game output
- Swap tools as needed
- Assemble parts
- Manage fuel/temperature
- Oil and finish
# workorders.lic
items_per_stock = materials_info['stock-volume'] / recipe['volume']
spare_stock = (materials_info['stock-volume'] % recipe['volume']).nonzero?
scrap = spare_stock || (quantity % items_per_stock).nonzero?
# Example: stock-volume = 10, recipe volume = 3
# items_per_stock = 3 (can make 3 items per stock)
# spare_stock = 1 (1 volume leftover per stock)Current State: Inconsistent error handling across scripts.
Issues:
| Location | Problem | Impact |
|---|---|---|
| forge.lic:416 | Generic "ERROR TRYING TO CRAFT" exit | No recovery, unclear cause |
| remedy.lic:394-400 | magic_cleanup() assumes @training_spells exists | Potential nil error |
| sew.lic:334-340 | magic_cleanup() references @settings outside class | Scope issue |
| enchant.lic:240-241 | Imbue backlash sends to safe-room and exits | No retry option |
| workorders.lic:852-854 | 500 iteration loop exits silently | No indication of why |
| carve.lic:135-136 | "You cannot figure out" triggers finish() | May exit prematurely on actual errors |
| carve.lic:160-161 | "ITEM NOT FOUND" exit without cleanup | No stowing, no magic release |
| tinker.lic:239-241 | "ERROR TRYING TO CRAFT" uses DRCI.stow_hands | Different cleanup pattern than other scripts |
| tinker.lic:260-267 | magic_cleanup() prepares then releases symbiosis | Unique pattern, inconsistent with others |
Pattern Problems:
# Common anti-pattern: Exit without cleanup
when 'I could not find what you were referring to'
echo '*** ERROR TRYING TO CRAFT, EXITING ***'
exit # No stowing, no magic release, no state saveRecommendation: Create standardized error handling:
def craft_error(message, cleanup: true, exit_script: true)
DRC.message("CRAFT ERROR: #{message}")
if cleanup
DRCC.stow_crafting_item(DRC.right_hand, @bag, @belt)
DRCC.stow_crafting_item(DRC.left_hand, @bag, @belt)
magic_cleanup
end
exit if exit_script
endCurrent State: Complex material counting with edge cases.
Issues:
| Script | Issue | Code Location |
|---|---|---|
| workorders.lic | Herb counting fails with players named "In*" | :484-488 |
| remedy.lic | Herb stacking logic complex and fragile | :193-227 |
| workorders.lic | count_combine_rem() has 75+ lines | :461-538 |
| forge.lic | Ingot restow flag can miss edge cases | :255-264 |
Herb Counting Bug (workorders.lic:484-488):
# Bug: "tap first flower in my haversack" matches "Inkin"
/You tap (.*) inside your|I could not find|You lightly tap/ =~
DRC.bput("tap #{stack_descriptor} #{herb_for_tapping} in my #{@bag}", ...)
# "You lightly tap Inkin on the shoulder" triggers wrong branchMaterial Volume Edge Cases:
- Bone carving stacks vs stone deeds
- Cloth/yarn combining when partially used
- Own ingot + deed management complexity
Current State: Adjustable tongs have complex state management.
Location: common-crafting.rb:366-425
Issues:
# State tracking via instance variable @tongs_status
# Problem 1: Not persistent across script restarts
# Problem 2: Can get out of sync with actual game state
case DRC.bput("adjust my tongs", ...)
when 'You lock the tongs' # Now shovel
when 'With a yank you fold' # Now tongs
when 'You cannot adjust' # Not adjustable!
when 'You have no idea how' # Not adjustable!Race Condition:
# forge.lic uses @adjustable_tongs at script start
@adjustable_tongs = DRCC.get_adjust_tongs?('reset tongs', @bag, @bag_items, @forging_belt)
# But if script crashes, @tongs_status in DRCC may be wrong on restartTool Swap Inconsistency:
-
forge.lic:swap_tool()- custom implementation with adjustable tongs -
shape.lic:swap_tool()- simpler implementation, no tongs logic -
sew.lic:swap_tool()- checks if already holding tool -
carve.lic- no swap_tool(), uses inline DRCC calls -
tinker.lic:swap_tool()- includes tinkering_tools lookup + adjustable tongs
Current State: Flags cleaned up in before_dying blocks.
Issues:
# forge.lic
before_dying do
Flags.delete('forge-assembly')
Flags.delete('hone-done') # 'hone-done' never created!
Flags.delete('ingot-restow')
endOrphan Flags: Some flags created but never deleted.
Flag Name Collision Risk: Similar names across scripts could interfere.
# Proposed: lib/dragonrealms/commons/common-crafting-errors.rb
module DRCC
class CraftError < StandardError
attr_reader :recoverable, :context
def initialize(message, recoverable: false, context: {})
@recoverable = recoverable
@context = context
super(message)
end
end
def handle_craft_error(error, bag:, belt:)
DRC.message("CRAFT: #{error.message}")
cleanup_hands(bag, belt)
raise unless error.recoverable
end
end# Proposed: Unified tool management
class CraftingToolManager
def initialize(bag:, belt:, tools:)
@bag = bag
@belt = belt
@tools = tools
@current_tool = nil
end
def swap_to(tool_name, consumable: false)
return if @current_tool == tool_name
stow_current
get_tool(tool_name, consumable: consumable)
@current_tool = tool_name
end
def stow_current
return unless @current_tool
DRCC.stow_crafting_item(@current_tool, @bag, @belt)
@current_tool = nil
end
end# Proposed: Material planning before crafting
class MaterialCalculator
def calculate_needs(recipe, quantity, inventory)
{
stock_needed: calculate_stock(recipe, quantity, inventory[:stock]),
parts_needed: calculate_parts(recipe, quantity, inventory[:parts]),
consumables_needed: calculate_consumables(quantity, inventory[:consumables]),
total_cost: calculate_cost(...)
}
end
end# Current: @debug = args.debug || settings.debug_mode
# Proposed: Structured logging
module DRCC
def debug_log(category, message, data = {})
return unless @debug
timestamp = Time.now.strftime('%H:%M:%S')
echo "[#{timestamp}][#{category}] #{message} #{data.inspect}"
end
end| Feature | Benefit | Complexity |
|---|---|---|
| Dry-run mode | Preview materials needed without crafting | Low |
| Progress persistence | Resume after disconnect | Medium |
| Multi-item batching | Craft same item multiple times efficiently | Low |
| Quality tracking | Track item quality for work orders | Medium |
| Skill requirements | Warn if skill too low for recipe | Low |
| Alternative recipes | Fallback if primary unavailable | Medium |
| Consumable forecasting | Warn before running out | Low |
| Discipline | Current Support | Gap |
|---|---|---|
| Weaving | Minimal | No weave.lic |
| Tanning | None | No tan.lic |
| Jewelry Making | None | No jewelry.lic (Outfitting sub-discipline) |
| Artistry | None | No artistry.lic (Outfitting sub-discipline) |
Note: Spinning (spin.lic), Carving (carve.lic), and Tinkering (tinker.lic) are fully implemented.
# Some recipes lack full metadata
- work_order: false # Many recipes excluded from work orders
- material: nil # Missing material type for some
- part: nil # Missing part requirements-
Fix Herb Counting Bug
- Use explicit item matching, not player name collision
- Add guard against "You lightly tap" responses
-
Standardize Error Handling
- Create
CraftErrorexception class - Implement cleanup on all error paths
- Add retry logic for recoverable errors
- Create
-
Fix Orphan Flags
- Audit all
Flags.add()andFlags.delete()calls - Ensure matching pairs
- Audit all
-
Unify Tool Swap Methods
- Create single
swap_tool()in DRCC - Remove duplicate implementations
- Create single
-
Extract Magic Cleanup
- Move to DRCC module
- Fix scope issues in sew.lic
-
Document Data Schemas
- Add YAML schema validation
- Document required vs optional fields
-
Introduce State Machine Pattern
- Formalize craft state transitions
- Enable persistence for resume
-
Create Material Tracker
- Centralized inventory tracking
- Pre-craft validation
-
Add Observability
- Structured logging
- Craft statistics
- Error telemetry
- Dry-Run Mode
- Progress Persistence
- Quality Tracking
- Consumable Forecasting
| File | Type | Primary Function |
|---|---|---|
| base-crafting.yaml | Data | Town infrastructure, stock info |
| base-recipes.yaml | Data | Recipe catalog |
| base-ingredients.yaml | Data | Foraging locations |
| base.yaml | Config | User settings template |
| craft.lic | Training | Skill training automation |
| workorders.lic | Orchestrator | Work order automation |
| smith.lic | Wrapper | Forging with enhancements |
| forge.lic | Engine | Core Forging logic (Blacksmithing/Weaponsmithing/Armorsmithing) |
| sew.lic | Engine | Tailoring (sewing/knitting/leather) |
| shape.lic | Engine | Shaping (lumber, bows) |
| carve.lic | Engine | Carving (stone/bone) |
| tinker.lic | Engine | Tinkering (crossbows, mechanisms) |
| remedy.lic | Engine | Remedies (potions, salves) |
| enchant.lic | Engine | Artificing (magic items) |
| spin.lic | Utility | Fiber spinning (thread/yarn production) |
| alchemy.lic | Utility | Ingredient prep (forage/process herbs) |
| smelt.lic | Utility | Metal combining in crucible |
| common-crafting.rb | Library | Shared functions (DRCC module) |
# Essential crafting settings
crafting_container: backpack
crafting_items_in_container: []
hometown: Crossing
force_crafting_town: null
# Tool belts (by skill)
forging_belt: { name: "forging belt", items: [tongs, hammer] }
outfitting_belt: null
engineering_belt: null
alchemy_belt: null
enchanting_belt: null
# Tools (by skill/discipline)
forging_tools: [tongs, hammer, bellows, shovel]
shaping_tools: [drawknife, shaper, carving knife, rasp, clamps]
carving_tools: [chisel, saw, rifflers, rasp, polish]
tinkering_tools: [drawknife, shaper, carving knife, rasp, clamps, pliers, tinker tools]
outfitting_tools: [scissors, sewing needles, pins, yardstick, slickstone]
alchemy_tools: [mortar, pestle, sieve]
enchanting_tools: [brazier, fount, burin, aug loop, imbue rod]
# Adjustable tools
adjustable_tongs: false
# Work orders (workorders.lic)
workorder_diff: challenging
workorder_min_items: 1
workorder_max_items: 6
craft_max_mindstate: 32
# Materials (workorders.lic)
workorders_materials:
metal_type: bronze
fabric_type: burlap
wood_type: balsa
stone_type:ite
bone_type: animal
knit_type: wool
# Skill training (craft.lic)
craft_max_mindstate: 32
craft_overrides:
Forging: { item: "custom item" }
Outfitting: { type: sew, chapter: 3, item: "item name", volumes: 10 }
Engineering: { chapter: 7, item: "item name" }
yarn_quantity: 100
worn_trashcan: bucket
worn_trashcan_verb: put
# Ingredient preparation (alchemy.lic)
alchemy_herb_quantity: 25
alchemy_herb_storage: haversack
herb_container: mortar
alchemy_prep_quantity: 75
alchemy_forage_type: careful
forage_override_room: null
forage_override_town: null| Sub-discipline | Script | Skill | Logbook | Stock Type |
|---|---|---|---|---|
| Blacksmithing | forge.lic | Forging | forging | ingot |
| Weaponsmithing | forge.lic | Forging | forging | ingot |
| Armorsmithing | forge.lic | Forging | forging | ingot |
| Tailoring (sew) | sew.lic | Outfitting | outfitting | cloth |
| Tailoring (knit) | sew.lic | Outfitting | outfitting | yarn |
| Tailoring (leather) | sew.lic | Outfitting | outfitting | leather |
| Spinning (material) | spin.lic | Outfitting | N/A | fibers |
| Shaping | shape.lic | Engineering | engineering | lumber |
| Carving (stone) | carve.lic | Engineering | engineering | deed/stone |
| Carving (bone) | carve.lic | Engineering | engineering | stack |
| Tinkering (crossbow) | tinker.lic | Engineering | engineering | lumber |
| Tinkering (bolts) | tinker.lic | Engineering | engineering | bolt shafts |
| Tinkering (mechanisms) | tinker.lic | Engineering | N/A | ingot |
| Remedies | remedy.lic | Alchemy | alchemy | herbs |
| Artificing | enchant.lic | Enchanting | enchanting | sigils + items |
| Script | Basic Usage | Common Options |
|---|---|---|
| craft.lic | ;craft <skill> |
forging, outfitting, engineering, alchemy, enchanting |
| workorders.lic | ;workorders <discipline> |
blacksmithing, weaponsmithing, tailoring, shaping, carving, remedies, artificing |
| smith.lic | ;smith <metal> <item> [buy] |
buy = purchase ingot |
| forge.lic | ;forge <chapter> <item> <noun> |
Also: resume, temper, hone, balance, lighten, reinforce |
| sew.lic | ;sew <mode> <chapter> <item> <material> <noun> |
mode: sewing, knitting, leather |
| shape.lic | ;shape <mode> <chapter> <item> <wood> <noun> |
mode: trash, normal; Also: laminate, lighten, cable |
| carve.lic | ;carve <chapter> <item> <material> <type> <noun> |
type: stack, rock, stone, boulder, deed |
| tinker.lic | ;tinker <chapter> <item> <material> <noun> |
Also: mechanisms [count], resume, laminate, lighten, cable |
| remedy.lic | ;remedy <discipline> <chapter> <item> <herb1> <herb2> <catalyst> <container> <noun> |
discipline: remedies |
| enchant.lic | ;enchant <chapter> <item> <noun> |
|
| spin.lic | ;spin <material> [weight] |
material: silk, cotton, linen, wool, burlap; weight: fine, thin, average, thick, heavy, yarn |
| alchemy.lic | ;alchemy <ingredient> [forage] [prepare] [quantity] |
forage = gather, prepare = process |
| smelt.lic | ;smelt <metal> |
Combines ingots in crucible |
Document generated for script contributor reference. Please update as changes are made. Reference: Elanthipedia Crafting