Implements craft command to create items from recipes by consuming ingredients from player inventory. Recipes command lists available recipes or shows details for a specific recipe. Registers commands and loads recipes at server startup.
130 lines
3.9 KiB
Python
130 lines
3.9 KiB
Python
"""Crafting commands."""
|
|
|
|
from collections import Counter
|
|
|
|
from mudlib.commands import CommandDefinition, register
|
|
from mudlib.crafting import recipes
|
|
from mudlib.player import Player
|
|
from mudlib.things import spawn_thing, thing_templates
|
|
|
|
|
|
async def cmd_craft(player: Player, args: str) -> None:
|
|
"""Craft an item from a recipe.
|
|
|
|
Args:
|
|
player: The player crafting
|
|
args: Recipe name
|
|
"""
|
|
if not args:
|
|
await player.send("Usage: craft <recipe>\r\n")
|
|
return
|
|
|
|
recipe_name = args.strip().lower()
|
|
|
|
# Find recipe by name (case-insensitive, prefix match)
|
|
matching_recipes = [
|
|
name for name in recipes if name.lower().startswith(recipe_name)
|
|
]
|
|
|
|
if not matching_recipes:
|
|
await player.send(f"Unknown recipe: {args}\r\n")
|
|
return
|
|
|
|
if len(matching_recipes) > 1:
|
|
await player.send(
|
|
f"Ambiguous recipe name. Matches: {', '.join(matching_recipes)}\r\n"
|
|
)
|
|
return
|
|
|
|
recipe = recipes[matching_recipes[0]]
|
|
|
|
# Count required ingredients
|
|
required = Counter(ingredient.lower() for ingredient in recipe.ingredients)
|
|
|
|
# Count available ingredients in inventory
|
|
inventory = player.contents
|
|
available = Counter(obj.name.lower() for obj in inventory)
|
|
|
|
# Check if player has all ingredients
|
|
missing = []
|
|
for ingredient, count in required.items():
|
|
if available[ingredient] < count:
|
|
needed = count - available[ingredient]
|
|
missing.append(f"{ingredient} (need {needed} more)")
|
|
|
|
if missing:
|
|
await player.send(f"Missing ingredients: {', '.join(missing)}\r\n")
|
|
return
|
|
|
|
# Check if result template exists
|
|
if recipe.result not in thing_templates:
|
|
await player.send(
|
|
f"Error: Recipe result '{recipe.result}' template not found.\r\n"
|
|
)
|
|
return
|
|
|
|
# Consume ingredients
|
|
consumed = Counter()
|
|
for obj in list(inventory):
|
|
obj_name = obj.name.lower()
|
|
if obj_name in required and consumed[obj_name] < required[obj_name]:
|
|
obj.move_to(None) # Remove from world
|
|
consumed[obj_name] += 1
|
|
|
|
# Create result item
|
|
result_template = thing_templates[recipe.result]
|
|
spawn_thing(result_template, player)
|
|
|
|
await player.send(f"You craft a {result_template.name}.\r\n")
|
|
|
|
|
|
async def cmd_recipes(player: Player, args: str) -> None:
|
|
"""List available recipes.
|
|
|
|
Args:
|
|
player: The player viewing recipes
|
|
args: Optional recipe name for details
|
|
"""
|
|
if not recipes:
|
|
await player.send("No recipes available.\r\n")
|
|
return
|
|
|
|
if args:
|
|
# Show details for specific recipe
|
|
recipe_name = args.strip().lower()
|
|
matching = [name for name in recipes if name.lower().startswith(recipe_name)]
|
|
|
|
if not matching:
|
|
await player.send(f"Unknown recipe: {args}\r\n")
|
|
return
|
|
|
|
if len(matching) > 1:
|
|
await player.send(
|
|
f"Ambiguous recipe name. Matches: {', '.join(matching)}\r\n"
|
|
)
|
|
return
|
|
|
|
recipe = recipes[matching[0]]
|
|
ingredient_counts = Counter(recipe.ingredients)
|
|
ingredient_list = ", ".join(
|
|
f"{count}x {name}" if count > 1 else name
|
|
for name, count in sorted(ingredient_counts.items())
|
|
)
|
|
|
|
await player.send(
|
|
f"Recipe: {recipe.name}\r\n"
|
|
f"{recipe.description}\r\n"
|
|
f"Ingredients: {ingredient_list}\r\n"
|
|
f"Result: {recipe.result}\r\n"
|
|
)
|
|
else:
|
|
# List all recipes
|
|
await player.send("Available recipes:\r\n")
|
|
for name, recipe in sorted(recipes.items()):
|
|
await player.send(f" {name}: {recipe.description}\r\n")
|
|
|
|
|
|
register(CommandDefinition("craft", cmd_craft, help="Craft items from recipes."))
|
|
register(
|
|
CommandDefinition("recipes", cmd_recipes, help="List available crafting recipes.")
|
|
)
|