spatial

package module
v0.4.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 7, 2026 License: GPL-3.0 Imports: 11 Imported by: 2

README

Spatial Module

The spatial module provides 2D spatial positioning and movement capabilities for the RPG Toolkit. It supports multiple grid systems, entity placement, movement tracking, and event-driven spatial queries.

Table of Contents

Overview

The spatial module is designed to handle 2D spatial positioning for tabletop RPGs and provides:

  • Multiple Grid Systems: Square grids (D&D 5e style), hex grids, and gridless (theater-of-mind) systems
  • Entity Management: Place, move, and track entities in spatial environments
  • Multi-Room Orchestration: Connect and manage multiple rooms with typed connections
  • Event Integration: Automatic event publishing for spatial changes
  • Query System: Efficient spatial queries for game mechanics
  • Line of Sight: Calculate visibility and obstacles
  • Distance Calculation: Grid-appropriate distance calculations
  • Layout Patterns: Common spatial arrangements (towers, dungeons, towns)

Key Concepts

Position

A Position represents a 2D coordinate in space:

type Position struct {
    X float64 `json:"x"`
    Y float64 `json:"y"`
}
Grid

The Grid interface defines how spatial calculations work:

  • Square Grid: Uses D&D 5e distance rules (Chebyshev distance)
  • Hex Grid: Uses cube coordinate system for hexagonal grids
  • Gridless: Uses Euclidean distance for theater-of-mind play
Room

A Room is a spatial container that implements core.Entity and manages:

  • Entity placement and movement
  • Grid-based spatial calculations
  • Event publishing for changes
  • Line of sight and range queries
Placeable

Entities that can be placed in rooms should implement the Placeable interface:

type Placeable interface {
    core.Entity
    GetSize() int
    BlocksMovement() bool
    BlocksLineOfSight() bool
}

Quick Start

1. Basic Setup
package main

import (
    "github.com/KirkDiggler/rpg-toolkit/core"
    "github.com/KirkDiggler/rpg-toolkit/events"
    "github.com/KirkDiggler/rpg-toolkit/tools/spatial"
)

func main() {
    // Create event bus
    eventBus := events.NewBus()
    
    // Create a 20x20 square grid
    grid := spatial.NewSquareGrid(spatial.SquareGridConfig{
        Width:  20,
        Height: 20,
    })
    
    // Create a room
    room := spatial.NewBasicRoom(spatial.BasicRoomConfig{
        ID:       "dungeon-room-1",
        Type:     "dungeon",
        Grid:     grid,
        EventBus: eventBus,
    })
    
    // Setup query system
    queryHandler := spatial.NewSpatialQueryHandler()
    queryHandler.RegisterRoom(room)
    queryHandler.RegisterWithEventBus(eventBus)
    
    // Create query utilities
    queryUtils := spatial.NewQueryUtils(eventBus)
}
2. Entity Placement
// Create an entity (must implement core.Entity)
type Character struct {
    id   string
    name string
}

func (c *Character) GetID() string   { return c.id }
func (c *Character) GetType() string { return "character" }

// Create and place entity
hero := &Character{id: "hero-1", name: "Aragorn"}
position := spatial.Position{X: 10, Y: 10}

err := room.PlaceEntity(hero, position)
if err != nil {
    log.Fatal(err)
}
3. Movement and Queries
// Move entity
newPosition := spatial.Position{X: 12, Y: 10}
err := room.MoveEntity("hero-1", newPosition)

// Query entities in range
entities := room.GetEntitiesInRange(position, 5.0)

// Check line of sight
losPositions := room.GetLineOfSight(position, newPosition)
blocked := room.IsLineOfSightBlocked(position, newPosition)

Grid Systems

Square Grid (D&D 5e Style)

Best for traditional tabletop RPGs with square battle mats:

grid := spatial.NewSquareGrid(spatial.SquareGridConfig{
    Width:  20,  // 20 squares wide
    Height: 20,  // 20 squares tall
})

Features:

  • Uses D&D 5e distance rules (Chebyshev distance)
  • Diagonal movement costs the same as orthogonal
  • 8 neighbors per position
  • Integer coordinates recommended
Hex Grid

Perfect for hex-based games:

grid := spatial.NewHexGrid(spatial.HexGridConfig{
    Width:     15,
    Height:    15,
    PointyTop: true,  // false for flat-top hexes
})

Features:

  • Uses cube coordinate system
  • 6 neighbors per position
  • More natural movement patterns
  • Supports pointy-top and flat-top orientations
Gridless (Theater-of-Mind)

For narrative-focused games without strict positioning:

grid := spatial.NewGridlessRoom(spatial.GridlessConfig{
    Width:  100.0,  // Arbitrary units
    Height: 100.0,
})

Features:

  • Uses Euclidean distance
  • Allows fractional positioning
  • 8 neighbors (conceptual)
  • Flexible positioning

Room Management

Creating Rooms
room := spatial.NewBasicRoom(spatial.BasicRoomConfig{
    ID:       "unique-room-id",
    Type:     "dungeon",  // or "outdoors", "tavern", etc.
    Grid:     grid,
    EventBus: eventBus,
})
Room Operations
// Place entity
err := room.PlaceEntity(entity, position)

// Move entity
err := room.MoveEntity(entityID, newPosition)

// Remove entity
err := room.RemoveEntity(entityID)

// Check if position is occupied
occupied := room.IsPositionOccupied(position)

// Get all entities at position
entities := room.GetEntitiesAt(position)

// Get entity position
pos, exists := room.GetEntityPosition(entityID)

Multi-Room Orchestration

The spatial module includes a powerful orchestration system for managing multiple connected rooms, enabling complex multi-room environments like dungeons, towns, towers, and more.

Key Concepts
RoomOrchestrator

A RoomOrchestrator manages multiple rooms and their connections:

  • Room Management: Add, remove, and track multiple rooms
  • Connection System: Define how rooms link together
  • Entity Tracking: Track entities across all managed rooms
  • Layout Patterns: Organize rooms using common spatial arrangements
  • Event Integration: Publish events for all orchestration changes
Connection Types

The orchestrator supports different connection types for various scenarios:

// Connection types
spatial.ConnectionTypeDoor     // Standard doorway
spatial.ConnectionTypeStairs   // Vertical connections (floors)
spatial.ConnectionTypePassage  // Open corridors/hallways
spatial.ConnectionTypePortal   // Magical transport
spatial.ConnectionTypeBridge   // Spanning gaps/obstacles
spatial.ConnectionTypeTunnel   // Underground passages
Layout Patterns

Common spatial arrangements for multiple rooms:

// Layout types
spatial.LayoutTypeTower      // Vertical stacking (floors)
spatial.LayoutTypeBranching  // Hub and spoke pattern
spatial.LayoutTypeGrid       // 2D grid arrangement
spatial.LayoutTypeOrganic    // Irregular connections
Basic Orchestrator Usage
1. Creating an Orchestrator
// Create orchestrator
orchestrator := spatial.NewBasicRoomOrchestrator(spatial.BasicRoomOrchestratorConfig{
    ID:       "dungeon-orchestrator",
    Type:     "orchestrator",
    EventBus: eventBus,
    Layout:   spatial.LayoutTypeOrganic,
})
2. Adding Rooms
// Create rooms
room1 := spatial.NewBasicRoom(spatial.BasicRoomConfig{
    ID:       "entrance-hall",
    Type:     "chamber",
    Grid:     spatial.NewSquareGrid(spatial.SquareGridConfig{Width: 20, Height: 20}),
    EventBus: eventBus,
})

room2 := spatial.NewBasicRoom(spatial.BasicRoomConfig{
    ID:       "treasure-room",
    Type:     "chamber", 
    Grid:     spatial.NewSquareGrid(spatial.SquareGridConfig{Width: 15, Height: 15}),
    EventBus: eventBus,
})

// Add to orchestrator
err := orchestrator.AddRoom(room1)
err = orchestrator.AddRoom(room2)
3. Creating Connections
// Create a door connection
door := spatial.CreateDoorConnection(
    "main-door",
    "entrance-hall",        // From room
    "treasure-room",        // To room
    spatial.Position{X: 19, Y: 10}, // Exit position in entrance-hall
    spatial.Position{X: 0, Y: 7},   // Entry position in treasure-room
)

// Add connection to orchestrator
err := orchestrator.AddConnection(door)
4. Moving Entities Between Rooms
// Place entity in first room
hero := &Character{id: "hero", entityType: "character"}
err := room1.PlaceEntity(hero, spatial.Position{X: 10, Y: 10})

// Move entity to second room through connection
err = orchestrator.MoveEntityBetweenRooms(
    "hero",           // Entity ID
    "entrance-hall",  // From room
    "treasure-room",  // To room
    "main-door",      // Connection ID
)

// Check which room contains the entity
roomID, exists := orchestrator.GetEntityRoom("hero")
// roomID will be "treasure-room"
Connection Helper Functions

The module provides helper functions for creating different connection types:

Door Connections
// Bidirectional door (most common)
door := spatial.CreateDoorConnection(
    "door-1", "room-a", "room-b",
    spatial.Position{X: 10, Y: 0},  // Exit position
    spatial.Position{X: 5, Y: 19},  // Entry position
)
// Cost: 1.0, Reversible: true, Requirements: none
Stair Connections
// Stairs between floors
stairs := spatial.CreateStairsConnection(
    "stairs-up", "floor-1", "floor-2",
    spatial.Position{X: 10, Y: 10},
    spatial.Position{X: 10, Y: 10},
    true, // goingUp - adds "can_climb" requirement
)
// Cost: 2.0, Reversible: true, Requirements: ["can_climb"] if going up
Portal Connections
// Magical portal
portal := spatial.CreatePortalConnection(
    "magic-portal", "material-plane", "feywild",
    spatial.Position{X: 5, Y: 5},
    spatial.Position{X: 12, Y: 8},
    true, // bidirectional
)
// Cost: 0.5, Requirements: ["can_use_portals"]
Secret Passages
// Hidden passage
secret := spatial.CreateSecretPassageConnection(
    "secret-passage", "library", "hidden-chamber",
    spatial.Position{X: 0, Y: 10},
    spatial.Position{X: 15, Y: 10},
    []string{"found_secret", "has_key"}, // Custom requirements
)
// Cost: 1.0, Reversible: true, Requirements: custom
Layout Patterns
Tower Layout (Vertical Stacking)

Perfect for multi-floor buildings:

// Create tower floors
floors := make([]spatial.Room, 5)
for i := 0; i < 5; i++ {
    floors[i] = spatial.NewBasicRoom(spatial.BasicRoomConfig{
        ID:   fmt.Sprintf("floor-%d", i+1),
        Type: "floor",
        Grid: spatial.NewSquareGrid(spatial.SquareGridConfig{Width: 20, Height: 20}),
        EventBus: eventBus,
    })
    orchestrator.AddRoom(floors[i])
}

// Connect floors with stairs
for i := 0; i < len(floors)-1; i++ {
    stairs := spatial.CreateStairsConnection(
        fmt.Sprintf("stairs-%d-%d", i+1, i+2),
        floors[i].GetID(),
        floors[i+1].GetID(),
        spatial.Position{X: 10, Y: 10},
        spatial.Position{X: 10, Y: 10},
        true, // going up
    )
    orchestrator.AddConnection(stairs)
}

// Set tower layout
orchestrator.SetLayout(spatial.LayoutTypeTower)
Branching Layout (Hub and Spoke)

Great for dungeons with a central hub:

// Create central hub
hub := spatial.NewBasicRoom(spatial.BasicRoomConfig{
    ID:   "central-hub",
    Type: "chamber",
    Grid: spatial.NewSquareGrid(spatial.SquareGridConfig{Width: 30, Height: 30}),
    EventBus: eventBus,
})
orchestrator.AddRoom(hub)

// Create branching rooms
branches := []string{"north-wing", "south-wing", "east-wing", "west-wing"}
positions := []spatial.Position{
    {X: 15, Y: 0},   // North exit
    {X: 15, Y: 29},  // South exit
    {X: 29, Y: 15},  // East exit
    {X: 0, Y: 15},   // West exit
}

for i, branchID := range branches {
    // Create branch room
    branch := spatial.NewBasicRoom(spatial.BasicRoomConfig{
        ID:   branchID,
        Type: "chamber",
        Grid: spatial.NewSquareGrid(spatial.SquareGridConfig{Width: 20, Height: 20}),
        EventBus: eventBus,
    })
    orchestrator.AddRoom(branch)
    
    // Connect to hub
    door := spatial.CreateDoorConnection(
        fmt.Sprintf("door-to-%s", branchID),
        "central-hub",
        branchID,
        positions[i],
        spatial.Position{X: 10, Y: 10}, // Center of branch room
    )
    orchestrator.AddConnection(door)
}

orchestrator.SetLayout(spatial.LayoutTypeBranching)
Advanced Features
Pathfinding Between Rooms
// Find path between rooms
path, err := orchestrator.FindPath("entrance-hall", "treasure-room", hero)
if err != nil {
    log.Fatal(err)
}

// path contains room IDs in order: ["entrance-hall", "treasure-room"]
fmt.Printf("Path: %v\n", path)
Entity Tracking
// Track entity location
roomID, exists := orchestrator.GetEntityRoom("hero")
if exists {
    fmt.Printf("Hero is in room: %s\n", roomID)
}

