Added cloaking.
authorEric S. Raymond <esr@thyrsus.com>
Fri, 24 Feb 2017 21:38:03 +0000 (16:38 -0500)
committerEric S. Raymond <esr@thyrsus.com>
Fri, 24 Feb 2017 21:38:03 +0000 (16:38 -0500)
NEWS
TODO
doc/HACKING
doc/sst-doc.xml
sst.py
test/test8.chk

diff --git a/NEWS b/NEWS
index d7289542ed86804174413ad2885ccb29a4d8968f..c9c70ecbf0906a25e024e43f634b422e83aeee3e 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,7 +1,8 @@
                        SST2K project news.
 
 2.2 @
                        SST2K project news.
 
 2.2 @
-    Enable GNU readline keys. Added color. Added BSD Trek 'CAPTURE'.
+    Enable GNU readline keys. Added color. Added BSD Trek 'CAPTURE'
+    and 'CLOAK' (from Tom Almy's 2013 changes).
 
 2.1 @ 2012-02-16
     Packaging fixes, pychecker cleanup, cope with Freshmeat name changes.
 
 2.1 @ 2012-02-16
     Packaging fixes, pychecker cleanup, cope with Freshmeat name changes.
diff --git a/TODO b/TODO
index dcc8996597bd26b4793838cc7e6a67bbe2ed1d6a..59569612377489833005ed7dafcfdaf6736c7096 100644 (file)
--- a/TODO
+++ b/TODO
@@ -18,10 +18,6 @@ Long-term items:
  
 BSD-Trek features we haven't swiped yet:
 
  
 BSD-Trek features we haven't swiped yet:
 
-* The cloaking device.
-
-* Summoning Klingons to surrender and taking captives.
-
 * Smarter computer, with multiple requests.
 
 * There is a small probility that a nova event will leave a black hole.
 * Smarter computer, with multiple requests.
 
 * There is a small probility that a nova event will leave a black hole.
index 1739465c1c6e3d60bf1b6f00d12506a26ad64261..cfc811e1e1c0ab15e7a506f9bb08111348aa0a0c 100644 (file)
@@ -210,7 +210,8 @@ and added more:
 the LRSCAN command is no longer needed.  (Controlled by OPTION_AUTOSCAN
 and turned off if game type is "plain" or "almy".)
 
 the LRSCAN command is no longer needed.  (Controlled by OPTION_AUTOSCAN
 and turned off if game type is "plain" or "almy".)
 
-10. I imported Tom Almy's SCORE command from his 2013 version.
+10. I imported Tom Almy's SCORE, CAPTURE, and CLOAK command from his
+2013 version.
 
 11. I added color.
 
 
 11. I added color.
 
index 03bf78934632521ac4b73a066a480cb7fe5b53e3..a5f73ad9a1599f201aa4190729059a95c814bd3d 100644 (file)
@@ -914,6 +914,29 @@ firing.</para>
 <para>Phasers have no effect on starbases (which are shielded) or on
 stars.</para>
 
 <para>Phasers have no effect on starbases (which are shielded) or on
 stars.</para>
 
+</sect1>
+<sect1><title>Cloak</title>
+
+<literallayout>
+  Mnemonic:  CLOAK
+  Shortest abbreviation:  CLOAK
+  Full commands:  CLOAK ON
+                  CLOAK OFF
+</literallayout>
+
+<para>The cloaking device prevents your ship from being seen by any enemy
+vessels.  When the cloaking device is in use, your subspace radio will
+not receive transmissions, torpedoes will be less accurate, you cannot
+dock, and you cannot use your warp engines. Enemy ships will get a
+chance to attack you when you turn cloaking on.</para>
+
+<para>The Treaty of Algeron with the Romulans in Stardate 2311 prohibits the
+use of cloaking devices. If a Romulan ship observes you cloaking or
+uncloaking after this point in time you will be in violation, which
+will hurt your final score.</para>
+
+<para>The Faerie Queene does not have a cloaking device.</para>
+
 </sect1>
 <sect1><title>Capture</title>
 
 </sect1>
 <sect1><title>Capture</title>
 
@@ -925,11 +948,17 @@ Full commands:  CAPTURE
 
 <para>Ask a Klingon To surrender.</para>
 
 
 <para>Ask a Klingon To surrender.</para>
 
-<para>The Subspace Radio is needed to ask a Klingon if he will kindly
-surrender.  The weakest Klingon from the ones in the quadrant is
-chosen. The probability of surrender is a function of that Klingon's
-remaining power, our power, etc.</para>
-
+<para>The capture command provides a more humane way to end a battle than
+just destroying the Klingon battleship with the crew aboard. Assuming
+the subspace radio and transporter are working, and there is room in
+the brig, this command will ask the captain of the weakest Klingon
+ship in the quadrant to surrender.  If the captain agrees, some of the
+crew will transport to your ship and the Klingon ship will be
+destroyed. This command does take time and you will be attacked by any
+other enemy ships if the surrender occurs.</para>
+
+<para>When you dock, any captured Klingons will be transferred to the base
+and you will be credited with the lives you save.</para>
 </sect1>
 <sect1><title>Report</title>
 
 </sect1>
 <sect1><title>Report</title>
 
diff --git a/sst.py b/sst.py
index 3c985160fba4019a27ce677f09b12dd9040f7eb4..a4ed80e97f5d5b29f23421e7846f9b4fa0816ee2 100755 (executable)
--- a/sst.py
+++ b/sst.py
@@ -43,6 +43,9 @@ MINCMDR       = 10            # Minimum number of Klingon commanders
 DOCKFAC                = 0.25          # Repair faster when docked
 PHASEFAC       = 2.0           # Unclear what this is, it was in the C version
 
 DOCKFAC                = 0.25          # Repair faster when docked
 PHASEFAC       = 2.0           # Unclear what this is, it was in the C version
 
+ALGERON                = 2311          # Date of the Treaty of Algeron
+
+
 DEFAULT      = -1
 BLACK        = 0
 BLUE         = 1
 DEFAULT      = -1
 BLACK        = 0
 BLUE         = 1
@@ -225,10 +228,11 @@ OPTION_BLKHOLE    = 0x00000200        # black hole may timewarp you (Stas, 2005)
 OPTION_BASE       = 0x00000400        # bases have good shields (Stas, 2005)
 OPTION_WORLDS     = 0x00000800        # logic for inhabited worlds (ESR, 2006)
 OPTION_AUTOSCAN   = 0x00001000        # automatic LRSCAN before CHART (ESR, 2006)
 OPTION_BASE       = 0x00000400        # bases have good shields (Stas, 2005)
 OPTION_WORLDS     = 0x00000800        # logic for inhabited worlds (ESR, 2006)
 OPTION_AUTOSCAN   = 0x00001000        # automatic LRSCAN before CHART (ESR, 2006)
+OPTION_CAPTURE    = 0x00002000        # Enable BSD-Trek capture (Almy, 2013).
+OPTION_CLOAK      = 0x80004000        # Enable BSD-Trek capture (Almy, 2013).
 OPTION_PLAIN      = 0x01000000        # user chose plain game
 OPTION_ALMY       = 0x02000000        # user chose Almy variant
 OPTION_COLOR      = 0x04000000        # enable color display (ESR, 2010)
 OPTION_PLAIN      = 0x01000000        # user chose plain game
 OPTION_ALMY       = 0x02000000        # user chose Almy variant
 OPTION_COLOR      = 0x04000000        # enable color display (ESR, 2010)
-OPTION_CAPTURE    = 0x80000000        # Enable BSD-Trek capture (Almy, 2013).
 
 # Define devices
 DSRSENS         = 0
 
 # Define devices
 DSRSENS         = 0
@@ -239,15 +243,16 @@ DLIFSUP         = 4
 DWARPEN         = 5
 DIMPULS         = 6
 DSHIELD         = 7
 DWARPEN         = 5
 DIMPULS         = 6
 DSHIELD         = 7
-DRADIO         = 0
-DSHUTTL  = 9
-DCOMPTR  = 10
+DRADIO          = 8
+DSHUTTL         = 9
+DCOMPTR         = 10
 DNAVSYS         = 11
 DNAVSYS         = 11
-DTRANSP  = 12
+DTRANSP         = 12
 DSHCTRL         = 13
 DSHCTRL         = 13
-DDRAY         = 14
-DDSP         = 15
-NDEVICES = 16        # Number of devices
+DDRAY           = 14
+DDSP            = 15
+DCLOAK          = 16
+NDEVICES        = 17        # Number of devices
 
 SKILL_NONE        = 0
 SKILL_NOVICE        = 1
 
 SKILL_NONE        = 0
 SKILL_NOVICE        = 1
@@ -398,6 +403,9 @@ class Gamestate:
         self.brigcapacity = 400     # Enterprise brig capacity
         self.brigfree = 400       # How many klingons can we put in the brig?
         self.kcaptured = 0      # Total Klingons captured, for scoring.
         self.brigcapacity = 400     # Enterprise brig capacity
         self.brigfree = 400       # How many klingons can we put in the brig?
         self.kcaptured = 0      # Total Klingons captured, for scoring.
+        self.iscloaked = False  # Cloaking device on?
+        self.ncviol = 0         # Algreon treaty violations
+        self.isviolreported = False # We have been warned
     def recompute(self):
         # Stas thinks this should be (C expression):
         # game.state.remkl + len(game.state.kcmdr) > 0 ?
     def recompute(self):
         # Stas thinks this should be (C expression):
         # game.state.remkl + len(game.state.kcmdr) > 0 ?
@@ -429,6 +437,7 @@ FDRAY = 18
 FTRIBBLE = 19
 FHOLE = 20
 FCREW = 21
 FTRIBBLE = 19
 FHOLE = 20
 FCREW = 21
+FCLOAK = 22
 
 def withprob(p):
     return random.random() < p
 
 def withprob(p):
     return random.random() < p
@@ -906,6 +915,85 @@ def movetholian():
 
 # Code from battle.c begins here
 
 
 # Code from battle.c begins here
 
+def cloak():
+    "Change cloaking-device status."
+    if game.ship == 'F':
+        prout(_("Ye Faerie Queene hath no cloaking device."));
+        return
+
+    key = scanner.nexttok()
+
+    if key == "IHREAL":
+        return
+
+    action = None
+    if key == "IHALPHA":
+        if scanner.sees("on"):
+            if game.iscloaked:
+                prout(_("The cloaking device has already been switched on."))
+                return
+            action = "CLON"
+        elif scanner.sees("off"):
+            if not game.iscloaked:
+                prout(_("The cloaking device has already been switched off."))
+                return
+            action = "CLOFF"
+        else:
+            huh()
+            return
+    else:
+        if not game.iscloaked:
+            proutn(_("Switch cloaking device on?"))
+            if not ja():
+                return
+            action = "CLON"
+        else:
+            proutn(_("Switch cloaking device off?"))
+            if not ja():
+                return
+            action = "CLOFF"
+    if action == None:
+        return;
+
+    if action == "CLOFF":
+        if game.irhere and game.state.date >= ALGERON and not game.isviolreported:
+            prout(_("Spock- \"Captain, the Treaty of Algeron is in effect.\n   Are you sure this is wise?\""))
+            if not ja():
+                return;
+        prout("Engineer Scott- \"Aye, Sir.\"");
+        game.iscloaked = FALSE;
+        if game.irhere and game.state.date >= ALGERON and not game.isviolreported:
+            prout(_("The Romulan ship discovers you are breaking the Treaty of Algeron!"))
+            game.ncviol += 1
+            game.isviolreported = True
+
+            #if (neutz and game.state.date >= ALGERON) finish(FCLOAK);
+            return;
+
+    if action == "CLON":
+        if damage(DCLOAK):
+            prout(_("Engineer Scott- \"The cloaking device is damaged, Sir.\""))
+            return;
+
+        if game.condition == "docked":
+            prout(_("You cannot cloak while docked."))
+
+       if game.state.date >= ALGERON and not game.isviolreported:
+            prout(_("Spock- \"Captain, using the cloaking device is be a violation"))
+            prout(_("  of the Treaty of Algeron. Considering the alternatives,"))
+            proutn("  are you sure this is wise?");
+            if not ja():
+                return
+       prout(_("Engineer Scott- \"Cloaking device has engaging, Sir...\""))
+        attack(True)
+       prout(_("Engineer Scott- \"Cloaking device has engaged, Sir.\""))
+       game.iscloaked = True
+
+        if game.irhere and game.state.date >= ALGERON and not game.isviolreported:
+            prout(_("The Romulan ship discovers you are breaking the Treaty of Algeron!"))
+            game.ncviol += 1
+            game.isviolreported = True
+
 def doshield(shraise):
     "Change shield status."
     action = "NONE"
 def doshield(shraise):
     "Change shield status."
     action = "NONE"
@@ -1031,6 +1119,7 @@ def randdevice():
         20,        # DSHCTRL: high-speed shield controller  2.0%
         10,        # DDRAY: death ray                         1.0%
         30,        # DDSP: deep-space probes                 3.0%
         20,        # DSHCTRL: high-speed shield controller  2.0%
         10,        # DDRAY: death ray                         1.0%
         30,        # DDSP: deep-space probes                 3.0%
+        0,         # DCLOAK: the cloaking device             0.0
     )
     assert(sum(weights) == 1000)
     idx = randrange(1000)
     )
     assert(sum(weights) == 1000)
     idx = randrange(1000)
