Skip to content

bushboy/kiroween

Repository files navigation

🌙 The Haunted Memory Trail

An interactive mystery adventure game set in a haunted African kraal under a blood moon. Players awaken in this supernatural setting and must interact with ancestral spirits, solve their riddles, and escape before midnight strikes.

Play in your browser or terminal! The game features a rich visual web interface with character sprites, atmospheric backgrounds, and ambient audio, or can be played as a classic text adventure in the terminal.

🎮 Game Overview

You find yourself in a mysterious kraal under a blood moon, surrounded by ancestral spirits who guard the path to freedom. Each spirit presents a unique challenge—solve their riddles correctly to earn your escape, or fail and remain trapped forever in the haunted realm.

The game features:

  • Visual web interface with character sprites, backgrounds, and ambient audio
  • Atmospheric narrative with rich descriptions and immersive storytelling
  • Branching choices that let you explore different locations
  • Spirit encounters with unique personalities and backstories
  • Riddle challenges that test your wisdom and intuition
  • Multiple endings based on your performance
  • Memory system that tracks your progress and decisions
  • Responsive design that works on desktop, tablet, and mobile devices

🚀 How to Play

Installation

  1. Ensure you have Node.js installed (version 14 or higher)
  2. Install dependencies:
    npm install

Playing in the Browser (Recommended)

The visual web interface provides the full immersive experience with graphics and sound:

  1. Development mode (with hot reload):

    npm run dev:web

    Then open your browser to http://localhost:5173

  2. Production build:

    npm run build:web
    npm run preview:web

    Then open your browser to http://localhost:4173

Web Features:

  • 🎨 Character sprites for each spirit (Gogo Thandi, Tokoloshe, Sangoma)
  • 🖼️ Atmospheric background images for different locations
  • 🔊 Ambient audio that changes with each scene
  • 📱 Responsive design for desktop, tablet, and mobile
  • 🎭 Visual feedback for riddle answers
  • 📊 Progress indicator showing your journey

Playing in the Terminal

For a classic text adventure experience:

  1. Build the TypeScript code:

    npm run build
  2. Start the game:

    npm start

Or run both steps together:

npm run play

Gameplay

  1. Read the narrative - Immerse yourself in the atmospheric descriptions
  2. Make choices - Click buttons (web) or select numbered options (terminal)
  3. Meet spirits - Encounter ancestral beings with unique stories
  4. Solve riddles - Answer challenges correctly to prove your worth
  5. Escape or be trapped - Your performance determines your fate

Controls

Web Interface:

  • Click buttons - Select choices and navigate
  • Audio toggle - Mute/unmute ambient sounds
  • Responsive - Works with mouse, touch, or keyboard

Terminal Interface:

  • Number keys (1-4) - Select choices from menus
  • Enter - Continue through narrative scenes
  • Ctrl+C - Exit the game at any time

📁 Project Structure

haunted-memory-trail/
├── .kiro/
│   ├── flows/              # YAML flow definitions
│   │   ├── intro_flow.yaml
│   │   ├── gogo_thandi_flow.yaml
│   │   ├── escape_ending.yaml
│   │   └── trapped_ending.yaml
│   └── personas/           # YAML persona definitions
│       └── gogo_thandi.yaml
├── assets/                 # Visual and audio assets
│   ├── images/
│   │   ├── backgrounds/    # Scene backgrounds
│   │   └── characters/     # Character sprites
│   └── audio/              # Ambient sound files
├── src/
│   ├── index.ts           # Terminal entry point
│   ├── GameEngine.ts      # Core game loop
│   ├── FlowManager.ts     # Flow/scene management
│   ├── PersonaManager.ts  # Character data management
│   ├── MemoryManager.ts   # State tracking
│   ├── PlayerInterface.ts # Terminal user interaction
│   └── web/               # Web application
│       ├── index.html     # Web entry point
│       ├── main.ts        # Web initialization
│       ├── styles.css     # Visual styling
│       ├── WebPlayerInterface.ts  # Browser UI
│       └── AssetManager.ts        # Asset loading
├── dist/                  # Compiled terminal version
├── dist-web/              # Compiled web version
├── package.json
├── tsconfig.json
├── vite.config.mjs        # Web bundler config
└── README.md

🎭 Game Architecture

The game uses a modular, YAML-driven architecture that separates content from code:

Core Components

  • Game Engine - Orchestrates the game loop and coordinates all managers
  • Flow Manager - Loads YAML flows and manages scene transitions
  • Persona Manager - Loads character data and provides spirit information
  • Memory Manager - Tracks player state using key-value pairs
  • Player Interface - Handles user interaction (terminal or web)
    • Terminal Interface - Text-based with colored formatting
    • Web Interface - Visual browser-based with graphics and audio

