Browse/Crafting & Building/Freeform Crafting
Crafting & Building

Freeform Crafting

Implementation of freeform crafting that defines how players interact with this aspect of the game, including feedback and progression.

Low complexity
3 examples
3 patterns

Overview

Freeform Crafting is a fundamental game mechanic that creates a structured experience around this game element. The mechanic interacts with multiple other game systems, creating emergent gameplay that extends beyond its individual components. Understanding the design principles behind this mechanic helps developers create more engaging and balanced game experiences.

Game Examples

Roguelites

Roguelites use this mechanic where players experiment with combinations to support their team effectively. Visual and audio feedback make the interaction satisfying, resulting in social interaction.

Auto-Battlers

Auto-Battlers use this mechanic where players solve environmental puzzles to establish dominance in PvP. The difficulty scales with player performance, resulting in build diversity.

Bullet Hell Games

Bullet Hell Games use this mechanic where players interact with NPCs to achieve mastery over the system. Failure states are informative rather than punishing, resulting in cooperative synergy.

Pros & Cons

Advantages

  • Creates satisfying contextual loops
  • Rewards both team coordination and mechanical skill
  • Provides clear cumulative feedback on player actions
  • Easy to understand but difficult to master

Disadvantages

  • Can become obsolete in the late game
  • Increases storage requirements significantly
  • Can create tedium if not carefully balanced
  • May create an entry barrier for new players

Implementation Patterns

Quality Calculator

Event-driven pattern that reacts to freeform crafting changes and updates dependent systems.

class FreeformCraftingController {
  recipes: Recipe[] = [];

  craft(recipeId: string, inventory: Inventory) {
    const recipe = this.recipes.find(r => r.id === recipeId);
    if (!recipe) return null;

    for (const ingredient of recipe.ingredients) {
      if (!inventory.has(ingredient.id, ingredient.amount)) {
        return null; // Missing materials
      }
    }

    for (const ingredient of recipe.ingredients) {
      inventory.remove(ingredient.id, ingredient.amount);
    }

    const quality = this.rollQuality(0.15);
    return { ...recipe.output, quality };
  }

  rollQuality(baseChance: number) {
    const roll = Math.random();
    if (roll < baseChance * 0.05) return "legendary";
    if (roll < baseChance * 0.2) return "rare";
    if (roll < baseChance) return "uncommon";
    return "common";
  }
}

Research Tree

Optimized pattern for freeform crafting that minimizes per-frame computation cost.

class FreeformCraftingProcessor {
  recipes: Recipe[] = [];

  craft(recipeId: string, inventory: Inventory) {
    const recipe = this.recipes.find(r => r.id === recipeId);
    if (!recipe) return null;

    for (const ingredient of recipe.ingredients) {
      if (!inventory.has(ingredient.id, ingredient.amount)) {
        return null; // Missing materials
      }
    }

    for (const ingredient of recipe.ingredients) {
      inventory.remove(ingredient.id, ingredient.amount);
    }

    const quality = this.rollQuality(0.15);
    return { ...recipe.output, quality };
  }

  rollQuality(baseChance: number) {
    const roll = Math.random();
    if (roll < baseChance * 0.02) return "legendary";
    if (roll < baseChance * 0.2) return "rare";
    if (roll < baseChance) return "uncommon";
    return "common";
  }
}

Upgrade Handler

Optimized pattern for freeform crafting that minimizes per-frame computation cost.

class FreeformCraftingEngine {
  recipes: Recipe[] = [];

  craft(recipeId: string, inventory: Inventory) {
    const recipe = this.recipes.find(r => r.id === recipeId);
    if (!recipe) return null;

    for (const ingredient of recipe.ingredients) {
      if (!inventory.has(ingredient.id, ingredient.amount)) {
        return null; // Missing materials
      }
    }

    for (const ingredient of recipe.ingredients) {
      inventory.remove(ingredient.id, ingredient.amount);
    }

    const quality = this.rollQuality(0.05);
    return { ...recipe.output, quality };
  }

  rollQuality(baseChance: number) {
    const roll = Math.random();
    if (roll < baseChance * 0.01) return "legendary";
    if (roll < baseChance * 0.15) return "rare";
    if (roll < baseChance) return "uncommon";
    return "common";
  }
}