@@ -1297,7 +1386,7 @@ def fry(hit):
         while True:
             j = randdevice()
             # Cheat to prevent shuttle damage unless on ship
         while True:
             j = randdevice()
             # Cheat to prevent shuttle damage unless on ship
-            if not (game.damage[j]<0.0 or (j == DSHUTTL and game.iscraft != "onship")):
+            if not (game.damage[j]<0.0 or (j == DSHUTTL and game.iscraft != "onship") or (j == DCLOAK and game.ship != 'E' or j == DDRAY)):
                 break
         cdam.append(j)
         extradm = (hit*game.damfac)/(ncrit*randreal(75, 100))
                 break
         cdam.append(j)
         extradm = (hit*game.damfac)/(ncrit*randreal(75, 100))
@@ -1315,10 +1404,15 @@ def fry(hit):
     if damaged(DSHIELD) and game.shldup:
         prout(_("***Shields knocked down."))
         game.shldup = False
     if damaged(DSHIELD) and game.shldup:
         prout(_("***Shields knocked down."))
         game.shldup = False
+    if damaged(DCLOAK) and game.iscloaked:
+        prout(_("***Cloaking device rendered inoperative."))
+        game.iscloaked = False
 
 def attack(torps_ok):
     # bad guy attacks us
     # torps_ok == False forces use of phasers in an attack
 
 def attack(torps_ok):
     # bad guy attacks us
     # torps_ok == False forces use of phasers in an attack
