Checkpoint with several changes, committed so Stas can play with debug code.
[super-star-trek.git] / src / sst.c
index 3ec832c719017269199db55ee48f624d4358ff83..36f444959238493d6414a44f1ab3b6c9e13671b3 100644 (file)
--- a/src/sst.c
+++ b/src/sst.c
@@ -152,17 +152,32 @@ 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.
 */
 
 /* 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",
@@ -202,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])
@@ -291,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? ");
@@ -368,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;
@@ -406,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);
@@ -422,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;
@@ -453,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();
@@ -466,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();
@@ -497,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();
@@ -511,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;
                }
            }
@@ -566,6 +581,7 @@ static void makemoves(void)
        }
        if (game.alldone) break;
     }
+    if (idebug) prout("=== Ending");
 }
 
 
@@ -579,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();
@@ -599,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');
@@ -620,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? ");
@@ -654,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) 
@@ -694,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) 
@@ -772,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\": ");
     }
 }
@@ -800,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? ");
@@ -817,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? ");
@@ -839,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;
@@ -851,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