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