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