Give names to the windows.
[super-star-trek.git] / sst.c
1 #define INCLUDED        // Define externs here
2 #include <ctype.h>
3 #include <getopt.h>
4 #include <time.h>
5 #include "conio.h"
6 #include "sstlinux.h"
7 #include "sst.h"
8
9 #ifndef SSTDOC
10 #define SSTDOC  "sst.doc"
11 #endif
12         
13 static char line[128], *linep = line;
14
15 /*
16
17 Here are Tom Almy's changes:
18
19  Compared to original version, I've changed the "help" command to
20    "call" and the "terminate" command to "quit" to better match
21    user expectations. The DECUS version apparently made those changes
22    as well as changing "freeze" to "save". However I like "freeze".
23
24    When I got a later version of Super Star Trek that I was converting
25    from, I added the emexit command.
26
27    That later version also mentions srscan and lrscan working when
28    docked (using the starbase's scanners), so I made some changes here
29    to do this (and indicating that fact to the player), and then realized
30    the base would have a subspace radio as well -- doing a Chart when docked
31    updates the star chart, and all radio reports will be heard. The Dock
32    command will also give a report if a base is under attack.
33
34    Movecom no longer reports movement if sensors are damaged so you wouldn't
35    otherwise know it.
36
37    Also added:
38
39    1. Better base positioning at startup
40
41    2. Deathray improvement (but keeping original failure alternatives)
42
43    3. Tholian Web.
44
45    4. Enemies can ram the Enterprise. Regular Klingons and Romulans can
46       move in Expert and Emeritus games. This code could use improvement.
47
48    5. The deep space probe looks interesting! DECUS version
49
50    6. Perhaps cloaking to be added later? BSD version
51
52 Here are Stas Sergeev's changes (controlled by the proprocessor symbol
53 SERGEEV, not yet completely merged):
54
55    1. The Space Thingy can be shoved, if you it ram, and can fire back if 
56       fired upon.
57
58    1 The Tholian can be hit with phasers
59
60    2. When you are docked, base covers you with an almost invincible shields 
61       (a commander can still ram you, or a Romulan can destroy the base,
62       or a SCom can even succeed with direct attack IIRC, but this rarely 
63       happens).
64
65     3. SCom can't escape from you if no more enemies remain (without this, 
66        chasing SCom can take an eternity).
67
68     4. Probe target you enter is now the destination quadrant. Before I don't 
69        remember what it was, but it was something I had difficulty using)
70
71     5. Secret password is now autogenerated.
72
73     6. "Plaque" is adjusted for A4 paper:)
74
75     7. Phasers now tells you how much energy needed, but only if the computer 
76        is alive.
77
78     8. Planets are auto-scanned when you enter the quadrant.
79
80     9. Mining or using crystals in presense of enemy now yields an attack.
81        There are other minor adjustments to what yields an attack
82        and what does not.
83
84     10. Ramming a black hole is no longer instant death.  There is a
85         chance you might get timewarped instead.
86
87     11. "freeze" command reverts to "save", most people will understand this
88         better anyway.
89
90 Eric Raymond's changes:
91
92      1. "sos" and "call" becomes "mayday", "freeze" and "save" are both good.
93
94    */
95
96 static struct 
97 {
98     char *name;
99     int value;
100 }
101 commands[] = {
102 #ifndef SERGEEV
103 #define SRSCAN  1
104         {"SRSCAN",      SRSCAN},
105         {"STATUS",      SRSCAN},
106 #define LRSCAN  2
107         {"LRSCAN",      LRSCAN},
108 #endif /* SERGEEV */
109 #define PHASERS 3
110         {"PHASERS",     PHASERS},
111 #define TORPEDO 4
112         {"TORPEDO",     TORPEDO},
113         {"PHOTONS",     TORPEDO},
114 #define MOVE    5
115         {"MOVE",        MOVE},
116 #define SHIELDS 6
117         {"SHIELDS",     SHIELDS},
118 #define DOCK    7
119         {"DOCK",        DOCK},
120 #define DAMAGES 8
121         {"DAMAGES",     DAMAGES},
122 #define CHART   9
123         {"CHART",       CHART},
124 #define IMPULSE 10
125         {"IMPULSE",     IMPULSE},
126 #define REST    11
127         {"REST",        REST},
128 #define WARP    12
129         {"WARP",        WARP},
130 #define SCORE   13
131         {"SCORE",       SCORE},
132 #ifndef SERGEEV
133 #define SENSORS 14
134         {"SENSORS",     SENSORS},
135 #endif /* SERGEEV */
136 #define ORBIT   15
137         {"ORBIT",       ORBIT},
138 #define TRANSPORT       17
139         {"TRANSPORT",   TRANSPORT},
140 #define MINE    18
141         {"MINE",        MINE},
142 #define CRYSTALS 19
143         {"CRYSTALS",    CRYSTALS},
144 #define SHUTTLE 20
145         {"SHUTTLE",     SHUTTLE},
146 #define PLANETS 21
147         {"PLANETS",     PLANETS},
148 #ifdef SERGEEV
149 #define REQUEST 22
150         {"REQUEST",     REQUEST},
151 #endif /* SERGEEV */
152 #define REPORT  23
153         {"REPORT",      REPORT},
154 #define COMPUTER        24
155         {"COMPUTER",    COMPUTER},
156 #define COMMANDS        25
157         {"COMMANDS",    COMMANDS},
158 #define EMEXIT  26
159         {"EMEXIT",      EMEXIT},
160 #define PROBE   27
161         {"PROBE",       PROBE},
162 #define SAVE    28
163         {"SAVE",        SAVE},
164         {"FREEZE",      SAVE},
165 #define ABANDON 29
166         {"ABANDON",     ABANDON},
167 #define DESTRUCT 30
168         {"DESTRUCT",    DESTRUCT},
169 #define DEATHRAY 31
170         {"DEATHRAY",    DEATHRAY},
171 #define DEBUGCMD        32
172         {"DEBUG",       DEBUGCMD},
173 #define MAYDAY  33
174         {"MAYDAY",      MAYDAY},
175         {"SOS",         MAYDAY},
176         {"CALL",        MAYDAY},
177 #define QUIT    34
178         {"QUIT",        QUIT},
179 #define HELP    35
180         {"HELP",        HELP},
181 };
182
183 #define NUMCOMMANDS     sizeof(commands)/sizeof(commands[0])
184
185 static void listCommands(int x) {
186     int i;
187     prout("LEGAL COMMANDS ARE:");
188     for (i = 0; i < NUMCOMMANDS; i++) {
189         proutn("%-12s ", commands[i].name);
190         if (i % 5 == 4)
191             skip(1);
192     }
193     skip(1);
194 }
195
196 static void helpme(void) {
197         int i, j;
198         char cmdbuf[32], *cp;
199         char linebuf[132];
200         FILE *fp;
201         /* Give help on commands */
202         int key;
203         key = scan();
204         while (TRUE) {
205                 if (key == IHEOL) {
206 #ifdef SERGEEV
207                         setwnd(BOTTOM_WINDOW);
208 #endif /* SERGEEV */
209                         proutn("Help on what command? ");
210                         key = scan();
211                 }
212 #ifdef SERGEEV
213                 setwnd(LOWER_WINDOW);
214 #endif /* SERGEEV */
215                 if (key == IHEOL) return;
216                 for (i = 0; i < NUMCOMMANDS; i++) {
217                     if (strcasecmp(commands[i].name, citem)==0) {
218                         i = commands[i].value;
219                         break;
220                     }
221                 }
222                 if (i != NUMCOMMANDS) break;
223                 skip(1);
224                 prout("Valid commands:");
225                 listCommands(FALSE);
226                 key = IHEOL;
227                 chew();
228                 skip(1);
229         }
230         if (i == COMMANDS) {
231                 strcpy(cmdbuf, " ABBREV");
232         }
233         else {
234             for (j = 0; commands[i].name[j]; j++)
235                 cmdbuf[j] = toupper(commands[i].name[j]);
236             cmdbuf[j] = '\0';
237         }
238         fp = fopen(SSTDOC, "r");
239         if (fp == NULL) {
240                 prout("Spock-  \"Captain, that information is missing from the");
241                 prout("   computer.\"");
242                 /*
243                  * This used to continue: "You need to find SST.DOC and put 
244                  * it in the current directory."
245                  */
246                 return;
247         }
248         for (;;) {
249             if (fgets(linebuf, sizeof(linebuf), fp) == NULL) {
250                         prout("Spock- \"Captain, there is no information on that command.\"");
251                         fclose(fp);
252                         return;
253                 }
254             if (linebuf[0] == '%' && linebuf[1] == '%'&& linebuf[2] == ' ') {
255                 for (cp = linebuf+3; isspace(*cp); cp++)
256                         continue;
257                 linebuf[strlen(linebuf)-1] = '\0';
258                 if (strcasecmp(cp, cmdbuf) == 0)
259                     break;
260             }
261         }
262
263         skip(1);
264         prout("Spock- \"Captain, I've found the following information:\"");
265         skip(1);
266
267         while (fgets(linebuf, sizeof(linebuf),fp)) {
268                 if (strstr(linebuf, "******"))
269                         break;
270                 proutc(linebuf);
271         }
272         fclose(fp);
273 }
274
275 void drawmaps(short l) {
276 /* hook to be called after moving to redraw maps */
277 #ifdef SERGEEV
278      _setcursortype(_NOCURSOR);
279      if (l==1) sensor();
280      if (l!=2) setwnd(LEFTUPPER_WINDOW);
281      gotoxy(1,1);
282      strcpy(line,"s");
283      srscan(1);
284      if (l!=2){
285         setwnd(SRSCAN_WINDOW);
286         clrscr();
287         srscan(2);
288         setwnd(LRSCAN_WINDOW);
289         clrscr();
290         strcpy(line,"l");
291         lrscan();
292         _setcursortype(_NORMALCURSOR);
293      }
294 #endif /* SERGEEV */
295 }
296
297 static void makemoves(void) {
298         int i, hitme;
299 #ifdef SERGEEV
300         clrscr();
301         setwnd(LOWER_WINDOW);
302 #endif /* SERGEEV */
303         while (TRUE) { /* command loop */
304                 drawmaps(1);
305                 while (TRUE)  { /* get a command */
306                         hitme = FALSE;
307                         justin = 0;
308                         Time = 0.0;
309                         i = -1;
310                         chew();
311 #ifdef SERGEEV
312                         setwnd(BOTTOM_WINDOW);
313                         clrscr();
314 #endif /* SERGEEV */
315                         proutn("COMMAND> ");
316                         if (scan() == IHEOL) {
317 #ifdef SERGEEV
318                             _setcursortype(_NOCURSOR);
319                             setwnd(LOWER_WINDOW);
320                             clrscr();
321                             chart(0);
322                             _setcursortype(_NORMALCURSOR);
323 #endif /* SERGEEV */
324                             continue;
325                         }
326 #ifdef SERGEEV
327                         ididit=0;
328                         clrscr();
329                         setwnd(LOWER_WINDOW);
330                         clrscr();
331 #endif /* SERGEEV */
332                         for (i=0; i < ABANDON; i++)
333                             if (isit(commands[i].name)) {
334                                 i = commands[i].value;
335                                 break;
336                             }
337                         if (i < ABANDON) break;
338                         for (; i < NUMCOMMANDS; i++)
339                             if (strcasecmp(commands[i].name, citem) == 0) {
340                                     i = commands[i].value;
341                                     break;
342                             }
343                         if (i < NUMCOMMANDS) break;
344
345                         listCommands(TRUE);
346                 }
347                 commandhook(commands[i].name, TRUE);
348                 switch (i) { /* command switch */
349 #ifndef SERGEEV
350                         case SRSCAN:                 // srscan
351                                 srscan(1);
352                                 break;
353                         case LRSCAN:                    // lrscan
354                                 lrscan();
355                                 break;
356 #endif /* SERGEEV */
357                         case PHASERS:                   // phasers
358                                 phasers();
359                                 if (ididit) hitme = TRUE;
360                                 break;
361                         case TORPEDO:                   // photons
362                                 photon();
363                                 if (ididit) hitme = TRUE;
364                                 break;
365                         case MOVE:                      // move
366                                 warp(1);
367                                 break;
368                         case SHIELDS:                   // shields
369                                 doshield(1);
370                                 if (ididit) {
371                                         hitme=TRUE;
372                                         shldchg = 0;
373                                 }
374                                 break;
375                         case DOCK:                      // dock
376                                 dock(1);
377                                 if (ididit) attack(0);
378                                 break;
379                         case DAMAGES:                   // damages
380                                 dreprt();
381                                 break;
382                         case CHART:                     // chart
383                                 chart(0);
384                                 break;
385                         case IMPULSE:                   // impulse
386                                 impuls();
387                                 break;
388                         case REST:              // rest
389                                 wait();
390                                 if (ididit) hitme = TRUE;
391                                 break;
392                         case WARP:              // warp
393                                 setwrp();
394                                 break;
395                         case SCORE:                // score
396                                 score();
397                                 break;
398 #ifndef SERGEEV
399                         case SENSORS:                   // sensors
400                                 sensor();
401                                 break;
402 #endif /* SERGEEV */
403                         case ORBIT:                     // orbit
404                                 orbit();
405                                 if (ididit) hitme = TRUE;
406                                 break;
407                         case TRANSPORT:                 // transport "beam"
408                                 beam();
409                                 break;
410                         case MINE:                      // mine
411                                 mine();
412                                 if (ididit) hitme = TRUE;
413                                 break;
414                         case CRYSTALS:                  // crystals
415                                 usecrystals();
416                                 if (ididit) hitme = TRUE;
417                                 break;
418                         case SHUTTLE:                   // shuttle
419                                 shuttle();
420                                 if (ididit) hitme = TRUE;
421                                 break;
422                         case PLANETS:                   // Planet list
423                                 preport();
424                                 break;
425                         case REPORT:                    // Game Report 
426                                 report();
427                                 break;
428                         case COMPUTER:                  // use COMPUTER!
429                                 eta();
430                                 break;
431                         case COMMANDS:
432                                 listCommands(TRUE);
433                                 break;
434                         case EMEXIT:            // Emergency exit
435                                 clrscr();       // Hide screen
436                                 freeze(TRUE);   // forced save
437                                 exit(1);                // And quick exit
438                                 break;
439                         case PROBE:
440                                 probe();                // Launch probe
441                                 if (ididit) hitme = TRUE;
442                                 break;
443                         case ABANDON:                   // Abandon Ship
444                                 abandn();
445                                 break;
446                         case DESTRUCT:                  // Self Destruct
447                                 dstrct();
448                                 break;
449                         case SAVE:                      // Save Game
450                                 freeze(FALSE);
451 #ifdef SERGEEV
452                                 clrscr();
453 #endif /* SERGEEV */
454                                 if (skill > 3)
455                                         prout("WARNING--Saved games produce no plaques!");
456                                 break;
457                         case DEATHRAY:          // Try a desparation measure
458                                 deathray();
459                                 if (ididit) hitme = TRUE;
460                                 break;
461                         case DEBUGCMD:          // What do we want for debug???
462 #ifdef DEBUG
463                                 debugme();
464 #endif
465                                 break;
466                         case MAYDAY:            // Call for help
467                                 help();
468                                 if (ididit) hitme = TRUE;
469                                 break;
470                         case QUIT:
471                                 alldone = 1;    // quit the game
472 #ifdef DEBUG
473                                 if (idebug) score();
474 #endif
475                                 break;
476                         case HELP:
477                                 helpme();       // get help
478                                 break;
479                 }
480                 commandhook(commands[i].name, FALSE);
481                 for (;;) {
482                         if (alldone) break;             // Game has ended
483 #ifdef DEBUG
484                         if (idebug) prout("2500");
485 #endif
486                         if (Time != 0.0) {
487                                 events();
488                                 if (alldone) break;             // Events did us in
489                         }
490                         if (game.state.galaxy[quadx][quady] == 1000) { // Galaxy went Nova!
491                                 atover(0);
492                                 continue;
493                         }
494                         if (hitme && justin==0) {
495                                 attack(2);
496                                 if (alldone) break;
497                                 if (game.state.galaxy[quadx][quady] == 1000) {  // went NOVA! 
498                                         atover(0);
499                                         hitme = TRUE;
500                                         continue;
501                                 }
502                         }
503                         break;
504                 }
505                 if (alldone) break;
506         }
507 }
508
509
510 int main(int argc, char **argv) {
511         int i, option, usecurses = TRUE;
512
513         while ((option = getopt(argc, argv, "t")) != -1) {
514             switch (option) {
515             case 't':
516                 usecurses = FALSE;
517                 break;
518             default:
519                 fprintf(stderr, "usage: sst [-t] [startcommand...].\n");
520                 exit(0);
521             }
522         }
523
524 #ifndef SERGEEV
525         iostart(usecurses);
526 #else
527         randomize();
528         textattr(7);
529         clrscr();
530         setwnd(FULLSCREEN_WINDOW);
531 #endif /* SERGEEV */
532         line[0] = '\0';
533         for (i = optind; i < argc;  i++) {
534                 strcat(line, argv[i]);
535                 strcat(line, " ");
536         }
537         while (TRUE) { /* Play a game */
538                 prelim();
539                 setup(line[0] == '\0');
540                 if (alldone) {
541                         score();
542                         alldone = 0;
543                 }
544                 else makemoves();
545                 skip(1);
546                 stars();
547                 skip(1);
548
549                 if (tourn && alldone) {
550                         proutn("Do you want your score recorded?");
551                         if (ja()) {
552                                 chew2();
553                                 freeze(FALSE);
554                         }
555                 }
556                 proutn("Do you want to play again? ");
557                 if (!ja()) break;
558 #ifdef SERGEEV
559                 setwnd(FULLSCREEN_WINDOW);
560                 clrscr();
561 #endif /* SERGEEV */
562         }
563         skip(1);
564 #ifndef SERGEEV
565         ioend();
566 #endif /* SERGEEV */
567         prout("May the Great Bird of the Galaxy roost upon your home planet.");
568         return 0;
569 }
570
571
572 void cramen(int i) {
573         /* return an enemy */
574         char *s;
575         
576         switch (i) {
577                 case IHR: s = "Romulan"; break;
578                 case IHK: s = "Klingon"; break;
579                 case IHC: s = "Commander"; break;
580                 case IHS: s = "Super-commander"; break;
581                 case IHSTAR: s = "Star"; break;
582                 case IHP: s = "Planet"; break;
583                 case IHB: s = "Starbase"; break;
584                 case IHBLANK: s = "Black hole"; break;
585                 case IHT: s = "Tholian"; break;
586                 case IHWEB: s = "Tholian web"; break;
587                 case IHQUEST: s = "Stranger"; break;
588                 default: s = "Unknown??"; break;
589         }
590         proutn(s);
591 }
592
593 char *cramlc(enum loctype key, int x, int y) {
594         static char buf[32];
595         buf[0] = '\0';
596         if (key == quadrant) strcpy(buf, "Quadrant ");
597         else if (key == sector) strcpy(buf, "Sector ");
598         sprintf(buf+strlen(buf), "%d - %d", x, y);
599         return buf;
600 }
601
602 void crmena(int i, int enemy, int key, int x, int y) {
603         if (i == 1) proutn("***");
604         cramen(enemy);
605         proutn(" at ");
606         proutn(cramlc(key, x, y));
607 }
608
609 void crmshp(void) {
610         char *s;
611         switch (ship) {
612                 case IHE: s = "Enterprise"; break;
613                 case IHF: s = "Faerie Queene"; break;
614                 default:  s = "Ship???"; break;
615         }
616         proutn(s);
617 }
618
619 void stars(void) {
620         prouts("******************************************************");
621         skip(1);
622 }
623
624 double expran(double avrage) {
625         return -avrage*log(1e-7 + Rand());
626 }
627
628 double Rand(void) {
629         return rand()/(1.0 + (double)RAND_MAX);
630 }
631
632 void iran8(int *i, int *j) {
633         *i = Rand()*8.0 + 1.0;
634         *j = Rand()*8.0 + 1.0;
635 }
636
637 void iran10(int *i, int *j) {
638         *i = Rand()*10.0 + 1.0;
639         *j = Rand()*10.0 + 1.0;
640 }
641
642 void chew(void) {
643         linep = line;
644         *linep = 0;
645 }
646
647 void chew2(void) {
648         /* return IHEOL next time */
649         linep = line+1;
650         *linep = 0;
651 }
652
653 int scan(void) {
654         int i;
655         char *cp;
656
657         // Init result
658         aaitem = 0.0;
659         *citem = 0;
660
661         // Read a line if nothing here
662         if (*linep == 0) {
663                 if (linep != line) {
664                         chew();
665                         return IHEOL;
666                 }
667                 getline(line, sizeof(line));
668 #ifdef SERGEEV
669                 fflush(stdin);
670                 if (curwnd==BOTTOM_WINDOW){
671                    clrscr();
672                    setwnd(LOWER_WINDOW);
673                    clrscr();
674                 }
675 #endif /* SERGEEV */
676                 linep = line;
677         }
678         // Skip leading white space
679         while (*linep == ' ') linep++;
680         // Nothing left
681         if (*linep == 0) {
682                 chew();
683                 return IHEOL;
684         }
685         if (isdigit(*linep) || *linep=='+' || *linep=='-' || *linep=='.') {
686                 // treat as a number
687             i = 0;
688             if (sscanf(linep, "%lf%n", &aaitem, &i) < 1) {
689                 linep = line; // Invalid numbers are ignored
690                 *linep = 0;
691                 return IHEOL;
692             }
693             else {
694                 // skip to end
695                 linep += i;
696                 return IHREAL;
697             }
698         }
699         // Treat as alpha
700         cp = citem;
701         while (*linep && *linep!=' ') {
702                 if ((cp - citem) < 9) *cp++ = tolower(*linep);
703                 linep++;
704         }
705         *cp = 0;
706         return IHALPHA;
707 }
708
709 int ja(void) {
710         chew();
711         while (TRUE) {
712                 scan();
713                 chew();
714                 if (*citem == 'y') return TRUE;
715                 if (*citem == 'n') return FALSE;
716                 proutn("Please answer with \"Y\" or \"N\": ");
717         }
718 }
719
720 void huh(void) {
721         chew();
722         skip(1);
723         prout("Beg your pardon, Captain?");
724 }
725
726 int isit(char *s) {
727         /* New function -- compares s to scaned citem and returns true if it
728            matches to the length of s */
729
730         return strncasecmp(s, citem, max(1, strlen(citem))) == 0;
731
732 }
733
734 #ifdef DEBUG
735 void debugme(void) {
736         proutn("Reset levels? ");
737         if (ja() != 0) {
738                 if (energy < inenrg) energy = inenrg;
739                 shield = inshld;
740                 torps = intorps;
741                 lsupres = inlsr;
742         }
743         proutn("Reset damage? ");
744         if (ja() != 0) {
745                 int i;
746                 for (i=0; i <= NDEVICES; i++) if (damage[i] > 0.0) damage[i] = 0.0;
747                 stdamtim = 1e30;
748         }
749         proutn("Toggle idebug? ");
750         if (ja() != 0) {
751                 idebug = !idebug;
752                 if (idebug) prout("Debug output ON");
753                 else prout("Debug output OFF");
754         }
755         proutn("Cause selective damage? ");
756         if (ja() != 0) {
757                 int i, key;
758                 for (i=1; i <= NDEVICES; i++) {
759                         proutn("Kill ");
760                         proutn(device[i]);
761                         proutn("? ");
762                         chew();
763                         key = scan();
764                         if (key == IHALPHA &&  isit("y")) {
765                                 damage[i] = 10.0;
766                                 if (i == DRADIO) stdamtim = game.state.date;
767                         }
768                 }
769         }
770         proutn("Examine/change events? ");
771         if (ja() != 0) {
772                 int i;
773                 for (i = 1; i < NEVENTS; i++) {
774                         int key;
775                         if (future[i] == 1e30) continue;
776                         switch (i) {
777                                 case FSNOVA:  proutn("Supernova       "); break;
778                                 case FTBEAM:  proutn("T Beam          "); break;
779                                 case FSNAP:   proutn("Snapshot        "); break;
780                                 case FBATTAK: proutn("Base Attack     "); break;
781                                 case FCDBAS:  proutn("Base Destroy    "); break;
782                                 case FSCMOVE: proutn("SC Move         "); break;
783                                 case FSCDBAS: proutn("SC Base Destroy "); break;
784                         }
785                         proutn("%.2f", future[i]-game.state.date);
786                         chew();
787                         proutn("  ?");
788                         key = scan();
789                         if (key == IHREAL) {
790                                 future[i] = game.state.date + aaitem;
791                         }
792                 }
793                 chew();
794         }
795 }
796                         
797
798 #endif