Navigation is finally subsumed by the course object.
[super-star-trek.git] / src / sst.py
index 76f320346534b6a4a5292810a007f787d91a0696..3cfffb7209734ef84e10a9c94e4cfb597fc63d42 100644 (file)
@@ -201,10 +201,6 @@ FOREVER    = 1e30
 MAXBURST       = 3
 MINCMDR        = 10
 
-# These functions hide the difference between 0-origin and 1-origin addressing.
-def VALID_QUADRANT(x, y):      return ((x)>=0 and (x)<GALSIZE and (y)>=0 and (y)<GALSIZE)
-def VALID_SECTOR(x, y):        return ((x)>=0 and (x)<QUADSIZE and (y)>=0 and (y)<QUADSIZE)
-
 # How to represent features
 IHR = 'R',
 IHK = 'K',
@@ -225,16 +221,25 @@ IHMATER0 = '-',
 IHMATER1 = 'o',
 IHMATER2 = '0'
 
+class TrekError:
+    pass
+
 class coord:
     def __init__(self, x=None, y=None):
         self.i = x
         self.j = y
+    def valid_quadrant(self):
+        return self.i>=0 and self.i<GALSIZE and self.j>=0 and self.j<GALSIZE
+    def valid_sector(self):
+       return self.i>=0 and self.i<QUADSIZE and self.j>=0 and self.j<QUADSIZE
     def invalidate(self):
         self.i = self.j = None
     def is_valid(self):
         return self.i != None and self.j != None
     def __eq__(self, other):
         return other != None and self.i == other.i and self.j == other.j
+    def __ne__(self, other):
+        return other == None or self.i != other.i or self.j != other.j
     def __add__(self, other):
         return coord(self.i+other.i, self.j+other.j)
     def __sub__(self, other):
@@ -245,9 +250,11 @@ class coord:
         return coord(self.i*other, self.j*other)
     def __div__(self, other):
         return coord(self.i/other, self.j/other)
+    def __mod__(self, other):
+        return coord(self.i % other, self.j % other)
     def __rdiv__(self, other):
         return coord(self.i/other, self.j/other)
-    def snaptogrid(self):
+    def roundtogrid(self):
         return coord(int(round(self.i)), int(round(self.j)))
     def distance(self, other=None):
         if not other: other = coord(0, 0)
@@ -266,13 +273,16 @@ class coord:
         else:
             s.j = self.j / abs(self.j)
         return s
+    def quadrant(self):
+        #print "Location %s -> %s" % (self, (self / QUADSIZE).roundtogrid())
+        return self.roundtogrid() / QUADSIZE
+    def sector(self):
+        return self.roundtogrid() % QUADSIZE
     def scatter(self):
         s = coord()
         s.i = self.i + randrange(-1, 2)
         s.j = self.j + randrange(-1, 2)
         return s
-    def __hash__(self):
-        return hash((x, y))
     def __str__(self):
         if self.i == None or self.j == None:
             return "Nowhere"
@@ -460,7 +470,6 @@ class gamestate:
         self.base = None       # position of base in current quadrant
         self.battle = None     # base coordinates being attacked
         self.plnet = None      # location of planet in quadrant
-        self.probec = None     # current probe quadrant
         self.gamewon = False   # Finished!
         self.ididit = False    # action taken -- allows enemy to attack
         self.alive = False     # we are alive (not killed)
@@ -503,7 +512,6 @@ class gamestate:
         self.irhere = 0                # Romulans in quadrant
         self.isatb = 0         # =1 if super commander is attacking base
         self.tourn = None      # tournament number
-        self.proben = 0                # number of moves for probe
         self.nprobes = 0       # number of probes available
         self.inresor = 0.0     # initial resources
         self.intime = 0.0      # initial time
@@ -516,15 +524,12 @@ class gamestate:
         self.warpfac = 0.0     # warp speed
         self.wfacsq = 0.0      # squared warp factor
         self.lsupres = 0.0     # life support reserves
-        self.dist = 0.0                # movement distance
-        self.direc = 0.0       # movement direction
         self.optime = 0.0      # time taken by current operation
         self.docfac = 0.0      # repair factor when docking (constant?)
         self.damfac = 0.0      # damage factor
         self.lastchart = 0.0   # time star chart was last updated
         self.cryprob = 0.0     # probability that crystal will work
-        self.probe = None      # location of probe
-        self.probein = None    # probe i,j increment
+        self.probe = None      # object holding probe course info
         self.height = 0.0      # height of orbit around planet
     def recompute(self):
         # Stas thinks this should be (C expression): 
@@ -600,7 +605,7 @@ def randreal(*args):
 
 def welcoming(iq):
     "Would this quadrant welcome another Klingon?"
-    return VALID_QUADRANT(iq.i,iq.j) and \
+    return iq.valid_quadrant() and \
        not game.state.galaxy[iq.i][iq.j].supernova and \
        game.state.galaxy[iq.i][iq.j].klingons < MAXKLQUAD
 
@@ -853,9 +858,9 @@ def movescom(iq, avoid):
     "Commander 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 True
+       return False
     if game.justin and not game.iscate:
-       return True
+       return False
     # do the move 
     game.state.galaxy[game.state.kscmdr.i][game.state.kscmdr.j].klingons -= 1
     game.state.kscmdr = iq
@@ -887,7 +892,7 @@ def movescom(iq, avoid):
                proutn(_("   a planet in Quadrant %s has been destroyed") % game.state.kscmdr)
                prout(_("   by the Super-commander.\""))
            break
-    return False; # looks good! 
+    return True; # looks good! 
                        
 def supercommander():
     "Move the Super Commander." 
@@ -945,24 +950,24 @@ def supercommander():
     # there was what looked like a bug in the Almy C code here,
     # but it might be this translation is just wrong.
     iq = game.state.kscmdr + idelta
-    if movescom(iq, avoid):
+    if not movescom(iq, avoid):
        # failed -- try some other maneuvers 
        if idelta.i==0 or idelta.j==0:
            # attempt angle move 
            if idelta.i != 0:
                iq.j = game.state.kscmdr.j + 1
-               if movescom(iq, avoid):
+               if not movescom(iq, avoid):
                    iq.j = game.state.kscmdr.j - 1
                    movescom(iq, avoid)
-           else:
+           elif idelta.j != 0:
                iq.i = game.state.kscmdr.i + 1
-               if movescom(iq, avoid):
+               if not movescom(iq, avoid):
                    iq.i = game.state.kscmdr.i - 1
                    movescom(iq, avoid)
        else:
            # try moving just in x or y 
            iq.j = game.state.kscmdr.j