+    if game.iscloaked:
+        return
     # game could be over at this point, check
     if game.alldone:
         return
     # game could be over at this point, check
     if game.alldone:
         return
@@ -1637,7 +1731,9 @@ def torps():
                 prout(_("***Photon tubes damaged by misfire."))
                 game.damage[DPHOTON] = game.damfac * randreal(1.0, 3.0)
             break
                 prout(_("***Photon tubes damaged by misfire."))
                 game.damage[DPHOTON] = game.damfac * randreal(1.0, 3.0)
             break
-        if game.shldup or game.condition == "docked":
+        if game.iscloaked:
+            dispersion *= 1.2
+        elif game.shldup or game.condition == "docked":
             dispersion *= 1.0 + 0.0001*game.shield
         torpedo(game.sector, tcourse[i], dispersion, number=i, nburst=n)
         if game.alldone or game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
             dispersion *= 1.0 + 0.0001*game.shield
         torpedo(game.sector, tcourse[i], dispersion, number=i, nburst=n)
         if game.alldone or game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
@@ -1990,7 +2086,7 @@ def phasers():
 
 def capture():
     game.ididit = False # Nothing if we fail
 
 def capture():
     game.ididit = False # Nothing if we fail
-    Time = 0.0;
+    game.optime = 0.0;
 
     # Make sure there is room in the brig */
     if game.brigfree == 0:
 
     # Make sure there is room in the brig */
     if game.brigfree == 0:
@@ -2016,7 +2112,7 @@ def capture():
     #  surrender (Tom Almy mod)
     klingons = [e for e in game.enemies if e.type == 'K']
     weakest = sorted(klingons, key=lambda e: e.power)
     #  surrender (Tom Almy mod)
     klingons = [e for e in game.enemies if e.type == 'K']
     weakest = sorted(klingons, key=lambda e: e.power)
-    Time = 0.05                # This action will take some time
+    game.optime = 0.05         # This action will take some time
     game.ididit = True #  So any others can strike back
 
     # check out that Klingon
     game.ididit = True #  So any others can strike back
 
     # check out that Klingon
@@ -2211,6 +2307,11 @@ def events():
                     prout("== Event %d fires" % evcode)
                 datemin = game.future[l].date
         xtime = datemin-game.state.date
                     prout("== Event %d fires" % evcode)
                 datemin = game.future[l].date
         xtime = datemin-game.state.date
+        if game.iscloaked:
+            game.energy -= xtime*500.0
+            if game.energy <= 0:
+                finish(FNRG)
+                return
         game.state.date = datemin
         # Decrement Federation resources and recompute remaining time
         game.state.remres -= (game.state.remkl+4*len(game.state.kcmdr))*xtime
         game.state.date = datemin
         # Decrement Federation resources and recompute remaining time
         game.state.remres -= (game.state.remkl+4*len(game.state.kcmdr))*xtime
