;;; Catbird Game Engine ;;; Copyright © 2022 David Thompson ;;; ;;; Licensed under the Apache License, Version 2.0 (the "License"); ;;; you may not use this file except in compliance with the License. ;;; You may obtain a copy of the License at ;;; ;;; http://www.apache.org/licenses/LICENSE-2.0 ;;; ;;; Unless required by applicable law or agreed to in writing, software ;;; distributed under the License is distributed on an "AS IS" BASIS, ;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ;;; See the License for the specific language governing permissions and ;;; limitations under the License. ;;; Commentary: ;; ;; Fundamental mix-in classes. ;; ;;; Code: (define-module (catbird mixins) #:use-module (catbird config) #:use-module (chickadee math vector) #:use-module (chickadee scripting) #:use-module (ice-9 exceptions) #:use-module (oop goops) #:use-module (srfi srfi-9) #:export ( name rank sort-by-rank/ascending parent attach detach on-enter on-exit on-attach on-detach update update/around agenda on-pause on-resume paused? pause resume run-script stop-scripts visible? on-show on-hide show hide render render/around render/before position position-x position-y position-y width height depth ignore listen responds-to? send) #:replace (listen pause send)) (define-class () (name #:accessor name #:init-keyword #:name #:init-value #f)) ;; For Z sorting objects and such. (define-class () (rank #:accessor rank #:init-keyword #:rank #:init-value 0)) (define (sort-by-rank/ascending lst) (sort lst (lambda (a b) (< (rank a) (rank b))))) ;;; ;;; Containable ;;; (define-class () (parent #:accessor parent #:init-form #f)) (define-method (on-enter (child )) #t) (define-method (on-exit (child )) #t) (define-method (on-attach parent (child )) #t) (define-method (on-detach parent (child )) #t) (define-method (attach (obj ) container) (when (parent obj) (raise-exception (make-exception-with-message "object already has a parent"))) (set! (parent obj) container) (on-enter obj) (on-attach container obj)) (define-method (detach (obj )) (unless (parent obj) (raise-exception (make-exception-with-message "object has no parent"))) (on-detach (parent obj) obj) (on-exit obj) (set! (parent obj) #f)) ;;; ;;; Updatable ;;; (define-class ()) (define-method (update (obj ) dt) #t) (define-method (update/around (obj ) dt) (update obj dt)) ;;; ;;; Scriptable ;;; (define-class () (paused? #:accessor paused? #:init-form #f #:init-keyword #:paused?) (agenda #:getter agenda #:init-thunk make-agenda)) (define-method (on-pause (obj )) #t) (define-method (on-resume (obj )) #t) (define-method (pause (obj )) (unless (paused? obj) (set! (paused? obj) #t) (on-pause obj))) (define-method (resume (obj )) (when (paused? obj) (set! (paused? obj) #f) (on-resume obj))) (define-method (update/around (obj ) dt) (unless (paused? obj) (with-agenda (agenda obj) (update-agenda dt) (next-method)))) (define-syntax-rule (run-script obj body ...) (with-agenda (agenda obj) (script body ...))) (define-method (stop-scripts obj) (with-agenda (agenda obj) (clear-agenda))) ;;; ;;; Renderable ;;; (define-class () (visible? #:accessor visible? #:init-form #t #:init-keyword #:visible?)) (define-method (on-show (obj )) #t) (define-method (on-hide (obj )) #t) (define-method (show (obj )) (set! (visible? obj) #t) (on-show obj)) (define-method (hide (obj )) (set! (visible? obj) #f) (on-hide obj)) (define-method (render (obj ) alpha) #t) (define-method (render/before (obj ) alpha) #t) (define-method (render/around (obj ) alpha) (when (visible? obj) (render/before obj alpha) (render obj alpha))) ;;; ;;; Movable ;;; (define-class () (position #:accessor position #:init-keyword #:position #:init-form (vec2 0.0 0.0))) (define-class () (position #:accessor position #:init-keyword #:position #:init-form (vec3 0.0 0.0 0.0))) (define-accessor position-x) (define-accessor position-y) (define-accessor position-z) (define-accessor width) (define-accessor height) (define-accessor depth) ;;; ;;; Event listener ;;; ;; For per-instance message passing/event handling. (define-class () (event-handlers #:getter event-handlers #:init-thunk make-hash-table)) ;; listen and send are built-in procedures, so make them generic. (define-generic listen) (define-generic send) (define-method (listen (listener ) message proc) (hashq-set! (event-handlers listener) message proc)) (define-method (ignore (listener ) message) (hashq-remove! (event-handlers listener) message)) (define-method (event-handler (listener ) message) (hashq-ref (event-handlers listener) message)) (define-method (responds-to? (listener ) message) (procedure? (event-handler listener))) (define-method (send (listener ) message . args) (let ((handler (event-handler listener message))) (and (procedure? handler) (apply handler args))))