1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* pyro --- fireworks */
3 
4 #if 0
5 static const char sccsid[] = "@(#)pyro.c	5.00 2000/11/01 xlockmore";
6 #endif
7 
8 /*-
9  * Copyright (c) 1991 by Patrick J. Naughton.
10  *
11  * Permission to use, copy, modify, and distribute this software and its
12  * documentation for any purpose and without fee is hereby granted,
13  * provided that the above copyright notice appear in all copies and that
14  * both that copyright notice and this permission notice appear in
15  * supporting documentation.
16  *
17  * This file is provided AS IS with no warranties of any kind.  The author
18  * shall have no liability with respect to the infringement of copyrights,
19  * trade secrets or any patents by this file or any part thereof.  In no
20  * event will the author be liable for any lost revenue or profits or
21  * other special, indirect and consequential damages.
22  *
23  * Revision History:
24  * 01-Nov-2000: Allocation checks
25  * 15-May-1997: jwz AT jwz.org: turned into a standalone program.
26  * 05-Sep-1996: Added 3d support Henrik Theiling <theiling@coli.uni-sb.de>
27  * 16-Mar-1991: Written, received from David Brooks <brooks@osf.org>
28  */
29 
30 /*-
31  * The physics of the rockets is a little bogus, but it looks OK.  Each is
32  * given an initial velocity impetus.  They decelerate slightly (gravity
33  * overcomes the rocket's impulse) and explode as the rocket's main fuse
34  * gives out (we could add a ballistic stage, maybe).  The individual
35  * stars fan out from the rocket, and they decelerate less quickly.
36  * That's called buoyancy, but really it's again a visual preference.
37  */
38 
39 #ifdef STANDALONE
40 #define MODE_pyro
41 #define DEFAULTS "*delay: 15000 \n" \
42 	"*count: 100 \n" \
43 	"*size: -3 \n" \
44 	"*ncolors: 200 \n" \
45 	"*use3d: False \n" \
46 	"*delta3d: 1.5 \n" \
47 	"*right3d: red \n" \
48 	"*left3d: blue \n" \
49 	"*both3d: magenta \n" \
50 	"*none3d: black \n" \
51 
52 # define free_pyro 0
53 # define reshape_pyro 0
54 # define pyro_handle_event 0
55 # define UNIFORM_COLORS
56 # include "xlockmore.h"		/* in xscreensaver distribution */
57 #else /* STANDALONE */
58 # include "xlock.h"		/* in xlockmore distribution */
59 #endif /* STANDALONE */
60 
61 #ifdef MODE_pyro
62 
63 ENTRYPOINT ModeSpecOpt pyro_opts =
64 {0, (XrmOptionDescRec *) NULL, 0, (argtype *) NULL, (OptionStruct *) NULL};
65 
66 #ifdef USE_MODULES
67 ModStruct   pyro_description =
68 {"pyro", "init_pyro", "draw_pyro", "release_pyro",
69  "refresh_pyro", "init_pyro", (char *) NULL, &pyro_opts,
70  15000, 100, 1, -3, 64, 1.0, "",
71  "Shows fireworks", 0, NULL};
72 
73 #endif
74 
75 #define ORANGE (5 * MI_NPIXELS(mi) / 64)
76 
77 #define MINROCKETS 1
78 #define MINSIZE 1
79 
80 #define SILENT 0
81 #define REDGLARE 1
82 #define BURSTINGINAIR 2
83 
84 #define CLOUD 0
85 #define DOUBLECLOUD 1
86 #define COLORCLOUD 2
87 /* Clearly other types and other fascinating visual effects could be added... */
88 
89 /* P_xxx parameters represent the reciprocal of the probability... */
90 #define P_IGNITE 5000		/* ...of ignition per cycle */
91 #define P_DOUBLECLOUD 10	/* ...of an ignition being double */
92 #define P_COLORCLOUD 5		/* ...of an ignition being full color */
93 #define P_MULTI 75		/* ...of an ignition being several @ once */
94 #define P_FUSILLADE 250		/* ...of an ignition starting a fusillade */
95 
96 #define ROCKETW 2		/* Dimensions of rocket */
97 #define ROCKETH 4
98 #define XVELFACTOR 0.0025	/* Max horizontal velocity / screen width */
99 #define ZVELFACTOR 0.0025
100 #define MINZVAL      250	/* absolute minimum of z values */
101 #define MINZVALSTART 300	/* range where they start */
102 #define MAXZVALSTART 1000
103 #define SCREENZ      400
104 /* the z-component still gets too small sometimes */
105 #define GETZDIFFAUX(z) (MI_DELTA3D(mi)*20.0*(1.0-(SCREENZ)/(z)))
106 #define GETZDIFF(z)    ((z)<MINZVAL?GETZDIFFAUX(MINZVAL):GETZDIFFAUX(z))
107 #define MINYVELFACTOR 0.016	/* Min vertical velocity / screen height */
108 #define MAXYVELFACTOR 0.018
109 #define GRAVFACTOR 0.0002	/* delta v / screen height */
110 #define MINFUSE 50		/* range of fuse lengths for rocket */
111 #define MAXFUSE 100
112 
113 #define FUSILFACTOR 10		/* Generate fusillade by reducing P_IGNITE */
114 #define FUSILLEN 100		/* Length of fusillade, in ignitions */
115 
116 #define SVELFACTOR 0.1		/* Max star velocity / yvel */
117 #define BUOYANCY 0.2		/* Reduction in grav deceleration for stars */
118 #define MAXSTARS 150		/* Number of stars issued from a shell */
119 #define MINSTARS 50
120 #define MINSFUSE 50		/* Range of fuse lengths for stars */
121 #define MAXSFUSE 100
122 
123 #define INTRAND(min,max) (NRAND((max+1)-(min))+(min))
124 #define FLOATRAND(min,max) ((min)+((double) LRAND()/((double) MAXRAND))*((max)-(min)))
125 #define INCZ(val,add) if((val)+(add)>MINZVAL) val+=(add)
126 #define ADDZ(val,add) (((val)+(add)>MINZVAL)?(val)+(add):(val))
127 #define TWOPI (2.0*M_PI)
128 
129 typedef struct {
130 	unsigned long color;
131 	float       sx, sy, sz;	/* Distance from notional center  */
132 	float       sxvel, syvel, szvel;	/* Relative to notional center */
133 } star;
134 
135 typedef struct {
136 	int         state;
137 	int         shelltype;
138 	int         fuse;
139 	float       xvel, yvel, zvel;
140 	float       x, y, z;
141 	unsigned long color[2];
142 	int         nstars;
143 	XRectangle  Xpoints[2][MAXSTARS];
144 	XRectangle  Xpointsleft[2][MAXSTARS];
145 	star        stars[MAXSTARS];
146 } rocket;
147 
148 typedef struct {
149 	int         p_ignite;
150 	unsigned long rockpixel;
151 	int         nflying, nrockets;
152 	int         fusilcount;
153 	int         width, height;
154 	int         lmargin, rmargin;
155 	int         star_size;
156 	float       minvelx, maxvelx;
157 	float       minvely, maxvely;
158 	float       minvelz, maxvelz;
159 	float       maxsvel;
160 	float       rockdecel, stardecel;
161 	rocket     *rockq;
162 } pyrostruct;
163 
164 static void shootup(ModeInfo * mi, pyrostruct * pp, rocket * rp);
165 static void burst(ModeInfo * mi, pyrostruct * pp, rocket * rp);
166 
167 static pyrostruct *pyros = (pyrostruct *) NULL;
168 static int  orig_p_ignite;
169 static int  just_started = True;	/* Greet the user right away */
170 
171 static void
ignite(ModeInfo * mi,pyrostruct * pp)172 ignite(ModeInfo * mi, pyrostruct * pp)
173 {
174 	rocket     *rp;
175 	int         multi, shelltype, nstars, fuse, pix;
176 	unsigned long color[2];
177 	float       xvel, yvel, x, zvel = 0.0, z = 0.0;
178 
179 	x = NRAND(pp->width);
180 	xvel = FLOATRAND(-pp->maxvelx, pp->maxvelx);
181 /* All this to stop too many rockets going offscreen: */
182 	if ((x < pp->lmargin && xvel < 0.0) || (x > pp->rmargin && xvel > 0.0))
183 		xvel = -xvel;
184 	yvel = FLOATRAND(pp->minvely, pp->maxvely);
185 	if (MI_IS_USE3D(mi)) {
186 		z = FLOATRAND(MINZVALSTART, MAXZVALSTART);
187 		zvel = FLOATRAND(pp->minvelz, pp->maxvelz);
188 	}
189 	fuse = INTRAND(MINFUSE, MAXFUSE);
190 	nstars = INTRAND(MINSTARS, MAXSTARS);
191 	if (MI_NPIXELS(mi) > 2) {
192 		pix = NRAND(MI_NPIXELS(mi));
193 		color[0] = MI_PIXEL(mi, pix);
194 		color[1] = MI_PIXEL(mi,
195 			    ((pix + (MI_NPIXELS(mi) / 2)) % MI_NPIXELS(mi)));
196 	} else {
197 		color[0] = color[1] = MI_WHITE_PIXEL(mi);
198 	}
199 
200 	multi = 1;
201 	if (NRAND(P_DOUBLECLOUD) == 0)
202 		shelltype = DOUBLECLOUD;
203 	else {
204 		shelltype = CLOUD;
205 		if (NRAND(P_MULTI) == 0)
206 			multi = INTRAND(5, 15);
207 	}
208 	if (NRAND(P_COLORCLOUD) == 0)
209 		shelltype |= COLORCLOUD;
210 
211 	rp = pp->rockq;
212 	while (multi--) {
213 		if (pp->nflying >= pp->nrockets)
214 			return;
215 		while (rp->state != SILENT)
216 			rp++;
217 		pp->nflying++;
218 		rp->shelltype = shelltype;
219 		rp->state = REDGLARE;
220 		rp->color[0] = color[0];
221 		rp->color[1] = color[1];
222 		rp->xvel = xvel;
223 		rp->yvel = FLOATRAND(yvel * 0.97, yvel * 1.03);
224 		rp->fuse = INTRAND((fuse * 90) / 100, (fuse * 110) / 100);
225 		rp->x = x + FLOATRAND(multi * 7.6, multi * 8.4);
226 		rp->y = pp->height - 1;
227 		if (MI_IS_USE3D(mi)) {
228 			rp->zvel = FLOATRAND(zvel * 0.97, zvel * 1.03);
229 			rp->z = z;
230 		}
231 		rp->nstars = nstars;
232 	}
233 }
234 
235 static void
animate(ModeInfo * mi,pyrostruct * pp,rocket * rp)236 animate(ModeInfo * mi, pyrostruct * pp, rocket * rp)
237 {
238 	int         starn, diff;
239 	float       r, theta;
240 
241 	if (rp->state == REDGLARE) {
242 		shootup(mi, pp, rp);
243 
244 /* Handle setup for explosion */
245 		if (rp->state == BURSTINGINAIR) {
246 			for (starn = 0; starn < rp->nstars; starn++) {
247 				rp->stars[starn].sx = rp->stars[starn].sy = 0.0;
248 				if (MI_IS_USE3D(mi)) {
249 					rp->stars[starn].sz = 0.0;
250 					diff = (int) GETZDIFF(rp->z);
251 					rp->Xpoints[0][starn].x = (int) rp->x + diff;
252 					rp->Xpointsleft[0][starn].x = (int) rp->x - diff;
253 					rp->Xpoints[0][starn].y =
254 						rp->Xpointsleft[0][starn].y = (int) rp->y;
255 					if (rp->shelltype & DOUBLECLOUD) {
256 						rp->Xpoints[1][starn].x = (int) rp->x + diff;
257 						rp->Xpointsleft[1][starn].x = (int) rp->x - diff;
258 						rp->Xpoints[1][starn].y =
259 							rp->Xpointsleft[1][starn].y = (int) rp->y;
260 					}
261 					/* this isn't really a 3d direction. it's */
262 					/* very much the same as in worm. */
263 					r = FLOATRAND(0.0, pp->maxsvel);
264 					theta = FLOATRAND(0.0, TWOPI);
265 					rp->stars[starn].sxvel = r * COSF(theta);
266 					rp->stars[starn].syvel = r * SINF(theta);
267 					theta = FLOATRAND(0.0, TWOPI);
268 					rp->stars[starn].szvel = FLOATRAND(0.0, pp->maxsvel) * SINF(theta);
269 				} else {
270 					rp->Xpoints[0][starn].x = (int) rp->x;
271 					rp->Xpoints[0][starn].y = (int) rp->y;
272 					if ((rp->shelltype & COLORCLOUD) && (MI_NPIXELS(mi) > 2)) {
273 						if (NRAND(6) < 1)
274 							rp->stars[starn].color = MI_WHITE_PIXEL(mi);
275 						else
276 							rp->stars[starn].color = MI_PIXEL(mi, NRAND(MI_NPIXELS(mi)));
277 					}
278 					if (rp->shelltype & DOUBLECLOUD) {
279 						rp->Xpoints[1][starn].x = (int) rp->x;
280 						rp->Xpoints[1][starn].y = (int) rp->y;
281 					}
282 					r = FLOATRAND(0.0, pp->maxsvel);
283 					theta = FLOATRAND(0.0, TWOPI);
284 					rp->stars[starn].sxvel = r * COSF(theta);
285 					rp->stars[starn].syvel = r * SINF(theta);
286 				}
287 /* This isn't accurate solid geometry, but it looks OK. */
288 			}
289 			rp->fuse = INTRAND(MINSFUSE, MAXSFUSE);
290 		}
291 	}
292 	if (rp->state == BURSTINGINAIR) {
293 		burst(mi, pp, rp);
294 	}
295 }
296 
297 static void
shootup(ModeInfo * mi,pyrostruct * pp,rocket * rp)298 shootup(ModeInfo * mi, pyrostruct * pp, rocket * rp)
299 {
300 	Display    *display = MI_DISPLAY(mi);
301 	Window      window = MI_WINDOW(mi);
302 	GC          gc = MI_GC(mi);
303 	int         diff, h;
304 
305 	if (MI_IS_INSTALL(mi) && MI_IS_USE3D(mi))
306 		XSetForeground(display, gc, MI_NONE_COLOR(mi));
307 	else
308 		XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
309 	if (MI_IS_USE3D(mi)) {
310 		diff = (int) GETZDIFF(rp->z);
311 		XFillRectangle(display, window, gc, (int) (rp->x) + diff, (int) (rp->y),
312 			       ROCKETW, ROCKETH + 3);
313 		XFillRectangle(display, window, gc, (int) (rp->x) - diff, (int) (rp->y),
314 			       ROCKETW, ROCKETH + 3);
315 	} else
316 		XFillRectangle(display, window, gc, (int) (rp->x), (int) (rp->y),
317 			       ROCKETW, ROCKETH + 3);
318 
319 	if (rp->fuse-- <= 0) {
320 		rp->state = BURSTINGINAIR;
321 		return;
322 	}
323 	rp->x += rp->xvel;
324 	rp->y += rp->yvel;
325 	rp->yvel += pp->rockdecel;
326 	if (MI_IS_USE3D(mi)) {
327 		INCZ(rp->z, rp->zvel);
328 		diff = (int) GETZDIFF(rp->z);
329 		h = (int) (ROCKETH + NRAND(4));
330 		if (MI_IS_INSTALL(mi))
331 			XSetFunction(display, gc, GXor);
332 		XSetForeground(display, gc, MI_RIGHT_COLOR(mi));
333 		XFillRectangle(display, window, gc, (int) (rp->x) + diff, (int) (rp->y),
334 			       ROCKETW, h);
335 		XSetForeground(display, gc, MI_LEFT_COLOR(mi));
336 		XFillRectangle(display, window, gc, (int) (rp->x) - diff, (int) (rp->y),
337 			       ROCKETW, h);
338 		if (MI_IS_INSTALL(mi))
339 			XSetFunction(display, gc, GXcopy);
340 	} else {
341 		XSetForeground(display, gc, pp->rockpixel);
342 		XFillRectangle(display, window, gc, (int) (rp->x), (int) (rp->y),
343 			       ROCKETW, (int) (ROCKETH + NRAND(4)));
344 	}
345 }
346 
347 static void
burst(ModeInfo * mi,pyrostruct * pp,rocket * rp)348 burst(ModeInfo * mi, pyrostruct * pp, rocket * rp)
349 {
350 	Display    *display = MI_DISPLAY(mi);
351 	Window      window = MI_WINDOW(mi);
352 	GC          gc = MI_GC(mi);
353 	register int starn;
354 	register int nstars, stype;
355 	register float rx, ry, rz = 0.0, sd;	/* Help compiler optimize :-) */
356 	register float sx, sy, sz;
357 	int         diff;
358 
359 	nstars = rp->nstars;
360 	stype = rp->shelltype;
361 	if (MI_IS_INSTALL(mi) && MI_IS_USE3D(mi))
362 		XSetForeground(display, gc, MI_NONE_COLOR(mi));
363 	else
364 		XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
365 
366 	XFillRectangles(display, window, gc, rp->Xpoints[0], nstars);
367 	if (stype & DOUBLECLOUD)
368 		XFillRectangles(display, window, gc, rp->Xpoints[1], nstars);
369 	if (MI_IS_USE3D(mi)) {
370 		XFillRectangles(display, window, gc, rp->Xpointsleft[0], nstars);
371 		if (stype & DOUBLECLOUD)
372 			XFillRectangles(display, window, gc, rp->Xpointsleft[1], nstars);
373 	}
374 	if (rp->fuse-- <= 0) {
375 		rp->state = SILENT;
376 		pp->nflying--;
377 		return;
378 	}
379 /* Stagger the stars' decay */
380 	if (rp->fuse <= 7) {
381 		if ((rp->nstars = nstars = nstars * 90 / 100) == 0)
382 			return;
383 	}
384 	rx = rp->x;
385 	ry = rp->y;
386 	if (MI_IS_USE3D(mi)) {
387 		rz = rp->z;
388 	}
389 	sd = pp->stardecel;
390 	for (starn = 0; starn < nstars; starn++) {
391 		sx = rp->stars[starn].sx += rp->stars[starn].sxvel;
392 		sy = rp->stars[starn].sy += rp->stars[starn].syvel;
393 		rp->stars[starn].syvel += sd;
394 		if (MI_IS_USE3D(mi)) {
395 			if (rz + rp->stars[starn].sz + rp->stars[starn].szvel > MINZVAL)
396 				rp->stars[starn].sz += rp->stars[starn].szvel;
397 			sz = rp->stars[starn].sz;
398 			diff = (int) GETZDIFF(rz + sz);
399 			rp->Xpoints[0][starn].x = (int) (rx + sx + diff);
400 			rp->Xpointsleft[0][starn].x = (int) (rx + sx - diff);
401 			rp->Xpoints[0][starn].y =
402 				rp->Xpointsleft[0][starn].y = (int) (ry + sy);
403 			if (stype & DOUBLECLOUD) {
404 				rp->Xpoints[1][starn].x = (int) (rx + 1.7 * sx) + diff;
405 				rp->Xpointsleft[1][starn].x = (int) (rx + 1.7 * sx) - diff;
406 				rp->Xpoints[1][starn].y =
407 					rp->Xpointsleft[1][starn].y = (int) (ry + 1.7 * sy);
408 			}
409 		} else {
410 
411 			rp->Xpoints[0][starn].x = (int) (rx + sx);
412 			rp->Xpoints[0][starn].y = (int) (ry + sy);
413 			if (stype & DOUBLECLOUD) {
414 				rp->Xpoints[1][starn].x = (int) (rx + 1.7 * sx);
415 				rp->Xpoints[1][starn].y = (int) (ry + 1.7 * sy);
416 			}
417 		}
418 	}
419 	rp->x = rx + rp->xvel;
420 	rp->y = ry + rp->yvel;
421 	if (MI_IS_USE3D(mi)) {
422 		rp->z = ADDZ(rz, rp->zvel);
423 	}
424 	rp->yvel += sd;
425 
426 	if (MI_IS_USE3D(mi)) {
427 		if (MI_IS_INSTALL(mi))
428 			XSetFunction(display, gc, GXor);
429 		XSetForeground(display, gc, MI_RIGHT_COLOR(mi));
430 		XFillRectangles(display, window, gc, rp->Xpoints[0], nstars);
431 		if (stype & DOUBLECLOUD) {
432 			XFillRectangles(display, window, gc, rp->Xpoints[1], nstars);
433 		}
434 		XSetForeground(display, gc, MI_LEFT_COLOR(mi));
435 		XFillRectangles(display, window, gc, rp->Xpointsleft[0], nstars);
436 		if (stype & DOUBLECLOUD) {
437 			XFillRectangles(display, window, gc, rp->Xpointsleft[1], nstars);
438 		}
439 		if (MI_IS_INSTALL(mi))
440 			XSetFunction(display, gc, GXcopy);
441 	} else {
442 
443 		if ((stype & COLORCLOUD) && (MI_NPIXELS(mi) > 2)) {
444 			for (starn = 0; starn < nstars; starn++) {
445 				XSetForeground(display, gc, rp->stars[starn].color);
446 				XFillRectangle(display, window, gc,
447 					       rp->Xpoints[0][starn].x, rp->Xpoints[0][starn].y,
448 					       rp->Xpoints[0][starn].width, rp->Xpoints[0][starn].height);
449 			}
450 		} else {
451 			XSetForeground(display, gc, rp->color[0]);
452 			XFillRectangles(display, window, gc, rp->Xpoints[0], nstars);
453 		}
454 		if (stype & DOUBLECLOUD) {
455 			XSetForeground(display, gc, rp->color[1]);
456 			XFillRectangles(display, window, gc, rp->Xpoints[1], nstars);
457 		}
458 	}
459 }
460 
461 static void
free_pyro_screen(pyrostruct * pp)462 free_pyro_screen(pyrostruct * pp)
463 {
464 	if (pp == NULL) {
465 		return;
466 	}
467 	if (pp->rockq != NULL) {
468 		free(pp->rockq);
469 	}
470 	pp = NULL;
471 }
472 
473 ENTRYPOINT void
init_pyro(ModeInfo * mi)474 init_pyro(ModeInfo * mi)
475 {
476 	int         rockn, starn;
477 	int         size = MI_SIZE(mi);
478 	rocket     *rp;
479 	pyrostruct *pp;
480 
481 	MI_INIT(mi, pyros);
482 	pp = &pyros[MI_SCREEN(mi)];
483 
484 	pp->width = MI_WIDTH(mi);
485 	pp->height = MI_HEIGHT(mi);
486 	pp->lmargin = pp->width / 16;
487 	pp->rmargin = pp->width - pp->lmargin;
488 
489 	pp->nrockets = MI_COUNT(mi);
490 	if (pp->nrockets < -MINROCKETS) {
491 		if (pp->rockq) {
492 			free(pp->rockq);
493 			pp->rockq = (rocket *) NULL;
494 		}
495 		pp->nrockets = NRAND(-pp->nrockets - MINROCKETS + 1) + MINROCKETS;
496 	} else if (pp->nrockets < MINROCKETS)
497 		pp->nrockets = MINROCKETS;
498 	if (size < -MINSIZE)
499 		pp->star_size = NRAND(MIN(-size, MAX(MINSIZE,
500 		  MIN(pp->width, pp->height) / 64)) - MINSIZE + 1) + MINSIZE;
501 	else if (size < MINSIZE) {
502 		if (!size)
503 			pp->star_size = MAX(MINSIZE, MIN(pp->width, pp->height) / 64);
504 		else
505 			pp->star_size = MINSIZE;
506 	} else
507 		pp->star_size = MIN(size, MAX(MINSIZE, MIN(pp->width, pp->height) / 64));
508 	orig_p_ignite = P_IGNITE / pp->nrockets;
509 	if (orig_p_ignite <= 0)
510 		orig_p_ignite = 1;
511 	pp->p_ignite = orig_p_ignite;
512 
513 	if (!pp->rockq) {
514 		if ((pp->rockq = (rocket *) malloc(pp->nrockets *
515 				 sizeof (rocket))) == NULL) {
516 			return;
517 		}
518 	}
519 	pp->nflying = pp->fusilcount = 0;
520 
521 	for (rockn = 0, rp = pp->rockq; rockn < pp->nrockets; rockn++, rp++) {
522 		rp->state = SILENT;
523 		for (starn = 0; starn < MAXSTARS; starn++) {
524 			rp->Xpoints[0][starn].width = rp->Xpoints[0][starn].height =
525 				rp->Xpoints[1][starn].width = rp->Xpoints[1][starn].height =
526 				pp->star_size;
527 			if (MI_IS_USE3D(mi)) {
528 				rp->Xpointsleft[0][starn].width = rp->Xpointsleft[0][starn].height =
529 					rp->Xpointsleft[1][starn].width = rp->Xpointsleft[1][starn].height =
530 					pp->star_size;
531 			}
532 		}
533 	}
534 
535 	if (MI_NPIXELS(mi) > 3)
536 		pp->rockpixel = MI_PIXEL(mi, ORANGE);
537 	else
538 		pp->rockpixel = MI_WHITE_PIXEL(mi);
539 
540 /* Geometry-dependent physical data: */
541 	pp->maxvelx = (float) (pp->width) * XVELFACTOR;
542 	pp->minvelx = -pp->maxvelx;
543 	pp->minvely = -(float) (pp->height) * MINYVELFACTOR;
544 	pp->maxvely = -(float) (pp->height) * MAXYVELFACTOR;
545 	if (MI_IS_USE3D(mi)) {
546 		pp->maxvelz = (float) (MAXZVALSTART - MINZVALSTART) * ZVELFACTOR;
547 		pp->minvelz = -pp->maxvelz;
548 	}
549 	pp->maxsvel = pp->minvely * SVELFACTOR;
550 	pp->rockdecel = (float) (pp->height) * GRAVFACTOR;
551 	pp->stardecel = pp->rockdecel * BUOYANCY;
552 	if (MI_IS_INSTALL(mi) && MI_IS_USE3D(mi)) {
553 		MI_CLEARWINDOWCOLOR(mi, MI_NONE_COLOR(mi));
554 	} else {
555 		MI_CLEARWINDOW(mi);
556 	}
557 }
558 
559 /* ARGSUSED */
560 ENTRYPOINT void
draw_pyro(ModeInfo * mi)561 draw_pyro(ModeInfo * mi)
562 {
563 	rocket     *rp;
564 	int         rockn;
565 	pyrostruct *pp;
566 
567 	if (pyros == NULL)
568 		return;
569 	pp = &pyros[MI_SCREEN(mi)];
570 	if (pp->rockq == NULL)
571 		return;
572 
573 	MI_IS_DRAWN(mi) = True;
574 	if (just_started || (NRAND(pp->p_ignite) == 0)) {
575 		just_started = False;
576 		if (NRAND(P_FUSILLADE) == 0) {
577 			pp->p_ignite = orig_p_ignite / FUSILFACTOR;
578 			if (pp->p_ignite <= 0)
579 				pp->p_ignite = 1;
580 			pp->fusilcount = INTRAND(FUSILLEN * 9 / 10, FUSILLEN * 11 / 10);
581 		}
582 		ignite(mi, pp);
583 		if (pp->fusilcount > 0) {
584 			if (--pp->fusilcount == 0)
585 				pp->p_ignite = orig_p_ignite;
586 		}
587 	}
588 	for (rockn = pp->nflying, rp = pp->rockq; rockn > 0; rp++) {
589 		if (rp->state != SILENT) {
590 			animate(mi, pp, rp);
591 			rockn--;
592 		}
593 	}
594 }
595 
596 ENTRYPOINT void
release_pyro(ModeInfo * mi)597 release_pyro(ModeInfo * mi)
598 {
599 	if (pyros != NULL) {
600 		int         screen;
601 
602 		for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
603 			free_pyro_screen(&pyros[screen]);
604 		free(pyros);
605 		pyros = (pyrostruct *) NULL;
606 	}
607 }
608 
609 #ifndef STANDALONE
610 ENTRYPOINT void
refresh_pyro(ModeInfo * mi)611 refresh_pyro(ModeInfo * mi)
612 {
613 	if (MI_IS_INSTALL(mi) && MI_IS_USE3D(mi)) {
614 		MI_CLEARWINDOWCOLOR(mi, MI_NONE_COLOR(mi));
615 	} else {
616 		MI_CLEARWINDOW(mi);
617 	}
618 }
619 #endif
620 
621 XSCREENSAVER_MODULE ("Pyro", pyro)
622 
623 #endif /* MODE_pyro */
624