Another step in consolidating the saved-state globals.
[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 /* Compared to original version, I've changed the "help" command to\r
17    "call" and the "terminate" command to "quit" to better match\r
18    user expectations. The DECUS version apparently made those changes\r
19    as well as changing "freeze" to "save". However I like "freeze".\r
20 \r
21    When I got a later version of Super Star Trek that I was converting\r
22    from, I added the emexit command.\r
23 \r
24    That later version also mentions srscan and lrscan working when\r
25    docked (using the starbase's scanners), so I made some changes here\r
26    to do this (and indicating that fact to the player), and then realized\r
27    the base would have a subspace radio as well -- doing a Chart when docked\r
28    updates the star chart, and all radio reports will be heard. The Dock\r
29    command will also give a report if a base is under attack.\r
30 \r
31    Movecom no longer reports movement if sensors are damaged so you wouldn't\r
32    otherwise know it.\r
33 \r
34    Also added:\r
35 \r
36    1. Better base positioning at startup\r
37 \r
38    2. deathray improvement (but keeping original failure alternatives)\r
39 \r
40    3. Tholian Web\r
41 \r
42    4. Enemies can ram the Enterprise. Regular Klingons and Romulans can\r
43       move in Expert and Emeritus games. This code could use improvement.\r
44 \r
45    5. The deep space probe looks interesting! DECUS version\r
46 \r
47    6. Perhaps cloaking to be added later? BSD version\r
48 \r
49 \r
50    */\r
51 \r
52 \r
53 static char *commands[] = {\r
54         "srscan",\r
55         "lrscan",\r
56         "phasers",\r
57         "photons",\r
58         "move",\r
59         "shields",\r
60         "dock",\r
61         "damages",\r
62         "chart",\r
63         "impulse",\r
64         "rest",\r
65         "warp",\r
66         "status",\r
67         "sensors",\r
68         "orbit",\r
69         "transport",\r
70         "mine",\r
71         "crystals",\r
72         "shuttle",\r
73         "planets",\r
74         "request",\r
75         "report",\r
76         "computer",\r
77         "commands",\r
78     "emexit",\r
79     "probe",\r
80         "abandon",\r
81         "destruct",\r
82         "freeze",\r
83         "deathray",\r
84         "debug",\r
85         "call",\r
86         "quit",\r
87         "help"\r
88 };\r
89 #define NUMCOMMANDS     sizeof(commands)/sizeof(commands[0])\r
90 \r
91 static void listCommands(int x) {\r
92         prout("   SRSCAN    MOVE      PHASERS   CALL\n"\r
93                   "   STATUS    IMPULSE   PHOTONS   ABANDON\n"\r
94                   "   LRSCAN    WARP      SHIELDS   DESTRUCT\n"\r
95                   "   CHART     REST      DOCK      QUIT\n"\r
96                   "   DAMAGES   REPORT    SENSORS   ORBIT\n"\r
97                   "   TRANSPORT MINE      CRYSTALS  SHUTTLE\n"\r
98                   "   PLANETS   REQUEST   DEATHRAY  FREEZE\n"\r
99                   "   COMPUTER  EMEXIT    PROBE     COMMANDS");\r
100         if (x) prout("   HELP");\r
101 }\r
102 \r
103 static void helpme(void) {\r
104         int i, j;\r
105         char cmdbuf[32];\r
106         char linebuf[132];\r
107         FILE *fp;\r
108         /* Give help on commands */\r
109         int key;\r
110         key = scan();\r
111         while (TRUE) {\r
112                 if (key == IHEOL) {\r
113                         proutn("Help on what command?");\r
114                         key = scan();\r
115                 }\r
116                 if (key == IHEOL) return;\r
117                 for (i = 0; i < NUMCOMMANDS; i++) {\r
118                         if (strcmp(commands[i], citem)==0) break;\r
119                 }\r
120                 if (i != NUMCOMMANDS) break;\r
121                 skip(1);\r
122                 prout("Valid commands:");\r
123                 listCommands(FALSE);\r
124                 key = IHEOL;\r
125                 chew();\r
126                 skip(1);\r
127         }\r
128         if (i == 23) {\r
129                 strcpy(cmdbuf, " ABBREV");\r
130         }\r
131         else {\r
132                 strcpy(cmdbuf, "  Mnemonic:  ");\r
133                 j = 0;\r
134                 while ((cmdbuf[j+13] = toupper(commands[i][j])) != 0) j++;\r
135         }\r
136         fp = fopen("sst.doc", "r");\r
137         if (fp == NULL) {\r
138                 prout("Spock-  \"Captain, that information is missing from the");\r
139                 prout("   computer. You need to find SST.DOC and put it in the");\r
140                 prout("   current directory.\"");\r
141                 return;\r
142         }\r
143         i = strlen(cmdbuf);\r
144         do {\r
145                 if (fgets(linebuf, 132, 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         } while (strncmp(linebuf, cmdbuf, i) != 0);\r
151 \r
152         skip(1);\r
153         prout("Spock- \"Captain, I've found the following information:\"");\r
154         skip(1);\r
155 \r
156         do {\r
157                 if (linebuf[0]!=12) { // ignore page break lines \r
158                         linebuf[strlen(linebuf)-1] = '\0'; // No \n at end\r
159                         prout(linebuf);\r
160                 }\r
161                 fgets(linebuf,132,fp);\r
162         } while (strstr(linebuf, "******")==NULL);\r
163         fclose(fp);\r
164 }\r
165 \r
166 static void makemoves(void) {\r
167         int i, hitme;\r
168         char ch;\r
169         while (TRUE) { /* command loop */\r
170                 hitme = FALSE;\r
171                 justin = 0;\r
172                 Time = 0.0;\r
173                 i = -1;\r
174                 while (TRUE)  { /* get a command */\r
175                         chew();\r
176                         skip(1);\r
177                         proutn("COMMAND> ");\r
178                         if (scan() == IHEOL) continue;\r
179                         for (i=0; i < 26; i++)\r
180                                 if (isit(commands[i]))\r
181                                         break;\r
182                         if (i < 26) break;\r
183                         for (; i < NUMCOMMANDS; i++)\r
184                                 if (strcmp(commands[i], citem) == 0) break;\r
185                         if (i < NUMCOMMANDS) break;\r
186 \r
187                         if (skill <= 2)  {\r
188                                 prout("UNRECOGNIZED COMMAND. LEGAL COMMANDS ARE:");\r
189                                 listCommands(TRUE);\r
190                         }\r
191                         else prout("UNRECOGNIZED COMMAND.");\r
192                 }\r
193                 switch (i) { /* command switch */\r
194                         case 0:                 // srscan\r
195                                 srscan(1);\r
196                                 break;\r
197                         case 1:                 // lrscan\r
198                                 lrscan();\r
199                                 break;\r
200                         case 2:                 // phasers\r
201                                 phasers();\r
202                                 if (ididit) hitme = TRUE;\r
203                                 break;\r
204                         case 3:                 // photons\r
205                                 photon();\r
206                                 if (ididit) hitme = TRUE;\r
207                                 break;\r
208                         case 4:                 // move\r
209                                 warp(1);\r
210                                 break;\r
211                         case 5:                 // shields\r
212                                 doshield(1);\r
213                                 if (ididit) {\r
214                                         attack(2);\r
215                                         shldchg = 0;\r
216                                 }\r
217                                 break;\r
218                         case 6:                 // dock\r
219                                 dock();\r
220                                 break;\r
221                         case 7:                 // damages\r
222                                 dreprt();\r
223                                 break;\r
224                         case 8:                 // chart\r
225                                 chart(0);\r
226                                 break;\r
227                         case 9:                 // impulse\r
228                                 impuls();\r
229                                 break;\r
230                         case 10:                // rest\r
231                                 wait();\r
232                                 if (ididit) hitme = TRUE;\r
233                                 break;\r
234                         case 11:                // warp\r
235                                 setwrp();\r
236                                 break;\r
237                         case 12:                // status\r
238                                 srscan(3);\r
239                                 break;\r
240                         case 13:                        // sensors\r
241                                 sensor();\r
242                                 break;\r
243                         case 14:                        // orbit\r
244                                 orbit();\r
245                                 if (ididit) hitme = TRUE;\r
246                                 break;\r
247                         case 15:                        // transport "beam"\r
248                                 beam();\r
249                                 break;\r
250                         case 16:                        // mine\r
251                                 mine();\r
252                                 if (ididit) hitme = TRUE;\r
253                                 break;\r
254                         case 17:                        // crystals\r
255                                 usecrystals();\r
256                                 break;\r
257                         case 18:                        // shuttle\r
258                                 shuttle();\r
259                                 if (ididit) hitme = TRUE;\r
260                                 break;\r
261                         case 19:                        // Planet list\r
262                                 preport();\r
263                                 break;\r
264                         case 20:                        // Status information\r
265                                 srscan(2);\r
266                                 break;\r
267                         case 21:                        // Game Report \r
268                                 report(0);\r
269                                 break;\r
270                         case 22:                        // use COMPUTER!\r
271                                 eta();\r
272                                 break;\r
273                         case 23:\r
274                                 listCommands(TRUE);\r
275                                 break;\r
276                         case 24:                // Emergency exit\r
277                                 clearscreen();  // Hide screen\r
278                                 freeze(TRUE);   // forced save\r
279                                 exit(1);                // And quick exit\r
280                                 break;\r
281                         case 25:\r
282                                 probe();                // Launch probe\r
283                                 break;\r
284                         case 26:                        // Abandon Ship\r
285                                 abandn();\r
286                                 break;\r
287                         case 27:                        // Self Destruct\r
288                                 dstrct();\r
289                                 break;\r
290                         case 28:                        // Save Game\r
291                                 freeze(FALSE);\r
292                                 if (skill > 3)\r
293                                         prout("WARNING--Frozen games produce no plaques!");\r
294                                 break;\r
295                         case 29:                        // Try a desparation measure\r
296                                 deathray();\r
297                                 if (ididit) hitme = TRUE;\r
298                                 break;\r
299                         case 30:                        // What do we want for debug???\r
300 #ifdef DEBUG\r
301                                 debugme();\r
302 #endif\r
303                                 break;\r
304                         case 31:                // Call for help\r
305                                 help();\r
306                                 break;\r
307                         case 32:\r
308                                 alldone = 1;    // quit the game\r
309 #ifdef DEBUG\r
310                                 if (idebug) score();\r
311 #endif\r
312                                 break;\r
313                         case 33:\r
314                                 helpme();       // get help\r
315                                 break;\r
316                 }\r
317                 for (;;) {\r
318                         if (alldone) break;             // Game has ended\r
319 #ifdef DEBUG\r
320                         if (idebug) prout("2500");\r
321 #endif\r
322                         if (Time != 0.0) {\r
323                                 events();\r
324                                 if (alldone) break;             // Events did us in\r
325                         }\r
326                         if (game.state.galaxy[quadx][quady] == 1000) { // Galaxy went Nova!\r
327                                 atover(0);\r
328                                 continue;\r
329                         }\r
330                         if (nenhere == 0) movetho();\r
331                         if (hitme && justin==0) {\r
332                                 attack(2);\r
333                                 if (alldone) break;\r
334                                 if (game.state.galaxy[quadx][quady] == 1000) {  // went NOVA! \r
335                                         atover(0);\r
336                                         hitme = TRUE;\r
337                                         continue;\r
338                                 }\r
339                         }\r
340                         break;\r
341                 }\r
342                 if (alldone) break;\r
343         }\r
344 }\r
345 \r
346 \r
347 int main(int argc, char **argv) {\r
348         int i;\r
349         int hitme;\r
350         char ch;\r
351         prelim();\r
352 \r
353         if (argc > 1) {\r
354                 fromcommandline = 1;\r
355                 line[0] = '\0';\r
356                 while (--argc > 0) {\r
357                         strcat(line, *(++argv));\r
358                         strcat(line, " ");\r
359                 }\r
360         }\r
361         else fromcommandline = 0;\r
362 \r
363 \r
364         while (TRUE) { /* Play a game */\r
365                 setup();\r
366                 if (alldone) {\r
367                         score();\r
368                         alldone = 0;\r
369                 }\r
370                 else makemoves();\r
371                 skip(2);\r
372                 stars();\r
373                 skip(1);\r
374 \r
375                 if (tourn && alldone) {\r
376                         printf("Do you want your score recorded?");\r
377                         if (ja()) {\r
378                                 chew2();\r
379                                 freeze(FALSE);\r
380                         }\r
381                 }\r
382                 printf("Do you want to play again?");\r
383                 if (!ja()) break;\r
384         }\r
385         skip(1);\r
386         prout("May the Great Bird of the Galaxy roost upon your home planet.");\r
387 }\r
388 \r
389 \r
390 void cramen(int i) {\r
391         /* return an enemy */\r
392         char *s;\r
393         \r
394         switch (i) {\r
395                 case IHR: s = "Romulan"; break;\r
396                 case IHK: s = "Klingon"; break;\r
397                 case IHC: s = "Commander"; break;\r
398                 case IHS: s = "Super-commander"; break;\r
399                 case IHSTAR: s = "Star"; break;\r
400                 case IHP: s = "Planet"; break;\r
401                 case IHB: s = "Starbase"; break;\r
402                 case IHBLANK: s = "Black hole"; break;\r
403                 case IHT: s = "Tholian"; break;\r
404                 case IHWEB: s = "Tholian web"; break;\r
405                 default: s = "Unknown??"; break;\r
406         }\r
407         proutn(s);\r
408 }\r
409 \r
410 void cramlc(int key, int x, int y) {\r
411         if (key == 1) proutn(" Quadrant");\r
412         else if (key == 2) proutn(" Sector");\r
413         proutn(" ");\r
414         crami(x, 1);\r
415         proutn(" - ");\r
416         crami(y, 1);\r
417 }\r
418 \r
419 void crmena(int i, int enemy, int key, int x, int y) {\r
420         if (i == 1) proutn("***");\r
421         cramen(enemy);\r
422         proutn(" at");\r
423         cramlc(key, x, y);\r
424 }\r
425 \r
426 void crmshp(void) {\r
427         char *s;\r
428         switch (ship) {\r
429                 case IHE: s = "Enterprise"; break;\r
430                 case IHF: s = "Faerie Queene"; break;\r
431                 default:  s = "Ship???"; break;\r
432         }\r
433         proutn(s);\r
434 }\r
435 \r
436 void stars(void) {\r
437         prouts("******************************************************");\r
438         skip(1);\r
439 }\r
440 \r
441 double expran(double avrage) {\r
442         return -avrage*log(1e-7 + Rand());\r
443 }\r
444 \r
445 double Rand(void) {\r
446         return rand()/(1.0 + (double)RAND_MAX);\r
447 }\r
448 \r
449 void iran8(int *i, int *j) {\r
450         *i = Rand()*8.0 + 1.0;\r
451         *j = Rand()*8.0 + 1.0;\r
452 }\r
453 \r
454 void iran10(int *i, int *j) {\r
455         *i = Rand()*10.0 + 1.0;\r
456         *j = Rand()*10.0 + 1.0;\r
457 }\r
458 \r
459 void chew(void) {\r
460         linecount = 0;\r
461         linep = line;\r
462         *linep = 0;\r
463 }\r
464 \r
465 void chew2(void) {\r
466         /* return IHEOL next time */\r
467         linecount = 0;\r
468         linep = line+1;\r
469         *linep = 0;\r
470 }\r
471 \r
472 int scan(void) {\r
473         int i;\r
474         char *cp;\r
475 \r
476         linecount = 0;\r
477 \r
478         // Init result\r
479         aaitem = 0.0;\r
480         *citem = 0;\r
481 \r
482         // Read a line if nothing here\r
483         if (*linep == 0) {\r
484                 if (linep != line) {\r
485                         chew();\r
486                         return IHEOL;\r
487                 }\r
488                 fgets(line, sizeof(line), stdin);\r
489                 line[strlen(line)-1] = '\0';\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 = game.state.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]-game.state.date, 8, 2);\r
680                         chew();\r
681                         proutn("  ?");\r
682                         key = scan();\r
683                         if (key == IHREAL) {\r
684                                 future[i] = game.state.date + aaitem;\r
685                         }\r
686                 }\r
687                 chew();\r
688         }\r
689 }\r
690                         \r
691 \r
692 #endif\r