Skip to content

Stingbatbot Architecture

Overview

Stingbatbot is designed with a modular, behavior-based architecture that enables flexible extension of game mechanics across a growing number of character types. The system follows these architectural principles:

  • Behavior-based interfaces for game mechanics (SpellCasting, Attack, with StatCheck planned)
  • Implementation modules that apply modifiers based on character attributes
  • Pattern matching for specialized handling of different game scenarios
  • Database storage for character data with import validation workflow
  • Integration with Pythonx for advanced dice rolling capabilities

The architecture is specifically designed to scale to accommodate all 22+ current classes and ancestries, plus future expansion content, without requiring core code modifications.

Core Modules

Character Data Management

Database Storage System

Responsibility: Persistent storage for character data

  • Stores character data in a database
  • Provides CRUD operations for character management
  • Replaces the older file-based storage system

Key Functions: - get_all/0: Returns all characters - get_character/1: Gets a character by name - save_character/1: Saves a character to the database - delete_character/1: Removes a character from the database

Character Import System

Responsibility: Validates and imports character data from JSON

  • Processes uploaded JSON files
  • Validates data structure and required fields
  • Presents validation results to users
  • Converts valid data for database storage

Key Functions: - validate_json/1: Checks if JSON contains required structure - preview_character/1: Generates a summary of the character for user confirmation - import_character/1: Finalizes import after user confirmation

Character and Character.Parse

Responsibility: Domain model for character data and parsing

  • Transforms data into structured character representations
  • Contains all business logic related to characters
  • Provides a comprehensive data model including all character attributes
  • Tracks character bonuses, abilities, and talents
  • Separates parsing logic into the Character.Parse module

For detailed information on the Character system, see characters.md.

Character Selection System

Responsibility: Manages user character selection across different context levels

  • Provides a hierarchical selection system (channel > guild > global)
  • Allows users to select different characters at different context levels
  • Tracks character selections persistently in the database
  • Validates character ownership before selection
  • Enforces uniqueness of character names per user

For detailed information on the Character selection system, see character_selection.md.

Game Mechanics Systems

SpellCasting Behavior

Responsibility: Defines the interface for spell casting mechanics

  • Maintains a list of implementation modules
  • Handles spell casting checks and modifiers
  • Combines modifiers from various sources (class, ancestry, etc.)
  • Designed to scale to all current and future classes/ancestries

Key Components: - apply_stat_modifier/1: Applies the primary spellcasting stat (e.g., INT for Wizards) - apply_casting_modifiers/1: Applies additional modifiers from abilities - Implementation modules for all spellcasting classes and relevant ancestries

SpellCastingRoll

Responsibility: Handles dice rolling for spell casting checks

  • Takes pre-calculated SpellCasting structures
  • Builds appropriate dice expressions with modifiers
  • Supports advantage/disadvantage mechanics
  • Detects critical success/failure
  • Determines success/failure against spell DC
  • Produces structured RollResult objects

Key Functions: - roll/2: Performs the spell casting roll with options - build_casting_expression/2: Creates dice expressions

Attack Behavior

Responsibility: Defines the interface for attack mechanics

  • Maintains a list of implementation modules
  • Handles attack rolls and damage calculations
  • Applies modifiers based on weapon types and character abilities
  • Designed to scale to all current and future classes/ancestries

Key Components: - apply_attack_modifiers/1: Applies modifiers to attack and damage - Implementation modules for all weapon-using classes and ancestries - Weapon type handling (melee, ranged, versatile)

StatCheck Behavior (Planned)

Responsibility: Defines the interface for stat checks and contested checks

  • Will follow the same behavior-based pattern as SpellCasting and Attack
  • Will allow classes and ancestries to modify stat checks
  • Will support both standard and contested stat checks
  • Will integrate with the dice rolling system

Dice Rolling Integration

Responsibility: Handles dice rolls with detailed results

  • Integrates with Pythonx to use Avrae's d20 dice rolling library
  • Provides rich roll results including individual dice values
  • Supports complex dice expressions for various game mechanics

Key Components: - Interface for attack rolls - Interface for spell casting rolls - Interface for stat checks (planned) - Interface for contested stat checks (planned) - Detailed display of roll results

Class and Ancestry Implementation Modules

Responsibility: Implement behavior-specific modifiers

  • Each class (22+ current) implements relevant behaviors
  • Each ancestry implements relevant behaviors
  • Modules apply specific bonuses based on character abilities
  • Designed for easy extension as new content is published

Implementation Pattern: - Pattern matching for character attributes (class, ancestry) - Conditional logic for ability bonuses - Struct updates to apply modifiers

User Interface

CharacterFormatter

Responsibility: Presentation layer for character data

  • Takes structured character data and formats it for display
  • Provides different views of character data (summary, details, sheet)
  • Contains only presentation logic, no business logic

Key Functions: - format_summary/1: Creates a basic character summary - format_details/1: Creates a detailed character sheet - format_ability/2: Formats a specific ability

Advanced User Interface (Planned)

Responsibility: Enhanced interaction with character data

  • Provides explanations of modifier calculations
  • Allows drilling down into specific character attributes
  • Presents contextual information about character abilities

Key Features: - explain command to show detailed modifier breakdowns - Interactive elements for exploring character attributes - Context-sensitive help for game mechanics

Commands

Responsibility: Interface layer for user interaction

  • Handles user commands
  • Uses core modules to process commands and generate responses
  • Maps user input to appropriate actions

