More boolean cleanups and explanator comments.
[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 istract=0, 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;
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(0,0);
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 > 0 ||
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=1;
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 == 1) { /* 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==0 &&
367                 game.isatb != 1 &&
368                 (game.iscate != 1 || !game.justin)) 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(1,0);
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(int ix, int iy) 
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, nov, scratch;
612
613     nov.x = ix; nov.y = iy;
614     if (Rand() < 0.05) {
615         /* Wow! We've supernova'ed */
616         snova(ix, iy);
617         return;
618     }
619
620     /* handle initial nova */
621     game.quad[ix][iy] = IHDOT;
622     crmena(false, IHSTAR, sector, nov);
623     prout(_(" novas."));
624     game.state.galaxy[game.quadrant.x][game.quadrant.y].stars--;
625     game.state.starkl++;
626         
627     /* Set up stack to recursively trigger adjacent stars */
628     bot = top = top2 = 1;
629     kount = 0;
630     icx = icy = 0;
631     hits[1][1] = ix;
632     hits[1][2] = iy;
633     while (1) {
634         for (mm = bot; mm <= top; mm++) 
635             for (nn = 1; nn <= 3; nn++)  /* nn,j represents coordinates around current */
636                 for (j = 1; j <= 3; j++) {
637                     if (j==2 && nn== 2) continue;
638                     scratch.x = hits[mm][1]+nn-2;
639                     scratch.y = hits[mm][2]+j-2;
640                     if (!VALID_SECTOR(scratch.y, scratch.x)) continue;
641                     iquad = game.quad[scratch.x][scratch.y];
642                     switch (iquad) {
643                     // case IHDOT:      /* Empty space ends reaction
644                     // case IHQUEST:
645                     // case IHBLANK:
646                     // case IHT:
647                     // case IHWEB:
648                     default:
649                         break;
650                     case IHSTAR: /* Affect another star */
651                         if (Rand() < 0.05) {
652                             /* This star supernovas */
653                             snova(scratch.x,scratch.y);
654                             return;
655                         }
656                         top2++;
657                         hits[top2][1]=scratch.x;
658                         hits[top2][2]=scratch.y;
659                         game.state.galaxy[game.quadrant.x][game.quadrant.y].stars -= 1;
660                         game.state.starkl++;
661                         crmena(true, IHSTAR, sector, scratch);
662                         prout(_(" novas."));
663                         game.quad[scratch.x][scratch.y] = IHDOT;
664                         break;
665                     case IHP: /* Destroy planet */
666                         game.state.galaxy[game.quadrant.x][game.quadrant.y].planet = NOPLANET;
667                         game.state.nplankl++;
668                         crmena(true, IHP, sector, scratch);
669                         prout(_(" destroyed."));
670                         DESTROY(&game.state.plnets[game.iplnet]);
671                         game.iplnet = game.plnet.x = game.plnet.y = 0;
672                         if (game.landed == 1) {
673                             finish(FPNOVA);
674                             return;
675                         }
676                         game.quad[scratch.x][scratch.y] = IHDOT;
677                         break;
678                     case IHB: /* Destroy base */
679                         game.state.galaxy[game.quadrant.x][game.quadrant.y].starbase = false;
680                         for_starbases(i)
681                             if (game.state.baseq[i].x==game.quadrant.x && game.state.baseq[i].y==game.quadrant.y) 
682                                 break;
683                         game.state.baseq[i] = game.state.baseq[game.state.rembase];
684                         game.state.rembase--;
685                         game.base.x = game.base.y = 0;
686                         game.state.basekl++;
687                         newcnd();
688                         crmena(true, IHB, sector, scratch);
689                         prout(_(" destroyed."));
690                         game.quad[scratch.x][scratch.y] = IHDOT;
691                         break;
692                     case IHE: /* Buffet ship */
693                     case IHF:
694                         prout(_("***Starship buffeted by nova."));
695                         if (game.shldup) {
696                             if (game.shield >= 2000.0) game.shield -= 2000.0;
697                             else {
698                                 double diff = 2000.0 - game.shield;
699                                 game.energy -= diff;
700                                 game.shield = 0.0;
701                                 game.shldup = false;
702                                 prout(_("***Shields knocked out."));
703                                 game.damage[DSHIELD] += 0.005*game.damfac*Rand()*diff;
704                             }
705                         }
706                         else game.energy -= 2000.0;
707                         if (game.energy <= 0) {
708                             finish(FNOVA);
709                             return;
710                         }
711                         /* add in course nova contributes to kicking starship*/
712                         icx += game.sector.x-hits[mm][1];
713                         icy += game.sector.y-hits[mm][2];
714                         kount++;
715                         break;
716                     case IHK: /* kill klingon */
717                         deadkl(scratch,iquad, scratch.x, scratch.y);
718                         break;
719                     case IHC: /* Damage/destroy big enemies */
720                     case IHS:
721                     case IHR:
722                         for_local_enemies(ll)
723                             if (game.ks[ll].x==scratch.x && game.ks[ll].y==scratch.y) break;
724                         game.kpower[ll] -= 800.0; /* If firepower is lost, die */
725                         if (game.kpower[ll] <= 0.0) {
726                             deadkl(scratch, iquad, scratch.x, scratch.y);
727                             break;
728                         }
729                         newc.x = scratch.x + scratch.x - hits[mm][1];
730                         newc.y = scratch.y + scratch.y - hits[mm][2];
731                         crmena(true, iquad, sector, scratch);
732                         proutn(_(" damaged"));
733                         if (!VALID_SECTOR(newc.x, newc.y)) {
734                             /* can't leave quadrant */
735                             skip(1);
736                             break;
737                         }
738                         iquad1 = game.quad[newc.x][newc.y];
739                         if (iquad1 == IHBLANK) {
740                             proutn(_(", blasted into "));
741                             crmena(false, IHBLANK, sector, newc);
742                             skip(1);
743                             deadkl(scratch, iquad, newc.x, newc.y);
744                             break;
745                         }
746                         if (iquad1 != IHDOT) {
747                             /* can't move into something else */
748                             skip(1);
749                             break;
750                         }
751                         proutn(_(", buffeted to "));
752                         proutn(cramlc(sector, newc));
753                         game.quad[scratch.x][scratch.y] = IHDOT;
754                         game.quad[newc.x][newc.y] = iquad;
755                         game.ks[ll].x = newc.x;
756                         game.ks[ll].y = newc.y;
757                         game.kavgd[ll] = sqrt(square(game.sector.x-newc.x)+square(game.sector.y-newc.y));
758                         game.kdist[ll] = game.kavgd[ll];
759                         skip(1);
760                         break;
761                     }
762                 }
763         if (top == top2) 
764             break;
765         bot = top + 1;
766         top = top2;
767     }
768     if (kount==0) 
769         return;
770
771     /* Starship affected by nova -- kick it away. */
772     game.dist = kount*0.1;
773     if (icx) icx = (icx < 0 ? -1 : 1);
774     if (icy) icy = (icy < 0 ? -1 : 1);
775     game.direc = course[3*(icx+1)+icy+2];
776     if (game.direc == 0.0) game.dist = 0.0;
777     if (game.dist == 0.0) return;
778     game.optime = 10.0*game.dist/16.0;
779     skip(1);
780     prout(_("Force of nova displaces starship."));
781     game.iattak=2;      /* Eliminates recursion problem */
782     imove();
783     game.optime = 10.0*game.dist/16.0;
784     return;
785 }
786         
787         
788 void snova(int insx, int insy) 
789 /* star goes supernova */
790 {
791     int comdead, nsx, nsy, num=0, kldead, iscdead;
792     int nrmdead, npdead;
793     bool incipient = false;
794     coord nq;
795
796     nq.x = nq.y = 0;
797     nsx = insy;
798     nsy = insy;
799
800     if (insy== 0) {
801         if (insx == 1)
802             /* NOVAMAX being used */
803             nq = game.probec;
804         else {
805             int stars = 0;
806             /* Scheduled supernova -- select star */
807             /* logic changed here so that we won't favor quadrants in top
808                left of universe */
809             for_quadrants(nq.x) {
810                 for_quadrants(nq.y) {
811                     stars += game.state.galaxy[nq.x][nq.y].stars;
812                 }
813             }
814             if (stars == 0) return; /* nothing to supernova exists */
815             num = Rand()*stars + 1;
816             for_quadrants(nq.x) {
817                 for_quadrants(nq.y) {
818                     num -= game.state.galaxy[nq.x][nq.y].stars;
819                     if (num <= 0) break;
820                 }
821                 if (num <=0) break;
822             }
823             if (idebug) {
824                 proutn("=== Super nova here?");
825                 if (ja() == true) {
826                     nq.x = game.quadrant.x;
827                     nq.y = game.quadrant.y;
828                 }
829             }
830         }
831
832         if (nq.x != game.quadrant.y || nq.y != game.quadrant.y || game.justin != 0) {
833             /* it isn't here, or we just entered (treat as inroute) */
834             if (!damaged(DRADIO) || game.condit == IHDOCKED) {
835                 skip(1);
836                 prout(_("Message from Starfleet Command       Stardate %.2f"), game.state.date);
837                 prout(_("     Supernova in %s; caution advised."),
838                       cramlc(quadrant, nq));
839             }
840         }
841         else {
842             /* we are in the quadrant! */
843             incipient = true;
844             num = Rand()* game.state.galaxy[nq.x][nq.y].stars + 1;
845             for_sectors(nsx) {
846                 for_sectors(nsy) {
847                     if (game.quad[nsx][nsy]==IHSTAR) {
848                         num--;
849                         if (num==0) break;
850                     }
851                 }
852                 if (num==0) break;
853             }
854         }
855     }
856     else {
857         incipient = true;
858     }
859
860     if (incipient) {
861         coord nd;
862         skip(1);
863         prouts(_("***RED ALERT!  RED ALERT!"));
864         skip(1);
865         nd.x = nsx; nd.y = nsy;
866         prout(_("***Incipient supernova detected at "), cramlc(sector, nd));
867         nq = game.quadrant;
868         if (square(nsx-game.sector.x) + square(nsy-game.sector.y) <= 2.1) {
869             proutn(_("Emergency override attempts t"));
870             prouts("***************");
871             skip(1);
872             stars();
873             game.alldone=1;
874         }
875     }
876     /* destroy any Klingons in supernovaed quadrant */
877     kldead = game.state.galaxy[nq.x][nq.y].klingons;
878     game.state.galaxy[nq.x][nq.y].klingons = 0;
879     comdead = iscdead = 0;
880     if (same(nq, game.state.kscmdr)) {
881         /* did in the Supercommander! */
882         game.state.nscrem = game.state.kscmdr.x = game.state.kscmdr.y = game.isatb = game.iscate = 0;
883         iscdead = 1;
884         unschedule(FSCMOVE);
885         unschedule(FSCDBAS);
886     }
887     if (game.state.remcom) {
888         int maxloop = game.state.remcom, l;
889         for (l = 1; l <= maxloop; l++) {
890             if (same(game.state.kcmdr[l], nq)) {
891                 game.state.kcmdr[l] = game.state.kcmdr[game.state.remcom];
892                 game.state.kcmdr[game.state.remcom].x = game.state.kcmdr[game.state.remcom].y = 0;
893                 game.state.remcom--;
894                 kldead--;
895                 comdead++;
896                 if (game.state.remcom==0) unschedule(FTBEAM);
897                 break;
898             }
899         }
900     }
901     game.state.remkl -= kldead;
902     /* destroy Romulans and planets in supernovaed quadrant */
903     nrmdead = game.state.galaxy[nq.x][nq.y].romulans;
904     game.state.galaxy[nq.x][nq.y].romulans = 0;
905     game.state.nromrem -= nrmdead;
906     npdead = num - nrmdead*10;
907     if (npdead) {
908         int loop;
909         for (loop = 0; loop < game.inplan; loop++)
910             if (same(game.state.plnets[loop].w, nq)) {
911                 DESTROY(&game.state.plnets[loop]);
912             }
913     }
914     /* Destroy any base in supernovaed quadrant */
915     if (game.state.rembase) {
916         int maxloop = game.state.rembase, loop;
917         for (loop = 1; loop <= maxloop; loop++)
918             if (same(game.state.baseq[loop], nq)) {
919                 game.state.baseq[loop] = game.state.baseq[game.state.rembase];
920                 game.state.baseq[game.state.rembase].x = game.state.baseq[game.state.rembase].y = 0;
921                 game.state.rembase--;
922                 break;
923             }
924     }
925     /* If starship caused supernova, tally up destruction */
926     if (insx) {
927         game.state.starkl += game.state.galaxy[nq.x][nq.y].stars;
928         game.state.basekl += game.state.galaxy[nq.x][nq.y].starbase;
929         game.state.nplankl += npdead;
930     }
931     /* mark supernova in galaxy and in star chart */
932     if (same(game.quadrant, nq) || !damaged(DRADIO) || game.condit == IHDOCKED)
933         game.state.galaxy[nq.x][nq.y].supernova = true;
934     /* If supernova destroys last Klingons give special message */
935     if (KLINGREM==0 && !same(nq, game.quadrant)) {
936         skip(2);
937         if (insx == 0) prout(_("Lucky you!"));
938         proutn(_("A supernova in %s has just destroyed the last Klingons."),
939                cramlc(quadrant, nq));
940         finish(FWON);
941         return;
942     }
943     /* if some Klingons remain, continue or die in supernova */
944     if (game.alldone) finish(FSNOVAED);
945     return;
946 }
947                 
948