]> git.jsancho.org Git - gacela.git/blob - src/system.scm
502ccd60beca405ef8a8d19908fcadb37d0f42df
[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
23
24 ;;; Component definitions
25
26 (define (symbol-concatenate . args)
27   (string->symbol
28    (string-concatenate
29     (map (lambda (a) (if (symbol? a) (symbol->string a) a)) args))))
30
31 (define-macro (define-component name . args)
32   `(begin
33      (use-modules (srfi srfi-9) (srfi srfi-9 gnu))
34      (define-record-type ,name
35        (,(symbol-concatenate "make-" name) ,@args)
36        ,(symbol-concatenate name "?")
37        ,@(map (lambda (a) (list a (symbol-concatenate name "-" a) (symbol-concatenate "set-" name "-" a "!"))) args))
38      (set-record-type-printer! ,name
39        (lambda (record port)
40          (format port "#<[~a]" ',name)
41          ,@(map (lambda (a) `(format port " ~a: ~a" ',a (,(symbol-concatenate name "-" a) record))) args)
42          (format port ">")))
43      ',name))
44
45 (define (export-component component)
46   (let ((name (record-type-name component))
47         (m (current-module)))
48     (module-export! m (list
49                        (symbol-concatenate "make-" name)
50                        (symbol-concatenate name "?")))
51     (for-each
52      (lambda (a)
53        (module-export! (current-module)
54                        (list
55                         (symbol-concatenate name "-" a)
56                         (symbol-concatenate "set-" name "-" a "!"))))
57      (record-type-fields component))))
58
59 (define (get-component-type component)
60   (record-type-name (record-type-descriptor component)))
61
62 (export define-component
63         export-component
64         get-component-type)
65
66
67 ;;; Entities and components
68
69 (define (normalize-components components)
70   (map
71    (lambda (c)
72      (if (record? c)
73          `(,(get-component-type c) . ,c)
74          c))
75    components))
76
77 (define (register-components entity components clist)
78   (cond ((null? components) clist)
79         (else
80          (let* ((type (car components))
81                 (elist (assoc-ref clist type)))
82            (register-components entity (cdr components)
83              (assoc-set! clist type
84                (cond (elist
85                       (lset-adjoin eq? elist entity))
86                      (else
87                       (list entity)))))))))
88
89 (define (unregister-components entity components clist)
90   (cond ((null? components) clist)
91         (else
92          (let* ((type (car components))
93                 (elist (lset-difference eq? (assoc-ref clist type) (list entity))))
94            (unregister-components entity (cdr components)
95              (cond ((null? elist)
96                     (assoc-remove! clist type))
97                    (else
98                     (assoc-set! clist type elist))))))))
99
100 (define (new-entity . new-components)
101   (lambda (entities components)
102     (let ((key (gensym))
103           (nc (normalize-components new-components)))
104       (values
105        (acons key nc entities)
106        (register-components key
107                             (map (lambda (c) (car c)) nc)
108                             components)
109        key))))
110
111 (define (remove-entity key)
112   (lambda (entities components)
113     (let ((clist (map (lambda (c) (car c)) (assoc-ref entities key))))
114       (values
115        (assoc-remove! entities key)
116        (unregister-components key clist components)))))
117
118 (define (set-entity key . new-components)
119   (lambda (entities components)
120     (let* ((nc (normalize-components new-components))
121            (clist (map (lambda (c) (car c)) (assoc-ref entities key)))
122            (nclist (map (lambda (c) (car c)) nc)))
123       (values
124        (assoc-set! entities key nc)
125        (register-components key (lset-difference eq? nclist clist)
126                             (unregister-components key (lset-difference eq? clist nclist) components))))))
127
128 (define (set-entity-components key . new-components)
129   (lambda (entities components)
130     (let ((nc (normalize-components new-components))
131           (clist (alist-copy (assoc-ref entities key))))
132       (for-each
133        (lambda (c)
134          (assoc-set! clist (car c) (cdr c)))
135        nc)
136       (values
137        (assoc-set! entities key clist)
138        (register-components key (map (lambda (c) (car c)) nc) components)))))
139
140 (define (remove-entity-components key . old-components)
141   (lambda (entities components)
142     (let ((clist (alist-copy (assoc-ref entities key))))
143       (for-each
144        (lambda (c)
145          (assoc-remove! clist c))
146        old-components)
147       (values
148        (assoc-set! entities key clist)
149        (unregister-components key old-components components)))))
150
151 (define (modify-entities changes entities components)
152   (cond ((null? changes)
153          (values entities components))
154         (else
155          (receive (e c) ((car changes) entities components)
156            (modify-entities (cdr changes) e c)))))
157
158 (export new-entity
159         remove-entity
160         set-entity
161         set-entity-components
162         remove-entity-components
163         modify-entities)
164
165
166 ;;; Making systems
167
168 (define* (find-entities-by-components c t)
169   (cond ((null? t) '())
170         (else
171          (let* ((e (assoc-ref c (car t)))
172                 (e* (if e e '())))
173            (cond ((null? (cdr t)) e*)
174                  (else
175                   (lset-intersection eq? e* (find-entities-by-components c (cdr t)))))))))
176                   
177
178 (define (make-system component-types system-fun)
179   (lambda (entities components)
180     (let* ((e (find-entities-by-components components component-types))
181            (e* (map (lambda (x) (assoc x entities)) e))
182            (e** (map (lambda (x) (cons (car x) (filter (lambda (x) (memq (car x) component-types)) (cdr x)))) e*))
183            (res (system-fun e**)))
184       (lambda* (#:optional (entities2 #f) (components2 #f))
185         (let ((e (if (and entities2 components2) entities2 entities))
186               (c (if (and entities2 components2) components2 components)))
187           (modify-entities res e c))))))
188
189
190 (define (join-systems . systems)
191   (lambda (entities components)
192     (let run ((s systems) (e entities) (c components))
193       (cond ((null? s)
194              (values e c))
195             (else
196              (receive (e2 c2) (((car s) e c))
197                (run (cdr s) e2 c2)))))))
198
199
200 (define (threaded-systems . systems)
201   (lambda (entities components)
202     (let run-wait ((thd
203                     (map
204                      (lambda (s)
205                        (call-with-new-thread
206                         (lambda () (s entities components))))
207                      systems))
208                    (e entities) (c components))
209       (cond ((null? thd)
210              (values e c))
211             (else
212              (receive (e2 c2) ((join-thread (car thd)) e c)
213                (run-wait (cdr thd) e2 c2)))))))
214
215
216 (export find-entities-by-components
217         make-system
218         join-systems
219         threaded-systems)