339 lines
9.2 KiB
Scheme
339 lines
9.2 KiB
Scheme
(module (engine core) ()
|
|
(import scheme
|
|
(chicken base)
|
|
(chicken module)
|
|
(chicken sort)
|
|
raylib
|
|
(srfi 1)
|
|
(srfi 69)
|
|
(srfi 99)
|
|
(srfi 113)
|
|
(srfi 128))
|
|
|
|
;; We export the world, component sets, systems, and event buses so that the core can be extended to
|
|
;; include serialization functions across all.
|
|
;; NOTE: direct read/write to these is undefined, and will cause problems.
|
|
(export world component-sets systems event-buses)
|
|
|
|
;; The world hash table
|
|
(define world (make-hash-table))
|
|
;; The component-sets hash table
|
|
(define component-sets (make-hash-table))
|
|
|
|
;; Insert an entity into component-sets, either creating
|
|
;; a new set or updating an existing one for the component
|
|
;; type provided.
|
|
(define (classify-entity-by id component)
|
|
(let ((component-type (rtd-name (record-rtd component))))
|
|
(if (hash-table-exists? component-sets component-type)
|
|
(let ((component-set (hash-table-ref component-sets component-type)))
|
|
;; TODO: this could be nicer with set-adjoin! but I'm not sure if hash-table-ref is locative
|
|
(hash-table-set! component-sets
|
|
component-type
|
|
(set-adjoin component-set
|
|
id)))
|
|
(hash-table-set! component-sets
|
|
component-type
|
|
(set (make-eq-comparator) id)))))
|
|
|
|
;; Create an entity in the world immediately,
|
|
;; and add it to the requisite component-sets
|
|
(define (create-entity-now id components)
|
|
(hash-table-set! world id components)
|
|
(for-each
|
|
(lambda (component)
|
|
(classify-entity-by id component))
|
|
components))
|
|
|
|
;; Remove a single matching item from a set
|
|
(define (remove-from-set! set element)
|
|
(set-search! set element
|
|
(lambda (insert ignore)
|
|
(ignore #f))
|
|
(lambda (old update remove)
|
|
(remove old))))
|
|
|
|
;; Remove an entity from the world immediately,
|
|
;; as well as from all component sets.
|
|
(define (remove-entity-now id)
|
|
(hash-table-delete! world id)
|
|
(for-each
|
|
(lambda (set)
|
|
(remove-from-set! set id))
|
|
(hash-table-values component-sets)))
|
|
|
|
;; Queues for entity creation and deletion
|
|
(define add-entity-queue '())
|
|
(define del-entity-queue '())
|
|
|
|
;; Add an entity to the incoming queue
|
|
;; TODO: append! doesn't work here and IDK why
|
|
(define (queue-add-entity id components-lst)
|
|
(set! add-entity-queue
|
|
(append add-entity-queue (list (cons id components-lst)))))
|
|
|
|
;; Add an entity to the deletion queue
|
|
(define (queue-del-entity id)
|
|
(set! del-entity-queue
|
|
(append del-entity-queue (list id))))
|
|
|
|
;; Resolve all new entity creations
|
|
(define (add-queued-entities)
|
|
(for-each
|
|
(lambda (entity)
|
|
(let ((id (car entity))
|
|
(components (cdr entity)))
|
|
(create-entity-now id components)))
|
|
add-entity-queue)
|
|
(set! add-entity-queue '()))
|
|
|
|
;; Resolve all queued entity deletions
|
|
(define (remove-queued-entities)
|
|
(for-each
|
|
(lambda (id)
|
|
(remove-entity-now id))
|
|
del-entity-queue)
|
|
(set! del-entity-queue '()))
|
|
|
|
;; Resolve all queued entity changes
|
|
(define (resolve-entity-queue)
|
|
(remove-queued-entities)
|
|
(add-queued-entities))
|
|
|
|
;; Entity creation/removal interface
|
|
(export create-named-entity create-entity remove-entity clear-world)
|
|
|
|
;; Create an entity in the world and return it's ID
|
|
(define (create-named-entity id . components)
|
|
(assert (symbol? id))
|
|
(assert (every record? components))
|
|
(queue-add-entity id components)
|
|
id)
|
|
|
|
;; Shortcut for anonymous instancing
|
|
(define (create-entity . components)
|
|
(apply create-named-entity (gensym) components))
|
|
|
|
;; Remove an entity from the world
|
|
(define (remove-entity id)
|
|
(assert (symbol? id))
|
|
(queue-del-entity id)
|
|
id)
|
|
|
|
;; Clear the entire world
|
|
(define (clear-world)
|
|
(for-each
|
|
remove-entity
|
|
(hash-table-keys world)))
|
|
|
|
;; System record interface
|
|
(export make-system system? system-name system-rendering
|
|
system-priority set-system-priority! system-criteria
|
|
set-system-criteria! system-process set-system-process!)
|
|
|
|
;; The system record
|
|
(define-record-type <system>
|
|
(internal-make-system name rendering priority criteria process)
|
|
system?
|
|
(name system-name)
|
|
(rendering system-rendering)
|
|
(priority system-priority internal-set-system-priority!)
|
|
(criteria system-criteria internal-set-system-criteria!)
|
|
(process system-process internal-set-system-process!))
|
|
|
|
;; Type-checked system constructor wrapper
|
|
(define (make-system name rendering priority criteria process)
|
|
(assert (symbol? name))
|
|
(assert (and (symbol? rendering) (member rendering '(3d 2d screen none))))
|
|
(assert (integer? priority))
|
|
(assert (every symbol? criteria))
|
|
(assert (procedure? process))
|
|
(internal-make-system name rendering priority criteria process))
|
|
|
|
;; Type-checked system priority mutator
|
|
(define (set-system-priority! system priority)
|
|
(assert (system? system))
|
|
(assert (integer? priority))
|
|
(internal-set-system-priority! system priority))
|
|
|
|
;; Type-checked system criteria mutator
|
|
(define (set-system-criteria! system criteria)
|
|
(assert (system? system))
|
|
(assert (every symbol? criteria))
|
|
(internal-set-system-criteria! system criteria))
|
|
|
|
;; Type-checked system process mutator
|
|
(define (set-system-process! system process)
|
|
(assert (system? system))
|
|
(assert (procedure? process))
|
|
(internal-set-system-process! system process))
|
|
|
|
;; The systems list
|
|
(define systems '())
|
|
|
|
;; Immediately add a new system
|
|
(define (add-system-now system)
|
|
(set! systems
|
|
(append systems (list system))))
|
|
|
|
;; Immediately remove a new system
|
|
(define (remove-system-now name)
|
|
(set! systems
|
|
(delete name systems
|
|
(lambda (n el)
|
|
(eqv? n (system-name el))))))
|
|
|
|
;; Sort the systems list by priority
|
|
(define (sort-systems)
|
|
(sort! systems
|
|
(lambda (x y)
|
|
(< (system-priority x)
|
|
(system-priority y)))))
|
|
|
|
;; System addition/deletion queues
|
|
(define add-system-queue '())
|
|
(define del-system-queue '())
|
|
|
|
;; Add a system to the incoming queue
|
|
(define (queue-add-system system)
|
|
(set! add-system-queue
|
|
(append add-system-queue (list system))))
|
|
|
|
;; Add a system to the deletion queue
|
|
(define (queue-del-system name)
|
|
(set! del-system-queue
|
|
(append del-system-queue (list name))))
|
|
|
|
;; Resolve all new system creations
|
|
(define (add-queued-systems)
|
|
(for-each
|
|
(lambda (system)
|
|
(add-system-now system))
|
|
add-system-queue)
|
|
(set! add-system-queue '()))
|
|
|
|
;; Resolve all queued system deletions
|
|
(define (remove-queued-systems)
|
|
(for-each
|
|
(lambda (system-name)
|
|
(remove-system-now system-name))
|
|
del-system-queue)
|
|
(set! del-system-queue '()))
|
|
|
|
;; Resolve all queued system changes
|
|
(define (resolve-system-queue)
|
|
(remove-queued-systems)
|
|
(add-queued-systems)
|
|
(sort-systems))
|
|
|
|
;; System creation/removal interface
|
|
(export add-system remove-system)
|
|
|
|
;; Add a system to the state processing and return its name
|
|
(define (add-system system)
|
|
(assert (system? system))
|
|
(queue-add-system system)
|
|
(system-name system))
|
|
|
|
;; Remove a system from process
|
|
(define (remove-system name)
|
|
(assert (symbol? name))
|
|
(queue-del-system name)
|
|
name)
|
|
|
|
(define (clear-systems)
|
|
(for-each
|
|
remove-system
|
|
(map system-name systems)))
|
|
|
|
;; Execute a single system
|
|
;; TODO: Implement
|
|
(define (execute-system system)
|
|
(assert (system? system))
|
|
#t)
|
|
|
|
(define (execute-systems)
|
|
(for-each
|
|
execute-system
|
|
systems))
|
|
|
|
;; Event buses hash table
|
|
(define event-buses (make-hash-table))
|
|
|
|
;; Event bus interface
|
|
(export register-event-bus remove-event-bus fetch-event-bus
|
|
push-event peek-event pop-event)
|
|
|
|
;; Register a new event bus
|
|
(define (register-event-bus name)
|
|
(assert (symbol? name))
|
|
(if (hash-table-exists? event-buses name)
|
|
#f
|
|
(begin
|
|
(hash-table-set! event-buses name (make-hash-table))
|
|
name)))
|
|
|
|
;; Remove an event bus
|
|
(define (remove-event-bus name)
|
|
(assert (symbol? name))
|
|
(if (hash-table-exists? event-buses name)
|
|
(begin
|
|
(hash-table-delete! event-buses name)
|
|
name)
|
|
#f))
|
|
|
|
;; Fetch an event bus by name, or #f if it doesn't exist
|
|
(define (fetch-event-bus name)
|
|
(assert (symbol? name))
|
|
(if (hash-table-exists? event-buses name)
|
|
(hash-table-ref event-buses name)
|
|
#f))
|
|
|
|
;; Push an event to the specified bus, return #f on failure (if the bus doesn't exist
|
|
(define (push-event bus action event)
|
|
(assert (symbol? bus))
|
|
(assert (symbol? action))
|
|
(assert (record? event))
|
|
(let ((event-bus (fetch-event-bus bus)))
|
|
(if event-bus
|
|
(begin
|
|
(hash-table-set! event-bus action event)
|
|
#t)
|
|
#f)))
|
|
|
|
;; Retrieve an event from the event bus, if it exists. Return false if it doesn't
|
|
(define (peek-event bus action)
|
|
(assert (symbol? bus))
|
|
(assert (symbol? action))
|
|
(let ((event-bus (fetch-event-bus bus)))
|
|
(if (and event-bus
|
|
(hash-table-exists? event-bus action))
|
|
(hash-table-ref event-bus action)
|
|
#f)))
|
|
|
|
;; Retrieve an event from the event bus, if it exists, then delete it. Return false if it doesn't exist.
|
|
(define (pop-event bus action)
|
|
(assert (symbol? bus))
|
|
(assert (symbol? action))
|
|
(let ((event-bus (fetch-event-bus bus)))
|
|
(if (and event-bus
|
|
(hash-table-exists? event-bus action))
|
|
(let ((event (hash-table-ref event-bus action)))
|
|
(hash-table-delete! event-bus action)
|
|
event)
|
|
#f)))
|
|
|
|
;; Frame generation and game loop
|
|
(export resolve-queues next-frame)
|
|
|
|
;; Resolve the entity and system queues. This is exported which allows breaking iteration
|
|
(define (resolve-queues)
|
|
(resolve-entity-queue)
|
|
(resolve-system-queue))
|
|
|
|
;; Generate the next frame, for use in the main game loop
|
|
(define (next-frame)
|
|
(resolve-queues)
|
|
(with-drawing
|
|
(execute-systems)))
|
|
)
|