Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 101 additions & 0 deletions docs/GENE_DRAGON_TYPES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# Gene Dragon Types

## Overview

Gene-based dragons are procedurally generated with unique sprites, but they are also assigned traditional dragon types (fire, ice, forest, storm, shadow) based on their color genetics. This ensures they have appropriate abilities in arena combat and proper classification.

## How Types Are Determined

When a gene-based dragon is created, its type is automatically determined by analyzing its RGB color values:

### Type Assignment Rules

1. **Fire Dragons** (Red Dominant)
- Red channel is highest
- Red > Green AND Red > Blue
- Example colors: (255, 100, 50), (200, 80, 80)

2. **Ice Dragons** (Blue Dominant)
- Blue channel is highest
- Blue > Red AND Blue > Green
- Example colors: (50, 100, 255), (80, 120, 220)

3. **Forest Dragons** (Green Dominant)
- Green channel is highest
- Green > Red AND Green > Blue
- Example colors: (50, 255, 100), (80, 200, 90)

4. **Storm Dragons** (Purple/Balanced)
- Red and Blue channels are similar
- Difference between Red and Blue < 40
- Both Red and Blue > 100
- Example colors: (150, 80, 140), (180, 90, 170)

5. **Shadow Dragons** (Low Brightness)
- Maximum RGB value < 100
- Overall dark coloration
- Example colors: (60, 50, 55), (80, 70, 75)

6. **Default: Fire**
- If no clear dominant color matches
- Ensures all dragons have a valid type

## Implementation

The type determination happens in `Dragon._determine_type_from_color()` when creating gene-based dragons:

```python
# In dragon.py
dragon_type = cls._determine_type_from_color(genotype.color)
```

## Benefits

### Combat Balance
- Gene dragons use moves and abilities appropriate to their type
- Fire dragons have fire attacks
- Ice dragons have ice attacks
- etc.

### Clear Classification
- Players can identify dragon types at a glance
- "Fire Elder" instead of "Gene-based elder"
- Consistent with legacy dragon system

### Visual Consistency
- Dragon's visual appearance (color) matches its type
- Red dragons breathe fire
- Blue dragons use ice abilities
- Green dragons have nature powers

## Display

Gene dragons are displayed with their assigned type:
- **Without title**: "Fire Elder", "Ice Adult", etc.
- **With title**: Title is shown instead (e.g., "the Eternal")
- **Legendary genes**: Can still show ★ legendary status

## Arena Behavior

In arena minigames, gene dragons now:
- Use abilities matching their assigned type
- Have type-appropriate stats and moves
- Balance properly against opponents

## Examples

| Color RGB | Assigned Type | Reason |
|-----------------|---------------|---------------------------|
| (255, 50, 50) | Fire | Red dominant |
| (50, 50, 255) | Ice | Blue dominant |
| (50, 255, 50) | Forest | Green dominant |
| (180, 80, 170) | Storm | Purple balanced |
| (60, 60, 60) | Shadow | Low brightness |
| (150, 150, 150) | Fire | No clear dominant (default)|

## Technical Notes

- Type is assigned once when dragon is created
- Type is saved/loaded with dragon data
- Cannot be changed after creation (tied to genetics)
- Deterministic: Same genotype seed = same color = same type
124 changes: 124 additions & 0 deletions docs/OFFLINE_PROGRESS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
# Offline Progress System

## Overview

The Dragon Riders game implements an offline progress system that rewards players for time spent away from the game, with specific rules for different game mechanics.

## How It Works

### Passive Coin Income
- **Continues while offline**: Yes, with a 4-hour cap
- **Rate**: 1 coin every 10 seconds (same as active play)
- **Maximum offline time**: 4 hours (14,400 seconds)
- **Maximum offline coins**: 1,440 coins (4 hours worth)

If you're away for longer than 4 hours, you'll still only receive 4 hours worth of coins.

### Dragon Growth
- **Continues while offline**: No
- Dragons will NOT grow or progress to the next stage while the game is closed
- Growth only happens during active play sessions
- This prevents dragons from growing too quickly without player interaction

### Other Systems
- **Feed/Pet cooldowns**: Continue counting down while offline
- **Inventory items**: Preserved as-is
- **Equipment**: No changes while offline
- **Eggs**: Do not progress while offline (require clicks)

## Offline Progress Summary

When you return to the game after being away for more than 1 minute, you'll see a popup displaying:
- Total time you were away
- Coins earned during offline time
- Notification if the 4-hour cap was applied
- Reminder that dragons don't grow offline

Press ENTER, SPACE, or click "Continue" to dismiss the popup.

## Technical Details

### How Time Is Tracked

1. **On Game Close/Save**:
- `last_close_time` is recorded
- All dragon `last_update` times are saved

2. **On Game Load**:
- System calculates `time_away = current_time - last_close_time`
- Caps offline time to 4 hours maximum
- Awards coins based on capped time
- Resets all dragon `last_update` times to current time (prevents offline growth)
- Creates offline progress popup if away > 1 minute

