Fix chart kossage in replay games.
[super-star-trek.git] / sst.py
diff --git a/sst.py b/sst.py
index 21fcb51e563dd732d06d69bb0e2a152e8895b7f1..409ba7aa0bac2375f04a4129c186902ebff14e1f 100755 (executable)
--- a/sst.py
+++ b/sst.py
@@ -261,12 +261,12 @@ DDSP            = 15
 DCLOAK          = 16
 NDEVICES        = 17        # Number of devices
 
-SKILL_NONE        = 0
-SKILL_NOVICE        = 1
-SKILL_FAIR        = 2
-SKILL_GOOD        = 3
-SKILL_EXPERT        = 4
-SKILL_EMERITUS        = 5
+SKILL_NONE      = 0
+SKILL_NOVICE    = 1
+SKILL_FAIR      = 2
+SKILL_GOOD      = 3
+SKILL_EXPERT    = 4
+SKILL_EMERITUS  = 5
 
 def damaged(dev):
     return (game.damage[dev] != 0.0)
@@ -274,8 +274,8 @@ def communicating():
     return not damaged(DRADIO) or game.condition=="docked"
 
 # Define future events
-FSPY        = 0        # Spy event happens always (no future[] entry)
-                # can cause SC to tractor beam Enterprise
+FSPY    = 0        # Spy event happens always (no future[] entry)
+                   # can cause SC to tractor beam Enterprise
 FSNOVA  = 1        # Supernova
 FTBEAM  = 2        # Commander tractor beams Enterprise
 FSNAP   = 3        # Snapshot for time warp
@@ -284,10 +284,10 @@ FCDBAS  = 5        # Commander destroys base
 FSCMOVE = 6        # Supercommander moves (might attack base)
 FSCDBAS = 7        # Supercommander destroys base
 FDSPROB = 8        # Move deep space probe
-FDISTR        = 9        # Emit distress call from an inhabited world
-FENSLV        = 10        # Inhabited word is enslaved */
-FREPRO        = 11        # Klingons build a ship in an enslaved system
-NEVENTS        = 12
+FDISTR  = 9        # Emit distress call from an inhabited world
+FENSLV  = 10       # Inhabited word is enslaved */
+FREPRO  = 11       # Klingons build a ship in an enslaved system
+NEVENTS = 12
 
 # Abstract out the event handling -- underlying data structures will change
 # when we implement stateful events
@@ -319,7 +319,9 @@ class Enemy:
         else:
             self.location = Coord()
             self.kdist = self.kavgd = None
-            game.enemies.remove(self)
+            # Guard prevents failure on Tholian or thingy
+            if self in game.enemies:
+                game.enemies.remove(self)
         return motion
     def __repr__(self):
         return "<%s,%s.%f>" % (self.type, self.location, self.power)        # For debugging
@@ -709,7 +711,7 @@ def moveklings():
     return tacmoves
 
 def movescom(iq, avoid):
-    "Commander movement helper."
+    "Supercommander movement helper."
     # Avoid quadrants with bases if we want to avoid Enterprise
     if not welcoming(iq) or (avoid and iq in game.state.baseq):
         return False
@@ -934,6 +936,7 @@ def cloak():
     key = scanner.nexttok()
 
     if key == "IHREAL":
+        huh()
         return
 
     action = None
@@ -1113,23 +1116,23 @@ def doshield(shraise):
 def randdevice():
     "Choose a device to damage, at random."
     weights = (
-        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%
+        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%
+        135,       # DSHIELD: deflector shields            13.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%
-        0,         # DCLOAK: the cloaking device             0.0
+        10,        # DDRAY: death ray                       1.0%
+        30,        # DDSP: deep-space probes                3.0%
+        10,        # DCLOAK: the cloaking device            1.0
     )
     assert(sum(weights) == 1000)
     idx = randrange(1000)
@@ -1607,16 +1610,16 @@ def deadkl(w, etype, mv):
         # Killed some type of Klingon
         game.state.galaxy[game.quadrant.i][game.quadrant.j].klingons -= 1
         game.klhere -= 1
-        if type == 'C':
+        if etype == 'C':
             game.state.kcmdr.remove(game.quadrant)
             unschedule(FTBEAM)
             if game.state.kcmdr:
                 schedule(FTBEAM, expran(1.0*game.incom/len(game.state.kcmdr)))
             if is_scheduled(FCDBAS) and game.battle == game.quadrant:
                 unschedule(FCDBAS)
-        elif type ==  'K':
+        elif etype ==  'K':
             game.state.remkl -= 1
-        elif type ==  'S':
+        elif etype ==  'S':
             game.state.nscrem -= 1
             game.state.kscmdr.invalidate()
             game.isatb = 0
