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")
21 return gettext.gettext(st)
23 GALSIZE = 8 # Galaxy size in quadrants
24 NINHAB = (GALSIZE * GALSIZE / 2) # Number of inhabited worlds
25 MAXUNINHAB = 10 # Maximum uninhabited worlds
26 QUADSIZE = 10 # Quadrant size in sectors
27 BASEMIN = 2 # Minimum starbases
28 BASEMAX = (GALSIZE * GALSIZE / 12) # Maximum starbases
29 MAXKLGAME = 127 # Maximum Klingons per game
30 MAXKLQUAD = 9 # Maximum Klingons per quadrant
31 FULLCREW = 428 # Crew size. BSD Trek was 387, that's wrong
32 FOREVER = 1e30 # Time for the indefinite future
33 MAXBURST = 3 # Max # of torps you can launch in one turn
34 MINCMDR = 10 # Minimum number of Klingon commanders
35 DOCKFAC = 0.25 # Repair faster when docked
36 PHASEFAC = 2.0 # Unclear what this is, it was in the C version
56 class TrekError(Exception):
59 class JumpOut(Exception):
63 def __init__(self, x=None, y=None):
66 def valid_quadrant(self):
67 return self.i >= 0 and self.i < GALSIZE and self.j >= 0 and self.j < GALSIZE
68 def valid_sector(self):
69 return self.i >= 0 and self.i < QUADSIZE and self.j >= 0 and self.j < QUADSIZE
71 self.i = self.j = None
73 return self.i != None and self.j != None
74 def __eq__(self, other):
75 return other != None and self.i == other.i and self.j == other.j
76 def __ne__(self, other):
77 return other == None or self.i != other.i or self.j != other.j
78 def __add__(self, other):
79 return Coord(self.i+other.i, self.j+other.j)
80 def __sub__(self, other):
81 return Coord(self.i-other.i, self.j-other.j)
82 def __mul__(self, other):
83 return Coord(self.i*other, self.j*other)
84 def __rmul__(self, other):
85 return Coord(self.i*other, self.j*other)
86 def __div__(self, other):
87 return Coord(self.i/other, self.j/other)
88 def __mod__(self, other):
89 return Coord(self.i % other, self.j % other)
90 def __rdiv__(self, other):
91 return Coord(self.i/other, self.j/other)
92 def roundtogrid(self):
93 return Coord(int(round(self.i)), int(round(self.j)))
94 def distance(self, other=None):
97 return math.sqrt((self.i - other.i)**2 + (self.j - other.j)**2)
99 return 1.90985*math.atan2(self.j, self.i)
105 s.i = self.i / abs(self.i)
109 s.j = self.j / abs(self.j)
112 #print "Location %s -> %s" % (self, (self / QUADSIZE).roundtogrid())
113 return self.roundtogrid() / QUADSIZE
115 return self.roundtogrid() % QUADSIZE
118 s.i = self.i + randrange(-1, 2)
119 s.j = self.j + randrange(-1, 2)
122 if self.i == None or self.j == None:
124 return "%s - %s" % (self.i+1, self.j+1)
128 "Do not anger the Space Thingy!"
137 self.name = None # string-valued if inhabited
138 self.quadrant = Coord() # quadrant located
139 self.pclass = None # could be ""M", "N", "O", or "destroyed"
140 self.crystals = "absent"# could be "mined", "present", "absent"
141 self.known = "unknown" # could be "unknown", "known", "shuttle_down"
142 self.inhabited = False # is it inhabites?
150 self.starbase = False
153 self.supernova = False
155 self.status = "secure" # Could be "secure", "distressed", "enslaved"
163 def fill2d(size, fillfun):
164 "Fill an empty list in 2D."
166 for i in range(size):
168 for j in range(size):
169 lst[i].append(fillfun(i, j))
174 self.snap = False # snapshot taken
175 self.crew = 0 # crew complement
176 self.remkl = 0 # remaining klingons
177 self.nscrem = 0 # remaining super commanders
178 self.starkl = 0 # destroyed stars
179 self.basekl = 0 # destroyed bases
180 self.nromrem = 0 # Romulans remaining
181 self.nplankl = 0 # destroyed uninhabited planets
182 self.nworldkl = 0 # destroyed inhabited planets
183 self.planets = [] # Planet information
184 self.date = 0.0 # stardate
185 self.remres = 0 # remaining resources
186 self.remtime = 0 # remaining time
187 self.baseq = [] # Base quadrant coordinates
188 self.kcmdr = [] # Commander quadrant coordinates
189 self.kscmdr = Coord() # Supercommander quadrant coordinates
191 self.galaxy = fill2d(GALSIZE, lambda i_unused, j_unused: Quadrant())
193 self.chart = fill2d(GALSIZE, lambda i_unused, j_unused: Page())
197 self.date = None # A real number
198 self.quadrant = None # A coord structure
201 OPTION_ALL = 0xffffffff
202 OPTION_TTY = 0x00000001 # old interface
203 OPTION_CURSES = 0x00000002 # new interface
204 OPTION_IOMODES = 0x00000003 # cover both interfaces
205 OPTION_PLANETS = 0x00000004 # planets and mining
206 OPTION_THOLIAN = 0x00000008 # Tholians and their webs (UT 1979 version)
207 OPTION_THINGY = 0x00000010 # Space Thingy can shoot back (Stas, 2005)
208 OPTION_PROBE = 0x00000020 # deep-space probes (DECUS version, 1980)
209 OPTION_SHOWME = 0x00000040 # bracket Enterprise in chart
210 OPTION_RAMMING = 0x00000080 # enemies may ram Enterprise (Almy)
211 OPTION_MVBADDY = 0x00000100 # more enemies can move (Almy)
212 OPTION_BLKHOLE = 0x00000200 # black hole may timewarp you (Stas, 2005)
213 OPTION_BASE = 0x00000400 # bases have good shields (Stas, 2005)
214 OPTION_WORLDS = 0x00000800 # logic for inhabited worlds (ESR, 2006)
215 OPTION_AUTOSCAN = 0x00001000 # automatic LRSCAN before CHART (ESR, 2006)
216 OPTION_PLAIN = 0x01000000 # user chose plain game
217 OPTION_ALMY = 0x02000000 # user chose Almy variant
218 OPTION_COLOR = 0x04000000 # enable color display (experimental, ESR, 2010)
237 NDEVICES = 16 # Number of devices
247 return (game.damage[dev] != 0.0)
249 return not damaged(DRADIO) or game.condition=="docked"
251 # Define future events
252 FSPY = 0 # Spy event happens always (no future[] entry)
253 # can cause SC to tractor beam Enterprise
254 FSNOVA = 1 # Supernova
255 FTBEAM = 2 # Commander tractor beams Enterprise
256 FSNAP = 3 # Snapshot for time warp
257 FBATTAK = 4 # Commander attacks base
258 FCDBAS = 5 # Commander destroys base
259 FSCMOVE = 6 # Supercommander moves (might attack base)
260 FSCDBAS = 7 # Supercommander destroys base
261 FDSPROB = 8 # Move deep space probe
262 FDISTR = 9 # Emit distress call from an inhabited world
263 FENSLV = 10 # Inhabited word is enslaved */
264 FREPRO = 11 # Klingons build a ship in an enslaved system
267 # Abstract out the event handling -- underlying data structures will change
268 # when we implement stateful events
269 def findevent(evtype):
270 return game.future[evtype]
273 def __init__(self, etype=None, loc=None, power=None):
275 self.location = Coord()
280 self.power = power # enemy energy level
281 game.enemies.append(self)
283 motion = (loc != self.location)
284 if self.location.i is not None and self.location.j is not None:
287 game.quad[self.location.i][self.location.j] = '#'
289 game.quad[self.location.i][self.location.j] = '.'
291 self.location = copy.copy(loc)
292 game.quad[self.location.i][self.location.j] = self.type
293 self.kdist = self.kavgd = (game.sector - loc).distance()
295 self.location = Coord()
296 self.kdist = self.kavgd = None
297 game.enemies.remove(self)
300 return "<%s,%s.%f>" % (self.type, self.location, self.power) # For debugging
304 self.options = None # Game options
305 self.state = Snapshot() # A snapshot structure
306 self.snapsht = Snapshot() # Last snapshot taken for time-travel purposes
307 self.quad = None # contents of our quadrant
308 self.damage = [0.0] * NDEVICES # damage encountered
309 self.future = [] # future events
313 self.future.append(Event())
314 self.passwd = None # Self Destruct password
316 self.quadrant = None # where we are in the large
317 self.sector = None # where we are in the small
318 self.tholian = None # Tholian enemy object
319 self.base = None # position of base in current quadrant
320 self.battle = None # base coordinates being attacked
321 self.plnet = None # location of planet in quadrant
322 self.gamewon = False # Finished!
323 self.ididit = False # action taken -- allows enemy to attack
324 self.alive = False # we are alive (not killed)
325 self.justin = False # just entered quadrant
326 self.shldup = False # shields are up
327 self.shldchg = False # shield is changing (affects efficiency)
328 self.iscate = False # super commander is here
329 self.ientesc = False # attempted escape from supercommander
330 self.resting = False # rest time
331 self.icraft = False # Kirk in Galileo
332 self.landed = False # party on planet (true), on ship (false)
333 self.alldone = False # game is now finished
334 self.neutz = False # Romulan Neutral Zone
335 self.isarmed = False # probe is armed
336 self.inorbit = False # orbiting a planet
337 self.imine = False # mining
338 self.icrystl = False # dilithium crystals aboard
339 self.iseenit = False # seen base attack report
340 self.thawed = False # thawed game
341 self.condition = None # "green", "yellow", "red", "docked", "dead"
342 self.iscraft = None # "onship", "offship", "removed"
343 self.skill = None # Player skill level
344 self.inkling = 0 # initial number of klingons
345 self.inbase = 0 # initial number of bases
346 self.incom = 0 # initial number of commanders
347 self.inscom = 0 # initial number of commanders
348 self.inrom = 0 # initial number of commanders
349 self.instar = 0 # initial stars
350 self.intorps = 0 # initial/max torpedoes
351 self.torps = 0 # number of torpedoes
352 self.ship = 0 # ship type -- 'E' is Enterprise
353 self.abandoned = 0 # count of crew abandoned in space
354 self.length = 0 # length of game
355 self.klhere = 0 # klingons here
356 self.casual = 0 # causalties
357 self.nhelp = 0 # calls for help
358 self.nkinks = 0 # count of energy-barrier crossings
359 self.iplnet = None # planet # in quadrant
360 self.inplan = 0 # initial planets
361 self.irhere = 0 # Romulans in quadrant
362 self.isatb = 0 # =2 if super commander is attacking base
363 self.tourn = None # tournament number
364 self.nprobes = 0 # number of probes available
365 self.inresor = 0.0 # initial resources
366 self.intime = 0.0 # initial time
367 self.inenrg = 0.0 # initial/max energy
368 self.inshld = 0.0 # initial/max shield
369 self.inlsr = 0.0 # initial life support resources
370 self.indate = 0.0 # initial date
371 self.energy = 0.0 # energy level
372 self.shield = 0.0 # shield level
373 self.warpfac = 0.0 # warp speed
374 self.lsupres = 0.0 # life support reserves
375 self.optime = 0.0 # time taken by current operation
376 self.damfac = 0.0 # damage factor
377 self.lastchart = 0.0 # time star chart was last updated
378 self.cryprob = 0.0 # probability that crystal will work
379 self.probe = None # object holding probe course info
380 self.height = 0.0 # height of orbit around planet
381 self.score = 0.0 # overall score
382 self.perdate = 0.0 # rate of kills
383 self.idebug = False # Debugging instrumentation enabled?
384 self.statekscmdr = None # No SuperCommander coordinates yet.
386 # Stas thinks this should be (C expression):
387 # game.state.remkl + len(game.state.kcmdr) > 0 ?
388 # game.state.remres/(game.state.remkl + 4*len(game.state.kcmdr)) : 99
389 # He says the existing expression is prone to divide-by-zero errors
390 # after killing the last klingon when score is shown -- perhaps also
391 # if the only remaining klingon is SCOM.
392 self.state.remtime = self.state.remres/(self.state.remkl + 4*len(self.state.kcmdr))
418 return random.random() < p
420 def randrange(*args):
421 return random.randrange(*args)
426 v *= args[0] # from [0, args[0])
428 v = args[0] + v*(args[1]-args[0]) # from [args[0], args[1])
431 # Code from ai.c begins here
434 "Would this quadrant welcome another Klingon?"
435 return iq.valid_quadrant() and \
436 not game.state.galaxy[iq.i][iq.j].supernova and \
437 game.state.galaxy[iq.i][iq.j].klingons < MAXKLQUAD
439 def tryexit(enemy, look, irun):
440 "A bad guy attempts to bug out."
442 iq.i = game.quadrant.i+(look.i+(QUADSIZE-1))/QUADSIZE - 1
443 iq.j = game.quadrant.j+(look.j+(QUADSIZE-1))/QUADSIZE - 1
444 if not welcoming(iq):
446 if enemy.type == 'R':
447 return False # Romulans cannot escape!
449 # avoid intruding on another commander's territory
450 if enemy.type == 'C':
451 if iq in game.state.kcmdr:
453 # refuse to leave if currently attacking starbase
454 if game.battle == game.quadrant:
456 # don't leave if over 1000 units of energy
457 if enemy.power > 1000.0:
459 # emit escape message and move out of quadrant.
460 # we know this if either short or long range sensors are working
461 if not damaged(DSRSENS) or not damaged(DLRSENS) or \
462 game.condition == "docked":
463 prout(crmena(True, enemy.type, "sector", enemy.location) + \
464 (_(" escapes to Quadrant %s (and regains strength).") % iq))
465 # handle local matters related to escape
468 if game.condition != "docked":
470 # Handle global matters related to escape
471 game.state.galaxy[game.quadrant.i][game.quadrant.j].klingons -= 1
472 game.state.galaxy[iq.i][iq.j].klingons += 1
473 if enemy.type == 'S':
477 schedule(FSCMOVE, 0.2777)
479 game.state.kscmdr = iq
481 for cmdr in game.state.kcmdr:
482 if cmdr == game.quadrant:
483 game.state.kcmdr.append(iq)
485 return True # success
487 # The bad-guy movement algorithm:
489 # 1. Enterprise has "force" based on condition of phaser and photon torpedoes.
490 # If both are operating full strength, force is 1000. If both are damaged,
491 # force is -1000. Having shields down subtracts an additional 1000.
493 # 2. Enemy has forces equal to the energy of the attacker plus
494 # 100*(K+R) + 500*(C+S) - 400 for novice through good levels OR
495 # 346*K + 400*R + 500*(C+S) - 400 for expert and emeritus.
497 # Attacker Initial energy levels (nominal):
498 # Klingon Romulan Commander Super-Commander
499 # Novice 400 700 1200
501 # Good 450 800 1300 1750
502 # Expert 475 850 1350 1875
503 # Emeritus 500 900 1400 2000
504 # VARIANCE 75 200 200 200
506 # Enemy vessels only move prior to their attack. In Novice - Good games
507 # only commanders move. In Expert games, all enemy vessels move if there
508 # is a commander present. In Emeritus games all enemy vessels move.
510 # 3. If Enterprise is not docked, an aggressive action is taken if enemy
511 # forces are 1000 greater than Enterprise.
513 # Agressive action on average cuts the distance between the ship and
514 # the enemy to 1/4 the original.
516 # 4. At lower energy advantage, movement units are proportional to the
517 # advantage with a 650 advantage being to hold ground, 800 to move forward
518 # 1, 950 for two, 150 for back 4, etc. Variance of 100.
520 # If docked, is reduced by roughly 1.75*game.skill, generally forcing a
521 # retreat, especially at high skill levels.
523 # 5. Motion is limited to skill level, except for SC hi-tailing it out.
525 def movebaddy(enemy):
526 "Tactical movement for the bad guys."
530 # This should probably be just (game.quadrant in game.state.kcmdr) + (game.state.kscmdr==game.quadrant)
531 if game.skill >= SKILL_EXPERT:
532 nbaddys = (((game.quadrant in game.state.kcmdr)*2 + (game.state.kscmdr==game.quadrant)*2+game.klhere*1.23+game.irhere*1.5)/2.0)
534 nbaddys = (game.quadrant in game.state.kcmdr) + (game.state.kscmdr==game.quadrant)
536 mdist = int(dist1 + 0.5) # Nearest integer distance
537 # If SC, check with spy to see if should hi-tail it
538 if enemy.type == 'S' and \
539 (enemy.power <= 500.0 or (game.condition=="docked" and not damaged(DPHOTON))):
543 # decide whether to advance, retreat, or hold position
544 forces = enemy.power+100.0*len(game.enemies)+400*(nbaddys-1)
546 forces += 1000 # Good for enemy if shield is down!
547 if not damaged(DPHASER) or not damaged(DPHOTON):
548 if damaged(DPHASER): # phasers damaged
551 forces -= 0.2*(game.energy - 2500.0)
552 if damaged(DPHOTON): # photon torpedoes damaged
555 forces -= 50.0*game.torps
557 # phasers and photon tubes both out!
560 if forces <= 1000.0 and game.condition != "docked": # Typical situation
561 motion = ((forces + randreal(200))/150.0) - 5.0
563 if forces > 1000.0: # Very strong -- move in for kill
564 motion = (1.0 - randreal())**2 * dist1 + 1.0
565 if game.condition == "docked" and (game.options & OPTION_BASE): # protected by base -- back off !
566 motion -= game.skill*(2.0-randreal()**2)
568 proutn("=== MOTION = %d, FORCES = %1.2f, " % (motion, forces))
569 # don't move if no motion
572 # Limit motion according to skill
573 if abs(motion) > game.skill:
578 # calculate preferred number of steps
579 nsteps = abs(int(motion))
580 if motion > 0 and nsteps > mdist:
581 nsteps = mdist # don't overshoot
582 if nsteps > QUADSIZE:
583 nsteps = QUADSIZE # This shouldn't be necessary
585 nsteps = 1 # This shouldn't be necessary
587 proutn("NSTEPS = %d:" % nsteps)
588 # Compute preferred values of delta X and Y
589 m = game.sector - enemy.location
590 if 2.0 * abs(m.i) < abs(m.j):
592 if 2.0 * abs(m.j) < abs(game.sector.i-enemy.location.i):
594 m = (motion * m).sgn()
595 goto = enemy.location
597 for ll in range(nsteps):
599 proutn(" %d" % (ll+1))
600 # Check if preferred position available
611 attempts = 0 # Settle mysterious hang problem
612 while attempts < 20 and not success:
614 if look.i < 0 or look.i >= QUADSIZE:
615 if motion < 0 and tryexit(enemy, look, irun):
617 if krawli == m.i or m.j == 0:
619 look.i = goto.i + krawli
621 elif look.j < 0 or look.j >= QUADSIZE:
622 if motion < 0 and tryexit(enemy, look, irun):
624 if krawlj == m.j or m.i == 0:
626 look.j = goto.j + krawlj
628 elif (game.options & OPTION_RAMMING) and game.quad[look.i][look.j] != '.':
629 # See if enemy should ram ship
630 if game.quad[look.i][look.j] == game.ship and \
631 (enemy.type == 'C' or enemy.type == 'S'):
632 collision(rammed=True, enemy=enemy)
634 if krawli != m.i and m.j != 0:
635 look.i = goto.i + krawli
637 elif krawlj != m.j and m.i != 0:
638 look.j = goto.j + krawlj
641 break # we have failed
653 if not damaged(DSRSENS) or game.condition == "docked":
654 proutn(_("*** %s from Sector %s") % (cramen(enemy.type), enemy.location))
655 if enemy.kdist < dist1:
656 proutn(_(" advances to "))
658 proutn(_(" retreats to "))
659 prout("Sector %s." % goto)
662 "Sequence Klingon tactical movement."
665 # Figure out which Klingon is the commander (or Supercommander)
667 if game.quadrant in game.state.kcmdr:
668 for enemy in game.enemies:
669 if enemy.type == 'C':
671 if game.state.kscmdr == game.quadrant:
672 for enemy in game.enemies:
673 if enemy.type == 'S':
676 # If skill level is high, move other Klingons and Romulans too!
677 # Move these last so they can base their actions on what the
679 if game.skill >= SKILL_EXPERT and (game.options & OPTION_MVBADDY):
680 for enemy in game.enemies:
681 if enemy.type in ('K', 'R'):
685 def movescom(iq, avoid):
686 "Commander movement helper."
687 # Avoid quadrants with bases if we want to avoid Enterprise
688 if not welcoming(iq) or (avoid and iq in game.state.baseq):
690 if game.justin and not game.iscate:
693 game.state.galaxy[game.state.kscmdr.i][game.state.kscmdr.j].klingons -= 1
694 game.state.kscmdr = iq
695 game.state.galaxy[game.state.kscmdr.i][game.state.kscmdr.j].klingons += 1
696 if game.state.kscmdr == game.quadrant:
697 # SC has scooted, remove him from current quadrant
702 for enemy in game.enemies:
703 if enemy.type == 'S':
706 if game.condition != "docked":
709 # check for a helpful planet
710 for i in range(game.inplan):
711 if game.state.planets[i].quadrant == game.state.kscmdr and \
712 game.state.planets[i].crystals == "present":
714 game.state.planets[i].pclass = "destroyed"
715 game.state.galaxy[game.state.kscmdr.i][game.state.kscmdr.j].planet = None
718 prout(_("Lt. Uhura- \"Captain, Starfleet Intelligence reports"))
719 proutn(_(" a planet in Quadrant %s has been destroyed") % game.state.kscmdr)
720 prout(_(" by the Super-commander.\""))
722 return True # looks good!
724 def supercommander():
725 "Move the Super Commander."
732 prout("== SUPERCOMMANDER")
733 # Decide on being active or passive
734 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 \
735 (game.state.date-game.indate) < 3.0)
736 if not game.iscate and avoid:
737 # compute move away from Enterprise
738 idelta = game.state.kscmdr-game.quadrant
739 if idelta.distance() > 2.0:
741 idelta.i = game.state.kscmdr.j-game.quadrant.j
742 idelta.j = game.quadrant.i-game.state.kscmdr.i
744 # compute distances to starbases
745 if not game.state.baseq:
749 sc = game.state.kscmdr
750 for (i, base) in enumerate(game.state.baseq):
751 basetbl.append((i, (base - sc).distance()))
752 if game.state.baseq > 1:
753 basetbl.sort(lambda x, y: cmp(x[1], y[1]))
754 # look for nearest base without a commander, no Enterprise, and
755 # without too many Klingons, and not already under attack.
756 ifindit = iwhichb = 0
757 for (i2, base) in enumerate(game.state.baseq):
758 i = basetbl[i2][0] # bug in original had it not finding nearest
759 if base == game.quadrant or base == game.battle or not welcoming(base):
761 # if there is a commander, and no other base is appropriate,
762 # we will take the one with the commander
763 for cmdr in game.state.kcmdr:
764 if base == cmdr and ifindit != 2:
768 else: # no commander -- use this one
773 return # Nothing suitable -- wait until next time
774 ibq = game.state.baseq[iwhichb]
775 # decide how to move toward base
776 idelta = ibq - game.state.kscmdr
777 # Maximum movement is 1 quadrant in either or both axes
778 idelta = idelta.sgn()
779 # try moving in both x and y directions
780 # there was what looked like a bug in the Almy C code here,
781 # but it might be this translation is just wrong.
782 iq = game.state.kscmdr + idelta
783 if not movescom(iq, avoid):
784 # failed -- try some other maneuvers
785 if idelta.i == 0 or idelta.j == 0:
788 iq.j = game.state.kscmdr.j + 1
789 if not movescom(iq, avoid):
790 iq.j = game.state.kscmdr.j - 1
793 iq.i = game.state.kscmdr.i + 1
794 if not movescom(iq, avoid):
795 iq.i = game.state.kscmdr.i - 1
798 # try moving just in x or y
799 iq.j = game.state.kscmdr.j
800 if not movescom(iq, avoid):
801 iq.j = game.state.kscmdr.j + idelta.j
802 iq.i = game.state.kscmdr.i
805 if len(game.state.baseq) == 0:
808 for ibq in game.state.baseq:
809 if ibq == game.state.kscmdr and game.state.kscmdr == game.battle:
812 return # no, don't attack base!
815 schedule(FSCDBAS, randreal(1.0, 3.0))
816 if is_scheduled(FCDBAS):
817 postpone(FSCDBAS, scheduled(FCDBAS)-game.state.date)
818 if not communicating():
822 prout(_("Lt. Uhura- \"Captain, the starbase in Quadrant %s") \
824 prout(_(" reports that it is under attack from the Klingon Super-commander."))
825 proutn(_(" It can survive until stardate %d.\"") \
826 % int(scheduled(FSCDBAS)))
829 prout(_("Mr. Spock- \"Captain, shall we cancel the rest period?\""))
833 game.optime = 0.0 # actually finished
835 # Check for intelligence report
836 if not game.idebug and \
838 (not communicating()) or \
839 not game.state.galaxy[game.state.kscmdr.i][game.state.kscmdr.j].charted):
842 prout(_("Lt. Uhura- \"Captain, Starfleet Intelligence reports"))
843 proutn(_(" the Super-commander is in Quadrant %s,") % game.state.kscmdr)
848 if not game.tholian or game.justin:
851 if game.tholian.location.i == 0 and game.tholian.location.j == 0:
854 elif game.tholian.location.i == 0 and game.tholian.location.j == QUADSIZE-1:
857 elif game.tholian.location.i == QUADSIZE-1 and game.tholian.location.j == QUADSIZE-1:
860 elif game.tholian.location.i == QUADSIZE-1 and game.tholian.location.j == 0:
864 # something is wrong!
865 game.tholian.move(None)
866 prout("***Internal error: Tholian in a bad spot.")
868 # do nothing if we are blocked
869 if game.quad[tid.i][tid.j] not in ('.', '#'):
871 here = copy.copy(game.tholian.location)
872 delta = (tid - game.tholian.location).sgn()
874 while here.i != tid.i:
876 if game.quad[here.i][here.j] == '.':
877 game.tholian.move(here)
879 while here.j != tid.j:
881 if game.quad[here.i][here.j] == '.':
882 game.tholian.move(here)
883 # check to see if all holes plugged
884 for i in range(QUADSIZE):
885 if game.quad[0][i] != '#' and game.quad[0][i] != 'T':
887 if game.quad[QUADSIZE-1][i] != '#' and game.quad[QUADSIZE-1][i] != 'T':
889 if game.quad[i][0] != '#' and game.quad[i][0] != 'T':
891 if game.quad[i][QUADSIZE-1] != '#' and game.quad[i][QUADSIZE-1] != 'T':
893 # All plugged up -- Tholian splits
894 game.quad[game.tholian.location.i][game.tholian.location.j] = '#'
896 prout(crmena(True, 'T', "sector", game.tholian) + _(" completes web."))
897 game.tholian.move(None)
900 # Code from battle.c begins here
902 def doshield(shraise):
903 "Change shield status."
911 if scanner.sees("transfer"):
915 prout(_("Shields damaged and down."))
917 if scanner.sees("up"):
919 elif scanner.sees("down"):
922 proutn(_("Do you wish to change shield energy? "))
925 elif damaged(DSHIELD):
926 prout(_("Shields damaged and down."))
929 proutn(_("Shields are up. Do you want them down? "))
936 proutn(_("Shields are down. Do you want them up? "))
942 if action == "SHUP": # raise shields
944 prout(_("Shields already up."))
948 if game.condition != "docked":
950 prout(_("Shields raised."))
953 prout(_("Shields raising uses up last of energy."))
958 elif action == "SHDN":
960 prout(_("Shields already down."))
964 prout(_("Shields lowered."))
967 elif action == "NRG":
968 while scanner.next() != "IHREAL":
970 proutn(_("Energy to transfer to shields- "))
975 if nrg > game.energy:
976 prout(_("Insufficient ship energy."))
979 if game.shield+nrg >= game.inshld:
980 prout(_("Shield energy maximized."))
981 if game.shield+nrg > game.inshld:
982 prout(_("Excess energy requested returned to ship energy"))
983 game.energy -= game.inshld-game.shield
984 game.shield = game.inshld
986 if nrg < 0.0 and game.energy-nrg > game.inenrg:
987 # Prevent shield drain loophole
989 prout(_("Engineering to bridge--"))
990 prout(_(" Scott here. Power circuit problem, Captain."))
991 prout(_(" I can't drain the shields."))
994 if game.shield+nrg < 0:
995 prout(_("All shield energy transferred to ship."))
996 game.energy += game.shield
999 proutn(_("Scotty- \""))
1001 prout(_("Transferring energy to shields.\""))
1003 prout(_("Draining energy from shields.\""))
1009 "Choose a device to damage, at random."
1011 105, # DSRSENS: short range scanners 10.5%
1012 105, # DLRSENS: long range scanners 10.5%
1013 120, # DPHASER: phasers 12.0%
1014 120, # DPHOTON: photon torpedoes 12.0%
1015 25, # DLIFSUP: life support 2.5%
1016 65, # DWARPEN: warp drive 6.5%
1017 70, # DIMPULS: impulse engines 6.5%
1018 145, # DSHIELD: deflector shields 14.5%
1019 30, # DRADIO: subspace radio 3.0%
1020 45, # DSHUTTL: shuttle 4.5%
1021 15, # DCOMPTR: computer 1.5%
1022 20, # NAVCOMP: navigation system 2.0%
1023 75, # DTRANSP: transporter 7.5%
1024 20, # DSHCTRL: high-speed shield controller 2.0%
1025 10, # DDRAY: death ray 1.0%
1026 30, # DDSP: deep-space probes 3.0%
1028 assert(sum(weights) == 1000)
1029 idx = randrange(1000)
1031 for (i, w) in enumerate(weights):
1035 return None # we should never get here
1037 def collision(rammed, enemy):
1038 "Collision handling fot rammong events."
1039 prouts(_("***RED ALERT! RED ALERT!"))
1041 prout(_("***COLLISION IMMINENT."))
1045 hardness = {'R':1.5, 'C':2.0, 'S':2.5, 'T':0.5, '?':4.0}.get(enemy.type, 1.0)
1047 proutn(_(" rammed by "))
1050 proutn(crmena(False, enemy.type, "sector", enemy.location))
1052 proutn(_(" (original position)"))
1054 deadkl(enemy.location, enemy.type, game.sector)
1055 proutn("***" + crmshp() + " heavily damaged.")
1056 icas = randrange(10, 30)
1057 prout(_("***Sickbay reports %d casualties") % icas)
1059 game.state.crew -= icas
1060 # In the pre-SST2K version, all devices got equiprobably damaged,
1061 # which was silly. Instead, pick up to half the devices at
1062 # random according to our weighting table,
1063 ncrits = randrange(NDEVICES/2)
1067 if game.damage[dev] < 0:
1069 extradm = (10.0*hardness*randreal()+1.0)*game.damfac
1070 # Damage for at least time of travel!
1071 game.damage[dev] += game.optime + extradm
1073 prout(_("***Shields are down."))
1074 if game.state.remkl + len(game.state.kcmdr) + game.state.nscrem:
1081 def torpedo(origin, bearing, dispersion, number, nburst):
1082 "Let a photon torpedo fly"
1083 if not damaged(DSRSENS) or game.condition == "docked":
1084 setwnd(srscan_window)
1086 setwnd(message_window)
1087 ac = bearing + 0.25*dispersion # dispersion is a random variable
1088 bullseye = (15.0 - bearing)*0.5235988
1089 track = course(bearing=ac, distance=QUADSIZE, origin=cartesian(origin))
1090 bumpto = Coord(0, 0)
1091 # Loop to move a single torpedo
1092 setwnd(message_window)
1093 for step in range(1, QUADSIZE*2):
1094 if not track.next():
1097 if not w.valid_sector():
1099 iquad = game.quad[w.i][w.j]
1100 tracktorpedo(w, step, number, nburst, iquad)
1104 setwnd(message_window)
1105 if not damaged(DSRSENS) or game.condition == "docked":
1106 skip(1) # start new line after text track
1107 if iquad in ('E', 'F'): # Hit our ship
1109 prout(_("Torpedo hits %s.") % crmshp())
1110 hit = 700.0 + randreal(100) - \
1111 1000.0 * (w-origin).distance() * math.fabs(math.sin(bullseye-track.angle))
1112 newcnd() # we're blown out of dock
1113 if game.landed or game.condition == "docked":
1114 return hit # Cheat if on a planet
1115 # In the C/FORTRAN version, dispersion was 2.5 radians, which
1116 # is 143 degrees, which is almost exactly 4.8 clockface units
1117 displacement = course(track.bearing+randreal(-2.4, 2.4), distance=2**0.5)
1119 bumpto = displacement.sector()
1120 if not bumpto.valid_sector():
1122 if game.quad[bumpto.i][bumpto.j] == ' ':
1125 if game.quad[bumpto.i][bumpto.j] != '.':
1126 # can't move into object
1128 game.sector = bumpto
1130 game.quad[w.i][w.j] = '.'
1131 game.quad[bumpto.i][bumpto.j] = iquad
1132 prout(_(" displaced by blast to Sector %s ") % bumpto)
1133 for enemy in game.enemies:
1134 enemy.kdist = enemy.kavgd = (game.sector-enemy.location).distance()
1137 elif iquad in ('C', 'S', 'R', 'K'): # Hit a regular enemy
1139 if iquad in ('C', 'S') and withprob(0.05):
1140 prout(crmena(True, iquad, "sector", w) + _(" uses anti-photon device;"))
1141 prout(_(" torpedo neutralized."))
1143 for enemy in game.enemies:
1144 if w == enemy.location:
1145 kp = math.fabs(enemy.power)
1146 h1 = 700.0 + randrange(100) - \
1147 1000.0 * (w-origin).distance() * math.fabs(math.sin(bullseye-track.angle))
1155 if enemy.power == 0:
1158 proutn(crmena(True, iquad, "sector", w))
1159 displacement = course(track.bearing+randreal(-2.4, 2.4), distance=2**0.5)
1161 bumpto = displacement.sector()
1162 if not bumpto.valid_sector():
1163 prout(_(" damaged but not destroyed."))
1165 if game.quad[bumpto.i][bumpto.j] == ' ':
1166 prout(_(" buffeted into black hole."))
1167 deadkl(w, iquad, bumpto)
1168 if game.quad[bumpto.i][bumpto.j] != '.':
1169 prout(_(" damaged but not destroyed."))
1171 prout(_(" damaged-- displaced by blast to Sector %s ")%bumpto)
1172 enemy.location = bumpto
1173 game.quad[w.i][w.j] = '.'
1174 game.quad[bumpto.i][bumpto.j] = iquad
1175 for enemy in game.enemies:
1176 enemy.kdist = enemy.kavgd = (game.sector-enemy.location).distance()
1180 prout("Internal error, no enemy where expected!")
1183 elif iquad == 'B': # Hit a base
1185 prout(_("***STARBASE DESTROYED.."))
1186 game.state.baseq = filter(lambda x: x != game.quadrant, game.state.baseq)
1187 game.quad[w.i][w.j] = '.'
1188 game.base.invalidate()
1189 game.state.galaxy[game.quadrant.i][game.quadrant.j].starbase -= 1
1190 game.state.chart[game.quadrant.i][game.quadrant.j].starbase -= 1
1191 game.state.basekl += 1
1194 elif iquad == 'P': # Hit a planet
1195 prout(crmena(True, iquad, "sector", w) + _(" destroyed."))
1196 game.state.nplankl += 1
1197 game.state.galaxy[game.quadrant.i][game.quadrant.j].planet = None
1198 game.iplnet.pclass = "destroyed"
1200 game.plnet.invalidate()
1201 game.quad[w.i][w.j] = '.'
1203 # captain perishes on planet
1206 elif iquad == '@': # Hit an inhabited world -- very bad!
1207 prout(crmena(True, iquad, "sector", w) + _(" destroyed."))
1208 game.state.nworldkl += 1
1209 game.state.galaxy[game.quadrant.i][game.quadrant.j].planet = None
1210 game.iplnet.pclass = "destroyed"
1212 game.plnet.invalidate()
1213 game.quad[w.i][w.j] = '.'
1215 # captain perishes on planet
1217 prout(_("The torpedo destroyed an inhabited planet."))
1219 elif iquad == '*': # Hit a star
1223 prout(crmena(True, '*', "sector", w) + _(" unaffected by photon blast."))
1225 elif iquad == '?': # Hit a thingy
1226 if not (game.options & OPTION_THINGY) or withprob(0.3):
1228 prouts(_("AAAAIIIIEEEEEEEEAAAAAAAAUUUUUGGGGGHHHHHHHHHHHH!!!"))
1230 prouts(_(" HACK! HACK! HACK! *CHOKE!* "))
1232 proutn(_("Mr. Spock-"))
1233 prouts(_(" \"Fascinating!\""))
1237 # Stas Sergeev added the possibility that
1238 # you can shove the Thingy and piss it off.
1239 # It then becomes an enemy and may fire at you.
1242 elif iquad == ' ': # Black hole
1244 prout(crmena(True, ' ', "sector", w) + _(" swallows torpedo."))
1246 elif iquad == '#': # hit the web
1248 prout(_("***Torpedo absorbed by Tholian web."))
1250 elif iquad == 'T': # Hit a Tholian
1251 h1 = 700.0 + randrange(100) - \
1252 1000.0 * (w-origin).distance() * math.fabs(math.sin(bullseye-track.angle))
1255 game.quad[w.i][w.j] = '.'
1260 proutn(crmena(True, 'T', "sector", w))
1262 prout(_(" survives photon blast."))
1264 prout(_(" disappears."))
1265 game.tholian.move(None)
1266 game.quad[w.i][w.j] = '#'
1271 proutn("Don't know how to handle torpedo collision with ")
1272 proutn(crmena(True, iquad, "sector", w))
1277 prout(_("Torpedo missed."))
1281 "Critical-hit resolution."
1282 if hit < (275.0-25.0*game.skill)*randreal(1.0, 1.5):
1284 ncrit = int(1.0 + hit/(500.0+randreal(100)))
1285 proutn(_("***CRITICAL HIT--"))
1286 # Select devices and cause damage
1292 # Cheat to prevent shuttle damage unless on ship
1293 if not (game.damage[j]<0.0 or (j == DSHUTTL and game.iscraft != "onship")):
1296 extradm = (hit*game.damfac)/(ncrit*randreal(75, 100))
1297 game.damage[j] += extradm
1299 for (i, j) in enumerate(cdam):
1301 if skipcount % 3 == 2 and i < len(cdam)-1:
1306 prout(_(" damaged."))
1307 if damaged(DSHIELD) and game.shldup:
1308 prout(_("***Shields knocked down."))
1311 def attack(torps_ok):
1312 # bad guy attacks us
1313 # torps_ok == False forces use of phasers in an attack
1314 # game could be over at this point, check
1324 prout("=== ATTACK!")
1325 # Tholian gets to move before attacking
1328 # if you have just entered the RNZ, you'll get a warning
1329 if game.neutz: # The one chance not to be attacked
1332 # commanders get a chance to tac-move towards you
1333 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:
1335 # if no enemies remain after movement, we're done
1336 if len(game.enemies) == 0 or (len(game.enemies) == 1 and thing == game.quadrant and not thing.angered):
1338 # set up partial hits if attack happens during shield status change
1339 pfac = 1.0/game.inshld
1341 chgfac = 0.25 + randreal(0.5)
1343 # message verbosity control
1344 if game.skill <= SKILL_FAIR:
1346 for enemy in game.enemies:
1348 continue # too weak to attack
1349 # compute hit strength and diminish shield power
1351 # Increase chance of photon torpedos if docked or enemy energy is low
1352 if game.condition == "docked":
1354 if enemy.power < 500:
1356 if enemy.type == 'T' or (enemy.type == '?' and not thing.angered):
1358 # different enemies have different probabilities of throwing a torp
1359 usephasers = not torps_ok or \
1360 (enemy.type == 'K' and r > 0.0005) or \
1361 (enemy.type == 'C' and r > 0.015) or \
1362 (enemy.type == 'R' and r > 0.3) or \
1363 (enemy.type == 'S' and r > 0.07) or \
1364 (enemy.type == '?' and r > 0.05)
1365 if usephasers: # Enemy uses phasers
1366 if game.condition == "docked":
1367 continue # Don't waste the effort!
1368 attempt = True # Attempt to attack
1369 dustfac = randreal(0.8, 0.85)
1370 hit = enemy.power*math.pow(dustfac, enemy.kavgd)
1372 else: # Enemy uses photon torpedo
1373 # We should be able to make the bearing() method work here
1374 pcourse = 1.90985*math.atan2(game.sector.j-enemy.location.j, enemy.location.i-game.sector.i)
1376 proutn(_("***TORPEDO INCOMING"))
1377 if not damaged(DSRSENS):
1378 proutn(_(" From ") + crmena(False, enemy.type, where, enemy.location))
1381 dispersion = (randreal()+randreal())*0.5 - 0.5
1382 dispersion += 0.002*enemy.power*dispersion
1383 hit = torpedo(enemy.location, pcourse, dispersion, number=1, nburst=1)
1384 if (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem) == 0:
1385 finish(FWON) # Klingons did themselves in!
1386 if game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova or game.alldone:
1387 return # Supernova or finished
1390 # incoming phaser or torpedo, shields may dissipate it
1391 if game.shldup or game.shldchg or game.condition == "docked":
1392 # shields will take hits
1393 propor = pfac * game.shield
1394 if game.condition == "docked":
1398 hitsh = propor*chgfac*hit+1.0
1400 if absorb > game.shield:
1401 absorb = game.shield
1402 game.shield -= absorb
1404 # taking a hit blasts us out of a starbase dock
1405 if game.condition == "docked":
1407 # but the shields may take care of it
1408 if propor > 0.1 and hit < 0.005*game.energy:
1410 # hit from this opponent got through shields, so take damage
1412 proutn(_("%d unit hit") % int(hit))
1413 if (damaged(DSRSENS) and usephasers) or game.skill<=SKILL_FAIR:
1414 proutn(_(" on the ") + crmshp())
1415 if not damaged(DSRSENS) and usephasers:
1416 prout(_(" from ") + crmena(False, enemy.type, where, enemy.location))
1418 # Decide if hit is critical
1424 if game.energy <= 0:
1425 # Returning home upon your shield, not with it...
1428 if not attempt and game.condition == "docked":
1429 prout(_("***Enemies decide against attacking your ship."))
1430 percent = 100.0*pfac*game.shield+0.5
1432 # Shields fully protect ship
1433 proutn(_("Enemy attack reduces shield strength to "))
1435 # Emit message if starship suffered hit(s)
1437 proutn(_("Energy left %2d shields ") % int(game.energy))
1440 elif not damaged(DSHIELD):
1443 proutn(_("damaged, "))
1444 prout(_("%d%%, torpedoes left %d") % (percent, game.torps))
1445 # Check if anyone was hurt
1446 if hitmax >= 200 or hittot >= 500:
1447 icas = randrange(int(hittot * 0.015))
1450 prout(_("Mc Coy- \"Sickbay to bridge. We suffered %d casualties") % icas)
1451 prout(_(" in that last attack.\""))
1453 game.state.crew -= icas
1454 # After attack, reset average distance to enemies
1455 for enemy in game.enemies:
1456 enemy.kavgd = enemy.kdist
1460 def deadkl(w, etype, mv):
1461 "Kill a Klingon, Tholian, Romulan, or Thingy."
1462 # Added mv to allow enemy to "move" before dying
1463 proutn(crmena(True, etype, "sector", mv))
1464 # Decide what kind of enemy it is and update appropriately
1466 # Chalk up a Romulan
1467 game.state.galaxy[game.quadrant.i][game.quadrant.j].romulans -= 1
1469 game.state.nromrem -= 1
1478 # Killed some type of Klingon
1479 game.state.galaxy[game.quadrant.i][game.quadrant.j].klingons -= 1
1482 game.state.kcmdr.remove(game.quadrant)
1484 if game.state.kcmdr:
1485 schedule(FTBEAM, expran(1.0*game.incom/len(game.state.kcmdr)))
1486 if is_scheduled(FCDBAS) and game.battle == game.quadrant:
1489 game.state.remkl -= 1
1491 game.state.nscrem -= 1
1492 game.state.kscmdr.invalidate()
1497 # For each kind of enemy, finish message to player
1498 prout(_(" destroyed."))
1499 if (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem) == 0:
1502 # Remove enemy ship from arrays describing local conditions
1503 for e in game.enemies:
1510 "Return None if target is invalid, otherwise return a course angle."
1511 if not w.valid_sector():
1515 # C code this was translated from is wacky -- why the sign reversal?
1516 delta.j = (w.j - game.sector.j)
1517 delta.i = (game.sector.i - w.i)
1518 if delta == Coord(0, 0):
1520 prout(_("Spock- \"Bridge to sickbay. Dr. McCoy,"))
1521 prout(_(" I recommend an immediate review of"))
1522 prout(_(" the Captain's psychological profile.\""))
1525 return delta.bearing()
1528 "Launch photon torpedo salvo."
1531 if damaged(DPHOTON):
1532 prout(_("Photon tubes damaged."))
1536 prout(_("No torpedoes left."))
1539 # First, get torpedo count
1542 if scanner.token == "IHALPHA":
1545 elif scanner.token == "IHEOL" or not scanner.waiting():
1546 prout(_("%d torpedoes left.") % game.torps)
1548 proutn(_("Number of torpedoes to fire- "))
1549 continue # Go back around to get a number
1550 else: # key == "IHREAL"
1552 if n <= 0: # abort command
1557 prout(_("Maximum of %d torpedoes per burst.") % MAXBURST)
1560 scanner.chew() # User requested more torps than available
1561 continue # Go back around
1562 break # All is good, go to next stage
1566 key = scanner.next()
1567 if i == 0 and key == "IHEOL":
1568 break # no coordinate waiting, we will try prompting
1569 if i == 1 and key == "IHEOL":
1570 # direct all torpedoes at one target
1572 target.append(target[0])
1573 tcourse.append(tcourse[0])
1576 scanner.push(scanner.token)
1577 target.append(scanner.getcoord())
1578 if target[-1] == None:
1580 tcourse.append(targetcheck(target[-1]))
1581 if tcourse[-1] == None:
1584 if len(target) == 0:
1585 # prompt for each one
1587 proutn(_("Target sector for torpedo number %d- ") % (i+1))
1589 target.append(scanner.getcoord())
1590 if target[-1] == None:
1592 tcourse.append(targetcheck(target[-1]))
1593 if tcourse[-1] == None:
1596 # Loop for moving <n> torpedoes
1598 if game.condition != "docked":
1600 dispersion = (randreal()+randreal())*0.5 -0.5
1601 if math.fabs(dispersion) >= 0.47:
1603 dispersion *= randreal(1.2, 2.2)
1605 prouts(_("***TORPEDO NUMBER %d MISFIRES") % (i+1))
1607 prouts(_("***TORPEDO MISFIRES."))
1610 prout(_(" Remainder of burst aborted."))
1612 prout(_("***Photon tubes damaged by misfire."))
1613 game.damage[DPHOTON] = game.damfac * randreal(1.0, 3.0)
1615 if game.shldup or game.condition == "docked":
1616 dispersion *= 1.0 + 0.0001*game.shield
1617 torpedo(game.sector, tcourse[i], dispersion, number=i, nburst=n)
1618 if game.alldone or game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
1620 if (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem)<=0:
1624 "Check for phasers overheating."
1626 checkburn = (rpow-1500.0)*0.00038
1627 if withprob(checkburn):
1628 prout(_("Weapons officer Sulu- \"Phasers overheated, sir.\""))
1629 game.damage[DPHASER] = game.damfac* randreal(1.0, 2.0) * (1.0+checkburn)
1631 def checkshctrl(rpow):
1632 "Check shield control."
1635 prout(_("Shields lowered."))
1637 # Something bad has happened
1638 prouts(_("***RED ALERT! RED ALERT!"))
1640 hit = rpow*game.shield/game.inshld
1641 game.energy -= rpow+hit*0.8
1642 game.shield -= hit*0.2
1643 if game.energy <= 0.0:
1644 prouts(_("Sulu- \"Captain! Shield malf***********************\""))
1649 prouts(_("Sulu- \"Captain! Shield malfunction! Phaser fire contained!\""))
1651 prout(_("Lt. Uhura- \"Sir, all decks reporting damage.\""))
1652 icas = randrange(int(hit*0.012))
1657 prout(_("McCoy to bridge- \"Severe radiation burns, Jim."))
1658 prout(_(" %d casualties so far.\"") % icas)
1660 game.state.crew -= icas
1662 prout(_("Phaser energy dispersed by shields."))
1663 prout(_("Enemy unaffected."))
1668 "Register a phaser hit on Klingons and Romulans."
1675 dustfac = randreal(0.9, 1.0)
1676 hit = wham*math.pow(dustfac, game.enemies[kk].kdist)
1677 kpini = game.enemies[kk].power
1678 kp = math.fabs(kpini)
1679 if PHASEFAC*hit < kp:
1681 if game.enemies[kk].power < 0:
1682 game.enemies[kk].power -= -kp
1684 game.enemies[kk].power -= kp
1685 kpow = game.enemies[kk].power
1686 w = game.enemies[kk].location
1688 if not damaged(DSRSENS):
1690 proutn(_("%d unit hit on ") % int(hit))
1692 proutn(_("Very small hit on "))
1693 ienm = game.quad[w.i][w.j]
1696 proutn(crmena(False, ienm, "sector", w))
1700 if (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem)==0:
1704 kk -= 1 # don't do the increment
1706 else: # decide whether or not to emasculate klingon
1707 if kpow > 0 and withprob(0.9) and kpow <= randreal(0.4, 0.8)*kpini:
1708 prout(_("***Mr. Spock- \"Captain, the vessel at Sector %s")%w)
1709 prout(_(" has just lost its firepower.\""))
1710 game.enemies[kk].power = -kpow
1715 "Fire phasers at bad guys."
1719 irec = 0 # Cheating inhibitor
1728 # SR sensors and Computer are needed for automode
1729 if damaged(DSRSENS) or damaged(DCOMPTR):
1731 if game.condition == "docked":
1732 prout(_("Phasers can't be fired through base shields."))
1735 if damaged(DPHASER):
1736 prout(_("Phaser control damaged."))
1740 if damaged(DSHCTRL):
1741 prout(_("High speed shield control damaged."))
1744 if game.energy <= 200.0:
1745 prout(_("Insufficient energy to activate high-speed shield control."))
1748 prout(_("Weapons Officer Sulu- \"High-speed shield control enabled, sir.\""))
1750 # Original code so convoluted, I re-did it all
1751 # (That was Tom Almy talking about the C code, I think -- ESR)
1752 while automode == "NOTSET":
1753 key = scanner.next()
1754 if key == "IHALPHA":
1755 if scanner.sees("manual"):
1756 if len(game.enemies)==0:
1757 prout(_("There is no enemy present to select."))
1760 automode = "AUTOMATIC"
1763 key = scanner.next()
1764 elif scanner.sees("automatic"):
1765 if (not itarg) and len(game.enemies) != 0:
1766 automode = "FORCEMAN"
1768 if len(game.enemies)==0:
1769 prout(_("Energy will be expended into space."))
1770 automode = "AUTOMATIC"
1771 key = scanner.next()
1772 elif scanner.sees("no"):
1777 elif key == "IHREAL":
1778 if len(game.enemies)==0:
1779 prout(_("Energy will be expended into space."))
1780 automode = "AUTOMATIC"
1782 automode = "FORCEMAN"
1784 automode = "AUTOMATIC"
1787 if len(game.enemies)==0:
1788 prout(_("Energy will be expended into space."))
1789 automode = "AUTOMATIC"
1791 automode = "FORCEMAN"
1793 proutn(_("Manual or automatic? "))
1798 if automode == "AUTOMATIC":
1799 if key == "IHALPHA" and scanner.sees("no"):
1801 key = scanner.next()
1802 if key != "IHREAL" and len(game.enemies) != 0:
1803 prout(_("Phasers locked on target. Energy available: %.2f")%avail)
1808 for i in range(len(game.enemies)):
1809 irec += math.fabs(game.enemies[i].power)/(PHASEFAC*math.pow(0.90, game.enemies[i].kdist))*randreal(1.01, 1.06) + 1.0
1811 proutn(_("%d units required. ") % irec)
1813 proutn(_("Units to fire= "))
1814 key = scanner.next()
1819 proutn(_("Energy available= %.2f") % avail)
1822 if not rpow > avail:
1828 key = scanner.next()
1829 if key == "IHALPHA" and scanner.sees("no"):
1832 game.energy -= 200 # Go and do it!
1833 if checkshctrl(rpow):
1838 if len(game.enemies):
1841 for i in range(len(game.enemies)):
1845 hits[i] = math.fabs(game.enemies[i].power)/(PHASEFAC*math.pow(0.90, game.enemies[i].kdist))
1846 over = randreal(1.01, 1.06) * hits[i]
1848 powrem -= hits[i] + over
1849 if powrem <= 0 and temp < hits[i]:
1858 if extra > 0 and not game.alldone:
1860 proutn(_("*** Tholian web absorbs "))
1861 if len(game.enemies)>0:
1862 proutn(_("excess "))
1863 prout(_("phaser energy."))
1865 prout(_("%d expended on empty space.") % int(extra))
1866 elif automode == "FORCEMAN":
1869 if damaged(DCOMPTR):
1870 prout(_("Battle computer damaged, manual fire only."))
1873 prouts(_("---WORKING---"))
1875 prout(_("Short-range-sensors-damaged"))
1876 prout(_("Insufficient-data-for-automatic-phaser-fire"))
1877 prout(_("Manual-fire-must-be-used"))
1879 elif automode == "MANUAL":
1881 for k in range(len(game.enemies)):
1882 aim = game.enemies[k].location
1883 ienm = game.quad[aim.i][aim.j]
1885 proutn(_("Energy available= %.2f") % (avail-0.006))
1889 if damaged(DSRSENS) and \
1890 not game.sector.distance(aim)<2**0.5 and ienm in ('C', 'S'):
1891 prout(cramen(ienm) + _(" can't be located without short range scan."))
1894 hits[k] = 0 # prevent overflow -- thanks to Alexei Voitenko
1899 if itarg and k > kz:
1900 irec = (abs(game.enemies[k].power)/(PHASEFAC*math.pow(0.9, game.enemies[k].kdist))) * randreal(1.01, 1.06) + 1.0
1903 if not damaged(DCOMPTR):
1908 proutn(_("units to fire at %s- ") % crmena(False, ienm, "sector", aim))
1909 key = scanner.next()
1910 if key == "IHALPHA" and scanner.sees("no"):
1912 key = scanner.next()
1914 if key == "IHALPHA":
1918 if k == 1: # Let me say I'm baffled by this
1921 if scanner.real < 0:
1925 hits[k] = scanner.real
1926 rpow += scanner.real
1927 # If total requested is too much, inform and start over
1929 prout(_("Available energy exceeded -- try again."))
1932 key = scanner.next() # scan for next value
1935 # zero energy -- abort
1938 if key == "IHALPHA" and scanner.sees("no"):
1943 game.energy -= 200.0
1944 if checkshctrl(rpow):
1948 # Say shield raised or malfunction, if necessary
1955 prout(_("Sulu- \"Sir, the high-speed shield control has malfunctioned . . ."))
1956 prouts(_(" CLICK CLICK POP . . ."))
1957 prout(_(" No response, sir!"))
1960 prout(_("Shields raised."))
1965 # Code from events,c begins here.
1967 # This isn't a real event queue a la BSD Trek yet -- you can only have one
1968 # event of each type active at any given time. Mostly these means we can
1969 # only have one FDISTR/FENSLV/FREPRO sequence going at any given time
1970 # BSD Trek, from which we swiped the idea, can have up to 5.
1972 def unschedule(evtype):
1973 "Remove an event from the schedule."
1974 game.future[evtype].date = FOREVER
1975 return game.future[evtype]
1977 def is_scheduled(evtype):
1978 "Is an event of specified type scheduled."
1979 return game.future[evtype].date != FOREVER
1981 def scheduled(evtype):
1982 "When will this event happen?"
1983 return game.future[evtype].date
1985 def schedule(evtype, offset):
1986 "Schedule an event of specified type."
1987 game.future[evtype].date = game.state.date + offset
1988 return game.future[evtype]
1990 def postpone(evtype, offset):
1991 "Postpone a scheduled event."
1992 game.future[evtype].date += offset
1995 "Rest period is interrupted by event."
1998 proutn(_("Mr. Spock- \"Captain, shall we cancel the rest period?\""))
2000 game.resting = False
2006 "Run through the event queue looking for things to do."
2008 fintim = game.state.date + game.optime
2017 def tractorbeam(yank):
2018 "Tractor-beaming cases merge here."
2020 game.optime = (10.0/(7.5*7.5))*yank # 7.5 is yank rate (warp 7.5)
2022 prout("***" + crmshp() + _(" caught in long range tractor beam--"))
2023 # If Kirk & Co. screwing around on planet, handle
2024 atover(True) # atover(true) is Grab
2027 if game.icraft: # Caught in Galileo?
2030 # Check to see if shuttle is aboard
2031 if game.iscraft == "offship":
2034 prout(_("Galileo, left on the planet surface, is captured"))
2035 prout(_("by aliens and made into a flying McDonald's."))
2036 game.damage[DSHUTTL] = -10
2037 game.iscraft = "removed"
2039 prout(_("Galileo, left on the planet surface, is well hidden."))
2041 game.quadrant = game.state.kscmdr
2043 game.quadrant = game.state.kcmdr[i]
2044 game.sector = randplace(QUADSIZE)
2045 prout(crmshp() + _(" is pulled to Quadrant %s, Sector %s") \
2046 % (game.quadrant, game.sector))
2048 prout(_("(Remainder of rest/repair period cancelled.)"))
2049 game.resting = False
2051 if not damaged(DSHIELD) and game.shield > 0:
2052 doshield(shraise=True) # raise shields
2053 game.shldchg = False
2055 prout(_("(Shields not currently useable.)"))
2057 # Adjust finish time to time of tractor beaming?
2058 # fintim = game.state.date+game.optime
2059 attack(torps_ok=False)
2060 if not game.state.kcmdr:
2063 schedule(FTBEAM, game.optime+expran(1.5*game.intime/len(game.state.kcmdr)))
2066 "Code merges here for any commander destroying a starbase."
2067 # Not perfect, but will have to do
2068 # Handle case where base is in same quadrant as starship
2069 if game.battle == game.quadrant:
2070 game.state.chart[game.battle.i][game.battle.j].starbase = False
2071 game.quad[game.base.i][game.base.j] = '.'
2072 game.base.invalidate()
2075 prout(_("Spock- \"Captain, I believe the starbase has been destroyed.\""))
2076 elif game.state.baseq and communicating():
2077 # Get word via subspace radio
2080 prout(_("Lt. Uhura- \"Captain, Starfleet Command reports that"))
2081 proutn(_(" the starbase in Quadrant %s has been destroyed by") % game.battle)
2083 prout(_("the Klingon Super-Commander"))
2085 prout(_("a Klingon Commander"))
2086 game.state.chart[game.battle.i][game.battle.j].starbase = False
2087 # Remove Starbase from galaxy
2088 game.state.galaxy[game.battle.i][game.battle.j].starbase = False
2089 game.state.baseq = filter(lambda x: x != game.battle, game.state.baseq)
2091 # reinstate a commander's base attack
2095 game.battle.invalidate()
2097 prout("=== EVENTS from %.2f to %.2f:" % (game.state.date, fintim))
2098 for i in range(1, NEVENTS):
2099 if i == FSNOVA: proutn("=== Supernova ")
2100 elif i == FTBEAM: proutn("=== T Beam ")
2101 elif i == FSNAP: proutn("=== Snapshot ")
2102 elif i == FBATTAK: proutn("=== Base Attack ")
2103 elif i == FCDBAS: proutn("=== Base Destroy ")
2104 elif i == FSCMOVE: proutn("=== SC Move ")
2105 elif i == FSCDBAS: proutn("=== SC Base Destroy ")
2106 elif i == FDSPROB: proutn("=== Probe Move ")
2107 elif i == FDISTR: proutn("=== Distress Call ")
2108 elif i == FENSLV: proutn("=== Enslavement ")
2109 elif i == FREPRO: proutn("=== Klingon Build ")
2111 prout("%.2f" % (scheduled(i)))
2114 radio_was_broken = damaged(DRADIO)
2117 # Select earliest extraneous event, evcode==0 if no events
2122 for l in range(1, NEVENTS):
2123 if game.future[l].date < datemin:
2126 prout("== Event %d fires" % evcode)
2127 datemin = game.future[l].date
2128 xtime = datemin-game.state.date
2129 game.state.date = datemin
2130 # Decrement Federation resources and recompute remaining time
2131 game.state.remres -= (game.state.remkl+4*len(game.state.kcmdr))*xtime
2133 if game.state.remtime <= 0:
2136 # Any crew left alive?
2137 if game.state.crew <= 0:
2140 # Is life support adequate?
2141 if damaged(DLIFSUP) and game.condition != "docked":
2142 if game.lsupres < xtime and game.damage[DLIFSUP] > game.lsupres:
2145 game.lsupres -= xtime
2146 if game.damage[DLIFSUP] <= xtime:
2147 game.lsupres = game.inlsr
2150 if game.condition == "docked":
2152 # Don't fix Deathray here
2153 for l in range(NDEVICES):
2154 if game.damage[l] > 0.0 and l != DDRAY:
2155 if game.damage[l]-repair > 0.0:
2156 game.damage[l] -= repair
2158 game.damage[l] = 0.0
2159 # If radio repaired, update star chart and attack reports
2160 if radio_was_broken and not damaged(DRADIO):
2161 prout(_("Lt. Uhura- \"Captain, the sub-space radio is working and"))
2162 prout(_(" surveillance reports are coming in."))
2164 if not game.iseenit:
2168 prout(_(" The star chart is now up to date.\""))
2170 # Cause extraneous event EVCODE to occur
2171 game.optime -= xtime
2172 if evcode == FSNOVA: # Supernova
2175 schedule(FSNOVA, expran(0.5*game.intime))
2176 if game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
2178 elif evcode == FSPY: # Check with spy to see if SC should tractor beam
2179 if game.state.nscrem == 0 or \
2180 ictbeam or istract or \
2181 game.condition == "docked" or game.isatb == 1 or game.iscate:
2183 if game.ientesc or \
2184 (game.energy<2000 and game.torps<4 and game.shield < 1250) or \
2185 (damaged(DPHASER) and (damaged(DPHOTON) or game.torps<4)) or \
2186 (damaged(DSHIELD) and \
2187 (game.energy < 2500 or damaged(DPHASER)) and \
2188 (game.torps < 5 or damaged(DPHOTON))):
2190 istract = ictbeam = True
2191 tractorbeam((game.state.kscmdr-game.quadrant).distance())
2194 elif evcode == FTBEAM: # Tractor beam
2195 if not game.state.kcmdr:
2198 i = randrange(len(game.state.kcmdr))
2199 yank = (game.state.kcmdr[i]-game.quadrant).distance()
2200 if istract or game.condition == "docked" or yank == 0:
2201 # Drats! Have to reschedule
2203 game.optime + expran(1.5*game.intime/len(game.state.kcmdr)))
2207 elif evcode == FSNAP: # Snapshot of the universe (for time warp)
2208 game.snapsht = copy.deepcopy(game.state)
2209 game.state.snap = True
2210 schedule(FSNAP, expran(0.5 * game.intime))
2211 elif evcode == FBATTAK: # Commander attacks starbase
2212 if not game.state.kcmdr or not game.state.baseq:
2218 for ibq in game.state.baseq:
2219 for cmdr in game.state.kcmdr:
2220 if ibq == cmdr and ibq != game.quadrant and ibq != game.state.kscmdr:
2223 # no match found -- try later
2224 schedule(FBATTAK, expran(0.3*game.intime))
2229 # commander + starbase combination found -- launch attack
2231 schedule(FCDBAS, randreal(1.0, 4.0))
2232 if game.isatb: # extra time if SC already attacking
2233 postpone(FCDBAS, scheduled(FSCDBAS)-game.state.date)
2234 game.future[FBATTAK].date = game.future[FCDBAS].date + expran(0.3*game.intime)
2235 game.iseenit = False
2236 if not communicating():
2237 continue # No warning :-(
2241 prout(_("Lt. Uhura- \"Captain, the starbase in Quadrant %s") % game.battle)
2242 prout(_(" reports that it is under attack and that it can"))
2243 prout(_(" hold out only until stardate %d.\"") % (int(scheduled(FCDBAS))))
2246 elif evcode == FSCDBAS: # Supercommander destroys base
2249 if not game.state.galaxy[game.state.kscmdr.i][game.state.kscmdr.j].starbase:
2250 continue # WAS RETURN!
2252 game.battle = game.state.kscmdr
2254 elif evcode == FCDBAS: # Commander succeeds in destroying base
2255 if evcode == FCDBAS:
2257 if not game.state.baseq() \
2258 or not game.state.galaxy[game.battle.i][game.battle.j].starbase:
2259 game.battle.invalidate()
2261 # find the lucky pair
2262 for cmdr in game.state.kcmdr:
2263 if cmdr == game.battle:
2266 # No action to take after all
2269 elif evcode == FSCMOVE: # Supercommander moves
2270 schedule(FSCMOVE, 0.2777)
2271 if not game.ientesc and not istract and game.isatb != 1 and \
2272 (not game.iscate or not game.justin):
2274 elif evcode == FDSPROB: # Move deep space probe
2275 schedule(FDSPROB, 0.01)
2276 if not game.probe.next():
2277 if not game.probe.quadrant().valid_quadrant() or \
2278 game.state.galaxy[game.probe.quadrant().i][game.probe.quadrant().j].supernova:
2279 # Left galaxy or ran into supernova
2283 proutn(_("Lt. Uhura- \"The deep space probe "))
2284 if not game.probe.quadrant().valid_quadrant():
2285 prout(_("has left the galaxy.\""))
2287 prout(_("is no longer transmitting.\""))
2293 prout(_("Lt. Uhura- \"The deep space probe is now in Quadrant %s.\"") % game.probe.quadrant())
2294 pdest = game.state.galaxy[game.probe.quadrant().i][game.probe.quadrant().j]
2296 chp = game.state.chart[game.probe.quadrant().i][game.probe.quadrant().j]
2297 chp.klingons = pdest.klingons
2298 chp.starbase = pdest.starbase
2299 chp.stars = pdest.stars
2300 pdest.charted = True
2301 game.probe.moves -= 1 # One less to travel
2302 if game.probe.arrived() and game.isarmed and pdest.stars:
2303 supernova(game.probe) # fire in the hole!
2305 if game.state.galaxy[game.quadrant().i][game.quadrant().j].supernova:
2307 elif evcode == FDISTR: # inhabited system issues distress call
2309 # try a whole bunch of times to find something suitable
2310 for i in range(100):
2311 # need a quadrant which is not the current one,
2312 # which has some stars which are inhabited and
2313 # not already under attack, which is not
2314 # supernova'ed, and which has some Klingons in it
2315 w = randplace(GALSIZE)
2316 q = game.state.galaxy[w.i][w.j]
2317 if not (game.quadrant == w or q.planet == None or \
2318 not q.planet.inhabited or \
2319 q.supernova or q.status!="secure" or q.klingons<=0):
2322 # can't seem to find one; ignore this call
2324 prout("=== Couldn't find location for distress event.")
2326 # got one!! Schedule its enslavement
2327 ev = schedule(FENSLV, expran(game.intime))
2329 q.status = "distressed"
2330 # tell the captain about it if we can
2332 prout(_("Uhura- Captain, %s in Quadrant %s reports it is under attack") \
2333 % (q.planet, repr(w)))
2334 prout(_("by a Klingon invasion fleet."))
2337 elif evcode == FENSLV: # starsystem is enslaved
2338 ev = unschedule(FENSLV)
2339 # see if current distress call still active
2340 q = game.state.galaxy[ev.quadrant.i][ev.quadrant.j]
2344 q.status = "enslaved"
2346 # play stork and schedule the first baby
2347 ev2 = schedule(FREPRO, expran(2.0 * game.intime))
2348 ev2.quadrant = ev.quadrant
2350 # report the disaster if we can
2352 prout(_("Uhura- We've lost contact with starsystem %s") % \
2354 prout(_("in Quadrant %s.\n") % ev.quadrant)
2355 elif evcode == FREPRO: # Klingon reproduces
2356 # If we ever switch to a real event queue, we'll need to
2357 # explicitly retrieve and restore the x and y.
2358 ev = schedule(FREPRO, expran(1.0 * game.intime))
2359 # see if current distress call still active
2360 q = game.state.galaxy[ev.quadrant.i][ev.quadrant.j]
2364 if game.state.remkl >= MAXKLGAME:
2365 continue # full right now
2366 # reproduce one Klingon
2369 if game.klhere >= MAXKLQUAD:
2371 # this quadrant not ok, pick an adjacent one
2372 for m.i in range(w.i - 1, w.i + 2):
2373 for m.j in range(w.j - 1, w.j + 2):
2374 if not m.valid_quadrant():
2376 q = game.state.galaxy[m.i][m.j]
2377 # check for this quad ok (not full & no snova)
2378 if q.klingons >= MAXKLQUAD or q.supernova:
2382 continue # search for eligible quadrant failed
2386 game.state.remkl += 1
2388 if game.quadrant == w:
2390 game.enemies.append(newkling())
2391 # recompute time left
2394 if game.quadrant == w:
2395 prout(_("Spock- sensors indicate the Klingons have"))
2396 prout(_("launched a warship from %s.") % q.planet)
2398 prout(_("Uhura- Starfleet reports increased Klingon activity"))
2399 if q.planet != None:
2400 proutn(_("near %s ") % q.planet)
2401 prout(_("in Quadrant %s.") % w)
2407 key = scanner.next()
2410 proutn(_("How long? "))
2415 origTime = delay = scanner.real
2418 if delay >= game.state.remtime or len(game.enemies) != 0:
2419 proutn(_("Are you sure? "))
2422 # Alternate resting periods (events) with attacks
2426 game.resting = False
2427 if not game.resting:
2428 prout(_("%d stardates left.") % int(game.state.remtime))
2430 temp = game.optime = delay
2431 if len(game.enemies):
2432 rtime = randreal(1.0, 2.0)
2436 if game.optime < delay:
2437 attack(torps_ok=False)
2445 # Repair Deathray if long rest at starbase
2446 if origTime-delay >= 9.99 and game.condition == "docked":
2447 game.damage[DDRAY] = 0.0
2448 # leave if quadrant supernovas
2449 if game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
2451 game.resting = False
2456 ncourse = (0.0, 10.5, 12.0, 1.5, 9.0, 0.0, 3.0, 7.5, 6.0, 4.5)
2457 newc = Coord(); neighbor = Coord(); bump = Coord(0, 0)
2459 # Wow! We've supernova'ed
2460 supernova(game.quadrant)
2462 # handle initial nova
2463 game.quad[nov.i][nov.j] = '.'
2464 prout(crmena(False, '*', "sector", nov) + _(" novas."))
2465 game.state.galaxy[game.quadrant.i][game.quadrant.j].stars -= 1
2466 game.state.starkl += 1
2467 # Set up queue to recursively trigger adjacent stars
2473 for offset.i in range(-1, 1+1):
2474 for offset.j in range(-1, 1+1):
2475 if offset.j == 0 and offset.i == 0:
2477 neighbor = start + offset
2478 if not neighbor.valid_sector():
2480 iquad = game.quad[neighbor.i][neighbor.j]
2481 # Empty space ends reaction
2482 if iquad in ('.', '?', ' ', 'T', '#'):
2484 elif iquad == '*': # Affect another star
2486 # This star supernovas
2487 supernova(game.quadrant)
2490 hits.append(neighbor)
2491 game.state.galaxy[game.quadrant.i][game.quadrant.j].stars -= 1
2492 game.state.starkl += 1
2493 proutn(crmena(True, '*', "sector", neighbor))
2495 game.quad[neighbor.i][neighbor.j] = '.'
2497 elif iquad in ('P', '@'): # Destroy planet
2498 game.state.galaxy[game.quadrant.i][game.quadrant.j].planet = None
2500 game.state.nplankl += 1
2502 game.state.nworldkl += 1
2503 prout(crmena(True, 'B', "sector", neighbor) + _(" destroyed."))
2504 game.iplnet.pclass = "destroyed"
2506 game.plnet.invalidate()
2510 game.quad[neighbor.i][neighbor.j] = '.'
2511 elif iquad == 'B': # Destroy base
2512 game.state.galaxy[game.quadrant.i][game.quadrant.j].starbase = False
2513 game.state.baseq = filter(lambda x: x!= game.quadrant, game.state.baseq)
2514 game.base.invalidate()
2515 game.state.basekl += 1
2517 prout(crmena(True, 'B', "sector", neighbor) + _(" destroyed."))
2518 game.quad[neighbor.i][neighbor.j] = '.'
2519 elif iquad in ('E', 'F'): # Buffet ship
2520 prout(_("***Starship buffeted by nova."))
2522 if game.shield >= 2000.0:
2523 game.shield -= 2000.0
2525 diff = 2000.0 - game.shield
2529 prout(_("***Shields knocked out."))
2530 game.damage[DSHIELD] += 0.005*game.damfac*randreal()*diff
2532 game.energy -= 2000.0
2533 if game.energy <= 0:
2536 # add in course nova contributes to kicking starship
2537 bump += (game.sector-hits[-1]).sgn()
2538 elif iquad == 'K': # kill klingon
2539 deadkl(neighbor, iquad, neighbor)
2540 elif iquad in ('C','S','R'): # Damage/destroy big enemies
2541 for ll in range(len(game.enemies)):
2542 if game.enemies[ll].location == neighbor:
2544 game.enemies[ll].power -= 800.0 # If firepower is lost, die
2545 if game.enemies[ll].power <= 0.0:
2546 deadkl(neighbor, iquad, neighbor)
2548 newc = neighbor + neighbor - hits[-1]
2549 proutn(crmena(True, iquad, "sector", neighbor) + _(" damaged"))
2550 if not newc.valid_sector():
2551 # can't leave quadrant
2554 iquad1 = game.quad[newc.i][newc.j]
2556 proutn(_(", blasted into ") + crmena(False, ' ', "sector", newc))
2558 deadkl(neighbor, iquad, newc)
2561 # can't move into something else
2564 proutn(_(", buffeted to Sector %s") % newc)
2565 game.quad[neighbor.i][neighbor.j] = '.'
2566 game.quad[newc.i][newc.j] = iquad
2567 game.enemies[ll].move(newc)
2568 # Starship affected by nova -- kick it away.
2570 direc = ncourse[3*(bump.i+1)+bump.j+2]
2575 scourse = course(bearing=direc, distance=dist)
2576 game.optime = scourse.time(warp=4)
2578 prout(_("Force of nova displaces starship."))
2579 imove(scourse, noattack=True)
2580 game.optime = scourse.time(warp=4)
2584 "Star goes supernova."
2589 # Scheduled supernova -- select star at random.
2592 for nq.i in range(GALSIZE):
2593 for nq.j in range(GALSIZE):
2594 stars += game.state.galaxy[nq.i][nq.j].stars
2596 return # nothing to supernova exists
2597 num = randrange(stars) + 1
2598 for nq.i in range(GALSIZE):
2599 for nq.j in range(GALSIZE):
2600 num -= game.state.galaxy[nq.i][nq.j].stars
2606 proutn("=== Super nova here?")
2609 if not nq == game.quadrant or game.justin:
2610 # it isn't here, or we just entered (treat as enroute)
2613 prout(_("Message from Starfleet Command Stardate %.2f") % game.state.date)
2614 prout(_(" Supernova in Quadrant %s; caution advised.") % nq)
2617 # we are in the quadrant!
2618 num = randrange(game.state.galaxy[nq.i][nq.j].stars) + 1
2619 for ns.i in range(QUADSIZE):
2620 for ns.j in range(QUADSIZE):
2621 if game.quad[ns.i][ns.j]=='*':
2628 prouts(_("***RED ALERT! RED ALERT!"))
2630 prout(_("***Incipient supernova detected at Sector %s") % ns)
2631 if (ns.i-game.sector.i)**2 + (ns.j-game.sector.j)**2 <= 2.1:
2632 proutn(_("Emergency override attempts t"))
2633 prouts("***************")
2637 # destroy any Klingons in supernovaed quadrant
2638 kldead = game.state.galaxy[nq.i][nq.j].klingons
2639 game.state.galaxy[nq.i][nq.j].klingons = 0
2640 if nq == game.state.kscmdr:
2641 # did in the Supercommander!
2642 game.state.nscrem = game.state.kscmdr.i = game.state.kscmdr.j = game.isatb = 0
2646 survivors = filter(lambda w: w != nq, game.state.kcmdr)
2647 comkills = len(game.state.kcmdr) - len(survivors)
2648 game.state.kcmdr = survivors
2650 if not game.state.kcmdr:
2652 game.state.remkl -= kldead
2653 # destroy Romulans and planets in supernovaed quadrant
2654 nrmdead = game.state.galaxy[nq.i][nq.j].romulans
2655 game.state.galaxy[nq.i][nq.j].romulans = 0
2656 game.state.nromrem -= nrmdead
2658 for loop in range(game.inplan):
2659 if game.state.planets[loop].quadrant == nq:
2660 game.state.planets[loop].pclass = "destroyed"
2662 # Destroy any base in supernovaed quadrant
2663 game.state.baseq = filter(lambda x: x != nq, game.state.baseq)
2664 # If starship caused supernova, tally up destruction
2666 game.state.starkl += game.state.galaxy[nq.i][nq.j].stars
2667 game.state.basekl += game.state.galaxy[nq.i][nq.j].starbase
2668 game.state.nplankl += npdead
2669 # mark supernova in galaxy and in star chart
2670 if game.quadrant == nq or communicating():
2671 game.state.galaxy[nq.i][nq.j].supernova = True
2672 # If supernova destroys last Klingons give special message
2673 if (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem)==0 and not nq == game.quadrant:
2676 prout(_("Lucky you!"))
2677 proutn(_("A supernova in %s has just destroyed the last Klingons.") % nq)
2680 # if some Klingons remain, continue or die in supernova
2685 # Code from finish.c ends here.
2688 "Self-destruct maneuver. Finish with a BANG!"
2690 if damaged(DCOMPTR):
2691 prout(_("Computer damaged; cannot execute destruct sequence."))
2693 prouts(_("---WORKING---")); skip(1)
2694 prouts(_("SELF-DESTRUCT-SEQUENCE-ACTIVATED")); skip(1)
2695 prouts(" 10"); skip(1)
2696 prouts(" 9"); skip(1)
2697 prouts(" 8"); skip(1)
2698 prouts(" 7"); skip(1)
2699 prouts(" 6"); skip(1)
2701 prout(_("ENTER-CORRECT-PASSWORD-TO-CONTINUE-"))
2703 prout(_("SELF-DESTRUCT-SEQUENCE-OTHERWISE-"))
2705 prout(_("SELF-DESTRUCT-SEQUENCE-WILL-BE-ABORTED"))
2708 if game.passwd != scanner.token:
2709 prouts(_("PASSWORD-REJECTED;"))
2711 prouts(_("CONTINUITY-EFFECTED"))
2714 prouts(_("PASSWORD-ACCEPTED")); skip(1)
2715 prouts(" 5"); skip(1)
2716 prouts(" 4"); skip(1)
2717 prouts(" 3"); skip(1)
2718 prouts(" 2"); skip(1)
2719 prouts(" 1"); skip(1)
2721 prouts(_("GOODBYE-CRUEL-WORLD"))
2729 prouts(_("********* Entropy of %s maximized *********") % crmshp())
2733 if len(game.enemies) != 0:
2734 whammo = 25.0 * game.energy
2735 for l in range(len(game.enemies)):
2736 if game.enemies[l].power*game.enemies[l].kdist <= whammo:
2737 deadkl(game.enemies[l].location, game.quad[game.enemies[l].location.i][game.enemies[l].location.j], game.enemies[l].location)
2741 "Compute our rate of kils over time."
2742 elapsed = game.state.date - game.indate
2743 if elapsed == 0: # Avoid divide-by-zero error if calculated on turn 0
2746 starting = (game.inkling + game.incom + game.inscom)
2747 remaining = (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem)
2748 return (starting - remaining)/elapsed
2752 badpt = 5.0*game.state.starkl + \
2754 10.0*game.state.nplankl + \
2755 300*game.state.nworldkl + \
2757 100.0*game.state.basekl +\
2759 if game.ship == 'F':
2761 elif game.ship == None:
2766 # end the game, with appropriate notfications
2770 prout(_("It is stardate %.1f.") % game.state.date)
2772 if ifin == FWON: # Game has been won
2773 if game.state.nromrem != 0:
2774 prout(_("The remaining %d Romulans surrender to Starfleet Command.") %
2777 prout(_("You have smashed the Klingon invasion fleet and saved"))
2778 prout(_("the Federation."))
2783 badpt = 0.0 # Close enough!
2784 # killsPerDate >= RateMax
2785 if game.state.date-game.indate < 5.0 or \
2786 killrate() >= 0.1*game.skill*(game.skill+1.0) + 0.1 + 0.008*badpt:
2788 prout(_("In fact, you have done so well that Starfleet Command"))
2789 if game.skill == SKILL_NOVICE:
2790 prout(_("promotes you one step in rank from \"Novice\" to \"Fair\"."))
2791 elif game.skill == SKILL_FAIR:
2792 prout(_("promotes you one step in rank from \"Fair\" to \"Good\"."))
2793 elif game.skill == SKILL_GOOD:
2794 prout(_("promotes you one step in rank from \"Good\" to \"Expert\"."))
2795 elif game.skill == SKILL_EXPERT:
2796 prout(_("promotes you to Commodore Emeritus."))
2798 prout(_("Now that you think you're really good, try playing"))
2799 prout(_("the \"Emeritus\" game. It will splatter your ego."))
2800 elif game.skill == SKILL_EMERITUS:
2802 proutn(_("Computer- "))
2803 prouts(_("ERROR-ERROR-ERROR-ERROR"))
2805 prouts(_(" YOUR-SKILL-HAS-EXCEEDED-THE-CAPACITY-OF-THIS-PROGRAM"))
2807 prouts(_(" THIS-PROGRAM-MUST-SURVIVE"))
2809 prouts(_(" THIS-PROGRAM-MUST-SURVIVE"))
2811 prouts(_(" THIS-PROGRAM-MUST-SURVIVE"))
2813 prouts(_(" THIS-PROGRAM-MUST?- MUST ? - SUR? ? -? VI"))
2815 prout(_("Now you can retire and write your own Star Trek game!"))
2817 elif game.skill >= SKILL_EXPERT:
2818 if game.thawed and not game.idebug:
2819 prout(_("You cannot get a citation, so..."))
2821 proutn(_("Do you want your Commodore Emeritus Citation printed? "))
2825 # Only grant long life if alive (original didn't!)
2827 prout(_("LIVE LONG AND PROSPER."))
2832 elif ifin == FDEPLETE: # Federation Resources Depleted
2833 prout(_("Your time has run out and the Federation has been"))
2834 prout(_("conquered. Your starship is now Klingon property,"))
2835 prout(_("and you are put on trial as a war criminal. On the"))
2836 proutn(_("basis of your record, you are "))
2837 if (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem)*3.0 > (game.inkling + game.incom + game.inscom):
2838 prout(_("acquitted."))
2840 prout(_("LIVE LONG AND PROSPER."))
2842 prout(_("found guilty and"))
2843 prout(_("sentenced to death by slow torture."))
2847 elif ifin == FLIFESUP:
2848 prout(_("Your life support reserves have run out, and"))
2849 prout(_("you die of thirst, starvation, and asphyxiation."))
2850 prout(_("Your starship is a derelict in space."))
2852 prout(_("Your energy supply is exhausted."))
2854 prout(_("Your starship is a derelict in space."))
2855 elif ifin == FBATTLE:
2856 prout(_("The %s has been destroyed in battle.") % crmshp())
2858 prout(_("Dulce et decorum est pro patria mori."))
2860 prout(_("You have made three attempts to cross the negative energy"))
2861 prout(_("barrier which surrounds the galaxy."))
2863 prout(_("Your navigation is abominable."))
2866 prout(_("Your starship has been destroyed by a nova."))
2867 prout(_("That was a great shot."))
2869 elif ifin == FSNOVAED:
2870 prout(_("The %s has been fried by a supernova.") % crmshp())
2871 prout(_("...Not even cinders remain..."))
2872 elif ifin == FABANDN:
2873 prout(_("You have been captured by the Klingons. If you still"))
2874 prout(_("had a starbase to be returned to, you would have been"))
2875 prout(_("repatriated and given another chance. Since you have"))
2876 prout(_("no starbases, you will be mercilessly tortured to death."))
2877 elif ifin == FDILITHIUM:
2878 prout(_("Your starship is now an expanding cloud of subatomic particles"))
2879 elif ifin == FMATERIALIZE:
2880 prout(_("Starbase was unable to re-materialize your starship."))
2881 prout(_("Sic transit gloria mundi"))
2882 elif ifin == FPHASER:
2883 prout(_("The %s has been cremated by its own phasers.") % crmshp())
2885 prout(_("You and your landing party have been"))
2886 prout(_("converted to energy, disipating through space."))
2887 elif ifin == FMINING:
2888 prout(_("You are left with your landing party on"))
2889 prout(_("a wild jungle planet inhabited by primitive cannibals."))
2891 prout(_("They are very fond of \"Captain Kirk\" soup."))
2893 prout(_("Without your leadership, the %s is destroyed.") % crmshp())
2894 elif ifin == FDPLANET:
2895 prout(_("You and your mining party perish."))
2897 prout(_("That was a great shot."))
2900 prout(_("The Galileo is instantly annihilated by the supernova."))
2901 prout(_("You and your mining party are atomized."))
2903 prout(_("Mr. Spock takes command of the %s and") % crmshp())
2904 prout(_("joins the Romulans, wreaking terror on the Federation."))
2905 elif ifin == FPNOVA:
2906 prout(_("You and your mining party are atomized."))
2908 prout(_("Mr. Spock takes command of the %s and") % crmshp())
2909 prout(_("joins the Romulans, wreaking terror on the Federation."))
2910 elif ifin == FSTRACTOR:
2911 prout(_("The shuttle craft Galileo is also caught,"))
2912 prout(_("and breaks up under the strain."))
2914 prout(_("Your debris is scattered for millions of miles."))
2915 prout(_("Without your leadership, the %s is destroyed.") % crmshp())
2917 prout(_("The mutants attack and kill Spock."))
2918 prout(_("Your ship is captured by Klingons, and"))
2919 prout(_("your crew is put on display in a Klingon zoo."))
2920 elif ifin == FTRIBBLE:
2921 prout(_("Tribbles consume all remaining water,"))
2922 prout(_("food, and oxygen on your ship."))
2924 prout(_("You die of thirst, starvation, and asphyxiation."))
2925 prout(_("Your starship is a derelict in space."))
2927 prout(_("Your ship is drawn to the center of the black hole."))
2928 prout(_("You are crushed into extremely dense matter."))
2930 prout(_("Your last crew member has died."))
2931 if game.ship == 'F':
2933 elif game.ship == 'E':
2936 if (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem) != 0:
2937 goodies = game.state.remres/game.inresor
2938 baddies = (game.state.remkl + 2.0*len(game.state.kcmdr))/(game.inkling+2.0*game.incom)
2939 if goodies/baddies >= randreal(1.0, 1.5):
2940 prout(_("As a result of your actions, a treaty with the Klingon"))
2941 prout(_("Empire has been signed. The terms of the treaty are"))
2942 if goodies/baddies >= randreal(3.0):
2943 prout(_("favorable to the Federation."))
2945 prout(_("Congratulations!"))
2947 prout(_("highly unfavorable to the Federation."))
2949 prout(_("The Federation will be destroyed."))
2951 prout(_("Since you took the last Klingon with you, you are a"))
2952 prout(_("martyr and a hero. Someday maybe they'll erect a"))
2953 prout(_("statue in your memory. Rest in peace, and try not"))
2954 prout(_("to think about pigeons."))
2959 "Compute player's score."
2960 timused = game.state.date - game.indate
2961 if (timused == 0 or (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem) != 0) and timused < 5.0:
2963 game.perdate = killrate()
2964 ithperd = 500*game.perdate + 0.5
2967 iwon = 100*game.skill
2968 if game.ship == 'E':
2970 elif game.ship == 'F':
2974 game.score = 10*(game.inkling - game.state.remkl) \
2975 + 50*(game.incom - len(game.state.kcmdr)) \
2977 + 20*(game.inrom - game.state.nromrem) \
2978 + 200*(game.inscom - game.state.nscrem) \
2979 - game.state.nromrem \
2984 prout(_("Your score --"))
2985 if game.inrom - game.state.nromrem:
2986 prout(_("%6d Romulans destroyed %5d") %
2987 (game.inrom - game.state.nromrem, 20*(game.inrom - game.state.nromrem)))
2988 if game.state.nromrem and game.gamewon:
2989 prout(_("%6d Romulans captured %5d") %
2990 (game.state.nromrem, game.state.nromrem))
2991 if game.inkling - game.state.remkl:
2992 prout(_("%6d ordinary Klingons destroyed %5d") %
2993 (game.inkling - game.state.remkl, 10*(game.inkling - game.state.remkl)))
2994 if game.incom - len(game.state.kcmdr):
2995 prout(_("%6d Klingon commanders destroyed %5d") %
2996 (game.incom - len(game.state.kcmdr), 50*(game.incom - len(game.state.kcmdr))))
2997 if game.inscom - game.state.nscrem:
2998 prout(_("%6d Super-Commander destroyed %5d") %
2999 (game.inscom - game.state.nscrem, 200*(game.inscom - game.state.nscrem)))
3001 prout(_("%6.2f Klingons per stardate %5d") %
3002 (game.perdate, ithperd))
3003 if game.state.starkl:
3004 prout(_("%6d stars destroyed by your action %5d") %
3005 (game.state.starkl, -5*game.state.starkl))
3006 if game.state.nplankl:
3007 prout(_("%6d planets destroyed by your action %5d") %
3008 (game.state.nplankl, -10*game.state.nplankl))
3009 if (game.options & OPTION_WORLDS) and game.state.nworldkl:
3010 prout(_("%6d inhabited planets destroyed by your action %5d") %
3011 (game.state.nworldkl, -300*game.state.nworldkl))
3012 if game.state.basekl:
3013 prout(_("%6d bases destroyed by your action %5d") %
3014 (game.state.basekl, -100*game.state.basekl))
3016 prout(_("%6d calls for help from starbase %5d") %
3017 (game.nhelp, -45*game.nhelp))
3019 prout(_("%6d casualties incurred %5d") %
3020 (game.casual, -game.casual))
3022 prout(_("%6d crew abandoned in space %5d") %
3023 (game.abandoned, -3*game.abandoned))
3025 prout(_("%6d ship(s) lost or destroyed %5d") %
3026 (klship, -100*klship))
3028 prout(_("Penalty for getting yourself killed -200"))
3030 proutn(_("Bonus for winning "))
3031 if game.skill == SKILL_NOVICE: proutn(_("Novice game "))
3032 elif game.skill == SKILL_FAIR: proutn(_("Fair game "))
3033 elif game.skill == SKILL_GOOD: proutn(_("Good game "))
3034 elif game.skill == SKILL_EXPERT: proutn(_("Expert game "))
3035 elif game.skill == SKILL_EMERITUS: proutn(_("Emeritus game"))
3036 prout(" %5d" % iwon)
3038 prout(_("TOTAL SCORE %5d") % game.score)
3041 "Emit winner's commemmorative plaque."
3044 proutn(_("File or device name for your plaque: "))
3047 fp = open(winner, "w")
3050 prout(_("Invalid name."))
3052 proutn(_("Enter name to go on plaque (up to 30 characters): "))
3054 # The 38 below must be 64 for 132-column paper
3055 nskip = 38 - len(winner)/2
3056 fp.write("\n\n\n\n")
3057 # --------DRAW ENTERPRISE PICTURE.
3058 fp.write(" EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n" )
3059 fp.write(" EEE E : : : E\n" )
3060 fp.write(" EE EEE E : : NCC-1701 : E\n")
3061 fp.write("EEEEEEEEEEEEEEEE EEEEEEEEEEEEEEE : : : E\n")
3062 fp.write(" E EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n")
3063 fp.write(" EEEEEEEEE EEEEEEEEEEEEE E E\n")
3064 fp.write(" EEEEEEE EEEEE E E E E\n")
3065 fp.write(" EEE E E E E\n")
3066 fp.write(" E E E E\n")
3067 fp.write(" EEEEEEEEEEEEE E E\n")
3068 fp.write(" EEE : EEEEEEE EEEEEEEE\n")
3069 fp.write(" :E : EEEE E\n")
3070 fp.write(" .-E -:----- E\n")
3071 fp.write(" :E : E\n")
3072 fp.write(" EE : EEEEEEEE\n")
3073 fp.write(" EEEEEEEEEEEEEEEEEEEEEEE\n")
3075 fp.write(_(" U. S. S. ENTERPRISE\n"))
3076 fp.write("\n\n\n\n")
3077 fp.write(_(" For demonstrating outstanding ability as a starship captain\n"))
3079 fp.write(_(" Starfleet Command bestows to you\n"))
3081 fp.write("%*s%s\n\n" % (nskip, "", winner))
3082 fp.write(_(" the rank of\n\n"))
3083 fp.write(_(" \"Commodore Emeritus\"\n\n"))
3085 if game.skill == SKILL_EXPERT:
3086 fp.write(_(" Expert level\n\n"))
3087 elif game.skill == SKILL_EMERITUS:
3088 fp.write(_("Emeritus level\n\n"))
3090 fp.write(_(" Cheat level\n\n"))
3091 timestring = time.ctime()
3092 fp.write(_(" This day of %.6s %.4s, %.8s\n\n") %
3093 (timestring+4, timestring+20, timestring+11))
3094 fp.write(_(" Your score: %d\n\n") % game.score)
3095 fp.write(_(" Klingons per stardate: %.2f\n") % game.perdate)
3098 # Code from io.c begins here
3100 rows = linecount = 0 # for paging
3103 fullscreen_window = None
3104 srscan_window = None
3105 report_window = None
3106 status_window = None
3107 lrscan_window = None
3108 message_window = None
3109 prompt_window = None
3114 "for some recent versions of python2, the following enables UTF8"
3115 "for the older ones we probably need to set C locale, and the python3"
3116 "has no problems at all"
3117 if sys.version_info[0] < 3:
3119 locale.setlocale(locale.LC_ALL, "")
3120 gettext.bindtextdomain("sst", "/usr/local/share/locale")
3121 gettext.textdomain("sst")
3122 if not (game.options & OPTION_CURSES):
3123 ln_env = os.getenv("LINES")
3129 stdscr = curses.initscr()
3133 if game.options & OPTION_COLOR:
3134 curses.start_color()
3135 curses.use_default_colors()
3136 curses.init_pair(curses.COLOR_BLACK, curses.COLOR_BLACK, -1)
3137 curses.init_pair(curses.COLOR_GREEN, curses.COLOR_GREEN, -1)
3138 curses.init_pair(curses.COLOR_RED, curses.COLOR_RED, -1)
3139 curses.init_pair(curses.COLOR_CYAN, curses.COLOR_CYAN, -1)
3140 curses.init_pair(curses.COLOR_WHITE, curses.COLOR_WHITE, -1)
3141 curses.init_pair(curses.COLOR_MAGENTA, curses.COLOR_MAGENTA, -1)
3142 curses.init_pair(curses.COLOR_BLUE, curses.COLOR_BLUE, -1)
3143 curses.init_pair(curses.COLOR_YELLOW, curses.COLOR_YELLOW, -1)
3144 global fullscreen_window, srscan_window, report_window, status_window
3145 global lrscan_window, message_window, prompt_window
3146 (rows, columns) = stdscr.getmaxyx()
3147 fullscreen_window = stdscr
3148 srscan_window = curses.newwin(12, 25, 0, 0)
3149 report_window = curses.newwin(11, 0, 1, 25)
3150 status_window = curses.newwin(10, 0, 1, 39)
3151 lrscan_window = curses.newwin(5, 0, 0, 64)
3152 message_window = curses.newwin(0, 0, 12, 0)
3153 prompt_window = curses.newwin(1, 0, rows-2, 0)
3154 message_window.scrollok(True)
3155 setwnd(fullscreen_window)
3159 if game.options & OPTION_CURSES:
3160 stdscr.keypad(False)
3166 "Wait for user action -- OK to do nothing if on a TTY"
3167 if game.options & OPTION_CURSES:
3172 prouts(_("[ANNOUNCEMENT ARRIVING...]"))
3176 if game.skill > SKILL_FAIR:
3177 prompt = _("[CONTINUE?]")
3179 prompt = _("[PRESS ENTER TO CONTINUE]")
3181 if game.options & OPTION_CURSES:
3183 setwnd(prompt_window)
3184 prompt_window.clear()
3185 prompt_window.addstr(prompt)
3186 prompt_window.getstr()
3187 prompt_window.clear()
3188 prompt_window.refresh()
3189 setwnd(message_window)
3192 sys.stdout.write('\n')
3196 sys.stdout.write('\n' * rows)
3200 "Skip i lines. Pause game if this would cause a scrolling event."
3201 for dummy in range(i):
3202 if game.options & OPTION_CURSES:
3203 (y, x) = curwnd.getyx()
3206 except curses.error:
3211 if rows and linecount >= rows:
3214 sys.stdout.write('\n')
3217 "Utter a line with no following line feed."
3218 if game.options & OPTION_CURSES:
3219 (y, x) = curwnd.getyx()
3220 (my, mx) = curwnd.getmaxyx()
3221 if curwnd == message_window and y >= my - 2:
3224 # Uncomment this to debug curses problems
3226 logfp.write("#curses: at %s proutn(%s)\n" % ((y, x), repr(line)))
3230 sys.stdout.write(line)
3240 if not replayfp or replayfp.closed: # Don't slow down replays
3243 if game.options & OPTION_CURSES:
3247 if not replayfp or replayfp.closed:
3251 "Get a line of input."
3252 if game.options & OPTION_CURSES:
3253 line = curwnd.getstr() + "\n"
3256 if replayfp and not replayfp.closed:
3258 line = replayfp.readline()
3261 prout("*** Replay finished")
3264 elif line[0] != "#":
3267 line = raw_input() + "\n"
3273 "Change windows -- OK for this to be a no-op in tty mode."
3275 if game.options & OPTION_CURSES:
3276 # Uncomment this to debug curses problems
3278 if wnd == fullscreen_window:
3279 legend = "fullscreen"
3280 elif wnd == srscan_window:
3282 elif wnd == report_window:
3284 elif wnd == status_window:
3286 elif wnd == lrscan_window:
3288 elif wnd == message_window:
3290 elif wnd == prompt_window:
3294 logfp.write("#curses: setwnd(%s)\n" % legend)
3296 # Some curses implementations get confused when you try this.
3298 curses.curs_set(wnd in (fullscreen_window, message_window, prompt_window))
3299 except curses.error:
3303 "Clear to end of line -- can be a no-op in tty mode"
3304 if game.options & OPTION_CURSES:
3309 "Clear screen -- can be a no-op in tty mode."
3311 if game.options & OPTION_CURSES:
3317 def textcolor(color=DEFAULT):
3318 if game.options & OPTION_COLOR:
3319 if color == DEFAULT:
3321 elif color == BLACK:
3322 curwnd.attron(curses.color_pair(curses.COLOR_BLACK))
3324 curwnd.attron(curses.color_pair(curses.COLOR_BLUE))
3325 elif color == GREEN:
3326 curwnd.attron(curses.color_pair(curses.COLOR_GREEN))
3328 curwnd.attron(curses.color_pair(curses.COLOR_CYAN))
3330 curwnd.attron(curses.color_pair(curses.COLOR_RED))
3331 elif color == MAGENTA:
3332 curwnd.attron(curses.color_pair(curses.COLOR_MAGENTA))
3333 elif color == BROWN:
3334 curwnd.attron(curses.color_pair(curses.COLOR_YELLOW))
3335 elif color == LIGHTGRAY:
3336 curwnd.attron(curses.color_pair(curses.COLOR_WHITE))
3337 elif color == DARKGRAY:
3338 curwnd.attron(curses.color_pair(curses.COLOR_BLACK) | curses.A_BOLD)
3339 elif color == LIGHTBLUE:
3340 curwnd.attron(curses.color_pair(curses.COLOR_BLUE) | curses.A_BOLD)
3341 elif color == LIGHTGREEN:
3342 curwnd.attron(curses.color_pair(curses.COLOR_GREEN) | curses.A_BOLD)
3343 elif color == LIGHTCYAN:
3344 curwnd.attron(curses.color_pair(curses.COLOR_CYAN) | curses.A_BOLD)
3345 elif color == LIGHTRED:
3346 curwnd.attron(curses.color_pair(curses.COLOR_RED) | curses.A_BOLD)
3347 elif color == LIGHTMAGENTA:
3348 curwnd.attron(curses.color_pair(curses.COLOR_MAGENTA) | curses.A_BOLD)
3349 elif color == YELLOW:
3350 curwnd.attron(curses.color_pair(curses.COLOR_YELLOW) | curses.A_BOLD)
3351 elif color == WHITE:
3352 curwnd.attron(curses.color_pair(curses.COLOR_WHITE) | curses.A_BOLD)
3355 if game.options & OPTION_COLOR:
3356 curwnd.attron(curses.A_REVERSE)
3359 # Things past this point have policy implications.
3363 "Hook to be called after moving to redraw maps."
3364 if game.options & OPTION_CURSES:
3367 setwnd(srscan_window)
3371 setwnd(status_window)
3372 status_window.clear()
3373 status_window.move(0, 0)
3374 setwnd(report_window)
3375 report_window.clear()
3376 report_window.move(0, 0)
3378 setwnd(lrscan_window)
3379 lrscan_window.clear()
3380 lrscan_window.move(0, 0)
3381 lrscan(silent=False)
3383 def put_srscan_sym(w, sym):
3384 "Emit symbol for short-range scan."
3385 srscan_window.move(w.i+1, w.j*2+2)
3386 srscan_window.addch(sym)
3387 srscan_window.refresh()
3390 "Enemy fall down, go boom."
3391 if game.options & OPTION_CURSES:
3393 setwnd(srscan_window)
3394 srscan_window.attron(curses.A_REVERSE)
3395 put_srscan_sym(w, game.quad[w.i][w.j])
3399 srscan_window.attroff(curses.A_REVERSE)
3400 put_srscan_sym(w, game.quad[w.i][w.j])
3401 curses.delay_output(500)
3402 setwnd(message_window)
3405 "Sound and visual effects for teleportation."
3406 if game.options & OPTION_CURSES:
3408 setwnd(message_window)
3410 prouts(" . . . . . ")
3411 if game.options & OPTION_CURSES:
3412 #curses.delay_output(1000)
3416 def tracktorpedo(w, step, i, n, iquad):
3417 "Torpedo-track animation."
3418 if not game.options & OPTION_CURSES:
3422 proutn(_("Track for torpedo number %d- ") % (i+1))
3425 proutn(_("Torpedo track- "))
3426 elif step==4 or step==9:
3430 if not damaged(DSRSENS) or game.condition=="docked":
3431 if i != 0 and step == 1:
3434 if (iquad=='.') or (iquad==' '):
3435 put_srscan_sym(w, '+')
3439 put_srscan_sym(w, iquad)
3441 curwnd.attron(curses.A_REVERSE)
3442 put_srscan_sym(w, iquad)
3446 curwnd.attroff(curses.A_REVERSE)
3447 put_srscan_sym(w, iquad)
3452 "Display the current galaxy chart."
3453 if game.options & OPTION_CURSES:
3454 setwnd(message_window)
3455 message_window.clear()
3457 if game.options & OPTION_TTY:
3462 def prstat(txt, data):
3464 if game.options & OPTION_CURSES:
3466 setwnd(status_window)
3468 proutn(" " * (NSYM - len(txt)))
3471 if game.options & OPTION_CURSES:
3472 setwnd(report_window)
3474 # Code from moving.c begins here
3476 def imove(icourse=None, noattack=False):
3477 "Movement execution for warp, impulse, supernova, and tractor-beam events."
3480 def newquadrant(noattack):
3481 # Leaving quadrant -- allow final enemy attack
3482 # Don't do it if being pushed by Nova
3483 if len(game.enemies) != 0 and not noattack:
3485 for enemy in game.enemies:
3486 finald = (w - enemy.location).distance()
3487 enemy.kavgd = 0.5 * (finald + enemy.kdist)
3488 # Stas Sergeev added the condition
3489 # that attacks only happen if Klingons
3490 # are present and your skill is good.
3491 if game.skill > SKILL_GOOD and game.klhere > 0 and not game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
3492 attack(torps_ok=False)
3495 # check for edge of galaxy
3499 if icourse.final.i < 0:
3500 icourse.final.i = -icourse.final.i
3502 if icourse.final.j < 0:
3503 icourse.final.j = -icourse.final.j
3505 if icourse.final.i >= GALSIZE*QUADSIZE:
3506 icourse.final.i = (GALSIZE*QUADSIZE*2) - icourse.final.i
3508 if icourse.final.j >= GALSIZE*QUADSIZE:
3509 icourse.final.j = (GALSIZE*QUADSIZE*2) - icourse.final.j
3517 if game.nkinks == 3:
3518 # Three strikes -- you're out!
3522 prout(_("YOU HAVE ATTEMPTED TO CROSS THE NEGATIVE ENERGY BARRIER"))
3523 prout(_("AT THE EDGE OF THE GALAXY. THE THIRD TIME YOU TRY THIS,"))
3524 prout(_("YOU WILL BE DESTROYED."))
3525 # Compute final position in new quadrant
3526 if trbeam: # Don't bother if we are to be beamed
3528 game.quadrant = icourse.final.quadrant()
3529 game.sector = icourse.final.sector()
3531 prout(_("Entering Quadrant %s.") % game.quadrant)
3532 game.quad[game.sector.i][game.sector.j] = game.ship
3534 if game.skill>SKILL_NOVICE:
3535 attack(torps_ok=False)
3537 def check_collision(h):
3538 iquad = game.quad[h.i][h.j]
3540 # object encountered in flight path
3541 stopegy = 50.0*icourse.distance/game.optime
3542 if iquad in ('T', 'K', 'C', 'S', 'R', '?'):
3543 for enemy in game.enemies:
3544 if enemy.location == game.sector:
3546 collision(rammed=False, enemy=enemy)
3550 prouts(_("***RED ALERT! RED ALERT!"))
3552 proutn("***" + crmshp())
3553 proutn(_(" pulled into black hole at Sector %s") % h)
3554 # Getting pulled into a black hole was certain
3555 # death in Almy's original. Stas Sergeev added a
3556 # possibility that you'll get timewarped instead.
3558 for m in range(NDEVICES):
3559 if game.damage[m]>0:
3561 probf=math.pow(1.4,(game.energy+game.shield)/5000.0-1.0)*math.pow(1.3,1.0/(n+1)-1.0)
3562 if (game.options & OPTION_BLKHOLE) and withprob(1-probf):
3572 prout(_(" encounters Tholian web at %s;") % h)
3574 prout(_(" blocked by object at %s;") % h)
3575 proutn(_("Emergency stop required "))
3576 prout(_("%2d units of energy.") % int(stopegy))
3577 game.energy -= stopegy
3578 if game.energy <= 0:
3585 prout(_("Helmsman Sulu- \"Leaving standard orbit.\""))
3586 game.inorbit = False
3587 # If tractor beam is to occur, don't move full distance
3588 if game.state.date+game.optime >= scheduled(FTBEAM):
3590 game.condition = "red"
3591 icourse.distance = icourse.distance*(scheduled(FTBEAM)-game.state.date)/game.optime + 0.1
3592 game.optime = scheduled(FTBEAM) - game.state.date + 1e-5
3594 game.quad[game.sector.i][game.sector.j] = '.'
3595 for m in range(icourse.moves):
3597 w = icourse.sector()
3598 if icourse.origin.quadrant() != icourse.location.quadrant():
3599 newquadrant(noattack)
3601 elif check_collision(w):
3602 print "Collision detected"
3606 # We're in destination quadrant -- compute new average enemy distances
3607 game.quad[game.sector.i][game.sector.j] = game.ship
3609 for enemy in game.enemies:
3610 finald = (w-enemy.location).distance()
3611 enemy.kavgd = 0.5 * (finald + enemy.kdist)
3612 enemy.kdist = finald
3614 if not game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
3615 attack(torps_ok=False)
3616 for enemy in game.enemies:
3617 enemy.kavgd = enemy.kdist
3620 setwnd(message_window)
3624 "Dock our ship at a starbase."
3626 if game.condition == "docked" and verbose:
3627 prout(_("Already docked."))
3630 prout(_("You must first leave standard orbit."))
3632 if not game.base.is_valid() or abs(game.sector.i-game.base.i) > 1 or abs(game.sector.j-game.base.j) > 1:
3633 prout(crmshp() + _(" not adjacent to base."))
3635 game.condition = "docked"
3639 if game.energy < game.inenrg:
3640 game.energy = game.inenrg
3641 game.shield = game.inshld
3642 game.torps = game.intorps
3643 game.lsupres = game.inlsr
3644 game.state.crew = FULLCREW
3645 if not damaged(DRADIO) and \
3646 ((is_scheduled(FCDBAS) or game.isatb == 1) and not game.iseenit):
3647 # get attack report from base
3648 prout(_("Lt. Uhura- \"Captain, an important message from the starbase:\""))
3652 def cartesian(loc1=None, loc2=None):
3654 return game.quadrant * QUADSIZE + game.sector
3656 return game.quadrant * QUADSIZE + loc1
3658 return loc1 * QUADSIZE + loc2
3660 def getcourse(isprobe):
3661 "Get a course and distance from the user."
3663 dquad = copy.copy(game.quadrant)
3664 navmode = "unspecified"
3668 if game.landed and not isprobe:
3669 prout(_("Dummy! You can't leave standard orbit until you"))
3670 proutn(_("are back aboard the ship."))
3673 while navmode == "unspecified":
3674 if damaged(DNAVSYS):
3676 prout(_("Computer damaged; manual navigation only"))
3678 prout(_("Computer damaged; manual movement only"))
3683 key = scanner.next()
3685 proutn(_("Manual or automatic- "))
3688 elif key == "IHALPHA":
3689 if scanner.sees("manual"):
3691 key = scanner.next()
3693 elif scanner.sees("automatic"):
3694 navmode = "automatic"
3695 key = scanner.next()
3703 prout(_("(Manual navigation assumed.)"))
3705 prout(_("(Manual movement assumed.)"))
3709 if navmode == "automatic":
3710 while key == "IHEOL":
3712 proutn(_("Target quadrant or quadrant§or- "))
3714 proutn(_("Destination sector or quadrant§or- "))
3717 key = scanner.next()
3721 xi = int(round(scanner.real))-1
3722 key = scanner.next()
3726 xj = int(round(scanner.real))-1
3727 key = scanner.next()
3729 # both quadrant and sector specified
3730 xk = int(round(scanner.real))-1
3731 key = scanner.next()
3735 xl = int(round(scanner.real))-1
3741 # only one pair of numbers was specified
3743 # only quadrant specified -- go to center of dest quad
3746 dsect.j = dsect.i = 4 # preserves 1-origin behavior
3748 # only sector specified
3752 if not dquad.valid_quadrant() or not dsect.valid_sector():
3759 prout(_("Helmsman Sulu- \"Course locked in for Sector %s.\"") % dsect)
3761 prout(_("Ensign Chekov- \"Course laid in, Captain.\""))
3762 # the actual deltas get computed here
3763 delta.j = dquad.j-game.quadrant.j + (dsect.j-game.sector.j)/(QUADSIZE*1.0)
3764 delta.i = game.quadrant.i-dquad.i + (game.sector.i-dsect.i)/(QUADSIZE*1.0)
3766 while key == "IHEOL":
3767 proutn(_("X and Y displacements- "))
3770 key = scanner.next()
3775 delta.j = scanner.real
3776 key = scanner.next()
3780 delta.i = scanner.real
3781 # Check for zero movement
3782 if delta.i == 0 and delta.j == 0:
3785 if itemp == "verbose" and not isprobe:
3787 prout(_("Helmsman Sulu- \"Aye, Sir.\""))
3789 return course(bearing=delta.bearing(), distance=delta.distance())
3792 def __init__(self, bearing, distance, origin=None):
3793 self.distance = distance
3794 self.bearing = bearing
3796 self.origin = cartesian(game.quadrant, game.sector)
3798 self.origin = origin
3799 # The bearing() code we inherited from FORTRAN is actually computing
3800 # clockface directions!
3801 if self.bearing < 0.0:
3802 self.bearing += 12.0
3803 self.angle = ((15.0 - self.bearing) * 0.5235988)
3805 self.origin = cartesian(game.quadrant, game.sector)
3807 self.origin = cartesian(game.quadrant, origin)
3808 self.increment = Coord(-math.sin(self.angle), math.cos(self.angle))
3809 bigger = max(abs(self.increment.i), abs(self.increment.j))
3810 self.increment /= bigger
3811 self.moves = int(round(10*self.distance*bigger))
3813 self.final = (self.location + self.moves*self.increment).roundtogrid()
3815 self.location = self.origin
3818 return self.location.roundtogrid() == self.final
3820 "Next step on course."
3822 self.nextlocation = self.location + self.increment
3823 samequad = (self.location.quadrant() == self.nextlocation.quadrant())
3824 self.location = self.nextlocation
3827 return self.location.quadrant()
3829 return self.location.sector()
3830 def power(self, warp):
3831 return self.distance*(warp**3)*(game.shldup+1)
3832 def time(self, warp):
3833 return 10.0*self.distance/warp**2
3836 "Move under impulse power."
3838 if damaged(DIMPULS):
3841 prout(_("Engineer Scott- \"The impulse engines are damaged, Sir.\""))
3843 if game.energy > 30.0:
3845 course = getcourse(isprobe=False)
3848 power = 20.0 + 100.0*course.distance
3851 if power >= game.energy:
3852 # Insufficient power for trip
3854 prout(_("First Officer Spock- \"Captain, the impulse engines"))
3855 prout(_("require 20.0 units to engage, plus 100.0 units per"))
3856 if game.energy > 30:
3857 proutn(_("quadrant. We can go, therefore, a maximum of %d") %
3858 int(0.01 * (game.energy-20.0)-0.05))
3859 prout(_(" quadrants.\""))
3861 prout(_("quadrant. They are, therefore, useless.\""))
3864 # Make sure enough time is left for the trip
3865 game.optime = course.dist/0.095
3866 if game.optime >= game.state.remtime:
3867 prout(_("First Officer Spock- \"Captain, our speed under impulse"))
3868 prout(_("power is only 0.95 sectors per stardate. Are you sure"))
3869 proutn(_("we dare spend the time?\" "))
3872 # Activate impulse engines and pay the cost
3873 imove(course, noattack=False)
3877 power = 20.0 + 100.0*course.dist
3878 game.energy -= power
3879 game.optime = course.dist/0.095
3880 if game.energy <= 0:
3884 def warp(wcourse, involuntary):
3885 "ove under warp drive."
3886 blooey = False; twarp = False
3887 if not involuntary: # Not WARPX entry
3889 if game.damage[DWARPEN] > 10.0:
3892 prout(_("Engineer Scott- \"The warp engines are damaged, Sir.\""))
3894 if damaged(DWARPEN) and game.warpfac > 4.0:
3897 prout(_("Engineer Scott- \"Sorry, Captain. Until this damage"))
3898 prout(_(" is repaired, I can only give you warp 4.\""))
3900 # Read in course and distance
3903 wcourse = getcourse(isprobe=False)