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