050f670edea5668ecae1469b4fe95ad5f9e115f9
[super-star-trek.git] / sst.c
1 #define INCLUDED        // Define externs here\r
2 #include <ctype.h>\r
3 #include <getopt.h>\r
4 #include "sst.h"\r
5 \r
6 #ifndef SSTDOC\r
7 #define SSTDOC  "sst.doc"\r
8 #endif\r
9         \r
10 static char line[128], *linep = line;\r
11 \r
12 /*\r
13 \r
14 Here are Tom Almy's changes:\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 Here are Stas Sergeev's changes (controlled by the proprocessor symbol\r
50 SERGEEV, not yet merged):\r
51 \r
52    1. The Space Thingy can be shoved, if you it ram, and can fire back if \r
53       fired upon.\r
54 \r
55    1 The Tholian can be hit with phasers\r
56 \r
57    2. When you are docked, base covers you with an almost invincible shields \r
58       (a commander can still ram you, or a Romulan can destroy the base,\r
59       or a SCom can even succeed with direct attack IIRC, but this rarely \r
60       happens).\r
61 \r
62     3. SCom can't escape from you if no more enemies remain (without this, \r
63        chasing SCom can take an eternity).\r
64 \r
65     4. Probe target you enter is now the destination quadrant. Before I don't \r
66        remember what it was, but it was something I had difficulty using)\r
67 \r
68     5. Secret password is now autogenerated.\r
69 \r
70     6. "Plaque" is adjusted for A4 paper:)\r
71 \r
72     7. Phasers now tells you how much energy needed, but only if the computer \r
73        is alive.\r
74 \r
75     8. Planets are auto-scanned when you enter the quadrant.\r
76 \r
77     9. Mining or using crystals in presense of enemy now yields an attack.\r
78        There are other minor adjustments to what yields an attack\r
79        and what does not.\r
80 \r
81     10. Ramming a black hole is no longer instant death.  \r
82 \r
83    */\r
84 \r
85 static char *commands[] = {\r
86         "srscan",\r
87         "lrscan",\r
88         "phasers",\r
89         "photons",\r
90         "move",\r
91         "shields",\r
92         "dock",\r
93         "damages",\r
94         "chart",\r
95         "impulse",\r
96         "rest",\r
97         "warp",\r
98         "status",\r
99         "sensors",\r
100         "orbit",\r
101         "transport",\r
102         "mine",\r
103         "crystals",\r
104         "shuttle",\r
105         "planets",\r
106         "request",\r
107         "report",\r
108         "computer",\r
109         "commands",\r
110     "emexit",\r
111     "probe",\r
112         "abandon",\r
113         "destruct",\r
114         "freeze",\r
115         "deathray",\r
116         "debug",\r
117         "call",\r
118         "quit",\r
119         "help"\r
120 };\r
121 #define NUMCOMMANDS     sizeof(commands)/sizeof(commands[0])\r
122 \r
123 static void listCommands(int x) {\r
124         prout("   SRSCAN    MOVE      PHASERS   CALL\n"\r
125                   "   STATUS    IMPULSE   PHOTONS   ABANDON\n"\r
126                   "   LRSCAN    WARP      SHIELDS   DESTRUCT\n"\r
127                   "   CHART     REST      DOCK      QUIT\n"\r
128                   "   DAMAGES   REPORT    SENSORS   ORBIT\n"\r
129                   "   TRANSPORT MINE      CRYSTALS  SHUTTLE\n"\r
130                   "   PLANETS   REQUEST   DEATHRAY  FREEZE\n"\r
131                   "   COMPUTER  EMEXIT    PROBE     COMMANDS");\r
132         if (x) prout("   HELP");\r
133 }\r
134 \r
135 static void helpme(void) {\r
136         int i, j;\r
137         char cmdbuf[32], *cp;\r
138         char linebuf[132];\r
139         FILE *fp;\r
140         /* Give help on commands */\r
141         int key;\r
142         key = scan();\r
143         while (TRUE) {\r
144                 if (key == IHEOL) {\r
145                         proutn("Help on what command?");\r
146                         key = scan();\r
147                 }\r
148                 if (key == IHEOL) return;\r
149                 for (i = 0; i < NUMCOMMANDS; i++) {\r
150                         if (strcmp(commands[i], citem)==0) break;\r
151                 }\r
152                 if (i != NUMCOMMANDS) break;\r
153                 skip(1);\r
154                 prout("Valid commands:");\r
155                 listCommands(FALSE);\r
156                 key = IHEOL;\r
157                 chew();\r
158                 skip(1);\r
159         }\r
160         if (i == 23) {\r
161                 strcpy(cmdbuf, " ABBREV");\r
162         }\r
163         else {\r
164             for (j = 0; commands[i][j]; j++)\r
165                 cmdbuf[j] = toupper(commands[i][j]);\r
166             cmdbuf[j] = '\0';\r
167         }\r
168         fp = fopen(SSTDOC, "r");\r
169         if (fp == NULL) {\r
170                 prout("Spock-  \"Captain, that information is missing from the");\r
171                 prout("   computer. You need to find SST.DOC and put it in the");\r
172                 prout("   current directory.\"");\r
173                 return;\r
174         }\r
175         for (;;) {\r
176             if (fgets(linebuf, sizeof(linebuf), fp) == NULL) {\r
177                         prout("Spock- \"Captain, there is no information on that command.\"");\r
178                         fclose(fp);\r
179                         return;\r
180                 }\r
181             if (linebuf[0] == '%' && linebuf[1] == '%'&& linebuf[2] == ' ') {\r
182                 for (cp = linebuf+3; isspace(*cp); cp++)\r
183                         continue;\r
184                 linebuf[strlen(linebuf)-1] = '\0';\r
185                 if (strcmp(cp, cmdbuf) == 0)\r
186                     break;\r
187             }\r
188         }\r
189 \r
190         skip(1);\r
191         prout("Spock- \"Captain, I've found the following information:\"");\r
192         skip(1);\r
193 \r
194         while (fgets(linebuf, sizeof(linebuf),fp)) {\r
195                 if (strstr(linebuf, "******"))\r
196                         break;\r
197                 proutc(linebuf);\r
198         }\r
199         fclose(fp);\r
200 }\r
201 \r
202 static void makemoves(void) {\r
203         int i, hitme;\r
204         while (TRUE) { /* command loop */\r
205                 hitme = FALSE;\r
206                 justin = 0;\r
207                 Time = 0.0;\r
208                 i = -1;\r
209                 while (TRUE)  { /* get a command */\r
210                         chew();\r
211                         skip(1);\r
212                         proutn("COMMAND> ");\r
213                         if (scan() == IHEOL) continue;\r
214                         for (i=0; i < 26; i++)\r
215                                 if (isit(commands[i]))\r
216                                         break;\r
217                         if (i < 26) break;\r
218                         for (; i < NUMCOMMANDS; i++)\r
219                                 if (strcmp(commands[i], citem) == 0) break;\r
220                         if (i < NUMCOMMANDS) break;\r
221 \r
222                         if (skill <= 2)  {\r
223                                 prout("UNRECOGNIZED COMMAND. LEGAL COMMANDS ARE:");\r
224                                 listCommands(TRUE);\r
225                         }\r
226                         else prout("UNRECOGNIZED COMMAND.");\r
227                 }\r
228                 commandhook(commands[i], TRUE);\r
229                 switch (i) { /* command switch */\r
230                         case 0:                 // srscan\r
231                                 srscan(1);\r
232                                 break;\r
233                         case 1:                 // lrscan\r
234                                 lrscan();\r
235                                 break;\r
236                         case 2:                 // phasers\r
237                                 phasers();\r
238                                 if (ididit) hitme = TRUE;\r
239                                 break;\r
240                         case 3:                 // photons\r
241                                 photon();\r
242                                 if (ididit) hitme = TRUE;\r
243                                 break;\r
244                         case 4:                 // move\r
245                                 warp(1);\r
246                                 break;\r
247                         case 5:                 // shields\r
248                                 doshield(1);\r
249                                 if (ididit) {\r
250                                         attack(2);\r
251                                         shldchg = 0;\r
252                                 }\r
253                                 break;\r
254                         case 6:                 // dock\r
255                                 dock(1);\r
256                                 if (ididit) attack(0);\r
257                                 break;\r
258                         case 7:                 // damages\r
259                                 dreprt();\r
260                                 break;\r
261                         case 8:                 // chart\r
262                                 chart(0);\r
263                                 break;\r
264                         case 9:                 // impulse\r
265                                 impuls();\r
266                                 break;\r
267                         case 10:                // rest\r
268                                 wait();\r
269                                 if (ididit) hitme = TRUE;\r
270                                 break;\r
271                         case 11:                // warp\r
272                                 setwrp();\r
273                                 break;\r
274                         case 12:                // status\r
275                                 srscan(3);\r
276                                 break;\r
277                         case 13:                        // sensors\r
278                                 sensor();\r
279                                 break;\r
280                         case 14:                        // orbit\r
281                                 orbit();\r
282                                 if (ididit) hitme = TRUE;\r
283                                 break;\r
284                         case 15:                        // transport "beam"\r
285                                 beam();\r
286                                 break;\r
287                         case 16:                        // mine\r
288                                 mine();\r
289                                 if (ididit) hitme = TRUE;\r
290                                 break;\r
291                         case 17:                        // crystals\r
292                                 usecrystals();\r
293                                 break;\r
294                         case 18:                        // shuttle\r
295                                 shuttle();\r
296                                 if (ididit) hitme = TRUE;\r
297                                 break;\r
298                         case 19:                        // Planet list\r
299                                 preport();\r
300                                 break;\r
301                         case 20:                        // Status information\r
302                                 srscan(2);\r
303                                 break;\r
304                         case 21:                        // Game Report \r
305                                 report(0);\r
306                                 break;\r
307                         case 22:                        // use COMPUTER!\r
308                                 eta();\r
309                                 break;\r
310                         case 23:\r
311                                 listCommands(TRUE);\r
312                                 break;\r
313                         case 24:                // Emergency exit\r
314                                 clearscreen();  // Hide screen\r
315                                 freeze(TRUE);   // forced save\r
316                                 exit(1);                // And quick exit\r
317                                 break;\r
318                         case 25:\r
319                                 probe();                // Launch probe\r
320                                 break;\r
321                         case 26:                        // Abandon Ship\r
322                                 abandn();\r
323                                 break;\r
324                         case 27:                        // Self Destruct\r
325                                 dstrct();\r
326                                 break;\r
327                         case 28:                        // Save Game\r
328                                 freeze(FALSE);\r
329                                 if (skill > 3)\r
330                                         prout("WARNING--Frozen games produce no plaques!");\r
331                                 break;\r
332                         case 29:                        // Try a desparation measure\r
333                                 deathray();\r
334                                 if (ididit) hitme = TRUE;\r
335                                 break;\r
336                         case 30:                        // What do we want for debug???\r
337 #ifdef DEBUG\r
338                                 debugme();\r
339 #endif\r
340                                 break;\r
341                         case 31:                // Call for help\r
342                                 help();\r
343                                 break;\r
344                         case 32:\r
345                                 alldone = 1;    // quit the game\r
346 #ifdef DEBUG\r
347                                 if (idebug) score();\r
348 #endif\r
349                                 break;\r
350                         case 33:\r
351                                 helpme();       // get help\r
352                                 break;\r
353                 }\r
354                 commandhook(commands[i], FALSE);\r
355                 for (;;) {\r
356                         if (alldone) break;             // Game has ended\r
357 #ifdef DEBUG\r
358                         if (idebug) prout("2500");\r
359 #endif\r
360                         if (Time != 0.0) {\r
361                                 events();\r
362                                 if (alldone) break;             // Events did us in\r
363                         }\r
364                         if (game.state.galaxy[quadx][quady] == 1000) { // Galaxy went Nova!\r
365                                 atover(0);\r
366                                 continue;\r
367                         }\r
368                         if (nenhere == 0) movetho();\r
369                         if (hitme && justin==0) {\r
370                                 attack(2);\r
371                                 if (alldone) break;\r
372                                 if (game.state.galaxy[quadx][quady] == 1000) {  // went NOVA! \r
373                                         atover(0);\r
374                                         hitme = TRUE;\r
375                                         continue;\r
376                                 }\r
377                         }\r
378                         break;\r
379                 }\r
380                 if (alldone) break;\r
381         }\r
382 }\r
383 \r
384 \r
385 int main(int argc, char **argv) {\r
386     int i, option, usecurses = TRUE;\r
387         while ((option = getopt(argc, argv, "t")) != -1) {\r
388             switch (option) {\r
389             case 't':\r
390                 usecurses = FALSE;\r
391                 break;\r
392             default:\r
393                 fprintf(stderr, "usage: sst [-t] [startcommand...].\n");\r
394                 exit(0);\r
395             }\r
396         }\r
397 \r
398         iostart(usecurses);\r
399         prelim(); \r
400         line[0] = '\0';\r
401         for (i = optind; i < argc;  i++) {\r
402                 strcat(line, argv[i]);\r
403                 strcat(line, " ");\r
404         }\r
405         while (TRUE) { /* Play a game */\r
406                 setup(line[0] == '\0');\r
407                 if (alldone) {\r
408                         score();\r
409                         alldone = 0;\r
410                 }\r
411                 else makemoves();\r
412                 skip(2);\r
413                 stars();\r
414                 skip(1);\r
415 \r
416                 if (tourn && alldone) {\r
417                         proutn("Do you want your score recorded?");\r
418                         if (ja()) {\r
419                                 chew2();\r
420                                 freeze(FALSE);\r
421                         }\r
422                 }\r
423                 proutn("Do you want to play again?");\r
424                 if (!ja()) break;\r
425         }\r
426         skip(1);\r
427         ioend();\r
428         puts("May the Great Bird of the Galaxy roost upon your home planet.");\r
429         exit(0);\r
430 }\r
431 \r
432 \r
433 void cramen(int i) {\r
434         /* return an enemy */\r
435         char *s;\r
436         \r
437         switch (i) {\r
438                 case IHR: s = "Romulan"; break;\r
439                 case IHK: s = "Klingon"; break;\r
440                 case IHC: s = "Commander"; break;\r
441                 case IHS: s = "Super-commander"; break;\r
442                 case IHSTAR: s = "Star"; break;\r
443                 case IHP: s = "Planet"; break;\r
444                 case IHB: s = "Starbase"; break;\r
445                 case IHBLANK: s = "Black hole"; break;\r
446                 case IHT: s = "Tholian"; break;\r
447                 case IHWEB: s = "Tholian web"; break;\r
448                 case IHQUEST: s = "Stranger"; break;\r
449                 default: s = "Unknown??"; break;\r
450         }\r
451         proutn(s);\r
452 }\r
453 \r
454 char *cramlc(enum loctype key, int x, int y) {\r
455         static char buf[32];\r
456         buf[0] = '\0';\r
457         if (key == quadrant) strcpy(buf, "Quadrant ");\r
458         else if (key == sector) strcpy(buf, "Sector ");\r
459         sprintf(buf+strlen(buf), "%d-%d", x, y);\r
460         return buf;\r
461 }\r
462 \r
463 void crmena(int i, int enemy, int key, int x, int y) {\r
464         if (i == 1) proutn("***");\r
465         cramen(enemy);\r
466         proutn(" at ");\r
467         proutn(cramlc(key, x, y));\r
468 }\r
469 \r
470 void crmshp(void) {\r
471         char *s;\r
472         switch (ship) {\r
473                 case IHE: s = "Enterprise"; break;\r
474                 case IHF: s = "Faerie Queene"; break;\r
475                 default:  s = "Ship???"; break;\r
476         }\r
477         proutn(s);\r
478 }\r
479 \r
480 void stars(void) {\r
481         prouts("******************************************************");\r
482         skip(1);\r
483 }\r
484 \r
485 double expran(double avrage) {\r
486         return -avrage*log(1e-7 + Rand());\r
487 }\r
488 \r
489 double Rand(void) {\r
490         return rand()/(1.0 + (double)RAND_MAX);\r
491 }\r
492 \r
493 void iran8(int *i, int *j) {\r
494         *i = Rand()*8.0 + 1.0;\r
495         *j = Rand()*8.0 + 1.0;\r
496 }\r
497 \r
498 void iran10(int *i, int *j) {\r
499         *i = Rand()*10.0 + 1.0;\r
500         *j = Rand()*10.0 + 1.0;\r
501 }\r
502 \r
503 void chew(void) {\r
504         linep = line;\r
505         *linep = 0;\r
506 }\r
507 \r
508 void chew2(void) {\r
509         /* return IHEOL next time */\r
510         linep = line+1;\r
511         *linep = 0;\r
512 }\r
513 \r
514 int scan(void) {\r
515         int i;\r
516         char *cp;\r
517 \r
518         // Init result\r
519         aaitem = 0.0;\r
520         *citem = 0;\r
521 \r
522         // Read a line if nothing here\r
523         if (*linep == 0) {\r
524                 if (linep != line) {\r
525                         chew();\r
526                         return IHEOL;\r
527                 }\r
528                 getline(line, sizeof(line));\r
529                 linep = line;\r
530         }\r
531         // Skip leading white space\r
532         while (*linep == ' ') linep++;\r
533         // Nothing left\r
534         if (*linep == 0) {\r
535                 chew();\r
536                 return IHEOL;\r
537         }\r
538         if (isdigit(*linep) || *linep=='+' || *linep=='-' || *linep=='.') {\r
539                 // treat as a number\r
540             i = 0;\r
541             if (sscanf(linep, "%lf%n", &aaitem, &i) < 1) {\r
542                 linep = line; // Invalid numbers are ignored\r
543                 *linep = 0;\r
544                 return IHEOL;\r
545             }\r
546             else {\r
547                 // skip to end\r
548                 linep += i;\r
549                 return IHREAL;\r
550             }\r
551         }\r
552         // Treat as alpha\r
553         cp = citem;\r
554         while (*linep && *linep!=' ') {\r
555                 if ((cp - citem) < 9) *cp++ = tolower(*linep);\r
556                 linep++;\r
557         }\r
558         *cp = 0;\r
559         return IHALPHA;\r
560 }\r
561 \r
562 int ja(void) {\r
563         chew();\r
564         while (TRUE) {\r
565                 scan();\r
566                 chew();\r
567                 if (*citem == 'y') return TRUE;\r
568                 if (*citem == 'n') return FALSE;\r
569                 proutn("Please answer with \"Y\" or \"N\":");\r
570         }\r
571 }\r
572 \r
573 void huh(void) {\r
574         chew();\r
575         skip(1);\r
576         prout("Beg your pardon, Captain?");\r
577 }\r
578 \r
579 int isit(char *s) {\r
580         /* New function -- compares s to scaned citem and returns true if it\r
581            matches to the length of s */\r
582 \r
583         return strncmp(s, citem, max(1, strlen(citem))) == 0;\r
584 \r
585 }\r
586 \r
587 #ifdef DEBUG\r
588 void debugme(void) {\r
589         proutn("Reset levels? ");\r
590         if (ja() != 0) {\r
591                 if (energy < inenrg) energy = inenrg;\r
592                 shield = inshld;\r
593                 torps = intorps;\r
594                 lsupres = inlsr;\r
595         }\r
596         proutn("Reset damage? ");\r
597         if (ja() != 0) {\r
598                 int i;\r
599                 for (i=0; i <= NDEVICES; i++) if (damage[i] > 0.0) damage[i] = 0.0;\r
600                 stdamtim = 1e30;\r
601         }\r
602         proutn("Toggle idebug? ");\r
603         if (ja() != 0) {\r
604                 idebug = !idebug;\r
605                 if (idebug) prout("Debug output ON");\r
606                 else prout("Debug output OFF");\r
607         }\r
608         proutn("Cause selective damage? ");\r
609         if (ja() != 0) {\r
610                 int i, key;\r
611                 for (i=1; i <= NDEVICES; i++) {\r
612                         proutn("Kill ");\r
613                         proutn(device[i]);\r
614                         proutn("? ");\r
615                         chew();\r
616                         key = scan();\r
617                         if (key == IHALPHA &&  isit("y")) {\r
618                                 damage[i] = 10.0;\r
619                                 if (i == DRADIO) stdamtim = game.state.date;\r
620                         }\r
621                 }\r
622         }\r
623         proutn("Examine/change events? ");\r
624         if (ja() != 0) {\r
625                 int i;\r
626                 for (i = 1; i < NEVENTS; i++) {\r
627                         int key;\r
628                         if (future[i] == 1e30) continue;\r
629                         switch (i) {\r
630                                 case FSNOVA:  proutn("Supernova       "); break;\r
631                                 case FTBEAM:  proutn("T Beam          "); break;\r
632                                 case FSNAP:   proutn("Snapshot        "); break;\r
633                                 case FBATTAK: proutn("Base Attack     "); break;\r
634                                 case FCDBAS:  proutn("Base Destroy    "); break;\r
635                                 case FSCMOVE: proutn("SC Move         "); break;\r
636                                 case FSCDBAS: proutn("SC Base Destroy "); break;\r
637                         }\r
638                         proutn("%.2f", future[i]-game.state.date);\r
639                         chew();\r
640                         proutn("  ?");\r
641                         key = scan();\r
642                         if (key == IHREAL) {\r
643                                 future[i] = game.state.date + aaitem;\r
644                         }\r
645                 }\r
646                 chew();\r
647         }\r
648 }\r
649                         \r
650 \r
651 #endif\r