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