7b915f22e28f45b3119f74d3c6c0013da5b74ed6
[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.quadx][game.quady].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.isx-game.quadx) + square(game.state.isy-game.quady);
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.cx[i]-game.quadx) + square(game.state.cy[i]-game.quady);
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.quadx = game.state.isx;
167                 game.quady = game.state.isy;
168             }
169             else {
170                 game.quadx = game.state.cx[i];
171                 game.quady = game.state.cy[i];
172             }
173             iran(QUADSIZE, &game.sectx, &game.secty);
174             crmshp();
175             proutn(_(" is pulled to "));
176             proutn(cramlc(quadrant, game.quadx, game.quady));
177             proutn(", ");
178             prout(cramlc(sector, game.sectx, game.secty));
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.baseqx[j]==game.state.cx[k] && game.state.baseqy[j]==game.state.cy[k] &&
213                         (game.state.baseqx[j]!=game.quadx || game.state.baseqy[j]!=game.quady) &&
214                         (game.state.baseqx[j]!=game.state.isx || game.state.baseqy[j]!=game.state.isy)) {
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.batx = game.state.baseqx[j];
228             game.baty = game.state.baseqy[j];
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.batx, game.baty));
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.isx][game.state.isy].starbase) 
260                 break; /* WAS RETURN! */
261             ixhold = game.batx;
262             iyhold = game.baty;
263             game.batx = game.state.isx;
264             game.baty = game.state.isy;
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.cx[i]==game.batx && game.state.cy[i]==game.baty) 
271                         break;
272                 if (i > game.state.remcom || game.state.rembase == 0 ||
273                     !game.state.galaxy[game.batx][game.baty].starbase) {
274                     /* No action to take after all */
275                     game.batx = game.baty = 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.batx==game.quadx && game.baty==game.quady) {
283                 game.state.chart[game.batx][game.baty].starbase = FALSE;
284                 game.quad[game.basex][game.basey]= IHDOT;
285                 game.basex=game.basey=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.batx, game.baty));
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.batx][game.baty].starbase = FALSE;
303             }
304             /* Remove Starbase from galaxy */
305             game.state.galaxy[game.batx][game.baty].starbase = FALSE;
306             for_starbases(i)
307                 if (game.state.baseqx[i]==game.batx && game.state.baseqy[i]==game.baty) {
308                     game.state.baseqx[i]=game.state.baseqx[game.state.rembase];
309                     game.state.baseqy[i]=game.state.baseqy[game.state.rembase];
310                 }
311             game.state.rembase--;
312             if (game.isatb == 2) {
313                 /* reinstate a commander's base attack */
314                 game.batx = ixhold;
315                 game.baty = iyhold;
316                 game.isatb = 0;
317             }
318             else {
319                 game.batx = game.baty = 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.probecx != i || game.probecy != j) {
335                 game.probecx = i;
336                 game.probecy = j;
337                 if (!VALID_QUADRANT(i, j) ||
338                     game.state.galaxy[game.probecx][game.probecy].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.probecx, game.probecy));
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.probecx][game.probecy].klingons = game.state.galaxy[game.probecx][game.probecy].klingons;
367                 game.state.chart[game.probecx][game.probecy].starbase = game.state.galaxy[game.probecx][game.probecy].starbase;
368                 game.state.chart[game.probecx][game.probecy].stars = game.state.galaxy[game.probecx][game.probecy].stars;
369                 game.state.galaxy[game.probecx][game.probecy].charted = TRUE;
370             }
371             game.proben--; // One less to travel
372             if (game.proben == 0 && game.isarmed &&
373                 game.state.galaxy[game.probecx][game.probecy].stars) {
374                 /* lets blow the sucker! */
375                 snova(1,0);
376                 unschedule(FDSPROB);
377                 if (game.state.galaxy[game.quadx][game.quady].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.quadx][game.quady];
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.quadx && iy == game.quady) || 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.quadx && iy == game.quady)
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.quadx][game.quady].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, newcx, newcy, ii, jj;
564     if (Rand() < 0.05) {
565         /* Wow! We've supernova'ed */
566         snova(ix, iy);
567         return;
568     }
569
570     /* handle initial nova */
571     game.quad[ix][iy] = IHDOT;
572     crmena(1, IHSTAR, 2, ix, iy);
573     prout(_(" novas."));
574     game.state.galaxy[game.quadx][game.quady].stars--;
575     game.state.starkl++;
576         
577     /* Set up stack to recursively trigger adjacent stars */
578     bot = top = top2 = 1;
579     kount = 0;
580     icx = icy = 0;
581     hits[1][1] = ix;
582     hits[1][2] = iy;
583     while (1) {
584         for (mm = bot; mm <= top; mm++) 
585             for (nn = 1; nn <= 3; nn++)  /* nn,j represents coordinates around current */
586                 for (j = 1; j <= 3; j++) {
587                     if (j==2 && nn== 2) continue;
588                     ii = hits[mm][1]+nn-2;
589                     jj = hits[mm][2]+j-2;
590                     if (!VALID_SECTOR(jj, ii)) continue;
591                     iquad = game.quad[ii][jj];
592                     switch (iquad) {
593                     // case IHDOT:      /* Empty space ends reaction
594                     // case IHQUEST:
595                     // case IHBLANK:
596                     // case IHT:
597                     // case IHWEB:
598                     default:
599                         break;
600                     case IHSTAR: /* Affect another star */
601                         if (Rand() < 0.05) {
602                             /* This star supernovas */
603                             snova(ii,jj);
604                             return;
605                         }
606                         top2++;
607                         hits[top2][1]=ii;
608                         hits[top2][2]=jj;
609                         game.state.galaxy[game.quadx][game.quady].stars -= 1;
610                         game.state.starkl++;
611                         crmena(1, IHSTAR, 2, ii, jj);
612                         prout(_(" novas."));
613                         game.quad[ii][jj] = IHDOT;
614                         break;
615                     case IHP: /* Destroy planet */
616                         game.state.galaxy[game.quadx][game.quady].planet = NULL;
617                         game.state.nplankl++;
618                         crmena(1, IHP, 2, ii, jj);
619                         prout(_(" destroyed."));
620                         DESTROY(&game.state.plnets[game.iplnet]);
621                         game.iplnet = game.plnetx = game.plnety = 0;
622                         if (game.landed == 1) {
623                             finish(FPNOVA);
624                             return;
625                         }
626                         game.quad[ii][jj] = IHDOT;
627                         break;
628                     case IHB: /* Destroy base */
629                         game.state.galaxy[game.quadx][game.quady].starbase = FALSE;
630                         for_starbases(i)
631                             if (game.state.baseqx[i]==game.quadx && game.state.baseqy[i]==game.quady) 
632                                 break;
633                         game.state.baseqx[i] = game.state.baseqx[game.state.rembase];
634                         game.state.baseqy[i] = game.state.baseqy[game.state.rembase];
635                         game.state.rembase--;
636                         game.basex = game.basey = 0;
637                         game.state.basekl++;
638                         newcnd();
639                         crmena(1, IHB, 2, ii, jj);
640                         prout(_(" destroyed."));
641                         game.quad[ii][jj] = IHDOT;
642                         break;
643                     case IHE: /* Buffet ship */
644                     case IHF:
645                         prout(_("***Starship buffeted by nova."));
646                         if (game.shldup) {
647                             if (game.shield >= 2000.0) game.shield -= 2000.0;
648                             else {
649                                 double diff = 2000.0 - game.shield;
650                                 game.energy -= diff;
651                                 game.shield = 0.0;
652                                 game.shldup = 0;
653                                 prout(_("***Shields knocked out."));
654                                 game.damage[DSHIELD] += 0.005*game.damfac*Rand()*diff;
655                             }
656                         }
657                         else game.energy -= 2000.0;
658                         if (game.energy <= 0) {
659                             finish(FNOVA);
660                             return;
661                         }
662                         /* add in course nova contributes to kicking starship*/
663                         icx += game.sectx-hits[mm][1];
664                         icy += game.secty-hits[mm][2];
665                         kount++;
666                         break;
667                     case IHK: /* kill klingon */
668                         deadkl(ii,jj,iquad, ii, jj);
669                         break;
670                     case IHC: /* Damage/destroy big enemies */
671                     case IHS:
672                     case IHR:
673                         for_local_enemies(ll)
674                             if (game.kx[ll]==ii && game.ky[ll]==jj) break;
675                         game.kpower[ll] -= 800.0; /* If firepower is lost, die */
676                         if (game.kpower[ll] <= 0.0) {
677                             deadkl(ii, jj, iquad, ii, jj);
678                             break;
679                         }
680                         newcx = ii + ii - hits[mm][1];
681                         newcy = jj + jj - hits[mm][2];
682                         crmena(1, iquad, 2, ii, jj);
683                         proutn(_(" damaged"));
684                         if (!VALID_SECTOR(newcx, newcy)) {
685                             /* can't leave quadrant */
686                             skip(1);
687                             break;
688                         }
689                         iquad1 = game.quad[newcx][newcy];
690                         if (iquad1 == IHBLANK) {
691                             proutn(_(", blasted into "));
692                             crmena(0, IHBLANK, 2, newcx, newcy);
693                             skip(1);
694                             deadkl(ii, jj, iquad, newcx, newcy);
695                             break;
696                         }
697                         if (iquad1 != IHDOT) {
698                             /* can't move into something else */
699                             skip(1);
700                             break;
701                         }
702                         proutn(_(", buffeted to "));
703                         proutn(cramlc(sector, newcx, newcy));
704                         game.quad[ii][jj] = IHDOT;
705                         game.quad[newcx][newcy] = iquad;
706                         game.kx[ll] = newcx;
707                         game.ky[ll] = newcy;
708                         game.kavgd[ll] = sqrt(square(game.sectx-newcx)+square(game.secty-newcy));
709                         game.kdist[ll] = game.kavgd[ll];
710                         skip(1);
711                         break;
712                     }
713                 }
714         if (top == top2) 
715             break;
716         bot = top + 1;
717         top = top2;
718     }
719     if (kount==0) 
720         return;
721
722     /* Starship affected by nova -- kick it away. */
723     game.dist = kount*0.1;
724     if (icx) icx = (icx < 0 ? -1 : 1);
725     if (icy) icy = (icy < 0 ? -1 : 1);
726     game.direc = course[3*(icx+1)+icy+2];
727     if (game.direc == 0.0) game.dist = 0.0;
728     if (game.dist == 0.0) return;
729     game.optime = 10.0*game.dist/16.0;
730     skip(1);
731     prout(_("Force of nova displaces starship."));
732     game.iattak=2;      /* Eliminates recursion problem */
733     imove();
734     game.optime = 10.0*game.dist/16.0;
735     return;
736 }
737         
738         
739 void snova(int insx, int insy) 
740 {
741     int comdead, nqx=0, nqy=0, nsx, nsy, num=0, kldead, iscdead;
742     int nrmdead, npdead;
743     int incipient=0;
744
745     nsx = insy;
746     nsy = insy;
747
748     if (insy== 0) {
749         if (insx == 1) {
750             /* NOVAMAX being used */
751             nqx = game.probecx;
752             nqy = game.probecy;
753         }
754         else {
755             int stars = 0;
756             /* Scheduled supernova -- select star */
757             /* logic changed here so that we won't favor quadrants in top
758                left of universe */
759             for_quadrants(nqx) {
760                 for_quadrants(nqy) {
761                     stars += game.state.galaxy[nqx][nqy].stars;
762                 }
763             }
764             if (stars == 0) return; /* nothing to supernova exists */
765             num = Rand()*stars + 1;
766             for_quadrants(nqx) {
767                 for_quadrants(nqy) {
768                     num -= game.state.galaxy[nqx][nqy].stars;
769                     if (num <= 0) break;
770                 }
771                 if (num <=0) break;
772             }
773 #ifdef DEBUG
774             if (game.idebug) {
775                 proutn("Super nova here?");
776                 if (ja()==1) {
777                     nqx = game.quadx;
778                     nqy = game.quady;
779                 }
780             }
781 #endif
782         }
783
784         if (nqx != game.quady || nqy != game.quady || game.justin != 0) {
785             /* it isn't here, or we just entered (treat as inroute) */
786             if (game.damage[DRADIO] == 0.0 || game.condit == IHDOCKED) {
787                 skip(1);
788                 prout(_("Message from Starfleet Command       Stardate %.2f"), game.state.date);
789                 prout(_("     Supernova in %s; caution advised."),
790                       cramlc(quadrant, nqx, nqy));
791             }
792         }
793         else {
794             /* we are in the quadrant! */
795             incipient = 1;
796             num = Rand()* game.state.galaxy[nqx][nqy].stars + 1;
797             for_sectors(nsx) {
798                 for_sectors(nsy) {
799                     if (game.quad[nsx][nsy]==IHSTAR) {
800                         num--;
801                         if (num==0) break;
802                     }
803                 }
804                 if (num==0) break;
805             }
806         }
807     }
808     else {
809         incipient = 1;
810     }
811
812     if (incipient) {
813         skip(1);
814         prouts(_("***RED ALERT!  RED ALERT!"));
815         skip(1);
816         prout(_("***Incipient supernova detected at "), cramlc(sector, nsx, nsy));
817         nqx = game.quadx;
818         nqy = game.quady;
819         if (square(nsx-game.sectx) + square(nsy-game.secty) <= 2.1) {
820             proutn(_("Emergency override attempts t"));
821             prouts("***************");
822             skip(1);
823             stars();
824             game.alldone=1;
825         }
826     }
827     /* destroy any Klingons in supernovaed quadrant */
828     kldead = game.state.galaxy[nqx][nqy].klingons;
829     game.state.galaxy[nqx][nqy].klingons = 0;
830     comdead = iscdead = 0;
831     if (nqx==game.state.isx && nqy == game.state.isy) {
832         /* did in the Supercommander! */
833         game.state.nscrem = game.state.isx = game.state.isy = game.isatb = game.iscate = 0;
834         iscdead = 1;
835         unschedule(FSCMOVE);
836         unschedule(FSCDBAS);
837     }
838     if (game.state.remcom) {
839         int maxloop = game.state.remcom, l;
840         for (l = 1; l <= maxloop; l++) {
841             if (game.state.cx[l] == nqx && game.state.cy[l] == nqy) {
842                 game.state.cx[l] = game.state.cx[game.state.remcom];
843                 game.state.cy[l] = game.state.cy[game.state.remcom];
844                 game.state.cx[game.state.remcom] = game.state.cy[game.state.remcom] = 0;
845                 game.state.remcom--;
846                 kldead--;
847                 comdead++;
848                 if (game.state.remcom==0) unschedule(FTBEAM);
849                 break;
850             }
851         }
852     }
853     game.state.remkl -= kldead;
854     /* destroy Romulans and planets in supernovaed quadrant */
855     nrmdead = game.state.galaxy[nqx][nqy].romulans;
856     game.state.galaxy[nqx][nqy].romulans = 0;
857     game.state.nromrem -= nrmdead;
858     npdead = num - nrmdead*10;
859     if (npdead) {
860         int l;
861         for (l = 0; l < game.inplan; l++)
862             if (game.state.plnets[l].x == nqx && game.state.plnets[l].y == nqy) {
863                 DESTROY(&game.state.plnets[l]);
864             }
865     }
866     /* Destroy any base in supernovaed quadrant */
867     if (game.state.rembase) {
868         int maxloop = game.state.rembase, l;
869         for (l = 1; l <= maxloop; l++)
870             if (game.state.baseqx[l]==nqx && game.state.baseqy[l]==nqy) {
871                 game.state.baseqx[l] = game.state.baseqx[game.state.rembase];
872                 game.state.baseqy[l] = game.state.baseqy[game.state.rembase];
873                 game.state.baseqx[game.state.rembase] = game.state.baseqy[game.state.rembase] = 0;
874                 game.state.rembase--;
875                 break;
876             }
877     }
878     /* If starship caused supernova, tally up destruction */
879     if (insx) {
880         game.state.starkl += game.state.galaxy[nqx][nqy].stars;
881         game.state.basekl += game.state.galaxy[nqx][nqy].starbase;
882         game.state.nplankl += npdead;
883     }
884     /* mark supernova in galaxy and in star chart */
885     if ((game.quadx == nqx && game.quady == nqy) ||
886         game.damage[DRADIO] == 0 ||
887         game.condit == IHDOCKED)
888         game.state.galaxy[nqx][nqy].supernova = TRUE;
889     /* If supernova destroys last klingons give special message */
890     if (KLINGREM==0 && (nqx != game.quadx || nqy != game.quady)) {
891         skip(2);
892         if (insx == 0) prout(_("Lucky you!"));
893         proutn(_("A supernova in %s has just destroyed the last Klingons."),
894                cramlc(quadrant, nqx, nqy));
895         finish(FWON);
896         return;
897     }
898     /* if some Klingons remain, continue or die in supernova */
899     if (game.alldone) finish(FSNOVAED);
900     return;
901 }
902                 
903