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