// Get all entities in orchestrator
allRooms := orchestrator.GetAllRooms()
for roomID, room := range allRooms {
    entities := room.GetAllEntities()
    fmt.Printf("Room %s has %d entities\n", roomID, len(entities))
}
Connection Management
// Get all connections for a room
connections := orchestrator.GetRoomConnections("entrance-hall")
for _, conn := range connections {
    fmt.Printf("Connection: %s (%s)\n", conn.GetID(), conn.GetConnectionType())
}

// Check if entity can move through connection
canMove := orchestrator.CanMoveEntityBetweenRooms("hero", "room-a", "room-b", "door-1")
Layout Metrics
// Get layout information
orchestrator.SetLayout(spatial.LayoutTypeGrid)

// Layout metrics are available through events
eventBus.SubscribeFunc(spatial.EventLayoutChanged, 0, func(ctx context.Context, event events.Event) error {
    metrics, _ := event.Context().Get("metrics")
    layoutMetrics := metrics.(spatial.LayoutMetrics)
    
    fmt.Printf("Layout: %s\n", layoutMetrics.LayoutType)
    fmt.Printf("Rooms: %d\n", layoutMetrics.TotalRooms)
    fmt.Printf("Connections: %d\n", layoutMetrics.TotalConnections)
    fmt.Printf("Connectivity: %.2f\n", layoutMetrics.Connectivity)
    
    return nil
})
Event System Integration

The orchestrator publishes events for all operations:

// Subscribe to orchestrator events
eventBus.SubscribeFunc(spatial.EventRoomAdded, 0, func(ctx context.Context, event events.Event) error {
    orchestratorID, _ := event.Context().Get("orchestrator_id")
    room, _ := event.Context().Get("room")
    fmt.Printf("Room added to orchestrator %s\n", orchestratorID)
    return nil
})

eventBus.SubscribeFunc(spatial.EventConnectionAdded, 0, func(ctx context.Context, event events.Event) error {
    connection, _ := event.Context().Get("connection")
    conn := connection.(spatial.Connection)
    fmt.Printf("Connection added: %s -> %s\n", conn.GetFromRoom(), conn.GetToRoom())
    return nil
})

eventBus.SubscribeFunc(spatial.EventEntityTransitionBegan, 0, func(ctx context.Context, event events.Event) error {
    transition, _ := event.Context().Get("transition")
    fmt.Printf("Entity transition started\n")
    return nil
})
Complete Multi-Room Example
func CreateDungeonExample() {
    // Setup
    eventBus := events.NewBus()
    orchestrator := spatial.NewBasicRoomOrchestrator(spatial.BasicRoomOrchestratorConfig{
        ID:       "dungeon-orchestrator",
        Type:     "orchestrator",
        EventBus: eventBus,
        Layout:   spatial.LayoutTypeBranching,
    })
    
    // Create rooms
    entrance := spatial.NewBasicRoom(spatial.BasicRoomConfig{
        ID:       "entrance",
        Type:     "chamber",
        Grid:     spatial.NewSquareGrid(spatial.SquareGridConfig{Width: 20, Height: 20}),
        EventBus: eventBus,
    })
    
    corridor := spatial.NewBasicRoom(spatial.BasicRoomConfig{
        ID:       "corridor",
        Type:     "hallway",
        Grid:     spatial.NewSquareGrid(spatial.SquareGridConfig{Width: 30, Height: 10}),
        EventBus: eventBus,
    })
    
    treasureRoom := spatial.NewBasicRoom(spatial.BasicRoomConfig{
        ID:       "treasure",
        Type:     "chamber",
        Grid:     spatial.NewSquareGrid(spatial.SquareGridConfig{Width: 15, Height: 15}),
        EventBus: eventBus,
    })
    
    // Add rooms to orchestrator
    orchestrator.AddRoom(entrance)
    orchestrator.AddRoom(corridor)
    orchestrator.AddRoom(treasureRoom)
    
    // Create connections
    door1 := spatial.CreateDoorConnection(
        "entrance-to-corridor",
        "entrance", "corridor",
        spatial.Position{X: 19, Y: 10},
        spatial.Position{X: 0, Y: 5},
    )
    
    door2 := spatial.CreateDoorConnection(
        "corridor-to-treasure",
        "corridor", "treasure",
        spatial.Position{X: 29, Y: 5},
        spatial.Position{X: 0, Y: 7},
    )
    
    orchestrator.AddConnection(door1)
    orchestrator.AddConnection(door2)
    
    // Place entities
    hero := &Character{id: "hero", entityType: "character"}
    monster := &Character{id: "orc", entityType: "monster"}
    
    entrance.PlaceEntity(hero, spatial.Position{X: 5, Y: 5})
    treasureRoom.PlaceEntity(monster, spatial.Position{X: 10, Y: 10})
    
    // Move hero through dungeon
    orchestrator.MoveEntityBetweenRooms("hero", "entrance", "corridor", "entrance-to-corridor")
    orchestrator.MoveEntityBetweenRooms("hero", "corridor", "treasure", "corridor-to-treasure")
    
    // Hero is now in treasure room with the monster
    heroRoom, _ := orchestrator.GetEntityRoom("hero")
    fmt.Printf("Hero is in: %s\n", heroRoom) // "treasure"
}

Entity Placement

Implementing Placeable

For entities that need spatial properties:

type Monster struct {
    id   string
    size int
    solid bool
}

func (m *Monster) GetID() string              { return m.id }
func (m *Monster) GetType() string            { return "monster" }
func (m *Monster) GetSize() int               { return m.size }
func (m *Monster) BlocksMovement() bool       { return m.solid }
func (m *Monster) BlocksLineOfSight() bool    { return m.solid }
Placement Rules
  • Entities cannot be placed on positions that would conflict with blocking entities
  • The same entity can be moved to different positions
  • Position validity depends on the grid system
  • Events are automatically published for placement changes

Event System Integration

The spatial module publishes events for all spatial changes:

Event Types
// Entity events
spatial.EventEntityPlaced   // "spatial.entity.placed"
spatial.EventEntityMoved    // "spatial.entity.moved"
spatial.EventEntityRemoved  // "spatial.entity.removed"

// Room events
spatial.EventRoomCreated    // "spatial.room.created"

// Query events
spatial.EventQueryPositionsInRange // "spatial.query.positions_in_range"
spatial.EventQueryEntitiesInRange  // "spatial.query.entities_in_range"
spatial.EventQueryLineOfSight      // "spatial.query.line_of_sight"
spatial.EventQueryMovement         // "spatial.query.movement"
spatial.EventQueryPlacement        // "spatial.query.placement"
Event Listening
// Listen for entity placement
eventBus.SubscribeFunc(spatial.EventEntityPlaced, 0, func(ctx context.Context, event events.Event) error {
    entity := event.Data().(core.Entity)
    position, _ := event.Context().Get("position")
    roomID, _ := event.Context().Get("room_id")
    
    fmt.Printf("Entity %s placed at %v in room %s\n", entity.GetID(), position, roomID)
    return nil
})

Query System

The spatial module provides two ways to perform spatial queries:

Direct Room Queries
// Get entities within range
entities := room.GetEntitiesInRange(center, radius)

// Get positions within range
positions := room.GetPositionsInRange(center, radius)

// Line of sight
losPositions := room.GetLineOfSight(from, to)
blocked := room.IsLineOfSightBlocked(from, to)
Event-Based Queries

For complex scenarios or when you need to query across multiple rooms:

queryUtils := spatial.NewQueryUtils(eventBus)

// Query entities in range with filtering
filter := spatial.CreateCharacterFilter()  // Only characters
entities, err := queryUtils.QueryEntitiesInRange(ctx, center, radius, roomID, filter)

// Query movement validity
valid, path, distance, err := queryUtils.QueryMovement(ctx, entity, from, to, roomID)

// Query line of sight
positions, blocked, err := queryUtils.QueryLineOfSight(ctx, from, to, roomID)
Entity Filters

Built-in filters for common queries:

// Pre-built filters
characterFilter := spatial.CreateCharacterFilter()
monsterFilter := spatial.CreateMonsterFilter()
combatantFilter := spatial.CreateCombatantFilter()  // Characters + monsters

// Custom filters
filter := spatial.NewSimpleEntityFilter().
    WithEntityTypes("character", "npc").
    WithExcludeIDs("hero-1")

API Reference

Core Interfaces
Grid Interface
type Grid interface {
    GetShape() GridShape
    IsValidPosition(pos Position) bool
    GetDimensions() Dimensions
    Distance(from, to Position) float64
    GetNeighbors(pos Position) []Position
    IsAdjacent(pos1, pos2 Position) bool
    GetLineOfSight(from, to Position) []Position
    GetPositionsInRange(center Position, radius float64) []Position
}
Room Interface
type Room interface {
    core.Entity
    GetGrid() Grid
    PlaceEntity(entity core.Entity, pos Position) error
    MoveEntity(entityID string, newPos Position) error
    RemoveEntity(entityID string) error
    GetEntitiesAt(pos Position) []core.Entity
    GetEntityPosition(entityID string) (Position, bool)
    GetAllEntities() map[string]core.Entity
    GetEntitiesInRange(center Position, radius float64) []core.Entity
    IsPositionOccupied(pos Position) bool
    CanPlaceEntity(entity core.Entity, pos Position) bool
    GetPositionsInRange(center Position, radius float64) []Position
    GetLineOfSight(from, to Position) []Position
    IsLineOfSightBlocked(from, to Position) bool
}
RoomOrchestrator Interface
type RoomOrchestrator interface {
    core.Entity
    EventBusIntegration
    
    // Room management
    AddRoom(room Room) error
    RemoveRoom(roomID string) error
    GetRoom(roomID string) (Room, bool)
    GetAllRooms() map[string]Room
    
    // Connection management
    AddConnection(connection Connection) error
    RemoveConnection(connectionID string) error
    GetConnection(connectionID string) (Connection, bool)
    GetRoomConnections(roomID string) []Connection
    GetAllConnections() map[string]Connection
    
    // Entity movement
    MoveEntityBetweenRooms(entityID, fromRoom, toRoom, connectionID string) error
    CanMoveEntityBetweenRooms(entityID, fromRoom, toRoom, connectionID string) bool
    GetEntityRoom(entityID string) (string, bool)
    
    // Pathfinding
    FindPath(fromRoom, toRoom string, entity core.Entity) ([]string, error)
    
    // Layout management
    GetLayout() LayoutType
    SetLayout(layout LayoutType) error
}
Connection Interface
type Connection interface {
    core.Entity
    
    GetConnectionType() ConnectionType
    GetFromRoom() string
    GetToRoom() string
    GetFromPosition() Position
    GetToPosition() Position
    IsPassable(entity core.Entity) bool
    GetTraversalCost(entity core.Entity) float64
    IsReversible() bool
    GetRequirements() []string
}
Grid Constructors
// Square grid
func NewSquareGrid(config SquareGridConfig) *SquareGrid

// Hex grid
func NewHexGrid(config HexGridConfig) *HexGrid

// Gridless room
func NewGridlessRoom(config GridlessConfig) *GridlessRoom
Room Constructor
func NewBasicRoom(config BasicRoomConfig) *BasicRoom
Query System
// Query handler
func NewSpatialQueryHandler() *SpatialQueryHandler

// Query utilities
func NewQueryUtils(eventBus events.EventBus) *QueryUtils

Examples

Complete Combat Scenario
package main

import (
    "context"
    "fmt"
    "log"
    
    "github.com/KirkDiggler/rpg-toolkit/core"
    "github.com/KirkDiggler/rpg-toolkit/events"
    "github.com/KirkDiggler/rpg-toolkit/tools/spatial"
)

type Combatant struct {
    id        string
    name      string
    entityType string
    size      int
    blocking  bool
}

func (c *Combatant) GetID() string              { return c.id }
func (c *Combatant) GetType() string            { return c.entityType }
func (c *Combatant) GetSize() int               { return c.size }
func (c *Combatant) BlocksMovement() bool       { return c.blocking }
func (c *Combatant) BlocksLineOfSight() bool    { return c.blocking }

