1 #define INCLUDED // Define externs here
9 #define SSTDOC "sst.doc"
16 SRSCAN, MOVE, PHASERS, CALL, STATUS, IMPULSE, PHOTONS, ABANDON,
17 LRSCAN, WARP, SHIELDS, DESTRUCT, CHART, REST, DOCK, QUIT, and DAMAGE
18 were in the original non-"super" version of UT FORTRAN Star Trek.
20 Tholians weren't in the original. Dave is dubious about their merits.
21 (They are now controlled by OPTION_THOLIAN and turned off if the game
24 Planets and dilithium crystals weren't in the original. Dave is OK
25 with this idea. (It's now controlled by OPTION_PLANETS and turned
26 off if the game type is "plain".)
28 Dave says the bit about the Galileo getting turned into a
29 McDonald's is "consistant with our original vision". (This has been
30 left permanently enabled, as it can only happen if OPTION_PLANETS
33 Dave also says the Space Thingy should not be preserved across saved
34 games, so you can't prove to others that you've seen it. He says it
35 shouldn't fire back, either. It should do nothing except scream and
36 disappear when hit by photon torpedos. It's OK that it may move
37 when attacked, but it didn't in the original. (Whether the Thingy
38 can fire back is now controlled by OPTION_THINGY and turned off if the
39 game type is "plain" or "almy". The no-save behavior has been restored.)
41 The Faerie Queen, black holes, and time warping were in the original.
43 Here are Tom Almy's changes:
45 Compared to original version, I've changed the "help" command to
46 "call" and the "terminate" command to "quit" to better match
47 user expectations. The DECUS version apparently made those changes
48 as well as changing "freeze" to "save". However I like "freeze".
49 (Both "freeze" and "save" work in SST2K.)
51 When I got a later version of Super Star Trek that I was converting
52 from, I added the emexit command.
54 That later version also mentions srscan and lrscan working when
55 docked (using the starbase's scanners), so I made some changes here
56 to do this (and indicating that fact to the player), and then realized
57 the base would have a subspace radio as well -- doing a Chart when docked
58 updates the star chart, and all radio reports will be heard. The Dock
59 command will also give a report if a base is under attack.
61 Klingon commander movements are no longer reported if long-range
66 1. Better base positioning at startup.
68 2. Deathray improvement (but keeping original failure alternatives).
70 3. Tholian Web. (Now controlled by OPTION_THOLIAN and turned off
71 if game type is "plain".)
73 4. Enemies can ram the Enterprise. (Now controlled by OPTION_RAMMING
74 and turned off if game type is "plain".)
76 5. Regular Klingons and Romulans can move in Expert and Emeritus games.
77 This code could use improvement. (Now controlled by OPTION_MVBADDY
78 and turned off if game type is "plain".)
80 6. The deep-space probe feature from the DECUS version. (Now controlled
81 by OPTION_PROBE and turned off if game type is "plain").
83 In June 2004 I fixed a number of bugs involving: 1) parsing invalid
84 numbers, 2) manual phasers when SR scan is damaged and commander is
85 present, 3) time warping into the future, 4) hang when moving
86 klingons in crowded quadrants. (These fixes are in SST2K.)
88 Here are Stas Sergeev's changes:
90 1. The Space Thingy can be shoved, if you ram it, and can fire back if
91 fired upon. (Now controlled by OPTION_THINGY and turned off if game
92 type is "plain" or "almy".)
94 2. When you are docked, base covers you with an almost invincible shield.
95 (A commander can still ram you, or a Romulan can destroy the base,
96 or a SCom can even succeed with direct attack IIRC, but this rarely
97 happens.) (Now controlled by OPTION_BASE and turned off if game
98 type is "plain" or "almy".)
100 3. Ramming a black hole is no longer instant death. There is a
101 chance you might get timewarped instead. (Now controlled by
102 OPTION_BLKHOLE and turned off if game type is "plain" or "almy".)
104 4. The Tholian can be hit with phasers.
106 5. SCom can't escape from you if no more enemies remain (without this,
107 chasing SCom can take an eternity).
109 6. Probe target you enter is now the destination quadrant. Before I don't
110 remember what it was, but it was something I had difficulty using.
112 7. Secret password is now autogenerated.
114 8. "Plaque" is adjusted for A4 paper :-)
116 9. Phasers now tells you how much energy needed, but only if the computer
119 10. Planets are auto-scanned when you enter the quadrant.
121 11. Mining or using crystals in presense of enemy now yields an attack.
122 There are other minor adjustments to what yields an attack
125 12. "freeze" command reverts to "save", most people will understand this
126 better anyway. (SST2K recognizes both.)
128 13. Screen-oriented interface, with sensor scans always up. (SST2K
129 supports both screen-oriented and TTY modes.)
131 Eric Raymond's changes:
133 Mainly, I translated this C code out of FORTRAN into C -- created #defines
134 for a lot of magic numbers and refactored the heck out of it.
136 1. "sos" and "call" becomes "mayday", "freeze" and "save" are both good.
138 2. Status report now indicates when dilithium crystals are on board.
140 3. Per Dave Matuszek's remarks, Thingy state is never saved across games.
142 4. Added game option selection so you can play a close (but not bug-for-
143 bug identical) approximation of older versions.
146 /* the input queue */
147 static char line[128], *linep = line;
153 unsigned long option;
157 {"SRSCAN", SRSCAN, OPTION_TTY},
159 {"STATUS", STATUS, OPTION_TTY},
161 {"REQUEST", REQUEST, OPTION_TTY},
163 {"LRSCAN", LRSCAN, OPTION_TTY},
165 {"PHASERS", PHASERS, 0},
167 {"TORPEDO", TORPEDO, 0},
168 {"PHOTONS", TORPEDO, 0},
172 {"SHIELDS", SHIELDS, 0},
176 {"DAMAGES", DAMAGES, 0},
180 {"IMPULSE", IMPULSE, 0},
188 {"SENSORS", SENSORS, OPTION_PLANETS},
190 {"ORBIT", ORBIT, OPTION_PLANETS},
192 {"TRANSPORT", TRANSPORT, OPTION_PLANETS},
194 {"MINE", MINE, OPTION_PLANETS},
196 {"CRYSTALS", CRYSTALS, OPTION_PLANETS},
198 {"SHUTTLE", SHUTTLE, OPTION_PLANETS},
200 {"PLANETS", PLANETS, OPTION_PLANETS},
202 {"REPORT", REPORT, 0},
204 {"COMPUTER", COMPUTER, 0},
206 {"COMMANDS", COMMANDS, 0},
208 {"EMEXIT", EMEXIT, 0},
210 {"PROBE", PROBE, OPTION_PROBE},
215 {"ABANDON", ABANDON, 0},
217 {"DESTRUCT", DESTRUCT, 0},
219 {"DEATHRAY", DEATHRAY, 0},
221 {"DEBUG", DEBUGCMD, 0},
223 {"MAYDAY", MAYDAY, 0},
224 //{"SOS", MAYDAY, 0},
225 //{"CALL", MAYDAY, 0},
232 #define NUMCOMMANDS sizeof(commands)/sizeof(commands[0])
233 #define ACCEPT(i) (!commands[i].option || (commands[i].option & game.options))
235 static void listCommands(void) {
237 proutn("LEGAL COMMANDS ARE:");
238 for (i = 0; i < NUMCOMMANDS; i++) {
243 proutn("%-12s ", commands[i].name);
249 static void helpme(void)
252 char cmdbuf[32], *cp;
255 /* Give help on commands */
260 setwnd(prompt_window);
261 proutn("Help on what command? ");
264 setwnd(message_window);
265 if (key == IHEOL) return;
266 for (i = 0; i < NUMCOMMANDS; i++) {
267 if (ACCEPT(i) && strcasecmp(commands[i].name, citem)==0) {
268 i = commands[i].value;
272 if (i != NUMCOMMANDS) break;
274 prout("Valid commands:");
281 strcpy(cmdbuf, " ABBREV");
284 for (j = 0; commands[i].name[j]; j++)
285 cmdbuf[j] = toupper(commands[i].name[j]);
288 fp = fopen(SSTDOC, "r");
290 prout("Spock- \"Captain, that information is missing from the");
291 prout(" computer.\"");
293 * This used to continue: "You need to find SST.DOC and put
294 * it in the current directory."
299 if (fgets(linebuf, sizeof(linebuf), fp) == NULL) {
300 prout("Spock- \"Captain, there is no information on that command.\"");
304 if (linebuf[0] == '%' && linebuf[1] == '%'&& linebuf[2] == ' ') {
305 for (cp = linebuf+3; isspace(*cp); cp++)
307 linebuf[strlen(linebuf)-1] = '\0';
308 if (strcasecmp(cp, cmdbuf) == 0)
314 prout("Spock- \"Captain, I've found the following information:\"");
317 while (fgets(linebuf, sizeof(linebuf),fp)) {
318 if (strstr(linebuf, "******"))
325 void enqueue(char *s)
330 static void makemoves(void)
334 setwnd(message_window);
335 while (TRUE) { /* command loop */
337 while (TRUE) { /* get a command */
343 setwnd(prompt_window);
346 if (scan() == IHEOL) {
352 setwnd(message_window);
354 for (i=0; i < ABANDON; i++)
355 if (ACCEPT(i) && isit(commands[i].name)) {
356 v = commands[i].value;
359 if (i < ABANDON && (!commands[i].option || (commands[i].option & game.options)))
361 for (; i < NUMCOMMANDS; i++)
362 if (ACCEPT(i) && strcasecmp(commands[i].name, citem) == 0) {
363 v = commands[i].value;
366 if (i < NUMCOMMANDS && (!commands[i].option || (commands[i].option & game.options)))
370 commandhook(commands[i].name, TRUE);
371 switch (v) { /* command switch */
372 case SRSCAN: // srscan
375 case STATUS: // status
378 case REQUEST: // status request
379 srscan(SCAN_REQUEST);
381 case LRSCAN: // lrscan
384 case PHASERS: // phasers
386 if (ididit) hitme = TRUE;
388 case TORPEDO: // photons
390 if (ididit) hitme = TRUE;
395 case SHIELDS: // shields
404 if (ididit) attack(0);
406 case DAMAGES: // damages
412 case IMPULSE: // impulse
417 if (ididit) hitme = TRUE;
425 case SENSORS: // sensors
430 if (ididit) hitme = TRUE;
432 case TRANSPORT: // transport "beam"
437 if (ididit) hitme = TRUE;
439 case CRYSTALS: // crystals
441 if (ididit) hitme = TRUE;
443 case SHUTTLE: // shuttle
445 if (ididit) hitme = TRUE;
447 case PLANETS: // Planet list
450 case REPORT: // Game Report
453 case COMPUTER: // use COMPUTER!
459 case EMEXIT: // Emergency exit
460 clrscr(); // Hide screen
461 freeze(TRUE); // forced save
462 exit(1); // And quick exit
465 probe(); // Launch probe
466 if (ididit) hitme = TRUE;
468 case ABANDON: // Abandon Ship
471 case DESTRUCT: // Self Destruct
474 case SAVE: // Save Game
477 if (skill > SKILL_GOOD)
478 prout("WARNING--Saved games produce no plaques!");
480 case DEATHRAY: // Try a desparation measure
482 if (ididit) hitme = TRUE;
484 case DEBUGCMD: // What do we want for debug???
489 case MAYDAY: // Call for help
491 if (ididit) hitme = TRUE;
494 alldone = 1; // quit the game
500 helpme(); // get help
503 commandhook(commands[i].name, FALSE);
505 if (alldone) break; // Game has ended
507 if (idebug) prout("2500");
511 if (alldone) break; // Events did us in
513 if (game.state.galaxy[quadx][quady].supernova) { // Galaxy went Nova!
517 if (hitme && justin==0) {
520 if (game.state.galaxy[quadx][quady].supernova) { // went NOVA!
533 int main(int argc, char **argv)
537 game.options = OPTION_ALL &~ (OPTION_IOMODES | OPTION_SHOWME);
539 game.options |= OPTION_CURSES | OPTION_SHOWME;
541 game.options |= OPTION_TTY;
543 while ((option = getopt(argc, argv, "t")) != -1) {
546 game.options |= OPTION_TTY;
547 game.options &=~ OPTION_CURSES;
550 fprintf(stderr, "usage: sst [-t] [startcommand...].\n");
559 for (i = optind; i < argc; i++) {
560 strcat(line, argv[i]);
563 while (TRUE) { /* Play a game */
564 setwnd(fullscreen_window);
566 prout("INITIAL OPTIONS: %0lx", game.options);
570 setup(line[0] == '\0');
580 if (tourn && alldone) {
581 proutn("Do you want your score recorded?");
587 proutn("Do you want to play again? ");
591 prout("May the Great Bird of the Galaxy roost upon your home planet.");
598 /* return an enemy */
602 case IHR: s = "Romulan"; break;
603 case IHK: s = "Klingon"; break;
604 case IHC: s = "Commander"; break;
605 case IHS: s = "Super-commander"; break;
606 case IHSTAR: s = "Star"; break;
607 case IHP: s = "Planet"; break;
608 case IHB: s = "Starbase"; break;
609 case IHBLANK: s = "Black hole"; break;
610 case IHT: s = "Tholian"; break;
611 case IHWEB: s = "Tholian web"; break;
612 case IHQUEST: s = "Stranger"; break;
613 default: s = "Unknown??"; break;
618 char *cramlc(enum loctype key, int x, int y)
622 if (key == quadrant) strcpy(buf, "Quadrant ");
623 else if (key == sector) strcpy(buf, "Sector ");
624 sprintf(buf+strlen(buf), "%d - %d", x, y);
628 void crmena(int i, int enemy, int key, int x, int y)
630 if (i == 1) proutn("***");
633 proutn(cramlc(key, x, y));
640 case IHE: s = "Enterprise"; break;
641 case IHF: s = "Faerie Queene"; break;
642 default: s = "Ship???"; break;
649 prouts("******************************************************");
653 double expran(double avrage)
655 return -avrage*log(1e-7 + Rand());
659 return rand()/(1.0 + (double)RAND_MAX);
662 void iran(int size, int *i, int *j)
664 *i = Rand()*(size*1.0) + 1.0;
665 *j = Rand()*(size*1.0) + 1.0;
676 /* return IHEOL next time */
690 // Read a line if nothing here
696 cgetline(line, sizeof(line));
698 if (curwnd==prompt_window){
700 setwnd(message_window);
705 // Skip leading white space
706 while (*linep == ' ') linep++;
712 if (isdigit(*linep) || *linep=='+' || *linep=='-' || *linep=='.') {
715 if (sscanf(linep, "%lf%n", &aaitem, &i) < 1) {
716 linep = line; // Invalid numbers are ignored
728 while (*linep && *linep!=' ') {
729 if ((cp - citem) < 9) *cp++ = tolower(*linep);
742 if (*citem == 'y') return TRUE;
743 if (*citem == 'n') return FALSE;
744 proutn("Please answer with \"Y\" or \"N\": ");
752 prout("Beg your pardon, Captain?");
757 /* New function -- compares s to scanned citem and returns true if it
758 matches to the length of s */
760 return strncasecmp(s, citem, max(1, strlen(citem))) == 0;
767 proutn("Reset levels? ");
769 if (energy < inenrg) energy = inenrg;
774 proutn("Reset damage? ");
777 for (i=0; i < NDEVICES; i++)
778 if (game.damage[i] > 0.0)
779 game.damage[i] = 0.0;
781 proutn("Toggle idebug? ");
784 if (idebug) prout("Debug output ON");
785 else prout("Debug output OFF");
787 proutn("Cause selective damage? ");
790 for (i=0; i < NDEVICES; i++) {
796 if (key == IHALPHA && isit("y")) {
797 game.damage[i] = 10.0;
801 proutn("Examine/change events? ");
804 for (i = 1; i < NEVENTS; i++) {
806 if (game.future[i] == FOREVER) continue;
808 case FSNOVA: proutn("Supernova "); break;
809 case FTBEAM: proutn("T Beam "); break;
810 case FSNAP: proutn("Snapshot "); break;
811 case FBATTAK: proutn("Base Attack "); break;
812 case FCDBAS: proutn("Base Destroy "); break;
813 case FSCMOVE: proutn("SC Move "); break;
814 case FSCDBAS: proutn("SC Base Destroy "); break;
816 proutn("%.2f", game.future[i]-game.state.date);
821 game.future[i] = game.state.date + aaitem;
826 proutn("Induce supernova here? ");
828 game.state.galaxy[quadx][quady].supernova = TRUE;