### Implementation Files

- **src/game_state.py**: Core offline progress logic
- `calculate_offline_progress()`: Calculates what was earned
- `load()`: Applies offline progress on game load
- `save()`: Records close time

- **src/offline_progress_popup.py**: Popup UI component
- Displays offline progress summary
- Blocks other input until dismissed
- Medieval-styled popup design

- **src/idle_game.py**: Integration
- Shows popup on game start if applicable
- Blocks input while popup is visible

- **src/save_system_sqlite.py**: Persistence
- Saves/loads `last_close_time` field

## Configuration

To modify offline progress behavior, edit these constants in `src/utils/constants.py`:

```python
PASSIVE_COIN_RATE = 10.0 # Seconds per coin
```

And this constant in `src/game_state.py`:

```python
MAX_OFFLINE_TIME = 4 * 60 * 60 # 4 hours in seconds (in calculate_offline_progress())
```

## Design Philosophy

The offline progress system is designed to:

1. **Reward returning players** without making active play feel wasteful
2. **Cap passive benefits** to encourage regular play sessions
3. **Prevent dragon auto-growth** to maintain player engagement with growth mechanics
4. **Maintain balance** by limiting maximum offline gains

This creates a balanced idle game experience where:
- You're rewarded for returning after time away
- Active play is still more beneficial than passive waiting
- Dragon care remains an active, engaging mechanic
- The game respects your time while encouraging regular check-ins

## Examples

### Short Break (30 minutes)
```
Time away: 30m
Coins earned: 180 coins
Dragons: No growth
```

### Long Session (8 hours)
```
Time away: 8h
Coins earned: 1,440 coins (capped at 4 hours)
Dragons: No growth
Warning: "Capped at 4 hours"
```

### Quick Check (45 seconds)
```
No popup shown (< 1 minute away)
Minimal coins earned: ~4 coins
Dragons: No growth
```
35 changes: 35 additions & 0 deletions docs/SAVE_SYSTEM.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,41 @@ Dragon Riders uses a secure SQLite-based save system to store game data. This sy
- **Prevent cheating** - Encrypt sensitive data and verify integrity with HMAC hashes
- **Provide safety** - Automatic migration from legacy JSON saves with backup support
- **Optimize storage** - Compress data and use efficient indexing
- **Save intelligently** - Only save when changes are made, with auto-save every 5 minutes

## Save Behavior

### When Does the Game Save?

The game uses a "dirty flag" system to track unsaved changes and only saves when necessary:

1. **Auto-save** - Every 5 minutes if there are unsaved changes
2. **On game close** - Always saves when you quit the game
3. **Manual saves removed** - No longer saves on every view change or action

### What Triggers Unsaved Changes?

The following actions mark the game state as "dirty" (needing save):

- **Shop**: Purchasing items
- **Arena**: Completing minigames
- **Enchanting**: Equipping or unequipping enchantments
- **Dragon Pen**: Selling dragons
- **Hatchery**: Hatching eggs
- **Main Screen**: Feeding, petting, or renaming dragons

### Visual Indicators

- **"● Unsaved"** indicator appears in the title bar (orange) when there are unsaved changes
- **"Auto-saved!"** message appears (blue) when the game auto-saves
- Both indicators disappear once changes are saved

### Benefits of This System

- **Better performance** - Reduces unnecessary disk writes
- **Smoother gameplay** - No stuttering when switching views
- **Data safety** - Still saves regularly (every 5 minutes) and on close
- **Clear feedback** - Visual indicator shows when changes need saving

## Architecture

Expand Down
14 changes: 5 additions & 9 deletions run_game.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,6 @@ def __init__(self):
# Initialize scenes
self.idle_game = IdleGame(self.game_state, self.sprite_manager, self.music_manager)

# Update mute button text if music is muted
if self.music_manager and self.music_manager.is_muted:
self.idle_game.mute_button.text = "Unmute"
self.egg_screen = EggScreen(self.game_state, self.sprite_manager, self.sound_manager)
self.dragon_pen = DragonPen(self.game_state, self.sprite_manager)
self.equipment_screen = EquipmentScreen(self.game_state, self.sprite_manager)
Expand Down Expand Up @@ -154,6 +151,10 @@ def run(self):
# Update current scene
self._update(dt)

# Auto-save check (works for all scenes)
if self.game_state.auto_save_if_needed():
print("✓ Auto-saved game")

# Draw current scene
self._draw()

Expand Down Expand Up @@ -289,12 +290,7 @@ def _switch_scene(self, scene: str):
self.arena_screen = ArenaScreen(self.game_state) # Refresh arena
self.current_scene = "arena"
elif scene == "idle":
# Save game when returning to idle
print("Saving game on scene return...")
if self.game_state.save():
print("✓ Game saved")
else:
print("✗ Save failed")
# Return to idle without saving (auto-save handles it)
self.current_scene = "idle"


Expand Down
Loading