func main() {
    // Setup
    eventBus := events.NewBus()
    
    grid := spatial.NewSquareGrid(spatial.SquareGridConfig{
        Width:  20,
        Height: 20,
    })
    
    room := spatial.NewBasicRoom(spatial.BasicRoomConfig{
        ID:       "combat-room",
        Type:     "dungeon",
        Grid:     grid,
        EventBus: eventBus,
    })
    
    queryHandler := spatial.NewSpatialQueryHandler()
    queryHandler.RegisterRoom(room)
    queryHandler.RegisterWithEventBus(eventBus)
    
    queryUtils := spatial.NewQueryUtils(eventBus)
    
    // Create combatants
    hero := &Combatant{
        id:        "hero",
        name:      "Hero",
        entityType: "character",
        size:      1,
        blocking:  true,
    }
    
    orc := &Combatant{
        id:        "orc",
        name:      "Orc",
        entityType: "monster",
        size:      1,
        blocking:  true,
    }
    
    goblin := &Combatant{
        id:        "goblin",
        name:      "Goblin",
        entityType: "monster",
        size:      1,
        blocking:  true,
    }
    
    // Place entities
    room.PlaceEntity(hero, spatial.Position{X: 5, Y: 5})
    room.PlaceEntity(orc, spatial.Position{X: 8, Y: 8})
    room.PlaceEntity(goblin, spatial.Position{X: 12, Y: 6})
    
    // Query nearby enemies
    ctx := context.Background()
    enemyFilter := spatial.CreateMonsterFilter()
    
    nearbyEnemies, err := queryUtils.QueryEntitiesInRange(
        ctx, 
        spatial.Position{X: 5, Y: 5}, // Hero's position
        10.0,                        // 10 unit range
        "combat-room",
        enemyFilter,
    )
    
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Printf("Enemies within 10 units of hero: %d\n", len(nearbyEnemies))
    
    // Check line of sight to orc
    losPositions, blocked, err := queryUtils.QueryLineOfSight(
        ctx,
        spatial.Position{X: 5, Y: 5},  // Hero
        spatial.Position{X: 8, Y: 8},  // Orc
        "combat-room",
    )
    
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Printf("Line of sight blocked: %v\n", blocked)
    fmt.Printf("LOS path length: %d\n", len(losPositions))
    
    // Validate movement
    valid, path, distance, err := queryUtils.QueryMovement(
        ctx,
        hero,
        spatial.Position{X: 5, Y: 5},  // From
        spatial.Position{X: 7, Y: 7},  // To
        "combat-room",
    )
    
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Printf("Movement valid: %v, distance: %.2f\n", valid, distance)
    fmt.Printf("Path length: %d\n", len(path))
    
    // Move hero if valid
    if valid {
        err = room.MoveEntity("hero", spatial.Position{X: 7, Y: 7})
        if err != nil {
            log.Fatal(err)
        }
        fmt.Println("Hero moved successfully!")
    }
}
Grid System Comparison
func compareGridSystems() {
    // Same positions for comparison
    from := spatial.Position{X: 2, Y: 2}
    to := spatial.Position{X: 6, Y: 6}
    
    // Square grid
    squareGrid := spatial.NewSquareGrid(spatial.SquareGridConfig{
        Width: 10, Height: 10,
    })
    
    // Hex grid
    hexGrid := spatial.NewHexGrid(spatial.HexGridConfig{
        Width: 10, Height: 10, PointyTop: true,
    })
    
    // Gridless
    gridlessRoom := spatial.NewGridlessRoom(spatial.GridlessConfig{
        Width: 10, Height: 10,
    })
    
    // Compare distances
    fmt.Printf("Distance from %v to %v:\n", from, to)
    fmt.Printf("Square Grid: %.2f\n", squareGrid.Distance(from, to))
    fmt.Printf("Hex Grid: %.2f\n", hexGrid.Distance(from, to))
    fmt.Printf("Gridless: %.2f\n", gridlessRoom.Distance(from, to))
    
    // Compare neighbors
    pos := spatial.Position{X: 5, Y: 5}
    fmt.Printf("\nNeighbors of %v:\n", pos)
    fmt.Printf("Square Grid: %d\n", len(squareGrid.GetNeighbors(pos)))
    fmt.Printf("Hex Grid: %d\n", len(hexGrid.GetNeighbors(pos)))
    fmt.Printf("Gridless: %d\n", len(gridlessRoom.GetNeighbors(pos)))
}

API Reference

Core Interfaces
Grid Interface
type Grid interface {
    GetShape() GridShape
    IsValidPosition(pos Position) bool
    GetDimensions() Dimensions
    Distance(from, to Position) float64
    GetNeighbors(pos Position) []Position
    IsAdjacent(pos1, pos2 Position) bool
    GetLineOfSight(from, to Position) []Position
    GetPositionsInRange(center Position, radius float64) []Position
}
Room Interface
type Room interface {
    core.Entity
    GetGrid() Grid
    PlaceEntity(entity core.Entity, pos Position) error
    MoveEntity(entityID string, newPos Position) error
    RemoveEntity(entityID string) error
    GetEntitiesAt(pos Position) []core.Entity
    GetEntityPosition(entityID string) (Position, bool)
    GetAllEntities() map[string]core.Entity
    GetEntitiesInRange(center Position, radius float64) []core.Entity
    IsPositionOccupied(pos Position) bool
    CanPlaceEntity(entity core.Entity, pos Position) bool
    GetPositionsInRange(center Position, radius float64) []Position
    GetLineOfSight(from, to Position) []Position
    IsLineOfSightBlocked(from, to Position) bool
}
RoomOrchestrator Interface
type RoomOrchestrator interface {
    core.Entity
    EventBusIntegration
    
    // Room management
    AddRoom(room Room) error
    RemoveRoom(roomID string) error
    GetRoom(roomID string) (Room, bool)
    GetAllRooms() map[string]Room
    
    // Connection management
    AddConnection(connection Connection) error
    RemoveConnection(connectionID string) error
    GetConnection(connectionID string) (Connection, bool)
    GetRoomConnections(roomID string) []Connection
    GetAllConnections() map[string]Connection
    
    // Entity movement
    MoveEntityBetweenRooms(entityID, fromRoom, toRoom, connectionID string) error
    CanMoveEntityBetweenRooms(entityID, fromRoom, toRoom, connectionID string) bool
    GetEntityRoom(entityID string) (string, bool)
    
    // Pathfinding
    FindPath(fromRoom, toRoom string, entity core.Entity) ([]string, error)
    
    // Layout management
    GetLayout() LayoutType
    SetLayout(layout LayoutType) error
}
Connection Interface
type Connection interface {
    core.Entity
    
    GetConnectionType() ConnectionType
    GetFromRoom() string
    GetToRoom() string
    GetFromPosition() Position
    GetToPosition() Position
    IsPassable(entity core.Entity) bool
    GetTraversalCost(entity core.Entity) float64
    IsReversible() bool
    GetRequirements() []string
}
Constructors
Grid Constructors
// Square grid
func NewSquareGrid(config SquareGridConfig) *SquareGrid

// Hex grid
func NewHexGrid(config HexGridConfig) *HexGrid

// Gridless room
func NewGridlessRoom(config GridlessConfig) *GridlessRoom
Room Constructor
func NewBasicRoom(config BasicRoomConfig) *BasicRoom
Orchestrator Constructor
func NewBasicRoomOrchestrator(config BasicRoomOrchestratorConfig) *BasicRoomOrchestrator
Connection Constructor
func NewBasicConnection(config BasicConnectionConfig) *BasicConnection
Connection Helper Functions

The module provides helper functions for creating common connection types:

// Door connection (bidirectional, cost 1.0)
func CreateDoorConnection(id, fromRoom, toRoom string, fromPos, toPos Position) *BasicConnection

// Stair connection (bidirectional, cost 2.0, may have climb requirement)
func CreateStairsConnection(id, fromRoom, toRoom string, fromPos, toPos Position, goingUp bool) *BasicConnection

// Secret passage (bidirectional, cost 1.0, requires discovery)
func CreateSecretPassageConnection(id, fromRoom, toRoom string, fromPos, toPos Position, requirements []string) *BasicConnection

// Portal connection (configurable direction, cost 0.5, requires portal use)
func CreatePortalConnection(id, fromRoom, toRoom string, fromPos, toPos Position, bidirectional bool) *BasicConnection

// Bridge connection (bidirectional, cost 1.0)
func CreateBridgeConnection(id, fromRoom, toRoom string, fromPos, toPos Position) *BasicConnection

// Tunnel connection (bidirectional, cost 1.5)
func CreateTunnelConnection(id, fromRoom, toRoom string, fromPos, toPos Position) *BasicConnection
Query System
// Query handler
func NewSpatialQueryHandler() *SpatialQueryHandler

// Query utilities
func NewQueryUtils(eventBus events.EventBus) *QueryUtils
Event Constants
Single Room Events
const (
    EventEntityPlaced   = "spatial.entity.placed"
    EventEntityMoved    = "spatial.entity.moved"
    EventEntityRemoved  = "spatial.entity.removed"
    EventRoomCreated    = "spatial.room.created"
)
Multi-Room Orchestration Events
const (
    EventRoomAdded             = "spatial.orchestrator.room_added"
    EventRoomRemoved           = "spatial.orchestrator.room_removed"
    EventConnectionAdded       = "spatial.orchestrator.connection_added"
    EventConnectionRemoved     = "spatial.orchestrator.connection_removed"
    EventEntityTransitionBegan = "spatial.orchestrator.entity_transition_began"
    EventEntityTransitionEnded = "spatial.orchestrator.entity_transition_ended"
    EventLayoutChanged         = "spatial.orchestrator.layout_changed"
)
Query Events
const (
    EventQueryPositionsInRange = "spatial.query.positions_in_range"
    EventQueryEntitiesInRange  = "spatial.query.entities_in_range"
    EventQueryLineOfSight      = "spatial.query.line_of_sight"
    EventQueryMovement         = "spatial.query.movement"
    EventQueryPlacement        = "spatial.query.placement"
)
Types and Constants
Connection Types
const (
    ConnectionTypeDoor    ConnectionType = "door"
    ConnectionTypeStairs  ConnectionType = "stairs"
    ConnectionTypePassage ConnectionType = "passage"
    ConnectionTypePortal  ConnectionType = "portal"
    ConnectionTypeBridge  ConnectionType = "bridge"
    ConnectionTypeTunnel  ConnectionType = "tunnel"
)
Layout Types
const (
    LayoutTypeTower     LayoutType = "tower"
    LayoutTypeBranching LayoutType = "branching"
    LayoutTypeGrid      LayoutType = "grid"
    LayoutTypeOrganic   LayoutType = "organic"
)

Testing

The spatial module includes comprehensive tests. To run them:

go test ./...
Test Structure
  • *_test.go - Unit tests for each grid system
  • room_test.go - Room functionality tests
  • query_handler_test.go - Query system tests
  • examples_test.go - Integration examples and usage patterns
Mock Entities

For testing, use the provided mock entity pattern:

type MockEntity struct {
    id       string
    entityType string
    size     int
    blocksMovement bool
    blocksLOS  bool
}

func (m *MockEntity) GetID() string              { return m.id }
func (m *MockEntity) GetType() string            { return m.entityType }
func (m *MockEntity) GetSize() int               { return m.size }
func (m *MockEntity) BlocksMovement() bool       { return m.blocksMovement }
func (m *MockEntity) BlocksLineOfSight() bool    { return m.blocksLOS }

Advanced Usage Patterns

Error Handling and Validation
// Always check for errors when working with orchestrators
orchestrator := spatial.NewBasicRoomOrchestrator(config)

// Validate room addition
if err := orchestrator.AddRoom(room); err != nil {
    log.Printf("Failed to add room: %v", err)
    return err
}

// Validate connection requirements
if !orchestrator.CanMoveEntityBetweenRooms(entityID, fromRoom, toRoom, connectionID) {
    log.Printf("Entity %s cannot move through connection %s", entityID, connectionID)
    return errors.New("movement blocked")
}

// Safe entity movement with rollback
if err := orchestrator.MoveEntityBetweenRooms(entityID, fromRoom, toRoom, connectionID); err != nil {
    log.Printf("Movement failed: %v", err)
    // Handle failure (entity remains in original room)
}
Event-Driven Game Logic
// Set up event handlers for game mechanics
eventBus.SubscribeFunc(spatial.EventEntityTransitionBegan, 0, func(ctx context.Context, event events.Event) error {
    // Handle entity entering new room
    transition := event.Context().Get("transition").(spatial.Transition)
    
    // Trigger room-specific events (traps, encounters, etc.)
    return triggerRoomEvents(transition.GetToRoom(), transition.GetEntity())
})

eventBus.SubscribeFunc(spatial.EventConnectionAdded, 0, func(ctx context.Context, event events.Event) error {
    // Update minimap or UI when new connections are discovered
    connection := event.Context().Get("connection").(spatial.Connection)
    return updateGameMap(connection)
})
Dynamic Connection Management
// Create locked door that can be unlocked
door := spatial.CreateDoorConnection("treasure-door", "hallway", "treasure-room", pos1, pos2)
door.SetPassable(false) // Initially locked
door.AddRequirement("has_key")
orchestrator.AddConnection(door)

// Unlock door when key is found
func unlockDoor(orchestrator spatial.RoomOrchestrator, connectionID string) error {
    if conn, exists := orchestrator.GetConnection(connectionID); exists {
        if basicConn, ok := conn.(*spatial.BasicConnection); ok {
            basicConn.SetPassable(true)
            basicConn.RemoveRequirement("has_key")
            return nil
        }
    }
    return errors.New("connection not found")
}
Performance Optimization
// For large orchestrators, consider batching operations
func addMultipleRooms(orchestrator spatial.RoomOrchestrator, rooms []spatial.Room) error {
    // Add rooms in batch to reduce event overhead
    for _, room := range rooms {
        if err := orchestrator.AddRoom(room); err != nil {
            return fmt.Errorf("failed to add room %s: %w", room.GetID(), err)
        }
    }
    return nil
}