@@ -2261,7 +2362,7 @@ def events():
             if game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
                 return
         elif evcode == FSPY: # Check with spy to see if SC should tractor beam
             if game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
                 return
         elif evcode == FSPY: # Check with spy to see if SC should tractor beam
-            if game.state.nscrem == 0 or \
+            if game.state.nscrem == 0 or game.state.iscloaked or \
                 ictbeam or istract or \
                 game.condition == "docked" or game.isatb == 1 or game.iscate:
                 return
                 ictbeam or istract or \
                 game.condition == "docked" or game.isatb == 1 or game.iscate:
                 return
@@ -2282,7 +2383,7 @@ def events():
                 continue
             i = randrange(len(game.state.kcmdr))
             yank = (game.state.kcmdr[i]-game.quadrant).distance()
                 continue
             i = randrange(len(game.state.kcmdr))
             yank = (game.state.kcmdr[i]-game.quadrant).distance()
-            if istract or game.condition == "docked" or yank == 0:
+            if istract or game.condition == "docked" or game.iscloaked or yank == 0:
                 # Drats! Have to reschedule
                 schedule(FTBEAM,
                          game.optime + expran(1.5*game.intime/len(game.state.kcmdr)))
                 # Drats! Have to reschedule
                 schedule(FTBEAM,
                          game.optime + expran(1.5*game.intime/len(game.state.kcmdr)))
@@ -2844,7 +2945,8 @@ def badpoints():
             300*game.state.nworldkl + \
             45.0*game.nhelp +\
             100.0*game.state.basekl +\
             300*game.state.nworldkl + \
             45.0*game.nhelp +\
             100.0*game.state.basekl +\
-            3.0*game.abandoned
+            3.0*game.abandoned +\
+            100*game.ncviol
     if game.ship == 'F':
         badpt += 100.0
     elif game.ship is None:
     if game.ship == 'F':
         badpt += 100.0
     elif game.ship is None:
@@ -2852,7 +2954,7 @@ def badpoints():
     return badpt
 
 def finish(ifin):
     return badpt
 
 def finish(ifin):
-    # end the game, with appropriate notfications
+    # end the game, with appropriate notifications
     igotit = False
     game.alldone = True
     skip(3)
     igotit = False
     game.alldone = True
     skip(3)
@@ -2975,7 +3077,7 @@ def finish(ifin):
         prout(_("The %s has been cremated by its own phasers.") % crmshp())
     elif ifin == FLOST:
         prout(_("You and your landing party have been"))
         prout(_("The %s has been cremated by its own phasers.") % crmshp())
     elif ifin == FLOST:
         prout(_("You and your landing party have been"))
-        prout(_("converted to energy, disipating through space."))
+        prout(_("converted to energy, dissipating through space."))
     elif ifin == FMINING:
         prout(_("You are left with your landing party on"))
         prout(_("a wild jungle planet inhabited by primitive cannibals."))
     elif ifin == FMINING:
         prout(_("You are left with your landing party on"))
         prout(_("a wild jungle planet inhabited by primitive cannibals."))
@@ -3018,8 +3120,16 @@ def finish(ifin):
     elif ifin == FHOLE:
         prout(_("Your ship is drawn to the center of the black hole."))
         prout(_("You are crushed into extremely dense matter."))
     elif ifin == FHOLE:
         prout(_("Your ship is drawn to the center of the black hole."))
         prout(_("You are crushed into extremely dense matter."))
