]> git.jsancho.org Git - gacela.git/blob - src/gacela.scm
61ff672c9d90c3f1adc11a18608d5ef685fd6d56
[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
105 (define (game-loop)
106   (refresh-active-mobs)
107   (init-video *width-screen* *height-screen* *bpp-screen* #:title *title* #:mode *mode* #:fps *frames-per-second*)
108   (set! loop-flag #t)
109   (while loop-flag
110          (init-frame-time)
111 ;           (check-connections)
112          (process-events)
113          (cond ((quit-signal?)
114                 (quit-gacela))
115                (else
116                 (clear-screen)
117                 (to-origin)
118                 (refresh-active-mobs)
119                 (run-mobs)
120                 (run-extensions)
121                 (flip-screen)
122                 (delay-frame))))
123   (quit-video))
124
125 (define (gacela-script args)
126   (while loop-flag (sleep 1)))
127
128 (define (game-running?)
129   loop-flag)
130
131
132 ;;; Extensions to main loop
133
134 (define extensions '())
135
136 (define (add-extension! proc pri)
137   "Add an extension with a priority to the main loop"
138   (set! extensions
139         (sort (assoc-set! extensions proc pri)
140               (lambda (a b)
141                 (< (cdr a) (cdr b))))))
142
143 (define (remove-extension! proc)
144   "Remove an extension from the main loop"
145   (set! extensions
146         (assoc-remove! extensions proc)))
147
148 (define (run-extensions)
149   (for-each (lambda (x) ((car x))) extensions))
150
151
152 ;;; Game Properties
153
154 (define *title* "Gacela")
155 (define *width-screen* 640)
156 (define *height-screen* 480)
157 (define *bpp-screen* 32)
158 (define *frames-per-second* 20)
159 (define *mode* '2d)
160 (define *fullscreen* 'off)
161
162 (define* (set-game-properties! #:key title width height bpp fps mode fullscreen)
163   (if title
164       (set-screen-title! title))
165   (if bpp
166       (set-screen-bpp! bpp))
167   (if (or width height)
168       (begin
169         (if (not width) (set! width (get-screen-width)))
170         (if (not height) (set! height (get-screen-height)))
171         (resize-screen width height)))
172   (if fps
173       (set-frames-per-second! fps))
174   (if mode
175       (if (eq? mode '3d) (set-3d-mode) (set-2d-mode)))
176   (if fullscreen
177       (set-fullscreen! fullscreen))
178   (get-game-properties))
179
180 (define (get-game-properties)
181   `((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))))
182
183
184 ;;; Mobs Factory
185
186 (define mobs-table (make-hash-table))
187 (define active-mobs '())
188 (define mobs-changed #f)
189
190 (define (show-mob-hash mob)
191   (hash-set! mobs-table (mob 'get-mob-id) mob)
192   (set! mobs-changed #t))
193
194 (define (hide-mob-hash mob-id)
195   (hash-remove! mobs-table mob-id)
196   (set! mobs-changed #t))
197
198 (define (refresh-active-mobs)
199   (cond (mobs-changed
200          (set! mobs-changed #f)
201          (set! active-mobs (hash-map->list (lambda (k v) v) mobs-table)))))
202
203 (define (get-active-mobs)
204   active-mobs)
205
206 (define (hide-all-mobs)
207   (set! mobs-changed #t)
208   (hash-clear! mobs-table))
209
210 (define (mobs-changed?)
211   mobs-changed)
212
213
214 (define-macro (show-mob mob)
215   (cond ((list? mob)
216          `(let ((m ,mob))
217             (show-mob-hash m)))
218         (else
219          `(show-mob-hash (lambda* (#:optional (option #f)) (,mob option))))))
220
221 (define-macro (hide-mob mob)
222   (cond ((list? mob)
223          `(let ((m ,mob))
224             (hide-mob-hash (m 'get-mob-id))))
225         (else
226          `(hide-mob-hash (,mob 'get-mob-id)))))
227
228 (define current-mob-id #f)
229
230 (define translate-mob translate)
231
232 (define (get-current-mob-id)
233   current-mob-id)
234
235 (define* (run-mobs #:optional (mobs (get-active-mobs)))
236   (let ((sorted-mobs (sort mobs (lambda (m1 m2) (< (m1 'get-z-index) (m2 'get-z-index))))))
237     (for-each
238      (lambda (m)
239        (set! current-mob-id (m 'get-mob-id))
240        (glmatrix-block (m)))
241      sorted-mobs)
242     (set! current-mob-id #f)))
243
244
245 ;;; Making mobs
246
247 (define mob-functions (make-hash-table))
248
249 (define (get-mob-function-name mob-name)
250   (let ((name (hash-ref mob-functions mob-name)))
251     (cond ((not name)
252            (set! name (gensym))
253            (hash-set! mob-functions mob-name name)))
254     name))
255
256 (define-macro (the-mob mob-name init-data)
257   `(let ((mob-id (gensym))
258          (mob-z-index 0)
259          (mob-time 0)
260          (mob-data ,init-data)
261          (saved-data ,init-data))
262      (lambda* (#:optional (option #f))
263        (define (save-data)
264          (let ((time (get-frame-time)))
265            (cond ((not (= time mob-time))
266                   (set! mob-time time)
267                   (set! saved-data mob-data)))))
268        (case option
269          ((get-mob-id)
270           mob-id)
271          ((get-z-index)
272           mob-z-index)
273          ((get-type)
274           (procedure-name ,mob-name))
275          ((get-data)
276           (save-data)
277           saved-data)
278          (else
279           (cond ((keyword? option)
280                  (assoc-ref saved-data (keyword->symbol option)))
281                 (else
282                  (save-data)
283                  (let ((res (,mob-name mob-id mob-data)))
284                    (set! mob-z-index (car res))
285                    (set! mob-data (cadr res))))))))))
286
287 (define-macro (define-mob-function attr . body)
288   (let ((attr (map (lambda (a) (if (list? a) a (list a #f))) attr))
289         (mob-id-symbol (gensym))
290         (mob-id-z-index (gensym))
291         (data-symbol (gensym)))
292     `(lambda (,mob-id-symbol ,data-symbol)
293        (let ((,mob-id-z-index 0))
294          (define (kill-me)
295            (hide-mob-hash ,mob-id-symbol))
296          (define* (translate x y #:optional (z 0))
297            (cond ((3d-mode?)
298                   (translate-mob x y z))
299                  (else
300                   (set! ,mob-id-z-index (+ ,mob-id-z-index z))
301                   (translate-mob x y))))
302          (let* ,attr
303            ,@(map
304               (lambda (a)
305                 `(let ((val (assoc-ref ,data-symbol ',(car a))))
306                    (cond (val (set! ,(car a) val)))))
307               attr)
308            (catch #t
309                   (lambda* () ,@body)
310                   (lambda (key . args) #f))
311            (list ,mob-id-z-index (list ,@(map (lambda (a) `(cons ',(car a) ,(car a))) attr))))))))
312
313 (define-macro (define-mob mob-head . body)
314   (let* ((name (car mob-head))
315          (attr (cdr mob-head))
316          (make-fun-symbol (gensym))
317          (mob-fun-symbol (gensym))
318          (params-symbol (gensym)))
319     `(define (,name . ,params-symbol)
320        (define ,make-fun-symbol
321          (lambda* ,(if (null? attr) '() `(#:key ,@attr))
322            (the-mob ,name (list ,@(map (lambda (a) `(cons ',(car a) ,(car a))) attr)))))
323        (define ,mob-fun-symbol
324          (define-mob-function ,attr ,@body))
325        (cond ((or (null? ,params-symbol) (keyword? (car ,params-symbol)))
326               (apply ,make-fun-symbol ,params-symbol))
327              (else
328               (apply ,mob-fun-symbol ,params-symbol))))))
329
330 (define-macro (lambda-mob attr . body)
331   (let ((fun-name (gensym)))
332     `(begin
333        (define-mob-function ,(cons fun-name attr) ,@body)
334        (the-mob 'undefined '() ,fun-name))))
335
336
337 ;;; Functions for checking mobs (collisions and more)
338
339 (define (map-mobs fun type)
340   (let ((mobs (filter (lambda (m) (and (eq? (m 'get-type) type) (not (eq? (m 'get-mob-id) (get-current-mob-id))))) (get-active-mobs))))
341     (map (lambda (m) (fun (m 'get-data))) mobs)))
342
343 (define-macro (define-checking-mobs head mob-def . body)
344   (let ((type (car mob-def)) (attr (cdr mob-def)))
345     `(define ,head
346        (map-mobs
347         (lambda (m)
348           (let ,(map (lambda (a) `(,(car a) (assoc-ref m ',(cadr a)))) attr)
349             ,@body))
350         ',type))))
351
352
353 ;;; Scenes
354
355 (define-macro (define-scene name . body)
356   `(define (,name)
357      ,@body))
358
359
360 (module-map (lambda (sym var)
361               (if (not (eq? sym '%module-public-interface))
362                   (module-export! (current-module) (list sym))))
363             (current-module))