First round of changes from Stas Sergeev.
[super-star-trek.git] / sst.c
1 #define INCLUDED        // Define externs here\r
2 #include <ctype.h>\r
3 #include <getopt.h>\r
4 #include "sst.h"\r
5 \r
6 #ifndef SSTDOC\r
7 #define SSTDOC  "sst.doc"\r
8 #endif\r
9         \r
10 static char line[128], *linep = line;\r
11 \r
12 /* Compared to original version, I've changed the "help" command to\r
13    "call" and the "terminate" command to "quit" to better match\r
14    user expectations. The DECUS version apparently made those changes\r
15    as well as changing "freeze" to "save". However I like "freeze".\r
16 \r
17    When I got a later version of Super Star Trek that I was converting\r
18    from, I added the emexit command.\r
19 \r
20    That later version also mentions srscan and lrscan working when\r
21    docked (using the starbase's scanners), so I made some changes here\r
22    to do this (and indicating that fact to the player), and then realized\r
23    the base would have a subspace radio as well -- doing a Chart when docked\r
24    updates the star chart, and all radio reports will be heard. The Dock\r
25    command will also give a report if a base is under attack.\r
26 \r
27    Movecom no longer reports movement if sensors are damaged so you wouldn't\r
28    otherwise know it.\r
29 \r
30    Also added:\r
31 \r
32    1. Better base positioning at startup\r
33 \r
34    2. deathray improvement (but keeping original failure alternatives)\r
35 \r
36    3. Tholian Web\r
37 s\r
38    4. Enemies can ram the Enterprise. Regular Klingons and Romulans can\r
39       move in Expert and Emeritus games. This code could use improvement.\r
40 \r
41    5. The deep space probe looks interesting! DECUS version\r
42 \r
43    6. Perhaps cloaking to be added later? BSD version\r
44 \r
45 \r
46    */\r
47 \r
48 \r
49 static char *commands[] = {\r
50         "srscan",\r
51         "lrscan",\r
52         "phasers",\r
53         "photons",\r
54         "move",\r
55         "shields",\r
56         "dock",\r
57         "damages",\r
58         "chart",\r
59         "impulse",\r
60         "rest",\r
61         "warp",\r
62         "status",\r
63         "sensors",\r
64         "orbit",\r
65         "transport",\r
66         "mine",\r
67         "crystals",\r
68         "shuttle",\r
69         "planets",\r
70         "request",\r
71         "report",\r
72         "computer",\r
73         "commands",\r
74     "emexit",\r
75     "probe",\r
76         "abandon",\r
77         "destruct",\r
78         "freeze",\r
79         "deathray",\r
80         "debug",\r
81         "call",\r
82         "quit",\r
83         "help"\r
84 };\r
85 #define NUMCOMMANDS     sizeof(commands)/sizeof(commands[0])\r
86 \r
87 static void listCommands(int x) {\r
88         prout("   SRSCAN    MOVE      PHASERS   CALL\n"\r
89                   "   STATUS    IMPULSE   PHOTONS   ABANDON\n"\r
90                   "   LRSCAN    WARP      SHIELDS   DESTRUCT\n"\r
91                   "   CHART     REST      DOCK      QUIT\n"\r
92                   "   DAMAGES   REPORT    SENSORS   ORBIT\n"\r
93                   "   TRANSPORT MINE      CRYSTALS  SHUTTLE\n"\r
94                   "   PLANETS   REQUEST   DEATHRAY  FREEZE\n"\r
95                   "   COMPUTER  EMEXIT    PROBE     COMMANDS");\r
96         if (x) prout("   HELP");\r
97 }\r
98 \r
99 static void helpme(void) {\r
100         int i, j;\r
101         char cmdbuf[32], *cp;\r
102         char linebuf[132];\r
103         FILE *fp;\r
104         /* Give help on commands */\r
105         int key;\r
106         key = scan();\r
107         while (TRUE) {\r
108                 if (key == IHEOL) {\r
109                         proutn("Help on what command?");\r
110                         key = scan();\r
111                 }\r
112                 if (key == IHEOL) return;\r
113                 for (i = 0; i < NUMCOMMANDS; i++) {\r
114                         if (strcmp(commands[i], citem)==0) break;\r
115                 }\r
116                 if (i != NUMCOMMANDS) break;\r
117                 skip(1);\r
118                 prout("Valid commands:");\r
119                 listCommands(FALSE);\r
120                 key = IHEOL;\r
121                 chew();\r
122                 skip(1);\r
123         }\r
124         if (i == 23) {\r
125                 strcpy(cmdbuf, " ABBREV");\r
126         }\r
127         else {\r
128             for (j = 0; commands[i][j]; j++)\r
129                 cmdbuf[j] = toupper(commands[i][j]);\r
130             cmdbuf[j] = '\0';\r
131         }\r
132         fp = fopen(SSTDOC, "r");\r
133         if (fp == NULL) {\r
134                 prout("Spock-  \"Captain, that information is missing from the");\r
135                 prout("   computer. You need to find SST.DOC and put it in the");\r
136                 prout("   current directory.\"");\r
137                 return;\r
138         }\r
139         for (;;) {\r
140             if (fgets(linebuf, sizeof(linebuf), fp) == NULL) {\r
141                         prout("Spock- \"Captain, there is no information on that command.\"");\r
142                         fclose(fp);\r
143                         return;\r
144                 }\r
145             if (linebuf[0] == '%' && linebuf[1] == '%'&& linebuf[2] == ' ') {\r
146                 for (cp = linebuf+3; isspace(*cp); cp++)\r
147                         continue;\r
148                 linebuf[strlen(linebuf)-1] = '\0';\r
149                 if (strcmp(cp, cmdbuf) == 0)\r
150                     break;\r
151             }\r
152         }\r
153 \r
154         skip(1);\r
155         prout("Spock- \"Captain, I've found the following information:\"");\r
156         skip(1);\r
157 \r
158         while (fgets(linebuf, sizeof(linebuf),fp)) {\r
159                 if (strstr(linebuf, "******"))\r
160                         break;\r
161                 proutc(linebuf);\r
162         }\r
163         fclose(fp);\r
164 }\r
165 \r
166 static void makemoves(void) {\r
167         int i, hitme;\r
168         char ch;\r
169         while (TRUE) { /* command loop */\r
170                 hitme = FALSE;\r
171                 justin = 0;\r
172                 Time = 0.0;\r
173                 i = -1;\r
174                 while (TRUE)  { /* get a command */\r
175                         chew();\r
176                         skip(1);\r
177                         proutn("COMMAND> ");\r
178                         if (scan() == IHEOL) continue;\r
179                         for (i=0; i < 26; i++)\r
180                                 if (isit(commands[i]))\r
181                                         break;\r
182                         if (i < 26) break;\r
183                         for (; i < NUMCOMMANDS; i++)\r
184                                 if (strcmp(commands[i], citem) == 0) break;\r
185                         if (i < NUMCOMMANDS) break;\r
186 \r
187                         if (skill <= 2)  {\r
188                                 prout("UNRECOGNIZED COMMAND. LEGAL COMMANDS ARE:");\r
189                                 listCommands(TRUE);\r
190                         }\r
191                         else prout("UNRECOGNIZED COMMAND.");\r
192                 }\r
193                 commandhook(commands[i], TRUE);\r
194                 switch (i) { /* command switch */\r
195                         case 0:                 // srscan\r
196                                 srscan(1);\r
197                                 break;\r
198                         case 1:                 // lrscan\r
199                                 lrscan();\r
200                                 break;\r
201                         case 2:                 // phasers\r
202                                 phasers();\r
203                                 if (ididit) hitme = TRUE;\r
204                                 break;\r
205                         case 3:                 // photons\r
206                                 photon();\r
207                                 if (ididit) hitme = TRUE;\r
208                                 break;\r
209                         case 4:                 // move\r
210                                 warp(1);\r
211                                 break;\r
212                         case 5:                 // shields\r
213                                 doshield(1);\r
214                                 if (ididit) {\r
215                                         attack(2);\r
216                                         shldchg = 0;\r
217                                 }\r
218                                 break;\r
219                         case 6:                 // dock\r
220                                 dock(1);\r
221                                 if (ididit) attack(0);\r
222                                 break;\r
223                         case 7:                 // damages\r
224                                 dreprt();\r
225                                 break;\r
226                         case 8:                 // chart\r
227                                 chart(0);\r
228                                 break;\r
229                         case 9:                 // impulse\r
230                                 impuls();\r
231                                 break;\r
232                         case 10:                // rest\r
233                                 wait();\r
234                                 if (ididit) hitme = TRUE;\r
235                                 break;\r
236                         case 11:                // warp\r
237                                 setwrp();\r
238                                 break;\r
239                         case 12:                // status\r
240                                 srscan(3);\r
241                                 break;\r
242                         case 13:                        // sensors\r
243                                 sensor();\r
244                                 break;\r
245                         case 14:                        // orbit\r
246                                 orbit();\r
247                                 if (ididit) hitme = TRUE;\r
248                                 break;\r
249                         case 15:                        // transport "beam"\r
250                                 beam();\r
251                                 break;\r
252                         case 16:                        // mine\r
253                                 mine();\r
254                                 if (ididit) hitme = TRUE;\r
255                                 break;\r
256                         case 17:                        // crystals\r
257                                 usecrystals();\r
258                                 break;\r
259                         case 18:                        // shuttle\r
260                                 shuttle();\r
261                                 if (ididit) hitme = TRUE;\r
262                                 break;\r
263                         case 19:                        // Planet list\r
264                                 preport();\r
265                                 break;\r
266                         case 20:                        // Status information\r
267                                 srscan(2);\r
268                                 break;\r
269                         case 21:                        // Game Report \r
270                                 report(0);\r
271                                 break;\r
272                         case 22:                        // use COMPUTER!\r
273                                 eta();\r
274                                 break;\r
275                         case 23:\r
276                                 listCommands(TRUE);\r
277                                 break;\r
278                         case 24:                // Emergency exit\r
279                                 clearscreen();  // Hide screen\r
280                                 freeze(TRUE);   // forced save\r
281                                 exit(1);                // And quick exit\r
282                                 break;\r
283                         case 25:\r
284                                 probe();                // Launch probe\r
285                                 break;\r
286                         case 26:                        // Abandon Ship\r
287                                 abandn();\r
288                                 break;\r
289                         case 27:                        // Self Destruct\r
290                                 dstrct();\r
291                                 break;\r
292                         case 28:                        // Save Game\r
293                                 freeze(FALSE);\r
294                                 if (skill > 3)\r
295                                         prout("WARNING--Frozen games produce no plaques!");\r
296                                 break;\r
297                         case 29:                        // Try a desparation measure\r
298                                 deathray();\r
299                                 if (ididit) hitme = TRUE;\r
300                                 break;\r
301                         case 30:                        // What do we want for debug???\r
302 #ifdef DEBUG\r
303                                 debugme();\r
304 #endif\r
305                                 break;\r
306                         case 31:                // Call for help\r
307                                 help();\r
308                                 break;\r
309                         case 32:\r
310                                 alldone = 1;    // quit the game\r
311 #ifdef DEBUG\r
312                                 if (idebug) score();\r
313 #endif\r
314                                 break;\r
315                         case 33:\r
316                                 helpme();       // get help\r
317                                 break;\r
318                 }\r
319                 commandhook(commands[i], FALSE);\r
320                 for (;;) {\r
321                         if (alldone) break;             // Game has ended\r
322 #ifdef DEBUG\r
323                         if (idebug) prout("2500");\r
324 #endif\r
325                         if (Time != 0.0) {\r
326                                 events();\r
327                                 if (alldone) break;             // Events did us in\r
328                         }\r
329                         if (game.state.galaxy[quadx][quady] == 1000) { // Galaxy went Nova!\r
330                                 atover(0);\r
331                                 continue;\r
332                         }\r
333                         if (nenhere == 0) movetho();\r
334                         if (hitme && justin==0) {\r
335                                 attack(2);\r
336                                 if (alldone) break;\r
337                                 if (game.state.galaxy[quadx][quady] == 1000) {  // went NOVA! \r
338                                         atover(0);\r
339                                         hitme = TRUE;\r
340                                         continue;\r
341                                 }\r
342                         }\r
343                         break;\r
344                 }\r
345                 if (alldone) break;\r
346         }\r
347 }\r
348 \r
349 \r
350 int main(int argc, char **argv) {\r
351     int i, option, usecurses = TRUE;\r
352         int hitme;\r
353         char ch;\r
354 \r
355         while ((option = getopt(argc, argv, "t")) != -1) {\r
356             switch (option) {\r
357             case 't':\r
358                 usecurses = FALSE;\r
359                 break;\r
360             default:\r
361                 fprintf(stderr, "usage: sst [-t] [startcommand...].\n");\r
362                 exit(0);\r
363             }\r
364         }\r
365 \r
366         iostart(usecurses);\r
367         prelim(); \r
368         line[0] = '\0';\r
369         for (i = optind; i < argc;  i++) {\r
370                 strcat(line, argv[i]);\r
371                 strcat(line, " ");\r
372         }\r
373         while (TRUE) { /* Play a game */\r
374                 setup(line[0] == '\0');\r
375                 if (alldone) {\r
376                         score();\r
377                         alldone = 0;\r
378                 }\r
379                 else makemoves();\r
380                 skip(2);\r
381                 stars();\r
382                 skip(1);\r
383 \r
384                 if (tourn && alldone) {\r
385                         proutn("Do you want your score recorded?");\r
386                         if (ja()) {\r
387                                 chew2();\r
388                                 freeze(FALSE);\r
389                         }\r
390                 }\r
391                 proutn("Do you want to play again?");\r
392                 if (!ja()) break;\r
393         }\r
394         skip(1);\r
395         ioend();\r
396         puts("May the Great Bird of the Galaxy roost upon your home planet.");\r
397 }\r
398 \r
399 \r
400 void cramen(int i) {\r
401         /* return an enemy */\r
402         char *s;\r
403         \r
404         switch (i) {\r
405                 case IHR: s = "Romulan"; break;\r
406                 case IHK: s = "Klingon"; break;\r
407                 case IHC: s = "Commander"; break;\r
408                 case IHS: s = "Super-commander"; break;\r
409                 case IHSTAR: s = "Star"; break;\r
410                 case IHP: s = "Planet"; break;\r
411                 case IHB: s = "Starbase"; break;\r
412                 case IHBLANK: s = "Black hole"; break;\r
413                 case IHT: s = "Tholian"; break;\r
414                 case IHWEB: s = "Tholian web"; break;\r
415                 case IHQUEST: s = "Stranger"; break;\r
416                 default: s = "Unknown??"; break;\r
417         }\r
418         proutn(s);\r
419 }\r
420 \r
421 char *cramlc(enum loctype key, int x, int y) {\r
422         static char buf[32];\r
423         buf[0] = '\0';\r
424         if (key == quadrant) strcpy(buf, "Quadrant ");\r
425         else if (key == sector) strcpy(buf, "Sector ");\r
426         sprintf(buf+strlen(buf), "%d-%d", x, y);\r
427         return buf;\r
428 }\r
429 \r
430 void crmena(int i, int enemy, int key, int x, int y) {\r
431         if (i == 1) proutn("***");\r
432         cramen(enemy);\r
433         proutn(" at ");\r
434         proutn(cramlc(key, x, y));\r
435 }\r
436 \r
437 void crmshp(void) {\r
438         char *s;\r
439         switch (ship) {\r
440                 case IHE: s = "Enterprise"; break;\r
441                 case IHF: s = "Faerie Queene"; break;\r
442                 default:  s = "Ship???"; break;\r
443         }\r
444         proutn(s);\r
445 }\r
446 \r
447 void stars(void) {\r
448         prouts("******************************************************");\r
449         skip(1);\r
450 }\r
451 \r
452 double expran(double avrage) {\r
453         return -avrage*log(1e-7 + Rand());\r
454 }\r
455 \r
456 double Rand(void) {\r
457         return rand()/(1.0 + (double)RAND_MAX);\r
458 }\r
459 \r
460 void iran8(int *i, int *j) {\r
461         *i = Rand()*8.0 + 1.0;\r
462         *j = Rand()*8.0 + 1.0;\r
463 }\r
464 \r
465 void iran10(int *i, int *j) {\r
466         *i = Rand()*10.0 + 1.0;\r
467         *j = Rand()*10.0 + 1.0;\r
468 }\r
469 \r
470 void chew(void) {\r
471         linep = line;\r
472         *linep = 0;\r
473 }\r
474 \r
475 void chew2(void) {\r
476         /* return IHEOL next time */\r
477         linep = line+1;\r
478         *linep = 0;\r
479 }\r
480 \r
481 int scan(void) {\r
482         int i;\r
483         char *cp;\r
484 \r
485         // Init result\r
486         aaitem = 0.0;\r
487         *citem = 0;\r
488 \r
489         // Read a line if nothing here\r
490         if (*linep == 0) {\r
491                 if (linep != line) {\r
492                         chew();\r
493                         return IHEOL;\r
494                 }\r
495                 getline(line, sizeof(line));\r
496                 linep = line;\r
497         }\r
498         // Skip leading white space\r
499         while (*linep == ' ') linep++;\r
500         // Nothing left\r
501         if (*linep == 0) {\r
502                 chew();\r
503                 return IHEOL;\r
504         }\r
505         if (isdigit(*linep) || *linep=='+' || *linep=='-' || *linep=='.') {\r
506                 // treat as a number\r
507             i = 0;\r
508             if (sscanf(linep, "%lf%n", &aaitem, &i) < 1) {\r
509                 linep = line; // Invalid numbers are ignored\r
510                 *linep = 0;\r
511                 return IHEOL;\r
512             }\r
513             else {\r
514                 // skip to end\r
515                 linep += i;\r
516                 return IHREAL;\r
517             }\r
518         }\r
519         // Treat as alpha\r
520         cp = citem;\r
521         while (*linep && *linep!=' ') {\r
522                 if ((cp - citem) < 9) *cp++ = tolower(*linep);\r
523                 linep++;\r
524         }\r
525         *cp = 0;\r
526         return IHALPHA;\r
527 }\r
528 \r
529 int ja(void) {\r
530         chew();\r
531         while (TRUE) {\r
532                 scan();\r
533                 chew();\r
534                 if (*citem == 'y') return TRUE;\r
535                 if (*citem == 'n') return FALSE;\r
536                 proutn("Please answer with \"Y\" or \"N\":");\r
537         }\r
538 }\r
539 \r
540 void huh(void) {\r
541         chew();\r
542         skip(1);\r
543         prout("Beg your pardon, Captain?");\r
544 }\r
545 \r
546 int isit(char *s) {\r
547         /* New function -- compares s to scaned citem and returns true if it\r
548            matches to the length of s */\r
549 \r
550         return strncmp(s, citem, max(1, strlen(citem))) == 0;\r
551 \r
552 }\r
553 \r
554 #ifdef DEBUG\r
555 void debugme(void) {\r
556         proutn("Reset levels? ");\r
557         if (ja() != 0) {\r
558                 if (energy < inenrg) energy = inenrg;\r
559                 shield = inshld;\r
560                 torps = intorps;\r
561                 lsupres = inlsr;\r
562         }\r
563         proutn("Reset damage? ");\r
564         if (ja() != 0) {\r
565                 int i;\r
566                 for (i=0; i <= NDEVICES; i++) if (damage[i] > 0.0) damage[i] = 0.0;\r
567                 stdamtim = 1e30;\r
568         }\r
569         proutn("Toggle idebug? ");\r
570         if (ja() != 0) {\r
571                 idebug = !idebug;\r
572                 if (idebug) prout("Debug output ON");\r
573                 else prout("Debug output OFF");\r
574         }\r
575         proutn("Cause selective damage? ");\r
576         if (ja() != 0) {\r
577                 int i, key;\r
578                 for (i=1; i <= NDEVICES; i++) {\r
579                         proutn("Kill ");\r
580                         proutn(device[i]);\r
581                         proutn("? ");\r
582                         chew();\r
583                         key = scan();\r
584                         if (key == IHALPHA &&  isit("y")) {\r
585                                 damage[i] = 10.0;\r
586                                 if (i == DRADIO) stdamtim = game.state.date;\r
587                         }\r
588                 }\r
589         }\r
590         proutn("Examine/change events? ");\r
591         if (ja() != 0) {\r
592                 int i;\r
593                 for (i = 1; i < NEVENTS; i++) {\r
594                         int key;\r
595                         if (future[i] == 1e30) continue;\r
596                         switch (i) {\r
597                                 case FSNOVA:  proutn("Supernova       "); break;\r
598                                 case FTBEAM:  proutn("T Beam          "); break;\r
599                                 case FSNAP:   proutn("Snapshot        "); break;\r
600                                 case FBATTAK: proutn("Base Attack     "); break;\r
601                                 case FCDBAS:  proutn("Base Destroy    "); break;\r
602                                 case FSCMOVE: proutn("SC Move         "); break;\r
603                                 case FSCDBAS: proutn("SC Base Destroy "); break;\r
604                         }\r
605                         proutn("%.2f", future[i]-game.state.date);\r
606                         chew();\r
607                         proutn("  ?");\r
608                         key = scan();\r
609                         if (key == IHREAL) {\r
610                                 future[i] = game.state.date + aaitem;\r
611                         }\r
612                 }\r
613                 chew();\r
614         }\r
615 }\r
616                         \r
617 \r
618 #endif\r