7 #define DOC_NAME "sst.doc"
13 SRSCAN, MOVE, PHASERS, CALL, STATUS, IMPULSE, PHOTONS, ABANDON,
14 LRSCAN, WARP, SHIELDS, DESTRUCT, CHART, REST, DOCK, QUIT, and DAMAGE
15 were in the original non-"super" version of UT FORTRAN Star Trek.
17 Tholians weren't in the original. Dave is dubious about their merits.
18 (They are now controlled by OPTION_THOLIAN and turned off if the game
21 Planets and dilithium crystals weren't in the original. Dave is OK
22 with this idea. (It's now controlled by OPTION_PLANETS and turned
23 off if the game type is "plain".)
25 Dave says the bit about the Galileo getting turned into a
26 McDonald's is "consistant with our original vision". (This has been
27 left permanently enabled, as it can only happen if OPTION_PLANETS
30 Dave also says the Space Thingy should not be preserved across saved
31 games, so you can't prove to others that you've seen it. He says it
32 shouldn't fire back, either. It should do nothing except scream and
33 disappear when hit by photon torpedos. It's OK that it may move
34 when attacked, but it didn't in the original. (Whether the Thingy
35 can fire back is now controlled by OPTION_THINGY and turned off if the
36 game type is "plain" or "almy". The no-save behavior has been restored.)
38 The Faerie Queen, black holes, and time warping were in the original.
40 Here are Tom Almy's changes:
42 In early 1997, I got the bright idea to look for references to
43 "Super Star Trek" on the World Wide Web. There weren't many hits,
44 but there was one that came up with 1979 Fortran sources! This
45 version had a few additional features that mine didn't have,
46 however mine had some feature it didn't have. So I merged its
47 features that I liked. I also took a peek at the DECUS version (a
48 port, less sources, to the PDP-10), and some other variations.
50 1, Compared to the original UT version, I've changed the "help" command to
51 "call" and the "terminate" command to "quit" to better match
52 user expectations. The DECUS version apparently made those changes
53 as well as changing "freeze" to "save". However I like "freeze".
54 (Both "freeze" and "save" work in SST2K.)
56 2. The experimental deathray originally had only a 5% chance of
57 success, but could be used repeatedly. I guess after a couple
58 years of use, it was less "experimental" because the 1979
59 version had a 70% success rate. However it was prone to breaking
60 after use. I upgraded the deathray, but kept the original set of
61 failure modes (great humor!). (Now controlled by OPTION_DEATHRAY
62 and turned off if game type is "plain".)
64 3. The 1979 version also mentions srscan and lrscan working when
65 docked (using the starbase's scanners), so I made some changes here
66 to do this (and indicating that fact to the player), and then realized
67 the base would have a subspace radio as well -- doing a Chart when docked
68 updates the star chart, and all radio reports will be heard. The Dock
69 command will also give a report if a base is under attack.
71 4. Tholian Web from the 1979 version. (Now controlled by
72 OPTION_THOLIAN and turned off if game type is "plain".)
74 5. Enemies can ram the Enterprise. (Now controlled by OPTION_RAMMING
75 and turned off if game type is "plain".)
77 6. Regular Klingons and Romulans can move in Expert and Emeritus games.
78 This code could use improvement. (Now controlled by OPTION_MVBADDY
79 and turned off if game type is "plain".)
81 7. The deep-space probe feature from the DECUS version. (Now controlled
82 by OPTION_PROBE and turned off if game type is "plain").
84 8. 'emexit' command from the 1979 version.
86 9. Bugfix: Klingon commander movements are no longer reported if long-range
89 10. Bugfix: Better base positioning at startup (more spread out).
90 That made sense to add because most people abort games with
93 In June 2002, I fixed two known bugs and a documentation typo.
94 In June 2004 I fixed a number of bugs involving: 1) parsing invalid
95 numbers, 2) manual phasers when SR scan is damaged and commander is
96 present, 3) time warping into the future, 4) hang when moving
97 klingons in crowded quadrants. (These fixes are in SST2K.)
99 Here are Stas Sergeev's changes:
101 1. The Space Thingy can be shoved, if you ram it, and can fire back if
102 fired upon. (Now controlled by OPTION_THINGY and turned off if game
103 type is "plain" or "almy".)
105 2. When you are docked, base covers you with an almost invincible shield.
106 (A commander can still ram you, or a Romulan can destroy the base,
107 or a SCom can even succeed with direct attack IIRC, but this rarely
108 happens.) (Now controlled by OPTION_BASE and turned off if game
109 type is "plain" or "almy".)
111 3. Ramming a black hole is no longer instant death. There is a
112 chance you might get timewarped instead. (Now controlled by
113 OPTION_BLKHOLE and turned off if game type is "plain" or "almy".)
115 4. The Tholian can be hit with phasers.
117 5. SCom can't escape from you if no more enemies remain
118 (without this, chasing SCom can take an eternity).
120 6. Probe target you enter is now the destination quadrant. Before I don't
121 remember what it was, but it was something I had difficulty using.
123 7. Secret password is now autogenerated.
125 8. "Plaque" is adjusted for A4 paper :-)
127 9. Phasers now tells you how much energy needed, but only if the computer
130 10. Planets are auto-scanned when you enter the quadrant.
132 11. Mining or using crystals in presense of enemy now yields an attack.
133 There are other minor adjustments to what yields an attack
136 12. "freeze" command reverts to "save", most people will understand this
137 better anyway. (SST2K recognizes both.)
139 13. Screen-oriented interface, with sensor scans always up. (SST2K
140 supports both screen-oriented and TTY modes.)
142 Eric Raymond's changes:
144 Mainly, I translated this C code out of FORTRAN into C -- created #defines
145 for a lot of magic numbers and refactored the heck out of it.
147 1. "sos" and "call" becomes "mayday", "freeze" and "save" are both good.
149 2. Status report now indicates when dilithium crystals are on board.
151 3. Per Dave Matuszek's remarks, Thingy state is never saved across games.
153 4. Added game option selection so you can play a close (but not bug-for-
154 bug identical) approximation of older versions.
156 5. Half the quadrants now have inhabited planets, from which one
157 cannot mine dilithium (there will still be the same additional number
158 of dilithium-bearing planets). Right now this is just color, but
159 eventually we'll fold in BSD-Trek-like logic for Klingons to attack
160 and enslave inhabited worlds.
163 /* the input queue */
164 static char line[128], *linep = line;
169 int iscore, iskill; // Common PLAQ
173 int seed; // the random-number seed
174 bool idebug; // debug mode
175 bool randready; // Has the random-number generator initialized?
178 char *device[NDEVICES] = {
199 unsigned long option;
204 {"SRSCAN", SRSCAN, OPTION_TTY},
206 {"STATUS", STATUS, OPTION_TTY},
208 {"REQUEST", REQUEST, OPTION_TTY},
210 {"LRSCAN", LRSCAN, OPTION_TTY},
212 {"PHASERS", PHASERS, 0},
214 {"TORPEDO", TORPEDO, 0},
215 {"PHOTONS", TORPEDO, 0},
219 {"SHIELDS", SHIELDS, 0},
223 {"DAMAGES", DAMAGES, 0},
227 {"IMPULSE", IMPULSE, 0},
235 {"SENSORS", SENSORS, OPTION_PLANETS},
237 {"ORBIT", ORBIT, OPTION_PLANETS},
239 {"TRANSPORT", TRANSPORT, OPTION_PLANETS},
241 {"MINE", MINE, OPTION_PLANETS},
243 {"CRYSTALS", CRYSTALS, OPTION_PLANETS},
245 {"SHUTTLE", SHUTTLE, OPTION_PLANETS},
247 {"PLANETS", PLANETS, OPTION_PLANETS},
249 {"REPORT", REPORT, 0},
251 {"COMPUTER", COMPUTER, 0},
253 {"COMMANDS", COMMANDS, 0},
255 {"EMEXIT", EMEXIT, 0},
257 {"PROBE", PROBE, OPTION_PROBE},
262 {"ABANDON", ABANDON, 0},
264 {"DESTRUCT", DESTRUCT, 0},
266 {"DEATHRAY", DEATHRAY, 0},
268 {"DEBUG", DEBUGCMD, 0},
270 {"MAYDAY", MAYDAY, 0},
271 //{"SOS", MAYDAY, 0},
272 //{"CALL", MAYDAY, 0},
279 #define NUMCOMMANDS sizeof(commands)/sizeof(commands[0])
280 #define ACCEPT(i) (!commands[i].option || (commands[i].option & game.options))
282 static void listCommands(void) {
284 proutn("LEGAL COMMANDS ARE:");
285 for (i = 0; i < NUMCOMMANDS; i++) {
290 proutn("%-12s ", commands[i].name);
296 static void helpme(void)
299 char cmdbuf[32], *cp;
302 /* Give help on commands */
307 setwnd(prompt_window);
308 proutn("Help on what command? ");
311 setwnd(message_window);
312 if (key == IHEOL) return;
313 for (i = 0; i < NUMCOMMANDS; i++) {
314 if (ACCEPT(i) && strcasecmp(commands[i].name, citem)==0) {
315 i = commands[i].value;
319 if (i != NUMCOMMANDS) break;
321 prout("Valid commands:");
328 strcpy(cmdbuf, " ABBREV");
331 for (j = 0; commands[i].name[j]; j++)
332 cmdbuf[j] = toupper(commands[i].name[j]);
335 fp = fopen(SSTDOC, "r");
337 fp = fopen(DOC_NAME, "r");
339 prout("Spock- \"Captain, that information is missing from the");
340 prout(" computer. You need to find "DOC_NAME" and put it in the");
341 prout(" current directory or to "SSTDOC".\"");
343 * This used to continue: "You need to find SST.DOC and put
344 * it in the current directory."
349 if (fgets(linebuf, sizeof(linebuf), fp) == NULL) {
350 prout("Spock- \"Captain, there is no information on that command.\"");
354 if (linebuf[0] == '%' && linebuf[1] == '%'&& linebuf[2] == ' ') {
355 for (cp = linebuf+3; isspace(*cp); cp++)
357 linebuf[strlen(linebuf)-1] = '\0';
358 if (strcasecmp(cp, cmdbuf) == 0)
364 prout("Spock- \"Captain, I've found the following information:\"");
367 while (fgets(linebuf, sizeof(linebuf),fp)) {
368 if (strstr(linebuf, "******"))
375 void enqueue(char *s)
380 static void makemoves(void)
385 setwnd(message_window);
386 for(;;) { /* command loop */
388 for(;;) { /* get a command */
394 setwnd(prompt_window);
397 if (scan() == IHEOL) {
403 setwnd(message_window);
405 for (i=0; i < ABANDON; i++)
406 if (ACCEPT(i) && isit(commands[i].name)) {
407 v = commands[i].value;
410 if (i < ABANDON && (!commands[i].option || (commands[i].option & game.options)))
412 for (; i < NUMCOMMANDS; i++)
413 if (ACCEPT(i) && strcasecmp(commands[i].name, citem) == 0) {
414 v = commands[i].value;
417 if (i < NUMCOMMANDS && (!commands[i].option || (commands[i].option & game.options)))
421 commandhook(commands[i].name, true);
422 switch (v) { /* command switch */
423 case SRSCAN: // srscan
426 case STATUS: // status
429 case REQUEST: // status request
430 srscan(SCAN_REQUEST);
432 case LRSCAN: // lrscan
435 case PHASERS: // phasers
437 if (game.ididit) hitme = true;
439 case TORPEDO: // photons
441 if (game.ididit) hitme = true;
446 case SHIELDS: // shields
455 if (game.ididit) attack(0);
457 case DAMAGES: // damages
463 case IMPULSE: // impulse
468 if (game.ididit) hitme = true;
476 case SENSORS: // sensors
481 if (game.ididit) hitme = true;
483 case TRANSPORT: // transport "beam"
488 if (game.ididit) hitme = true;
490 case CRYSTALS: // crystals
492 if (game.ididit) hitme = true;
494 case SHUTTLE: // shuttle
496 if (game.ididit) hitme = true;
498 case PLANETS: // Planet list
501 case REPORT: // Game Report
504 case COMPUTER: // use COMPUTER!
510 case EMEXIT: // Emergency exit
511 clrscr(); // Hide screen
512 freeze(true); // forced save
513 exit(1); // And quick exit
516 probe(); // Launch probe
517 if (game.ididit) hitme = true;
519 case ABANDON: // Abandon Ship
522 case DESTRUCT: // Self Destruct
525 case SAVE: // Save Game
528 if (game.skill > SKILL_GOOD)
529 prout("WARNING--Saved games produce no plaques!");
531 case DEATHRAY: // Try a desparation measure
533 if (game.ididit) hitme = true;
535 case DEBUGCMD: // What do we want for debug???
538 case MAYDAY: // Call for help
540 if (game.ididit) hitme = true;
543 game.alldone = 1; // quit the game
546 helpme(); // get help
549 commandhook(commands[i].name, false);
551 if (game.alldone) break; // Game has ended
552 if (game.optime != 0.0) {
554 if (game.alldone) break; // Events did us in
556 if (game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova) { // Galaxy went Nova!
560 if (hitme && game.justin==0) {
562 if (game.alldone) break;
563 if (game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova) { // went NOVA!
571 if (game.alldone) break;
573 if (idebug) prout("=== Ending");
577 int main(int argc, char **argv)
581 game.options = OPTION_ALL &~ (OPTION_IOMODES | OPTION_SHOWME | OPTION_PLAIN | OPTION_ALMY);
583 game.options |= OPTION_CURSES | OPTION_SHOWME;
585 game.options |= OPTION_TTY;
587 while ((option = getopt(argc, argv, "tx")) != -1) {
590 game.options |= OPTION_TTY;
591 game.options &=~ OPTION_CURSES;
597 fprintf(stderr, "usage: sst [-t] [-x] [startcommand...].\n");
606 for (i = optind; i < argc; i++) {
607 strcat(line, argv[i]);
610 for(;;) { /* Play a game */
611 setwnd(fullscreen_window);
614 setup(line[0] == '\0');
624 if (game.tourn && game.alldone) {
625 proutn("Do you want your score recorded?");
631 proutn("Do you want to play again? ");
635 prout("May the Great Bird of the Galaxy roost upon your home planet.");
642 /* return an enemy */
646 case IHR: s = "Romulan"; break;
647 case IHK: s = "Klingon"; break;
648 case IHC: s = "Commander"; break;
649 case IHS: s = "Super-commander"; break;
650 case IHSTAR: s = "Star"; break;
651 case IHP: s = "Planet"; break;
652 case IHB: s = "Starbase"; break;
653 case IHBLANK: s = "Black hole"; break;
654 case IHT: s = "Tholian"; break;
655 case IHWEB: s = "Tholian web"; break;
656 case IHQUEST: s = "Stranger"; break;
657 default: s = "Unknown??"; break;
662 char *cramlc(enum loctype key, coord w)
666 if (key == quadrant) strcpy(buf, "Quadrant ");
667 else if (key == sector) strcpy(buf, "Sector ");
668 sprintf(buf+strlen(buf), "%d - %d", w.x, w.y);
672 void crmena(int i, int enemy, int key, coord w)
674 if (i == 1) proutn("***");
677 proutn(cramlc(key, w));
684 case IHE: s = "Enterprise"; break;
685 case IHF: s = "Faerie Queene"; break;
686 default: s = "Ship???"; break;
693 prouts("******************************************************");
697 double expran(double avrage)
699 return -avrage*log(1e-7 + Rand());
706 seed = (unsigned)time(NULL);
708 fprintf(logfp, "seed %d\n", seed);
712 return rand()/(1.0 + (double)RAND_MAX);
715 void iran(int size, int *i, int *j)
717 *i = Rand()*(size*1.0) + 1.0;
718 *j = Rand()*(size*1.0) + 1.0;
729 /* return IHEOL next time */
743 // Read a line if nothing here
749 cgetline(line, sizeof(line));
751 if (curwnd==prompt_window){
753 setwnd(message_window);
758 // Skip leading white space
759 while (*linep == ' ') linep++;
765 if (isdigit(*linep) || *linep=='+' || *linep=='-' || *linep=='.') {
768 if (sscanf(linep, "%lf%n", &aaitem, &i) < 1) {
769 linep = line; // Invalid numbers are ignored
781 while (*linep && *linep!=' ') {
782 if ((cp - citem) < 9) *cp++ = tolower(*linep);
795 if (*citem == 'y') return true;
796 if (*citem == 'n') return false;
797 proutn("Please answer with \"Y\" or \"N\": ");
805 prout("Beg your pardon, Captain?");
810 /* New function -- compares s to scanned citem and returns true if it
811 matches to the length of s */
813 return strncasecmp(s, citem, max(1, strlen(citem))) == 0;
819 proutn("Reset levels? ");
821 if (game.energy < game.inenrg) game.energy = game.inenrg;
822 game.shield = game.inshld;
823 game.torps = game.intorps;
824 game.lsupres = game.inlsr;
826 proutn("Reset damage? ");
829 for (i=0; i < NDEVICES; i++)
830 if (game.damage[i] > 0.0)
831 game.damage[i] = 0.0;
833 proutn("Toggle game.idebug? ");
836 if (idebug) prout("Debug output ON");
837 else prout("Debug output OFF");
839 proutn("Cause selective damage? ");
842 for (i=0; i < NDEVICES; i++) {
848 if (key == IHALPHA && isit("y")) {
849 game.damage[i] = 10.0;
853 proutn("Examine/change events? ");
856 for (i = 1; i < NEVENTS; i++) {
858 if (!is_scheduled(i)) continue;
860 case FSNOVA: proutn("Supernova "); break;
861 case FTBEAM: proutn("T Beam "); break;
862 case FSNAP: proutn("Snapshot "); break;
863 case FBATTAK: proutn("Base Attack "); break;
864 case FCDBAS: proutn("Base Destroy "); break;
865 case FSCMOVE: proutn("SC Move "); break;
866 case FSCDBAS: proutn("SC Base Destroy "); break;
868 proutn("%.2f", scheduled(i)-game.state.date);
878 proutn("Induce supernova here? ");
880 game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova = true;