-           if movescom(iq, avoid):
+           if not movescom(iq, avoid):
                iq.j = game.state.kscmdr.j + idelta.j
                iq.i = game.state.kscmdr.i
                movescom(iq, avoid)
@@ -970,7 +975,7 @@ def supercommander():
     if len(game.state.baseq) == 0:
        unschedule(FSCMOVE)
     else:
-        for (i, ibq) in enumerate(game.state.baseq):
+        for ibq in game.state.baseq:
            if ibq == game.state.kscmdr and game.state.kscmdr == game.battle:
                # attack the base 
                if avoid:
@@ -1259,63 +1264,63 @@ def collision(rammed, enemy):
        finish(FWON)
     return
 
-def torpedo(origin, course, dispersion, number, nburst):
+def torpedo(origin, bearing, dispersion, number, nburst):
     "Let a photon torpedo fly" 
     if not damaged(DSRSENS) or game.condition=="docked":
        setwnd(srscan_window)
     else: 
        setwnd(message_window)
-    shoved = False
-    ac = course + 0.25*dispersion
-    angle = (15.0-ac)*0.5235988
-    bullseye = (15.0 - course)*0.5235988
-    delta = coord(-math.sin(angle), math.cos(angle))          
-    bigger = max(abs(delta.i), abs(delta.j))
-    delta /= bigger
-    w = coord(0, 0); jw = coord(0, 0)
-    ungridded = copy.copy(origin)
+    ac = bearing + 0.25*dispersion     # dispersion is a random variable
+    bullseye = (15.0 - bearing)*0.5235988
+    track = course(bearing=ac, distance=QUADSIZE, origin=cartesian(origin)) 
+    bumpto = coord(0, 0)
     # Loop to move a single torpedo 
+    setwnd(message_window)
     for step in range(1, QUADSIZE*2):
-       ungridded += delta
-       w = ungridded.snaptogrid()
-       if not VALID_SECTOR(w.i, w.j):
+       track.next()
+        w = track.sector()
+       if not w.valid_sector():
            break
        iquad=game.quad[w.i][w.j]
        tracktorpedo(origin, w, step, number, nburst, iquad)
        if iquad==IHDOT:
            continue
        # hit something 
-       setwnd(message_window)
-       if damaged(DSRSENS) and not game.condition=="docked":
+       if not damaged(DSRSENS) or game.condition == "docked":
            skip(1);    # start new line after text track 
        if iquad in (IHE, IHF): # Hit our ship 
            skip(1)
            prout(_("Torpedo hits %s.") % crmshp())
            hit = 700.0 + randreal(100) - \
-               1000.0 * (w-origin).distance() * math.fabs(math.sin(bullseye-angle))
+               1000.0 * (w-origin).distance() * math.fabs(math.sin(bullseye-track.angle))
            newcnd(); # we're blown out of dock 
-           # We may be displaced. 
            if game.landed or game.condition=="docked":
                return hit # Cheat if on a planet 
-           ang = angle + 2.5*(randreal()-0.5)
+           ang = track.angle + 2.5*(randreal()-0.5)
            temp = math.fabs(math.sin(ang))
            if math.fabs(math.cos(ang)) > temp:
                temp = math.fabs(math.cos(ang))
            xx = -math.sin(ang)/temp
            yy = math.cos(ang)/temp
-           jw.i = int(w.i+xx+0.5)
-           jw.j = int(w.j+yy+0.5)
-           if not VALID_SECTOR(jw.i, jw.j):
+           bumpto.i = int(w.i+xx+0.5)
+           bumpto.j = int(w.j+yy+0.5)
+           if not bumpto.valid_sector():
                return hit
-           if game.quad[jw.i][jw.j]==IHBLANK:
+           if game.quad[bumpto.i][bumpto.j]==IHBLANK:
                finish(FHOLE)
                return hit
-           if game.quad[jw.i][jw.j]!=IHDOT:
+           if game.quad[bumpto.i][bumpto.j]!=IHDOT:
                # can't move into object 
                return hit
-           game.sector = jw
+           game.sector = bumpto
            proutn(crmshp())
-           shoved = True
+            game.quad[w.i][w.j]=IHDOT
+            game.quad[bumpto.i][bumpto.j]=iquad
+            prout(_(" displaced by blast to Sector %s ") % bumpto)
+            for enemy in game.enemies:
+                enemy.kdist = enemy.kavgd = (game.sector-enemy.kloc).distance()
+            game.enemies.sort(lambda x, y: cmp(x.kdist, y.kdist))
+            return None
        elif iquad in (IHC, IHS, IHR, IHK): # Hit a regular enemy 
            # find the enemy 
            if iquad in (IHC, IHS) and withprob(0.05):
@@ -1327,7 +1332,7 @@ def torpedo(origin, course, dispersion, number, nburst):
                    break
            kp = math.fabs(enemy.kpower)
            h1 = 700.0 + randrange(100) - \
-               1000.0 * (w-origin).distance() * math.fabs(math.sin(bullseye-angle))
+               1000.0 * (w-origin).distance() * math.fabs(math.sin(bullseye-track.angle))
            h1 = math.fabs(h1)
            if kp < h1:
                h1 = kp
@@ -1340,29 +1345,31 @@ def torpedo(origin, course, dispersion, number, nburst):
                return None
            proutn(crmena(True, iquad, "sector", w))
            # If enemy damaged but not destroyed, try to displace 
-           ang = angle + 2.5*(randreal()-0.5)
+           ang = track.angle + 2.5*(randreal()-0.5)
            temp = math.fabs(math.sin(ang))
            if math.fabs(math.cos(ang)) > temp:
                temp = math.fabs(math.cos(ang))
            xx = -math.sin(ang)/temp
            yy = math.cos(ang)/temp
-           jw.i = int(w.i+xx+0.5)
-           jw.j = int(w.j+yy+0.5)
-           if not VALID_SECTOR(jw.i, jw.j):
+           bumpto.i = int(w.i+xx+0.5)
+           bumpto.j = int(w.j+yy+0.5)
+            if not bumpto.valid_sector():
                prout(_(" damaged but not destroyed."))
                return
-           if game.quad[jw.i][jw.j]==IHBLANK:
+           if game.quad[bumpto.i][bumpto.j] == IHBLANK:
                prout(_(" buffeted into black hole."))
-               deadkl(w, iquad, jw)
-               return None
-           if game.quad[jw.i][jw.j]!=IHDOT:
-               # can't move into object 
+               deadkl(w, iquad, bumpto)
+           if game.quad[bumpto.i][bumpto.j] != IHDOT:
                prout(_(" damaged but not destroyed."))
