- Adaptive welcome message based on progression (50/200/500 boxes thresholds) - Raw inventory display before InventoryPanel unlock (plain text list) - Terminal.Gui as default mode (--classic flag for old sequential renderer) - Early-game box counter before StatsPanel unlock - Encouraging messages in empty ResourcePanel and locked panel placeholders - Wider inventory name column (28 chars), cookie detail/consume, box teaser - Richer StatsPanel with items discovered and play time
135 lines
6.5 KiB
Markdown
135 lines
6.5 KiB
Markdown
# 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](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 # Default: Terminal.Gui panel layout
|
|
dotnet run --project src/OpenTheBox -- --classic # Classic Spectre.Console sequential mode
|
|
dotnet run --project src/OpenTheBox -- --snapshot 5 # Load snapshot save #5
|
|
```
|
|
|
|
## 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.
|
|
|
|
## Save Snapshots (Visual Testing)
|
|
Generate save files at 9 progression stages for quick visual testing:
|
|
```
|
|
dotnet test --filter "GenerateSaveSnapshots" --logger "console;verbosity=detailed"
|
|
```
|
|
This creates `saves/snapshot_1.otb` through `saves/snapshot_9.otb` (from 10 boxes to 2000 boxes opened).
|
|
|
|
Load a snapshot directly:
|
|
```
|
|
dotnet run --project src/OpenTheBox -- --snapshot 3
|
|
```
|
|
Where 1-9 corresponds to: (1) very early, (2) first unlocks, (3) several panels, (4) adventures, (5) crafting, (6) most features, (7) near completion, (8) endgame, (9) post-endgame.
|
|
|
|
## Render Capture Tests (Automated Visual Testing)
|
|
|
|
These tests simulate gameplay and capture rendered panel output as plain text for review in the test console. They are the primary way to verify visual rendering without launching the game interactively.
|
|
|
|
### PlaythroughCapture
|
|
Simulates N box openings and renders the full game state panels at each step:
|
|
```
|
|
dotnet test --filter "PlaythroughCapture" --logger "console;verbosity=detailed"
|
|
```
|
|
- Runs 2 seeds (42 and 777), 15 steps each
|
|
- Shows: portrait, stats, resources, inventory, crafting panels at each step
|
|
- Logs all events (items received, UI unlocks, crafting, adventures)
|
|
- Useful for checking progressive UI unlock rendering and panel composition
|
|
|
|
### InventoryRenderCapture
|
|
Captures the interactive inventory panel rendering at 5 progression stages (box #20, 50, 100, 200, 500):
|
|
```
|
|
dotnet test --filter "InventoryRenderCapture" --logger "console;verbosity=detailed"
|
|
```
|
|
- Shows the inventory table with selection highlight (► indicator on first item)
|
|
- Renders the detail panel for each item type present at that stage:
|
|
- **Consumable**: effect (+N resource) and "Press Enter to use" prompt
|
|
- **LoreFragment**: full lore text displayed in italic
|
|
- **Cosmetic**: slot information (Hair, Eyes, Body, etc.)
|
|
- **Material**: material type and form (Raw, Ingot, Sheet, etc.)
|
|
- Useful after modifying `InventoryPanel.cs`, `RenderDetailPanel()`, or loot tables
|
|
|
|
### Adding New Render Capture Tests
|
|
|
|
To capture rendering for a new panel or UI element:
|
|
1. Use the helper pattern from `RenderGameStatePanels()` or `InventoryRenderCapture`:
|
|
```csharp
|
|
var writer = new StringWriter();
|
|
var console = AnsiConsole.Create(new AnsiConsoleSettings {
|
|
Out = new AnsiConsoleOutput(writer),
|
|
Ansi = AnsiSupport.No,
|
|
ColorSystem = ColorSystemSupport.NoColors
|
|
});
|
|
console.Profile.Width = SpectreRenderer.RefWidth;
|
|
console.Write(MyPanel.Render(...));
|
|
// writer.ToString() contains the plain-text rendering
|
|
```
|
|
2. Use `--logger "console;verbosity=detailed"` to see `Console.WriteLine()` output in the test runner
|
|
3. Tests should `Assert.True(true)` or use lightweight assertions — the goal is visual inspection of output
|
|
|
|
## Classic Mode
|
|
Terminal.Gui panel layout is the default. Use `--classic` for the old sequential Spectre.Console renderer:
|
|
```
|
|
dotnet run --project src/OpenTheBox -- --classic
|
|
```
|
|
|
|
## 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)
|