event_queue

package
v0.1.0-preview.1 Latest Latest
Warning

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

Go to latest
Published: Oct 12, 2026 License: MIT Imports: 4 Imported by: 0

Documentation

Index

Constants

View Source
const (
	// PriorityCritical - Highest priority, interrupts everything
	// Use for: Fatal errors, system dialogs, save corruption warnings
	// Examples: "Game must exit", "Cannot continue", critical system messages
	PriorityCritical = 1

	// PriorityHigh - High priority gameplay events
	// Use for: Moongate transitions, ship collisions, environmental hazards, combat encounters
	// Examples: Moongate teleportation, lava damage, earthquake, ship collision
	// Interrupts: Normal and Low priority queues
	PriorityHigh = 2

	// PriorityMedium - User-initiated interactions
	// Use for: Player commands (Open, Talk, Search), dialog interactions, NPC conversations
	// Examples: Dialog workflows, command sequences, conversation trees
	// Interrupts: Normal and Low priority queues
	PriorityMedium = 3

	// PriorityNormal - Standard gameplay operations
	// Use for: Sailing, camping, background state updates, routine gameplay
	// Examples: Automatic sailing, camping sequences, time progression
	// Interrupted by: Critical, High, Medium priorities
	PriorityNormal = 5

	// PriorityLow - Background effects and animations
	// Use for: Visual effects, ambient animations, non-critical sound triggers
	// Examples: Kapow effects, missile animations, background flourishes
	// Interrupted by: All higher priorities
	PriorityLow = 10
)

Variables

This section is empty.

Functions

This section is empty.

Types

type AnimateEvent

type AnimateEvent struct {
	DurationMs uint64                 // Total animation duration in milliseconds
	ElapsedMs  uint64                 // Time elapsed so far
	OnFrame    func(progress float32) // Called each frame with progress 0.0-1.0
	OnComplete func()                 // Optional callback when animation completes
}

AnimateEvent provides smooth frame-by-frame animations with progress callbacks

func NewAnimateEvent

func NewAnimateEvent(durationMs uint64, onFrame func(float32)) *AnimateEvent

NewAnimateEvent creates a new animation event

func NewAnimateEventWithCallback

func NewAnimateEventWithCallback(durationMs uint64, onFrame func(float32), onComplete func()) *AnimateEvent

NewAnimateEventWithCallback creates a new animation event with completion callback

func (*AnimateEvent) Execute

func (a *AnimateEvent) Execute(deltaMs uint64) EventResult

Execute updates the animation and calls progress callback each frame

type AnimationDriver

type AnimationDriver interface {
	Update(deltaMs int)
	IsComplete() bool
}

AnimationDriver interface for frame-by-frame animation updates Used by RunScript to drive script executors without circular imports

type ContinuousEvent

type ContinuousEvent interface {
	// Execute processes the event and returns whether it should continue
	Execute(deltaMs uint64) EventResult
}

ContinuousEvent interface for events that need to run every frame

type DelayedEvent

type DelayedEvent struct {
	ExecuteAt int64
	Action    func() error

	// Dynamic chaining support
	NextAction  func() error // Next action to execute after this one
	NextDelay   int64        // Delay in ms before executing NextAction
	IsWaitPoint bool         // True if this event should wait for input before continuing
}

DelayedEvent represents a single delayed action with optional chaining

type EventQueueManager

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

EventQueueManager manages multiple named event queues

IMPORTANT: Single-threaded Assumption EventQueueManager assumes single-threaded execution in Ebitengine's game loop. Update() is called once per frame on the main thread only. No goroutines access EventQueueManager (web profiling goroutine is isolated). If multi-threaded access is ever needed, add sync.RWMutex protection.

Workflow and feedback queues are serialized independently. Legacy background queues remain concurrent and should not be used for new presentation effects.

func NewEventQueueManager

func NewEventQueueManager(timeSource func() int64, systemCallbacks SystemCallbacks) *EventQueueManager

NewEventQueueManager creates a new event queue manager

func (*EventQueueManager) HasActiveBackgroundQueues

func (eqm *EventQueueManager) HasActiveBackgroundQueues() bool

HasActiveBackgroundQueues returns true if there are any active background queues

func (*EventQueueManager) HasActiveWorkflows

