first commit

This commit is contained in:
2025-11-24 23:10:55 -06:00
commit 8315fa51c9
279 changed files with 74600 additions and 0 deletions

558
godot_client/CLAUDE.md Normal file
View File

@@ -0,0 +1,558 @@
# CLAUDE.md - Godot Client
## Service Overview
**Godot Client** for Code of Conquest - Native cross-platform game client using Godot 4.5 + GDScript.
**Tech Stack:** Godot 4.5 + GDScript + HTTPRequest
**Platforms:** Desktop (Windows, macOS, Linux), Mobile (Android, iOS), Web (HTML5)
**Location:** `/godot_client`
---
## Architecture Role
This Godot client is a **thin UI layer** that makes HTTP requests to the API backend:
- ✅ Render game UI (scenes, components)
- ✅ Handle user input
- ✅ Make HTTP requests to API backend
- ✅ Display API responses
- ✅ Client-side animations and effects
- ✅ Local state management (UI state only)
**What this service does NOT do:**
- ❌ No business logic (all in `/api`)
- ❌ No game mechanics calculations (use API)
- ❌ No direct database access (use API)
- ❌ No AI calls (use API)
- ❌ No authoritative game state (API is source of truth)
**Communication:**
```
Godot Client → HTTP Request → API Backend
↑ ↓
←───── HTTP Response ────────┘
```
---
## Documentation Index
**Godot Client Documentation:**
- **[README.md](README.md)** - Setup and usage guide
- **[docs/ARCHITECTURE.md](docs/ARCHITECTURE.md)** - Client architecture overview
- **[docs/GETTING_STARTED.md](docs/GETTING_STARTED.md)** - Quickstart guide
- **[docs/EXPORT.md](docs/EXPORT.md)** - Platform export instructions
- **[docs/THEME_SETUP.md](docs/THEME_SETUP.md)** - UI theming guide
- **[docs/MULTIPLAYER.md](docs/MULTIPLAYER.md)** - Multiplayer client implementation
- **[docs/README.md](docs/README.md)** - Scene documentation workflow
**Project-Wide Documentation:**
- **[../docs/ARCHITECTURE.md](../docs/ARCHITECTURE.md)** - System architecture overview
- **[../api/docs/API_REFERENCE.md](../api/docs/API_REFERENCE.md)** - API endpoints to call
- **[../docs/WEB_VS_CLIENT_SYSTEMS.md](../docs/WEB_VS_CLIENT_SYSTEMS.md)** - Feature distribution between frontends
---
## Development Guidelines
### Project Structure
```
godot_client/
├── project.godot # Godot project configuration
├── scenes/ # Godot scene files (.tscn)
│ ├── main.tscn # Entry point
│ ├── auth/ # Authentication scenes
│ ├── character/ # Character scenes
│ ├── combat/ # Combat scenes
│ └── components/ # Reusable UI components
├── scripts/ # GDScript code
│ ├── services/ # Singleton autoloads
│ ├── models/ # Data models
│ ├── components/ # Component scripts
│ └── utils/ # Helper utilities
└── assets/ # Game assets
├── fonts/ # Fonts
├── themes/ # UI themes
└── ui/ # UI assets
```
### GDScript Coding Standards
**Style & Structure**
- Follow GDScript style guide (snake_case for functions/variables)
- Use PascalCase for class names
- Always include doc comments for classes and functions
- Prefer explicit types over dynamic typing
- Group related code with region comments
- **Do NOT apply styles/themes in code** - All visual styling (StyleBoxFlat, colors, fonts, margins) should be done in the Godot editor, not programmatically in GDScript
**Example:**
```gdscript
class_name CharacterCard
extends Control
## A UI component that displays character information.
##
## This card shows the character's name, level, class, and stats.
## It makes an HTTP request to fetch character data from the API.
#region Exports
@export var character_id: String = ""
@export var auto_load: bool = true
#endregion
#region Private Variables
var _character_data: Dictionary = {}
var _is_loading: bool = false
#endregion
#region Lifecycle
func _ready() -> void:
"""Initialize the character card."""
if auto_load and character_id != "":
load_character()
func _process(_delta: float) -> void:
"""Update card UI each frame."""
pass
#endregion
#region Public Methods
func load_character() -> void:
"""Load character data from API."""
if _is_loading:
return
_is_loading = true
var url = Settings.api_base_url + "/api/v1/characters/" + character_id
HTTPClient.get_request(url, _on_character_loaded, _on_character_error)
func refresh() -> void:
"""Refresh character data."""
load_character()
#endregion
#region Private Methods
func _update_ui() -> void:
"""Update UI elements with character data."""
$Name.text = _character_data.get("name", "Unknown")
$Level.text = "Level " + str(_character_data.get("level", 1))
func _show_error(message: String) -> void:
"""Display error message to user."""
$ErrorLabel.text = message
$ErrorLabel.visible = true
#endregion
#region Signal Handlers
func _on_character_loaded(response: Dictionary) -> void:
"""Handle successful character data load."""
_is_loading = false
if response.has("result"):
_character_data = response.result
_update_ui()
func _on_character_error(error: String) -> void:
"""Handle character data load error."""
_is_loading = false
_show_error("Failed to load character: " + error)
func _on_button_pressed() -> void:
"""Handle button click."""
refresh()
#endregion
```
**Code Organization:**
1. Class documentation
2. Exports (inspector variables)
3. Signals
4. Public constants
5. Private variables
6. Lifecycle methods (_ready, _process, etc.)
7. Public methods
8. Private methods
9. Signal handlers
### Service Singletons (Autoloads)
**Settings Service (`scripts/services/settings.gd`):**
```gdscript
extends Node
## Global settings and configuration management.
var api_base_url: String = "http://localhost:5000"
var environment: String = "development"
var version: String = "0.1.0"
func _ready() -> void:
load_settings()
func load_settings() -> void:
"""Load settings from file or environment."""
# Load from settings file or use defaults
if FileAccess.file_exists("user://settings.json"):
var file = FileAccess.open("user://settings.json", FileAccess.READ)
var json = JSON.parse_string(file.get_as_text())
file.close()
if json:
api_base_url = json.get("api_base_url", api_base_url)
environment = json.get("environment", environment)
func save_settings() -> void:
"""Save settings to file."""
var data = {
"api_base_url": api_base_url,
"environment": environment
}
var file = FileAccess.open("user://settings.json", FileAccess.WRITE)
file.store_string(JSON.stringify(data, "\t"))
file.close()
```
**HTTP Client Service (`scripts/services/http_client.gd`):**
```gdscript
extends Node
## Centralized HTTP client for API communication.
signal request_completed(response: Dictionary)
signal request_failed(error: String)
var _http_requests: Dictionary = {}
func get_request(url: String, on_success: Callable, on_error: Callable = Callable()) -> void:
"""Make a GET request to the API."""
_make_request(url, HTTPClient.METHOD_GET, {}, on_success, on_error)
func post_request(url: String, data: Dictionary, on_success: Callable, on_error: Callable = Callable()) -> void:
"""Make a POST request to the API."""
_make_request(url, HTTPClient.METHOD_POST, data, on_success, on_error)
func _make_request(url: String, method: int, data: Dictionary, on_success: Callable, on_error: Callable) -> void:
"""Internal method to make HTTP requests."""
var http_request = HTTPRequest.new()
add_child(http_request)
# Store callbacks
var request_id = str(http_request.get_instance_id())
_http_requests[request_id] = {
"on_success": on_success,
"on_error": on_error,
"node": http_request
}
# Connect signals
http_request.request_completed.connect(_on_request_completed.bind(request_id))
# Make request
var headers = ["Content-Type: application/json"]
var body = JSON.stringify(data) if method != HTTPClient.METHOD_GET else ""
var error = http_request.request(url, headers, method, body)
if error != OK:
_handle_error(request_id, "Request failed: " + str(error))
func _on_request_completed(result: int, response_code: int, headers: PackedStringArray, body: PackedByteArray, request_id: String) -> void:
"""Handle HTTP request completion."""
var request_data = _http_requests.get(request_id)
if not request_data:
return
# Parse response
var json = JSON.parse_string(body.get_string_from_utf8())
# Check for errors
if result != HTTPRequest.RESULT_SUCCESS or response_code >= 400:
var error_message = json.get("error", {}).get("message", "Request failed") if json else "Request failed"
_handle_error(request_id, error_message)
return
# Success
if request_data.on_success.is_valid():
request_data.on_success.call(json)
# Cleanup
_cleanup_request(request_id)
func _handle_error(request_id: String, error: String) -> void:
"""Handle request error."""
var request_data = _http_requests.get(request_id)
if request_data and request_data.on_error.is_valid():
request_data.on_error.call(error)
_cleanup_request(request_id)
func _cleanup_request(request_id: String) -> void:
"""Clean up completed request."""
var request_data = _http_requests.get(request_id)
if request_data:
request_data.node.queue_free()
_http_requests.erase(request_id)
```
### Scene Organization
**Scene Structure:**
```
Main Scene (main.tscn)
├── UI Layer (CanvasLayer)
│ ├── MainMenu
│ ├── LoadingScreen
│ └── ErrorPopup
├── Game Layer (Node2D/Node3D)
│ ├── Character
│ ├── World
│ └── Combat
└── Services (Node)
├── AudioManager
└── InputManager
```
**Scene Best Practices:**
- Keep scenes focused (single responsibility)
- Use scenes as components (reusable UI elements)
- Attach scripts to scene root nodes
- Use groups for easy node access (`node.add_to_group("enemies")`)
- Prefer signals over direct node references
### UI Theming
**Theme Structure:**
```gdscript
# Create programmatically or use theme resource
var theme = Theme.new()
# Colors
theme.set_color("font_color", "Label", Color.WHITE)
theme.set_color("font_color", "Button", Color("#e5e7eb"))
# Fonts
var title_font = load("res://assets/fonts/Cinzel-Bold.ttf")
theme.set_font("font", "TitleLabel", title_font)
# Stylebox
var button_style = StyleBoxFlat.new()
button_style.bg_color = Color("#8b5cf6")
button_style.corner_radius_top_left = 8
button_style.corner_radius_top_right = 8
button_style.corner_radius_bottom_left = 8
button_style.corner_radius_bottom_right = 8
theme.set_stylebox("normal", "Button", button_style)
```
### API Communication Patterns
**Simple GET Request:**
```gdscript
func load_characters() -> void:
var url = Settings.api_base_url + "/api/v1/characters"
HTTPClient.get_request(url, _on_characters_loaded, _on_characters_error)
func _on_characters_loaded(response: Dictionary) -> void:
var characters = response.get("result", [])
# Update UI with characters
func _on_characters_error(error: String) -> void:
print("Error loading characters: ", error)
```
**POST Request with Data:**
```gdscript
func create_character(name: String, class_id: String, origin_id: String) -> void:
var url = Settings.api_base_url + "/api/v1/characters"
var data = {
"name": name,
"class_id": class_id,
"origin_id": origin_id
}
HTTPClient.post_request(url, data, _on_character_created, _on_create_error)
func _on_character_created(response: Dictionary) -> void:
var character = response.get("result")
print("Character created: ", character.name)
# Navigate to character screen
func _on_create_error(error: String) -> void:
show_error_popup("Failed to create character: " + error)
```
### State Management
**StateManager Service (`scripts/services/state_manager.gd`):**
```gdscript
extends Node
## Global game state management.
signal state_changed(key: String, value: Variant)
var _state: Dictionary = {}
func set_value(key: String, value: Variant) -> void:
"""Set a state value."""
_state[key] = value
state_changed.emit(key, value)
func get_value(key: String, default: Variant = null) -> Variant:
"""Get a state value."""
return _state.get(key, default)
func has_value(key: String) -> bool:
"""Check if state has a key."""
return _state.has(key)
func clear() -> void:
"""Clear all state."""
_state.clear()
```
**Usage:**
```gdscript
# Set current character
StateManager.set_value("current_character_id", "char_123")
# Get current character
var char_id = StateManager.get_value("current_character_id")
# Listen for changes
StateManager.state_changed.connect(_on_state_changed)
func _on_state_changed(key: String, value: Variant) -> void:
if key == "current_character_id":
load_character(value)
```
---
## Godot Workflow
### Project Setup (Autoloads)
In Project Settings → Autoload, add these singletons:
1. **Settings** - `scripts/services/settings.gd`
2. **HTTPClient** - `scripts/services/http_client.gd`
3. **StateManager** - `scripts/services/state_manager.gd`
### Creating a New Scene
1. **Plan the scene** - Sketch layout, define purpose
2. **Create scene file** - `scenes/feature/scene_name.tscn`
3. **Add nodes** - Build UI hierarchy
4. **Attach script** - `scripts/feature/scene_name.gd`
5. **Connect signals** - Wire up interactions
6. **Test in editor** - F6 to test scene
7. **Integrate** - Add to main scene or navigation
### Testing
**In Editor:**
- F5 - Run project
- F6 - Run current scene
- Use Debugger panel for breakpoints
- Monitor output for print statements
**Export Testing:**
- Test on target platforms (Desktop, Mobile, Web)
- Check API connectivity
- Verify UI scaling
---
## Export Configuration
### Desktop Export
**Windows:**
```
Platform: Windows Desktop
Architecture: x86_64
Export Template: Release
```
**macOS:**
```
Platform: macOS
Architecture: Universal (arm64 + x86_64)
Export Template: Release
Code Sign: Required for distribution
```
**Linux:**
```
Platform: Linux/X11
Architecture: x86_64
Export Template: Release
```
### Mobile Export
**Android:**
```
Platform: Android
Architecture: arm64-v8a
Min SDK: 21
Target SDK: 33
```
**iOS:**
```
Platform: iOS
Architecture: arm64
Deployment Target: iOS 12.0
Bundle ID: com.codeofconquest.game
```
### Web Export
```
Platform: Web
Export Type: HTML5
Progressive Web App: Yes
```
See [EXPORT.md](EXPORT.md) for detailed instructions.
---
## Git Standards
**Commit Messages:**
- Use conventional commit format: `feat:`, `fix:`, `style:`, `asset:`, etc.
- Examples:
- `feat(godot): add character list scene`
- `fix(http): handle API timeout errors`
- `style(ui): update character card layout`
- `asset(fonts): add Cinzel font family`
**Branch Strategy:**
- Branch off `dev` for features
- Merge back to `dev` for testing
- Promote to `master` for production
---
## Notes for Claude Code
When working on the Godot client:
1. **Thin client only** - No business logic, just UI and user interaction
2. **Always call API** - Use HTTPClient service for all data operations
3. **Handle errors gracefully** - Show user-friendly error popups
4. **Keep scenes modular** - Create reusable component scenes
5. **Use signals** - Prefer signals over direct node references
6. **Test on target platforms** - Verify exports work correctly
7. **Performance matters** - Optimize for mobile (low draw calls, minimize allocations)
8. **Follow Godot conventions** - Use GDScript idioms, not Python/JavaScript patterns
**Remember:**
- This is a thin client - all logic lives in the API backend
- The API serves multiple frontends (this Godot client and web UI)
- Godot handles rendering and input - API handles game state
- Keep it responsive - show loading indicators during API calls
- Mobile-first design - optimize for touch and smaller screens