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