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