Make sure we actually take input!
[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         iostart(usecurses);
506         prelim(); 
507         line[0] = '\0';
508         for (i = optind; i < argc;  i++) {
509                 strcat(line, argv[i]);
510                 strcat(line, " ");
511         }
512         while (TRUE) { /* Play a game */
513                 setup(line[0] == '\0');
514                 if (alldone) {
515                         score();
516                         alldone = 0;
517                 }
518                 else makemoves();
519                 skip(2);
520                 stars();
521                 skip(1);
522
523                 if (tourn && alldone) {
524                         proutn("Do you want your score recorded?");
525                         if (ja()) {
526                                 chew2();
527                                 freeze(FALSE);
528                         }
529                 }
530                 proutn("Do you want to play again? ");
531                 if (!ja()) break;
532 #ifdef SERGEEV
533                 setwnd(0);
534                 clrscr();
535 #endif /* SERGEEV */
536         }
537         skip(1);
538 #ifndef SERGEEV
539         ioend();
540 #endif /* SERGEEV */
541         prout("May the Great Bird of the Galaxy roost upon your home planet.");
542         return 0;
543 }
544
545
546 void cramen(int i) {
547         /* return an enemy */
548         char *s;
549         
550         switch (i) {
551                 case IHR: s = "Romulan"; break;
552                 case IHK: s = "Klingon"; break;
553                 case IHC: s = "Commander"; break;
554                 case IHS: s = "Super-commander"; break;
555                 case IHSTAR: s = "Star"; break;
556                 case IHP: s = "Planet"; break;
557                 case IHB: s = "Starbase"; break;
558                 case IHBLANK: s = "Black hole"; break;
559                 case IHT: s = "Tholian"; break;
560                 case IHWEB: s = "Tholian web"; break;
561                 case IHQUEST: s = "Stranger"; break;
562                 default: s = "Unknown??"; break;
563         }
564         proutn(s);
565 }
566
567 char *cramlc(enum loctype key, int x, int y) {
568         static char buf[32];
569         buf[0] = '\0';
570         if (key == quadrant) strcpy(buf, "Quadrant ");
571         else if (key == sector) strcpy(buf, "Sector ");
572         sprintf(buf+strlen(buf), "%d-%d", x, y);
573         return buf;
574 }
575
576 void crmena(int i, int enemy, int key, int x, int y) {
577         if (i == 1) proutn("***");
578         cramen(enemy);
579         proutn(" at ");
580         proutn(cramlc(key, x, y));
581 }
582
583 void crmshp(void) {
584         char *s;
585         switch (ship) {
586                 case IHE: s = "Enterprise"; break;
587                 case IHF: s = "Faerie Queene"; break;
588                 default:  s = "Ship???"; break;
589         }
590         proutn(s);
591 }
592
593 void stars(void) {
594         prouts("******************************************************");
595         skip(1);
596 }
597
598 double expran(double avrage) {
599         return -avrage*log(1e-7 + Rand());
600 }
601
602 double Rand(void) {
603         return rand()/(1.0 + (double)RAND_MAX);
604 }
605
606 void iran8(int *i, int *j) {
607         *i = Rand()*8.0 + 1.0;
608         *j = Rand()*8.0 + 1.0;
609 }
610
611 void iran10(int *i, int *j) {
612         *i = Rand()*10.0 + 1.0;
613         *j = Rand()*10.0 + 1.0;
614 }
615
616 void chew(void) {
617         linep = line;
618         *linep = 0;
619 }
620
621 void chew2(void) {
622         /* return IHEOL next time */
623         linep = line+1;
624         *linep = 0;
625 }
626
627 int scan(void) {
628         int i;
629         char *cp;
630
631         // Init result
632         aaitem = 0.0;
633         *citem = 0;
634
635         // Read a line if nothing here
636         if (*linep == 0) {
637                 if (linep != line) {
638                         chew();
639                         return IHEOL;
640                 }
641                 getline(line, sizeof(line));
642                 linep = line;
643         }
644         // Skip leading white space
645         while (*linep == ' ') linep++;
646         // Nothing left
647         if (*linep == 0) {
648                 chew();
649                 return IHEOL;
650         }
651         if (isdigit(*linep) || *linep=='+' || *linep=='-' || *linep=='.') {
652                 // treat as a number
653             i = 0;
654             if (sscanf(linep, "%lf%n", &aaitem, &i) < 1) {
655                 linep = line; // Invalid numbers are ignored
656                 *linep = 0;
657                 return IHEOL;
658             }
659             else {
660                 // skip to end
661                 linep += i;
662                 return IHREAL;
663             }
664         }
665         // Treat as alpha
666         cp = citem;
667         while (*linep && *linep!=' ') {
668                 if ((cp - citem) < 9) *cp++ = tolower(*linep);
669                 linep++;
670         }
671         *cp = 0;
672         return IHALPHA;
673 }
674
675 int ja(void) {
676         chew();
677         while (TRUE) {
678                 scan();
679                 chew();
680                 if (*citem == 'y') return TRUE;
681                 if (*citem == 'n') return FALSE;
682                 proutn("Please answer with \"Y\" or \"N\": ");
683         }
684 }
685
686 void huh(void) {
687         chew();
688         skip(1);
689         prout("Beg your pardon, Captain?");
690 }
691
692 int isit(char *s) {
693         /* New function -- compares s to scaned citem and returns true if it
694            matches to the length of s */
695
696         return strncmp(s, citem, max(1, strlen(citem))) == 0;
697
698 }
699
700 #ifdef DEBUG
701 void debugme(void) {
702         proutn("Reset levels? ");
703         if (ja() != 0) {
704                 if (energy < inenrg) energy = inenrg;
705                 shield = inshld;
706                 torps = intorps;
707                 lsupres = inlsr;
708         }
709         proutn("Reset damage? ");
710         if (ja() != 0) {
711                 int i;
712                 for (i=0; i <= NDEVICES; i++) if (damage[i] > 0.0) damage[i] = 0.0;
713                 stdamtim = 1e30;
714         }
715         proutn("Toggle idebug? ");
716         if (ja() != 0) {
717                 idebug = !idebug;
718                 if (idebug) prout("Debug output ON");
719                 else prout("Debug output OFF");
720         }
721         proutn("Cause selective damage? ");
722         if (ja() != 0) {
723                 int i, key;
724                 for (i=1; i <= NDEVICES; i++) {
725                         proutn("Kill ");
726                         proutn(device[i]);
727                         proutn("? ");
728                         chew();
729                         key = scan();
730                         if (key == IHALPHA &&  isit("y")) {
731                                 damage[i] = 10.0;
732                                 if (i == DRADIO) stdamtim = game.state.date;
733                         }
734                 }
735         }
736         proutn("Examine/change events? ");
737         if (ja() != 0) {
738                 int i;
739                 for (i = 1; i < NEVENTS; i++) {
740                         int key;
741                         if (future[i] == 1e30) continue;
742                         switch (i) {
743                                 case FSNOVA:  proutn("Supernova       "); break;
744                                 case FTBEAM:  proutn("T Beam          "); break;
745                                 case FSNAP:   proutn("Snapshot        "); break;
746                                 case FBATTAK: proutn("Base Attack     "); break;
747                                 case FCDBAS:  proutn("Base Destroy    "); break;
748                                 case FSCMOVE: proutn("SC Move         "); break;
749                                 case FSCDBAS: proutn("SC Base Destroy "); break;
750                         }
751                         proutn("%.2f", future[i]-game.state.date);
752                         chew();
753                         proutn("  ?");
754                         key = scan();
755                         if (key == IHREAL) {
756                                 future[i] = game.state.date + aaitem;
757                         }
758                 }
759                 chew();
760         }
761 }
762                         
763
764 #endif