Checkpoint with several changes, committed so Stas can play with debug code.
[super-star-trek.git] / src / sst.c
index 4288bbbd8651e7e4c2ecfbb81c421108e4700a82..36f444959238493d6414a44f1ab3b6c9e13671b3 100644 (file)
--- a/src/sst.c
+++ b/src/sst.c
@@ -155,20 +155,29 @@ 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.
 */
 
 /* 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
+bool randready;                // Has the random-number generator initialized?
+FILE *logfp;
 
 char *device[NDEVICES] = {
        "S. R. Sensors",
@@ -208,67 +217,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 +308,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 +385,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 +424,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 +440,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;
@@ -459,7 +471,7 @@ static void makemoves(void)
            break;
        case REST:                      // rest
            wait();
-           if (game.ididit) hitme = TRUE;
+           if (game.ididit) hitme = true;
            break;
        case WARP:                      // warp
            setwrp();
@@ -472,22 +484,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,12 +515,12 @@ 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();
@@ -517,54 +529,51 @@ static void makemoves(void)
            dstrct();
            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!
+           if (game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova) { // Galaxy went Nova!
                atover(0);
                continue;
            }
            if (hitme && game.justin==0) {
                attack(2);
                if (game.alldone) break;
-               if (game.state.galaxy[game.quadx][game.quady].supernova) {      // went NOVA! 
+               if (game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova) {    // went NOVA! 
                    atover(0);
-                   hitme = TRUE;
+                   hitme = true;
                    continue;
                }
            }
@@ -572,6 +581,7 @@ static void makemoves(void)
        }
        if (game.alldone) break;
     }
+    if (idebug) prout("=== Ending");
 }
 
 
@@ -585,17 +595,23 @@ int main(int argc, char **argv)
     else
        game.options |= OPTION_TTY;
 
-    while ((option = getopt(argc, argv, "t")) != -1) {
+    while ((option = getopt(argc, argv, "tx")) != -1) {
        switch (option) {
        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);
 
     randomize();
     iostart();
@@ -605,11 +621,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');
@@ -626,7 +639,7 @@ int main(int argc, char **argv)
            proutn("Do you want your score recorded?");
            if (ja()) {
                chew2();
-               freeze(FALSE);
+               freeze(false);
            }
        }
        proutn("Do you want to play again? ");
@@ -660,22 +673,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(int i, int enemy, int key, coord w
 {
     if (i == 1) proutn("***");
     cramen(enemy);
     proutn(" at ");
-    proutn(cramlc(key, x, y));
+    proutn(cramlc(key, w));
 }
 
 void crmshp(void) 
@@ -700,8 +713,17 @@ double expran(double avrage)
     return -avrage*log(1e-7 + Rand());
 }
 
-double Rand(void) {
-       return rand()/(1.0 + (double)RAND_MAX);
+double Rand(void) 
+{
+    if (!randready) {
+       if (seed == 0)
+           seed = (unsigned)time(NULL);
+       if (logfp)
+           fprintf(logfp, "seed %d\n", seed);
+       srand(seed);
+       randready = true;
+    }
+    return rand()/(1.0 + (double)RAND_MAX);
 }
 
 void iran(int size, int *i, int *j) 
@@ -778,14 +800,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,14 +828,13 @@ 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 (game.energy < game.inenrg) game.energy = game.inenrg;
+       game.shield = game.inshld;
+       game.torps = game.intorps;
        game.lsupres = game.inlsr;
     }
     proutn("Reset damage? ");
@@ -823,10 +844,10 @@ void debugme(void)
            if (game.damage[i] > 0.0) 
                game.damage[i] = 0.0;
     }
-    proutn("Toggle game.idebug? ");
+    proutn("Toggle debug flag? ");
     if (ja() != 0) {
-       game.idebug = !game.idebug;
-       if (game.idebug) prout("Debug output ON");
+       idebug = !idebug;
+       if (idebug) prout("Debug output ON");
        else prout("Debug output OFF");
     }
     proutn("Cause selective damage? ");
@@ -845,10 +866,12 @@ void debugme(void)
     }
     proutn("Examine/change events? ");
     if (ja() != 0) {
+       event *ev;
+       coord w;
        int i;
        for (i = 1; i < NEVENTS; i++) {
            int key;
-           if (game.future[i] == FOREVER) continue;
+           if (!is_scheduled(i)) continue;
            switch (i) {
            case FSNOVA:  proutn("Supernova       "); break;
            case FTBEAM:  proutn("T Beam          "); break;
@@ -857,21 +880,43 @@ 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);
+           proutn("%.2f", scheduled(i)-game.state.date);
            chew();
            proutn("  ?");
            key = scan();
            if (key == IHREAL) {
-               game.future[i] = game.state.date + aaitem;
+               ev = schedule(i, aaitem);
+               if (i == FENSLV || i == FREPRO) {
+                   chew();
+                   proutn("In quadrant- ");
+                   key = scan();
+                   if (key != IHREAL) {
+                       prout("Event %d canceled, no y coordinate.", i);
+                       unschedule(i);
+                       continue;
+                   }
+                   w.y = (int)aaitem;
+                   key = scan();
+                   if (key != IHREAL) {
+                       prout("Event %d canceled, no x coordinate.", i);
+                       unschedule(i);
+                       continue;
+                   }
+                   w.x = (int)aaitem;
+                   ev->quadrant = w;
+               }
            }
        }
        chew();
     }
     proutn("Induce supernova here? ");
     if (ja() != 0) {
-       game.state.galaxy[game.quadx][game.quady].supernova = TRUE;
+       game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova = true;
        atover(1);
     }
 }
-#endif