Web-Specific Components

  • WebPlayerInterface - Implements the browser UI with DOM manipulation
  • AssetManager - Loads and caches images and audio files
  • Vite Bundler - Packages the application for web deployment

Content Files

All game content is defined in YAML files, making it easy to add new spirits, locations, and challenges without modifying code. The same YAML files power both the terminal and web versions.

🔧 Extending the Game

Adding a New Spirit

Follow these steps to add a new ancestral spirit to the game:

1. Create the Persona File

Create a new YAML file in .kiro/personas/ (e.g., baba_themba.yaml):

persona_id: "baba_themba"
name: "Baba Themba"
archetype: "Warrior Chief"
tone: "Stern but honorable"
backstory: "Baba Themba led the kraal's warriors through countless battles. He fell defending his people from raiders, his spear still clutched in his ghostly hand."
introduction: "Halt! I am Baba Themba, guardian of this sacred ground. Prove your courage, or turn back now."
challenge:
  type: "riddle"
  question: "I am won without fighting, lost without defeat, and held without hands. What am I?"
  choices:
    - choice_id: "honor"
      text: "Honor"
      correct: true
      memory_key: "solved_baba_riddle"
      memory_value: true
    - choice_id: "peace"
      text: "Peace"
      correct: false
      memory_key: "solved_baba_riddle"
      memory_value: false
    - choice_id: "respect"
      text: "Respect"
      correct: false
      memory_key: "solved_baba_riddle"
      memory_value: false
  success_message: "Well spoken! You understand what truly matters. Pass, brave one."
  failure_message: "No... you have much to learn about what it means to be a warrior."

2. Create the Spirit Flow

Create a new YAML file in .kiro/flows/ (e.g., baba_themba_flow.yaml):

flow_id: "baba_themba_flow"
scenes:
  - scene_id: "encounter"
    type: "narrative"
    text: "{{persona.introduction}}"
    persona_id: "baba_themba"
    background_image: "warrior_spirit.jpg"
    ambient_audio: "drums.mp3"
    memory_set:
      - key: "met_baba_themba"
        value: true
    next: "challenge"
  
  - scene_id: "challenge"
    type: "challenge"
    persona_id: "baba_themba"
    challenge_type: "riddle"
    on_success: "success_response"
    on_failure: "failure_response"
  
  - scene_id: "success_response"
    type: "narrative"
    text: "{{persona.challenge.success_message}}"
    next: "continue_choice"
  
  - scene_id: "failure_response"
    type: "narrative"
    text: "{{persona.challenge.failure_message}}"
    next: "continue_choice"
  
  - scene_id: "continue_choice"
    type: "choice"
    text: "What will you do now?"
    choices:
      - choice_id: "return"
        text: "Return to the center of the kraal"
        next_flow: "entry_choice_flow"
      - choice_id: "continue"
        text: "Continue exploring"
        next_flow: "entry_choice_flow"

3. Link to Entry Flow

Update .kiro/flows/entry_choice_flow.yaml to add the new spirit as a choice option:

flow_id: "entry_choice_flow"
scenes:
  - scene_id: "main_choice"
    type: "choice"
    text: "The kraal stretches before you. Where will you go?"
    choices:
      - choice_id: "gogo_thandi"
        text: "Visit the old healer's rondavel"
        next_flow: "gogo_thandi_flow"
      - choice_id: "baba_themba"
        text: "Approach the warrior's memorial"
        next_flow: "baba_themba_flow"
      # ... other choices

Adding a New Location

To add a new explorable location:

1. Create the Location Flow

Create a new YAML file in .kiro/flows/ (e.g., sacred_tree_flow.yaml):

flow_id: "sacred_tree_flow"
scenes:
  - scene_id: "arrival"
    type: "narrative"
    text: "You approach the ancient marula tree at the edge of the kraal. Its gnarled branches reach toward the blood moon like skeletal fingers. The air here feels thick with old magic."
    background_image: "sacred_tree.jpg"
    ambient_audio: "wind_whispers.mp3"
    memory_set:
      - key: "visited_sacred_tree"
        value: true
    next: "explore_choice"
  
  - scene_id: "explore_choice"
    type: "choice"
    text: "What do you do?"
    choices:
      - choice_id: "examine"
        text: "Examine the tree more closely"
        next_scene: "examination"
      - choice_id: "return"
        text: "Return to the center of the kraal"
        next_flow: "entry_choice_flow"
  
  - scene_id: "examination"
    type: "narrative"
    text: "You notice strange carvings in the bark—symbols of protection and remembrance. This tree has witnessed generations of life and death."
    next: "explore_choice"

2. Add to Entry Choices

Update the entry choice flow to include the new location:

choices:
  - choice_id: "sacred_tree"
    text: "Walk to the sacred marula tree"
    next_flow: "sacred_tree_flow"

YAML Structure Reference

Flow File Structure