// Use pathfinding sparingly for large orchestrators
func findOptimalPath(orchestrator spatial.RoomOrchestrator, entity core.Entity, fromRoom, toRoom string) ([]string, error) {
    // Cache paths for frequently used routes
    cacheKey := fmt.Sprintf("%s-%s-%s", entity.GetType(), fromRoom, toRoom)
    if cachedPath, exists := pathCache[cacheKey]; exists {
        return cachedPath, nil
    }
    
    path, err := orchestrator.FindPath(fromRoom, toRoom, entity)
    if err == nil {
        pathCache[cacheKey] = path
    }
    return path, err
}
Layout-Specific Patterns
// Tower layout - vertical progression
func createTowerDungeon(orchestrator spatial.RoomOrchestrator, floors int) {
    orchestrator.SetLayout(spatial.LayoutTypeTower)
    
    for i := 0; i < floors; i++ {
        // Create floor room
        floor := createFloorRoom(i)
        orchestrator.AddRoom(floor)
        
        // Connect to previous floor
        if i > 0 {
            stairs := spatial.CreateStairsConnection(
                fmt.Sprintf("stairs-%d", i),
                fmt.Sprintf("floor-%d", i-1),
                fmt.Sprintf("floor-%d", i),
                spatial.Position{X: 10, Y: 10},
                spatial.Position{X: 10, Y: 10},
                true, // going up
            )
            orchestrator.AddConnection(stairs)
        }
    }
}

// Branching layout - hub and spoke
func createBranchingDungeon(orchestrator spatial.RoomOrchestrator, hubRoom spatial.Room, branches []spatial.Room) {
    orchestrator.SetLayout(spatial.LayoutTypeBranching)
    orchestrator.AddRoom(hubRoom)
    
    for i, branch := range branches {
        orchestrator.AddRoom(branch)
        
        // Connect each branch to hub
        door := spatial.CreateDoorConnection(
            fmt.Sprintf("hub-to-branch-%d", i),
            hubRoom.GetID(),
            branch.GetID(),
            getHubExitPosition(i),
            getBranchEntryPosition(),
        )
        orchestrator.AddConnection(door)
    }
}
Testing Orchestrator Behavior
func TestOrchestratorBehavior(t *testing.T) {
    // Setup
    eventBus := events.NewBus()
    orchestrator := spatial.NewBasicRoomOrchestrator(spatial.BasicRoomOrchestratorConfig{
        ID:       "test-orchestrator",
        EventBus: eventBus,
    })
    
    // Create test scenario
    room1 := createTestRoom("room1")
    room2 := createTestRoom("room2")
    orchestrator.AddRoom(room1)
    orchestrator.AddRoom(room2)
    
    door := spatial.CreateDoorConnection("door1", "room1", "room2", pos1, pos2)
    orchestrator.AddConnection(door)
    
    // Test entity movement
    entity := createTestEntity("hero")
    room1.PlaceEntity(entity, spatial.Position{X: 5, Y: 5})
    
    // Verify initial state
    roomID, exists := orchestrator.GetEntityRoom("hero")
    assert.True(t, exists)
    assert.Equal(t, "room1", roomID)
    
    // Test movement
    err := orchestrator.MoveEntityBetweenRooms("hero", "room1", "room2", "door1")
    assert.NoError(t, err)
    
    // Verify final state
    roomID, exists = orchestrator.GetEntityRoom("hero")
    assert.True(t, exists)
    assert.Equal(t, "room2", roomID)
    
    // Test pathfinding
    path, err := orchestrator.FindPath("room1", "room2", entity)
    assert.NoError(t, err)
    assert.Equal(t, []string{"room1", "room2"}, path)
}

Integration with Other Modules

The spatial module integrates seamlessly with other RPG Toolkit modules:

  • Events: Automatic event publishing for spatial changes
  • Core: Uses core.Entity for type safety
  • Conditions: Spatial conditions (e.g., "within 30 feet")
  • Spells: Area of effect calculations
  • Combat: Movement and positioning
  • Resources: Movement costs and ability usage
  • Mechanics: Integration with game rule systems

Performance Considerations

  • Query Caching: The query system includes built-in caching
  • Event Throttling: Consider throttling high-frequency movement events
  • Large Grids: For very large areas, consider partitioning rooms
  • Memory Usage: Remove entities from rooms when no longer needed

Contributing

When contributing to the spatial module:

  1. Ensure all tests pass
  2. Add tests for new functionality
  3. Follow the existing code patterns
  4. Update this README for new features
  5. Consider event system integration for new features

License

Part of the RPG Toolkit - see main repository for license information.

Documentation

Overview

Package spatial provides 2D positioning and movement infrastructure for entity placement and spatial queries without imposing game-specific rules.

Purpose: This package handles all spatial mathematics, collision detection, and movement validation without imposing any game-specific movement rules or combat mechanics. It provides the mathematical foundation for position-based game systems.

Scope:

  • 2D coordinate system with configurable units
  • Grid support (square, hex, gridless)
  • Room-based spatial organization
  • Collision detection and spatial queries
  • Path validation (not pathfinding algorithms)
  • Multi-room orchestration and connections
  • Distance calculations and area queries
  • Entity position tracking

Non-Goals:

  • Movement rules: Speed, difficult terrain are game-specific
  • Line of sight rules: Cover/concealment mechanics belong in games
  • Pathfinding algorithms: AI navigation belongs in behavior package
  • Combat ranges: Weapon/spell ranges are game-specific
  • 3D positioning: This is explicitly 2D only
  • Movement costs: Action economy is game-specific
  • Elevation: Height/flying is game-specific

Integration: This package integrates with:

  • behavior: Provides position queries for AI decisions
  • spawn: Validates entity placement
  • environments: Provides room infrastructure
  • events: Publishes movement and room transition events

The spatial package is the foundation for any position-based mechanics but deliberately avoids encoding any game rules about how space is used.

Example:

// Create a room with square grid
room := spatial.NewBasicRoom(spatial.RoomConfig{
    ID:     "throne-room",
    Width:  40,
    Height: 30,
    Grid:   spatial.GridTypeSquare,
})

// Place entities
err := room.PlaceEntity("guard-1", spatial.Position{X: 10, Y: 5})
err = room.PlaceEntity("king", spatial.Position{X: 20, Y: 25})

// Query nearby entities
nearby := room.GetEntitiesWithinDistance(
    spatial.Position{X: 15, Y: 15},
    10.0, // 10 units radius
)

// Multi-room orchestration
orchestrator := spatial.NewBasicOrchestrator(spatial.OrchestratorConfig{})
orchestrator.AddRoom(room)
orchestrator.AddRoom(hallway)

// Connect rooms
door := spatial.NewDoorConnection("door-1", "throne-room", "hallway",
    spatial.Position{X: 40, Y: 15}, // Exit position
    spatial.Position{X: 0, Y: 5},   // Entry position
)
orchestrator.AddConnection(door)

Package spatial provides 2D spatial positioning and movement capabilities for RPG games.

Package spatial provides 2D spatial positioning and movement capabilities for RPG games.

Index

Constants

View Source
const (
	// GridTypeSquare represents square grid type in room data
	GridTypeSquare = "square"
	// GridTypeHex represents hexagonal grid type in room data
	GridTypeHex = "hex"
	// GridTypeGridless represents gridless type in room data
	GridTypeGridless = "gridless"
)

Grid type constants for room data

View Source
const (
	// Query events - these remain as direct function calls, not migrated to typed events
	EventQueryPositionsInRange = "spatial.query.positions_in_range"
	EventQueryEntitiesInRange  = "spatial.query.entities_in_range"
	EventQueryLineOfSight      = "spatial.query.line_of_sight"
	EventQueryMovement         = "spatial.query.movement"
	EventQueryPlacement        = "spatial.query.placement"
)

Legacy event constants kept only for query operations (non-notification events)

Variables

View Source
var (
	// EntityPlacedTopic publishes events when entities are placed in rooms
	EntityPlacedTopic = events.DefineTypedTopic[EntityPlacedEvent]("spatial.entity.placed")
	// EntityMovedTopic publishes events when entities are moved within or between rooms
	EntityMovedTopic = events.DefineTypedTopic[EntityMovedEvent]("spatial.entity.moved")
	// EntityRemovedTopic publishes events when entities are removed from rooms
	EntityRemovedTopic = events.DefineTypedTopic[EntityRemovedEvent]("spatial.entity.removed")

	// RoomCreatedTopic publishes events when rooms are created
	RoomCreatedTopic = events.DefineTypedTopic[RoomCreatedEvent]("spatial.room.created")

	// RoomAddedTopic publishes events when rooms are added to orchestrators
	RoomAddedTopic = events.DefineTypedTopic[RoomAddedEvent]("spatial.orchestrator.room_added")
	// RoomRemovedTopic publishes events when rooms are removed from orchestrators
	RoomRemovedTopic = events.DefineTypedTopic[RoomRemovedEvent]("spatial.orchestrator.room_removed")
	// ConnectionAddedTopic publishes events when connections are added between rooms
	ConnectionAddedTopic = events.DefineTypedTopic[ConnectionAddedEvent]("spatial.orchestrator.connection_added")
	// ConnectionRemovedTopic publishes events when connections are removed between rooms
	ConnectionRemovedTopic = events.DefineTypedTopic[ConnectionRemovedEvent]("spatial.orchestrator.connection_removed")
	// EntityTransitionBeganTopic publishes events when entity transitions begin
	EntityTransitionBeganTopic = events.DefineTypedTopic[EntityTransitionBeganEvent]("spatial.entity.transition.began")
	// EntityTransitionEndedTopic publishes events when entity transitions complete
	EntityTransitionEndedTopic = events.DefineTypedTopic[EntityTransitionEndedEvent]("spatial.entity.transition.ended")
	// EntityRoomTransitionTopic publishes events when entities transition between rooms
	EntityRoomTransitionTopic = events.DefineTypedTopic[EntityRoomTransitionEvent]("entity.room_transition")
	// LayoutChangedTopic publishes events when orchestrator layouts change
	LayoutChangedTopic = events.DefineTypedTopic[LayoutChangedEvent]("spatial.orchestrator.layout_changed")
)

Functions

func RunPositionValidationTests

func RunPositionValidationTests(t *testing.T, grid Grid)

RunPositionValidationTests runs common position validation tests for any Grid

Types

type BasicConnection

type BasicConnection struct {
	// contains filtered or unexported fields
}

BasicConnection implements the Connection interface (ADR-0015: Abstract Connections)

func CreateBridgeConnection

func CreateBridgeConnection(id, fromRoom, toRoom string, cost float64) *BasicConnection

CreateBridgeConnection creates a bridge connection that might be destructible

func CreateDoorConnection

func CreateDoorConnection(id, fromRoom, toRoom string, cost float64) *BasicConnection

CreateDoorConnection creates a bidirectional door connection between two rooms (ADR-0015: Abstract Connections)

func CreatePortalConnection

func CreatePortalConnection(id, fromRoom, toRoom string, cost float64, bidirectional bool) *BasicConnection

CreatePortalConnection creates a magical portal connection

func CreateSecretPassageConnection

func CreateSecretPassageConnection(id, fromRoom, toRoom string, cost float64, requirements []string) *BasicConnection

CreateSecretPassageConnection creates a hidden passage that may have requirements

func CreateStairsConnection

func CreateStairsConnection(id, fromRoom, toRoom string, cost float64, goingUp bool) *BasicConnection

CreateStairsConnection creates a stairway connection between floors

func CreateTunnelConnection

func CreateTunnelConnection(id, fromRoom, toRoom string, cost float64) *BasicConnection

CreateTunnelConnection creates an underground tunnel

func NewBasicConnection

func NewBasicConnection(config BasicConnectionConfig) *BasicConnection

NewBasicConnection creates a new basic connection

func (*BasicConnection) AddRequirement

func (bc *BasicConnection) AddRequirement(requirement string)

AddRequirement adds a new requirement

func (*BasicConnection) GetConnectionType

func (bc *BasicConnection) GetConnectionType() ConnectionType

GetConnectionType returns the connection type

func (*BasicConnection) GetFromRoom

func (bc *BasicConnection) GetFromRoom() string

GetFromRoom returns the source room ID

func (*BasicConnection) GetID

func (bc *BasicConnection) GetID() string

GetID returns the connection ID

func (*BasicConnection) GetRequirements

func (bc *BasicConnection) GetRequirements() []string

GetRequirements returns any requirements for using this connection

func (*BasicConnection) GetToRoom

func (bc *BasicConnection) GetToRoom() string

GetToRoom returns the destination room ID

func (*BasicConnection) GetTraversalCost

func (bc *BasicConnection) GetTraversalCost(_ core.Entity) float64

GetTraversalCost returns the cost to traverse this connection

func (*BasicConnection) GetType

func (bc *BasicConnection) GetType() core.EntityType

GetType returns the entity type (implementing core.Entity)

func (*BasicConnection) HasRequirement

func (bc *BasicConnection) HasRequirement(requirement string) bool

HasRequirement checks if a specific requirement exists

func (*BasicConnection) IsPassable

func (bc *BasicConnection) IsPassable(_ core.Entity) bool

IsPassable checks if entities can currently traverse this connection

func (*BasicConnection) IsReversible

func (bc *BasicConnection) IsReversible() bool

IsReversible returns true if the connection works both ways

func (*BasicConnection) RemoveRequirement

func (bc *BasicConnection) RemoveRequirement(requirement string)

RemoveRequirement removes a requirement

func (*BasicConnection) SetPassable

func (bc *BasicConnection) SetPassable(passable bool)

SetPassable changes the passable state of the connection

type BasicConnectionConfig

type BasicConnectionConfig struct {
	ID           string
	Type         string
	ConnType     ConnectionType
	FromRoom     string
	ToRoom       string
	Reversible   bool
	Passable     bool
	Cost         float64
	Requirements []string
}

