Merge Stas's patch #3.
[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();
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 #ifdef SERGEEV
421                                 clrscr();       // Hide screen
422 #endif /* SERGEEV */
423                                 freeze(TRUE);   // forced save
424                                 exit(1);                // And quick exit
425                                 break;
426                         case 25:
427                                 probe();                // Launch probe
428                                 if (ididit) hitme = TRUE;
429                                 break;
430                         case 26:                        // Abandon Ship
431                                 abandn();
432                                 break;
433                         case 27:                        // Self Destruct
434                                 dstrct();
435                                 break;
436                         case 28:                        // Save Game
437                                 freeze(FALSE);
438 #ifdef SERGEEV
439                                 clrscr();
440 #endif /* SERGEEV */
441                                 if (skill > 3)
442                                         prout("WARNING--Saved games produce no plaques!");
443                                 break;
444                         case 29:                        // Try a desparation measure
445                                 deathray();
446                                 if (ididit) hitme = TRUE;
447                                 break;
448                         case 30:                        // What do we want for debug???
449 #ifdef DEBUG
450                                 debugme();
451 #endif
452                                 break;
453                         case 31:                // Call for help
454                                 help();
455                                 if (ididit) hitme = TRUE;
456                                 break;
457                         case 32:
458                                 alldone = 1;    // quit the game
459 #ifdef DEBUG
460                                 if (idebug) score();
461 #endif
462                                 break;
463                         case 33:
464                                 helpme();       // get help
465                                 break;
466                 }
467                 commandhook(commands[i], FALSE);
468                 for (;;) {
469                         if (alldone) break;             // Game has ended
470 #ifdef DEBUG
471                         if (idebug) prout("2500");
472 #endif
473                         if (Time != 0.0) {
474                                 events();
475                                 if (alldone) break;             // Events did us in
476                         }
477                         if (game.state.galaxy[quadx][quady] == 1000) { // Galaxy went Nova!
478                                 atover(0);
479                                 continue;
480                         }
481                         if (hitme && justin==0) {
482                                 attack(2);
483                                 if (alldone) break;
484                                 if (game.state.galaxy[quadx][quady] == 1000) {  // went NOVA! 
485                                         atover(0);
486                                         hitme = TRUE;
487                                         continue;
488                                 }
489                         }
490                         break;
491                 }
492                 if (alldone) break;
493         }
494 }
495
496
497 int main(int argc, char **argv) {
498         int i, option, usecurses = TRUE;
499
500         while ((option = getopt(argc, argv, "t")) != -1) {
501             switch (option) {
502             case 't':
503                 usecurses = FALSE;
504                 break;
505             default:
506                 fprintf(stderr, "usage: sst [-t] [startcommand...].\n");
507                 exit(0);
508             }
509         }
510
511 #ifndef SERGEEV
512         iostart(usecurses);
513 #else
514         randomize();
515         textattr(7);
516         clrscr();
517         setwnd(0);
518 #endif /* SERGEEV */
519         line[0] = '\0';
520         for (i = optind; i < argc;  i++) {
521                 strcat(line, argv[i]);
522                 strcat(line, " ");
523         }
524         while (TRUE) { /* Play a game */
525                 prelim();
526                 setup(line[0] == '\0');
527                 if (alldone) {
528                         score();
529                         alldone = 0;
530                 }
531                 else makemoves();
532                 skip(1);
533                 stars();
534                 skip(1);
535
536                 if (tourn && alldone) {
537                         proutn("Do you want your score recorded?");
538                         if (ja()) {
539                                 chew2();
540                                 freeze(FALSE);
541                         }
542                 }
543                 proutn("Do you want to play again? ");
544                 if (!ja()) break;
545 #ifdef SERGEEV
546                 setwnd(0);
547                 clrscr();
548 #endif /* SERGEEV */
549         }
550         skip(1);
551 #ifndef SERGEEV
552         ioend();
553 #endif /* SERGEEV */
554         prout("May the Great Bird of the Galaxy roost upon your home planet.");
555         return 0;
556 }
557
558
559 void cramen(int i) {
560         /* return an enemy */
561         char *s;
562         
563         switch (i) {
564                 case IHR: s = "Romulan"; break;
565                 case IHK: s = "Klingon"; break;
566                 case IHC: s = "Commander"; break;
567                 case IHS: s = "Super-commander"; break;
568                 case IHSTAR: s = "Star"; break;
569                 case IHP: s = "Planet"; break;
570                 case IHB: s = "Starbase"; break;
571                 case IHBLANK: s = "Black hole"; break;
572                 case IHT: s = "Tholian"; break;
573                 case IHWEB: s = "Tholian web"; break;
574                 case IHQUEST: s = "Stranger"; break;
575                 default: s = "Unknown??"; break;
576         }
577         proutn(s);
578 }
579
580 char *cramlc(enum loctype key, int x, int y) {
581         static char buf[32];
582         buf[0] = '\0';
583         if (key == quadrant) strcpy(buf, "Quadrant ");
584         else if (key == sector) strcpy(buf, "Sector ");
585         sprintf(buf+strlen(buf), "%d - %d", x, y);
586         return buf;
587 }
588
589 void crmena(int i, int enemy, int key, int x, int y) {
590         if (i == 1) proutn("***");
591         cramen(enemy);
592         proutn(" at ");
593         proutn(cramlc(key, x, y));
594 }
595
596 void crmshp(void) {
597         char *s;
598         switch (ship) {
599                 case IHE: s = "Enterprise"; break;
600                 case IHF: s = "Faerie Queene"; break;
601                 default:  s = "Ship???"; break;
602         }
603         proutn(s);
604 }
605
606 void stars(void) {
607         prouts("******************************************************");
608         skip(1);
609 }
610
611 double expran(double avrage) {
612         return -avrage*log(1e-7 + Rand());
613 }
614
615 double Rand(void) {
616         return rand()/(1.0 + (double)RAND_MAX);
617 }
618
619 void iran8(int *i, int *j) {
620         *i = Rand()*8.0 + 1.0;
621         *j = Rand()*8.0 + 1.0;
622 }
623
624 void iran10(int *i, int *j) {
625         *i = Rand()*10.0 + 1.0;
626         *j = Rand()*10.0 + 1.0;
627 }
628
629 void chew(void) {
630         linep = line;
631         *linep = 0;
632 }
633
634 void chew2(void) {
635         /* return IHEOL next time */
636         linep = line+1;
637         *linep = 0;
638 }
639
640 int scan(void) {
641         int i;
642         char *cp;
643
644         // Init result
645         aaitem = 0.0;
646         *citem = 0;
647
648         // Read a line if nothing here
649         if (*linep == 0) {
650                 if (linep != line) {
651                         chew();
652                         return IHEOL;
653                 }
654                 getline(line, sizeof(line));
655 #ifdef SERGEEV
656                 fflush(stdin);
657                 if (curwnd==5){
658                    clrscr();
659                    setwnd(4);
660                    clrscr();
661                 }
662 #endif /* SERGEEV */
663                 linep = line;
664         }
665         // Skip leading white space
666         while (*linep == ' ') linep++;
667         // Nothing left
668         if (*linep == 0) {
669                 chew();
670                 return IHEOL;
671         }
672         if (isdigit(*linep) || *linep=='+' || *linep=='-' || *linep=='.') {
673                 // treat as a number
674             i = 0;
675             if (sscanf(linep, "%lf%n", &aaitem, &i) < 1) {
676                 linep = line; // Invalid numbers are ignored
677                 *linep = 0;
678                 return IHEOL;
679             }
680             else {
681                 // skip to end
682                 linep += i;
683                 return IHREAL;
684             }
685         }
686         // Treat as alpha
687         cp = citem;
688         while (*linep && *linep!=' ') {
689                 if ((cp - citem) < 9) *cp++ = tolower(*linep);
690                 linep++;
691         }
692         *cp = 0;
693         return IHALPHA;
694 }
695
696 int ja(void) {
697         chew();
698         while (TRUE) {
699                 scan();
700                 chew();
701                 if (*citem == 'y') return TRUE;
702                 if (*citem == 'n') return FALSE;
703                 proutn("Please answer with \"Y\" or \"N\": ");
704         }
705 }
706
707 void huh(void) {
708         chew();
709         skip(1);
710         prout("Beg your pardon, Captain?");
711 }
712
713 int isit(char *s) {
714         /* New function -- compares s to scaned citem and returns true if it
715            matches to the length of s */
716
717         return strncmp(s, citem, max(1, strlen(citem))) == 0;
718
719 }
720
721 #ifdef DEBUG
722 void debugme(void) {
723         proutn("Reset levels? ");
724         if (ja() != 0) {
725                 if (energy < inenrg) energy = inenrg;
726                 shield = inshld;
727                 torps = intorps;
728                 lsupres = inlsr;
729         }
730         proutn("Reset damage? ");
731         if (ja() != 0) {
732                 int i;
733                 for (i=0; i <= NDEVICES; i++) if (damage[i] > 0.0) damage[i] = 0.0;
734                 stdamtim = 1e30;
735         }
736         proutn("Toggle idebug? ");
737         if (ja() != 0) {
738                 idebug = !idebug;
739                 if (idebug) prout("Debug output ON");
740                 else prout("Debug output OFF");
741         }
742         proutn("Cause selective damage? ");
743         if (ja() != 0) {
744                 int i, key;
745                 for (i=1; i <= NDEVICES; i++) {
746                         proutn("Kill ");
747                         proutn(device[i]);
748                         proutn("? ");
749                         chew();
750                         key = scan();
751                         if (key == IHALPHA &&  isit("y")) {
752                                 damage[i] = 10.0;
753                                 if (i == DRADIO) stdamtim = game.state.date;
754                         }
755                 }
756         }
757         proutn("Examine/change events? ");
758         if (ja() != 0) {
759                 int i;
760                 for (i = 1; i < NEVENTS; i++) {
761                         int key;
762                         if (future[i] == 1e30) continue;
763                         switch (i) {
764                                 case FSNOVA:  proutn("Supernova       "); break;
765                                 case FTBEAM:  proutn("T Beam          "); break;
766                                 case FSNAP:   proutn("Snapshot        "); break;
767                                 case FBATTAK: proutn("Base Attack     "); break;
768                                 case FCDBAS:  proutn("Base Destroy    "); break;
769                                 case FSCMOVE: proutn("SC Move         "); break;
770                                 case FSCDBAS: proutn("SC Base Destroy "); break;
771                         }
772                         proutn("%.2f", future[i]-game.state.date);
773                         chew();
774                         proutn("  ?");
775                         key = scan();
776                         if (key == IHREAL) {
777                                 future[i] = game.state.date + aaitem;
778                         }
779                 }
780                 chew();
781         }
782 }
783                         
784
785 #endif