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