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