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, offset hex, axial hex, gridless)
- Room-based spatial organization
- Collision detection and spatial queries
- Path validation, and distance fields over a grid (Field, PathTo)
- Footprint geometry: freely placed box coverage over an explicit hex-cell universe and closed-segment contact/interior intervals (PlacedCoverage, TraceFootprint)
- Caller-supplied obstruction evaluation over canonical sight lanes (SightLanes)
- Cell-anchored footprint coverage for compatibility (Coverage)
- 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
- Cell meaning: what a cell means (blocked, costly, burning) is the game's; spatial answers where and how far, and Field is the search over that
- Thresholds: how much of a cell a footprint must cover to count is the game's; Coverage answers with fractions and has no opinion about half
- Interaction ranges: Their meanings and effects are game-specific
- Durable prop/entity occupancy: geometry queries do not place footprints into rooms or choose playable cells
- 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: Optionally publishes movement and room transition observations
Standalone rooms are directly mutable. Entity membership in rooms added to a BasicRoomOrchestrator is mutated through ManagedRoomMutator, whose verbs return spatial deltas as values. Events are observer-only and are never consumed by the orchestrator as a hidden result channel. Cross-room TransitionEntity returns the removed entity and leaves it unplaced until managed PlaceEntity chooses a destination position.
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
- Variables
- func RunPositionValidationTests(t *testing.T, grid Grid)
- type AnchorRule
- type AxialHexGrid
- func (a *AxialHexGrid) Distance(from, to Position) float64
- func (a *AxialHexGrid) GetDimensions() Dimensions
- func (a *AxialHexGrid) GetLineOfSight(from, to Position) []Position
- func (a *AxialHexGrid) GetNeighbors(pos Position) []Position
- func (a *AxialHexGrid) GetPositionsInCircle(circle Circle) []Position
- func (a *AxialHexGrid) GetPositionsInCone(origin, direction Position, length, angle float64) []Position
- func (a *AxialHexGrid) GetPositionsInLine(from, to Position) []Position
- func (a *AxialHexGrid) GetPositionsInRange(center Position, radius float64) []Position
- func (a *AxialHexGrid) GetPositionsInRectangle(rect Rectangle) []Position
- func (a *AxialHexGrid) GetShape() GridShape
- func (a *AxialHexGrid) IsAdjacent(pos1, pos2 Position) bool
- func (a *AxialHexGrid) IsValidPosition(pos Position) bool
- type AxialHexGridConfig
- type BasicConnection
- func CreateBridgeConnection(id, fromRoom, toRoom string, cost float64) *BasicConnection
- func CreateDoorConnection(id, fromRoom, toRoom string, cost float64) *BasicConnection
- func CreatePortalConnection(id, fromRoom, toRoom string, cost float64, bidirectional bool) *BasicConnection
- func CreateSecretPassageConnection(id, fromRoom, toRoom string, cost float64, requirements []string) *BasicConnection
- func CreateStairsConnection(id, fromRoom, toRoom string, cost float64, goingUp bool) *BasicConnection
- func CreateTunnelConnection(id, fromRoom, toRoom string, cost float64) *BasicConnection
- func NewBasicConnection(config BasicConnectionConfig) *BasicConnection
- func (bc *BasicConnection) AddRequirement(requirement string)
- func (bc *BasicConnection) GetConnectionType() ConnectionType
- func (bc *BasicConnection) GetFromRoom() string
- func (bc *BasicConnection) GetID() string
- func (bc *BasicConnection) GetRequirements() []string
- func (bc *BasicConnection) GetToRoom() string
- func (bc *BasicConnection) GetTraversalCost(_ core.Entity) float64
- func (bc *BasicConnection) GetType() core.EntityType
- func (bc *BasicConnection) HasRequirement(requirement string) bool
- func (bc *BasicConnection) IsPassable(_ core.Entity) bool
- func (bc *BasicConnection) IsReversible() bool
- func (bc *BasicConnection) RemoveRequirement(requirement string)
- func (bc *BasicConnection) SetPassable(passable bool)
- type BasicConnectionConfig
- type BasicRoom
- func (r *BasicRoom) CanPlaceEntity(entity core.Entity, pos Position) bool
- func (r *BasicRoom) ConnectToEventBus(bus events.EventBus)
- func (r *BasicRoom) GetAllEntities() map[string]core.Entity
- func (r *BasicRoom) GetBoundary(from, to Position) (Boundary, bool)
- func (r *BasicRoom) GetEntitiesAt(pos Position) []core.Entity
- func (r *BasicRoom) GetEntitiesInRange(center Position, radius float64) []core.Entity
- func (r *BasicRoom) GetEntityCount() int
- func (r *BasicRoom) GetEntityCubePosition(entityID string) *CubeCoordinate
- func (r *BasicRoom) GetEntityPosition(entityID string) (Position, bool)
- func (r *BasicRoom) GetGrid() Grid
- func (r *BasicRoom) GetID() string
- func (r *BasicRoom) GetLineOfSight(from, to Position) []Position
- func (r *BasicRoom) GetOccupiedPositions() []Position
- func (r *BasicRoom) GetPositionsInRange(center Position, radius float64) []Position
- func (r *BasicRoom) GetType() core.EntityType
- func (r *BasicRoom) IsBoundaryLineOfSightBlocked(from, to Position) bool
- func (r *BasicRoom) IsBoundaryMovementBlocked(from, to Position) bool
- func (r *BasicRoom) IsLineOfSightBlocked(from, to Position) bool
- func (r *BasicRoom) IsPositionOccupied(pos Position) bool
- func (r *BasicRoom) MoveEntity(entityID string, newPos Position) error
- func (r *BasicRoom) PlaceEntity(entity core.Entity, pos Position) error
- func (r *BasicRoom) RegisterBoundary(boundary Boundary) error
- func (r *BasicRoom) RemoveBoundary(from, to Position) error
- func (r *BasicRoom) RemoveEntity(entityID string) error
- func (r *BasicRoom) ToData() RoomData
- type BasicRoomConfig
- type BasicRoomOrchestrator
- func (bro *BasicRoomOrchestrator) AddConnection(connection Connection) error
- func (bro *BasicRoomOrchestrator) AddRoom(room Room) error
- func (bro *BasicRoomOrchestrator) CanMoveEntityBetweenRooms(entityIDStr, fromRoomStr, toRoomStr, connectionIDStr string) bool
- func (bro *BasicRoomOrchestrator) ConnectToEventBus(bus events.EventBus)
- func (bro *BasicRoomOrchestrator) FindPath(fromRoom, toRoom string, entity core.Entity) ([]string, error)
- func (bro *BasicRoomOrchestrator) GetAllConnections() map[string]Connection
- func (bro *BasicRoomOrchestrator) GetAllRooms() map[string]Room
- func (bro *BasicRoomOrchestrator) GetConnection(connectionIDStr string) (Connection, bool)
- func (bro *BasicRoomOrchestrator) GetEntityRoom(entityIDStr string) (string, bool)
- func (bro *BasicRoomOrchestrator) GetEventBus() events.EventBus
- func (bro *BasicRoomOrchestrator) GetID() string
- func (bro *BasicRoomOrchestrator) GetLayout() LayoutType
- func (bro *BasicRoomOrchestrator) GetRoom(roomIDStr string) (Room, bool)
- func (bro *BasicRoomOrchestrator) GetRoomConnections(roomIDStr string) []Connection
- func (bro *BasicRoomOrchestrator) GetType() core.EntityType
- func (bro *BasicRoomOrchestrator) MoveEntity(in *MoveEntityInput) (*MoveEntityOutput, error)
- func (bro *BasicRoomOrchestrator) MoveEntityBetweenRooms(entityIDStr, fromRoomStr, toRoomStr, connectionIDStr string) error
- func (bro *BasicRoomOrchestrator) PlaceEntity(in *PlaceEntityInput) (*PlaceEntityOutput, error)
- func (bro *BasicRoomOrchestrator) RemoveConnection(connectionIDStr string) error
- func (bro *BasicRoomOrchestrator) RemoveEntity(in *RemoveEntityInput) (*RemoveEntityOutput, error)
- func (bro *BasicRoomOrchestrator) RemoveRoom(roomIDStr string) error
- func (bro *BasicRoomOrchestrator) SetEventBus(bus events.EventBus)
- func (bro *BasicRoomOrchestrator) SetLayout(layout LayoutType) error
- func (bro *BasicRoomOrchestrator) TransitionEntity(in *TransitionEntityInput) (*TransitionEntityOutput, error)
- type BasicRoomOrchestratorConfig
- type Boundary
- type BoundaryAwareRoom
- type Box
- type Circle
- type Connection
- type ConnectionAddedEvent
- type ConnectionID
- type ConnectionRemovedEvent
- type ConnectionType
- type CoverageInput
- type CoverageOutput
- type CubeCoordinate
- func (c CubeCoordinate) Add(other CubeCoordinate) CubeCoordinate
- func (c CubeCoordinate) Distance(other CubeCoordinate) int
- func (c CubeCoordinate) Equals(other CubeCoordinate) bool
- func (c CubeCoordinate) GetNeighbors() []CubeCoordinate
- func (c CubeCoordinate) IsValid() bool
- func (c CubeCoordinate) Scale(factor int) CubeCoordinate
- func (c CubeCoordinate) String() string
- func (c CubeCoordinate) Subtract(other CubeCoordinate) CubeCoordinate
- func (c CubeCoordinate) ToAxial() Position
- func (c CubeCoordinate) ToOffsetCoordinate() Position
- func (c CubeCoordinate) ToOffsetCoordinateWithOrientation(orientation HexOrientation) Position
- type Dimensions
- type EntityCubePlacement
- type EntityFilter
- type EntityMovedEvent
- type EntityMovementDelta
- type EntityPlacedEvent
- type EntityPlacement
- type EntityPlacementDelta
- type EntityRemovalDelta
- type EntityRemovedEvent
- type EntityRoomTransitionEvent
- type EntityTransitionDelta
- type EventBusIntegration
- type FieldInput
- type FieldOutput
- type Footprint
- type FootprintPlacement
- type FootprintTraceInput
- type FootprintTraceOutput
- type Grid
- type GridShape
- type GridlessConfig
- type GridlessRoom
- func (gr *GridlessRoom) Distance(from, to Position) float64
- func (gr *GridlessRoom) GetDimensions() Dimensions
- func (gr *GridlessRoom) GetLineOfSight(from, to Position) []Position
- func (gr *GridlessRoom) GetNearestPosition(pos Position) Position
- func (gr *GridlessRoom) GetNeighbors(pos Position) []Position
- func (gr *GridlessRoom) GetPositionsInArc(center Position, radius float64, startAngle, endAngle float64) []Position
- func (gr *GridlessRoom) GetPositionsInCircle(circle Circle) []Position
- func (gr *GridlessRoom) GetPositionsInCone(origin Position, direction Position, length float64, angle float64) []Position
- func (gr *GridlessRoom) GetPositionsInLine(from, to Position) []Position
- func (gr *GridlessRoom) GetPositionsInRange(center Position, radius float64) []Position
- func (gr *GridlessRoom) GetPositionsInRectangle(rect Rectangle) []Position
- func (gr *GridlessRoom) GetShape() GridShape
- func (gr *GridlessRoom) IsAdjacent(pos1, pos2 Position) bool
- func (gr *GridlessRoom) IsValidPosition(pos Position) bool
- type HexEmbedding
- type HexEmbeddingConfig
- type HexGrid
- func (hg *HexGrid) CubeToOffset(cube CubeCoordinate) Position
- func (hg *HexGrid) Distance(from, to Position) float64
- func (hg *HexGrid) GetCubeNeighbors(pos Position) []CubeCoordinate
- func (hg *HexGrid) GetDimensions() Dimensions
- func (hg *HexGrid) GetHexRing(center Position, radius int) []Position
- func (hg *HexGrid) GetHexSpiral(center Position, radius int) []Position
- func (hg *HexGrid) GetLineOfSight(from, to Position) []Position
- func (hg *HexGrid) GetNeighbors(pos Position) []Position
- func (hg *HexGrid) GetOrientation() HexOrientation
- func (hg *HexGrid) GetPositionsInCircle(circle Circle) []Position
- func (hg *HexGrid) GetPositionsInCone(origin Position, direction Position, length float64, angle float64) []Position
- func (hg *HexGrid) GetPositionsInLine(from, to Position) []Position
- func (hg *HexGrid) GetPositionsInRange(center Position, radius float64) []Position
- func (hg *HexGrid) GetPositionsInRectangle(rect Rectangle) []Position
- func (hg *HexGrid) GetShape() GridShape
- func (hg *HexGrid) IsAdjacent(pos1, pos2 Position) bool
- func (hg *HexGrid) IsPointyTop() bool
- func (hg *HexGrid) IsValidPosition(pos Position) bool
- func (hg *HexGrid) OffsetToCube(pos Position) CubeCoordinate
- type HexGridConfig
- type HexOrientation
- type LayoutChangedEvent
- type LayoutType
- type ManagedRoomMutator
- type MoveEntityInput
- type MoveEntityOutput
- type OrchestratorID
- type PathFinder
- type PlaceEntityInput
- type PlaceEntityOutput
- type Placeable
- type PlaceableData
- type PlacedCoverageInput
- type Point
- type Position
- type Query
- type QueryEntitiesInRangeData
- type QueryHandler
- type QueryLineOfSightData
- type QueryMovementData
- type QueryPlacementData
- type QueryPositionsInRangeData
- type QueryResult
- type QueryUtils
- func (q *QueryUtils) QueryEntitiesInRange(ctx context.Context, center Position, radius float64, roomID string, ...) ([]core.Entity, error)
- func (q *QueryUtils) QueryLineOfSight(ctx context.Context, from, to Position, roomID string) ([]Position, bool, error)
- func (q *QueryUtils) QueryMovement(ctx context.Context, entity core.Entity, from, to Position, roomID string) (bool, []Position, float64, error)
- func (q *QueryUtils) QueryPlacement(ctx context.Context, entity core.Entity, position Position, roomID string) (bool, error)
- func (q *QueryUtils) QueryPositionsInRange(ctx context.Context, center Position, radius float64, roomID string) ([]Position, error)
- type Rectangle
- type RemoveEntityInput
- type RemoveEntityOutput
- type Room
- type RoomAddedEvent
- type RoomCreatedEvent
- type RoomData
- type RoomID
- type RoomOrchestrator
- type RoomRemovedEvent
- type SightCellInput
- type SightCellOutput
- type SightLaneInput
- type SightLaneOutput
- type SightLanesInput
- type SightLanesOutput
- type SightObstructions
- type SimpleEntityFilter
- func (f *SimpleEntityFilter) Matches(entity core.Entity) bool
- func (f *SimpleEntityFilter) WithEntityIDs(ids ...string) *SimpleEntityFilter
- func (f *SimpleEntityFilter) WithEntityTypes(types ...string) *SimpleEntityFilter
- func (f *SimpleEntityFilter) WithExcludeIDs(ids ...string) *SimpleEntityFilter
- type SimplePathFinder
- type SpatialQueryHandler
- type SquareGrid
- func (sg *SquareGrid) Distance(from, to Position) float64
- func (sg *SquareGrid) GetDimensions() Dimensions
- func (sg *SquareGrid) GetLineOfSight(from, to Position) []Position
- func (sg *SquareGrid) GetNeighbors(pos Position) []Position
- func (sg *SquareGrid) GetPositionsInCircle(circle Circle) []Position
- func (sg *SquareGrid) GetPositionsInCone(origin Position, direction Position, length float64, angle float64) []Position
- func (sg *SquareGrid) GetPositionsInLine(from, to Position) []Position
- func (sg *SquareGrid) GetPositionsInRange(center Position, radius float64) []Position
- func (sg *SquareGrid) GetPositionsInRectangle(rect Rectangle) []Position
- func (sg *SquareGrid) GetShape() GridShape
- func (sg *SquareGrid) IsAdjacent(pos1, pos2 Position) bool
- func (sg *SquareGrid) IsValidPosition(pos Position) bool
- type SquareGridConfig
- type TransitionEntityInput
- type TransitionEntityOutput
- type TraversalPathFinder
- type TraversalPredicate
- type TraversalSearchLimit
Examples ¶
Constants ¶
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
Variables ¶
var ( // ErrNoSightGrid indicates that SightLanes was called without a grid. ErrNoSightGrid = errors.New("spatial: sight lanes require a grid") // ErrNoSightObstructions indicates that SightLanes was called without obstruction reads. ErrNoSightObstructions = errors.New("spatial: sight lanes require obstruction reads") // ErrBadSightPosition indicates that a sight endpoint contains a non-finite coordinate. ErrBadSightPosition = errors.New("spatial: sight endpoints must be finite") )
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") // 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") )
var ErrBadCellWidth = errors.New("spatial: cell width must be positive")
ErrBadCellWidth reports a cell width that is not a positive length. A hex with no width has no corners and no centre, so an embedding built from one answers nothing rather than answering something plausible.
var ErrBadCoverageCell = errors.New("spatial: invalid coverage cell")
ErrBadCoverageCell reports a non-finite, fractional, or unrepresentable cell.
var ErrBadFootprint = errors.New("spatial: footprint sides must be positive")
ErrBadFootprint reports a footprint whose sides are not positive lengths.
var ErrBadFootprintPlacement = errors.New("spatial: invalid footprint placement")
ErrBadFootprintPlacement reports non-finite or unrepresentable placed geometry.
var ErrBadFootprintTrace = errors.New("spatial: invalid footprint trace")
ErrBadFootprintTrace reports non-finite endpoints or unrepresentable arithmetic.
var ErrNoFootprint = errors.New("spatial: coverage needs a footprint")
ErrNoFootprint reports coverage asked for with no shape to rasterise.
var ErrNoPassable = errors.New("spatial: field needs a passable predicate")
ErrNoPassable is returned when Passable is nil; a field with no predicate would silently treat every cell as open, which is a zero value that lies.
var ErrNoSources = errors.New("spatial: field needs at least one source")
ErrNoSources is returned when a field is asked for with nothing to flood from.
Functions ¶
func RunPositionValidationTests ¶
RunPositionValidationTests runs common position validation tests for any Grid
Types ¶
type AnchorRule ¶ added in v0.13.0
type AnchorRule int
AnchorRule says where a footprint sits relative to the cell it is placed at.
const ( // AnchorAtCentre puts the footprint's centre on the anchor cell's centre. // The anchor cell is under the footprint. AnchorAtCentre AnchorRule = iota // AnchorAtEdge puts the footprint's near edge on the anchor cell's // boundary along Facing, so the anchor cell is never under it and the // footprint's depth is measured from that boundary outward. AnchorAtEdge )
type AxialHexGrid ¶ added in v0.5.0
type AxialHexGrid struct {
// contains filtered or unexported fields
}
AxialHexGrid implements an origin-centered hex grid whose Position values are axial coordinates: Position.X = Q, Position.Y = R, S = -(Q+R). It is intentionally distinct from HexGrid, which interprets the same fields as bounded offset column/row coordinates and supports orientation selection.
Distance uses the cube formula: (|ΔQ| + |ΔR| + |ΔS|) / 2, so adjacent hexes are distance 1 in every axial direction.
func NewAxialHexGrid ¶ added in v0.5.0
func NewAxialHexGrid(config AxialHexGridConfig) *AxialHexGrid
NewAxialHexGrid creates a new hex grid that treats Position.X/Y as axial Q/R coordinates. Use this instead of NewHexGrid when positions are stored in axial (not offset) form.
func (*AxialHexGrid) Distance ¶ added in v0.5.0
func (a *AxialHexGrid) Distance(from, to Position) float64
Distance returns the hex distance between two axial positions. Interprets Position.X as Q and Position.Y as R; derives S = -(Q+R). Uses the cube formula: (|ΔQ| + |ΔR| + |ΔS|) / 2.
func (*AxialHexGrid) GetDimensions ¶ added in v0.5.0
func (a *AxialHexGrid) GetDimensions() Dimensions
GetDimensions returns the configured bounding-box dimensions.
func (*AxialHexGrid) GetLineOfSight ¶ added in v0.5.0
func (a *AxialHexGrid) GetLineOfSight(from, to Position) []Position
GetLineOfSight returns hex positions along the line from from to to using cube-coordinate linear interpolation (standard hex line algorithm).
func (*AxialHexGrid) GetNeighbors ¶ added in v0.5.0
func (a *AxialHexGrid) GetNeighbors(pos Position) []Position
GetNeighbors returns the 6 axial positions adjacent to pos.
func (*AxialHexGrid) GetPositionsInCircle ¶ added in v0.5.0
func (a *AxialHexGrid) GetPositionsInCircle(circle Circle) []Position
GetPositionsInCircle returns all valid axial positions within the given circle.
func (*AxialHexGrid) GetPositionsInCone ¶ added in v0.5.0
func (a *AxialHexGrid) GetPositionsInCone(origin, direction Position, length, angle float64) []Position
GetPositionsInCone returns axial positions within a cone.
func (*AxialHexGrid) GetPositionsInLine ¶ added in v0.5.0
func (a *AxialHexGrid) GetPositionsInLine(from, to Position) []Position
GetPositionsInLine returns the positions along a line from from to to.
func (*AxialHexGrid) GetPositionsInRange ¶ added in v0.5.0
func (a *AxialHexGrid) GetPositionsInRange(center Position, radius float64) []Position
GetPositionsInRange returns all valid axial positions within radius hex steps of center.
func (*AxialHexGrid) GetPositionsInRectangle ¶ added in v0.5.0
func (a *AxialHexGrid) GetPositionsInRectangle(rect Rectangle) []Position
GetPositionsInRectangle returns all valid axial positions within the given rectangle (treating X as Q and Y as R for the bounding box).
func (*AxialHexGrid) GetShape ¶ added in v0.5.0
func (a *AxialHexGrid) GetShape() GridShape
GetShape returns GridShapeHex.
func (*AxialHexGrid) IsAdjacent ¶ added in v0.5.0
func (a *AxialHexGrid) IsAdjacent(pos1, pos2 Position) bool
IsAdjacent reports whether two positions are adjacent (hex distance <= 1).
func (*AxialHexGrid) IsValidPosition ¶ added in v0.5.0
func (a *AxialHexGrid) IsValidPosition(pos Position) bool
IsValidPosition reports true when (Q, R) falls within ±SpanWidth/2 along Q and ±SpanHeight/2 along R. The check is centered on the origin because axial hex coordinates are symmetric — positions can have negative Q or R. This differs from SquareGrid/HexGrid whose bounds start at (0,0).
type AxialHexGridConfig ¶ added in v0.5.0
AxialHexGridConfig holds configuration for creating an AxialHexGrid. SpanWidth and SpanHeight define the total span of valid axial coordinates. IsValidPosition checks are centered on the origin — a position (Q, R) is valid when Q ∈ [-SpanWidth/2, SpanWidth/2) and R ∈ [-SpanHeight/2, SpanHeight/2). This differs from SquareGrid/HexGrid, whose Width/Height are one-sided bounds starting at (0,0). Pass large values (e.g. 1000×1000) for encounter rooms where position bounds are not meaningful.
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 ¶
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 ¶
CanPlaceEntity checks if an entity can be placed at a position
func (*BasicRoom) ConnectToEventBus ¶ added in v0.1.1
ConnectToEventBus connects the room to an event bus for typed event publishing
func (*BasicRoom) GetAllEntities ¶
GetAllEntities returns all entities in the room
func (*BasicRoom) GetBoundary ¶ added in v0.6.0
GetBoundary returns the normalized boundary for an endpoint pair regardless of the direction passed by the caller.
func (*BasicRoom) GetEntitiesAt ¶
GetEntitiesAt returns all entities at a specific position
func (*BasicRoom) GetEntitiesInRange ¶
GetEntitiesInRange returns entities within a given range
func (*BasicRoom) GetEntityCount ¶
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 ¶
GetEntityPosition returns the position of an entity
func (*BasicRoom) GetLineOfSight ¶
GetLineOfSight returns positions along the line of sight
func (*BasicRoom) GetOccupiedPositions ¶
GetOccupiedPositions returns all positions that have entities
func (*BasicRoom) GetPositionsInRange ¶
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) IsBoundaryLineOfSightBlocked ¶ added in v0.6.0
IsBoundaryLineOfSightBlocked reports whether a registered boundary blocks line of sight across the crossing between two positions.
func (*BasicRoom) IsBoundaryMovementBlocked ¶ added in v0.6.0
IsBoundaryMovementBlocked reports whether a registered boundary blocks the crossing between two positions.
func (*BasicRoom) IsLineOfSightBlocked ¶
IsLineOfSightBlocked reports whether sight between two cells is blocked.
SIGHT IS NOT ONE LINE. A single centre-to-centre ray was the rule here until rpg-toolkit#1022, and it was wrong in two ways that turned out to be one: squares disagreed with themselves by direction (Bresenham steps X first one way and Y first the other, so A→B and B→A are different cells), and every grid family blocked far more than the game's own rule allows. Measured against 5e's stated test — you can see a target if a line from ANY corner of your space to ANY corner of theirs is unobstructed — the old rule blocked 3.6x too many pairs on squares and 4.5x too many on hexes, and hid about one in seven things a player should have been able to see. It never blocked too little; it was uniformly stricter than the game.
So sight asks for a LANE rather than a line: blocked only when the direct lane is obstructed AND so is every lane from a neighbouring cell that does not give ground. Corner-clipping stops costing you the whole sightline, which is what a player at the table already assumes.
This reaches the corner rule exactly on squares and within 0.01% of it on hexes. The remaining gap is the price of staying grid-native rather than evaluating every pair of cell-polygon corners in the plane.
SYMMETRY IS STRUCTURAL, not incidental: every lane is rasterized on the canonical ray, and the neighbour lanes are explored from both ends, so the rule has no direction left to disagree about. It is pinned as a law over fuzzed rooms in every grid family.
Boundaries are hard obstructions and remain absolute. Entities are soft obstructions that eligible neighbour lanes can bypass. Gridless rooms keep a single lane because their positions have no cell extent to lean around.
The common case costs exactly what it used to. A pair whose direct lane is clear returns on that first test, and most pairs are clear — the extra work lands only where the answer used to be wrong. That matters because callers run this O(range²) per viewer.
func (*BasicRoom) IsPositionOccupied ¶
IsPositionOccupied checks if a position is occupied
func (*BasicRoom) MoveEntity ¶
MoveEntity moves an entity to a new position
func (*BasicRoom) PlaceEntity ¶
PlaceEntity places an entity at a specific position
func (*BasicRoom) RegisterBoundary ¶ added in v0.6.0
RegisterBoundary validates and registers an undirected boundary between two adjacent in-grid positions. Endpoint order is normalized and registering the same pair replaces its blocking flags.
func (*BasicRoom) RemoveBoundary ¶ added in v0.6.0
RemoveBoundary validates an undirected boundary pair and removes it. Removing an otherwise-valid pair with no registered boundary is a no-op.
func (*BasicRoom) RemoveEntity ¶
RemoveEntity removes an entity from the room
type BasicRoomConfig ¶
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. Entities already in the room are indexed synchronously. Adding a room that duplicates an entity already indexed in another managed room fails without changing the orchestrator.
func (*BasicRoomOrchestrator) CanMoveEntityBetweenRooms ¶
func (bro *BasicRoomOrchestrator) CanMoveEntityBetweenRooms( entityIDStr, fromRoomStr, toRoomStr, connectionIDStr string, ) bool
CanMoveEntityBetweenRooms checks whether an indexed, physically present entity can depart through the named connection.
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) MoveEntity ¶ added in v0.8.0
func (bro *BasicRoomOrchestrator) MoveEntity(in *MoveEntityInput) (*MoveEntityOutput, error)
MoveEntity moves an indexed entity within its managed room and returns the completed movement. The index remains unchanged.
func (*BasicRoomOrchestrator) MoveEntityBetweenRooms ¶
func (bro *BasicRoomOrchestrator) MoveEntityBetweenRooms( entityIDStr, fromRoomStr, toRoomStr, connectionIDStr string, ) error
MoveEntityBetweenRooms preserves the legacy signature for source compatibility. It performs a logical transition only: the entity is removed from the source and remains unplaced and unindexed until PlaceEntity chooses a destination position. New compositions should call TransitionEntity and consume its explicit output.
func (*BasicRoomOrchestrator) PlaceEntity ¶ added in v0.8.0
func (bro *BasicRoomOrchestrator) PlaceEntity(in *PlaceEntityInput) (*PlaceEntityOutput, error)
PlaceEntity places an entity in a managed room and synchronously indexes its membership. Room publication remains an observer tail and is not consumed by the orchestrator.
func (*BasicRoomOrchestrator) RemoveConnection ¶
func (bro *BasicRoomOrchestrator) RemoveConnection(connectionIDStr string) error
RemoveConnection removes a connection.
func (*BasicRoomOrchestrator) RemoveEntity ¶ added in v0.8.0
func (bro *BasicRoomOrchestrator) RemoveEntity(in *RemoveEntityInput) (*RemoveEntityOutput, error)
RemoveEntity removes an indexed entity from its managed room, synchronously clears membership, and returns both the entity and spatial removal.
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.
func (*BasicRoomOrchestrator) TransitionEntity ¶ added in v0.8.0
func (bro *BasicRoomOrchestrator) TransitionEntity( in *TransitionEntityInput, ) (*TransitionEntityOutput, error)
TransitionEntity removes an entity from its source room through a passable connection and leaves it deliberately unplaced and unindexed. The returned entity and transition value let a composition choose a destination position and finish the operation through PlaceEntity.
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 Boundary ¶ added in v0.6.0
type Boundary struct {
// From is one endpoint of the crossing. Registered boundaries expose the
// lexicographically first endpoint here.
From Position
// To is the other endpoint of the crossing. Registered boundaries expose
// the lexicographically second endpoint here.
To Position
// BlocksMovement reports whether an entity may cross this boundary.
BlocksMovement bool
// BlocksLineOfSight reports whether line of sight may cross this boundary.
BlocksLineOfSight bool
}
Boundary represents an undirected crossing between two adjacent grid positions. BasicRoom normalizes From and To when the boundary is registered, so the same boundary is found regardless of the caller's direction.
Boundaries are runtime spatial state only. Higher-level domains own any authored or persisted representation and register their current crossings when reconstructing a room.
type BoundaryAwareRoom ¶ added in v0.6.0
type BoundaryAwareRoom interface {
Room
// RegisterBoundary validates and normalizes an undirected adjacent in-grid
// boundary. Registering an existing pair updates its blocking flags.
RegisterBoundary(boundary Boundary) error
// RemoveBoundary validates an undirected adjacent in-grid pair and removes
// its boundary. Removing an unregistered valid pair is a no-op.
RemoveBoundary(from, to Position) error
// GetBoundary returns the normalized boundary registered for either order
// of the endpoint pair.
GetBoundary(from, to Position) (Boundary, bool)
// IsBoundaryMovementBlocked reports whether the endpoint crossing blocks
// movement. It returns false when no boundary is registered.
IsBoundaryMovementBlocked(from, to Position) bool
// IsBoundaryLineOfSightBlocked reports whether the endpoint crossing blocks
// line of sight. It returns false when no boundary is registered.
IsBoundaryLineOfSightBlocked(from, to Position) bool
}
BoundaryAwareRoom is an optional Room capability for registering and querying barriers between adjacent grid positions. It is separate from Room so legacy Room implementations and callers remain compatible.
type Box ¶ added in v0.13.0
Box is a rectangular footprint: W across the bearing it is drawn at, D along it. Both are in the caller's unit, the same one the embedding's cell width was measured in.
type Circle ¶
Circle represents a circular area
func (Circle) Intersects ¶
Intersects checks if this circle intersects with another 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 CoverageInput ¶ added in v0.13.0
type CoverageInput struct {
Footprint Footprint
At Position
Facing float64
Anchor AnchorRule
}
CoverageInput places a footprint on the plane: which cell it is anchored at, which way it faces, and whether it sits on that cell or in front of it.
Facing is in degrees from east in the embedding's numeric plane, as HexEmbedding.Bearing reports it; positive 90 points south. Any finite angle is legal: nothing here snaps to a grid axis.
type CoverageOutput ¶ added in v0.13.0
CoverageOutput is which cells the footprint lies on, and how much of each.
Cells holds the fraction of each cell's area under the footprint, in (0, 1]. A cell the footprint misses is absent rather than zero. Edges — the cell boundaries the footprint's outline crosses, which is how a thin thing that covers under half of everything still blocks something — arrive with the first thin prop that needs them.
func Coverage ¶ added in v0.13.0
func Coverage(emb HexEmbedding, g Grid, in CoverageInput) (CoverageOutput, error)
Coverage rasterises a footprint at a transform onto a grid, returning the fraction of each cell's area under it.
Fractions come out; thresholds go in above. What a covered cell means — caught by the blast, blocked, difficult — is the game's, and a rule that counts a cell at half would be a different number in a different rulebook without this changing. Only cells the grid considers valid are reported.
Returns ErrNoFootprint when no shape was given, ErrBadFootprint when its sides are not positive and finite, ErrBadCellWidth when the embedding has no frame, and ErrBadFootprintPlacement for an invalid anchor or facing.
func PlacedCoverage ¶ added in v0.14.0
func PlacedCoverage(in PlacedCoverageInput) (CoverageOutput, error)
PlacedCoverage returns area fractions for a freely placed footprint over Cells. Misses are absent. Inputs are not retained; errors return no partial map.
Example ¶
package main
import (
"fmt"
"github.com/KirkDiggler/rpg-toolkit/tools/spatial"
)
func main() {
emb := spatial.NewHexEmbedding(spatial.HexEmbeddingConfig{CellWidth: 5})
placement := spatial.FootprintPlacement{
Footprint: spatial.Footprint{Box: &spatial.Box{W: 4, D: 10}},
Origin: spatial.Point{X: 3.25, Y: -1.75}, Facing: 37,
}
coverage, err := spatial.PlacedCoverage(spatial.PlacedCoverageInput{
Embedding: emb, Placement: placement,
Cells: []spatial.Position{{}, {X: 1}, {Y: -1}},
})
if err != nil {
fmt.Println(err)
return
}
trace, err := spatial.TraceFootprint(spatial.FootprintTraceInput{
Placement: placement,
From: spatial.Point{X: -5}, To: spatial.Point{X: 8},
})
if err != nil {
fmt.Println(err)
return
}
fmt.Println(coverage.Cells, trace.Contact, trace.Interior)
}
Output:
type CubeCoordinate ¶
CubeCoordinate represents a position in cube coordinate system (for hex grids) In hex grids, cube coordinates simplify distance and neighbor calculations
func AxialToCube ¶ added in v0.11.0
func AxialToCube(pos Position) CubeCoordinate
AxialToCube converts an axial Position (X=Q, Y=R) to a CubeCoordinate. It is the inverse of CubeCoordinate.ToAxial and, with it, the ONE definition of the axial basis this package speaks.
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 ¶
func (c CubeCoordinate) Add(other CubeCoordinate) CubeCoordinate
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 whether the cube coordinate is valid (x + y + z == 0). It compares exact unsigned magnitudes by sign instead of adding native ints, so valid and invalid coordinates remain distinguishable at int extremes.
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) ToAxial ¶ added in v0.11.0
func (c CubeCoordinate) ToAxial() Position
ToAxial reads a cube coordinate as the axial Position (X=Q, Y=R) this package hands out for hex cells: Q is cube X and R is cube Z.
WHICH TWO AXES is not a free choice, even though the lattice cannot tell (rpg-toolkit#1150). Any pair gives identical distance, neighbours and sight; what differs is the PICTURE. "Pointy-top" means the R axis runs straight across the screen, and the standard formula — x = √3·(q + r/2), y = 1.5·r — assumes R is cube Z. Reading Y as R instead drew the reference tomb as a diagonal band and survived every round-trip test in three modules. This matches OffsetCoordinateToCubeWithOrientation, which already puts the authored row in Z, and is pinned by hex_axial_basis_test.go against the pixel formula rather than against another conversion.
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 ¶
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 caller-defined type of the entity (e.g., "actor", "obstacle", "marker")
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 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
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 EntityMovementDelta ¶ added in v0.8.0
EntityMovementDelta describes a completed move within one managed room.
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 caller-defined type of the entity (e.g., "actor", "obstacle", "marker")
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 EntityPlacementDelta ¶ added in v0.8.0
EntityPlacementDelta describes a completed placement in a managed room.
type EntityRemovalDelta ¶ added in v0.8.0
EntityRemovalDelta describes a completed removal from a managed room.
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 EntityTransitionDelta ¶ added in v0.8.0
type EntityTransitionDelta struct {
EntityID core.EntityID
FromRoom RoomID
ToRoom RoomID
ConnectionID ConnectionID
PlacementRequired bool
}
EntityTransitionDelta describes a logical room transition after departure. PlacementRequired is true because TransitionEntity does not choose a destination position and therefore does not claim destination membership.
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 FieldInput ¶ added in v0.12.0
type FieldInput struct {
Sources []Position
Passable func(from, to Position) bool
Cost func(from, to Position) int
Limit int
}
FieldInput asks for a distance field: every cell reachable from Sources under Passable, with its cost-weighted distance.
Sources is plural on purpose — a field flooded from several threats at once is a different question than a field flooded from one, and both are reads of the same primitive.
Passable is required: a field with no predicate would silently treat every cell as open, which is a zero value that lies. Cost nil means one per step, which makes the flood a breadth-first search. Limit 0 means unbounded.
type FieldOutput ¶ added in v0.12.0
FieldOutput is the field. Dist holds every reached cell keyed by position, including the sources at distance 0. Prev holds the cell each reached cell was entered from, and is absent for sources.
func Field ¶ added in v0.12.0
func Field(g Grid, in FieldInput) (FieldOutput, error)
Field floods outward from Sources over g.GetNeighbors, Dijkstra by Cost, and returns the distance and predecessor of every cell it reached.
It answers geometry and search over geometry only. What a cell means — blocked, costly, burning — is the game's, and reaches the field through Passable and Cost. Reach is a field with a Limit, a path is PathTo on the same field, and a blast that spreads around corners is a field under a walls-only predicate.
Neighbors are relaxed in X-then-Y order so equal-cost ties break the same way on every run and on every grid.
func (FieldOutput) PathTo ¶ added in v0.12.0
func (f FieldOutput) PathTo(goal Position) ([]Position, bool)
PathTo reads the field backwards from goal to the source that reached it. The returned path excludes the source and ends at goal, matching the contract a route has always had. ok is false when goal was never reached.
type Footprint ¶ added in v0.13.0
type Footprint struct {
Box *Box `json:"box,omitempty"`
}
Footprint is a shape in the plane. Today a box is the only one; a polygon joins it when something in the game is shaped like one.
type FootprintPlacement ¶ added in v0.14.0
type FootprintPlacement struct {
Footprint Footprint
Origin Point
Facing float64
LocalOffset Point
}
FootprintPlacement positions a footprint in the caller's continuous plane. LocalOffset is in the footprint's own along/across axes, before Facing.
type FootprintTraceInput ¶ added in v0.14.0
type FootprintTraceInput struct {
Placement FootprintPlacement
From Point
To Point
}
FootprintTraceInput describes a closed planar segment and one placed footprint.
type FootprintTraceOutput ¶ added in v0.14.0
FootprintTraceOutput distinguishes closed contact from positive-length interior. Enter and Leave parameterize the contact interval; a miss is the zero value.
func TraceFootprint ¶ added in v0.14.0
func TraceFootprint(in FootprintTraceInput) (FootprintTraceOutput, error)
TraceFootprint reports contact with the full rectangle, independent of any grid. It does not decide whether contact blocks movement, sight, or an attack.
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 GridlessConfig ¶
GridlessConfig holds configuration for creating a gridless room
type GridlessRoom ¶
type GridlessRoom struct {
// contains filtered or unexported fields
}
GridlessRoom implements a continuous gridless spatial system 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 with arc-shaped areas
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 HexEmbedding ¶ added in v0.13.0
type HexEmbedding struct {
// contains filtered or unexported fields
}
HexEmbedding is a hex grid's embedding in the plane: where a cell's centre sits, where its six corners are, and the bearing from one cell to another.
It is the frame every areal question is answered in. Distance and adjacency are answered in axial coordinates, which are orientation-free and unitless; coverage, angles and areas are not, and this is what they need.
The zero value has no frame and answers zero points and no bearings. Build one with NewHexEmbedding, and check the config with HexEmbeddingConfig.Validate first if the width came from outside.
func NewHexEmbedding ¶ added in v0.13.0
func NewHexEmbedding(c HexEmbeddingConfig) HexEmbedding
NewHexEmbedding builds the embedding for one orientation at one cell width.
X runs east and Y runs south, the screen's axes and the axes the authored grids already run on. A cell's circumradius — the distance from its centre to a corner — is CellWidth/sqrt(3) under both orientations, because across the flats is sqrt(3) circumradii either way.
A CellWidth that is not positive or finite yields an embedding with no frame, whose methods return zero points and report no bearing. That is deliberate: the alternative is a panic in the middle of a raster, or a silent frame of some invented size. Callers that take a width from content validate the config.
func (HexEmbedding) Bearing ¶ added in v0.13.0
func (e HexEmbedding) Bearing(from, to Position) (degrees float64, ok bool)
Bearing reports the direction from one cell's centre to another's, in degrees within [0, 360), measured from east in the numeric plane. Because Y runs south, positive 90 points south.
Reports false when the two cells are the same, and when the embedding has no frame: neither has a direction at all, and a caller that wants to say "toward yourself" words that refusal for itself.
func (HexEmbedding) CellCentre ¶ added in v0.13.0
func (e HexEmbedding) CellCentre(cell Position) Point
CellCentre is the point at the middle of a cell.
func (HexEmbedding) CellCorners ¶ added in v0.13.0
func (e HexEmbedding) CellCorners(cell Position) [6]Point
CellCorners is a cell's six corners in the plane, in boundary order. The polygon is convex and closed by its first point.
type HexEmbeddingConfig ¶ added in v0.13.0
type HexEmbeddingConfig struct {
// Orientation is which way the hexes are turned. The same lengths hold
// under both: a flat-top hex is a pointy-top hex rotated thirty degrees.
Orientation HexOrientation
// CellWidth is the across-the-flats width of one cell, in the caller's
// unit. Must be positive.
CellWidth float64
}
HexEmbeddingConfig describes one hex grid's place in the plane.
CellWidth is measured ACROSS THE FLATS — the distance between a hex's two parallel sides, which is also the distance between the centres of two neighbouring cells. It is in the caller's own unit: spatial holds no notion of feet, so a rulebook that calls a cell five feet passes 5 and reads everything back in feet.
func (HexEmbeddingConfig) Validate ¶ added in v0.13.0
func (c HexEmbeddingConfig) Validate() error
Validate reports whether the config describes a usable plane. Returns ErrBadCellWidth when CellWidth is not positive or finite.
type HexGrid ¶
type HexGrid struct {
// contains filtered or unexported fields
}
HexGrid implements a bounded hex grid whose Position values are non-negative offset column/row coordinates. Orientation selects pointy-top or flat-top offset conversion; cube coordinates are used internally for hex mathematics.
func NewHexGrid ¶
func NewHexGrid(config HexGridConfig) *HexGrid
NewHexGrid creates a bounded offset-coordinate hex grid with the given dimensions. The zero-value orientation is pointy-top.
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 ¶
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 ¶
GetHexRing returns positions forming a ring at a specific distance from center This is a hex-specific function for ring-shaped areas
func (*HexGrid) GetHexSpiral ¶
GetHexSpiral returns positions in a spiral pattern from center outward Useful for area effects that expand outward
func (*HexGrid) GetLineOfSight ¶
GetLineOfSight returns positions along the line of sight between two positions Uses cube coordinate lerp for hex line drawing
func (*HexGrid) GetNeighbors ¶
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 ¶
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 ¶
GetPositionsInLine returns positions along a line from start to end
func (*HexGrid) GetPositionsInRange ¶
GetPositionsInRange returns all positions within a given range using hex distance
func (*HexGrid) GetPositionsInRectangle ¶
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) IsAdjacent ¶
IsAdjacent checks if two positions are adjacent (within 1 hex)
func (*HexGrid) IsPointyTop ¶ added in v0.2.0
IsPointyTop returns true if the grid uses pointy-top orientation
func (*HexGrid) IsValidPosition ¶
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 zero-value 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 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 ManagedRoomMutator ¶ added in v0.8.0
type ManagedRoomMutator interface {
PlaceEntity(in *PlaceEntityInput) (*PlaceEntityOutput, error)
MoveEntity(in *MoveEntityInput) (*MoveEntityOutput, error)
RemoveEntity(in *RemoveEntityInput) (*RemoveEntityOutput, error)
TransitionEntity(in *TransitionEntityInput) (*TransitionEntityOutput, error)
}
ManagedRoomMutator is the supported mutation seam for entity membership in rooms owned by a BasicRoomOrchestrator. Hosts serialize mutating calls; read methods remain safe to call concurrently.
type MoveEntityInput ¶ added in v0.8.0
MoveEntityInput names a managed entity and its new in-room position.
type MoveEntityOutput ¶ added in v0.8.0
type MoveEntityOutput struct {
Delta EntityMovementDelta
}
MoveEntityOutput returns the completed spatial movement as a value.
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 PlaceEntityInput ¶ added in v0.8.0
PlaceEntityInput names an entity, managed room, and destination position.
type PlaceEntityOutput ¶ added in v0.8.0
type PlaceEntityOutput struct {
Delta EntityPlacementDelta
}
PlaceEntityOutput returns the completed spatial placement as a value.
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 PlacedCoverageInput ¶ added in v0.14.0
type PlacedCoverageInput struct {
Embedding HexEmbedding
Placement FootprintPlacement
Cells []Position
}
PlacedCoverageInput declares the exact axial cells to inspect in an embedding.
type Point ¶ added in v0.13.0
Point is a location in the plane, in whatever unit the caller measured CellWidth in. Position addresses a cell; Point addresses the continuous space that cell occupies, and only geometry that needs an area or an angle ever leaves the one for the other.
type Position ¶
Position represents a spatial position in 2D space NOTE: Distance calculations are grid-dependent and handled by Grid implementations
func CanonicalBoundaryRay ¶ added in v0.6.0
CanonicalBoundaryRay returns the one deterministic grid ray for an unordered endpoint pair, oriented from from toward to. It derives the ray once from lexicographically ordered endpoints, then reverses it when the caller requested the opposite direction. Boundary traversal therefore crosses identical physical edges in both directions while callers retain their requested path direction.
func (Position) Normalize ¶
Normalize returns a normalized version of the position (for vector math)
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) Intersects ¶
Intersects checks if this rectangle intersects with another rectangle
type RemoveEntityInput ¶ added in v0.8.0
RemoveEntityInput names an entity to remove from a managed room.
type RemoveEntityOutput ¶ added in v0.8.0
type RemoveEntityOutput struct {
Entity core.Entity
Delta EntityRemovalDelta
}
RemoveEntityOutput returns the removed entity and completed spatial delta.
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 every eligible sight lane is obstructed.
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 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 performs a logical departure through a connection.
// The entity is unplaced until ManagedRoomMutator.PlaceEntity selects a
// destination position. Prefer TransitionEntity for its explicit output.
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 is the legacy topology and query contract for multiple rooms. BasicRoomOrchestrator also implements the additive ManagedRoomMutator entity-membership seam; this interface stays unchanged for source compatibility.
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 SightCellInput ¶ added in v0.15.0
type SightCellInput struct {
At Position
}
SightCellInput identifies an alternate origin whose opacity should be read.
type SightCellOutput ¶ added in v0.15.0
type SightCellOutput struct {
Blocked bool
}
SightCellOutput reports whether a cell is opaque as an alternate origin.
type SightLaneInput ¶ added in v0.15.0
SightLaneInput describes one lane whose obstruction facts should be read. Ray is the canonical grid ray from From toward To. Implementations must treat it as read-only and must not retain it after Along returns.
type SightLaneOutput ¶ added in v0.15.0
SightLaneOutput reports the obstruction facts for one lane. A hard block cannot be bypassed by alternate lanes; a soft block can be bypassed, though either kind blocks the lane on which it is reported.
type SightLanesInput ¶ added in v0.15.0
type SightLanesInput struct {
Grid Grid
From Position
To Position
Obstructions SightObstructions
}
SightLanesInput contains the collaborators and endpoints for a sight query.
type SightLanesOutput ¶ added in v0.15.0
type SightLanesOutput struct {
Blocked bool
}
SightLanesOutput reports whether every eligible lane is blocked.
func SightLanes ¶ added in v0.15.0
func SightLanes(in SightLanesInput) (SightLanesOutput, error)
SightLanes evaluates the direct grid lane and, for a soft direct obstruction, progress-making alternate lanes from both endpoints. A hard direct obstruction is absolute, and gridless queries evaluate only the direct lane. It returns the zero output with any validation or obstruction callback error.
type SightObstructions ¶ added in v0.15.0
type SightObstructions interface {
Along(SightLaneInput) (SightLaneOutput, error)
At(SightCellInput) (SightCellOutput, error)
}
SightObstructions supplies obstruction facts for a SightLanes query. Along reads a canonical lane and At reads whether an alternate origin is opaque. Implementations should expose a stable view for the whole query.
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).
func (*SimplePathFinder) FindPathWithTraversal ¶ added in v0.6.0
func (p *SimplePathFinder) FindPathWithTraversal( start, goal CubeCoordinate, blocked map[CubeCoordinate]bool, canTraverse TraversalPredicate, limit TraversalSearchLimit, ) []CubeCoordinate
FindPathWithTraversal uses A* while also requiring each adjacent step to be permitted by canTraverse. Traversal-aware searches require an explicit limit because a predicate can seal an otherwise-unblocked goal on the unbounded hex plane. It preserves legacy FindPath behavior by leaving that API unbounded.
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 using Chebyshev distance
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 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 Chebyshev-radius area
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 Chebyshev 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 ¶
SquareGridConfig holds configuration for creating a square grid
type TransitionEntityInput ¶ added in v0.8.0
type TransitionEntityInput struct {
EntityID core.EntityID
FromRoom RoomID
ToRoom RoomID
ConnectionID ConnectionID
}
TransitionEntityInput names a logical transition through a connection.
type TransitionEntityOutput ¶ added in v0.8.0
type TransitionEntityOutput struct {
Entity core.Entity
Departure EntityRemovalDelta
Transition EntityTransitionDelta
}
TransitionEntityOutput returns the removed entity, its departure, and the logical transition that a composition must finish with PlaceEntity.
type TraversalPathFinder ¶ added in v0.6.0
type TraversalPathFinder interface {
// FindPathWithTraversal returns a path while honoring blocked cells and a
// traversal predicate. A nil predicate permits every adjacent crossing.
// Search is bounded by limit; it returns an empty slice when no route fits
// within the bound or when no route exists.
FindPathWithTraversal(
start, goal CubeCoordinate,
blocked map[CubeCoordinate]bool,
canTraverse TraversalPredicate,
limit TraversalSearchLimit,
) []CubeCoordinate
}
TraversalPathFinder is an optional extension for path finders that can consult a traversal predicate in addition to legacy blocked cells.
type TraversalPredicate ¶ added in v0.6.0
type TraversalPredicate func(from, to CubeCoordinate) bool
TraversalPredicate decides whether A* may traverse one directed adjacent pair. It must return true to permit the step from to. A predicate can model blocked crossings such as closed boundaries without converting them into blocked cells.
type TraversalSearchLimit ¶ added in v0.6.0
type TraversalSearchLimit struct {
// MaxSteps is the inclusive path-length and search-cost bound.
MaxSteps int
}
TraversalSearchLimit bounds predicate-aware searches on the otherwise unbounded hex plane. MaxSteps is the inclusive maximum number of crossings in a returned path. A non-positive limit permits no path unless start equals goal, and a limit shorter than the direct distance cannot reach the goal.
The limit is also a deterministic safety contract: A* never adds a position whose path cost exceeds MaxSteps, so a predicate that seals an unblocked goal terminates after examining a finite search area. Callers should set MaxSteps above the direct distance by enough steps for the detours their map allows.
Source Files
¶
- basic_orchestrator.go
- boundary.go
- connection.go
- connection_helpers.go
- coverage.go
- data.go
- doc.go
- embedding.go
- events.go
- field.go
- footprint_trace.go
- gridless.go
- hex_grid.go
- ids.go
- interfaces.go
- managed_membership.go
- orchestrator.go
- pathfinder.go
- placed_coverage.go
- placed_footprint.go
- position.go
- query_handler.go
- query_utils.go
- room.go
- sight_lanes.go
- square_grid.go
- test_helpers.go
- topics.go