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