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