X-Git-Url: https://jxself.org/git/?p=super-star-trek.git;a=blobdiff_plain;f=src%2Fsst.c;h=d19698491d71a64611cf7b7a0fd6a99591a9d6cd;hp=3ec832c719017269199db55ee48f624d4358ff83;hb=0f68a074434296ae409accdc39ba0142600ea255;hpb=e3730bb826efe601fe51626aab064d395733d4c3 diff --git a/src/sst.c b/src/sst.c index 3ec832c..d196984 100644 --- a/src/sst.c +++ b/src/sst.c @@ -152,17 +152,42 @@ for a lot of magic numbers and refactored the heck out of it. 4. Added game option selection so you can play a close (but not bug-for- bug identical) approximation of older versions. + + 5. Half the quadrants now have inhabited planets, from which one + cannot mine dilithium (there will still be the same additional number + of dilithium-bearing planets). Torpedoing an inhabited world is *bad*. + There is BSD-Trek-like logic for Klingons to attack and enslave + inhabited worlds, producing more ships (only is skill is 'good' or + better). (Controlled by OPTION_WORLDS and turned off if game + type is "plain" or "almy".) + + 6. User input is now logged so we can do regression testing. + + 7. More BSD-Trek features: You can now lose if your entire crew + dies in battle. When abandoning ship in a game with inhabited + worlds enabled, they must have one in the quadrant to beam down + to; otherwise they die in space and this counts heavily against + your score. Docking at a starbase replenishes your crew. + + 8. Still more BSD-Trek: we now have a weighted damage table. + Also, the nav subsystem (enabling automatic course + setting) can be damaged separately from the main computer (which + handles weapons targeting, ETA calculation, and self-destruct). */ /* the input queue */ static char line[128], *linep = line; struct game game; -int thingx, thingy, iqhere, iqengry; -int iscore, iskill; // Common PLAQ +coord thing; +bool iqhere, iqengry; +int iscore, iskill; // Common PLAQ double aaitem; double perdate; char citem[10]; +int seed; // the random-number seed +bool idebug; // debug mode +FILE *logfp, *replayfp; char *device[NDEVICES] = { "S. R. Sensors", @@ -176,6 +201,7 @@ char *device[NDEVICES] = { "Subspace Radio", "Shuttle Craft", "Computer", + "Navigation System", "Transporter", "Shield Control", "Death Ray", @@ -202,73 +228,77 @@ commands[] = { #define TORPEDO 5 {"TORPEDO", TORPEDO, 0}, {"PHOTONS", TORPEDO, 0}, -#define MOVE 6 +#define MOVE 7 {"MOVE", MOVE, 0}, -#define SHIELDS 7 +#define SHIELDS 8 {"SHIELDS", SHIELDS, 0}, -#define DOCK 8 +#define DOCK 9 {"DOCK", DOCK, 0}, -#define DAMAGES 9 +#define DAMAGES 10 {"DAMAGES", DAMAGES, 0}, -#define CHART 10 +#define CHART 11 {"CHART", CHART, 0}, -#define IMPULSE 11 +#define IMPULSE 12 {"IMPULSE", IMPULSE, 0}, -#define REST 12 +#define REST 13 {"REST", REST, 0}, -#define WARP 13 +#define WARP 14 {"WARP", WARP, 0}, -#define SCORE 14 +#define SCORE 15 {"SCORE", SCORE, 0}, -#define SENSORS 15 +#define SENSORS 16 {"SENSORS", SENSORS, OPTION_PLANETS}, -#define ORBIT 16 +#define ORBIT 17 {"ORBIT", ORBIT, OPTION_PLANETS}, -#define TRANSPORT 17 +#define TRANSPORT 18 {"TRANSPORT", TRANSPORT, OPTION_PLANETS}, -#define MINE 18 +#define MINE 19 {"MINE", MINE, OPTION_PLANETS}, -#define CRYSTALS 19 +#define CRYSTALS 20 {"CRYSTALS", CRYSTALS, OPTION_PLANETS}, -#define SHUTTLE 20 +#define SHUTTLE 21 {"SHUTTLE", SHUTTLE, OPTION_PLANETS}, -#define PLANETS 21 +#define PLANETS 22 {"PLANETS", PLANETS, OPTION_PLANETS}, -#define REPORT 22 +#define REPORT 23 {"REPORT", REPORT, 0}, -#define COMPUTER 23 +#define COMPUTER 24 {"COMPUTER", COMPUTER, 0}, -#define COMMANDS 24 +#define COMMANDS 25 {"COMMANDS", COMMANDS, 0}, -#define EMEXIT 25 +#define EMEXIT 26 {"EMEXIT", EMEXIT, 0}, -#define PROBE 26 +#define PROBE 27 {"PROBE", PROBE, OPTION_PROBE}, -#define SAVE 27 +#define SAVE 28 {"SAVE", SAVE, 0}, {"FREEZE", SAVE, 0}, -#define ABANDON 28 +#define ABANDON 30 {"ABANDON", ABANDON, 0}, -#define DESTRUCT 29 +#define DESTRUCT 31 {"DESTRUCT", DESTRUCT, 0}, -#define DEATHRAY 30 +#define DEATHRAY 32 {"DEATHRAY", DEATHRAY, 0}, -#define DEBUGCMD 31 +#define DEBUGCMD 33 {"DEBUG", DEBUGCMD, 0}, -#define MAYDAY 32 +#define MAYDAY 34 {"MAYDAY", MAYDAY, 0}, //{"SOS", MAYDAY, 0}, //{"CALL", MAYDAY, 0}, -#define QUIT 33 +#define QUIT 35 {"QUIT", QUIT, 0}, -#define HELP 34 +#define HELP 36 {"HELP", HELP, 0}, +#define SEED 37 + {"SEED", SEED, 0}, }; #define NUMCOMMANDS sizeof(commands)/sizeof(commands[0]) #define ACCEPT(i) (!commands[i].option || (commands[i].option & game.options)) -static void listCommands(void) { +static void listCommands(void) +/* generate a list of legal commands */ +{ int i, k = 0; proutn("LEGAL COMMANDS ARE:"); for (i = 0; i < NUMCOMMANDS; i++) { @@ -282,7 +312,8 @@ static void listCommands(void) { skip(1); } -static void helpme(void) +static void helpme(void) +/* browse on-line help */ { int i, j; char cmdbuf[32], *cp; @@ -291,7 +322,7 @@ static void helpme(void) /* Give help on commands */ int key; key = scan(); - while (TRUE) { + for(;;) { if (key == IHEOL) { setwnd(prompt_window); proutn("Help on what command? "); @@ -361,21 +392,24 @@ static void helpme(void) fclose(fp); } -void enqueue(char *s) +void enqueue(char *s) +/* enqueue input for the command parser */ { strcpy(line, s); } -static void makemoves(void) +static void makemoves(void) +/* command-interpretation loop */ { - int i, v = 0, hitme; + int key, i, v = 0; + bool hitme; clrscr(); setwnd(message_window); - while (TRUE) { /* command loop */ + for(;;) { /* command loop */ drawmaps(1); - while (TRUE) { /* get a command */ - hitme = FALSE; - game.justin = 0; + for(;;) { /* get a command */ + hitme = false; + game.justin = false; game.optime = 0.0; i = -1; chew(); @@ -386,7 +420,7 @@ static void makemoves(void) makechart(); continue; } - game.ididit=0; + game.ididit = false; clrscr(); setwnd(message_window); clrscr(); @@ -406,7 +440,7 @@ static void makemoves(void) break; listCommands(); } - commandhook(commands[i].name, TRUE); + commandhook(commands[i].name, true); switch (v) { /* command switch */ case SRSCAN: // srscan srscan(SCAN_FULL); @@ -422,20 +456,20 @@ static void makemoves(void) break; case PHASERS: // phasers phasers(); - if (game.ididit) hitme = TRUE; + if (game.ididit) hitme = true; break; case TORPEDO: // photons photon(); - if (game.ididit) hitme = TRUE; + if (game.ididit) hitme = true; break; case MOVE: // move - warp(1); + warp(false); break; case SHIELDS: // shields - doshield(1); + doshield(false); if (game.ididit) { - hitme=TRUE; - game.shldchg = 0; + hitme = true; + game.shldchg = false; } break; case DOCK: // dock @@ -446,14 +480,14 @@ static void makemoves(void) dreprt(); break; case CHART: // chart - chart(0); + chart(false); break; case IMPULSE: // impulse impuls(); break; case REST: // rest wait(); - if (game.ididit) hitme = TRUE; + if (game.ididit) hitme = true; break; case WARP: // warp setwrp(); @@ -466,22 +500,22 @@ static void makemoves(void) break; case ORBIT: // orbit orbit(); - if (game.ididit) hitme = TRUE; + if (game.ididit) hitme = true; break; case TRANSPORT: // transport "beam" beam(); break; case MINE: // mine mine(); - if (game.ididit) hitme = TRUE; + if (game.ididit) hitme = true; break; case CRYSTALS: // crystals usecrystals(); - if (game.ididit) hitme = TRUE; + if (game.ididit) hitme = true; break; case SHUTTLE: // shuttle shuttle(); - if (game.ididit) hitme = TRUE; + if (game.ididit) hitme = true; break; case PLANETS: // Planet list preport(); @@ -497,68 +531,65 @@ static void makemoves(void) break; case EMEXIT: // Emergency exit clrscr(); // Hide screen - freeze(TRUE); // forced save + freeze(true); // forced save exit(1); // And quick exit break; case PROBE: probe(); // Launch probe - if (game.ididit) hitme = TRUE; + if (game.ididit) hitme = true; break; case ABANDON: // Abandon Ship abandn(); break; case DESTRUCT: // Self Destruct - dstrct(); + selfdestruct(); break; case SAVE: // Save Game - freeze(FALSE); + freeze(false); clrscr(); if (game.skill > SKILL_GOOD) prout("WARNING--Saved games produce no plaques!"); break; case DEATHRAY: // Try a desparation measure deathray(); - if (game.ididit) hitme = TRUE; + if (game.ididit) hitme = true; break; case DEBUGCMD: // What do we want for debug??? -#ifdef DEBUG debugme(); -#endif break; case MAYDAY: // Call for help - help(); - if (game.ididit) hitme = TRUE; + mayday(); + if (game.ididit) hitme = true; break; case QUIT: - game.alldone = 1; // quit the game -#ifdef DEBUG - if (game.idebug) score(); -#endif + game.alldone = true; // quit the game break; case HELP: - helpme(); // get help + helpme(); // get help + break; + case SEED: // set random-number seed + key = scan(); + if (key == IHREAL) + seed = (int)aaitem; break; } - commandhook(commands[i].name, FALSE); + commandhook(commands[i].name, false); for (;;) { if (game.alldone) break; // Game has ended -#ifdef DEBUG - if (game.idebug) prout("2500"); -#endif if (game.optime != 0.0) { events(); if (game.alldone) break; // Events did us in } - if (game.state.galaxy[game.quadx][game.quady].supernova) { // Galaxy went Nova! - atover(0); + if (game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova) { // Galaxy went Nova! + atover(false); continue; } - if (hitme && game.justin==0) { + if (hitme && !game.justin) { attack(2); if (game.alldone) break; - if (game.state.galaxy[game.quadx][game.quady].supernova) { // went NOVA! - atover(0); - hitme = TRUE; + if (game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova) { // went NOVA! + atover(false); + hitme = true; continue; } } @@ -566,6 +597,7 @@ static void makemoves(void) } if (game.alldone) break; } + if (idebug) prout("=== Ending"); } @@ -579,19 +611,39 @@ int main(int argc, char **argv) else game.options |= OPTION_TTY; - while ((option = getopt(argc, argv, "t")) != -1) { + seed = (int)time(NULL); + while ((option = getopt(argc, argv, "r:tx")) != -1) { switch (option) { + case 'r': + replayfp = fopen(optarg, "r"); + if (replayfp == NULL) { + fprintf(stderr, "sst: can't open replay file %s\n", optarg); + exit(1); + } + if (fscanf(replayfp, "seed %d\n", &seed) != 1) { + fprintf(stderr, "sst: replay file %s is ill-formed\n", optarg); + exit(1); + } + /* FALL THROUGH */ case 't': game.options |= OPTION_TTY; game.options &=~ OPTION_CURSES; break; + case 'x': + idebug = true; + break; default: - fprintf(stderr, "usage: sst [-t] [startcommand...].\n"); + fprintf(stderr, "usage: sst [-t] [-x] [startcommand...].\n"); exit(0); } } + /* where to save the input in case of bugs */ + logfp = fopen("sst-input.log", "w"); + setlinebuf(logfp); + fprintf(logfp, "seed %d\n", seed); + srand(seed); - randomize(); + srand(seed); iostart(); line[0] = '\0'; @@ -599,17 +651,14 @@ int main(int argc, char **argv) strcat(line, argv[i]); strcat(line, " "); } - while (TRUE) { /* Play a game */ + for(;;) { /* Play a game */ setwnd(fullscreen_window); -#ifdef DEBUG - prout("INITIAL OPTIONS: %0lx", game.options); -#endif /* DEBUG */ clrscr(); prelim(); setup(line[0] == '\0'); if (game.alldone) { score(); - game.alldone = 0; + game.alldone = false; } else makemoves(); skip(1); @@ -618,9 +667,9 @@ int main(int argc, char **argv) if (game.tourn && game.alldone) { proutn("Do you want your score recorded?"); - if (ja()) { + if (ja() == true) { chew2(); - freeze(FALSE); + freeze(false); } } proutn("Do you want to play again? "); @@ -632,7 +681,8 @@ int main(int argc, char **argv) } -void cramen(int i) +void cramen(feature i) +/* print the name of an enemy */ { /* return an enemy */ char *s; @@ -654,25 +704,28 @@ void cramen(int i) proutn(s); } -char *cramlc(enum loctype key, int x, int y) +char *cramlc(enum loctype key, coord w) +/* name a location */ { static char buf[32]; buf[0] = '\0'; if (key == quadrant) strcpy(buf, "Quadrant "); else if (key == sector) strcpy(buf, "Sector "); - sprintf(buf+strlen(buf), "%d - %d", x, y); + sprintf(buf+strlen(buf), "%d - %d", w.x, w.y); return buf; } -void crmena(int i, int enemy, int key, int x, int y) +void crmena(bool stars, feature enemy, enum loctype key, coord w) +/* print an enemy and his location */ { - if (i == 1) proutn("***"); + if (stars) proutn("***"); cramen(enemy); proutn(" at "); - proutn(cramlc(key, x, y)); + proutn(cramlc(key, w)); } -void crmshp(void) +void crmshp(void) +/* print our ship name */ { char *s; switch (game.ship) { @@ -683,7 +736,8 @@ void crmshp(void) proutn(s); } -void stars(void) +void stars(void) +/* print a line of stars */ { prouts("******************************************************"); skip(1); @@ -694,14 +748,18 @@ double expran(double avrage) return -avrage*log(1e-7 + Rand()); } -double Rand(void) { - return rand()/(1.0 + (double)RAND_MAX); +double Rand(void) +{ + return rand()/(1.0 + (double)RAND_MAX); } -void iran(int size, int *i, int *j) +coord randplace(int size) +/* choose a random location */ { - *i = Rand()*(size*1.0) + 1.0; - *j = Rand()*(size*1.0) + 1.0; + coord w; + w.x = Rand()*(size*1.0) + 1.0; + w.y = Rand()*(size*1.0) + 1.0; + return w; } void chew(void) @@ -772,59 +830,58 @@ int scan(void) return IHALPHA; } -int ja(void) +bool ja(void) +/* yes-or-no confirmation */ { chew(); - while (TRUE) { + for(;;) { scan(); chew(); - if (*citem == 'y') return TRUE; - if (*citem == 'n') return FALSE; + if (*citem == 'y') return true; + if (*citem == 'n') return false; proutn("Please answer with \"Y\" or \"N\": "); } } -void huh(void) +void huh(void) +/* complain about unparseable input */ { chew(); skip(1); prout("Beg your pardon, Captain?"); } -int isit(char *s) +bool isit(char *s) +/* compares s to citem and returns true if it matches to the length of s */ { - /* New function -- compares s to scanned citem and returns true if it - matches to the length of s */ - return strncasecmp(s, citem, max(1, strlen(citem))) == 0; - } -#ifdef DEBUG -void debugme(void) +void debugme(void) +/* access to the internals for debugging */ { proutn("Reset levels? "); - if (ja() != 0) { - if (energy < game.inenrg) energy = game.inenrg; - shield = game.inshld; - torps = game.intorps; + if (ja() == true) { + if (game.energy < game.inenrg) game.energy = game.inenrg; + game.shield = game.inshld; + game.torps = game.intorps; game.lsupres = game.inlsr; } proutn("Reset damage? "); - if (ja() != 0) { + if (ja() == true) { int i; for (i=0; i < NDEVICES; i++) if (game.damage[i] > 0.0) game.damage[i] = 0.0; } - proutn("Toggle game.idebug? "); - if (ja() != 0) { - game.idebug = !game.idebug; - if (game.idebug) prout("Debug output ON"); + proutn("Toggle debug flag? "); + if (ja() == true) { + idebug = !idebug; + if (idebug) prout("Debug output ON"); else prout("Debug output OFF"); } proutn("Cause selective damage? "); - if (ja() != 0) { + if (ja() == true) { int i, key; for (i=0; i < NDEVICES; i++) { proutn("Kill "); @@ -838,11 +895,12 @@ void debugme(void) } } proutn("Examine/change events? "); - if (ja() != 0) { + if (ja() == true) { + event *ev; + coord w; int i; for (i = 1; i < NEVENTS; i++) { int key; - if (game.future[i] == FOREVER) continue; switch (i) { case FSNOVA: proutn("Supernova "); break; case FTBEAM: proutn("T Beam "); break; @@ -851,21 +909,56 @@ void debugme(void) case FCDBAS: proutn("Base Destroy "); break; case FSCMOVE: proutn("SC Move "); break; case FSCDBAS: proutn("SC Base Destroy "); break; + case FDSPROB: proutn("Probe Move "); break; + case FDISTR: proutn("Distress Call "); break; + case FENSLV: proutn("Enlavement "); break; + case FREPRO: proutn("Klingon Build "); break; } - proutn("%.2f", game.future[i]-game.state.date); + if (is_scheduled(i)) { + proutn("%.2f", scheduled(i)-game.state.date); + if (i == FENSLV || i == FREPRO) { + ev = findevent(i); + proutn(" in %d-%d", ev->quadrant.x,ev->quadrant.y); + } + } else + proutn("never"); + proutn("? "); chew(); - proutn(" ?"); key = scan(); - if (key == IHREAL) { - game.future[i] = game.state.date + aaitem; + if (key == 'n') { + unschedule(i); + chew(); + } else if (key == IHREAL) { + ev = schedule(i, aaitem); + if (i == FENSLV || i == FREPRO) { + chew(); + proutn("In quadrant- "); + key = scan(); + /* IHEOL says to leave coordinates as they are */ + if (key != IHEOL) { + if (key != IHREAL) { + prout("Event %d canceled, no x coordinate.", i); + unschedule(i); + continue; + } + w.x = (int)aaitem; + key = scan(); + if (key != IHREAL) { + prout("Event %d canceled, no y coordinate.", i); + unschedule(i); + continue; + } + w.y = (int)aaitem; + ev->quadrant = w; + } + } } } chew(); } proutn("Induce supernova here? "); - if (ja() != 0) { - game.state.galaxy[game.quadx][game.quady].supernova = TRUE; - atover(1); + if (ja() == true) { + game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova = true; + atover(true); } } -#endif