BasicConnectionConfig holds configuration for creating a basic connection

type BasicRoom

type BasicRoom struct {
	// contains filtered or unexported fields
}

BasicRoom implements the Room interface with event integration

func LoadRoomFromContext

func LoadRoomFromContext(_ context.Context, gameCtx game.Context[RoomData]) (*BasicRoom, error)

LoadRoomFromContext creates a BasicRoom from data using the GameContext pattern. This allows the room to integrate with the event system and other game infrastructure.

func NewBasicRoom

func NewBasicRoom(config BasicRoomConfig) *BasicRoom

NewBasicRoom creates a new basic room (call ConnectToEventBus after creation)

func (*BasicRoom) CanPlaceEntity

func (r *BasicRoom) CanPlaceEntity(entity core.Entity, pos Position) bool

CanPlaceEntity checks if an entity can be placed at a position

func (*BasicRoom) ConnectToEventBus added in v0.1.1

func (r *BasicRoom) ConnectToEventBus(bus events.EventBus)

ConnectToEventBus connects the room to an event bus for typed event publishing

func (*BasicRoom) GetAllEntities

func (r *BasicRoom) GetAllEntities() map[string]core.Entity

GetAllEntities returns all entities in the room

func (*BasicRoom) GetEntitiesAt

func (r *BasicRoom) GetEntitiesAt(pos Position) []core.Entity

GetEntitiesAt returns all entities at a specific position

func (*BasicRoom) GetEntitiesInRange

func (r *BasicRoom) GetEntitiesInRange(center Position, radius float64) []core.Entity

GetEntitiesInRange returns entities within a given range

func (*BasicRoom) GetEntityCount

func (r *BasicRoom) GetEntityCount() int

GetEntityCount returns the number of entities in the room

func (*BasicRoom) GetEntityCubePosition added in v0.2.0

func (r *BasicRoom) GetEntityCubePosition(entityID string) *CubeCoordinate

GetEntityCubePosition returns the cube coordinate position of an entity Returns nil if the entity doesn't exist or the grid is not a hex grid

func (*BasicRoom) GetEntityPosition

func (r *BasicRoom) GetEntityPosition(entityID string) (Position, bool)

GetEntityPosition returns the position of an entity

func (*BasicRoom) GetGrid

func (r *BasicRoom) GetGrid() Grid

GetGrid returns the grid system used by this room

func (*BasicRoom) GetID

func (r *BasicRoom) GetID() string

GetID returns the room's unique identifier (implements core.Entity)

func (*BasicRoom) GetLineOfSight

func (r *BasicRoom) GetLineOfSight(from, to Position) []Position

GetLineOfSight returns positions along the line of sight

func (*BasicRoom) GetOccupiedPositions

func (r *BasicRoom) GetOccupiedPositions() []Position

GetOccupiedPositions returns all positions that have entities

func (*BasicRoom) GetPositionsInRange

func (r *BasicRoom) GetPositionsInRange(center Position, radius float64) []Position

GetPositionsInRange returns all positions within a given range

func (*BasicRoom) GetType

func (r *BasicRoom) GetType() core.EntityType

GetType returns the room's type (implements core.Entity)

func (*BasicRoom) IsLineOfSightBlocked

func (r *BasicRoom) IsLineOfSightBlocked(from, to Position) bool

IsLineOfSightBlocked checks if line of sight is blocked by entities

func (*BasicRoom) IsPositionOccupied

func (r *BasicRoom) IsPositionOccupied(pos Position) bool

IsPositionOccupied checks if a position is occupied

func (*BasicRoom) MoveEntity

func (r *BasicRoom) MoveEntity(entityID string, newPos Position) error

MoveEntity moves an entity to a new position

func (*BasicRoom) PlaceEntity

func (r *BasicRoom) PlaceEntity(entity core.Entity, pos Position) error

PlaceEntity places an entity at a specific position

func (*BasicRoom) RemoveEntity

func (r *BasicRoom) RemoveEntity(entityID string) error

RemoveEntity removes an entity from the room

func (*BasicRoom) ToData

func (r *BasicRoom) ToData() RoomData

ToData converts a BasicRoom to RoomData for persistence. This captures the room's state including all placed entities.

type BasicRoomConfig

type BasicRoomConfig struct {
	ID   string
	Type string
	Grid Grid
}

BasicRoomConfig holds configuration for creating a basic room

type BasicRoomOrchestrator

type BasicRoomOrchestrator struct {
	// contains filtered or unexported fields
}

BasicRoomOrchestrator implements the RoomOrchestrator interface

func NewBasicRoomOrchestrator

func NewBasicRoomOrchestrator(config BasicRoomOrchestratorConfig) *BasicRoomOrchestrator

NewBasicRoomOrchestrator creates a new basic room orchestrator

func (*BasicRoomOrchestrator) AddConnection

func (bro *BasicRoomOrchestrator) AddConnection(connection Connection) error

AddConnection creates a connection between two rooms

func (*BasicRoomOrchestrator) AddRoom

func (bro *BasicRoomOrchestrator) AddRoom(room Room) error

AddRoom adds a room to the orchestrator

func (*BasicRoomOrchestrator) CanMoveEntityBetweenRooms

func (bro *BasicRoomOrchestrator) CanMoveEntityBetweenRooms(
	entityIDStr, fromRoomStr, toRoomStr, connectionIDStr string,
) bool

CanMoveEntityBetweenRooms checks if entity movement is possible

func (*BasicRoomOrchestrator) ConnectToEventBus added in v0.1.1

func (bro *BasicRoomOrchestrator) ConnectToEventBus(bus events.EventBus)

ConnectToEventBus connects all typed topics to the event bus

func (*BasicRoomOrchestrator) FindPath

func (bro *BasicRoomOrchestrator) FindPath(fromRoom, toRoom string, entity core.Entity) ([]string, error)

FindPath finds a path between rooms using connections (simple implementation)

func (*BasicRoomOrchestrator) GetAllConnections

func (bro *BasicRoomOrchestrator) GetAllConnections() map[string]Connection

GetAllConnections returns all connections

func (*BasicRoomOrchestrator) GetAllRooms

func (bro *BasicRoomOrchestrator) GetAllRooms() map[string]Room

GetAllRooms returns all managed rooms

func (*BasicRoomOrchestrator) GetConnection

func (bro *BasicRoomOrchestrator) GetConnection(connectionIDStr string) (Connection, bool)

GetConnection retrieves a connection by ID

func (*BasicRoomOrchestrator) GetEntityRoom

func (bro *BasicRoomOrchestrator) GetEntityRoom(entityIDStr string) (string, bool)

GetEntityRoom returns which room contains the entity

func (*BasicRoomOrchestrator) GetEventBus

func (bro *BasicRoomOrchestrator) GetEventBus() events.EventBus

GetEventBus returns the current event bus (implements EventBusIntegration)

func (*BasicRoomOrchestrator) GetID

func (bro *BasicRoomOrchestrator) GetID() string

GetID returns the orchestrator ID

func (*BasicRoomOrchestrator) GetLayout

func (bro *BasicRoomOrchestrator) GetLayout() LayoutType

GetLayout returns the current layout pattern

func (*BasicRoomOrchestrator) GetRoom

func (bro *BasicRoomOrchestrator) GetRoom(roomIDStr string) (Room, bool)

GetRoom retrieves a room by ID

func (*BasicRoomOrchestrator) GetRoomConnections

func (bro *BasicRoomOrchestrator) GetRoomConnections(roomIDStr string) []Connection

GetRoomConnections returns all connections for a specific room

func (*BasicRoomOrchestrator) GetType

func (bro *BasicRoomOrchestrator) GetType() core.EntityType

GetType returns the entity type

func (*BasicRoomOrchestrator) MoveEntityBetweenRooms

func (bro *BasicRoomOrchestrator) MoveEntityBetweenRooms(
	entityIDStr, fromRoomStr, toRoomStr, connectionIDStr string,
) error

MoveEntityBetweenRooms moves an entity from one room to another (ADR-0015: Abstract Connections)

func (*BasicRoomOrchestrator) RemoveConnection

func (bro *BasicRoomOrchestrator) RemoveConnection(connectionIDStr string) error

RemoveConnection removes a connection

func (*BasicRoomOrchestrator) RemoveRoom

func (bro *BasicRoomOrchestrator) RemoveRoom(roomIDStr string) error

RemoveRoom removes a room from the orchestrator

func (*BasicRoomOrchestrator) SetEventBus

func (bro *BasicRoomOrchestrator) SetEventBus(bus events.EventBus)

SetEventBus sets the event bus for the orchestrator (implements EventBusIntegration)

func (*BasicRoomOrchestrator) SetLayout

func (bro *BasicRoomOrchestrator) SetLayout(layout LayoutType) error

SetLayout configures the arrangement pattern

type BasicRoomOrchestratorConfig

type BasicRoomOrchestratorConfig struct {
	ID     OrchestratorID // Optional: if empty, will auto-generate
	Type   string
	Layout LayoutType
}

BasicRoomOrchestratorConfig holds configuration for creating a basic room orchestrator

type Circle

type Circle struct {
	Center Position `json:"center"`
	Radius float64  `json:"radius"`
}

Circle represents a circular area

func (Circle) Contains

func (c Circle) Contains(pos Position) bool

Contains checks if a position is within the circle

func (Circle) Intersects

func (c Circle) Intersects(other Circle) bool

Intersects checks if this circle intersects with another circle

func (Circle) String

func (c Circle) String() string

String returns a string representation of the circle

type Connection

type Connection interface {
	core.Entity

	// GetConnectionType returns the connection type
	GetConnectionType() ConnectionType

	// GetFromRoom returns the source room ID
	GetFromRoom() string

	// GetToRoom returns the destination room ID
	GetToRoom() string

	// IsPassable checks if entities can currently traverse this connection
	IsPassable(entity core.Entity) bool

	// GetTraversalCost returns the cost to traverse this connection
	GetTraversalCost(entity core.Entity) float64

	// IsReversible returns true if the connection works both ways
	IsReversible() bool

	// GetRequirements returns any requirements for using this connection
	GetRequirements() []string
}

Connection represents a logical link between two rooms (ADR-0015: Abstract Connections)

type ConnectionAddedEvent added in v0.1.1

type ConnectionAddedEvent struct {
	OrchestratorID string    `json:"orchestrator_id"`
	ConnectionID   string    `json:"connection_id"`
	FromRoom       string    `json:"from_room"`
	ToRoom         string    `json:"to_room"`
	ConnectionType string    `json:"connection_type"`
	AddedAt        time.Time `json:"added_at"`
}

ConnectionAddedEvent contains data for connection addition events

type ConnectionID

type ConnectionID string

ConnectionID is a unique identifier for a connection

func NewConnectionID

func NewConnectionID() ConnectionID

NewConnectionID generates a new unique connection identifier

func (ConnectionID) String

func (id ConnectionID) String() string

type ConnectionRemovedEvent added in v0.1.1

type ConnectionRemovedEvent struct {
	OrchestratorID string    `json:"orchestrator_id"`
	ConnectionID   string    `json:"connection_id"`
	Reason         string    `json:"reason,omitempty"`
	RemovedAt      time.Time `json:"removed_at"`
}

ConnectionRemovedEvent contains data for connection removal events

type ConnectionType

type ConnectionType string

ConnectionType represents different types of connections between rooms

const (
	ConnectionTypeDoor    ConnectionType = "door"    // Standard doorway connection
	ConnectionTypeStairs  ConnectionType = "stairs"  // Stairway between different levels
	ConnectionTypePassage ConnectionType = "passage" // Corridor or hallway connection
	ConnectionTypePortal  ConnectionType = "portal"  // Magical or teleportation connection
	ConnectionTypeBridge  ConnectionType = "bridge"  // Bridge spanning a gap or obstacle
	ConnectionTypeTunnel  ConnectionType = "tunnel"  // Underground or enclosed tunnel
)

Connection type constants define the various ways rooms can be linked

type CubeCoordinate

type CubeCoordinate struct {
	X int `json:"x"`
	Y int `json:"y"`
	Z int `json:"z"`
}

CubeCoordinate represents a position in cube coordinate system (for hex grids) In hex grids, cube coordinates simplify distance and neighbor calculations

func OffsetCoordinateToCube

func OffsetCoordinateToCube(pos Position) CubeCoordinate

OffsetCoordinateToCube converts offset coordinate to cube coordinate Uses pointy-top orientation by default. Use OffsetCoordinateToCubeWithOrientation for flat-top.

func OffsetCoordinateToCubeWithOrientation added in v0.2.0

func OffsetCoordinateToCubeWithOrientation(pos Position, orientation HexOrientation) CubeCoordinate

OffsetCoordinateToCubeWithOrientation converts offset coordinate to cube coordinate using the specified hex orientation

func (CubeCoordinate) Add

Add adds another cube coordinate to this one

func (CubeCoordinate) Distance

func (c CubeCoordinate) Distance(other CubeCoordinate) int

Distance calculates the hex distance between two cube coordinates This is specific to hex grids and uses cube coordinate math

func (CubeCoordinate) Equals

func (c CubeCoordinate) Equals(other CubeCoordinate) bool

Equals checks if two cube coordinates are equal

func (CubeCoordinate) GetNeighbors

