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