More type cleanup and explanatory comments.
[super-star-trek.git] / src / sst.c
index 4288bbbd8651e7e4c2ecfbb81c421108e4700a82..850a940096efc21980d0cf5a4ba4e3e988f9543d 100644 (file)
--- a/src/sst.c
+++ b/src/sst.c
@@ -155,20 +155,34 @@ for a lot of magic numbers and refactored the heck out of it.
 
    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).  Right now this is just color, but
-      eventually we'll fold in BSD-Trek-like logic for Klingons to attack
-      and enslave inhabited worlds.
+      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.
 */
 
 /* the input queue */
 static char line[128], *linep = line;
 
 struct game game;
-int thingx, thingy, iqhere, iqengry;
-int iscore, iskill; // Common PLAQ
+coord thing;
+int 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",
@@ -208,67 +222,69 @@ 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])
@@ -297,7 +313,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? ");
@@ -374,13 +390,14 @@ void enqueue(char *s)
 
 static void makemoves(void) 
 {
-    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;
+       for(;;)  { /* get a command */
+           hitme = false;
            game.justin = 0;
            game.optime = 0.0;
            i = -1;
@@ -412,7 +429,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);
@@ -428,19 +445,19 @@ 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);
            if (game.ididit) {
-               hitme=TRUE;
+               hitme=true;
                game.shldchg = 0;
            }
            break;
@@ -452,14 +469,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();
@@ -472,22 +489,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();
@@ -503,68 +520,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
            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;
                }
            }
@@ -572,6 +586,7 @@ static void makemoves(void)
        }
        if (game.alldone) break;
     }
+    if (idebug) prout("=== Ending");
 }
 
 
@@ -585,19 +600,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';
@@ -605,11 +640,8 @@ 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');
@@ -624,9 +656,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? ");
@@ -660,22 +692,22 @@ void cramen(int i)
     proutn(s);
 }
 
-char *cramlc(enum loctype key, int x, int y)
+char *cramlc(enum loctype key, coord w)
 {
     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, int enemy, enum loctype key, coord w
 {
-    if (i == 1) proutn("***");
+    if (stars) proutn("***");
     cramen(enemy);
     proutn(" at ");
-    proutn(cramlc(key, x, y));
+    proutn(cramlc(key, w));
 }
 
 void crmshp(void) 
@@ -700,8 +732,9 @@ 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) 
@@ -778,14 +811,14 @@ int scan(void)
     return IHALPHA;
 }
 
-int ja(void) 
+bool ja(void) 
 {
     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\": ");
     }
 }
@@ -806,31 +839,30 @@ int isit(char *s)
 
 }
 
-#ifdef DEBUG
 void debugme(void) 
 {
     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 ");
@@ -844,11 +876,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;
@@ -857,21 +890,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