Initial import of the CDE 2.1.30 sources from the Open Group.
[oweals/cde.git] / cde / programs / dtscreen / pyro.c
1 /* $XConsortium: pyro.c /main/3 1995/11/02 16:07:59 rswiston $ */
2 /*
3  */
4 /*                                                                      *
5  * (c) Copyright 1993, 1994 Hewlett-Packard Company                     *
6  * (c) Copyright 1993, 1994 International Business Machines Corp.       *
7  * (c) Copyright 1993, 1994 Sun Microsystems, Inc.                      *
8  * (c) Copyright 1993, 1994 Novell, Inc.                                *
9  */
10 /*-
11  * pyro.c - Fireworks for dtscreen, the X Window System lockscreen.
12  *
13  * Copyright (c) 1991 by Patrick J. Naughton.
14  *
15  * See dtscreen.c for copying information.
16  *
17  * Revision History:
18  * 16-Mar-91: Written. (received from David Brooks, brooks@osf.org).
19  */
20
21 /* The physics of the rockets is a little bogus, but it looks OK.  Each is
22  * given an initial velocity impetus.  They decelerate slightly (gravity
23  * overcomes the rocket's impulse) and explode as the rocket's main fuse
24  * gives out (we could add a ballistic stage, maybe).  The individual
25  * stars fan out from the rocket, and they decelerate less quickly.
26  * That's called bouyancy, but really it's again a visual preference.
27  */
28
29 #include "dtscreen.h"
30 #include <math.h>
31 #define TWOPI 6.2831853
32
33 /* Define this >1 to get small rectangles instead of points */
34 #ifndef STARSIZE
35 #define STARSIZE 2
36 #endif
37
38 #define SILENT 0
39 #define REDGLARE 1
40 #define BURSTINGINAIR 2
41
42 #define CLOUD 0
43 #define DOUBLECLOUD 1
44 /* Clearly other types and other fascinating visual effects could be added...*/
45
46 /* P_xxx parameters represent the reciprocal of the probability... */
47 #define P_IGNITE 5000           /* ...of ignition per cycle */
48 #define P_DOUBLECLOUD 10        /* ...of an ignition being double */
49 #define P_MULTI 75              /* ...of an ignition being several @ once */
50 #define P_FUSILLADE 250         /* ...of an ignition starting a fusillade */
51
52 #define ROCKETW 2               /* Dimensions of rocket */
53 #define ROCKETH 4
54 #define XVELFACTOR 0.0025       /* Max horizontal velocity / screen width */
55 #define MINYVELFACTOR 0.016     /* Min vertical velocity / screen height */
56 #define MAXYVELFACTOR 0.018
57 #define GRAVFACTOR 0.0002       /* delta v / screen height */
58 #define MINFUSE 50              /* range of fuse lengths for rocket */
59 #define MAXFUSE 100
60
61 #define FUSILFACTOR 10          /* Generate fusillade by reducing P_IGNITE */
62 #define FUSILLEN 100            /* Length of fusillade, in ignitions */
63
64 #define SVELFACTOR 0.1          /* Max star velocity / yvel */
65 #define BOUYANCY 0.2            /* Reduction in grav deceleration for stars */
66 #define MAXSTARS 75             /* Number of stars issued from a shell */
67 #define MINSTARS 50
68 #define MINSFUSE 50             /* Range of fuse lengths for stars */
69 #define MAXSFUSE 100
70
71 #define INTRAND(min,max) (random()%((max+1)-(min))+(min))
72 #define FLOATRAND(min,max) ((min)+(random()/MAXRAND)*((max)-(min)))
73
74 static void ignite();
75 static void animate();
76 static void shootup();
77 static void burst();
78
79 typedef struct {
80     int         state;
81     int         shelltype;
82     int         color1, color2;
83     int         fuse;
84     float       xvel, yvel;
85     float       x, y;
86     int         nstars;
87 #if STARSIZE > 1
88     XRectangle  Xpoints[MAXSTARS];
89     XRectangle  Xpoints2[MAXSTARS];
90 #else
91     XPoint      Xpoints[MAXSTARS];
92     XPoint      Xpoints2[MAXSTARS];
93 #endif
94     float       sx[MAXSTARS], sy[MAXSTARS];     /* Distance from notional
95                                                  * center  */
96     float       sxvel[MAXSTARS], syvel[MAXSTARS];       /* Relative to notional
97                                                          * center */
98 }           rocket;
99
100 typedef struct {
101     Screen     *scr;
102     Colormap    cmap;
103     int         p_ignite;
104     unsigned long bgpixel;
105     unsigned long fgpixel;
106     unsigned long rockpixel;
107     GC          bgGC;
108     int         nflying;
109     int         fusilcount;
110     int         width, lmargin, rmargin, height;
111     float       minvelx, maxvelx;
112     float       minvely, maxvely;
113     float       maxsvel;
114     float       rockdecel, stardecel;
115     rocket     *rockq;
116 }           pyrostruct;
117
118 static int  orig_p_ignite;
119 static int  just_started = True;/* Greet the user right away */
120
121 void
122 initpyro(pwin)
123     perwindow *pwin;
124 {
125     pyrostruct *pp;
126     rocket     *rp;
127     XWindowAttributes xwa;
128     XGCValues   xgcv;
129     int         rockn, starn, bsize;
130
131     if (pwin->data) free(pwin->data);
132     pwin->data = (pyrostruct *)malloc(sizeof(pyrostruct));
133     memset(pwin->data, '\0', sizeof(pyrostruct));
134     pp = (pyrostruct *)pwin->data;
135     XGetWindowAttributes(dsp, pwin->w, &xwa);
136
137     orig_p_ignite = P_IGNITE / batchcount;
138     if (orig_p_ignite <= 0)
139         orig_p_ignite = 1;
140     pp->p_ignite = orig_p_ignite;
141
142     pp->rockq = (rocket *) malloc(batchcount * sizeof(rocket));
143
144     pp->nflying = pp->fusilcount = 0;
145
146     bsize = (xwa.height <= 64) ? 1 : STARSIZE;
147     for (rockn = 0, rp = pp->rockq; rockn < batchcount; rockn++, rp++) {
148         rp->state = SILENT;
149 #if STARSIZE > 1
150         for (starn = 0; starn < MAXSTARS; starn++) {
151             rp->Xpoints[starn].width = rp->Xpoints[starn].height =
152                 rp->Xpoints2[starn].width = rp->Xpoints2[starn].height = bsize;
153         }
154 #endif
155     }
156
157     pp->width = xwa.width;
158     pp->lmargin = xwa.width / 16;
159     pp->rmargin = xwa.width - pp->lmargin;
160     pp->height = xwa.height;
161     pp->scr = pwin->perscreen->screen;
162     pp->cmap = DefaultColormapOfScreen(pp->scr);
163
164     pp->fgpixel = WhitePixelOfScreen(pp->scr);
165     pp->bgpixel = BlackPixelOfScreen(pp->scr);
166     if (!mono && pwin->perscreen->npixels > 3)
167         pp->rockpixel = pwin->perscreen->pixels[3];/* Just the right shade of
168                                                  * orange */
169     else
170         pp->rockpixel = pp->fgpixel;
171
172     xgcv.foreground = pp->bgpixel;
173     pp->bgGC = XCreateGC(dsp, pwin->w, GCForeground, &xgcv);
174     
175 /* Geometry-dependent physical data: */
176     pp->maxvelx = (float) (xwa.width) * XVELFACTOR;
177     pp->minvelx = -pp->maxvelx;
178     pp->minvely = -(float) (xwa.height) * MINYVELFACTOR;
179     pp->maxvely = -(float) (xwa.height) * MAXYVELFACTOR;
180     pp->maxsvel = pp->minvely * SVELFACTOR;
181     pp->rockdecel = (float) (pp->height) * GRAVFACTOR;
182     pp->stardecel = pp->rockdecel * BOUYANCY;
183
184     XFillRectangle(dsp, pwin->w, pp->bgGC, 0, 0, xwa.width, xwa.height);
185 }
186
187 /*ARGSUSED*/
188 void
189 drawpyro(pwin)
190     perwindow *pwin;
191 {
192     pyrostruct *pp;
193     rocket     *rp;
194     int         rockn;
195
196     pp = (pyrostruct *)pwin->data;
197     if (just_started || (random() % pp->p_ignite == 0)) {
198         just_started = False;
199         if (random() % P_FUSILLADE == 0) {
200             pp->p_ignite = orig_p_ignite / FUSILFACTOR;
201             pp->fusilcount = INTRAND(FUSILLEN * 9 / 10, FUSILLEN * 11 / 10);
202         }
203         ignite(pwin, pp);
204         if (pp->fusilcount > 0) {
205             if (--pp->fusilcount == 0)
206                 pp->p_ignite = orig_p_ignite;
207         }
208     }
209     for (rockn = pp->nflying, rp = pp->rockq; rockn > 0; rp++) {
210         if (rp->state != SILENT) {
211             animate(pwin, pp, rp);
212             rockn--;
213         }
214     }
215 }
216
217 static void
218 ignite(pwin, pp)
219     perwindow *pwin;
220     pyrostruct *pp;
221 {
222     rocket     *rp;
223     int         multi, shelltype, nstars, fuse, npix, pix, color1, color2;
224     float       xvel, yvel, x;
225
226     x = random() % pp->width;
227     xvel = FLOATRAND(-pp->maxvelx, pp->maxvelx);
228 /* All this to stop too many rockets going offscreen: */
229     if (x < pp->lmargin && xvel < 0.0 || x > pp->rmargin && xvel > 0.0)
230         xvel = -xvel;
231     yvel = FLOATRAND(pp->minvely, pp->maxvely);
232     fuse = INTRAND(MINFUSE, MAXFUSE);
233     nstars = INTRAND(MINSTARS, MAXSTARS);
234     if (!mono && (npix = pwin->perscreen->npixels) > 2) {
235         color1 = pwin->perscreen->pixels[pix = random() % npix];
236         color2 = pwin->perscreen->pixels[(pix + (npix / 2)) % npix];
237     } else {
238         color1 = color2 = WhitePixelOfScreen(pwin->perscreen->screen);
239     }
240
241     multi = 1;
242     if (random() % P_DOUBLECLOUD == 0)
243         shelltype = DOUBLECLOUD;
244     else {
245         shelltype = CLOUD;
246         if (random() % P_MULTI == 0)
247             multi = INTRAND(5, 15);
248     }
249
250     rp = pp->rockq;
251     while (multi--) {
252         if (pp->nflying >= batchcount)
253             return;
254         while (rp->state != SILENT)
255             rp++;
256         pp->nflying++;
257         rp->shelltype = shelltype;
258         rp->state = REDGLARE;
259         rp->color1 = color1;
260         rp->color2 = color2;
261         rp->xvel = xvel;
262         rp->yvel = FLOATRAND(yvel * 0.97, yvel * 1.03);
263         rp->fuse = INTRAND((fuse * 90) / 100, (fuse * 110) / 100);
264         rp->x = x + FLOATRAND(multi * 7.6, multi * 8.4);
265         rp->y = pp->height - 1;
266         rp->nstars = nstars;
267     }
268 }
269
270 static void
271 animate(pwin, pp, rp)
272     perwindow *pwin;
273     pyrostruct *pp;
274     rocket     *rp;
275 {
276     int         starn;
277     float       r, theta;
278
279     if (rp->state == REDGLARE) {
280         shootup(pwin, pp, rp);
281
282 /* Handle setup for explosion */
283         if (rp->state == BURSTINGINAIR) {
284             for (starn = 0; starn < rp->nstars; starn++) {
285                 rp->sx[starn] = rp->sy[starn] = 0.0;
286                 rp->Xpoints[starn].x = (int) rp->x;
287                 rp->Xpoints[starn].y = (int) rp->y;
288                 if (rp->shelltype == DOUBLECLOUD) {
289                     rp->Xpoints2[starn].x = (int) rp->x;
290                     rp->Xpoints2[starn].y = (int) rp->y;
291                 }
292 /* This isn't accurate solid geometry, but it looks OK. */
293
294                 r = FLOATRAND(0.0, pp->maxsvel);
295                 theta = FLOATRAND(0.0, TWOPI);
296                 rp->sxvel[starn] = r * cos(theta);
297                 rp->syvel[starn] = r * sin(theta);
298             }
299             rp->fuse = INTRAND(MINSFUSE, MAXSFUSE);
300         }
301     }
302     if (rp->state == BURSTINGINAIR) {
303         burst(pwin, pp, rp);
304     }
305 }
306
307 static void
308 shootup(pwin, pp, rp)
309     perwindow *pwin;
310     pyrostruct *pp;
311     rocket     *rp;
312 {
313     XFillRectangle(dsp, pwin->w, pp->bgGC, (int) (rp->x), (int) (rp->y),
314                    ROCKETW, ROCKETH + 3);
315
316     if (rp->fuse-- <= 0) {
317         rp->state = BURSTINGINAIR;
318         return;
319     }
320     rp->x += rp->xvel;
321     rp->y += rp->yvel;
322     rp->yvel += pp->rockdecel;
323     XSetForeground(dsp, pwin->gc, pp->rockpixel);
324     XFillRectangle(dsp, pwin->w, pwin->gc, (int) (rp->x), (int) (rp->y),
325                    ROCKETW, ROCKETH + random() % 4);
326 }
327
328 static void
329 burst(pwin, pp, rp)
330     perwindow *pwin;
331     pyrostruct *pp;
332     rocket     *rp;
333 {
334     register int starn;
335     register int nstars, stype;
336     register float rx, ry, sd;  /* Help compiler optimize :-) */
337     register float sx, sy;
338
339     nstars = rp->nstars;
340     stype = rp->shelltype;
341
342 #if STARSIZE > 1
343     XFillRectangles(dsp, pwin->w, pp->bgGC, rp->Xpoints, nstars);
344     if (stype == DOUBLECLOUD)
345         XFillRectangles(dsp, pwin->w, pp->bgGC, rp->Xpoints2, nstars);
346 #else
347     XDrawPoints(dsp, pwin->w, pp->bgGC, rp->Xpoints, nstars, CoordModeOrigin);
348     if (stype == DOUBLECLOUD)
349         XDrawPoints(dsp, pwin->w, pp->bgGC, rp->Xpoints2, nstars, CoordModeOrigin);
350 #endif
351
352     if (rp->fuse-- <= 0) {
353         rp->state = SILENT;
354         pp->nflying--;
355         return;
356     }
357 /* Stagger the stars' decay */
358     if (rp->fuse <= 7) {
359         if ((rp->nstars = nstars = nstars * 90 / 100) == 0)
360             return;
361     }
362     rx = rp->x;
363     ry = rp->y;
364     sd = pp->stardecel;
365     for (starn = 0; starn < nstars; starn++) {
366         sx = rp->sx[starn] += rp->sxvel[starn];
367         sy = rp->sy[starn] += rp->syvel[starn];
368         rp->syvel[starn] += sd;
369         rp->Xpoints[starn].x = (int) (rx + sx);
370         rp->Xpoints[starn].y = (int) (ry + sy);
371         if (stype == DOUBLECLOUD) {
372             rp->Xpoints2[starn].x = (int) (rx + 1.7 * sx);
373             rp->Xpoints2[starn].y = (int) (ry + 1.7 * sy);
374         }
375     }
376     rp->x = rx + rp->xvel;
377     rp->y = ry + rp->yvel;
378     rp->yvel += sd;
379
380     XSetForeground(dsp, pwin->gc, rp->color1);
381 #if STARSIZE > 1
382     XFillRectangles(dsp, pwin->w, pwin->gc, rp->Xpoints, nstars);
383     if (stype == DOUBLECLOUD) {
384         XSetForeground(dsp, pwin->gc, rp->color2);
385         XFillRectangles(dsp, pwin->w, pwin->gc, rp->Xpoints2, nstars);
386     }
387 #else
388     XDrawPoints(dsp, pwin->w, pwin->gc, rp->Xpoints, nstars, CoordModeOrigin);
389     if (stype == DOUBLECLOUD) {
390         XSetForeground(dsp, pwin->gc, rp->color2);
391         XDrawPoints(dsp, pwin->w, pwin->gc, rp->Xpoints2, nstars,
392                     CoordModeOrigin);
393     }
394 #endif
395 }