@@ -1832,7 +1835,6 @@ def hittem(hits):
                 finish(FWON)
             if game.alldone:
                 return
-            kk -= 1        # don't do the increment
             continue
         else: # decide whether or not to emasculate klingon
             if kpow > 0 and withprob(0.9) and kpow <= randreal(0.4, 0.8)*kpini:
@@ -2606,7 +2608,7 @@ def wait():
         if key  != "IHEOL":
             break
         proutn(_("How long? "))
-    scanner.chew()
+        scanner.chew()
     if key != "IHREAL":
         huh()
         return
@@ -2747,7 +2749,7 @@ def nova(nov):
                             deadkl(neighbor, iquad, neighbor)
                             continue   # neighbor loop
                         # Else enemy gets flung by the blast wave
-                        newc = neighbor + neighbor - hits[-1]
+                        newc = neighbor + neighbor - start
                         proutn(crmena(True, iquad, "sector", neighbor) + _(" damaged"))
                         if not newc.valid_sector():
                             # can't leave quadrant
@@ -3278,24 +3280,9 @@ def plaque():
     winner = cgetline()
     # The 38 below must be 64 for 132-column paper
     nskip = 38 - len(winner)/2
-    fp.write("\n\n\n\n")
-    # --------DRAW ENTERPRISE PICTURE.
-    fp.write("                                       EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n" )
-    fp.write("                                      EEE                      E  : :                                         :  E\n" )
-    fp.write("                                    EE   EEE                   E  : :                   NCC-1701              :  E\n")
-    fp.write("EEEEEEEEEEEEEEEE        EEEEEEEEEEEEEEE  : :                              : E\n")
-    fp.write(" E                                     EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n")
-    fp.write("                      EEEEEEEEE               EEEEEEEEEEEEE                 E  E\n")
-    fp.write("                               EEEEEEE   EEEEE    E          E              E  E\n")
-    fp.write("                                      EEE           E          E            E  E\n")
-    fp.write("                                                       E         E          E  E\n")
-    fp.write("                                                         EEEEEEEEEEEEE      E  E\n")
-    fp.write("                                                      EEE :           EEEEEEE  EEEEEEEE\n")
-    fp.write("                                                    :E    :                 EEEE       E\n")
-    fp.write("                                                   .-E   -:-----                       E\n")
-    fp.write("                                                    :E    :                            E\n")
-    fp.write("                                                      EE  :                    EEEEEEEE\n")
-    fp.write("                                                       EEEEEEEEEEEEEEEEEEEEEEE\n")
+    # This is where the ASCII art picture was emitted.
+    # It got garbled somewhere in the chain of transmission to the Almy version.
+    # We should restore it if we can find old enough FORTRAN sources.
     fp.write("\n\n\n")
     fp.write(_("                                                       U. S. S. ENTERPRISE\n"))
     fp.write("\n\n\n\n")
@@ -3446,8 +3433,8 @@ def proutn(proutntline):
             pause_game()
             clrscr()
         # Uncomment this to debug curses problems
-        if logfp:
-            logfp.write("#curses: at %s proutn(%s)\n" % ((y, x), repr(proutntline)))
+        #if logfp:
+        #    logfp.write("#curses: at %s proutn(%s)\n" % ((y, x), repr(proutntline)))
         curwnd.addstr(proutntline)
         curwnd.refresh()
     else:
@@ -3519,7 +3506,7 @@ def setwnd(wnd):
                 legend = "prompt"
             else:
                 legend = "unknown"
-            logfp.write("#curses: setwnd(%s)\n" % legend)
+            #logfp.write("#curses: setwnd(%s)\n" % legend)
         curwnd = wnd
         # Some curses implementations get confused when you try this.
         try:
@@ -3723,8 +3710,6 @@ def imove(icourse=None, noattack=False):
         # check for edge of galaxy
         kinks = 0
         while True:
-
-
             kink = False
             if icourse.final.i < 0:
                 icourse.final.i = -icourse.final.i
@@ -3887,7 +3872,7 @@ def dock(verbose):
         prout(_("%d captured Klingons transferred to base") % (game.brigcapacity-game.brigfree))
         game.kcaptured += game.brigcapacity-game.brigfree
         game.brigfree = game.brigcapacity
-    if not damaged(DRADIO) and \
+    if communicating() and \
         ((is_scheduled(FCDBAS) or game.isatb == 1) and not game.iseenit):
         # get attack report from base
         prout(_("Lt. Uhura- \"Captain, an important message from the starbase:\""))
@@ -4022,8 +4007,12 @@ def getcourse(isprobe):
         key = scanner.nexttok()
         if key == "IHREAL":
             delta.i = scanner.real