func (c CubeCoordinate) GetNeighbors() []CubeCoordinate

GetNeighbors returns all 6 neighboring cube coordinates

func (CubeCoordinate) IsValid

func (c CubeCoordinate) IsValid() bool

IsValid checks if the cube coordinate is valid (x + y + z == 0)

func (CubeCoordinate) Scale

func (c CubeCoordinate) Scale(factor int) CubeCoordinate

Scale scales the cube coordinate by a factor

func (CubeCoordinate) String

func (c CubeCoordinate) String() string

String returns a string representation of the cube coordinate

func (CubeCoordinate) Subtract

func (c CubeCoordinate) Subtract(other CubeCoordinate) CubeCoordinate

Subtract subtracts another cube coordinate from this one

func (CubeCoordinate) ToOffsetCoordinate

func (c CubeCoordinate) ToOffsetCoordinate() Position

ToOffsetCoordinate converts cube coordinate to offset coordinate (for display) Uses pointy-top orientation by default. Use ToOffsetCoordinateWithOrientation for flat-top.

func (CubeCoordinate) ToOffsetCoordinateWithOrientation added in v0.2.0

func (c CubeCoordinate) ToOffsetCoordinateWithOrientation(orientation HexOrientation) Position

ToOffsetCoordinateWithOrientation converts cube coordinate to offset coordinate using the specified hex orientation

type Dimensions

type Dimensions struct {
	Width  float64 `json:"width"`
	Height float64 `json:"height"`
}

Dimensions represents the size of a spatial area

func (Dimensions) Area

func (d Dimensions) Area() float64

Area calculates the area of the dimensions

func (Dimensions) Contains

func (d Dimensions) Contains(pos Position) bool

Contains checks if a position is within the dimensions (assuming origin at 0,0)

func (Dimensions) String

func (d Dimensions) String() string

String returns a string representation of the dimensions

type EntityCubePlacement added in v0.3.0

type EntityCubePlacement struct {
	// EntityID is the unique identifier of the entity
	EntityID string `json:"entity_id"`

	// EntityType is the type of the entity (e.g., "character", "monster", "object")
	EntityType string `json:"entity_type"`

	// CubePosition is where the entity is placed in the room (cube coordinates)
	CubePosition CubeCoordinate `json:"cube_position"`

	// Size is how many grid spaces the entity occupies (default 1)
	Size int `json:"size,omitempty"`

	// BlocksMovement indicates if this entity blocks movement through its space
	BlocksMovement bool `json:"blocks_movement"`

	// BlocksLineOfSight indicates if this entity blocks line of sight
	BlocksLineOfSight bool `json:"blocks_line_of_sight"`
}

EntityCubePlacement represents an entity's position using cube coordinates. Used for hex grids where cube coordinates (x, y, z where x+y+z=0) are the native format.

type EntityFilter

type EntityFilter interface {
	// Matches returns true if the entity matches the filter
	Matches(entity core.Entity) bool
}

EntityFilter defines filtering criteria for spatial queries

func CreateCharacterFilter

func CreateCharacterFilter() EntityFilter

CreateCharacterFilter creates a filter for character entities

func CreateCombatantFilter

func CreateCombatantFilter() EntityFilter

CreateCombatantFilter creates a filter for combatant entities (characters + monsters)

func CreateExcludeFilter

func CreateExcludeFilter(excludeIDs ...string) EntityFilter

CreateExcludeFilter creates a filter that excludes specific entity IDs

func CreateIncludeFilter

func CreateIncludeFilter(includeIDs ...string) EntityFilter

CreateIncludeFilter creates a filter that includes only specific entity IDs

func CreateItemFilter

func CreateItemFilter() EntityFilter

CreateItemFilter creates a filter for item entities

func CreateMonsterFilter

func CreateMonsterFilter() EntityFilter

CreateMonsterFilter creates a filter for monster entities

type EntityID

type EntityID string

EntityID is a unique identifier for an entity

func NewEntityID

func NewEntityID() EntityID

NewEntityID generates a new unique entity identifier

func (EntityID) String

func (id EntityID) String() string

type EntityMovedEvent added in v0.1.1

type EntityMovedEvent struct {
	EntityID         string          `json:"entity_id"`
	FromPosition     Position        `json:"from_position"`
	ToPosition       Position        `json:"to_position"`
	FromCubePosition *CubeCoordinate `json:"from_cube_position,omitempty"` // Only set for hex grids
	ToCubePosition   *CubeCoordinate `json:"to_cube_position,omitempty"`   // Only set for hex grids
	RoomID           string          `json:"room_id"`
	MovementType     string          `json:"movement_type"` // "normal", "teleport", "forced"
}

EntityMovedEvent contains data for entity movement events

type EntityPlacedEvent added in v0.1.1

type EntityPlacedEvent struct {
	EntityID     string          `json:"entity_id"`
	Position     Position        `json:"position"`
	CubePosition *CubeCoordinate `json:"cube_position,omitempty"` // Only set for hex grids
	RoomID       string          `json:"room_id"`
	GridType     string          `json:"grid_type"` // "square", "hex", "gridless"
}

EntityPlacedEvent contains data for entity placement events

type EntityPlacement

type EntityPlacement struct {
	// EntityID is the unique identifier of the entity
	EntityID string `json:"entity_id"`

	// EntityType is the type of the entity (e.g., "character", "monster", "object")
	EntityType string `json:"entity_type"`

	// Position is where the entity is placed in the room (offset coordinates)
	Position Position `json:"position"`

	// Size is how many grid spaces the entity occupies (default 1)
	Size int `json:"size,omitempty"`

	// BlocksMovement indicates if this entity blocks movement through its space
	BlocksMovement bool `json:"blocks_movement"`

	// BlocksLineOfSight indicates if this entity blocks line of sight
	BlocksLineOfSight bool `json:"blocks_line_of_sight"`
}

EntityPlacement represents an entity's position and spatial properties in a room. Used for square and gridless grids that use offset coordinates.

type EntityRemovedEvent added in v0.1.1

type EntityRemovedEvent struct {
	EntityID    string   `json:"entity_id"`
	Position    Position `json:"position"`
	RoomID      string   `json:"room_id"`
	RemovalType string   `json:"removal_type"` // "normal", "destroyed", "teleported"
}

EntityRemovedEvent contains data for entity removal events

type EntityRoomTransitionEvent added in v0.1.1

type EntityRoomTransitionEvent struct {
	EntityID  string    `json:"entity_id"`
	FromRoom  string    `json:"from_room"`
	ToRoom    string    `json:"to_room"`
	Reason    string    `json:"reason,omitempty"`
	Timestamp time.Time `json:"timestamp"`
}

EntityRoomTransitionEvent contains data for entity room transition events

type EntityTransitionBeganEvent added in v0.1.1

type EntityTransitionBeganEvent struct {
	EntityID       string    `json:"entity_id"`
	FromRoom       string    `json:"from_room"`
	ToRoom         string    `json:"to_room"`
	ConnectionID   string    `json:"connection_id,omitempty"`
	TransitionType string    `json:"transition_type"` // "door", "stairs", "portal", etc.
	BeganAt        time.Time `json:"began_at"`
}

EntityTransitionBeganEvent contains data for entity transition start events

type EntityTransitionEndedEvent added in v0.1.1

type EntityTransitionEndedEvent struct {
	EntityID       string    `json:"entity_id"`
	FromRoom       string    `json:"from_room"`
	ToRoom         string    `json:"to_room"`
	ConnectionID   string    `json:"connection_id,omitempty"`
	TransitionType string    `json:"transition_type"`
	Success        bool      `json:"success"`
	EndedAt        time.Time `json:"ended_at"`
}

EntityTransitionEndedEvent contains data for entity transition completion events

type EventBusIntegration

type EventBusIntegration interface {
	// SetEventBus sets the event bus for the spatial module
	SetEventBus(bus events.EventBus)

	// GetEventBus returns the current event bus
	GetEventBus() events.EventBus
}

EventBusIntegration defines how the spatial module integrates with the event bus

type Grid

type Grid interface {
	// GetShape returns the grid type
	GetShape() GridShape

	// IsValidPosition checks if a position is valid within the grid
	IsValidPosition(pos Position) bool

	// GetDimensions returns the grid dimensions
	GetDimensions() Dimensions

	// Distance calculates the distance between two positions
	Distance(from, to Position) float64

	// GetNeighbors returns all adjacent positions
	GetNeighbors(pos Position) []Position

	// IsAdjacent checks if two positions are adjacent
	IsAdjacent(pos1, pos2 Position) bool

	// GetLineOfSight returns positions along the line of sight
	GetLineOfSight(from, to Position) []Position

	// GetPositionsInRange returns all positions within a given range
	GetPositionsInRange(center Position, radius float64) []Position
}

Grid defines the interface for all grid systems

type GridShape

type GridShape int

GridShape represents the type of grid system

const (
	// GridShapeSquare represents a square grid system with D&D 5e distance rules
	GridShapeSquare GridShape = iota
	// GridShapeHex represents a hexagonal grid system using cube coordinates
	GridShapeHex
	// GridShapeGridless represents a gridless system for theater-of-mind play
	GridShapeGridless
)

type GridlessConfig

type GridlessConfig struct {
	Width  float64
	Height float64
}

GridlessConfig holds configuration for creating a gridless room

type GridlessRoom

type GridlessRoom struct {
	// contains filtered or unexported fields
}

GridlessRoom implements a gridless spatial system for theater-of-mind play Uses Euclidean distance and allows approximate positioning

func NewGridlessRoom

func NewGridlessRoom(config GridlessConfig) *GridlessRoom

NewGridlessRoom creates a new gridless room with the given dimensions

func (*GridlessRoom) Distance

func (gr *GridlessRoom) Distance(from, to Position) float64

Distance calculates the Euclidean distance between two positions This is the true geometric distance, not constrained by grid

func (*GridlessRoom) GetDimensions

func (gr *GridlessRoom) GetDimensions() Dimensions

GetDimensions returns the room dimensions

func (*GridlessRoom) GetLineOfSight

func (gr *GridlessRoom) GetLineOfSight(from, to Position) []Position

GetLineOfSight returns positions along the line of sight Since there's no grid, we sample points along the line

func (*GridlessRoom) GetNearestPosition

func (gr *GridlessRoom) GetNearestPosition(pos Position) Position

GetNearestPosition returns the nearest valid position to the given position Useful for "snapping" entities to valid positions

func (*GridlessRoom) GetNeighbors

func (gr *GridlessRoom) GetNeighbors(pos Position) []Position

GetNeighbors returns positions in a circle around the given position Since there's no grid, we return positions at various angles

func (*GridlessRoom) GetPositionsInArc

func (gr *GridlessRoom) GetPositionsInArc(center Position, radius float64, startAngle, endAngle float64) []Position

GetPositionsInArc returns positions within an arc (portion of a circle) This is useful for gridless rooms where you want spell effects in arcs

func (*GridlessRoom) GetPositionsInCircle

func (gr *GridlessRoom) GetPositionsInCircle(circle Circle) []Position

GetPositionsInCircle returns positions within a circular area

func (*GridlessRoom) GetPositionsInCone

func (gr *GridlessRoom) GetPositionsInCone(
	origin Position, direction Position, length float64, angle float64,
) []Position

GetPositionsInCone returns positions within a cone shape

func (*GridlessRoom) GetPositionsInLine

func (gr *GridlessRoom) GetPositionsInLine(from, to Position) []Position

GetPositionsInLine returns positions along a line

func (*GridlessRoom) GetPositionsInRange

func (gr *GridlessRoom) GetPositionsInRange(center Position, radius float64) []Position

GetPositionsInRange returns all positions within range Since there's no grid, we sample in a circular pattern

func (*GridlessRoom) GetPositionsInRectangle

func (gr *GridlessRoom) GetPositionsInRectangle(rect Rectangle) []Position

GetPositionsInRectangle returns positions within a rectangular area

func (*GridlessRoom) GetShape

func (gr *GridlessRoom) GetShape() GridShape

GetShape returns the grid shape type

func (*GridlessRoom) IsAdjacent

func (gr *GridlessRoom) IsAdjacent(pos1, pos2 Position) bool

IsAdjacent checks if two positions are adjacent (within distance 1)

func (*GridlessRoom) IsValidPosition

func (gr *GridlessRoom) IsValidPosition(pos Position) bool

IsValidPosition checks if a position is within the room bounds In gridless rooms, any position within the dimensions is valid

type HexGrid

type HexGrid struct {
	// contains filtered or unexported fields
}

HexGrid implements a hexagonal grid system using cube coordinates

func NewHexGrid

func NewHexGrid(config HexGridConfig) *HexGrid

NewHexGrid creates a new hex grid with the given dimensions Defaults to pointy-top orientation for D&D 5e compatibility

func (*HexGrid) CubeToOffset

func (hg *HexGrid) CubeToOffset(cube CubeCoordinate) Position

CubeToOffset converts a cube coordinate to offset coordinate using this grid's orientation

func (*HexGrid) Distance

func (hg *HexGrid) Distance(from, to Position) float64

Distance calculates the distance between two positions using hex grid rules Converts to cube coordinates and uses hex distance formula

func (*HexGrid) GetCubeNeighbors

func (hg *HexGrid) GetCubeNeighbors(pos Position) []CubeCoordinate

