http://slackware.bholcomb.com/slackware/slackware-11.0/source/xap/xscreensaver/xscree...
[xscreensaver] / hacks / speedmine.c
1 /* -*- Mode: C; c-basic-offset: 4; tab-width: 4 -*-
2  * speedmine, Copyright (C) 2001 Conrad Parker <conrad@deephackmode.org>
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation.  No representations are made about the suitability of this
9  * software for any purpose.  It is provided "as is" without express or 
10  * implied warranty.
11  */
12
13 /*
14  * Written mostly over the Easter holiday, 2001. Psychedelic option due to
15  * a night at Home nightclub, Sydney. Three all-nighters of solid partying
16  * were involved in the week this hack was written.
17  *
18  * Happy Birthday to WierdArms (17 April) and Pat (18 April)
19  */
20
21 /*
22  * Hacking notes
23  *
24  * This program generates a rectangular terrain grid and maps this onto
25  * a semi-circular tunnel. The terrain has length TERRAIN_LENGTH, which
26  * corresponds to length along the tunnel, and breadth TERRAIN_BREADTH,
27  * which corresponds to circumference around the tunnel. For each frame,
28  * the tunnel is perspective mapped onto a set of X and Y screen values.
29  *
30  * Throughout this code the following temporary variable names are used:
31  *
32  *                      i       iterates along the tunnel in the direction of travel
33  *                      j       iterates around the tunnel clockwise
34  *                      t       iterates along the length of the perspective mapped values
35  *                              from the furthest to the nearest
36  *
37  * Thus, the buffers are used with these iterators:
38  *
39  *                      terrain[i][j]                                   terrain map
40  *                      worldx[i][j], worldy[i][j]              world coordinates (after wrapping)
41  *                      {x,y,z}curvature[i]                             tunnel curvature
42  *                      wideness[i]                                             tunnel wideness
43  *                      bonuses[i]                                              bonus values
44  *
45  *                      xvals[t][j], yvals[t][j]                screen coordinates
46  *                      {min,max}{x,y}[t]                               bounding boxes of screen coords
47  */
48
49 /* Define or undefine NDEBUG to turn assert and abort debugging off or on */
50 #define NDEBUG
51 #include <assert.h>
52
53 #include <math.h>
54
55 #include "screenhack.h"
56 #include "erase.h"
57
58 #define MIN(a,b) ((a)<(b)?(a):(b))
59 #define MAX(a,b) ((a)>(b)?(a):(b))
60
61 #define RAND(r) (int)(((r)>0)?(random() % (long)(r)): -(random() % (long)(-r)))
62
63 #define SIGN3(a) ((a)>0?1:((a)<0?-1:0))
64
65 #define MODULO(a,b) while ((a)<0) (a)+=(b); (a) %= (b);
66
67 /* No. of shades of each color (ground, walls, bonuses) */
68 #define MAX_COLORS 32
69
70 #ifdef NDEBUG
71 #define DEBUG_FLAG 0
72 #else
73 #define DEBUG_FLAG 1
74 #endif
75
76
77 #define FORWARDS 1
78 #define BACKWARDS -1
79 /* Apparently AIX's math.h bogusly defines `nearest' as a function,
80    in violation of the ANSI C spec. */
81 #undef nearest
82 #define nearest n3arest
83
84 #define wireframe (st->wire_flag||st->wire_bonus>8||st->wire_bonus%2==1)
85 #define effective_speed (st->direction*(st->speed+st->speed_bonus))
86
87 /* No. of levels of interpolation, for perspective */
88 #define INTERP 32
89
90 /* These must be powers of 2 */
91 #define TERRAIN_LENGTH 256
92 #define TERRAIN_BREADTH 32
93
94 /* total "perspective distance" of terrain */
95 #define TERRAIN_PDIST (INTERP*TERRAIN_LENGTH)
96
97 #define ROTS 1024
98 #define TB_MUL (ROTS/TERRAIN_BREADTH)
99
100 #define random_elevation() (st->terrain_flag?(random() % 200):0)
101 #define random_curvature() (st->curviness>0.0?((double)(random() % 40)-20)*st->curviness:0.0)
102 #define random_twist() (st->twistiness>0.0?((double)(random() % 40)-20)*st->twistiness:0.0)
103 #define random_wideness() (st->widening_flag?(int)(random() % 1200):0)
104
105 #define STEEL_ELEVATION 300
106
107 struct state {
108     Display *dpy;
109     Window window;
110
111     Pixmap dbuf, stars_mask;
112     Colormap cmap;
113     unsigned int default_fg_pixel;
114     GC draw_gc, erase_gc, tunnelend_gc, stars_gc, stars_erase_gc;
115
116     int ncolors, nr_ground_colors, nr_wall_colors, nr_bonus_colors;
117     XColor ground_colors[MAX_COLORS], wall_colors[MAX_COLORS];
118     XColor bonus_colors[MAX_COLORS];
119     GC ground_gcs[MAX_COLORS], wall_gcs[MAX_COLORS], bonus_gcs[MAX_COLORS];
120
121     int be_wormy;
122
123     int width, height;
124     int delay;
125
126     int smoothness;
127     int verbose_flag;
128     int wire_flag;
129     int terrain_flag;
130     int widening_flag;
131     int bumps_flag;
132     int bonuses_flag;
133     int crosshair_flag;
134     int psychedelic_flag;
135
136     double maxspeed;
137
138     double thrust, gravity;
139
140     double vertigo;
141     double curviness;
142     double twistiness;
143
144     double pos;
145     double speed;
146     double accel;
147     double step;
148
149     int direction;
150
151     int pindex, nearest;
152     int flipped_at;
153     int xoffset, yoffset;
154
155     int bonus_bright;
156     int wire_bonus;
157
158     double speed_bonus;
159
160     int spin_bonus;
161     int backwards_bonus;
162
163     double sintab[ROTS], costab[ROTS];
164
165     int orientation;
166
167     int terrain[TERRAIN_LENGTH][TERRAIN_BREADTH];
168     double xcurvature[TERRAIN_LENGTH];
169     double ycurvature[TERRAIN_LENGTH];
170     double zcurvature[TERRAIN_LENGTH];
171     int wideness[TERRAIN_LENGTH];
172     int bonuses[TERRAIN_LENGTH];
173     int xvals[TERRAIN_LENGTH][TERRAIN_BREADTH];
174     int yvals[TERRAIN_LENGTH][TERRAIN_BREADTH];
175     double worldx[TERRAIN_LENGTH][TERRAIN_BREADTH];
176     double worldy[TERRAIN_LENGTH][TERRAIN_BREADTH];
177     int minx[TERRAIN_LENGTH], maxx[TERRAIN_LENGTH];
178     int miny[TERRAIN_LENGTH], maxy[TERRAIN_LENGTH];
179
180     int total_nframes;
181     int nframes;
182     double fps;
183     double fps_start, fps_end;
184     struct timeval start_time;
185
186     int rotation_offset;
187     int jamming;
188 };
189
190 /* a forward declaration ... */
191 static void change_colors(struct state *st);
192
193
194
195 /*
196  * get_time ()
197  *
198  * returns the total time elapsed since the beginning of the demo
199  */
200 static double get_time(struct state *st) {
201   struct timeval t;
202   float f;
203 #if GETTIMEOFDAY_TWO_ARGS
204   gettimeofday(&t, NULL);
205 #else
206   gettimeofday(&t);
207 #endif
208   t.tv_sec -= st->start_time.tv_sec;
209   f = ((double)t.tv_sec) + t.tv_usec*1e-6;
210   return f;
211 }
212
213 /*
214  * init_time ()
215  *
216  * initialises the timing structures
217  */
218 static void init_time(struct state *st) {
219 #if GETTIMEOFDAY_TWO_ARGS
220   gettimeofday(&st->start_time, NULL);
221 #else
222   gettimeofday(&st->start_time);
223 #endif
224   st->fps_start = get_time(st);
225 }
226
227
228 /*
229  * perspective()
230  *
231  * perspective map the world coordinates worldx[i][j], worldy[i][j] onto
232  * screen coordinates xvals[t][j], yvals[t][j]
233  */
234 static void
235 perspective (struct state *st)
236 {
237         int i, j, jj, t=0, depth, view_pos;
238         int rotation_bias, r;
239         double xc=0.0, yc=0.0, zc=0.0;
240         double xcc=0.0, ycc=0.0, zcc=0.0;
241         double xx, yy;
242         double zfactor, zf;
243
244         zf = 8.0*28.0 / (double)(st->width*TERRAIN_LENGTH);
245         if (st->be_wormy) zf *= 3.0;
246
247         depth = TERRAIN_PDIST - INTERP + st->pindex;
248
249         view_pos = (st->nearest+3*TERRAIN_LENGTH/4)%TERRAIN_LENGTH;
250         
251         st->xoffset += - st->xcurvature[view_pos]*st->curviness/8;
252         st->xoffset /= 2;
253
254         st->yoffset += - st->ycurvature[view_pos]*st->curviness/4;
255         st->yoffset /= 2;
256
257         st->rotation_offset += (int)((st->zcurvature[view_pos]-st->zcurvature[st->nearest])*ROTS/8);
258         st->rotation_offset /= 2;
259         rotation_bias = st->orientation + st->spin_bonus - st->rotation_offset;
260
261         if (st->bumps_flag) {
262                 if (st->be_wormy) {
263                         st->yoffset -= ((st->terrain[view_pos][TERRAIN_BREADTH/4] * st->width /(8*1600)));
264                         rotation_bias += (st->terrain[view_pos][TERRAIN_BREADTH/4+2] -
265                                                          st->terrain[view_pos][TERRAIN_BREADTH/4-2])/8;
266                 } else {
267                         st->yoffset -= ((st->terrain[view_pos][TERRAIN_BREADTH/4] * st->width /(2*1600)));
268                         rotation_bias += (st->terrain[view_pos][TERRAIN_BREADTH/4+2] -
269                                                          st->terrain[view_pos][TERRAIN_BREADTH/4-2])/16;
270                 }
271         }
272
273         MODULO(rotation_bias, ROTS);
274
275         for (t=0; t < TERRAIN_LENGTH; t++) {
276                 i = st->nearest + t; MODULO(i, TERRAIN_LENGTH);
277                 xc += st->xcurvature[i]; yc += st->ycurvature[i]; zc += st->zcurvature[i];
278                 xcc += xc; ycc += yc; zcc += zc;
279                 st->maxx[i] = st->maxy[i] = 0;
280                 st->minx[i] = st->width; st->miny[i] = st->height;
281         }
282
283         for (t=0; t < TERRAIN_LENGTH; t++) {
284                 i = st->nearest - 1 - t; MODULO(i, TERRAIN_LENGTH);
285
286                 zfactor = (double)depth* (12.0 - TERRAIN_LENGTH/8.0) * zf;
287                 for (j=0; j < TERRAIN_BREADTH; j++) {
288                         jj = st->direction * j; MODULO(jj, TERRAIN_BREADTH);
289             /* jwz: not totally sure if this is right, but it avoids div0 */
290             if (zfactor != 0) {
291                 xx = (st->worldx[i][jj]-(st->vertigo*xcc))/zfactor;
292                 yy = (st->worldy[i][j]-(st->vertigo*ycc))/zfactor;
293             } else {
294                 xx = 0;
295                 yy = 0;
296             }
297                         r = rotation_bias + (int)(st->vertigo*zcc); MODULO(r, ROTS);
298
299                         st->xvals[t][j] = st->xoffset + (st->width>>1) +
300                                           (int)(xx * st->costab[r] - yy * st->sintab[r]);
301                         st->maxx[t] = MAX(st->maxx[t], st->xvals[t][j]);
302                         st->minx[t] = MIN(st->minx[t], st->xvals[t][j]);
303
304                         st->yvals[t][j] = st->yoffset + st->height/2 +
305                                                   (int)(xx * st->sintab[r] + yy * st->costab[r]);
306                         st->maxy[t] = MAX(st->maxy[t], st->yvals[t][j]);
307                         st->miny[t] = MIN(st->miny[t], st->yvals[t][j]);
308                 }
309                 xcc -= xc; ycc -= yc; zcc -= zc;
310                 xc -= st->xcurvature[i]; yc -= st->ycurvature[i]; zc -= st->zcurvature[i];
311                 depth -= INTERP;
312         }
313 }
314
315 /*
316  * wrap_tunnel (start, end)
317  *
318  * wrap the terrain terrain[i][j] around the semi-circular tunnel function
319  * 
320  *                      x' = x/2 * cos(theta) - (y-k) * x * sin(theta)
321  *                      y' = x/4 * sin(theta) + y * cos(theta)
322  *
323  * between i=start and i=end inclusive, producing world coordinates
324  * worldx[i][j], worldy[i][j]
325  */
326 static void
327 wrap_tunnel (struct state *st, int start, int end)
328 {
329         int i, j, v;
330         double x, y;
331
332         assert (start < end);
333
334         for (i=start; i <= end; i++) {
335                 for (j=0; j < TERRAIN_BREADTH; j++) {
336                         x = j * (1.0/TERRAIN_BREADTH);
337                         v = st->terrain[i][j];
338                         y = (double)(v==STEEL_ELEVATION?200:v) - st->wideness[i] - 1200;
339
340                         /* lower road */
341                         if (j > TERRAIN_BREADTH/8 && j < 3*TERRAIN_BREADTH/8) y -= 300;
342
343                     st->worldx[i][j] = x/2 * st->costab[j*TB_MUL] -
344                                                         (y-st->height/4.0)*x*st->sintab[j*TB_MUL];
345                         st->worldy[i][j] = x/4 * st->sintab[j*TB_MUL] +
346                                                         y * st->costab[j*TB_MUL];
347                 }
348         }
349 }
350
351 /*
352  * flip_direction()
353  *
354  * perform the state transitions and terrain transformation for the
355  * "look backwards/look forwards" bonus
356  */
357 static void
358 flip_direction (struct state *st)
359 {
360         int i, ip, in, j, t;
361
362         st->direction = -st->direction;
363
364         st->bonus_bright = 20;
365
366         for (i=0; i < TERRAIN_LENGTH; i++) {
367                 in = st->nearest + i; MODULO(in, TERRAIN_BREADTH);
368                 ip = st->nearest - i; MODULO(ip, TERRAIN_BREADTH);
369                 for (j=0; j < TERRAIN_BREADTH; j++) {
370                         t = st->terrain[ip][j];
371                         st->terrain[ip][j] = st->terrain[in][j];
372                         st->terrain[in][j] = t;
373                 }
374         }
375 }
376
377 /*
378  * generate_smooth (start, end)
379  *
380  * generate smooth terrain between i=start and i=end inclusive
381  */
382 static void
383 generate_smooth (struct state *st, int start, int end)
384 {
385         int i,j, ii;
386
387         assert (start < end);
388
389         for (i=start; i <= end; i++) {
390                 ii = i; MODULO(ii, TERRAIN_LENGTH);
391                 for (j=0; j < TERRAIN_BREADTH; j++) {
392                         st->terrain[i][j] = STEEL_ELEVATION;
393                 }
394         }
395 }
396
397 /*
398  * generate_straight (start, end)
399  *
400  * zero the curvature and wideness between i=start and i=end inclusive
401  */
402 static void
403 generate_straight (struct state *st, int start, int end)
404 {
405         int i,j, ii;
406
407         assert (start < end);
408
409         for (i=start; i <= end; i++) {
410                 ii = i; MODULO(ii, TERRAIN_LENGTH);
411                 for (j=0; j < TERRAIN_BREADTH; j++) {
412                         st->xcurvature[ii] = 0;
413                         st->ycurvature[ii] = 0;
414                         st->zcurvature[ii] = 0;
415                         st->wideness[ii] = 0;
416                 }
417         }
418 }
419
420 /*
421  * int generate_terrain_value (v1, v2, v3, v4, w)
422  *
423  * generate terrain value near the average of v1, v2, v3, v4, with
424  * perturbation proportional to w
425  */
426 static int
427 generate_terrain_value (struct state *st, int v1, int v2, int v3, int v4, int w)
428 {
429         int sum, ret;
430         int rval;
431
432         if (!st->terrain_flag) return 0;
433
434         sum = v1 + v2 + v3 + v4;
435
436         rval = w*sum/st->smoothness;
437         if (rval == 0) rval = 2;
438
439         ret = (sum/4 -(rval/2) + RAND(rval));
440
441         if (ret < -400 || ret > 400) {
442                 ret = sum/4;
443         }
444
445         return ret;
446 }
447
448 /*
449  * generate_terrain (start, end, final)
450  *
451  * generate terrain[i][j] between i=start and i=end inclusive
452  *
453  * This is performed by successive subdivision of the terrain into
454  * rectangles of decreasing size. Subdivision continues until the
455  * the minimum width or height of these rectangles is 'final'; ie.
456  * final=1 indicates to subdivide as far as possible, final=2 indicates
457  * to stop one subdivision before that (leaving a checkerboard pattern
458  * uncalculated) etc.
459  */
460 static void
461 generate_terrain (struct state *st, int start, int end, int final)
462 {
463         int i,j,w,l;
464         int ip, jp, in, jn; /* prev, next values */
465         int diff;
466
467         assert (start < end);
468         assert (start >= 0 && start < TERRAIN_LENGTH);
469         assert (end >= 0 && end < TERRAIN_LENGTH);
470
471         diff = end - start + 1;
472
473         st->terrain[end][0] = random_elevation();
474         st->terrain[end][TERRAIN_BREADTH/2] = random_elevation();
475
476         for (w= diff/2, l=TERRAIN_BREADTH/4;
477              w >= final || l >= final; w /= 2, l /= 2) {
478
479                 if (w<1) w=1; if (l<1) l=1;
480
481                 for (i=start+w-1; i < end; i += (w*2)) {
482                         ip = i-w; MODULO(ip, TERRAIN_LENGTH);
483                         in = i+w; MODULO(in, TERRAIN_LENGTH);
484                         for (j=l-1; j < TERRAIN_BREADTH; j += (l*2)) {
485                                 jp = j-1; MODULO(jp, TERRAIN_BREADTH);
486                                 jn = j+1; MODULO(jn, TERRAIN_BREADTH);
487                                 st->terrain[i][j] =
488                                         generate_terrain_value (st, st->terrain[ip][jp], st->terrain[in][jp], 
489                                             st->terrain[ip][jn], st->terrain[in][jn], w);
490                         }
491                 }
492
493                 for (i=start+(w*2)-1; i < end; i += (w*2)) {
494                         ip = i-w; MODULO(ip, TERRAIN_LENGTH);
495                         in = i+w; MODULO(in, TERRAIN_LENGTH);
496                         for (j=l-1; j < TERRAIN_BREADTH; j += (l*2)) {
497                                 jp = j-1; MODULO(jp, TERRAIN_BREADTH);
498                                 jn = j+1; MODULO(jn, TERRAIN_BREADTH);
499                                 st->terrain[i][j] =
500                                         generate_terrain_value (st, st->terrain[ip][j], st->terrain[in][j],
501                                             st->terrain[i][jp], st->terrain[i][jn], w);
502                         }
503                 }
504
505                 for (i=start+w-1; i < end; i += (w*2)) {
506                         ip = i-w; MODULO(ip, TERRAIN_LENGTH);
507                         in = i+w; MODULO(in, TERRAIN_LENGTH);
508                         for (j=2*l-1; j < TERRAIN_BREADTH; j += (l*2)) {
509                                 jp = j-1; MODULO(jp, TERRAIN_BREADTH);
510                                 jn = j+1; MODULO(jn, TERRAIN_BREADTH);
511                                 st->terrain[i][j] =
512                                         generate_terrain_value (st, st->terrain[ip][j], st->terrain[in][j],
513                                             st->terrain[i][jp], st->terrain[i][jn], w);
514                         }
515                 }
516         }
517 }
518
519 /*
520  * double generate_curvature_value (v1, v2, w)
521  *
522  * generate curvature value near the average of v1 and v2, with perturbation
523  * proportional to w
524  */
525 static double
526 generate_curvature_value (double v1, double v2, int w)
527 {
528         double sum, avg, diff, ret;
529         int rval;
530
531         assert (!isnan(v1) && !isnan(v2));
532
533         sum = v1+v2;
534         avg = sum/2.0;
535
536         diff = MIN(v1 - avg, v2 - avg);
537
538         rval = (int)diff * w;
539         if (rval == 0.0) return avg;
540
541         ret = (avg -((double)rval)/500.0 + ((double)RAND(rval))/1000.0);
542
543         assert (!isnan(ret));
544
545         return ret;
546 }
547
548 /*
549  * generate_curves (start, end)
550  *
551  * generate xcurvature[i], ycurvature[i], zcurvature[i] and wideness[i]
552  * between start and end inclusive
553  */
554 static void
555 generate_curves (struct state *st, int start, int end)
556 {
557         int i, diff, ii, in, ip, w;
558
559         assert (start < end);
560
561         diff = end - start + 1; MODULO (diff, TERRAIN_LENGTH);
562
563         if (random() % 100 == 0)
564           st->xcurvature[end] = 30 * random_curvature();
565         else if (random() % 10 == 0)
566           st->xcurvature[end] = 20 * random_curvature();
567         else
568           st->xcurvature[end] = 10 * random_curvature();
569
570         if (random() % 50 == 0)
571           st->ycurvature[end] = 20 * random_curvature();
572         else if (random() % 25 == 0)
573           st->ycurvature[end] = 30 * random_curvature();
574         else
575           st->ycurvature[end] = 10 * random_curvature();
576
577         if (random() % 3 == 0)
578           st->zcurvature[end] = random_twist();
579         else
580           st->zcurvature[end] =
581                           generate_curvature_value (st->zcurvature[end], random_twist(), 1);
582
583         if (st->be_wormy)
584                         st->wideness[end] = random_wideness();
585         else
586                 st->wideness[end] =
587                         generate_curvature_value (st->wideness[end], random_wideness(), 1);
588
589     for (w=diff/2; w >= 1; w /= 2) {
590       for (i=start+w-1; i < end; i+=(w*2)) {
591         ii = i; MODULO (ii, TERRAIN_LENGTH);
592                 ip = i-w; MODULO (ip, TERRAIN_LENGTH);
593             in = i+w; MODULO (in, TERRAIN_LENGTH);
594             st->xcurvature[ii] =
595                                 generate_curvature_value (st->xcurvature[ip], st->xcurvature[in], w);
596             st->ycurvature[ii] =
597                                 generate_curvature_value (st->ycurvature[ip], st->ycurvature[in], w);
598             st->zcurvature[ii] =
599                                 generate_curvature_value (st->zcurvature[ip], st->zcurvature[in], w);
600             st->wideness[ii] =
601                                 generate_curvature_value (st->wideness[ip], st->wideness[in], w);
602       }
603     }
604 }
605
606 /*
607  * do_bonus ()
608  *
609  * choose a random bonus and perform its state transition
610  */
611 static void
612 do_bonus (struct state *st)
613 {
614         st->bonus_bright = 20;
615
616         if (st->jamming > 0) {
617                 st->jamming--;
618                 st->nearest -= 2; MODULO(st->nearest, TERRAIN_LENGTH);
619                 return;
620         }
621
622         if (st->psychedelic_flag) change_colors(st);
623
624         switch (random() % 7) {
625         case 0: /* switch to or from wireframe */
626                 st->wire_bonus = (st->wire_bonus?0:300);
627                 break;
628         case 1: /* speedup */
629                 st->speed_bonus = 40.0;
630                 break;
631         case 2:
632                 st->spin_bonus += ROTS;
633                 break;
634         case 3:
635                 st->spin_bonus -= ROTS;
636                 break;
637         case 4: /* look backwards / look forwards */
638                 st->flipped_at = st->nearest;
639                 flip_direction (st);
640                 st->backwards_bonus = (st->backwards_bonus?0:10);
641                 break;
642         case 5:
643                 change_colors(st);
644                 break;
645         case 6: /* jam against the bonus a few times; deja vu! */
646                 st->nearest -= 2; MODULO(st->nearest, TERRAIN_LENGTH);
647                 st->jamming = 3;
648                 break;
649         default:
650                 assert(0);
651                 break;
652         }
653 }
654
655 /*
656  * check_bonus ()
657  *
658  * check if a bonus has been passed in the last frame, and handle it
659  */
660 static void
661 check_bonuses (struct state *st)
662 {
663         int i, ii, start, end;
664
665         if (!st->bonuses_flag) return;
666
667         if (st->step >= 0.0) {
668                 start = st->nearest; end = st->nearest + (int)floor(st->step);
669         } else {
670                 end = st->nearest; start = st->nearest + (int)floor(st->step);
671         }
672
673         if (st->be_wormy) {
674                 start += TERRAIN_LENGTH/4;
675                 end += TERRAIN_LENGTH/4;
676         }
677
678         for (i=start; i < end; i++) {
679                 ii = i; MODULO(ii, TERRAIN_LENGTH);
680                 if (st->bonuses[ii] == 1) do_bonus (st);
681         }
682 }
683
684 /*
685  * decrement_bonuses (double time_per_frame)
686  *
687  * decrement timers associated with bonuses
688  */
689 static void
690 decrement_bonuses (struct state *st, double time_per_frame)
691 {
692         if (!st->bonuses_flag) return;
693
694         if (st->bonus_bright > 0) st->bonus_bright-=4;
695         if (st->wire_bonus > 0) st->wire_bonus--;
696         if (st->speed_bonus > 0) st->speed_bonus -= 2.0;
697
698         if (st->spin_bonus > 10) st->spin_bonus -= (int)(st->step*13.7);
699         else if (st->spin_bonus < -10) st->spin_bonus += (int)(st->step*11.3);
700
701         if (st->backwards_bonus > 1) st->backwards_bonus--;
702         else if (st->backwards_bonus == 1) {
703                 st->nearest += 2*(MAX(st->flipped_at, st->nearest) - MIN(st->flipped_at,st->nearest));
704                 MODULO(st->nearest, TERRAIN_LENGTH);
705                 flip_direction (st);
706                 st->backwards_bonus = 0;
707         }
708 }
709
710 /*
711  * set_bonuses (start, end)
712  *
713  * choose if to and where to set a bonus between i=start and i=end inclusive
714  */
715 static void
716 set_bonuses (struct state *st, int start, int end)
717 {
718         int i, diff, ii;
719
720         if (!st->bonuses_flag) return;
721
722         assert (start < end);
723
724         diff = end - start;
725
726         for (i=start; i <= end; i++) {
727                 ii = i; if (ii>=TERRAIN_LENGTH) ii -= TERRAIN_LENGTH;
728                 st->bonuses[ii] = 0;
729         }
730         if (random() % 4 == 0) {
731                 i = start + RAND(diff-3);
732                 ii = i; if (ii>=TERRAIN_LENGTH) ii -= TERRAIN_LENGTH;
733                 st->bonuses[ii] = 2; /* marker */
734                 ii = i+3; if (ii>=TERRAIN_LENGTH) ii -= TERRAIN_LENGTH;
735                 st->bonuses[ii] = 1; /* real thing */
736         }
737 }
738
739 /*
740  * regenerate_terrain ()
741  *
742  * regenerate a portion of the terrain map of length TERRAIN_LENGTH/4 iff
743  * we just passed between two quarters of the terrain.
744  *
745  * choose the kind of terrain to produce, produce it and wrap the tunnel
746  */
747 static void
748 regenerate_terrain (struct state *st)
749 {
750         int start, end;
751         int passed;
752
753         passed = st->nearest % (TERRAIN_LENGTH/4);
754
755         if (st->speed == 0.0 ||
756                 (st->speed > 0.0 && passed > (int)st->step) ||
757                 (st->speed < 0.0 && (TERRAIN_LENGTH/4)-passed > (int)fabs(st->step))) {
758
759                 return;
760         }
761
762         end = st->nearest - passed - 1; MODULO(end, TERRAIN_LENGTH);
763         start = end - TERRAIN_LENGTH/4 + 1; MODULO(start, TERRAIN_LENGTH);
764
765         if (DEBUG_FLAG) printf ("Regenerating [%d - %d]\n", start, end);
766
767         set_bonuses (st, start, end);
768
769         switch (random() % 64) {
770         case 0:
771         case 1:
772                 generate_terrain (st, start, end, 1);
773                 generate_smooth (st, start,
774                         start + TERRAIN_LENGTH/8 + (random() % TERRAIN_LENGTH/8));
775                 break;
776         case 2:
777                 generate_smooth (st, start, end);
778                 generate_terrain (st, start, end, 4); break;
779         case 3:
780                 generate_smooth (st, start, end);
781                 generate_terrain (st, start, end, 2); break;
782         default:
783                 generate_terrain (st, start, end, 1);
784         }
785
786         if (random() % 16 == 0) {
787                 generate_straight (st, start, end);
788         } else {
789                 generate_curves (st, start, end);
790         }
791
792         wrap_tunnel (st, start, end);
793 }
794
795 /*
796  * init_terrain ()
797  *
798  * initialise the terrain map for the beginning of the demo
799  */
800 static void
801 init_terrain (struct state *st)
802 {
803         int i, j;
804
805         for (i=0; i < TERRAIN_LENGTH; i++) {
806                 for (j=0; j < TERRAIN_BREADTH; j++) {
807                         st->terrain[i][j] = 0;
808                 }
809         }
810
811         st->terrain[TERRAIN_LENGTH-1][0] =  - (random() % 300);
812         st->terrain[TERRAIN_LENGTH-1][TERRAIN_BREADTH/2] =  - (random() % 300);
813
814         generate_smooth (st, 0, TERRAIN_LENGTH-1);
815         generate_terrain (st, 0, TERRAIN_LENGTH/4 -1, 4);
816         generate_terrain (st, TERRAIN_LENGTH/4, TERRAIN_LENGTH/2 -1, 2);
817         generate_terrain (st, TERRAIN_LENGTH/2, 3*TERRAIN_LENGTH/4 -1, 1);
818         generate_smooth (st, 3*TERRAIN_LENGTH/4, TERRAIN_LENGTH-1);
819 }
820
821 /*
822  * init_curves ()
823  *
824  * initialise the curvatures and wideness for the beginning of the demo.
825  */
826 static void
827 init_curves (struct state *st)
828 {
829         int i;
830
831         for (i=0; i < TERRAIN_LENGTH-1; i++) {
832         st->xcurvature[i] = 0.0;
833             st->ycurvature[i] = 0.0;
834                 st->zcurvature[i] = 0.0;
835         }
836
837     st->xcurvature[TERRAIN_LENGTH-1] = random_curvature();
838     st->ycurvature[TERRAIN_LENGTH-1] = random_curvature();
839     st->zcurvature[TERRAIN_LENGTH-1] = random_twist();
840
841         generate_straight (st, 0, TERRAIN_LENGTH/4-1);
842         generate_curves (st, TERRAIN_LENGTH/4, TERRAIN_LENGTH/2-1);
843         generate_curves (st, TERRAIN_LENGTH/2, 3*TERRAIN_LENGTH/4-1);
844         generate_straight (st, 3*TERRAIN_LENGTH/4, TERRAIN_LENGTH-1);
845
846 }
847
848 /*
849  * render_quads (dpy, d, t, dt, i)
850  *
851  * renders the quadrilaterals from perspective depth t to t+dt.
852  * i is passed as a hint, where i corresponds to t as asserted.
853  */
854 static void
855 render_quads (struct state *st, Drawable d, int t, int dt, int i)
856 {
857         int j, t2, j2, in;
858         int index;
859         XPoint points[4];
860         GC gc;
861
862         assert (i == (st->nearest - (t + dt) + TERRAIN_LENGTH) % TERRAIN_LENGTH);
863
864         in = i + 1; MODULO(in, TERRAIN_LENGTH);
865
866         for (j=0; j < TERRAIN_BREADTH; j+=dt) {
867                 t2 = t+dt; if (t2 >= TERRAIN_LENGTH) t2 -= TERRAIN_LENGTH;
868                 j2 = j+dt; if (j2 >= TERRAIN_BREADTH) j2 -= TERRAIN_BREADTH;
869                 points[0].x = st->xvals[t][j]; points[0].y = st->yvals[t][j];
870                 points[1].x = st->xvals[t2][j]; points[1].y = st->yvals[t2][j];
871                 points[2].x = st->xvals[t2][j2]; points[2].y = st->yvals[t2][j2];
872                 points[3].x = st->xvals[t][j2]; points[3].y = st->yvals[t][j2];
873
874             index = st->bonus_bright + st->ncolors/3 +
875                                 t*(t*INTERP + st->pindex) * st->ncolors /
876                             (3*TERRAIN_LENGTH*TERRAIN_PDIST);
877                 if (!wireframe) {
878                         index += (int)((points[0].y - points[3].y) / 8);
879                         index += (int)((st->worldx[i][j] - st->worldx[in][j]) / 40);
880                         index += (int)((st->terrain[in][j] - st->terrain[i][j]) / 100);
881                 }
882                 if (st->be_wormy && st->psychedelic_flag) index += st->ncolors/4;
883
884                 index = MIN (index, st->ncolors-1);
885                 index = MAX (index, 0);
886
887                 if (st->bonuses[i]) {
888                         XSetClipMask (st->dpy, st->bonus_gcs[index], None);
889                 }
890
891                 if (wireframe) {
892                         if (st->bonuses[i]) gc = st->bonus_gcs[index];
893                         else gc = st->ground_gcs[index];
894                         XDrawLines (st->dpy, d, gc, points, 4, CoordModeOrigin);
895                 } else {
896                         if (st->bonuses[i])
897                                 gc = st->bonus_gcs[index];
898                         else if ((st->direction>0 && j < TERRAIN_BREADTH/8) ||
899                                 (j > TERRAIN_BREADTH/8 && j < 3*TERRAIN_BREADTH/8-1) ||
900                                 (st->direction < 0 && j > 3*TERRAIN_BREADTH/8-1 &&
901                                         j < TERRAIN_BREADTH/2) ||
902                                 st->terrain[i][j] == STEEL_ELEVATION ||
903                                 st->wideness[in] - st->wideness[i] > 200) 
904                                 gc = st->ground_gcs[index];
905                         else
906                                 gc = st->wall_gcs[index];
907
908                         XFillPolygon (st->dpy, d, gc, points, 4, Nonconvex, CoordModeOrigin);
909                 }
910         }
911 }
912
913 /*
914  * render_pentagons (dpy, d, t, dt, i)
915  *
916  * renders the pentagons from perspective depth t to t+dt.
917  * i is passed as a hint, where i corresponds to t as asserted.
918  */
919 static void
920 render_pentagons (struct state *st, Drawable d, int t, int dt, int i)
921 {
922         int j, t2, j2, j3, in;
923         int index;
924         XPoint points[5];
925         GC gc;
926
927         assert (i == (st->nearest -t + TERRAIN_LENGTH) % TERRAIN_LENGTH);
928
929         in = i + 1; MODULO(in, TERRAIN_LENGTH);
930
931         for (j=0; j < TERRAIN_BREADTH; j+=dt*2) {
932                 t2 = t+(dt*2); if (t2 >= TERRAIN_LENGTH) t2 -= TERRAIN_LENGTH;
933                 j2 = j+dt; if (j2 >= TERRAIN_BREADTH) j2 -= TERRAIN_BREADTH;
934                 j3 = j+dt+dt; if (j3 >= TERRAIN_BREADTH) j3 -= TERRAIN_BREADTH;
935                 points[0].x = st->xvals[t][j]; points[0].y = st->yvals[t][j];
936                 points[1].x = st->xvals[t2][j]; points[1].y = st->yvals[t2][j];
937                 points[2].x = st->xvals[t2][j2]; points[2].y = st->yvals[t2][j2];
938                 points[3].x = st->xvals[t2][j3]; points[3].y = st->yvals[t2][j3];
939                 points[4].x = st->xvals[t][j3]; points[4].y = st->yvals[t][j3];
940
941             index = st->bonus_bright + st->ncolors/3 +
942                                 t*(t*INTERP + st->pindex) * st->ncolors /
943                             (3*TERRAIN_LENGTH*TERRAIN_PDIST);
944                 if (!wireframe) {
945                         index += (int)((points[0].y - points[3].y) / 8);
946                         index += (int)((st->worldx[i][j] - st->worldx[in][j]) / 40);
947                         index += (int)((st->terrain[in][j] - st->terrain[i][j]) / 100);
948                 }
949                 if (st->be_wormy && st->psychedelic_flag) index += st->ncolors/4;
950
951                 index = MIN (index, st->ncolors-1);
952                 index = MAX (index, 0);
953
954                 if (st->bonuses[i]) {
955                         XSetClipMask (st->dpy, st->bonus_gcs[index], None);
956                 }
957
958                 if (wireframe) {
959                         if (st->bonuses[i]) gc = st->bonus_gcs[index];
960                         else gc = st->ground_gcs[index];
961                         XDrawLines (st->dpy, d, gc, points, 5, CoordModeOrigin);
962                 } else {
963                         if (st->bonuses[i])
964                                 gc = st->bonus_gcs[index];
965                         else if (j < TERRAIN_BREADTH/8 ||
966                                 (j > TERRAIN_BREADTH/8 && j < 3*TERRAIN_BREADTH/8-1) ||
967                                 st->terrain[i][j] == STEEL_ELEVATION ||
968                                 st->wideness[in] - st->wideness[i] > 200) 
969                                 gc = st->ground_gcs[index];
970                         else
971                                 gc = st->wall_gcs[index];
972
973                         XFillPolygon (st->dpy, d, gc, points, 5, Complex, CoordModeOrigin);
974                 }
975         }
976 }
977
978 /*
979  * render_block (dpy, d, gc, t)
980  *
981  * render a filled polygon at perspective depth t using the given GC
982  */
983 static void
984 render_block (struct state *st, Drawable d, GC gc, int t)
985 {
986         int i;
987
988         XPoint erase_points[TERRAIN_BREADTH/2];
989
990         for (i=0; i < TERRAIN_BREADTH/2; i++) {
991                 erase_points[i].x = st->xvals[t][i*2];
992                 erase_points[i].y = st->yvals[t][i*2];
993         }
994
995         XFillPolygon (st->dpy, d, gc, erase_points,
996                                   TERRAIN_BREADTH/2, Complex, CoordModeOrigin);
997 }
998
999 /*
1000  * regenerate_stars_mask (dpy, t)
1001  *
1002  * regenerate the clip mask 'stars_mask' for drawing the bonus stars at
1003  * random positions within the bounding box at depth t
1004  */
1005 static void
1006 regenerate_stars_mask (struct state *st, int t)
1007 {
1008         int i, w, h, a, b, l1, l2;
1009         const int lim = st->width*TERRAIN_LENGTH/(300*(TERRAIN_LENGTH-t));
1010
1011         w = st->maxx[t] - st->minx[t];
1012         h = st->maxy[t] - st->miny[t];
1013
1014         if (w<6||h<6) return;
1015
1016         XFillRectangle (st->dpy, st->stars_mask, st->stars_erase_gc,
1017                                         0, 0, st->width, st->height);
1018
1019         l1 = (t>3*TERRAIN_LENGTH/4?2:1);
1020         l2 = (t>7*TERRAIN_LENGTH/8?2:1);
1021
1022         for (i=0; i < lim; i++) {
1023                 a = RAND(w); b = RAND(h);
1024                 XDrawLine (st->dpy, st->stars_mask, st->stars_gc,
1025                                         st->minx[t]+a-l1, st->miny[t]+b, st->minx[t]+a+l1, st->miny[t]+b);
1026                 XDrawLine (st->dpy, st->stars_mask, st->stars_gc,
1027                                         st->minx[t]+a, st->miny[t]+b-l1, st->minx[t]+a, st->miny[t]+b+l1);
1028         }
1029         for (i=0; i < lim; i++) {
1030                 a = RAND(w); b = RAND(h);
1031                 XDrawLine (st->dpy, st->stars_mask, st->stars_gc,
1032                                         st->minx[t]+a-l2, st->miny[t]+b, st->minx[t]+a+l2, st->miny[t]+b);
1033                 XDrawLine (st->dpy, st->stars_mask, st->stars_gc,
1034                                         st->minx[t]+a, st->miny[t]+b-l2, st->minx[t]+a, st->miny[t]+b+l2);
1035         }
1036 }
1037
1038 /*
1039  * render_bonus_block (dpy, d, t, i)
1040  *
1041  * draw the bonus stars at depth t.
1042  * i is passed as a hint, where i corresponds to t as asserted.
1043  */
1044 static void
1045 render_bonus_block (struct state *st, Drawable d, int t, int i)
1046 {
1047         int bt;
1048
1049         assert (i == (st->nearest -t + TERRAIN_LENGTH) % TERRAIN_LENGTH);
1050
1051         if (!st->bonuses[i] || wireframe) return;
1052
1053         regenerate_stars_mask (st, t);
1054
1055         bt = t * st->nr_bonus_colors / (2*TERRAIN_LENGTH);
1056
1057         XSetClipMask (st->dpy, st->bonus_gcs[bt], st->stars_mask);
1058
1059         render_block (st, d, st->bonus_gcs[bt], t);
1060 }
1061
1062 static int
1063 begin_at (struct state *st)
1064 {
1065         int t;
1066         int max_minx=0, min_maxx=st->width, max_miny=0, min_maxy=st->height;
1067
1068         for (t=TERRAIN_LENGTH-1; t > 0; t--) {
1069                 max_minx = MAX (max_minx, st->minx[t]);
1070                 min_maxx = MIN (min_maxx, st->maxx[t]);
1071                 max_miny = MAX (max_miny, st->miny[t]);
1072                 min_maxy = MIN (min_maxy, st->maxy[t]);
1073
1074                 if (max_miny >= min_maxy || max_minx >= min_maxx) break;
1075         }
1076
1077         return t;
1078 }
1079
1080 /*
1081  * render_speedmine (dpy, d)
1082  *
1083  * render the current frame.
1084  */
1085 static void
1086 render_speedmine (struct state *st, Drawable d)
1087 {
1088         int t, i=st->nearest, dt=1;
1089         GC gc;
1090
1091         assert (st->nearest >= 0 && st->nearest < TERRAIN_LENGTH);
1092
1093         if (st->be_wormy || wireframe) {
1094                 XFillRectangle (st->dpy, d, st->erase_gc, 0, 0, st->width, st->height);
1095
1096                 dt=4;
1097                 for (t=0; t < TERRAIN_LENGTH/4; t+=dt) {
1098                         render_bonus_block (st, d, t, i);
1099                         i -= dt; MODULO(i, TERRAIN_LENGTH);
1100                         render_quads (st, d, t, dt, i);
1101                 }
1102
1103                 assert (t == TERRAIN_LENGTH/4);
1104         } else {
1105                 t = MAX(begin_at(st), TERRAIN_LENGTH/4);
1106                 /*t = TERRAIN_LENGTH/4; dt = 2; */
1107                 dt = (t >= 3*TERRAIN_LENGTH/4 ? 1 : 2);
1108                 i = (st->nearest -t + TERRAIN_LENGTH) % TERRAIN_LENGTH;
1109                 render_block (st, d, st->tunnelend_gc, t);
1110         }
1111
1112         dt=2;
1113
1114         if (t == TERRAIN_LENGTH/4)
1115                 render_pentagons (st, d, t, dt, i);
1116
1117         for (; t < 3*TERRAIN_LENGTH/4; t+=dt) {
1118                 render_bonus_block (st, d, t, i);
1119                 i -= dt; MODULO(i, TERRAIN_LENGTH);
1120                 render_quads (st, d, t, dt, i);
1121         }
1122
1123         dt=1;
1124         if (st->be_wormy) {
1125                 for (; t < TERRAIN_LENGTH-(1+(st->pindex<INTERP/2)); t+=dt) {
1126                         render_bonus_block (st, d, t, i);
1127                         i -= dt; MODULO(i, TERRAIN_LENGTH);
1128                 }
1129         } else {
1130                 if (wireframe) {assert (t == 3*TERRAIN_LENGTH/4);}
1131
1132                 if (t == 3*TERRAIN_LENGTH/4)
1133                         render_pentagons (st, d, t, dt, i);
1134
1135                 for (; t < TERRAIN_LENGTH-(1+(st->pindex<INTERP/2)); t+=dt) {
1136                         render_bonus_block (st, d, t, i);
1137                         i -= dt; MODULO(i, TERRAIN_LENGTH);
1138                         render_quads (st, d, t, dt, i);
1139                 }
1140         }
1141
1142         /* Draw crosshair */
1143         if (st->crosshair_flag) {
1144                 gc = (wireframe ? st->bonus_gcs[st->nr_bonus_colors/2] : st->erase_gc);
1145                 XFillRectangle (st->dpy, d, gc,
1146                                                 st->width/2+(st->xoffset)-8, st->height/2+(st->yoffset*2)-1, 16, 3);
1147                 XFillRectangle (st->dpy, d, gc,
1148                                                 st->width/2+(st->xoffset)-1, st->height/2+(st->yoffset*2)-8, 3, 16);
1149         }
1150
1151 }
1152
1153 /*
1154  * move (step)
1155  *
1156  * move to the position for the next frame, and modify the state variables
1157  * st->nearest, pindex, pos, speed
1158  */
1159 static void
1160 move (struct state *st)
1161 {
1162         double dpos;
1163
1164         st->pos += st->step;
1165         dpos = SIGN3(st->pos) * floor(fabs(st->pos));
1166
1167         st->pindex += SIGN3(effective_speed) + INTERP;
1168         while (st->pindex >= INTERP) {
1169                 st->nearest --;
1170                 st->pindex -= INTERP;
1171         }
1172         while (st->pindex < 0) {
1173                 st->nearest ++;
1174                 st->pindex += INTERP;
1175         }
1176
1177     st->nearest += dpos; MODULO(st->nearest, TERRAIN_LENGTH);
1178
1179         st->pos -= dpos;
1180
1181         st->accel = st->thrust + st->ycurvature[st->nearest] * st->gravity;
1182         st->speed += st->accel;
1183         if (st->speed > st->maxspeed) st->speed = st->maxspeed;
1184         if (st->speed < -st->maxspeed) st->speed = -st->maxspeed;
1185 }
1186
1187 /*
1188  * speedmine (dpy, window)
1189  *
1190  * do everything required for one frame of the demo
1191  */
1192 static unsigned long
1193 speedmine_draw (Display *dpy, Window window, void *closure)
1194 {
1195   struct state *st = (struct state *) closure;
1196         double elapsed, time_per_frame = 0.04;
1197
1198         regenerate_terrain (st);
1199
1200         perspective (st);
1201
1202         render_speedmine (st, st->dbuf);
1203     if (st->dbuf != st->window)
1204       XCopyArea (st->dpy, st->dbuf, st->window, st->draw_gc, 0, 0, st->width, st->height, 0, 0);
1205
1206         st->fps_end = get_time(st);
1207         st->nframes++;
1208         st->total_nframes++;
1209
1210         if (st->fps_end > st->fps_start + 0.5) {
1211                 elapsed = st->fps_end - st->fps_start;
1212                 st->fps_start = get_time(st);
1213
1214                 time_per_frame = elapsed / st->nframes - st->delay*1e-6;
1215                 st->fps = st->nframes / elapsed;
1216                 if (DEBUG_FLAG) {
1217                         printf ("%f s elapsed\t%3f s/frame\t%.1f FPS\n", elapsed,
1218                                         time_per_frame, st->fps);
1219                 }
1220                 st->step = effective_speed * elapsed;
1221
1222                 st->nframes = 0;
1223         }
1224
1225
1226         move (st);
1227
1228         decrement_bonuses (st, time_per_frame);
1229
1230         check_bonuses (st);
1231
1232     return st->delay;
1233 }
1234
1235 /*
1236  * speedmine_color_ramp (dpy, gcs, colors, ncolors, s1, s2, v1, v2)
1237  *
1238  * generate a color ramp of up to *ncolors between randomly chosen hues,
1239  * varying from saturation s1 to s2 and value v1 to v2, placing the colors
1240  * in 'colors' and creating corresponding GCs in 'gcs'.
1241  *
1242  * The number of colors actually allocated is returned in ncolors.
1243  */
1244 static void
1245 speedmine_color_ramp (struct state *st, GC *gcs, XColor * colors,
1246                                          int *ncolors, double s1, double s2, double v1, double v2)
1247 {
1248         XGCValues gcv;
1249         int h1, h2;
1250         unsigned long flags;
1251         int i;
1252
1253         assert (*st->ncolors >= 0);
1254         assert (s1 >= 0.0 && s1 <= 1.0 && v1 >= 0.0 && v2 <= 1.0);
1255
1256         if (st->psychedelic_flag) {
1257                 h1 = RAND(360); h2 = (h1 + 180) % 360;
1258         } else {
1259                 h1 = h2 = RAND(360);
1260         }
1261
1262         make_color_ramp (st->dpy, st->cmap, 
1263                      h1, s1, v1, h2, s2, v2,
1264                                      colors, ncolors, False, True, False);
1265
1266         flags = GCForeground;
1267         for (i=0; i < *ncolors; i++) {
1268                 gcv.foreground = colors[i].pixel;
1269                 gcs[i] = XCreateGC (st->dpy, st->dbuf, flags, &gcv);
1270         }
1271
1272 }
1273
1274 /*
1275  * change_colors ()
1276  *
1277  * perform the color changing bonus. New colors are allocated for the
1278  * walls and bonuses, and if the 'psychedelic' option is set then new
1279  * colors are also chosen for the ground.
1280  */
1281 static void
1282 change_colors (struct state *st)
1283 {
1284         double s1, s2;
1285
1286         if (st->psychedelic_flag) {
1287                 free_colors (st->dpy, st->cmap, st->bonus_colors, st->nr_bonus_colors);
1288                 free_colors (st->dpy, st->cmap, st->wall_colors, st->nr_wall_colors);
1289                 free_colors (st->dpy, st->cmap, st->ground_colors, st->nr_ground_colors);
1290                 st->ncolors = MAX_COLORS;
1291
1292                 s1 = 0.4; s2 = 0.9;
1293
1294                 speedmine_color_ramp (st, st->ground_gcs, st->ground_colors,
1295                                                           &st->ncolors, 0.0, 0.8, 0.0, 0.9);
1296                 st->nr_ground_colors = st->ncolors;
1297         } else {
1298                 free_colors (st->dpy, st->cmap, st->bonus_colors, st->nr_bonus_colors);
1299                 free_colors (st->dpy, st->cmap, st->wall_colors, st->nr_wall_colors);
1300                 st->ncolors = st->nr_ground_colors;
1301
1302                 s1 = 0.0; s2 = 0.6;
1303         }
1304
1305     speedmine_color_ramp (st, st->wall_gcs, st->wall_colors, &st->ncolors,
1306                                                   s1, s2, 0.0, 0.9);
1307     st->nr_wall_colors = st->ncolors;
1308
1309     speedmine_color_ramp (st, st->bonus_gcs, st->bonus_colors, &st->ncolors,
1310                                                   0.6, 0.9, 0.4, 1.0);
1311         st->nr_bonus_colors = st->ncolors;
1312 }
1313
1314 /*
1315  * init_psychedelic_colors (dpy, window, cmap)
1316  *
1317  * initialise a psychedelic colormap
1318  */
1319 static void
1320 init_psychedelic_colors (struct state *st)
1321 {
1322   XGCValues gcv;
1323
1324   gcv.foreground = get_pixel_resource (st->dpy, st->cmap, "tunnelend", "TunnelEnd");
1325   st->tunnelend_gc = XCreateGC (st->dpy, st->window, GCForeground, &gcv);
1326
1327   st->ncolors = MAX_COLORS;
1328
1329   speedmine_color_ramp (st, st->ground_gcs, st->ground_colors, &st->ncolors,
1330                                                 0.0, 0.8, 0.0, 0.9);
1331   st->nr_ground_colors = st->ncolors;
1332
1333   speedmine_color_ramp (st, st->wall_gcs, st->wall_colors, &st->ncolors,
1334                                                 0.0, 0.6, 0.0, 0.9);
1335   st->nr_wall_colors = st->ncolors;
1336
1337   speedmine_color_ramp (st, st->bonus_gcs, st->bonus_colors, &st->ncolors,
1338                                                 0.6, 0.9, 0.4, 1.0);
1339   st->nr_bonus_colors = st->ncolors;
1340 }
1341
1342 /*
1343  * init_colors (dpy, window, cmap)
1344  *
1345  * initialise a normal colormap
1346  */
1347 static void
1348 init_colors (struct state *st)
1349 {
1350   XGCValues gcv;
1351   XColor dark, light;
1352   int h1, h2;
1353   double s1, s2, v1, v2;
1354   unsigned long flags;
1355   int i;
1356
1357   gcv.foreground = get_pixel_resource (st->dpy, st->cmap, "tunnelend", "TunnelEnd");
1358   st->tunnelend_gc = XCreateGC (st->dpy, st->window, GCForeground, &gcv);
1359
1360   st->ncolors = MAX_COLORS;
1361
1362   dark.pixel = get_pixel_resource (st->dpy, st->cmap, "darkground", "DarkGround");
1363   XQueryColor (st->dpy, st->cmap, &dark);
1364
1365   light.pixel = get_pixel_resource (st->dpy, st->cmap, "lightground", "LightGround");
1366   XQueryColor (st->dpy, st->cmap, &light);
1367
1368   rgb_to_hsv (dark.red, dark.green, dark.blue, &h1, &s1, &v1);
1369   rgb_to_hsv (light.red, light.green, light.blue, &h2, &s2, &v2);
1370   make_color_ramp (st->dpy, st->cmap, h1, s1, v1, h2, s2, v2,
1371                                   st->ground_colors, &st->ncolors, False, True, False);
1372   st->nr_ground_colors = st->ncolors;
1373
1374   flags = GCForeground;
1375   for (i=0; i < st->ncolors; i++) {
1376         gcv.foreground = st->ground_colors[i].pixel;
1377         st->ground_gcs[i] = XCreateGC (st->dpy, st->dbuf, flags, &gcv);
1378   }
1379
1380   speedmine_color_ramp (st, st->wall_gcs, st->wall_colors, &st->ncolors,
1381                                                 0.0, 0.6, 0.0, 0.9);
1382   st->nr_wall_colors = st->ncolors;
1383
1384   speedmine_color_ramp (st, st->bonus_gcs, st->bonus_colors, &st->ncolors,
1385                                                 0.6, 0.9, 0.4, 1.0);
1386   st->nr_bonus_colors = st->ncolors;
1387 }
1388
1389 /*
1390  * print_stats ()
1391  *
1392  * print out average FPS stats for the demo
1393  */
1394 #if 0
1395 static void
1396 print_stats (struct state *st)
1397 {
1398         if (st->total_nframes >= 1)
1399                 printf ("Rendered %d frames averaging %f FPS\n", st->total_nframes,
1400                                 st->total_nframes / get_time(st));
1401 }
1402 #endif
1403
1404 /*
1405  * init_speedmine (dpy, window)
1406  *
1407  * initialise the demo
1408  */
1409 static void *
1410 speedmine_init (Display *dpy, Window window)
1411 {
1412   struct state *st = (struct state *) calloc (1, sizeof(*st));
1413   XGCValues gcv;
1414   XWindowAttributes xgwa;
1415   int i;
1416   double th;
1417   int wide;
1418
1419   st->dpy = dpy;
1420   st->window = window;
1421
1422   st->speed = 1.1;
1423   st->accel = 0.00000001;
1424   st->direction = FORWARDS;
1425   st->orientation = (17*ROTS)/22;
1426
1427   XGetWindowAttributes (st->dpy, st->window, &xgwa);
1428   st->cmap = xgwa.colormap;
1429   st->width = xgwa.width;
1430   st->height = xgwa.height;
1431
1432   st->verbose_flag = get_boolean_resource (st->dpy, "verbose", "Boolean");
1433
1434 # ifdef HAVE_COCOA      /* Don't second-guess Quartz's double-buffering */
1435   st->dbuf = st->window;
1436 #else
1437   st->dbuf = XCreatePixmap (st->dpy, st->window, st->width, st->height, xgwa.depth);
1438 #endif
1439   st->stars_mask = XCreatePixmap (st->dpy, st->window, st->width, st->height, 1);
1440
1441   gcv.foreground = st->default_fg_pixel =
1442     get_pixel_resource (st->dpy, st->cmap, "foreground", "Foreground");
1443   st->draw_gc = XCreateGC (st->dpy, st->window, GCForeground, &gcv);
1444   gcv.foreground = 1;
1445   st->stars_gc = XCreateGC (st->dpy, st->stars_mask, GCForeground, &gcv);
1446
1447   gcv.foreground = get_pixel_resource (st->dpy, st->cmap, "background", "Background");
1448   st->erase_gc = XCreateGC (st->dpy, st->dbuf, GCForeground, &gcv);
1449   gcv.foreground = 0;
1450   st->stars_erase_gc = XCreateGC (st->dpy, st->stars_mask, GCForeground, &gcv);
1451
1452   st->wire_flag = get_boolean_resource (st->dpy, "wire", "Boolean");
1453
1454   st->psychedelic_flag = get_boolean_resource (st->dpy, "psychedelic", "Boolean");
1455
1456   st->delay = get_integer_resource(st->dpy, "delay", "Integer");
1457
1458   st->smoothness = get_integer_resource(st->dpy, "smoothness", "Integer");
1459   if (st->smoothness < 1) st->smoothness = 1;
1460
1461   st->maxspeed = get_float_resource(st->dpy, "maxspeed", "Float");
1462   st->maxspeed *= 0.01;
1463   st->maxspeed = fabs(st->maxspeed);
1464
1465   st->thrust = get_float_resource(st->dpy, "thrust", "Float");
1466   st->thrust *= 0.2;
1467
1468   st->gravity = get_float_resource(st->dpy, "gravity", "Float");
1469   st->gravity *= 0.002/9.8;
1470
1471   st->vertigo = get_float_resource(st->dpy, "vertigo", "Float");
1472   st->vertigo *= 0.2;
1473
1474   st->curviness = get_float_resource(st->dpy, "curviness", "Float");
1475   st->curviness *= 0.25;
1476
1477   st->twistiness = get_float_resource(st->dpy, "twistiness", "Float");
1478   st->twistiness *= 0.125;
1479
1480   st->terrain_flag = get_boolean_resource (st->dpy, "terrain", "Boolean");
1481   st->widening_flag = get_boolean_resource (st->dpy, "widening", "Boolean");
1482   st->bumps_flag = get_boolean_resource (st->dpy, "bumps", "Boolean");
1483   st->bonuses_flag = get_boolean_resource (st->dpy, "bonuses", "Boolean");
1484   st->crosshair_flag = get_boolean_resource (st->dpy, "crosshair", "Boolean");
1485
1486   st->be_wormy = get_boolean_resource (st->dpy, "worm", "Boolean");
1487   if (st->be_wormy) {
1488       st->maxspeed   *= 1.43;
1489       st->thrust     *= 10;
1490       st->gravity    *= 3;
1491       st->vertigo    *= 0.5;
1492       st->smoothness *= 2;
1493       st->curviness  *= 2;
1494       st->twistiness *= 2;
1495       st->psychedelic_flag = True;
1496       st->crosshair_flag = False;
1497   }
1498
1499   if (st->psychedelic_flag) init_psychedelic_colors (st);
1500   else init_colors (st);
1501
1502   for (i=0; i<ROTS; i++) {
1503         th = M_PI * 2.0 * i / ROTS;
1504         st->costab[i] = cos(th);
1505         st->sintab[i] = sin(th);
1506   }
1507
1508   wide = random_wideness();
1509
1510   for (i=0; i < TERRAIN_LENGTH; i++) {
1511         st->wideness[i] = wide;
1512         st->bonuses[i] = 0;
1513   }
1514
1515   init_terrain (st);
1516   init_curves (st);
1517   wrap_tunnel (st, 0, TERRAIN_LENGTH-1);
1518
1519 #if 0
1520   if (DEBUG_FLAG || st->verbose_flag) atexit(print_stats);
1521 #endif
1522
1523   st->step = effective_speed;
1524
1525   init_time (st);
1526
1527   return st;
1528 }
1529
1530
1531 static void
1532 speedmine_reshape (Display *dpy, Window window, void *closure, 
1533                  unsigned int w, unsigned int h)
1534 {
1535 }
1536
1537 static Bool
1538 speedmine_event (Display *dpy, Window window, void *closure, XEvent *event)
1539 {
1540   return False;
1541 }
1542
1543 static void
1544 speedmine_free (Display *dpy, Window window, void *closure)
1545 {
1546   struct state *st = (struct state *) closure;
1547   free (st);
1548 }
1549
1550
1551 \f
1552 /*
1553  * Down the speedmine, you'll find speed
1554  * to satisfy your moving needs;
1555  * So if you're looking for a blast
1556  * then hit the speedmine, really fast.
1557  */
1558
1559 /*
1560  * Speedworm likes to choke and spit
1561  * and chase his tail, and dance a bit
1562  * he really is a funky friend;
1563  * he's made of speed from end to end.
1564  */
1565
1566
1567 static const char *speedmine_defaults [] = {
1568   ".verbose: False",
1569   "*worm: False",
1570   "*wire: False",
1571   ".background: black",
1572   ".foreground: white",
1573   "*darkground: #101010",
1574   "*lightground: #a0a0a0",
1575   "*tunnelend: #000000",
1576   "*delay:      30000",
1577   "*maxspeed: 700",
1578   "*thrust: 1.0",
1579   "*gravity: 9.8",
1580   "*vertigo: 1.0",
1581   "*terrain: True",
1582   "*smoothness: 6",
1583   "*curviness: 1.0",
1584   "*twistiness: 1.0",
1585   "*widening: True",
1586   "*bumps: True",
1587   "*bonuses: True",
1588   "*crosshair: True",
1589   "*psychedelic: False",
1590   0
1591 };
1592
1593 static XrmOptionDescRec speedmine_options [] = {
1594   { "-verbose",                 ".verbose",                             XrmoptionNoArg, "True"},
1595   { "-worm",                    ".worm",                                XrmoptionNoArg, "True"},
1596   { "-wireframe",               ".wire",                                XrmoptionNoArg, "True"},
1597   { "-no-wireframe",    ".wire",                                XrmoptionNoArg, "False"},
1598   { "-darkground",              ".darkground",                  XrmoptionSepArg, 0 },
1599   { "-lightground",             ".lightground",                 XrmoptionSepArg, 0 },
1600   { "-tunnelend",               ".tunnelend",                   XrmoptionSepArg, 0 },
1601   { "-delay",           ".delay",               XrmoptionSepArg, 0 },
1602   { "-maxspeed",                ".maxspeed",                    XrmoptionSepArg, 0 },
1603   { "-thrust",                  ".thrust",                              XrmoptionSepArg, 0 },
1604   { "-gravity",                 ".gravity",                             XrmoptionSepArg, 0 },
1605   { "-vertigo",                 ".vertigo",                             XrmoptionSepArg, 0 },
1606   { "-terrain",                 ".terrain",                             XrmoptionNoArg, "True"},
1607   { "-no-terrain",              ".terrain",                             XrmoptionNoArg, "False"},
1608   { "-smoothness",      ".smoothness",                  XrmoptionSepArg, 0 },
1609   { "-curviness",               ".curviness",                   XrmoptionSepArg, 0 },
1610   { "-twistiness",              ".twistiness",                  XrmoptionSepArg, 0 },
1611   { "-widening",                ".widening",                    XrmoptionNoArg, "True"},
1612   { "-no-widening",             ".widening",                    XrmoptionNoArg, "False"},
1613   { "-bumps",                   ".bumps",                               XrmoptionNoArg, "True"},
1614   { "-no-bumps",                ".bumps",                               XrmoptionNoArg, "False"},
1615   { "-bonuses",                 ".bonuses",                             XrmoptionNoArg, "True"},
1616   { "-no-bonuses",              ".bonuses",                             XrmoptionNoArg, "False"},
1617   { "-crosshair",               ".crosshair",                   XrmoptionNoArg, "True"},
1618   { "-no-crosshair",    ".crosshair",                   XrmoptionNoArg, "False"},
1619   { "-psychedelic",             ".psychedelic",                 XrmoptionNoArg, "True"},
1620   { "-no-psychedelic",  ".psychedelic",                 XrmoptionNoArg, "False"},
1621   { 0, 0, 0, 0 }
1622 };
1623
1624
1625 XSCREENSAVER_MODULE ("Speedmine", speedmine)
1626
1627 /* vim: ts=4
1628  */