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