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