func (eqm *EventQueueManager) HasActiveWorkflows() bool

HasActiveWorkflows reports whether any workflow is active or pending.

func (*EventQueueManager) HasBackground

func (eqm *EventQueueManager) HasBackground(name string) bool

HasBackground reports whether a legacy background queue name is registered.

func (*EventQueueManager) HasFeedback

func (eqm *EventQueueManager) HasFeedback(name string) bool

HasFeedback reports whether feedback is active, pending, deferred, or finalizing.

func (*EventQueueManager) HasInputBlockingWorkflows

func (eqm *EventQueueManager) HasInputBlockingWorkflows() bool

HasInputBlockingWorkflows includes reserved, queued, and finalizing input owners, not just the currently executing workflow.

func (*EventQueueManager) HasOnlyWorkflow

func (eqm *EventQueueManager) HasOnlyWorkflow(name string) bool

HasOnlyWorkflow reports whether name is the sole deferred, active, pending, or finalizing gameplay workflow.

func (*EventQueueManager) HasWorkflow

func (eqm *EventQueueManager) HasWorkflow(name string) bool

HasWorkflow reports whether a workflow is deferred, active, pending, or finalizing.

func (*EventQueueManager) RegisterBackgroundQueue

func (eqm *EventQueueManager) RegisterBackgroundQueue(name string) *NamedEventQueue

RegisterBackgroundQueue creates a background event queue that runs concurrently with the main queue. Background queues are designed for UI-only effects that don't block input or mutate game state.

Key differences from Workflow:

  • Never blocks input (isBackground=true)
  • Runs in parallel with main queue (not serialized)
  • FIFO stacking: multiple background queues execute in order
  • Independent lifecycle: continues running even after main queue completes

Use cases: Action feedback popups, visual effects, notifications

Example usage:

eqm.RegisterBackgroundQueue("action_feedback").
    Priority(PriorityNormal).
    Sequential().
    First(func() { dialog.Show() }).
    Wait(2000).
    Then(func() { dialog.Hide() }).
    Start()

func (*EventQueueManager) SetShutdownAction

func (eqm *EventQueueManager) SetShutdownAction(action func())

SetShutdownAction registers owner cleanup for transient state that may outlive an individual queue. It must be configured before shutdown begins.

func (*EventQueueManager) ShouldYield

func (eqm *EventQueueManager) ShouldYield(currentPriority int) bool

ShouldYield checks if a higher priority queue is waiting to execute Returns true if any pending queue has higher priority (lower number) than currentPriority Use in long-running loops (While, WhileAwaitingInput) to cooperatively yield to interruptions

func (*EventQueueManager) Shutdown

func (eqm *EventQueueManager) Shutdown()

Shutdown permanently cancels all scene-owned queues. Every accepted queue runs Finally at most once before input ownership and registrations are released. Repeated shutdowns and later queue starts are harmless.

func (*EventQueueManager) TriggerFeedback

func (eqm *EventQueueManager) TriggerFeedback(
	name string,
	priority int,
	build func(*SequentialEventBuilder),
)

TriggerFeedback requests presentation-only feedback and continues without waiting.

func (*EventQueueManager) Update

func (eqm *EventQueueManager) Update()

Update processes the active queue and executes ready events

func (*EventQueueManager) UpdateBackground

func (eqm *EventQueueManager) UpdateBackground()

UpdateBackground processes all background queues concurrently with the main queue. Called from gamescene_update.go after Update() to process visual effects. Background queues never block input and run independently of main queue lifecycle. All active background queues are processed in parallel - each fades independently.

func (*EventQueueManager) UpdateFeedback

func (eqm *EventQueueManager) UpdateFeedback()

UpdateFeedback advances the serialized presentation lane independently of workflows.

func (*EventQueueManager) Workflow

func (eqm *EventQueueManager) Workflow(name string) *NamedEventQueue

Workflow creates an unstarted gameplay workflow. Start atomically reserves its name and safely defers activation when called from another queue callback.

type EventResult

type EventResult int

EventResult represents the result of an event execution

const (
	// EventContinue means the event should continue running next frame
	EventContinue EventResult = iota
	// EventComplete means the event is finished and should be removed from the queue
	EventComplete
)

type NamedEventQueue

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

NamedEventQueue represents an isolated event sequence

func (*NamedEventQueue) Priority

func (neq *NamedEventQueue) Priority(p int) *NamedEventQueue

Priority sets the execution priority for this event queue Lower numbers indicate higher priority (can interrupt lower priority queues) Use constants from priorities.go: PriorityCritical(1), PriorityHigh(2), PriorityMedium(3), PriorityNormal(5), PriorityLow(10) Must be called before Sequential()

func (*NamedEventQueue) Sequential

func (neq *NamedEventQueue) Sequential() *SequentialEventBuilder

Sequential returns a sequential event builder for chained events

type Sequence

Sequence adds reusable runtime steps to an event sequence.

type SequentialEventBuilder

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

SequentialEventBuilder builds sequential (chained) event sequences All events use chain mode - legacy pre-calculated timing removed

func (*SequentialEventBuilder) Animate

func (seb *SequentialEventBuilder) Animate(durationMs uint64, onFrame func(float32)) *SequentialEventBuilder

Animate adds a smooth animation event that calls onFrame each frame with progress 0.0-1.0

func (*SequentialEventBuilder) AwaitFeedback

func (seb *SequentialEventBuilder) AwaitFeedback(
	name string,
	priority int,
	build func(*SequentialEventBuilder),
) *SequentialEventBuilder

AwaitFeedback triggers serialized feedback and pauses this sequence until it completes.

func (*SequentialEventBuilder) BlockInput

BlockInput gives this workflow ownership of the global input block until finalization.

func (*SequentialEventBuilder) Finally

func (seb *SequentialEventBuilder) Finally(action func()) *SequentialEventBuilder

Finally registers guaranteed cleanup that runs exactly once before ownership release. This action is guaranteed to run at the end of the sequence Use for cleanup: hiding dialogs, stopping sounds, clearing state

Example:

g.EventQueueManager.Workflow("camping").
    First(func() { dialog.Show() }).
    Then(func() { dialog.AppendLine("Healing...") }).
    WaitForAnyKey().
    Finally(func() {
        dialog.Hide()           // Cleanup
        audio.Stop(sound).Now() // Cleanup
    }).
    Start()

func (*SequentialEventBuilder) First

func (seb *SequentialEventBuilder) First(action func()) *SequentialEventBuilder

First schedules the first event in a sequential chain

func (*SequentialEventBuilder) HandoffTo

func (seb *SequentialEventBuilder) HandoffTo(
	newQueueName string,
	newPriority int,
	builderFunc func(*SequentialEventBuilder),
) *SequentialEventBuilder

HandoffTo marks the current queue for termination and starts another after cleanup. The current queue's Finally() will run for cleanup, then the chained queue starts via OnComplete Safe to call from inside If()/While() callbacks - termination is deferred until event completes

Use this for internal self-termination with chaining (e.g., sailing → whirlpool transportation) This works for internal conditions where a running queue needs to stop itself and chain to another

Example:

If(func() bool { return landedOnWhirlpool }, func(q *SequentialEventBuilder) {
    q.Then(func() { stopSailing() }).
      HandoffTo("whirlpool_transport", PriorityHigh, func(wq *SequentialEventBuilder) {
          wq.First(func() { showMessage() }).
             Then(func() { teleport() })
      })
})

func (*SequentialEventBuilder) If

func (seb *SequentialEventBuilder) If(
	condition func() bool,
	build func(*SequentialEventBuilder),
) *SequentialEventBuilder

If conditionally executes a block of chained events at runtime. Condition is evaluated at EXECUTION time, not build time, allowing conditions that depend on state set by previous events in the chain.

If condition() returns true when executed, the closure is called to build the sub-chain If condition() returns false, the entire block is skipped

Example:

.If(func() bool { return wandererAppeared }, func(q *SequentialEventBuilder) {
    q.Then(func() error { /* wanderer logic */ }).
      WaitForAnyKey()
})

func (*SequentialEventBuilder) RepeatWithDelay

func (seb *SequentialEventBuilder) RepeatWithDelay(iterations int, delayMs int64, fn func(iteration int) bool) *SequentialEventBuilder

RepeatWithDelay executes a function N times with a delay between each iteration The function receives the current iteration (0-based) and returns true to continue, false to break early Total duration will be approximately: iterations * delayMs

func (*SequentialEventBuilder) RunScript

RunScript executes an animation driver until completion. The driver is updated every frame (~16ms) until IsComplete() returns true

This method allows script executors to integrate with the event queue without requiring callback-based completion handling. The script runs inline with normal event queue flow.

Parameters:

  • driver: Any AnimationDriver implementation (e.g., minimap.ScriptExecutor)

Example:

executor := minimap.NewScriptExecutor(scene, walkScript)
q.First(func() { /* setup */ }).
  RunScript(executor).
  Then(func() { /* runs after script completes */ })

func (*SequentialEventBuilder) Start

func (seb *SequentialEventBuilder) Start()

Start executes the sequential event sequence

func (*SequentialEventBuilder) Then

func (seb *SequentialEventBuilder) Then(action func()) *SequentialEventBuilder

Then executes an action immediately after the previous event completes (no delay) The action cannot fail - use RepeatWithDelay if you need error-based early termination If called on an empty queue (no First() yet), automatically becomes First()

func (*SequentialEventBuilder) ThenSequence

func (seb *SequentialEventBuilder) ThenSequence(sequence Sequence) *SequentialEventBuilder

ThenSequence inserts reusable steps when execution reaches this point.

func (*SequentialEventBuilder) UntilKeyPressed

func (seb *SequentialEventBuilder) UntilKeyPressed(
	action func(),
	intervalMs int64,
) *SequentialEventBuilder

UntilKeyPressed runs the action repeatedly until any key is pressed This is a convenience wrapper around While() for the common pattern of "run until user input"

Parameters:

  • action: Called every iteration (use func() {} for polling without side effects)
  • intervalMs: Delay between iterations (e.g., 100ms for smooth movement)

The loop stops when SystemCallbacks.IsAnyKeyPressed() returns true The pressed key is NOT consumed - it will be processed by normal input handlers after the loop exits

Example - Automatic sailing:

.UntilKeyPressed(
    func() { moveSailingStep() },  // Called every interval
    100,                             // 100ms between moves
)

func (*SequentialEventBuilder) Wait

Wait pauses for the specified duration without executing any action

func (*SequentialEventBuilder) WaitForAnyKey

func (seb *SequentialEventBuilder) WaitForAnyKey() *SequentialEventBuilder

WaitForAnyKey pauses the event sequence until any key is pressed

func (*SequentialEventBuilder) While

func (seb *SequentialEventBuilder) While(
	condition func() bool,
	action func(),
	intervalMs int64,
) *SequentialEventBuilder

func (*SequentialEventBuilder) WhileAwaitingInput

func (seb *SequentialEventBuilder) WhileAwaitingInput(
	action func(),
	keyCallback func(key *semantic_input.Key) bool,
	intervalMs int64,
) *SequentialEventBuilder

WhileAwaitingInput runs the action repeatedly, checking for key input after each iteration This is similar to UntilKeyPressed but provides the specific key to the callback for decision-making

Parameters:

  • action: Called every iteration before key check (use func() {} for polling without side effects)
  • keyCallback: Called with the currently pressed key (or nil if no key pressed) Returns true to stop loop, false to continue
  • intervalMs: Delay between iterations (e.g., 100ms for smooth movement)

The callback receives *semantic_input.Key which can be:

  • nil: No key currently pressed
  • Arrow key: Same direction (continue sailing) or different direction (change sailing direction)
  • Space bar: Stop sailing with "Sheets in irons!" message
  • Any other key: Stop loop AND process key as normal command (not consumed)

Example - Automatic sailing with direction changes:

.WhileAwaitingInput(
    func() { moveSailingStep() },  // Called every interval
    func(key *semantic_input.Key) bool {
        if key == nil { return false }  // No key - continue
        if *key == semantic_input.KeySpace { return true }  // Space - stop
        if isArrowKey(key) {
            changeSailingDirection(key)  // Arrow - change direction
            return false  // Continue sailing
        }
        return true  // Other key - stop and process
    },
    100,  // 100ms between moves
)

type SystemCallbacks

type SystemCallbacks interface {
	BlockAllInput(block bool)
	IsAnyKeyPressed() bool
	GetPressedKey() *semantic_input.Key
}

SystemCallbacks interface for input management

Jump to

Keyboard shortcuts

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