flow_id: "unique_flow_identifier"
scenes:
  - scene_id: "unique_scene_id"
    type: "narrative" | "choice" | "challenge"
    text: "Scene text content"
    
    # Optional fields
    background_image: "image.jpg"
    ambient_audio: "audio.mp3"
    persona_id: "persona_reference"
    
    # For narrative scenes
    memory_set:
      - key: "memory_key_name"
        value: true | false | "string" | number
    next: "next_scene_id" | null
    
    # For choice scenes
    choices:
      - choice_id: "choice_identifier"
        text: "Choice display text"
        next_scene: "scene_id"        # Stay in current flow
        next_flow: "flow_id"           # Transition to new flow
        conditions:                    # Optional
          key: "memory_key"
          value: true
    
    # For challenge scenes
    challenge_type: "riddle"
    on_success: "success_scene_id"
    on_failure: "failure_scene_id"

Persona File Structure

persona_id: "unique_persona_identifier"
name: "Spirit Display Name"
archetype: "Character archetype"
tone: "Dialogue tone description"
backstory: "Character background story"
introduction: "First encounter message"
challenge:
  type: "riddle"
  question: "The riddle or challenge question"
  choices:
    - choice_id: "choice_identifier"
      text: "Answer option text"
      correct: true | false
      memory_key: "solved_spirit_riddle"
      memory_value: true | false
  success_message: "Message when player succeeds"
  failure_message: "Message when player fails"

Memory Key Conventions

The game uses these memory key patterns:

  • met_<spirit_name> - Boolean tracking spirit encounters (e.g., met_gogo_thandi)
  • solved_<spirit_name>_riddle - Boolean tracking challenge results (e.g., solved_gogo_riddle)
  • visited_<location> - Boolean tracking location visits (e.g., visited_rondavel)
  • spirits_solved - Integer count of successfully solved challenges
  • game_complete - Boolean indicating game has ended

🎯 Game Endings

The game has two possible endings based on your performance:

  • Escape Ending - Solve all spirit riddles correctly to break free from the haunted kraal
  • Trapped Ending - Fail or ignore any riddle and remain trapped in the spirit realm forever

The ending is determined by checking all solved_*_riddle memory keys when the game concludes.

🛠️ Development

Building

Terminal version:

npm run build

Web version:

npm run build:web

Testing

Run the comprehensive test suite:

npm test

This runs all tests including:

  • Unit tests for core components
  • Property-based tests for correctness validation
  • Integration tests for full game flows
  • Web interface tests for UI components
  • Asset loading tests
  • YAML compatibility tests

Test coverage includes:

  • 75+ tests across 6 test suites
  • Property-based testing with 100+ iterations per property
  • Full playthrough scenarios for both endings
  • Cross-browser compatibility validation

Development Mode

Web development with hot reload:

npm run dev:web

Terminal development:

npm run play

Project Dependencies

Core:

  • yaml - YAML file parsing for content files
  • TypeScript - Type-safe development

Terminal:

  • chalk - Terminal color formatting
  • readline-sync - Synchronous user input

Web:

  • vite - Fast build tool and dev server
  • vitest - Testing framework
  • jsdom - DOM testing environment
  • fast-check - Property-based testing library

Adding Visual Assets

Place your assets in the appropriate directories:

Character sprites: assets/images/characters/

  • Format: SVG or PNG
  • Naming: {persona_id}_sprite.svg
  • Example: gogo_thandi_sprite.svg

Background images: assets/images/backgrounds/

  • Format: SVG, PNG, or JPG
  • Referenced in YAML flow files

Audio files: assets/audio/

  • Format: MP3
  • Referenced in YAML flow files

The AssetManager automatically loads and caches these files, with graceful fallbacks for missing assets.

🌐 Deployment

Web Deployment

The web version can be deployed to any static hosting service:

  1. Build the production bundle:

    npm run build:web
  2. Deploy the dist-web folder to your hosting service:

    • GitHub Pages
    • Netlify
    • Vercel
    • AWS S3
    • Any static web host

The build includes all assets and is optimized for production with:

  • Minified JavaScript and CSS
  • Optimized asset loading
  • Browser caching support
  • Responsive design for all devices

Local Preview

Preview the production build locally:

npm run preview:web

📝 License

ISC

🎨 Credits

The Haunted Memory Trail is a demonstration of YAML-driven interactive fiction, showcasing how content and code can be cleanly separated for easy extension and modification. The dual-interface design (terminal and web) demonstrates how the same game engine can power different presentation layers.

Technical Highlights:

  • YAML-driven content architecture
  • Dual interface (terminal + web) from single codebase
  • Property-based testing for correctness validation
  • Responsive web design with visual and audio assets
  • Modular component architecture

May the ancestors guide your path through the haunted kraal! 🌙

About

Fun Halloween based African themed quiz game

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages