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