More boolean cleanups. More consistent use of coord type
[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 bool 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 /* generate a list of legal commands */
295 {
296     int i, k = 0;
297     proutn("LEGAL COMMANDS ARE:");
298     for (i = 0; i < NUMCOMMANDS; i++) {
299         if (!ACCEPT(i))
300             continue;
301         if (k % 5 == 0)
302             skip(1);
303         proutn("%-12s ", commands[i].name); 
304         k++;
305     }
306     skip(1);
307 }
308
309 static void helpme(void)
310 /* browse on-line help */
311 {
312     int i, j;
313     char cmdbuf[32], *cp;
314     char linebuf[132];
315     FILE *fp;
316     /* Give help on commands */
317     int key;
318     key = scan();
319     for(;;) {
320         if (key == IHEOL) {
321             setwnd(prompt_window);
322             proutn("Help on what command? ");
323             key = scan();
324         }
325         setwnd(message_window);
326         if (key == IHEOL) return;
327         for (i = 0; i < NUMCOMMANDS; i++) {
328             if (ACCEPT(i) && strcasecmp(commands[i].name, citem)==0) {
329                 i = commands[i].value;
330                 break;
331             }
332         }
333         if (i != NUMCOMMANDS) break;
334         skip(1);
335         prout("Valid commands:");
336         listCommands();
337         key = IHEOL;
338         chew();
339         skip(1);
340     }
341     if (i == COMMANDS) {
342         strcpy(cmdbuf, " ABBREV");
343     }
344     else {
345         for (j = 0; commands[i].name[j]; j++)
346             cmdbuf[j] = toupper(commands[i].name[j]);
347         cmdbuf[j] = '\0';
348     }
349     fp = fopen(SSTDOC, "r");
350     if (fp == NULL)
351         fp = fopen(DOC_NAME, "r");
352     if (fp == NULL) {
353         prout("Spock-  \"Captain, that information is missing from the");
354         prout("   computer. You need to find "DOC_NAME" and put it in the");
355         prout("   current directory or to "SSTDOC".\"");
356         /*
357          * This used to continue: "You need to find SST.DOC and put 
358          * it in the current directory."
359          */
360         return;
361     }
362     for (;;) {
363         if (fgets(linebuf, sizeof(linebuf), fp) == NULL) {
364             prout("Spock- \"Captain, there is no information on that command.\"");
365             fclose(fp);
366             return;
367         }
368         if (linebuf[0] == '%' && linebuf[1] == '%'&& linebuf[2] == ' ') {
369             for (cp = linebuf+3; isspace(*cp); cp++)
370                 continue;
371             linebuf[strlen(linebuf)-1] = '\0';
372             if (strcasecmp(cp, cmdbuf) == 0)
373                 break;
374         }
375     }
376
377     skip(1);
378     prout("Spock- \"Captain, I've found the following information:\"");
379     skip(1);
380
381     while (fgets(linebuf, sizeof(linebuf),fp)) {
382         if (strstr(linebuf, "******"))
383             break;
384         proutn(linebuf);
385     }
386     fclose(fp);
387 }
388
389 void enqueue(char *s)
390 /* enqueue input for the command parser */
391 {
392     strcpy(line, s);
393 }
394
395 static void makemoves(void)
396 /* command-interpretation loop */
397 {
398     int key, i, v = 0;
399     bool hitme;
400     clrscr();
401     setwnd(message_window);
402     for(;;) { /* command loop */
403         drawmaps(1);
404         for(;;)  { /* get a command */
405             hitme = false;
406             game.justin = 0;
407             game.optime = 0.0;
408             i = -1;
409             chew();
410             setwnd(prompt_window);
411             clrscr();
412             proutn("COMMAND> ");
413             if (scan() == IHEOL) {
414                 makechart();
415                 continue;
416             }
417             game.ididit = false;
418             clrscr();
419             setwnd(message_window);
420             clrscr();
421             for (i=0; i < ABANDON; i++)
422                 if (ACCEPT(i) && isit(commands[i].name)) {
423                     v = commands[i].value;
424                     break;
425                 }
426             if (i < ABANDON && (!commands[i].option || (commands[i].option & game.options))) 
427                 break;
428             for (; i < NUMCOMMANDS; i++)
429                 if (ACCEPT(i) && strcasecmp(commands[i].name, citem) == 0) {
430                     v = commands[i].value;
431                     break;
432                 }
433             if (i < NUMCOMMANDS && (!commands[i].option || (commands[i].option & game.options))) 
434                 break;
435             listCommands();
436         }
437         commandhook(commands[i].name, true);
438         switch (v) { /* command switch */
439         case SRSCAN:                 // srscan
440             srscan(SCAN_FULL);
441             break;
442         case STATUS:                 // status
443             srscan(SCAN_STATUS);
444             break;
445         case REQUEST:                   // status request 
446             srscan(SCAN_REQUEST);
447             break;
448         case LRSCAN:                    // lrscan
449             lrscan();
450             break;
451         case PHASERS:                   // phasers
452             phasers();
453             if (game.ididit) hitme = true;
454             break;
455         case TORPEDO:                   // photons
456             photon();
457             if (game.ididit) hitme = true;
458             break;
459         case MOVE:                      // move
460             warp(false);
461             break;
462         case SHIELDS:                   // shields
463             doshield(1);
464             if (game.ididit) {
465                 hitme=true;
466                 game.shldchg = 0;
467             }
468             break;
469         case DOCK:                      // dock
470             dock(1);
471             if (game.ididit) attack(0);
472             break;
473         case DAMAGES:                   // damages
474             dreprt();
475             break;
476         case CHART:                     // chart
477             chart(false);
478             break;
479         case IMPULSE:                   // impulse
480             impuls();
481             break;
482         case REST:                      // rest
483             wait();
484             if (game.ididit) hitme = true;
485             break;
486         case WARP:                      // warp
487             setwrp();
488             break;
489         case SCORE:                     // score
490             score();
491             break;
492         case SENSORS:                   // sensors
493             sensor();
494             break;
495         case ORBIT:                     // orbit
496             orbit();
497             if (game.ididit) hitme = true;
498             break;
499         case TRANSPORT:                 // transport "beam"
500             beam();
501             break;
502         case MINE:                      // mine
503             mine();
504             if (game.ididit) hitme = true;
505             break;
506         case CRYSTALS:                  // crystals
507             usecrystals();
508             if (game.ididit) hitme = true;
509             break;
510         case SHUTTLE:                   // shuttle
511             shuttle();
512             if (game.ididit) hitme = true;
513             break;
514         case PLANETS:                   // Planet list
515             preport();
516             break;
517         case REPORT:                    // Game Report 
518             report();
519             break;
520         case COMPUTER:                  // use COMPUTER!
521             eta();
522             break;
523         case COMMANDS:
524             listCommands();
525             break;
526         case EMEXIT:                    // Emergency exit
527             clrscr();                   // Hide screen
528             freeze(true);               // forced save
529             exit(1);                    // And quick exit
530             break;
531         case PROBE:
532             probe();                    // Launch probe
533             if (game.ididit) hitme = true;
534             break;
535         case ABANDON:                   // Abandon Ship
536             abandn();
537             break;
538         case DESTRUCT:                  // Self Destruct
539             selfdestruct();
540             break;
541         case SAVE:                      // Save Game
542             freeze(false);
543             clrscr();
544             if (game.skill > SKILL_GOOD)
545                 prout("WARNING--Saved games produce no plaques!");
546             break;
547         case DEATHRAY:                  // Try a desparation measure
548             deathray();
549             if (game.ididit) hitme = true;
550             break;
551         case DEBUGCMD:                  // What do we want for debug???
552             debugme();
553             break;
554         case MAYDAY:                    // Call for help
555             mayday();
556             if (game.ididit) hitme = true;
557             break;
558         case QUIT:
559             game.alldone = true;                // quit the game
560             break;
561         case HELP:
562             helpme();                   // get help
563             break;
564         case SEED:                      // set random-number seed
565             key = scan();
566             if (key == IHREAL)
567                 seed = (int)aaitem;
568             break;
569         }
570         commandhook(commands[i].name, false);
571         for (;;) {
572             if (game.alldone) break;            // Game has ended
573             if (game.optime != 0.0) {
574                 events();
575                 if (game.alldone) break;        // Events did us in
576             }
577             if (game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova) { // Galaxy went Nova!
578                 atover(false);
579                 continue;
580             }
581             if (hitme && !game.justin) {
582                 attack(2);
583                 if (game.alldone) break;
584                 if (game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova) {    // went NOVA! 
585                     atover(false);
586                     hitme = true;
587                     continue;
588                 }
589             }
590             break;
591         }
592         if (game.alldone) break;
593     }
594     if (idebug) prout("=== Ending");
595 }
596
597
598 int main(int argc, char **argv) 
599 {
600     int i, option;
601
602     game.options = OPTION_ALL &~ (OPTION_IOMODES | OPTION_SHOWME | OPTION_PLAIN | OPTION_ALMY);
603     if (getenv("TERM"))
604         game.options |= OPTION_CURSES | OPTION_SHOWME;
605     else
606         game.options |= OPTION_TTY;
607
608     seed = (int)time(NULL);
609     while ((option = getopt(argc, argv, "r:tx")) != -1) {
610         switch (option) {
611         case 'r':
612             replayfp = fopen(optarg, "r");
613             if (replayfp == NULL) {
614                 fprintf(stderr, "sst: can't open replay file %s\n", optarg);
615                 exit(1);        
616             }
617             if (fscanf(replayfp, "seed %d\n", &seed) != 1) {
618                 fprintf(stderr, "sst: replay file %s is ill-formed\n", optarg);
619                 exit(1);        
620             }
621             /* FALL THROUGH */
622         case 't':
623             game.options |= OPTION_TTY;
624             game.options &=~ OPTION_CURSES;
625             break;
626         case 'x':
627             idebug = true;
628             break;
629         default:
630             fprintf(stderr, "usage: sst [-t] [-x] [startcommand...].\n");
631             exit(0);
632         }
633     }
634     /* where to save the input in case of bugs */
635     logfp = fopen("sst-input.log", "w");
636     setlinebuf(logfp);
637     fprintf(logfp, "seed %d\n", seed);
638     srand(seed);
639
640     srand(seed);
641     iostart();
642
643     line[0] = '\0';
644     for (i = optind; i < argc;  i++) {
645         strcat(line, argv[i]);
646         strcat(line, " ");
647     }
648     for(;;) { /* Play a game */
649         setwnd(fullscreen_window);
650         clrscr();
651         prelim();
652         setup(line[0] == '\0');
653         if (game.alldone) {
654             score();
655             game.alldone = false;
656         }
657         else makemoves();
658         skip(1);
659         stars();
660         skip(1);
661
662         if (game.tourn && game.alldone) {
663             proutn("Do you want your score recorded?");
664             if (ja() == true) {
665                 chew2();
666                 freeze(false);
667             }
668         }
669         proutn("Do you want to play again? ");
670         if (!ja()) break;
671     }
672     skip(1);
673     prout("May the Great Bird of the Galaxy roost upon your home planet.");
674     return 0;
675 }
676
677
678 void cramen(int i) 
679 /* print the name of an enemy */
680 {
681     /* return an enemy */
682     char *s;
683         
684     switch (i) {
685     case IHR: s = "Romulan"; break;
686     case IHK: s = "Klingon"; break;
687     case IHC: s = "Commander"; break;
688     case IHS: s = "Super-commander"; break;
689     case IHSTAR: s = "Star"; break;
690     case IHP: s = "Planet"; break;
691     case IHB: s = "Starbase"; break;
692     case IHBLANK: s = "Black hole"; break;
693     case IHT: s = "Tholian"; break;
694     case IHWEB: s = "Tholian web"; break;
695     case IHQUEST: s = "Stranger"; break;
696     default: s = "Unknown??"; break;
697     }
698     proutn(s);
699 }
700
701 char *cramlc(enum loctype key, coord w)
702 /* name a location */
703 {
704     static char buf[32];
705     buf[0] = '\0';
706     if (key == quadrant) strcpy(buf, "Quadrant ");
707     else if (key == sector) strcpy(buf, "Sector ");
708     sprintf(buf+strlen(buf), "%d - %d", w.x, w.y);
709     return buf;
710 }
711
712 void crmena(bool stars, int enemy, enum loctype key, coord w) 
713 /* print an enemy and his location */
714 {
715     if (stars) proutn("***");
716     cramen(enemy);
717     proutn(" at ");
718     proutn(cramlc(key, w));
719 }
720
721 void crmshp(void)
722 /* print our ship name */
723 {
724     char *s;
725     switch (game.ship) {
726     case IHE: s = "Enterprise"; break;
727     case IHF: s = "Faerie Queene"; break;
728     default:  s = "Ship???"; break;
729     }
730     proutn(s);
731 }
732
733 void stars(void)
734 /* print a line of stars */
735 {
736     prouts("******************************************************");
737     skip(1);
738 }
739
740 double expran(double avrage) 
741 {
742     return -avrage*log(1e-7 + Rand());
743 }
744
745 double Rand(void) 
746 {
747     return rand()/(1.0 + (double)RAND_MAX);
748 }
749
750 coord iran(int size)
751 /* choose a random location */ 
752 {
753     coord w;
754     w.x = Rand()*(size*1.0) + 1.0;
755     w.y = Rand()*(size*1.0) + 1.0;
756     return w;
757 }
758
759 void chew(void)
760 {
761     linep = line;
762     *linep = 0;
763 }
764
765 void chew2(void) 
766 {
767     /* return IHEOL next time */
768     linep = line+1;
769     *linep = 0;
770 }
771
772 int scan(void) 
773 {
774     int i;
775     char *cp;
776
777     // Init result
778     aaitem = 0.0;
779     *citem = 0;
780
781     // Read a line if nothing here
782     if (*linep == 0) {
783         if (linep != line) {
784             chew();
785             return IHEOL;
786         }
787         cgetline(line, sizeof(line));
788         fflush(stdin);
789         if (curwnd==prompt_window){
790             clrscr();
791             setwnd(message_window);
792             clrscr();
793         }
794         linep = line;
795     }
796     // Skip leading white space
797     while (*linep == ' ') linep++;
798     // Nothing left
799     if (*linep == 0) {
800         chew();
801         return IHEOL;
802     }
803     if (isdigit(*linep) || *linep=='+' || *linep=='-' || *linep=='.') {
804         // treat as a number
805         i = 0;
806         if (sscanf(linep, "%lf%n", &aaitem, &i) < 1) {
807             linep = line; // Invalid numbers are ignored
808             *linep = 0;
809             return IHEOL;
810         }
811         else {
812             // skip to end
813             linep += i;
814             return IHREAL;
815         }
816     }
817     // Treat as alpha
818     cp = citem;
819     while (*linep && *linep!=' ') {
820         if ((cp - citem) < 9) *cp++ = tolower(*linep);
821         linep++;
822     }
823     *cp = 0;
824     return IHALPHA;
825 }
826
827 bool ja(void)
828 /* yes-or-no confirmation */
829 {
830     chew();
831     for(;;) {
832         scan();
833         chew();
834         if (*citem == 'y') return true;
835         if (*citem == 'n') return false;
836         proutn("Please answer with \"Y\" or \"N\": ");
837     }
838 }
839
840 void huh(void)
841 /* complain about unparseable input */
842 {
843     chew();
844     skip(1);
845     prout("Beg your pardon, Captain?");
846 }
847
848 int isit(char *s) 
849 /* compares s to citem and returns true if it matches to the length of s */
850 {
851     return strncasecmp(s, citem, max(1, strlen(citem))) == 0;
852 }
853
854 void debugme(void)
855 /* access to the internals for debugging */
856 {
857     proutn("Reset levels? ");
858     if (ja() == true) {
859         if (game.energy < game.inenrg) game.energy = game.inenrg;
860         game.shield = game.inshld;
861         game.torps = game.intorps;
862         game.lsupres = game.inlsr;
863     }
864     proutn("Reset damage? ");
865     if (ja() == true) {
866         int i;
867         for (i=0; i < NDEVICES; i++) 
868             if (game.damage[i] > 0.0) 
869                 game.damage[i] = 0.0;
870     }
871     proutn("Toggle debug flag? ");
872     if (ja() == true) {
873         idebug = !idebug;
874         if (idebug) prout("Debug output ON");
875         else prout("Debug output OFF");
876     }
877     proutn("Cause selective damage? ");
878     if (ja() == true) {
879         int i, key;
880         for (i=0; i < NDEVICES; i++) {
881             proutn("Kill ");
882             proutn(device[i]);
883             proutn("? ");
884             chew();
885             key = scan();
886             if (key == IHALPHA &&  isit("y")) {
887                 game.damage[i] = 10.0;
888             }
889         }
890     }
891     proutn("Examine/change events? ");
892     if (ja() == true) {
893         event *ev;
894         coord w;
895         int i;
896         for (i = 1; i < NEVENTS; i++) {
897             int key;
898             switch (i) {
899             case FSNOVA:  proutn("Supernova       "); break;
900             case FTBEAM:  proutn("T Beam          "); break;
901             case FSNAP:   proutn("Snapshot        "); break;
902             case FBATTAK: proutn("Base Attack     "); break;
903             case FCDBAS:  proutn("Base Destroy    "); break;
904             case FSCMOVE: proutn("SC Move         "); break;
905             case FSCDBAS: proutn("SC Base Destroy "); break;
906             case FDSPROB: proutn("Probe Move      "); break;
907             case FDISTR:  proutn("Distress Call   "); break;
908             case FENSLV:  proutn("Enlavement      "); break;
909             case FREPRO:  proutn("Klingon Build   "); break;
910             }
911             if (is_scheduled(i)) {
912                 proutn("%.2f", scheduled(i)-game.state.date);
913                 if (i == FENSLV || i == FREPRO) {
914                     ev = findevent(i);
915                     proutn(" in %d-%d", ev->quadrant.x,ev->quadrant.y);
916                 }
917             } else
918                 proutn("never");
919             proutn("? ");
920             chew();
921             key = scan();
922             if (key == 'n') {
923                 unschedule(i);
924                 chew();
925             } else if (key == IHREAL) {
926                 ev = schedule(i, aaitem);
927                 if (i == FENSLV || i == FREPRO) {
928                     chew();
929                     proutn("In quadrant- ");
930                     key = scan();
931                     /* IHEOL says to leave coordinates as they are */
932                     if (key != IHEOL) {
933                         if (key != IHREAL) {
934                             prout("Event %d canceled, no x coordinate.", i);
935                             unschedule(i);
936                             continue;
937                         }
938                         w.x = (int)aaitem;
939                         key = scan();
940                         if (key != IHREAL) {
941                             prout("Event %d canceled, no y coordinate.", i);
942                             unschedule(i);
943                             continue;
944                         }
945                         w.y = (int)aaitem;
946                         ev->quadrant = w;
947                     }
948                 }
949             }
950         }
951         chew();
952     }
953     proutn("Induce supernova here? ");
954     if (ja() == true) {
955         game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova = true;
956         atover(true);
957     }
958 }