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