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