Skip to content

Monster System Architecture

Overview

The Monster System provides a modular, scalable architecture for managing monster data in Stingbatbot. The system follows separation of concerns principles with specialized modules handling different aspects of monster management, including both core monsters and custom user-created monsters.

Architecture Components

Context Module (Stingbatbot.Monsters)

Responsibility: Public API and delegation to specialized modules

The main context module serves as the public interface for all monster operations. It uses defdelegate to route calls to appropriate submodules, providing a clean and maintainable API.

Key Functions: - create/1, update/2: Database operations - search_by_name/3: Enhanced search with server_id and user_id context - csv_row_to_attrs/1: CSV data conversion - parse_attack_string/1: Attack description parsing

Operations Module (Stingbatbot.Monsters.Operations)

Responsibility: Database operations and data transformation

This module handles all database interactions and complex data transformations, keeping the schema focused on validation.

Key Functions: - create/1: Creates monsters with data transformation - update/2: Updates monsters with data transformation - create_changeset/1: Creates changesets with data preparation - prepare_monster_attrs/1: Handles attack parsing and talent processing - talents_to_array/1: Converts talent fields to arrays

Data Transformation Pipeline: 1. Attack Parsing: Converts atk_description to structured attack data 2. Talent Processing: Consolidates talent_1 through talent_10 into arrays 3. Type Conversion: Handles data type conversions for database storage

Maps Module (Stingbatbot.Monsters.Maps)

Responsibility: Struct/map conversions for JSONB storage

This module handles the conversion between Elixir structs and plain maps for JSONB storage, including key type conversions.

Key Functions: - monster_to_map/1: Converts Monster struct to map for storage - monster_from_map/1: Converts map back to Monster struct - attack_to_map/1: Converts MonsterAttack struct to map - attack_from_map/1: Converts map to MonsterAttack struct - attacks_from_maps/1: Batch conversion of attack maps to structs

Query Module (Stingbatbot.Monsters.Query)

Responsibility: Complex database queries and search functionality

This module handles advanced query operations with optimized search algorithms, including support for custom monsters and source filtering.

Key Functions: - search_by_name/3: Enhanced search with server_id and user_id context - Source filtering support (e.g., source:CR, source:Homebrew) - Handles name conflicts between core and custom monsters - Excludes unapproved monsters from search results

Search Features: - Core Monsters: Available everywhere (server_id and user_id are nil) - Custom Monsters: Server-scoped (filtered by server_id) - Source Filtering: Filter by specific sources - Approval Status: Only approved monsters are searchable

ListBy Module (Stingbatbot.Monsters.ListBy)

Responsibility: Filtered monster listings

This module provides specialized listing functionality with various filtering options.

Key Functions: - list_by/1: Lists monsters with filtering options

TextParser Module (Stingbatbot.Monsters.TextParser)

Responsibility: Parse raw monster text into structured data

This module handles the parsing of free-form monster descriptions in Shadowdark format.

Key Functions: - parse/1: Main parsing function for monster text - Handles multi-line descriptions, complex stat blocks, and talents - Supports various monster formats and edge cases

Utility Modules

CsvParser: Converts CSV rows to monster attributes MonsterImporter: Handles CSV file processing and import workflow ParseAttack: Parses attack descriptions into structured data MonsterNaming: Generates unique monster names

Data Flow

Monster Creation Flow

  1. Input: Raw data (CSV row, user input, API call)
  2. Conversion: Data converted to attribute map
  3. Transformation: prepare_monster_attrs/1 processes:
  4. Attack descriptions → structured attack data
  5. Talent fields → consolidated arrays
  6. Type conversions and normalization
  7. Validation: Monster schema validates transformed data
  8. Storage: Data stored with JSONB for complex fields

Custom Monster Creation Flow

  1. Input: Raw text from Discord command
  2. Parsing: TextParser.parse/1 converts text to attributes
  3. Validation: Monster schema validates parsed data
  4. Storage: Monster stored as unapproved (approved_at is nil)
  5. Approval: User approves via Discord interaction
  6. Activation: Monster becomes searchable after approval

Monster Retrieval Flow

  1. Query: Database query for monster data with context
  2. Filtering: Results filtered by server_id and approval status
  3. Conversion: Maps converted back to structs via Maps module
  4. Usage: Structured data available for game mechanics

Schema Design

Monster Schema

The Monster schema focuses purely on validation, with data transformation handled externally:

schema "monsters" do
  field :name, :string
  field :ac, :integer
  field :hp, :integer
  field :atk_description, :string
  field :attacks, {:array, :map}, default: []  # JSONB storage
  field :talents, {:array, :string}, default: []  # JSONB storage
  field :server_id, :integer  # For custom monsters
  field :user_id, :integer    # For custom monsters
  field :source, :string      # Source identifier
  field :raw_text, :string    # Original text for custom monsters
  field :approved_at, :utc_datetime_usec  # Approval timestamp
  # ... other fields
end

Unique Constraints: - name and source: Allows same monster name in different sources - Core monsters have server_id and user_id as nil and source as CR

Attack Storage

Attacks are stored as JSONB maps for flexibility: - Storage: Plain maps with string keys - Usage: Converted to MonsterAttack structs when needed - Benefits: Flexible schema, easy to extend

Integration with Game Mechanics

Attack System Integration

  • Parsing: Attack descriptions parsed into MonsterAttack structs
  • Storage: Attacks stored as maps in JSONB
  • Combat: Converted back to structs for attack rolls
  • Critical Hits: Full support for critical hit mechanics

Command Integration

  • Monster Commands: Use the public API for all operations
  • Search: Integrated search functionality with source filtering
  • Import: CSV import workflow support
  • Custom Monsters: Add monster command with approval workflow

Benefits of This Architecture

Separation of Concerns

  • Schema: Pure validation logic
  • Operations: Data transformation and database operations
  • Maps: Struct/map conversions
  • Query/ListBy: Specialized query logic
  • TextParser: Custom monster text parsing

Maintainability

  • Clear APIs: Each module has a well-defined responsibility
  • Easy Testing: Modules can be tested in isolation
  • Extensibility: New functionality can be added without modifying existing code

Performance

  • Optimized Queries: Specialized query modules for complex operations
  • Efficient Storage: JSONB for complex data structures
  • Lazy Loading: Data converted only when needed
  • Context-Aware Search: Efficient filtering by server and user context

Future Considerations

Planned Enhancements

  • Advanced Search: More sophisticated search and filtering options
  • Bulk Operations: Efficient bulk import and update operations
  • Caching: Performance optimization through caching
  • Monster Sharing: Export/import custom monsters between servers

Extension Points

  • New Data Types: Easy to add new monster attributes
  • Custom Parsers: Extensible parsing for different data formats
  • Integration APIs: Clean interfaces for external integrations
  • Image Support: Potential for custom monster images