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