BSD-Trek-like critical hits with weighting.
authorEric S. Raymond <esr@thyrsus.com>
Wed, 20 Sep 2006 08:55:41 +0000 (08:55 +0000)
committerEric S. Raymond <esr@thyrsus.com>
Wed, 20 Sep 2006 08:55:41 +0000 (08:55 +0000)
src/battle.c
src/moving.c
src/sst.c
src/sst.h

index 59d2d9340b23791e033804244024156fb0fff526..b36f86ac05f8859c1b5bde63575b8d36e0c0bbfa 100644 (file)
@@ -131,11 +131,66 @@ void doshield(bool raise)
     }
 }
 
+static int randdevice(void)
+/* choose a device to damage, at random. */
+{
+    /*
+     * Quoth Eric Allman in the code of BSD-Trek:
+     * "Under certain conditions you can get a critical hit.  This
+     * sort of hit damages devices.  The probability that a given
+     * device is damaged depends on the device.  Well protected
+     * devices (such as the computer, which is in the core of the
+     * ship and has considerable redundancy) almost never get
+     * damaged, whereas devices which are exposed (such as the
+     * warp engines) or which are particularly delicate (such as
+     * the transporter) have a much higher probability of being
+     * damaged."
+     *
+     * This is one place where OPTION_PLAIN does not restore the
+     * original behavior, which was equiprobable damage across
+     * all devices.  If we wanted that, we'd return NDEVICES*Rand()
+     * and have done with it.  Also, in the original game, DNAVYS
+     * and DCOMPTR were the same device. 
+     *
+     * Instead, we use a table of weights similar to the one from BSD Trek.
+     * BSD doesn't have the shuttle, shield controller, death ray, or probes. 
+     * We don't have a cloaking device.  The shuttle got the allocation
+     * for the cloaking device, then we shaved a half-percent off
+     * everything to have some weight to give DSHCTRL/DDRAY/DDSP.
+     */
+    static int weights[NDEVICES] = {
+       105,    /* DSRSENS: short range scanners        10.5% */
+       105,    /* DLRSENS: long range scanners         10.5% */
+       120,    /* DPHASER: phasers                     12.0% */
+       120,    /* DPHOTON: photon torpedoes            12.0% */
+       25,     /* DLIFSUP: life support                 2.5% */
+       65,     /* DWARPEN: warp drive                   6.5% */
+       70,     /* DIMPULS: impulse engines              6.5% */
+       145,    /* DSHIELD: deflector shields           14.5% */
+       30,     /* DRADIO:  subspace radio               3.0% */
+       45,     /* DSHUTTL: shuttle                      4.5% */
+       15,     /* DCOMPTR: computer                     1.5% */
+       20,     /* NAVCOMP: navigation system            2.0% */
+       75,     /* DTRANSP: transporter                  7.5% */
+       20,     /* DSHCTRL: high-speed shield controller 2.0% */
+       10,     /* DDRAY: death ray                      1.0% */
+       30,     /* DDSP: deep-space probes               3.0% */
+    };
+    int sum, i, idx = Rand() * 1000.0; /* weights must sum to 1000 */
+
+    for (i = sum = 0; i < NDEVICES; i++) {
+       sum += weights[i];
+       if (idx < sum)
+           return i;
+    }
+    return -1; /* we should never get here, but this quiets GCC */
+}
+
 void ram(bool ibumpd, feature ienm, coord w)
 /* make our ship ram something */
 {
     double hardness, extradm;
-    int icas, m;
+    int icas, m, ncrits;
        
     prouts(_("***RED ALERT!  RED ALERT!"));
     skip(1);
@@ -163,15 +218,22 @@ void ram(bool ibumpd, feature ienm, coord w)
     prout(_("***Sickbay reports %d casualties"), icas);
     game.casual += icas;
     game.state.crew -= icas;
-    for (m=0; m < NDEVICES; m++) {
-       if (m == DDRAY) 
-           continue; // Don't damage deathray 
-       if (game.damage[m] < 0) 
+    /*
+     * In the pre-SST2K version, all devices got equiprobably damaged,
+     * which was silly.  Instead, pick up to half the devices at
+     * random according to our weighting table,
+     */
+    ncrits = Rand() * (NDEVICES/2);
+    for (m=0; m < ncrits; m++) {
+       int dev = randdevice();
+       if (game.damage[dev] < 0) 
            continue;
        extradm = (10.0*hardness*Rand()+1.0)*game.damfac;
-       game.damage[m] += game.optime + extradm; /* Damage for at least time of travel! */
+       /* Damage for at least time of travel! */
+       game.damage[dev] += game.optime + extradm;
     }
     game.shldup = false;
+    prout(_("***Shields are down."));
     if (KLINGREM) {
        pause_game(true);
        dreprt();
@@ -446,12 +508,12 @@ static void fry(double hit)
     ncrit = 1.0 + hit/(500.0+100.0*Rand());
     proutn(_("***CRITICAL HIT--"));
     /* Select devices and cause damage */
-    for (loop1 = 0; loop1 < ncrit && 0 < NDEVICES; loop1++) {
+    for (loop1 = 0; loop1 < ncrit; loop1++) {
        do {
-           j = NDEVICES*Rand();
+           j = randdevice();
            /* Cheat to prevent shuttle damage unless on ship */
        } while 
-             (game.damage[j]<0.0 || (j==DSHUTTL && game.iscraft!=1) || j==DDRAY);
+             (game.damage[j]<0.0 || (j==DSHUTTL && game.iscraft!=1));
        cdam[loop1] = j;
        extradm = (hit*game.damfac)/(ncrit*(75.0+25.0*Rand()));
        game.damage[j] += extradm;
@@ -872,7 +934,7 @@ static bool checkshctrl(double rpow)
     int icas;
        
     skip(1);
-    if (Rand() < .998) {
+    if (Rand() < 0.998) {
        prout(_("Shields lowered."));
        return false;
     }
@@ -915,13 +977,14 @@ void phasers(void)
 {
     double hits[21], rpow=0, extra, powrem, over, temp;
     int kz = 0, k=1, i, irec=0; /* Cheating inhibitor */
-    bool ifast = false, no = false, ipoop = true, msgflag = true;
+    bool ifast = false, no = false, itarg = true, msgflag = true;
     enum {NOTSET, MANUAL, FORCEMAN, AUTOMATIC} automode = NOTSET;
     int key=0;
 
     skip(1);
-    /* SR sensors and Computer */
-    if (damaged(DSRSENS) || damaged(DCOMPTR)) ipoop = false;
+    /* SR sensors and Computer are needed fopr automode */
+    if (damaged(DSRSENS) || damaged(DCOMPTR)) 
+       itarg = false;
     if (game.condition == docked) {
        prout(_("Phasers can't be fired through base shields."));
        chew();
@@ -964,7 +1027,7 @@ void phasers(void)
                }
            }
            else if (isit("automatic")) {
-               if ((!ipoop) && game.nenhere != 0) {
+               if ((!itarg) && game.nenhere != 0) {
                    automode = FORCEMAN;
                }
                else {
@@ -987,7 +1050,7 @@ void phasers(void)
                prout(_("Energy will be expended into space."));
                automode = AUTOMATIC;
            }
-           else if (!ipoop)
+           else if (!itarg)
                automode = FORCEMAN;
            else
                automode = AUTOMATIC;
@@ -998,7 +1061,7 @@ void phasers(void)
                prout(_("Energy will be expended into space."));
                automode = AUTOMATIC;
            }
-           else if (!ipoop)
+           else if (!itarg)
                automode = FORCEMAN;
            else 
                proutn(_("Manual or automatic? "));
@@ -1084,7 +1147,7 @@ void phasers(void)
        chew();
        key = IHEOL;
        if (damaged(DCOMPTR))
-           prout(_("Battle comuter damaged, manual file only."));
+           prout(_("Battle computer damaged, manual file only."));
        else {
            skip(1);
            prouts(_("---WORKING---"));
@@ -1118,7 +1181,7 @@ void phasers(void)
            }
            if (key == IHEOL) {
                chew();
-               if (ipoop && k > kz)
+               if (itarg && k > kz)
                    irec=(fabs(game.kpower[k])/(PHASEFAC*pow(0.9,game.kdist[k])))*
                        (1.01+0.05*Rand()) + 1.0;
                kz = k;
index c02f1c4c63ab234eae9eac9be8e5b17ecdb37da6..3e243a4eee0870383d3e68ae1988d0b47af0ba45 100644 (file)
@@ -277,7 +277,7 @@ static void getcd(bool isprobe, int akey)
        return;
     }
     while (navmode == unspecified) {
-       if (damaged(DCOMPTR)) {
+       if (damaged(DNAVSYS)) {
            if (isprobe)
                prout(_("Computer damaged; manual navigation only"));
            else
index f0ee5f9b6c9403853b0fceb967bdb57ab4d846c4..62ffe2547293718d4abf7dfdd80c7aa0d822c98e 100644 (file)
--- a/src/sst.c
+++ b/src/sst.c
@@ -168,6 +168,11 @@ for a lot of magic numbers and refactored the heck out of it.
       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 */
@@ -196,6 +201,7 @@ char *device[NDEVICES] = {
        "Subspace Radio",
        "Shuttle Craft",
        "Computer",
+       "Navigation System",
        "Transporter",
        "Shield Control",
        "Death Ray",
index 75e9be9d72859fc459f6fd190d2d6af765d34abc..4063c4e86765474e2a0c2109028ce0be92891c7a 100644 (file)
--- a/src/sst.h
+++ b/src/sst.h
@@ -165,13 +165,14 @@ typedef struct {
 #define DRADIO  8
 #define DSHUTTL 9
 #define DCOMPTR 10
-#define DTRANSP 11
-#define DSHCTRL 12
-#define DDRAY   13  // Added deathray
-#define DDSP    14  // Added deep space probe
-#define NDEVICES (15)  // Number of devices
-
-#define damaged(dev)   game.damage[dev] != 0.0
+#define DNAVSYS        11
+#define DTRANSP 12
+#define DSHCTRL 13
+#define DDRAY   14
+#define DDSP    15
+#define NDEVICES (16)  // Number of devices
+
+#define damaged(dev)   (game.damage[dev] != 0.0)
 
 #define FOREVER        1e30