Fix enemy displacement ob torpedo hit.
[super-star-trek.git] / sst.py
diff --git a/sst.py b/sst.py
index 61650143d5b9b56baaedb0a53c57b7cd848620d7..5200f24619fce2d5e8b677a872934df1baf050cb 100755 (executable)
--- a/sst.py
+++ b/sst.py
@@ -15,6 +15,9 @@ from __future__ import print_function, division
 # Runs under Python 2 an Python 3. Preserve this property!
 # SPDX-License-Identifier: BSD-2-clause
 
 # Runs under Python 2 an Python 3. Preserve this property!
 # SPDX-License-Identifier: BSD-2-clause
 
+# pylint: disable=line-too-long,superfluous-parens,too-many-lines,invalid-name,missing-function-docstring,missing-class-docstring,multiple-statements,too-many-branches,too-many-statements,too-many-locals,too-many-nested-blocks,too-many-return-statements,too-many-instance-attributes,global-statement,no-else-break,no-else-return,no-else-continue,too-few-public-methods,too-many-boolean-expressions,consider-using-f-string,consider-using-enumerate,consider-using-with,unspecified-encoding
+
+# pylint: disable=multiple-imports
 import os, sys, math, curses, time, pickle, copy, gettext, getpass
 import getopt, socket, locale
 import codecs
 import os, sys, math, curses, time, pickle, copy, gettext, getpass
 import getopt, socket, locale
 import codecs
@@ -22,6 +25,7 @@ import codecs
 # This import only works on Unixes.  The intention is to enable
 # Ctrl-P, Ctrl-N, and friends in Cmd.
 try:
 # This import only works on Unixes.  The intention is to enable
 # Ctrl-P, Ctrl-N, and friends in Cmd.
 try:
+    # pylint: disable=unused-import
     import readline
 except ImportError:
     pass
     import readline
 except ImportError:
     pass
@@ -138,9 +142,9 @@ class Coord:
         self.i = x     # Row
         self.j = y     # Column
     def valid_quadrant(self):
         self.i = x     # Row
         self.j = y     # Column
     def valid_quadrant(self):
-        return self.i >= 0 and self.i < GALSIZE and self.j >= 0 and self.j < GALSIZE
+        return (self.i is not None) and (self.j is not None) and (self.i >= 0) and (self.i < GALSIZE) and (self.j >= 0) and (self.j < GALSIZE)
     def valid_sector(self):
     def valid_sector(self):
-        return self.i >= 0 and self.i < QUADSIZE and self.j >= 0 and self.j < QUADSIZE
+        return (self.i is not None) and (self.j is not None) and (self.i >= 0) and (self.i < QUADSIZE) and (self.j >= 0) and (self.j < QUADSIZE)
     def invalidate(self):
         self.i = self.j = None
     def __eq__(self, other):
     def invalidate(self):
         self.i = self.j = None
     def __eq__(self, other):
@@ -540,9 +544,9 @@ def tryexit(enemy, look, irun):
     iq.i = game.quadrant.i+(look.i+(QUADSIZE-1))//QUADSIZE - 1
     iq.j = game.quadrant.j+(look.j+(QUADSIZE-1))//QUADSIZE - 1
     if not welcoming(iq):
     iq.i = game.quadrant.i+(look.i+(QUADSIZE-1))//QUADSIZE - 1
     iq.j = game.quadrant.j+(look.j+(QUADSIZE-1))//QUADSIZE - 1
     if not welcoming(iq):
-        return False
+        return []
     if enemy.type == 'R':
     if enemy.type == 'R':
-        return False # Romulans cannot escape!
+        return [] # Romulans cannot escape!
     if not irun:
         # avoid intruding on another commander's territory
         if enemy.type == 'C':
     if not irun:
         # avoid intruding on another commander's territory
         if enemy.type == 'C':
@@ -673,10 +677,8 @@ def movebaddy(enemy):
     nsteps = abs(int(motion))
     if motion > 0 and nsteps > mdist:
         nsteps = mdist # don't overshoot
     nsteps = abs(int(motion))
     if motion > 0 and nsteps > mdist:
         nsteps = mdist # don't overshoot
-    if nsteps > QUADSIZE:
-        nsteps = QUADSIZE # This shouldn't be necessary
-    if nsteps < 1:
-        nsteps = 1 # This shouldn't be necessary
+    nsteps = min(nsteps, QUADSIZE) # This shouldn't be necessary
+    nsteps = max(nsteps, 1) # This shouldn't be necessary
     if game.idebug:
         proutn("NSTEPS = %d:" % nsteps)
     # Compute preferred values of delta X and Y
     if game.idebug:
         proutn("NSTEPS = %d:" % nsteps)
     # Compute preferred values of delta X and Y