GetCubeNeighbors returns the 6 cube coordinate neighbors of a position

func (*HexGrid) GetDimensions

func (hg *HexGrid) GetDimensions() Dimensions

GetDimensions returns the grid dimensions

func (*HexGrid) GetHexRing

func (hg *HexGrid) GetHexRing(center Position, radius int) []Position

GetHexRing returns positions forming a ring at a specific distance from center This is a hex-specific function that's useful for spell effects

func (*HexGrid) GetHexSpiral

func (hg *HexGrid) GetHexSpiral(center Position, radius int) []Position

GetHexSpiral returns positions in a spiral pattern from center outward Useful for area effects that expand outward

func (*HexGrid) GetLineOfSight

func (hg *HexGrid) GetLineOfSight(from, to Position) []Position

GetLineOfSight returns positions along the line of sight between two positions Uses cube coordinate lerp for hex line drawing

func (*HexGrid) GetNeighbors

func (hg *HexGrid) GetNeighbors(pos Position) []Position

GetNeighbors returns all 6 adjacent positions in hex grid

func (*HexGrid) GetOrientation

func (hg *HexGrid) GetOrientation() HexOrientation

GetOrientation returns the hex grid orientation (pointy-top or flat-top)

func (*HexGrid) GetPositionsInCircle

func (hg *HexGrid) GetPositionsInCircle(circle Circle) []Position

GetPositionsInCircle returns all positions within a circular area using hex distance

func (*HexGrid) GetPositionsInCone

func (hg *HexGrid) GetPositionsInCone(origin Position, direction Position, length float64, angle float64) []Position

GetPositionsInCone returns positions within a cone shape This is more complex for hex grids due to the 6-sided nature

func (*HexGrid) GetPositionsInLine

func (hg *HexGrid) GetPositionsInLine(from, to Position) []Position

GetPositionsInLine returns positions along a line from start to end

func (*HexGrid) GetPositionsInRange

func (hg *HexGrid) GetPositionsInRange(center Position, radius float64) []Position

GetPositionsInRange returns all positions within a given range using hex distance

func (*HexGrid) GetPositionsInRectangle

func (hg *HexGrid) GetPositionsInRectangle(rect Rectangle) []Position

GetPositionsInRectangle returns all positions within a rectangular area Note: This is approximate for hex grids since rectangles don't align perfectly with hex geometry

func (*HexGrid) GetShape

func (hg *HexGrid) GetShape() GridShape

GetShape returns the grid shape type

func (*HexGrid) IsAdjacent

func (hg *HexGrid) IsAdjacent(pos1, pos2 Position) bool

IsAdjacent checks if two positions are adjacent (within 1 hex)

func (*HexGrid) IsPointyTop added in v0.2.0

func (hg *HexGrid) IsPointyTop() bool

IsPointyTop returns true if the grid uses pointy-top orientation

func (*HexGrid) IsValidPosition

func (hg *HexGrid) IsValidPosition(pos Position) bool

IsValidPosition checks if a position is valid within the grid bounds

func (*HexGrid) OffsetToCube

func (hg *HexGrid) OffsetToCube(pos Position) CubeCoordinate

OffsetToCube converts an offset coordinate to cube coordinate using this grid's orientation

type HexGridConfig

type HexGridConfig struct {
	Width       float64
	Height      float64
	PointyTop   bool           // Deprecated: use Orientation instead. true for pointy-top, false for flat-top
	Orientation HexOrientation // The hex orientation (pointy-top or flat-top)
}

HexGridConfig holds configuration for creating a hex grid

type HexOrientation added in v0.2.0

type HexOrientation int

HexOrientation represents the orientation of a hexagonal grid

const (
	// HexOrientationPointyTop is the default orientation where hexes have a pointed top
	// This is the standard D&D 5e hex grid orientation
	HexOrientationPointyTop HexOrientation = iota
	// HexOrientationFlatTop is an alternative orientation where hexes have a flat top
	HexOrientationFlatTop
)

func (HexOrientation) String added in v0.2.0

func (o HexOrientation) String() string

String returns the string representation of the hex orientation

type LayoutChangedEvent added in v0.1.1

type LayoutChangedEvent struct {
	OrchestratorID string    `json:"orchestrator_id"`
	OldLayout      string    `json:"old_layout,omitempty"`
	NewLayout      string    `json:"new_layout"`
	ChangedAt      time.Time `json:"changed_at"`
}

LayoutChangedEvent contains data for orchestrator layout change events

type LayoutMetrics

type LayoutMetrics struct {
	TotalRooms       int                 `json:"total_rooms"`
	TotalConnections int                 `json:"total_connections"`
	AverageDistance  float64             `json:"average_distance"`
	MaxDistance      float64             `json:"max_distance"`
	Connectivity     float64             `json:"connectivity"` // connections per room
	RoomPositions    map[string]Position `json:"room_positions"`
	LayoutType       LayoutType          `json:"layout_type"`
}

LayoutMetrics contains information about the spatial arrangement

type LayoutOrchestrator

type LayoutOrchestrator interface {
	// ArrangeRooms arranges rooms according to the layout pattern
	ArrangeRooms(rooms map[string]Room, layout LayoutType) error

	// CalculateRoomPositions calculates optimal positions for rooms
	CalculateRoomPositions(rooms map[string]Room, connections []Connection) map[string]Position

	// ValidateLayout checks if a layout is structurally sound
	ValidateLayout(rooms map[string]Room, connections []Connection) error

	// GetLayoutMetrics returns metrics about the current layout
	GetLayoutMetrics() LayoutMetrics
}

LayoutOrchestrator handles spatial arrangement of multiple rooms

type LayoutType

type LayoutType string

LayoutType represents different arrangement patterns for multiple rooms

const (
	LayoutTypeTower     LayoutType = "tower"     // Vertical stacking arrangement
	LayoutTypeBranching LayoutType = "branching" // Hub and spoke arrangement
	LayoutTypeGrid      LayoutType = "grid"      // 2D grid arrangement
	LayoutTypeOrganic   LayoutType = "organic"   // Irregular organic connections
)

Layout type constants define how multiple rooms can be spatially arranged

type OrchestratorID

type OrchestratorID string

OrchestratorID is a unique identifier for an orchestrator

func NewOrchestratorID

func NewOrchestratorID() OrchestratorID

NewOrchestratorID generates a new unique orchestrator identifier

func (OrchestratorID) String

func (id OrchestratorID) String() string

type PathFinder added in v0.4.0

type PathFinder interface {
	// FindPath returns a path from start to goal avoiding blocked hexes.
	// Returns the path excluding start, including goal.
	// Returns empty slice if no path exists or start == goal.
	FindPath(start, goal CubeCoordinate, blocked map[CubeCoordinate]bool) []CubeCoordinate
}

PathFinder finds paths between hex positions avoiding obstacles. Implementations can use different algorithms (A*, Dijkstra, weighted, etc.)

type Placeable

type Placeable interface {
	core.Entity

	// GetSize returns the size of the entity (for multi-space entities)
	GetSize() int

	// BlocksMovement returns true if the entity blocks movement
	BlocksMovement() bool

	// BlocksLineOfSight returns true if the entity blocks line of sight
	BlocksLineOfSight() bool
}

Placeable defines the interface for entities that can be placed spatially

type PlaceableData

type PlaceableData struct {
	// contains filtered or unexported fields
}

PlaceableData is a minimal implementation of Placeable for spatial queries. It contains just enough data to support movement and line of sight calculations.

func (*PlaceableData) BlocksLineOfSight

func (p *PlaceableData) BlocksLineOfSight() bool

BlocksLineOfSight returns true if the entity blocks line of sight

func (*PlaceableData) BlocksMovement

func (p *PlaceableData) BlocksMovement() bool

BlocksMovement returns true if the entity blocks movement

func (*PlaceableData) GetID

func (p *PlaceableData) GetID() string

GetID returns the entity's unique identifier

func (*PlaceableData) GetSize

func (p *PlaceableData) GetSize() int

GetSize returns the size of the entity

func (*PlaceableData) GetType

func (p *PlaceableData) GetType() core.EntityType

GetType returns the entity's type

type Position

type Position struct {
	X float64 `json:"x"`
	Y float64 `json:"y"`
}

Position represents a spatial position in 2D space NOTE: Distance calculations are grid-dependent and handled by Grid implementations

func (Position) Add

func (p Position) Add(other Position) Position

Add adds another position to this position

func (Position) Equals

func (p Position) Equals(other Position) bool

Equals checks if two positions are equal

func (Position) IsZero

func (p Position) IsZero() bool

IsZero checks if the position is at the origin

func (Position) Normalize

func (p Position) Normalize() Position

Normalize returns a normalized version of the position (for vector math)

func (Position) Scale

func (p Position) Scale(factor float64) Position

Scale scales the position by a factor

func (Position) String

func (p Position) String() string

String returns a string representation of the position

func (Position) Subtract

func (p Position) Subtract(other Position) Position

Subtract subtracts another position from this position

type Query

type Query interface {
	// GetType returns the query type
	GetType() string

	// GetRoom returns the room being queried
	GetRoom() Room

	// GetCenter returns the center position for the query
	GetCenter() Position

	// GetRadius returns the radius for range-based queries
	GetRadius() float64

	// GetFilter returns any entity filter for the query
	GetFilter() EntityFilter
}

Query represents a spatial query

type QueryEntitiesInRangeData

type QueryEntitiesInRangeData struct {
	Center  Position      `json:"center"`
	Radius  float64       `json:"radius"`
	RoomID  string        `json:"room_id"`
	Filter  EntityFilter  `json:"filter,omitempty"`
	Results []core.Entity `json:"results,omitempty"`
	Error   error         `json:"error,omitempty"`
}

QueryEntitiesInRangeData contains data for entity range queries

type QueryHandler

type QueryHandler interface {
	// ProcessQuery processes a spatial query and returns results
	ProcessQuery(query Query) (QueryResult, error)
}

QueryHandler defines the interface for spatial query processing

type QueryLineOfSightData

type QueryLineOfSightData struct {
	From    Position   `json:"from"`
	To      Position   `json:"to"`
	RoomID  string     `json:"room_id"`
	Results []Position `json:"results,omitempty"`
	Blocked bool       `json:"blocked,omitempty"`
	Error   error      `json:"error,omitempty"`
}

QueryLineOfSightData contains data for line of sight queries

type QueryMovementData

type QueryMovementData struct {
	Entity   core.Entity `json:"entity"`
	From     Position    `json:"from"`
	To       Position    `json:"to"`
	RoomID   string      `json:"room_id"`
	Valid    bool        `json:"valid,omitempty"`
	Path     []Position  `json:"path,omitempty"`
	Distance float64     `json:"distance,omitempty"`
	Error    error       `json:"error,omitempty"`
}

QueryMovementData contains data for movement queries

type QueryPlacementData

type QueryPlacementData struct {
	Entity   core.Entity `json:"entity"`
	Position Position    `json:"position"`
	RoomID   string      `json:"room_id"`
	Valid    bool        `json:"valid,omitempty"`
	Error    error       `json:"error,omitempty"`
}

QueryPlacementData contains data for placement queries

type QueryPositionsInRangeData

type QueryPositionsInRangeData struct {
	Center  Position   `json:"center"`
	Radius  float64    `json:"radius"`
	RoomID  string     `json:"room_id"`
	Results []Position `json:"results,omitempty"`
	Error   error      `json:"error,omitempty"`
}

QueryPositionsInRangeData contains data for position range queries

type QueryResult

type QueryResult interface {
	// GetPositions returns positions that match the query
	GetPositions() []Position

	// GetEntities returns entities that match the query
	GetEntities() []core.Entity

	// GetDistances returns distances for each result
	GetDistances() map[string]float64
}

QueryResult represents the result of a spatial query

type QueryUtils

type QueryUtils struct {
	// contains filtered or unexported fields
}

QueryUtils provides convenient methods for performing spatial queries

func NewQueryUtils

func NewQueryUtils(queryHandler *SpatialQueryHandler) *QueryUtils

NewQueryUtils creates a new query utilities instance

func (*QueryUtils) QueryEntitiesInRange

func (q *QueryUtils) QueryEntitiesInRange(
	ctx context.Context, center Position, radius float64, roomID string, filter EntityFilter,
) ([]core.Entity, error)

QueryEntitiesInRange performs an entities-in-range query directly through the query handler

func (*QueryUtils) QueryLineOfSight

func (q *QueryUtils) QueryLineOfSight(ctx context.Context, from, to Position, roomID string) ([]Position, bool, error)

QueryLineOfSight performs a line-of-sight query directly through the query handler

func (*QueryUtils) QueryMovement

func (q *QueryUtils) QueryMovement(
	ctx context.Context, entity core.Entity, from, to Position, roomID string,
) (bool, []Position, float64, error)

QueryMovement performs a movement query directly through the query handler

func (*QueryUtils) QueryPlacement

func (q *QueryUtils) QueryPlacement(
	ctx context.Context, entity core.Entity, position Position, roomID string,
) (bool, error)

QueryPlacement performs a placement query directly through the query handler

func (*QueryUtils) QueryPositionsInRange

func (q *QueryUtils) QueryPositionsInRange(
	ctx context.Context, center Position, radius float64, roomID string,
) ([]Position, error)

QueryPositionsInRange performs a positions-in-range query directly through the query handler

type Rectangle

