]> git.jsancho.org Git - gacela.git/blob - src/gacela.scm
Main loop with recursivity
[gacela.git] / src / gacela.scm
1 ;;; Gacela, a GNU Guile extension for fast games development
2 ;;; Copyright (C) 2009 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 gacela)
19   #:use-module (gacela events)
20   #:use-module (gacela video)
21   #:use-module (gacela audio)
22   #:use-module (ice-9 optargs)
23   #:export (*title*
24             *width-screen*
25             *height-screen*
26             *bpp-screen*
27             *frames-per-second*
28             *mode*
29             set-game-properties!
30             get-game-properties
31             init-gacela
32             quit-gacela
33             game-loop
34             gacela-script
35             game-running?
36             show-mob-hash
37             hide-mob-hash
38             get-active-mobs
39             hide-all-mobs
40             get-current-mob-id
41             get-mob-function-name
42             map-mobs
43             translate-mob)
44   #:export-syntax (game
45                    show-mob
46                    hide-mob
47                    the-mob
48                    define-mob-function
49                    define-mob
50                    lambda-mob
51                    define-checking-mobs)
52   #:re-export (get-frame-time
53                3d-mode?))
54
55
56 ;;; Main Loop
57
58 (define loop-flag #f)
59 (define game-loop-thread #f)
60
61 (define-macro (run-in-game-loop proc)
62   (let ((pgl (string->symbol (string-concatenate (list (symbol->string proc) "-in-game-loop"))))
63         (flag-symbol (gensym))
64         (value-symbol (gensym)))
65     `(begin
66        (define ,pgl ,proc)
67        (define (,proc . param)
68          (cond ((and game-loop-thread (not (eq? game-loop-thread (current-thread))))
69                 (let ((,flag-symbol #f))
70                   (define ,value-symbol)
71                   (system-async-mark
72                    (lambda ()
73                      (catch #t
74                            (lambda () (set! ,value-symbol (apply ,pgl param)))
75                            (lambda (key . args) #f))
76                      (set! ,flag-symbol #t))
77                    game-loop-thread)
78                   (while (not ,flag-symbol))
79                   ,value-symbol))
80                (else
81                 (apply ,pgl param)))))))
82
83 (run-in-game-loop load-texture)
84 (run-in-game-loop load-font)
85 (run-in-game-loop set-screen-bpp!)
86 (run-in-game-loop resize-screen)
87
88 (define-macro (game . code)
89   (if (null? code)
90       #f
91       `(call-with-new-thread (lambda () ,@code))))
92
93 (define (init-gacela)
94   (hide-all-mobs)
95   (cond ((not game-loop-thread)
96          (set! game-loop-thread (call-with-new-thread (lambda () (cond ((not (game-running?)) (game-loop))))))))
97   (while (not loop-flag))
98   #t)
99
100 (define (quit-gacela)
101   (hide-all-mobs)
102   (set! game-loop-thread #f)
103   (set! loop-flag #f)
104   (quit-video))
105
106 (define (game-loop)
107   (refresh-active-mobs)
108   (init-video *width-screen* *height-screen* *bpp-screen* #:title *title* #:mode *mode* #:fps *frames-per-second*)
109   (set! loop-flag #t)
110   (let loop ()
111     (cond (loop-flag
112            (init-frame-time)
113 ;           (check-connections)
114            (process-events)
115            (cond ((quit-signal?)
116                   (quit-gacela))
117                  (else
118                   (clear-screen)
119                   (to-origin)
120                   (refresh-active-mobs)
121                   (run-mobs)
122                   (run-extensions)
123                   (flip-screen)
124                   (delay-frame)
125                   (loop)))))))
126
127 (define (gacela-script args)
128   (while loop-flag (sleep 1)))
129
130 (define (game-running?)
131   loop-flag)
132
133
134 ;;; Extensions to main loop
135
136 (define extensions '())
137
138 (define (add-extension! proc pri)
139   "Add an extension with a priority to the main loop"
140   (set! extensions
141         (sort (assoc-set! extensions proc pri)
142               (lambda (a b)
143                 (< (cdr a) (cdr b))))))
144
145 (define (remove-extension! proc)
146   "Remove an extension from the main loop"
147   (set! extensions
148         (assoc-remove! extensions proc)))
149
150 (define (run-extensions)
151   (for-each (lambda (x) ((car x))) extensions))
152
153
154 ;;; Game Properties
155
156 (define *title* "Gacela")
157 (define *width-screen* 640)
158 (define *height-screen* 480)
159 (define *bpp-screen* 32)
160 (define *frames-per-second* 20)
161 (define *mode* '2d)
162 (define *fullscreen* 'off)
163
164 (define* (set-game-properties! #:key title width height bpp fps mode fullscreen)
165   (if title
166       (set-screen-title! title))
167   (if bpp
168       (set-screen-bpp! bpp))
169   (if (or width height)
170       (begin
171         (if (not width) (set! width (get-screen-width)))
172         (if (not height) (set! height (get-screen-height)))
173         (resize-screen width height)))
174   (if fps
175       (set-frames-per-second! fps))
176   (if mode
177       (if (eq? mode '3d) (set-3d-mode) (set-2d-mode)))
178   (if fullscreen
179       (set-fullscreen! fullscreen))
180   (get-game-properties))
181
182 (define (get-game-properties)
183   `((title . ,(get-screen-title)) (width . ,(get-screen-width)) (height . ,(get-screen-height)) (bpp . ,(get-screen-bpp)) (fps . ,(get-frames-per-second)) (mode . ,(if (3d-mode?) '3d '2d)) (fullscreen . ,(get-fullscreen))))
184
185
186 ;;; Mobs Factory
187
188 (define mobs-table (make-hash-table))
189 (define active-mobs '())
190 (define mobs-changed #f)
191
192 (define (show-mob-hash mob)
193   (hash-set! mobs-table (mob 'get-mob-id) mob)
194   (set! mobs-changed #t))
195
196 (define (hide-mob-hash mob-id)
197   (hash-remove! mobs-table mob-id)
198   (set! mobs-changed #t))
199
200 (define (refresh-active-mobs)
201   (cond (mobs-changed
202          (set! mobs-changed #f)
203          (set! active-mobs (hash-map->list (lambda (k v) v) mobs-table)))))
204
205 (define (get-active-mobs)
206   active-mobs)
207
208 (define (hide-all-mobs)
209   (set! mobs-changed #t)
210   (hash-clear! mobs-table))
211
212 (define (mobs-changed?)
213   mobs-changed)
214
215
216 (define-macro (show-mob mob)
217   (cond ((list? mob)
218          `(let ((m ,mob))
219             (show-mob-hash m)))
220         (else
221          `(show-mob-hash (lambda* (#:optional (option #f)) (,mob option))))))
222
223 (define-macro (hide-mob mob)
224   (cond ((list? mob)
225          `(let ((m ,mob))
226             (hide-mob-hash (m 'get-mob-id))))
227         (else
228          `(hide-mob-hash (,mob 'get-mob-id)))))
229
230 (define current-mob-id #f)
231
232 (define translate-mob translate)
233
234 (define (get-current-mob-id)
235   current-mob-id)
236
237 (define* (run-mobs #:optional (mobs (get-active-mobs)))
238   (let ((sorted-mobs (sort mobs (lambda (m1 m2) (< (m1 'get-z-index) (m2 'get-z-index))))))
239     (for-each
240      (lambda (m)
241        (set! current-mob-id (m 'get-mob-id))
242        (glmatrix-block (m)))
243      sorted-mobs)
244     (set! current-mob-id #f)))
245
246
247 ;;; Making mobs
248
249 (define mob-functions (make-hash-table))
250
251 (define (get-mob-function-name mob-name)
252   (let ((name (hash-ref mob-functions mob-name)))
253     (cond ((not name)
254            (set! name (gensym))
255            (hash-set! mob-functions mob-name name)))
256     name))
257
258 (define-macro (the-mob mob-name init-data)
259   `(let ((mob-id (gensym))
260          (mob-z-index 0)
261          (mob-time 0)
262          (mob-data ,init-data)
263          (saved-data ,init-data))
264      (lambda* (#:optional (option #f))
265        (define (save-data)
266          (let ((time (get-frame-time)))
267            (cond ((not (= time mob-time))
268                   (set! mob-time time)
269                   (set! saved-data mob-data)))))
270        (case option
271          ((get-mob-id)
272           mob-id)
273          ((get-z-index)
274           mob-z-index)
275          ((get-type)
276           (procedure-name ,mob-name))
277          ((get-data)
278           (save-data)
279           saved-data)
280          (else
281           (cond ((keyword? option)
282                  (assoc-ref saved-data (keyword->symbol option)))
283                 (else
284                  (save-data)
285                  (let ((res (,mob-name mob-id mob-data)))
286                    (set! mob-z-index (car res))
287                    (set! mob-data (cadr res))))))))))
288
289 (define-macro (define-mob-function attr . body)
290   (let ((attr (map (lambda (a) (if (list? a) a (list a #f))) attr))
291         (mob-id-symbol (gensym))
292         (mob-id-z-index (gensym))
293         (data-symbol (gensym)))
294     `(lambda (,mob-id-symbol ,data-symbol)
295        (let ((,mob-id-z-index 0))
296          (define (kill-me)
297            (hide-mob-hash ,mob-id-symbol))
298          (define* (translate x y #:optional (z 0))
299            (cond ((3d-mode?)
300                   (translate-mob x y z))
301                  (else
302                   (set! ,mob-id-z-index (+ ,mob-id-z-index z))
303                   (translate-mob x y))))
304          (let* ,attr
305            ,@(map
306               (lambda (a)
307                 `(let ((val (assoc-ref ,data-symbol ',(car a))))
308                    (cond (val (set! ,(car a) val)))))
309               attr)
310            (catch #t
311                   (lambda* () ,@body)
312                   (lambda (key . args) #f))
313            (list ,mob-id-z-index (list ,@(map (lambda (a) `(cons ',(car a) ,(car a))) attr))))))))
314
315 (define-macro (define-mob mob-head . body)
316   (let* ((name (car mob-head))
317          (attr (cdr mob-head))
318          (make-fun-symbol (gensym))
319          (mob-fun-symbol (gensym))
320          (params-symbol (gensym)))
321     `(define (,name . ,params-symbol)
322        (define ,make-fun-symbol
323          (lambda* ,(if (null? attr) '() `(#:key ,@attr))
324            (the-mob ,name (list ,@(map (lambda (a) `(cons ',(car a) ,(car a))) attr)))))
325        (define ,mob-fun-symbol
326          (define-mob-function ,attr ,@body))
327        (cond ((or (null? ,params-symbol) (keyword? (car ,params-symbol)))
328               (apply ,make-fun-symbol ,params-symbol))
329              (else
330               (apply ,mob-fun-symbol ,params-symbol))))))
331
332 (define-macro (lambda-mob attr . body)
333   (let ((fun-name (gensym)))
334     `(begin
335        (define-mob-function ,(cons fun-name attr) ,@body)
336        (the-mob 'undefined '() ,fun-name))))
337
338
339 ;;; Functions for checking mobs (collisions and more)
340
341 (define (map-mobs fun type)
342   (let ((mobs (filter (lambda (m) (and (eq? (m 'get-type) type) (not (eq? (m 'get-mob-id) (get-current-mob-id))))) (get-active-mobs))))
343     (map (lambda (m) (fun (m 'get-data))) mobs)))
344
345 (define-macro (define-checking-mobs head mob-def . body)
346   (let ((type (car mob-def)) (attr (cdr mob-def)))
347     `(define ,head
348        (map-mobs
349         (lambda (m)
350           (let ,(map (lambda (a) `(,(car a) (assoc-ref m ',(cadr a)))) attr)
351             ,@body))
352         ',type))))
353
354
355 ;;; Scenes
356
357 (define-macro (define-scene name . body)
358   `(define (,name)
359      ,@body))
360
361
362 (module-map (lambda (sym var)
363               (if (not (eq? sym '%module-public-interface))
364                   (module-export! (current-module) (list sym))))
365             (current-module))