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 #define assert(X)
53 #define DEBUG_FLAG 0
54 
55 #include <math.h>
56 
57 #include "screenhack.h"
58 #include "erase.h"
59 
60 #define MIN(a,b) ((a)<(b)?(a):(b))
61 #define MAX(a,b) ((a)>(b)?(a):(b))
62 
63 #define RAND(r) (int)(((r)>0)?(random() % (long)(r)): -(random() % (long)(-r)))
64 
65 #define SIGN3(a) ((a)>0?1:((a)<0?-1:0))
66 
67 #define MODULO(a,b) while ((a)<0) (a)+=(b); (a) %= (b);
68 
69 /* No. of shades of each color (ground, walls, bonuses) */
70 #define MAX_COLORS 32
71 
72 
73 
74 #define FORWARDS 1
75 #define BACKWARDS -1
76 /* Apparently AIX's math.h bogusly defines `nearest' as a function,
77    in violation of the ANSI C spec. */
78 #undef nearest
79 #define nearest n3arest
80 
81 #define wireframe (st->wire_flag||st->wire_bonus>8||st->wire_bonus%2==1)
82 #define effective_speed (st->direction*(st->speed+st->speed_bonus))
83 
84 /* No. of levels of interpolation, for perspective */
85 #define INTERP 32
86 
87 /* These must be powers of 2 */
88 #define TERRAIN_LENGTH 256
89 #define TERRAIN_BREADTH 32
90 
91 /* total "perspective distance" of terrain */
92 #define TERRAIN_PDIST (INTERP*TERRAIN_LENGTH)
93 
94 #define ROTS 1024
95 #define TB_MUL (ROTS/TERRAIN_BREADTH)
96 
97 #define random_elevation() (st->terrain_flag?(random() % 200):0)
98 #define random_curvature() (st->curviness>0.0?((double)(random() % 40)-20)*st->curviness:0.0)
99 #define random_twist() (st->twistiness>0.0?((double)(random() % 40)-20)*st->twistiness:0.0)
100 #define random_wideness() (st->widening_flag?(int)(random() % 1200):0)
101 
102 #define STEEL_ELEVATION 300
103 
104 struct state {
105     Display *dpy;
106     Window window;
107 
108     Pixmap dbuf, stars_mask;
109     Colormap cmap;
110     Visual *visual;
111     Screen *screen;
112     unsigned int default_fg_pixel;
113     GC draw_gc, erase_gc, tunnelend_gc, stars_gc, stars_erase_gc;
114 
115     int ncolors, nr_ground_colors, nr_wall_colors, nr_bonus_colors;
116     XColor ground_colors[MAX_COLORS], wall_colors[MAX_COLORS];
117     XColor bonus_colors[MAX_COLORS];
118     GC ground_gcs[MAX_COLORS], wall_gcs[MAX_COLORS], bonus_gcs[MAX_COLORS];
119 
120     int be_wormy;
121 
122     int width, height;
123     int delay;
124 
125     int smoothness;
126     int verbose_flag;
127     int wire_flag;
128     int terrain_flag;
129     int widening_flag;
130     int bumps_flag;
131     int bonuses_flag;
132     int crosshair_flag;
133     int psychedelic_flag;
134 
135     double maxspeed;
136 
137     double thrust, gravity;
138 
139     double vertigo;
140     double curviness;
141     double twistiness;
142 
143     double pos;
144     double speed;
145     double accel;
146     double step;
147 
148     int direction;
149 
150     int pindex, nearest;
151     int flipped_at;
152     int xoffset, yoffset;
153 
154     int bonus_bright;
155     int wire_bonus;
156 
157     double speed_bonus;
158 
159     int spin_bonus;
160     int backwards_bonus;
161 
162     double sintab[ROTS], costab[ROTS];
163 
164     int orientation;
165 
166     int terrain[TERRAIN_LENGTH][TERRAIN_BREADTH];
167     double xcurvature[TERRAIN_LENGTH];
168     double ycurvature[TERRAIN_LENGTH];
169     double zcurvature[TERRAIN_LENGTH];
170     int wideness[TERRAIN_LENGTH];
171     int bonuses[TERRAIN_LENGTH];
172     int xvals[TERRAIN_LENGTH][TERRAIN_BREADTH];
173     int yvals[TERRAIN_LENGTH][TERRAIN_BREADTH];
174     double worldx[TERRAIN_LENGTH][TERRAIN_BREADTH];
175     double worldy[TERRAIN_LENGTH][TERRAIN_BREADTH];
176     int minx[TERRAIN_LENGTH], maxx[TERRAIN_LENGTH];
177     int miny[TERRAIN_LENGTH], maxy[TERRAIN_LENGTH];
178 
179     int total_nframes;
180     int nframes;
181     double fps;
182     double fps_start, fps_end;
183     struct timeval start_time;
184 
185     int rotation_offset;
186     int jamming;
187 };
188 
189 /* a forward declaration ... */
190 static void change_colors(struct state *st);
191 
192 
193 
194 /*
195  * get_time ()
196  *
197  * returns the total time elapsed since the beginning of the demo
198  */
get_time(struct state * st)199 static double get_time(struct state *st) {
200   struct timeval t;
201   float f;
202 #if GETTIMEOFDAY_TWO_ARGS
203   gettimeofday(&t, NULL);
204 #else
205   gettimeofday(&t);
206 #endif
207   t.tv_sec -= st->start_time.tv_sec;
208   f = ((double)t.tv_sec) + t.tv_usec*1e-6;
209   return f;
210 }
211 
212 /*
213  * init_time ()
214  *
215  * initialises the timing structures
216  */
init_time(struct state * st)217 static void init_time(struct state *st) {
218 #if GETTIMEOFDAY_TWO_ARGS
219   gettimeofday(&st->start_time, NULL);
220 #else
221   gettimeofday(&st->start_time);
222 #endif
223   st->fps_start = get_time(st);
224 }
225 
226 
227 /*
228  * perspective()
229  *
230  * perspective map the world coordinates worldx[i][j], worldy[i][j] onto
231  * screen coordinates xvals[t][j], yvals[t][j]
232  */
233 static void
perspective(struct state * st)234 perspective (struct state *st)
235 {
236 	int i, j, jj, t=0, depth, view_pos;
237    	int rotation_bias, r;
238 	double xc=0.0, yc=0.0, zc=0.0;
239 	double xcc=0.0, ycc=0.0, zcc=0.0;
240 	double xx, yy;
241 	double zfactor, zf;
242 
243 	zf = 8.0*28.0 / (double)(st->width*TERRAIN_LENGTH);
244 	if (st->be_wormy) zf *= 3.0;
245 
246 	depth = TERRAIN_PDIST - INTERP + st->pindex;
247 
248 	view_pos = (st->nearest+3*TERRAIN_LENGTH/4)%TERRAIN_LENGTH;
249 
250 	st->xoffset += - st->xcurvature[view_pos]*st->curviness/8;
251 	st->xoffset /= 2;
252 
253 	st->yoffset += - st->ycurvature[view_pos]*st->curviness/4;
254 	st->yoffset /= 2;
255 
256 	st->rotation_offset += (int)((st->zcurvature[view_pos]-st->zcurvature[st->nearest])*ROTS/8);
257 	st->rotation_offset /= 2;
258 	rotation_bias = st->orientation + st->spin_bonus - st->rotation_offset;
259 
260 	if (st->bumps_flag) {
261 		if (st->be_wormy) {
262 			st->yoffset -= ((st->terrain[view_pos][TERRAIN_BREADTH/4] * st->width /(8*1600)));
263 			rotation_bias += (st->terrain[view_pos][TERRAIN_BREADTH/4+2] -
264 							 st->terrain[view_pos][TERRAIN_BREADTH/4-2])/8;
265 		} else {
266 			st->yoffset -= ((st->terrain[view_pos][TERRAIN_BREADTH/4] * st->width /(2*1600)));
267 			rotation_bias += (st->terrain[view_pos][TERRAIN_BREADTH/4+2] -
268 							 st->terrain[view_pos][TERRAIN_BREADTH/4-2])/16;
269 		}
270 	}
271 
272 	MODULO(rotation_bias, ROTS);
273 
274 	for (t=0; t < TERRAIN_LENGTH; t++) {
275 		i = st->nearest + t; MODULO(i, TERRAIN_LENGTH);
276 		xc += st->xcurvature[i]; yc += st->ycurvature[i]; zc += st->zcurvature[i];
277 		xcc += xc; ycc += yc; zcc += zc;
278 		st->maxx[i] = st->maxy[i] = 0;
279 		st->minx[i] = st->width; st->miny[i] = st->height;
280 	}
281 
282 	for (t=0; t < TERRAIN_LENGTH; t++) {
283 		i = st->nearest - 1 - t; MODULO(i, TERRAIN_LENGTH);
284 
285 		zfactor = (double)depth* (12.0 - TERRAIN_LENGTH/8.0) * zf;
286 		for (j=0; j < TERRAIN_BREADTH; j++) {
287 			jj = st->direction * j; MODULO(jj, TERRAIN_BREADTH);
288             /* jwz: not totally sure if this is right, but it avoids div0 */
289             if (zfactor != 0) {
290                 xx = (st->worldx[i][jj]-(st->vertigo*xcc))/zfactor;
291                 yy = (st->worldy[i][j]-(st->vertigo*ycc))/zfactor;
292             } else {
293                 xx = 0;
294                 yy = 0;
295             }
296 			r = rotation_bias + (int)(st->vertigo*zcc); MODULO(r, ROTS);
297 
298 			st->xvals[t][j] = st->xoffset + (st->width>>1) +
299 				       	  (int)(xx * st->costab[r] - yy * st->sintab[r]);
300 			st->maxx[t] = MAX(st->maxx[t], st->xvals[t][j]);
301 			st->minx[t] = MIN(st->minx[t], st->xvals[t][j]);
302 
303 			st->yvals[t][j] = st->yoffset + st->height/2 +
304 						  (int)(xx * st->sintab[r] + yy * st->costab[r]);
305 			st->maxy[t] = MAX(st->maxy[t], st->yvals[t][j]);
306 			st->miny[t] = MIN(st->miny[t], st->yvals[t][j]);
307 		}
308 		xcc -= xc; ycc -= yc; zcc -= zc;
309 		xc -= st->xcurvature[i]; yc -= st->ycurvature[i]; zc -= st->zcurvature[i];
310 		depth -= INTERP;
311 	}
312 }
313 
314 /*
315  * wrap_tunnel (start, end)
316  *
317  * wrap the terrain terrain[i][j] around the semi-circular tunnel function
318  *
319  * 			x' = x/2 * cos(theta) - (y-k) * x * sin(theta)
320  * 			y' = x/4 * sin(theta) + y * cos(theta)
321  *
322  * between i=start and i=end inclusive, producing world coordinates
323  * worldx[i][j], worldy[i][j]
324  */
325 static void
wrap_tunnel(struct state * st,int start,int end)326 wrap_tunnel (struct state *st, int start, int end)
327 {
328 	int i, j, v;
329 	double x, y;
330 
331 	assert (start < end);
332 
333 	for (i=start; i <= end; i++) {
334 		for (j=0; j < TERRAIN_BREADTH; j++) {
335 			x = j * (1.0/TERRAIN_BREADTH);
336 			v = st->terrain[i][j];
337 			y = (double)(v==STEEL_ELEVATION?200:v) - st->wideness[i] - 1200;
338 
339 			/* lower road */
340 			if (j > TERRAIN_BREADTH/8 && j < 3*TERRAIN_BREADTH/8) y -= 300;
341 
342 		    st->worldx[i][j] = x/2 * st->costab[j*TB_MUL] -
343 				 			(y-st->height/4.0)*x*st->sintab[j*TB_MUL];
344 			st->worldy[i][j] = x/4 * st->sintab[j*TB_MUL] +
345 							y * st->costab[j*TB_MUL];
346 		}
347 	}
348 }
349 
350 /*
351  * flip_direction()
352  *
353  * perform the state transitions and terrain transformation for the
354  * "look backwards/look forwards" bonus
355  */
356 static void
flip_direction(struct state * st)357 flip_direction (struct state *st)
358 {
359 	int i, ip, in, j, t;
360 
361 	st->direction = -st->direction;
362 
363 	st->bonus_bright = 20;
364 
365 	for (i=0; i < TERRAIN_LENGTH; i++) {
366 		in = st->nearest + i; MODULO(in, TERRAIN_BREADTH);
367 		ip = st->nearest - i; MODULO(ip, TERRAIN_BREADTH);
368 		for (j=0; j < TERRAIN_BREADTH; j++) {
369 			t = st->terrain[ip][j];
370 			st->terrain[ip][j] = st->terrain[in][j];
371 			st->terrain[in][j] = t;
372 		}
373 	}
374 }
375 
376 /*
377  * generate_smooth (start, end)
378  *
379  * generate smooth terrain between i=start and i=end inclusive
380  */
381 static void
generate_smooth(struct state * st,int start,int end)382 generate_smooth (struct state *st, int start, int end)
383 {
384 	int i,j, ii;
385 
386 	assert (start < end);
387 
388 	for (i=start; i <= end; i++) {
389 		ii = i; MODULO(ii, TERRAIN_LENGTH);
390 		for (j=0; j < TERRAIN_BREADTH; j++) {
391 			st->terrain[i][j] = STEEL_ELEVATION;
392 		}
393 	}
394 }
395 
396 /*
397  * generate_straight (start, end)
398  *
399  * zero the curvature and wideness between i=start and i=end inclusive
400  */
401 static void
generate_straight(struct state * st,int start,int end)402 generate_straight (struct state *st, int start, int end)
403 {
404 	int i,j, ii;
405 
406 	assert (start < end);
407 
408 	for (i=start; i <= end; i++) {
409 		ii = i; MODULO(ii, TERRAIN_LENGTH);
410 		for (j=0; j < TERRAIN_BREADTH; j++) {
411 	  		st->xcurvature[ii] = 0;
412 	 		st->ycurvature[ii] = 0;
413 			st->zcurvature[ii] = 0;
414 			st->wideness[ii] = 0;
415 		}
416 	}
417 }
418 
419 /*
420  * int generate_terrain_value (v1, v2, v3, v4, w)
421  *
422  * generate terrain value near the average of v1, v2, v3, v4, with
423  * perturbation proportional to w
424  */
425 static int
generate_terrain_value(struct state * st,int v1,int v2,int v3,int v4,int w)426 generate_terrain_value (struct state *st, int v1, int v2, int v3, int v4, int w)
427 {
428 	int sum, ret;
429 	int rval;
430 
431 	if (!st->terrain_flag) return 0;
432 
433 	sum = v1 + v2 + v3 + v4;
434 
435 	rval = w*sum/st->smoothness;
436 	if (rval == 0) rval = 2;
437 
438 	ret = (sum/4 -(rval/2) + RAND(rval));
439 
440 	if (ret < -400 || ret > 400) {
441 		ret = sum/4;
442 	}
443 
444 	return ret;
445 }
446 
447 /*
448  * generate_terrain (start, end, final)
449  *
450  * generate terrain[i][j] between i=start and i=end inclusive
451  *
452  * This is performed by successive subdivision of the terrain into
453  * rectangles of decreasing size. Subdivision continues until the
454  * the minimum width or height of these rectangles is 'final'; ie.
455  * final=1 indicates to subdivide as far as possible, final=2 indicates
456  * to stop one subdivision before that (leaving a checkerboard pattern
457  * uncalculated) etc.
458  */
459 static void
generate_terrain(struct state * st,int start,int end,int final)460 generate_terrain (struct state *st, int start, int end, int final)
461 {
462 	int i,j,w,l;
463 	int ip, jp, in, jn; /* prev, next values */
464 	int diff;
465 
466 	assert (start < end);
467 	assert (start >= 0 && start < TERRAIN_LENGTH);
468 	assert (end >= 0 && end < TERRAIN_LENGTH);
469 
470 	diff = end - start + 1;
471 
472 	st->terrain[end][0] = random_elevation();
473 	st->terrain[end][TERRAIN_BREADTH/2] = random_elevation();
474 
475 	for (w= diff/2, l=TERRAIN_BREADTH/4;
476 	     w >= final || l >= final; w /= 2, l /= 2) {
477 
478 		if (w<1) w=1;
479         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
generate_curvature_value(double v1,double v2,int w)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
generate_curves(struct state * st,int start,int end)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
do_bonus(struct state * st)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
check_bonuses(struct state * st)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
decrement_bonuses(struct state * st,double time_per_frame)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
set_bonuses(struct state * st,int start,int end)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
regenerate_terrain(struct state * st)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
init_terrain(struct state * st)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
init_curves(struct state * st)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
render_quads(struct state * st,Drawable d,int t,int dt,int i)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         if (st->ncolors > MAX_COLORS) abort();
885 		index = MIN (index, st->ncolors-1);
886 		index = MAX (index, 0);
887 
888 		if (st->bonuses[i]) {
889 			XSetClipMask (st->dpy, st->bonus_gcs[index], None);
890 		}
891 
892 		if (wireframe) {
893 			if (st->bonuses[i]) gc = st->bonus_gcs[index];
894 			else gc = st->ground_gcs[index];
895 			XDrawLines (st->dpy, d, gc, points, 4, CoordModeOrigin);
896 		} else {
897 			if (st->bonuses[i])
898 				gc = st->bonus_gcs[index];
899 			else if ((st->direction>0 && j < TERRAIN_BREADTH/8) ||
900 				(j > TERRAIN_BREADTH/8 && j < 3*TERRAIN_BREADTH/8-1) ||
901 				(st->direction < 0 && j > 3*TERRAIN_BREADTH/8-1 &&
902 					j < TERRAIN_BREADTH/2) ||
903 				st->terrain[i][j] == STEEL_ELEVATION ||
904 				st->wideness[in] - st->wideness[i] > 200)
905 				gc = st->ground_gcs[index];
906 			else
907 				gc = st->wall_gcs[index];
908 
909 			XFillPolygon (st->dpy, d, gc, points, 4, Nonconvex, CoordModeOrigin);
910 		}
911 	}
912 }
913 
914 /*
915  * render_pentagons (dpy, d, t, dt, i)
916  *
917  * renders the pentagons from perspective depth t to t+dt.
918  * i is passed as a hint, where i corresponds to t as asserted.
919  */
920 static void
render_pentagons(struct state * st,Drawable d,int t,int dt,int i)921 render_pentagons (struct state *st, Drawable d, int t, int dt, int i)
922 {
923 	int j, t2, j2, j3, in;
924 	int index;
925 	XPoint points[5];
926 	GC gc;
927 
928 	assert (i == (st->nearest -t + TERRAIN_LENGTH) % TERRAIN_LENGTH);
929 
930 	in = i + 1; MODULO(in, TERRAIN_LENGTH);
931 
932 	for (j=0; j < TERRAIN_BREADTH; j+=dt*2) {
933 		t2 = t+(dt*2); if (t2 >= TERRAIN_LENGTH) t2 -= TERRAIN_LENGTH;
934 		j2 = j+dt; if (j2 >= TERRAIN_BREADTH) j2 -= TERRAIN_BREADTH;
935 		j3 = j+dt+dt; if (j3 >= TERRAIN_BREADTH) j3 -= TERRAIN_BREADTH;
936 		points[0].x = st->xvals[t][j]; points[0].y = st->yvals[t][j];
937 		points[1].x = st->xvals[t2][j]; points[1].y = st->yvals[t2][j];
938 		points[2].x = st->xvals[t2][j2]; points[2].y = st->yvals[t2][j2];
939 		points[3].x = st->xvals[t2][j3]; points[3].y = st->yvals[t2][j3];
940 		points[4].x = st->xvals[t][j3]; points[4].y = st->yvals[t][j3];
941 
942 	    index = st->bonus_bright + st->ncolors/3 +
943 				t*(t*INTERP + st->pindex) * st->ncolors /
944 			    (3*TERRAIN_LENGTH*TERRAIN_PDIST);
945 		if (!wireframe) {
946 			index += (int)((points[0].y - points[3].y) / 8);
947 			index += (int)((st->worldx[i][j] - st->worldx[in][j]) / 40);
948 			index += (int)((st->terrain[in][j] - st->terrain[i][j]) / 100);
949 		}
950 		if (st->be_wormy && st->psychedelic_flag) index += st->ncolors/4;
951 
952 		index = MIN (index, st->ncolors-1);
953 		index = MAX (index, 0);
954 
955 		if (st->bonuses[i]) {
956 			XSetClipMask (st->dpy, st->bonus_gcs[index], None);
957 		}
958 
959 		if (wireframe) {
960 			if (st->bonuses[i]) gc = st->bonus_gcs[index];
961 			else gc = st->ground_gcs[index];
962 			XDrawLines (st->dpy, d, gc, points, 5, CoordModeOrigin);
963 		} else {
964 			if (st->bonuses[i])
965 				gc = st->bonus_gcs[index];
966 			else if (j < TERRAIN_BREADTH/8 ||
967 				(j > TERRAIN_BREADTH/8 && j < 3*TERRAIN_BREADTH/8-1) ||
968 				st->terrain[i][j] == STEEL_ELEVATION ||
969 				st->wideness[in] - st->wideness[i] > 200)
970 				gc = st->ground_gcs[index];
971 			else
972 				gc = st->wall_gcs[index];
973 
974 			XFillPolygon (st->dpy, d, gc, points, 5, Complex, CoordModeOrigin);
975 		}
976 	}
977 }
978 
979 /*
980  * render_block (dpy, d, gc, t)
981  *
982  * render a filled polygon at perspective depth t using the given GC
983  */
984 static void
render_block(struct state * st,Drawable d,GC gc,int t)985 render_block (struct state *st, Drawable d, GC gc, int t)
986 {
987 	int i;
988 
989 	XPoint erase_points[TERRAIN_BREADTH/2];
990 
991 	for (i=0; i < TERRAIN_BREADTH/2; i++) {
992 		erase_points[i].x = st->xvals[t][i*2];
993 		erase_points[i].y = st->yvals[t][i*2];
994 	}
995 
996 	XFillPolygon (st->dpy, d, gc, erase_points,
997 				  TERRAIN_BREADTH/2, Complex, CoordModeOrigin);
998 }
999 
1000 /*
1001  * regenerate_stars_mask (dpy, t)
1002  *
1003  * regenerate the clip mask 'stars_mask' for drawing the bonus stars at
1004  * random positions within the bounding box at depth t
1005  */
1006 static void
regenerate_stars_mask(struct state * st,int t)1007 regenerate_stars_mask (struct state *st, int t)
1008 {
1009 	int i, w, h, a, b, l1, l2;
1010 	const int lim = st->width*TERRAIN_LENGTH/(300*(TERRAIN_LENGTH-t));
1011 
1012 	w = st->maxx[t] - st->minx[t];
1013 	h = st->maxy[t] - st->miny[t];
1014 
1015 	if (w<6||h<6) return;
1016 
1017 	XFillRectangle (st->dpy, st->stars_mask, st->stars_erase_gc,
1018 					0, 0, st->width, st->height);
1019 
1020 	l1 = (t>3*TERRAIN_LENGTH/4?2:1);
1021 	l2 = (t>7*TERRAIN_LENGTH/8?2:1);
1022 
1023 	for (i=0; i < lim; i++) {
1024 		a = RAND(w); b = RAND(h);
1025 		XDrawLine (st->dpy, st->stars_mask, st->stars_gc,
1026 					st->minx[t]+a-l1, st->miny[t]+b, st->minx[t]+a+l1, st->miny[t]+b);
1027 		XDrawLine (st->dpy, st->stars_mask, st->stars_gc,
1028 					st->minx[t]+a, st->miny[t]+b-l1, st->minx[t]+a, st->miny[t]+b+l1);
1029 	}
1030 	for (i=0; i < lim; i++) {
1031 		a = RAND(w); b = RAND(h);
1032 		XDrawLine (st->dpy, st->stars_mask, st->stars_gc,
1033 					st->minx[t]+a-l2, st->miny[t]+b, st->minx[t]+a+l2, st->miny[t]+b);
1034 		XDrawLine (st->dpy, st->stars_mask, st->stars_gc,
1035 					st->minx[t]+a, st->miny[t]+b-l2, st->minx[t]+a, st->miny[t]+b+l2);
1036 	}
1037 }
1038 
1039 /*
1040  * render_bonus_block (dpy, d, t, i)
1041  *
1042  * draw the bonus stars at depth t.
1043  * i is passed as a hint, where i corresponds to t as asserted.
1044  */
1045 static void
render_bonus_block(struct state * st,Drawable d,int t,int i)1046 render_bonus_block (struct state *st, Drawable d, int t, int i)
1047 {
1048 	int bt;
1049 
1050 	assert (i == (st->nearest -t + TERRAIN_LENGTH) % TERRAIN_LENGTH);
1051 
1052 	if (!st->bonuses[i] || wireframe) return;
1053 
1054 	regenerate_stars_mask (st, t);
1055 
1056 	bt = t * st->nr_bonus_colors / (2*TERRAIN_LENGTH);
1057 
1058 	XSetClipMask (st->dpy, st->bonus_gcs[bt], st->stars_mask);
1059 
1060 	render_block (st, d, st->bonus_gcs[bt], t);
1061 }
1062 
1063 static int
begin_at(struct state * st)1064 begin_at (struct state *st)
1065 {
1066 	int t;
1067 	int max_minx=0, min_maxx=st->width, max_miny=0, min_maxy=st->height;
1068 
1069 	for (t=TERRAIN_LENGTH-1; t > 0; t--) {
1070 		max_minx = MAX (max_minx, st->minx[t]);
1071 		min_maxx = MIN (min_maxx, st->maxx[t]);
1072 		max_miny = MAX (max_miny, st->miny[t]);
1073 		min_maxy = MIN (min_maxy, st->maxy[t]);
1074 
1075 		if (max_miny >= min_maxy || max_minx >= min_maxx) break;
1076 	}
1077 
1078 	return t;
1079 }
1080 
1081 /*
1082  * render_speedmine (dpy, d)
1083  *
1084  * render the current frame.
1085  */
1086 static void
render_speedmine(struct state * st,Drawable d)1087 render_speedmine (struct state *st, Drawable d)
1088 {
1089 	int t, i=st->nearest, dt=1;
1090 	GC gc;
1091 
1092 	assert (st->nearest >= 0 && st->nearest < TERRAIN_LENGTH);
1093 
1094 	if (st->be_wormy || wireframe) {
1095 		XFillRectangle (st->dpy, d, st->erase_gc, 0, 0, st->width, st->height);
1096 
1097 		dt=4;
1098 		for (t=0; t < TERRAIN_LENGTH/4; t+=dt) {
1099 			render_bonus_block (st, d, t, i);
1100 			i -= dt; MODULO(i, TERRAIN_LENGTH);
1101 			render_quads (st, d, t, dt, i);
1102 		}
1103 
1104 		assert (t == TERRAIN_LENGTH/4);
1105 	} else {
1106 		t = MAX(begin_at(st), TERRAIN_LENGTH/4);
1107 		/*t = TERRAIN_LENGTH/4; dt = 2; */
1108 		/*dt = (t >= 3*TERRAIN_LENGTH/4 ? 1 : 2);*/
1109 		i = (st->nearest -t + TERRAIN_LENGTH) % TERRAIN_LENGTH;
1110 		render_block (st, d, st->tunnelend_gc, t);
1111 	}
1112 
1113 	dt=2;
1114 
1115 	if (t == TERRAIN_LENGTH/4)
1116 		render_pentagons (st, d, t, dt, i);
1117 
1118 	for (; t < 3*TERRAIN_LENGTH/4; t+=dt) {
1119 		render_bonus_block (st, d, t, i);
1120 		i -= dt; MODULO(i, TERRAIN_LENGTH);
1121 		render_quads (st, d, t, dt, i);
1122 	}
1123 
1124 	dt=1;
1125 	if (st->be_wormy) {
1126 		for (; t < TERRAIN_LENGTH-(1+(st->pindex<INTERP/2)); t+=dt) {
1127 			render_bonus_block (st, d, t, i);
1128 			i -= dt; MODULO(i, TERRAIN_LENGTH);
1129 		}
1130 	} else {
1131 		if (wireframe) {assert (t == 3*TERRAIN_LENGTH/4);}
1132 
1133 		if (t == 3*TERRAIN_LENGTH/4)
1134 			render_pentagons (st, d, t, dt, i);
1135 
1136 		for (; t < TERRAIN_LENGTH-(1+(st->pindex<INTERP/2)); t+=dt) {
1137 			render_bonus_block (st, d, t, i);
1138 			i -= dt; MODULO(i, TERRAIN_LENGTH);
1139 			render_quads (st, d, t, dt, i);
1140 		}
1141 	}
1142 
1143 	/* Draw crosshair */
1144 	if (st->crosshair_flag) {
1145 		gc = (wireframe ? st->bonus_gcs[st->nr_bonus_colors/2] : st->erase_gc);
1146 		XFillRectangle (st->dpy, d, gc,
1147 						st->width/2+(st->xoffset)-8, st->height/2+(st->yoffset*2)-1, 16, 3);
1148 		XFillRectangle (st->dpy, d, gc,
1149 						st->width/2+(st->xoffset)-1, st->height/2+(st->yoffset*2)-8, 3, 16);
1150 	}
1151 
1152 }
1153 
1154 /*
1155  * move (step)
1156  *
1157  * move to the position for the next frame, and modify the state variables
1158  * st->nearest, pindex, pos, speed
1159  */
1160 static void
move(struct state * st)1161 move (struct state *st)
1162 {
1163 	double dpos;
1164 
1165 	st->pos += st->step;
1166 	dpos = SIGN3(st->pos) * floor(fabs(st->pos));
1167 
1168 	st->pindex += SIGN3(effective_speed) + INTERP;
1169 	while (st->pindex >= INTERP) {
1170 		st->nearest --;
1171 		st->pindex -= INTERP;
1172 	}
1173 	while (st->pindex < 0) {
1174 		st->nearest ++;
1175 		st->pindex += INTERP;
1176 	}
1177 
1178     st->nearest += dpos; MODULO(st->nearest, TERRAIN_LENGTH);
1179 
1180 	st->pos -= dpos;
1181 
1182 	st->accel = st->thrust + st->ycurvature[st->nearest] * st->gravity;
1183 	st->speed += st->accel;
1184 	if (st->speed > st->maxspeed) st->speed = st->maxspeed;
1185 	if (st->speed < -st->maxspeed) st->speed = -st->maxspeed;
1186 }
1187 
1188 /*
1189  * speedmine (dpy, window)
1190  *
1191  * do everything required for one frame of the demo
1192  */
1193 static unsigned long
speedmine_draw(Display * dpy,Window window,void * closure)1194 speedmine_draw (Display *dpy, Window window, void *closure)
1195 {
1196   struct state *st = (struct state *) closure;
1197 	double elapsed, time_per_frame = 0.04;
1198 
1199 	regenerate_terrain (st);
1200 
1201 	perspective (st);
1202 
1203 	render_speedmine (st, st->dbuf);
1204     if (st->dbuf != st->window)
1205       XCopyArea (st->dpy, st->dbuf, st->window, st->draw_gc, 0, 0, st->width, st->height, 0, 0);
1206 
1207 	st->fps_end = get_time(st);
1208 	st->nframes++;
1209 	st->total_nframes++;
1210 
1211 	if (st->fps_end > st->fps_start + 0.5) {
1212 		elapsed = st->fps_end - st->fps_start;
1213 		st->fps_start = get_time(st);
1214 
1215 		time_per_frame = elapsed / st->nframes - st->delay*1e-6;
1216 		st->fps = st->nframes / elapsed;
1217 		if (DEBUG_FLAG) {
1218 			printf ("%f s elapsed\t%3f s/frame\t%.1f FPS\n", elapsed,
1219 					time_per_frame, st->fps);
1220 		}
1221 		st->step = effective_speed * elapsed;
1222 
1223 		st->nframes = 0;
1224 	}
1225 
1226 
1227 	move (st);
1228 
1229 	decrement_bonuses (st, time_per_frame);
1230 
1231 	check_bonuses (st);
1232 
1233     return st->delay;
1234 }
1235 
1236 /*
1237  * speedmine_color_ramp (dpy, gcs, colors, ncolors, s1, s2, v1, v2)
1238  *
1239  * generate a color ramp of up to *ncolors between randomly chosen hues,
1240  * varying from saturation s1 to s2 and value v1 to v2, placing the colors
1241  * in 'colors' and creating corresponding GCs in 'gcs'.
1242  *
1243  * The number of colors actually allocated is returned in ncolors.
1244  */
1245 static void
speedmine_color_ramp(struct state * st,GC * gcs,XColor * colors,int * ncolors,double s1,double s2,double v1,double v2)1246 speedmine_color_ramp (struct state *st, GC *gcs, XColor * colors,
1247 					 int *ncolors, double s1, double s2, double v1, double v2)
1248 {
1249 	XGCValues gcv;
1250 	int h1, h2;
1251 	unsigned long flags;
1252 	int i;
1253 
1254 	assert (*st->ncolors >= 0);
1255 	assert (s1 >= 0.0 && s1 <= 1.0 && v1 >= 0.0 && v2 <= 1.0);
1256 
1257 	if (st->psychedelic_flag) {
1258 		h1 = RAND(360); h2 = (h1 + 180) % 360;
1259 	} else {
1260 		h1 = h2 = RAND(360);
1261 	}
1262 
1263 	make_color_ramp (st->screen, st->visual, st->cmap,
1264                      h1, s1, v1, h2, s2, v2,
1265 				     colors, ncolors, False, True, False);
1266 
1267 	flags = GCForeground;
1268 	for (i=0; i < *ncolors; i++) {
1269 		gcv.foreground = colors[i].pixel;
1270         if (gcs[i]) XFreeGC (st->dpy, gcs[i]);
1271 		gcs[i] = XCreateGC (st->dpy, st->dbuf, flags, &gcv);
1272 	}
1273 
1274 }
1275 
1276 /*
1277  * change_colors ()
1278  *
1279  * perform the color changing bonus. New colors are allocated for the
1280  * walls and bonuses, and if the 'psychedelic' option is set then new
1281  * colors are also chosen for the ground.
1282  */
1283 static void
change_colors(struct state * st)1284 change_colors (struct state *st)
1285 {
1286 	double s1, s2;
1287 
1288 	if (st->psychedelic_flag) {
1289 		free_colors (st->screen, st->cmap, st->bonus_colors, st->nr_bonus_colors);
1290 		free_colors (st->screen, st->cmap, st->wall_colors, st->nr_wall_colors);
1291 		free_colors (st->screen, st->cmap, st->ground_colors, st->nr_ground_colors);
1292 		s1 = 0.4; s2 = 0.9;
1293 
1294 		st->ncolors = MAX_COLORS;
1295   		speedmine_color_ramp (st, st->ground_gcs, st->ground_colors,
1296 							  &st->ncolors, 0.0, 0.8, 0.0, 0.9);
1297   		st->nr_ground_colors = st->ncolors;
1298 	} else {
1299 		free_colors (st->screen, st->cmap, st->bonus_colors, st->nr_bonus_colors);
1300 		free_colors (st->screen, st->cmap, st->wall_colors, st->nr_wall_colors);
1301 		st->ncolors = st->nr_ground_colors;
1302 
1303 		s1 = 0.0; s2 = 0.6;
1304 	}
1305 
1306     st->ncolors = MAX_COLORS;
1307     speedmine_color_ramp (st, st->wall_gcs, st->wall_colors, &st->ncolors,
1308 				 		  s1, s2, 0.0, 0.9);
1309     st->nr_wall_colors = st->ncolors;
1310 
1311     st->ncolors = MAX_COLORS;
1312     speedmine_color_ramp (st, st->bonus_gcs, st->bonus_colors, &st->ncolors,
1313 				  		  0.6, 0.9, 0.4, 1.0);
1314 	st->nr_bonus_colors = st->ncolors;
1315 }
1316 
1317 /*
1318  * init_psychedelic_colors (dpy, window, cmap)
1319  *
1320  * initialise a psychedelic colormap
1321  */
1322 static void
init_psychedelic_colors(struct state * st)1323 init_psychedelic_colors (struct state *st)
1324 {
1325   XGCValues gcv;
1326 
1327   gcv.foreground = get_pixel_resource (st->dpy, st->cmap, "tunnelend", "TunnelEnd");
1328   st->tunnelend_gc = XCreateGC (st->dpy, st->window, GCForeground, &gcv);
1329 
1330   st->ncolors = MAX_COLORS;
1331   speedmine_color_ramp (st, st->ground_gcs, st->ground_colors, &st->ncolors,
1332 				  		0.0, 0.8, 0.0, 0.9);
1333   st->nr_ground_colors = st->ncolors;
1334 
1335   st->ncolors = MAX_COLORS;
1336   speedmine_color_ramp (st, st->wall_gcs, st->wall_colors, &st->ncolors,
1337 				 		0.0, 0.6, 0.0, 0.9);
1338   st->nr_wall_colors = st->ncolors;
1339 
1340   st->ncolors = MAX_COLORS;
1341   speedmine_color_ramp (st, st->bonus_gcs, st->bonus_colors, &st->ncolors,
1342 				  		0.6, 0.9, 0.4, 1.0);
1343   st->nr_bonus_colors = st->ncolors;
1344 }
1345 
1346 /*
1347  * init_colors (dpy, window, cmap)
1348  *
1349  * initialise a normal colormap
1350  */
1351 static void
init_colors(struct state * st)1352 init_colors (struct state *st)
1353 {
1354   XGCValues gcv;
1355   XColor dark, light;
1356   int h1, h2;
1357   double s1, s2, v1, v2;
1358   unsigned long flags;
1359   int i;
1360 
1361   gcv.foreground = get_pixel_resource (st->dpy, st->cmap, "tunnelend", "TunnelEnd");
1362   st->tunnelend_gc = XCreateGC (st->dpy, st->window, GCForeground, &gcv);
1363 
1364   st->ncolors = MAX_COLORS;
1365 
1366   dark.pixel = get_pixel_resource (st->dpy, st->cmap, "darkground", "DarkGround");
1367   XQueryColor (st->dpy, st->cmap, &dark);
1368 
1369   light.pixel = get_pixel_resource (st->dpy, st->cmap, "lightground", "LightGround");
1370   XQueryColor (st->dpy, st->cmap, &light);
1371 
1372   rgb_to_hsv (dark.red, dark.green, dark.blue, &h1, &s1, &v1);
1373   rgb_to_hsv (light.red, light.green, light.blue, &h2, &s2, &v2);
1374   make_color_ramp (st->screen, st->visual, st->cmap,
1375                    h1, s1, v1, h2, s2, v2,
1376 				  st->ground_colors, &st->ncolors, False, True, False);
1377   st->nr_ground_colors = st->ncolors;
1378 
1379   flags = GCForeground;
1380   for (i=0; i < st->ncolors; i++) {
1381 	gcv.foreground = st->ground_colors[i].pixel;
1382 	st->ground_gcs[i] = XCreateGC (st->dpy, st->dbuf, flags, &gcv);
1383   }
1384 
1385   st->ncolors = MAX_COLORS;
1386   speedmine_color_ramp (st, st->wall_gcs, st->wall_colors, &st->ncolors,
1387 				 		0.0, 0.6, 0.0, 0.9);
1388   st->nr_wall_colors = st->ncolors;
1389 
1390   st->ncolors = MAX_COLORS;
1391   speedmine_color_ramp (st, st->bonus_gcs, st->bonus_colors, &st->ncolors,
1392 				  		0.6, 0.9, 0.4, 1.0);
1393   st->nr_bonus_colors = st->ncolors;
1394 }
1395 
1396 /*
1397  * print_stats ()
1398  *
1399  * print out average FPS stats for the demo
1400  */
1401 #if 0
1402 static void
1403 print_stats (struct state *st)
1404 {
1405 	if (st->total_nframes >= 1)
1406 		printf ("Rendered %d frames averaging %f FPS\n", st->total_nframes,
1407 				st->total_nframes / get_time(st));
1408 }
1409 #endif
1410 
1411 /*
1412  * init_speedmine (dpy, window)
1413  *
1414  * initialise the demo
1415  */
1416 static void *
speedmine_init(Display * dpy,Window window)1417 speedmine_init (Display *dpy, Window window)
1418 {
1419   struct state *st = (struct state *) calloc (1, sizeof(*st));
1420   XGCValues gcv;
1421   XWindowAttributes xgwa;
1422   int i;
1423   double th;
1424   int wide;
1425 
1426   st->dpy = dpy;
1427   st->window = window;
1428 
1429   st->speed = 1.1;
1430   st->accel = 0.00000001;
1431   st->direction = FORWARDS;
1432   st->orientation = (17*ROTS)/22;
1433 
1434   XGetWindowAttributes (st->dpy, st->window, &xgwa);
1435   st->cmap = xgwa.colormap;
1436   st->visual = xgwa.visual;
1437   st->screen = xgwa.screen;
1438   st->width = xgwa.width;
1439   st->height = xgwa.height;
1440 
1441   st->verbose_flag = get_boolean_resource (st->dpy, "verbose", "Boolean");
1442 
1443 # ifdef HAVE_JWXYZ	/* Don't second-guess Quartz's double-buffering */
1444   st->dbuf = st->window;
1445 #else
1446   st->dbuf = XCreatePixmap (st->dpy, st->window, st->width, st->height, xgwa.depth);
1447 #endif
1448   st->stars_mask = XCreatePixmap (st->dpy, st->window, st->width, st->height, 1);
1449 
1450   gcv.foreground = st->default_fg_pixel =
1451     get_pixel_resource (st->dpy, st->cmap, "foreground", "Foreground");
1452   st->draw_gc = XCreateGC (st->dpy, st->window, GCForeground, &gcv);
1453   gcv.foreground = 1;
1454   st->stars_gc = XCreateGC (st->dpy, st->stars_mask, GCForeground, &gcv);
1455 
1456   gcv.foreground = get_pixel_resource (st->dpy, st->cmap, "background", "Background");
1457   st->erase_gc = XCreateGC (st->dpy, st->dbuf, GCForeground, &gcv);
1458   gcv.foreground = 0;
1459   st->stars_erase_gc = XCreateGC (st->dpy, st->stars_mask, GCForeground, &gcv);
1460 
1461   st->wire_flag = get_boolean_resource (st->dpy, "wire", "Boolean");
1462 
1463   st->psychedelic_flag = get_boolean_resource (st->dpy, "psychedelic", "Boolean");
1464 
1465   st->delay = get_integer_resource(st->dpy, "delay", "Integer");
1466 
1467   st->smoothness = get_integer_resource(st->dpy, "smoothness", "Integer");
1468   if (st->smoothness < 1) st->smoothness = 1;
1469 
1470   st->maxspeed = get_float_resource(st->dpy, "maxspeed", "Float");
1471   st->maxspeed *= 0.01;
1472   st->maxspeed = fabs(st->maxspeed);
1473 
1474   st->thrust = get_float_resource(st->dpy, "thrust", "Float");
1475   st->thrust *= 0.2;
1476 
1477   st->gravity = get_float_resource(st->dpy, "gravity", "Float");
1478   st->gravity *= 0.002/9.8;
1479 
1480   st->vertigo = get_float_resource(st->dpy, "vertigo", "Float");
1481   st->vertigo *= 0.2;
1482 
1483   st->curviness = get_float_resource(st->dpy, "curviness", "Float");
1484   st->curviness *= 0.25;
1485 
1486   st->twistiness = get_float_resource(st->dpy, "twistiness", "Float");
1487   st->twistiness *= 0.125;
1488 
1489   st->terrain_flag = get_boolean_resource (st->dpy, "terrain", "Boolean");
1490   st->widening_flag = get_boolean_resource (st->dpy, "widening", "Boolean");
1491   st->bumps_flag = get_boolean_resource (st->dpy, "bumps", "Boolean");
1492   st->bonuses_flag = get_boolean_resource (st->dpy, "bonuses", "Boolean");
1493   st->crosshair_flag = get_boolean_resource (st->dpy, "crosshair", "Boolean");
1494 
1495   st->be_wormy = get_boolean_resource (st->dpy, "worm", "Boolean");
1496   if (st->be_wormy) {
1497       st->maxspeed   *= 1.43;
1498       st->thrust     *= 10;
1499       st->gravity    *= 3;
1500       st->vertigo    *= 0.5;
1501       st->smoothness *= 2;
1502       st->curviness  *= 2;
1503       st->twistiness *= 2;
1504       st->psychedelic_flag = True;
1505       st->crosshair_flag = False;
1506   }
1507 
1508   if (st->psychedelic_flag) init_psychedelic_colors (st);
1509   else init_colors (st);
1510 
1511   for (i=0; i<ROTS; i++) {
1512 	th = M_PI * 2.0 * i / ROTS;
1513 	st->costab[i] = cos(th);
1514 	st->sintab[i] = sin(th);
1515   }
1516 
1517   wide = random_wideness();
1518 
1519   for (i=0; i < TERRAIN_LENGTH; i++) {
1520 	st->wideness[i] = wide;
1521 	st->bonuses[i] = 0;
1522   }
1523 
1524   init_terrain (st);
1525   init_curves (st);
1526   wrap_tunnel (st, 0, TERRAIN_LENGTH-1);
1527 
1528 #if 0
1529   if (DEBUG_FLAG || st->verbose_flag) atexit(print_stats);
1530 #endif
1531 
1532   st->step = effective_speed;
1533 
1534   init_time (st);
1535 
1536   return st;
1537 }
1538 
1539 
1540 static void
speedmine_reshape(Display * dpy,Window window,void * closure,unsigned int w,unsigned int h)1541 speedmine_reshape (Display *dpy, Window window, void *closure,
1542                  unsigned int w, unsigned int h)
1543 {
1544   struct state *st = (struct state *) closure;
1545   st->width = w;
1546   st->height = h;
1547   if (st->dbuf != st->window) {
1548       XWindowAttributes xgwa;
1549       XGetWindowAttributes (st->dpy, st->window, &xgwa);
1550       XFreePixmap (dpy, st->dbuf);
1551       st->dbuf = XCreatePixmap (st->dpy, st->window,
1552                                 st->width, st->height, xgwa.depth);
1553   }
1554 }
1555 
1556 static Bool
speedmine_event(Display * dpy,Window window,void * closure,XEvent * event)1557 speedmine_event (Display *dpy, Window window, void *closure, XEvent *event)
1558 {
1559   return False;
1560 }
1561 
1562 static void
speedmine_free(Display * dpy,Window window,void * closure)1563 speedmine_free (Display *dpy, Window window, void *closure)
1564 {
1565   struct state *st = (struct state *) closure;
1566   int i;
1567   XFreeGC (dpy, st->draw_gc);
1568   XFreeGC (dpy, st->erase_gc);
1569   XFreeGC (dpy, st->tunnelend_gc);
1570   XFreeGC (dpy, st->stars_gc);
1571   XFreeGC (dpy, st->stars_erase_gc);
1572   for (i = 0; i < MAX_COLORS; i++) {
1573       if (st->ground_gcs[i]) XFreeGC (dpy, (st->ground_gcs[i]));
1574       if (st->wall_gcs[i]) XFreeGC (dpy, (st->wall_gcs[i]));
1575       if (st->bonus_gcs[i]) XFreeGC (dpy, (st->bonus_gcs[i]));
1576   }
1577   free (st);
1578 }
1579 
1580 
1581 
1582 /*
1583  * Down the speedmine, you'll find speed
1584  * to satisfy your moving needs;
1585  * So if you're looking for a blast
1586  * then hit the speedmine, really fast.
1587  */
1588 
1589 /*
1590  * Speedworm likes to choke and spit
1591  * and chase his tail, and dance a bit
1592  * he really is a funky friend;
1593  * he's made of speed from end to end.
1594  */
1595 
1596 
1597 static const char *speedmine_defaults [] = {
1598   ".verbose: False",
1599   "*worm: False",
1600   "*wire: False",
1601   ".background:	black",
1602   ".foreground:	white",
1603   "*darkground: #101010",
1604   "*lightground: #a0a0a0",
1605   "*tunnelend: #000000",
1606   "*delay:	30000",
1607   "*maxspeed: 700",
1608   "*thrust: 1.0",
1609   "*gravity: 9.8",
1610   "*vertigo: 1.0",
1611   "*terrain: True",
1612   "*smoothness: 6",
1613   "*curviness: 1.0",
1614   "*twistiness: 1.0",
1615   "*widening: True",
1616   "*bumps: True",
1617   "*bonuses: True",
1618   "*crosshair: True",
1619   "*psychedelic: False",
1620   0
1621 };
1622 
1623 static XrmOptionDescRec speedmine_options [] = {
1624   { "-verbose",			".verbose",				XrmoptionNoArg, "True"},
1625   { "-worm",			".worm",				XrmoptionNoArg, "True"},
1626   { "-wireframe",		".wire",				XrmoptionNoArg, "True"},
1627   { "-no-wireframe",	".wire",				XrmoptionNoArg, "False"},
1628   { "-darkground",		".darkground",			XrmoptionSepArg, 0 },
1629   { "-lightground",		".lightground",			XrmoptionSepArg, 0 },
1630   { "-tunnelend",		".tunnelend",			XrmoptionSepArg, 0 },
1631   { "-delay",           ".delay",               XrmoptionSepArg, 0 },
1632   { "-maxspeed",		".maxspeed",			XrmoptionSepArg, 0 },
1633   { "-thrust",			".thrust",				XrmoptionSepArg, 0 },
1634   { "-gravity",			".gravity",				XrmoptionSepArg, 0 },
1635   { "-vertigo",			".vertigo",				XrmoptionSepArg, 0 },
1636   { "-terrain",			".terrain",				XrmoptionNoArg, "True"},
1637   { "-no-terrain",		".terrain",				XrmoptionNoArg, "False"},
1638   { "-smoothness",      ".smoothness",			XrmoptionSepArg, 0 },
1639   { "-curviness",		".curviness",			XrmoptionSepArg, 0 },
1640   { "-twistiness",		".twistiness",			XrmoptionSepArg, 0 },
1641   { "-widening",		".widening",			XrmoptionNoArg, "True"},
1642   { "-no-widening",		".widening",			XrmoptionNoArg, "False"},
1643   { "-bumps",			".bumps",				XrmoptionNoArg, "True"},
1644   { "-no-bumps",		".bumps",				XrmoptionNoArg, "False"},
1645   { "-bonuses",			".bonuses",				XrmoptionNoArg, "True"},
1646   { "-no-bonuses",		".bonuses",				XrmoptionNoArg, "False"},
1647   { "-crosshair",		".crosshair",			XrmoptionNoArg, "True"},
1648   { "-no-crosshair",	".crosshair",			XrmoptionNoArg, "False"},
1649   { "-psychedelic",		".psychedelic",			XrmoptionNoArg, "True"},
1650   { "-no-psychedelic",	".psychedelic",			XrmoptionNoArg, "False"},
1651   { 0, 0, 0, 0 }
1652 };
1653 
1654 
1655 XSCREENSAVER_MODULE ("SpeedMine", speedmine)
1656 
1657 /* vim: ts=4
1658  */
1659