diff --git a/.github/instructions/godot.instructions.md b/.github/instructions/godot.instructions.md new file mode 100644 index 00000000..ae6f5a27 --- /dev/null +++ b/.github/instructions/godot.instructions.md @@ -0,0 +1,395 @@ +# CLAUDE.md - AI Assistant Guidelines for Godot 4 C# Development + +## File Creation Guidelines + +### Files AI CAN Create/Edit +- **`.cs` files** - All C# script files (preferred for game logic) +- **`.tscn` files** - Scene files with basic/logical structure +- **`.cfg` files** - Configuration files (like .ini format) + +### Files AI Should Create WITH CAUTION +- **`.gd` files** - Only for EditorScript tools (preferred over C# for editor extensions) +- **`.tres` files** - Simple resources CAN be created as text: + ``` + [gd_resource type="Resource" script_class="Recipe" path="res://scripts/Recipe.cs"] + + id = "fire_water_steam" + ingredients = ["fire", "water"] + instability_cost = 20.0 + ``` + BUT: Complex resources with node references or nested resources are better created in editor + +### Files AI Should RARELY Create +- **`.json` files** - Only for external data exchange or modding support + - Use `.tres` for all game data instead (type-safe, Inspector-editable) +- **`.md` files** - Documentation files, only when specifically requested + +### Files AI Should NOT Create +- **`.import` files** - Godot manages these automatically +- **`project.godot`** - Only modify through Project Settings UI +- **`.gdshader` files** - Visual shader editing is more efficient +- **Binary files** - Images, sounds, models, etc. + + +## Scene File (.tscn) Guidelines + +### AI CAN Generate in .tscn +```tscn +# Basic node structure +[node name="Player" type="CharacterBody2D"] +unique_name_in_owner = true +script = ExtResource("1") + +# Node hierarchy +[node name="Sprite2D" type="Sprite2D" parent="."] + +# Signal connections +[connection signal="pressed" from="Button" to="." method="_on_button_pressed"] + +# Simple collision shapes +[sub_resource type="RectangleShape2D" id="1"] +size = Vector2(32, 32) +``` + +### Human Handles in Editor +- Sprite textures and animations +- Precise collision shape adjustments +- Animation tracks in AnimationPlayer +- Particle system parameters +- Complex UI layouts with exact positioning +- Tilemap painting +- Navigation mesh baking +- Audio bus assignments + +## Division of Labor + +### AI Handles (Logic & Structure) +- Game logic and systems in C# +- Node hierarchy structure +- Script functionality +- Basic scene composition +- Export variable definitions +- Signal method implementations +- Resource class definitions + +### Human Handles (Visual & Feel) +- Sprite assignment and animation +- Collision shape fine-tuning +- Visual effects and particles +- UI precise positioning +- Audio integration +- Testing game feel +- Input mapping + +## C# Code Best Practices + +### Node References +```csharp +// GOOD - Unique names (set in .tscn with unique_name_in_owner = true) +private Node player; + +public override void _Ready() +{ + player = GetNode("%Player"); +} + +// GOOD - Relative paths for direct children +private Sprite2D sprite; + +public override void _Ready() +{ + sprite = GetNode("Sprite2D"); +} + +// AVOID - Fragile absolute paths +var player = GetNode("/root/Main/World/Player"); +``` + +### Signals +```csharp +// For static connections (nodes in same scene): +// AI provides the method, human connects in editor OR AI adds to .tscn +private void _OnButtonPressed() +{ + GD.Print("Button pressed!"); +} + +// For dynamic connections (runtime created nodes): +// AI writes the connection code +public override void _Ready() +{ + if (!enemy.Died.IsConnected(Callable.From(_OnEnemyDied))) + { + enemy.Died.Connect(Callable.From(_OnEnemyDied)); + } +} +``` + +### Export Variables +```csharp +// AI defines them with sensible defaults +[Export] public float MoveSpeed { get; set; } = 300.0f; // Human tweaks in Inspector +[Export] public PackedScene EnemyScene { get; set; } // Human assigns in Inspector + +// Group related exports +[ExportGroup("Movement")] +[Export] public float Speed { get; set; } = 100.0f; +[Export] public float Acceleration { get; set; } = 10.0f; +``` + +### Scene Instantiation +```csharp +// Always check if assigned +[Export] public PackedScene ProjectileScene { get; set; } + +private void Shoot() +{ + if (ProjectileScene == null) + { + GD.PrintErr("ProjectileScene not assigned in Inspector"); + return; + } + var bullet = ProjectileScene.Instantiate(); +} +``` + +## Standard C# Code Structure +```csharp +using Godot; + +public partial class Player : CharacterBody2D +{ + // Signals + [Signal] public delegate void HealthChangedEventHandler(int newValue); + + // Constants + private const float MaxSpeed = 400.0f; + + // Export variables + [ExportGroup("Combat")] + [Export] public int Damage { get; set; } = 10; + [Export] public float AttackRate { get; set; } = 1.0f; + + // Private variables + private string _currentState = "idle"; + private Vector2 _velocity = Vector2.Zero; + + // Node references (initialized in _Ready) + private Sprite2D _sprite; + private Control _healthBar; + + // Godot callbacks + public override void _Ready() + { + _sprite = GetNode("Sprite2D"); + _healthBar = GetNode("%HealthBar"); // Unique name + } + + public override void _PhysicsProcess(double delta) + { + // Physics logic + } + + // Public methods + public void TakeDamage(int amount) + { + // Implementation + } + + // Private methods + private void UpdateHealthBar() + { + // Implementation + } + + // Signal callbacks + private void _OnAreaEntered(Area2D area) + { + // Implementation + } +} +``` + +## Resource Creation +```csharp +// AI provides the Resource class definition +using Godot; + +[GlobalClass] +public partial class Recipe : Resource +{ + [Export] public string Id { get; set; } = ""; + [Export] public string[] Ingredients { get; set; } = Array.Empty(); + [Export] public float InstabilityCost { get; set; } = 0.0f; +} + +// Human creates instances: Right-click > Create Resource > Recipe +``` + +## Testing & Debug Helpers +```csharp +// Always include debug capability +[Export] public bool DebugMode { get; set; } = false; + +// Debug methods +public override void _Ready() +{ + if (DebugMode) + { + GD.Print($"[{Name}] Ready with speed: {MoveSpeed}"); + } +} + +// Unit test methods (can be called from debugger or test scenes) +public static float TestBrewingTiming() +{ + float totalTime = 0.4f * 3 + 0.8f; // 3 ingredients + set time + System.Diagnostics.Debug.Assert(totalTime < 2.5f, "Brewing too slow!"); + return totalTime; +} +``` + +## Common C# Patterns + +### Object Pooling Setup +```csharp +// AI provides the structure, human assigns the scene +[Export] public PackedScene PooledScene { get; set; } // Assign in Inspector +private readonly Queue _pool = new(); + +private Node GetInstance() +{ + if (_pool.Count == 0) + return PooledScene.Instantiate(); + return _pool.Dequeue(); +} +``` + +### State Machine +```csharp +public enum State { Idle, Moving, Attacking } +private State _currentState = State.Idle; + +private void TransitionTo(State newState) +{ + _currentState = newState; + switch (newState) + { + case State.Idle: + _animationPlayer.Play("idle"); + break; + case State.Moving: + _animationPlayer.Play("run"); + break; + } +} +``` +## Instructions for Humans +When AI generates code/scenes, it should include clear TODO comments: +```csharp +// TODO: In Godot Editor: +// 1. Assign EnemyScene in Inspector (drag Enemy.tscn) +// 2. Set up collision shape (Circle, radius ~16) +// 3. Add sprite texture +// 4. Connect hurt_box's area_entered signal +``` + +## What to Avoid + +### DON'T +- Hardcode paths to scenes (use [Export]) +- Create complex AnimationPlayer tracks in code +- Generate particle system parameters in code +- Edit .import files +- Assume node paths without proper initialization +- Use old Godot 3 syntax or GDScript patterns in C# +- Create collision polygons via code arrays +- Modify project.godot directly +- Create .gd files for game logic (use C# instead) +- Create documentation files unless specifically requested +- Use esotheric powershell one-liners for file manipulation (use the available tools or ask the user instead) + +### DO +- Use [Export] for all scene references +- Use % for unique named nodes with GetNode +- Include debug helpers +- Check for null before using nodes +- Use proper C# naming conventions (PascalCase for public, camelCase for private) +- Provide fallbacks and error messages +- Keep visual things in the editor +- Use C# for all game logic and scripts + +## File Organization Example +``` +/project +├── /Scripts # All C# scripts organized by category +│ ├── /Resources # Resource class definitions (AI creates these) +│ │ └── Recipe.cs +│ ├── /Player # Player-related scripts +│ │ └── PlayerController.cs +│ ├── /Enemies # Enemy-related scripts +│ │ └── EnemyAI.cs +│ ├── GameManager.cs # Global scripts at root level +│ └── EventBus.cs +├── /Scenes # Mix of AI and human work, organized by category +│ ├── /Player # Player-related scenes +│ │ └── Player.tscn # AI creates structure, human adds visuals +│ ├── /Enemies # Enemy-related scenes +│ │ └── Enemy.tscn +│ └── /UI # UI scenes +│ └── MainMenu.tscn +├── /Resources # .tres resource files organized by category +│ ├── /Recipes # Based on AI's Resource classes +│ │ └── FireWaterSteam.tres +│ └── /Items # Item resources +│ └── Sword.tres +├── /Shaders # Shader files +│ ├── Water.gdshader +│ └── Fire.gdshader +└── /Sprites # Human manages visual assets + ├── /Player + └── /Enemies +``` + +## C# Specific Guidelines + +### Naming Conventions +- Classes: PascalCase (`PlayerController`) +- Public properties/methods: PascalCase (`MoveSpeed`, `TakeDamage()`) +- Private fields: _camelCase (`_currentHealth`, `_velocity`) +- Constants: PascalCase (`MaxHealth`) +- Enums: PascalCase (`PlayerState.Moving`) + +### Property Usage +```csharp +// Prefer properties over fields for exports +[Export] public float Speed { get; set; } = 100.0f; + +// Use private fields for internal state +private float _currentSpeed; +``` + +### Null Safety +```csharp +// Always check exported scenes/nodes +if (ProjectileScene != null) +{ + var projectile = ProjectileScene.Instantiate(); + // Use the projectile +} +``` + +## Project-Specific Notes + +For **Your game**: + + +## Summary +- AI handles: Logic, structure, systems in C# +- Human handles: Visuals, feel, precise adjustments +- .tscn files are human-readable and AI can generate them +- Always use [Export] and Inspector for scene/resource references +- Use C# for all game logic, GDScript only for EditorScript tools +- Include debug modes and clear TODOs +- Test core mechanics with static methods +- Avoid creating documentation unless requested