# State Management System

## Architecture Overview

The game uses a **dual-state system** to handle the time loop mechanic:

### 1. **Loop State** (Resets Every Loop)
Variables that reset when the 3-hour loop completes (14:23 → 17:23):
- NPCs talked to this loop
- Items collected this loop
- Doors unlocked this loop
- Quests completed this loop
- Temporary flags (e.g., "found_key", "door_opened")

### 2. **Persistent State** (Survives Loops)
Variables that carry over between loops:
- Knowledge gained (player remembers things)
- Permanent unlocks (shortcuts, abilities)
- Story progression flags
- Achievements
- Total loops completed

## Where Variables Are Created/Modified

### In Godot Editor
1. **Export variables in scripts**:
```gdscript
@export var npc_id: String = "fisherman"
@export var required_knowledge: String = "fishing_spot"
```

2. **Set in Inspector**: Select node → Inspector → set exported values

### In Code (Runtime)
All state modifications happen through `StateManager`:

```gdscript
# Loop state (resets each loop)
StateManager.set_flag("door_opened")
StateManager.add_npc_talked("bob")
StateManager.add_item_collected("key")
StateManager.unlock_door("mansion_door")
StateManager.complete_quest("find_the_key")

# Persistent state (survives loops)
StateManager.add_knowledge("safe_code")
StateManager.add_unlock("shortcut_unlocked")
StateManager.set_story_flag("chapter_1_done")
StateManager.add_achievement("speed_runner")
```

## File Structure

```
scripts/
  autoload/
    state_manager.gd       # Core state system
    time_manager.gd        # Time loop tracking
    save_manager.gd        # Save/load (includes state)
    game_manager.gd        # Game flow
    
scenes/
  npcs/
    example_npc.gd         # Example NPC using state
  interactables/
    door.gd                # Example door using state
    
docs/
  state_system_examples.md # Usage examples
  STATE_SYSTEM.md          # This file
```

## API Reference

### Loop State (Resets)

```gdscript
# Flags
StateManager.set_flag("flag_name", true)
StateManager.has_flag("flag_name") -> bool
StateManager.clear_flag("flag_name")

# NPCs
StateManager.add_npc_talked("npc_id")
StateManager.has_talked_to_npc("npc_id") -> bool

# Items
StateManager.add_item_collected("item_id")
StateManager.has_collected_item("item_id") -> bool

# Doors
StateManager.unlock_door("door_id")
StateManager.is_door_unlocked("door_id") -> bool

# Quests
StateManager.complete_quest("quest_id")
StateManager.is_quest_completed("quest_id") -> bool
```

### Persistent State (Survives)

```gdscript
# Knowledge
StateManager.add_knowledge("knowledge_id")
StateManager.has_knowledge("knowledge_id") -> bool

# Unlocks
StateManager.add_unlock("unlock_id")
StateManager.has_unlock("unlock_id") -> bool

# Story Flags
StateManager.set_story_flag("flag_name", true)
StateManager.has_story_flag("flag_name") -> bool

# Achievements
StateManager.add_achievement("achievement_id")
StateManager.has_achievement("achievement_id") -> bool

# Loop Count
StateManager.persistent_state.total_loops -> int
```

### Signals

```gdscript
StateManager.state_changed.connect(func(key, value): ...)
StateManager.flag_set.connect(func(flag): ...)
StateManager.flag_cleared.connect(func(flag): ...)
TimeManager.loop_completed.connect(func(): ...)
```

## Integration with Time System

```gdscript
# Get current time
var time = TimeManager.get_current_time()
# Returns: {hours: 14, minutes: 45, loop: 2}

# Time-based events
if time.hours == 15 and time.minutes == 30:
    if not StateManager.has_flag("3:30_event"):
        StateManager.set_flag("3:30_event")
        trigger_event()

# Check time remaining
var time_str = TimeManager.get_time_string()  # "15:30"
```

## Save/Load Integration

State is automatically saved and loaded:

```gdscript
# Save (in pause menu)
SaveManager.save_game(player)
# Saves: position, time, loop count, AND all state

# Load (in main menu)
var data = SaveManager.load_game()
# Restores: position, time, loop count, AND all state
```

## Best Practices

