From 2f2ea060b19fb408f5e637355298ecdca461d4ed Mon Sep 17 00:00:00 2001 From: Dominic Masters Date: Mon, 8 Jun 2026 22:50:09 -0500 Subject: [PATCH] Add CLAUDE --- CLAUDE.md | 129 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..707f8d0 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,129 @@ +# Dawn Godot — Claude Code Guide + +## Project + +Godot 4.4 RPG prototype, GDScript only. Viewport 480×270 (scaled 3× to 1440×810), GL Compatibility renderer. + +--- + +## Code Style + +### Naming +- **camelCase** for variables and functions: `fighterMap`, `startBattle()`, `getFullParty()` +- **PascalCase** for class names, enums, and enum values: `BattleFighter`, `FighterTeam`, `ALLY` +- **SCREAMING_SNAKE_CASE** for constants and static data: `CUTSCENE_CONTINUE`, `ITEM_DATA`, `PARTY_JOHN` +- **No** trailing underscores on private methods — use a leading underscore only for internal helpers that shouldn't be called externally: `_onConversationInteract()`, `_applyGravity()` + +### Formatting +- 2-space indent (not 4, not tabs) +- Type annotations on all variable declarations and function signatures: `var health:int`, `func damage(amount:int, crit:bool) -> void:` +- No space between variable name and type: `var foo:int` not `var foo : int` +- Blank lines between logical sections within a file; comment headers (`# Health`, `# Signals`) to label groups + +### General Rules +- `assert()` for invariants and preconditions — prefer it over silent failures +- `params:Dictionary` pattern for multi-argument constructors/callables; access with `.get('key', default)` or `.has('key')` guards +- `match` for enum dispatch; `if/elif` chains for non-exhaustive checks +- `continue` / early `return` to reduce nesting rather than deep else-branches +- Avoid long inline lambdas; extract named static functions where logic is non-trivial + +--- + +## Architecture + +### Singletons (Autoloads) +All globally accessible by their handle. Never instantiate these — access via the global name. + +| Handle | Role | +|---|---| +| `SCENE` | Current scene state; call `SCENE.setScene(SceneSingleton.SceneType.X)` to switch | +| `TRANSITION` | Fade in/out; `TRANSITION.fade(FadeType, duration, color)` | +| `BATTLE` | Battle state and fighter map | +| `PARTY` | Party members (`PARTY.getFullParty()`) and `PARTY.BACKPACK` inventory | +| `OVERWORLD` | Map switching with threaded loading | +| `COOKING` | Cooking mini-game lifecycle | +| `SAVE` | Persistence (stub) | +| `QUEST` | Quest management (stub) | +| `CUTSCENE` | Cutscene global (stub) | +| `UI` | Root UI accessor — `UI.TEXTBOX`, `UI.DEBUG_MENU` | + +### Scene Graph +``` +RootScene (Node3D) + └─ overworld / battle / cooking / initial ← one shown at a time +RootUI (Control, always visible) + └─ VNTextbox, DebugMenu +``` + +`RootScene` listens to `SCENE.sceneChanged` and shows/hides the appropriate sub-tree. + +### Cutscene / Event Queue +`Cutscene` is the universal sequencing engine. It holds an `Array[Dictionary]` queue; each entry has a `"function": Callable` plus arbitrary data keys. + +**Return codes from a callable:** +- `Cutscene.CUTSCENE_CONTINUE` — advance to next item +- `Cutscene.CUTSCENE_END` — stop the cutscene +- An integer index — jump to that position + +**Position constants when adding:** +- `Cutscene.CUTSCENE_ADD_END` — append (default) +- `Cutscene.CUTSCENE_ADD_NEXT` — insert immediately after current + +**Callable pattern** — every action class exposes a pair: +```gdscript +# The actual callable (static, takes params:Dictionary, returns int) +static func myCallable(params:Dictionary) -> int: + ... + return Cutscene.CUTSCENE_CONTINUE + +# Factory that builds the dictionary for addCallable() +static func getMyCallable(arg) -> Dictionary: + return { "function": myCallable, "myArg": arg } +``` + +### Data Registry Pattern +Static registries (Item, Recipe) follow this pattern: +1. `enum Id { NULL, ... }` — typed identifier +2. `static var DATA:Array = []` — indexed by Id value +3. `static func define(params) -> Dictionary` — called at class load to populate `DATA` +4. `static var FOO = define({...})` — registers the entry as a static var +5. `static func get*(id) -> *` — typed accessors + +### `_init(params:Dictionary)` Pattern +Non-Node data classes (`BattleFighter`, `BattleDecision`, `ItemStack`, etc.) use a single `params` dictionary constructor with `.get('key', default)` for optional fields. + +--- + +## System Conventions + +### Battle +- Fighters live in `BATTLE.fighterMap: Dictionary[BattlePosition, BattleFighter]` +- `BattleFighter` is pure data (no Node); `BattleFighterScene` is the 3D visual +- Battle flow is driven by `BattleCutsceneAction.playerDecisionCallable` which loops via `CUTSCENE_ADD_END` +- New moves go in [battle/fighter/BattleMove.gd](battle/fighter/BattleMove.gd) as static presets; add corresponding `perform()` logic in [battle/action/BattleMove.gd](battle/action/BattleMove.gd) + +### Entities (Overworld) +- All interactable world objects extend `Entity` (CharacterBody3D) +- Interaction type is set via `@export var interactType:InteractType` +- Interaction routing lives in `EntityInteractableArea.onInteract()` — add new `InteractType` values there + +### Items +- Register new items in [item/Item.gd](item/Item.gd) — add to `Id` enum and call `itemDefine()` +- Item ID order in the enum must match insertion order into `ITEM_DATA` + +### UI +- `UI.TEXTBOX.setTextAndWait(text)` — show dialogue and await player dismiss (use `await`) +- Movement is blocked automatically when `UI.TEXTBOX` is visible (`EntityMovement._canMove()` checks this) +- Menus extend `ClosableMenu` for open/close/toggle + `closed`/`opened` signals + +--- + +## What's Incomplete / Stub +- `Save.gd` — no actual serialization yet +- `Quest.gd` — empty singleton +- `CutsceneSingleton.gd` — minimal stub +- `BattleFighter.getAIDecision()` — always returns `null` +- `BattleDecision.execute()` — stub +- `BattleItem.perform()` — stub +- `CookingScene.tscn` — placeholder UI only +- `Pause.gd` — commented-out logic