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