+    elif ifin == FCLOAK:
+        game.ncviol += 1
+        prout(_("You have violated the Treaty of Algeron."))
+        prout(_("The Romulan Empire can never trust you again."))
     elif ifin == FCREW:
         prout(_("Your last crew member has died."))
     elif ifin == FCREW:
         prout(_("Your last crew member has died."))
+    if ifin != FWON and ifin != FCLOAK and game.iscloaked:
+        prout(_("Your ship was cloaked so your subspace radio did not receive anything."))
+        prout(_("You may have missed some warning messages."))
+        skip(1)
     if game.ship == 'F':
         game.ship = None
     elif game.ship == 'E':
     if game.ship == 'F':
         game.ship = None
     elif game.ship == 'E':
@@ -3120,6 +3230,12 @@ def score():
     if klship:
         prout(_("%6d ship(s) lost or destroyed          %5d") %
               (klship, -100*klship))
     if klship:
         prout(_("%6d ship(s) lost or destroyed          %5d") %
               (klship, -100*klship))
+    if game.ncviol > 0:
+        if ncviol == 1:
+            prout(_("1 Treaty of Algeron violation          -100"))
+        else:
+            prout(_("%6d Treaty of Algeron violations       %5d\n") %
+                  (ncviol, -100*ncviol))
     if not game.alive:
         prout(_("Penalty for getting yourself killed        -200"))
     if game.gamewon:
     if not game.alive:
         prout(_("Penalty for getting yourself killed        -200"))
     if game.gamewon:
@@ -3574,8 +3690,8 @@ def imove(icourse=None, noattack=False):
 
     def newquadrant(noattack):
         # Leaving quadrant -- allow final enemy attack
 
     def newquadrant(noattack):
         # Leaving quadrant -- allow final enemy attack
-        # Don't do it if being pushed by Nova
-        if len(game.enemies) != 0 and not noattack:
+        # Don't set up attack if being pushed by nova or cloaked
+        if len(game.enemies) != 0 and not noattack and not game.iscloaked:
             newcnd()
             for enemy in game.enemies:
                 finald = (w - enemy.location).distance()
             newcnd()
             for enemy in game.enemies:
                 finald = (w - enemy.location).distance()
@@ -3685,10 +3801,16 @@ def imove(icourse=None, noattack=False):
         game.inorbit = False
     # If tractor beam is to occur, don't move full distance
     if game.state.date+game.optime >= scheduled(FTBEAM):
         game.inorbit = False
     # If tractor beam is to occur, don't move full distance
     if game.state.date+game.optime >= scheduled(FTBEAM):
-        trbeam = True
-        game.condition = "red"
-        icourse.distance = icourse.distance*(scheduled(FTBEAM)-game.state.date)/game.optime + 0.1
-        game.optime = scheduled(FTBEAM) - game.state.date + 1e-5
+        if game.iscloaked:
+            # We can't be tractor beamed if cloaked,
+            # so move the event into the future
+            postpone(FTBEAM, game.optime + expran(1.5*game.intime/len(game.kcmdr)))
+            pass
+        else:
+            trbeam = True
+            game.condition = "red"
+            icourse.distance = icourse.distance*(scheduled(FTBEAM)-game.state.date)/game.optime + 0.1
+            game.optime = scheduled(FTBEAM) - game.state.date + 1e-5
     # Move out
     game.quad[game.sector.i][game.sector.j] = '.'
     for _m in range(icourse.moves):
     # Move out
     game.quad[game.sector.i][game.sector.j] = '.'
     for _m in range(icourse.moves):
@@ -3731,6 +3853,9 @@ def dock(verbose):
     if not game.base.is_valid() or abs(game.sector.i-game.base.i) > 1 or abs(game.sector.j-game.base.j) > 1:
         prout(crmshp() + _(" not adjacent to base."))
         return
     if not game.base.is_valid() or abs(game.sector.i-game.base.i) > 1 or abs(game.sector.j-game.base.j) > 1:
         prout(crmshp() + _(" not adjacent to base."))
         return
