diff --git a/CLAUDE.md b/CLAUDE.md index c894cf8..a147cc1 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,239 +1,62 @@ -# CLAUDE.md +# EfD Trading Card Mod -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. +Unity mod for Escape from Duckov adding a customizable trading card system with storage. -## Project Overview - -A Unity mod for Escape from Duckov that adds a customizable trading card system with storage solutions. The framework supports user-generated content through simple pipe-separated text files, allowing non-programmers to add their own card sets. - -## Build Commands +## Commands ```bash -# Build the mod -dotnet build TradingCardMod.csproj - -# Build release version -dotnet build TradingCardMod.csproj -c Release - -# Output location: bin/Debug/netstandard2.1/TradingCardMod.dll +dotnet build TradingCardMod.csproj # Build (debug) +dotnet build TradingCardMod.csproj -c Release # Build (release) +dotnet test TradingCardMod.Tests.csproj # Run tests +./deploy.sh # Build + deploy to game +./deploy.sh --release # Deploy release build +./remove.sh # Remove mod from game ``` -**Important**: Before building, update `DuckovPath` in `TradingCardMod.csproj` (line 10) to your actual game installation path. - -## Deployment - -```bash -# Deploy to game (builds and copies all files) -./deploy.sh - -# Deploy release build -./deploy.sh --release - -# Remove mod from game -./remove.sh - -# Remove with backup -./remove.sh --backup -``` +**Before building**: Update `DuckovPath` in `TradingCardMod.csproj` (line 10) to your game path. ## Project Skills -Two workflow skills are available in this project: - -- `/build` - Build and deploy locally for testing (runs `dotnet build` + `./deploy.sh`) -- `/deploy` - Build Release and stage for Steam Workshop upload (uses `workshop-upload.sh`) - -Skills are defined in `.claude/skills/`. - -## Testing - -```bash -# Run all unit tests -dotnet test TradingCardMod.Tests.csproj - -# Run tests with verbose output -dotnet test TradingCardMod.Tests.csproj --verbosity normal - -# Run specific test class -dotnet test TradingCardMod.Tests.csproj --filter "FullyQualifiedName~CardParserTests" - -# Run single test -dotnet test TradingCardMod.Tests.csproj --filter "ParseLine_ValidLineWithoutDescription_ReturnsCard" -``` - -**Test Coverage:** Parsing logic, validation, rarity mapping, TypeID generation, and description auto-generation are all tested. Unity-dependent code (item creation, tags) cannot be unit tested. +- `/build` — Build and deploy locally for testing +- `/deploy` — Build Release and stage for Steam Workshop upload ## Architecture -### Mod Loading System - -The game loads mods from `Duckov_Data/Mods/`. Each mod requires: -- A DLL with namespace matching `info.ini`'s `name` field -- A `ModBehaviour` class inheriting from `Duckov.Modding.ModBehaviour` -- The `info.ini` and `preview.png` files +- **Clone + Reflection**: Clone game items as templates (base ID 135), set private fields via reflection +- **Custom tags**: Clone existing ScriptableObject tags. Parent tag pattern for slot filtering (Unity requireTags uses AND logic) +- **TypeIDs**: 100000+ range to avoid game/mod conflicts +- **Cost struct reflection**: Cost is a value type — must box before reflection, then unbox (see `DisassemblyHelper.cs`) ### Key Classes - -- **`ModBehaviour`** (`src/ModBehaviour.cs`): Main entry point. Inherits from `Duckov.Modding.ModBehaviour` (which extends `MonoBehaviour`). Handles card set loading in `Start()` and cleanup in `OnDestroy()`. - -- **`Patches`** (`src/Patches.cs`): Harmony patch definitions. Uses `HarmonyLib` for runtime method patching. Patches are applied in `Start()` and removed in `OnDestroy()`. - -- **`TradingCard`** (`src/TradingCard.cs`): Data class representing card properties. Contains `GenerateTypeID()` for creating unique item IDs (100000+ range to avoid game conflicts). - -- **`CardParser`** (`src/CardParser.cs`): Parses card definition files. Pure C# with no Unity dependencies, fully unit tested. - -- **`ItemExtensions`** (`src/ItemExtensions.cs`): Reflection helpers for setting private fields on game objects. - -- **`TagHelper`** (`src/TagHelper.cs`): Utilities for working with game tags, including creating custom tags. - -- **`PackUsageBehavior`** (`src/PackUsageBehavior.cs`): Handles card pack opening mechanics. Implements gacha-style random card distribution based on rarity weights. - -- **`ModConfigApi`** (`src/ModConfigApi.cs`): Optional integration with ModConfig mod. Adds card set information (set name, card number, rarity) to item descriptions in inventory. Also displays mod statistics including disabled card sets count. - -- **`StorageHelper`** (`src/StorageHelper.cs`): Helper class for creating storage items with multi-tag slot filtering, enabling hierarchical storage systems. - -- **`DisassemblyHelper`** (`src/DisassemblyHelper.cs`): Helper class for adding disassembly (deconstruction) formulas to custom items at runtime. - -### Disassembly System Pattern - -The game's disassembly system uses `DecomposeDatabase` (singleton in `Duckov.Economy` namespace) to store formulas. Key implementation details: - -**Types:** -- `DecomposeFormula`: Contains `item` (TypeID), `valid` (bool), and `result` (Cost) -- `Cost`: A **struct** with `money` (long) and `items` (ItemEntry[]) -- `Cost+ItemEntry`: **Nested struct** inside Cost with `id` (int) and `amount` (long) - -**Critical Reflection Pattern (Cost is a struct):** -```csharp -// Cost is a VALUE TYPE - must box before reflection, then unbox -Cost result = new Cost { money = 0L }; -object boxedResult = result; // Box the struct -itemsField.SetValue(boxedResult, itemEntries); // Modify boxed copy -result = (Cost)boxedResult; // Unbox back -formula.result = result; -``` - -**Adding a Formula:** -1. Get `DecomposeDatabase.Instance` -2. Access private `entries` field via reflection -3. Create `DecomposeFormula` with item TypeID and `valid = true` -4. Create `Cost` with money and/or items array -5. For items: Create `Cost+ItemEntry[]` via reflection (type found from `Cost.items` field type) -6. Add formula to list, write back via reflection -7. Call private `RebuildDictionary()` method - -**Known Material TypeIDs:** -- Polyethylene Sheet: `764` - -**Cleanup:** Track added item IDs and remove formulas in `OnDestroy()`. - -See `DisassemblyHelper.cs` for complete implementation. +| Class | Role | +|-------|------| +| `ModBehaviour` | Entry point, card set loading/cleanup | +| `Patches` | Harmony runtime method patches | +| `CardParser` | Pipe-separated card file parser (pure C#, fully tested) | +| `PackUsageBehavior` | Card pack opening with gacha-style rarity weights | +| `StorageHelper` | Multi-tag slot filtering for hierarchical storage | +| `DisassemblyHelper` | Runtime disassembly formulas via reflection | ### Dependencies +- **HarmonyLoadMod** (Workshop 3589088839): Required — provides Harmony 2.4.1 +- **ModConfig** (Workshop 3592433938): Optional — enhances card descriptions in inventory -- **HarmonyLoadMod** (Workshop ID: 3589088839): Required mod dependency providing Harmony 2.4.1. Referenced at build time but not bundled to avoid version conflicts. +## Card Sets -- **ModConfig** (Workshop ID: 3592433938): Optional mod dependency. When installed, enhances card descriptions with set information in the inventory UI. - -### Card Definition Format - -Cards are defined in `CardSets/{SetName}/cards.txt` using pipe-separated values: +Cards defined in `CardSets/{SetName}/cards.txt`: ``` CardName | SetName | SetNumber | ImageFile | Rarity | Weight | Value | Description (optional) ``` +Images in `CardSets/{SetName}/images/`. Prefix folder with `_` to disable loading. -The Description field is optional. If provided, it will be displayed in the item's in-game description/tooltip. +## Storage System -Images go in `CardSets/{SetName}/images/`. +- **Binder Sheet** (9 slots): Holds trading cards only +- **Card Binder** (12 slots): Holds cards OR binder sheets (nested storage) -**Disabling Card Sets:** Prefix a card set folder name with `_` (underscore) to exclude it from loading. For example, `_TestSet/` will be skipped. This is useful for work-in-progress sets or seasonal content. The count of disabled sets is displayed in ModConfig. +## Dev Notes -## Game API Reference - -Key namespaces and APIs from the game: -- `ItemStatsSystem.ItemAssetsCollection.AddDynamicEntry(Item prefab)` - Register custom items -- `ItemStatsSystem.ItemAssetsCollection.RemoveDynamicEntry(Item prefab)` - Remove on unload -- `SodaCraft.Localizations.LocalizationManager.SetOverrideText(string key, string value)` - Localization -- `ItemStatsSystem.ItemUtilities.SendToPlayer(Item item)` - Give items to player - -## Development Notes - -- Target framework: .NET Standard 2.1 -- C# language version: 8.0 -- All logging uses `Debug.Log()` with `[TradingCardMod]` prefix -- Custom items need unique TypeIDs to avoid conflicts with base game and other mods - -## Testing - -1. Copy build output to `{GamePath}/Duckov_Data/Mods/TradingCardMod/` -2. Include: `TradingCardMod.dll`, `info.ini`, `preview.png`, `CardSets/` folder -3. Launch game and enable mod in Mods menu -4. Check game logs for `[TradingCardMod]` messages - -## Current Project Status - -**Phase:** 3 - Storage System ✅ -**Status:** Ready for first release -**Project Plan:** `.claude/scratchpad/PROJECT_PLAN.md` -**Technical Analysis:** `.claude/scratchpad/item-system-analysis.md` - -### Completed Features - -- Cards load from `CardSets/*/cards.txt` files with optional descriptions -- Custom PNG images display as item icons -- Cards register as game items with proper TypeIDs -- Custom tags for slot filtering: - - "TradingCard": Identifies trading cards - - "BinderSheet": Identifies binder sheet items - - "CardBinderContent": Parent tag for both cards and binder sheets (enables hierarchical storage) -- Hierarchical storage system: - - **Binder Sheet** (9 slots, weight 0.1): Holds trading cards only - - **Card Binder** (12 slots, weight 1.5): Holds trading cards OR binder sheets - - Nested storage: Cards → Binder Sheets → Card Binders -- Card set exclusion: Prefix folders with `_` to disable loading -- ModConfig integration: - - Enhanced card info display (set name, number, rarity) - - Mod statistics display (total cards, disabled sets) -- Debug spawn window with F10 key (for testing): - - Spawn storage items (Card Binder, Binder Sheet) - - Spawn random cards by rarity - - Draggable OnGUI window interface -- Disassembly support for storage items: - - Binder Sheet → 2x Polyethylene Sheet - - Card Binder → 4x Polyethylene Sheet - - Cards intentionally have no disassembly (collectibles) -- Deploy/remove scripts for quick iteration -- Unit tests for parsing logic - -### Implementation Approach: Clone + Reflection - -Based on analysis of the AdditionalCollectibles mod: - -1. **Clone existing game items** as templates (base item ID 135) -2. **Use reflection** to set private fields (typeID, weight, value, etc.) -3. **Create custom tags** by cloning existing ScriptableObject tags -4. **Parent tag pattern** for slot filtering (Unity's requireTags uses AND logic, so parent tags enable OR-like behavior) -5. **Load sprites** from user files in `CardSets/*/images/` -6. **Attach custom behaviors** for pack opening mechanics - -### Upcoming Features - -- **Card packs** with gacha-style mechanics (weighted random distribution) - disabled pending fix -- Additional storage variants or customization options (e.g., larger binders, themed storage boxes) - -### Future Considerations - -- Investigate new ItemBuilder API (added in recent game update) as potential replacement for reflection-based approach - -### Log File Location - -Unity logs (for debugging): -``` -/mnt/NV2/SteamLibrary/steamapps/compatdata/3167020/pfx/drive_c/users/steamuser/AppData/LocalLow/TeamSoda/Duckov/Player.log -``` - -## Research References - -- **Decompiled game code:** `.claude/scratchpad/decompiled/` -- **Item system analysis:** `.claude/scratchpad/item-system-analysis.md` -- **AdditionalCollectibles mod:** Workshop ID 3591453758 (reference implementation) +- Target: .NET Standard 2.1, C# 8.0 +- Logging: `Debug.Log()` with `[TradingCardMod]` prefix +- Log file: `/mnt/NV2/SteamLibrary/steamapps/compatdata/3167020/pfx/drive_c/users/steamuser/AppData/LocalLow/TeamSoda/Duckov/Player.log` +- Research: `.claude/scratchpad/decompiled/`, `.claude/scratchpad/item-system-analysis.md`