# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

This is a 3D multiplayer fighting game built in Godot 4.5 using an inheritance-based architecture. The game uses ENet for client-server networking with the Network singleton (autoload) managing all multiplayer connections.

## Key Commands

### Running the Game
- Open the project in Godot Editor and press F5 to run
- Or use: `godot --path . res://level/scenes/level.tscn`

### Testing Multiplayer Locally
1. Run the main scene (level.tscn)
2. Click "Host" on one instance to create a server (default port 8080)
3. Run another instance and click "Join" to connect as a client

## Architecture

### Inheritance Structure
The codebase follows an inheritance-based design pattern:
- **BaseUnit** (base class) - Common functionality for all game entities that can take damage/have health
- **Character** extends **CharacterBody3D** - Player character class (class_name: Character)
  - Currently implements movement, jumping, sprinting, respawning, and skin customization
  - Uses multiplayer authority for input handling (only the owner processes input)

### Networking (Network.gd)
- **Type**: Autoload singleton (accessible via `Network` globally)
- **Protocol**: ENet (UDP-based)
- **Architecture**: Client-Server (one host, multiple clients up to MAX_PLAYERS=10)
- **Player Data**: Stored in `Network.players` dictionary keyed by peer_id
  - Contains: `{"nick": String, "skin": Character.SkinColor}`

### RPC Patterns
The project uses Godot 4's RPC system for multiplayer synchronization:
- Use `@rpc("any_peer", "reliable")` for critical data (health, damage)
- Use `@rpc("any_peer", "unreliable")` for frequent position updates
- The server (multiplayer.is_server()) is authoritative for game state
- Use `multiplayer.get_remote_sender_id()` to identify RPC sender
- Use `rpc_id(peer_id, "method_name")` for targeted RPCs

### Scene Structure
- **level/scenes/level.tscn** - Main scene with menu UI, chat, and PlayersContainer
  - level.gd spawns players dynamically when they connect
- **level/scenes/player.tscn** - Player character scene
  - Instantiated by server when players connect
  - Named with peer_id for easy lookup

### Physics Layers
- Layer 1: "player" - Player collision
- Layer 2: "world" - Environment collision

### Input Actions
Defined in project.godot:
- `move_left`, `move_right`, `move_forward`, `move_backward` (WASD)
- `jump` (Space)
- `shift` (Left Shift for sprinting)
- `quit` (Escape)
- `toggle_chat` (Enter)

## Development Guidelines

### Git Commits
When creating git commits, do NOT include "🤖 Generated with [Claude Code]" or "Co-Authored-By: Claude" in commit messages. Keep commit messages clean and professional.

### Adding New Components
1. Create scripts in `level/scripts/`
2. For multiplayer-synchronized components:
   - Always check `is_multiplayer_authority()` before processing input
   - Use RPC for state changes that need to replicate
   - Server should validate all important state changes

### Adding New Unit Types (Enemies, NPCs)
1. Create a new class that extends BaseUnit
2. Implement required virtual methods
3. Add any unique behavior in _physics_process/_process
4. Ensure multiplayer authority is set correctly

### Testing Multiplayer
- Always test with at least 2 instances (1 host, 1 client)
- Test edge cases: late joins, disconnections, packet loss
- Verify state is synchronized correctly across all clients

## Common Patterns

### Spawning Networked Entities
```gdscript
# Server-side only
if multiplayer.is_server():
    var entity = entity_scene.instantiate()
    entity.name = str(unique_id)  # Name with ID for easy lookup
    entity.set_multiplayer_authority(owner_peer_id)
    container.add_child(entity, true)  # true = force readable name
```

### Multiplayer Authority Check
```gdscript
func _physics_process(delta):
    if not is_multiplayer_authority():
        return
    # Only the authority processes input/logic
```

### RPC for State Changes
```gdscript
@rpc("any_peer", "reliable")
func take_damage(amount: int, attacker_id: int):
    if not multiplayer.is_server():
        return  # Server validates
    # Apply damage logic
    rpc("sync_health", current_health)  # Broadcast to all
```