+    if game.iscloaked:
+        prout(_("You cannot dock while cloaked."))
+        return
     game.condition = "docked"
     if verbose:
         prout(_("Docked."))
     game.condition = "docked"
     if verbose:
         prout(_("Docked."))
@@ -3987,6 +4112,11 @@ def warp(wcourse, involuntary):
     blooey = False; twarp = False
     if not involuntary: # Not WARPX entry
         game.ididit = False
     blooey = False; twarp = False
     if not involuntary: # Not WARPX entry
         game.ididit = False
+        if game.iscloaked:
+            scanner.chew()
+            skip(1)
+            prout(_("Engineer Scott- \"The warp engines can not be used while cloaked, Sir.\""))
+            return
         if game.damage[DWARPEN] > 10.0:
             scanner.chew()
             skip(1)
         if game.damage[DWARPEN] > 10.0:
             scanner.chew()
             skip(1)
@@ -5156,6 +5286,8 @@ def sectscan(goodScan, i, j):
                        'R':LIGHTRED,
                        'T':LIGHTRED,
                        }.get(game.quad[i][j], DEFAULT))
                        'R':LIGHTRED,
                        'T':LIGHTRED,
                        }.get(game.quad[i][j], DEFAULT))
+        if game.iscloaked:
+            highvideo()
         proutn("%c " % game.quad[i][j])
         textcolor(DEFAULT)
     else:
         proutn("%c " % game.quad[i][j])
         textcolor(DEFAULT)
     else:
@@ -5171,6 +5303,8 @@ def status(req=0):
             newcnd()
         prstat(_("Condition"), _("%s, %i DAMAGES") % \
                (game.condition.upper(), sum([x > 0 for x in game.damage])))
             newcnd()
         prstat(_("Condition"), _("%s, %i DAMAGES") % \
                (game.condition.upper(), sum([x > 0 for x in game.damage])))
+        if game.iscloaked:
+            prout(_(", CLOAKED"))
     if not req or req == 3:
         prstat(_("Position"), "%s , %s" % (game.quadrant, game.sector))
     if not req or req == 4:
     if not req or req == 3:
         prstat(_("Position"), "%s , %s" % (game.quadrant, game.sector))
     if not req or req == 4:
@@ -5500,6 +5634,7 @@ device = (
     _("Shield Control"), \
     _("Death Ray"), \
     _("D. S. Probe"), \
     _("Shield Control"), \
     _("Death Ray"), \
     _("D. S. Probe"), \
+    _("Cloaking Device"), \
 )
 
 def setup():
 )
 
 def setup():
@@ -5786,7 +5921,7 @@ def choose():
         scanner.nexttok()
     if scanner.sees("plain"):
         # Approximates the UT FORTRAN version.
         scanner.nexttok()
     if scanner.sees("plain"):
         # Approximates the UT FORTRAN version.
-        game.options &=~ (OPTION_THOLIAN | OPTION_PLANETS | OPTION_THINGY | OPTION_PROBE | OPTION_RAMMING | OPTION_MVBADDY | OPTION_BLKHOLE | OPTION_BASE | OPTION_WORLDS | OPTION_COLOR | OPTION_CAPTURE)
+        game.options &=~ (OPTION_THOLIAN | OPTION_PLANETS | OPTION_THINGY | OPTION_PROBE | OPTION_RAMMING | OPTION_MVBADDY | OPTION_BLKHOLE | OPTION_BASE | OPTION_WORLDS | OPTION_COLOR | OPTION_CAPTURE | OPTION_CLOAK)
         game.options |= OPTION_PLAIN
     elif scanner.sees("almy"):
         # Approximates Tom Almy's version.
         game.options |= OPTION_PLAIN
     elif scanner.sees("almy"):
         # Approximates Tom Almy's version.
@@ -5808,7 +5943,7 @@ def choose():
         game.inplan += randrange(MAXUNINHAB/2, MAXUNINHAB+1)
     if game.options & OPTION_WORLDS:
         game.inplan += int(NINHAB)
         game.inplan += randrange(MAXUNINHAB/2, MAXUNINHAB+1)
     if game.options & OPTION_WORLDS:
         game.inplan += int(NINHAB)
