1 ;;; Gacela, a GNU Guile extension for fast games development
2 ;;; Copyright (C) 2013 by Javier Sancho Fernandez <jsf at jsancho dot org>
4 ;;; This program is free software: you can redistribute it and/or modify
5 ;;; it under the terms of the GNU General Public License as published by
6 ;;; the Free Software Foundation, either version 3 of the License, or
7 ;;; (at your option) any later version.
9 ;;; This program is distributed in the hope that it will be useful,
10 ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
11 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 ;;; GNU General Public License for more details.
14 ;;; You should have received a copy of the GNU General Public License
15 ;;; along with this program. If not, see <http://www.gnu.org/licenses/>.
18 (define-module (gacela system)
19 #:use-module ((bongodb) #:renamer (symbol-prefix-proc 'bongodb:))
20 #:use-module (ice-9 receive)
21 #:use-module (srfi srfi-1)
22 #:use-module (srfi srfi-9)
23 #:use-module (srfi srfi-9 gnu)
24 #:export (define-component
35 remove-entity-components
40 find-entities-by-components
49 ;;; Component definitions
51 (define (symbol-concatenate . args)
54 (map (lambda (a) (if (symbol? a) (symbol->string a) a)) args))))
56 (define-syntax define-component
58 (define (concat . args)
60 (apply symbol-concatenate
66 (define (filtered-args args)
67 (let ((datum (map (lambda (a) (syntax->datum a)) args)))
68 (map (lambda (a) (datum->syntax x a))
69 (map (lambda (a) (if (list? a) (car a) a))
70 (filter (lambda (a) (not (keyword? a))) datum)))))
73 (with-syntax ((make-name (concat "make-" #'name))
74 (make-name-record (concat "make-" #'name "-record"))
75 (name? (concat #'name "?"))
76 ((field-name ...) (filtered-args #'(field ...)))
77 ((field-getter ...) (map (lambda (f) (concat #'name "-" f)) (filtered-args #'(field ...))))
78 ((field-setter ...) (map (lambda (f) (concat "set-" #'name "-" f "!")) (filtered-args #'(field ...)))))
80 (define* (make-name field ...)
81 (make-name-record field-name ...))
82 (define-record-type name
83 (make-name-record field-name ...)
85 (field-name field-getter field-setter)
87 (set-record-type-printer! name
89 (format port "#<[~a]" 'name)
90 (format port " ~a: ~a" 'field-name (field-getter record))
95 (define (export-component component)
96 (let ((name (record-type-name component))
98 (module-export! m (list
99 (symbol-concatenate "make-" name)
100 (symbol-concatenate name "?")))
103 (module-export! (current-module)
105 (symbol-concatenate name "-" a)
106 (symbol-concatenate "set-" name "-" a "!"))))
107 (record-type-fields component))))
109 (define (get-component-type component)
110 (record-type-name (record-type-descriptor component)))
113 ;;; Entities and components
115 (define (make-entity-set . changes)
116 (modify-entities (bongodb:make-collection) changes))
118 (define (entity-list entity-set)
119 (bongodb:find entity-set))
121 (define (entity-count entity-set)
122 (bongodb:count entity-set))
124 (define (normalize-components components)
128 `(,(get-component-type c) . ,c)
132 (define (entity-ref key entity-set)
133 (hash-get-handle (car entity-set) key))
135 (define (new-entity entity-set . new-components)
136 (receive (new-set new-keys) (bongodb:insert entity-set (normalize-components new-components))
137 (values new-set (car new-keys))))
139 (define (get-entity entity-set key)
140 (let ((entity (bongodb:find entity-set (bongodb:$eq '_id key))))
141 (and entity (car entity))))
143 (define (remove-entity key)
145 (let ((clist (entity-component-names key entity-set))
146 (entity (entity-ref key entity-set)))
147 (hash-remove! (car entity-set) key)
148 (unregister-components key clist (cdr entity-set))
153 (define (set-entity key . new-components)
155 (let* ((nc (normalize-components new-components))
156 (clist (entity-component-names key entity-set))
157 (nclist (component-names nc)))
158 (hash-set! (car entity-set) key nc)
159 (register-components key
160 (lset-difference eq? nclist clist)
161 (unregister-components key (lset-difference eq? clist nclist) (cdr entity-set)))
166 (define (set-entity-components key . new-components)
168 (let ((nc (normalize-components new-components))
169 (clist (alist-copy (hash-ref (car entity-set) key))))
172 (set! clist (assoc-set! clist (car c) (cdr c))))
174 (hash-set! (car entity-set) key clist)
175 (register-components key (component-names nc) (cdr entity-set))
180 (define (remove-entity-components key . old-components)
182 (let ((clist (alist-copy (hash-ref (car entity-set) key))))
185 (set! clist (assoc-remove! clist c)))
187 (hash-set! (car entity-set) key clist)
188 (unregister-components key old-components (cdr entity-set))
193 (define (modify-entities entity-set changes)
194 (cond ((null? changes)
197 (modify-entities ((car changes) entity-set) (cdr changes)))))
202 (define-record-type entities-changes-type
203 (entities-changes changes)
205 (changes get-entities-changes))
207 (define (append-changes changes)
210 (map get-entities-changes changes))))
212 (define (find-entities-by-components entity-set clist)
213 (cond ((null? clist) '())
215 (let ((e (hash-ref (cdr entity-set) (car clist) '()))
216 (e* (find-entities-by-components entity-set (cdr clist))))
219 (lset-intersection eq? e e*))))))
221 (define-syntax make-system
223 ((_ ((name (component-type ...)) ...) form ...)
225 (let ((name (map (lambda (x)
228 (memq (car x) '(component-type ...)))
231 (entity-ref x entity-set))
232 (find-entities-by-components entity-set '(component-type ...)))))
237 (define-syntax define-system
239 ((_ system-name ((name (component-type ...)) ...) form ...)
241 (make-system ((name (component-type ...)) ...)
245 (define (composed-systems-result results)
246 (let ((changes (filter (lambda (r) (entities-changes? r)) results)))
247 (cond ((null? changes)
250 (append-changes changes)))))
252 (define (join-systems . systems)
254 (let run ((s systems) (res '()))
256 (composed-systems-result res))
258 (run (cdr s) (cons ((car s) entity-set) res)))))))
260 (define (thread-systems . systems)
264 (call-with-new-thread
265 (lambda () (s entity-set))))
269 (composed-systems-result res))
271 (run-wait (cdr thd) (cons (join-thread (car thd)) res)))))))
274 ;;; Entities and components access inside systems
276 (define (get-key entity)
279 (define (get-component component-name entity)
280 (assoc-ref (cdr entity) component-name))