printfs have been banished. All outputnow goes through prout/proutn/prouts.
[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 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(" %d - %d", x, y);\r
415 }\r
416 \r
417 void crmena(int i, int enemy, int key, int x, int y) {\r
418         if (i == 1) proutn("***");\r
419         cramen(enemy);\r
420         proutn(" at");\r
421         cramlc(key, x, y);\r
422 }\r
423 \r
424 void crmshp(void) {\r
425         char *s;\r
426         switch (ship) {\r
427                 case IHE: s = "Enterprise"; break;\r
428                 case IHF: s = "Faerie Queene"; break;\r
429                 default:  s = "Ship???"; break;\r
430         }\r
431         proutn(s);\r
432 }\r
433 \r
434 void stars(void) {\r
435         prouts("******************************************************");\r
436         skip(1);\r
437 }\r
438 \r
439 double expran(double avrage) {\r
440         return -avrage*log(1e-7 + Rand());\r
441 }\r
442 \r
443 double Rand(void) {\r
444         return rand()/(1.0 + (double)RAND_MAX);\r
445 }\r
446 \r
447 void iran8(int *i, int *j) {\r
448         *i = Rand()*8.0 + 1.0;\r
449         *j = Rand()*8.0 + 1.0;\r
450 }\r
451 \r
452 void iran10(int *i, int *j) {\r
453         *i = Rand()*10.0 + 1.0;\r
454         *j = Rand()*10.0 + 1.0;\r
455 }\r
456 \r
457 void chew(void) {\r
458         linecount = 0;\r
459         linep = line;\r
460         *linep = 0;\r
461 }\r
462 \r
463 void chew2(void) {\r
464         /* return IHEOL next time */\r
465         linecount = 0;\r
466         linep = line+1;\r
467         *linep = 0;\r
468 }\r
469 \r
470 int scan(void) {\r
471         int i;\r
472         char *cp;\r
473 \r
474         linecount = 0;\r
475 \r
476         // Init result\r
477         aaitem = 0.0;\r
478         *citem = 0;\r
479 \r
480         // Read a line if nothing here\r
481         if (*linep == 0) {\r
482                 if (linep != line) {\r
483                         chew();\r
484                         return IHEOL;\r
485                 }\r
486                 fgets(line, sizeof(line), stdin);\r
487                 line[strlen(line)-1] = '\0';\r
488                 linep = line;\r
489         }\r
490         // Skip leading white space\r
491         while (*linep == ' ') linep++;\r
492         // Nothing left\r
493         if (*linep == 0) {\r
494                 chew();\r
495                 return IHEOL;\r
496         }\r
497         if (isdigit(*linep) || *linep=='+' || *linep=='-' || *linep=='.') {\r
498                 // treat as a number\r
499             if (sscanf(linep, "%lf%n", &aaitem, &i) < 1) {\r
500                 linep = line; // Invalid numbers are ignored\r
501                 *linep = 0;\r
502                 return IHEOL;\r
503             }\r
504             else {\r
505                 // skip to end\r
506                 linep += i;\r
507                 return IHREAL;\r
508             }\r
509         }\r
510         // Treat as alpha\r
511         cp = citem;\r
512         while (*linep && *linep!=' ') {\r
513                 if ((cp - citem) < 9) *cp++ = tolower(*linep);\r
514                 linep++;\r
515         }\r
516         *cp = 0;\r
517         return IHALPHA;\r
518 }\r
519 \r
520 int ja(void) {\r
521         chew();\r
522         while (TRUE) {\r
523                 scan();\r
524                 chew();\r
525                 if (*citem == 'y') return TRUE;\r
526                 if (*citem == 'n') return FALSE;\r
527                 proutn("Please answer with \"Y\" or \"N\":");\r
528         }\r
529 }\r
530 \r
531 void cramf(double x, int w, int d) {\r
532         char buf[64];\r
533         sprintf(buf, "%*.*f", w, d, x);\r
534         proutn(buf);\r
535 }\r
536 \r
537 double square(double i) { return i*i; }\r
538                                                                         \r
539 static void clearscreen(void) {\r
540         /* Somehow we need to clear the screen */\r
541 #ifdef __BORLANDC__\r
542         extern void clrscr(void);\r
543         clrscr();\r
544 #else\r
545         proutn("\033[2J");      /* Hope for an ANSI display */\r
546 #endif\r
547 }\r
548 \r
549 /* We will pull these out in case we want to do something special later */\r
550 \r
551 void pause(int i) {\r
552         putchar('\n');\r
553         if (i==1) {\r
554                 if (skill > 2)\r
555                         prout("[ANOUNCEMENT ARRIVING...]");\r
556                 else\r
557                         prout("[IMPORTANT ANNOUNCEMENT ARRIVING -- HIT SPACE BAR TO CONTINUE]");\r
558                 getch();\r
559         }\r
560         else {\r
561                 if (skill > 2)\r
562                         proutn("[CONTINUE?]");\r
563                 else\r
564                         proutn("[HIT SPACE BAR TO CONTINUE]");\r
565                 getch();\r
566                 proutn("\r                           \r");\r
567         }\r
568         if (i != 0) {\r
569                 clearscreen();\r
570         }\r
571         linecount = 0;\r
572 }\r
573 \r
574 \r
575 void skip(int i) {\r
576         while (i-- > 0) {\r
577                 linecount++;\r
578                 if (linecount >= 23)\r
579                         pause(0);\r
580                 else\r
581                         putchar('\n');\r
582         }\r
583 }\r
584 \r
585 \r
586 void proutn(char *fmt, ...) {\r
587     va_list ap;\r
588     va_start(ap, fmt);\r
589     vprintf(fmt, ap);\r
590     va_end(ap);\r
591 }\r
592 \r
593 void prout(char *fmt, ...) {\r
594     va_list ap;\r
595     va_start(ap, fmt);\r
596     vprintf(fmt, ap);\r
597     va_end(ap);\r
598     skip(1);\r
599 }\r
600 \r
601 void prouts(char *fmt, ...) {\r
602         clock_t endTime;\r
603         char *s, buf[BUFSIZ];\r
604         /* print slowly! */\r
605         va_list ap;\r
606         va_start(ap, fmt);\r
607         vsprintf(buf, fmt, ap);\r
608         va_end(ap);\r
609         skip(1);\r
610         for (s = buf; *s; s++) {\r
611                 endTime = clock() + CLOCKS_PER_SEC*0.05;\r
612                 while (clock() < endTime) ;\r
613                 putchar(*s);\r
614                 fflush(stdout);\r
615         }\r
616 }\r
617 \r
618 void huh(void) {\r
619         chew();\r
620         skip(1);\r
621         prout("Beg your pardon, Captain?");\r
622 }\r
623 \r
624 int isit(char *s) {\r
625         /* New function -- compares s to scaned citem and returns true if it\r
626            matches to the length of s */\r
627 \r
628         return strncmp(s, citem, max(1, strlen(citem))) == 0;\r
629 \r
630 }\r
631 \r
632 #ifdef DEBUG\r
633 void debugme(void) {\r
634         proutn("Reset levels? ");\r
635         if (ja() != 0) {\r
636                 if (energy < inenrg) energy = inenrg;\r
637                 shield = inshld;\r
638                 torps = intorps;\r
639                 lsupres = inlsr;\r
640         }\r
641         proutn("Reset damage? ");\r
642         if (ja() != 0) {\r
643                 int i;\r
644                 for (i=0; i <= NDEVICES; i++) if (damage[i] > 0.0) damage[i] = 0.0;\r
645                 stdamtim = 1e30;\r
646         }\r
647         proutn("Toggle idebug? ");\r
648         if (ja() != 0) {\r
649                 idebug = !idebug;\r
650                 if (idebug) prout("Debug output ON");\r
651                 else prout("Debug output OFF");\r
652         }\r
653         proutn("Cause selective damage? ");\r
654         if (ja() != 0) {\r
655                 int i, key;\r
656                 for (i=1; i <= NDEVICES; i++) {\r
657                         proutn("Kill ");\r
658                         proutn(device[i]);\r
659                         proutn("? ");\r
660                         chew();\r
661                         key = scan();\r
662                         if (key == IHALPHA &&  isit("y")) {\r
663                                 damage[i] = 10.0;\r
664                                 if (i == DRADIO) stdamtim = game.state.date;\r
665                         }\r
666                 }\r
667         }\r
668         proutn("Examine/change events? ");\r
669         if (ja() != 0) {\r
670                 int i;\r
671                 for (i = 1; i < NEVENTS; i++) {\r
672                         int key;\r
673                         if (future[i] == 1e30) continue;\r
674                         switch (i) {\r
675                                 case FSNOVA:  proutn("Supernova       "); break;\r
676                                 case FTBEAM:  proutn("T Beam          "); break;\r
677                                 case FSNAP:   proutn("Snapshot        "); break;\r
678                                 case FBATTAK: proutn("Base Attack     "); break;\r
679                                 case FCDBAS:  proutn("Base Destroy    "); break;\r
680                                 case FSCMOVE: proutn("SC Move         "); break;\r
681                                 case FSCDBAS: proutn("SC Base Destroy "); break;\r
682                         }\r
683                         cramf(future[i]-game.state.date, 8, 2);\r
684                         chew();\r
685                         proutn("  ?");\r
686                         key = scan();\r
687                         if (key == IHREAL) {\r
688                                 future[i] = game.state.date + aaitem;\r
689                         }\r
690                 }\r
691                 chew();\r
692         }\r
693 }\r
694                         \r
695 \r
696 #endif\r