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 (1979) version of Super Star Trek that I was converting
52 from, I added the emexit command from it.
54 That 1979 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.
63 1. The experimental deathray originally had only a 5% chance of
64 success, but could be used repeatedly. I guess after a couple
65 years of use, it was less "experimental" because the 1979
66 version had a 70% success rate. However it was prone to breaking
67 after use. I upgraded the deathray, but kept the original set of
68 failure modes (great humor!). (Now controlled by OPTION_DEATHRAY
69 and turned off if game type is "plain".)
71 2. Tholian Web from the 1979 version. (Now controlled by
72 OPTION_THOLIAN and turned off if game type is "plain".)
74 3. Enemies can ram the Enterprise. (Now controlled by OPTION_RAMMING
75 and turned off if game type is "plain".)
77 4. 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 5. The deep-space probe feature from the DECUS version. (Now controlled
82 by OPTION_PROBE and turned off if game type is "plain").
84 6. Bugfix: Klingon commander movements are no longer reported if long-range
87 7. Bugfix: Better base positioning at startup (more spread out).
88 That made sense to add because most people abort games with
91 In June 2002, I fixed two known bugs and a documentation typo.
92 In June 2004 I fixed a number of bugs involving: 1) parsing invalid
93 numbers, 2) manual phasers when SR scan is damaged and commander is
94 present, 3) time warping into the future, 4) hang when moving
95 klingons in crowded quadrants. (These fixes are in SST2K.)
97 Here are Stas Sergeev's changes:
99 1. The Space Thingy can be shoved, if you ram it, and can fire back if
100 fired upon. (Now controlled by OPTION_THINGY and turned off if game
101 type is "plain" or "almy".)
103 2. When you are docked, base covers you with an almost invincible shield.
104 (A commander can still ram you, or a Romulan can destroy the base,
105 or a SCom can even succeed with direct attack IIRC, but this rarely
106 happens.) (Now controlled by OPTION_BASE and turned off if game
107 type is "plain" or "almy".)
109 3. Ramming a black hole is no longer instant death. There is a
110 chance you might get timewarped instead. (Now controlled by
111 OPTION_BLKHOLE and turned off if game type is "plain" or "almy".)
113 4. The Tholian can be hit with phasers.
115 5. SCom can't escape from you if no more enemies remain
116 (without this, chasing SCom can take an eternity).
118 6. Probe target you enter is now the destination quadrant. Before I don't
119 remember what it was, but it was something I had difficulty using.
121 7. Secret password is now autogenerated.
123 8. "Plaque" is adjusted for A4 paper :-)
125 9. Phasers now tells you how much energy needed, but only if the computer
128 10. Planets are auto-scanned when you enter the quadrant.
130 11. Mining or using crystals in presense of enemy now yields an attack.
131 There are other minor adjustments to what yields an attack
134 12. "freeze" command reverts to "save", most people will understand this
135 better anyway. (SST2K recognizes both.)
137 13. Screen-oriented interface, with sensor scans always up. (SST2K
138 supports both screen-oriented and TTY modes.)
140 Eric Raymond's changes:
142 Mainly, I translated this C code out of FORTRAN into C -- created #defines
143 for a lot of magic numbers and refactored the heck out of it.
145 1. "sos" and "call" becomes "mayday", "freeze" and "save" are both good.
147 2. Status report now indicates when dilithium crystals are on board.
149 3. Per Dave Matuszek's remarks, Thingy state is never saved across games.
151 4. Added game option selection so you can play a close (but not bug-for-
152 bug identical) approximation of older versions.
155 /* the input queue */
156 static char line[128], *linep = line;
162 unsigned long option;
166 {"SRSCAN", SRSCAN, OPTION_TTY},
168 {"STATUS", STATUS, OPTION_TTY},
170 {"REQUEST", REQUEST, OPTION_TTY},
172 {"LRSCAN", LRSCAN, OPTION_TTY},
174 {"PHASERS", PHASERS, 0},
176 {"TORPEDO", TORPEDO, 0},
177 {"PHOTONS", TORPEDO, 0},
181 {"SHIELDS", SHIELDS, 0},
185 {"DAMAGES", DAMAGES, 0},
189 {"IMPULSE", IMPULSE, 0},
197 {"SENSORS", SENSORS, OPTION_PLANETS},
199 {"ORBIT", ORBIT, OPTION_PLANETS},
201 {"TRANSPORT", TRANSPORT, OPTION_PLANETS},
203 {"MINE", MINE, OPTION_PLANETS},
205 {"CRYSTALS", CRYSTALS, OPTION_PLANETS},
207 {"SHUTTLE", SHUTTLE, OPTION_PLANETS},
209 {"PLANETS", PLANETS, OPTION_PLANETS},
211 {"REPORT", REPORT, 0},
213 {"COMPUTER", COMPUTER, 0},
215 {"COMMANDS", COMMANDS, 0},
217 {"EMEXIT", EMEXIT, 0},
219 {"PROBE", PROBE, OPTION_PROBE},
224 {"ABANDON", ABANDON, 0},
226 {"DESTRUCT", DESTRUCT, 0},
228 {"DEATHRAY", DEATHRAY, 0},
230 {"DEBUG", DEBUGCMD, 0},
232 {"MAYDAY", MAYDAY, 0},
233 //{"SOS", MAYDAY, 0},
234 //{"CALL", MAYDAY, 0},
241 #define NUMCOMMANDS sizeof(commands)/sizeof(commands[0])
242 #define ACCEPT(i) (!commands[i].option || (commands[i].option & game.options))
244 static void listCommands(void) {
246 proutn("LEGAL COMMANDS ARE:");
247 for (i = 0; i < NUMCOMMANDS; i++) {
252 proutn("%-12s ", commands[i].name);
258 static void helpme(void)
261 char cmdbuf[32], *cp;
264 /* Give help on commands */
269 setwnd(prompt_window);
270 proutn("Help on what command? ");
273 setwnd(message_window);
274 if (key == IHEOL) return;
275 for (i = 0; i < NUMCOMMANDS; i++) {
276 if (ACCEPT(i) && strcasecmp(commands[i].name, citem)==0) {
277 i = commands[i].value;
281 if (i != NUMCOMMANDS) break;
283 prout("Valid commands:");
290 strcpy(cmdbuf, " ABBREV");
293 for (j = 0; commands[i].name[j]; j++)
294 cmdbuf[j] = toupper(commands[i].name[j]);
297 fp = fopen(SSTDOC, "r");
299 prout("Spock- \"Captain, that information is missing from the");
300 prout(" computer.\"");
302 * This used to continue: "You need to find SST.DOC and put
303 * it in the current directory."
308 if (fgets(linebuf, sizeof(linebuf), fp) == NULL) {
309 prout("Spock- \"Captain, there is no information on that command.\"");
313 if (linebuf[0] == '%' && linebuf[1] == '%'&& linebuf[2] == ' ') {
314 for (cp = linebuf+3; isspace(*cp); cp++)
316 linebuf[strlen(linebuf)-1] = '\0';
317 if (strcasecmp(cp, cmdbuf) == 0)
323 prout("Spock- \"Captain, I've found the following information:\"");
326 while (fgets(linebuf, sizeof(linebuf),fp)) {
327 if (strstr(linebuf, "******"))
334 void enqueue(char *s)
339 static void makemoves(void)
343 setwnd(message_window);
344 while (TRUE) { /* command loop */
346 while (TRUE) { /* get a command */
352 setwnd(prompt_window);
355 if (scan() == IHEOL) {
361 setwnd(message_window);
363 for (i=0; i < ABANDON; i++)
364 if (ACCEPT(i) && isit(commands[i].name)) {
365 v = commands[i].value;
368 if (i < ABANDON && (!commands[i].option || (commands[i].option & game.options)))
370 for (; i < NUMCOMMANDS; i++)
371 if (ACCEPT(i) && strcasecmp(commands[i].name, citem) == 0) {
372 v = commands[i].value;
375 if (i < NUMCOMMANDS && (!commands[i].option || (commands[i].option & game.options)))
379 commandhook(commands[i].name, TRUE);
380 switch (v) { /* command switch */
381 case SRSCAN: // srscan
384 case STATUS: // status
387 case REQUEST: // status request
388 srscan(SCAN_REQUEST);
390 case LRSCAN: // lrscan
393 case PHASERS: // phasers
395 if (ididit) hitme = TRUE;
397 case TORPEDO: // photons
399 if (ididit) hitme = TRUE;
404 case SHIELDS: // shields
413 if (ididit) attack(0);
415 case DAMAGES: // damages
421 case IMPULSE: // impulse
426 if (ididit) hitme = TRUE;
434 case SENSORS: // sensors
439 if (ididit) hitme = TRUE;
441 case TRANSPORT: // transport "beam"
446 if (ididit) hitme = TRUE;
448 case CRYSTALS: // crystals
450 if (ididit) hitme = TRUE;
452 case SHUTTLE: // shuttle
454 if (ididit) hitme = TRUE;
456 case PLANETS: // Planet list
459 case REPORT: // Game Report
462 case COMPUTER: // use COMPUTER!
468 case EMEXIT: // Emergency exit
469 clrscr(); // Hide screen
470 freeze(TRUE); // forced save
471 exit(1); // And quick exit
474 probe(); // Launch probe
475 if (ididit) hitme = TRUE;
477 case ABANDON: // Abandon Ship
480 case DESTRUCT: // Self Destruct
483 case SAVE: // Save Game
486 if (skill > SKILL_GOOD)
487 prout("WARNING--Saved games produce no plaques!");
489 case DEATHRAY: // Try a desparation measure
491 if (ididit) hitme = TRUE;
493 case DEBUGCMD: // What do we want for debug???
498 case MAYDAY: // Call for help
500 if (ididit) hitme = TRUE;
503 alldone = 1; // quit the game
509 helpme(); // get help
512 commandhook(commands[i].name, FALSE);
514 if (alldone) break; // Game has ended
516 if (idebug) prout("2500");
520 if (alldone) break; // Events did us in
522 if (game.state.galaxy[quadx][quady].supernova) { // Galaxy went Nova!
526 if (hitme && justin==0) {
529 if (game.state.galaxy[quadx][quady].supernova) { // went NOVA!
542 int main(int argc, char **argv)
546 game.options = OPTION_ALL &~ (OPTION_IOMODES | OPTION_SHOWME | OPTION_PLAIN | OPTION_ALMY);
548 game.options |= OPTION_CURSES | OPTION_SHOWME;
550 game.options |= OPTION_TTY;
552 while ((option = getopt(argc, argv, "t")) != -1) {
555 game.options |= OPTION_TTY;
556 game.options &=~ OPTION_CURSES;
559 fprintf(stderr, "usage: sst [-t] [startcommand...].\n");
568 for (i = optind; i < argc; i++) {
569 strcat(line, argv[i]);
572 while (TRUE) { /* Play a game */
573 setwnd(fullscreen_window);
575 prout("INITIAL OPTIONS: %0lx", game.options);
579 setup(line[0] == '\0');
589 if (tourn && alldone) {
590 proutn("Do you want your score recorded?");
596 proutn("Do you want to play again? ");
600 prout("May the Great Bird of the Galaxy roost upon your home planet.");
607 /* return an enemy */
611 case IHR: s = "Romulan"; break;
612 case IHK: s = "Klingon"; break;
613 case IHC: s = "Commander"; break;
614 case IHS: s = "Super-commander"; break;
615 case IHSTAR: s = "Star"; break;
616 case IHP: s = "Planet"; break;
617 case IHB: s = "Starbase"; break;
618 case IHBLANK: s = "Black hole"; break;
619 case IHT: s = "Tholian"; break;
620 case IHWEB: s = "Tholian web"; break;
621 case IHQUEST: s = "Stranger"; break;
622 default: s = "Unknown??"; break;
627 char *cramlc(enum loctype key, int x, int y)
631 if (key == quadrant) strcpy(buf, "Quadrant ");
632 else if (key == sector) strcpy(buf, "Sector ");
633 sprintf(buf+strlen(buf), "%d - %d", x, y);
637 void crmena(int i, int enemy, int key, int x, int y)
639 if (i == 1) proutn("***");
642 proutn(cramlc(key, x, y));
649 case IHE: s = "Enterprise"; break;
650 case IHF: s = "Faerie Queene"; break;
651 default: s = "Ship???"; break;
658 prouts("******************************************************");
662 double expran(double avrage)
664 return -avrage*log(1e-7 + Rand());
668 return rand()/(1.0 + (double)RAND_MAX);
671 void iran(int size, int *i, int *j)
673 *i = Rand()*(size*1.0) + 1.0;
674 *j = Rand()*(size*1.0) + 1.0;
685 /* return IHEOL next time */
699 // Read a line if nothing here
705 cgetline(line, sizeof(line));
707 if (curwnd==prompt_window){
709 setwnd(message_window);
714 // Skip leading white space
715 while (*linep == ' ') linep++;
721 if (isdigit(*linep) || *linep=='+' || *linep=='-' || *linep=='.') {
724 if (sscanf(linep, "%lf%n", &aaitem, &i) < 1) {
725 linep = line; // Invalid numbers are ignored
737 while (*linep && *linep!=' ') {
738 if ((cp - citem) < 9) *cp++ = tolower(*linep);
751 if (*citem == 'y') return TRUE;
752 if (*citem == 'n') return FALSE;
753 proutn("Please answer with \"Y\" or \"N\": ");
761 prout("Beg your pardon, Captain?");
766 /* New function -- compares s to scanned citem and returns true if it
767 matches to the length of s */
769 return strncasecmp(s, citem, max(1, strlen(citem))) == 0;
776 proutn("Reset levels? ");
778 if (energy < inenrg) energy = inenrg;
783 proutn("Reset damage? ");
786 for (i=0; i < NDEVICES; i++)
787 if (game.damage[i] > 0.0)
788 game.damage[i] = 0.0;
790 proutn("Toggle idebug? ");
793 if (idebug) prout("Debug output ON");
794 else prout("Debug output OFF");
796 proutn("Cause selective damage? ");
799 for (i=0; i < NDEVICES; i++) {
805 if (key == IHALPHA && isit("y")) {
806 game.damage[i] = 10.0;
810 proutn("Examine/change events? ");
813 for (i = 1; i < NEVENTS; i++) {
815 if (game.future[i] == FOREVER) continue;
817 case FSNOVA: proutn("Supernova "); break;
818 case FTBEAM: proutn("T Beam "); break;
819 case FSNAP: proutn("Snapshot "); break;
820 case FBATTAK: proutn("Base Attack "); break;
821 case FCDBAS: proutn("Base Destroy "); break;
822 case FSCMOVE: proutn("SC Move "); break;
823 case FSCDBAS: proutn("SC Base Destroy "); break;
825 proutn("%.2f", game.future[i]-game.state.date);
830 game.future[i] = game.state.date + aaitem;
835 proutn("Induce supernova here? ");
837 game.state.galaxy[quadx][quady].supernova = TRUE;