@@ -722,7 +724,7 @@ def movebaddy(enemy):
             elif (game.options & OPTION_RAMMING) and game.quad[look.i][look.j] != '.':
                 # See if enemy should ram ship
                 if game.quad[look.i][look.j] == game.ship and \
             elif (game.options & OPTION_RAMMING) and game.quad[look.i][look.j] != '.':
                 # See if enemy should ram ship
                 if game.quad[look.i][look.j] == game.ship and \
-                    (enemy.type == 'C' or enemy.type == 'S'):
+                    enemy.type in ('C', 'S'):
                     collision(rammed=True, enemy=enemy)
                     return []
                 if krawli != m.i and m.j != 0:
                     collision(rammed=True, enemy=enemy)
                     return []
                 if krawli != m.i and m.j != 0:
@@ -1246,7 +1248,6 @@ def collision(rammed, enemy):
         damagereport()
     else:
         finish(FWON)
         damagereport()
     else:
         finish(FWON)
-    return
 
 def torpedo(origin, bearing, dispersion, number, nburst):
     "Let a photon torpedo fly"
 
 def torpedo(origin, bearing, dispersion, number, nburst):
     "Let a photon torpedo fly"
@@ -1326,12 +1327,12 @@ def torpedo(origin, bearing, dispersion, number, nburst):
                         deadkl(w, iquad, w)
                         return None
                     proutn(crmena(True, iquad, "sector", w))
                         deadkl(w, iquad, w)
                         return None
                     proutn(crmena(True, iquad, "sector", w))
-                    displacement = course(track.bearing+rnd.real(-2.4, 2.4), distance=2**0.5)
+                    displacement = course(track.bearing+rnd.real(-2.4, 2.4), distance=2**0.5, origin=w)
                     displacement.nexttok()
                     bumpto = displacement.sector()
                     if not bumpto.valid_sector():
                         prout(_(" damaged but not destroyed."))
                     displacement.nexttok()
                     bumpto = displacement.sector()
                     if not bumpto.valid_sector():
                         prout(_(" damaged but not destroyed."))
-                        return
+                        return None
                     if game.quad[bumpto.i][bumpto.j] == ' ':
                         prout(_(" buffeted into black hole."))
                         deadkl(w, iquad, bumpto)
                     if game.quad[bumpto.i][bumpto.j] == ' ':
                         prout(_(" buffeted into black hole."))
                         deadkl(w, iquad, bumpto)
@@ -1586,8 +1587,7 @@ def attack(torps_ok):
             propor = pfac * game.shield
             if game.condition == "docked":
                 propor *= 2.1
             propor = pfac * game.shield
             if game.condition == "docked":
                 propor *= 2.1
-            if propor < 0.1:
-                propor = 0.1
+            propor = max(propor, 0.1)
             hitsh = propor*chgfac*hit+1.0
             absorb = 0.8*hitsh
             if absorb > game.shield:
             hitsh = propor*chgfac*hit+1.0
             absorb = 0.8*hitsh
             if absorb > game.shield:
@@ -1902,6 +1902,7 @@ def hittem(hits):
                 return
             continue
         else: # decide whether or not to emasculate klingon
                 return
             continue
         else: # decide whether or not to emasculate klingon
+            # pylint: disable=chained-comparison
             if kpow > 0 and rnd.withprob(0.9) and kpow <= rnd.real(0.4, 0.8)*kpini:
                 prout(_("***Mr. Spock-  \"Captain, the vessel at Sector %s")%w)
                 prout(_("   has just lost its firepower.\""))
             if kpow > 0 and rnd.withprob(0.9) and kpow <= rnd.real(0.4, 0.8)*kpini:
                 prout(_("***Mr. Spock-  \"Captain, the vessel at Sector %s")%w)
                 prout(_("   has just lost its firepower.\""))
@@ -2517,7 +2518,7 @@ def events():
         elif evcode == FCDBAS: # Commander succeeds in destroying base
             if evcode == FCDBAS:
                 unschedule(FCDBAS)
         elif evcode == FCDBAS: # Commander succeeds in destroying base
             if evcode == FCDBAS:
                 unschedule(FCDBAS)
-                if not game.state.baseq() \
+                if not game.state.baseq \
                        or not game.state.galaxy[game.battle.i][game.battle.j].starbase:
                     game.battle.invalidate()
                     continue
                        or not game.state.galaxy[game.battle.i][game.battle.j].starbase:
                     game.battle.invalidate()
                     continue