### 1. Use Descriptive IDs
```gdscript
# Good
StateManager.add_knowledge("safe_combination_1234")
StateManager.set_flag("beach_door_unlocked")

# Bad
StateManager.add_knowledge("k1")
StateManager.set_flag("flag1")
```

### 2. Check Before Setting
```gdscript
# Avoid duplicate triggers
if not StateManager.has_flag("event_triggered"):
    StateManager.set_flag("event_triggered")
    trigger_event()
```

### 3. Use Signals for Reactions
```gdscript
func _ready():
    StateManager.state_changed.connect(_on_state_changed)

func _on_state_changed(key: String, value: Variant):
    if key == "knowledge_gained":
        update_journal()
    elif key == "quest_completed":
        show_reward()
```

### 4. Separate Loop vs Persistent Logic
```gdscript
# Loop state: Temporary progress
if StateManager.has_collected_item("key"):
    # Can open door THIS loop
    
# Persistent: Permanent knowledge
if StateManager.has_knowledge("door_code"):
    # Always know the code, even in new loops
```

## Common Patterns

### Pattern 1: Progressive Dialogue
```gdscript
func get_dialogue() -> String:
    if StateManager.has_knowledge("secret"):
        return "You know the secret!"
    elif StateManager.has_talked_to_npc(npc_id):
        return "We already talked."
    else:
        StateManager.add_npc_talked(npc_id)
        return "First time meeting!"
```

### Pattern 2: Conditional Access
```gdscript
func can_enter() -> bool:
    return (
        StateManager.has_collected_item("key") or
        StateManager.has_knowledge("door_code") or
        StateManager.has_unlock("master_key")
    )
```

### Pattern 3: Time + State Events
```gdscript
func check_special_event():
    var time = TimeManager.get_current_time()
    
    if time.hours == 16 and time.minutes == 0:
        if StateManager.has_knowledge("ritual_time"):
            if not StateManager.has_flag("ritual_started"):
                StateManager.set_flag("ritual_started")
                start_ritual()
```

### Pattern 4: Loop Progression
```gdscript
func _on_loop_completed():
    var loops = StateManager.persistent_state.total_loops
    
    if loops == 1:
        StateManager.add_knowledge("experienced_loop")
    elif loops == 5:
        StateManager.add_unlock("time_sense")
    elif loops == 10:
        StateManager.add_achievement("loop_master")
```

## Debugging

### Print Current State
```gdscript
# In console or debug script
print("Loop State:", StateManager.loop_state)
print("Persistent State:", StateManager.persistent_state)
print("Current Time:", TimeManager.get_time_string())
print("Loop #:", StateManager.persistent_state.total_loops)
```

### Reset State (Testing)
```gdscript
# Reset everything (like New Game)
StateManager.reset_all()
TimeManager.reset_loop()

# Reset just loop state
StateManager._reset_loop_state()
```

## Example: Complete NPC Interaction

```gdscript
extends Interactable

@export var npc_id: String = "wise_elder"

func interact(by: Node) -> void:
    super.interact(by)
    
    # Check persistent knowledge
    if StateManager.has_knowledge("elder_secret"):
        show_dialogue("You already know my secret.")
        return
    
    # Check if talked this loop
    if StateManager.has_talked_to_npc(npc_id):
        show_dialogue("Come back next loop.")
        return
    
    # Mark as talked
    StateManager.add_npc_talked(npc_id)
    
    # Check quest progress
    if StateManager.is_quest_completed("gather_herbs"):
        show_dialogue("Thank you for the herbs!")
        StateManager.add_knowledge("elder_secret")
        StateManager.add_achievement("elder_friend")
    else:
        show_dialogue("Bring me 3 herbs.")
```

## Migration from Old System

If you have existing state variables:

```gdscript
# Old way
var has_key = false
var talked_to_bob = false

# New way
StateManager.set_flag("has_key")
StateManager.add_npc_talked("bob")

# Check
if StateManager.has_flag("has_key"):
    # ...
```

## Performance Notes

- State checks are O(1) for flags/story_flags (dictionary lookup)
- State checks are O(n) for arrays (npcs_talked_to, items_collected, etc.)
- For large arrays, consider using flags instead:
  ```gdscript
  # Instead of checking array
  StateManager.has_collected_item("key_1")
  
  # Use flag for better performance
  StateManager.has_flag("key_1_collected")
  ```
