More type cleanup.
[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     for(;;) {
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;
379     bool hitme;
380     clrscr();
381     setwnd(message_window);
382     for(;;) { /* command loop */
383         drawmaps(1);
384         for(;;)  { /* get a command */
385             hitme = false;
386             game.justin = 0;
387             game.optime = 0.0;
388             i = -1;
389             chew();
390             setwnd(prompt_window);
391             clrscr();
392             proutn("COMMAND> ");
393             if (scan() == IHEOL) {
394                 makechart();
395                 continue;
396             }
397             game.ididit=0;
398             clrscr();
399             setwnd(message_window);
400             clrscr();
401             for (i=0; i < ABANDON; i++)
402                 if (ACCEPT(i) && isit(commands[i].name)) {
403                     v = commands[i].value;
404                     break;
405                 }
406             if (i < ABANDON && (!commands[i].option || (commands[i].option & game.options))) 
407                 break;
408             for (; i < NUMCOMMANDS; i++)
409                 if (ACCEPT(i) && strcasecmp(commands[i].name, citem) == 0) {
410                     v = commands[i].value;
411                     break;
412                 }
413             if (i < NUMCOMMANDS && (!commands[i].option || (commands[i].option & game.options))) 
414                 break;
415             listCommands();
416         }
417         commandhook(commands[i].name, true);
418         switch (v) { /* command switch */
419         case SRSCAN:                 // srscan
420             srscan(SCAN_FULL);
421             break;
422         case STATUS:                 // status
423             srscan(SCAN_STATUS);
424             break;
425         case REQUEST:                   // status request 
426             srscan(SCAN_REQUEST);
427             break;
428         case LRSCAN:                    // lrscan
429             lrscan();
430             break;
431         case PHASERS:                   // phasers
432             phasers();
433             if (game.ididit) hitme = true;
434             break;
435         case TORPEDO:                   // photons
436             photon();
437             if (game.ididit) hitme = true;
438             break;
439         case MOVE:                      // move
440             warp(1);
441             break;
442         case SHIELDS:                   // shields
443             doshield(1);
444             if (game.ididit) {
445                 hitme=true;
446                 game.shldchg = 0;
447             }
448             break;
449         case DOCK:                      // dock
450             dock(1);
451             if (game.ididit) attack(0);
452             break;
453         case DAMAGES:                   // damages
454             dreprt();
455             break;
456         case CHART:                     // chart
457             chart(0);
458             break;
459         case IMPULSE:                   // impulse
460             impuls();
461             break;
462         case REST:                      // rest
463             wait();
464             if (game.ididit) hitme = true;
465             break;
466         case WARP:                      // warp
467             setwrp();
468             break;
469         case SCORE:                     // score
470             score();
471             break;
472         case SENSORS:                   // sensors
473             sensor();
474             break;
475         case ORBIT:                     // orbit
476             orbit();
477             if (game.ididit) hitme = true;
478             break;
479         case TRANSPORT:                 // transport "beam"
480             beam();
481             break;
482         case MINE:                      // mine
483             mine();
484             if (game.ididit) hitme = true;
485             break;
486         case CRYSTALS:                  // crystals
487             usecrystals();
488             if (game.ididit) hitme = true;
489             break;
490         case SHUTTLE:                   // shuttle
491             shuttle();
492             if (game.ididit) hitme = true;
493             break;
494         case PLANETS:                   // Planet list
495             preport();
496             break;
497         case REPORT:                    // Game Report 
498             report();
499             break;
500         case COMPUTER:                  // use COMPUTER!
501             eta();
502             break;
503         case COMMANDS:
504             listCommands();
505             break;
506         case EMEXIT:                    // Emergency exit
507             clrscr();                   // Hide screen
508             freeze(true);               // forced save
509             exit(1);                    // And quick exit
510             break;
511         case PROBE:
512             probe();                    // Launch probe
513             if (game.ididit) hitme = true;
514             break;
515         case ABANDON:                   // Abandon Ship
516             abandn();
517             break;
518         case DESTRUCT:                  // Self Destruct
519             dstrct();
520             break;
521         case SAVE:                      // Save Game
522             freeze(false);
523             clrscr();
524             if (game.skill > SKILL_GOOD)
525                 prout("WARNING--Saved games produce no plaques!");
526             break;
527         case DEATHRAY:                  // Try a desparation measure
528             deathray();
529             if (game.ididit) hitme = true;
530             break;
531         case DEBUGCMD:                  // What do we want for debug???
532 #ifdef DEBUG
533             debugme();
534 #endif
535             break;
536         case MAYDAY:                    // Call for help
537             mayday();
538             if (game.ididit) hitme = true;
539             break;
540         case QUIT:
541             game.alldone = 1;           // quit the game
542 #ifdef DEBUG
543             if (game.idebug) score();
544 #endif
545             break;
546         case HELP:
547             helpme();   // get help
548             break;
549         }
550         commandhook(commands[i].name, false);
551         for (;;) {
552             if (game.alldone) break;            // Game has ended
553 #ifdef DEBUG
554             if (game.idebug) prout("2500");
555 #endif
556             if (game.optime != 0.0) {
557                 events();
558                 if (game.alldone) break;        // Events did us in
559             }
560             if (game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova) { // Galaxy went Nova!
561                 atover(0);
562                 continue;
563             }
564             if (hitme && game.justin==0) {
565                 attack(2);
566                 if (game.alldone) break;
567                 if (game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova) {    // went NOVA! 
568                     atover(0);
569                     hitme = true;
570                     continue;
571                 }
572             }
573             break;
574         }
575         if (game.alldone) break;
576     }
577 }
578
579
580 int main(int argc, char **argv) 
581 {
582     int i, option;
583
584     game.options = OPTION_ALL &~ (OPTION_IOMODES | OPTION_SHOWME | OPTION_PLAIN | OPTION_ALMY);
585     if (getenv("TERM"))
586         game.options |= OPTION_CURSES | OPTION_SHOWME;
587     else
588         game.options |= OPTION_TTY;
589
590     while ((option = getopt(argc, argv, "t")) != -1) {
591         switch (option) {
592         case 't':
593             game.options |= OPTION_TTY;
594             game.options &=~ OPTION_CURSES;
595             break;
596         default:
597             fprintf(stderr, "usage: sst [-t] [startcommand...].\n");
598             exit(0);
599         }
600     }
601
602     randomize();
603     iostart();
604
605     line[0] = '\0';
606     for (i = optind; i < argc;  i++) {
607         strcat(line, argv[i]);
608         strcat(line, " ");
609     }
610     for(;;) { /* Play a game */
611         setwnd(fullscreen_window);
612 #ifdef DEBUG
613         prout("INITIAL OPTIONS: %0lx", game.options);
614 #endif /* DEBUG */
615         clrscr();
616         prelim();
617         setup(line[0] == '\0');
618         if (game.alldone) {
619             score();
620             game.alldone = 0;
621         }
622         else makemoves();
623         skip(1);
624         stars();
625         skip(1);
626
627         if (game.tourn && game.alldone) {
628             proutn("Do you want your score recorded?");
629             if (ja()) {
630                 chew2();
631                 freeze(false);
632             }
633         }
634         proutn("Do you want to play again? ");
635         if (!ja()) break;
636     }
637     skip(1);
638     prout("May the Great Bird of the Galaxy roost upon your home planet.");
639     return 0;
640 }
641
642
643 void cramen(int i) 
644 {
645     /* return an enemy */
646     char *s;
647         
648     switch (i) {
649     case IHR: s = "Romulan"; break;
650     case IHK: s = "Klingon"; break;
651     case IHC: s = "Commander"; break;
652     case IHS: s = "Super-commander"; break;
653     case IHSTAR: s = "Star"; break;
654     case IHP: s = "Planet"; break;
655     case IHB: s = "Starbase"; break;
656     case IHBLANK: s = "Black hole"; break;
657     case IHT: s = "Tholian"; break;
658     case IHWEB: s = "Tholian web"; break;
659     case IHQUEST: s = "Stranger"; break;
660     default: s = "Unknown??"; break;
661     }
662     proutn(s);
663 }
664
665 char *cramlc(enum loctype key, coord w)
666 {
667     static char buf[32];
668     buf[0] = '\0';
669     if (key == quadrant) strcpy(buf, "Quadrant ");
670     else if (key == sector) strcpy(buf, "Sector ");
671     sprintf(buf+strlen(buf), "%d - %d", w.x, w.y);
672     return buf;
673 }
674
675 void crmena(int i, int enemy, int key, coord w) 
676 {
677     if (i == 1) proutn("***");
678     cramen(enemy);
679     proutn(" at ");
680     proutn(cramlc(key, w));
681 }
682
683 void crmshp(void) 
684 {
685     char *s;
686     switch (game.ship) {
687     case IHE: s = "Enterprise"; break;
688     case IHF: s = "Faerie Queene"; break;
689     default:  s = "Ship???"; break;
690     }
691     proutn(s);
692 }
693
694 void stars(void) 
695 {
696     prouts("******************************************************");
697     skip(1);
698 }
699
700 double expran(double avrage) 
701 {
702     return -avrage*log(1e-7 + Rand());
703 }
704
705 double Rand(void) {
706         return rand()/(1.0 + (double)RAND_MAX);
707 }
708
709 void iran(int size, int *i, int *j) 
710 {
711     *i = Rand()*(size*1.0) + 1.0;
712     *j = Rand()*(size*1.0) + 1.0;
713 }
714
715 void chew(void)
716 {
717     linep = line;
718     *linep = 0;
719 }
720
721 void chew2(void) 
722 {
723     /* return IHEOL next time */
724     linep = line+1;
725     *linep = 0;
726 }
727
728 int scan(void) 
729 {
730     int i;
731     char *cp;
732
733     // Init result
734     aaitem = 0.0;
735     *citem = 0;
736
737     // Read a line if nothing here
738     if (*linep == 0) {
739         if (linep != line) {
740             chew();
741             return IHEOL;
742         }
743         cgetline(line, sizeof(line));
744         fflush(stdin);
745         if (curwnd==prompt_window){
746             clrscr();
747             setwnd(message_window);
748             clrscr();
749         }
750         linep = line;
751     }
752     // Skip leading white space
753     while (*linep == ' ') linep++;
754     // Nothing left
755     if (*linep == 0) {
756         chew();
757         return IHEOL;
758     }
759     if (isdigit(*linep) || *linep=='+' || *linep=='-' || *linep=='.') {
760         // treat as a number
761         i = 0;
762         if (sscanf(linep, "%lf%n", &aaitem, &i) < 1) {
763             linep = line; // Invalid numbers are ignored
764             *linep = 0;
765             return IHEOL;
766         }
767         else {
768             // skip to end
769             linep += i;
770             return IHREAL;
771         }
772     }
773     // Treat as alpha
774     cp = citem;
775     while (*linep && *linep!=' ') {
776         if ((cp - citem) < 9) *cp++ = tolower(*linep);
777         linep++;
778     }
779     *cp = 0;
780     return IHALPHA;
781 }
782
783 bool ja(void) 
784 {
785     chew();
786     for(;;) {
787         scan();
788         chew();
789         if (*citem == 'y') return true;
790         if (*citem == 'n') return false;
791         proutn("Please answer with \"Y\" or \"N\": ");
792     }
793 }
794
795 void huh(void) 
796 {
797     chew();
798     skip(1);
799     prout("Beg your pardon, Captain?");
800 }
801
802 int isit(char *s) 
803 {
804     /* New function -- compares s to scanned citem and returns true if it
805        matches to the length of s */
806
807     return strncasecmp(s, citem, max(1, strlen(citem))) == 0;
808
809 }
810
811 #ifdef DEBUG
812 void debugme(void) 
813 {
814     proutn("Reset levels? ");
815     if (ja() != 0) {
816         if (energy < game.inenrg) energy = game.inenrg;
817         shield = game.inshld;
818         torps = game.intorps;
819         game.lsupres = game.inlsr;
820     }
821     proutn("Reset damage? ");
822     if (ja() != 0) {
823         int i;
824         for (i=0; i < NDEVICES; i++) 
825             if (game.damage[i] > 0.0) 
826                 game.damage[i] = 0.0;
827     }
828     proutn("Toggle game.idebug? ");
829     if (ja() != 0) {
830         game.idebug = !game.idebug;
831         if (game.idebug) prout("Debug output ON");
832         else prout("Debug output OFF");
833     }
834     proutn("Cause selective damage? ");
835     if (ja() != 0) {
836         int i, key;
837         for (i=0; i < NDEVICES; i++) {
838             proutn("Kill ");
839             proutn(device[i]);
840             proutn("? ");
841             chew();
842             key = scan();
843             if (key == IHALPHA &&  isit("y")) {
844                 game.damage[i] = 10.0;
845             }
846         }
847     }
848     proutn("Examine/change events? ");
849     if (ja() != 0) {
850         int i;
851         for (i = 1; i < NEVENTS; i++) {
852             int key;
853             if (!is_scheduled(i)) continue;
854             switch (i) {
855             case FSNOVA:  proutn("Supernova       "); break;
856             case FTBEAM:  proutn("T Beam          "); break;
857             case FSNAP:   proutn("Snapshot        "); break;
858             case FBATTAK: proutn("Base Attack     "); break;
859             case FCDBAS:  proutn("Base Destroy    "); break;
860             case FSCMOVE: proutn("SC Move         "); break;
861             case FSCDBAS: proutn("SC Base Destroy "); break;
862             }
863             proutn("%.2f", scheduled(i)-game.state.date);
864             chew();
865             proutn("  ?");
866             key = scan();
867             if (key == IHREAL) {
868                 schedule(i, aaitem);
869             }
870         }
871         chew();
872     }
873     proutn("Induce supernova here? ");
874     if (ja() != 0) {
875         game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova = true;
876         atover(1);
877     }
878 }
879 #endif