@@ -2563,7 +2564,7 @@ def events():
                 pdest.charted = True
             game.probe.moves -= 1 # One less to travel
             if game.probe.arrived() and game.isarmed and pdest.stars:
                 pdest.charted = True
             game.probe.moves -= 1 # One less to travel
             if game.probe.arrived() and game.isarmed and pdest.stars:
-                supernova(game.probe)                # fire in the hole!
+                supernova(game.probe.quadrant())                # fire in the hole!
                 unschedule(FDSPROB)
                 if game.state.galaxy[pquad.i][pquad.j].supernova:
                     return
                 unschedule(FDSPROB)
                 if game.state.galaxy[pquad.i][pquad.j].supernova:
                     return
@@ -2873,7 +2874,7 @@ def supernova(w):
             proutn("=== Super nova here?")
             if ja():
                 nq = game.quadrant
             proutn("=== Super nova here?")
             if ja():
                 nq = game.quadrant
-    if not nq == game.quadrant or game.justin:
+    if nq != game.quadrant or game.justin:
         # it isn't here, or we just entered (treat as enroute)
         if communicating():
             skip(1)
         # it isn't here, or we just entered (treat as enroute)
         if communicating():
             skip(1)
@@ -2936,7 +2937,7 @@ def supernova(w):
     if game.quadrant == nq or communicating():
         game.state.galaxy[nq.i][nq.j].supernova = True
     # If supernova destroys last Klingons give special message
     if game.quadrant == nq or communicating():
         game.state.galaxy[nq.i][nq.j].supernova = True
     # If supernova destroys last Klingons give special message
-    if game.unwon()==0 and not nq == game.quadrant:
+    if game.unwon()==0 and nq != game.quadrant:
         skip(2)
         if w is None:
             prout(_("Lucky you!"))
         skip(2)
         if w is None:
             prout(_("Lucky you!"))
@@ -3699,7 +3700,7 @@ def tracktorpedo(w, step, i, n, iquad):
             else:
                 skip(1)
                 proutn(_("Torpedo track- "))
             else:
                 skip(1)
                 proutn(_("Torpedo track- "))
-        elif step==4 or step==9:
+        elif step in {4, 9}:
             skip(1)
         proutn("%s   " % w)
     else:
             skip(1)
         proutn("%s   " % w)
     else:
@@ -3707,7 +3708,7 @@ def tracktorpedo(w, step, i, n, iquad):
             if i != 0 and step == 1:
                 drawmaps(2)
                 time.sleep(0.4)
             if i != 0 and step == 1:
                 drawmaps(2)
                 time.sleep(0.4)
-            if (iquad=='.') or (iquad==' '):
+            if iquad in {'.', ' '}:
                 put_srscan_sym(w, '+')
                 #sound(step*10)
                 #time.sleep(0.1)
                 put_srscan_sym(w, '+')
                 #sound(step*10)
                 #time.sleep(0.1)
@@ -3867,7 +3868,7 @@ def imove(icourse=None, noattack=False):
         if game.iscloaked:
             # We can't be tractor beamed if cloaked,
             # so move the event into the future
         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)))
+            postpone(FTBEAM, game.optime + expran(1.5*game.intime/len(game.state.kcmdr)))
         else:
             trbeam = True
             game.condition = "red"
         else:
             trbeam = True
             game.condition = "red"
@@ -3901,7 +3902,6 @@ def imove(icourse=None, noattack=False):
     newcnd()
     drawmaps(0)
     setwnd(message_window)
     newcnd()
     drawmaps(0)
     setwnd(message_window)
-    return
 
 def dock(verbose):
     "Dock our ship at a starbase."
 
 def dock(verbose):
     "Dock our ship at a starbase."
@@ -3912,7 +3912,10 @@ def dock(verbose):
     if game.inorbit:
         prout(_("You must first leave standard orbit."))
         return
     if game.inorbit:
         prout(_("You must first leave standard orbit."))
         return
