text-adventure-tui 0.1.1
A dynamic text adventure game for your terminal, powered by Ollama LLMs.
License: MIT
Requires Python: >=3.7

# Terminal Text Adventure

Welcome to **Terminal Text Adventure**! This is an interactive fiction game played in your terminal, where the story and choices are dynamically generated by a Large Language Model (LLM) powered by Ollama.

## Features

*   **Dynamic Storytelling:** Every playthrough can be unique as the story adapts to your choices, powered by a Large Language Model.
*   **LLM-Powered:** Utilizes Ollama and compatible models (e.g., `phi3`, `llama3`) to generate immersive narratives and engaging player options.
*   **Rich Terminal Interface:** Employs the `rich` library for a visually appealing and user-friendly experience in the terminal.
*   **Main Menu:** Start new adventures by choosing from available stories or resume a previous game.
*   **Save and Resume:** Don't lose your progress! Save your game at any choice point using the `/save` command and resume later from the main menu.
*   **Extensible Story System:**
    *   Stories are defined by `*_story_arc.yaml` files in `text_adventure_tui_lib/story_parts/`.
    *   Narrative content and initial segments are loaded from text files.
*   **Event-Driven Mechanics:**
    *   A powerful event system (see `text_adventure_tui_lib/events/`) allows for complex game logic.
    *   Events are defined in YAML and can be triggered by player actions, game state changes (flags, location, inventory), turn counts, or even LLM outputs (planned).
    *   Actions include overriding/injecting narrative, modifying LLM prompts, setting game flags, changing locations, and managing inventory.
*   **Configurable:** Set your Ollama host and model via environment variables.

## Prerequisites

Before you can play, you need to have Ollama installed and running with a suitable model.

