Set up pre- and post-command hooks.
[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();\r
221                                 break;\r
222                         case 7:                 // damages\r
223                                 dreprt();\r
224                                 break;\r
225                         case 8:                 // chart\r
226                                 chart(0);\r
227                                 break;\r
228                         case 9:                 // impulse\r
229                                 impuls();\r
230                                 break;\r
231                         case 10:                // rest\r
232                                 wait();\r
233                                 if (ididit) hitme = TRUE;\r
234                                 break;\r
235                         case 11:                // warp\r
236                                 setwrp();\r
237                                 break;\r
238                         case 12:                // status\r
239                                 srscan(3);\r
240                                 break;\r
241                         case 13:                        // sensors\r
242                                 sensor();\r
243                                 break;\r
244                         case 14:                        // orbit\r
245                                 orbit();\r
246                                 if (ididit) hitme = TRUE;\r
247                                 break;\r
248                         case 15:                        // transport "beam"\r
249                                 beam();\r
250                                 break;\r
251                         case 16:                        // mine\r
252                                 mine();\r
253                                 if (ididit) hitme = TRUE;\r
254                                 break;\r
255                         case 17:                        // crystals\r
256                                 usecrystals();\r
257                                 break;\r
258                         case 18:                        // shuttle\r
259                                 shuttle();\r
260                                 if (ididit) hitme = TRUE;\r
261                                 break;\r
262                         case 19:                        // Planet list\r
263                                 preport();\r
264                                 break;\r
265                         case 20:                        // Status information\r
266                                 srscan(2);\r
267                                 break;\r
268                         case 21:                        // Game Report \r
269                                 report(0);\r
270                                 break;\r
271                         case 22:                        // use COMPUTER!\r
272                                 eta();\r
273                                 break;\r
274                         case 23:\r
275                                 listCommands(TRUE);\r
276                                 break;\r
277                         case 24:                // Emergency exit\r
278                                 clearscreen();  // Hide screen\r
279                                 freeze(TRUE);   // forced save\r
280                                 exit(1);                // And quick exit\r
281                                 break;\r
282                         case 25:\r
283                                 probe();                // Launch probe\r
284                                 break;\r
285                         case 26:                        // Abandon Ship\r
286                                 abandn();\r
287                                 break;\r
288                         case 27:                        // Self Destruct\r
289                                 dstrct();\r
290                                 break;\r
291                         case 28:                        // Save Game\r
292                                 freeze(FALSE);\r
293                                 if (skill > 3)\r
294                                         prout("WARNING--Frozen games produce no plaques!");\r
295                                 break;\r
296                         case 29:                        // Try a desparation measure\r
297                                 deathray();\r
298                                 if (ididit) hitme = TRUE;\r
299                                 break;\r
300                         case 30:                        // What do we want for debug???\r
301 #ifdef DEBUG\r
302                                 debugme();\r
303 #endif\r
304                                 break;\r
305                         case 31:                // Call for help\r
306                                 help();\r
307                                 break;\r
308                         case 32:\r
309                                 alldone = 1;    // quit the game\r
310 #ifdef DEBUG\r
311                                 if (idebug) score();\r
312 #endif\r
313                                 break;\r
314                         case 33:\r
315                                 helpme();       // get help\r
316                                 break;\r
317                 }\r
318                 commandhook(commands[i], FALSE);\r
319                 for (;;) {\r
320                         if (alldone) break;             // Game has ended\r
321 #ifdef DEBUG\r
322                         if (idebug) prout("2500");\r
323 #endif\r
324                         if (Time != 0.0) {\r
325                                 events();\r
326                                 if (alldone) break;             // Events did us in\r
327                         }\r
328                         if (game.state.galaxy[quadx][quady] == 1000) { // Galaxy went Nova!\r
329                                 atover(0);\r
330                                 continue;\r
331                         }\r
332                         if (nenhere == 0) movetho();\r
333                         if (hitme && justin==0) {\r
334                                 attack(2);\r
335                                 if (alldone) break;\r
336                                 if (game.state.galaxy[quadx][quady] == 1000) {  // went NOVA! \r
337                                         atover(0);\r
338                                         hitme = TRUE;\r
339                                         continue;\r
340                                 }\r
341                         }\r
342                         break;\r
343                 }\r
344                 if (alldone) break;\r
345         }\r
346 }\r
347 \r
348 \r
349 int main(int argc, char **argv) {\r
350     int i, option, usecurses = TRUE;\r
351         int hitme;\r
352         char ch;\r
353 \r
354         while ((option = getopt(argc, argv, "t")) != -1) {\r
355             switch (option) {\r
356             case 't':\r
357                 usecurses = FALSE;\r
358                 break;\r
359             default:\r
360                 fprintf(stderr, "usage: sst [-t] [startcommand...].\n");\r
361                 exit(0);\r
362             }\r
363         }\r
364 \r
365         iostart(usecurses);\r
366         prelim(); \r
367         line[0] = '\0';\r
368         for (i = optind; i < argc;  i++) {\r
369                 strcat(line, argv[i]);\r
370                 strcat(line, " ");\r
371         }\r
372         while (TRUE) { /* Play a game */\r
373                 setup(line[0] == '\0');\r
374                 if (alldone) {\r
375                         score();\r
376                         alldone = 0;\r
377                 }\r
378                 else makemoves();\r
379                 skip(2);\r
380                 stars();\r
381                 skip(1);\r
382 \r
383                 if (tourn && alldone) {\r
384                         proutn("Do you want your score recorded?");\r
385                         if (ja()) {\r
386                                 chew2();\r
387                                 freeze(FALSE);\r
388                         }\r
389                 }\r
390                 proutn("Do you want to play again?");\r
391                 if (!ja()) break;\r
392         }\r
393         skip(1);\r
394         ioend();\r
395         puts("May the Great Bird of the Galaxy roost upon your home planet.");\r
396 }\r
397 \r
398 \r
399 void cramen(int i) {\r
400         /* return an enemy */\r
401         char *s;\r
402         \r
403         switch (i) {\r
404                 case IHR: s = "Romulan"; break;\r
405                 case IHK: s = "Klingon"; break;\r
406                 case IHC: s = "Commander"; break;\r
407                 case IHS: s = "Super-commander"; break;\r
408                 case IHSTAR: s = "Star"; break;\r
409                 case IHP: s = "Planet"; break;\r
410                 case IHB: s = "Starbase"; break;\r
411                 case IHBLANK: s = "Black hole"; break;\r
412                 case IHT: s = "Tholian"; break;\r
413                 case IHWEB: s = "Tholian web"; break;\r
414                 default: s = "Unknown??"; break;\r
415         }\r
416         proutn(s);\r
417 }\r
418 \r
419 char *cramlc(enum loctype key, int x, int y) {\r
420         static char buf[32];\r
421         buf[0] = '\0';\r
422         if (key == quadrant) strcpy(buf, "Quadrant ");\r
423         else if (key == sector) strcpy(buf, "Sector ");\r
424         sprintf(buf+strlen(buf), "%d-%d", x, y);\r
425         return buf;\r
426 }\r
427 \r
428 void crmena(int i, int enemy, int key, int x, int y) {\r
429         if (i == 1) proutn("***");\r
430         cramen(enemy);\r
431         proutn(" at ");\r
432         proutn(cramlc(key, x, y));\r
433 }\r
434 \r
435 void crmshp(void) {\r
436         char *s;\r
437         switch (ship) {\r
438                 case IHE: s = "Enterprise"; break;\r
439                 case IHF: s = "Faerie Queene"; break;\r
440                 default:  s = "Ship???"; break;\r
441         }\r
442         proutn(s);\r
443 }\r
444 \r
445 void stars(void) {\r
446         prouts("******************************************************");\r
447         skip(1);\r
448 }\r
449 \r
450 double expran(double avrage) {\r
451         return -avrage*log(1e-7 + Rand());\r
452 }\r
453 \r
454 double Rand(void) {\r
455         return rand()/(1.0 + (double)RAND_MAX);\r
456 }\r
457 \r
458 void iran8(int *i, int *j) {\r
459         *i = Rand()*8.0 + 1.0;\r
460         *j = Rand()*8.0 + 1.0;\r
461 }\r
462 \r
463 void iran10(int *i, int *j) {\r
464         *i = Rand()*10.0 + 1.0;\r
465         *j = Rand()*10.0 + 1.0;\r
466 }\r
467 \r
468 void chew(void) {\r
469         linep = line;\r
470         *linep = 0;\r
471 }\r
472 \r
473 void chew2(void) {\r
474         /* return IHEOL next time */\r
475         linep = line+1;\r
476         *linep = 0;\r
477 }\r
478 \r
479 int scan(void) {\r
480         int i;\r
481         char *cp;\r
482 \r
483         // Init result\r
484         aaitem = 0.0;\r
485         *citem = 0;\r
486 \r
487         // Read a line if nothing here\r
488         if (*linep == 0) {\r
489                 if (linep != line) {\r
490                         chew();\r
491                         return IHEOL;\r
492                 }\r
493                 getline(line, sizeof(line));\r
494                 linep = line;\r
495         }\r
496         // Skip leading white space\r
497         while (*linep == ' ') linep++;\r
498         // Nothing left\r
499         if (*linep == 0) {\r
500                 chew();\r
501                 return IHEOL;\r
502         }\r
503         if (isdigit(*linep) || *linep=='+' || *linep=='-' || *linep=='.') {\r
504                 // treat as a number\r
505             if (sscanf(linep, "%lf%n", &aaitem, &i) < 1) {\r
506                 linep = line; // Invalid numbers are ignored\r
507                 *linep = 0;\r
508                 return IHEOL;\r
509             }\r
510             else {\r
511                 // skip to end\r
512                 linep += i;\r
513                 return IHREAL;\r
514             }\r
515         }\r
516         // Treat as alpha\r
517         cp = citem;\r
518         while (*linep && *linep!=' ') {\r
519                 if ((cp - citem) < 9) *cp++ = tolower(*linep);\r
520                 linep++;\r
521         }\r
522         *cp = 0;\r
523         return IHALPHA;\r
524 }\r
525 \r
526 int ja(void) {\r
527         chew();\r
528         while (TRUE) {\r
529                 scan();\r
530                 chew();\r
531                 if (*citem == 'y') return TRUE;\r
532                 if (*citem == 'n') return FALSE;\r
533                 proutn("Please answer with \"Y\" or \"N\":");\r
534         }\r
535 }\r
536 \r
537 double square(double i) { return i*i; }\r
538                                                                         \r
539 void huh(void) {\r
540         chew();\r
541         skip(1);\r
542         prout("Beg your pardon, Captain?");\r
543 }\r
544 \r
545 int isit(char *s) {\r
546         /* New function -- compares s to scaned citem and returns true if it\r
547            matches to the length of s */\r
548 \r
549         return strncmp(s, citem, max(1, strlen(citem))) == 0;\r
550 \r
551 }\r
552 \r
553 #ifdef DEBUG\r
554 void debugme(void) {\r
555         proutn("Reset levels? ");\r
556         if (ja() != 0) {\r
557                 if (energy < inenrg) energy = inenrg;\r
558                 shield = inshld;\r
559                 torps = intorps;\r
560                 lsupres = inlsr;\r
561         }\r
562         proutn("Reset damage? ");\r
563         if (ja() != 0) {\r
564                 int i;\r
565                 for (i=0; i <= NDEVICES; i++) if (damage[i] > 0.0) damage[i] = 0.0;\r
566                 stdamtim = 1e30;\r
567         }\r
568         proutn("Toggle idebug? ");\r
569         if (ja() != 0) {\r
570                 idebug = !idebug;\r
571                 if (idebug) prout("Debug output ON");\r
572                 else prout("Debug output OFF");\r
573         }\r
574         proutn("Cause selective damage? ");\r
575         if (ja() != 0) {\r
576                 int i, key;\r
577                 for (i=1; i <= NDEVICES; i++) {\r
578                         proutn("Kill ");\r
579                         proutn(device[i]);\r
580                         proutn("? ");\r
581                         chew();\r
582                         key = scan();\r
583                         if (key == IHALPHA &&  isit("y")) {\r
584                                 damage[i] = 10.0;\r
585                                 if (i == DRADIO) stdamtim = game.state.date;\r
586                         }\r
587                 }\r
588         }\r
589         proutn("Examine/change events? ");\r
590         if (ja() != 0) {\r
591                 int i;\r
592                 for (i = 1; i < NEVENTS; i++) {\r
593                         int key;\r
594                         if (future[i] == 1e30) continue;\r
595                         switch (i) {\r
596                                 case FSNOVA:  proutn("Supernova       "); break;\r
597                                 case FTBEAM:  proutn("T Beam          "); break;\r
598                                 case FSNAP:   proutn("Snapshot        "); break;\r
599                                 case FBATTAK: proutn("Base Attack     "); break;\r
600                                 case FCDBAS:  proutn("Base Destroy    "); break;\r
601                                 case FSCMOVE: proutn("SC Move         "); break;\r
602                                 case FSCDBAS: proutn("SC Base Destroy "); break;\r
603                         }\r
604                         proutn("%.2f", future[i]-game.state.date);\r
605                         chew();\r
606                         proutn("  ?");\r
607                         key = scan();\r
608                         if (key == IHREAL) {\r
609                                 future[i] = game.state.date + aaitem;\r
610                         }\r
611                 }\r
612                 chew();\r
613         }\r
614 }\r
615                         \r
616 \r
617 #endif\r