Introduce 'coord' data structure, an (x,y) tuple.
[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).  Right now this is just color, but
159       eventually we'll fold in BSD-Trek-like logic for Klingons to attack
160       and enslave inhabited worlds.
161 */
162
163 /* the input queue */
164 static char line[128], *linep = line;
165
166 struct game game;
167 coord thing;
168 int iqhere, iqengry;
169 int iscore, iskill; // Common PLAQ
170 double aaitem;
171 double perdate;
172 char citem[10];
173
174 char *device[NDEVICES] = {
175         "S. R. Sensors",
176         "L. R. Sensors",
177         "Phasers",
178         "Photon Tubes",
179         "Life Support",
180         "Warp Engines",
181         "Impulse Engines",
182         "Shields",
183         "Subspace Radio",
184         "Shuttle Craft",
185         "Computer",
186         "Transporter",
187         "Shield Control",
188         "Death Ray",
189         "D. S. Probe"};                                                                 
190
191 static struct 
192 {
193     char *name;
194     int value;
195     unsigned long option;
196 }
197
198 commands[] = {
199 #define SRSCAN  0
200         {"SRSCAN",      SRSCAN,         OPTION_TTY},
201 #define STATUS  1
202         {"STATUS",      STATUS,         OPTION_TTY},
203 #define REQUEST 2
204         {"REQUEST",     REQUEST,        OPTION_TTY},
205 #define LRSCAN  3
206         {"LRSCAN",      LRSCAN,         OPTION_TTY},
207 #define PHASERS 4
208         {"PHASERS",     PHASERS,        0},
209 #define TORPEDO 5
210         {"TORPEDO",     TORPEDO,        0},
211         {"PHOTONS",     TORPEDO,        0},
212 #define MOVE    7
213         {"MOVE",        MOVE,           0},
214 #define SHIELDS 8
215         {"SHIELDS",     SHIELDS,        0},
216 #define DOCK    9
217         {"DOCK",        DOCK,           0},
218 #define DAMAGES 10
219         {"DAMAGES",     DAMAGES,        0},
220 #define CHART   11
221         {"CHART",       CHART,          0},
222 #define IMPULSE 12
223         {"IMPULSE",     IMPULSE,        0},
224 #define REST    13
225         {"REST",        REST,           0},
226 #define WARP    14
227         {"WARP",        WARP,           0},
228 #define SCORE   15
229         {"SCORE",       SCORE,          0},
230 #define SENSORS 16
231         {"SENSORS",     SENSORS,        OPTION_PLANETS},
232 #define ORBIT   17
233         {"ORBIT",       ORBIT,          OPTION_PLANETS},
234 #define TRANSPORT       18
235         {"TRANSPORT",   TRANSPORT,      OPTION_PLANETS},
236 #define MINE    19
237         {"MINE",        MINE,           OPTION_PLANETS},
238 #define CRYSTALS        20
239         {"CRYSTALS",    CRYSTALS,       OPTION_PLANETS},
240 #define SHUTTLE 21
241         {"SHUTTLE",     SHUTTLE,        OPTION_PLANETS},
242 #define PLANETS 22
243         {"PLANETS",     PLANETS,        OPTION_PLANETS},
244 #define REPORT  23
245         {"REPORT",      REPORT,         0},
246 #define COMPUTER        24
247         {"COMPUTER",    COMPUTER,       0},
248 #define COMMANDS        25
249         {"COMMANDS",    COMMANDS,       0},
250 #define EMEXIT  26
251         {"EMEXIT",      EMEXIT,         0},
252 #define PROBE   27
253         {"PROBE",       PROBE,          OPTION_PROBE},
254 #define SAVE    28
255         {"SAVE",        SAVE,           0},
256         {"FREEZE",      SAVE,           0},
257 #define ABANDON 30
258         {"ABANDON",     ABANDON,        0},
259 #define DESTRUCT        31
260         {"DESTRUCT",    DESTRUCT,       0},
261 #define DEATHRAY        32
262         {"DEATHRAY",    DEATHRAY,       0},
263 #define DEBUGCMD        33
264         {"DEBUG",       DEBUGCMD,       0},
265 #define MAYDAY  34
266         {"MAYDAY",      MAYDAY,         0},
267         //{"SOS",               MAYDAY,         0},
268         //{"CALL",      MAYDAY,         0},
269 #define QUIT    35
270         {"QUIT",        QUIT,           0},
271 #define HELP    36
272         {"HELP",        HELP,           0},
273 };
274
275 #define NUMCOMMANDS     sizeof(commands)/sizeof(commands[0])
276 #define ACCEPT(i)       (!commands[i].option || (commands[i].option & game.options))
277
278 static void listCommands(void) {
279     int i, k = 0;
280     proutn("LEGAL COMMANDS ARE:");
281     for (i = 0; i < NUMCOMMANDS; i++) {
282         if (!ACCEPT(i))
283             continue;
284         if (k % 5 == 0)
285             skip(1);
286         proutn("%-12s ", commands[i].name); 
287         k++;
288     }
289     skip(1);
290 }
291
292 static void helpme(void) 
293 {
294     int i, j;
295     char cmdbuf[32], *cp;
296     char linebuf[132];
297     FILE *fp;
298     /* Give help on commands */
299     int key;
300     key = scan();
301     while (TRUE) {
302         if (key == IHEOL) {
303             setwnd(prompt_window);
304             proutn("Help on what command? ");
305             key = scan();
306         }
307         setwnd(message_window);
308         if (key == IHEOL) return;
309         for (i = 0; i < NUMCOMMANDS; i++) {
310             if (ACCEPT(i) && strcasecmp(commands[i].name, citem)==0) {
311                 i = commands[i].value;
312                 break;
313             }
314         }
315         if (i != NUMCOMMANDS) break;
316         skip(1);
317         prout("Valid commands:");
318         listCommands();
319         key = IHEOL;
320         chew();
321         skip(1);
322     }
323     if (i == COMMANDS) {
324         strcpy(cmdbuf, " ABBREV");
325     }
326     else {
327         for (j = 0; commands[i].name[j]; j++)
328             cmdbuf[j] = toupper(commands[i].name[j]);
329         cmdbuf[j] = '\0';
330     }
331     fp = fopen(SSTDOC, "r");
332     if (fp == NULL)
333         fp = fopen(DOC_NAME, "r");
334     if (fp == NULL) {
335         prout("Spock-  \"Captain, that information is missing from the");
336         prout("   computer. You need to find "DOC_NAME" and put it in the");
337         prout("   current directory or to "SSTDOC".\"");
338         /*
339          * This used to continue: "You need to find SST.DOC and put 
340          * it in the current directory."
341          */
342         return;
343     }
344     for (;;) {
345         if (fgets(linebuf, sizeof(linebuf), fp) == NULL) {
346             prout("Spock- \"Captain, there is no information on that command.\"");
347             fclose(fp);
348             return;
349         }
350         if (linebuf[0] == '%' && linebuf[1] == '%'&& linebuf[2] == ' ') {
351             for (cp = linebuf+3; isspace(*cp); cp++)
352                 continue;
353             linebuf[strlen(linebuf)-1] = '\0';
354             if (strcasecmp(cp, cmdbuf) == 0)
355                 break;
356         }
357     }
358
359     skip(1);
360     prout("Spock- \"Captain, I've found the following information:\"");
361     skip(1);
362
363     while (fgets(linebuf, sizeof(linebuf),fp)) {
364         if (strstr(linebuf, "******"))
365             break;
366         proutn(linebuf);
367     }
368     fclose(fp);
369 }
370
371 void enqueue(char *s) 
372 {
373     strcpy(line, s);
374 }
375
376 static void makemoves(void) 
377 {
378     int i, v = 0, hitme;
379     clrscr();
380     setwnd(message_window);
381     while (TRUE) { /* command loop */
382         drawmaps(1);
383         while (TRUE)  { /* get a command */
384             hitme = FALSE;
385             game.justin = 0;
386             game.optime = 0.0;
387             i = -1;
388             chew();
389             setwnd(prompt_window);
390             clrscr();
391             proutn("COMMAND> ");
392             if (scan() == IHEOL) {
393                 makechart();
394                 continue;
395             }
396             game.ididit=0;
397             clrscr();
398             setwnd(message_window);
399             clrscr();
400             for (i=0; i < ABANDON; i++)
401                 if (ACCEPT(i) && isit(commands[i].name)) {
402                     v = commands[i].value;
403                     break;
404                 }
405             if (i < ABANDON && (!commands[i].option || (commands[i].option & game.options))) 
406                 break;
407             for (; i < NUMCOMMANDS; i++)
408                 if (ACCEPT(i) && strcasecmp(commands[i].name, citem) == 0) {
409                     v = commands[i].value;
410                     break;
411                 }
412             if (i < NUMCOMMANDS && (!commands[i].option || (commands[i].option & game.options))) 
413                 break;
414             listCommands();
415         }
416         commandhook(commands[i].name, TRUE);
417         switch (v) { /* command switch */
418         case SRSCAN:                 // srscan
419             srscan(SCAN_FULL);
420             break;
421         case STATUS:                 // status
422             srscan(SCAN_STATUS);
423             break;
424         case REQUEST:                   // status request 
425             srscan(SCAN_REQUEST);
426             break;
427         case LRSCAN:                    // lrscan
428             lrscan();
429             break;
430         case PHASERS:                   // phasers
431             phasers();
432             if (game.ididit) hitme = TRUE;
433             break;
434         case TORPEDO:                   // photons
435             photon();
436             if (game.ididit) hitme = TRUE;
437             break;
438         case MOVE:                      // move
439             warp(1);
440             break;
441         case SHIELDS:                   // shields
442             doshield(1);
443             if (game.ididit) {
444                 hitme=TRUE;
445                 game.shldchg = 0;
446             }
447             break;
448         case DOCK:                      // dock
449             dock(1);
450             if (game.ididit) attack(0);
451             break;
452         case DAMAGES:                   // damages
453             dreprt();
454             break;
455         case CHART:                     // chart
456             chart(0);
457             break;
458         case IMPULSE:                   // impulse
459             impuls();
460             break;
461         case REST:                      // rest
462             wait();
463             if (game.ididit) hitme = TRUE;
464             break;
465         case WARP:                      // warp
466             setwrp();
467             break;
468         case SCORE:                     // score
469             score();
470             break;
471         case SENSORS:                   // sensors
472             sensor();
473             break;
474         case ORBIT:                     // orbit
475             orbit();
476             if (game.ididit) hitme = TRUE;
477             break;
478         case TRANSPORT:                 // transport "beam"
479             beam();
480             break;
481         case MINE:                      // mine
482             mine();
483             if (game.ididit) hitme = TRUE;
484             break;
485         case CRYSTALS:                  // crystals
486             usecrystals();
487             if (game.ididit) hitme = TRUE;
488             break;
489         case SHUTTLE:                   // shuttle
490             shuttle();
491             if (game.ididit) hitme = TRUE;
492             break;
493         case PLANETS:                   // Planet list
494             preport();
495             break;
496         case REPORT:                    // Game Report 
497             report();
498             break;
499         case COMPUTER:                  // use COMPUTER!
500             eta();
501             break;
502         case COMMANDS:
503             listCommands();
504             break;
505         case EMEXIT:                    // Emergency exit
506             clrscr();                   // Hide screen
507             freeze(TRUE);               // forced save
508             exit(1);                    // And quick exit
509             break;
510         case PROBE:
511             probe();                    // Launch probe
512             if (game.ididit) hitme = TRUE;
513             break;
514         case ABANDON:                   // Abandon Ship
515             abandn();
516             break;
517         case DESTRUCT:                  // Self Destruct
518             dstrct();
519             break;
520         case SAVE:                      // Save Game
521             freeze(FALSE);
522             clrscr();
523             if (game.skill > SKILL_GOOD)
524                 prout("WARNING--Saved games produce no plaques!");
525             break;
526         case DEATHRAY:                  // Try a desparation measure
527             deathray();
528             if (game.ididit) hitme = TRUE;
529             break;
530         case DEBUGCMD:                  // What do we want for debug???
531 #ifdef DEBUG
532             debugme();
533 #endif
534             break;
535         case MAYDAY:                    // Call for help
536             mayday();
537             if (game.ididit) hitme = TRUE;
538             break;
539         case QUIT:
540             game.alldone = 1;           // quit the game
541 #ifdef DEBUG
542             if (game.idebug) score();
543 #endif
544             break;
545         case HELP:
546             helpme();   // get help
547             break;
548         }
549         commandhook(commands[i].name, FALSE);
550         for (;;) {
551             if (game.alldone) break;            // Game has ended
552 #ifdef DEBUG
553             if (game.idebug) prout("2500");
554 #endif
555             if (game.optime != 0.0) {
556                 events();
557                 if (game.alldone) break;        // Events did us in
558             }
559             if (game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova) { // Galaxy went Nova!
560                 atover(0);
561                 continue;
562             }
563             if (hitme && game.justin==0) {
564                 attack(2);
565                 if (game.alldone) break;
566                 if (game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova) {    // went NOVA! 
567                     atover(0);
568                     hitme = TRUE;
569                     continue;
570                 }
571             }
572             break;
573         }
574         if (game.alldone) break;
575     }
576 }
577
578
579 int main(int argc, char **argv) 
580 {
581     int i, option;
582
583     game.options = OPTION_ALL &~ (OPTION_IOMODES | OPTION_SHOWME | OPTION_PLAIN | OPTION_ALMY);
584     if (getenv("TERM"))
585         game.options |= OPTION_CURSES | OPTION_SHOWME;
586     else
587         game.options |= OPTION_TTY;
588
589     while ((option = getopt(argc, argv, "t")) != -1) {
590         switch (option) {
591         case 't':
592             game.options |= OPTION_TTY;
593             game.options &=~ OPTION_CURSES;
594             break;
595         default:
596             fprintf(stderr, "usage: sst [-t] [startcommand...].\n");
597             exit(0);
598         }
599     }
600
601     randomize();
602     iostart();
603
604     line[0] = '\0';
605     for (i = optind; i < argc;  i++) {
606         strcat(line, argv[i]);
607         strcat(line, " ");
608     }
609     while (TRUE) { /* Play a game */
610         setwnd(fullscreen_window);
611 #ifdef DEBUG
612         prout("INITIAL OPTIONS: %0lx", game.options);
613 #endif /* DEBUG */
614         clrscr();
615         prelim();
616         setup(line[0] == '\0');
617         if (game.alldone) {
618             score();
619             game.alldone = 0;
620         }
621         else makemoves();
622         skip(1);
623         stars();
624         skip(1);
625
626         if (game.tourn && game.alldone) {
627             proutn("Do you want your score recorded?");
628             if (ja()) {
629                 chew2();
630                 freeze(FALSE);
631             }
632         }
633         proutn("Do you want to play again? ");
634         if (!ja()) break;
635     }
636     skip(1);
637     prout("May the Great Bird of the Galaxy roost upon your home planet.");
638     return 0;
639 }
640
641
642 void cramen(int i) 
643 {
644     /* return an enemy */
645     char *s;
646         
647     switch (i) {
648     case IHR: s = "Romulan"; break;
649     case IHK: s = "Klingon"; break;
650     case IHC: s = "Commander"; break;
651     case IHS: s = "Super-commander"; break;
652     case IHSTAR: s = "Star"; break;
653     case IHP: s = "Planet"; break;
654     case IHB: s = "Starbase"; break;
655     case IHBLANK: s = "Black hole"; break;
656     case IHT: s = "Tholian"; break;
657     case IHWEB: s = "Tholian web"; break;
658     case IHQUEST: s = "Stranger"; break;
659     default: s = "Unknown??"; break;
660     }
661     proutn(s);
662 }
663
664 char *cramlc(enum loctype key, coord w)
665 {
666     static char buf[32];
667     buf[0] = '\0';
668     if (key == quadrant) strcpy(buf, "Quadrant ");
669     else if (key == sector) strcpy(buf, "Sector ");
670     sprintf(buf+strlen(buf), "%d - %d", w.x, w.y);
671     return buf;
672 }
673
674 void crmena(int i, int enemy, int key, coord w) 
675 {
676     if (i == 1) proutn("***");
677     cramen(enemy);
678     proutn(" at ");
679     proutn(cramlc(key, w));
680 }
681
682 void crmshp(void) 
683 {
684     char *s;
685     switch (game.ship) {
686     case IHE: s = "Enterprise"; break;
687     case IHF: s = "Faerie Queene"; break;
688     default:  s = "Ship???"; break;
689     }
690     proutn(s);
691 }
692
693 void stars(void) 
694 {
695     prouts("******************************************************");
696     skip(1);
697 }
698
699 double expran(double avrage) 
700 {
701     return -avrage*log(1e-7 + Rand());
702 }
703
704 double Rand(void) {
705         return rand()/(1.0 + (double)RAND_MAX);
706 }
707
708 void iran(int size, int *i, int *j) 
709 {
710     *i = Rand()*(size*1.0) + 1.0;
711     *j = Rand()*(size*1.0) + 1.0;
712 }
713
714 void chew(void)
715 {
716     linep = line;
717     *linep = 0;
718 }
719
720 void chew2(void) 
721 {
722     /* return IHEOL next time */
723     linep = line+1;
724     *linep = 0;
725 }
726
727 int scan(void) 
728 {
729     int i;
730     char *cp;
731
732     // Init result
733     aaitem = 0.0;
734     *citem = 0;
735
736     // Read a line if nothing here
737     if (*linep == 0) {
738         if (linep != line) {
739             chew();
740             return IHEOL;
741         }
742         cgetline(line, sizeof(line));
743         fflush(stdin);
744         if (curwnd==prompt_window){
745             clrscr();
746             setwnd(message_window);
747             clrscr();
748         }
749         linep = line;
750     }
751     // Skip leading white space
752     while (*linep == ' ') linep++;
753     // Nothing left
754     if (*linep == 0) {
755         chew();
756         return IHEOL;
757     }
758     if (isdigit(*linep) || *linep=='+' || *linep=='-' || *linep=='.') {
759         // treat as a number
760         i = 0;
761         if (sscanf(linep, "%lf%n", &aaitem, &i) < 1) {
762             linep = line; // Invalid numbers are ignored
763             *linep = 0;
764             return IHEOL;
765         }
766         else {
767             // skip to end
768             linep += i;
769             return IHREAL;
770         }
771     }
772     // Treat as alpha
773     cp = citem;
774     while (*linep && *linep!=' ') {
775         if ((cp - citem) < 9) *cp++ = tolower(*linep);
776         linep++;
777     }
778     *cp = 0;
779     return IHALPHA;
780 }
781
782 int ja(void) 
783 {
784     chew();
785     while (TRUE) {
786         scan();
787         chew();
788         if (*citem == 'y') return TRUE;
789         if (*citem == 'n') return FALSE;
790         proutn("Please answer with \"Y\" or \"N\": ");
791     }
792 }
793
794 void huh(void) 
795 {
796     chew();
797     skip(1);
798     prout("Beg your pardon, Captain?");
799 }
800
801 int isit(char *s) 
802 {
803     /* New function -- compares s to scanned citem and returns true if it
804        matches to the length of s */
805
806     return strncasecmp(s, citem, max(1, strlen(citem))) == 0;
807
808 }
809
810 #ifdef DEBUG
811 void debugme(void) 
812 {
813     proutn("Reset levels? ");
814     if (ja() != 0) {
815         if (energy < game.inenrg) energy = game.inenrg;
816         shield = game.inshld;
817         torps = game.intorps;
818         game.lsupres = game.inlsr;
819     }
820     proutn("Reset damage? ");
821     if (ja() != 0) {
822         int i;
823         for (i=0; i < NDEVICES; i++) 
824             if (game.damage[i] > 0.0) 
825                 game.damage[i] = 0.0;
826     }
827     proutn("Toggle game.idebug? ");
828     if (ja() != 0) {
829         game.idebug = !game.idebug;
830         if (game.idebug) prout("Debug output ON");
831         else prout("Debug output OFF");
832     }
833     proutn("Cause selective damage? ");
834     if (ja() != 0) {
835         int i, key;
836         for (i=0; i < NDEVICES; i++) {
837             proutn("Kill ");
838             proutn(device[i]);
839             proutn("? ");
840             chew();
841             key = scan();
842             if (key == IHALPHA &&  isit("y")) {
843                 game.damage[i] = 10.0;
844             }
845         }
846     }
847     proutn("Examine/change events? ");
848     if (ja() != 0) {
849         int i;
850         for (i = 1; i < NEVENTS; i++) {
851             int key;
852             if (!is_scheduled(i)) continue;
853             switch (i) {
854             case FSNOVA:  proutn("Supernova       "); break;
855             case FTBEAM:  proutn("T Beam          "); break;
856             case FSNAP:   proutn("Snapshot        "); break;
857             case FBATTAK: proutn("Base Attack     "); break;
858             case FCDBAS:  proutn("Base Destroy    "); break;
859             case FSCMOVE: proutn("SC Move         "); break;
860             case FSCDBAS: proutn("SC Base Destroy "); break;
861             }
862             proutn("%.2f", scheduled(i)-game.state.date);
863             chew();
864             proutn("  ?");
865             key = scan();
866             if (key == IHREAL) {
867                 schedule(i, aaitem);
868             }
869         }
870         chew();
871     }
872     proutn("Induce supernova here? ");
873     if (ja() != 0) {
874         game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova = TRUE;
875         atover(1);
876     }
877 }
878 #endif