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