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