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