Key Commands: - sheet: Displays the currently selected character's summary - detail: Displays detailed character information - import: Initiates character import workflow - roll: Handles dice rolling requests - attack: Manages attack rolls for characters (planned) - cast: Manages spell casting rolls (planned) - check: Manages stat checks for characters (planned)

Architectural Patterns

Behavior Implementation Pattern

The system uses Elixir behaviors to define interfaces that are implemented by various modules:

  1. Behavior Definition: Core modules define callback functions
  2. Implementation List: Each behavior maintains a list of modules that implement it
  3. Module Application: When processing, each implementation module is called in sequence
  4. Modifier Accumulation: Results accumulate as they pass through each implementation

This pattern enables the system to scale efficiently to the current 22+ classes and ancestries, plus future expansions, without requiring core code changes.

# Example from SpellCasting
def apply_modifiers(spell_casting, implementations) do
  Enum.reduce(implementations, spell_casting, fn module, acc ->
    module.apply_casting_modifiers(acc)
  end)
end

Roll Modifiers Pipeline Pattern

The system employs a pipeline-based callback architecture for all dice roll mechanics:

  1. Roll Components Structure: Core dice components (count, dice, modifier) are encapsulated in a dedicated structure
  2. Pipeline Transformations: Dice roll logic flows through a series of transformation functions
  3. Callback Interface: Each character component module can modify roll mechanics through a standardized callback
  4. Context Preservation: Each transformation maintains and passes along the full context of the roll
  5. User Modifiers: Custom modifiers provided by users are applied after all other transformations
  6. Extensibility: The pipeline can be extended with new transformations without modifying existing code

This pattern is applied consistently across all roll-based mechanics (attacks, spell casting, skill checks) and provides several benefits:

  • Clear Separation: Distinguishes between static modifiers and dynamic dice mechanics
  • Composable Transformations: Multiple modifications can be applied in sequence
  • Encapsulation: Implementation details stay within specific modules
  • Testability: Each transformation can be tested in isolation
  • Maintainability: Common logic is centralized while specific logic is distributed appropriately
# Example pipeline from AttackRoll
def build_damage_modifiers(%Attack{} = attack, opts \\ %{}) do
  # Run the pipeline to get modified attack and components
  {modified_attack, _roll_type, components, options} = build_damage_roll_components(attack, opts)
    |> handle_versatile_weapon()
    |> handle_crit_damage()

  # Get the expression with any additional modifications from DiceExpression
  DiceExpression.build_expression_with_context(modified_attack, :damage, components, options)
end

This architecture creates a unified approach to game mechanics across the entire system, ensuring that features like critical hits, advantage/disadvantage, and class-specific abilities work consistently in all contexts.

Pattern Matching for Game Logic

The system uses pattern matching extensively to handle different game scenarios:

# Example from the Elf module
defp apply_farsight_attack_bonus(%{character: %{ancestry: "Elf"}, weapon: weapon} = attack) 
     when not is_nil(weapon) do
  # Process Elf-specific logic
end

# Catch-all for non-matching cases
defp apply_farsight_attack_bonus(attack), do: attack

Testing Architecture

The testing architecture follows the same principles as the rest of the system, emphasizing modularity, separation of concerns, and realistic testing:

Dependency Injection Pattern

Commands and services accept external dependencies through parameters, enabling easy test substitution:

  1. Function Parameters: Functions accept optional parameters for dependencies
  2. Default Production Implementations: Default parameter values use real implementations
  3. Test Substitution: Tests supply test implementations that maintain the same interface
  4. Consistent Interfaces: Test modules mirror the interface of real modules

This pattern allows for testing without mocking our own code, focusing mocks only on external services.

Test Helpers

The system includes specialized test implementations for external dependencies:

Message API Test Modules

  • MessageApi: Test implementation that captures sent messages for verification
  • ErrorMessageApi: Simulates message creation failures
  • MessageApiHelper: Creates customized message API test implementations

HTTP Client Test Modules

  • JSONHTTPClient: Returns configurable JSON responses
  • ErrorHTTPClient: Simulates HTTP request failures
  • StatusHTTPClient: Simulates specific HTTP status codes
  • HTTPClientHelper: Factory for creating HTTP client test implementations

Testing Approach Benefits

This architecture enables:

  • Clean Tests: No mocking of our own code
  • Realistic Testing: Test implementations mimic real behavior
  • Testable Components: Clear separation of external dependencies
  • Easy Verification: Captured outputs can be inspected directly
  • Consistent Patterns: All commands follow the same testing approach

Data Flow

Character Data Flow

  1. User uploads a character JSON file through the import command
  2. System validates the character data structure
  3. Preview is shown to the user for confirmation
  4. Upon acceptance, character is stored in the database
  5. Character data is retrieved from database when needed
  6. Character module parses and structures the data
  7. Game mechanics are applied via behavior implementations
  8. Results are formatted for display

Game Mechanics Flow

  1. Character module generates game mechanics structures:
  2. get_attacks/1

Implementation Status

  • ✅ Core character data model
  • ✅ Database storage system
  • ✅ Character import workflow
  • ✅ SpellCasting behavior system
  • ✅ Attack behavior system
  • ✅ Character.Parse class/ancestry modification system
  • ✅ Character class implementations (Wizard, Priest, and others)
  • ✅ Ancestry implementations (Elf and others) with appropriate bonuses

Data Types

The system uses several standard data types for consistency:

  • Discord IDs (guild_id, user_id, channel_id, message_id):
  • Stored as :integer in schemas
  • Stored as :bigint in the database
  • This includes all IDs from Discord's API