Restored REQUEST.
[super-star-trek.git] / sst.c
1 #define INCLUDED        // Define externs here
2 #include <ctype.h>
3 #include <getopt.h>
4 #include <time.h>
5 #include "conio.h"
6 #include "sstlinux.h"
7 #include "sst.h"
8
9 #ifndef SSTDOC
10 #define SSTDOC  "sst.doc"
11 #endif
12         
13 /*
14
15 Here are Tom Almy's changes:
16
17  Compared to original version, I've changed the "help" command to
18    "call" and the "terminate" command to "quit" to better match
19    user expectations. The DECUS version apparently made those changes
20    as well as changing "freeze" to "save". However I like "freeze".
21
22    When I got a later version of Super Star Trek that I was converting
23    from, I added the emexit command.
24
25    That later version also mentions srscan and lrscan working when
26    docked (using the starbase's scanners), so I made some changes here
27    to do this (and indicating that fact to the player), and then realized
28    the base would have a subspace radio as well -- doing a Chart when docked
29    updates the star chart, and all radio reports will be heard. The Dock
30    command will also give a report if a base is under attack.
31
32    Movecom no longer reports movement if sensors are damaged so you wouldn't
33    otherwise know it.
34
35    Also added:
36
37    1. Better base positioning at startup
38
39    2. Deathray improvement (but keeping original failure alternatives)
40
41    3. Tholian Web.
42
43    4. Enemies can ram the Enterprise. Regular Klingons and Romulans can
44       move in Expert and Emeritus games. This code could use improvement.
45
46    5. The deep space probe looks interesting! DECUS version
47
48    6. Perhaps cloaking to be added later? BSD version
49
50 Here are Stas Sergeev's changes:
51
52    1. The Space Thingy can be shoved, if you it ram, and can fire back if 
53       fired upon.
54
55    2. The Tholian can be hit with phasers
56
57    3. When you are docked, base covers you with an almost invincible shields 
58       (a commander can still ram you, or a Romulan can destroy the base,
59       or a SCom can even succeed with direct attack IIRC, but this rarely 
60       happens).
61
62    4. SCom can't escape from you if no more enemies remain (without this, 
63       chasing SCom can take an eternity).
64
65    5. Probe target you enter is now the destination quadrant. Before I don't 
66       remember what it was, but it was something I had difficulty using)
67
68    6. Secret password is now autogenerated.
69
70    7. "Plaque" is adjusted for A4 paper :-)
71
72    8. Phasers now tells you how much energy needed, but only if the computer 
73        is alive.
74
75    9. Planets are auto-scanned when you enter the quadrant.
76
77    10. Mining or using crystals in presense of enemy now yields an attack.
78        There are other minor adjustments to what yields an attack
79        and what does not.
80
81    11. Ramming a black hole is no longer instant death.  There is a
82        chance you might get timewarped instead.
83
84    12. "freeze" command reverts to "save", most people will understand this
85         better anyway.
86
87    13. Screen-oriented interface, with sensor scans always up.
88
89 Eric Raymond's changes:
90
91    1. "sos" and "call" becomes "mayday", "freeze" and "save" are both good.
92
93    */
94
95 /* the input queue */
96 static char line[128], *linep = line;
97
98 static struct 
99 {
100     char *name;
101     int value;
102 }
103 commands[] = {
104 #define SRSCAN  1
105         {"SRSCAN",      SRSCAN},
106         {"STATUS",      SRSCAN},
107 #define LRSCAN  2
108         {"LRSCAN",      LRSCAN},
109 #define PHASERS 3
110         {"PHASERS",     PHASERS},
111 #define TORPEDO 4
112         {"TORPEDO",     TORPEDO},
113         {"PHOTONS",     TORPEDO},
114 #define MOVE    5
115         {"MOVE",        MOVE},
116 #define SHIELDS 6
117         {"SHIELDS",     SHIELDS},
118 #define DOCK    7
119         {"DOCK",        DOCK},
120 #define DAMAGES 8
121         {"DAMAGES",     DAMAGES},
122 #define CHART   9
123         {"CHART",       CHART},
124 #define IMPULSE 10
125         {"IMPULSE",     IMPULSE},
126 #define REST    11
127         {"REST",        REST},
128 #define WARP    12
129         {"WARP",        WARP},
130 #define SCORE   13
131         {"SCORE",       SCORE},
132 #define SENSORS 14
133         {"SENSORS",     SENSORS},
134 #define ORBIT   15
135         {"ORBIT",       ORBIT},
136 #define TRANSPORT       16
137         {"TRANSPORT",   TRANSPORT},
138 #define MINE    17
139         {"MINE",        MINE},
140 #define CRYSTALS 18
141         {"CRYSTALS",    CRYSTALS},
142 #define SHUTTLE 19
143         {"SHUTTLE",     SHUTTLE},
144 #define PLANETS 20
145         {"PLANETS",     PLANETS},
146 #define REPORT  21
147         {"REPORT",      REPORT},
148 #define COMPUTER        23
149         {"COMPUTER",    COMPUTER},
150 #define COMMANDS        24
151         {"COMMANDS",    COMMANDS},
152 #define EMEXIT  25
153         {"EMEXIT",      EMEXIT},
154 #define PROBE   26
155         {"PROBE",       PROBE},
156 #define SAVE    27
157         {"SAVE",        SAVE},
158         {"FREEZE",      SAVE},
159 #define ABANDON 28
160         {"ABANDON",     ABANDON},
161 #define DESTRUCT 29
162         {"DESTRUCT",    DESTRUCT},
163 #define DEATHRAY 30
164         {"DEATHRAY",    DEATHRAY},
165 #define DEBUGCMD        31
166         {"DEBUG",       DEBUGCMD},
167 #define MAYDAY  32
168         {"MAYDAY",      MAYDAY},
169         {"SOS",         MAYDAY},
170         {"CALL",        MAYDAY},
171 #define QUIT    33
172         {"QUIT",        QUIT},
173 #define HELP    34
174         {"HELP",        HELP},
175 #define REQUEST 35
176         {"REQUEST",     REQUEST},
177 };
178
179 #define NUMCOMMANDS     sizeof(commands)/sizeof(commands[0])
180
181 static void listCommands(int x) {
182     int i;
183     prout("LEGAL COMMANDS ARE:");
184     for (i = 0; i < NUMCOMMANDS; i++) {
185         proutn("%-12s ", commands[i].name);
186         if (i % 5 == 4)
187             skip(1);
188     }
189     skip(1);
190 }
191
192 static void helpme(void) {
193         int i, j;
194         char cmdbuf[32], *cp;
195         char linebuf[132];
196         FILE *fp;
197         /* Give help on commands */
198         int key;
199         key = scan();
200         while (TRUE) {
201                 if (key == IHEOL) {
202                         setwnd(BOTTOM_WINDOW);
203                         proutn("Help on what command? ");
204                         key = scan();
205                 }
206                 setwnd(LOWER_WINDOW);
207                 if (key == IHEOL) return;
208                 for (i = 0; i < NUMCOMMANDS; i++) {
209                     if (strcasecmp(commands[i].name, citem)==0) {
210                         i = commands[i].value;
211                         break;
212                     }
213                 }
214                 if (i != NUMCOMMANDS) break;
215                 skip(1);
216                 prout("Valid commands:");
217                 listCommands(FALSE);
218                 key = IHEOL;
219                 chew();
220                 skip(1);
221         }
222         if (i == COMMANDS) {
223                 strcpy(cmdbuf, " ABBREV");
224         }
225         else {
226             for (j = 0; commands[i].name[j]; j++)
227                 cmdbuf[j] = toupper(commands[i].name[j]);
228             cmdbuf[j] = '\0';
229         }
230         fp = fopen(SSTDOC, "r");
231         if (fp == NULL) {
232                 prout("Spock-  \"Captain, that information is missing from the");
233                 prout("   computer.\"");
234                 /*
235                  * This used to continue: "You need to find SST.DOC and put 
236                  * it in the current directory."
237                  */
238                 return;
239         }
240         for (;;) {
241             if (fgets(linebuf, sizeof(linebuf), fp) == NULL) {
242                         prout("Spock- \"Captain, there is no information on that command.\"");
243                         fclose(fp);
244                         return;
245                 }
246             if (linebuf[0] == '%' && linebuf[1] == '%'&& linebuf[2] == ' ') {
247                 for (cp = linebuf+3; isspace(*cp); cp++)
248                         continue;
249                 linebuf[strlen(linebuf)-1] = '\0';
250                 if (strcasecmp(cp, cmdbuf) == 0)
251                     break;
252             }
253         }
254
255         skip(1);
256         prout("Spock- \"Captain, I've found the following information:\"");
257         skip(1);
258
259         while (fgets(linebuf, sizeof(linebuf),fp)) {
260                 if (strstr(linebuf, "******"))
261                         break;
262                 proutc(linebuf);
263         }
264         fclose(fp);
265 }
266
267 void enqueue(char *s) {
268     strcpy(line, s);
269 }
270
271 static void makemoves(void) {
272         int i, hitme;
273         clrscr();
274         setwnd(LOWER_WINDOW);
275         while (TRUE) { /* command loop */
276                 drawmaps(1);
277                 while (TRUE)  { /* get a command */
278                         hitme = FALSE;
279                         justin = 0;
280                         Time = 0.0;
281                         i = -1;
282                         chew();
283                         setwnd(BOTTOM_WINDOW);
284                         clrscr();
285                         proutn("COMMAND> ");
286                         if (scan() == IHEOL) {
287                             makechart();
288                             continue;
289                         }
290                         ididit=0;
291                         clrscr();
292                         setwnd(LOWER_WINDOW);
293                         clrscr();
294                         for (i=0; i < ABANDON; i++)
295                             if (isit(commands[i].name)) {
296                                 i = commands[i].value;
297                                 break;
298                             }
299                         if (i < ABANDON) break;
300                         for (; i < NUMCOMMANDS; i++)
301                             if (strcasecmp(commands[i].name, citem) == 0) {
302                                     i = commands[i].value;
303                                     break;
304                             }
305                         if (i < NUMCOMMANDS) break;
306
307                         listCommands(TRUE);
308                 }
309                 commandhook(commands[i].name, TRUE);
310                 switch (i) { /* command switch */
311                         case SRSCAN:                 // srscan
312                                 srscan(SCAN_FULL);
313                                 break;
314                         case LRSCAN:                    // lrscan
315                                 lrscan();
316                                 break;
317                         case PHASERS:                   // phasers
318                                 phasers();
319                                 if (ididit) hitme = TRUE;
320                                 break;
321                         case TORPEDO:                   // photons
322                                 photon();
323                                 if (ididit) hitme = TRUE;
324                                 break;
325                         case MOVE:                      // move
326                                 warp(1);
327                                 break;
328                         case SHIELDS:                   // shields
329                                 doshield(1);
330                                 if (ididit) {
331                                         hitme=TRUE;
332                                         shldchg = 0;
333                                 }
334                                 break;
335                         case DOCK:                      // dock
336                                 dock(1);
337                                 if (ididit) attack(0);
338                                 break;
339                         case DAMAGES:                   // damages
340                                 dreprt();
341                                 break;
342                         case CHART:                     // chart
343                                 chart(0);
344                                 break;
345                         case IMPULSE:                   // impulse
346                                 impuls();
347                                 break;
348                         case REST:              // rest
349                                 wait();
350                                 if (ididit) hitme = TRUE;
351                                 break;
352                         case WARP:              // warp
353                                 setwrp();
354                                 break;
355                         case SCORE:                // score
356                                 score();
357                                 break;
358                         case SENSORS:                   // sensors
359                                 sensor();
360                                 break;
361                         case ORBIT:                     // orbit
362                                 orbit();
363                                 if (ididit) hitme = TRUE;
364                                 break;
365                         case TRANSPORT:                 // transport "beam"
366                                 beam();
367                                 break;
368                         case MINE:                      // mine
369                                 mine();
370                                 if (ididit) hitme = TRUE;
371                                 break;
372                         case CRYSTALS:                  // crystals
373                                 usecrystals();
374                                 if (ididit) hitme = TRUE;
375                                 break;
376                         case SHUTTLE:                   // shuttle
377                                 shuttle();
378                                 if (ididit) hitme = TRUE;
379                                 break;
380                         case PLANETS:                   // Planet list
381                                 preport();
382                                 break;
383                         case REPORT:                    // Game Report 
384                                 report();
385                                 break;
386                         case REQUEST:                   // status request 
387                                 srscan(SCAN_REQUEST);
388                                 break;
389                         case COMPUTER:                  // use COMPUTER!
390                                 eta();
391                                 break;
392                         case COMMANDS:
393                                 listCommands(TRUE);
394                                 break;
395                         case EMEXIT:            // Emergency exit
396                                 clrscr();       // Hide screen
397                                 freeze(TRUE);   // forced save
398                                 exit(1);                // And quick exit
399                                 break;
400                         case PROBE:
401                                 probe();                // Launch probe
402                                 if (ididit) hitme = TRUE;
403                                 break;
404                         case ABANDON:                   // Abandon Ship
405                                 abandn();
406                                 break;
407                         case DESTRUCT:                  // Self Destruct
408                                 dstrct();
409                                 break;
410                         case SAVE:                      // Save Game
411                                 freeze(FALSE);
412                                 clrscr();
413                                 if (skill > 3)
414                                         prout("WARNING--Saved games produce no plaques!");
415                                 break;
416                         case DEATHRAY:          // Try a desparation measure
417                                 deathray();
418                                 if (ididit) hitme = TRUE;
419                                 break;
420                         case DEBUGCMD:          // What do we want for debug???
421 #ifdef DEBUG
422                                 debugme();
423 #endif
424                                 break;
425                         case MAYDAY:            // Call for help
426                                 help();
427                                 if (ididit) hitme = TRUE;
428                                 break;
429                         case QUIT:
430                                 alldone = 1;    // quit the game
431 #ifdef DEBUG
432                                 if (idebug) score();
433 #endif
434                                 break;
435                         case HELP:
436                                 helpme();       // get help
437                                 break;
438                 }
439                 commandhook(commands[i].name, FALSE);
440                 for (;;) {
441                         if (alldone) break;             // Game has ended
442 #ifdef DEBUG
443                         if (idebug) prout("2500");
444 #endif
445                         if (Time != 0.0) {
446                                 events();
447                                 if (alldone) break;             // Events did us in
448                         }
449                         if (game.state.galaxy[quadx][quady] == 1000) { // Galaxy went Nova!
450                                 atover(0);
451                                 continue;
452                         }
453                         if (hitme && justin==0) {
454                                 attack(2);
455                                 if (alldone) break;
456                                 if (game.state.galaxy[quadx][quady] == 1000) {  // went NOVA! 
457                                         atover(0);
458                                         hitme = TRUE;
459                                         continue;
460                                 }
461                         }
462                         break;
463                 }
464                 if (alldone) break;
465         }
466 }
467
468
469 int main(int argc, char **argv) {
470         int i, option, usecurses = TRUE;
471
472         while ((option = getopt(argc, argv, "t")) != -1) {
473             switch (option) {
474             case 't':
475                 usecurses = FALSE;
476                 break;
477             default:
478                 fprintf(stderr, "usage: sst [-t] [startcommand...].\n");
479                 exit(0);
480             }
481         }
482
483         randomize();
484         iostart(usecurses);
485
486         line[0] = '\0';
487         for (i = optind; i < argc;  i++) {
488                 strcat(line, argv[i]);
489                 strcat(line, " ");
490         }
491         while (TRUE) { /* Play a game */
492                 prelim();
493                 setup(line[0] == '\0');
494                 if (alldone) {
495                         score();
496                         alldone = 0;
497                 }
498                 else makemoves();
499                 skip(1);
500                 stars();
501                 skip(1);
502
503                 if (tourn && alldone) {
504                         proutn("Do you want your score recorded?");
505                         if (ja()) {
506                                 chew2();
507                                 freeze(FALSE);
508                         }
509                 }
510                 proutn("Do you want to play again? ");
511                 if (!ja()) break;
512                 setwnd(FULLSCREEN_WINDOW);
513                 clrscr();
514         }
515         skip(1);
516         ioend();
517         prout("May the Great Bird of the Galaxy roost upon your home planet.");
518         return 0;
519 }
520
521
522 void cramen(int i) {
523         /* return an enemy */
524         char *s;
525         
526         switch (i) {
527                 case IHR: s = "Romulan"; break;
528                 case IHK: s = "Klingon"; break;
529                 case IHC: s = "Commander"; break;
530                 case IHS: s = "Super-commander"; break;
531                 case IHSTAR: s = "Star"; break;
532                 case IHP: s = "Planet"; break;
533                 case IHB: s = "Starbase"; break;
534                 case IHBLANK: s = "Black hole"; break;
535                 case IHT: s = "Tholian"; break;
536                 case IHWEB: s = "Tholian web"; break;
537                 case IHQUEST: s = "Stranger"; break;
538                 default: s = "Unknown??"; break;
539         }
540         proutn(s);
541 }
542
543 char *cramlc(enum loctype key, int x, int y) {
544         static char buf[32];
545         buf[0] = '\0';
546         if (key == quadrant) strcpy(buf, "Quadrant ");
547         else if (key == sector) strcpy(buf, "Sector ");
548         sprintf(buf+strlen(buf), "%d - %d", x, y);
549         return buf;
550 }
551
552 void crmena(int i, int enemy, int key, int x, int y) {
553         if (i == 1) proutn("***");
554         cramen(enemy);
555         proutn(" at ");
556         proutn(cramlc(key, x, y));
557 }
558
559 void crmshp(void) {
560         char *s;
561         switch (ship) {
562                 case IHE: s = "Enterprise"; break;
563                 case IHF: s = "Faerie Queene"; break;
564                 default:  s = "Ship???"; break;
565         }
566         proutn(s);
567 }
568
569 void stars(void) {
570         prouts("******************************************************");
571         skip(1);
572 }
573
574 double expran(double avrage) {
575         return -avrage*log(1e-7 + Rand());
576 }
577
578 double Rand(void) {
579         return rand()/(1.0 + (double)RAND_MAX);
580 }
581
582 void iran8(int *i, int *j) {
583         *i = Rand()*8.0 + 1.0;
584         *j = Rand()*8.0 + 1.0;
585 }
586
587 void iran10(int *i, int *j) {
588         *i = Rand()*10.0 + 1.0;
589         *j = Rand()*10.0 + 1.0;
590 }
591
592 void chew(void) {
593         linep = line;
594         *linep = 0;
595 }
596
597 void chew2(void) {
598         /* return IHEOL next time */
599         linep = line+1;
600         *linep = 0;
601 }
602
603 int scan(void) {
604         int i;
605         char *cp;
606
607         // Init result
608         aaitem = 0.0;
609         *citem = 0;
610
611         // Read a line if nothing here
612         if (*linep == 0) {
613                 if (linep != line) {
614                         chew();
615                         return IHEOL;
616                 }
617                 cgetline(line, sizeof(line));
618                 fflush(stdin);
619                 if (curwnd==BOTTOM_WINDOW){
620                    clrscr();
621                    setwnd(LOWER_WINDOW);
622                    clrscr();
623                 }
624                 linep = line;
625         }
626         // Skip leading white space
627         while (*linep == ' ') linep++;
628         // Nothing left
629         if (*linep == 0) {
630                 chew();
631                 return IHEOL;
632         }
633         if (isdigit(*linep) || *linep=='+' || *linep=='-' || *linep=='.') {
634                 // treat as a number
635             i = 0;
636             if (sscanf(linep, "%lf%n", &aaitem, &i) < 1) {
637                 linep = line; // Invalid numbers are ignored
638                 *linep = 0;
639                 return IHEOL;
640             }
641             else {
642                 // skip to end
643                 linep += i;
644                 return IHREAL;
645             }
646         }
647         // Treat as alpha
648         cp = citem;
649         while (*linep && *linep!=' ') {
650                 if ((cp - citem) < 9) *cp++ = tolower(*linep);
651                 linep++;
652         }
653         *cp = 0;
654         return IHALPHA;
655 }
656
657 int ja(void) {
658         chew();
659         while (TRUE) {
660                 scan();
661                 chew();
662                 if (*citem == 'y') return TRUE;
663                 if (*citem == 'n') return FALSE;
664                 proutn("Please answer with \"Y\" or \"N\": ");
665         }
666 }
667
668 void huh(void) {
669         chew();
670         skip(1);
671         prout("Beg your pardon, Captain?");
672 }
673
674 int isit(char *s) {
675         /* New function -- compares s to scaned citem and returns true if it
676            matches to the length of s */
677
678         return strncasecmp(s, citem, max(1, strlen(citem))) == 0;
679
680 }
681
682 #ifdef DEBUG
683 void debugme(void) {
684         proutn("Reset levels? ");
685         if (ja() != 0) {
686                 if (energy < inenrg) energy = inenrg;
687                 shield = inshld;
688                 torps = intorps;
689                 lsupres = inlsr;
690         }
691         proutn("Reset damage? ");
692         if (ja() != 0) {
693                 int i;
694                 for (i=0; i <= NDEVICES; i++) if (damage[i] > 0.0) damage[i] = 0.0;
695                 stdamtim = 1e30;
696         }
697         proutn("Toggle idebug? ");
698         if (ja() != 0) {
699                 idebug = !idebug;
700                 if (idebug) prout("Debug output ON");
701                 else prout("Debug output OFF");
702         }
703         proutn("Cause selective damage? ");
704         if (ja() != 0) {
705                 int i, key;
706                 for (i=1; i <= NDEVICES; i++) {
707                         proutn("Kill ");
708                         proutn(device[i]);
709                         proutn("? ");
710                         chew();
711                         key = scan();
712                         if (key == IHALPHA &&  isit("y")) {
713                                 damage[i] = 10.0;
714                                 if (i == DRADIO) stdamtim = game.state.date;
715                         }
716                 }
717         }
718         proutn("Examine/change events? ");
719         if (ja() != 0) {
720                 int i;
721                 for (i = 1; i < NEVENTS; i++) {
722                         int key;
723                         if (future[i] == 1e30) continue;
724                         switch (i) {
725                                 case FSNOVA:  proutn("Supernova       "); break;
726                                 case FTBEAM:  proutn("T Beam          "); break;
727                                 case FSNAP:   proutn("Snapshot        "); break;
728                                 case FBATTAK: proutn("Base Attack     "); break;
729                                 case FCDBAS:  proutn("Base Destroy    "); break;
730                                 case FSCMOVE: proutn("SC Move         "); break;
731                                 case FSCDBAS: proutn("SC Base Destroy "); break;
732                         }
733                         proutn("%.2f", future[i]-game.state.date);
734                         chew();
735                         proutn("  ?");
736                         key = scan();
737                         if (key == IHREAL) {
738                                 future[i] = game.state.date + aaitem;
739                         }
740                 }
741                 chew();
742         }
743 }
744                         
745
746 #endif