768 lines
22 KiB
Markdown
768 lines
22 KiB
Markdown
# Godot Client Architecture
|
|
|
|
## Overview
|
|
|
|
The Godot client is a native frontend for Code of Conquest that connects to the Flask backend via REST API. It provides a consistent experience across desktop, mobile, and web platforms.
|
|
|
|
## Architecture Diagram
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
│ Godot Client │
|
|
│ │
|
|
│ ┌───────────────────────────────────────────────────────┐ │
|
|
│ │ UI Layer (Scenes) │ │
|
|
│ │ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ │ │
|
|
│ │ │ Auth │ │ Char │ │ Combat │ │ World │ │ │
|
|
│ │ │ Screens│ │ Mgmt │ │ UI │ │ UI │ │ │
|
|
│ │ └────────┘ └────────┘ └────────┘ └────────┘ │ │
|
|
│ └───────────────────────────────────────────────────────┘ │
|
|
│ │ │
|
|
│ ┌───────────────────────────────────────────────────────┐ │
|
|
│ │ Component Layer │ │
|
|
│ │ ┌─────────┐ ┌──────┐ ┌──────────┐ ┌──────┐ │ │
|
|
│ │ │ Buttons │ │ Cards│ │FormFields│ │ ... │ │ │
|
|
│ │ └─────────┘ └──────┘ └──────────┘ └──────┘ │ │
|
|
│ └───────────────────────────────────────────────────────┘ │
|
|
│ │ │
|
|
│ ┌───────────────────────────────────────────────────────┐ │
|
|
│ │ Service Layer (Singletons) │ │
|
|
│ │ ┌──────────────┐ ┌────────────────┐ │ │
|
|
│ │ │ HTTPClient │ │ StateManager │ │ │
|
|
│ │ │ - API calls │ │ - Session │ │ │
|
|
│ │ │ - Auth token │ │ - Characters │ │ │
|
|
│ │ │ - Error │ │ - Wizard state │ │ │
|
|
│ │ │ handling │ │ - Persistence │ │ │
|
|
│ │ └──────────────┘ └────────────────┘ │ │
|
|
│ └───────────────────────────────────────────────────────┘ │
|
|
│ │ │
|
|
│ ┌───────────────────────────────────────────────────────┐ │
|
|
│ │ Data Models (GDScript) │ │
|
|
│ │ Character, CharacterClass, Origin, Skill, etc. │ │
|
|
│ └───────────────────────────────────────────────────────┘ │
|
|
└─────────────────────────────────────────────────────────────┘
|
|
│
|
|
HTTP/JSON REST API
|
|
│
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
│ Flask Backend │
|
|
│ (Existing REST API) │
|
|
│ /api/v1/auth/*, /api/v1/characters/* │
|
|
└─────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
## Layers
|
|
|
|
### 1. UI Layer (Scenes)
|
|
|
|
**Location**: `scenes/`
|
|
|
|
Individual screens and pages of the application.
|
|
|
|
**Responsibilities**:
|
|
- Render UI elements
|
|
- Handle user input
|
|
- Display data from StateManager
|
|
- Trigger API calls via HTTPClient
|
|
- Navigate between scenes
|
|
|
|
**Examples**:
|
|
- `scenes/auth/login.tscn` - Login screen
|
|
- `scenes/character/character_list.tscn` - Character list
|
|
- `scenes/character/create_wizard_step1.tscn` - Character creation step 1
|
|
|
|
**Communication**:
|
|
- Uses components from Component Layer
|
|
- Reads/writes to StateManager
|
|
- Calls HTTPClient for API requests
|
|
- Emits signals for navigation
|
|
|
|
### 2. Component Layer
|
|
|
|
**Location**: `scenes/components/`, `scripts/components/`
|
|
|
|
Reusable UI components shared across scenes.
|
|
|
|
**Responsibilities**:
|
|
- Provide consistent UI elements
|
|
- Encapsulate common patterns
|
|
- Handle component-specific logic
|
|
- Emit signals for parent scenes
|
|
|
|
**Examples**:
|
|
- `CustomButton` - Themed button with variants
|
|
- `Card` - Container with header/footer
|
|
- `FormField` - Input field with validation
|
|
|
|
**Communication**:
|
|
- Receives props from parent scenes
|
|
- Emits signals to parent scenes
|
|
- Uses ThemeColors for styling
|
|
- Independent of business logic
|
|
|
|
### 3. Service Layer (Singletons)
|
|
|
|
**Location**: `scripts/services/`
|
|
|
|
Global services accessible from anywhere in the app.
|
|
|
|
**Responsibilities**:
|
|
- Manage API communication
|
|
- Store global state
|
|
- Handle persistence
|
|
- Provide utilities
|
|
|
|
**Singletons**:
|
|
|
|
#### HTTPClient
|
|
- Base URL configuration
|
|
- Authentication token management
|
|
- JSON request/response handling
|
|
- Error handling and retries
|
|
- Timeout management
|
|
|
|
**API**:
|
|
```gdscript
|
|
HTTPClient.http_get(endpoint, callback, error_callback)
|
|
HTTPClient.http_post(endpoint, data, callback, error_callback)
|
|
HTTPClient.http_put(endpoint, data, callback, error_callback)
|
|
HTTPClient.http_delete(endpoint, callback, error_callback)
|
|
HTTPClient.http_patch(endpoint, data, callback, error_callback)
|
|
HTTPClient.set_auth_token(token)
|
|
```
|
|
|
|
#### StateManager
|
|
- User session state
|
|
- Character data cache
|
|
- Wizard state (character creation)
|
|
- Navigation history
|
|
- Settings and preferences
|
|
- Save/load to local storage
|
|
|
|
**API**:
|
|
```gdscript
|
|
StateManager.set_user_session(user_data, token)
|
|
StateManager.get_characters()
|
|
StateManager.set_wizard_origin(origin_data)
|
|
StateManager.save_state()
|
|
```
|
|
|
|
**Communication**:
|
|
- HTTPClient → Backend API (HTTP)
|
|
- StateManager → Local storage (FileAccess)
|
|
- Services → UI via signals
|
|
|
|
**Accessing Autoloads**:
|
|
|
|
Autoload singletons can be accessed in three ways:
|
|
|
|
1. **Direct access** (simple, works in `_ready()` and later):
|
|
```gdscript
|
|
func _ready():
|
|
HTTPClient.http_get("/api/v1/characters", _on_success)
|
|
```
|
|
|
|
2. **Get node explicitly** (verbose but always works):
|
|
```gdscript
|
|
func _on_button_clicked():
|
|
get_node("/root/HTTPClient").http_get("/api/v1/characters", _on_success)
|
|
```
|
|
|
|
3. **Cached reference with @onready** (best for multiple uses):
|
|
```gdscript
|
|
@onready var http_client = get_node("/root/HTTPClient")
|
|
@onready var state_manager = get_node("/root/StateManager")
|
|
|
|
func _ready():
|
|
http_client.http_get("/api/v1/characters", _on_success)
|
|
```
|
|
|
|
**Important**: When one autoload needs to reference another (like StateManager calling HTTPClient), use `@onready` to avoid script parsing errors. Direct access (`HTTPClient.method()`) only works during runtime, not during script compilation.
|
|
|
|
### 4. Data Models
|
|
|
|
**Location**: `scripts/models/`
|
|
|
|
GDScript classes that mirror backend data structures.
|
|
|
|
**Responsibilities**:
|
|
- Define typed data structures
|
|
- Validate data
|
|
- Serialize/deserialize JSON
|
|
- Provide helper methods
|
|
|
|
**Examples**:
|
|
```gdscript
|
|
class_name Character
|
|
|
|
var id: String
|
|
var name: String
|
|
var origin_id: String
|
|
var class_id: String
|
|
var level: int
|
|
var hp: int
|
|
var max_hp: int
|
|
var gold: int
|
|
var skills: Array[Skill]
|
|
|
|
static func from_json(data: Dictionary) -> Character:
|
|
# Parse JSON to Character object
|
|
pass
|
|
```
|
|
|
|
**Communication**:
|
|
- Created from API responses
|
|
- Stored in StateManager
|
|
- Displayed in UI scenes
|
|
|
|
## Data Flow
|
|
|
|
### Example: User Login
|
|
|
|
```
|
|
1. User enters credentials in LoginScene
|
|
↓
|
|
2. LoginScene calls HTTPClient.http_post("/api/v1/auth/login", {...})
|
|
↓
|
|
3. HTTPClient sends HTTP request to Flask backend
|
|
↓
|
|
4. Backend validates and returns JWT token + user data
|
|
↓
|
|
5. HTTPClient receives response, creates APIResponse object
|
|
↓
|
|
6. LoginScene receives APIResponse via callback
|
|
↓
|
|
7. LoginScene calls StateManager.set_user_session(user_data, token)
|
|
↓
|
|
8. StateManager stores session and emits user_logged_in signal
|
|
↓
|
|
9. StateManager saves state to local storage
|
|
↓
|
|
10. LoginScene navigates to CharacterListScene
|
|
```
|
|
|
|
### Example: Load Characters
|
|
|
|
```
|
|
1. CharacterListScene calls HTTPClient.http_get("/api/v1/characters")
|
|
↓
|
|
2. HTTPClient includes auth token in headers
|
|
↓
|
|
3. Backend returns array of character data
|
|
↓
|
|
4. CharacterListScene receives response
|
|
↓
|
|
5. CharacterListScene calls StateManager.set_characters(characters)
|
|
↓
|
|
6. StateManager emits characters_updated signal
|
|
↓
|
|
7. CharacterListScene updates UI with character cards
|
|
```
|
|
|
|
### Example: Character Creation Wizard
|
|
|
|
```
|
|
1. User selects origin in WizardStep1Scene
|
|
↓
|
|
2. Scene calls StateManager.set_wizard_origin(origin_data)
|
|
↓
|
|
3. Scene navigates to WizardStep2Scene
|
|
↓
|
|
4. User selects class in WizardStep2Scene
|
|
↓
|
|
5. Scene calls StateManager.set_wizard_class(class_data)
|
|
↓
|
|
6. User continues through steps 3 and 4
|
|
↓
|
|
7. WizardStep4Scene (confirmation) calls HTTPClient.http_post(
|
|
"/api/v1/characters",
|
|
{
|
|
"origin_id": StateManager.get_wizard_origin().id,
|
|
"class_id": StateManager.get_wizard_class().id,
|
|
"name": StateManager.get_wizard_name()
|
|
}
|
|
)
|
|
↓
|
|
8. Backend creates character and returns character data
|
|
↓
|
|
9. Scene calls StateManager.add_character(character_data)
|
|
↓
|
|
10. Scene calls StateManager.reset_wizard()
|
|
↓
|
|
11. Scene navigates back to CharacterListScene
|
|
```
|
|
|
|
## Navigation
|
|
|
|
### Scene Management
|
|
|
|
Godot's built-in scene tree is used for navigation:
|
|
|
|
```gdscript
|
|
# Navigate to another scene
|
|
get_tree().change_scene_to_file("res://scenes/character/character_list.tscn")
|
|
|
|
# Or use PackedScene for preloading
|
|
var next_scene = preload("res://scenes/auth/login.tscn")
|
|
get_tree().change_scene_to_packed(next_scene)
|
|
```
|
|
|
|
### Navigation Flow
|
|
|
|
```
|
|
App Start
|
|
↓
|
|
Main Scene (checks auth)
|
|
├─ Authenticated? → CharacterListScene
|
|
└─ Not authenticated? → LoginScene
|
|
↓
|
|
Login successful → CharacterListScene
|
|
↓
|
|
Create Character → WizardStep1Scene
|
|
↓
|
|
Step 2 → WizardStep2Scene
|
|
↓
|
|
Step 3 → WizardStep3Scene
|
|
↓
|
|
Step 4 → WizardStep4Scene
|
|
↓
|
|
Complete → CharacterListScene
|
|
↓
|
|
Select Character → CharacterDetailScene
|
|
↓
|
|
Start Game → GameWorldScene
|
|
↓
|
|
Enter Combat → CombatScene
|
|
```
|
|
|
|
### Navigation Helper
|
|
|
|
Consider creating a navigation manager:
|
|
|
|
```gdscript
|
|
# scripts/services/navigation_manager.gd
|
|
extends Node
|
|
|
|
const SCENE_LOGIN = "res://scenes/auth/login.tscn"
|
|
const SCENE_CHARACTER_LIST = "res://scenes/character/character_list.tscn"
|
|
const SCENE_WIZARD_STEP_1 = "res://scenes/character/create_wizard_step1.tscn"
|
|
# ... etc
|
|
|
|
func navigate_to(scene_path: String) -> void:
|
|
StateManager.set_current_scene(scene_path)
|
|
get_tree().change_scene_to_file(scene_path)
|
|
|
|
func navigate_to_login() -> void:
|
|
navigate_to(SCENE_LOGIN)
|
|
|
|
func navigate_to_character_list() -> void:
|
|
navigate_to(SCENE_CHARACTER_LIST)
|
|
|
|
# etc.
|
|
```
|
|
|
|
## Authentication Flow
|
|
|
|
### Initial Load
|
|
|
|
```gdscript
|
|
# main.gd (attached to main.tscn, first scene loaded)
|
|
extends Control
|
|
|
|
func _ready() -> void:
|
|
# StateManager auto-loads from local storage in its _ready()
|
|
# Check if user is authenticated
|
|
if StateManager.is_authenticated():
|
|
# Validate token by making a test API call
|
|
HTTPClient.http_get("/api/v1/auth/validate", _on_token_validated, _on_token_invalid)
|
|
else:
|
|
# Go to login
|
|
NavigationManager.navigate_to_login()
|
|
|
|
func _on_token_validated(response: HTTPClient.APIResponse) -> void:
|
|
if response.is_success():
|
|
# Token is valid, go to character list
|
|
NavigationManager.navigate_to_character_list()
|
|
else:
|
|
# Token expired, clear and go to login
|
|
StateManager.clear_user_session()
|
|
NavigationManager.navigate_to_login()
|
|
|
|
func _on_token_invalid(response: HTTPClient.APIResponse) -> void:
|
|
# Network error or token invalid
|
|
StateManager.clear_user_session()
|
|
NavigationManager.navigate_to_login()
|
|
```
|
|
|
|
### Login
|
|
|
|
```gdscript
|
|
# scenes/auth/login.gd
|
|
extends Control
|
|
|
|
@onready var email_field: FormField = $EmailField
|
|
@onready var password_field: FormField = $PasswordField
|
|
@onready var login_button: CustomButton = $LoginButton
|
|
@onready var error_label: Label = $ErrorLabel
|
|
|
|
func _on_login_button_clicked() -> void:
|
|
# Validate fields
|
|
if not email_field.validate() or not password_field.validate():
|
|
return
|
|
|
|
# Show loading state
|
|
login_button.set_loading(true)
|
|
error_label.visible = false
|
|
|
|
# Make API call
|
|
var credentials = {
|
|
"email": email_field.get_value(),
|
|
"password": password_field.get_value()
|
|
}
|
|
|
|
HTTPClient.http_post("/api/v1/auth/login", credentials, _on_login_success, _on_login_error)
|
|
|
|
func _on_login_success(response: APIResponse) -> void:
|
|
login_button.set_loading(false)
|
|
|
|
if response.is_success():
|
|
# Extract user data and token from response
|
|
var user_data = response.result.get("user", {})
|
|
var token = response.result.get("token", "")
|
|
|
|
# Store in StateManager
|
|
StateManager.set_user_session(user_data, token)
|
|
|
|
# Navigate to character list
|
|
NavigationManager.navigate_to_character_list()
|
|
else:
|
|
# Show error
|
|
error_label.text = response.get_error_message()
|
|
error_label.visible = true
|
|
|
|
func _on_login_error(response: APIResponse) -> void:
|
|
login_button.set_loading(false)
|
|
error_label.text = "Failed to connect. Please try again."
|
|
error_label.visible = true
|
|
```
|
|
|
|
### Logout
|
|
|
|
```gdscript
|
|
func _on_logout_button_clicked() -> void:
|
|
# Call logout endpoint (optional, for server-side cleanup)
|
|
HTTPClient.http_post("/api/v1/auth/logout", {}, _on_logout_complete)
|
|
|
|
func _on_logout_complete(response: APIResponse) -> void:
|
|
# Clear local session regardless of API response
|
|
StateManager.clear_user_session()
|
|
|
|
# Navigate to login
|
|
NavigationManager.navigate_to_login()
|
|
```
|
|
|
|
## State Persistence
|
|
|
|
### What Gets Saved
|
|
|
|
StateManager saves to `user://coc_state.save` (platform-specific location):
|
|
|
|
- User session data
|
|
- Auth token (if "remember me")
|
|
- Character list cache
|
|
- Selected character ID
|
|
- Character limits
|
|
- Settings/preferences
|
|
- Timestamp of last save
|
|
|
|
### Save Triggers
|
|
|
|
State is saved automatically:
|
|
- On login
|
|
- On logout (clears save)
|
|
- On character list update
|
|
- On character selection
|
|
- On settings change
|
|
- On app exit (if auto-save enabled)
|
|
|
|
Manual save:
|
|
```gdscript
|
|
StateManager.save_state()
|
|
```
|
|
|
|
### Load on Startup
|
|
|
|
StateManager automatically loads on `_ready()`:
|
|
- Restores user session if valid
|
|
- Restores character list
|
|
- Sets HTTPClient auth token
|
|
|
|
## Error Handling
|
|
|
|
### API Errors
|
|
|
|
```gdscript
|
|
HTTPClient.http_get("/api/v1/characters", _on_success, _on_error)
|
|
|
|
func _on_success(response: APIResponse) -> void:
|
|
if response.is_success():
|
|
# Handle success
|
|
var characters = response.result
|
|
else:
|
|
# API returned error (4xx, 5xx)
|
|
_show_error(response.get_error_message())
|
|
|
|
func _on_error(response: APIResponse) -> void:
|
|
# Network error or JSON parse error
|
|
_show_error("Failed to connect to server")
|
|
```
|
|
|
|
### Network Errors
|
|
|
|
HTTPClient handles:
|
|
- Connection timeout (30s default)
|
|
- DNS resolution failure
|
|
- TLS/SSL errors
|
|
- No response from server
|
|
- Invalid JSON responses
|
|
|
|
All errors return an `APIResponse` with:
|
|
- `status`: HTTP status code (or 500 for network errors)
|
|
- `error.message`: Human-readable error message
|
|
- `error.code`: Machine-readable error code
|
|
|
|
### User-Facing Errors
|
|
|
|
Show errors to users:
|
|
|
|
```gdscript
|
|
# Toast notification (create a notification system)
|
|
NotificationManager.show_error("Failed to load characters")
|
|
|
|
# Inline error label
|
|
error_label.text = response.get_error_message()
|
|
error_label.visible = true
|
|
|
|
# Error dialog
|
|
var dialog = AcceptDialog.new()
|
|
dialog.dialog_text = "An error occurred. Please try again."
|
|
dialog.popup_centered()
|
|
```
|
|
|
|
## Platform Considerations
|
|
|
|
### Mobile
|
|
|
|
**Touch Input**:
|
|
- Larger tap targets (minimum 44x44 dp)
|
|
- No hover states
|
|
- Swipe gestures for navigation
|
|
|
|
**Screen Sizes**:
|
|
- Responsive layouts
|
|
- Safe areas (notches, rounded corners)
|
|
- Portrait and landscape orientations
|
|
|
|
**Performance**:
|
|
- Limit draw calls
|
|
- Use compressed textures
|
|
- Reduce particle effects
|
|
|
|
**Networking**:
|
|
- Handle intermittent connectivity
|
|
- Show loading indicators
|
|
- Cache data locally
|
|
|
|
### Desktop
|
|
|
|
**Input**:
|
|
- Keyboard shortcuts
|
|
- Mouse hover effects
|
|
- Scroll wheel support
|
|
|
|
**Window Management**:
|
|
- Resizable windows
|
|
- Fullscreen mode
|
|
- Multiple monitor support
|
|
|
|
### Web
|
|
|
|
**Limitations**:
|
|
- No threading (slower)
|
|
- No file system access (use IndexedDB)
|
|
- Larger download size
|
|
- CORS restrictions
|
|
|
|
**Requirements**:
|
|
- HTTPS for SharedArrayBuffer
|
|
- Proper MIME types
|
|
- COOP/COEP headers
|
|
|
|
## Security Considerations
|
|
|
|
### Token Storage
|
|
|
|
**Desktop/Mobile**: Tokens stored in encrypted local storage
|
|
**Web**: Tokens stored in localStorage (less secure, consider sessionStorage)
|
|
|
|
### HTTPS Only
|
|
|
|
Always use HTTPS in production for API calls.
|
|
|
|
### Token Expiration
|
|
|
|
Handle expired tokens:
|
|
```gdscript
|
|
func _on_api_error(response: HTTPClient.APIResponse) -> void:
|
|
if response.status == 401: # Unauthorized
|
|
# Token expired, logout
|
|
StateManager.clear_user_session()
|
|
NavigationManager.navigate_to_login()
|
|
```
|
|
|
|
### Input Validation
|
|
|
|
Always validate user input:
|
|
- FormField components have built-in validation
|
|
- Re-validate on backend (never trust client)
|
|
- Sanitize before displaying (prevent XSS if user-generated content)
|
|
|
|
## Testing Strategy
|
|
|
|
### Unit Tests
|
|
|
|
Test individual components:
|
|
- HTTPClient request/response handling
|
|
- StateManager save/load
|
|
- Data model serialization
|
|
- Component validation logic
|
|
|
|
### Integration Tests
|
|
|
|
Test scene flows:
|
|
- Login → Character List
|
|
- Character Creation wizard
|
|
- Character selection → Game
|
|
|
|
### Platform Tests
|
|
|
|
Test on each platform:
|
|
- Desktop: Windows, Mac, Linux
|
|
- Mobile: Android, iOS
|
|
- Web: Chrome, Firefox, Safari
|
|
|
|
### API Mocking
|
|
|
|
For offline testing, create mock responses:
|
|
|
|
```gdscript
|
|
# scripts/services/mock_http_client.gd
|
|
extends Node
|
|
|
|
# Same API as HTTPClient but returns mock data
|
|
func get(endpoint: String, callback: Callable, error_callback: Callable = Callable()) -> void:
|
|
match endpoint:
|
|
"/api/v1/characters":
|
|
callback.call(_mock_character_list_response())
|
|
_:
|
|
error_callback.call(_mock_error_response())
|
|
|
|
func _mock_character_list_response() -> HTTPClient.APIResponse:
|
|
var mock_data = {
|
|
"app": "Code of Conquest",
|
|
"version": "0.1.0",
|
|
"status": 200,
|
|
"timestamp": Time.get_datetime_string_from_system(),
|
|
"result": [
|
|
{"id": "1", "name": "Warrior", "level": 5, "hp": 100, "max_hp": 100},
|
|
{"id": "2", "name": "Mage", "level": 3, "hp": 60, "max_hp": 60}
|
|
],
|
|
"error": {}
|
|
}
|
|
return HTTPClient.APIResponse.new(mock_data)
|
|
```
|
|
|
|
## Performance Optimization
|
|
|
|
### Minimize API Calls
|
|
|
|
- Cache data in StateManager
|
|
- Only refetch when necessary
|
|
- Use pagination for large lists
|
|
|
|
### Optimize Rendering
|
|
|
|
- Use object pooling for lists
|
|
- Minimize StyleBox allocations
|
|
- Use TextureRect instead of Sprite when possible
|
|
- Reduce shadow/glow effects on mobile
|
|
|
|
### Lazy Loading
|
|
|
|
Load scenes only when needed:
|
|
```gdscript
|
|
# Preload for instant transition
|
|
const CharacterList = preload("res://scenes/character/character_list.tscn")
|
|
|
|
# Or load on-demand
|
|
var scene = load("res://scenes/character/character_list.tscn")
|
|
```
|
|
|
|
## Debugging
|
|
|
|
### Enable Logging
|
|
|
|
```gdscript
|
|
# Add to HTTPClient, StateManager, etc.
|
|
print("[HTTPClient] GET /api/v1/characters")
|
|
print("[StateManager] User logged in: ", user_data.get("email"))
|
|
```
|
|
|
|
### Remote Debugging
|
|
|
|
For mobile:
|
|
1. Enable remote debug in Godot
|
|
2. Connect device via USB
|
|
3. Run with "Remote Debug" option
|
|
|
|
### Network Inspector
|
|
|
|
Monitor network traffic:
|
|
- Desktop: Use browser dev tools for Godot Web export
|
|
- Mobile: Use Charles Proxy or similar
|
|
- Check request/response bodies, headers, timing
|
|
|
|
## Next Steps (Phase 2)
|
|
|
|
After Phase 1 (Foundation) is complete:
|
|
|
|
1. **Implement Auth Screens** (Week 2)
|
|
- Login, Register, Password Reset
|
|
- Email verification
|
|
- Form validation
|
|
|
|
2. **Implement Character Management** (Weeks 3-4)
|
|
- Character creation wizard
|
|
- Character list
|
|
- Character detail
|
|
- Delete character
|
|
|
|
3. **Platform Optimization** (Week 5)
|
|
- Mobile touch controls
|
|
- Desktop keyboard shortcuts
|
|
- Web export configuration
|
|
|
|
4. **Future Features** (Week 6+)
|
|
- Combat UI
|
|
- World exploration
|
|
- Quest system
|
|
- Multiplayer
|
|
|
|
## Resources
|
|
|
|
- [Godot Documentation](https://docs.godotengine.org/)
|
|
- [GDScript Style Guide](https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/gdscript_styleguide.html)
|
|
- [Godot UI Tutorials](https://docs.godotengine.org/en/stable/tutorials/ui/index.html)
|
|
- [HTTP Request](https://docs.godotengine.org/en/stable/classes/class_httprequest.html)
|
|
- [FileAccess](https://docs.godotengine.org/en/stable/classes/class_fileaccess.html)
|