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, pickle, 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!"
135 return (q.i, q.j) == (self.i, self.j)
139 self.name = None # string-valued if inhabited
140 self.quadrant = Coord() # quadrant located
141 self.pclass = None # could be ""M", "N", "O", or "destroyed"
142 self.crystals = "absent"# could be "mined", "present", "absent"
143 self.known = "unknown" # could be "unknown", "known", "shuttle_down"
144 self.inhabited = False # is it inhabited?
152 self.starbase = False
155 self.supernova = False
157 self.status = "secure" # Could be "secure", "distressed", "enslaved"
162 self.starbase = False
165 return "<%s,%s,%s>" % (self.klingons, self.starbase, self.stars)
167 def fill2d(size, fillfun):
168 "Fill an empty list in 2D."
170 for i in range(size):
172 for j in range(size):
173 lst[i].append(fillfun(i, j))
178 self.snap = False # snapshot taken
179 self.crew = 0 # crew complement
180 self.remkl = 0 # remaining klingons
181 self.nscrem = 0 # remaining super commanders
182 self.starkl = 0 # destroyed stars
183 self.basekl = 0 # destroyed bases
184 self.nromrem = 0 # Romulans remaining
185 self.nplankl = 0 # destroyed uninhabited planets
186 self.nworldkl = 0 # destroyed inhabited planets
187 self.planets = [] # Planet information
188 self.date = 0.0 # stardate
189 self.remres = 0 # remaining resources
190 self.remtime = 0 # remaining time
191 self.baseq = [] # Base quadrant coordinates
192 self.kcmdr = [] # Commander quadrant coordinates
193 self.kscmdr = Coord() # Supercommander quadrant coordinates
195 self.galaxy = fill2d(GALSIZE, lambda i_unused, j_unused: Quadrant())
197 self.chart = fill2d(GALSIZE, lambda i_unused, j_unused: Page())
201 self.date = None # A real number
202 self.quadrant = None # A coord structure
205 OPTION_ALL = 0xffffffff
206 OPTION_TTY = 0x00000001 # old interface
207 OPTION_CURSES = 0x00000002 # new interface
208 OPTION_IOMODES = 0x00000003 # cover both interfaces
209 OPTION_PLANETS = 0x00000004 # planets and mining
210 OPTION_THOLIAN = 0x00000008 # Tholians and their webs (UT 1979 version)
211 OPTION_THINGY = 0x00000010 # Space Thingy can shoot back (Stas, 2005)
212 OPTION_PROBE = 0x00000020 # deep-space probes (DECUS version, 1980)
213 OPTION_SHOWME = 0x00000040 # bracket Enterprise in chart
214 OPTION_RAMMING = 0x00000080 # enemies may ram Enterprise (Almy)
215 OPTION_MVBADDY = 0x00000100 # more enemies can move (Almy)
216 OPTION_BLKHOLE = 0x00000200 # black hole may timewarp you (Stas, 2005)
217 OPTION_BASE = 0x00000400 # bases have good shields (Stas, 2005)
218 OPTION_WORLDS = 0x00000800 # logic for inhabited worlds (ESR, 2006)
219 OPTION_AUTOSCAN = 0x00001000 # automatic LRSCAN before CHART (ESR, 2006)
220 OPTION_PLAIN = 0x01000000 # user chose plain game
221 OPTION_ALMY = 0x02000000 # user chose Almy variant
222 OPTION_COLOR = 0x04000000 # enable color display (experimental, ESR, 2010)
241 NDEVICES = 16 # Number of devices
251 return (game.damage[dev] != 0.0)
253 return not damaged(DRADIO) or game.condition=="docked"
255 # Define future events
256 FSPY = 0 # Spy event happens always (no future[] entry)
257 # can cause SC to tractor beam Enterprise
258 FSNOVA = 1 # Supernova
259 FTBEAM = 2 # Commander tractor beams Enterprise
260 FSNAP = 3 # Snapshot for time warp
261 FBATTAK = 4 # Commander attacks base
262 FCDBAS = 5 # Commander destroys base
263 FSCMOVE = 6 # Supercommander moves (might attack base)
264 FSCDBAS = 7 # Supercommander destroys base
265 FDSPROB = 8 # Move deep space probe
266 FDISTR = 9 # Emit distress call from an inhabited world
267 FENSLV = 10 # Inhabited word is enslaved */
268 FREPRO = 11 # Klingons build a ship in an enslaved system
271 # Abstract out the event handling -- underlying data structures will change
272 # when we implement stateful events
273 def findevent(evtype):
274 return game.future[evtype]
277 def __init__(self, etype=None, loc=None, power=None):
279 self.location = Coord()
284 self.power = power # enemy energy level
285 game.enemies.append(self)
287 motion = (loc != self.location)
288 if self.location.i is not None and self.location.j is not None:
291 game.quad[self.location.i][self.location.j] = '#'
293 game.quad[self.location.i][self.location.j] = '.'
295 self.location = copy.copy(loc)
296 game.quad[self.location.i][self.location.j] = self.type
297 self.kdist = self.kavgd = (game.sector - loc).distance()
299 self.location = Coord()
300 self.kdist = self.kavgd = None
301 game.enemies.remove(self)
304 return "<%s,%s.%f>" % (self.type, self.location, self.power) # For debugging
308 self.options = None # Game options
309 self.state = Snapshot() # A snapshot structure
310 self.snapsht = Snapshot() # Last snapshot taken for time-travel purposes
311 self.quad = None # contents of our quadrant
312 self.damage = [0.0] * NDEVICES # damage encountered
313 self.future = [] # future events
317 self.future.append(Event())
318 self.passwd = None # Self Destruct password
320 self.quadrant = None # where we are in the large
321 self.sector = None # where we are in the small
322 self.tholian = None # Tholian enemy object
323 self.base = None # position of base in current quadrant
324 self.battle = None # base coordinates being attacked
325 self.plnet = None # location of planet in quadrant
326 self.gamewon = False # Finished!
327 self.ididit = False # action taken -- allows enemy to attack
328 self.alive = False # we are alive (not killed)
329 self.justin = False # just entered quadrant
330 self.shldup = False # shields are up
331 self.shldchg = False # shield is changing (affects efficiency)
332 self.iscate = False # super commander is here
333 self.ientesc = False # attempted escape from supercommander
334 self.resting = False # rest time
335 self.icraft = False # Kirk in Galileo
336 self.landed = False # party on planet (true), on ship (false)
337 self.alldone = False # game is now finished
338 self.neutz = False # Romulan Neutral Zone
339 self.isarmed = False # probe is armed
340 self.inorbit = False # orbiting a planet
341 self.imine = False # mining
342 self.icrystl = False # dilithium crystals aboard
343 self.iseenit = False # seen base attack report
344 self.thawed = False # thawed game
345 self.condition = None # "green", "yellow", "red", "docked", "dead"
346 self.iscraft = None # "onship", "offship", "removed"
347 self.skill = None # Player skill level
348 self.inkling = 0 # initial number of klingons
349 self.inbase = 0 # initial number of bases
350 self.incom = 0 # initial number of commanders
351 self.inscom = 0 # initial number of commanders
352 self.inrom = 0 # initial number of commanders
353 self.instar = 0 # initial stars
354 self.intorps = 0 # initial/max torpedoes
355 self.torps = 0 # number of torpedoes
356 self.ship = 0 # ship type -- 'E' is Enterprise
357 self.abandoned = 0 # count of crew abandoned in space
358 self.length = 0 # length of game
359 self.klhere = 0 # klingons here
360 self.casual = 0 # causalties
361 self.nhelp = 0 # calls for help
362 self.nkinks = 0 # count of energy-barrier crossings
363 self.iplnet = None # planet # in quadrant
364 self.inplan = 0 # initial planets
365 self.irhere = 0 # Romulans in quadrant
366 self.isatb = 0 # =2 if super commander is attacking base
367 self.tourn = None # tournament number
368 self.nprobes = 0 # number of probes available
369 self.inresor = 0.0 # initial resources
370 self.intime = 0.0 # initial time
371 self.inenrg = 0.0 # initial/max energy
372 self.inshld = 0.0 # initial/max shield
373 self.inlsr = 0.0 # initial life support resources
374 self.indate = 0.0 # initial date
375 self.energy = 0.0 # energy level
376 self.shield = 0.0 # shield level
377 self.warpfac = 0.0 # warp speed
378 self.lsupres = 0.0 # life support reserves
379 self.optime = 0.0 # time taken by current operation
380 self.damfac = 0.0 # damage factor
381 self.lastchart = 0.0 # time star chart was last updated
382 self.cryprob = 0.0 # probability that crystal will work
383 self.probe = None # object holding probe course info
384 self.height = 0.0 # height of orbit around planet
385 self.score = 0.0 # overall score
386 self.perdate = 0.0 # rate of kills
387 self.idebug = False # Debugging instrumentation enabled?
388 self.statekscmdr = None # No SuperCommander coordinates yet.
390 # Stas thinks this should be (C expression):
391 # game.state.remkl + len(game.state.kcmdr) > 0 ?
392 # game.state.remres/(game.state.remkl + 4*len(game.state.kcmdr)) : 99
393 # He says the existing expression is prone to divide-by-zero errors
394 # after killing the last klingon when score is shown -- perhaps also
395 # if the only remaining klingon is SCOM.
396 self.state.remtime = self.state.remres/(self.state.remkl + 4*len(self.state.kcmdr))
422 return random.random() < p
424 def randrange(*args):
425 return random.randrange(*args)
430 v *= args[0] # from [0, args[0])
432 v = args[0] + v*(args[1]-args[0]) # from [args[0], args[1])
435 # Code from ai.c begins here
438 "Would this quadrant welcome another Klingon?"
439 return iq.valid_quadrant() and \
440 not game.state.galaxy[iq.i][iq.j].supernova and \
441 game.state.galaxy[iq.i][iq.j].klingons < MAXKLQUAD
443 def tryexit(enemy, look, irun):
444 "A bad guy attempts to bug out."
446 iq.i = game.quadrant.i+(look.i+(QUADSIZE-1))/QUADSIZE - 1
447 iq.j = game.quadrant.j+(look.j+(QUADSIZE-1))/QUADSIZE - 1
448 if not welcoming(iq):
450 if enemy.type == 'R':
451 return False # Romulans cannot escape!
453 # avoid intruding on another commander's territory
454 if enemy.type == 'C':
455 if iq in game.state.kcmdr:
457 # refuse to leave if currently attacking starbase
458 if game.battle == game.quadrant:
460 # don't leave if over 1000 units of energy
461 if enemy.power > 1000.0:
463 oldloc = copy.copy(enemy.location)
464 # handle local matters related to escape
467 if game.condition != "docked":
469 # Handle global matters related to escape
470 game.state.galaxy[game.quadrant.i][game.quadrant.j].klingons -= 1
471 game.state.galaxy[iq.i][iq.j].klingons += 1
472 if enemy.type == 'S':
476 schedule(FSCMOVE, 0.2777)
478 game.state.kscmdr = iq
480 for cmdr in game.state.kcmdr:
481 if cmdr == game.quadrant:
482 game.state.kcmdr.append(iq)
484 # report move out of quadrant.
485 return [(True, enemy, oldloc, ibq)]
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)
535 old_dist = enemy.kdist
536 mdist = int(old_dist + 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 * old_dist + 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:
616 return 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:
623 return 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
652 # Enemy moved, but is still in sector
653 return [(False, enemy, old_dist, goto)]
656 "Sequence Klingon tactical movement."
659 # Figure out which Klingon is the commander (or Supercommander)
662 if game.quadrant in game.state.kcmdr:
663 for enemy in game.enemies:
664 if enemy.type == 'C':
665 tacmoves += movebaddy(enemy)
666 if game.state.kscmdr == game.quadrant:
667 for enemy in game.enemies:
668 if enemy.type == 'S':
669 tacmoves += movebaddy(enemy)
671 # If skill level is high, move other Klingons and Romulans too!
672 # Move these last so they can base their actions on what the
674 if game.skill >= SKILL_EXPERT and (game.options & OPTION_MVBADDY):
675 for enemy in game.enemies:
676 if enemy.type in ('K', 'R'):
677 tacmoves += movebaddy(enemy)
680 def movescom(iq, avoid):
681 "Commander movement helper."
682 # Avoid quadrants with bases if we want to avoid Enterprise
683 if not welcoming(iq) or (avoid and iq in game.state.baseq):
685 if game.justin and not game.iscate:
688 game.state.galaxy[game.state.kscmdr.i][game.state.kscmdr.j].klingons -= 1
689 game.state.kscmdr = iq
690 game.state.galaxy[game.state.kscmdr.i][game.state.kscmdr.j].klingons += 1
691 if game.state.kscmdr == game.quadrant:
692 # SC has scooted, remove him from current quadrant
697 for enemy in game.enemies:
698 if enemy.type == 'S':
701 if game.condition != "docked":
704 # check for a helpful planet
705 for i in range(game.inplan):
706 if game.state.planets[i].quadrant == game.state.kscmdr and \
707 game.state.planets[i].crystals == "present":
709 game.state.planets[i].pclass = "destroyed"
710 game.state.galaxy[game.state.kscmdr.i][game.state.kscmdr.j].planet = None
713 prout(_("Lt. Uhura- \"Captain, Starfleet Intelligence reports"))
714 proutn(_(" a planet in Quadrant %s has been destroyed") % game.state.kscmdr)
715 prout(_(" by the Super-commander.\""))
717 return True # looks good!
719 def supercommander():
720 "Move the Super Commander."
727 prout("== SUPERCOMMANDER")
728 # Decide on being active or passive
729 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 \
730 (game.state.date-game.indate) < 3.0)
731 if not game.iscate and avoid:
732 # compute move away from Enterprise
733 idelta = game.state.kscmdr-game.quadrant
734 if idelta.distance() > 2.0:
736 idelta.i = game.state.kscmdr.j-game.quadrant.j
737 idelta.j = game.quadrant.i-game.state.kscmdr.i
739 # compute distances to starbases
740 if not game.state.baseq:
744 sc = game.state.kscmdr
745 for (i, base) in enumerate(game.state.baseq):
746 basetbl.append((i, (base - sc).distance()))
747 if game.state.baseq > 1:
748 basetbl.sort(lambda x, y: cmp(x[1], y[1]))
749 # look for nearest base without a commander, no Enterprise, and
750 # without too many Klingons, and not already under attack.
751 ifindit = iwhichb = 0
752 for (i2, base) in enumerate(game.state.baseq):
753 i = basetbl[i2][0] # bug in original had it not finding nearest
754 if base == game.quadrant or base == game.battle or not welcoming(base):
756 # if there is a commander, and no other base is appropriate,
757 # we will take the one with the commander
758 for cmdr in game.state.kcmdr:
759 if base == cmdr and ifindit != 2:
763 else: # no commander -- use this one
768 return # Nothing suitable -- wait until next time
769 ibq = game.state.baseq[iwhichb]
770 # decide how to move toward base
771 idelta = ibq - game.state.kscmdr
772 # Maximum movement is 1 quadrant in either or both axes
773 idelta = idelta.sgn()
774 # try moving in both x and y directions
775 # there was what looked like a bug in the Almy C code here,
776 # but it might be this translation is just wrong.
777 iq = game.state.kscmdr + idelta
778 if not movescom(iq, avoid):
779 # failed -- try some other maneuvers
780 if idelta.i == 0 or idelta.j == 0:
783 iq.j = game.state.kscmdr.j + 1
784 if not movescom(iq, avoid):
785 iq.j = game.state.kscmdr.j - 1
788 iq.i = game.state.kscmdr.i + 1
789 if not movescom(iq, avoid):
790 iq.i = game.state.kscmdr.i - 1
793 # try moving just in x or y
794 iq.j = game.state.kscmdr.j
795 if not movescom(iq, avoid):
796 iq.j = game.state.kscmdr.j + idelta.j
797 iq.i = game.state.kscmdr.i
800 if len(game.state.baseq) == 0:
803 for ibq in game.state.baseq:
804 if ibq == game.state.kscmdr and game.state.kscmdr == game.battle:
807 return # no, don't attack base!
810 schedule(FSCDBAS, randreal(1.0, 3.0))
811 if is_scheduled(FCDBAS):
812 postpone(FSCDBAS, scheduled(FCDBAS)-game.state.date)
813 if not communicating():
817 prout(_("Lt. Uhura- \"Captain, the starbase in Quadrant %s") \
819 prout(_(" reports that it is under attack from the Klingon Super-commander."))
820 proutn(_(" It can survive until stardate %d.\"") \
821 % int(scheduled(FSCDBAS)))
824 prout(_("Mr. Spock- \"Captain, shall we cancel the rest period?\""))
828 game.optime = 0.0 # actually finished
830 # Check for intelligence report
831 if not game.idebug and \
833 (not communicating()) or \
834 not game.state.galaxy[game.state.kscmdr.i][game.state.kscmdr.j].charted):
837 prout(_("Lt. Uhura- \"Captain, Starfleet Intelligence reports"))
838 proutn(_(" the Super-commander is in Quadrant %s,") % game.state.kscmdr)
843 if not game.tholian or game.justin:
846 if game.tholian.location.i == 0 and game.tholian.location.j == 0:
849 elif game.tholian.location.i == 0 and game.tholian.location.j == QUADSIZE-1:
852 elif game.tholian.location.i == QUADSIZE-1 and game.tholian.location.j == QUADSIZE-1:
855 elif game.tholian.location.i == QUADSIZE-1 and game.tholian.location.j == 0:
859 # something is wrong!
860 game.tholian.move(None)
861 prout("***Internal error: Tholian in a bad spot.")
863 # do nothing if we are blocked
864 if game.quad[tid.i][tid.j] not in ('.', '#'):
866 here = copy.copy(game.tholian.location)
867 delta = (tid - game.tholian.location).sgn()
869 while here.i != tid.i:
871 if game.quad[here.i][here.j] == '.':
872 game.tholian.move(here)
874 while here.j != tid.j:
876 if game.quad[here.i][here.j] == '.':
877 game.tholian.move(here)
878 # check to see if all holes plugged
879 for i in range(QUADSIZE):
880 if game.quad[0][i] != '#' and game.quad[0][i] != 'T':
882 if game.quad[QUADSIZE-1][i] != '#' and game.quad[QUADSIZE-1][i] != 'T':
884 if game.quad[i][0] != '#' and game.quad[i][0] != 'T':
886 if game.quad[i][QUADSIZE-1] != '#' and game.quad[i][QUADSIZE-1] != 'T':
888 # All plugged up -- Tholian splits
889 game.quad[game.tholian.location.i][game.tholian.location.j] = '#'
891 prout(crmena(True, 'T', "sector", game.tholian) + _(" completes web."))
892 game.tholian.move(None)
895 # Code from battle.c begins here
897 def doshield(shraise):
898 "Change shield status."
904 key = scanner.nexttok()
906 if scanner.sees("transfer"):
910 prout(_("Shields damaged and down."))
912 if scanner.sees("up"):
914 elif scanner.sees("down"):
917 proutn(_("Do you wish to change shield energy? "))
920 elif damaged(DSHIELD):
921 prout(_("Shields damaged and down."))
924 proutn(_("Shields are up. Do you want them down? "))
931 proutn(_("Shields are down. Do you want them up? "))
937 if action == "SHUP": # raise shields
939 prout(_("Shields already up."))
943 if game.condition != "docked":
945 prout(_("Shields raised."))
948 prout(_("Shields raising uses up last of energy."))
953 elif action == "SHDN":
955 prout(_("Shields already down."))
959 prout(_("Shields lowered."))
962 elif action == "NRG":
963 while scanner.nexttok() != "IHREAL":
965 proutn(_("Energy to transfer to shields- "))
970 if nrg > game.energy:
971 prout(_("Insufficient ship energy."))
974 if game.shield+nrg >= game.inshld:
975 prout(_("Shield energy maximized."))
976 if game.shield+nrg > game.inshld:
977 prout(_("Excess energy requested returned to ship energy"))
978 game.energy -= game.inshld-game.shield
979 game.shield = game.inshld
981 if nrg < 0.0 and game.energy-nrg > game.inenrg:
982 # Prevent shield drain loophole
984 prout(_("Engineering to bridge--"))
985 prout(_(" Scott here. Power circuit problem, Captain."))
986 prout(_(" I can't drain the shields."))
989 if game.shield+nrg < 0:
990 prout(_("All shield energy transferred to ship."))
991 game.energy += game.shield
994 proutn(_("Scotty- \""))
996 prout(_("Transferring energy to shields.\""))
998 prout(_("Draining energy from shields.\""))
1004 "Choose a device to damage, at random."
1006 105, # DSRSENS: short range scanners 10.5%
1007 105, # DLRSENS: long range scanners 10.5%
1008 120, # DPHASER: phasers 12.0%
1009 120, # DPHOTON: photon torpedoes 12.0%
1010 25, # DLIFSUP: life support 2.5%
1011 65, # DWARPEN: warp drive 6.5%
1012 70, # DIMPULS: impulse engines 6.5%
1013 145, # DSHIELD: deflector shields 14.5%
1014 30, # DRADIO: subspace radio 3.0%
1015 45, # DSHUTTL: shuttle 4.5%
1016 15, # DCOMPTR: computer 1.5%
1017 20, # NAVCOMP: navigation system 2.0%
1018 75, # DTRANSP: transporter 7.5%
1019 20, # DSHCTRL: high-speed shield controller 2.0%
1020 10, # DDRAY: death ray 1.0%
1021 30, # DDSP: deep-space probes 3.0%
1023 assert(sum(weights) == 1000)
1024 idx = randrange(1000)
1026 for (i, w) in enumerate(weights):
1030 return None # we should never get here
1032 def collision(rammed, enemy):
1033 "Collision handling for rammong events."
1034 prouts(_("***RED ALERT! RED ALERT!"))
1036 prout(_("***COLLISION IMMINENT."))
1040 hardness = {'R':1.5, 'C':2.0, 'S':2.5, 'T':0.5, '?':4.0}.get(enemy.type, 1.0)
1042 proutn(_(" rammed by "))
1045 proutn(crmena(False, enemy.type, "sector", enemy.location))
1047 proutn(_(" (original position)"))
1049 deadkl(enemy.location, enemy.type, game.sector)
1050 proutn("***" + crmshp() + " heavily damaged.")
1051 icas = randrange(10, 30)
1052 prout(_("***Sickbay reports %d casualties") % icas)
1054 game.state.crew -= icas
1055 # In the pre-SST2K version, all devices got equiprobably damaged,
1056 # which was silly. Instead, pick up to half the devices at
1057 # random according to our weighting table,
1058 ncrits = randrange(NDEVICES/2)
1062 if game.damage[dev] < 0:
1064 extradm = (10.0*hardness*randreal()+1.0)*game.damfac
1065 # Damage for at least time of travel!
1066 game.damage[dev] += game.optime + extradm
1068 prout(_("***Shields are down."))
1069 if game.state.remkl + len(game.state.kcmdr) + game.state.nscrem:
1076 def torpedo(origin, bearing, dispersion, number, nburst):
1077 "Let a photon torpedo fly"
1078 if not damaged(DSRSENS) or game.condition == "docked":
1079 setwnd(srscan_window)
1081 setwnd(message_window)
1082 ac = bearing + 0.25*dispersion # dispersion is a random variable
1083 bullseye = (15.0 - bearing)*0.5235988
1084 track = course(bearing=ac, distance=QUADSIZE, origin=cartesian(origin))
1085 bumpto = Coord(0, 0)
1086 # Loop to move a single torpedo
1087 setwnd(message_window)
1088 for step in range(1, QUADSIZE*2):
1089 if not track.nexttok():
1092 if not w.valid_sector():
1094 iquad = game.quad[w.i][w.j]
1095 tracktorpedo(w, step, number, nburst, iquad)
1099 setwnd(message_window)
1100 if not damaged(DSRSENS) or game.condition == "docked":
1101 skip(1) # start new line after text track
1102 if iquad in ('E', 'F'): # Hit our ship
1104 prout(_("Torpedo hits %s.") % crmshp())
1105 hit = 700.0 + randreal(100) - \
1106 1000.0 * (w-origin).distance() * math.fabs(math.sin(bullseye-track.angle))
1107 newcnd() # we're blown out of dock
1108 if game.landed or game.condition == "docked":
1109 return hit # Cheat if on a planet
1110 # In the C/FORTRAN version, dispersion was 2.5 radians, which
1111 # is 143 degrees, which is almost exactly 4.8 clockface units
1112 displacement = course(track.bearing+randreal(-2.4, 2.4), distance=2**0.5)
1113 displacement.nexttok()
1114 bumpto = displacement.sector()
1115 if not bumpto.valid_sector():
1117 if game.quad[bumpto.i][bumpto.j] == ' ':
1120 if game.quad[bumpto.i][bumpto.j] != '.':
1121 # can't move into object
1123 game.sector = bumpto
1125 game.quad[w.i][w.j] = '.'
1126 game.quad[bumpto.i][bumpto.j] = iquad
1127 prout(_(" displaced by blast to Sector %s ") % bumpto)
1128 for enemy in game.enemies:
1129 enemy.kdist = enemy.kavgd = (game.sector-enemy.location).distance()
1132 elif iquad in ('C', 'S', 'R', 'K'): # Hit a regular enemy
1134 if iquad in ('C', 'S') and withprob(0.05):
1135 prout(crmena(True, iquad, "sector", w) + _(" uses anti-photon device;"))
1136 prout(_(" torpedo neutralized."))
1138 for enemy in game.enemies:
1139 if w == enemy.location:
1140 kp = math.fabs(enemy.power)
1141 h1 = 700.0 + randrange(100) - \
1142 1000.0 * (w-origin).distance() * math.fabs(math.sin(bullseye-track.angle))
1150 if enemy.power == 0:
1153 proutn(crmena(True, iquad, "sector", w))
1154 displacement = course(track.bearing+randreal(-2.4, 2.4), distance=2**0.5)
1155 displacement.nexttok()
1156 bumpto = displacement.sector()
1157 if not bumpto.valid_sector():
1158 prout(_(" damaged but not destroyed."))
1160 if game.quad[bumpto.i][bumpto.j] == ' ':
1161 prout(_(" buffeted into black hole."))
1162 deadkl(w, iquad, bumpto)
1163 if game.quad[bumpto.i][bumpto.j] != '.':
1164 prout(_(" damaged but not destroyed."))
1166 prout(_(" damaged-- displaced by blast to Sector %s ")%bumpto)
1167 enemy.location = bumpto
1168 game.quad[w.i][w.j] = '.'
1169 game.quad[bumpto.i][bumpto.j] = iquad
1170 for enemy in game.enemies:
1171 enemy.kdist = enemy.kavgd = (game.sector-enemy.location).distance()
1175 prout("Internal error, no enemy where expected!")
1178 elif iquad == 'B': # Hit a base
1180 prout(_("***STARBASE DESTROYED.."))
1181 game.state.baseq = filter(lambda x: x != game.quadrant, game.state.baseq)
1182 game.quad[w.i][w.j] = '.'
1183 game.base.invalidate()
1184 game.state.galaxy[game.quadrant.i][game.quadrant.j].starbase = False
1185 game.state.chart[game.quadrant.i][game.quadrant.j].starbase = False
1186 game.state.basekl += 1
1189 elif iquad == 'P': # Hit a planet
1190 prout(crmena(True, iquad, "sector", w) + _(" destroyed."))
1191 game.state.nplankl += 1
1192 game.state.galaxy[game.quadrant.i][game.quadrant.j].planet = None
1193 game.iplnet.pclass = "destroyed"
1195 game.plnet.invalidate()
1196 game.quad[w.i][w.j] = '.'
1198 # captain perishes on planet
1201 elif iquad == '@': # Hit an inhabited world -- very bad!
1202 prout(crmena(True, iquad, "sector", w) + _(" destroyed."))
1203 game.state.nworldkl += 1
1204 game.state.galaxy[game.quadrant.i][game.quadrant.j].planet = None
1205 game.iplnet.pclass = "destroyed"
1207 game.plnet.invalidate()
1208 game.quad[w.i][w.j] = '.'
1210 # captain perishes on planet
1212 prout(_("The torpedo destroyed an inhabited planet."))
1214 elif iquad == '*': # Hit a star
1218 prout(crmena(True, '*', "sector", w) + _(" unaffected by photon blast."))
1220 elif iquad == '?': # Hit a thingy
1221 if not (game.options & OPTION_THINGY) or withprob(0.3):
1223 prouts(_("AAAAIIIIEEEEEEEEAAAAAAAAUUUUUGGGGGHHHHHHHHHHHH!!!"))
1225 prouts(_(" HACK! HACK! HACK! *CHOKE!* "))
1227 proutn(_("Mr. Spock-"))
1228 prouts(_(" \"Fascinating!\""))
1232 # Stas Sergeev added the possibility that
1233 # you can shove the Thingy and piss it off.
1234 # It then becomes an enemy and may fire at you.
1237 elif iquad == ' ': # Black hole
1239 prout(crmena(True, ' ', "sector", w) + _(" swallows torpedo."))
1241 elif iquad == '#': # hit the web
1243 prout(_("***Torpedo absorbed by Tholian web."))
1245 elif iquad == 'T': # Hit a Tholian
1246 h1 = 700.0 + randrange(100) - \
1247 1000.0 * (w-origin).distance() * math.fabs(math.sin(bullseye-track.angle))
1250 game.quad[w.i][w.j] = '.'
1255 proutn(crmena(True, 'T', "sector", w))
1257 prout(_(" survives photon blast."))
1259 prout(_(" disappears."))
1260 game.tholian.move(None)
1261 game.quad[w.i][w.j] = '#'
1266 proutn("Don't know how to handle torpedo collision with ")
1267 proutn(crmena(True, iquad, "sector", w))
1272 setwnd(message_window)
1273 prout(_("Torpedo missed."))
1277 "Critical-hit resolution."
1278 if hit < (275.0-25.0*game.skill)*randreal(1.0, 1.5):
1280 ncrit = int(1.0 + hit/(500.0+randreal(100)))
1281 proutn(_("***CRITICAL HIT--"))
1282 # Select devices and cause damage
1287 # Cheat to prevent shuttle damage unless on ship
1288 if not (game.damage[j]<0.0 or (j == DSHUTTL and game.iscraft != "onship")):
1291 extradm = (hit*game.damfac)/(ncrit*randreal(75, 100))
1292 game.damage[j] += extradm
1295 for (i, j) in enumerate(cdam):
1297 if skipcount % 3 == 2 and i < len(cdam)-1:
1302 prout(_(" damaged."))
1303 if damaged(DSHIELD) and game.shldup:
1304 prout(_("***Shields knocked down."))
1307 def attack(torps_ok):
1308 # bad guy attacks us
1309 # torps_ok == False forces use of phasers in an attack
1310 # game could be over at this point, check
1320 prout("=== ATTACK!")
1321 # Tholian gets to move before attacking
1324 # if you have just entered the RNZ, you'll get a warning
1325 if game.neutz: # The one chance not to be attacked
1328 # commanders get a chance to tac-move towards you
1329 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:
1330 for (bugout, enemy, old, goto) in moveklings():
1332 # we know about this if either short or long range
1333 # sensors are working
1334 if damaged(DSRSENS) and damaged(DLRSENS) \
1335 and game.condition != "docked":
1336 prout(crmena(True, enemy.type, "sector", old) + \
1337 (_(" escapes to Quadrant %s (and regains strength).") % goto))
1338 else: # Enemy still in-sector
1339 if enemy.move(goto):
1340 if not damaged(DSRSENS) or game.condition == "docked":
1341 proutn(_("*** %s from Sector %s") % (cramen(enemy.type), enemy.location))
1342 if enemy.kdist < old:
1343 proutn(_(" advances to "))
1345 proutn(_(" retreats to "))
1346 prout("Sector %s." % goto)
1348 # if no enemies remain after movement, we're done
1349 if len(game.enemies) == 0 or (len(game.enemies) == 1 and thing.at(game.quadrant) and not thing.angered):
1351 # set up partial hits if attack happens during shield status change
1352 pfac = 1.0/game.inshld
1354 chgfac = 0.25 + randreal(0.5)
1356 # message verbosity control
1357 if game.skill <= SKILL_FAIR:
1359 for enemy in game.enemies:
1361 continue # too weak to attack
1362 # compute hit strength and diminish shield power
1364 # Increase chance of photon torpedos if docked or enemy energy is low
1365 if game.condition == "docked":
1367 if enemy.power < 500:
1369 if enemy.type == 'T' or (enemy.type == '?' and not thing.angered):
1371 # different enemies have different probabilities of throwing a torp
1372 usephasers = not torps_ok or \
1373 (enemy.type == 'K' and r > 0.0005) or \
1374 (enemy.type == 'C' and r > 0.015) or \
1375 (enemy.type == 'R' and r > 0.3) or \
1376 (enemy.type == 'S' and r > 0.07) or \
1377 (enemy.type == '?' and r > 0.05)
1378 if usephasers: # Enemy uses phasers
1379 if game.condition == "docked":
1380 continue # Don't waste the effort!
1381 attempt = True # Attempt to attack
1382 dustfac = randreal(0.8, 0.85)
1383 hit = enemy.power*math.pow(dustfac, enemy.kavgd)
1385 else: # Enemy uses photon torpedo
1386 # We should be able to make the bearing() method work here
1387 pcourse = 1.90985*math.atan2(game.sector.j-enemy.location.j, enemy.location.i-game.sector.i)
1389 proutn(_("***TORPEDO INCOMING"))
1390 if not damaged(DSRSENS):
1391 proutn(_(" From ") + crmena(False, enemy.type, where, enemy.location))
1394 dispersion = (randreal()+randreal())*0.5 - 0.5
1395 dispersion += 0.002*enemy.power*dispersion
1396 hit = torpedo(enemy.location, pcourse, dispersion, number=1, nburst=1)
1397 if (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem) == 0:
1398 finish(FWON) # Klingons did themselves in!
1399 if game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova or game.alldone:
1400 return # Supernova or finished
1403 # incoming phaser or torpedo, shields may dissipate it
1404 if game.shldup or game.shldchg or game.condition == "docked":
1405 # shields will take hits
1406 propor = pfac * game.shield
1407 if game.condition == "docked":
1411 hitsh = propor*chgfac*hit+1.0
1413 if absorb > game.shield:
1414 absorb = game.shield
1415 game.shield -= absorb
1417 # taking a hit blasts us out of a starbase dock
1418 if game.condition == "docked":
1420 # but the shields may take care of it
1421 if propor > 0.1 and hit < 0.005*game.energy:
1423 # hit from this opponent got through shields, so take damage
1425 proutn(_("%d unit hit") % int(hit))
1426 if (damaged(DSRSENS) and usephasers) or game.skill<=SKILL_FAIR:
1427 proutn(_(" on the ") + crmshp())
1428 if not damaged(DSRSENS) and usephasers:
1429 prout(_(" from ") + crmena(False, enemy.type, where, enemy.location))
1431 # Decide if hit is critical
1437 if game.energy <= 0:
1438 # Returning home upon your shield, not with it...
1441 if not attempt and game.condition == "docked":
1442 prout(_("***Enemies decide against attacking your ship."))
1443 percent = 100.0*pfac*game.shield+0.5
1445 # Shields fully protect ship
1446 proutn(_("Enemy attack reduces shield strength to "))
1448 # Emit message if starship suffered hit(s)
1450 proutn(_("Energy left %2d shields ") % int(game.energy))
1453 elif not damaged(DSHIELD):
1456 proutn(_("damaged, "))
1457 prout(_("%d%%, torpedoes left %d") % (percent, game.torps))
1458 # Check if anyone was hurt
1459 if hitmax >= 200 or hittot >= 500:
1460 icas = randrange(int(hittot * 0.015))
1463 prout(_("Mc Coy- \"Sickbay to bridge. We suffered %d casualties") % icas)
1464 prout(_(" in that last attack.\""))
1466 game.state.crew -= icas
1467 # After attack, reset average distance to enemies
1468 for enemy in game.enemies:
1469 enemy.kavgd = enemy.kdist
1473 def deadkl(w, etype, mv):
1474 "Kill a Klingon, Tholian, Romulan, or Thingy."
1475 # Added mv to allow enemy to "move" before dying
1476 proutn(crmena(True, etype, "sector", mv))
1477 # Decide what kind of enemy it is and update appropriately
1479 # Chalk up a Romulan
1480 game.state.galaxy[game.quadrant.i][game.quadrant.j].romulans -= 1
1482 game.state.nromrem -= 1
1491 # Killed some type of Klingon
1492 game.state.galaxy[game.quadrant.i][game.quadrant.j].klingons -= 1
1495 game.state.kcmdr.remove(game.quadrant)
1497 if game.state.kcmdr:
1498 schedule(FTBEAM, expran(1.0*game.incom/len(game.state.kcmdr)))
1499 if is_scheduled(FCDBAS) and game.battle == game.quadrant:
1502 game.state.remkl -= 1
1504 game.state.nscrem -= 1
1505 game.state.kscmdr.invalidate()
1510 # For each kind of enemy, finish message to player
1511 prout(_(" destroyed."))
1512 if (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem) == 0:
1515 # Remove enemy ship from arrays describing local conditions
1516 for e in game.enemies:
1523 "Return None if target is invalid, otherwise return a course angle."
1524 if not w.valid_sector():
1528 # C code this was translated from is wacky -- why the sign reversal?
1529 delta.j = (w.j - game.sector.j)
1530 delta.i = (game.sector.i - w.i)
1531 if delta == Coord(0, 0):
1533 prout(_("Spock- \"Bridge to sickbay. Dr. McCoy,"))
1534 prout(_(" I recommend an immediate review of"))
1535 prout(_(" the Captain's psychological profile.\""))
1538 return delta.bearing()
1541 "Launch photon torpedo salvo."
1544 if damaged(DPHOTON):
1545 prout(_("Photon tubes damaged."))
1549 prout(_("No torpedoes left."))
1552 # First, get torpedo count
1555 if scanner.token == "IHALPHA":
1558 elif scanner.token == "IHEOL" or not scanner.waiting():
1559 prout(_("%d torpedoes left.") % game.torps)
1561 proutn(_("Number of torpedoes to fire- "))
1562 continue # Go back around to get a number
1563 else: # key == "IHREAL"
1565 if n <= 0: # abort command
1570 prout(_("Maximum of %d torpedoes per burst.") % MAXBURST)
1573 scanner.chew() # User requested more torps than available
1574 continue # Go back around
1575 break # All is good, go to next stage
1579 key = scanner.nexttok()
1580 if i == 0 and key == "IHEOL":
1581 break # no coordinate waiting, we will try prompting
1582 if i == 1 and key == "IHEOL":
1583 # direct all torpedoes at one target
1585 target.append(target[0])
1586 tcourse.append(tcourse[0])
1589 scanner.push(scanner.token)
1590 target.append(scanner.getcoord())
1591 if target[-1] == None:
1593 tcourse.append(targetcheck(target[-1]))
1594 if tcourse[-1] == None:
1597 if len(target) == 0:
1598 # prompt for each one
1600 proutn(_("Target sector for torpedo number %d- ") % (i+1))
1602 target.append(scanner.getcoord())
1603 if target[-1] == None:
1605 tcourse.append(targetcheck(target[-1]))
1606 if tcourse[-1] == None:
1609 # Loop for moving <n> torpedoes
1611 if game.condition != "docked":
1613 dispersion = (randreal()+randreal())*0.5 -0.5
1614 if math.fabs(dispersion) >= 0.47:
1616 dispersion *= randreal(1.2, 2.2)
1618 prouts(_("***TORPEDO NUMBER %d MISFIRES") % (i+1))
1620 prouts(_("***TORPEDO MISFIRES."))
1623 prout(_(" Remainder of burst aborted."))
1625 prout(_("***Photon tubes damaged by misfire."))
1626 game.damage[DPHOTON] = game.damfac * randreal(1.0, 3.0)
1628 if game.shldup or game.condition == "docked":
1629 dispersion *= 1.0 + 0.0001*game.shield
1630 torpedo(game.sector, tcourse[i], dispersion, number=i, nburst=n)
1631 if game.alldone or game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
1633 if (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem)<=0:
1637 "Check for phasers overheating."
1639 checkburn = (rpow-1500.0)*0.00038
1640 if withprob(checkburn):
1641 prout(_("Weapons officer Sulu- \"Phasers overheated, sir.\""))
1642 game.damage[DPHASER] = game.damfac* randreal(1.0, 2.0) * (1.0+checkburn)
1644 def checkshctrl(rpow):
1645 "Check shield control."
1648 prout(_("Shields lowered."))
1650 # Something bad has happened
1651 prouts(_("***RED ALERT! RED ALERT!"))
1653 hit = rpow*game.shield/game.inshld
1654 game.energy -= rpow+hit*0.8
1655 game.shield -= hit*0.2
1656 if game.energy <= 0.0:
1657 prouts(_("Sulu- \"Captain! Shield malf***********************\""))
1662 prouts(_("Sulu- \"Captain! Shield malfunction! Phaser fire contained!\""))
1664 prout(_("Lt. Uhura- \"Sir, all decks reporting damage.\""))
1665 icas = randrange(int(hit*0.012))
1670 prout(_("McCoy to bridge- \"Severe radiation burns, Jim."))
1671 prout(_(" %d casualties so far.\"") % icas)
1673 game.state.crew -= icas
1675 prout(_("Phaser energy dispersed by shields."))
1676 prout(_("Enemy unaffected."))
1681 "Register a phaser hit on Klingons and Romulans."
1688 dustfac = randreal(0.9, 1.0)
1689 hit = wham*math.pow(dustfac, game.enemies[kk].kdist)
1690 kpini = game.enemies[kk].power
1691 kp = math.fabs(kpini)
1692 if PHASEFAC*hit < kp:
1694 if game.enemies[kk].power < 0:
1695 game.enemies[kk].power -= -kp
1697 game.enemies[kk].power -= kp
1698 kpow = game.enemies[kk].power
1699 w = game.enemies[kk].location
1701 if not damaged(DSRSENS):
1703 proutn(_("%d unit hit on ") % int(hit))
1705 proutn(_("Very small hit on "))
1706 ienm = game.quad[w.i][w.j]
1709 proutn(crmena(False, ienm, "sector", w))
1713 if (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem)==0:
1717 kk -= 1 # don't do the increment
1719 else: # decide whether or not to emasculate klingon
1720 if kpow > 0 and withprob(0.9) and kpow <= randreal(0.4, 0.8)*kpini:
1721 prout(_("***Mr. Spock- \"Captain, the vessel at Sector %s")%w)
1722 prout(_(" has just lost its firepower.\""))
1723 game.enemies[kk].power = -kpow
1728 "Fire phasers at bad guys."
1732 irec = 0 # Cheating inhibitor
1741 # SR sensors and Computer are needed for automode
1742 if damaged(DSRSENS) or damaged(DCOMPTR):
1744 if game.condition == "docked":
1745 prout(_("Phasers can't be fired through base shields."))
1748 if damaged(DPHASER):
1749 prout(_("Phaser control damaged."))
1753 if damaged(DSHCTRL):
1754 prout(_("High speed shield control damaged."))
1757 if game.energy <= 200.0:
1758 prout(_("Insufficient energy to activate high-speed shield control."))
1761 prout(_("Weapons Officer Sulu- \"High-speed shield control enabled, sir.\""))
1763 # Original code so convoluted, I re-did it all
1764 # (That was Tom Almy talking about the C code, I think -- ESR)
1765 while automode == "NOTSET":
1766 key = scanner.nexttok()
1767 if key == "IHALPHA":
1768 if scanner.sees("manual"):
1769 if len(game.enemies)==0:
1770 prout(_("There is no enemy present to select."))
1773 automode = "AUTOMATIC"
1776 key = scanner.nexttok()
1777 elif scanner.sees("automatic"):
1778 if (not itarg) and len(game.enemies) != 0:
1779 automode = "FORCEMAN"
1781 if len(game.enemies)==0:
1782 prout(_("Energy will be expended into space."))
1783 automode = "AUTOMATIC"
1784 key = scanner.nexttok()
1785 elif scanner.sees("no"):
1790 elif key == "IHREAL":
1791 if len(game.enemies)==0:
1792 prout(_("Energy will be expended into space."))
1793 automode = "AUTOMATIC"
1795 automode = "FORCEMAN"
1797 automode = "AUTOMATIC"
1800 if len(game.enemies)==0:
1801 prout(_("Energy will be expended into space."))
1802 automode = "AUTOMATIC"
1804 automode = "FORCEMAN"
1806 proutn(_("Manual or automatic? "))
1811 if automode == "AUTOMATIC":
1812 if key == "IHALPHA" and scanner.sees("no"):
1814 key = scanner.nexttok()
1815 if key != "IHREAL" and len(game.enemies) != 0:
1816 prout(_("Phasers locked on target. Energy available: %.2f")%avail)
1821 for i in range(len(game.enemies)):
1822 irec += math.fabs(game.enemies[i].power)/(PHASEFAC*math.pow(0.90, game.enemies[i].kdist))*randreal(1.01, 1.06) + 1.0
1824 proutn(_("%d units required. ") % irec)
1826 proutn(_("Units to fire= "))
1827 key = scanner.nexttok()
1832 proutn(_("Energy available= %.2f") % avail)
1835 if not rpow > avail:
1841 key = scanner.nexttok()
1842 if key == "IHALPHA" and scanner.sees("no"):
1845 game.energy -= 200 # Go and do it!
1846 if checkshctrl(rpow):
1851 if len(game.enemies):
1854 for i in range(len(game.enemies)):
1858 hits[i] = math.fabs(game.enemies[i].power)/(PHASEFAC*math.pow(0.90, game.enemies[i].kdist))
1859 over = randreal(1.01, 1.06) * hits[i]
1861 powrem -= hits[i] + over
1862 if powrem <= 0 and temp < hits[i]:
1871 if extra > 0 and not game.alldone:
1873 proutn(_("*** Tholian web absorbs "))
1874 if len(game.enemies)>0:
1875 proutn(_("excess "))
1876 prout(_("phaser energy."))
1878 prout(_("%d expended on empty space.") % int(extra))
1879 elif automode == "FORCEMAN":
1882 if damaged(DCOMPTR):
1883 prout(_("Battle computer damaged, manual fire only."))
1886 prouts(_("---WORKING---"))
1888 prout(_("Short-range-sensors-damaged"))
1889 prout(_("Insufficient-data-for-automatic-phaser-fire"))
1890 prout(_("Manual-fire-must-be-used"))
1892 elif automode == "MANUAL":
1894 for k in range(len(game.enemies)):
1895 aim = game.enemies[k].location
1896 ienm = game.quad[aim.i][aim.j]
1898 proutn(_("Energy available= %.2f") % (avail-0.006))
1902 if damaged(DSRSENS) and \
1903 not game.sector.distance(aim)<2**0.5 and ienm in ('C', 'S'):
1904 prout(cramen(ienm) + _(" can't be located without short range scan."))
1907 hits[k] = 0 # prevent overflow -- thanks to Alexei Voitenko
1912 if itarg and k > kz:
1913 irec = (abs(game.enemies[k].power)/(PHASEFAC*math.pow(0.9, game.enemies[k].kdist))) * randreal(1.01, 1.06) + 1.0
1916 if not damaged(DCOMPTR):
1921 proutn(_("units to fire at %s- ") % crmena(False, ienm, "sector", aim))
1922 key = scanner.nexttok()
1923 if key == "IHALPHA" and scanner.sees("no"):
1925 key = scanner.nexttok()
1927 if key == "IHALPHA":
1931 if k == 1: # Let me say I'm baffled by this
1934 if scanner.real < 0:
1938 hits[k] = scanner.real
1939 rpow += scanner.real
1940 # If total requested is too much, inform and start over
1942 prout(_("Available energy exceeded -- try again."))
1945 key = scanner.nexttok() # scan for next value
1948 # zero energy -- abort
1951 if key == "IHALPHA" and scanner.sees("no"):
1956 game.energy -= 200.0
1957 if checkshctrl(rpow):
1961 # Say shield raised or malfunction, if necessary
1968 prout(_("Sulu- \"Sir, the high-speed shield control has malfunctioned . . ."))
1969 prouts(_(" CLICK CLICK POP . . ."))
1970 prout(_(" No response, sir!"))
1973 prout(_("Shields raised."))
1978 # Code from events,c begins here.
1980 # This isn't a real event queue a la BSD Trek yet -- you can only have one
1981 # event of each type active at any given time. Mostly these means we can
1982 # only have one FDISTR/FENSLV/FREPRO sequence going at any given time
1983 # BSD Trek, from which we swiped the idea, can have up to 5.
1985 def unschedule(evtype):
1986 "Remove an event from the schedule."
1987 game.future[evtype].date = FOREVER
1988 return game.future[evtype]
1990 def is_scheduled(evtype):
1991 "Is an event of specified type scheduled."
1992 return game.future[evtype].date != FOREVER
1994 def scheduled(evtype):
1995 "When will this event happen?"
1996 return game.future[evtype].date
1998 def schedule(evtype, offset):
1999 "Schedule an event of specified type."
2000 game.future[evtype].date = game.state.date + offset
2001 return game.future[evtype]
2003 def postpone(evtype, offset):
2004 "Postpone a scheduled event."
2005 game.future[evtype].date += offset
2008 "Rest period is interrupted by event."
2011 proutn(_("Mr. Spock- \"Captain, shall we cancel the rest period?\""))
2013 game.resting = False
2019 "Run through the event queue looking for things to do."
2021 fintim = game.state.date + game.optime
2030 def tractorbeam(yank):
2031 "Tractor-beaming cases merge here."
2033 game.optime = (10.0/(7.5*7.5))*yank # 7.5 is yank rate (warp 7.5)
2035 prout("***" + crmshp() + _(" caught in long range tractor beam--"))
2036 # If Kirk & Co. screwing around on planet, handle
2037 atover(True) # atover(true) is Grab
2040 if game.icraft: # Caught in Galileo?
2043 # Check to see if shuttle is aboard
2044 if game.iscraft == "offship":
2047 prout(_("Galileo, left on the planet surface, is captured"))
2048 prout(_("by aliens and made into a flying McDonald's."))
2049 game.damage[DSHUTTL] = -10
2050 game.iscraft = "removed"
2052 prout(_("Galileo, left on the planet surface, is well hidden."))
2054 game.quadrant = game.state.kscmdr
2056 game.quadrant = game.state.kcmdr[i]
2057 game.sector = randplace(QUADSIZE)
2058 prout(crmshp() + _(" is pulled to Quadrant %s, Sector %s") \
2059 % (game.quadrant, game.sector))
2061 prout(_("(Remainder of rest/repair period cancelled.)"))
2062 game.resting = False
2064 if not damaged(DSHIELD) and game.shield > 0:
2065 doshield(shraise=True) # raise shields
2066 game.shldchg = False
2068 prout(_("(Shields not currently useable.)"))
2070 # Adjust finish time to time of tractor beaming?
2071 # fintim = game.state.date+game.optime
2072 attack(torps_ok=False)
2073 if not game.state.kcmdr:
2076 schedule(FTBEAM, game.optime+expran(1.5*game.intime/len(game.state.kcmdr)))
2079 "Code merges here for any commander destroying a starbase."
2080 # Not perfect, but will have to do
2081 # Handle case where base is in same quadrant as starship
2082 if game.battle == game.quadrant:
2083 game.state.chart[game.battle.i][game.battle.j].starbase = False
2084 game.quad[game.base.i][game.base.j] = '.'
2085 game.base.invalidate()
2088 prout(_("Spock- \"Captain, I believe the starbase has been destroyed.\""))
2089 elif game.state.baseq and communicating():
2090 # Get word via subspace radio
2093 prout(_("Lt. Uhura- \"Captain, Starfleet Command reports that"))
2094 proutn(_(" the starbase in Quadrant %s has been destroyed by") % game.battle)
2096 prout(_("the Klingon Super-Commander"))
2098 prout(_("a Klingon Commander"))
2099 game.state.chart[game.battle.i][game.battle.j].starbase = False
2100 # Remove Starbase from galaxy
2101 game.state.galaxy[game.battle.i][game.battle.j].starbase = False
2102 game.state.baseq = filter(lambda x: x != game.battle, game.state.baseq)
2104 # reinstate a commander's base attack
2108 game.battle.invalidate()
2110 prout("=== EVENTS from %.2f to %.2f:" % (game.state.date, fintim))
2111 for i in range(1, NEVENTS):
2112 if i == FSNOVA: proutn("=== Supernova ")
2113 elif i == FTBEAM: proutn("=== T Beam ")
2114 elif i == FSNAP: proutn("=== Snapshot ")
2115 elif i == FBATTAK: proutn("=== Base Attack ")
2116 elif i == FCDBAS: proutn("=== Base Destroy ")
2117 elif i == FSCMOVE: proutn("=== SC Move ")
2118 elif i == FSCDBAS: proutn("=== SC Base Destroy ")
2119 elif i == FDSPROB: proutn("=== Probe Move ")
2120 elif i == FDISTR: proutn("=== Distress Call ")
2121 elif i == FENSLV: proutn("=== Enslavement ")
2122 elif i == FREPRO: proutn("=== Klingon Build ")
2124 prout("%.2f" % (scheduled(i)))
2127 radio_was_broken = damaged(DRADIO)
2130 # Select earliest extraneous event, evcode==0 if no events
2135 for l in range(1, NEVENTS):
2136 if game.future[l].date < datemin:
2139 prout("== Event %d fires" % evcode)
2140 datemin = game.future[l].date
2141 xtime = datemin-game.state.date
2142 game.state.date = datemin
2143 # Decrement Federation resources and recompute remaining time
2144 game.state.remres -= (game.state.remkl+4*len(game.state.kcmdr))*xtime
2146 if game.state.remtime <= 0:
2149 # Any crew left alive?
2150 if game.state.crew <= 0:
2153 # Is life support adequate?
2154 if damaged(DLIFSUP) and game.condition != "docked":
2155 if game.lsupres < xtime and game.damage[DLIFSUP] > game.lsupres:
2158 game.lsupres -= xtime
2159 if game.damage[DLIFSUP] <= xtime:
2160 game.lsupres = game.inlsr
2163 if game.condition == "docked":
2165 # Don't fix Deathray here
2166 for l in range(NDEVICES):
2167 if game.damage[l] > 0.0 and l != DDRAY:
2168 if game.damage[l]-repair > 0.0:
2169 game.damage[l] -= repair
2171 game.damage[l] = 0.0
2172 # If radio repaired, update star chart and attack reports
2173 if radio_was_broken and not damaged(DRADIO):
2174 prout(_("Lt. Uhura- \"Captain, the sub-space radio is working and"))
2175 prout(_(" surveillance reports are coming in."))
2177 if not game.iseenit:
2181 prout(_(" The star chart is now up to date.\""))
2183 # Cause extraneous event EVCODE to occur
2184 game.optime -= xtime
2185 if evcode == FSNOVA: # Supernova
2188 schedule(FSNOVA, expran(0.5*game.intime))
2189 if game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
2191 elif evcode == FSPY: # Check with spy to see if SC should tractor beam
2192 if game.state.nscrem == 0 or \
2193 ictbeam or istract or \
2194 game.condition == "docked" or game.isatb == 1 or game.iscate:
2196 if game.ientesc or \
2197 (game.energy<2000 and game.torps<4 and game.shield < 1250) or \
2198 (damaged(DPHASER) and (damaged(DPHOTON) or game.torps<4)) or \
2199 (damaged(DSHIELD) and \
2200 (game.energy < 2500 or damaged(DPHASER)) and \
2201 (game.torps < 5 or damaged(DPHOTON))):
2203 istract = ictbeam = True
2204 tractorbeam((game.state.kscmdr-game.quadrant).distance())
2207 elif evcode == FTBEAM: # Tractor beam
2208 if not game.state.kcmdr:
2211 i = randrange(len(game.state.kcmdr))
2212 yank = (game.state.kcmdr[i]-game.quadrant).distance()
2213 if istract or game.condition == "docked" or yank == 0:
2214 # Drats! Have to reschedule
2216 game.optime + expran(1.5*game.intime/len(game.state.kcmdr)))
2220 elif evcode == FSNAP: # Snapshot of the universe (for time warp)
2221 game.snapsht = copy.deepcopy(game.state)
2222 game.state.snap = True
2223 schedule(FSNAP, expran(0.5 * game.intime))
2224 elif evcode == FBATTAK: # Commander attacks starbase
2225 if not game.state.kcmdr or not game.state.baseq:
2231 for ibq in game.state.baseq:
2232 for cmdr in game.state.kcmdr:
2233 if ibq == cmdr and ibq != game.quadrant and ibq != game.state.kscmdr:
2236 # no match found -- try later
2237 schedule(FBATTAK, expran(0.3*game.intime))
2242 # commander + starbase combination found -- launch attack
2244 schedule(FCDBAS, randreal(1.0, 4.0))
2245 if game.isatb: # extra time if SC already attacking
2246 postpone(FCDBAS, scheduled(FSCDBAS)-game.state.date)
2247 game.future[FBATTAK].date = game.future[FCDBAS].date + expran(0.3*game.intime)
2248 game.iseenit = False
2249 if not communicating():
2250 continue # No warning :-(
2254 prout(_("Lt. Uhura- \"Captain, the starbase in Quadrant %s") % game.battle)
2255 prout(_(" reports that it is under attack and that it can"))
2256 prout(_(" hold out only until stardate %d.\"") % (int(scheduled(FCDBAS))))
2259 elif evcode == FSCDBAS: # Supercommander destroys base
2262 if not game.state.galaxy[game.state.kscmdr.i][game.state.kscmdr.j].starbase:
2263 continue # WAS RETURN!
2265 game.battle = game.state.kscmdr
2267 elif evcode == FCDBAS: # Commander succeeds in destroying base
2268 if evcode == FCDBAS:
2270 if not game.state.baseq() \
2271 or not game.state.galaxy[game.battle.i][game.battle.j].starbase:
2272 game.battle.invalidate()
2274 # find the lucky pair
2275 for cmdr in game.state.kcmdr:
2276 if cmdr == game.battle:
2279 # No action to take after all
2282 elif evcode == FSCMOVE: # Supercommander moves
2283 schedule(FSCMOVE, 0.2777)
2284 if not game.ientesc and not istract and game.isatb != 1 and \
2285 (not game.iscate or not game.justin):
2287 elif evcode == FDSPROB: # Move deep space probe
2288 schedule(FDSPROB, 0.01)
2289 if not game.probe.nexttok():
2290 if not game.probe.quadrant().valid_quadrant() or \
2291 game.state.galaxy[game.probe.quadrant().i][game.probe.quadrant().j].supernova:
2292 # Left galaxy or ran into supernova
2296 proutn(_("Lt. Uhura- \"The deep space probe "))
2297 if not game.probe.quadrant().valid_quadrant():
2298 prout(_("has left the galaxy.\""))
2300 prout(_("is no longer transmitting.\""))
2306 prout(_("Lt. Uhura- \"The deep space probe is now in Quadrant %s.\"") % game.probe.quadrant())
2307 pquad = game.probe.quadrant()
2308 pdest = game.state.galaxy[pquad.i][pquad.j]
2310 game.state.chart[pquad.i][pquad.j].klingons = pdest.klingons
2311 game.state.chart[pquad.i][pquad.j].starbase = pdest.starbase
2312 game.state.chart[pquad.i][pquad.j].stars = pdest.stars
2313 pdest.charted = True
2314 game.probe.moves -= 1 # One less to travel
2315 if game.probe.arrived() and game.isarmed and pdest.stars:
2316 supernova(game.probe) # fire in the hole!
2318 if game.state.galaxy[pquad.i][pquad.j].supernova:
2320 elif evcode == FDISTR: # inhabited system issues distress call
2322 # try a whole bunch of times to find something suitable
2323 for i in range(100):
2324 # need a quadrant which is not the current one,
2325 # which has some stars which are inhabited and
2326 # not already under attack, which is not
2327 # supernova'ed, and which has some Klingons in it
2328 w = randplace(GALSIZE)
2329 q = game.state.galaxy[w.i][w.j]
2330 if not (game.quadrant == w or q.planet == None or \
2331 not q.planet.inhabited or \
2332 q.supernova or q.status!="secure" or q.klingons<=0):
2335 # can't seem to find one; ignore this call
2337 prout("=== Couldn't find location for distress event.")
2339 # got one!! Schedule its enslavement
2340 ev = schedule(FENSLV, expran(game.intime))
2342 q.status = "distressed"
2343 # tell the captain about it if we can
2345 prout(_("Uhura- Captain, %s in Quadrant %s reports it is under attack") \
2346 % (q.planet, repr(w)))
2347 prout(_("by a Klingon invasion fleet."))
2350 elif evcode == FENSLV: # starsystem is enslaved
2351 ev = unschedule(FENSLV)
2352 # see if current distress call still active
2353 q = game.state.galaxy[ev.quadrant.i][ev.quadrant.j]
2357 q.status = "enslaved"
2359 # play stork and schedule the first baby
2360 ev2 = schedule(FREPRO, expran(2.0 * game.intime))
2361 ev2.quadrant = ev.quadrant
2363 # report the disaster if we can
2365 prout(_("Uhura- We've lost contact with starsystem %s") % \
2367 prout(_("in Quadrant %s.\n") % ev.quadrant)
2368 elif evcode == FREPRO: # Klingon reproduces
2369 # If we ever switch to a real event queue, we'll need to
2370 # explicitly retrieve and restore the x and y.
2371 ev = schedule(FREPRO, expran(1.0 * game.intime))
2372 # see if current distress call still active
2373 q = game.state.galaxy[ev.quadrant.i][ev.quadrant.j]
2377 if game.state.remkl >= MAXKLGAME:
2378 continue # full right now
2379 # reproduce one Klingon
2382 if game.klhere >= MAXKLQUAD:
2384 # this quadrant not ok, pick an adjacent one
2385 for m.i in range(w.i - 1, w.i + 2):
2386 for m.j in range(w.j - 1, w.j + 2):
2387 if not m.valid_quadrant():
2389 q = game.state.galaxy[m.i][m.j]
2390 # check for this quad ok (not full & no snova)
2391 if q.klingons >= MAXKLQUAD or q.supernova:
2395 continue # search for eligible quadrant failed
2399 game.state.remkl += 1
2401 if game.quadrant == w:
2403 game.enemies.append(newkling())
2404 # recompute time left
2407 if game.quadrant == w:
2408 prout(_("Spock- sensors indicate the Klingons have"))
2409 prout(_("launched a warship from %s.") % q.planet)
2411 prout(_("Uhura- Starfleet reports increased Klingon activity"))
2412 if q.planet != None:
2413 proutn(_("near %s ") % q.planet)
2414 prout(_("in Quadrant %s.") % w)
2420 key = scanner.nexttok()
2423 proutn(_("How long? "))
2428 origTime = delay = scanner.real
2431 if delay >= game.state.remtime or len(game.enemies) != 0:
2432 proutn(_("Are you sure? "))
2435 # Alternate resting periods (events) with attacks
2439 game.resting = False
2440 if not game.resting:
2441 prout(_("%d stardates left.") % int(game.state.remtime))
2443 temp = game.optime = delay
2444 if len(game.enemies):
2445 rtime = randreal(1.0, 2.0)
2449 if game.optime < delay:
2450 attack(torps_ok=False)
2458 # Repair Deathray if long rest at starbase
2459 if origTime-delay >= 9.99 and game.condition == "docked":
2460 game.damage[DDRAY] = 0.0
2461 # leave if quadrant supernovas
2462 if game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
2464 game.resting = False
2469 ncourse = (0.0, 10.5, 12.0, 1.5, 9.0, 0.0, 3.0, 7.5, 6.0, 4.5)
2470 newc = Coord(); neighbor = Coord(); bump = Coord(0, 0)
2472 # Wow! We've supernova'ed
2473 supernova(game.quadrant)
2475 # handle initial nova
2476 game.quad[nov.i][nov.j] = '.'
2477 prout(crmena(False, '*', "sector", nov) + _(" novas."))
2478 game.state.galaxy[game.quadrant.i][game.quadrant.j].stars -= 1
2479 game.state.starkl += 1
2480 # Set up queue to recursively trigger adjacent stars
2486 for offset.i in range(-1, 1+1):
2487 for offset.j in range(-1, 1+1):
2488 if offset.j == 0 and offset.i == 0:
2490 neighbor = start + offset
2491 if not neighbor.valid_sector():
2493 iquad = game.quad[neighbor.i][neighbor.j]
2494 # Empty space ends reaction
2495 if iquad in ('.', '?', ' ', 'T', '#'):
2497 elif iquad == '*': # Affect another star
2499 # This star supernovas
2500 supernova(game.quadrant)
2503 hits.append(neighbor)
2504 game.state.galaxy[game.quadrant.i][game.quadrant.j].stars -= 1
2505 game.state.starkl += 1
2506 proutn(crmena(True, '*', "sector", neighbor))
2508 game.quad[neighbor.i][neighbor.j] = '.'
2510 elif iquad in ('P', '@'): # Destroy planet
2511 game.state.galaxy[game.quadrant.i][game.quadrant.j].planet = None
2513 game.state.nplankl += 1
2515 game.state.nworldkl += 1
2516 prout(crmena(True, 'B', "sector", neighbor) + _(" destroyed."))
2517 game.iplnet.pclass = "destroyed"
2519 game.plnet.invalidate()
2523 game.quad[neighbor.i][neighbor.j] = '.'
2524 elif iquad == 'B': # Destroy base
2525 game.state.galaxy[game.quadrant.i][game.quadrant.j].starbase = False
2526 game.state.baseq = filter(lambda x: x!= game.quadrant, game.state.baseq)
2527 game.base.invalidate()
2528 game.state.basekl += 1
2530 prout(crmena(True, 'B', "sector", neighbor) + _(" destroyed."))
2531 game.quad[neighbor.i][neighbor.j] = '.'
2532 elif iquad in ('E', 'F'): # Buffet ship
2533 prout(_("***Starship buffeted by nova."))
2535 if game.shield >= 2000.0:
2536 game.shield -= 2000.0
2538 diff = 2000.0 - game.shield
2542 prout(_("***Shields knocked out."))
2543 game.damage[DSHIELD] += 0.005*game.damfac*randreal()*diff
2545 game.energy -= 2000.0
2546 if game.energy <= 0:
2549 # add in course nova contributes to kicking starship
2550 bump += (game.sector-hits[-1]).sgn()
2551 elif iquad == 'K': # kill klingon
2552 deadkl(neighbor, iquad, neighbor)
2553 elif iquad in ('C','S','R'): # Damage/destroy big enemies
2554 for ll in range(len(game.enemies)):
2555 if game.enemies[ll].location == neighbor:
2557 game.enemies[ll].power -= 800.0 # If firepower is lost, die
2558 if game.enemies[ll].power <= 0.0:
2559 deadkl(neighbor, iquad, neighbor)
2561 newc = neighbor + neighbor - hits[-1]
2562 proutn(crmena(True, iquad, "sector", neighbor) + _(" damaged"))
2563 if not newc.valid_sector():
2564 # can't leave quadrant
2567 iquad1 = game.quad[newc.i][newc.j]
2569 proutn(_(", blasted into ") + crmena(False, ' ', "sector", newc))
2571 deadkl(neighbor, iquad, newc)
2574 # can't move into something else
2577 proutn(_(", buffeted to Sector %s") % newc)
2578 game.quad[neighbor.i][neighbor.j] = '.'
2579 game.quad[newc.i][newc.j] = iquad
2580 game.enemies[ll].move(newc)
2581 # Starship affected by nova -- kick it away.
2583 direc = ncourse[3*(bump.i+1)+bump.j+2]
2588 scourse = course(bearing=direc, distance=dist)
2589 game.optime = scourse.time(warp=4)
2591 prout(_("Force of nova displaces starship."))
2592 imove(scourse, noattack=True)
2593 game.optime = scourse.time(warp=4)
2597 "Star goes supernova."
2602 # Scheduled supernova -- select star at random.
2605 for nq.i in range(GALSIZE):
2606 for nq.j in range(GALSIZE):
2607 stars += game.state.galaxy[nq.i][nq.j].stars
2609 return # nothing to supernova exists
2610 num = randrange(stars) + 1
2611 for nq.i in range(GALSIZE):
2612 for nq.j in range(GALSIZE):
2613 num -= game.state.galaxy[nq.i][nq.j].stars
2619 proutn("=== Super nova here?")
2622 if not nq == game.quadrant or game.justin:
2623 # it isn't here, or we just entered (treat as enroute)
2626 prout(_("Message from Starfleet Command Stardate %.2f") % game.state.date)
2627 prout(_(" Supernova in Quadrant %s; caution advised.") % nq)
2630 # we are in the quadrant!
2631 num = randrange(game.state.galaxy[nq.i][nq.j].stars) + 1
2632 for ns.i in range(QUADSIZE):
2633 for ns.j in range(QUADSIZE):
2634 if game.quad[ns.i][ns.j]=='*':
2641 prouts(_("***RED ALERT! RED ALERT!"))
2643 prout(_("***Incipient supernova detected at Sector %s") % ns)
2644 if (ns.i-game.sector.i)**2 + (ns.j-game.sector.j)**2 <= 2.1:
2645 proutn(_("Emergency override attempts t"))
2646 prouts("***************")
2650 # destroy any Klingons in supernovaed quadrant
2651 kldead = game.state.galaxy[nq.i][nq.j].klingons
2652 game.state.galaxy[nq.i][nq.j].klingons = 0
2653 if nq == game.state.kscmdr:
2654 # did in the Supercommander!
2655 game.state.nscrem = game.state.kscmdr.i = game.state.kscmdr.j = game.isatb = 0
2659 survivors = filter(lambda w: w != nq, game.state.kcmdr)
2660 comkills = len(game.state.kcmdr) - len(survivors)
2661 game.state.kcmdr = survivors
2663 if not game.state.kcmdr:
2665 game.state.remkl -= kldead
2666 # destroy Romulans and planets in supernovaed quadrant
2667 nrmdead = game.state.galaxy[nq.i][nq.j].romulans
2668 game.state.galaxy[nq.i][nq.j].romulans = 0
2669 game.state.nromrem -= nrmdead
2671 for loop in range(game.inplan):
2672 if game.state.planets[loop].quadrant == nq:
2673 game.state.planets[loop].pclass = "destroyed"
2675 # Destroy any base in supernovaed quadrant
2676 game.state.baseq = filter(lambda x: x != nq, game.state.baseq)
2677 # If starship caused supernova, tally up destruction
2679 game.state.starkl += game.state.galaxy[nq.i][nq.j].stars
2680 game.state.basekl += game.state.galaxy[nq.i][nq.j].starbase
2681 game.state.nplankl += npdead
2682 # mark supernova in galaxy and in star chart
2683 if game.quadrant == nq or communicating():
2684 game.state.galaxy[nq.i][nq.j].supernova = True
2685 # If supernova destroys last Klingons give special message
2686 if (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem)==0 and not nq == game.quadrant:
2689 prout(_("Lucky you!"))
2690 proutn(_("A supernova in %s has just destroyed the last Klingons.") % nq)
2693 # if some Klingons remain, continue or die in supernova
2698 # Code from finish.c ends here.
2701 "Self-destruct maneuver. Finish with a BANG!"
2703 if damaged(DCOMPTR):
2704 prout(_("Computer damaged; cannot execute destruct sequence."))
2706 prouts(_("---WORKING---")); skip(1)
2707 prouts(_("SELF-DESTRUCT-SEQUENCE-ACTIVATED")); skip(1)
2708 prouts(" 10"); skip(1)
2709 prouts(" 9"); skip(1)
2710 prouts(" 8"); skip(1)
2711 prouts(" 7"); skip(1)
2712 prouts(" 6"); skip(1)
2714 prout(_("ENTER-CORRECT-PASSWORD-TO-CONTINUE-"))
2716 prout(_("SELF-DESTRUCT-SEQUENCE-OTHERWISE-"))
2718 prout(_("SELF-DESTRUCT-SEQUENCE-WILL-BE-ABORTED"))
2721 if game.passwd != scanner.token:
2722 prouts(_("PASSWORD-REJECTED;"))
2724 prouts(_("CONTINUITY-EFFECTED"))
2727 prouts(_("PASSWORD-ACCEPTED")); skip(1)
2728 prouts(" 5"); skip(1)
2729 prouts(" 4"); skip(1)
2730 prouts(" 3"); skip(1)
2731 prouts(" 2"); skip(1)
2732 prouts(" 1"); skip(1)
2734 prouts(_("GOODBYE-CRUEL-WORLD"))
2742 prouts(_("********* Entropy of %s maximized *********") % crmshp())
2746 if len(game.enemies) != 0:
2747 whammo = 25.0 * game.energy
2748 for l in range(len(game.enemies)):
2749 if game.enemies[l].power*game.enemies[l].kdist <= whammo:
2750 deadkl(game.enemies[l].location, game.quad[game.enemies[l].location.i][game.enemies[l].location.j], game.enemies[l].location)
2754 "Compute our rate of kils over time."
2755 elapsed = game.state.date - game.indate
2756 if elapsed == 0: # Avoid divide-by-zero error if calculated on turn 0
2759 starting = (game.inkling + game.incom + game.inscom)
2760 remaining = (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem)
2761 return (starting - remaining)/elapsed
2765 badpt = 5.0*game.state.starkl + \
2767 10.0*game.state.nplankl + \
2768 300*game.state.nworldkl + \
2770 100.0*game.state.basekl +\
2772 if game.ship == 'F':
2774 elif game.ship == None:
2779 # end the game, with appropriate notfications
2783 prout(_("It is stardate %.1f.") % game.state.date)
2785 if ifin == FWON: # Game has been won
2786 if game.state.nromrem != 0:
2787 prout(_("The remaining %d Romulans surrender to Starfleet Command.") %
2790 prout(_("You have smashed the Klingon invasion fleet and saved"))
2791 prout(_("the Federation."))
2796 badpt = 0.0 # Close enough!
2797 # killsPerDate >= RateMax
2798 if game.state.date-game.indate < 5.0 or \
2799 killrate() >= 0.1*game.skill*(game.skill+1.0) + 0.1 + 0.008*badpt:
2801 prout(_("In fact, you have done so well that Starfleet Command"))
2802 if game.skill == SKILL_NOVICE:
2803 prout(_("promotes you one step in rank from \"Novice\" to \"Fair\"."))
2804 elif game.skill == SKILL_FAIR:
2805 prout(_("promotes you one step in rank from \"Fair\" to \"Good\"."))
2806 elif game.skill == SKILL_GOOD:
2807 prout(_("promotes you one step in rank from \"Good\" to \"Expert\"."))
2808 elif game.skill == SKILL_EXPERT:
2809 prout(_("promotes you to Commodore Emeritus."))
2811 prout(_("Now that you think you're really good, try playing"))
2812 prout(_("the \"Emeritus\" game. It will splatter your ego."))
2813 elif game.skill == SKILL_EMERITUS:
2815 proutn(_("Computer- "))
2816 prouts(_("ERROR-ERROR-ERROR-ERROR"))
2818 prouts(_(" YOUR-SKILL-HAS-EXCEEDED-THE-CAPACITY-OF-THIS-PROGRAM"))
2820 prouts(_(" THIS-PROGRAM-MUST-SURVIVE"))
2822 prouts(_(" THIS-PROGRAM-MUST-SURVIVE"))
2824 prouts(_(" THIS-PROGRAM-MUST-SURVIVE"))
2826 prouts(_(" THIS-PROGRAM-MUST?- MUST ? - SUR? ? -? VI"))
2828 prout(_("Now you can retire and write your own Star Trek game!"))
2830 elif game.skill >= SKILL_EXPERT:
2831 if game.thawed and not game.idebug:
2832 prout(_("You cannot get a citation, so..."))
2834 proutn(_("Do you want your Commodore Emeritus Citation printed? "))
2838 # Only grant long life if alive (original didn't!)
2840 prout(_("LIVE LONG AND PROSPER."))
2845 elif ifin == FDEPLETE: # Federation Resources Depleted
2846 prout(_("Your time has run out and the Federation has been"))
2847 prout(_("conquered. Your starship is now Klingon property,"))
2848 prout(_("and you are put on trial as a war criminal. On the"))
2849 proutn(_("basis of your record, you are "))
2850 if (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem)*3.0 > (game.inkling + game.incom + game.inscom):
2851 prout(_("acquitted."))
2853 prout(_("LIVE LONG AND PROSPER."))
2855 prout(_("found guilty and"))
2856 prout(_("sentenced to death by slow torture."))
2860 elif ifin == FLIFESUP:
2861 prout(_("Your life support reserves have run out, and"))
2862 prout(_("you die of thirst, starvation, and asphyxiation."))
2863 prout(_("Your starship is a derelict in space."))
2865 prout(_("Your energy supply is exhausted."))
2867 prout(_("Your starship is a derelict in space."))
2868 elif ifin == FBATTLE:
2869 prout(_("The %s has been destroyed in battle.") % crmshp())
2871 prout(_("Dulce et decorum est pro patria mori."))
2873 prout(_("You have made three attempts to cross the negative energy"))
2874 prout(_("barrier which surrounds the galaxy."))
2876 prout(_("Your navigation is abominable."))
2879 prout(_("Your starship has been destroyed by a nova."))
2880 prout(_("That was a great shot."))
2882 elif ifin == FSNOVAED:
2883 prout(_("The %s has been fried by a supernova.") % crmshp())
2884 prout(_("...Not even cinders remain..."))
2885 elif ifin == FABANDN:
2886 prout(_("You have been captured by the Klingons. If you still"))
2887 prout(_("had a starbase to be returned to, you would have been"))
2888 prout(_("repatriated and given another chance. Since you have"))
2889 prout(_("no starbases, you will be mercilessly tortured to death."))
2890 elif ifin == FDILITHIUM:
2891 prout(_("Your starship is now an expanding cloud of subatomic particles"))
2892 elif ifin == FMATERIALIZE:
2893 prout(_("Starbase was unable to re-materialize your starship."))
2894 prout(_("Sic transit gloria mundi"))
2895 elif ifin == FPHASER:
2896 prout(_("The %s has been cremated by its own phasers.") % crmshp())
2898 prout(_("You and your landing party have been"))
2899 prout(_("converted to energy, disipating through space."))
2900 elif ifin == FMINING:
2901 prout(_("You are left with your landing party on"))
2902 prout(_("a wild jungle planet inhabited by primitive cannibals."))
2904 prout(_("They are very fond of \"Captain Kirk\" soup."))
2906 prout(_("Without your leadership, the %s is destroyed.") % crmshp())
2907 elif ifin == FDPLANET:
2908 prout(_("You and your mining party perish."))
2910 prout(_("That was a great shot."))
2913 prout(_("The Galileo is instantly annihilated by the supernova."))
2914 prout(_("You and your mining party are atomized."))
2916 prout(_("Mr. Spock takes command of the %s and") % crmshp())
2917 prout(_("joins the Romulans, wreaking terror on the Federation."))
2918 elif ifin == FPNOVA:
2919 prout(_("You and your mining party are atomized."))
2921 prout(_("Mr. Spock takes command of the %s and") % crmshp())
2922 prout(_("joins the Romulans, wreaking terror on the Federation."))
2923 elif ifin == FSTRACTOR:
2924 prout(_("The shuttle craft Galileo is also caught,"))
2925 prout(_("and breaks up under the strain."))
2927 prout(_("Your debris is scattered for millions of miles."))
2928 prout(_("Without your leadership, the %s is destroyed.") % crmshp())
2930 prout(_("The mutants attack and kill Spock."))
2931 prout(_("Your ship is captured by Klingons, and"))
2932 prout(_("your crew is put on display in a Klingon zoo."))
2933 elif ifin == FTRIBBLE:
2934 prout(_("Tribbles consume all remaining water,"))
2935 prout(_("food, and oxygen on your ship."))
2937 prout(_("You die of thirst, starvation, and asphyxiation."))
2938 prout(_("Your starship is a derelict in space."))
2940 prout(_("Your ship is drawn to the center of the black hole."))
2941 prout(_("You are crushed into extremely dense matter."))
2943 prout(_("Your last crew member has died."))
2944 if game.ship == 'F':
2946 elif game.ship == 'E':
2949 if (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem) != 0:
2950 goodies = game.state.remres/game.inresor
2951 baddies = (game.state.remkl + 2.0*len(game.state.kcmdr))/(game.inkling+2.0*game.incom)
2952 if goodies/baddies >= randreal(1.0, 1.5):
2953 prout(_("As a result of your actions, a treaty with the Klingon"))
2954 prout(_("Empire has been signed. The terms of the treaty are"))
2955 if goodies/baddies >= randreal(3.0):
2956 prout(_("favorable to the Federation."))
2958 prout(_("Congratulations!"))
2960 prout(_("highly unfavorable to the Federation."))
2962 prout(_("The Federation will be destroyed."))
2964 prout(_("Since you took the last Klingon with you, you are a"))
2965 prout(_("martyr and a hero. Someday maybe they'll erect a"))
2966 prout(_("statue in your memory. Rest in peace, and try not"))
2967 prout(_("to think about pigeons."))
2972 "Compute player's score."
2973 timused = game.state.date - game.indate
2974 if (timused == 0 or (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem) != 0) and timused < 5.0:
2976 game.perdate = killrate()
2977 ithperd = 500*game.perdate + 0.5
2980 iwon = 100*game.skill
2981 if game.ship == 'E':
2983 elif game.ship == 'F':
2987 game.score = 10*(game.inkling - game.state.remkl) \
2988 + 50*(game.incom - len(game.state.kcmdr)) \
2990 + 20*(game.inrom - game.state.nromrem) \
2991 + 200*(game.inscom - game.state.nscrem) \
2992 - game.state.nromrem \
2997 prout(_("Your score --"))
2998 if game.inrom - game.state.nromrem:
2999 prout(_("%6d Romulans destroyed %5d") %
3000 (game.inrom - game.state.nromrem, 20*(game.inrom - game.state.nromrem)))
3001 if game.state.nromrem and game.gamewon:
3002 prout(_("%6d Romulans captured %5d") %
3003 (game.state.nromrem, game.state.nromrem))
3004 if game.inkling - game.state.remkl:
3005 prout(_("%6d ordinary Klingons destroyed %5d") %
3006 (game.inkling - game.state.remkl, 10*(game.inkling - game.state.remkl)))
3007 if game.incom - len(game.state.kcmdr):
3008 prout(_("%6d Klingon commanders destroyed %5d") %
3009 (game.incom - len(game.state.kcmdr), 50*(game.incom - len(game.state.kcmdr))))
3010 if game.inscom - game.state.nscrem:
3011 prout(_("%6d Super-Commander destroyed %5d") %
3012 (game.inscom - game.state.nscrem, 200*(game.inscom - game.state.nscrem)))
3014 prout(_("%6.2f Klingons per stardate %5d") %
3015 (game.perdate, ithperd))
3016 if game.state.starkl:
3017 prout(_("%6d stars destroyed by your action %5d") %
3018 (game.state.starkl, -5*game.state.starkl))
3019 if game.state.nplankl:
3020 prout(_("%6d planets destroyed by your action %5d") %
3021 (game.state.nplankl, -10*game.state.nplankl))
3022 if (game.options & OPTION_WORLDS) and game.state.nworldkl:
3023 prout(_("%6d inhabited planets destroyed by your action %5d") %
3024 (game.state.nworldkl, -300*game.state.nworldkl))
3025 if game.state.basekl:
3026 prout(_("%6d bases destroyed by your action %5d") %
3027 (game.state.basekl, -100*game.state.basekl))
3029 prout(_("%6d calls for help from starbase %5d") %
3030 (game.nhelp, -45*game.nhelp))
3032 prout(_("%6d casualties incurred %5d") %
3033 (game.casual, -game.casual))
3035 prout(_("%6d crew abandoned in space %5d") %
3036 (game.abandoned, -3*game.abandoned))
3038 prout(_("%6d ship(s) lost or destroyed %5d") %
3039 (klship, -100*klship))
3041 prout(_("Penalty for getting yourself killed -200"))
3043 proutn(_("Bonus for winning "))
3044 if game.skill == SKILL_NOVICE: proutn(_("Novice game "))
3045 elif game.skill == SKILL_FAIR: proutn(_("Fair game "))
3046 elif game.skill == SKILL_GOOD: proutn(_("Good game "))
3047 elif game.skill == SKILL_EXPERT: proutn(_("Expert game "))
3048 elif game.skill == SKILL_EMERITUS: proutn(_("Emeritus game"))
3049 prout(" %5d" % iwon)
3051 prout(_("TOTAL SCORE %5d") % game.score)
3054 "Emit winner's commemmorative plaque."
3057 proutn(_("File or device name for your plaque: "))
3060 fp = open(winner, "w")
3063 prout(_("Invalid name."))
3065 proutn(_("Enter name to go on plaque (up to 30 characters): "))
3067 # The 38 below must be 64 for 132-column paper
3068 nskip = 38 - len(winner)/2
3069 fp.write("\n\n\n\n")
3070 # --------DRAW ENTERPRISE PICTURE.
3071 fp.write(" EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n" )
3072 fp.write(" EEE E : : : E\n" )
3073 fp.write(" EE EEE E : : NCC-1701 : E\n")
3074 fp.write("EEEEEEEEEEEEEEEE EEEEEEEEEEEEEEE : : : E\n")
3075 fp.write(" E EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n")
3076 fp.write(" EEEEEEEEE EEEEEEEEEEEEE E E\n")
3077 fp.write(" EEEEEEE EEEEE E E E E\n")
3078 fp.write(" EEE E E E E\n")
3079 fp.write(" E E E E\n")
3080 fp.write(" EEEEEEEEEEEEE E E\n")
3081 fp.write(" EEE : EEEEEEE EEEEEEEE\n")
3082 fp.write(" :E : EEEE E\n")
3083 fp.write(" .-E -:----- E\n")
3084 fp.write(" :E : E\n")
3085 fp.write(" EE : EEEEEEEE\n")
3086 fp.write(" EEEEEEEEEEEEEEEEEEEEEEE\n")
3088 fp.write(_(" U. S. S. ENTERPRISE\n"))
3089 fp.write("\n\n\n\n")
3090 fp.write(_(" For demonstrating outstanding ability as a starship captain\n"))
3092 fp.write(_(" Starfleet Command bestows to you\n"))
3094 fp.write("%*s%s\n\n" % (nskip, "", winner))
3095 fp.write(_(" the rank of\n\n"))
3096 fp.write(_(" \"Commodore Emeritus\"\n\n"))
3098 if game.skill == SKILL_EXPERT:
3099 fp.write(_(" Expert level\n\n"))
3100 elif game.skill == SKILL_EMERITUS:
3101 fp.write(_("Emeritus level\n\n"))
3103 fp.write(_(" Cheat level\n\n"))
3104 timestring = time.ctime()
3105 fp.write(_(" This day of %.6s %.4s, %.8s\n\n") %
3106 (timestring+4, timestring+20, timestring+11))
3107 fp.write(_(" Your score: %d\n\n") % game.score)
3108 fp.write(_(" Klingons per stardate: %.2f\n") % game.perdate)
3111 # Code from io.c begins here
3113 rows = linecount = 0 # for paging
3116 fullscreen_window = None
3117 srscan_window = None
3118 report_window = None
3119 status_window = None
3120 lrscan_window = None
3121 message_window = None
3122 prompt_window = None
3127 "for some recent versions of python2, the following enables UTF8"
3128 "for the older ones we probably need to set C locale, and the python3"
3129 "has no problems at all"
3130 if sys.version_info[0] < 3:
3132 locale.setlocale(locale.LC_ALL, "")
3133 gettext.bindtextdomain("sst", "/usr/local/share/locale")
3134 gettext.textdomain("sst")
3135 if not (game.options & OPTION_CURSES):
3136 ln_env = os.getenv("LINES")
3142 stdscr = curses.initscr()
3146 if game.options & OPTION_COLOR:
3147 curses.start_color()
3148 curses.use_default_colors()
3149 curses.init_pair(curses.COLOR_BLACK, curses.COLOR_BLACK, -1)
3150 curses.init_pair(curses.COLOR_GREEN, curses.COLOR_GREEN, -1)
3151 curses.init_pair(curses.COLOR_RED, curses.COLOR_RED, -1)
3152 curses.init_pair(curses.COLOR_CYAN, curses.COLOR_CYAN, -1)
3153 curses.init_pair(curses.COLOR_WHITE, curses.COLOR_WHITE, -1)
3154 curses.init_pair(curses.COLOR_MAGENTA, curses.COLOR_MAGENTA, -1)
3155 curses.init_pair(curses.COLOR_BLUE, curses.COLOR_BLUE, -1)
3156 curses.init_pair(curses.COLOR_YELLOW, curses.COLOR_YELLOW, -1)
3157 global fullscreen_window, srscan_window, report_window, status_window
3158 global lrscan_window, message_window, prompt_window
3159 (rows, columns) = stdscr.getmaxyx()
3160 fullscreen_window = stdscr
3161 srscan_window = curses.newwin(12, 25, 0, 0)
3162 report_window = curses.newwin(11, 0, 1, 25)
3163 status_window = curses.newwin(10, 0, 1, 39)
3164 lrscan_window = curses.newwin(5, 0, 0, 64)
3165 message_window = curses.newwin(0, 0, 12, 0)
3166 prompt_window = curses.newwin(1, 0, rows-2, 0)
3167 message_window.scrollok(True)
3168 setwnd(fullscreen_window)
3172 if game.options & OPTION_CURSES:
3173 stdscr.keypad(False)
3179 "Wait for user action -- OK to do nothing if on a TTY"
3180 if game.options & OPTION_CURSES:
3185 prouts(_("[ANNOUNCEMENT ARRIVING...]"))
3189 if game.skill > SKILL_FAIR:
3190 prompt = _("[CONTINUE?]")
3192 prompt = _("[PRESS ENTER TO CONTINUE]")
3194 if game.options & OPTION_CURSES:
3196 setwnd(prompt_window)
3197 prompt_window.clear()
3198 prompt_window.addstr(prompt)
3199 prompt_window.getstr()
3200 prompt_window.clear()
3201 prompt_window.refresh()
3202 setwnd(message_window)
3205 sys.stdout.write('\n')
3209 sys.stdout.write('\n' * rows)
3213 "Skip i lines. Pause game if this would cause a scrolling event."
3214 for dummy in range(i):
3215 if game.options & OPTION_CURSES:
3216 (y, x) = curwnd.getyx()
3219 except curses.error:
3224 if rows and linecount >= rows:
3227 sys.stdout.write('\n')
3230 "Utter a line with no following line feed."
3231 if game.options & OPTION_CURSES:
3232 (y, x) = curwnd.getyx()
3233 (my, mx) = curwnd.getmaxyx()
3234 if curwnd == message_window and y >= my - 2:
3237 # Uncomment this to debug curses problems
3239 logfp.write("#curses: at %s proutn(%s)\n" % ((y, x), repr(line)))
3243 sys.stdout.write(line)
3253 if not replayfp or replayfp.closed: # Don't slow down replays
3256 if game.options & OPTION_CURSES:
3260 if not replayfp or replayfp.closed:
3264 "Get a line of input."
3265 if game.options & OPTION_CURSES:
3266 line = curwnd.getstr() + "\n"
3269 if replayfp and not replayfp.closed:
3271 line = replayfp.readline()
3274 prout("*** Replay finished")
3277 elif line[0] != "#":
3280 line = input() + "\n"
3286 "Change windows -- OK for this to be a no-op in tty mode."
3288 if game.options & OPTION_CURSES:
3289 # Uncomment this to debug curses problems
3291 if wnd == fullscreen_window:
3292 legend = "fullscreen"
3293 elif wnd == srscan_window:
3295 elif wnd == report_window:
3297 elif wnd == status_window:
3299 elif wnd == lrscan_window:
3301 elif wnd == message_window:
3303 elif wnd == prompt_window:
3307 logfp.write("#curses: setwnd(%s)\n" % legend)
3309 # Some curses implementations get confused when you try this.
3311 curses.curs_set(wnd in (fullscreen_window, message_window, prompt_window))
3312 except curses.error:
3316 "Clear to end of line -- can be a no-op in tty mode"
3317 if game.options & OPTION_CURSES:
3322 "Clear screen -- can be a no-op in tty mode."
3324 if game.options & OPTION_CURSES:
3330 def textcolor(color=DEFAULT):
3331 if game.options & OPTION_COLOR:
3332 if color == DEFAULT:
3334 elif color == BLACK:
3335 curwnd.attron(curses.color_pair(curses.COLOR_BLACK))
3337 curwnd.attron(curses.color_pair(curses.COLOR_BLUE))
3338 elif color == GREEN:
3339 curwnd.attron(curses.color_pair(curses.COLOR_GREEN))
3341 curwnd.attron(curses.color_pair(curses.COLOR_CYAN))
3343 curwnd.attron(curses.color_pair(curses.COLOR_RED))
3344 elif color == MAGENTA:
3345 curwnd.attron(curses.color_pair(curses.COLOR_MAGENTA))
3346 elif color == BROWN:
3347 curwnd.attron(curses.color_pair(curses.COLOR_YELLOW))
3348 elif color == LIGHTGRAY:
3349 curwnd.attron(curses.color_pair(curses.COLOR_WHITE))
3350 elif color == DARKGRAY:
3351 curwnd.attron(curses.color_pair(curses.COLOR_BLACK) | curses.A_BOLD)
3352 elif color == LIGHTBLUE:
3353 curwnd.attron(curses.color_pair(curses.COLOR_BLUE) | curses.A_BOLD)
3354 elif color == LIGHTGREEN:
3355 curwnd.attron(curses.color_pair(curses.COLOR_GREEN) | curses.A_BOLD)
3356 elif color == LIGHTCYAN:
3357 curwnd.attron(curses.color_pair(curses.COLOR_CYAN) | curses.A_BOLD)
3358 elif color == LIGHTRED:
3359 curwnd.attron(curses.color_pair(curses.COLOR_RED) | curses.A_BOLD)
3360 elif color == LIGHTMAGENTA:
3361 curwnd.attron(curses.color_pair(curses.COLOR_MAGENTA) | curses.A_BOLD)
3362 elif color == YELLOW:
3363 curwnd.attron(curses.color_pair(curses.COLOR_YELLOW) | curses.A_BOLD)
3364 elif color == WHITE:
3365 curwnd.attron(curses.color_pair(curses.COLOR_WHITE) | curses.A_BOLD)
3368 if game.options & OPTION_COLOR:
3369 curwnd.attron(curses.A_REVERSE)
3372 # Things past this point have policy implications.
3376 "Hook to be called after moving to redraw maps."
3377 if game.options & OPTION_CURSES:
3380 setwnd(srscan_window)
3384 setwnd(status_window)
3385 status_window.clear()
3386 status_window.move(0, 0)
3387 setwnd(report_window)
3388 report_window.clear()
3389 report_window.move(0, 0)
3391 setwnd(lrscan_window)
3392 lrscan_window.clear()
3393 lrscan_window.move(0, 0)
3394 lrscan(silent=False)
3396 def put_srscan_sym(w, sym):
3397 "Emit symbol for short-range scan."
3398 srscan_window.move(w.i+1, w.j*2+2)
3399 srscan_window.addch(sym)
3400 srscan_window.refresh()
3403 "Enemy fall down, go boom."
3404 if game.options & OPTION_CURSES:
3406 setwnd(srscan_window)
3407 srscan_window.attron(curses.A_REVERSE)
3408 put_srscan_sym(w, game.quad[w.i][w.j])
3412 srscan_window.attroff(curses.A_REVERSE)
3413 put_srscan_sym(w, game.quad[w.i][w.j])
3414 curses.delay_output(500)
3415 setwnd(message_window)
3418 "Sound and visual effects for teleportation."
3419 if game.options & OPTION_CURSES:
3421 setwnd(message_window)
3423 prouts(" . . . . . ")
3424 if game.options & OPTION_CURSES:
3425 #curses.delay_output(1000)
3429 def tracktorpedo(w, step, i, n, iquad):
3430 "Torpedo-track animation."
3431 if not game.options & OPTION_CURSES:
3435 proutn(_("Track for torpedo number %d- ") % (i+1))
3438 proutn(_("Torpedo track- "))
3439 elif step==4 or step==9:
3443 if not damaged(DSRSENS) or game.condition=="docked":
3444 if i != 0 and step == 1:
3447 if (iquad=='.') or (iquad==' '):
3448 put_srscan_sym(w, '+')
3452 put_srscan_sym(w, iquad)
3454 curwnd.attron(curses.A_REVERSE)
3455 put_srscan_sym(w, iquad)
3459 curwnd.attroff(curses.A_REVERSE)
3460 put_srscan_sym(w, iquad)
3465 "Display the current galaxy chart."
3466 if game.options & OPTION_CURSES:
3467 setwnd(message_window)
3468 message_window.clear()
3470 if game.options & OPTION_TTY:
3475 def prstat(txt, data):
3477 if game.options & OPTION_CURSES:
3479 setwnd(status_window)
3481 proutn(" " * (NSYM - len(txt)))
3484 if game.options & OPTION_CURSES:
3485 setwnd(report_window)
3487 # Code from moving.c begins here
3489 def imove(icourse=None, noattack=False):
3490 "Movement execution for warp, impulse, supernova, and tractor-beam events."
3493 def newquadrant(noattack):
3494 # Leaving quadrant -- allow final enemy attack
3495 # Don't do it if being pushed by Nova
3496 if len(game.enemies) != 0 and not noattack:
3498 for enemy in game.enemies:
3499 finald = (w - enemy.location).distance()
3500 enemy.kavgd = 0.5 * (finald + enemy.kdist)
3501 # Stas Sergeev added the condition
3502 # that attacks only happen if Klingons
3503 # are present and your skill is good.
3504 if game.skill > SKILL_GOOD and game.klhere > 0 and not game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
3505 attack(torps_ok=False)
3508 # check for edge of galaxy
3512 if icourse.final.i < 0:
3513 icourse.final.i = -icourse.final.i
3515 if icourse.final.j < 0:
3516 icourse.final.j = -icourse.final.j
3518 if icourse.final.i >= GALSIZE*QUADSIZE:
3519 icourse.final.i = (GALSIZE*QUADSIZE*2) - icourse.final.i
3521 if icourse.final.j >= GALSIZE*QUADSIZE:
3522 icourse.final.j = (GALSIZE*QUADSIZE*2) - icourse.final.j
3530 if game.nkinks == 3:
3531 # Three strikes -- you're out!
3535 prout(_("YOU HAVE ATTEMPTED TO CROSS THE NEGATIVE ENERGY BARRIER"))
3536 prout(_("AT THE EDGE OF THE GALAXY. THE THIRD TIME YOU TRY THIS,"))
3537 prout(_("YOU WILL BE DESTROYED."))
3538 # Compute final position in new quadrant
3539 if trbeam: # Don't bother if we are to be beamed
3541 game.quadrant = icourse.final.quadrant()
3542 game.sector = icourse.final.sector()
3544 prout(_("Entering Quadrant %s.") % game.quadrant)
3545 game.quad[game.sector.i][game.sector.j] = game.ship
3547 if game.skill>SKILL_NOVICE:
3548 attack(torps_ok=False)
3550 def check_collision(h):
3551 iquad = game.quad[h.i][h.j]
3553 # object encountered in flight path
3554 stopegy = 50.0*icourse.distance/game.optime
3555 if iquad in ('T', 'K', 'C', 'S', 'R', '?'):
3556 for enemy in game.enemies:
3557 if enemy.location == game.sector:
3559 collision(rammed=False, enemy=enemy)
3563 prouts(_("***RED ALERT! RED ALERT!"))
3565 proutn("***" + crmshp())
3566 proutn(_(" pulled into black hole at Sector %s") % h)
3567 # Getting pulled into a black hole was certain
3568 # death in Almy's original. Stas Sergeev added a
3569 # possibility that you'll get timewarped instead.
3571 for m in range(NDEVICES):
3572 if game.damage[m]>0:
3574 probf=math.pow(1.4,(game.energy+game.shield)/5000.0-1.0)*math.pow(1.3,1.0/(n+1)-1.0)
3575 if (game.options & OPTION_BLKHOLE) and withprob(1-probf):
3585 prout(_(" encounters Tholian web at %s;") % h)
3587 prout(_(" blocked by object at %s;") % h)
3588 proutn(_("Emergency stop required "))
3589 prout(_("%2d units of energy.") % int(stopegy))
3590 game.energy -= stopegy
3591 if game.energy <= 0:
3598 prout(_("Helmsman Sulu- \"Leaving standard orbit.\""))
3599 game.inorbit = False
3600 # If tractor beam is to occur, don't move full distance
3601 if game.state.date+game.optime >= scheduled(FTBEAM):
3603 game.condition = "red"
3604 icourse.distance = icourse.distance*(scheduled(FTBEAM)-game.state.date)/game.optime + 0.1
3605 game.optime = scheduled(FTBEAM) - game.state.date + 1e-5
3607 game.quad[game.sector.i][game.sector.j] = '.'
3608 for m in range(icourse.moves):
3610 w = icourse.sector()
3611 if icourse.origin.quadrant() != icourse.location.quadrant():
3612 newquadrant(noattack)
3614 elif check_collision(w):
3615 print("Collision detected")
3619 # We're in destination quadrant -- compute new average enemy distances
3620 game.quad[game.sector.i][game.sector.j] = game.ship
3622 for enemy in game.enemies:
3623 finald = (w-enemy.location).distance()
3624 enemy.kavgd = 0.5 * (finald + enemy.kdist)
3625 enemy.kdist = finald
3627 if not game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
3628 attack(torps_ok=False)
3629 for enemy in game.enemies:
3630 enemy.kavgd = enemy.kdist
3633 setwnd(message_window)
3637 "Dock our ship at a starbase."
3639 if game.condition == "docked" and verbose:
3640 prout(_("Already docked."))
3643 prout(_("You must first leave standard orbit."))
3645 if not game.base.is_valid() or abs(game.sector.i-game.base.i) > 1 or abs(game.sector.j-game.base.j) > 1:
3646 prout(crmshp() + _(" not adjacent to base."))
3648 game.condition = "docked"
3652 if game.energy < game.inenrg:
3653 game.energy = game.inenrg
3654 game.shield = game.inshld
3655 game.torps = game.intorps
3656 game.lsupres = game.inlsr
3657 game.state.crew = FULLCREW
3658 if not damaged(DRADIO) and \
3659 ((is_scheduled(FCDBAS) or game.isatb == 1) and not game.iseenit):
3660 # get attack report from base
3661 prout(_("Lt. Uhura- \"Captain, an important message from the starbase:\""))
3665 def cartesian(loc1=None, loc2=None):
3667 return game.quadrant * QUADSIZE + game.sector
3669 return game.quadrant * QUADSIZE + loc1
3671 return loc1 * QUADSIZE + loc2
3673 def getcourse(isprobe):
3674 "Get a course and distance from the user."
3676 dquad = copy.copy(game.quadrant)
3677 navmode = "unspecified"
3681 if game.landed and not isprobe:
3682 prout(_("Dummy! You can't leave standard orbit until you"))
3683 proutn(_("are back aboard the ship."))
3686 while navmode == "unspecified":
3687 if damaged(DNAVSYS):
3689 prout(_("Computer damaged; manual navigation only"))
3691 prout(_("Computer damaged; manual movement only"))
3696 key = scanner.nexttok()
3698 proutn(_("Manual or automatic- "))
3701 elif key == "IHALPHA":
3702 if scanner.sees("manual"):
3704 key = scanner.nexttok()
3706 elif scanner.sees("automatic"):
3707 navmode = "automatic"
3708 key = scanner.nexttok()
3716 prout(_("(Manual navigation assumed.)"))
3718 prout(_("(Manual movement assumed.)"))
3722 if navmode == "automatic":
3723 while key == "IHEOL":
3725 proutn(_("Target quadrant or quadrant§or- "))
3727 proutn(_("Destination sector or quadrant§or- "))
3730 key = scanner.nexttok()
3734 xi = int(round(scanner.real))-1
3735 key = scanner.nexttok()
3739 xj = int(round(scanner.real))-1
3740 key = scanner.nexttok()
3742 # both quadrant and sector specified
3743 xk = int(round(scanner.real))-1
3744 key = scanner.nexttok()
3748 xl = int(round(scanner.real))-1
3754 # only one pair of numbers was specified
3756 # only quadrant specified -- go to center of dest quad
3759 dsect.j = dsect.i = 4 # preserves 1-origin behavior
3761 # only sector specified
3765 if not dquad.valid_quadrant() or not dsect.valid_sector():
3772 prout(_("Helmsman Sulu- \"Course locked in for Sector %s.\"") % dsect)
3774 prout(_("Ensign Chekov- \"Course laid in, Captain.\""))
3775 # the actual deltas get computed here
3776 delta.j = dquad.j-game.quadrant.j + (dsect.j-game.sector.j)/(QUADSIZE*1.0)
3777 delta.i = game.quadrant.i-dquad.i + (game.sector.i-dsect.i)/(QUADSIZE*1.0)
3779 while key == "IHEOL":
3780 proutn(_("X and Y displacements- "))
3783 key = scanner.nexttok()
3788 delta.j = scanner.real
3789 key = scanner.nexttok()
3793 delta.i = scanner.real
3794 # Check for zero movement
3795 if delta.i == 0 and delta.j == 0:
3798 if itemp == "verbose" and not isprobe:
3800 prout(_("Helmsman Sulu- \"Aye, Sir.\""))
3802 return course(bearing=delta.bearing(), distance=delta.distance())
3805 def __init__(self, bearing, distance, origin=None):
3806 self.distance = distance
3807 self.bearing = bearing
3809 self.origin = cartesian(game.quadrant, game.sector)
3811 self.origin = origin
3812 # The bearing() code we inherited from FORTRAN is actually computing
3813 # clockface directions!
3814 if self.bearing < 0.0:
3815 self.bearing += 12.0
3816 self.angle = ((15.0 - self.bearing) * 0.5235988)
3818 self.origin = cartesian(game.quadrant, game.sector)
3820 self.origin = cartesian(game.quadrant, origin)
3821 self.increment = Coord(-math.sin(self.angle), math.cos(self.angle))
3822 bigger = max(abs(self.increment.i), abs(self.increment.j))
3823 self.increment /= bigger
3824 self.moves = int(round(10*self.distance*bigger))
3826 self.final = (self.location + self.moves*self.increment).roundtogrid()
3828 self.location = self.origin
3831 return self.location.roundtogrid() == self.final
3833 "Next step on course."
3835 self.nextlocation = self.location + self.increment
3836 samequad = (self.location.quadrant() == self.nextlocation.quadrant())
3837 self.location = self.nextlocation
3840 return self.location.quadrant()
3842 return self.location.sector()
3843 def power(self, warp):
3844 return self.distance*(warp**3)*(game.shldup+1)
3845 def time(self, warp):
3846 return 10.0*self.distance/warp**2
3849 "Move under impulse power."
3851 if damaged(DIMPULS):
3854 prout(_("Engineer Scott- \"The impulse engines are damaged, Sir.\""))
3856 if game.energy > 30.0:
3858 course = getcourse(isprobe=False)
3861 power = 20.0 + 100.0*course.distance
3864 if power >= game.energy:
3865 # Insufficient power for trip
3867 prout(_("First Officer Spock- \"Captain, the impulse engines"))
3868 prout(_("require 20.0 units to engage, plus 100.0 units per"))
3869 if game.energy > 30:
3870 proutn(_("quadrant. We can go, therefore, a maximum of %d") %
3871 int(0.01 * (game.energy-20.0)-0.05))
3872 prout(_(" quadrants.\""))
3874 prout(_("quadrant. They are, therefore, useless.\""))
3877 # Make sure enough time is left for the trip
3878 game.optime = course.distance/0.095
3879 if game.optime >= game.state.remtime:
3880 prout(_("First Officer Spock- \"Captain, our speed under impulse"))
3881 prout(_("power is only 0.95 sectors per stardate. Are you sure"))
3882 proutn(_("we dare spend the time?\" "))
3885 # Activate impulse engines and pay the cost
3886 imove(course, noattack=False)
3890 power = 20.0 + 100.0*course.distance
3891 game.energy -= power
3892 game.optime = course.distance/0.095
3893 if game.energy <= 0:
3897 def warp(wcourse, involuntary):
3898 "ove under warp drive."
3899 blooey = False; twarp = False
3900 if not involuntary: # Not WARPX entry