3 sst.py -- Super Star Trek 2K
5 SST2K is a Python translation of a C translation of a FORTRAN
6 original dating back to 1973. Beautiful Python it is not, but it
7 works. Translation by Eric S. Raymond; original game by David Matuszek
8 and Paul Reynolds, with modifications by Don Smith, Tom Almy,
9 Stas Sergeev, and Eric S. Raymond.
11 See the doc/HACKING file in the distribution for designers notes and advice
12 on how to modify (and how not to modify!) this code.
14 import os, sys, math, curses, time, readline, cPickle, random, copy, gettext, getpass
18 docpath = (".", "../doc", "/usr/share/doc/sst")
20 def _(str): return gettext.gettext(str)
22 GALSIZE = 8 # Galaxy size in quadrants
23 NINHAB = (GALSIZE * GALSIZE / 2) # Number of inhabited worlds
24 MAXUNINHAB = 10 # Maximum uninhabited worlds
25 QUADSIZE = 10 # Quadrant size in sectors
26 BASEMIN = 2 # Minimum starbases
27 BASEMAX = (GALSIZE * GALSIZE / 12) # Maximum starbases
28 MAXKLGAME = 127 # Maximum Klingons per game
29 MAXKLQUAD = 9 # Maximum Klingons per quadrant
30 FULLCREW = 428 # Crew size. BSD Trek was 387, that's wrong
31 FOREVER = 1e30 # Time for the indefinite future
32 MAXBURST = 3 # Max # of torps you can launch in one turn
33 MINCMDR = 10 # Minimum number of Klingon commanders
34 DOCKFAC = 0.25 # Repair faster when docked
35 PHASEFAC = 2.0 # Unclear what this is, it was in the C version
55 class TrekError(Exception):
58 class JumpOut(Exception):
62 def __init__(self, x=None, y=None):
65 def valid_quadrant(self):
66 return self.i>=0 and self.i<GALSIZE and self.j>=0 and self.j<GALSIZE
67 def valid_sector(self):
68 return self.i>=0 and self.i<QUADSIZE and self.j>=0 and self.j<QUADSIZE
70 self.i = self.j = None
72 return self.i != None and self.j != None
73 def __eq__(self, other):
74 return other != None and self.i == other.i and self.j == other.j
75 def __ne__(self, other):
76 return other == None or self.i != other.i or self.j != other.j
77 def __add__(self, other):
78 return Coord(self.i+other.i, self.j+other.j)
79 def __sub__(self, other):
80 return Coord(self.i-other.i, self.j-other.j)
81 def __mul__(self, other):
82 return Coord(self.i*other, self.j*other)
83 def __rmul__(self, other):
84 return Coord(self.i*other, self.j*other)
85 def __div__(self, other):
86 return Coord(self.i/other, self.j/other)
87 def __mod__(self, other):
88 return Coord(self.i % other, self.j % other)
89 def __rdiv__(self, other):
90 return Coord(self.i/other, self.j/other)
91 def roundtogrid(self):
92 return Coord(int(round(self.i)), int(round(self.j)))
93 def distance(self, other=None):
94 if not other: other = Coord(0, 0)
95 return math.sqrt((self.i - other.i)**2 + (self.j - other.j)**2)
97 return 1.90985*math.atan2(self.j, self.i)
103 s.i = self.i / abs(self.i)
107 s.j = self.j / abs(self.j)
110 #print "Location %s -> %s" % (self, (self / QUADSIZE).roundtogrid())
111 return self.roundtogrid() / QUADSIZE
113 return self.roundtogrid() % QUADSIZE
116 s.i = self.i + randrange(-1, 2)
117 s.j = self.j + randrange(-1, 2)
120 if self.i == None or self.j == None:
122 return "%s - %s" % (self.i+1, self.j+1)
127 self.name = None # string-valued if inhabited
128 self.quadrant = Coord() # quadrant located
129 self.pclass = None # could be ""M", "N", "O", or "destroyed"
130 self.crystals = "absent"# could be "mined", "present", "absent"
131 self.known = "unknown" # could be "unknown", "known", "shuttle_down"
132 self.inhabited = False # is it inhabites?
140 self.starbase = False
143 self.supernova = False
145 self.status = "secure" # Could be "secure", "distressed", "enslaved"
153 def fill2d(size, fillfun):
154 "Fill an empty list in 2D."
156 for i in range(size):
158 for j in range(size):
159 lst[i].append(fillfun(i, j))
164 self.snap = False # snapshot taken
165 self.crew = 0 # crew complement
166 self.remkl = 0 # remaining klingons
167 self.nscrem = 0 # remaining super commanders
168 self.starkl = 0 # destroyed stars
169 self.basekl = 0 # destroyed bases
170 self.nromrem = 0 # Romulans remaining
171 self.nplankl = 0 # destroyed uninhabited planets
172 self.nworldkl = 0 # destroyed inhabited planets
173 self.planets = [] # Planet information
174 self.date = 0.0 # stardate
175 self.remres = 0 # remaining resources
176 self.remtime = 0 # remaining time
177 self.baseq = [] # Base quadrant coordinates
178 self.kcmdr = [] # Commander quadrant coordinates
179 self.kscmdr = Coord() # Supercommander quadrant coordinates
181 self.galaxy = fill2d(GALSIZE, lambda i_unused, j_unused: Quadrant())
183 self.chart = fill2d(GALSIZE, lambda i_unused, j_unused: Page())
187 self.date = None # A real number
188 self.quadrant = None # A coord structure
191 OPTION_ALL = 0xffffffff
192 OPTION_TTY = 0x00000001 # old interface
193 OPTION_CURSES = 0x00000002 # new interface
194 OPTION_IOMODES = 0x00000003 # cover both interfaces
195 OPTION_PLANETS = 0x00000004 # planets and mining
196 OPTION_THOLIAN = 0x00000008 # Tholians and their webs (UT 1979 version)
197 OPTION_THINGY = 0x00000010 # Space Thingy can shoot back (Stas, 2005)
198 OPTION_PROBE = 0x00000020 # deep-space probes (DECUS version, 1980)
199 OPTION_SHOWME = 0x00000040 # bracket Enterprise in chart
200 OPTION_RAMMING = 0x00000080 # enemies may ram Enterprise (Almy)
201 OPTION_MVBADDY = 0x00000100 # more enemies can move (Almy)
202 OPTION_BLKHOLE = 0x00000200 # black hole may timewarp you (Stas, 2005)
203 OPTION_BASE = 0x00000400 # bases have good shields (Stas, 2005)
204 OPTION_WORLDS = 0x00000800 # logic for inhabited worlds (ESR, 2006)
205 OPTION_AUTOSCAN = 0x00001000 # automatic LRSCAN before CHART (ESR, 2006)
206 OPTION_PLAIN = 0x01000000 # user chose plain game
207 OPTION_ALMY = 0x02000000 # user chose Almy variant
208 OPTION_COLOR = 0x04000000 # enable color display (experimental, ESR, 2010)
227 NDEVICES= 16 # Number of devices
236 def damaged(dev): return (game.damage[dev] != 0.0)
237 def communicating(): return not damaged(DRADIO) or game.condition=="docked"
239 # Define future events
240 FSPY = 0 # Spy event happens always (no future[] entry)
241 # can cause SC to tractor beam Enterprise
242 FSNOVA = 1 # Supernova
243 FTBEAM = 2 # Commander tractor beams Enterprise
244 FSNAP = 3 # Snapshot for time warp
245 FBATTAK = 4 # Commander attacks base
246 FCDBAS = 5 # Commander destroys base
247 FSCMOVE = 6 # Supercommander moves (might attack base)
248 FSCDBAS = 7 # Supercommander destroys base
249 FDSPROB = 8 # Move deep space probe
250 FDISTR = 9 # Emit distress call from an inhabited world
251 FENSLV = 10 # Inhabited word is enslaved */
252 FREPRO = 11 # Klingons build a ship in an enslaved system
255 # Abstract out the event handling -- underlying data structures will change
256 # when we implement stateful events
257 def findevent(evtype): return game.future[evtype]
260 def __init__(self, type=None, loc=None, power=None):
262 self.location = Coord()
265 self.power = power # enemy energy level
266 game.enemies.append(self)
268 motion = (loc != self.location)
269 if self.location.i is not None and self.location.j is not None:
272 game.quad[self.location.i][self.location.j] = '#'
274 game.quad[self.location.i][self.location.j] = '.'
276 self.location = copy.copy(loc)
277 game.quad[self.location.i][self.location.j] = self.type
278 self.kdist = self.kavgd = (game.sector - loc).distance()
280 self.location = Coord()
281 self.kdist = self.kavgd = None
282 game.enemies.remove(self)
285 return "<%s,%s.%f>" % (self.type, self.location, self.power) # For debugging
289 self.options = None # Game options
290 self.state = Snapshot() # A snapshot structure
291 self.snapsht = Snapshot() # Last snapshot taken for time-travel purposes
292 self.quad = None # contents of our quadrant
293 self.damage = [0.0] * NDEVICES # damage encountered
294 self.future = [] # future events
298 self.future.append(Event())
299 self.passwd = None; # Self Destruct password
301 self.quadrant = None # where we are in the large
302 self.sector = None # where we are in the small
303 self.tholian = None # Tholian enemy object
304 self.base = None # position of base in current quadrant
305 self.battle = None # base coordinates being attacked
306 self.plnet = None # location of planet in quadrant
307 self.gamewon = False # Finished!
308 self.ididit = False # action taken -- allows enemy to attack
309 self.alive = False # we are alive (not killed)
310 self.justin = False # just entered quadrant
311 self.shldup = False # shields are up
312 self.shldchg = False # shield is changing (affects efficiency)
313 self.iscate = False # super commander is here
314 self.ientesc = False # attempted escape from supercommander
315 self.resting = False # rest time
316 self.icraft = False # Kirk in Galileo
317 self.landed = False # party on planet (true), on ship (false)
318 self.alldone = False # game is now finished
319 self.neutz = False # Romulan Neutral Zone
320 self.isarmed = False # probe is armed
321 self.inorbit = False # orbiting a planet
322 self.imine = False # mining
323 self.icrystl = False # dilithium crystals aboard
324 self.iseenit = False # seen base attack report
325 self.thawed = False # thawed game
326 self.condition = None # "green", "yellow", "red", "docked", "dead"
327 self.iscraft = None # "onship", "offship", "removed"
328 self.skill = None # Player skill level
329 self.inkling = 0 # initial number of klingons
330 self.inbase = 0 # initial number of bases
331 self.incom = 0 # initial number of commanders
332 self.inscom = 0 # initial number of commanders
333 self.inrom = 0 # initial number of commanders
334 self.instar = 0 # initial stars
335 self.intorps = 0 # initial/max torpedoes
336 self.torps = 0 # number of torpedoes
337 self.ship = 0 # ship type -- 'E' is Enterprise
338 self.abandoned = 0 # count of crew abandoned in space
339 self.length = 0 # length of game
340 self.klhere = 0 # klingons here
341 self.casual = 0 # causalties
342 self.nhelp = 0 # calls for help
343 self.nkinks = 0 # count of energy-barrier crossings
344 self.iplnet = None # planet # in quadrant
345 self.inplan = 0 # initial planets
346 self.irhere = 0 # Romulans in quadrant
347 self.isatb = 0 # =2 if super commander is attacking base
348 self.tourn = None # tournament number
349 self.nprobes = 0 # number of probes available
350 self.inresor = 0.0 # initial resources
351 self.intime = 0.0 # initial time
352 self.inenrg = 0.0 # initial/max energy
353 self.inshld = 0.0 # initial/max shield
354 self.inlsr = 0.0 # initial life support resources
355 self.indate = 0.0 # initial date
356 self.energy = 0.0 # energy level
357 self.shield = 0.0 # shield level
358 self.warpfac = 0.0 # warp speed
359 self.lsupres = 0.0 # life support reserves
360 self.optime = 0.0 # time taken by current operation
361 self.damfac = 0.0 # damage factor
362 self.lastchart = 0.0 # time star chart was last updated
363 self.cryprob = 0.0 # probability that crystal will work
364 self.probe = None # object holding probe course info
365 self.height = 0.0 # height of orbit around planet
366 self.idebug = False # Debugging instrumentation enabled?
368 # Stas thinks this should be (C expression):
369 # game.state.remkl + len(game.state.kcmdr) > 0 ?
370 # game.state.remres/(game.state.remkl + 4*len(game.state.kcmdr)) : 99
371 # He says the existing expression is prone to divide-by-zero errors
372 # after killing the last klingon when score is shown -- perhaps also
373 # if the only remaining klingon is SCOM.
374 game.state.remtime = game.state.remres/(game.state.remkl + 4*len(game.state.kcmdr))
400 return random.random() < p
402 def randrange(*args):
403 return random.randrange(*args)
408 v *= args[0] # from [0, args[0])
410 v = args[0] + v*(args[1]-args[0]) # from [args[0], args[1])
413 # Code from ai.c begins here
416 "Would this quadrant welcome another Klingon?"
417 return iq.valid_Quadrant() and \
418 not game.state.galaxy[iq.i][iq.j].supernova and \
419 game.state.galaxy[iq.i][iq.j].klingons < MAXKLQUAD
421 def tryexit(enemy, look, irun):
422 "A bad guy attempts to bug out."
424 iq.i = game.quadrant.i+(look.i+(QUADSIZE-1))/QUADSIZE - 1
425 iq.j = game.quadrant.j+(look.j+(QUADSIZE-1))/QUADSIZE - 1
426 if not welcoming(iq):
428 if enemy.type == 'R':
429 return False; # Romulans cannot escape!
431 # avoid intruding on another commander's territory
432 if enemy.type == 'C':
433 if iq in game.state.kcmdr:
435 # refuse to leave if currently attacking starbase
436 if game.battle == game.quadrant:
438 # don't leave if over 1000 units of energy
439 if enemy.power > 1000.0:
441 # emit escape message and move out of quadrant.
442 # we know this if either short or long range sensors are working
443 if not damaged(DSRSENS) or not damaged(DLRSENS) or \
444 game.condition == "docked":
445 prout(crmena(True, enemy.type, "sector", enemy.location) + \
446 (_(" escapes to Quadrant %s (and regains strength).") % iq))
447 # handle local matters related to escape
450 if game.condition != "docked":
452 # Handle global matters related to escape
453 game.state.galaxy[game.quadrant.i][game.quadrant.j].klingons -= 1
454 game.state.galaxy[iq.i][iq.j].klingons += 1
459 schedule(FSCMOVE, 0.2777)
463 for cmdr in game.state.kcmdr:
464 if cmdr == game.quadrant:
465 game.state.kcmdr.append(iq)
467 return True; # success
469 # The bad-guy movement algorithm:
471 # 1. Enterprise has "force" based on condition of phaser and photon torpedoes.
472 # If both are operating full strength, force is 1000. If both are damaged,
473 # force is -1000. Having shields down subtracts an additional 1000.
475 # 2. Enemy has forces equal to the energy of the attacker plus
476 # 100*(K+R) + 500*(C+S) - 400 for novice through good levels OR
477 # 346*K + 400*R + 500*(C+S) - 400 for expert and emeritus.
479 # Attacker Initial energy levels (nominal):
480 # Klingon Romulan Commander Super-Commander
481 # Novice 400 700 1200
483 # Good 450 800 1300 1750
484 # Expert 475 850 1350 1875
485 # Emeritus 500 900 1400 2000
486 # VARIANCE 75 200 200 200
488 # Enemy vessels only move prior to their attack. In Novice - Good games
489 # only commanders move. In Expert games, all enemy vessels move if there
490 # is a commander present. In Emeritus games all enemy vessels move.
492 # 3. If Enterprise is not docked, an aggressive action is taken if enemy
493 # forces are 1000 greater than Enterprise.
495 # Agressive action on average cuts the distance between the ship and
496 # the enemy to 1/4 the original.
498 # 4. At lower energy advantage, movement units are proportional to the
499 # advantage with a 650 advantage being to hold ground, 800 to move forward
500 # 1, 950 for two, 150 for back 4, etc. Variance of 100.
502 # If docked, is reduced by roughly 1.75*game.skill, generally forcing a
503 # retreat, especially at high skill levels.
505 # 5. Motion is limited to skill level, except for SC hi-tailing it out.
507 def movebaddy(enemy):
508 "Tactical movement for the bad guys."
509 goto = Coord(); look = Coord()
511 # This should probably be just (game.quadrant in game.state.kcmdr) + (game.state.kscmdr==game.quadrant)
512 if game.skill >= SKILL_EXPERT:
513 nbaddys = (((game.quadrant in game.state.kcmdr)*2 + (game.state.kscmdr==game.quadrant)*2+game.klhere*1.23+game.irhere*1.5)/2.0)
515 nbaddys = (game.quadrant in game.state.kcmdr) + (game.state.kscmdr==game.quadrant)
517 mdist = int(dist1 + 0.5); # Nearest integer distance
518 # If SC, check with spy to see if should hi-tail it
519 if enemy.type=='S' and \
520 (enemy.power <= 500.0 or (game.condition=="docked" and not damaged(DPHOTON))):
524 # decide whether to advance, retreat, or hold position
525 forces = enemy.power+100.0*len(game.enemies)+400*(nbaddys-1)
527 forces += 1000; # Good for enemy if shield is down!
528 if not damaged(DPHASER) or not damaged(DPHOTON):
529 if damaged(DPHASER): # phasers damaged
532 forces -= 0.2*(game.energy - 2500.0)
533 if damaged(DPHOTON): # photon torpedoes damaged
536 forces -= 50.0*game.torps
538 # phasers and photon tubes both out!
541 if forces <= 1000.0 and game.condition != "docked": # Typical situation
542 motion = ((forces + randreal(200))/150.0) - 5.0
544 if forces > 1000.0: # Very strong -- move in for kill
545 motion = (1.0 - randreal())**2 * dist1 + 1.0
546 if game.condition=="docked" and (game.options & OPTION_BASE): # protected by base -- back off !
547 motion -= game.skill*(2.0-randreal()**2)
549 proutn("=== MOTION = %d, FORCES = %1.2f, " % (motion, forces))
550 # don't move if no motion
553 # Limit motion according to skill
554 if abs(motion) > game.skill:
559 # calculate preferred number of steps
560 nsteps = abs(int(motion))
561 if motion > 0 and nsteps > mdist:
562 nsteps = mdist; # don't overshoot
563 if nsteps > QUADSIZE:
564 nsteps = QUADSIZE; # This shouldn't be necessary
566 nsteps = 1; # This shouldn't be necessary
568 proutn("NSTEPS = %d:" % nsteps)
569 # Compute preferred values of delta X and Y
570 m = game.sector - enemy.location
571 if 2.0 * abs(m.i) < abs(m.j):
573 if 2.0 * abs(m.j) < abs(game.sector.i-enemy.location.i):
575 m = (motion * m).sgn()
576 goto = enemy.location
578 for ll in range(nsteps):
580 proutn(" %d" % (ll+1))
581 # Check if preferred position available
592 attempts = 0; # Settle mysterious hang problem
593 while attempts < 20 and not success:
595 if look.i < 0 or look.i >= QUADSIZE:
596 if motion < 0 and tryexit(enemy, look, irun):
598 if krawli == m.i or m.j == 0:
600 look.i = goto.i + krawli
602 elif look.j < 0 or look.j >= QUADSIZE:
603 if motion < 0 and tryexit(enemy, look, irun):
605 if krawlj == m.j or m.i == 0:
607 look.j = goto.j + krawlj
609 elif (game.options & OPTION_RAMMING) and game.quad[look.i][look.j] != '.':
610 # See if enemy should ram ship
611 if game.quad[look.i][look.j] == game.ship and \
612 (enemy.type == 'C' or enemy.type == 'S'):
613 collision(rammed=True, enemy=enemy)
615 if krawli != m.i and m.j != 0:
616 look.i = goto.i + krawli
618 elif krawlj != m.j and m.i != 0:
619 look.j = goto.j + krawlj
622 break; # we have failed
634 if not damaged(DSRSENS) or game.condition == "docked":
635 proutn(_("*** %s from Sector %s") % (cramen(enemy.type), enemy.location))
636 if enemy.kdist < dist1:
637 proutn(_(" advances to "))
639 proutn(_(" retreats to "))
640 prout("Sector %s." % goto)
643 "Sequence Klingon tactical movement."
646 # Figure out which Klingon is the commander (or Supercommander)
648 if game.quadrant in game.state.kcmdr:
649 for enemy in game.enemies:
650 if enemy.type == 'C':
652 if game.state.kscmdr==game.quadrant:
653 for enemy in game.enemies:
654 if enemy.type == 'S':
657 # If skill level is high, move other Klingons and Romulans too!
658 # Move these last so they can base their actions on what the
660 if game.skill >= SKILL_EXPERT and (game.options & OPTION_MVBADDY):
661 for enemy in game.enemies:
662 if enemy.type in ('K', 'R'):
664 game.enemies.sort(lambda x, y: cmp(x.kdist, y.kdist))
666 def movescom(iq, avoid):
667 "Commander movement helper."
668 # Avoid quadrants with bases if we want to avoid Enterprise
669 if not welcoming(iq) or (avoid and iq in game.state.baseq):
671 if game.justin and not game.iscate:
674 game.state.galaxy[game.state.kscmdr.i][game.state.kscmdr.j].klingons -= 1
675 game.state.kscmdr = iq
676 game.state.galaxy[game.state.kscmdr.i][game.state.kscmdr.j].klingons += 1
677 if game.state.kscmdr==game.quadrant:
678 # SC has scooted, remove him from current quadrant
683 for enemy in game.enemies:
684 if enemy.type == 'S':
688 if game.condition != "docked":
690 game.enemies.sort(lambda x, y: cmp(x.kdist, y.kdist))
691 # check for a helpful planet
692 for i in range(game.inplan):
693 if game.state.planets[i].quadrant == game.state.kscmdr and \
694 game.state.planets[i].crystals == "present":
696 game.state.planets[i].pclass = "destroyed"
697 game.state.galaxy[game.state.kscmdr.i][game.state.kscmdr.j].planet = None
700 prout(_("Lt. Uhura- \"Captain, Starfleet Intelligence reports"))
701 proutn(_(" a planet in Quadrant %s has been destroyed") % game.state.kscmdr)
702 prout(_(" by the Super-commander.\""))
704 return True; # looks good!
706 def supercommander():
707 "Move the Super Commander."
708 iq = Coord(); sc = Coord(); ibq = Coord(); idelta = Coord()
711 prout("== SUPERCOMMANDER")
712 # Decide on being active or passive
713 avoid = ((game.incom - len(game.state.kcmdr) + game.inkling - game.state.remkl)/(game.state.date+0.01-game.indate) < 0.1*game.skill*(game.skill+1.0) or \
714 (game.state.date-game.indate) < 3.0)
715 if not game.iscate and avoid:
716 # compute move away from Enterprise
717 idelta = game.state.kscmdr-game.quadrant
718 if idelta.distance() > 2.0:
720 idelta.i = game.state.kscmdr.j-game.quadrant.j
721 idelta.j = game.quadrant.i-game.state.kscmdr.i
723 # compute distances to starbases
724 if not game.state.baseq:
728 sc = game.state.kscmdr
729 for base in game.state.baseq:
730 basetbl.append((i, (base - sc).distance()))
731 if game.state.baseq > 1:
732 basetbl.sort(lambda x, y: cmp(x[1], y[1]))
733 # look for nearest base without a commander, no Enterprise, and
734 # without too many Klingons, and not already under attack.
735 ifindit = iwhichb = 0
736 for (i2, base) in enumerate(game.state.baseq):
737 i = basetbl[i2][0]; # bug in original had it not finding nearest
738 if base==game.quadrant or base==game.battle or not welcoming(base):
740 # if there is a commander, and no other base is appropriate,
741 # we will take the one with the commander
742 for cmdr in game.state.kcmdr:
743 if base == cmdr and ifindit != 2:
747 else: # no commander -- use this one
752 return # Nothing suitable -- wait until next time
753 ibq = game.state.baseq[iwhichb]
754 # decide how to move toward base
755 idelta = ibq - game.state.kscmdr
756 # Maximum movement is 1 quadrant in either or both axes
757 idelta = idelta.sgn()
758 # try moving in both x and y directions
759 # there was what looked like a bug in the Almy C code here,
760 # but it might be this translation is just wrong.
761 iq = game.state.kscmdr + idelta
762 if not movescom(iq, avoid):
763 # failed -- try some other maneuvers
764 if idelta.i==0 or idelta.j==0:
767 iq.j = game.state.kscmdr.j + 1
768 if not movescom(iq, avoid):
769 iq.j = game.state.kscmdr.j - 1
772 iq.i = game.state.kscmdr.i + 1
773 if not movescom(iq, avoid):
774 iq.i = game.state.kscmdr.i - 1
777 # try moving just in x or y
778 iq.j = game.state.kscmdr.j
779 if not movescom(iq, avoid):
780 iq.j = game.state.kscmdr.j + idelta.j
781 iq.i = game.state.kscmdr.i
784 if len(game.state.baseq) == 0:
787 for ibq in game.state.baseq:
788 if ibq == game.state.kscmdr and game.state.kscmdr == game.battle:
791 return # no, don't attack base!
794 schedule(FSCDBAS, randreal(1.0, 3.0))
795 if is_scheduled(FCDBAS):
796 postpone(FSCDBAS, scheduled(FCDBAS)-game.state.date)
797 if not communicating():
801 prout(_("Lt. Uhura- \"Captain, the starbase in Quadrant %s") \
803 prout(_(" reports that it is under attack from the Klingon Super-commander."))
804 proutn(_(" It can survive until stardate %d.\"") \
805 % int(scheduled(FSCDBAS)))
808 prout(_("Mr. Spock- \"Captain, shall we cancel the rest period?\""))
812 game.optime = 0.0; # actually finished
814 # Check for intelligence report
815 if not game.idebug and \
817 (not communicating()) or \
818 not game.state.galaxy[game.state.kscmdr.i][game.state.kscmdr.j].charted):
821 prout(_("Lt. Uhura- \"Captain, Starfleet Intelligence reports"))
822 proutn(_(" the Super-commander is in Quadrant %s,") % game.state.kscmdr)
827 if not game.tholian or game.justin:
830 if game.tholian.location.i == 0 and game.tholian.location.j == 0:
831 tid.i = 0; tid.j = QUADSIZE-1
832 elif game.tholian.location.i == 0 and game.tholian.location.j == QUADSIZE-1:
833 tid.i = QUADSIZE-1; tid.j = QUADSIZE-1
834 elif game.tholian.location.i == QUADSIZE-1 and game.tholian.location.j == QUADSIZE-1:
835 tid.i = QUADSIZE-1; tid.j = 0
836 elif game.tholian.location.i == QUADSIZE-1 and game.tholian.location.j == 0:
839 # something is wrong!
840 game.tholian.move(None)
841 prout("***Internal error: Tholian in a bad spot.")
843 # do nothing if we are blocked
844 if game.quad[tid.i][tid.j] not in ('.', '#'):
846 here = copy.copy(game.tholian.location)
847 delta = (tid - game.tholian.location).sgn()
849 while here.i != tid.i:
851 if game.quad[here.i][here.j]=='.':
852 game.tholian.move(here)
854 while here.j != tid.j:
856 if game.quad[here.i][here.j]=='.':
857 game.tholian.move(here)
858 # check to see if all holes plugged
859 for i in range(QUADSIZE):
860 if game.quad[0][i]!='#' and game.quad[0][i]!='T':
862 if game.quad[QUADSIZE-1][i]!='#' and game.quad[QUADSIZE-1][i]!='T':
864 if game.quad[i][0]!='#' and game.quad[i][0]!='T':
866 if game.quad[i][QUADSIZE-1]!='#' and game.quad[i][QUADSIZE-1]!='T':
868 # All plugged up -- Tholian splits
869 game.quad[game.tholian.location.i][game.tholian.location.j]='#'
871 prout(crmena(True, 'T', "sector", game.tholian) + _(" completes web."))
872 game.tholian.move(None)
875 # Code from battle.c begins here
877 def doshield(shraise):
878 "Change shield status."
886 if scanner.sees("transfer"):
890 prout(_("Shields damaged and down."))
892 if scanner.sees("up"):
894 elif scanner.sees("down"):
897 proutn(_("Do you wish to change shield energy? "))
900 elif damaged(DSHIELD):
901 prout(_("Shields damaged and down."))
904 proutn(_("Shields are up. Do you want them down? "))
911 proutn(_("Shields are down. Do you want them up? "))
917 if action == "SHUP": # raise shields
919 prout(_("Shields already up."))
923 if game.condition != "docked":
925 prout(_("Shields raised."))
928 prout(_("Shields raising uses up last of energy."))
933 elif action == "SHDN":
935 prout(_("Shields already down."))
939 prout(_("Shields lowered."))
942 elif action == "NRG":
943 while scanner.next() != "IHREAL":
945 proutn(_("Energy to transfer to shields- "))
950 if nrg > game.energy:
951 prout(_("Insufficient ship energy."))
954 if game.shield+nrg >= game.inshld:
955 prout(_("Shield energy maximized."))
956 if game.shield+nrg > game.inshld:
957 prout(_("Excess energy requested returned to ship energy"))
958 game.energy -= game.inshld-game.shield
959 game.shield = game.inshld
961 if nrg < 0.0 and game.energy-nrg > game.inenrg:
962 # Prevent shield drain loophole
964 prout(_("Engineering to bridge--"))
965 prout(_(" Scott here. Power circuit problem, Captain."))
966 prout(_(" I can't drain the shields."))
969 if game.shield+nrg < 0:
970 prout(_("All shield energy transferred to ship."))
971 game.energy += game.shield
974 proutn(_("Scotty- \""))
976 prout(_("Transferring energy to shields.\""))
978 prout(_("Draining energy from shields.\""))
984 "Choose a device to damage, at random."
986 105, # DSRSENS: short range scanners 10.5%
987 105, # DLRSENS: long range scanners 10.5%
988 120, # DPHASER: phasers 12.0%
989 120, # DPHOTON: photon torpedoes 12.0%
990 25, # DLIFSUP: life support 2.5%
991 65, # DWARPEN: warp drive 6.5%
992 70, # DIMPULS: impulse engines 6.5%
993 145, # DSHIELD: deflector shields 14.5%
994 30, # DRADIO: subspace radio 3.0%
995 45, # DSHUTTL: shuttle 4.5%
996 15, # DCOMPTR: computer 1.5%
997 20, # NAVCOMP: navigation system 2.0%
998 75, # DTRANSP: transporter 7.5%
999 20, # DSHCTRL: high-speed shield controller 2.0%
1000 10, # DDRAY: death ray 1.0%
1001 30, # DDSP: deep-space probes 3.0%
1003 assert(sum(weights) == 1000)
1004 idx = randrange(1000)
1006 for (i, w) in enumerate(weights):
1010 return None; # we should never get here
1012 def collision(rammed, enemy):
1013 "Collision handling fot rammong events."
1014 prouts(_("***RED ALERT! RED ALERT!"))
1016 prout(_("***COLLISION IMMINENT."))
1020 hardness = {'R':1.5, 'C':2.0, 'S':2.5, 'T':0.5, '?':4.0}.get(enemy.type, 1.0)
1022 proutn(_(" rammed by "))
1025 proutn(crmena(False, enemy.type, "sector", enemy.location))
1027 proutn(_(" (original position)"))
1029 deadkl(enemy.location, enemy.type, game.sector)
1030 proutn("***" + crmshp() + " heavily damaged.")
1031 icas = randrange(10, 30)
1032 prout(_("***Sickbay reports %d casualties") % icas)
1034 game.state.crew -= icas
1035 # In the pre-SST2K version, all devices got equiprobably damaged,
1036 # which was silly. Instead, pick up to half the devices at
1037 # random according to our weighting table,
1038 ncrits = randrange(NDEVICES/2)
1042 if game.damage[dev] < 0:
1044 extradm = (10.0*hardness*randreal()+1.0)*game.damfac
1045 # Damage for at least time of travel!
1046 game.damage[dev] += game.optime + extradm
1048 prout(_("***Shields are down."))
1049 if game.state.remkl + len(game.state.kcmdr) + game.state.nscrem:
1056 def torpedo(origin, bearing, dispersion, number, nburst):
1057 "Let a photon torpedo fly"
1058 if not damaged(DSRSENS) or game.condition=="docked":
1059 setwnd(srscan_window)
1061 setwnd(message_window)
1062 ac = bearing + 0.25*dispersion # dispersion is a random variable
1063 bullseye = (15.0 - bearing)*0.5235988
1064 track = course(bearing=ac, distance=QUADSIZE, origin=cartesian(origin))
1065 bumpto = Coord(0, 0)
1066 # Loop to move a single torpedo
1067 setwnd(message_window)
1068 for step in range(1, QUADSIZE*2):
1069 if not track.next(): break
1071 if not w.valid_sector():
1073 iquad=game.quad[w.i][w.j]
1074 tracktorpedo(origin, w, step, number, nburst, iquad)
1078 if not damaged(DSRSENS) or game.condition == "docked":
1079 skip(1); # start new line after text track
1080 if iquad in ('E', 'F'): # Hit our ship
1082 prout(_("Torpedo hits %s.") % crmshp())
1083 hit = 700.0 + randreal(100) - \
1084 1000.0 * (w-origin).distance() * math.fabs(math.sin(bullseye-track.angle))
1085 newcnd(); # we're blown out of dock
1086 if game.landed or game.condition=="docked":
1087 return hit # Cheat if on a planet
1088 # In the C/FORTRAN version, dispersion was 2.5 radians, which
1089 # is 143 degrees, which is almost exactly 4.8 clockface units
1090 displacement = course(track.bearing+randreal(-2.4,2.4), distance=2**0.5)
1092 bumpto = displacement.sector()
1093 if not bumpto.valid_sector():
1095 if game.quad[bumpto.i][bumpto.j]==' ':
1098 if game.quad[bumpto.i][bumpto.j]!='.':
1099 # can't move into object
1101 game.sector = bumpto
1103 game.quad[w.i][w.j]='.'
1104 game.quad[bumpto.i][bumpto.j]=iquad
1105 prout(_(" displaced by blast to Sector %s ") % bumpto)
1106 for enemy in game.enemies:
1107 enemy.kdist = enemy.kavgd = (game.sector-enemy.location).distance()
1108 game.enemies.sort(lambda x, y: cmp(x.kdist, y.kdist))
1110 elif iquad in ('C', 'S', 'R', 'K'): # Hit a regular enemy
1112 if iquad in ('C', 'S') and withprob(0.05):
1113 prout(crmena(True, iquad, "sector", w) + _(" uses anti-photon device;"))
1114 prout(_(" torpedo neutralized."))
1116 for enemy in game.enemies:
1117 if w == enemy.location:
1119 kp = math.fabs(enemy.power)
1120 h1 = 700.0 + randrange(100) - \
1121 1000.0 * (w-origin).distance() * math.fabs(math.sin(bullseye-track.angle))
1129 if enemy.power == 0:
1132 proutn(crmena(True, iquad, "sector", w))
1133 displacement = course(track.bearing+randreal(-2.4,2.4), distance=2**0.5)
1135 bumpto = displacement.sector()
1136 if not bumpto.valid_sector():
1137 prout(_(" damaged but not destroyed."))
1139 if game.quad[bumpto.i][bumpto.j] == ' ':
1140 prout(_(" buffeted into black hole."))
1141 deadkl(w, iquad, bumpto)
1142 if game.quad[bumpto.i][bumpto.j] != '.':
1143 prout(_(" damaged but not destroyed."))
1145 prout(_(" damaged-- displaced by blast to Sector %s ")%bumpto)
1146 enemy.location = bumpto
1147 game.quad[w.i][w.j]='.'
1148 game.quad[bumpto.i][bumpto.j]=iquad
1149 for enemy in game.enemies:
1150 enemy.kdist = enemy.kavgd = (game.sector-enemy.location).distance()
1151 game.enemies.sort(lambda x, y: cmp(x.kdist, y.kdist))
1153 elif iquad == 'B': # Hit a base
1155 prout(_("***STARBASE DESTROYED.."))
1156 game.state.baseq = filter(lambda x: x != game.quadrant, game.state.baseq)
1157 game.quad[w.i][w.j]='.'
1158 game.base.invalidate()
1159 game.state.galaxy[game.quadrant.i][game.quadrant.j].starbase -= 1
1160 game.state.chart[game.quadrant.i][game.quadrant.j].starbase -= 1
1161 game.state.basekl += 1
1164 elif iquad == 'P': # Hit a planet
1165 prout(crmena(True, iquad, "sector", w) + _(" destroyed."))
1166 game.state.nplankl += 1
1167 game.state.galaxy[game.quadrant.i][game.quadrant.j].planet = None
1168 game.iplnet.pclass = "destroyed"
1170 game.plnet.invalidate()
1171 game.quad[w.i][w.j] = '.'
1173 # captain perishes on planet
1176 elif iquad == '@': # Hit an inhabited world -- very bad!
1177 prout(crmena(True, iquad, "sector", w) + _(" destroyed."))
1178 game.state.nworldkl += 1
1179 game.state.galaxy[game.quadrant.i][game.quadrant.j].planet = None
1180 game.iplnet.pclass = "destroyed"
1182 game.plnet.invalidate()
1183 game.quad[w.i][w.j] = '.'
1185 # captain perishes on planet
1187 prout(_("The torpedo destroyed an inhabited planet."))
1189 elif iquad == '*': # Hit a star
1193 prout(crmena(True, '*', "sector", w) + _(" unaffected by photon blast."))
1195 elif iquad == '?': # Hit a thingy
1196 if not (game.options & OPTION_THINGY) or withprob(0.3):
1198 prouts(_("AAAAIIIIEEEEEEEEAAAAAAAAUUUUUGGGGGHHHHHHHHHHHH!!!"))
1200 prouts(_(" HACK! HACK! HACK! *CHOKE!* "))
1202 proutn(_("Mr. Spock-"))
1203 prouts(_(" \"Fascinating!\""))
1207 # Stas Sergeev added the possibility that
1208 # you can shove the Thingy and piss it off.
1209 # It then becomes an enemy and may fire at you.
1212 elif iquad == ' ': # Black hole
1214 prout(crmena(True, ' ', "sector", w) + _(" swallows torpedo."))
1216 elif iquad == '#': # hit the web
1218 prout(_("***Torpedo absorbed by Tholian web."))
1220 elif iquad == 'T': # Hit a Tholian
1221 h1 = 700.0 + randrange(100) - \
1222 1000.0 * (w-origin).distance() * math.fabs(math.sin(bullseye-angle))
1225 game.quad[w.i][w.j] = '.'
1230 proutn(crmena(True, 'T', "sector", w))
1232 prout(_(" survives photon blast."))
1234 prout(_(" disappears."))
1235 game.tholian.move(None)
1236 game.quad[w.i][w.j] = '#'
1241 proutn("Don't know how to handle torpedo collision with ")
1242 proutn(crmena(True, iquad, "sector", w))
1247 prout(_("Torpedo missed."))
1251 "Critical-hit resolution."
1252 if hit < (275.0-25.0*game.skill)*randreal(1.0, 1.5):
1254 ncrit = int(1.0 + hit/(500.0+randreal(100)))
1255 proutn(_("***CRITICAL HIT--"))
1256 # Select devices and cause damage
1262 # Cheat to prevent shuttle damage unless on ship
1263 if not (game.damage[j]<0.0 or (j==DSHUTTL and game.iscraft != "onship")):
1266 extradm = (hit*game.damfac)/(ncrit*randreal(75, 100))
1267 game.damage[j] += extradm
1269 for (i, j) in enumerate(cdam):
1271 if skipcount % 3 == 2 and i < len(cdam)-1:
1276 prout(_(" damaged."))
1277 if damaged(DSHIELD) and game.shldup:
1278 prout(_("***Shields knocked down."))
1281 def attack(torps_ok):
1282 # bad guy attacks us
1283 # torps_ok == False forces use of phasers in an attack
1284 # game could be over at this point, check
1287 attempt = False; ihurt = False;
1288 hitmax=0.0; hittot=0.0; chgfac=1.0
1291 prout("=== ATTACK!")
1292 # Tholian gets to move before attacking
1295 # if you have just entered the RNZ, you'll get a warning
1296 if game.neutz: # The one chance not to be attacked
1299 # commanders get a chance to tac-move towards you
1300 if (((game.quadrant in game.state.kcmdr or game.state.kscmdr==game.quadrant) and not game.justin) or game.skill == SKILL_EMERITUS) and torps_ok:
1302 # if no enemies remain after movement, we're done
1303 if len(game.enemies)==0 or (len(game.enemies)==1 and thing == game.quadrant and not thing.angry):
1305 # set up partial hits if attack happens during shield status change
1306 pfac = 1.0/game.inshld
1308 chgfac = 0.25 + randreal(0.5)
1310 # message verbosity control
1311 if game.skill <= SKILL_FAIR:
1313 for enemy in game.enemies:
1315 continue; # too weak to attack
1316 # compute hit strength and diminish shield power
1318 # Increase chance of photon torpedos if docked or enemy energy is low
1319 if game.condition == "docked":
1321 if enemy.power < 500:
1323 if enemy.type=='T' or (enemy.type=='?' and not thing.angry):
1325 # different enemies have different probabilities of throwing a torp
1326 usephasers = not torps_ok or \
1327 (enemy.type == 'K' and r > 0.0005) or \
1328 (enemy.type=='C' and r > 0.015) or \
1329 (enemy.type=='R' and r > 0.3) or \
1330 (enemy.type=='S' and r > 0.07) or \
1331 (enemy.type=='?' and r > 0.05)
1332 if usephasers: # Enemy uses phasers
1333 if game.condition == "docked":
1334 continue; # Don't waste the effort!
1335 attempt = True; # Attempt to attack
1336 dustfac = randreal(0.8, 0.85)
1337 hit = enemy.power*math.pow(dustfac,enemy.kavgd)
1339 else: # Enemy uses photon torpedo
1340 # We should be able to make the bearing() method work here
1341 pcourse = 1.90985*math.atan2(game.sector.j-enemy.location.j, enemy.location.i-game.sector.i)
1343 proutn(_("***TORPEDO INCOMING"))
1344 if not damaged(DSRSENS):
1345 proutn(_(" From ") + crmena(False, enemy.type, where, enemy.location))
1348 dispersion = (randreal()+randreal())*0.5 - 0.5
1349 dispersion += 0.002*enemy.power*dispersion
1350 hit = torpedo(enemy.location, pcourse, dispersion, number=1, nburst=1)
1351 if (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem)==0:
1352 finish(FWON); # Klingons did themselves in!
1353 if game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova or game.alldone:
1354 return # Supernova or finished
1357 # incoming phaser or torpedo, shields may dissipate it
1358 if game.shldup or game.shldchg or game.condition=="docked":
1359 # shields will take hits
1360 propor = pfac * game.shield
1361 if game.condition =="docked":
1365 hitsh = propor*chgfac*hit+1.0
1367 if absorb > game.shield:
1368 absorb = game.shield
1369 game.shield -= absorb
1371 # taking a hit blasts us out of a starbase dock
1372 if game.condition == "docked":
1374 # but the shields may take care of it
1375 if propor > 0.1 and hit < 0.005*game.energy:
1377 # hit from this opponent got through shields, so take damage
1379 proutn(_("%d unit hit") % int(hit))
1380 if (damaged(DSRSENS) and usephasers) or game.skill<=SKILL_FAIR:
1381 proutn(_(" on the ") + crmshp())
1382 if not damaged(DSRSENS) and usephasers:
1383 prout(_(" from ") + crmena(False, enemy.type, where, enemy.location))
1385 # Decide if hit is critical
1391 if game.energy <= 0:
1392 # Returning home upon your shield, not with it...
1395 if not attempt and game.condition == "docked":
1396 prout(_("***Enemies decide against attacking your ship."))
1397 percent = 100.0*pfac*game.shield+0.5
1399 # Shields fully protect ship
1400 proutn(_("Enemy attack reduces shield strength to "))
1402 # Emit message if starship suffered hit(s)
1404 proutn(_("Energy left %2d shields ") % int(game.energy))
1407 elif not damaged(DSHIELD):
1410 proutn(_("damaged, "))
1411 prout(_("%d%%, torpedoes left %d") % (percent, game.torps))
1412 # Check if anyone was hurt
1413 if hitmax >= 200 or hittot >= 500:
1414 icas = randrange(int(hittot * 0.015))
1417 prout(_("Mc Coy- \"Sickbay to bridge. We suffered %d casualties") % icas)
1418 prout(_(" in that last attack.\""))
1420 game.state.crew -= icas
1421 # After attack, reset average distance to enemies
1422 for enemy in game.enemies:
1423 enemy.kavgd = enemy.kdist
1424 game.enemies.sort(lambda x, y: cmp(x.kdist, y.kdist))
1427 def deadkl(w, type, mv):
1428 "Kill a Klingon, Tholian, Romulan, or Thingy."
1429 # Added mv to allow enemy to "move" before dying
1430 proutn(crmena(True, type, "sector", mv))
1431 # Decide what kind of enemy it is and update appropriately
1433 # Chalk up a Romulan
1434 game.state.galaxy[game.quadrant.i][game.quadrant.j].romulans -= 1
1436 game.state.nromrem -= 1
1445 # Killed some type of Klingon
1446 game.state.galaxy[game.quadrant.i][game.quadrant.j].klingons -= 1
1449 game.state.kcmdr.remove(game.quadrant)
1451 if game.state.kcmdr:
1452 schedule(FTBEAM, expran(1.0*game.incom/len(game.state.kcmdr)))
1453 if is_scheduled(FCDBAS) and game.battle == game.quadrant:
1456 game.state.remkl -= 1
1458 game.state.nscrem -= 1
1459 game.state.kscmdr.invalidate()
1464 # For each kind of enemy, finish message to player
1465 prout(_(" destroyed."))
1466 if (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem)==0:
1469 # Remove enemy ship from arrays describing local conditions
1470 for e in game.enemies:
1477 "Return None if target is invalid, otherwise return a course angle."
1478 if not w.valid_sector():
1482 # FIXME: C code this was translated from is wacky -- why the sign reversal?
1483 delta.j = (w.j - game.sector.j);
1484 delta.i = (game.sector.i - w.i);
1485 if delta == Coord(0, 0):
1487 prout(_("Spock- \"Bridge to sickbay. Dr. McCoy,"))
1488 prout(_(" I recommend an immediate review of"))
1489 prout(_(" the Captain's psychological profile.\""))
1492 return delta.bearing()
1495 "Launch photon torpedo salvo."
1498 if damaged(DPHOTON):
1499 prout(_("Photon tubes damaged."))
1503 prout(_("No torpedoes left."))
1506 # First, get torpedo count
1509 if scanner.token == "IHALPHA":
1512 elif scanner.token == "IHEOL" or not scanner.waiting():
1513 prout(_("%d torpedoes left.") % game.torps)
1515 proutn(_("Number of torpedoes to fire- "))
1516 continue # Go back around to get a number
1517 else: # key == "IHREAL"
1519 if n <= 0: # abort command
1524 prout(_("Maximum of %d torpedoes per burst.") % MAXBURST)
1527 scanner.chew() # User requested more torps than available
1528 continue # Go back around
1529 break # All is good, go to next stage
1533 key = scanner.next()
1534 if i==0 and key == "IHEOL":
1535 break; # no coordinate waiting, we will try prompting
1536 if i==1 and key == "IHEOL":
1537 # direct all torpedoes at one target
1539 target.append(target[0])
1540 tcourse.append(tcourse[0])
1543 scanner.push(scanner.token)
1544 target.append(scanner.getcoord())
1545 if target[-1] == None:
1547 tcourse.append(targetcheck(target[-1]))
1548 if tcourse[-1] == None:
1551 if len(target) == 0:
1552 # prompt for each one
1554 proutn(_("Target sector for torpedo number %d- ") % (i+1))
1556 target.append(scanner.getcoord())
1557 if target[-1] == None:
1559 tcourse.append(targetcheck(target[-1]))
1560 if tcourse[-1] == None:
1563 # Loop for moving <n> torpedoes
1565 if game.condition != "docked":
1567 dispersion = (randreal()+randreal())*0.5 -0.5
1568 if math.fabs(dispersion) >= 0.47:
1570 dispersion *= randreal(1.2, 2.2)
1572 prouts(_("***TORPEDO NUMBER %d MISFIRES") % (i+1))
1574 prouts(_("***TORPEDO MISFIRES."))
1577 prout(_(" Remainder of burst aborted."))
1579 prout(_("***Photon tubes damaged by misfire."))
1580 game.damage[DPHOTON] = game.damfac * randreal(1.0, 3.0)
1582 if game.shldup or game.condition == "docked":
1583 dispersion *= 1.0 + 0.0001*game.shield
1584 torpedo(game.sector, tcourse[i], dispersion, number=i, nburst=n)
1585 if game.alldone or game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
1587 if (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem)<=0:
1591 "Check for phasers overheating."
1593 checkburn = (rpow-1500.0)*0.00038
1594 if withprob(checkburn):
1595 prout(_("Weapons officer Sulu- \"Phasers overheated, sir.\""))
1596 game.damage[DPHASER] = game.damfac* randreal(1.0, 2.0) * (1.0+checkburn)
1598 def checkshctrl(rpow):
1599 "Check shield control."
1602 prout(_("Shields lowered."))
1604 # Something bad has happened
1605 prouts(_("***RED ALERT! RED ALERT!"))
1607 hit = rpow*game.shield/game.inshld
1608 game.energy -= rpow+hit*0.8
1609 game.shield -= hit*0.2
1610 if game.energy <= 0.0:
1611 prouts(_("Sulu- \"Captain! Shield malf***********************\""))
1616 prouts(_("Sulu- \"Captain! Shield malfunction! Phaser fire contained!\""))
1618 prout(_("Lt. Uhura- \"Sir, all decks reporting damage.\""))
1619 icas = randrange(int(hit*0.012))
1624 prout(_("McCoy to bridge- \"Severe radiation burns, Jim."))
1625 prout(_(" %d casualties so far.\"") % icas)
1627 game.state.crew -= icas
1629 prout(_("Phaser energy dispersed by shields."))
1630 prout(_("Enemy unaffected."))
1635 "Register a phaser hit on Klingons and Romulans."
1639 for (k, wham) in enumerate(hits):
1642 dustfac = randreal(0.9, 1.0)
1643 hit = wham*math.pow(dustfac,game.enemies[kk].kdist)
1644 kpini = game.enemies[kk].power
1645 kp = math.fabs(kpini)
1646 if PHASEFAC*hit < kp:
1648 if game.enemies[kk].power < 0:
1649 game.enemies[kk].power -= -kp
1651 game.enemies[kk].power -= kp
1652 kpow = game.enemies[kk].power
1653 w = game.enemies[kk].location
1655 if not damaged(DSRSENS):
1657 proutn(_("%d unit hit on ") % int(hit))
1659 proutn(_("Very small hit on "))
1660 ienm = game.quad[w.i][w.j]
1663 proutn(crmena(False, ienm, "sector", w))
1667 if (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem)==0:
1671 kk -= 1 # don't do the increment
1673 else: # decide whether or not to emasculate klingon
1674 if kpow>0 and withprob(0.9) and kpow <= randreal(0.4, 0.8)*kpini:
1675 prout(_("***Mr. Spock- \"Captain, the vessel at Sector %s")%w)
1676 prout(_(" has just lost its firepower.\""))
1677 game.enemies[kk].power = -kpow
1682 "Fire phasers at bad guys."
1684 kz = 0; k = 1; irec=0 # Cheating inhibitor
1685 ifast = False; no = False; itarg = True; msgflag = True; rpow=0
1689 # SR sensors and Computer are needed for automode
1690 if damaged(DSRSENS) or damaged(DCOMPTR):
1692 if game.condition == "docked":
1693 prout(_("Phasers can't be fired through base shields."))
1696 if damaged(DPHASER):
1697 prout(_("Phaser control damaged."))
1701 if damaged(DSHCTRL):
1702 prout(_("High speed shield control damaged."))
1705 if game.energy <= 200.0:
1706 prout(_("Insufficient energy to activate high-speed shield control."))
1709 prout(_("Weapons Officer Sulu- \"High-speed shield control enabled, sir.\""))
1711 # Original code so convoluted, I re-did it all
1712 # (That was Tom Almy talking about the C code, I think -- ESR)
1713 while automode=="NOTSET":
1715 if key == "IHALPHA":
1716 if scanner.sees("manual"):
1717 if len(game.enemies)==0:
1718 prout(_("There is no enemy present to select."))
1721 automode="AUTOMATIC"
1724 key = scanner.next()
1725 elif scanner.sees("automatic"):
1726 if (not itarg) and len(game.enemies) != 0:
1727 automode = "FORCEMAN"
1729 if len(game.enemies)==0:
1730 prout(_("Energy will be expended into space."))
1731 automode = "AUTOMATIC"
1732 key = scanner.next()
1733 elif scanner.sees("no"):
1738 elif key == "IHREAL":
1739 if len(game.enemies)==0:
1740 prout(_("Energy will be expended into space."))
1741 automode = "AUTOMATIC"
1743 automode = "FORCEMAN"
1745 automode = "AUTOMATIC"
1748 if len(game.enemies)==0:
1749 prout(_("Energy will be expended into space."))
1750 automode = "AUTOMATIC"
1752 automode = "FORCEMAN"
1754 proutn(_("Manual or automatic? "))
1759 if automode == "AUTOMATIC":
1760 if key == "IHALPHA" and scanner.sees("no"):
1762 key = scanner.next()
1763 if key != "IHREAL" and len(game.enemies) != 0:
1764 prout(_("Phasers locked on target. Energy available: %.2f")%avail)
1769 for i in range(len(game.enemies)):
1770 irec += math.fabs(game.enemies[i].power)/(PHASEFAC*math.pow(0.90,game.enemies[i].kdist))*randreal(1.01, 1.06) + 1.0
1772 proutn(_("%d units required. ") % irec)
1774 proutn(_("Units to fire= "))
1775 key = scanner.next()
1780 proutn(_("Energy available= %.2f") % avail)
1783 if not rpow > avail:
1790 if key == "IHALPHA" and scanner.sees("no"):
1793 game.energy -= 200; # Go and do it!
1794 if checkshctrl(rpow):
1799 if len(game.enemies):
1802 for i in range(len(game.enemies)):
1806 hits[i] = math.fabs(game.enemies[i].power)/(PHASEFAC*math.pow(0.90,game.enemies[i].kdist))
1807 over = randreal(1.01, 1.06) * hits[i]
1809 powrem -= hits[i] + over
1810 if powrem <= 0 and temp < hits[i]:
1819 if extra > 0 and not game.alldone:
1821 proutn(_("*** Tholian web absorbs "))
1822 if len(game.enemies)>0:
1823 proutn(_("excess "))
1824 prout(_("phaser energy."))
1826 prout(_("%d expended on empty space.") % int(extra))
1827 elif automode == "FORCEMAN":
1830 if damaged(DCOMPTR):
1831 prout(_("Battle computer damaged, manual fire only."))
1834 prouts(_("---WORKING---"))
1836 prout(_("Short-range-sensors-damaged"))
1837 prout(_("Insufficient-data-for-automatic-phaser-fire"))
1838 prout(_("Manual-fire-must-be-used"))
1840 elif automode == "MANUAL":
1842 for k in range(len(game.enemies)):
1843 aim = game.enemies[k].location
1844 ienm = game.quad[aim.i][aim.j]
1846 proutn(_("Energy available= %.2f") % (avail-0.006))
1850 if damaged(DSRSENS) and \
1851 not game.sector.distance(aim)<2**0.5 and ienm in ('C', 'S'):
1852 prout(cramen(ienm) + _(" can't be located without short range scan."))
1855 hits[k] = 0; # prevent overflow -- thanks to Alexei Voitenko
1860 if itarg and k > kz:
1861 irec=(abs(game.enemies[k].power)/(PHASEFAC*math.pow(0.9,game.enemies[k].kdist))) * randreal(1.01, 1.06) + 1.0
1864 if not damaged(DCOMPTR):
1869 proutn(_("units to fire at %s- ") % crmena(False, ienm, "sector", aim))
1870 key = scanner.next()
1871 if key == "IHALPHA" and scanner.sees("no"):
1873 key = scanner.next()
1875 if key == "IHALPHA":
1879 if k==1: # Let me say I'm baffled by this
1882 if scanner.real < 0:
1886 hits[k] = scanner.real
1887 rpow += scanner.real
1888 # If total requested is too much, inform and start over
1890 prout(_("Available energy exceeded -- try again."))
1893 key = scanner.next(); # scan for next value
1896 # zero energy -- abort
1899 if key == "IHALPHA" and scanner.sees("no"):
1904 game.energy -= 200.0
1905 if checkshctrl(rpow):
1909 # Say shield raised or malfunction, if necessary
1916 prout(_("Sulu- \"Sir, the high-speed shield control has malfunctioned . . ."))
1917 prouts(_(" CLICK CLICK POP . . ."))
1918 prout(_(" No response, sir!"))
1921 prout(_("Shields raised."))
1926 # Code from events,c begins here.
1928 # This isn't a real event queue a la BSD Trek yet -- you can only have one
1929 # event of each type active at any given time. Mostly these means we can
1930 # only have one FDISTR/FENSLV/FREPRO sequence going at any given time
1931 # BSD Trek, from which we swiped the idea, can have up to 5.
1933 def unschedule(evtype):
1934 "Remove an event from the schedule."
1935 game.future[evtype].date = FOREVER
1936 return game.future[evtype]
1938 def is_scheduled(evtype):
1939 "Is an event of specified type scheduled."
1940 return game.future[evtype].date != FOREVER
1942 def scheduled(evtype):
1943 "When will this event happen?"
1944 return game.future[evtype].date
1946 def schedule(evtype, offset):
1947 "Schedule an event of specified type."
1948 game.future[evtype].date = game.state.date + offset
1949 return game.future[evtype]
1951 def postpone(evtype, offset):
1952 "Postpone a scheduled event."
1953 game.future[evtype].date += offset
1956 "Rest period is interrupted by event."
1959 proutn(_("Mr. Spock- \"Captain, shall we cancel the rest period?\""))
1961 game.resting = False
1967 "Run through the event queue looking for things to do."
1969 fintim = game.state.date + game.optime; yank=0
1970 ictbeam = False; istract = False
1971 w = Coord(); hold = Coord()
1972 ev = Event(); ev2 = Event()
1974 def tractorbeam(yank):
1975 "Tractor-beaming cases merge here."
1977 game.optime = (10.0/(7.5*7.5))*yank # 7.5 is yank rate (warp 7.5)
1979 prout("***" + crmshp() + _(" caught in long range tractor beam--"))
1980 # If Kirk & Co. screwing around on planet, handle
1981 atover(True) # atover(true) is Grab
1984 if game.icraft: # Caught in Galileo?
1987 # Check to see if shuttle is aboard
1988 if game.iscraft == "offship":
1991 prout(_("Galileo, left on the planet surface, is captured"))
1992 prout(_("by aliens and made into a flying McDonald's."))
1993 game.damage[DSHUTTL] = -10
1994 game.iscraft = "removed"
1996 prout(_("Galileo, left on the planet surface, is well hidden."))
1998 game.quadrant = game.state.kscmdr
2000 game.quadrant = game.state.kcmdr[i]
2001 game.sector = randplace(QUADSIZE)
2002 prout(crmshp() + _(" is pulled to Quadrant %s, Sector %s") \
2003 % (game.quadrant, game.sector))
2005 prout(_("(Remainder of rest/repair period cancelled.)"))
2006 game.resting = False
2008 if not damaged(DSHIELD) and game.shield > 0:
2009 doshield(shraise=True) # raise shields
2010 game.shldchg = False
2012 prout(_("(Shields not currently useable.)"))
2014 # Adjust finish time to time of tractor beaming
2015 fintim = game.state.date+game.optime
2016 attack(torps_ok=False)
2017 if not game.state.kcmdr:
2020 schedule(FTBEAM, game.optime+expran(1.5*game.intime/len(game.state.kcmdr)))
2023 "Code merges here for any commander destroying a starbase."
2024 # Not perfect, but will have to do
2025 # Handle case where base is in same quadrant as starship
2026 if game.battle == game.quadrant:
2027 game.state.chart[game.battle.i][game.battle.j].starbase = False
2028 game.quad[game.base.i][game.base.j] = '.'
2029 game.base.invalidate()
2032 prout(_("Spock- \"Captain, I believe the starbase has been destroyed.\""))
2033 elif game.state.baseq and communicating():
2034 # Get word via subspace radio
2037 prout(_("Lt. Uhura- \"Captain, Starfleet Command reports that"))
2038 proutn(_(" the starbase in Quadrant %s has been destroyed by") % game.battle)
2040 prout(_("the Klingon Super-Commander"))
2042 prout(_("a Klingon Commander"))
2043 game.state.chart[game.battle.i][game.battle.j].starbase = False
2044 # Remove Starbase from galaxy
2045 game.state.galaxy[game.battle.i][game.battle.j].starbase = False
2046 game.state.baseq = filter(lambda x: x != game.battle, game.state.baseq)
2048 # reinstate a commander's base attack
2052 game.battle.invalidate()
2054 prout("=== EVENTS from %.2f to %.2f:" % (game.state.date, fintim))
2055 for i in range(1, NEVENTS):
2056 if i == FSNOVA: proutn("=== Supernova ")
2057 elif i == FTBEAM: proutn("=== T Beam ")
2058 elif i == FSNAP: proutn("=== Snapshot ")
2059 elif i == FBATTAK: proutn("=== Base Attack ")
2060 elif i == FCDBAS: proutn("=== Base Destroy ")
2061 elif i == FSCMOVE: proutn("=== SC Move ")
2062 elif i == FSCDBAS: proutn("=== SC Base Destroy ")
2063 elif i == FDSPROB: proutn("=== Probe Move ")
2064 elif i == FDISTR: proutn("=== Distress Call ")
2065 elif i == FENSLV: proutn("=== Enslavement ")
2066 elif i == FREPRO: proutn("=== Klingon Build ")
2068 prout("%.2f" % (scheduled(i)))
2071 radio_was_broken = damaged(DRADIO)
2074 # Select earliest extraneous event, evcode==0 if no events
2079 for l in range(1, NEVENTS):
2080 if game.future[l].date < datemin:
2083 prout("== Event %d fires" % evcode)
2084 datemin = game.future[l].date
2085 xtime = datemin-game.state.date
2086 game.state.date = datemin
2087 # Decrement Federation resources and recompute remaining time
2088 game.state.remres -= (game.state.remkl+4*len(game.state.kcmdr))*xtime
2090 if game.state.remtime <=0:
2093 # Any crew left alive?
2094 if game.state.crew <=0:
2097 # Is life support adequate?
2098 if damaged(DLIFSUP) and game.condition != "docked":
2099 if game.lsupres < xtime and game.damage[DLIFSUP] > game.lsupres:
2102 game.lsupres -= xtime
2103 if game.damage[DLIFSUP] <= xtime:
2104 game.lsupres = game.inlsr
2107 if game.condition == "docked":
2109 # Don't fix Deathray here
2110 for l in range(NDEVICES):
2111 if game.damage[l] > 0.0 and l != DDRAY:
2112 if game.damage[l]-repair > 0.0:
2113 game.damage[l] -= repair
2115 game.damage[l] = 0.0
2116 # If radio repaired, update star chart and attack reports
2117 if radio_was_broken and not damaged(DRADIO):
2118 prout(_("Lt. Uhura- \"Captain, the sub-space radio is working and"))
2119 prout(_(" surveillance reports are coming in."))
2121 if not game.iseenit:
2125 prout(_(" The star chart is now up to date.\""))
2127 # Cause extraneous event EVCODE to occur
2128 game.optime -= xtime
2129 if evcode == FSNOVA: # Supernova
2132 schedule(FSNOVA, expran(0.5*game.intime))
2133 if game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
2135 elif evcode == FSPY: # Check with spy to see if SC should tractor beam
2136 if game.state.nscrem == 0 or \
2137 ictbeam or istract or \
2138 game.condition=="docked" or game.isatb==1 or game.iscate:
2140 if game.ientesc or \
2141 (game.energy<2000 and game.torps<4 and game.shield < 1250) or \
2142 (damaged(DPHASER) and (damaged(DPHOTON) or game.torps<4)) or \
2143 (damaged(DSHIELD) and \
2144 (game.energy < 2500 or damaged(DPHASER)) and \
2145 (game.torps < 5 or damaged(DPHOTON))):
2147 istract = ictbeam = True
2148 tractorbeam((game.state.kscmdr-game.quadrant).distance())
2151 elif evcode == FTBEAM: # Tractor beam
2152 if not game.state.kcmdr:
2155 i = randrange(len(game.state.kcmdr))
2156 yank = (game.state.kcmdr[i]-game.quadrant).distance()
2157 if istract or game.condition == "docked" or yank == 0:
2158 # Drats! Have to reschedule
2160 game.optime + expran(1.5*game.intime/len(game.state.kcmdr)))
2164 elif evcode == FSNAP: # Snapshot of the universe (for time warp)
2165 game.snapsht = copy.deepcopy(game.state)
2166 game.state.snap = True
2167 schedule(FSNAP, expran(0.5 * game.intime))
2168 elif evcode == FBATTAK: # Commander attacks starbase
2169 if not game.state.kcmdr or not game.state.baseq:
2175 for ibq in game.state.baseq:
2176 for cmdr in game.state.kcmdr:
2177 if ibq == cmdr and ibq != game.quadrant and ibq != game.state.kscmdr:
2180 # no match found -- try later
2181 schedule(FBATTAK, expran(0.3*game.intime))
2186 # commander + starbase combination found -- launch attack
2188 schedule(FCDBAS, randreal(1.0, 4.0))
2189 if game.isatb: # extra time if SC already attacking
2190 postpone(FCDBAS, scheduled(FSCDBAS)-game.state.date)
2191 game.future[FBATTAK].date = game.future[FCDBAS].date + expran(0.3*game.intime)
2192 game.iseenit = False
2193 if not communicating():
2194 continue # No warning :-(
2198 prout(_("Lt. Uhura- \"Captain, the starbase in Quadrant %s") % game.battle)
2199 prout(_(" reports that it is under attack and that it can"))
2200 prout(_(" hold out only until stardate %d.\"") % (int(scheduled(FCDBAS))))
2203 elif evcode == FSCDBAS: # Supercommander destroys base
2206 if not game.state.galaxy[game.state.kscmdr.i][game.state.kscmdr.j].starbase:
2207 continue # WAS RETURN!
2209 game.battle = game.state.kscmdr
2211 elif evcode == FCDBAS: # Commander succeeds in destroying base
2214 if not game.state.baseq() \
2215 or not game.state.galaxy[game.battle.i][game.battle.j].starbase:
2216 game.battle.invalidate()
2218 # find the lucky pair
2219 for cmdr in game.state.kcmdr:
2220 if cmdr == game.battle:
2223 # No action to take after all
2226 elif evcode == FSCMOVE: # Supercommander moves
2227 schedule(FSCMOVE, 0.2777)
2228 if not game.ientesc and not istract and game.isatb != 1 and \
2229 (not game.iscate or not game.justin):
2231 elif evcode == FDSPROB: # Move deep space probe
2232 schedule(FDSPROB, 0.01)
2233 if not game.probe.next():
2234 if not game.probe.quadrant().valid_quadrant() or \
2235 game.state.galaxy[game.probe.quadrant().i][game.probe.quadrant().j].supernova:
2236 # Left galaxy or ran into supernova
2240 proutn(_("Lt. Uhura- \"The deep space probe "))
2241 if not game.probe.quadrant().valid_quadrant():
2242 prout(_("has left the galaxy.\""))
2244 prout(_("is no longer transmitting.\""))
2250 prout(_("Lt. Uhura- \"The deep space probe is now in Quadrant %s.\"") % game.probe.quadrant())
2251 pdest = game.state.galaxy[game.probe.quadrant().i][game.probe.quadrant().j]
2253 chp = game.state.chart[game.probe.quadrant().i][game.probe.quadrant().j]
2254 chp.klingons = pdest.klingons
2255 chp.starbase = pdest.starbase
2256 chp.stars = pdest.stars
2257 pdest.charted = True
2258 game.probe.moves -= 1 # One less to travel
2259 if game.probe.arrived() and game.isarmed and pdest.stars:
2260 supernova(game.probe) # fire in the hole!
2262 if game.state.galaxy[game.quadrant().i][game.quadrant().j].supernova:
2264 elif evcode == FDISTR: # inhabited system issues distress call
2266 # try a whole bunch of times to find something suitable
2267 for i in range(100):
2268 # need a quadrant which is not the current one,
2269 # which has some stars which are inhabited and
2270 # not already under attack, which is not
2271 # supernova'ed, and which has some Klingons in it
2272 w = randplace(GALSIZE)
2273 q = game.state.galaxy[w.i][w.j]
2274 if not (game.quadrant == w or q.planet == None or \
2275 not q.planet.inhabited or \
2276 q.supernova or q.status!="secure" or q.klingons<=0):
2279 # can't seem to find one; ignore this call
2281 prout("=== Couldn't find location for distress event.")
2283 # got one!! Schedule its enslavement
2284 ev = schedule(FENSLV, expran(game.intime))
2286 q.status = "distressed"
2287 # tell the captain about it if we can
2289 prout(_("Uhura- Captain, %s in Quadrant %s reports it is under attack") \
2291 prout(_("by a Klingon invasion fleet."))
2294 elif evcode == FENSLV: # starsystem is enslaved
2295 ev = unschedule(FENSLV)
2296 # see if current distress call still active
2297 q = game.state.galaxy[ev.quadrant.i][ev.quadrant.j]
2301 q.status = "enslaved"
2303 # play stork and schedule the first baby
2304 ev2 = schedule(FREPRO, expran(2.0 * game.intime))
2305 ev2.quadrant = ev.quadrant
2307 # report the disaster if we can
2309 prout(_("Uhura- We've lost contact with starsystem %s") % \
2311 prout(_("in Quadrant %s.\n") % ev.quadrant)
2312 elif evcode == FREPRO: # Klingon reproduces
2313 # If we ever switch to a real event queue, we'll need to
2314 # explicitly retrieve and restore the x and y.
2315 ev = schedule(FREPRO, expran(1.0 * game.intime))
2316 # see if current distress call still active
2317 q = game.state.galaxy[ev.quadrant.i][ev.quadrant.j]
2321 if game.state.remkl >=MAXKLGAME:
2322 continue # full right now
2323 # reproduce one Klingon
2326 if game.klhere >= MAXKLQUAD:
2328 # this quadrant not ok, pick an adjacent one
2329 for m.i in range(w.i - 1, w.i + 2):
2330 for m.j in range(w.j - 1, w.j + 2):
2331 if not m.valid_quadrant():
2333 q = game.state.galaxy[m.i][m.j]
2334 # check for this quad ok (not full & no snova)
2335 if q.klingons >= MAXKLQUAD or q.supernova:
2339 continue # search for eligible quadrant failed
2343 game.state.remkl += 1
2345 if game.quadrant == w:
2347 game.enemies.append(newkling())
2348 # recompute time left
2351 if game.quadrant == w:
2352 prout(_("Spock- sensors indicate the Klingons have"))
2353 prout(_("launched a warship from %s.") % q.planet)
2355 prout(_("Uhura- Starfleet reports increased Klingon activity"))
2356 if q.planet != None:
2357 proutn(_("near %s ") % q.planet)
2358 prout(_("in Quadrant %s.") % w)
2364 key = scanner.next()
2367 proutn(_("How long? "))
2372 origTime = delay = scanner.real
2375 if delay >= game.state.remtime or len(game.enemies) != 0:
2376 proutn(_("Are you sure? "))
2379 # Alternate resting periods (events) with attacks
2383 game.resting = False
2384 if not game.resting:
2385 prout(_("%d stardates left.") % int(game.state.remtime))
2387 temp = game.optime = delay
2388 if len(game.enemies):
2389 rtime = randreal(1.0, 2.0)
2393 if game.optime < delay:
2394 attack(torps_ok=False)
2402 # Repair Deathray if long rest at starbase
2403 if origTime-delay >= 9.99 and game.condition == "docked":
2404 game.damage[DDRAY] = 0.0
2405 # leave if quadrant supernovas
2406 if game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
2408 game.resting = False
2413 ncourse = (0.0, 10.5, 12.0, 1.5, 9.0, 0.0, 3.0, 7.5, 6.0, 4.5)
2414 newc = Coord(); neighbor = Coord(); bump = Coord(0, 0)
2416 # Wow! We've supernova'ed
2417 supernova(game.quadrant)
2419 # handle initial nova
2420 game.quad[nov.i][nov.j] = '.'
2421 prout(crmena(False, '*', "sector", nov) + _(" novas."))
2422 game.state.galaxy[game.quadrant.i][game.quadrant.j].stars -= 1
2423 game.state.starkl += 1
2424 # Set up queue to recursively trigger adjacent stars
2430 for offset.i in range(-1, 1+1):
2431 for offset.j in range(-1, 1+1):
2432 if offset.j==0 and offset.i==0:
2434 neighbor = start + offset
2435 if not neighbor.valid_sector():
2437 iquad = game.quad[neighbor.i][neighbor.j]
2438 # Empty space ends reaction
2439 if iquad in ('.', '?', ' ', 'T', '#'):
2441 elif iquad == '*': # Affect another star
2443 # This star supernovas
2444 supernova(game.quadrant)
2447 hits.append(neighbor)
2448 game.state.galaxy[game.quadrant.i][game.quadrant.j].stars -= 1
2449 game.state.starkl += 1
2450 proutn(crmena(True, '*', "sector", neighbor))
2452 game.quad[neighbor.i][neighbor.j] = '.'
2454 elif iquad in ('P', '@'): # Destroy planet
2455 game.state.galaxy[game.quadrant.i][game.quadrant.j].planet = None
2457 game.state.nplankl += 1
2459 game.state.worldkl += 1
2460 prout(crmena(True, 'B', "sector", neighbor) + _(" destroyed."))
2461 game.iplnet.pclass = "destroyed"
2463 game.plnet.invalidate()
2467 game.quad[neighbor.i][neighbor.j] = '.'
2468 elif iquad == 'B': # Destroy base
2469 game.state.galaxy[game.quadrant.i][game.quadrant.j].starbase = False
2470 game.state.baseq = filter(lambda x: x!= game.quadrant, game.state.baseq)
2471 game.base.invalidate()
2472 game.state.basekl += 1
2474 prout(crmena(True, 'B', "sector", neighbor) + _(" destroyed."))
2475 game.quad[neighbor.i][neighbor.j] = '.'
2476 elif iquad in ('E', 'F'): # Buffet ship
2477 prout(_("***Starship buffeted by nova."))
2479 if game.shield >= 2000.0:
2480 game.shield -= 2000.0
2482 diff = 2000.0 - game.shield
2486 prout(_("***Shields knocked out."))
2487 game.damage[DSHIELD] += 0.005*game.damfac*randreal()*diff
2489 game.energy -= 2000.0
2490 if game.energy <= 0:
2493 # add in course nova contributes to kicking starship
2494 bump += (game.sector-hits[mm]).sgn()
2495 elif iquad == 'K': # kill klingon
2496 deadkl(neighbor, iquad, neighbor)
2497 elif iquad in ('C','S','R'): # Damage/destroy big enemies
2498 for ll in range(len(game.enemies)):
2499 if game.enemies[ll].location == neighbor:
2501 game.enemies[ll].power -= 800.0 # If firepower is lost, die
2502 if game.enemies[ll].power <= 0.0:
2503 deadkl(neighbor, iquad, neighbor)
2505 newc = neighbor + neighbor - hits[mm]
2506 proutn(crmena(True, iquad, "sector", neighbor) + _(" damaged"))
2507 if not newc.valid_sector():
2508 # can't leave quadrant
2511 iquad1 = game.quad[newc.i][newc.j]
2513 proutn(_(", blasted into ") + crmena(False, ' ', "sector", newc))
2515 deadkl(neighbor, iquad, newc)
2518 # can't move into something else
2521 proutn(_(", buffeted to Sector %s") % newc)
2522 game.quad[neighbor.i][neighbor.j] = '.'
2523 game.quad[newc.i][newc.j] = iquad
2524 game.enemies[ll].move(newc)
2525 # Starship affected by nova -- kick it away.
2527 direc = ncourse[3*(bump.i+1)+bump.j+2]
2532 scourse = course(bearing=direc, distance=dist)
2533 game.optime = scourse.time(warp=4)
2535 prout(_("Force of nova displaces starship."))
2536 imove(scourse, noattack=True)
2537 game.optime = scourse.time(warp=4)
2541 "Star goes supernova."
2546 # Scheduled supernova -- select star at random.
2549 for nq.i in range(GALSIZE):
2550 for nq.j in range(GALSIZE):
2551 stars += game.state.galaxy[nq.i][nq.j].stars
2553 return # nothing to supernova exists
2554 num = randrange(stars) + 1
2555 for nq.i in range(GALSIZE):
2556 for nq.j in range(GALSIZE):
2557 num -= game.state.galaxy[nq.i][nq.j].stars
2563 proutn("=== Super nova here?")
2566 if not nq == game.quadrant or game.justin:
2567 # it isn't here, or we just entered (treat as enroute)
2570 prout(_("Message from Starfleet Command Stardate %.2f") % game.state.date)
2571 prout(_(" Supernova in Quadrant %s; caution advised.") % nq)
2574 # we are in the quadrant!
2575 num = randrange(game.state.galaxy[nq.i][nq.j].stars) + 1
2576 for ns.i in range(QUADSIZE):
2577 for ns.j in range(QUADSIZE):
2578 if game.quad[ns.i][ns.j]=='*':
2585 prouts(_("***RED ALERT! RED ALERT!"))
2587 prout(_("***Incipient supernova detected at Sector %s") % ns)
2588 if (ns.i-game.sector.i)**2 + (ns.j-game.sector.j)**2 <= 2.1:
2589 proutn(_("Emergency override attempts t"))
2590 prouts("***************")
2594 # destroy any Klingons in supernovaed quadrant
2595 kldead = game.state.galaxy[nq.i][nq.j].klingons
2596 game.state.galaxy[nq.i][nq.j].klingons = 0
2597 if nq == game.state.kscmdr:
2598 # did in the Supercommander!
2599 game.state.nscrem = game.state.kscmdr.i = game.state.kscmdr.j = game.isatb = 0
2603 survivors = filter(lambda w: w != nq, game.state.kcmdr)
2604 comkills = len(game.state.kcmdr) - len(survivors)
2605 game.state.kcmdr = survivors
2607 if not game.state.kcmdr:
2609 game.state.remkl -= kldead
2610 # destroy Romulans and planets in supernovaed quadrant
2611 nrmdead = game.state.galaxy[nq.i][nq.j].romulans
2612 game.state.galaxy[nq.i][nq.j].romulans = 0
2613 game.state.nromrem -= nrmdead
2615 for loop in range(game.inplan):
2616 if game.state.planets[loop].quadrant == nq:
2617 game.state.planets[loop].pclass = "destroyed"
2619 # Destroy any base in supernovaed quadrant
2620 game.state.baseq = filter(lambda x: x != nq, game.state.baseq)
2621 # If starship caused supernova, tally up destruction
2623 game.state.starkl += game.state.galaxy[nq.i][nq.j].stars
2624 game.state.basekl += game.state.galaxy[nq.i][nq.j].starbase
2625 game.state.nplankl += npdead
2626 # mark supernova in galaxy and in star chart
2627 if game.quadrant == nq or communicating():
2628 game.state.galaxy[nq.i][nq.j].supernova = True
2629 # If supernova destroys last Klingons give special message
2630 if (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem)==0 and not nq == game.quadrant:
2633 prout(_("Lucky you!"))
2634 proutn(_("A supernova in %s has just destroyed the last Klingons.") % nq)
2637 # if some Klingons remain, continue or die in supernova
2642 # Code from finish.c ends here.
2645 "Self-destruct maneuver. Finish with a BANG!"
2647 if damaged(DCOMPTR):
2648 prout(_("Computer damaged; cannot execute destruct sequence."))
2650 prouts(_("---WORKING---")); skip(1)
2651 prouts(_("SELF-DESTRUCT-SEQUENCE-ACTIVATED")); skip(1)
2652 prouts(" 10"); skip(1)
2653 prouts(" 9"); skip(1)
2654 prouts(" 8"); skip(1)
2655 prouts(" 7"); skip(1)
2656 prouts(" 6"); skip(1)
2658 prout(_("ENTER-CORRECT-PASSWORD-TO-CONTINUE-"))
2660 prout(_("SELF-DESTRUCT-SEQUENCE-OTHERWISE-"))
2662 prout(_("SELF-DESTRUCT-SEQUENCE-WILL-BE-ABORTED"))
2666 if game.passwd != scanner.token:
2667 prouts(_("PASSWORD-REJECTED;"))
2669 prouts(_("CONTINUITY-EFFECTED"))
2672 prouts(_("PASSWORD-ACCEPTED")); skip(1)
2673 prouts(" 5"); skip(1)
2674 prouts(" 4"); skip(1)
2675 prouts(" 3"); skip(1)
2676 prouts(" 2"); skip(1)
2677 prouts(" 1"); skip(1)
2679 prouts(_("GOODBYE-CRUEL-WORLD"))
2687 prouts(_("********* Entropy of %s maximized *********") % crmshp())
2691 if len(game.enemies) != 0:
2692 whammo = 25.0 * game.energy
2694 while l <= len(game.enemies):
2695 if game.enemies[l].power*game.enemies[l].kdist <= whammo:
2696 deadkl(game.enemies[l].location, game.quad[game.enemies[l].location.i][game.enemies[l].location.j], game.enemies[l].location)
2701 "Compute our rate of kils over time."
2702 elapsed = game.state.date - game.indate
2703 if elapsed == 0: # Avoid divide-by-zero error if calculated on turn 0
2706 starting = (game.inkling + game.incom + game.inscom)
2707 remaining = (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem)
2708 return (starting - remaining)/elapsed
2712 badpt = 5.0*game.state.starkl + \
2714 10.0*game.state.nplankl + \
2715 300*game.state.nworldkl + \
2717 100.0*game.state.basekl +\
2719 if game.ship == 'F':
2721 elif game.ship == None:
2726 # end the game, with appropriate notfications
2730 prout(_("It is stardate %.1f.") % game.state.date)
2732 if ifin == FWON: # Game has been won
2733 if game.state.nromrem != 0:
2734 prout(_("The remaining %d Romulans surrender to Starfleet Command.") %
2737 prout(_("You have smashed the Klingon invasion fleet and saved"))
2738 prout(_("the Federation."))
2743 badpt = 0.0 # Close enough!
2744 # killsPerDate >= RateMax
2745 if game.state.date-game.indate < 5.0 or \
2746 killrate() >= 0.1*game.skill*(game.skill+1.0) + 0.1 + 0.008*badpt:
2748 prout(_("In fact, you have done so well that Starfleet Command"))
2749 if game.skill == SKILL_NOVICE:
2750 prout(_("promotes you one step in rank from \"Novice\" to \"Fair\"."))
2751 elif game.skill == SKILL_FAIR:
2752 prout(_("promotes you one step in rank from \"Fair\" to \"Good\"."))
2753 elif game.skill == SKILL_GOOD:
2754 prout(_("promotes you one step in rank from \"Good\" to \"Expert\"."))
2755 elif game.skill == SKILL_EXPERT:
2756 prout(_("promotes you to Commodore Emeritus."))
2758 prout(_("Now that you think you're really good, try playing"))
2759 prout(_("the \"Emeritus\" game. It will splatter your ego."))
2760 elif game.skill == SKILL_EMERITUS:
2762 proutn(_("Computer- "))
2763 prouts(_("ERROR-ERROR-ERROR-ERROR"))
2765 prouts(_(" YOUR-SKILL-HAS-EXCEEDED-THE-CAPACITY-OF-THIS-PROGRAM"))
2767 prouts(_(" THIS-PROGRAM-MUST-SURVIVE"))
2769 prouts(_(" THIS-PROGRAM-MUST-SURVIVE"))
2771 prouts(_(" THIS-PROGRAM-MUST-SURVIVE"))
2773 prouts(_(" THIS-PROGRAM-MUST?- MUST ? - SUR? ? -? VI"))
2775 prout(_("Now you can retire and write your own Star Trek game!"))
2777 elif game.skill >= SKILL_EXPERT:
2778 if game.thawed and not game.idebug:
2779 prout(_("You cannot get a citation, so..."))
2781 proutn(_("Do you want your Commodore Emeritus Citation printed? "))
2785 # Only grant long life if alive (original didn't!)
2787 prout(_("LIVE LONG AND PROSPER."))
2792 elif ifin == FDEPLETE: # Federation Resources Depleted
2793 prout(_("Your time has run out and the Federation has been"))
2794 prout(_("conquered. Your starship is now Klingon property,"))
2795 prout(_("and you are put on trial as a war criminal. On the"))
2796 proutn(_("basis of your record, you are "))
2797 if (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem)*3.0 > (game.inkling + game.incom + game.inscom):
2798 prout(_("acquitted."))
2800 prout(_("LIVE LONG AND PROSPER."))
2802 prout(_("found guilty and"))
2803 prout(_("sentenced to death by slow torture."))
2807 elif ifin == FLIFESUP:
2808 prout(_("Your life support reserves have run out, and"))
2809 prout(_("you die of thirst, starvation, and asphyxiation."))
2810 prout(_("Your starship is a derelict in space."))
2812 prout(_("Your energy supply is exhausted."))
2814 prout(_("Your starship is a derelict in space."))
2815 elif ifin == FBATTLE:
2816 prout(_("The %s has been destroyed in battle.") % crmshp())
2818 prout(_("Dulce et decorum est pro patria mori."))
2820 prout(_("You have made three attempts to cross the negative energy"))
2821 prout(_("barrier which surrounds the galaxy."))
2823 prout(_("Your navigation is abominable."))
2826 prout(_("Your starship has been destroyed by a nova."))
2827 prout(_("That was a great shot."))
2829 elif ifin == FSNOVAED:
2830 prout(_("The %s has been fried by a supernova.") % crmshp())
2831 prout(_("...Not even cinders remain..."))
2832 elif ifin == FABANDN:
2833 prout(_("You have been captured by the Klingons. If you still"))
2834 prout(_("had a starbase to be returned to, you would have been"))
2835 prout(_("repatriated and given another chance. Since you have"))
2836 prout(_("no starbases, you will be mercilessly tortured to death."))
2837 elif ifin == FDILITHIUM:
2838 prout(_("Your starship is now an expanding cloud of subatomic particles"))
2839 elif ifin == FMATERIALIZE:
2840 prout(_("Starbase was unable to re-materialize your starship."))
2841 prout(_("Sic transit gloria mundi"))
2842 elif ifin == FPHASER:
2843 prout(_("The %s has been cremated by its own phasers.") % crmshp())
2845 prout(_("You and your landing party have been"))
2846 prout(_("converted to energy, disipating through space."))
2847 elif ifin == FMINING:
2848 prout(_("You are left with your landing party on"))
2849 prout(_("a wild jungle planet inhabited by primitive cannibals."))
2851 prout(_("They are very fond of \"Captain Kirk\" soup."))
2853 prout(_("Without your leadership, the %s is destroyed.") % crmshp())
2854 elif ifin == FDPLANET:
2855 prout(_("You and your mining party perish."))
2857 prout(_("That was a great shot."))
2860 prout(_("The Galileo is instantly annihilated by the supernova."))
2861 prout(_("You and your mining party are atomized."))
2863 prout(_("Mr. Spock takes command of the %s and") % crmshp())
2864 prout(_("joins the Romulans, wreaking terror on the Federation."))
2865 elif ifin == FPNOVA:
2866 prout(_("You and your mining party are atomized."))
2868 prout(_("Mr. Spock takes command of the %s and") % crmshp())
2869 prout(_("joins the Romulans, wreaking terror on the Federation."))
2870 elif ifin == FSTRACTOR:
2871 prout(_("The shuttle craft Galileo is also caught,"))
2872 prout(_("and breaks up under the strain."))
2874 prout(_("Your debris is scattered for millions of miles."))
2875 prout(_("Without your leadership, the %s is destroyed.") % crmshp())
2877 prout(_("The mutants attack and kill Spock."))
2878 prout(_("Your ship is captured by Klingons, and"))
2879 prout(_("your crew is put on display in a Klingon zoo."))
2880 elif ifin == FTRIBBLE:
2881 prout(_("Tribbles consume all remaining water,"))
2882 prout(_("food, and oxygen on your ship."))
2884 prout(_("You die of thirst, starvation, and asphyxiation."))
2885 prout(_("Your starship is a derelict in space."))
2887 prout(_("Your ship is drawn to the center of the black hole."))
2888 prout(_("You are crushed into extremely dense matter."))
2890 prout(_("Your last crew member has died."))
2891 if game.ship == 'F':
2893 elif game.ship == 'E':
2896 if (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem) != 0:
2897 goodies = game.state.remres/game.inresor
2898 baddies = (game.state.remkl + 2.0*len(game.state.kcmdr))/(game.inkling+2.0*game.incom)
2899 if goodies/baddies >= randreal(1.0, 1.5):
2900 prout(_("As a result of your actions, a treaty with the Klingon"))
2901 prout(_("Empire has been signed. The terms of the treaty are"))
2902 if goodies/baddies >= randreal(3.0):
2903 prout(_("favorable to the Federation."))
2905 prout(_("Congratulations!"))
2907 prout(_("highly unfavorable to the Federation."))
2909 prout(_("The Federation will be destroyed."))
2911 prout(_("Since you took the last Klingon with you, you are a"))
2912 prout(_("martyr and a hero. Someday maybe they'll erect a"))
2913 prout(_("statue in your memory. Rest in peace, and try not"))
2914 prout(_("to think about pigeons."))
2919 "Compute player's score."
2920 timused = game.state.date - game.indate
2922 if (timused == 0 or (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem) != 0) and timused < 5.0:
2924 perdate = killrate()
2925 ithperd = 500*perdate + 0.5
2928 iwon = 100*game.skill
2929 if game.ship == 'E':
2931 elif game.ship == 'F':
2935 iscore = 10*(game.inkling - game.state.remkl) \
2936 + 50*(game.incom - len(game.state.kcmdr)) \
2938 + 20*(game.inrom - game.state.nromrem) \
2939 + 200*(game.inscom - game.state.nscrem) \
2940 - game.state.nromrem \
2945 prout(_("Your score --"))
2946 if game.inrom - game.state.nromrem:
2947 prout(_("%6d Romulans destroyed %5d") %
2948 (game.inrom - game.state.nromrem, 20*(game.inrom - game.state.nromrem)))
2949 if game.state.nromrem and game.gamewon:
2950 prout(_("%6d Romulans captured %5d") %
2951 (game.state.nromrem, game.state.nromrem))
2952 if game.inkling - game.state.remkl:
2953 prout(_("%6d ordinary Klingons destroyed %5d") %
2954 (game.inkling - game.state.remkl, 10*(game.inkling - game.state.remkl)))
2955 if game.incom - len(game.state.kcmdr):
2956 prout(_("%6d Klingon commanders destroyed %5d") %
2957 (game.incom - len(game.state.kcmdr), 50*(game.incom - len(game.state.kcmdr))))
2958 if game.inscom - game.state.nscrem:
2959 prout(_("%6d Super-Commander destroyed %5d") %
2960 (game.inscom - game.state.nscrem, 200*(game.inscom - game.state.nscrem)))
2962 prout(_("%6.2f Klingons per stardate %5d") %
2964 if game.state.starkl:
2965 prout(_("%6d stars destroyed by your action %5d") %
2966 (game.state.starkl, -5*game.state.starkl))
2967 if game.state.nplankl:
2968 prout(_("%6d planets destroyed by your action %5d") %
2969 (game.state.nplankl, -10*game.state.nplankl))
2970 if (game.options & OPTION_WORLDS) and game.state.nworldkl:
2971 prout(_("%6d inhabited planets destroyed by your action %5d") %
2972 (game.state.nworldkl, -300*game.state.nworldkl))
2973 if game.state.basekl:
2974 prout(_("%6d bases destroyed by your action %5d") %
2975 (game.state.basekl, -100*game.state.basekl))
2977 prout(_("%6d calls for help from starbase %5d") %
2978 (game.nhelp, -45*game.nhelp))
2980 prout(_("%6d casualties incurred %5d") %
2981 (game.casual, -game.casual))
2983 prout(_("%6d crew abandoned in space %5d") %
2984 (game.abandoned, -3*game.abandoned))
2986 prout(_("%6d ship(s) lost or destroyed %5d") %
2987 (klship, -100*klship))
2989 prout(_("Penalty for getting yourself killed -200"))
2991 proutn(_("Bonus for winning "))
2992 if game.skill == SKILL_NOVICE: proutn(_("Novice game "))
2993 elif game.skill == SKILL_FAIR: proutn(_("Fair game "))
2994 elif game.skill == SKILL_GOOD: proutn(_("Good game "))
2995 elif game.skill == SKILL_EXPERT: proutn(_("Expert game "))
2996 elif game.skill == SKILL_EMERITUS: proutn(_("Emeritus game"))
2997 prout(" %5d" % iwon)
2999 prout(_("TOTAL SCORE %5d") % iscore)
3002 "Emit winner's commemmorative plaque."
3005 proutn(_("File or device name for your plaque: "))
3008 fp = open(winner, "w")
3011 prout(_("Invalid name."))
3013 proutn(_("Enter name to go on plaque (up to 30 characters): "))
3015 # The 38 below must be 64 for 132-column paper
3016 nskip = 38 - len(winner)/2
3017 fp.write("\n\n\n\n")
3018 # --------DRAW ENTERPRISE PICTURE.
3019 fp.write(" EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n" )
3020 fp.write(" EEE E : : : E\n" )
3021 fp.write(" EE EEE E : : NCC-1701 : E\n")
3022 fp.write("EEEEEEEEEEEEEEEE EEEEEEEEEEEEEEE : : : E\n")
3023 fp.write(" E EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n")
3024 fp.write(" EEEEEEEEE EEEEEEEEEEEEE E E\n")
3025 fp.write(" EEEEEEE EEEEE E E E E\n")
3026 fp.write(" EEE E E E E\n")
3027 fp.write(" E E E E\n")
3028 fp.write(" EEEEEEEEEEEEE E E\n")
3029 fp.write(" EEE : EEEEEEE EEEEEEEE\n")
3030 fp.write(" :E : EEEE E\n")
3031 fp.write(" .-E -:----- E\n")
3032 fp.write(" :E : E\n")
3033 fp.write(" EE : EEEEEEEE\n")
3034 fp.write(" EEEEEEEEEEEEEEEEEEEEEEE\n")
3036 fp.write(_(" U. S. S. ENTERPRISE\n"))
3037 fp.write("\n\n\n\n")
3038 fp.write(_(" For demonstrating outstanding ability as a starship captain\n"))
3040 fp.write(_(" Starfleet Command bestows to you\n"))
3042 fp.write("%*s%s\n\n" % (nskip, "", winner))
3043 fp.write(_(" the rank of\n\n"))
3044 fp.write(_(" \"Commodore Emeritus\"\n\n"))
3046 if game.skill == SKILL_EXPERT:
3047 fp.write(_(" Expert level\n\n"))
3048 elif game.skill == SKILL_EMERITUS:
3049 fp.write(_("Emeritus level\n\n"))
3051 fp.write(_(" Cheat level\n\n"))
3052 timestring = time.ctime()
3053 fp.write(_(" This day of %.6s %.4s, %.8s\n\n") %
3054 (timestring+4, timestring+20, timestring+11))
3055 fp.write(_(" Your score: %d\n\n") % iscore)
3056 fp.write(_(" Klingons per stardate: %.2f\n") % perdate)
3059 # Code from io.c begins here
3061 rows = linecount = 0 # for paging
3064 fullscreen_window = None
3065 srscan_window = None
3066 report_window = None
3067 status_window = None
3068 lrscan_window = None
3069 message_window = None
3070 prompt_window = None
3075 "for some recent versions of python2, the following enables UTF8"
3076 "for the older ones we probably need to set C locale, and the python3"
3077 "has no problems at all"
3078 if sys.version_info[0] < 3:
3080 locale.setlocale(locale.LC_ALL, "")
3081 gettext.bindtextdomain("sst", "/usr/local/share/locale")
3082 gettext.textdomain("sst")
3083 if not (game.options & OPTION_CURSES):
3084 ln_env = os.getenv("LINES")
3090 stdscr = curses.initscr()
3094 if game.options & OPTION_COLOR:
3095 curses.start_color();
3096 curses.use_default_colors()
3097 curses.init_pair(curses.COLOR_BLACK, curses.COLOR_BLACK, -1);
3098 curses.init_pair(curses.COLOR_GREEN, curses.COLOR_GREEN, -1);
3099 curses.init_pair(curses.COLOR_RED, curses.COLOR_RED, -1);
3100 curses.init_pair(curses.COLOR_CYAN, curses.COLOR_CYAN, -1);
3101 curses.init_pair(curses.COLOR_WHITE, curses.COLOR_WHITE, -1);
3102 curses.init_pair(curses.COLOR_MAGENTA, curses.COLOR_MAGENTA, -1);
3103 curses.init_pair(curses.COLOR_BLUE, curses.COLOR_BLUE, -1);
3104 curses.init_pair(curses.COLOR_YELLOW, curses.COLOR_YELLOW, -1);
3105 global fullscreen_window, srscan_window, report_window, status_window
3106 global lrscan_window, message_window, prompt_window
3107 (rows, columns) = stdscr.getmaxyx()
3108 fullscreen_window = stdscr
3109 srscan_window = curses.newwin(12, 25, 0, 0)
3110 report_window = curses.newwin(11, 0, 1, 25)
3111 status_window = curses.newwin(10, 0, 1, 39)
3112 lrscan_window = curses.newwin(5, 0, 0, 64)
3113 message_window = curses.newwin(0, 0, 12, 0)
3114 prompt_window = curses.newwin(1, 0, rows-2, 0)
3115 message_window.scrollok(True)
3116 setwnd(fullscreen_window)
3120 if game.options & OPTION_CURSES:
3121 stdscr.keypad(False)
3127 "Wait for user action -- OK to do nothing if on a TTY"
3128 if game.options & OPTION_CURSES:
3133 prouts(_("[ANNOUNCEMENT ARRIVING...]"))
3137 if game.skill > SKILL_FAIR:
3138 prompt = _("[CONTINUE?]")
3140 prompt = _("[PRESS ENTER TO CONTINUE]")
3142 if game.options & OPTION_CURSES:
3144 setwnd(prompt_window)
3145 prompt_window.clear()
3146 prompt_window.addstr(prompt)
3147 prompt_window.getstr()
3148 prompt_window.clear()
3149 prompt_window.refresh()
3150 setwnd(message_window)
3153 sys.stdout.write('\n')
3156 sys.stdout.write('\n' * rows)
3160 "Skip i lines. Pause game if this would cause a scrolling event."
3161 for dummy in range(i):
3162 if game.options & OPTION_CURSES:
3163 (y, x) = curwnd.getyx()
3164 (my, mx) = curwnd.getmaxyx()
3165 if curwnd == message_window and y >= my - 2:
3171 except curses.error:
3176 if rows and linecount >= rows:
3179 sys.stdout.write('\n')
3182 "Utter a line with no following line feed."
3183 if game.options & OPTION_CURSES:
3187 sys.stdout.write(line)
3197 if not replayfp or replayfp.closed: # Don't slow down replays
3200 if game.options & OPTION_CURSES:
3204 if not replayfp or replayfp.closed:
3208 "Get a line of input."
3209 if game.options & OPTION_CURSES:
3210 line = curwnd.getstr() + "\n"
3213 if replayfp and not replayfp.closed:
3215 line = replayfp.readline()
3218 prout("*** Replay finished")
3221 elif line[0] != "#":
3224 line = raw_input() + "\n"
3230 "Change windows -- OK for this to be a no-op in tty mode."
3232 if game.options & OPTION_CURSES:
3234 curses.curs_set(wnd == fullscreen_window or wnd == message_window or wnd == prompt_window)
3237 "Clear to end of line -- can be a no-op in tty mode"
3238 if game.options & OPTION_CURSES:
3243 "Clear screen -- can be a no-op in tty mode."
3245 if game.options & OPTION_CURSES:
3251 def textcolor(color=DEFAULT):
3252 if game.options & OPTION_COLOR:
3253 if color == DEFAULT:
3255 elif color == BLACK:
3256 curwnd.attron(curses.color_pair(curses.COLOR_BLACK));
3258 curwnd.attron(curses.color_pair(curses.COLOR_BLUE));
3259 elif color == GREEN:
3260 curwnd.attron(curses.color_pair(curses.COLOR_GREEN));
3262 curwnd.attron(curses.color_pair(curses.COLOR_CYAN));
3264 curwnd.attron(curses.color_pair(curses.COLOR_RED));
3265 elif color == MAGENTA:
3266 curwnd.attron(curses.color_pair(curses.COLOR_MAGENTA));
3267 elif color == BROWN:
3268 curwnd.attron(curses.color_pair(curses.COLOR_YELLOW));
3269 elif color == LIGHTGRAY:
3270 curwnd.attron(curses.color_pair(curses.COLOR_WHITE));
3271 elif color == DARKGRAY:
3272 curwnd.attron(curses.color_pair(curses.COLOR_BLACK) | curses.A_BOLD);
3273 elif color == LIGHTBLUE:
3274 curwnd.attron(curses.color_pair(curses.COLOR_BLUE) | curses.A_BOLD);
3275 elif color == LIGHTGREEN:
3276 curwnd.attron(curses.color_pair(curses.COLOR_GREEN) | curses.A_BOLD);
3277 elif color == LIGHTCYAN:
3278 curwnd.attron(curses.color_pair(curses.COLOR_CYAN) | curses.A_BOLD);
3279 elif color == LIGHTRED:
3280 curwnd.attron(curses.color_pair(curses.COLOR_RED) | curses.A_BOLD);
3281 elif color == LIGHTMAGENTA:
3282 curwnd.attron(curses.color_pair(curses.COLOR_MAGENTA) | curses.A_BOLD);
3283 elif color == YELLOW:
3284 curwnd.attron(curses.color_pair(curses.COLOR_YELLOW) | curses.A_BOLD);
3285 elif color == WHITE:
3286 curwnd.attron(curses.color_pair(curses.COLOR_WHITE) | curses.A_BOLD);
3289 if game.options & OPTION_COLOR:
3290 curwnd.attron(curses.A_REVERSE)
3293 # Things past this point have policy implications.
3297 "Hook to be called after moving to redraw maps."
3298 if game.options & OPTION_CURSES:
3301 setwnd(srscan_window)
3305 setwnd(status_window)
3306 status_window.clear()
3307 status_window.move(0, 0)
3308 setwnd(report_window)
3309 report_window.clear()
3310 report_window.move(0, 0)
3312 setwnd(lrscan_window)
3313 lrscan_window.clear()
3314 lrscan_window.move(0, 0)
3315 lrscan(silent=False)
3317 def put_srscan_sym(w, sym):
3318 "Emit symbol for short-range scan."
3319 srscan_window.move(w.i+1, w.j*2+2)
3320 srscan_window.addch(sym)
3321 srscan_window.refresh()
3324 "Enemy fall down, go boom."
3325 if game.options & OPTION_CURSES:
3327 setwnd(srscan_window)
3328 srscan_window.attron(curses.A_REVERSE)
3329 put_srscan_sym(w, game.quad[w.i][w.j])
3333 srscan_window.attroff(curses.A_REVERSE)
3334 put_srscan_sym(w, game.quad[w.i][w.j])
3335 curses.delay_output(500)
3336 setwnd(message_window)
3339 "Sound and visual effects for teleportation."
3340 if game.options & OPTION_CURSES:
3342 setwnd(message_window)
3344 prouts(" . . . . . ")
3345 if game.options & OPTION_CURSES:
3346 #curses.delay_output(1000)
3350 def tracktorpedo(origin, w, step, i, n, iquad):
3351 "Torpedo-track animation."
3352 if not game.options & OPTION_CURSES:
3356 proutn(_("Track for torpedo number %d- ") % (i+1))
3359 proutn(_("Torpedo track- "))
3360 elif step==4 or step==9:
3364 if not damaged(DSRSENS) or game.condition=="docked":
3365 if i != 0 and step == 1:
3368 if (iquad=='.') or (iquad==' '):
3369 put_srscan_sym(w, '+')
3373 put_srscan_sym(w, iquad)
3375 curwnd.attron(curses.A_REVERSE)
3376 put_srscan_sym(w, iquad)
3380 curwnd.attroff(curses.A_REVERSE)
3381 put_srscan_sym(w, iquad)
3386 "Display the current galaxy chart."
3387 if game.options & OPTION_CURSES:
3388 setwnd(message_window)
3389 message_window.clear()
3391 if game.options & OPTION_TTY:
3396 def prstat(txt, data):
3398 if game.options & OPTION_CURSES:
3400 setwnd(status_window)
3402 proutn(" " * (NSYM - len(txt)))
3405 if game.options & OPTION_CURSES:
3406 setwnd(report_window)
3408 # Code from moving.c begins here
3410 def imove(icourse=None, noattack=False):
3411 "Movement execution for warp, impulse, supernova, and tractor-beam events."
3414 def newquadrant(noattack):
3415 # Leaving quadrant -- allow final enemy attack
3416 # Don't do it if being pushed by Nova
3417 if len(game.enemies) != 0 and not noattack:
3419 for enemy in game.enemies:
3420 finald = (w - enemy.location).distance()
3421 enemy.kavgd = 0.5 * (finald + enemy.kdist)
3422 # Stas Sergeev added the condition
3423 # that attacks only happen if Klingons
3424 # are present and your skill is good.
3425 if game.skill > SKILL_GOOD and game.klhere > 0 and not game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
3426 attack(torps_ok=False)
3429 # check for edge of galaxy
3433 if icourse.final.i < 0:
3434 icourse.final.i = -icourse.final.i
3436 if icourse.final.j < 0:
3437 icourse.final.j = -icourse.final.j
3439 if icourse.final.i >= GALSIZE*QUADSIZE:
3440 icourse.final.i = (GALSIZE*QUADSIZE*2) - icourse.final.i
3442 if icourse.final.j >= GALSIZE*QUADSIZE:
3443 icourse.final.j = (GALSIZE*QUADSIZE*2) - icourse.final.j
3451 if game.nkinks == 3:
3452 # Three strikes -- you're out!
3456 prout(_("YOU HAVE ATTEMPTED TO CROSS THE NEGATIVE ENERGY BARRIER"))
3457 prout(_("AT THE EDGE OF THE GALAXY. THE THIRD TIME YOU TRY THIS,"))
3458 prout(_("YOU WILL BE DESTROYED."))
3459 # Compute final position in new quadrant
3460 if trbeam: # Don't bother if we are to be beamed
3462 game.quadrant = icourse.final.quadrant()
3463 game.sector = icourse.final.sector()
3465 prout(_("Entering Quadrant %s.") % game.quadrant)
3466 game.quad[game.sector.i][game.sector.j] = game.ship
3468 if game.skill>SKILL_NOVICE:
3469 attack(torps_ok=False)
3471 def check_collision(h):
3472 iquad = game.quad[h.i][h.j]
3474 # object encountered in flight path
3475 stopegy = 50.0*icourse.distance/game.optime
3476 if iquad in ('T', 'K', 'C', 'S', 'R', '?'):
3477 for enemy in game.enemies:
3478 if enemy.location == game.sector:
3480 collision(rammed=False, enemy=enemy)
3484 prouts(_("***RED ALERT! RED ALERT!"))
3486 proutn("***" + crmshp())
3487 proutn(_(" pulled into black hole at Sector %s") % h)
3488 # Getting pulled into a black hole was certain
3489 # death in Almy's original. Stas Sergeev added a
3490 # possibility that you'll get timewarped instead.
3492 for m in range(NDEVICES):
3493 if game.damage[m]>0:
3495 probf=math.pow(1.4,(game.energy+game.shield)/5000.0-1.0)*math.pow(1.3,1.0/(n+1)-1.0)
3496 if (game.options & OPTION_BLKHOLE) and withprob(1-probf):
3506 prout(_(" encounters Tholian web at %s;") % h)
3508 prout(_(" blocked by object at %s;") % h)
3509 proutn(_("Emergency stop required "))
3510 prout(_("%2d units of energy.") % int(stopegy))
3511 game.energy -= stopegy
3512 if game.energy <= 0:
3519 prout(_("Helmsman Sulu- \"Leaving standard orbit.\""))
3520 game.inorbit = False
3521 # If tractor beam is to occur, don't move full distance
3522 if game.state.date+game.optime >= scheduled(FTBEAM):
3524 game.condition = "red"
3525 icourse.distance = icourse.distance*(scheduled(FTBEAM)-game.state.date)/game.optime + 0.1
3526 game.optime = scheduled(FTBEAM) - game.state.date + 1e-5
3528 game.quad[game.sector.i][game.sector.j] = '.'
3529 for m in range(icourse.moves):
3531 w = icourse.sector()
3532 if icourse.origin.quadrant() != icourse.location.quadrant():
3533 newquadrant(noattack)
3535 elif check_collision(icourse, w):
3536 print "Collision detected"
3540 # We're in destination quadrant -- compute new average enemy distances
3541 game.quad[game.sector.i][game.sector.j] = game.ship
3543 for enemy in game.enemies:
3544 finald = (w-enemy.location).distance()
3545 enemy.kavgd = 0.5 * (finald + enemy.kdist)
3546 enemy.kdist = finald
3547 game.enemies.sort(lambda x, y: cmp(x.kdist, y.kdist))
3548 if not game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
3549 attack(torps_ok=False)
3550 for enemy in game.enemies:
3551 enemy.kavgd = enemy.kdist
3554 setwnd(message_window)
3558 "Dock our ship at a starbase."
3560 if game.condition == "docked" and verbose:
3561 prout(_("Already docked."))
3564 prout(_("You must first leave standard orbit."))
3566 if not game.base.is_valid() or abs(game.sector.i-game.base.i) > 1 or abs(game.sector.j-game.base.j) > 1:
3567 prout(crmshp() + _(" not adjacent to base."))
3569 game.condition = "docked"
3573 if game.energy < game.inenrg:
3574 game.energy = game.inenrg
3575 game.shield = game.inshld
3576 game.torps = game.intorps
3577 game.lsupres = game.inlsr
3578 game.state.crew = FULLCREW
3579 if not damaged(DRADIO) and \
3580 ((is_scheduled(FCDBAS) or game.isatb == 1) and not game.iseenit):
3581 # get attack report from base
3582 prout(_("Lt. Uhura- \"Captain, an important message from the starbase:\""))
3586 def cartesian(loc1=None, loc2=None):
3588 return game.quadrant * QUADSIZE + game.sector
3590 return game.quadrant * QUADSIZE + loc1
3592 return loc1 * QUADSIZE + loc2
3594 def getcourse(isprobe):
3595 "Get a course and distance from the user."
3597 dquad = copy.copy(game.quadrant)
3598 navmode = "unspecified"
3602 if game.landed and not isprobe:
3603 prout(_("Dummy! You can't leave standard orbit until you"))
3604 proutn(_("are back aboard the ship."))
3607 while navmode == "unspecified":
3608 if damaged(DNAVSYS):
3610 prout(_("Computer damaged; manual navigation only"))
3612 prout(_("Computer damaged; manual movement only"))
3617 key = scanner.next()
3619 proutn(_("Manual or automatic- "))
3622 elif key == "IHALPHA":
3623 if scanner.sees("manual"):
3625 key = scanner.next()
3627 elif scanner.sees("automatic"):
3628 navmode = "automatic"
3629 key = scanner.next()
3637 prout(_("(Manual navigation assumed.)"))
3639 prout(_("(Manual movement assumed.)"))
3643 if navmode == "automatic":
3644 while key == "IHEOL":
3646 proutn(_("Target quadrant or quadrant§or- "))
3648 proutn(_("Destination sector or quadrant§or- "))
3651 key = scanner.next()
3655 xi = int(round(scanner.real))-1
3656 key = scanner.next()
3660 xj = int(round(scanner.real))-1
3661 key = scanner.next()
3663 # both quadrant and sector specified
3664 xk = int(round(scanner.real))-1
3665 key = scanner.next()
3669 xl = int(round(scanner.real))-1
3675 # only one pair of numbers was specified
3677 # only quadrant specified -- go to center of dest quad
3680 dsect.j = dsect.i = 4 # preserves 1-origin behavior
3682 # only sector specified
3686 if not dquad.valid_quadrant() or not dsect.valid_sector():
3693 prout(_("Helmsman Sulu- \"Course locked in for Sector %s.\"") % dsect)
3695 prout(_("Ensign Chekov- \"Course laid in, Captain.\""))
3696 # the actual deltas get computed here
3697 delta.j = dquad.j-game.quadrant.j + (dsect.j-game.sector.j)/(QUADSIZE*1.0)
3698 delta.i = game.quadrant.i-dquad.i + (game.sector.i-dsect.i)/(QUADSIZE*1.0)
3700 while key == "IHEOL":
3701 proutn(_("X and Y displacements- "))
3704 key = scanner.next()
3709 delta.j = scanner.real
3710 key = scanner.next()
3714 delta.i = scanner.real
3715 # Check for zero movement
3716 if delta.i == 0 and delta.j == 0:
3719 if itemp == "verbose" and not isprobe:
3721 prout(_("Helmsman Sulu- \"Aye, Sir.\""))
3723 return course(bearing=delta.bearing(), distance=delta.distance())
3726 def __init__(self, bearing, distance, origin=None):
3727 self.distance = distance
3728 self.bearing = bearing
3730 self.origin = cartesian(game.quadrant, game.sector)
3732 self.origin = origin
3733 # The bearing() code we inherited from FORTRAN is actually computing
3734 # clockface directions!
3735 if self.bearing < 0.0:
3736 self.bearing += 12.0
3737 self.angle = ((15.0 - self.bearing) * 0.5235988)
3739 self.origin = cartesian(game.quadrant, game.sector)
3741 self.origin = cartesian(game.quadrant, origin)
3742 self.increment = Coord(-math.sin(self.angle), math.cos(self.angle))
3743 bigger = max(abs(self.increment.i), abs(self.increment.j))
3744 self.increment /= bigger
3745 self.moves = int(round(10*self.distance*bigger))
3747 self.final = (self.location + self.moves*self.increment).roundtogrid()
3749 self.location = self.origin
3752 return self.location.roundtogrid() == self.final
3754 "Next step on course."
3756 self.nextlocation = self.location + self.increment
3757 samequad = (self.location.quadrant() == self.nextlocation.quadrant())
3758 self.location = self.nextlocation
3761 return self.location.quadrant()
3763 return self.location.sector()
3764 def power(self, warp):
3765 return self.distance*(warp**3)*(game.shldup+1)
3766 def time(self, warp):
3767 return 10.0*self.distance/warp**2
3770 "Move under impulse power."
3772 if damaged(DIMPULS):
3775 prout(_("Engineer Scott- \"The impulse engines are damaged, Sir.\""))
3777 if game.energy > 30.0:
3779 course = getcourse(isprobe=False)
3782 power = 20.0 + 100.0*course.distance
3785 if power >= game.energy:
3786 # Insufficient power for trip
3788 prout(_("First Officer Spock- \"Captain, the impulse engines"))
3789 prout(_("require 20.0 units to engage, plus 100.0 units per"))
3790 if game.energy > 30:
3791 proutn(_("quadrant. We can go, therefore, a maximum of %d") %
3792 int(0.01 * (game.energy-20.0)-0.05))
3793 prout(_(" quadrants.\""))
3795 prout(_("quadrant. They are, therefore, useless.\""))
3798 # Make sure enough time is left for the trip
3799 game.optime = course.dist/0.095
3800 if game.optime >= game.state.remtime:
3801 prout(_("First Officer Spock- \"Captain, our speed under impulse"))
3802 prout(_("power is only 0.95 sectors per stardate. Are you sure"))
3803 proutn(_("we dare spend the time?\" "))
3806 # Activate impulse engines and pay the cost
3807 imove(course, noattack=False)
3811 power = 20.0 + 100.0*course.dist
3812 game.energy -= power
3813 game.optime = course.dist/0.095
3814 if game.energy <= 0:
3818 def warp(wcourse, involuntary):
3819 "ove under warp drive."
3820 blooey = False; twarp = False
3821 if not involuntary: # Not WARPX entry
3823 if game.damage[DWARPEN] > 10.0:
3826 prout(_("Engineer Scott- \"The warp engines are damaged, Sir.\""))
3828 if damaged(DWARPEN) and game.warpfac > 4.0:
3831 prout(_("Engineer Scott- \"Sorry, Captain. Until this damage"))
3832 prout(_(" is repaired, I can only give you warp 4.\""))
3834 # Read in course and distance
3837 wcourse = getcourse(isprobe=False)
3840 # Make sure starship has enough energy for the trip
3841 # Note: this formula is slightly different from the C version,
3842 # and lets you skate a bit closer to the edge.
3843 if wcourse.power(game.warpfac) >= game.energy:
3844 # Insufficient power for trip
3847 prout(_("Engineering to bridge--"))
3848 if not game.shldup or 0.5*wcourse.power(game.warpfac) > game.energy:
3849 iwarp = (game.energy/(wcourse.dist+0.05)) ** 0.333333333
3851 prout(_("We can't do it, Captain. We don't have enough energy."))
3853 proutn(_("We don't have enough energy, but we could do it at warp %d") % iwarp)
3856 prout(_("if you'll lower the shields."))
3860 prout(_("We haven't the energy to go that far with the shields up."))
3862 # Make sure enough time is left for the trip
3863 game.optime = wcourse.time(game.warpfac)
3864 if game.optime >= 0.8*game.state.remtime:
3866 prout(_("First Officer Spock- \"Captain, I compute that such"))
3867 proutn(_(" a trip would require approximately %2.0f") %
3868 (100.0*game.optime/game.state.remtime))
3869 prout(_(" percent of our"))
3870 proutn(_(" remaining time. Are you sure this is wise?\" "))
3876 if game.warpfac > 6.0:
3877 # Decide if engine damage will occur
3878 # ESR: Seems wrong. Probability of damage goes *down* with distance?
3879 prob = wcourse.distance*(6.0-game.warpfac)**2/66.666666666
3880 if prob > randreal():