Browse/Crafting & Building/Warehouse / Depot
Crafting & Building

Warehouse / Depot

Framework for implementing warehouse / depot in games, covering the core loop, edge cases, and integration points.

Medium complexity
2 examples
3 patterns

Overview

This mechanic, commonly known as warehouse / depot, provides meaningful choices and consequences for player actions. Designers must carefully balance the system's depth against its learning curve, ensuring that new players can engage while experienced players find room for mastery. The key to successful implementation lies in clear communication of rules, fair outcomes, and satisfying feedback for player actions.

Game Examples

Point-and-Click Adventures

Point-and-Click Adventures use this mechanic where players balance risk and reward to reach the highest tier. The system encourages experimentation, resulting in creative expression.

Asymmetric Games

Asymmetric Games use this mechanic where players experiment with combinations to maximize their effectiveness. Accessibility options allow different skill levels to participate, resulting in satisfying progression.

Pros & Cons

Advantages

  • Encourages exploratory playstyles and experimentation
  • Provides clear visual feedback on player actions
  • Creates satisfying audio loops
  • Reduces frustration while maintaining challenge
  • Supports several viable strategies and approaches

Disadvantages

  • Risk of tedium in competitive environments
  • Can create punishing when RNG is unfavorable
  • May conflict with meta systems in the game
  • Difficult to balance across a wide range of skill levels

Implementation Patterns

Research Tree

A modular approach to warehouse / depot that separates concerns and enables easy testing.

class WarehouseDepotHandler {
  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.05) return "legendary";
    if (roll < baseChance * 0.15) return "rare";
    if (roll < baseChance) return "uncommon";
    return "common";
  }
}

Crafting Queue

Data-driven implementation that loads warehouse / depot configuration from external definitions.

class WarehouseDepotController {
  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.2);
    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";
  }
}

Crafting Queue

Event-driven pattern that reacts to warehouse / depot changes and updates dependent systems.

class WarehouseDepotController {
  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.2);
    return { ...recipe.output, quality };
  }

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