-               return None
-           proutn(_(" damaged--"))
-           enemy.kloc = jw
-           shoved = True
-           break
+            else:
+                prout(_(" damaged-- displaced by blast to Sector %s ")%bumpto)
+                enemy.kloc = bumpto
+                game.quad[w.i][w.j]=IHDOT
+                game.quad[bumpto.i][bumpto.j]=iquad
+                for enemy in game.enemies:
+                    enemy.kdist = enemy.kavgd = (game.sector-enemy.kloc).distance()
+                game.enemies.sort(lambda x, y: cmp(x.kdist, y.kdist))
+            return None
        elif iquad == IHB: # Hit a base 
            skip(1)
            prout(_("***STARBASE DESTROYED.."))
@@ -1458,16 +1465,6 @@ def torpedo(origin, course, dispersion, number, nburst):
            skip(1)
            return None
        break
-    if curwnd!=message_window:
-       setwnd(message_window)
-    if shoved:
-       game.quad[w.i][w.j]=IHDOT
-       game.quad[jw.i][jw.j]=iquad
-       prout(_(" displaced by blast to Sector %s ") % jw)
-       for ll in range(len(game.enemies)):
-           game.enemies[ll].kdist = game.enemies[ll].kavgd = (game.sector-game.enemies[ll].kloc).distance()
-        game.enemies.sort(lambda x, y: cmp(x.kdist, y.kdist))
-       return None
     skip(1)
     prout(_("Torpedo missed."))
     return None;
@@ -1654,7 +1651,7 @@ def deadkl(w, type, mv):
     proutn(crmena(True, type, "sector", mv))
     # Decide what kind of enemy it is and update appropriately 
     if type == IHR:
-        # chalk up a Romulan 
+        # Chalk up a Romulan 
         game.state.galaxy[game.quadrant.i][game.quadrant.j].romulans -= 1
         game.irhere -= 1
         game.state.nromrem -= 1
@@ -1666,7 +1663,7 @@ def deadkl(w, type, mv):
         global thing
         thing = None
     else:
-        # Some type of a Klingon 
+        # Killed some type of Klingon 
         game.state.galaxy[game.quadrant.i][game.quadrant.j].klingons -= 1
         game.klhere -= 1
         if type == IHC:
@@ -1699,7 +1696,7 @@ def deadkl(w, type, mv):
 
 def targetcheck(w):
     "Return None if target is invalid, otherwise return a course angle."
-    if not VALID_SECTOR(w.i, w.j):
+    if not w.valid_sector():
        huh()
        return None
     delta = coord()
@@ -2136,7 +2133,7 @@ def phasers():
     if ifast:
        skip(1)
        if no == 0:
-           if withprob(0.99):
+           if withprob(0.01):
                prout(_("Sulu-  \"Sir, the high-speed shield control has malfunctioned . . ."))
                prouts(_("         CLICK   CLICK   POP  . . ."))
                prout(_(" No response, sir!"))
@@ -2454,43 +2451,36 @@ def events():
                supercommander()
        elif evcode == FDSPROB: # Move deep space probe 
            schedule(FDSPROB, 0.01)
-           game.probe += game.probein
-           i = int(round(game.probe.i/float(QUADSIZE)))
-           j = int(round(game.probe.j/float(QUADSIZE)))
-           if game.probec.i != i or game.probec.j != j:
-               game.probec.i = i
-               game.probec.j = j
-               if not VALID_QUADRANT(i, j) or \
-                   game.state.galaxy[game.probec.i][game.probec.j].supernova:
+            if game.probe.next(grain=QUADSIZE):
+               if not game.probe.quadrant().valid_quadrant() or \
+                   game.state.galaxy[game.probe.quadrant().i][game.probe.quadrant().j].supernova:
                    # Left galaxy or ran into supernova
                     if communicating():
                        announce()
                        skip(1)
                        proutn(_("Lt. Uhura-  \"The deep space probe "))
-                       if not VALID_QUADRANT(i, j):
-                           proutn(_("has left the galaxy"))
+                       if not game.probe.quadrant().valid_quadrant():
+                           prout(_("has left the galaxy.\""))
                        else:
-                           proutn(_("is no longer transmitting"))
-                       prout(".\"")
+                           prout(_("is no longer transmitting.\""))
                    unschedule(FDSPROB)
                    continue
-                if not communicating():
-                   announce()
+                if communicating():
+                   #announce()
                    skip(1)
-                   proutn(_("Lt. Uhura-  \"The deep space probe is now in Quadrant %s.\"") % game.probec)
-           pdest = game.state.galaxy[game.probec.i][game.probec.j]
-           # Update star chart if Radio is working or have access to radio
+                   prout(_("Lt. Uhura-  \"The deep space probe is now in Quadrant %s.\"") % game.probe.quadrant())
+           pdest = game.state.galaxy[game.probe.quadrant().i][game.probe.quadrant().j]
            if communicating():
-               chp = game.state.chart[game.probec.i][game.probec.j]
+               chp = game.state.chart[game.probe.quadrant().i][game.probe.quadrant().j]
                chp.klingons = pdest.klingons
                chp.starbase = pdest.starbase
                chp.stars = pdest.stars
                pdest.charted = True
