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