-    if game.base is None or abs(game.sector.i-game.base.i) > 1 or abs(game.sector.j-game.base.j) > 1:
+    if game.base is None or not game.base.valid_sector():
+        prout(_("No starbase available for docking in this quadrant."))
+        return
+    if (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(crmshp() + _(" not adjacent to base."))
         return
     if game.iscloaked:
@@ -4566,7 +4569,8 @@ def mayday():
             break
         prout(_("fails."))
         textcolor(DEFAULT)
             break
         prout(_("fails."))
         textcolor(DEFAULT)
-        curses.delay_output(500)
+        if game.options & OPTION_CURSES:
+            curses.delay_output(500)
     if m > 3:
         game.quad[game.sector.i][game.sector.j]='?'
         game.alive = False
     if m > 3:
         game.quad[game.sector.i][game.sector.j]='?'
         game.alive = False
@@ -5161,6 +5165,7 @@ def attackreport(curt):
 def report():
     # report on general game status
     scanner.chew()
 def report():
     # report on general game status
     scanner.chew()
+    # pylint: disable=consider-using-ternary
     s1 = (game.thawed and _("thawed ")) or ""
     s2 = {1:"short", 2:"medium", 4:"long"}[game.length]
     s3 = (None, _("novice"), _("fair"),
     s1 = (game.thawed and _("thawed ")) or ""
     s2 = {1:"short", 2:"medium", 4:"long"}[game.length]
     s3 = (None, _("novice"), _("fair"),
@@ -5513,8 +5518,7 @@ def eta():
                 prout(_("We'll never make it, sir."))
                 scanner.chew()
                 return
                 prout(_("We'll never make it, sir."))
                 scanner.chew()
                 return
-            if twarp < 1.0:
-                twarp = 1.0
+            twarp = max(twarp, 1.0)
             break
         scanner.chew()
         proutn(_("Warp factor? "))
             break
         scanner.chew()
         proutn(_("Warp factor? "))
@@ -5793,13 +5797,11 @@ def setup():
     # Position ordinary Klingon Battle Cruisers
     krem = game.inkling
     klumper = 0.25*game.skill*(9.0-game.length)+1.0
     # Position ordinary Klingon Battle Cruisers
     krem = game.inkling
     klumper = 0.25*game.skill*(9.0-game.length)+1.0
-    if klumper > MAXKLQUAD:
-        klumper = MAXKLQUAD
+    klumper = min(klumper, MAXKLQUAD)
     while True:
         r = rnd.real()
         klump = int((1.0 - r*r)*klumper)
     while True:
         r = rnd.real()
         klump = int((1.0 - r*r)*klumper)
-        if klump > krem:
-            klump = krem
+        klump = min(klump, krem)
         krem -= klump
         while True:
             w = randplace(GALSIZE)
         krem -= klump
         while True:
             w = randplace(GALSIZE)
@@ -6665,7 +6667,7 @@ def debugme():
             proutn(legends[i])
             if is_scheduled(i):
                 proutn("%.2f" % (scheduled(i)-game.state.date))
             proutn(legends[i])
             if is_scheduled(i):
                 proutn("%.2f" % (scheduled(i)-game.state.date))
-                if i == FENSLV or i == FREPRO:
+                if i in {FENSLV, FREPRO}:
                     ev = findevent(i)
                     proutn(" in %s" % ev.quadrant)
             else:
                     ev = findevent(i)
                     proutn(" in %s" % ev.quadrant)
             else:
@@ -6678,7 +6680,7 @@ def debugme():
                 scanner.chew()
             elif key == "IHREAL":
                 ev = schedule(i, scanner.real)
                 scanner.chew()
             elif key == "IHREAL":
                 ev = schedule(i, scanner.real)
-                if i == FENSLV or i == FREPRO:
+                if i in {FENSLV, FREPRO}:
                     scanner.chew()
                     proutn("In quadrant- ")
                     key = scanner.nexttok()
                     scanner.chew()
                     proutn("In quadrant- ")
                     key = scanner.nexttok()
@@ -6720,14 +6722,17 @@ if __name__ == '__main__':
         replay = False
         for (switch, val) in options:
             if switch == '-r':
         replay = False
         for (switch, val) in options:
             if switch == '-r':
+                # pylint: disable=raise-missing-from
                 try:
                     replayfp = open(val, "r")
                 except IOError:
                     sys.stderr.write("sst: can't open replay file %s\n" % val)
                     raise SystemExit(1)
                 try:
                     replayfp = open(val, "r")
                 except IOError:
                     sys.stderr.write("sst: can't open replay file %s\n" % val)
                     raise SystemExit(1)
+                # pylint: disable=raise-missing-from
                 try:
                     line = replayfp.readline().strip()
                     (leader, __, seed) = line.split()
                 try:
                     line = replayfp.readline().strip()
                     (leader, __, seed) = line.split()
+                    # pylint: disable=eval-used
                     seed = eval(seed)
                     line = replayfp.readline().strip()
                     arguments += line.split()[2:]
                     seed = eval(seed)
                     line = replayfp.readline().strip()
                     arguments += line.split()[2:]