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