type Rectangle struct {
	Position   Position   `json:"position"`
	Dimensions Dimensions `json:"dimensions"`
}

Rectangle represents a rectangular area

func (Rectangle) Center

func (r Rectangle) Center() Position

Center returns the center position of the rectangle

func (Rectangle) Contains

func (r Rectangle) Contains(pos Position) bool

Contains checks if a position is within the rectangle

func (Rectangle) Intersects

func (r Rectangle) Intersects(other Rectangle) bool

Intersects checks if this rectangle intersects with another rectangle

func (Rectangle) String

func (r Rectangle) String() string

String returns a string representation of the rectangle

type Room

type Room interface {
	core.Entity

	// GetGrid returns the grid system used by this room
	GetGrid() Grid

	// PlaceEntity places an entity at a specific position
	PlaceEntity(entity core.Entity, pos Position) error

	// MoveEntity moves an entity to a new position
	MoveEntity(entityID string, newPos Position) error

	// RemoveEntity removes an entity from the room
	RemoveEntity(entityID string) error

	// GetEntitiesAt returns all entities at a specific position
	GetEntitiesAt(pos Position) []core.Entity

	// GetEntityPosition returns the position of an entity
	GetEntityPosition(entityID string) (Position, bool)

	// GetAllEntities returns all entities in the room
	GetAllEntities() map[string]core.Entity

	// GetEntitiesInRange returns entities within a given range
	GetEntitiesInRange(center Position, radius float64) []core.Entity

	// IsPositionOccupied checks if a position is occupied
	IsPositionOccupied(pos Position) bool

	// CanPlaceEntity checks if an entity can be placed at a position
	CanPlaceEntity(entity core.Entity, pos Position) bool

	// GetPositionsInRange returns all positions within a given range
	GetPositionsInRange(center Position, radius float64) []Position

	// GetLineOfSight returns positions along the line of sight
	GetLineOfSight(from, to Position) []Position

	// IsLineOfSightBlocked checks if line of sight is blocked by entities
	IsLineOfSightBlocked(from, to Position) bool
}

Room defines the interface for spatial containers

type RoomAddedEvent added in v0.1.1

type RoomAddedEvent struct {
	OrchestratorID string    `json:"orchestrator_id"`
	RoomID         string    `json:"room_id"`
	RoomType       string    `json:"room_type,omitempty"`
	AddedAt        time.Time `json:"added_at"`
}

RoomAddedEvent contains data for room addition to orchestrator events

type RoomCreatedEvent added in v0.1.1

type RoomCreatedEvent struct {
	RoomID       string    `json:"room_id"`
	RoomType     string    `json:"room_type"`
	GridType     string    `json:"grid_type"`
	Width        int       `json:"width"`
	Height       int       `json:"height"`
	CreationTime time.Time `json:"creation_time"`
}

RoomCreatedEvent contains data for room creation events

type RoomData

type RoomData struct {
	// ID is the unique identifier for the room
	ID string `json:"id"`

	// Type categorizes the room (e.g., "dungeon", "tavern", "outdoor")
	Type string `json:"type"`

	// Width defines the horizontal size of the room
	Width int `json:"width"`

	// Height defines the vertical size of the room
	Height int `json:"height"`

	// GridType specifies the grid system: "square", "hex", or "gridless"
	GridType string `json:"grid_type"`

	// HexFlatTop specifies hex grid orientation
	// Only used when GridType is "hex"
	// false = pointy-top (default), true = flat-top
	HexFlatTop bool `json:"hex_flat_top,omitempty"`

	// Entities contains positioned entities within the room using offset coordinates.
	// Used for square and gridless grids.
	// Map of entity ID to their position and data.
	Entities map[string]EntityPlacement `json:"entities,omitempty"`

	// CubeEntities contains positioned entities within the room using cube coordinates.
	// Used for hex grids where cube coordinates are the native format.
	// Map of entity ID to their position and data.
	CubeEntities map[string]EntityCubePlacement `json:"cube_entities,omitempty"`
}

RoomData contains all information needed to persist and reconstruct a room. This follows the established data pattern for serialization and loading.

type RoomID

type RoomID string

RoomID is a unique identifier for a room

func NewRoomID

func NewRoomID() RoomID

NewRoomID generates a new unique room identifier

func (RoomID) String

func (id RoomID) String() string

String conversion methods

type RoomOrchestrator

type RoomOrchestrator interface {
	core.Entity
	EventBusIntegration

	// AddRoom adds a room to the orchestrator
	AddRoom(room Room) error

	// RemoveRoom removes a room from the orchestrator
	RemoveRoom(roomID string) error

	// GetRoom retrieves a room by ID
	GetRoom(roomID string) (Room, bool)

	// GetAllRooms returns all managed rooms
	GetAllRooms() map[string]Room

	// AddConnection creates a connection between two rooms
	AddConnection(connection Connection) error

	// RemoveConnection removes a connection
	RemoveConnection(connectionID string) error

	// GetConnection retrieves a connection by ID
	GetConnection(connectionID string) (Connection, bool)

	// GetRoomConnections returns all connections for a specific room
	GetRoomConnections(roomID string) []Connection

	// GetAllConnections returns all connections
	GetAllConnections() map[string]Connection

	// MoveEntityBetweenRooms moves an entity from one room to another
	MoveEntityBetweenRooms(entityID, fromRoom, toRoom, connectionID string) error

	// CanMoveEntityBetweenRooms checks if entity movement is possible
	CanMoveEntityBetweenRooms(entityID, fromRoom, toRoom, connectionID string) bool

	// GetEntityRoom returns which room contains the entity
	GetEntityRoom(entityID string) (string, bool)

	// FindPath finds a path between rooms using connections
	FindPath(fromRoom, toRoom string, entity core.Entity) ([]string, error)

	// GetLayout returns the current layout pattern
	GetLayout() LayoutType

	// SetLayout configures the arrangement pattern
	SetLayout(layout LayoutType) error
}

RoomOrchestrator manages multiple rooms and their connections

type RoomRemovedEvent added in v0.1.1

type RoomRemovedEvent struct {
	OrchestratorID string    `json:"orchestrator_id"`
	RoomID         string    `json:"room_id"`
	Reason         string    `json:"reason,omitempty"`
	RemovedAt      time.Time `json:"removed_at"`
}

RoomRemovedEvent contains data for room removal from orchestrator events

type SimpleEntityFilter

type SimpleEntityFilter struct {
	EntityTypes []string `json:"entity_types,omitempty"`
	EntityIDs   []string `json:"entity_ids,omitempty"`
	ExcludeIDs  []string `json:"exclude_ids,omitempty"`
}

SimpleEntityFilter implements basic entity filtering

func NewSimpleEntityFilter

func NewSimpleEntityFilter() *SimpleEntityFilter

NewSimpleEntityFilter creates a new simple entity filter

func (*SimpleEntityFilter) Matches

func (f *SimpleEntityFilter) Matches(entity core.Entity) bool

Matches checks if an entity matches the filter criteria

func (*SimpleEntityFilter) WithEntityIDs

func (f *SimpleEntityFilter) WithEntityIDs(ids ...string) *SimpleEntityFilter

WithEntityIDs adds entity ID filtering

func (*SimpleEntityFilter) WithEntityTypes

func (f *SimpleEntityFilter) WithEntityTypes(types ...string) *SimpleEntityFilter

WithEntityTypes adds entity type filtering

func (*SimpleEntityFilter) WithExcludeIDs

func (f *SimpleEntityFilter) WithExcludeIDs(ids ...string) *SimpleEntityFilter

WithExcludeIDs adds entity ID exclusion

type SimplePathFinder added in v0.4.0

type SimplePathFinder struct{}

SimplePathFinder uses A* algorithm with uniform movement cost. It finds the shortest path around obstacles using hex distance as heuristic.

func NewSimplePathFinder added in v0.4.0

func NewSimplePathFinder() *SimplePathFinder

NewSimplePathFinder creates a new A* pathfinder

func (*SimplePathFinder) FindPath added in v0.4.0

func (p *SimplePathFinder) FindPath(start, goal CubeCoordinate, blocked map[CubeCoordinate]bool) []CubeCoordinate

FindPath implements PathFinder using A* algorithm. Uses hex distance as heuristic (admissible - never overestimates).

type SpatialQueryHandler

type SpatialQueryHandler struct {
	// contains filtered or unexported fields
}

SpatialQueryHandler handles spatial query events

func NewSpatialQueryHandler

func NewSpatialQueryHandler() *SpatialQueryHandler

NewSpatialQueryHandler creates a new spatial query handler

func (*SpatialQueryHandler) HandleQuery

func (h *SpatialQueryHandler) HandleQuery(ctx context.Context, query interface{}) (interface{}, error)

HandleQuery processes spatial queries

func (*SpatialQueryHandler) RegisterRoom

func (h *SpatialQueryHandler) RegisterRoom(room Room)

RegisterRoom registers a room with the query handler

func (*SpatialQueryHandler) UnregisterRoom

func (h *SpatialQueryHandler) UnregisterRoom(roomID string)

UnregisterRoom removes a room from the query handler

type SquareGrid

type SquareGrid struct {
	// contains filtered or unexported fields
}

SquareGrid implements a square grid system with D&D 5e distance rules

func NewSquareGrid

func NewSquareGrid(config SquareGridConfig) *SquareGrid

NewSquareGrid creates a new square grid with the given dimensions

func (*SquareGrid) Distance

func (sg *SquareGrid) Distance(from, to Position) float64

Distance calculates the distance between two positions using D&D 5e rules D&D 5e uses Chebyshev distance: max(|x2-x1|, |y2-y1|) This means diagonals cost the same as orthogonal movement

func (*SquareGrid) GetDimensions

func (sg *SquareGrid) GetDimensions() Dimensions

GetDimensions returns the grid dimensions

func (*SquareGrid) GetLineOfSight

func (sg *SquareGrid) GetLineOfSight(from, to Position) []Position

GetLineOfSight returns positions along the line of sight between two positions Uses Bresenham's line algorithm for grid-based line drawing

func (*SquareGrid) GetNeighbors

func (sg *SquareGrid) GetNeighbors(pos Position) []Position

GetNeighbors returns all 8 adjacent positions (including diagonals)

func (*SquareGrid) GetPositionsInCircle

func (sg *SquareGrid) GetPositionsInCircle(circle Circle) []Position

GetPositionsInCircle returns all positions within a circular area using D&D 5e distance

func (*SquareGrid) GetPositionsInCone

func (sg *SquareGrid) GetPositionsInCone(
	origin Position, direction Position, length float64, angle float64,
) []Position

GetPositionsInCone returns positions within a cone shape This is a simplified cone implementation - games may need more sophisticated cone logic

func (*SquareGrid) GetPositionsInLine

func (sg *SquareGrid) GetPositionsInLine(from, to Position) []Position

GetPositionsInLine returns positions along a line from start to end

func (*SquareGrid) GetPositionsInRange

func (sg *SquareGrid) GetPositionsInRange(center Position, radius float64) []Position

GetPositionsInRange returns all positions within a given range using D&D 5e distance

func (*SquareGrid) GetPositionsInRectangle

func (sg *SquareGrid) GetPositionsInRectangle(rect Rectangle) []Position

GetPositionsInRectangle returns all positions within a rectangular area

func (*SquareGrid) GetShape

func (sg *SquareGrid) GetShape() GridShape

GetShape returns the grid shape type

func (*SquareGrid) IsAdjacent

func (sg *SquareGrid) IsAdjacent(pos1, pos2 Position) bool

IsAdjacent checks if two positions are adjacent (within 1 square, including diagonals)

func (*SquareGrid) IsValidPosition

func (sg *SquareGrid) IsValidPosition(pos Position) bool

IsValidPosition checks if a position is valid within the grid bounds

type SquareGridConfig

type SquareGridConfig struct {
	Width  float64
	Height float64
}

SquareGridConfig holds configuration for creating a square grid

type Transition

type Transition interface {
	// GetID returns the transition ID
	GetID() TransitionID

	// GetEntity returns the entity being moved
	GetEntity() core.Entity

	// GetFromRoom returns the source room ID
	GetFromRoom() string

	// GetToRoom returns the destination room ID
	GetToRoom() string

	// GetConnection returns the connection being used
	GetConnection() Connection

	// GetProgress returns completion progress (0.0 to 1.0)
	GetProgress() float64

	// IsComplete returns true if transition is finished
	IsComplete() bool

	// GetStartTime returns when the transition began
	GetStartTime() int64

	// GetEstimatedDuration returns expected transition time
	GetEstimatedDuration() int64
}

Transition represents an entity moving between rooms

type TransitionID

type TransitionID string

TransitionID uniquely identifies a transition

type TransitionSystem

type TransitionSystem interface {
	// BeginTransition initiates movement between rooms
	BeginTransition(entityID, fromRoom, toRoom, connectionID string) (TransitionID, error)

	// CompleteTransition finalizes the movement
	CompleteTransition(transitionID TransitionID) error

	// CancelTransition cancels an in-progress transition
	CancelTransition(transitionID TransitionID) error

	// GetActiveTransitions returns all in-progress transitions
	GetActiveTransitions() []Transition

	// GetTransition retrieves a specific transition
	GetTransition(transitionID TransitionID) (Transition, bool)
}

TransitionSystem handles entity movement between rooms

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL