ShatteredSword/src/Events/EventQueue.ts
2021-01-13 13:30:45 -05:00

105 lines
3.8 KiB
TypeScript

import Queue from "../DataTypes/Queue";
import Map from "../DataTypes/Map";
import GameEvent from "./GameEvent";
import Receiver from "./Receiver";
import { GameEventType } from "./GameEventType";
/**
* The main event system of the game engine.
* Events are sent to the EventQueue, which handles distribution to any systems that are listening for those events.
* This allows for handling of input without having classes directly hook into javascript event handles,
* and allows otherwise separate classes to communicate with each other cleanly, such as a Player object
* requesting a sound be played by the audio system.
*
* The distribution of @reference[GameEvent]s happens as follows:
*
* Events are recieved throughout a frame and are queued up by the EventQueue.
* At the beginning of the next frame, events are sent out to any receivers that are hooked into the event type.
* @reference[Receiver]s are then free to process events as they see fit.
*
* Overall, the EventQueue can be considered as something similar to an email server,
* and the @reference[Receiver]s can be considered as the client inboxes.
*
* See @link(Game Programming Patterns)(https://gameprogrammingpatterns.com/event-queue.html) for more discussion on EventQueues
*/
export default class EventQueue {
private static instance: EventQueue = null;
/** The maximum number of events visible */
private readonly MAX_SIZE: number;
/** The actual queue of events */
private q: Queue<GameEvent>;
/** The map of receivers registered for an event name */
private receivers: Map<Array<Receiver>>;
private constructor(){
this.MAX_SIZE = 100;
this.q = new Queue<GameEvent>(this.MAX_SIZE);
this.receivers = new Map<Array<Receiver>>();
}
/** Retrieves the instance of the Singleton EventQueue */
static getInstance(): EventQueue {
if(this.instance === null){
this.instance = new EventQueue();
}
return this.instance;
}
/** Adds an event to the EventQueue.
* This is exposed to the rest of the game engine through the @reference[Emitter] class */
addEvent(event: GameEvent): void {
this.q.enqueue(event);
}
/**
* Associates a receiver with a type of event. Every time this event appears in the future,
* it will be given to the receiver (and any others watching that type).
* This is exposed to the rest of the game engine through the @reference[Receiver] class
* @param receiver The event receiver
* @param type The type or types of events to subscribe to
*/
subscribe(receiver: Receiver, type: string | Array<string>): void {
if(type instanceof Array){
// If it is an array, subscribe to all event types
for(let t of type){
this.addListener(receiver, t);
}
} else {
this.addListener(receiver, type);
}
}
// Associate the receiver and the type
private addListener(receiver: Receiver, type: string): void {
if(this.receivers.has(type)){
this.receivers.get(type).push(receiver);
} else {
this.receivers.add(type, [receiver]);
}
}
update(deltaT: number): void {
while(this.q.hasItems()){
// Retrieve each event
let event = this.q.dequeue();
// If a receiver has this event type, send it the event
if(this.receivers.has(event.type)){
for(let receiver of this.receivers.get(event.type)){
receiver.receive(event);
}
}
// If a receiver is subscribed to all events, send it the event
if(this.receivers.has(GameEventType.ALL)){
for(let receiver of this.receivers.get(GameEventType.ALL)){
receiver.receive(event);
}
}
}
}
}