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