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