Better project documentation.
[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], *cp;\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             for (j = 0; commands[i][j]; j++)\r
134                 cmdbuf[j] = toupper(commands[i][j]);\r
135             cmdbuf[j] = '\0';\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         for (;;) {\r
145             if (fgets(linebuf, sizeof(linebuf), fp) == NULL) {\r
146                         prout("Spock- \"Captain, there is no information on that command.\"");\r
147                         fclose(fp);\r
148                         return;\r
149                 }\r
150             if (linebuf[0] == '%' && linebuf[1] == '%'&& linebuf[2] == ' ') {\r
151                 for (cp = linebuf+3; isspace(*cp); cp++)\r
152                         continue;\r
153                 linebuf[strlen(linebuf)-1] = '\0';\r
154                 if (strcmp(cp, cmdbuf) == 0)\r
155                     break;\r
156             }\r
157         }\r
158 \r
159         skip(1);\r
160         prout("Spock- \"Captain, I've found the following information:\"");\r
161         skip(1);\r
162 \r
163         while (fgets(linebuf, sizeof(linebuf),fp)) {\r
164                 if (strstr(linebuf, "******"))\r
165                         break;\r
166                 linebuf[strlen(linebuf)-1] = '\0'; // No \n at end\r
167                 prout(linebuf);\r
168         }\r
169         fclose(fp);\r
170 }\r
171 \r
172 static void makemoves(void) {\r
173         int i, hitme;\r
174         char ch;\r
175         while (TRUE) { /* command loop */\r
176                 hitme = FALSE;\r
177                 justin = 0;\r
178                 Time = 0.0;\r
179                 i = -1;\r
180                 while (TRUE)  { /* get a command */\r
181                         chew();\r
182                         skip(1);\r
183                         proutn("COMMAND> ");\r
184                         if (scan() == IHEOL) continue;\r
185                         for (i=0; i < 26; i++)\r
186                                 if (isit(commands[i]))\r
187                                         break;\r
188                         if (i < 26) break;\r
189                         for (; i < NUMCOMMANDS; i++)\r
190                                 if (strcmp(commands[i], citem) == 0) break;\r
191                         if (i < NUMCOMMANDS) break;\r
192 \r
193                         if (skill <= 2)  {\r
194                                 prout("UNRECOGNIZED COMMAND. LEGAL COMMANDS ARE:");\r
195                                 listCommands(TRUE);\r
196                         }\r
197                         else prout("UNRECOGNIZED COMMAND.");\r
198                 }\r
199                 switch (i) { /* command switch */\r
200                         case 0:                 // srscan\r
201                                 srscan(1);\r
202                                 break;\r
203                         case 1:                 // lrscan\r
204                                 lrscan();\r
205                                 break;\r
206                         case 2:                 // phasers\r
207                                 phasers();\r
208                                 if (ididit) hitme = TRUE;\r
209                                 break;\r
210                         case 3:                 // photons\r
211                                 photon();\r
212                                 if (ididit) hitme = TRUE;\r
213                                 break;\r
214                         case 4:                 // move\r
215                                 warp(1);\r
216                                 break;\r
217                         case 5:                 // shields\r
218                                 doshield(1);\r
219                                 if (ididit) {\r
220                                         attack(2);\r
221                                         shldchg = 0;\r
222                                 }\r
223                                 break;\r
224                         case 6:                 // dock\r
225                                 dock();\r
226                                 break;\r
227                         case 7:                 // damages\r
228                                 dreprt();\r
229                                 break;\r
230                         case 8:                 // chart\r
231                                 chart(0);\r
232                                 break;\r
233                         case 9:                 // impulse\r
234                                 impuls();\r
235                                 break;\r
236                         case 10:                // rest\r
237                                 wait();\r
238                                 if (ididit) hitme = TRUE;\r
239                                 break;\r
240                         case 11:                // warp\r
241                                 setwrp();\r
242                                 break;\r
243                         case 12:                // status\r
244                                 srscan(3);\r
245                                 break;\r
246                         case 13:                        // sensors\r
247                                 sensor();\r
248                                 break;\r
249                         case 14:                        // orbit\r
250                                 orbit();\r
251                                 if (ididit) hitme = TRUE;\r
252                                 break;\r
253                         case 15:                        // transport "beam"\r
254                                 beam();\r
255                                 break;\r
256                         case 16:                        // mine\r
257                                 mine();\r
258                                 if (ididit) hitme = TRUE;\r
259                                 break;\r
260                         case 17:                        // crystals\r
261                                 usecrystals();\r
262                                 break;\r
263                         case 18:                        // shuttle\r
264                                 shuttle();\r
265                                 if (ididit) hitme = TRUE;\r
266                                 break;\r
267                         case 19:                        // Planet list\r
268                                 preport();\r
269                                 break;\r
270                         case 20:                        // Status information\r
271                                 srscan(2);\r
272                                 break;\r
273                         case 21:                        // Game Report \r
274                                 report(0);\r
275                                 break;\r
276                         case 22:                        // use COMPUTER!\r
277                                 eta();\r
278                                 break;\r
279                         case 23:\r
280                                 listCommands(TRUE);\r
281                                 break;\r
282                         case 24:                // Emergency exit\r
283                                 clearscreen();  // Hide screen\r
284                                 freeze(TRUE);   // forced save\r
285                                 exit(1);                // And quick exit\r
286                                 break;\r
287                         case 25:\r
288                                 probe();                // Launch probe\r
289                                 break;\r
290                         case 26:                        // Abandon Ship\r
291                                 abandn();\r
292                                 break;\r
293                         case 27:                        // Self Destruct\r
294                                 dstrct();\r
295                                 break;\r
296                         case 28:                        // Save Game\r
297                                 freeze(FALSE);\r
298                                 if (skill > 3)\r
299                                         prout("WARNING--Frozen games produce no plaques!");\r
300                                 break;\r
301                         case 29:                        // Try a desparation measure\r
302                                 deathray();\r
303                                 if (ididit) hitme = TRUE;\r
304                                 break;\r
305                         case 30:                        // What do we want for debug???\r
306 #ifdef DEBUG\r
307                                 debugme();\r
308 #endif\r
309                                 break;\r
310                         case 31:                // Call for help\r
311                                 help();\r
312                                 break;\r
313                         case 32:\r
314                                 alldone = 1;    // quit the game\r
315 #ifdef DEBUG\r
316                                 if (idebug) score();\r
317 #endif\r
318                                 break;\r
319                         case 33:\r
320                                 helpme();       // get help\r
321                                 break;\r
322                 }\r
323                 for (;;) {\r
324                         if (alldone) break;             // Game has ended\r
325 #ifdef DEBUG\r
326                         if (idebug) prout("2500");\r
327 #endif\r
328                         if (Time != 0.0) {\r
329                                 events();\r
330                                 if (alldone) break;             // Events did us in\r
331                         }\r
332                         if (game.state.galaxy[quadx][quady] == 1000) { // Galaxy went Nova!\r
333                                 atover(0);\r
334                                 continue;\r
335                         }\r
336                         if (nenhere == 0) movetho();\r
337                         if (hitme && justin==0) {\r
338                                 attack(2);\r
339                                 if (alldone) break;\r
340                                 if (game.state.galaxy[quadx][quady] == 1000) {  // went NOVA! \r
341                                         atover(0);\r
342                                         hitme = TRUE;\r
343                                         continue;\r
344                                 }\r
345                         }\r
346                         break;\r
347                 }\r
348                 if (alldone) break;\r
349         }\r
350 }\r
351 \r
352 \r
353 int main(int argc, char **argv) {\r
354         int i;\r
355         int hitme;\r
356         char ch;\r
357         prelim();\r
358 \r
359         if (argc > 1) {\r
360                 fromcommandline = 1;\r
361                 line[0] = '\0';\r
362                 while (--argc > 0) {\r
363                         strcat(line, *(++argv));\r
364                         strcat(line, " ");\r
365                 }\r
366         }\r
367         else fromcommandline = 0;\r
368 \r
369 \r
370         while (TRUE) { /* Play a game */\r
371                 setup();\r
372                 if (alldone) {\r
373                         score();\r
374                         alldone = 0;\r
375                 }\r
376                 else makemoves();\r
377                 skip(2);\r
378                 stars();\r
379                 skip(1);\r
380 \r
381                 if (tourn && alldone) {\r
382                         proutn("Do you want your score recorded?");\r
383                         if (ja()) {\r
384                                 chew2();\r
385                                 freeze(FALSE);\r
386                         }\r
387                 }\r
388                 proutn("Do you want to play again?");\r
389                 if (!ja()) break;\r
390         }\r
391         skip(1);\r
392         prout("May the Great Bird of the Galaxy roost upon your home planet.");\r
393 }\r
394 \r
395 \r
396 void cramen(int i) {\r
397         /* return an enemy */\r
398         char *s;\r
399         \r
400         switch (i) {\r
401                 case IHR: s = "Romulan"; break;\r
402                 case IHK: s = "Klingon"; break;\r
403                 case IHC: s = "Commander"; break;\r
404                 case IHS: s = "Super-commander"; break;\r
405                 case IHSTAR: s = "Star"; break;\r
406                 case IHP: s = "Planet"; break;\r
407                 case IHB: s = "Starbase"; break;\r
408                 case IHBLANK: s = "Black hole"; break;\r
409                 case IHT: s = "Tholian"; break;\r
410                 case IHWEB: s = "Tholian web"; break;\r
411                 default: s = "Unknown??"; break;\r
412         }\r
413         proutn(s);\r
414 }\r
415 \r
416 char *cramlc(enum loctype key, int x, int y) {\r
417         static char buf[32];\r
418         buf[0] = '\0';\r
419         if (key == quadrant) strcpy(buf, "Quadrant ");\r
420         else if (key == sector) strcpy(buf, "Sector ");\r
421         sprintf(buf+strlen(buf), "%d-%d", x, y);\r
422         return buf;\r
423 }\r
424 \r
425 void crmena(int i, int enemy, int key, int x, int y) {\r
426         if (i == 1) proutn("***");\r
427         cramen(enemy);\r
428         proutn(" at");\r
429         proutn(cramlc(key, x, y));\r
430 }\r
431 \r
432 void crmshp(void) {\r
433         char *s;\r
434         switch (ship) {\r
435                 case IHE: s = "Enterprise"; break;\r
436                 case IHF: s = "Faerie Queene"; break;\r
437                 default:  s = "Ship???"; break;\r
438         }\r
439         proutn(s);\r
440 }\r
441 \r
442 void stars(void) {\r
443         prouts("******************************************************");\r
444         skip(1);\r
445 }\r
446 \r
447 double expran(double avrage) {\r
448         return -avrage*log(1e-7 + Rand());\r
449 }\r
450 \r
451 double Rand(void) {\r
452         return rand()/(1.0 + (double)RAND_MAX);\r
453 }\r
454 \r
455 void iran8(int *i, int *j) {\r
456         *i = Rand()*8.0 + 1.0;\r
457         *j = Rand()*8.0 + 1.0;\r
458 }\r
459 \r
460 void iran10(int *i, int *j) {\r
461         *i = Rand()*10.0 + 1.0;\r
462         *j = Rand()*10.0 + 1.0;\r
463 }\r
464 \r
465 void chew(void) {\r
466         linecount = 0;\r
467         linep = line;\r
468         *linep = 0;\r
469 }\r
470 \r
471 void chew2(void) {\r
472         /* return IHEOL next time */\r
473         linecount = 0;\r
474         linep = line+1;\r
475         *linep = 0;\r
476 }\r
477 \r
478 int scan(void) {\r
479         int i;\r
480         char *cp;\r
481 \r
482         linecount = 0;\r
483 \r
484         // Init result\r
485         aaitem = 0.0;\r
486         *citem = 0;\r
487 \r
488         // Read a line if nothing here\r
489         if (*linep == 0) {\r
490                 if (linep != line) {\r
491                         chew();\r
492                         return IHEOL;\r
493                 }\r
494                 fgets(line, sizeof(line), stdin);\r
495                 line[strlen(line)-1] = '\0';\r
496                 linep = line;\r
497         }\r
498         // Skip leading white space\r
499         while (*linep == ' ') linep++;\r
500         // Nothing left\r
501         if (*linep == 0) {\r
502                 chew();\r
503                 return IHEOL;\r
504         }\r
505         if (isdigit(*linep) || *linep=='+' || *linep=='-' || *linep=='.') {\r
506                 // treat as a number\r
507             if (sscanf(linep, "%lf%n", &aaitem, &i) < 1) {\r
508                 linep = line; // Invalid numbers are ignored\r
509                 *linep = 0;\r
510                 return IHEOL;\r
511             }\r
512             else {\r
513                 // skip to end\r
514                 linep += i;\r
515                 return IHREAL;\r
516             }\r
517         }\r
518         // Treat as alpha\r
519         cp = citem;\r
520         while (*linep && *linep!=' ') {\r
521                 if ((cp - citem) < 9) *cp++ = tolower(*linep);\r
522                 linep++;\r
523         }\r
524         *cp = 0;\r
525         return IHALPHA;\r
526 }\r
527 \r
528 int ja(void) {\r
529         chew();\r
530         while (TRUE) {\r
531                 scan();\r
532                 chew();\r
533                 if (*citem == 'y') return TRUE;\r
534                 if (*citem == 'n') return FALSE;\r
535                 proutn("Please answer with \"Y\" or \"N\":");\r
536         }\r
537 }\r
538 \r
539 double square(double i) { return i*i; }\r
540                                                                         \r
541 static void clearscreen(void) {\r
542         /* Somehow we need to clear the screen */\r
543 #ifdef __BORLANDC__\r
544         extern void clrscr(void);\r
545         clrscr();\r
546 #else\r
547         proutn("\033[2J");      /* Hope for an ANSI display */\r
548 #endif\r
549 }\r
550 \r
551 /* We will pull these out in case we want to do something special later */\r
552 \r
553 void pause(int i) {\r
554         putchar('\n');\r
555         if (i==1) {\r
556                 if (skill > 2)\r
557                         prout("[ANOUNCEMENT ARRIVING...]");\r
558                 else\r
559                         prout("[IMPORTANT ANNOUNCEMENT ARRIVING -- HIT SPACE BAR TO CONTINUE]");\r
560                 getch();\r
561         }\r
562         else {\r
563                 if (skill > 2)\r
564                         proutn("[CONTINUE?]");\r
565                 else\r
566                         proutn("[HIT SPACE BAR TO CONTINUE]");\r
567                 getch();\r
568                 proutn("\r                           \r");\r
569         }\r
570         if (i != 0) {\r
571                 clearscreen();\r
572         }\r
573         linecount = 0;\r
574 }\r
575 \r
576 \r
577 void skip(int i) {\r
578         while (i-- > 0) {\r
579                 linecount++;\r
580                 if (linecount >= 23)\r
581                         pause(0);\r
582                 else\r
583                         putchar('\n');\r
584         }\r
585 }\r
586 \r
587 \r
588 void proutn(char *fmt, ...) {\r
589     va_list ap;\r
590     va_start(ap, fmt);\r
591     vprintf(fmt, ap);\r
592     va_end(ap);\r
593 }\r
594 \r
595 void prout(char *fmt, ...) {\r
596     va_list ap;\r
597     va_start(ap, fmt);\r
598     vprintf(fmt, ap);\r
599     va_end(ap);\r
600     skip(1);\r
601 }\r
602 \r
603 void prouts(char *fmt, ...) {\r
604         clock_t endTime;\r
605         char *s, buf[BUFSIZ];\r
606         /* print slowly! */\r
607         va_list ap;\r
608         va_start(ap, fmt);\r
609         vsprintf(buf, fmt, ap);\r
610         va_end(ap);\r
611         skip(1);\r
612         for (s = buf; *s; s++) {\r
613                 endTime = clock() + CLOCKS_PER_SEC*0.05;\r
614                 while (clock() < endTime) ;\r
615                 putchar(*s);\r
616                 fflush(stdout);\r
617         }\r
618 }\r
619 \r
620 void huh(void) {\r
621         chew();\r
622         skip(1);\r
623         prout("Beg your pardon, Captain?");\r
624 }\r
625 \r
626 int isit(char *s) {\r
627         /* New function -- compares s to scaned citem and returns true if it\r
628            matches to the length of s */\r
629 \r
630         return strncmp(s, citem, max(1, strlen(citem))) == 0;\r
631 \r
632 }\r
633 \r
634 #ifdef DEBUG\r
635 void debugme(void) {\r
636         proutn("Reset levels? ");\r
637         if (ja() != 0) {\r
638                 if (energy < inenrg) energy = inenrg;\r
639                 shield = inshld;\r
640                 torps = intorps;\r
641                 lsupres = inlsr;\r
642         }\r
643         proutn("Reset damage? ");\r
644         if (ja() != 0) {\r
645                 int i;\r
646                 for (i=0; i <= NDEVICES; i++) if (damage[i] > 0.0) damage[i] = 0.0;\r
647                 stdamtim = 1e30;\r
648         }\r
649         proutn("Toggle idebug? ");\r
650         if (ja() != 0) {\r
651                 idebug = !idebug;\r
652                 if (idebug) prout("Debug output ON");\r
653                 else prout("Debug output OFF");\r
654         }\r
655         proutn("Cause selective damage? ");\r
656         if (ja() != 0) {\r
657                 int i, key;\r
658                 for (i=1; i <= NDEVICES; i++) {\r
659                         proutn("Kill ");\r
660                         proutn(device[i]);\r
661                         proutn("? ");\r
662                         chew();\r
663                         key = scan();\r
664                         if (key == IHALPHA &&  isit("y")) {\r
665                                 damage[i] = 10.0;\r
666                                 if (i == DRADIO) stdamtim = game.state.date;\r
667                         }\r
668                 }\r
669         }\r
670         proutn("Examine/change events? ");\r
671         if (ja() != 0) {\r
672                 int i;\r
673                 for (i = 1; i < NEVENTS; i++) {\r
674                         int key;\r
675                         if (future[i] == 1e30) continue;\r
676                         switch (i) {\r
677                                 case FSNOVA:  proutn("Supernova       "); break;\r
678                                 case FTBEAM:  proutn("T Beam          "); break;\r
679                                 case FSNAP:   proutn("Snapshot        "); break;\r
680                                 case FBATTAK: proutn("Base Attack     "); break;\r
681                                 case FCDBAS:  proutn("Base Destroy    "); break;\r
682                                 case FSCMOVE: proutn("SC Move         "); break;\r
683                                 case FSCDBAS: proutn("SC Base Destroy "); break;\r
684                         }\r
685                         proutn("%.2f", future[i]-game.state.date);\r
686                         chew();\r
687                         proutn("  ?");\r
688                         key = scan();\r
689                         if (key == IHREAL) {\r
690                                 future[i] = game.state.date + aaitem;\r
691                         }\r
692                 }\r
693                 chew();\r
694         }\r
695 }\r
696                         \r
697 \r
698 #endif\r