Comment describing Stas Sergeev's changes, which we'll merge in next.
[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 s\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         char ch;\r
205         while (TRUE) { /* command loop */\r
206                 hitme = FALSE;\r
207                 justin = 0;\r
208                 Time = 0.0;\r
209                 i = -1;\r
210                 while (TRUE)  { /* get a command */\r
211                         chew();\r
212                         skip(1);\r
213                         proutn("COMMAND> ");\r
214                         if (scan() == IHEOL) continue;\r
215                         for (i=0; i < 26; i++)\r
216                                 if (isit(commands[i]))\r
217                                         break;\r
218                         if (i < 26) break;\r
219                         for (; i < NUMCOMMANDS; i++)\r
220                                 if (strcmp(commands[i], citem) == 0) break;\r
221                         if (i < NUMCOMMANDS) break;\r
222 \r
223                         if (skill <= 2)  {\r
224                                 prout("UNRECOGNIZED COMMAND. LEGAL COMMANDS ARE:");\r
225                                 listCommands(TRUE);\r
226                         }\r
227                         else prout("UNRECOGNIZED COMMAND.");\r
228                 }\r
229                 commandhook(commands[i], TRUE);\r
230                 switch (i) { /* command switch */\r
231                         case 0:                 // srscan\r
232                                 srscan(1);\r
233                                 break;\r
234                         case 1:                 // lrscan\r
235                                 lrscan();\r
236                                 break;\r
237                         case 2:                 // phasers\r
238                                 phasers();\r
239                                 if (ididit) hitme = TRUE;\r
240                                 break;\r
241                         case 3:                 // photons\r
242                                 photon();\r
243                                 if (ididit) hitme = TRUE;\r
244                                 break;\r
245                         case 4:                 // move\r
246                                 warp(1);\r
247                                 break;\r
248                         case 5:                 // shields\r
249                                 doshield(1);\r
250                                 if (ididit) {\r
251                                         attack(2);\r
252                                         shldchg = 0;\r
253                                 }\r
254                                 break;\r
255                         case 6:                 // dock\r
256                                 dock(1);\r
257                                 if (ididit) attack(0);\r
258                                 break;\r
259                         case 7:                 // damages\r
260                                 dreprt();\r
261                                 break;\r
262                         case 8:                 // chart\r
263                                 chart(0);\r
264                                 break;\r
265                         case 9:                 // impulse\r
266                                 impuls();\r
267                                 break;\r
268                         case 10:                // rest\r
269                                 wait();\r
270                                 if (ididit) hitme = TRUE;\r
271                                 break;\r
272                         case 11:                // warp\r
273                                 setwrp();\r
274                                 break;\r
275                         case 12:                // status\r
276                                 srscan(3);\r
277                                 break;\r
278                         case 13:                        // sensors\r
279                                 sensor();\r
280                                 break;\r
281                         case 14:                        // orbit\r
282                                 orbit();\r
283                                 if (ididit) hitme = TRUE;\r
284                                 break;\r
285                         case 15:                        // transport "beam"\r
286                                 beam();\r
287                                 break;\r
288                         case 16:                        // mine\r
289                                 mine();\r
290                                 if (ididit) hitme = TRUE;\r
291                                 break;\r
292                         case 17:                        // crystals\r
293                                 usecrystals();\r
294                                 break;\r
295                         case 18:                        // shuttle\r
296                                 shuttle();\r
297                                 if (ididit) hitme = TRUE;\r
298                                 break;\r
299                         case 19:                        // Planet list\r
300                                 preport();\r
301                                 break;\r
302                         case 20:                        // Status information\r
303                                 srscan(2);\r
304                                 break;\r
305                         case 21:                        // Game Report \r
306                                 report(0);\r
307                                 break;\r
308                         case 22:                        // use COMPUTER!\r
309                                 eta();\r
310                                 break;\r
311                         case 23:\r
312                                 listCommands(TRUE);\r
313                                 break;\r
314                         case 24:                // Emergency exit\r
315                                 clearscreen();  // Hide screen\r
316                                 freeze(TRUE);   // forced save\r
317                                 exit(1);                // And quick exit\r
318                                 break;\r
319                         case 25:\r
320                                 probe();                // Launch probe\r
321                                 break;\r
322                         case 26:                        // Abandon Ship\r
323                                 abandn();\r
324                                 break;\r
325                         case 27:                        // Self Destruct\r
326                                 dstrct();\r
327                                 break;\r
328                         case 28:                        // Save Game\r
329                                 freeze(FALSE);\r
330                                 if (skill > 3)\r
331                                         prout("WARNING--Frozen games produce no plaques!");\r
332                                 break;\r
333                         case 29:                        // Try a desparation measure\r
334                                 deathray();\r
335                                 if (ididit) hitme = TRUE;\r
336                                 break;\r
337                         case 30:                        // What do we want for debug???\r
338 #ifdef DEBUG\r
339                                 debugme();\r
340 #endif\r
341                                 break;\r
342                         case 31:                // Call for help\r
343                                 help();\r
344                                 break;\r
345                         case 32:\r
346                                 alldone = 1;    // quit the game\r
347 #ifdef DEBUG\r
348                                 if (idebug) score();\r
349 #endif\r
350                                 break;\r
351                         case 33:\r
352                                 helpme();       // get help\r
353                                 break;\r
354                 }\r
355                 commandhook(commands[i], FALSE);\r
356                 for (;;) {\r
357                         if (alldone) break;             // Game has ended\r
358 #ifdef DEBUG\r
359                         if (idebug) prout("2500");\r
360 #endif\r
361                         if (Time != 0.0) {\r
362                                 events();\r
363                                 if (alldone) break;             // Events did us in\r
364                         }\r
365                         if (game.state.galaxy[quadx][quady] == 1000) { // Galaxy went Nova!\r
366                                 atover(0);\r
367                                 continue;\r
368                         }\r
369                         if (nenhere == 0) movetho();\r
370                         if (hitme && justin==0) {\r
371                                 attack(2);\r
372                                 if (alldone) break;\r
373                                 if (game.state.galaxy[quadx][quady] == 1000) {  // went NOVA! \r
374                                         atover(0);\r
375                                         hitme = TRUE;\r
376                                         continue;\r
377                                 }\r
378                         }\r
379                         break;\r
380                 }\r
381                 if (alldone) break;\r
382         }\r
383 }\r
384 \r
385 \r
386 int main(int argc, char **argv) {\r
387     int i, option, usecurses = TRUE;\r
388         int hitme;\r
389         char ch;\r
390 \r
391         while ((option = getopt(argc, argv, "t")) != -1) {\r
392             switch (option) {\r
393             case 't':\r
394                 usecurses = FALSE;\r
395                 break;\r
396             default:\r
397                 fprintf(stderr, "usage: sst [-t] [startcommand...].\n");\r
398                 exit(0);\r
399             }\r
400         }\r
401 \r
402         iostart(usecurses);\r
403         prelim(); \r
404         line[0] = '\0';\r
405         for (i = optind; i < argc;  i++) {\r
406                 strcat(line, argv[i]);\r
407                 strcat(line, " ");\r
408         }\r
409         while (TRUE) { /* Play a game */\r
410                 setup(line[0] == '\0');\r
411                 if (alldone) {\r
412                         score();\r
413                         alldone = 0;\r
414                 }\r
415                 else makemoves();\r
416                 skip(2);\r
417                 stars();\r
418                 skip(1);\r
419 \r
420                 if (tourn && alldone) {\r
421                         proutn("Do you want your score recorded?");\r
422                         if (ja()) {\r
423                                 chew2();\r
424                                 freeze(FALSE);\r
425                         }\r
426                 }\r
427                 proutn("Do you want to play again?");\r
428                 if (!ja()) break;\r
429         }\r
430         skip(1);\r
431         ioend();\r
432         puts("May the Great Bird of the Galaxy roost upon your home planet.");\r
433 }\r
434 \r
435 \r
436 void cramen(int i) {\r
437         /* return an enemy */\r
438         char *s;\r
439         \r
440         switch (i) {\r
441                 case IHR: s = "Romulan"; break;\r
442                 case IHK: s = "Klingon"; break;\r
443                 case IHC: s = "Commander"; break;\r
444                 case IHS: s = "Super-commander"; break;\r
445                 case IHSTAR: s = "Star"; break;\r
446                 case IHP: s = "Planet"; break;\r
447                 case IHB: s = "Starbase"; break;\r
448                 case IHBLANK: s = "Black hole"; break;\r
449                 case IHT: s = "Tholian"; break;\r
450                 case IHWEB: s = "Tholian web"; break;\r
451                 case IHQUEST: s = "Stranger"; break;\r
452                 default: s = "Unknown??"; break;\r
453         }\r
454         proutn(s);\r
455 }\r
456 \r
457 char *cramlc(enum loctype key, int x, int y) {\r
458         static char buf[32];\r
459         buf[0] = '\0';\r
460         if (key == quadrant) strcpy(buf, "Quadrant ");\r
461         else if (key == sector) strcpy(buf, "Sector ");\r
462         sprintf(buf+strlen(buf), "%d-%d", x, y);\r
463         return buf;\r
464 }\r
465 \r
466 void crmena(int i, int enemy, int key, int x, int y) {\r
467         if (i == 1) proutn("***");\r
468         cramen(enemy);\r
469         proutn(" at ");\r
470         proutn(cramlc(key, x, y));\r
471 }\r
472 \r
473 void crmshp(void) {\r
474         char *s;\r
475         switch (ship) {\r
476                 case IHE: s = "Enterprise"; break;\r
477                 case IHF: s = "Faerie Queene"; break;\r
478                 default:  s = "Ship???"; break;\r
479         }\r
480         proutn(s);\r
481 }\r
482 \r
483 void stars(void) {\r
484         prouts("******************************************************");\r
485         skip(1);\r
486 }\r
487 \r
488 double expran(double avrage) {\r
489         return -avrage*log(1e-7 + Rand());\r
490 }\r
491 \r
492 double Rand(void) {\r
493         return rand()/(1.0 + (double)RAND_MAX);\r
494 }\r
495 \r
496 void iran8(int *i, int *j) {\r
497         *i = Rand()*8.0 + 1.0;\r
498         *j = Rand()*8.0 + 1.0;\r
499 }\r
500 \r
501 void iran10(int *i, int *j) {\r
502         *i = Rand()*10.0 + 1.0;\r
503         *j = Rand()*10.0 + 1.0;\r
504 }\r
505 \r
506 void chew(void) {\r
507         linep = line;\r
508         *linep = 0;\r
509 }\r
510 \r
511 void chew2(void) {\r
512         /* return IHEOL next time */\r
513         linep = line+1;\r
514         *linep = 0;\r
515 }\r
516 \r
517 int scan(void) {\r
518         int i;\r
519         char *cp;\r
520 \r
521         // Init result\r
522         aaitem = 0.0;\r
523         *citem = 0;\r
524 \r
525         // Read a line if nothing here\r
526         if (*linep == 0) {\r
527                 if (linep != line) {\r
528                         chew();\r
529                         return IHEOL;\r
530                 }\r
531                 getline(line, sizeof(line));\r
532                 linep = line;\r
533         }\r
534         // Skip leading white space\r
535         while (*linep == ' ') linep++;\r
536         // Nothing left\r
537         if (*linep == 0) {\r
538                 chew();\r
539                 return IHEOL;\r
540         }\r
541         if (isdigit(*linep) || *linep=='+' || *linep=='-' || *linep=='.') {\r
542                 // treat as a number\r
543             i = 0;\r
544             if (sscanf(linep, "%lf%n", &aaitem, &i) < 1) {\r
545                 linep = line; // Invalid numbers are ignored\r
546                 *linep = 0;\r
547                 return IHEOL;\r
548             }\r
549             else {\r
550                 // skip to end\r
551                 linep += i;\r
552                 return IHREAL;\r
553             }\r
554         }\r
555         // Treat as alpha\r
556         cp = citem;\r
557         while (*linep && *linep!=' ') {\r
558                 if ((cp - citem) < 9) *cp++ = tolower(*linep);\r
559                 linep++;\r
560         }\r
561         *cp = 0;\r
562         return IHALPHA;\r
563 }\r
564 \r
565 int ja(void) {\r
566         chew();\r
567         while (TRUE) {\r
568                 scan();\r
569                 chew();\r
570                 if (*citem == 'y') return TRUE;\r
571                 if (*citem == 'n') return FALSE;\r
572                 proutn("Please answer with \"Y\" or \"N\":");\r
573         }\r
574 }\r
575 \r
576 void huh(void) {\r
577         chew();\r
578         skip(1);\r
579         prout("Beg your pardon, Captain?");\r
580 }\r
581 \r
582 int isit(char *s) {\r
583         /* New function -- compares s to scaned citem and returns true if it\r
584            matches to the length of s */\r
585 \r
586         return strncmp(s, citem, max(1, strlen(citem))) == 0;\r
587 \r
588 }\r
589 \r
590 #ifdef DEBUG\r
591 void debugme(void) {\r
592         proutn("Reset levels? ");\r
593         if (ja() != 0) {\r
594                 if (energy < inenrg) energy = inenrg;\r
595                 shield = inshld;\r
596                 torps = intorps;\r
597                 lsupres = inlsr;\r
598         }\r
599         proutn("Reset damage? ");\r
600         if (ja() != 0) {\r
601                 int i;\r
602                 for (i=0; i <= NDEVICES; i++) if (damage[i] > 0.0) damage[i] = 0.0;\r
603                 stdamtim = 1e30;\r
604         }\r
605         proutn("Toggle idebug? ");\r
606         if (ja() != 0) {\r
607                 idebug = !idebug;\r
608                 if (idebug) prout("Debug output ON");\r
609                 else prout("Debug output OFF");\r
610         }\r
611         proutn("Cause selective damage? ");\r
612         if (ja() != 0) {\r
613                 int i, key;\r
614                 for (i=1; i <= NDEVICES; i++) {\r
615                         proutn("Kill ");\r
616                         proutn(device[i]);\r
617                         proutn("? ");\r
618                         chew();\r
619                         key = scan();\r
620                         if (key == IHALPHA &&  isit("y")) {\r
621                                 damage[i] = 10.0;\r
622                                 if (i == DRADIO) stdamtim = game.state.date;\r
623                         }\r
624                 }\r
625         }\r
626         proutn("Examine/change events? ");\r
627         if (ja() != 0) {\r
628                 int i;\r
629                 for (i = 1; i < NEVENTS; i++) {\r
630                         int key;\r
631                         if (future[i] == 1e30) continue;\r
632                         switch (i) {\r
633                                 case FSNOVA:  proutn("Supernova       "); break;\r
634                                 case FTBEAM:  proutn("T Beam          "); break;\r
635                                 case FSNAP:   proutn("Snapshot        "); break;\r
636                                 case FBATTAK: proutn("Base Attack     "); break;\r
637                                 case FCDBAS:  proutn("Base Destroy    "); break;\r
638                                 case FSCMOVE: proutn("SC Move         "); break;\r
639                                 case FSCDBAS: proutn("SC Base Destroy "); break;\r
640                         }\r
641                         proutn("%.2f", future[i]-game.state.date);\r
642                         chew();\r
643                         proutn("  ?");\r
644                         key = scan();\r
645                         if (key == IHREAL) {\r
646                                 future[i] = game.state.date + aaitem;\r
647                         }\r
648                 }\r
649                 chew();\r
650         }\r
651 }\r
652                         \r
653 \r
654 #endif\r