1.  **Install Ollama:**
    *   Follow the instructions on the [official Ollama website](https://ollama.com/download).
2.  **Pull a Model:**
    *   Once Ollama is running, pull a model. This game is tested with `phi4:latest`. You can pull it using the command:
        ```bash
        ollama pull phi4:latest
        ```
    *   Ensure the Ollama server is running (usually `ollama serve` or by starting the Ollama application). By default, it runs on `http://localhost:11434`.

## Installation

1.  **Clone the Repository:**
    ```bash
    git clone https://github.com/Theblackcat98/Text-Adventure-TUI.git
    cd Text-Adventure-TUI
    ```

2.  **Create a Virtual Environment (Recommended):**
    ```bash
    python -m venv venv
    source venv/bin/activate  # On Windows: venv\Scripts\activate
    ```

3.  **Install Dependencies:**
    For playing the game, you need the core dependencies:
    ```bash
    pip install -r requirements.txt
    ```

    If you intend to use the game as an installed package (allowing you to run `text-adventure-tui` from anywhere), which also installs core dependencies:
    ```bash
    pip install .
    ```

## Development Setup

If you plan to contribute to the development of this game, follow these additional steps after cloning and setting up your virtual environment:

1.  **Install Development Dependencies:**
    This includes linters, testing tools, and build tools.
    ```bash
    pip install -r requirements-dev.txt
    ```

2.  **Install in Editable Mode:**
    This allows you to make changes to the code and test them immediately without reinstalling.
    ```bash
    pip install -e .
    ```
    After this, you can run the game using `text-adventure-tui` and your code changes will be live.

## How to Play

1.  **Ensure Ollama is Running:**
    Make sure your Ollama server is active and the model (e.g., `phi4:latest`) is available. The game defaults to connecting to `http://localhost:11434` and using the `phi4:latest` model. These can be configured via environment variables (see Configuration).

2.  **Run the Game:**
    *   If you installed the package (standard or editable mode):
        ```bash
        text-adventure-tui
        ```
    *   For development, to run directly from the repository root without installing (after `pip install -r requirements-dev.txt` or `pip install -r requirements.txt`):
        ```bash
        python -m text_adventure_tui_lib.game
        ```

3.  **Interact with the Story:**
    *   The game will start with a **Main Menu**. From here, you can:
        *   **Start a New Adventure:** Choose from a list of available stories.
        *   **Resume Game:** Load a previously saved game.
        *   **Toggle Debug Output:** Show or hide detailed debug messages in the console (useful for development or understanding game flow).
        *   **Quit Game.**
    *   During gameplay, the game will present you with a story segment.
    *   You will then be given a list of choices generated by the LLM.
    *   Enter the number corresponding to your desired choice and press Enter.
    *   **In-game Commands:** At most choice prompts, you can also type:
        *   `/save`: To save your current progress.
        *   `/quit` (or `quit`): To exit the game.
        *   `/debug state`: (If debug output is on or for general use) Displays the current game state including location, flags, inventory, stats, and event status.
        *   `/force_event <event_id>`: (Primarily for debugging) Manually triggers the actions of the specified event.

## Debugging

The game includes a few tools to help with development and debugging:

*   **Debug Output Toggle:** In the Main Menu, you can toggle the visibility of detailed debug messages printed to the console. These messages show internal game operations, such as LLM prompts, event triggering, and state changes.
*   **`/debug state` command:** When playing, type this command at a choice prompt to see a snapshot of the current game state:
    *   Current location
    *   Set game flags
    *   Player inventory
    *   Player statistics
    *   Global turn counter and turns in current location
    *   List of all loaded event IDs
    *   List of 'once' event IDs that have already been triggered
*   **`/force_event <event_id>` command:** Type this command followed by an event ID (e.g., `/force_event welcome_to_eldoria`) to manually execute all actions defined for that event, bypassing its trigger conditions. This is useful for testing specific event outcomes.

## Configuration

You can configure the Ollama host and model using environment variables:

*   `OLLAMA_HOST`: The URL of your Ollama server (e.g., `http://localhost:11434`).
*   `OLLAMA_MODEL`: The name of the Ollama model to use (e.g., `phi4:latest`, `llama3:latest`).

**Example:**
```bash
export OLLAMA_HOST="http://127.0.0.1:11434"
export OLLAMA_MODEL="llama3:latest"
python game.py
```

## Game Structure

The core components of the game are organized within the `text_adventure_tui_lib` package:

*   `game.py`: Contains the main game loop, user interface logic (using Rich), LLM interaction, main menu, and save/resume game functionality. This is the primary entry point when running `python -m text_adventure_tui_lib.game`.
*   `event_manager.py`: Manages game events, including loading event definitions from YAML files, checking trigger conditions, and executing event actions.
*   `story_parts/`: This directory holds the narrative content for different stories.
    *   `*_story_arc.yaml`: Defines the overall structure, title, starting conditions, and potentially major plot points (checkpoints) for a story.
    *   `*.txt`: Contains initial story segments or specific narrative blocks that can be loaded by the game. `01_intro.txt` is a common starting point.
*   `events/`: Contains YAML files that define specific game events.
    *   `general_events.yaml`: Events that might be common across multiple stories.
    *   `*_story_events.yaml`: Events specific to a particular story (e.g., `short_story_events.yaml`).
*   `__init__.py`: Makes `text_adventure_tui_lib` a Python package.

Key Features reflected in the structure:
*   **Main Menu**: Allows selection of different stories or resuming a saved game.
*   **Save/Resume**: Game progress can be saved (via `/save` command) and resumed later. Save files are stored in `~/.text_adventure_tui_saves/`.
*   **Event-Driven Storytelling**: Uses an `EventManager` to react to game state changes and player actions, allowing for dynamic narrative adjustments, flag setting, etc., based on YAML event definitions.

## Creating Your Own Stories

You can extend Terminal Text Adventure by creating your own stories! This involves defining a story arc and a set of events using YAML files.

### 1. Story Arc Files (`*_story_arc.yaml`)

Story arc files define the overall structure and metadata for your story. They should be placed in the `text_adventure_tui_lib/story_parts/` directory. The filename convention is `<your_story_name>_story_arc.yaml` (e.g., `my_epic_adventure_story_arc.yaml`).

**Structure:**

```yaml
title: "My Epic Adventure" # The title displayed in the main menu
starting_location: "village_green" # The initial location ID for the player
initial_story_part: "01_introduction.txt" # (Optional) Initial .txt file to load for the story's beginning. Defaults to "01_intro.txt" if not specified.
total_turns_estimate: 50 # (Optional) An estimate for story length, currently for informational purposes.

# (Optional) Define starting inventory for the player
initial_inventory:
  - "basic_sword"
  - "healing_potion"

# (Optional) Define initial player stats
initial_player_stats:
  health: 100
  mana: 50
  luck: 5

# (Optional) Checkpoints for legacy turn-based events or forced endings
checkpoints:
  - turn: 25
    prompt_injection: "A storm brews on the horizon, a sense of urgency fills the air."
  - turn: 50
    force_end_game: true
    prompt_injection: "The final confrontation has passed."
    # Customize end message based on flags
    flag_messages:
      - flag: "artifact_recovered"
        message_if_set: "With the artifact recovered, peace returns to the land. You are hailed as a hero!"
        message_if_not_set: "Though the main threat is gone, the missing artifact leaves a lingering shadow..."
      - flag: "ally_saved" # Example of another condition (the first one matched will be used for now)
        message_if_set: "And your brave ally stands by your side!"
        # If message_if_not_set is omitted for a subsequent flag, the previous final_message will persist if this flag isn't set.
```

**Key Fields:**

*   `title`: (Required) The name of your story, shown in the main menu.
*   `starting_location`: (Required) An identifier for the player's first location. This ID is used in events.
*   `initial_story_part`: (Optional) The filename of a `.txt` file in `story_parts/` that contains the introductory text for your story. If omitted, the game might default to a common intro or require an initial event to provide the narrative.
*   `total_turns_estimate`: (Optional) Informational.
*   `initial_inventory`: (Optional) A list of item IDs the player starts with.
*   `initial_player_stats`: (Optional) A dictionary of player stats and their starting values.
*   `checkpoints`: (Optional) A list of turn-based events.
    *   `turn`: The global turn number at which this checkpoint activates.
    *   `prompt_injection`: Text added to the LLM's context for that turn.
    *   `force_end_game: true`: If present, the game will end after this turn.
    *   `flag_messages`: (Optional, for `force_end_game` checkpoints) A list to customize the final message.
        *   `flag`: The game flag to check.
        *   `message_if_set`: Text to use if the flag is true.
        *   `message_if_not_set`: Text to use if the flag is false.

### 2. Event Files (`*_story_events.yaml` or `general_events.yaml`)

Event files define specific game events, their triggers, and their actions.
*   Place story-specific events in `text_adventure_tui_lib/events/<your_story_name>_story_events.yaml`. This file is automatically loaded if it matches your story arc's name.
*   Common events usable by any story can be placed in `text_adventure_tui_lib/events/general_events.yaml` (this is always loaded).

**Event Structure:**

Each event is a YAML dictionary in a list:

```yaml
- id: "unique_event_identifier" # (Required) A unique string ID for this event.
  name: "Descriptive Event Name" # (Optional) For readability/debugging.
  options:
    once: true # (Optional) If true, event only triggers once per game. Defaults to false.
    # priority: 1 # (Future) For ordering event checks. Not currently implemented.
  trigger: # (Required) Defines when the event should activate.
    mode: "AND" # (Optional) "AND" or "OR". How conditions are combined. Defaults to "AND".
    conditions: # (Required) A list of conditions.
      - type: "location"
        value: "castle_throne_room"
      - type: "flag_set"
        value: "king_is_angry"
      # ... more conditions
  actions: # (Required) A list of actions to perform when triggered.
    - type: "set_flag"
      value: "player_has_spoken_to_king"
    - type: "override_narrative"
      text: "The King slams his fist on the throne. 'Enough!'"
    # ... more actions
```

**Trigger Condition Types:**

*   `location`: Player is in a specific location.
    *   `value`: Location ID string.
    *   Example: `{ type: location, value: "secret_library" }`
*   `flag_set`: A specific game flag is true.
    *   `value`: Flag name string.
    *   Example: `{ type: flag_set, value: "knows_secret_password" }`
*   `flag_not_set`: A specific game flag is false or not set.
    *   `value`: Flag name string.
    *   Example: `{ type: flag_not_set, value: "has_met_wizard" }`
*   `player_action_keyword`: Player's input text contains specific keywords.
    *   `keywords`: A list of strings (or a single string). Case-insensitive.
    *   Example: `{ type: player_action_keyword, keywords: ["open chest", "unlock box"] }`
*   `inventory_has`: Player possesses a specific item.
    *   `value`: Item ID string.
    *   Example: `{ type: inventory_has, value: "rusty_key" }`
*   `inventory_not_has`: Player does not possess a specific item.
    *   `value`: Item ID string.
    *   Example: `{ type: inventory_not_has, value: "magic_amulet" }`
*   `turn_count_global`: Based on the global game turn counter.
    *   `value`: Integer turn number.
    *   `operator`: (Optional) "==", ">=", "<=", ">", "<". Defaults to "==".
    *   Example: `{ type: turn_count_global, value: 10, operator: ">=" }` (Triggers on turn 10 or later)
*   `turn_count_in_location`: Based on consecutive turns spent in the current location.
    *   `value`: Integer turn number.
    *   `operator`: (Optional) Defaults to "==".
    *   `location_context`: (Optional) If you need to check turns for a specific location (not fully utilized yet, typically refers to current).
    *   Example: `{ type: turn_count_in_location, value: 3 }` (Triggers if player has been in current location for 3 turns)

**Action Types:**

*   `set_flag`: Sets a game flag to true.
    *   `value`: Flag name string.
    *   Example: `{ type: set_flag, value: "door_unlocked" }`
*   `clear_flag`: Removes a game flag (sets to false/deletes).
    *   `value`: Flag name string.
    *   Example: `{ type: clear_flag, value: "is_poisoned" }`
*   `override_narrative`: Replaces the LLM's generated story for the current turn with fixed text.
    *   `text`: The narrative string. Can be multi-line.
    *   Example: `{ type: override_narrative, text: "A hidden passage creaks open!" }`
*   `inject_narrative`: Adds fixed text before or after the LLM's main narrative for the turn.
    *   `text`: The narrative string.
    *   `position`: (Optional) "pre" or "post". Defaults to "post".
    *   Example: `{ type: inject_narrative, text: "A chill wind blows through the room.", position: "pre" }`
*   `modify_prompt`: Adds specific instructions or context to the prompt sent to the LLM for story continuation.
    *   `instruction`: The string to add to the prompt.
    *   Example: `{ type: modify_prompt, instruction: "REMINDER: The player is currently injured and should act accordingly." }`
*   `add_item`: Adds an item to the player's inventory.
    *   `value`: Item ID string.
    *   Example: `{ type: add_item, value: "golden_key" }`
*   `remove_item`: Removes an item from the player's inventory.
    *   `value`: Item ID string.
    *   Example: `{ type: remove_item, value: "used_torch" }`
*   `change_location`: Moves the player to a new location.
    *   `value`: New location ID string.
    *   Example: `{ type: change_location, value: "dungeon_cell_04" }`
*   `update_stat`: Modifies a player statistic.
    *   `stat`: Name of the stat (e.g., "health", "mana").
    *   `change_by`: Integer value to add (can be negative to subtract).
    *   Example: `{ type: update_stat, stat: "health", change_by: -10 }`

### 3. Linking Story Arcs and Events

*   When a new game is started with `your_story_name_story_arc.yaml`, the game will automatically look for and load `your_story_name_story_events.yaml` from the `events/` directory, in addition to `general_events.yaml`.
*   Use location IDs, flag names, and item IDs consistently across your story arc and event files to link them logically.

By combining these YAML files, you can craft intricate narratives, puzzles, and character interactions within Terminal Text Adventure!

## Contributing

Contributions are welcome! If you have ideas for improvements, new features, or bug fixes, please feel free to:

1.  Fork the repository.
2.  Create a new branch for your feature (`git checkout -b feature/amazing-feature`).
3.  Commit your changes (`git commit -m 'Add some amazing feature'`).
4.  Push to the branch (`git push origin feature/amazing-feature`).
5.  Open a Pull Request.

## Future Enhancements

*   **Advanced Event Triggers:** Implement LLM output-based triggers (e.g., scan LLM response for keywords to trigger dynamic events).
*   **Complex Event Actions:** Expand actions (e.g., `add_item`, `remove_item`, `change_location`, `update_stat`, `present_choices`).
*   **Debugging Tools:** Add in-game debugging commands (e.g., `/debug state`, `/force_event`).
*   **Dynamic Event Loading:** Allow events to enable or disable other event files or definitions.
*   **Broader LLM Choice Support:** Test and document compatibility with a wider range of Ollama models.
*   **Enhanced Story Crafting Guide:** Detailed documentation on how to create compelling stories and events using the YAML structure.
*   **More Example Stories:** Include a wider variety of starting scenarios and story arcs.
*   **Improved LLM Interaction Management:** More sophisticated prompt engineering and handling of unexpected LLM outputs.
*   **Test Coverage:** Increase unit and integration test coverage.

## Publishing to PyPI (For Maintainers)

This section is for maintainers who want to publish the package to the Python Package Index (PyPI).

1.  **Update `setup.py`**:
    *   Ensure `version` is updated for the new release.
    *   Verify/update `author`, `author_email`, and `url`.
    *   Confirm the `LICENSE` file is correct and the license classifier in `setup.py` matches.

2.  **Install Build Tools**:
    ```bash
    pip install build twine
    ```

3.  **Clean Previous Builds (Optional)**:
    ```bash
    rm -rf build dist *.egg-info
    ```

4.  **Build Distribution Files**:
    ```bash
    python -m build
    ```
    This will create a `dist` directory with a `.tar.gz` (source distribution) and a `.whl` (wheel) file.

5.  **Check Distribution Files (Optional but Recommended)**:
    ```bash
    twine check dist/*
    ```

6.  **Upload to TestPyPI (Recommended First Step)**:
    *   Ensure you have a TestPyPI account and API token.
    *   Configure your `~/.pypirc` file or use token authentication.
    ```bash
    twine upload --repository testpypi dist/*
    ```
    *   Install from TestPyPI to verify:
    ```bash
    pip install --index-url https://test.pypi.org/simple/ --no-deps terminal-text-adventure
    ```

7.  **Upload to PyPI (Production)**:
    *   Ensure you have a PyPI account and API token.
    ```bash
    twine upload dist/*
    ```

---

Enjoy your adventure!