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 In early 1997, I got the bright idea to look for references to
46 "Super Star Trek" on the World Wide Web. There weren't many hits,
47 but there was one that came up with 1979 Fortran sources! This
48 version had a few additional features that mine didn't have,
49 however mine had some feature it didn't have. So I merged its
50 features that I liked. I also took a peek at the DECUS version (a
51 port, less sources, to the PDP-10), and some other variations.
53 1, Compared to the original UT version, I've changed the "help" command to
54 "call" and the "terminate" command to "quit" to better match
55 user expectations. The DECUS version apparently made those changes
56 as well as changing "freeze" to "save". However I like "freeze".
57 (Both "freeze" and "save" work in SST2K.)
59 2. The experimental deathray originally had only a 5% chance of
60 success, but could be used repeatedly. I guess after a couple
61 years of use, it was less "experimental" because the 1979
62 version had a 70% success rate. However it was prone to breaking
63 after use. I upgraded the deathray, but kept the original set of
64 failure modes (great humor!). (Now controlled by OPTION_DEATHRAY
65 and turned off if game type is "plain".)
67 3. The 1979 version also mentions srscan and lrscan working when
68 docked (using the starbase's scanners), so I made some changes here
69 to do this (and indicating that fact to the player), and then realized
70 the base would have a subspace radio as well -- doing a Chart when docked
71 updates the star chart, and all radio reports will be heard. The Dock
72 command will also give a report if a base is under attack.
74 4. Tholian Web from the 1979 version. (Now controlled by
75 OPTION_THOLIAN and turned off if game type is "plain".)
77 5. Enemies can ram the Enterprise. (Now controlled by OPTION_RAMMING
78 and turned off if game type is "plain".)
80 6. Regular Klingons and Romulans can move in Expert and Emeritus games.
81 This code could use improvement. (Now controlled by OPTION_MVBADDY
82 and turned off if game type is "plain".)
84 7. The deep-space probe feature from the DECUS version. (Now controlled
85 by OPTION_PROBE and turned off if game type is "plain").
87 8. 'emexit' command from the 1979 version.
89 9. Bugfix: Klingon commander movements are no longer reported if long-range
92 10. Bugfix: Better base positioning at startup (more spread out).
93 That made sense to add because most people abort games with
96 In June 2002, I fixed two known bugs and a documentation typo.
97 In June 2004 I fixed a number of bugs involving: 1) parsing invalid
98 numbers, 2) manual phasers when SR scan is damaged and commander is
99 present, 3) time warping into the future, 4) hang when moving
100 klingons in crowded quadrants. (These fixes are in SST2K.)
102 Here are Stas Sergeev's changes:
104 1. The Space Thingy can be shoved, if you ram it, and can fire back if
105 fired upon. (Now controlled by OPTION_THINGY and turned off if game
106 type is "plain" or "almy".)
108 2. When you are docked, base covers you with an almost invincible shield.
109 (A commander can still ram you, or a Romulan can destroy the base,
110 or a SCom can even succeed with direct attack IIRC, but this rarely
111 happens.) (Now controlled by OPTION_BASE and turned off if game
112 type is "plain" or "almy".)
114 3. Ramming a black hole is no longer instant death. There is a
115 chance you might get timewarped instead. (Now controlled by
116 OPTION_BLKHOLE and turned off if game type is "plain" or "almy".)
118 4. The Tholian can be hit with phasers.
120 5. SCom can't escape from you if no more enemies remain
121 (without this, chasing SCom can take an eternity).
123 6. Probe target you enter is now the destination quadrant. Before I don't
124 remember what it was, but it was something I had difficulty using.
126 7. Secret password is now autogenerated.
128 8. "Plaque" is adjusted for A4 paper :-)
130 9. Phasers now tells you how much energy needed, but only if the computer
133 10. Planets are auto-scanned when you enter the quadrant.
135 11. Mining or using crystals in presense of enemy now yields an attack.
136 There are other minor adjustments to what yields an attack
139 12. "freeze" command reverts to "save", most people will understand this
140 better anyway. (SST2K recognizes both.)
142 13. Screen-oriented interface, with sensor scans always up. (SST2K
143 supports both screen-oriented and TTY modes.)
145 Eric Raymond's changes:
147 Mainly, I translated this C code out of FORTRAN into C -- created #defines
148 for a lot of magic numbers and refactored the heck out of it.
150 1. "sos" and "call" becomes "mayday", "freeze" and "save" are both good.
152 2. Status report now indicates when dilithium crystals are on board.
154 3. Per Dave Matuszek's remarks, Thingy state is never saved across games.
156 4. Added game option selection so you can play a close (but not bug-for-
157 bug identical) approximation of older versions.
160 /* the input queue */
161 static char line[128], *linep = line;
167 unsigned long option;
171 {"SRSCAN", SRSCAN, OPTION_TTY},
173 {"STATUS", STATUS, OPTION_TTY},
175 {"REQUEST", REQUEST, OPTION_TTY},
177 {"LRSCAN", LRSCAN, OPTION_TTY},
179 {"PHASERS", PHASERS, 0},
181 {"TORPEDO", TORPEDO, 0},
182 {"PHOTONS", TORPEDO, 0},
186 {"SHIELDS", SHIELDS, 0},
190 {"DAMAGES", DAMAGES, 0},
194 {"IMPULSE", IMPULSE, 0},
202 {"SENSORS", SENSORS, OPTION_PLANETS},
204 {"ORBIT", ORBIT, OPTION_PLANETS},
206 {"TRANSPORT", TRANSPORT, OPTION_PLANETS},
208 {"MINE", MINE, OPTION_PLANETS},
210 {"CRYSTALS", CRYSTALS, OPTION_PLANETS},
212 {"SHUTTLE", SHUTTLE, OPTION_PLANETS},
214 {"PLANETS", PLANETS, OPTION_PLANETS},
216 {"REPORT", REPORT, 0},
218 {"COMPUTER", COMPUTER, 0},
220 {"COMMANDS", COMMANDS, 0},
222 {"EMEXIT", EMEXIT, 0},
224 {"PROBE", PROBE, OPTION_PROBE},
229 {"ABANDON", ABANDON, 0},
231 {"DESTRUCT", DESTRUCT, 0},
233 {"DEATHRAY", DEATHRAY, 0},
235 {"DEBUG", DEBUGCMD, 0},
237 {"MAYDAY", MAYDAY, 0},
238 //{"SOS", MAYDAY, 0},
239 //{"CALL", MAYDAY, 0},
246 #define NUMCOMMANDS sizeof(commands)/sizeof(commands[0])
247 #define ACCEPT(i) (!commands[i].option || (commands[i].option & game.options))
249 static void listCommands(void) {
251 proutn("LEGAL COMMANDS ARE:");
252 for (i = 0; i < NUMCOMMANDS; i++) {
257 proutn("%-12s ", commands[i].name);
263 static void helpme(void)
266 char cmdbuf[32], *cp;
269 /* Give help on commands */
274 setwnd(prompt_window);
275 proutn("Help on what command? ");
278 setwnd(message_window);
279 if (key == IHEOL) return;
280 for (i = 0; i < NUMCOMMANDS; i++) {
281 if (ACCEPT(i) && strcasecmp(commands[i].name, citem)==0) {
282 i = commands[i].value;
286 if (i != NUMCOMMANDS) break;
288 prout("Valid commands:");
295 strcpy(cmdbuf, " ABBREV");
298 for (j = 0; commands[i].name[j]; j++)
299 cmdbuf[j] = toupper(commands[i].name[j]);
302 fp = fopen(SSTDOC, "r");
304 prout("Spock- \"Captain, that information is missing from the");
305 prout(" computer.\"");
307 * This used to continue: "You need to find SST.DOC and put
308 * it in the current directory."
313 if (fgets(linebuf, sizeof(linebuf), fp) == NULL) {
314 prout("Spock- \"Captain, there is no information on that command.\"");
318 if (linebuf[0] == '%' && linebuf[1] == '%'&& linebuf[2] == ' ') {
319 for (cp = linebuf+3; isspace(*cp); cp++)
321 linebuf[strlen(linebuf)-1] = '\0';
322 if (strcasecmp(cp, cmdbuf) == 0)
328 prout("Spock- \"Captain, I've found the following information:\"");
331 while (fgets(linebuf, sizeof(linebuf),fp)) {
332 if (strstr(linebuf, "******"))
339 void enqueue(char *s)
344 static void makemoves(void)
348 setwnd(message_window);
349 while (TRUE) { /* command loop */
351 while (TRUE) { /* get a command */
357 setwnd(prompt_window);
360 if (scan() == IHEOL) {
366 setwnd(message_window);
368 for (i=0; i < ABANDON; i++)
369 if (ACCEPT(i) && isit(commands[i].name)) {
370 v = commands[i].value;
373 if (i < ABANDON && (!commands[i].option || (commands[i].option & game.options)))
375 for (; i < NUMCOMMANDS; i++)
376 if (ACCEPT(i) && strcasecmp(commands[i].name, citem) == 0) {
377 v = commands[i].value;
380 if (i < NUMCOMMANDS && (!commands[i].option || (commands[i].option & game.options)))
384 commandhook(commands[i].name, TRUE);
385 switch (v) { /* command switch */
386 case SRSCAN: // srscan
389 case STATUS: // status
392 case REQUEST: // status request
393 srscan(SCAN_REQUEST);
395 case LRSCAN: // lrscan
398 case PHASERS: // phasers
400 if (ididit) hitme = TRUE;
402 case TORPEDO: // photons
404 if (ididit) hitme = TRUE;
409 case SHIELDS: // shields
418 if (ididit) attack(0);
420 case DAMAGES: // damages
426 case IMPULSE: // impulse
431 if (ididit) hitme = TRUE;
439 case SENSORS: // sensors
444 if (ididit) hitme = TRUE;
446 case TRANSPORT: // transport "beam"
451 if (ididit) hitme = TRUE;
453 case CRYSTALS: // crystals
455 if (ididit) hitme = TRUE;
457 case SHUTTLE: // shuttle
459 if (ididit) hitme = TRUE;
461 case PLANETS: // Planet list
464 case REPORT: // Game Report
467 case COMPUTER: // use COMPUTER!
473 case EMEXIT: // Emergency exit
474 clrscr(); // Hide screen
475 freeze(TRUE); // forced save
476 exit(1); // And quick exit
479 probe(); // Launch probe
480 if (ididit) hitme = TRUE;
482 case ABANDON: // Abandon Ship
485 case DESTRUCT: // Self Destruct
488 case SAVE: // Save Game
491 if (skill > SKILL_GOOD)
492 prout("WARNING--Saved games produce no plaques!");
494 case DEATHRAY: // Try a desparation measure
496 if (ididit) hitme = TRUE;
498 case DEBUGCMD: // What do we want for debug???
503 case MAYDAY: // Call for help
505 if (ididit) hitme = TRUE;
508 alldone = 1; // quit the game
514 helpme(); // get help
517 commandhook(commands[i].name, FALSE);
519 if (alldone) break; // Game has ended
521 if (idebug) prout("2500");
525 if (alldone) break; // Events did us in
527 if (game.state.galaxy[quadx][quady].supernova) { // Galaxy went Nova!
531 if (hitme && justin==0) {
534 if (game.state.galaxy[quadx][quady].supernova) { // went NOVA!
547 int main(int argc, char **argv)
551 game.options = OPTION_ALL &~ (OPTION_IOMODES | OPTION_SHOWME | OPTION_PLAIN | OPTION_ALMY);
553 game.options |= OPTION_CURSES | OPTION_SHOWME;
555 game.options |= OPTION_TTY;
557 while ((option = getopt(argc, argv, "t")) != -1) {
560 game.options |= OPTION_TTY;
561 game.options &=~ OPTION_CURSES;
564 fprintf(stderr, "usage: sst [-t] [startcommand...].\n");
573 for (i = optind; i < argc; i++) {
574 strcat(line, argv[i]);
577 while (TRUE) { /* Play a game */
578 setwnd(fullscreen_window);
580 prout("INITIAL OPTIONS: %0lx", game.options);
584 setup(line[0] == '\0');
594 if (tourn && alldone) {
595 proutn("Do you want your score recorded?");
601 proutn("Do you want to play again? ");
605 prout("May the Great Bird of the Galaxy roost upon your home planet.");
612 /* return an enemy */
616 case IHR: s = "Romulan"; break;
617 case IHK: s = "Klingon"; break;
618 case IHC: s = "Commander"; break;
619 case IHS: s = "Super-commander"; break;
620 case IHSTAR: s = "Star"; break;
621 case IHP: s = "Planet"; break;
622 case IHB: s = "Starbase"; break;
623 case IHBLANK: s = "Black hole"; break;
624 case IHT: s = "Tholian"; break;
625 case IHWEB: s = "Tholian web"; break;
626 case IHQUEST: s = "Stranger"; break;
627 default: s = "Unknown??"; break;
632 char *cramlc(enum loctype key, int x, int y)
636 if (key == quadrant) strcpy(buf, "Quadrant ");
637 else if (key == sector) strcpy(buf, "Sector ");
638 sprintf(buf+strlen(buf), "%d - %d", x, y);
642 void crmena(int i, int enemy, int key, int x, int y)
644 if (i == 1) proutn("***");
647 proutn(cramlc(key, x, y));
654 case IHE: s = "Enterprise"; break;
655 case IHF: s = "Faerie Queene"; break;
656 default: s = "Ship???"; break;
663 prouts("******************************************************");
667 double expran(double avrage)
669 return -avrage*log(1e-7 + Rand());
673 return rand()/(1.0 + (double)RAND_MAX);
676 void iran(int size, int *i, int *j)
678 *i = Rand()*(size*1.0) + 1.0;
679 *j = Rand()*(size*1.0) + 1.0;
690 /* return IHEOL next time */
704 // Read a line if nothing here
710 cgetline(line, sizeof(line));
712 if (curwnd==prompt_window){
714 setwnd(message_window);
719 // Skip leading white space
720 while (*linep == ' ') linep++;
726 if (isdigit(*linep) || *linep=='+' || *linep=='-' || *linep=='.') {
729 if (sscanf(linep, "%lf%n", &aaitem, &i) < 1) {
730 linep = line; // Invalid numbers are ignored
742 while (*linep && *linep!=' ') {
743 if ((cp - citem) < 9) *cp++ = tolower(*linep);
756 if (*citem == 'y') return TRUE;
757 if (*citem == 'n') return FALSE;
758 proutn("Please answer with \"Y\" or \"N\": ");
766 prout("Beg your pardon, Captain?");
771 /* New function -- compares s to scanned citem and returns true if it
772 matches to the length of s */
774 return strncasecmp(s, citem, max(1, strlen(citem))) == 0;
781 proutn("Reset levels? ");
783 if (energy < inenrg) energy = inenrg;
788 proutn("Reset damage? ");
791 for (i=0; i < NDEVICES; i++)
792 if (game.damage[i] > 0.0)
793 game.damage[i] = 0.0;
795 proutn("Toggle idebug? ");
798 if (idebug) prout("Debug output ON");
799 else prout("Debug output OFF");
801 proutn("Cause selective damage? ");
804 for (i=0; i < NDEVICES; i++) {
810 if (key == IHALPHA && isit("y")) {
811 game.damage[i] = 10.0;
815 proutn("Examine/change events? ");
818 for (i = 1; i < NEVENTS; i++) {
820 if (game.future[i] == FOREVER) continue;
822 case FSNOVA: proutn("Supernova "); break;
823 case FTBEAM: proutn("T Beam "); break;
824 case FSNAP: proutn("Snapshot "); break;
825 case FBATTAK: proutn("Base Attack "); break;
826 case FCDBAS: proutn("Base Destroy "); break;
827 case FSCMOVE: proutn("SC Move "); break;
828 case FSCDBAS: proutn("SC Base Destroy "); break;
830 proutn("%.2f", game.future[i]-game.state.date);
835 game.future[i] = game.state.date + aaitem;
840 proutn("Induce supernova here? ");
842 game.state.galaxy[quadx][quady].supernova = TRUE;