- Created UI system in level/ui/ folder - Action bar with 12 ability slots (Attack, Block, Dash, Jump + 8 expansion slots) - Unit frame showing player portrait with health bar - Character sheet/spellbook (toggle with Tab) displaying stats, weapons, and abilities - Tab hint indicator showing how to open character sheet - Custom theme with golden borders and dark backgrounds - UI Components: - HUD Manager autoload handles all UI initialization and player connections - Ability buttons with cooldown overlay and keybind display - Real-time health and cooldown tracking via signals - Scrollable character sheet with two-page layout - Player Integration: - Added UI signals: dash_cooldown_updated, attack_cooldown_updated, weapon_equipped_changed - Mouse capture system (Escape to toggle, click to recapture) - Synced attack cooldown from weapons to player for UI tracking - Updated level.gd to connect local player to HUD Manager - Updated CLAUDE.md with git commit guidelines
115 lines
4.2 KiB
Markdown
115 lines
4.2 KiB
Markdown
# 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
|
|
```
|