Deathray is now regressed to original 5% probability in 'plain' games.
[super-star-trek.git] / sst.c
1 #define INCLUDED        // Define externs here
2 #include <ctype.h>
3 #include <getopt.h>
4 #include <time.h>
5 #include "sstlinux.h"
6 #include "sst.h"
7
8 #ifndef SSTDOC
9 #define SSTDOC  "sst.doc"
10 #endif
11         
12 /*
13
14 Dave Matuszek says:
15
16    SRSCAN, MOVE, PHASERS, CALL, STATUS, IMPULSE, PHOTONS, ABANDON,
17    LRSCAN, WARP, SHIELDS, DESTRUCT, CHART, REST, DOCK, QUIT, and DAMAGE
18    were in the original non-"super" version of UT FORTRAN Star Trek.
19
20    Tholians weren't in the original. Dave is dubious about their merits.
21    (They are now controlled by OPTION_THOLIAN and turned off if the game
22    type is "plain".)
23
24    Planets and dilithium crystals weren't in the original.  Dave is OK
25    with this idea. (It's now controlled by OPTION_PLANETS and turned 
26    off if the game type is "plain".)
27
28    Dave says the bit about the Galileo getting turned into a
29    McDonald's is "consistant with our original vision".  (This has been
30    left permanently enabled, as it can only happen if OPTION_PLANETS
31    is on.)
32
33    Dave also says the Space Thingy should not be preserved across saved
34    games, so you can't prove to others that you've seen it.  He says it
35    shouldn't fire back, either.  It should do nothing except scream and
36    disappear when hit by photon torpedos.  It's OK that it may move
37    when attacked, but it didn't in the original.  (Whether the Thingy
38    can fire back is now controlled by OPTION_THINGY and turned off if the
39    game type is "plain" or "almy".  The no-save behavior has been restored.)
40
41    The Faerie Queen, black holes, and time warping were in the original.
42
43 Here are Tom Almy's changes:
44
45    Compared to original version, I've changed the "help" command to
46    "call" and the "terminate" command to "quit" to better match
47    user expectations. The DECUS version apparently made those changes
48    as well as changing "freeze" to "save". However I like "freeze".
49    (Both "freeze" and "save" work in SST2K.)
50
51    When I got a later (1979) version of Super Star Trek that I was converting
52    from, I added the emexit command from it.
53
54    That 1979 version also mentions srscan and lrscan working when
55    docked (using the starbase's scanners), so I made some changes here
56    to do this (and indicating that fact to the player), and then realized
57    the base would have a subspace radio as well -- doing a Chart when docked
58    updates the star chart, and all radio reports will be heard. The Dock
59    command will also give a report if a base is under attack.
60
61    Also added:
62
63    1. The experimental deathray originally had only a 5% chance of
64       success, but could be used repeatedly. I guess after a couple
65       years of use, it was less "experimental" because the 1979
66       version had a 70% success rate. However it was prone to breaking
67       after use. I upgraded the deathray, but kept the original set of
68       failure modes (great humor!).  (Now controlled by OPTION_DEATHRAY
69       and turned off if game type is "plain".)
70
71    2. Tholian Web from the 1979 version.  (Now controlled by
72       OPTION_THOLIAN and turned off if game type is "plain".)
73
74    3. Enemies can ram the Enterprise. (Now controlled by OPTION_RAMMING
75       and turned off if game type is "plain".)
76
77    4. Regular Klingons and Romulans can move in Expert and Emeritus games. 
78       This code could use improvement. (Now controlled by OPTION_MVBADDY
79       and turned off if game type is "plain".)
80
81    5. The deep-space probe feature from the DECUS version.  (Now controlled
82       by OPTION_PROBE and turned off if game type is "plain").
83
84    6. Bugfix: Klingon commander movements are no longer reported if long-range 
85       sensors are damaged.
86
87    7. Bugfix: Better base positioning at startup (more spread out).
88       That made sense to add because most people abort games with 
89       bad base placement.
90
91    In June 2002, I fixed two known bugs and a documentation typo.
92    In June 2004 I fixed a number of bugs involving: 1) parsing invalid
93    numbers, 2) manual phasers when SR scan is damaged and commander is
94    present, 3) time warping into the future, 4) hang when moving
95    klingons in crowded quadrants.  (These fixes are in SST2K.)
96
97 Here are Stas Sergeev's changes:
98
99    1. The Space Thingy can be shoved, if you ram it, and can fire back if 
100       fired upon. (Now controlled by OPTION_THINGY and turned off if game 
101       type is "plain" or "almy".)
102
103    2. When you are docked, base covers you with an almost invincible shield. 
104       (A commander can still ram you, or a Romulan can destroy the base,
105       or a SCom can even succeed with direct attack IIRC, but this rarely 
106       happens.)  (Now controlled by OPTION_BASE and turned off if game 
107       type is "plain" or "almy".)
108
109    3. Ramming a black hole is no longer instant death.  There is a
110       chance you might get timewarped instead. (Now controlled by 
111       OPTION_BLKHOLE and turned off if game type is "plain" or "almy".)
112
113    4. The Tholian can be hit with phasers.
114
115    5. SCom can't escape from you if no more enemies remain 
116       (without this, chasing SCom can take an eternity).
117
118    6. Probe target you enter is now the destination quadrant. Before I don't 
119       remember what it was, but it was something I had difficulty using.
120
121    7. Secret password is now autogenerated.
122
123    8. "Plaque" is adjusted for A4 paper :-)
124
125    9. Phasers now tells you how much energy needed, but only if the computer 
126       is alive.
127
128    10. Planets are auto-scanned when you enter the quadrant.
129
130    11. Mining or using crystals in presense of enemy now yields an attack.
131        There are other minor adjustments to what yields an attack
132        and what does not.
133
134    12. "freeze" command reverts to "save", most people will understand this
135        better anyway. (SST2K recognizes both.)
136
137    13. Screen-oriented interface, with sensor scans always up.  (SST2K
138        supports both screen-oriented and TTY modes.)
139
140 Eric Raymond's changes:
141
142 Mainly, I translated this C code out of FORTRAN into C -- created #defines
143 for a lot of magic numbers and refactored the heck out of it.
144
145    1. "sos" and "call" becomes "mayday", "freeze" and "save" are both good.
146
147    2. Status report now indicates when dilithium crystals are on board.
148
149    3. Per Dave Matuszek's remarks, Thingy state is never saved across games.
150
151    4. Added game option selection so you can play a close (but not bug-for-
152       bug identical) approximation of older versions.
153 */
154
155 /* the input queue */
156 static char line[128], *linep = line;
157
158 static struct 
159 {
160     char *name;
161     int value;
162     unsigned long option;
163 }
164 commands[] = {
165 #define SRSCAN  0
166         {"SRSCAN",      SRSCAN,         OPTION_TTY},
167 #define STATUS  1
168         {"STATUS",      STATUS,         OPTION_TTY},
169 #define REQUEST 2
170         {"REQUEST",     REQUEST,        OPTION_TTY},
171 #define LRSCAN  3
172         {"LRSCAN",      LRSCAN,         OPTION_TTY},
173 #define PHASERS 4
174         {"PHASERS",     PHASERS,        0},
175 #define TORPEDO 5
176         {"TORPEDO",     TORPEDO,        0},
177         {"PHOTONS",     TORPEDO,        0},
178 #define MOVE    6
179         {"MOVE",        MOVE,           0},
180 #define SHIELDS 7
181         {"SHIELDS",     SHIELDS,        0},
182 #define DOCK    8
183         {"DOCK",        DOCK,           0},
184 #define DAMAGES 9
185         {"DAMAGES",     DAMAGES,        0},
186 #define CHART   10
187         {"CHART",       CHART,          0},
188 #define IMPULSE 11
189         {"IMPULSE",     IMPULSE,        0},
190 #define REST    12
191         {"REST",        REST,           0},
192 #define WARP    13
193         {"WARP",        WARP,           0},
194 #define SCORE   14
195         {"SCORE",       SCORE,          0},
196 #define SENSORS 15
197         {"SENSORS",     SENSORS,        OPTION_PLANETS},
198 #define ORBIT   16
199         {"ORBIT",       ORBIT,          OPTION_PLANETS},
200 #define TRANSPORT       17
201         {"TRANSPORT",   TRANSPORT,      OPTION_PLANETS},
202 #define MINE    18
203         {"MINE",        MINE,           OPTION_PLANETS},
204 #define CRYSTALS        19
205         {"CRYSTALS",    CRYSTALS,       OPTION_PLANETS},
206 #define SHUTTLE 20
207         {"SHUTTLE",     SHUTTLE,        OPTION_PLANETS},
208 #define PLANETS 21
209         {"PLANETS",     PLANETS,        OPTION_PLANETS},
210 #define REPORT  22
211         {"REPORT",      REPORT,         0},
212 #define COMPUTER        23
213         {"COMPUTER",    COMPUTER,       0},
214 #define COMMANDS        24
215         {"COMMANDS",    COMMANDS,       0},
216 #define EMEXIT  25
217         {"EMEXIT",      EMEXIT,         0},
218 #define PROBE   26
219         {"PROBE",       PROBE,          OPTION_PROBE},
220 #define SAVE    27
221         {"SAVE",        SAVE,           0},
222         {"FREEZE",      SAVE,           0},
223 #define ABANDON 28
224         {"ABANDON",     ABANDON,        0},
225 #define DESTRUCT        29
226         {"DESTRUCT",    DESTRUCT,       0},
227 #define DEATHRAY        30
228         {"DEATHRAY",    DEATHRAY,       0},
229 #define DEBUGCMD        31
230         {"DEBUG",       DEBUGCMD,       0},
231 #define MAYDAY  32
232         {"MAYDAY",      MAYDAY,         0},
233         //{"SOS",               MAYDAY,         0},
234         //{"CALL",      MAYDAY,         0},
235 #define QUIT    33
236         {"QUIT",        QUIT,           0},
237 #define HELP    34
238         {"HELP",        HELP,           0},
239 };
240
241 #define NUMCOMMANDS     sizeof(commands)/sizeof(commands[0])
242 #define ACCEPT(i)       (!commands[i].option || (commands[i].option & game.options))
243
244 static void listCommands(void) {
245     int i, k = 0;
246     proutn("LEGAL COMMANDS ARE:");
247     for (i = 0; i < NUMCOMMANDS; i++) {
248         if (!ACCEPT(i))
249             continue;
250         if (k % 5 == 0)
251             skip(1);
252         proutn("%-12s ", commands[i].name); 
253         k++;
254     }
255     skip(1);
256 }
257
258 static void helpme(void) 
259 {
260     int i, j;
261     char cmdbuf[32], *cp;
262     char linebuf[132];
263     FILE *fp;
264     /* Give help on commands */
265     int key;
266     key = scan();
267     while (TRUE) {
268         if (key == IHEOL) {
269             setwnd(prompt_window);
270             proutn("Help on what command? ");
271             key = scan();
272         }
273         setwnd(message_window);
274         if (key == IHEOL) return;
275         for (i = 0; i < NUMCOMMANDS; i++) {
276             if (ACCEPT(i) && strcasecmp(commands[i].name, citem)==0) {
277                 i = commands[i].value;
278                 break;
279             }
280         }
281         if (i != NUMCOMMANDS) break;
282         skip(1);
283         prout("Valid commands:");
284         listCommands();
285         key = IHEOL;
286         chew();
287         skip(1);
288     }
289     if (i == COMMANDS) {
290         strcpy(cmdbuf, " ABBREV");
291     }
292     else {
293         for (j = 0; commands[i].name[j]; j++)
294             cmdbuf[j] = toupper(commands[i].name[j]);
295         cmdbuf[j] = '\0';
296     }
297     fp = fopen(SSTDOC, "r");
298     if (fp == NULL) {
299         prout("Spock-  \"Captain, that information is missing from the");
300         prout("   computer.\"");
301         /*
302          * This used to continue: "You need to find SST.DOC and put 
303          * it in the current directory."
304          */
305         return;
306     }
307     for (;;) {
308         if (fgets(linebuf, sizeof(linebuf), fp) == NULL) {
309             prout("Spock- \"Captain, there is no information on that command.\"");
310             fclose(fp);
311             return;
312         }
313         if (linebuf[0] == '%' && linebuf[1] == '%'&& linebuf[2] == ' ') {
314             for (cp = linebuf+3; isspace(*cp); cp++)
315                 continue;
316             linebuf[strlen(linebuf)-1] = '\0';
317             if (strcasecmp(cp, cmdbuf) == 0)
318                 break;
319         }
320     }
321
322     skip(1);
323     prout("Spock- \"Captain, I've found the following information:\"");
324     skip(1);
325
326     while (fgets(linebuf, sizeof(linebuf),fp)) {
327         if (strstr(linebuf, "******"))
328             break;
329         proutn(linebuf);
330     }
331     fclose(fp);
332 }
333
334 void enqueue(char *s) 
335 {
336     strcpy(line, s);
337 }
338
339 static void makemoves(void) 
340 {
341     int i, v = 0, hitme;
342     clrscr();
343     setwnd(message_window);
344     while (TRUE) { /* command loop */
345         drawmaps(1);
346         while (TRUE)  { /* get a command */
347             hitme = FALSE;
348             justin = 0;
349             Time = 0.0;
350             i = -1;
351             chew();
352             setwnd(prompt_window);
353             clrscr();
354             proutn("COMMAND> ");
355             if (scan() == IHEOL) {
356                 makechart();
357                 continue;
358             }
359             ididit=0;
360             clrscr();
361             setwnd(message_window);
362             clrscr();
363             for (i=0; i < ABANDON; i++)
364                 if (ACCEPT(i) && isit(commands[i].name)) {
365                     v = commands[i].value;
366                     break;
367                 }
368             if (i < ABANDON && (!commands[i].option || (commands[i].option & game.options))) 
369                 break;
370             for (; i < NUMCOMMANDS; i++)
371                 if (ACCEPT(i) && strcasecmp(commands[i].name, citem) == 0) {
372                     v = commands[i].value;
373                     break;
374                 }
375             if (i < NUMCOMMANDS && (!commands[i].option || (commands[i].option & game.options))) 
376                 break;
377             listCommands();
378         }
379         commandhook(commands[i].name, TRUE);
380         switch (v) { /* command switch */
381         case SRSCAN:                 // srscan
382             srscan(SCAN_FULL);
383             break;
384         case STATUS:                 // status
385             srscan(SCAN_STATUS);
386             break;
387         case REQUEST:                   // status request 
388             srscan(SCAN_REQUEST);
389             break;
390         case LRSCAN:                    // lrscan
391             lrscan();
392             break;
393         case PHASERS:                   // phasers
394             phasers();
395             if (ididit) hitme = TRUE;
396             break;
397         case TORPEDO:                   // photons
398             photon();
399             if (ididit) hitme = TRUE;
400             break;
401         case MOVE:                      // move
402             warp(1);
403             break;
404         case SHIELDS:                   // shields
405             doshield(1);
406             if (ididit) {
407                 hitme=TRUE;
408                 shldchg = 0;
409             }
410             break;
411         case DOCK:                      // dock
412             dock(1);
413             if (ididit) attack(0);
414             break;
415         case DAMAGES:                   // damages
416             dreprt();
417             break;
418         case CHART:                     // chart
419             chart(0);
420             break;
421         case IMPULSE:                   // impulse
422             impuls();
423             break;
424         case REST:                      // rest
425             wait();
426             if (ididit) hitme = TRUE;
427             break;
428         case WARP:                      // warp
429             setwrp();
430             break;
431         case SCORE:                     // score
432             score();
433             break;
434         case SENSORS:                   // sensors
435             sensor();
436             break;
437         case ORBIT:                     // orbit
438             orbit();
439             if (ididit) hitme = TRUE;
440             break;
441         case TRANSPORT:                 // transport "beam"
442             beam();
443             break;
444         case MINE:                      // mine
445             mine();
446             if (ididit) hitme = TRUE;
447             break;
448         case CRYSTALS:                  // crystals
449             usecrystals();
450             if (ididit) hitme = TRUE;
451             break;
452         case SHUTTLE:                   // shuttle
453             shuttle();
454             if (ididit) hitme = TRUE;
455             break;
456         case PLANETS:                   // Planet list
457             preport();
458             break;
459         case REPORT:                    // Game Report 
460             report();
461             break;
462         case COMPUTER:                  // use COMPUTER!
463             eta();
464             break;
465         case COMMANDS:
466             listCommands();
467             break;
468         case EMEXIT:                    // Emergency exit
469             clrscr();                   // Hide screen
470             freeze(TRUE);               // forced save
471             exit(1);                    // And quick exit
472             break;
473         case PROBE:
474             probe();                    // Launch probe
475             if (ididit) hitme = TRUE;
476             break;
477         case ABANDON:                   // Abandon Ship
478             abandn();
479             break;
480         case DESTRUCT:                  // Self Destruct
481             dstrct();
482             break;
483         case SAVE:                      // Save Game
484             freeze(FALSE);
485             clrscr();
486             if (skill > SKILL_GOOD)
487                 prout("WARNING--Saved games produce no plaques!");
488             break;
489         case DEATHRAY:                  // Try a desparation measure
490             deathray();
491             if (ididit) hitme = TRUE;
492             break;
493         case DEBUGCMD:                  // What do we want for debug???
494 #ifdef DEBUG
495             debugme();
496 #endif
497             break;
498         case MAYDAY:                    // Call for help
499             help();
500             if (ididit) hitme = TRUE;
501             break;
502         case QUIT:
503             alldone = 1;                // quit the game
504 #ifdef DEBUG
505             if (idebug) score();
506 #endif
507             break;
508         case HELP:
509             helpme();   // get help
510             break;
511         }
512         commandhook(commands[i].name, FALSE);
513         for (;;) {
514             if (alldone) break;         // Game has ended
515 #ifdef DEBUG
516             if (idebug) prout("2500");
517 #endif
518             if (Time != 0.0) {
519                 events();
520                 if (alldone) break;     // Events did us in
521             }
522             if (game.state.galaxy[quadx][quady].supernova) { // Galaxy went Nova!
523                 atover(0);
524                 continue;
525             }
526             if (hitme && justin==0) {
527                 attack(2);
528                 if (alldone) break;
529                 if (game.state.galaxy[quadx][quady].supernova) {        // went NOVA! 
530                     atover(0);
531                     hitme = TRUE;
532                     continue;
533                 }
534             }
535             break;
536         }
537         if (alldone) break;
538     }
539 }
540
541
542 int main(int argc, char **argv) 
543 {
544     int i, option;
545
546     game.options = OPTION_ALL &~ (OPTION_IOMODES | OPTION_SHOWME | OPTION_PLAIN | OPTION_ALMY);
547     if (getenv("TERM"))
548         game.options |= OPTION_CURSES | OPTION_SHOWME;
549     else
550         game.options |= OPTION_TTY;
551
552     while ((option = getopt(argc, argv, "t")) != -1) {
553         switch (option) {
554         case 't':
555             game.options |= OPTION_TTY;
556             game.options &=~ OPTION_CURSES;
557             break;
558         default:
559             fprintf(stderr, "usage: sst [-t] [startcommand...].\n");
560             exit(0);
561         }
562     }
563
564     randomize();
565     iostart();
566
567     line[0] = '\0';
568     for (i = optind; i < argc;  i++) {
569         strcat(line, argv[i]);
570         strcat(line, " ");
571     }
572     while (TRUE) { /* Play a game */
573         setwnd(fullscreen_window);
574 #ifdef DEBUG
575         prout("INITIAL OPTIONS: %0lx", game.options);
576 #endif /* DEBUG */
577         clrscr();
578         prelim();
579         setup(line[0] == '\0');
580         if (alldone) {
581             score();
582             alldone = 0;
583         }
584         else makemoves();
585         skip(1);
586         stars();
587         skip(1);
588
589         if (tourn && alldone) {
590             proutn("Do you want your score recorded?");
591             if (ja()) {
592                 chew2();
593                 freeze(FALSE);
594             }
595         }
596         proutn("Do you want to play again? ");
597         if (!ja()) break;
598     }
599     skip(1);
600     prout("May the Great Bird of the Galaxy roost upon your home planet.");
601     return 0;
602 }
603
604
605 void cramen(int i) 
606 {
607     /* return an enemy */
608     char *s;
609         
610     switch (i) {
611     case IHR: s = "Romulan"; break;
612     case IHK: s = "Klingon"; break;
613     case IHC: s = "Commander"; break;
614     case IHS: s = "Super-commander"; break;
615     case IHSTAR: s = "Star"; break;
616     case IHP: s = "Planet"; break;
617     case IHB: s = "Starbase"; break;
618     case IHBLANK: s = "Black hole"; break;
619     case IHT: s = "Tholian"; break;
620     case IHWEB: s = "Tholian web"; break;
621     case IHQUEST: s = "Stranger"; break;
622     default: s = "Unknown??"; break;
623     }
624     proutn(s);
625 }
626
627 char *cramlc(enum loctype key, int x, int y)
628 {
629     static char buf[32];
630     buf[0] = '\0';
631     if (key == quadrant) strcpy(buf, "Quadrant ");
632     else if (key == sector) strcpy(buf, "Sector ");
633     sprintf(buf+strlen(buf), "%d - %d", x, y);
634     return buf;
635 }
636
637 void crmena(int i, int enemy, int key, int x, int y) 
638 {
639     if (i == 1) proutn("***");
640     cramen(enemy);
641     proutn(" at ");
642     proutn(cramlc(key, x, y));
643 }
644
645 void crmshp(void) 
646 {
647     char *s;
648     switch (ship) {
649     case IHE: s = "Enterprise"; break;
650     case IHF: s = "Faerie Queene"; break;
651     default:  s = "Ship???"; break;
652     }
653     proutn(s);
654 }
655
656 void stars(void) 
657 {
658     prouts("******************************************************");
659     skip(1);
660 }
661
662 double expran(double avrage) 
663 {
664     return -avrage*log(1e-7 + Rand());
665 }
666
667 double Rand(void) {
668         return rand()/(1.0 + (double)RAND_MAX);
669 }
670
671 void iran(int size, int *i, int *j) 
672 {
673     *i = Rand()*(size*1.0) + 1.0;
674     *j = Rand()*(size*1.0) + 1.0;
675 }
676
677 void chew(void)
678 {
679     linep = line;
680     *linep = 0;
681 }
682
683 void chew2(void) 
684 {
685     /* return IHEOL next time */
686     linep = line+1;
687     *linep = 0;
688 }
689
690 int scan(void) 
691 {
692     int i;
693     char *cp;
694
695     // Init result
696     aaitem = 0.0;
697     *citem = 0;
698
699     // Read a line if nothing here
700     if (*linep == 0) {
701         if (linep != line) {
702             chew();
703             return IHEOL;
704         }
705         cgetline(line, sizeof(line));
706         fflush(stdin);
707         if (curwnd==prompt_window){
708             clrscr();
709             setwnd(message_window);
710             clrscr();
711         }
712         linep = line;
713     }
714     // Skip leading white space
715     while (*linep == ' ') linep++;
716     // Nothing left
717     if (*linep == 0) {
718         chew();
719         return IHEOL;
720     }
721     if (isdigit(*linep) || *linep=='+' || *linep=='-' || *linep=='.') {
722         // treat as a number
723         i = 0;
724         if (sscanf(linep, "%lf%n", &aaitem, &i) < 1) {
725             linep = line; // Invalid numbers are ignored
726             *linep = 0;
727             return IHEOL;
728         }
729         else {
730             // skip to end
731             linep += i;
732             return IHREAL;
733         }
734     }
735     // Treat as alpha
736     cp = citem;
737     while (*linep && *linep!=' ') {
738         if ((cp - citem) < 9) *cp++ = tolower(*linep);
739         linep++;
740     }
741     *cp = 0;
742     return IHALPHA;
743 }
744
745 int ja(void) 
746 {
747     chew();
748     while (TRUE) {
749         scan();
750         chew();
751         if (*citem == 'y') return TRUE;
752         if (*citem == 'n') return FALSE;
753         proutn("Please answer with \"Y\" or \"N\": ");
754     }
755 }
756
757 void huh(void) 
758 {
759     chew();
760     skip(1);
761     prout("Beg your pardon, Captain?");
762 }
763
764 int isit(char *s) 
765 {
766     /* New function -- compares s to scanned citem and returns true if it
767        matches to the length of s */
768
769     return strncasecmp(s, citem, max(1, strlen(citem))) == 0;
770
771 }
772
773 #ifdef DEBUG
774 void debugme(void) 
775 {
776     proutn("Reset levels? ");
777     if (ja() != 0) {
778         if (energy < inenrg) energy = inenrg;
779         shield = inshld;
780         torps = intorps;
781         lsupres = inlsr;
782     }
783     proutn("Reset damage? ");
784     if (ja() != 0) {
785         int i;
786         for (i=0; i < NDEVICES; i++) 
787             if (game.damage[i] > 0.0) 
788                 game.damage[i] = 0.0;
789     }
790     proutn("Toggle idebug? ");
791     if (ja() != 0) {
792         idebug = !idebug;
793         if (idebug) prout("Debug output ON");
794         else prout("Debug output OFF");
795     }
796     proutn("Cause selective damage? ");
797     if (ja() != 0) {
798         int i, key;
799         for (i=0; i < NDEVICES; i++) {
800             proutn("Kill ");
801             proutn(device[i]);
802             proutn("? ");
803             chew();
804             key = scan();
805             if (key == IHALPHA &&  isit("y")) {
806                 game.damage[i] = 10.0;
807             }
808         }
809     }
810     proutn("Examine/change events? ");
811     if (ja() != 0) {
812         int i;
813         for (i = 1; i < NEVENTS; i++) {
814             int key;
815             if (game.future[i] == FOREVER) continue;
816             switch (i) {
817             case FSNOVA:  proutn("Supernova       "); break;
818             case FTBEAM:  proutn("T Beam          "); break;
819             case FSNAP:   proutn("Snapshot        "); break;
820             case FBATTAK: proutn("Base Attack     "); break;
821             case FCDBAS:  proutn("Base Destroy    "); break;
822             case FSCMOVE: proutn("SC Move         "); break;
823             case FSCDBAS: proutn("SC Base Destroy "); break;
824             }
825             proutn("%.2f", game.future[i]-game.state.date);
826             chew();
827             proutn("  ?");
828             key = scan();
829             if (key == IHREAL) {
830                 game.future[i] = game.state.date + aaitem;
831             }
832         }
833         chew();
834     }
835     proutn("Induce supernova here? ");
836     if (ja() != 0) {
837         game.state.galaxy[quadx][quady].supernova = TRUE;
838         atover(1);
839     }
840 }
841 #endif