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