]> git.jsancho.org Git - gacela.git/blob - src/system.scm
e6feb4eb0e18d8a0700c386772eecee5c31c826d
[gacela.git] / src / system.scm
1 ;;; Gacela, a GNU Guile extension for fast games development
2 ;;; Copyright (C) 2013 by Javier Sancho Fernandez <jsf at jsancho dot org>
3 ;;;
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.
8 ;;;
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.
13 ;;;
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/>.
16
17
18 (define-module (gacela system)
19   #:use-module (ice-9 receive)
20   #:use-module (srfi srfi-1)
21   #:use-module (srfi srfi-9)
22   #:use-module (srfi srfi-9 gnu))
23
24
25 ;;; Component definitions
26
27 (define (symbol-concatenate . args)
28   (string->symbol
29    (string-concatenate
30     (map (lambda (a) (if (symbol? a) (symbol->string a) a)) args))))
31
32 (define-syntax define-component
33   (lambda (x)
34     (define (concat . args)
35       (datum->syntax x
36         (apply symbol-concatenate
37           (map (lambda (a)
38                  (if (string? a)
39                      a
40                      (syntax->datum a)))
41                args))))
42     (syntax-case x ()
43       ((_ name field ...)
44        (with-syntax ((make-name (concat "make-" #'name))
45                      (name? (concat #'name "?"))
46                      ((field-getter ...) (map (lambda (f) (concat #'name "-" f)) #'(field ...)))
47                      ((field-setter ...) (map (lambda (f) (concat "set-" #'name "-" f "!")) #'(field ...))))
48          #'(begin
49              (define-record-type name
50                (make-name field ...)
51                name?
52                (field field-getter field-setter)
53                ...)
54              (set-record-type-printer! name
55                (lambda (record port)
56                  (format port "#<[~a]" 'name)
57                  (format port " ~a: ~a" 'field (field-getter record))
58                  ...
59                  (format port ">")))
60              'name))))))
61
62 (define (export-component component)
63   (let ((name (record-type-name component))
64         (m (current-module)))
65     (module-export! m (list
66                        (symbol-concatenate "make-" name)
67                        (symbol-concatenate name "?")))
68     (for-each
69      (lambda (a)
70        (module-export! (current-module)
71                        (list
72                         (symbol-concatenate name "-" a)
73                         (symbol-concatenate "set-" name "-" a "!"))))
74      (record-type-fields component))))
75
76 (define (get-component-type component)
77   (record-type-name (record-type-descriptor component)))
78
79 (export define-component
80         export-component
81         get-component-type)
82
83
84 ;;; Entities and components
85
86 (define (make-entity-set . changes)
87   (modify-entities
88    (cons (make-hash-table) (make-hash-table))
89    changes))
90
91 (define (entity-list entity-set)
92   (hash-map->list (lambda (k v) (cons k v)) (car entity-set)))
93
94 (define (entity-count entity-set)
95   (hash-count (const #t) (car entity-set)))
96
97 (define (normalize-components components)
98   (map
99    (lambda (c)
100      (if (record? c)
101          `(,(get-component-type c) . ,c)
102          c))
103    components))
104
105 (define (register-components entity components clist)
106   (cond ((null? components) clist)
107         (else
108          (let* ((type (car components))
109                 (elist (hash-ref clist type)))
110            (hash-set! clist type
111              (cond (elist
112                     (lset-adjoin eq? elist entity))
113                    (else
114                     (list entity))))
115            (register-components entity (cdr components) clist)))))
116
117 (define (unregister-components entity components clist)
118   (cond ((null? components) clist)
119         (else
120          (let* ((type (car components))
121                 (elist (lset-difference eq? (hash-ref clist type) (list entity))))
122            (cond ((null? elist)
123                   (hash-remove! clist type))
124                  (else
125                   (hash-set! clist type elist)))
126            (unregister-components entity (cdr components) clist)))))
127
128 (define (component-names components)
129   (map (lambda (c) (car c)) components))
130
131 (define (entity-component-names key entity-set)
132   (component-names
133    (hash-ref (car entity-set) key)))
134
135 (define (entity-ref key entity-set)
136   (hash-get-handle (car entity-set) key))
137
138 (define (new-entity . new-components)
139   (lambda (entity-set)
140     (let ((key (gensym))
141           (nc (normalize-components new-components)))
142       (hash-set! (car entity-set) key nc)
143       (register-components key (component-names nc) (cdr entity-set))
144       (values
145        entity-set
146        (cons key nc)))))
147
148 (define (remove-entity key)
149   (lambda (entity-set)
150     (let ((clist (entity-component-names key entity-set))
151           (entity (entity-ref key entity-set)))
152       (hash-remove! (car entity-set) key)
153       (unregister-components key clist (cdr entity-set))
154       (values
155        entity-set
156        entity))))
157
158 (define (set-entity key . new-components)
159   (lambda (entity-set)
160     (let* ((nc (normalize-components new-components))
161            (clist (entity-component-names key entity-set))
162            (nclist (component-names nc)))
163       (hash-set! (car entity-set) key nc)
164       (register-components key
165        (lset-difference eq? nclist clist)
166        (unregister-components key (lset-difference eq? clist nclist) (cdr entity-set)))
167       (values
168        entity-set
169        (cons key nc)))))
170
171 (define (set-entity-components key . new-components)
172   (lambda (entity-set)
173     (let ((nc (normalize-components new-components))
174           (clist (alist-copy (hash-ref (car entity-set) key))))
175       (for-each
176        (lambda (c)
177          (set! clist (assoc-set! clist (car c) (cdr c))))
178        nc)
179       (hash-set! (car entity-set) key clist)
180       (register-components key (component-names nc) (cdr entity-set))
181       (values
182        entity-set
183        (cons key clist)))))
184
185 (define (remove-entity-components key . old-components)
186   (lambda (entity-set)
187     (let ((clist (alist-copy (hash-ref (car entity-set) key))))
188       (for-each
189        (lambda (c)
190          (set! clist (assoc-remove! clist c)))
191        old-components)
192       (hash-set! (car entity-set) key clist)
193       (unregister-components key old-components (cdr entity-set))
194       (values
195        entity-set
196        (cons key clist)))))
197
198 (define (modify-entities entity-set changes)
199   (cond ((null? changes)
200          entity-set)
201         (else
202          (modify-entities ((car changes) entity-set) (cdr changes)))))
203
204 (export make-entity-set
205         entity-list
206         entity-count
207         new-entity
208         remove-entity
209         set-entity
210         set-entity-components
211         remove-entity-components
212         modify-entities)
213
214
215 ;;; Making systems
216
217 (define-record-type entities-changes-type
218   (entities-changes changes)
219   entities-changes?
220   (changes get-entities-changes))
221
222 (define (append-changes changes)
223   (entities-changes
224    (apply append
225           (map get-entities-changes changes))))
226
227 (define (find-entities-by-components entity-set clist)
228   (cond ((null? clist) '())
229         (else
230          (let ((e (hash-ref (cdr entity-set) (car clist) '()))
231                (e* (find-entities-by-components entity-set (cdr clist))))
232            (if (null? e*)
233                e
234                (lset-intersection eq? e e*))))))
235
236 (define-syntax make-system
237   (syntax-rules ()
238     ((_ ((name (component-type ...)) ...) form ...)
239      (lambda (entity-set)
240        (let ((name (map (lambda (x)
241                           (cons (car x)
242                                 (filter (lambda (x)
243                                           (memq (car x) '(component-type ...)))
244                                         (cdr x))))
245                         (map (lambda (x)
246                                (entity-ref x entity-set))
247                              (find-entities-by-components entity-set '(component-type ...)))))
248              ...)
249          form
250          ...)))))
251
252 (define-syntax define-system
253   (syntax-rules ()
254     ((_ system-name ((name (component-type ...)) ...) form ...)
255      (define system-name
256        (make-system ((name (component-type ...)) ...)
257          form
258          ...)))))
259
260 (define (composed-systems-result results)
261   (let ((changes (filter (lambda (r) (entities-changes? r)) results)))
262     (cond ((null? changes)
263            (car results))
264           (else
265            (append-changes changes)))))
266
267 (define (join-systems . systems)
268   (lambda (entity-set)
269     (let run ((s systems) (res '()))
270       (cond ((null? s)
271              (composed-systems-result res))
272             (else
273              (run (cdr s) (cons ((car s) entity-set) res)))))))
274
275 (define (thread-systems . systems)
276   (lambda (entity-set)
277     (let run-wait ((thd
278                     (map (lambda (s)
279                            (call-with-new-thread
280                             (lambda () (s entity-set))))
281                          systems))
282                    (res '()))
283       (cond ((null? thd)
284              (composed-systems-result res))
285             (else
286              (run-wait (cdr thd) (cons (join-thread (car thd)) res)))))))
287
288 (export entities-changes
289         entities-changes?
290         get-entities-changes
291         find-entities-by-components
292         define-system
293         make-system
294         join-systems
295         thread-systems)
296
297
298 ;;; Entities and components access inside systems
299
300 (define (get-key entity)
301   (car entity))
302
303 (define (get-component component-name entity)
304   (assoc-ref (cdr entity) component-name))
305
306 (export get-key
307         get-component)