-        else:
+        elif key == "IHEOL":
             delta.i = 0
+            scanner.push("\n")
+        else:
+            huh()
+            raise TrekError
     # Check for zero movement
     if delta.i == 0 and delta.j == 0:
         scanner.chew()
@@ -5219,7 +5208,8 @@ def lrscan(silent):
                 if not silent and game.state.galaxy[x][y].supernova:
                     proutn(" ***")
                 elif not silent:
-                    proutn(" %3d" % (game.state.chart[x][y].klingons*100 + game.state.chart[x][y].starbase * 10 + game.state.chart[x][y].stars))
+                    cn = " %3d" % (game.state.chart[x][y].klingons*100 + game.state.chart[x][y].starbase * 10 + game.state.chart[x][y].stars)
+                    proutn(((3 - len(cn)) * '.') + cn)
         if not silent:
             prout(" ")
 
@@ -5254,7 +5244,7 @@ def chart():
     scanner.chew()
     if (game.options & OPTION_AUTOSCAN):
         lrscan(silent=True)
-    if not damaged(DRADIO):
+    if communicating():
         rechart()
     if game.lastchart < game.state.date and game.condition == "docked":
         prout(_("Spock-  \"I revised the Star Chart from the starbase's records.\""))
@@ -5354,8 +5344,7 @@ def status(req=0):
                % (int((100.0*game.shield)/game.inshld + 0.5), game.shield)
         prstat(_("Shields"), s+data)
     if not req or req == 9:
-        prstat(_("Klingons Left"), "%d" \
-               % (game.state.remkl+len(game.state.kcmdr)+game.state.nscrem))
+        prstat(_("Klingons Left"), "%d" % game.unwon())
     if not req or req == 10:
         if game.options & OPTION_WORLDS:
             plnet = game.state.galaxy[game.quadrant.i][game.quadrant.j].planet
@@ -5391,7 +5380,7 @@ def srscan():
             prout(_("  [Using Base's sensors]"))
     else:
         prout(_("     Short-range scan"))
-    if goodScan and not damaged(DRADIO):
+    if goodScan and communicating():
         game.state.chart[game.quadrant.i][game.quadrant.j].klingons = game.state.galaxy[game.quadrant.i][game.quadrant.j].klingons
         game.state.chart[game.quadrant.i][game.quadrant.j].starbase = game.state.galaxy[game.quadrant.i][game.quadrant.j].starbase
         game.state.chart[game.quadrant.i][game.quadrant.j].stars = game.state.galaxy[game.quadrant.i][game.quadrant.j].stars
@@ -5965,7 +5954,7 @@ def choose():
     game.state.nscrem = game.inscom = (game.skill > SKILL_FAIR)
     game.state.remtime = 7.0 * game.length
     game.intime = game.state.remtime
-    game.state.remkl = game.inkling = 2.0*game.intime*((game.skill+1 - 2*randreal())*game.skill*0.1+.15)
+    game.state.remkl = game.inkling = int(2.0*game.intime*((game.skill+1 - 2*randreal())*game.skill*0.1+.15))
     game.incom = min(MINCMDR, int(game.skill + 0.0625*game.inkling*randreal()))
     game.state.remres = (game.inkling+4*game.incom)*game.intime
     game.inresor = game.state.remres
@@ -6112,6 +6101,9 @@ def newqad():
             game.quad[QUADSIZE-1][0] = '.'
         if game.quad[QUADSIZE-1][QUADSIZE-1]=='X':
             game.quad[QUADSIZE-1][QUADSIZE-1] = '.'
+    # This should guarantee that replay games don't lose info about the chart
+    if (game.options & OPTION_AUTOSCAN) or replayfp:
+        lrscan(silent=True)
 
 def setpassword():
     "Set the self-destruct password."
@@ -6173,6 +6165,7 @@ commands = [
     ("QUIT",             0),
     ("HELP",             0),
     ("SCORE",            0),
+    ("CURSES",            0),
     ("",                 0),
 ]
 
@@ -6285,6 +6278,8 @@ def makemoves():
                 huh()
             else:
                 break
+        if game.options & OPTION_CURSES:
+            prout("COMMAND> %s" % cmd)
         if cmd == "SRSCAN":                # srscan
             srscan()
         elif cmd == "STATUS":                # status
@@ -6391,6 +6386,9 @@ def makemoves():
             helpme()                        # get help
         elif cmd == "SCORE":
             score()                         # see current score
+        elif cmd == "CURSES":
+            game.options |= (OPTION_CURSES | OPTION_COLOR)
+            iostart()
         while True:
             if game.alldone:
                 break                # Game has ended