]> git.jsancho.org Git - gacela.git/blob - src/system.scm
Replace define-macro with define-syntax
[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 (normalize-components components)
87   (map
88    (lambda (c)
89      (if (record? c)
90          `(,(get-component-type c) . ,c)
91          c))
92    components))
93
94 (define (register-components entity components clist)
95   (cond ((null? components) clist)
96         (else
97          (let* ((type (car components))
98                 (elist (assoc-ref clist type)))
99            (register-components entity (cdr components)
100              (assoc-set! clist type
101                (cond (elist
102                       (lset-adjoin eq? elist entity))
103                      (else
104                       (list entity)))))))))
105
106 (define (unregister-components entity components clist)
107   (cond ((null? components) clist)
108         (else
109          (let* ((type (car components))
110                 (elist (lset-difference eq? (assoc-ref clist type) (list entity))))
111            (unregister-components entity (cdr components)
112              (cond ((null? elist)
113                     (assoc-remove! clist type))
114                    (else
115                     (assoc-set! clist type elist))))))))
116
117 (define (new-entity . new-components)
118   (lambda (entities components)
119     (let ((key (gensym))
120           (nc (normalize-components new-components)))
121       (values
122        (acons key nc entities)
123        (register-components key
124                             (map (lambda (c) (car c)) nc)
125                             components)
126        key))))
127
128 (define (remove-entity key)
129   (lambda (entities components)
130     (let ((clist (map (lambda (c) (car c)) (assoc-ref entities key))))
131       (values
132        (assoc-remove! entities key)
133        (unregister-components key clist components)))))
134
135 (define (set-entity key . new-components)
136   (lambda (entities components)
137     (let* ((nc (normalize-components new-components))
138            (clist (map (lambda (c) (car c)) (assoc-ref entities key)))
139            (nclist (map (lambda (c) (car c)) nc)))
140       (values
141        (assoc-set! entities key nc)
142        (register-components key (lset-difference eq? nclist clist)
143                             (unregister-components key (lset-difference eq? clist nclist) components))))))
144
145 (define (set-entity-components key . new-components)
146   (lambda (entities components)
147     (let ((nc (normalize-components new-components))
148           (clist (alist-copy (assoc-ref entities key))))
149       (for-each
150        (lambda (c)
151          (assoc-set! clist (car c) (cdr c)))
152        nc)
153       (values
154        (assoc-set! entities key clist)
155        (register-components key (map (lambda (c) (car c)) nc) components)))))
156
157 (define (remove-entity-components key . old-components)
158   (lambda (entities components)
159     (let ((clist (alist-copy (assoc-ref entities key))))
160       (for-each
161        (lambda (c)
162          (assoc-remove! clist c))
163        old-components)
164       (values
165        (assoc-set! entities key clist)
166        (unregister-components key old-components components)))))
167
168 (define (modify-entities changes entities components)
169   (cond ((null? changes)
170          (values entities components))
171         (else
172          (receive (e c) ((car changes) entities components)
173            (modify-entities (cdr changes) e c)))))
174
175 (export new-entity
176         remove-entity
177         set-entity
178         set-entity-components
179         remove-entity-components
180         modify-entities)
181
182
183 ;;; Making systems
184
185 (define* (find-entities-by-components c t)
186   (cond ((null? t) '())
187         (else
188          (let* ((e (assoc-ref c (car t)))
189                 (e* (if e e '())))
190            (cond ((null? (cdr t)) e*)
191                  (else
192                   (lset-intersection eq? e* (find-entities-by-components c (cdr t)))))))))
193
194 (define-syntax make-system
195   (syntax-rules ()
196     ((_ component-types system-func)
197      (lambda (entities components)
198        (let* ((e (find-entities-by-components components 'component-types))
199               (e* (map (lambda (x) (assoc x entities)) e))
200               (e** (map (lambda (x) (cons (car x) (filter (lambda (x) (memq (car x) 'component-types)) (cdr x)))) e*))
201               (res (system-func e**)))
202          (lambda* (#:optional (entities2 #f) (components2 #f))
203            (let ((e (if (and entities2 components2) entities2 entities))
204                  (c (if (and entities2 components2) components2 components)))
205              (modify-entities res e c))))))))
206
207 (define-syntax define-system
208   (syntax-rules ()
209     ((_ (name . component-types) system-func)
210      (define name (make-system component-types system-func)))))
211
212 (define (join-systems . systems)
213   (lambda (entities components)
214     (let run ((s systems) (e entities) (c components))
215       (cond ((null? s)
216              (values e c))
217             (else
218              (receive (e2 c2) (((car s) e c))
219                (run (cdr s) e2 c2)))))))
220
221 (define (threaded-systems . systems)
222   (lambda (entities components)
223     (let run-wait ((thd
224                     (map
225                      (lambda (s)
226                        (call-with-new-thread
227                         (lambda () (s entities components))))
228                      systems))
229                    (e entities) (c components))
230       (cond ((null? thd)
231              (values e c))
232             (else
233              (receive (e2 c2) ((join-thread (car thd)) e c)
234                (run-wait (cdr thd) e2 c2)))))))
235
236 (export find-entities-by-components
237         define-system
238         make-system
239         join-systems
240         threaded-systems)
241
242
243 ;;; Entities and components access inside systems
244
245 (define (get-key entity)
246   (car entity))
247
248 (define (get-component component-name entity)
249   (assoc-ref (cdr entity) component-name))
250
251 (export get-key
252         get-component)