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