More type cleanup.
[super-star-trek.git] / src / events.c
1 #include "sst.h"
2 #include <math.h>
3
4 void unschedule(int evtype)
5 /* remove an event from the schedule */
6 {
7     game.future[evtype] = FOREVER;
8 }
9
10 int is_scheduled(int evtype)
11 /* is an event of specified type scheduled */
12 {
13     return game.future[evtype] != FOREVER;
14 }
15
16 extern double scheduled(int evtype)
17 /* when will this event happen? */
18 {
19     return game.future[evtype];
20 }
21
22 void schedule(int evtype, double offset)
23 /* schedule an event of specified type */
24 {
25     game.future[evtype] = game.state.date + offset;
26 }
27
28 void postpone(int evtype, double offset)
29 /* poistpone a scheduled event */
30 {
31     game.future[evtype] += offset;
32 }
33
34 void events(void) 
35 {
36     int ictbeam=0, ipage=0, istract=0, line, i=0, j, k, l, ixhold=0, iyhold=0;
37     double fintim = game.state.date + game.optime, datemin, xtime, repair, yank=0;
38     int radio_was_broken;
39     struct quadrant *pdest;
40
41 #ifdef DEBUG
42     if (game.idebug) prout("EVENTS");
43 #endif
44
45     radio_was_broken = (game.damage[DRADIO] != 0.0);
46
47     for (;;) {
48         /* Select earliest extraneous event, line==0 if no events */
49         line = FSPY;
50         if (game.alldone) return;
51         datemin = fintim;
52         for (l = 1; l < NEVENTS; l++)
53             if (game.future[l] < datemin) {
54                 line = l;
55                 datemin = game.future[l];
56             }
57         xtime = datemin-game.state.date;
58         game.state.date = datemin;
59         /* Decrement Federation resources and recompute remaining time */
60         game.state.remres -= (game.state.remkl+4*game.state.remcom)*xtime;
61         game.state.remtime = game.state.remres/(game.state.remkl+4*game.state.remcom);
62         if (game.state.remtime <=0) {
63             finish(FDEPLETE);
64             return;
65         }
66         /* Is life support adequate? */
67         if (game.damage[DLIFSUP] && game.condit != IHDOCKED) {
68             if (game.lsupres < xtime && game.damage[DLIFSUP] > game.lsupres) {
69                 finish(FLIFESUP);
70                 return;
71             }
72             game.lsupres -= xtime;
73             if (game.damage[DLIFSUP] <= xtime) game.lsupres = game.inlsr;
74         }
75         /* Fix devices */
76         repair = xtime;
77         if (game.condit == IHDOCKED) repair /= game.docfac;
78         /* Don't fix Deathray here */
79         for (l=0; l<NDEVICES; l++)
80             if (game.damage[l] > 0.0 && l != DDRAY)
81                 game.damage[l] -= (game.damage[l]-repair > 0.0 ? repair : game.damage[l]);
82         /* If radio repaired, update star chart and attack reports */
83         if (radio_was_broken && game.damage[DRADIO] == 0.0) {
84             prout(_("Lt. Uhura- \"Captain, the sub-space radio is working and"));
85             prout(_("   surveillance reports are coming in."));
86             skip(1);
87             if (game.iseenit==0) {
88                 attakreport(0);
89                 game.iseenit = 1;
90             }
91             rechart();
92             prout(_("   The star chart is now up to date.\""));
93             skip(1);
94         }
95         /* Cause extraneous event LINE to occur */
96         game.optime -= xtime;
97         switch (line) {
98         case FSNOVA: /* Supernova */
99             if (ipage==0) pause_game(1);
100             ipage=1;
101             snova(0,0);
102             schedule(FSNOVA, expran(0.5*game.intime));
103             if (game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova) return;
104             break;
105         case FSPY: /* Check with spy to see if S.C. should tractor beam */
106             if (game.state.nscrem == 0 ||
107                 ictbeam+istract > 0 ||
108                 game.condit==IHDOCKED || game.isatb==1 || game.iscate==1) return;
109             if (game.ientesc ||
110                 (game.energy < 2000 && game.torps < 4 && game.shield < 1250) ||
111                 (game.damage[DPHASER]>0 && (game.damage[DPHOTON]>0 || game.torps < 4)) ||
112                 (game.damage[DSHIELD] > 0 &&
113                  (game.energy < 2500 || game.damage[DPHASER] > 0) &&
114                  (game.torps < 5 || game.damage[DPHOTON] > 0))) {
115                 /* Tractor-beam her! */
116                 istract=1;
117                 yank = square(game.state.kscmdr.x-game.quadrant.x) + square(game.state.kscmdr.y-game.quadrant.y);
118                 /********* fall through to FTBEAM code ***********/
119             }
120             else return;
121         case FTBEAM: /* Tractor beam */
122             if (line==FTBEAM) {
123                 if (game.state.remcom == 0) {
124                     unschedule(FTBEAM);
125                     break;
126                 }
127                 i = Rand()*game.state.remcom+1.0;
128                 yank = square(game.state.kcmdr[i].x-game.quadrant.x) + square(game.state.kcmdr[i].y-game.quadrant.y);
129                 if (istract || game.condit == IHDOCKED || yank == 0) {
130                     /* Drats! Have to reschedule */
131                     schedule(FTBEAM, 
132                              game.optime + expran(1.5*game.intime/game.state.remcom));
133                     break;
134                 }
135             }
136             /* tractor beaming cases merge here */
137             yank = sqrt(yank);
138             if (ipage==0) pause_game(1);
139             ipage=1;
140             game.optime = (10.0/(7.5*7.5))*yank; /* 7.5 is yank rate (warp 7.5) */
141             ictbeam = 1;
142             skip(1);
143             proutn("***");
144             crmshp();
145             prout(_(" caught in long range tractor beam--"));
146             /* If Kirk & Co. screwing around on planet, handle */
147             atover(1); /* atover(1) is Grab */
148             if (game.alldone) return;
149             if (game.icraft == 1) { /* Caught in Galileo? */
150                 finish(FSTRACTOR);
151                 return;
152             }
153             /* Check to see if shuttle is aboard */
154             if (game.iscraft==0) {
155                 skip(1);
156                 if (Rand() > 0.5) {
157                     prout(_("Galileo, left on the planet surface, is captured"));
158                     prout(_("by aliens and made into a flying McDonald's."));
159                     game.damage[DSHUTTL] = -10;
160                     game.iscraft = -1;
161                 }
162                 else {
163                     prout(_("Galileo, left on the planet surface, is well hidden."));
164                 }
165             }
166             if (line==0) {
167                 game.quadrant.x = game.state.kscmdr.x;
168                 game.quadrant.y = game.state.kscmdr.y;
169             }
170             else {
171                 game.quadrant.x = game.state.kcmdr[i].x;
172                 game.quadrant.y = game.state.kcmdr[i].y;
173             }
174             iran(QUADSIZE, &game.sector.x, &game.sector.y);
175             crmshp();
176             proutn(_(" is pulled to "));
177             proutn(cramlc(quadrant, game.quadrant));
178             proutn(", ");
179             prout(cramlc(sector, game.sector));
180             if (game.resting) {
181                 prout(_("(Remainder of rest/repair period cancelled.)"));
182                 game.resting = 0;
183             }
184             if (game.shldup==0) {
185                 if (game.damage[DSHIELD]==0 && game.shield > 0) {
186                     doshield(2); /* Shldsup */
187                     game.shldchg=0;
188                 }
189                 else prout(_("(Shields not currently useable.)"));
190             }
191             newqad(0);
192             /* Adjust finish time to time of tractor beaming */
193             fintim = game.state.date+game.optime;
194             attack(0);
195             if (game.state.remcom <= 0) unschedule(FTBEAM);
196             else schedule(FTBEAM, game.optime+expran(1.5*game.intime/game.state.remcom));
197             break;
198         case FSNAP: /* Snapshot of the universe (for time warp) */
199             game.snapsht = game.state;
200             game.state.snap = 1;
201             schedule(FSNAP, expran(0.5 * game.intime));
202             break;
203         case FBATTAK: /* Commander attacks starbase */
204             if (game.state.remcom==0 || game.state.rembase==0) {
205                 /* no can do */
206                 unschedule(FBATTAK);
207                 unschedule(FCDBAS);
208                 break;
209             }
210             i = 0;
211             for_starbases(j) {
212                 for_commanders(k)
213                     if (game.state.baseq[j].x==game.state.kcmdr[k].x && game.state.baseq[j].y==game.state.kcmdr[k].y &&
214                         (game.state.baseq[j].x!=game.quadrant.x || game.state.baseq[j].y!=game.quadrant.y) &&
215                         (game.state.baseq[j].x!=game.state.kscmdr.x || game.state.baseq[j].y!=game.state.kscmdr.y)) {
216                         i = 1;
217                         break;
218                     }
219                 if (i == 1) break;
220             }
221             if (j>game.state.rembase) {
222                 /* no match found -- try later */
223                 schedule(FBATTAK, expran(0.3*game.intime));
224                 unschedule(FCDBAS);
225                 break;
226             }
227             /* commander + starbase combination found -- launch attack */
228             game.battle.x = game.state.baseq[j].x;
229             game.battle.y = game.state.baseq[j].y;
230             schedule(FCDBAS, 1.0+3.0*Rand());
231             if (game.isatb) /* extra time if SC already attacking */
232                 postpone(FCDBAS, scheduled(FSCDBAS)-game.state.date);
233             game.future[FBATTAK] = game.future[FCDBAS] +expran(0.3*game.intime);
234             game.iseenit = 0;
235             if (game.damage[DRADIO] != 0.0 &&
236                 game.condit != IHDOCKED) break; /* No warning :-( */
237             game.iseenit = 1;
238             if (ipage==0) pause_game(1);
239             ipage = 1;
240             skip(1);
241             proutn(_("Lt. Uhura-  \"Captain, the starbase in "));
242             prout(cramlc(quadrant, game.battle));
243             prout(_("   reports that it is under attack and that it can"));
244             proutn(_("   hold out only until stardate %d"),
245                    (int)scheduled(FCDBAS));
246             prout(".\"");
247             if (game.resting) {
248                 skip(1);
249                 proutn(_("Mr. Spock-  \"Captain, shall we cancel the rest period?\""));
250                 if (ja()) {
251                     game.resting = 0;
252                     game.optime = 0.0;
253                     return;
254                 }
255             }
256             break;
257         case FSCDBAS: /* Supercommander destroys base */
258             unschedule(FSCDBAS);
259             game.isatb = 2;
260             if (!game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].starbase) 
261                 break; /* WAS RETURN! */
262             ixhold = game.battle.x;
263             iyhold = game.battle.y;
264             game.battle.x = game.state.kscmdr.x;
265             game.battle.y = game.state.kscmdr.y;
266         case FCDBAS: /* Commander succeeds in destroying base */
267             if (line==FCDBAS) {
268                 unschedule(FCDBAS);
269                 /* find the lucky pair */
270                 for_commanders(i)
271                     if (game.state.kcmdr[i].x==game.battle.x && game.state.kcmdr[i].y==game.battle.y) 
272                         break;
273                 if (i > game.state.remcom || game.state.rembase == 0 ||
274                     !game.state.galaxy[game.battle.x][game.battle.y].starbase) {
275                     /* No action to take after all */
276                     game.battle.x = game.battle.y = 0;
277                     break;
278                 }
279             }
280             /* Code merges here for any commander destroying base */
281             /* Not perfect, but will have to do */
282             /* Handle case where base is in same quadrant as starship */
283             if (game.battle.x==game.quadrant.x && game.battle.y==game.quadrant.y) {
284                 game.state.chart[game.battle.x][game.battle.y].starbase = false;
285                 game.quad[game.base.x][game.base.y] = IHDOT;
286                 game.base.x=game.base.y=0;
287                 newcnd();
288                 skip(1);
289                 prout(_("Spock-  \"Captain, I believe the starbase has been destroyed.\""));
290             }
291             else if (game.state.rembase != 1 &&
292                      (game.damage[DRADIO] <= 0.0 || game.condit == IHDOCKED)) {
293                 /* Get word via subspace radio */
294                 if (ipage==0) pause_game(1);
295                 ipage = 1;
296                 skip(1);
297                 prout(_("Lt. Uhura-  \"Captain, Starfleet Command reports that"));
298                 proutn(_("   the starbase in "));
299                 proutn(cramlc(quadrant, game.battle));
300                 prout(_(" has been destroyed by"));
301                 if (game.isatb==2) prout(_("the Klingon Super-Commander"));
302                 else prout(_("a Klingon Commander"));
303                 game.state.chart[game.battle.x][game.battle.y].starbase = false;
304             }
305             /* Remove Starbase from galaxy */
306             game.state.galaxy[game.battle.x][game.battle.y].starbase = false;
307             for_starbases(i)
308                 if (game.state.baseq[i].x==game.battle.x && game.state.baseq[i].y==game.battle.y) {
309                     game.state.baseq[i].x=game.state.baseq[game.state.rembase].x;
310                     game.state.baseq[i].y=game.state.baseq[game.state.rembase].y;
311                 }
312             game.state.rembase--;
313             if (game.isatb == 2) {
314                 /* reinstate a commander's base attack */
315                 game.battle.x = ixhold;
316                 game.battle.y = iyhold;
317                 game.isatb = 0;
318             }
319             else {
320                 game.battle.x = game.battle.y = 0;
321             }
322             break;
323         case FSCMOVE: /* Supercommander moves */
324             schedule(FSCMOVE, 0.2777);
325             if (game.ientesc+istract==0 &&
326                 game.isatb!=1 &&
327                 (game.iscate!=1 || game.justin==1)) scom(&ipage);
328             break;
329         case FDSPROB: /* Move deep space probe */
330             schedule(FDSPROB, 0.01);
331             game.probex += game.probeinx;
332             game.probey += game.probeiny;
333             i = (int)(game.probex/QUADSIZE +0.05);
334             j = (int)(game.probey/QUADSIZE + 0.05);
335             if (game.probec.x != i || game.probec.y != j) {
336                 game.probec.x = i;
337                 game.probec.y = j;
338                 if (!VALID_QUADRANT(i, j) ||
339                     game.state.galaxy[game.probec.x][game.probec.y].supernova) {
340                     // Left galaxy or ran into supernova
341                     if (game.damage[DRADIO]==0.0 || game.condit == IHDOCKED) {
342                         if (ipage==0) pause_game(1);
343                         ipage = 1;
344                         skip(1);
345                         proutn(_("Lt. Uhura-  \"The deep space probe "));
346                         if (!VALID_QUADRANT(j, i))
347                             proutn(_("has left the galaxy"));
348                         else
349                             proutn(_("is no longer transmitting"));
350                         prout(".\"");
351                     }
352                     unschedule(FDSPROB);
353                     break;
354                 }
355                 if (game.damage[DRADIO]==0.0   || game.condit == IHDOCKED) {
356                     if (ipage==0) pause_game(1);
357                     ipage = 1;
358                     skip(1);
359                     proutn(_("Lt. Uhura-  \"The deep space probe is now in "));
360                     proutn(cramlc(quadrant, game.probec));
361                     prout(".\"");
362                 }
363             }
364             pdest = &game.state.galaxy[game.probec.x][game.probec.y];
365             /* Update star chart if Radio is working or have access to
366                radio. */
367             if (game.damage[DRADIO] == 0.0 || game.condit == IHDOCKED) {
368                 struct page *chp = &game.state.chart[game.probec.x][game.probec.y];
369
370                 chp->klingons = pdest->klingons;
371                 chp->starbase = pdest->starbase;
372                 chp->stars = pdest->stars;
373                 pdest->charted = true;
374             }
375             game.proben--; // One less to travel
376             if (game.proben == 0 && game.isarmed && pdest->stars) {
377                 /* lets blow the sucker! */
378                 snova(1,0);
379                 unschedule(FDSPROB);
380                 if (game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova) 
381                     return;
382             }
383             break;
384 #ifdef EXPERIMENTAL
385         case FDISTR: /* inhabited system issues distress call */
386             /* in BSD Trek this is a straight 1 stardate ahead */ 
387             schedule(FDISTR, 1.0 + Rand());
388             /* if we already have too many, throw this one away */
389             if (game.ndistr >= MAXDISTR)
390                 break;
391             /* try a whole bunch of times to find something suitable */
392             for (i = 0; i < 100; i++) {
393                 struct quadrant *q;
394                 iran(GALSIZE, &ix, &iy);
395                 q = &game.state.galaxy[game.quadrant.x][game.quadrant.y];
396                 /* need a quadrant which is not the current one,
397                    which has some stars which are inhabited and
398                    not already under attack, which is not
399                    supernova'ed, and which has some Klingons in it */
400                 if (!((ix == game.quadrant.x && iy == game.quadrant.y) || q->stars<=0 ||
401                       (q->qsystemname & Q_DISTRESSED) ||
402                       (q->qsystemname & Q_SYSTEM) == 0 || q->klings <= 0))
403                     break;
404             }
405             if (i >= 100)
406                 /* can't seem to find one; ignore this call */
407                 break;
408
409             /* got one!!  Schedule its enslavement */
410             game.ndistr++;
411             e = xsched(E_ENSLV, 1, ix, iy, q->qsystemname);
412             q->qsystemname = (e - Event) | Q_DISTRESSED;
413
414             /* tell the captain about it if we can */
415             if (game.damage[DRADIO] == 0.0)
416             {
417                 printf("\nUhura: Captain, starsystem %s in quadrant %d,%d is under attack\n",
418                        Systemname[e->systemname], ix, iy);
419                 restcancel++;
420             }
421             else
422                 /* if we can't tell him, make it invisible */
423                 e->evcode |= E_HIDDEN;
424             break;
425       case FENSLV:              /* starsystem is enslaved */
426             unschedule(e);
427             /* see if current distress call still active */
428             q = &Quad[e->x][e->y];
429             if (q->klings <= 0)
430             {
431                 /* no Klingons, clean up */
432                 /* restore the system name */
433                 q->qsystemname = e->systemname;
434                 break;
435             }
436
437             /* play stork and schedule the first baby */
438             e = schedule(E_REPRO, Param.eventdly[E_REPRO] * franf(), e->x, e->y, e->systemname);
439
440             /* report the disaster if we can */
441             if (game.damage[DRADIO] == 0.0)
442             {
443                 printf("\nUhura:  We've lost contact with starsystem %s\n",
444                        Systemname[e->systemname]);
445                 printf("  in quadrant %d,%d.\n", e->x, e->y);
446             }
447             else
448                 e->evcode |= E_HIDDEN;
449             break;
450       case FREPRO:              /* Klingon reproduces */
451             /* see if distress call is still active */
452             q = &Quad[e->x][e->y];
453             if (q->klings <= 0)
454             {
455                 unschedule(e);
456                 q->qsystemname = e->systemname;
457                 break;
458             }
459             xresched(e, E_REPRO, 1);
460             /* reproduce one Klingon */
461             ix = e->x;
462             iy = e->y;
463             if (Now.klings == 127)
464                 break;          /* full right now */
465             if (q->klings >= MAXKLQUAD)
466             {
467                 /* this quadrant not ok, pick an adjacent one */
468                 for (i = ix - 1; i <= ix + 1; i++)
469                 {
470                     if (!VALID_QUADRANT(i))
471                         continue;
472                     for (j = iy - 1; j <= iy + 1; j++)
473                     {
474                         if (!VALID_QUADRANT(j))
475                             continue;
476                         q = &Quad[i][j];
477                         /* check for this quad ok (not full & no snova) */
478                         if (q->klings >= MAXKLQUAD || q->stars < 0)
479                             continue;
480                         break;
481                     }
482                     if (j <= iy + 1)
483                         break;
484                 }
485                 if (j > iy + 1)
486                     /* cannot create another yet */
487                     break;
488                 ix = i;
489                 iy = j;
490             }
491             /* deliver the child */
492             game.remkl++;
493             if (ix == game.quadrant.x && iy == game.quadrant.y)
494                 newkling(++game.klhere, &ixhold, &iyhold);
495
496             /* recompute time left */
497             game.state.remtime = game.state.remres/(game.state.remkl+4*game.state.remcom);
498             break;
499 #endif /* EXPERIMENTAL */
500         }
501     }
502 }
503
504                                 
505 void wait(void) 
506 {
507     int key;
508     double temp, delay, origTime;
509
510     game.ididit = 0;
511     for (;;) {
512         key = scan();
513         if (key  != IHEOL) break;
514         proutn(_("How long? "));
515     }
516     chew();
517     if (key != IHREAL) {
518         huh();
519         return;
520     }
521     origTime = delay = aaitem;
522     if (delay <= 0.0) return;
523     if (delay >= game.state.remtime || game.nenhere != 0) {
524         proutn(_("Are you sure? "));
525         if (ja() == 0) return;
526     }
527
528     /* Alternate resting periods (events) with attacks */
529
530     game.resting = 1;
531     do {
532         if (delay <= 0) game.resting = 0;
533         if (game.resting == 0) {
534             prout(_("%d stardates left."), (int)game.state.remtime);
535             return;
536         }
537         temp = game.optime = delay;
538
539         if (game.nenhere) {
540             double rtime = 1.0 + Rand();
541             if (rtime < temp) temp = rtime;
542             game.optime = temp;
543         }
544         if (game.optime < delay) attack(0);
545         if (game.alldone) return;
546         events();
547         game.ididit = 1;
548         if (game.alldone) return;
549         delay -= temp;
550         /* Repair Deathray if long rest at starbase */
551         if (origTime-delay >= 9.99 && game.condit == IHDOCKED)
552             game.damage[DDRAY] = 0.0;
553     } while 
554         // leave if quadrant supernovas
555         (!game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova);
556
557     game.resting = 0;
558     game.optime = 0;
559 }
560
561 void nova(int ix, int iy) 
562 {
563     static double course[] =
564         {0.0, 10.5, 12.0, 1.5, 9.0, 0.0, 3.0, 7.5, 6.0, 4.5};
565     int bot, top, top2, hits[QUADSIZE+1][3], kount, icx, icy, mm, nn, j;
566     int iquad, iquad1, i, ll;
567     coord newc, nov, scratch;
568
569     nov.x = ix; nov.y = iy;
570     if (Rand() < 0.05) {
571         /* Wow! We've supernova'ed */
572         snova(ix, iy);
573         return;
574     }
575
576     /* handle initial nova */
577     game.quad[ix][iy] = IHDOT;
578     crmena(1, IHSTAR, 2, nov);
579     prout(_(" novas."));
580     game.state.galaxy[game.quadrant.x][game.quadrant.y].stars--;
581     game.state.starkl++;
582         
583     /* Set up stack to recursively trigger adjacent stars */
584     bot = top = top2 = 1;
585     kount = 0;
586     icx = icy = 0;
587     hits[1][1] = ix;
588     hits[1][2] = iy;
589     while (1) {
590         for (mm = bot; mm <= top; mm++) 
591             for (nn = 1; nn <= 3; nn++)  /* nn,j represents coordinates around current */
592                 for (j = 1; j <= 3; j++) {
593                     if (j==2 && nn== 2) continue;
594                     scratch.x = hits[mm][1]+nn-2;
595                     scratch.y = hits[mm][2]+j-2;
596                     if (!VALID_SECTOR(scratch.y, scratch.x)) continue;
597                     iquad = game.quad[scratch.x][scratch.y];
598                     switch (iquad) {
599                     // case IHDOT:      /* Empty space ends reaction
600                     // case IHQUEST:
601                     // case IHBLANK:
602                     // case IHT:
603                     // case IHWEB:
604                     default:
605                         break;
606                     case IHSTAR: /* Affect another star */
607                         if (Rand() < 0.05) {
608                             /* This star supernovas */
609                             snova(scratch.x,scratch.y);
610                             return;
611                         }
612                         top2++;
613                         hits[top2][1]=scratch.x;
614                         hits[top2][2]=scratch.y;
615                         game.state.galaxy[game.quadrant.x][game.quadrant.y].stars -= 1;
616                         game.state.starkl++;
617                         crmena(1, IHSTAR, 2, scratch);
618                         prout(_(" novas."));
619                         game.quad[scratch.x][scratch.y] = IHDOT;
620                         break;
621                     case IHP: /* Destroy planet */
622                         game.state.galaxy[game.quadrant.x][game.quadrant.y].planet = NULL;
623                         game.state.nplankl++;
624                         crmena(1, IHP, 2, scratch);
625                         prout(_(" destroyed."));
626                         DESTROY(&game.state.plnets[game.iplnet]);
627                         game.iplnet = game.plnet.x = game.plnet.y = 0;
628                         if (game.landed == 1) {
629                             finish(FPNOVA);
630                             return;
631                         }
632                         game.quad[scratch.x][scratch.y] = IHDOT;
633                         break;
634                     case IHB: /* Destroy base */
635                         game.state.galaxy[game.quadrant.x][game.quadrant.y].starbase = false;
636                         for_starbases(i)
637                             if (game.state.baseq[i].x==game.quadrant.x && game.state.baseq[i].y==game.quadrant.y) 
638                                 break;
639                         game.state.baseq[i] = game.state.baseq[game.state.rembase];
640                         game.state.rembase--;
641                         game.base.x = game.base.y = 0;
642                         game.state.basekl++;
643                         newcnd();
644                         crmena(1, IHB, 2, scratch);
645                         prout(_(" destroyed."));
646                         game.quad[scratch.x][scratch.y] = IHDOT;
647                         break;
648                     case IHE: /* Buffet ship */
649                     case IHF:
650                         prout(_("***Starship buffeted by nova."));
651                         if (game.shldup) {
652                             if (game.shield >= 2000.0) game.shield -= 2000.0;
653                             else {
654                                 double diff = 2000.0 - game.shield;
655                                 game.energy -= diff;
656                                 game.shield = 0.0;
657                                 game.shldup = 0;
658                                 prout(_("***Shields knocked out."));
659                                 game.damage[DSHIELD] += 0.005*game.damfac*Rand()*diff;
660                             }
661                         }
662                         else game.energy -= 2000.0;
663                         if (game.energy <= 0) {
664                             finish(FNOVA);
665                             return;
666                         }
667                         /* add in course nova contributes to kicking starship*/
668                         icx += game.sector.x-hits[mm][1];
669                         icy += game.sector.y-hits[mm][2];
670                         kount++;
671                         break;
672                     case IHK: /* kill klingon */
673                         deadkl(scratch,iquad, scratch.x, scratch.y);
674                         break;
675                     case IHC: /* Damage/destroy big enemies */
676                     case IHS:
677                     case IHR:
678                         for_local_enemies(ll)
679                             if (game.ks[ll].x==scratch.x && game.ks[ll].y==scratch.y) break;
680                         game.kpower[ll] -= 800.0; /* If firepower is lost, die */
681                         if (game.kpower[ll] <= 0.0) {
682                             deadkl(scratch, iquad, scratch.x, scratch.y);
683                             break;
684                         }
685                         newc.x = scratch.x + scratch.x - hits[mm][1];
686                         newc.y = scratch.y + scratch.y - hits[mm][2];
687                         crmena(1, iquad, 2, scratch);
688                         proutn(_(" damaged"));
689                         if (!VALID_SECTOR(newc.x, newc.y)) {
690                             /* can't leave quadrant */
691                             skip(1);
692                             break;
693                         }
694                         iquad1 = game.quad[newc.x][newc.y];
695                         if (iquad1 == IHBLANK) {
696                             proutn(_(", blasted into "));
697                             crmena(0, IHBLANK, 2, newc);
698                             skip(1);
699                             deadkl(scratch, iquad, newc.x, newc.y);
700                             break;
701                         }
702                         if (iquad1 != IHDOT) {
703                             /* can't move into something else */
704                             skip(1);
705                             break;
706                         }
707                         proutn(_(", buffeted to "));
708                         proutn(cramlc(sector, newc));
709                         game.quad[scratch.x][scratch.y] = IHDOT;
710                         game.quad[newc.x][newc.y] = iquad;
711                         game.ks[ll].x = newc.x;
712                         game.ks[ll].y = newc.y;
713                         game.kavgd[ll] = sqrt(square(game.sector.x-newc.x)+square(game.sector.y-newc.y));
714                         game.kdist[ll] = game.kavgd[ll];
715                         skip(1);
716                         break;
717                     }
718                 }
719         if (top == top2) 
720             break;
721         bot = top + 1;
722         top = top2;
723     }
724     if (kount==0) 
725         return;
726
727     /* Starship affected by nova -- kick it away. */
728     game.dist = kount*0.1;
729     if (icx) icx = (icx < 0 ? -1 : 1);
730     if (icy) icy = (icy < 0 ? -1 : 1);
731     game.direc = course[3*(icx+1)+icy+2];
732     if (game.direc == 0.0) game.dist = 0.0;
733     if (game.dist == 0.0) return;
734     game.optime = 10.0*game.dist/16.0;
735     skip(1);
736     prout(_("Force of nova displaces starship."));
737     game.iattak=2;      /* Eliminates recursion problem */
738     imove();
739     game.optime = 10.0*game.dist/16.0;
740     return;
741 }
742         
743         
744 void snova(int insx, int insy) 
745 {
746     int comdead, nsx, nsy, num=0, kldead, iscdead;
747     int nrmdead, npdead;
748     int incipient=0;
749     coord nq;
750
751     nq.x = nq.y = 0;
752     nsx = insy;
753     nsy = insy;
754
755     if (insy== 0) {
756         if (insx == 1)
757             /* NOVAMAX being used */
758             nq = game.probec;
759         else {
760             int stars = 0;
761             /* Scheduled supernova -- select star */
762             /* logic changed here so that we won't favor quadrants in top
763                left of universe */
764             for_quadrants(nq.x) {
765                 for_quadrants(nq.y) {
766                     stars += game.state.galaxy[nq.x][nq.y].stars;
767                 }
768             }
769             if (stars == 0) return; /* nothing to supernova exists */
770             num = Rand()*stars + 1;
771             for_quadrants(nq.x) {
772                 for_quadrants(nq.y) {
773                     num -= game.state.galaxy[nq.x][nq.y].stars;
774                     if (num <= 0) break;
775                 }
776                 if (num <=0) break;
777             }
778 #ifdef DEBUG
779             if (game.idebug) {
780                 proutn("Super nova here?");
781                 if (ja()==1) {
782                     nq.x = game.quadrant.x;
783                     nq.y = game.quadrant.y;
784                 }
785             }
786 #endif
787         }
788
789         if (nq.x != game.quadrant.y || nq.y != game.quadrant.y || game.justin != 0) {
790             /* it isn't here, or we just entered (treat as inroute) */
791             if (game.damage[DRADIO] == 0.0 || game.condit == IHDOCKED) {
792                 skip(1);
793                 prout(_("Message from Starfleet Command       Stardate %.2f"), game.state.date);
794                 prout(_("     Supernova in %s; caution advised."),
795                       cramlc(quadrant, nq));
796             }
797         }
798         else {
799             /* we are in the quadrant! */
800             incipient = 1;
801             num = Rand()* game.state.galaxy[nq.x][nq.y].stars + 1;
802             for_sectors(nsx) {
803                 for_sectors(nsy) {
804                     if (game.quad[nsx][nsy]==IHSTAR) {
805                         num--;
806                         if (num==0) break;
807                     }
808                 }
809                 if (num==0) break;
810             }
811         }
812     }
813     else {
814         incipient = 1;
815     }
816
817     if (incipient) {
818         coord nd;
819         skip(1);
820         prouts(_("***RED ALERT!  RED ALERT!"));
821         skip(1);
822         nd.x = nsx; nd.y = nsy;
823         prout(_("***Incipient supernova detected at "), cramlc(sector, nd));
824         nq = game.quadrant;
825         if (square(nsx-game.sector.x) + square(nsy-game.sector.y) <= 2.1) {
826             proutn(_("Emergency override attempts t"));
827             prouts("***************");
828             skip(1);
829             stars();
830             game.alldone=1;
831         }
832     }
833     /* destroy any Klingons in supernovaed quadrant */
834     kldead = game.state.galaxy[nq.x][nq.y].klingons;
835     game.state.galaxy[nq.x][nq.y].klingons = 0;
836     comdead = iscdead = 0;
837     if (same(nq, game.state.kscmdr)) {
838         /* did in the Supercommander! */
839         game.state.nscrem = game.state.kscmdr.x = game.state.kscmdr.y = game.isatb = game.iscate = 0;
840         iscdead = 1;
841         unschedule(FSCMOVE);
842         unschedule(FSCDBAS);
843     }
844     if (game.state.remcom) {
845         int maxloop = game.state.remcom, l;
846         for (l = 1; l <= maxloop; l++) {
847             if (same(game.state.kcmdr[l], nq)) {
848                 game.state.kcmdr[l] = game.state.kcmdr[game.state.remcom];
849                 game.state.kcmdr[game.state.remcom].x = game.state.kcmdr[game.state.remcom].y = 0;
850                 game.state.remcom--;
851                 kldead--;
852                 comdead++;
853                 if (game.state.remcom==0) unschedule(FTBEAM);
854                 break;
855             }
856         }
857     }
858     game.state.remkl -= kldead;
859     /* destroy Romulans and planets in supernovaed quadrant */
860     nrmdead = game.state.galaxy[nq.x][nq.y].romulans;
861     game.state.galaxy[nq.x][nq.y].romulans = 0;
862     game.state.nromrem -= nrmdead;
863     npdead = num - nrmdead*10;
864     if (npdead) {
865         int l;
866         for (l = 0; l < game.inplan; l++)
867             if (same(game.state.plnets[l].w, nq)) {
868                 DESTROY(&game.state.plnets[l]);
869             }
870     }
871     /* Destroy any base in supernovaed quadrant */
872     if (game.state.rembase) {
873         int maxloop = game.state.rembase, l;
874         for (l = 1; l <= maxloop; l++)
875             if (same(game.state.baseq[l], nq)) {
876                 game.state.baseq[l] = game.state.baseq[game.state.rembase];
877                 game.state.baseq[game.state.rembase].x = game.state.baseq[game.state.rembase].y = 0;
878                 game.state.rembase--;
879                 break;
880             }
881     }
882     /* If starship caused supernova, tally up destruction */
883     if (insx) {
884         game.state.starkl += game.state.galaxy[nq.x][nq.y].stars;
885         game.state.basekl += game.state.galaxy[nq.x][nq.y].starbase;
886         game.state.nplankl += npdead;
887     }
888     /* mark supernova in galaxy and in star chart */
889     if ((game.quadrant.x == nq.x && game.quadrant.y == nq.y) ||
890         game.damage[DRADIO] == 0 ||
891         game.condit == IHDOCKED)
892         game.state.galaxy[nq.x][nq.y].supernova = true;
893     /* If supernova destroys last klingons give special message */
894     if (KLINGREM==0 && (nq.x != game.quadrant.x || nq.y != game.quadrant.y)) {
895         skip(2);
896         if (insx == 0) prout(_("Lucky you!"));
897         proutn(_("A supernova in %s has just destroyed the last Klingons."),
898                cramlc(quadrant, nq));
899         finish(FWON);
900         return;
901     }
902     /* if some Klingons remain, continue or die in supernova */
903     if (game.alldone) finish(FSNOVAED);
904     return;
905 }
906                 
907