-    game.state.nromrem = game.inrom = randrange(2 *game.skill)
+    game.state.nromrem = game.inrom = randrange(2 * game.skill)
     game.state.nscrem = game.inscom = (game.skill > SKILL_FAIR)
     game.state.remtime = 7.0 * game.length
     game.intime = game.state.remtime
     game.state.nscrem = game.inscom = (game.skill > SKILL_FAIR)
     game.state.remtime = 7.0 * game.length
     game.intime = game.state.remtime
@@ -5853,7 +5988,7 @@ def newqad():
     game.justin = True
     game.iplnet = None
     game.neutz = game.inorbit = game.landed = False
     game.justin = True
     game.iplnet = None
     game.neutz = game.inorbit = game.landed = False
-    game.ientesc = game.iseenit = False
+    game.ientesc = game.iseenit = game.isviolreported = False
     # Create a blank quadrant
     game.quad = fill2d(QUADSIZE, lambda i, j: '.')
     if game.iscate:
     # Create a blank quadrant
     game.quad = fill2d(QUADSIZE, lambda i, j: '.')
     if game.iscate:
@@ -6012,6 +6147,7 @@ commands = [
     ("DESTRUCT",         0),
     ("DEATHRAY",         0),
     ("CAPTURE",          OPTION_CAPTURE),
     ("DESTRUCT",         0),
     ("DEATHRAY",         0),
     ("CAPTURE",          OPTION_CAPTURE),
+    ("CLOAK",            OPTION_CLOAK),
     ("DEBUG",            0),
     ("MAYDAY",           0),
     ("SOS",              0),        # Synonym for MAYDAY
     ("DEBUG",            0),
     ("MAYDAY",           0),
     ("SOS",              0),        # Synonym for MAYDAY
@@ -6090,6 +6226,11 @@ def helpme():
 
 def makemoves():
     "Command-interpretation loop."
 
 def makemoves():
     "Command-interpretation loop."
+    def checkviol():
+        if game.irhere and game.state.date >= ALGERON and not game.isviolreported and game.iscloaked:
+            prout(_("The Romulan ship discovers you are breaking the Treaty of Algeron!"))
+            game.ncviol += 1
+            game.isviolreported = True
     while True:         # command loop
         drawmaps(1)
         while True:        # get a command
     while True:         # command loop
         drawmaps(1)
         while True:        # get a command
@@ -6137,10 +6278,12 @@ def makemoves():
         elif cmd == "PHASERS":                # phasers
             phasers()
             if game.ididit:
         elif cmd == "PHASERS":                # phasers
             phasers()
             if game.ididit:
+                checkviol()
                 hitme = True
         elif cmd in ("TORPEDO", "PHOTONS"):        # photon torpedos
             torps()
             if game.ididit:
                 hitme = True
         elif cmd in ("TORPEDO", "PHOTONS"):        # photon torpedos
             torps()
             if game.ididit:
+                checkviol()
                 hitme = True
         elif cmd == "MOVE":                # move under warp
             warp(wcourse=None, involuntary=False)
                 hitme = True
         elif cmd == "MOVE":                # move under warp
             warp(wcourse=None, involuntary=False)
index 42d5abfafcc1676011e9cd10bf1f1c060282ed42..63de67ec1f7530a9ca991e07f2810733319bc852 100644 (file)
@@ -194,9 +194,9 @@ DOCK         DAMAGES      CHART        IMPULSE      REST
 WARP         SENSORS      ORBIT        TRANSPORT    MINE         
 CRYSTALS     SHUTTLE      PLANETS      REPORT       COMPUTER     
 COMMANDS     EMEXIT       PROBE        SAVE         FREEZE       
 WARP         SENSORS      ORBIT        TRANSPORT    MINE         
 CRYSTALS     SHUTTLE      PLANETS      REPORT       COMPUTER     
 COMMANDS     EMEXIT       PROBE        SAVE         FREEZE       
-ABANDON      DESTRUCT     DEATHRAY     CAPTURE      DEBUG        
-MAYDAY       SOS          CALL         QUIT         HELP         
-SCORE                     
+ABANDON      DESTRUCT     DEATHRAY     CAPTURE      CLOAK        
+DEBUG        MAYDAY       SOS          CALL         QUIT         
+HELP         SCORE                     
 COMMAND> quit
 
 May the Great Bird of the Galaxy roost upon your home planet.
 COMMAND> quit
 
 May the Great Bird of the Galaxy roost upon your home planet.