openthebox/CLAUDE.md
Samuel Bouchet 4703ff1e05 Document pacing test workflow in CLAUDE.md
Add instructions for running the pacing report test and interpreting
results after loot table changes. Also fix stale [if] syntax note.
2026-03-11 20:57:30 +01:00

3.4 KiB

Open The Box

Project Overview

A console-based incremental/idle game built in C# (.NET 9) where players open boxes to discover items, unlock features, and progress through themed adventures. Uses Spectre.Console for rich terminal rendering and Loreline for interactive narrative scripting.

Architecture

See specifications.md for detailed content organization.

Key Directories

  • src/OpenTheBox/ — Main game source code
  • content/data/ — JSON data files (items, boxes, crafting recipes)
  • content/adventures/ — Loreline .lor adventure scripts (one folder per theme)
  • content/localization/ — Translation files

Source Code Structure

  • Core/ — Game state, enums, item/character models
  • Core/Enums/ — All enum types (StatType, ResourceType, AdventureTheme, CosmeticSlot, etc.)
  • Adventures/ — AdventureEngine (Loreline bridge + custom functions)
  • Simulation/ — Game engines (BoxEngine, MetaEngine, ResourceEngine, CraftingEngine, GameSimulation)
  • Rendering/ — IRenderer interface, SpectreRenderer, RenderContext, panel components
  • Rendering/Panels/ — Individual UI panels (PortraitPanel, ResourcePanel, StatsPanel, etc.)
  • Data/ — ContentRegistry, ItemDefinition, BoxDefinition data loading
  • Persistence/ — SaveManager, SaveData (JSON serialization)
  • Localization/ — LocalizationManager, Locale enum

Build & Run

dotnet build
dotnet run --project src/OpenTheBox

Test

dotnet test

Adventure System

Adventures use Loreline .lor script format. Custom functions available in scripts:

  • Inventory: grantItem(id, qty), hasItem(id), removeItem(id)
  • Resources: hasResource(name, min), getResourceValue(name), addResource(name, amount)
  • Stats: hasStat(name, min), getStatValue(name)
  • Cosmetics: hasCosmetic(id), hasEquipped(slot, style)
  • Progression: hasCompletedAdventure(theme), markSecretBranch(id), hasSecretBranch(id), countSecretBranches(), allSecretBranches()

Hints for disabled choices use ||| separator: Option text|||Hint text #label if condition

full documentation: https://loreline.app/fr/docs/

Pacing Test

To check game progression balance after modifying loot tables (content/data/boxes.json):

dotnet test --filter "FullRun_PacingReport" --logger "console;verbosity=detailed"

This runs a full simulation (3 seeds: 42, 123, 777) and prints a report showing when each milestone is first reached (UI features, adventures, cosmetics, resources, crafting, lore). Use this to verify that rebalancing changes produce the desired early-game pacing.

Key things to look for:

  • 1st Meta UI unlock should happen before box ~50 for a good early experience
  • 1st Adventure should appear before box ~120
  • All content reachable within ~1000 boxes (game completion)
  • No long gaps between milestones (>100 boxes without progress feels stale)

Weights are in content/data/boxes.json. The main generator is box_of_boxes (auto-opens, produces the next box). Adjust weights there and in tier boxes (box_not_great, box_ok_tier, etc.) to tune pacing.

Conventions

  • C# 12 with file-scoped namespaces, primary constructors where appropriate
  • Immutable records for value types, sealed classes for services
  • GameState is mutable, passed by reference to engines
  • All user-facing strings go through LocalizationManager
  • Enum names match JSON string values (PascalCase)