Code, comment, and documentation typo fixes.
[super-star-trek.git] / sst.c
1 #define INCLUDED        // Define externs here\r
2 #include "sst.h"\r
3 #include <ctype.h>\r
4 #ifdef MSDOS\r
5 #include <dos.h>\r
6 #endif\r
7 #include <time.h>\r
8         \r
9 int getch(void);\r
10 \r
11 static char line[128], *linep = line;\r
12 static int linecount;   /* for paging */\r
13 \r
14 static void clearscreen(void);\r
15 \r
16 #define NUMCOMMANDS 34\r
17 \r
18 /* Compared to original version, I've changed the "help" command to\r
19    "call" and the "terminate" command to "quit" to better match\r
20    user expectations. The DECUS version apparently made those changes\r
21    as well as changing "freeze" to "save". However I like "freeze".\r
22 \r
23    When I got a later version of Super Star Trek that I was converting\r
24    from, I added the emexit command.\r
25 \r
26    That later version also mentions srscan and lrscan working when\r
27    docked (using the starbase's scanners), so I made some changes here\r
28    to do this (and indicating that fact to the player), and then realized\r
29    the base would have a subspace radio as well -- doing a Chart when docked\r
30    updates the star chart, and all radio reports will be heard. The Dock\r
31    command will also give a report if a base is under attack.\r
32 \r
33    Movecom no longer reports movement if sensors are damaged so you wouldn't\r
34    otherwise know it.\r
35 \r
36    Also added:\r
37 \r
38    1. Better base positioning at startup\r
39 \r
40    2. deathray improvement (but keeping original failure alternatives)\r
41 \r
42    3. Tholian Web\r
43 \r
44    4. Enemies can ram the Enterprise. Regular Klingons and Romulans can\r
45       move in Expert and Emeritus games. This code could use improvement.\r
46 \r
47    5. The deep space probe looks interesting! DECUS version\r
48 \r
49    6. Perhaps cloaking to be added later? BSD version\r
50 \r
51 \r
52    */\r
53 \r
54 \r
55 static char *commands[NUMCOMMANDS] = {\r
56         "srscan",\r
57         "lrscan",\r
58         "phasers",\r
59         "photons",\r
60         "move",\r
61         "shields",\r
62         "dock",\r
63         "damages",\r
64         "chart",\r
65         "impulse",\r
66         "rest",\r
67         "warp",\r
68         "status",\r
69         "sensors",\r
70         "orbit",\r
71         "transport",\r
72         "mine",\r
73         "crystals",\r
74         "shuttle",\r
75         "planets",\r
76         "request",\r
77         "report",\r
78         "computer",\r
79         "commands",\r
80     "emexit",\r
81     "probe",\r
82         "abandon",\r
83         "destruct",\r
84         "freeze",\r
85         "deathray",\r
86         "debug",\r
87         "call",\r
88         "quit",\r
89         "help"\r
90 };\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 (d.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 (d.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 void 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                         printf("Do you want your score recorded?");\r
378                         if (ja()) {\r
379                                 chew2();\r
380                                 freeze(FALSE);\r
381                         }\r
382                 }\r
383                 printf("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 void cramlc(int key, int x, int y) {\r
412         if (key == 1) proutn(" Quadrant");\r
413         else if (key == 2) proutn(" Sector");\r
414         proutn(" ");\r
415         crami(x, 1);\r
416         proutn(" - ");\r
417         crami(y, 1);\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         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                 gets(line);\r
490                 linep = line;\r
491         }\r
492         // Skip leading white space\r
493         while (*linep == ' ') linep++;\r
494         // Nothing left\r
495         if (*linep == 0) {\r
496                 chew();\r
497                 return IHEOL;\r
498         }\r
499         if (isdigit(*linep) || *linep=='+' || *linep=='-' || *linep=='.') {\r
500                 // treat as a number\r
501             if (sscanf(linep, "%lf%n", &aaitem, &i) < 1) {\r
502                 linep = line; // Invalid numbers are ignored\r
503                 *linep = 0;\r
504                 return IHEOL;\r
505             }\r
506             else {\r
507                 // skip to end\r
508                 linep += i;\r
509                 return IHREAL;\r
510             }\r
511         }\r
512         // Treat as alpha\r
513         cp = citem;\r
514         while (*linep && *linep!=' ') {\r
515                 if ((cp - citem) < 9) *cp++ = tolower(*linep);\r
516                 linep++;\r
517         }\r
518         *cp = 0;\r
519         return IHALPHA;\r
520 }\r
521 \r
522 int ja(void) {\r
523         chew();\r
524         while (TRUE) {\r
525                 scan();\r
526                 chew();\r
527                 if (*citem == 'y') return TRUE;\r
528                 if (*citem == 'n') return FALSE;\r
529                 proutn("Please answer with \"Y\" or \"N\":");\r
530         }\r
531 }\r
532 \r
533 void cramf(double x, int w, int d) {\r
534         char buf[64];\r
535         sprintf(buf, "%*.*f", w, d, x);\r
536         proutn(buf);\r
537 }\r
538 \r
539 void crami(int i, int w) {\r
540         char buf[16];\r
541         sprintf(buf, "%*d", w, i);\r
542         proutn(buf);\r
543 }\r
544 \r
545 double square(double i) { return i*i; }\r
546                                                                         \r
547 static void clearscreen(void) {\r
548         /* Somehow we need to clear the screen */\r
549 #ifdef __BORLANDC__\r
550         extern void clrscr(void);\r
551         clrscr();\r
552 #else\r
553         proutn("\033[2J");      /* Hope for an ANSI display */\r
554 #endif\r
555 }\r
556 \r
557 /* We will pull these out in case we want to do something special later */\r
558 \r
559 void pause(int i) {\r
560         putchar('\n');\r
561         if (i==1) {\r
562                 if (skill > 2)\r
563                         prout("[ANOUNCEMENT ARRIVING...]");\r
564                 else\r
565                         prout("[IMPORTANT ANNOUNCEMENT ARRIVING -- HIT SPACE BAR TO CONTINUE]");\r
566                 getch();\r
567         }\r
568         else {\r
569                 if (skill > 2)\r
570                         proutn("[CONTINUE?]");\r
571                 else\r
572                         proutn("[HIT SPACE BAR TO CONTINUE]");\r
573                 getch();\r
574                 proutn("\r                           \r");\r
575         }\r
576         if (i != 0) {\r
577                 clearscreen();\r
578         }\r
579         linecount = 0;\r
580 }\r
581 \r
582 \r
583 void skip(int i) {\r
584         while (i-- > 0) {\r
585                 linecount++;\r
586                 if (linecount >= 23)\r
587                         pause(0);\r
588                 else\r
589                         putchar('\n');\r
590         }\r
591 }\r
592 \r
593 \r
594 void proutn(char *s) {\r
595         fputs(s, stdout);\r
596 }\r
597 \r
598 void prout(char *s) {\r
599         proutn(s);\r
600         skip(1);\r
601 }\r
602 \r
603 void prouts(char *s) {\r
604         clock_t endTime;\r
605         /* print slowly! */\r
606         while (*s) {\r
607                 endTime = clock() + CLOCKS_PER_SEC*0.05;\r
608                 while (clock() < endTime) ;\r
609                 putchar(*s++);\r
610                 fflush(stdout);\r
611         }\r
612 }\r
613 \r
614 void huh(void) {\r
615         chew();\r
616         skip(1);\r
617         prout("Beg your pardon, Captain?");\r
618 }\r
619 \r
620 int isit(char *s) {\r
621         /* New function -- compares s to scaned citem and returns true if it\r
622            matches to the length of s */\r
623 \r
624         return strncmp(s, citem, max(1, strlen(citem))) == 0;\r
625 \r
626 }\r
627 \r
628 #ifdef DEBUG\r
629 void debugme(void) {\r
630         proutn("Reset levels? ");\r
631         if (ja() != 0) {\r
632                 if (energy < inenrg) energy = inenrg;\r
633                 shield = inshld;\r
634                 torps = intorps;\r
635                 lsupres = inlsr;\r
636         }\r
637         proutn("Reset damage? ");\r
638         if (ja() != 0) {\r
639                 int i;\r
640                 for (i=0; i <= ndevice; i++) if (damage[i] > 0.0) damage[i] = 0.0;\r
641                 stdamtim = 1e30;\r
642         }\r
643         proutn("Toggle idebug? ");\r
644         if (ja() != 0) {\r
645                 idebug = !idebug;\r
646                 if (idebug) prout("Debug output ON");\r
647                 else prout("Debug output OFF");\r
648         }\r
649         proutn("Cause selective damage? ");\r
650         if (ja() != 0) {\r
651                 int i, key;\r
652                 for (i=1; i <= ndevice; i++) {\r
653                         proutn("Kill ");\r
654                         proutn(device[i]);\r
655                         proutn("? ");\r
656                         chew();\r
657                         key = scan();\r
658                         if (key == IHALPHA &&  isit("y")) {\r
659                                 damage[i] = 10.0;\r
660                                 if (i == DRADIO) stdamtim = d.date;\r
661                         }\r
662                 }\r
663         }\r
664         proutn("Examine/change events? ");\r
665         if (ja() != 0) {\r
666                 int i;\r
667                 for (i = 1; i < NEVENTS; i++) {\r
668                         int key;\r
669                         if (future[i] == 1e30) continue;\r
670                         switch (i) {\r
671                                 case FSNOVA:  proutn("Supernova       "); break;\r
672                                 case FTBEAM:  proutn("T Beam          "); break;\r
673                                 case FSNAP:   proutn("Snapshot        "); break;\r
674                                 case FBATTAK: proutn("Base Attack     "); break;\r
675                                 case FCDBAS:  proutn("Base Destroy    "); break;\r
676                                 case FSCMOVE: proutn("SC Move         "); break;\r
677                                 case FSCDBAS: proutn("SC Base Destroy "); break;\r
678                         }\r
679                         cramf(future[i]-d.date, 8, 2);\r
680                         chew();\r
681                         proutn("  ?");\r
682                         key = scan();\r
683                         if (key == IHREAL) {\r
684                                 future[i] = d.date + aaitem;\r
685                         }\r
686                 }\r
687                 chew();\r
688         }\r
689 }\r
690                         \r
691 \r
692 #endif\r