-           game.proben -= 1 # One less to travel
-           if game.proben == 0 and game.isarmed and pdest.stars:
-               supernova(game.probec)          # fire in the hole!
+           game.probe.moves -= 1 # One less to travel
+           if game.probe.moves == 0 and game.isarmed and pdest.stars:
+               supernova(game.probe          # fire in the hole!
                unschedule(FDSPROB)
-               if game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova: 
+               if game.state.galaxy[game.quadrant().i][game.quadrant().j].supernova: 
                    return
        elif evcode == FDISTR: # inhabited system issues distress call 
            unschedule(FDISTR)
@@ -2553,14 +2543,15 @@ def events():
                continue                # full right now 
            # reproduce one Klingon 
            w = ev.quadrant
+            m = coord()
            if game.klhere >= MAXKLQUAD:
                 try:
                     # this quadrant not ok, pick an adjacent one 
-                    for i in range(w.i - 1, w.i + 2):
-                        for j in range(w.j - 1, w.j + 2):
-                            if not VALID_QUADRANT(i, j):
+                    for m.i in range(w.i - 1, w.i + 2):
+                        for m.j in range(w.j - 1, w.j + 2):
+                            if not m.valid_quadrant():
                                 continue
-                            q = game.state.galaxy[w.i][w.j]
+                            q = game.state.galaxy[m.i][m.j]
                             # check for this quad ok (not full & no snova) 
                             if q.klingons >= MAXKLQUAD or q.supernova:
                                 continue
@@ -2568,7 +2559,7 @@ def events():
                     else:
                         continue       # search for eligible quadrant failed
                 except "FOUNDIT":
-                    w.i = i; w.j = j
+                    w = m
            # deliver the child 
            game.state.remkl += 1
            q.klingons += 1
@@ -2577,7 +2568,6 @@ def events():
                game.enemies.append(newkling())
            # recompute time left
             game.recompute()
-           # report the disaster if we can 
            if communicating():
                if game.quadrant == w:
                    prout(_("Spock- sensors indicate the Klingons have"))
@@ -2663,7 +2653,7 @@ def nova(nov):
                 if offset.j==0 and offset.i==0:
                     continue
                 neighbor = start + offset
-                if not VALID_SECTOR(neighbor.j, neighbor.i):
+                if not neighbor.valid_sector():
                     continue
                 iquad = game.quad[neighbor.i][neighbor.j]
                 # Empty space ends reaction
@@ -2735,7 +2725,7 @@ def nova(nov):
                         break
                     newc = neighbor + neighbor - hits[mm]
                     proutn(crmena(True, iquad, "sector", neighbor) + _(" damaged"))
-                    if not VALID_SECTOR(newc.i, newc.j):
+                    if not newc.valid_sector():
                         # can't leave quadrant 
                         skip(1)
                         break
@@ -2754,17 +2744,18 @@ def nova(nov):
                     game.quad[newc.i][newc.j] = iquad
                     game.enemies[ll].move(newc)
     # Starship affected by nova -- kick it away. 
-    game.dist = kount*0.1
-    game.direc = course[3*(bump.i+1)+bump.j+2]
-    if game.direc == 0.0:
-       game.dist = 0.0
-    if game.dist == 0.0:
+    dist = kount*0.1
+    direc = course[3*(bump.i+1)+bump.j+2]
+    if direc == 0.0:
+       dist = 0.0
+    if dist == 0.0:
        return
-    game.optime = 10.0*game.dist/16.0
+    course = course(bearing=direc, distance=dist)
+    game.optime = course.time(warp=4)
     skip(1)
     prout(_("Force of nova displaces starship."))
-    imove(novapush=True)
-    game.optime = 10.0*game.dist/16.0
+    imove(course, novapush=True)
+    game.optime = course.time(warp=4)
     return
        
 def supernova(w):
@@ -3281,7 +3272,7 @@ def plaque():
         fp.write(_("Emeritus level\n\n"))
     else:
         fp.write(_(" Cheat level\n\n"))
-    timestring = ctime()
+    timestring = time.ctime()
     fp.write(_("                                                 This day of %.6s %.4s, %.8s\n\n") %
                     (timestring+4, timestring+20, timestring+11))
     fp.write(_("                                                        Your score:  %d\n\n") % iscore)
@@ -3345,7 +3336,7 @@ def waitfor():
 
 def announce():
     skip(1)
-    prouts(_("[ANOUNCEMENT ARRIVING...]"))
+    prouts(_("[ANNOUNCEMENT ARRIVING...]"))
     skip(1)
 
 def pause_game():
@@ -3533,7 +3524,7 @@ def tracktorpedo(origin, w, step, i, n, iquad):
        if step == 1:
            if n != 1:
                skip(1)
-               proutn(_("Track for %s torpedo number %d-  ") % (game.quad[origin.i][origin.j],i+1))
+               proutn(_("Track for torpedo number %d-  ") % (i+1))
            else:
                skip(1)
                proutn(_("Torpedo track- "))
@@ -3587,7 +3578,7 @@ def prstat(txt, data):
 
 # Code from moving.c begins here
 
-def imove(novapush):
+def imove(course=None, novapush=False):
     "Movement execution for warp, impulse, supernova, and tractor-beam events."
     w = coord(); final = coord()
     trbeam = False
@@ -3608,156 +3599,128 @@ def imove(novapush):
         newcnd()
         drawmaps(0)
         setwnd(message_window)
-    w.i = w.j = 0
+
     if game.inorbit:
        prout(_("Helmsman Sulu- \"Leaving standard orbit.\""))
        game.inorbit = False
-    angle = ((15.0 - game.direc) * 0.5235988)
-    deltax = -math.sin(angle)
-    deltay = math.cos(angle)
-    if math.fabs(deltax) > math.fabs(deltay):
-       bigger = math.fabs(deltax)
-    else:
-       bigger = math.fabs(deltay)
-    deltay /= bigger
-    deltax /= bigger
     # If tractor beam is to occur, don't move full distance 
     if game.state.date+game.optime >= scheduled(FTBEAM):
        trbeam = True
        game.condition = "red"
-       game.dist = game.dist*(scheduled(FTBEAM)-game.state.date)/game.optime + 0.1
+       course.distance = course.distance*(scheduled(FTBEAM)-game.state.date)/game.optime + 0.1
        game.optime = scheduled(FTBEAM) - game.state.date + 1e-5
     # Move within the quadrant 
     game.quad[game.sector.i][game.sector.j] = IHDOT
-    x = game.sector.i
-    y = game.sector.j
-    n = int(10.0*game.dist*bigger+0.5)
-    if n > 0:
-       for m in range(1, n+1):
-            x += deltax
-            y += deltay
-           w.i = int(round(x))
-           w.j = int(round(y))
-           if not VALID_SECTOR(w.i, w.j):
-               # Leaving quadrant -- allow final enemy attack 
-               # Don't do it if being pushed by Nova 
-               if len(game.enemies) != 0 and not novapush:
-                   newcnd()
-                   for enemy in game.enemies:
-                       finald = (w - enemy.kloc).distance()
-                       enemy.kavgd = 0.5 * (finald + enemy.kdist)
-                   #
-                   # Stas Sergeev added the condition
-                   # that attacks only happen if Klingons
-                   # are present and your skill is good.
-                   # 
-                   if game.skill > SKILL_GOOD and game.klhere > 0 and not game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
-                       attack(torps_ok=False)
-                   if game.alldone:
-                       return
-               # compute final position -- new quadrant and sector 
-               x = (QUADSIZE*game.quadrant.i)+game.sector.i
-               y = (QUADSIZE*game.quadrant.j)+game.sector.j
-               w.i = int(round(x+10.0*game.dist*bigger*deltax))
-               w.j = int(round(y+10.0*game.dist*bigger*deltay))
-               # check for edge of galaxy 
-               kinks = 0
-                while True:
-                   kink = False
-                   if w.i < 0:
-                       w.i = -w.i
-                       kink = True
-                   if w.j < 0:
-                       w.j = -w.j
-                       kink = True
-                   if w.i >= GALSIZE*QUADSIZE:
-                       w.i = (GALSIZE*QUADSIZE*2) - w.i
-                       kink = True
-                   if w.j >= GALSIZE*QUADSIZE:
-                       w.j = (GALSIZE*QUADSIZE*2) - w.j
-                       kink = True
-                   if kink:
-                       kinks += 1
-                    else:
+    for m in range(course.moves):
+        course.next()
+        w = course.sector()
+        if course.origin.quadrant() != course.location.quadrant():
+            # Leaving quadrant -- allow final enemy attack 
+            # Don't do it if being pushed by Nova 
+            if len(game.enemies) != 0 and not novapush:
+                newcnd()
+                for enemy in game.enemies:
+                    finald = (w - enemy.kloc).distance()
+                    enemy.kavgd = 0.5 * (finald + enemy.kdist)
+                # Stas Sergeev added the condition
+                # that attacks only happen if Klingons
+                # are present and your skill is good.
+                if game.skill > SKILL_GOOD and game.klhere > 0 and not game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
+                    attack(torps_ok=False)
+                if game.alldone:
+                    return
+            # check for edge of galaxy 
+            kinks = 0
+            while True:
+                kink = False
+                if course.final.i < 0:
+                    course.final.i = -course.final.i
+                    kink = True
+                if course.final.j < 0:
+                    course.final.j = -course.final.j
+                    kink = True
+                if course.final.i >= GALSIZE*QUADSIZE:
+                    course.final.i = (GALSIZE*QUADSIZE*2) - course.final.i
+                    kink = True
+                if course.final.j >= GALSIZE*QUADSIZE:
+                    course.final.j = (GALSIZE*QUADSIZE*2) - course.final.j
+                    kink = True
+                if kink:
+                    kinks += 1
+                else:
+                    break
+            if kinks:
+                game.nkinks += 1
+                if game.nkinks == 3:
+                    # Three strikes -- you're out! 
+                    finish(FNEG3)
+                    return
+                skip(1)
+                prout(_("YOU HAVE ATTEMPTED TO CROSS THE NEGATIVE ENERGY BARRIER"))
+                prout(_("AT THE EDGE OF THE GALAXY.  THE THIRD TIME YOU TRY THIS,"))
+                prout(_("YOU WILL BE DESTROYED."))
+            # Compute final position in new quadrant 
+            if trbeam: # Don't bother if we are to be beamed 
+                return
+            game.quadrant = course.final.quadrant()
+            game.sector = course.final.sector()
+            skip(1)
+            prout(_("Entering Quadrant %s.") % game.quadrant)
+            game.quad[game.sector.i][game.sector.j] = game.ship
+            newqad()
+            if game.skill>SKILL_NOVICE:
+                attack(torps_ok=False)  
+            return
+        iquad = game.quad[w.i][w.j]
+        if iquad != IHDOT:
+            # object encountered in flight path 
+            stopegy = 50.0*course.distance/game.optime
+            course.distance = (game.sector - w).distance() / (QUADSIZE * 1.0)
+            game.sector = w
+            if iquad in (IHT, IHK, IHC, IHS, IHR, IHQUEST):
+                for enemy in game.enemies:
+                    if enemy.kloc == game.sector:
                         break
-               if kinks:
-                   game.nkinks += 1
-                   if game.nkinks == 3:
-                       # Three strikes -- you're out! 
-                       finish(FNEG3)
-                       return
-                   skip(1)
-                   prout(_("YOU HAVE ATTEMPTED TO CROSS THE NEGATIVE ENERGY BARRIER"))
-                   prout(_("AT THE EDGE OF THE GALAXY.  THE THIRD TIME YOU TRY THIS,"))
-                   prout(_("YOU WILL BE DESTROYED."))
-               # Compute final position in new quadrant 
-               if trbeam: # Don't bother if we are to be beamed 
-                   return
-               game.quadrant.i = w.i/QUADSIZE
-               game.quadrant.j = w.j/QUADSIZE
-               game.sector.i = w.i - (QUADSIZE*game.quadrant.i)
-               game.sector.j = w.j - (QUADSIZE*game.quadrant.j)
-               skip(1)
-               prout(_("Entering Quadrant %s.") % game.quadrant)
-               game.quad[game.sector.i][game.sector.j] = game.ship
-               newqad()
-               if game.skill>SKILL_NOVICE:
-                   attack(torps_ok=False)  
-               return
-           iquad = game.quad[w.i][w.j]
-           if iquad != IHDOT:
-               # object encountered in flight path 
-               stopegy = 50.0*game.dist/game.optime
-               game.dist = (game.sector - w).distance() / (QUADSIZE * 1.0)
-                if iquad in (IHT, IHK, IHC, IHS, IHR, IHQUEST):
-                   game.sector = w
-                    for enemy in game.enemies:
-                        if enemy.kloc == game.sector:
-                            break
-                   collision(rammed=False, enemy=enemy)
-                   final = game.sector
-               elif iquad == IHBLANK:
-                   skip(1)
-                   prouts(_("***RED ALERT!  RED ALERT!"))
-                   skip(1)
-                   proutn("***" + crmshp())
-                   proutn(_(" pulled into black hole at Sector %s") % w)
-                   #
-                   # Getting pulled into a black hole was certain
-                   # death in Almy's original.  Stas Sergeev added a
-                   # possibility that you'll get timewarped instead.
-                   # 
-                   n=0
-                   for m in range(NDEVICES):
-                       if game.damage[m]>0: 
-                           n += 1
-                   probf=math.pow(1.4,(game.energy+game.shield)/5000.0-1.0)*math.pow(1.3,1.0/(n+1)-1.0)
-                   if (game.options & OPTION_BLKHOLE) and withprob(1-probf): 
-                       timwrp()
-                   else: 
-                       finish(FHOLE)
-                   return
-               else:
-                   # something else 
-                   skip(1)
-                   proutn(crmshp())
-                   if iquad == IHWEB:
-                       prout(_(" encounters Tholian web at %s;") % w)
-                   else:
-                       prout(_(" blocked by object at %s;") % w)
-                   proutn(_("Emergency stop required "))
-                   prout(_("%2d units of energy.") % int(stopegy))
-                   game.energy -= stopegy
-                   final.i = int(round(deltax))
-                   final.j = int(round(deltay))
-                   game.sector = final
-                   if game.energy <= 0:
-                       finish(FNRG)
-                       return
-                # We're here!
-               no_quad_change()
+                collision(rammed=False, enemy=enemy)
+                final = game.sector
+            elif iquad == IHBLANK:
+                skip(1)
+                prouts(_("***RED ALERT!  RED ALERT!"))
+                skip(1)
+                proutn("***" + crmshp())
+                proutn(_(" pulled into black hole at Sector %s") % w)
+                # Getting pulled into a black hole was certain
+                # death in Almy's original.  Stas Sergeev added a
+                # possibility that you'll get timewarped instead.
+                n=0
+                for m in range(NDEVICES):
+                    if game.damage[m]>0: 
+                        n += 1
+                probf=math.pow(1.4,(game.energy+game.shield)/5000.0-1.0)*math.pow(1.3,1.0/(n+1)-1.0)
+                if (game.options & OPTION_BLKHOLE) and withprob(1-probf): 
+                    timwrp()
+                else: 
+                    finish(FHOLE)
                 return
-       game.dist = (game.sector - w).distance() / (QUADSIZE * 1.0)
+            else:
+                # something else 
+                skip(1)
+                proutn(crmshp())
+                if iquad == IHWEB:
+                    prout(_(" encounters Tholian web at %s;") % w)
+                else:
+                    prout(_(" blocked by object at %s;") % w)
+                proutn(_("Emergency stop required "))
+                prout(_("%2d units of energy.") % int(stopegy))
+                game.energy -= stopegy
+                game.sector = w
+                if game.energy <= 0:
+                    finish(FNRG)
+                    return
+            # We're here!
+            no_quad_change()
+            return
+       course.distance = (game.sector - w).distance() / (QUADSIZE * 1.0)
        game.sector = w
     final = game.sector
     no_quad_change()
@@ -3800,6 +3763,14 @@ def dock(verbose):
 # because it involves giving x and y motions, yet the coordinates
 # are always displayed y - x, where +y is downward!
 
+def cartesian(loc1=None, loc2=None):
+    if loc1 is None:
+        return game.quadrant * QUADSIZE + game.sector
+    elif loc2 is None:
+        return game.quadrant * QUADSIZE + loc1
+    else:
+        return loc1 * QUADSIZE + loc2
+
 def getcourse(isprobe):
     "Get a course and distance from the user."
     key = 0
@@ -3812,7 +3783,7 @@ def getcourse(isprobe):
        prout(_("Dummy! You can't leave standard orbit until you"))
        proutn(_("are back aboard the ship."))
        scanner.chew()
-       return False
+       raise TrekError
     while navmode == "unspecified":
        if damaged(DNAVSYS):
            if isprobe:
@@ -3840,7 +3811,7 @@ def getcourse(isprobe):
            else:
                huh()
                scanner.chew()
-               return False
+               raise TrekError
        else: # numeric 
            if isprobe:
                prout(_("(Manual navigation assumed.)"))
@@ -3848,6 +3819,7 @@ def getcourse(isprobe):
                prout(_("(Manual movement assumed.)"))
            navmode = "manual"
            break
+    delta = coord()
     if navmode == "automatic":
        while key == "IHEOL":
            if isprobe:
@@ -3859,12 +3831,12 @@ def getcourse(isprobe):
            key = scanner.next()
        if key != "IHREAL":
            huh()
-           return False
+           raise TrekError
        xi = int(round(scanner.real))-1
        key = scanner.next()
        if key != "IHREAL":
            huh()
-           return False
+           raise TrekError
        xj = int(round(scanner.real))-1
        key = scanner.next()
        if key == "IHREAL":
@@ -3873,7 +3845,7 @@ def getcourse(isprobe):
            key = scanner.next()
            if key != "IHREAL":
                huh()
-               return False
+               raise TrekError
            xl = int(round(scanner.real))-1
            dquad.i = xi
            dquad.j = xj
@@ -3891,9 +3863,9 @@ def getcourse(isprobe):
                dsect.i = xi
                dsect.j = xj
            itemp = "normal"
-       if not VALID_QUADRANT(dquad.i,dquad.j) or not VALID_SECTOR(dsect.i,dsect.j):
+       if not dquad.valid_quadrant() or not dsect.valid_sector():
            huh()
-           return False
+           raise TrekError
        skip(1)
        if not isprobe:
            if itemp > "curt":
@@ -3902,7 +3874,6 @@ def getcourse(isprobe):
            else:
                prout(_("Ensign Chekov- \"Course laid in, Captain.\""))
         # the actual deltas get computed here
-        delta = coord()
        delta.j = dquad.j-game.quadrant.j + (dsect.j-game.sector.j)/(QUADSIZE*1.0)
        delta.i = game.quadrant.i-dquad.i + (game.sector.i-dsect.i)/(QUADSIZE*1.0)
     else: # manual 
@@ -3914,27 +3885,64 @@ def getcourse(isprobe):
        itemp = "verbose"
        if key != "IHREAL":
            huh()
-           return False
+           raise TrekError
        delta.j = scanner.real
        key = scanner.next()
        if key != "IHREAL":
            huh()
-           return False
+           raise TrekError
        delta.i = scanner.real
     # Check for zero movement 
     if delta.i == 0 and delta.j == 0:
        scanner.chew()
-       return False
+       raise TrekError
     if itemp == "verbose" and not isprobe:
        skip(1)
        prout(_("Helmsman Sulu- \"Aye, Sir.\""))
-    # Course actually laid in.
-    game.dist = delta.distance()
-    game.direc = delta.bearing()
-    if game.direc < 0.0:
-       game.direc += 12.0
     scanner.chew()
-    return True
+    return course(bearing=delta.bearing(), distance=delta.distance())
+
+class course:
+    def __init__(self, bearing, distance, origin=None): 
+        self.distance = distance
+        self.bearing = bearing
+        if origin is None:
+            self.origin = cartesian(game.quadrant, game.sector)
+        else:
+            self.origin = origin
+        # The bearing() code we inherited from FORTRAN is actually computing
+        # clockface directions!
+        if self.bearing < 0.0:
+            self.bearing += 12.0
+        self.angle = ((15.0 - self.bearing) * 0.5235988)
+        if origin is None:
+            self.location = cartesian(game.quadrant, game.sector)
+        else:
+            self.location = cartesian(game.quadrant, origin)
+        self.increment = coord(-math.sin(self.angle), math.cos(self.angle))
+        bigger = max(abs(self.increment.i), abs(self.increment.j))
+        self.increment /= bigger
+        self.moves = int(round(10*self.distance*bigger))
+        self.final = (self.location + self.moves*self.increment).roundtogrid()
+    def next(self, grain=1):
+        "Next step on course."
+        self.moves -=1
+        self.nextlocation = self.location + self.increment
+        oldloc = (self.location/grain).roundtogrid()
+        newloc = (self.nextlocation/grain).roundtogrid()
+        self.location = self.nextlocation
+        if newloc != oldloc:
+            return True
+        else:
+            return False
+    def quadrant(self):
+        return self.location.quadrant()
+    def sector(self):
+        return self.location.sector()
+    def power(self, warp):
+       return self.distance*(warp**3)*(game.shldup+1)
+    def time(self, warp):
+        return 10.0*self.distance/warp**2
 
 def impulse():
     "Move under impulse power."
@@ -3945,9 +3953,11 @@ def impulse():
        prout(_("Engineer Scott- \"The impulse engines are damaged, Sir.\""))
        return
     if game.energy > 30.0:
-        if not getcourse(isprobe=False):
+        try:
+            course = getcourse(isprobe=False)
+        except TrekError:
            return
-       power = 20.0 + 100.0*game.dist
+       power = 20.0 + 100.0*course.distance
     else:
        power = 30.0
     if power >= game.energy:
@@ -3964,7 +3974,7 @@ def impulse():
        scanner.chew()
        return
     # Make sure enough time is left for the trip 
-    game.optime = game.dist/0.095
+    game.optime = course.dist/0.095
     if game.optime >= game.state.remtime:
        prout(_("First Officer Spock- \"Captain, our speed under impulse"))
        prout(_("power is only 0.95 sectors per stardate. Are you sure"))
@@ -3972,26 +3982,26 @@ def impulse():
        if ja() == False:
            return
     # Activate impulse engines and pay the cost 
-    imove(novapush=False)
+    imove(course, novapush=False)
     game.ididit = True
     if game.alldone:
        return
-    power = 20.0 + 100.0*game.dist
+    power = 20.0 + 100.0*course.dist
     game.energy -= power
-    game.optime = game.dist/0.095
+    game.optime = course.dist/0.095
     if game.energy <= 0:
        finish(FNRG)
     return
 
-def warp(timewarp):
+def warp(course, involuntary):
     "ove under warp drive."
     blooey = False; twarp = False
-    if not timewarp: # Not WARPX entry 
+    if not involuntary: # Not WARPX entry 
        game.ididit = False
        if game.damage[DWARPEN] > 10.0:
            scanner.chew()
            skip(1)
-           prout(_("Engineer Scott- \"The impulse engines are damaged, Sir.\""))
+           prout(_("Engineer Scott- \"The warp engines are damaged, Sir.\""))
            return
        if damaged(DWARPEN) and game.warpfac > 4.0:
            scanner.chew()
@@ -3999,18 +4009,22 @@ def warp(timewarp):
            prout(_("Engineer Scott- \"Sorry, Captain. Until this damage"))
            prout(_("  is repaired, I can only give you warp 4.\""))
            return
-               # Read in course and distance 
-        if not getcourse(isprobe=False):
-           return
-       # Make sure starship has enough energy for the trip 
-       power = (game.dist+0.05)*game.warpfac*game.warpfac*game.warpfac*(game.shldup+1)
-       if power >= game.energy:
+               # Read in course and distance
+        if course==None:
+            try:
+                course = getcourse(isprobe=False)
+            except TrekError:
+                return
+       # Make sure starship has enough energy for the trip
+        # Note: this formula is slightly different from the C version,
+        # and lets you skate a bit closer to the edge.
+       if course.power(game.warpfac) >= game.energy:
            # Insufficient power for trip 
            game.ididit = False
            skip(1)
            prout(_("Engineering to bridge--"))
            if not game.shldup or 0.5*power > game.energy:
-               iwarp = math.pow((game.energy/(game.dist+0.05)), 0.333333333)
+               iwarp = (game.energy/(course.dist+0.05)) ** 0.333333333
                if iwarp <= 0:
                    prout(_("We can't do it, Captain. We don't have enough energy."))
                else:
@@ -4022,10 +4036,9 @@ def warp(timewarp):
                        prout(".")
            else:
                prout(_("We haven't the energy to go that far with the shields up."))
-           return
-                                               
+           return                              
        # Make sure enough time is left for the trip 
-       game.optime = 10.0*game.dist/game.warpfac**2
+       game.optime = course.time(game.warpfac)
        if game.optime >= 0.8*game.state.remtime:
            skip(1)
            prout(_("First Officer Spock- \"Captain, I compute that such"))
@@ -4041,12 +4054,12 @@ def warp(timewarp):
     if game.warpfac > 6.0:
        # Decide if engine damage will occur
         # ESR: Seems wrong. Probability of damage goes *down* with distance? 
-       prob = game.dist*(6.0-game.warpfac)**2/66.666666666
+       prob = course.dist*(6.0-game.warpfac)**2/66.666666666
        if prob > randreal():
            blooey = True
-           game.dist = randreal(game.dist)
+           course.distance = randreal(course.distance)
        # Decide if time warp will occur 
-       if 0.5*game.dist*math.pow(7.0,game.warpfac-10.0) > randreal():
+       if 0.5*course.dist*math.pow(7.0,game.warpfac-10.0) > randreal():
            twarp = True
        if idebug and game.warpfac==10 and not twarp:
            blooey = False
@@ -4056,7 +4069,7 @@ def warp(timewarp):
        if blooey or twarp:
            # If time warp or engine damage, check path 
            # If it is obstructed, don't do warp or damage 
-           angle = ((15.0-game.direc)*0.5235998)
+           angle = ((15.0-course.bearing)*0.5235998)
            deltax = -math.sin(angle)
            deltay = math.cos(angle)
            if math.fabs(deltax) > math.fabs(deltay):
@@ -4065,7 +4078,7 @@ def warp(timewarp):
                bigger = math.fabs(deltay)
            deltax /= bigger
            deltay /= bigger
-           n = 10.0 * game.dist * bigger +0.5
+           n = 10.0 * course.distance * bigger +0.5
            x = game.sector.i
            y = game.sector.j
            for l in range(1, n+1):
@@ -4073,19 +4086,19 @@ def warp(timewarp):
                ix = x + 0.5
                y += deltay
                iy = y +0.5
-               if not VALID_SECTOR(ix, iy):
+               if not coord(ix, iy).valid_sector():
                    break
                if game.quad[ix][iy] != IHDOT:
                    blooey = False
                    twarp = False
     # Activate Warp Engines and pay the cost 
-    imove(novapush=False)
+    imove(course, novapush=False)
     if game.alldone:
        return
-    game.energy -= game.dist*game.warpfac*game.warpfac*game.warpfac*(game.shldup+1)
+    game.energy -= course.power(game.warpfac)
     if game.energy <= 0:
        finish(FNRG)
-    game.optime = 10.0*game.dist/game.warpfac**2
+    game.optime = course.time(game.warpfac)
     if twarp:
        timwrp()
     if blooey:
@@ -4192,15 +4205,15 @@ def atover(igrab):
        game.warpfac = randreal(6.0, 8.0)
        prout(_("Warp factor set to %d") % int(game.warpfac))
        power = 0.75*game.energy
-       game.dist = power/(game.warpfac*game.warpfac*game.warpfac*(game.shldup+1))
+       dist = power/(game.warpfac*game.warpfac*game.warpfac*(game.shldup+1))
        distreq = randreal(math.sqrt(2))
        if distreq < game.dist:
-           game.dist = distreq
-       game.optime = 10.0*game.dist/game.warpfac**2
-       game.direc = randreal(12)       # How dumb! 
+           dist = distreq
+        course = course(bearing=randreal(12), distance=dist)   # How dumb!
+       game.optime = course.time()
        game.justin = False
        game.inorbit = False
-       warp(True)
+       warp(course, involuntary=True)
        if not game.justin:
            # This is bad news, we didn't leave quadrant. 
            if game.alldone:
@@ -4237,7 +4250,6 @@ def timwrp():
        unschedule(FCDBAS)
        unschedule(FSCDBAS)
        game.battle.invalidate()
-
        # Make sure Galileo is consistant -- Snapshot may have been taken
         # when on planet, which would give us two Galileos! 
        gotit = False
@@ -4308,17 +4320,11 @@ def probe():
        game.isarmed = ja()
     elif key == "IHREAL":              # first element of course
         scanner.push(scanner.token)
-    if not getcourse(isprobe=True):
-       return
+    try:
+        game.probe = getcourse(isprobe=True)
+    except TrekError:
+        return
     game.nprobes -= 1
-    angle = ((15.0 - game.direc) * 0.5235988)
-    game.probein = coord(-math.sin(angle), math.cos(angle))
-    bigger = max(abs(game.probein.i), abs(game.probein.j))
-    game.probein /= bigger
-    game.proben = 10.0*game.dist*bigger +0.5
-    game.probe = coord(game.quadrant.i*QUADSIZE + game.sector.i, 
-                       game.quadrant.j*QUADSIZE + game.sector.j)
-    game.probec = copy.copy(game.quadrant)
     schedule(FDSPROB, 0.01) # Time to move one sector
     prout(_("Ensign Chekov-  \"The deep space probe is launched, Captain.\""))
     game.ididit = True
@@ -4362,7 +4368,7 @@ def mayday():
     game.sector.invalidate()
     for m in range(1, 5+1):
         w = game.base.scatter() 
-       if VALID_SECTOR(w.i,w.j) and game.quad[w.i][w.j]==IHDOT:
+       if w.valid_sector() and game.quad[w.i][w.j]==IHDOT:
            # found one -- finish up 
             game.sector = w
            break
@@ -4465,7 +4471,7 @@ def abandon():
            game.quad[game.sector.i][game.sector.j] = IHDOT
            for l in range(QUADSIZE):
                game.sector = game.base.scatter()
-               if VALID_SECTOR(game.sector.i, game.sector.j) and \
+               if game.sector.valid_sector() and \
                        game.quad[game.sector.i][game.sector.j] == IHDOT:
                     break
            if l < QUADSIZE+1:
@@ -5061,7 +5067,7 @@ def lrscan(silent):
         if not silent:
             proutn(" ")
         for y in range(game.quadrant.j-1, game.quadrant.j+2):
-           if not VALID_QUADRANT(x, y):
+           if not coord(x, y).valid_quadrant():
                 if not silent:
                     proutn("  -1")
            else:
@@ -5080,7 +5086,6 @@ def damagereport():
     "Damage report."
     jdam = False
     scanner.chew()
-
     for i in range(NDEVICES):
        if damaged(i):
            if not jdam:
@@ -5287,10 +5292,10 @@ def eta():
            w2.j = 0
        else:
            w2.j=QUADSIZE-1
-    if not VALID_QUADRANT(w1.i, w1.j) or not VALID_SECTOR(w2.i, w2.j):
+    if not w1.valid_quadrant() or not w2.valid_sector():
        huh()
        return
-    game.dist = math.sqrt((w1.j-game.quadrant.j+(w2.j-game.sector.j)/(QUADSIZE*1.0))**2+
+    dist = math.sqrt((w1.j-game.quadrant.j+(w2.j-game.sector.j)/(QUADSIZE*1.0))**2+
                (w1.i-game.quadrant.i+(w2.i-game.sector.i)/(QUADSIZE*1.0))**2)
     wfl = False
     if prompt:
@@ -5302,7 +5307,7 @@ def eta():
            ttime = scanner.real
            if ttime > game.state.date:
                ttime -= game.state.date # Actually a star date
-            twarp=(math.floor(math.sqrt((10.0*game.dist)/ttime)*10.0)+1.0)/10.0
+            twarp=(math.floor(math.sqrt((10.0*dist)/ttime)*10.0)+1.0)/10.0
             if ttime <= 1e-10 or twarp > 10:
                prout(_("We'll never make it, sir."))
                scanner.chew()
@@ -5323,7 +5328,7 @@ def eta():
     while True:
        scanner.chew()
        ttime = (10.0*game.dist)/twarp**2
-       tpower = game.dist*twarp*twarp*twarp*(game.shldup+1)
+       tpower = dist*twarp*twarp*twarp*(game.shldup+1)
        if tpower >= game.energy:
            prout(_("Insufficient energy, sir."))
            if not game.shldup or tpower > game.energy*2.0:
@@ -6040,10 +6045,8 @@ def helpme():
             proutn(_("   current directory or to "))
             proutn(SSTDOC)
             prout(".\"")
-            #
             # This used to continue: "You need to find SST.DOC and put 
             # it in the current directory."
-            # 
             return
     while True:
         linebuf = fp.readline()
@@ -6115,7 +6118,7 @@ def makemoves():
            if game.ididit:
                hitme = True
        elif cmd == "MOVE":             # move under warp
-           warp(False)
+           warp(course=None, involuntary=False)
        elif cmd == "SHIELDS":          # shields
            doshield(shraise=False)
            if game.ididit: