2 sst.py =-- Super Star Trek in Python
5 import os, sys, math, curses
7 SSTDOC = "/usr/share/doc/sst/sst.doc"
10 def _(str): return str
14 NINHAB = (GALSIZE * GALSIZE / 2)
16 PLNETMAX = (NINHAB + MAXUNINHAB)
18 BASEMAX = (GALSIZE * GALSIZE / 12)
21 FULLCREW = 428 # BSD Trek was 387, that's wrong
24 # These functions hide the difference between 0-origin and 1-origin addressing.
25 def VALID_QUADRANT(x, y): return ((x)>=1 and (x)<=GALSIZE and (y)>=1 and (y)<=GALSIZE)
26 def VALID_SECTOR(x, y): return ((x)>=1 and (x)<=QUADSIZE and (y)>=1 and (y)<=QUADSIZE)
28 def square(i): return ((i)*(i))
29 def distance(c1, c2): return math.sqrt(square(c1.x - c2.x) + square(c1.y - c2.y))
30 def invalidate(w): w.x = w.y = 0
31 def is_valid(w): return (w.x != 0 and w.y != 0)
34 def __init(self, x=None, y=None):
38 self.x = self.y = None
40 return self.x != None and self.y != None
41 def __eq__(self, other):
42 return self.x == other.y and self.x == other.y
43 def __add__(self, other):
44 return coord(self.x+self.x, self.y+self.y)
45 def __sub__(self, other):
46 return coord(self.x-self.x, self.y-self.y)
47 def distance(self, other):
48 return math.sqrt((self.x - other.x)**2 + (self.y - other.y)**2)
50 return coord(self.x / abs(x), self.y / abs(y));
54 return "%d - %d" % (self.x, self.y)
58 self.name = None # string-valued if inhabited
59 self.w = coord() # quadrant located
60 self.pclass = None # could be ""M", "N", "O", or "destroyed"
61 self.crystals = None # could be "mined", "present", "absent"
62 self.known = None # could be "unknown", "known", "shuttle_down"
64 # How to represent features
94 self.status = None # Could be "secure", "distressed", "enslaved"
104 self.snap = False # snapshot taken
105 self.crew = None # crew complement
106 self.remkl = None # remaining klingons
107 self.remcom = None # remaining commanders
108 self.nscrem = None # remaining super commanders
109 self.rembase = None # remaining bases
110 self.starkl = None # destroyed stars
111 self.basekl = None # destroyed bases
112 self.nromrem = None # Romulans remaining
113 self.nplankl = None # destroyed uninhabited planets
114 self.nworldkl = None # destroyed inhabited planets
115 self.planets = [] # Planet information
116 for i in range(PLNETMAX):
117 self.planets.append(planet())
118 self.date = None # stardate
119 self.remres = None # remaining resources
120 self.remtime = None # remaining time
121 self.baseq = [] # Base quadrant coordinates
122 for i in range(BASEMAX+1):
123 self.baseq.append(coord())
124 self.kcmdr = [] # Commander quadrant coordinates
125 for i in range(QUADSIZE+1):
126 self.kcmdr.append(coord())
127 self.kscmdr = coord() # Supercommander quadrant coordinates
128 self.galaxy = [] # The Galaxy (subscript 0 not used)
129 for i in range(GALSIZE+1):
130 self.chart.append([])
131 for j in range(GALSIZE+1):
132 self.galaxy[i].append(quadrant())
133 self.chart = [] # the starchart (subscript 0 not used)
134 for i in range(GALSIZE+1):
135 self.chart.append([])
136 for j in range(GALSIZE+1):
137 self.chart[i].append(page())
141 self.date = None # A real number
142 self.quadrant = None # A coord structure
145 OPTION_ALL = 0xffffffff
146 OPTION_TTY = 0x00000001 # old interface
147 OPTION_CURSES = 0x00000002 # new interface
148 OPTION_IOMODES = 0x00000003 # cover both interfaces
149 OPTION_PLANETS = 0x00000004 # planets and mining
150 OPTION_THOLIAN = 0x00000008 # Tholians and their webs
151 OPTION_THINGY = 0x00000010 # Space Thingy can shoot back
152 OPTION_PROBE = 0x00000020 # deep-space probes
153 OPTION_SHOWME = 0x00000040 # bracket Enterprise in chart
154 OPTION_RAMMING = 0x00000080 # enemies may ram Enterprise
155 OPTION_MVBADDY = 0x00000100 # more enemies can move
156 OPTION_BLKHOLE = 0x00000200 # black hole may timewarp you
157 OPTION_BASE = 0x00000400 # bases have good shields
158 OPTION_WORLDS = 0x00000800 # logic for inhabited worlds
159 OPTION_PLAIN = 0x01000000 # user chose plain game
160 OPTION_ALMY = 0x02000000 # user chose Almy variant
179 NDEVICES= 16 # Number of devices
181 def damaged(dev): return (game.damage[dev] != 0.0)
183 # Define future events
184 FSPY = 0 # Spy event happens always (no future[] entry)
185 # can cause SC to tractor beam Enterprise
186 FSNOVA = 1 # Supernova
187 FTBEAM = 2 # Commander tractor beams Enterprise
188 FSNAP = 3 # Snapshot for time warp
189 FBATTAK = 4 # Commander attacks base
190 FCDBAS = 5 # Commander destroys base
191 FSCMOVE = 6 # Supercommander moves (might attack base)
192 FSCDBAS = 7 # Supercommander destroys base
193 FDSPROB = 8 # Move deep space probe
194 FDISTR = 9 # Emit distress call from an inhabited world
195 FENSLV = 10 # Inhabited word is enslaved */
196 FREPRO = 11 # Klingons build a ship in an enslaved system
200 # abstract out the event handling -- underlying data structures will change
201 # when we implement stateful events
203 def findevent(evtype): return game.future[evtype]
207 self.options = None # Game options
208 self.state = None # A snapshot structure
209 self.snapsht = None # Last snapshot taken for time-travel purposes
210 self.quad = [[IHDOT * (QUADSIZE+1)] * (QUADSIZE+1)] # contents of our quadrant
211 self.kpower = [[0 * (QUADSIZE+1)] * (QUADSIZE+1)] # enemy energy levels
212 self.kdist = [[0 * (QUADSIZE+1)] * (QUADSIZE+1)] # enemy distances
213 self.kavgd = [[0 * (QUADSIZE+1)] * (QUADSIZE+1)] # average distances
214 self.damage = [0] * NDEVICES # damage encountered
215 self.future = [0.0] * NEVENTS # future events
216 for i in range(NEVENTS):
217 self.future.append(event())
218 self.passwd = None; # Self Destruct password
219 self.ks = [[None * (QUADSIZE+1)] * (QUADSIZE+1)] # enemy sector locations
220 self.quadrant = None # where we are in the large
221 self.sector = None # where we are in the small
222 self.tholian = None # coordinates of Tholian
223 self.base = None # position of base in current quadrant
224 self.battle = None # base coordinates being attacked
225 self.plnet = None # location of planet in quadrant
226 self.probec = None # current probe quadrant
227 self.gamewon = False # Finished!
228 self.ididit = False # action taken -- allows enemy to attack
229 self.alive = False # we are alive (not killed)
230 self.justin = False # just entered quadrant
231 self.shldup = False # shields are up
232 self.shldchg = False # shield is changing (affects efficiency)
233 self.comhere = False # commander here
234 self.ishere = False # super-commander in quadrant
235 self.iscate = False # super commander is here
236 self.ientesc = False # attempted escape from supercommander
237 self.ithere = False # Tholian is here
238 self.resting = False # rest time
239 self.icraft = False # Kirk in Galileo
240 self.landed = False # party on planet (true), on ship (false)
241 self.alldone = False # game is now finished
242 self.neutz = False # Romulan Neutral Zone
243 self.isarmed = False # probe is armed
244 self.inorbit = False # orbiting a planet
245 self.imine = False # mining
246 self.icrystl = False # dilithium crystals aboard
247 self.iseenit = False # seen base attack report
248 self.thawed = False # thawed game
249 self.condition = None # "green", "yellow", "red", "docked", "dead"
250 self.iscraft = None # "onship", "offship", "removed"
251 self.skill = None # Player skill level
252 self.inkling = 0 # initial number of klingons
253 self.inbase = 0 # initial number of bases
254 self.incom = 0 # initial number of commanders
255 self.inscom = 0 # initial number of commanders
256 self.inrom = 0 # initial number of commanders
257 self.instar = 0 # initial stars
258 self.intorps = 0 # initial/max torpedoes
259 self.torps = 0 # number of torpedoes
260 self.ship = 0 # ship type -- 'E' is Enterprise
261 self.abandoned = 0 # count of crew abandoned in space
262 self.length = 0 # length of game
263 self.klhere = 0 # klingons here
264 self.casual = 0 # causalties
265 self.nhelp = 0 # calls for help
266 self.nkinks = 0 # count of energy-barrier crossings
267 self.iplnet = 0 # planet # in quadrant
268 self.inplan = 0 # initial planets
269 self.nenhere = 0 # number of enemies in quadrant
270 self.irhere = 0 # Romulans in quadrant
271 self.isatb = 0 # =1 if super commander is attacking base
272 self.tourn = 0 # tournament number
273 self.proben = 0 # number of moves for probe
274 self.nprobes = 0 # number of probes available
275 self.inresor = 0.0 # initial resources
276 self.intime = 0.0 # initial time
277 self.inenrg = 0.0 # initial/max energy
278 self.inshld = 0.0 # initial/max shield
279 self.inlsr = 0.0 # initial life support resources
280 self.indate = 0.0 # initial date
281 self.energy = 0.0 # energy level
282 self.shield = 0.0 # shield level
283 self.warpfac = 0.0 # warp speed
284 self.wfacsq = 0.0 # squared warp factor
285 self.lsupres = 0.0 # life support reserves
286 self.dist = 0.0 # movement distance
287 self.direc = 0.0 # movement direction
288 self.optime = 0.0 # time taken by current operation
289 self.docfac = 0.0 # repair factor when docking (constant?)
290 self.damfac = 0.0 # damage factor
291 self.lastchart = 0.0 # time star chart was last updated
292 self.cryprob = 0.0 # probability that crystal will work
293 self.probex = 0.0 # location of probe
295 self.probeinx = 0.0 # probe x,y increment
296 self.probeiny = 0.0 #
297 self.height = 0.0 # height of orbit around planet
299 # From enumerated type 'feature'
320 # From enumerated type 'FINTYPE'
344 # From enumerated type 'COLORS'
363 # Code from ai.c begins here
365 def tryexit(look, ienm, loccom, irun):
366 # a bad guy attempts to bug out
368 iq.x = game.quadrant.x+(look.x+(QUADSIZE-1))/QUADSIZE - 1
369 iq.y = game.quadrant.y+(look.y+(QUADSIZE-1))/QUADSIZE - 1
370 if not VALID_QUADRANT(iq.x,iq.y) or \
371 game.state.galaxy[iq.x][iq.y].supernova or \
372 game.state.galaxy[iq.x][iq.y].klingons > MAXKLQUAD-1:
373 return False; # no can do -- neg energy, supernovae, or >MAXKLQUAD-1 Klingons
375 return False; # Romulans cannot escape!
377 # avoid intruding on another commander's territory
379 for n in range(1, game.state.remcom+1):
380 if same(game.state.kcmdr[n],iq):
382 # refuse to leave if currently attacking starbase
383 if same(game.battle, game.quadrant):
385 # don't leave if over 1000 units of energy
386 if game.kpower[loccom] > 1000.0:
388 # print escape message and move out of quadrant.
389 # we know this if either short or long range sensors are working
390 if not damaged(DSRSENS) or not damaged(DLRSENS) or \
391 game.condition == docked:
392 crmena(True, ienm, "sector", game.ks[loccom])
393 prout(_(" escapes to Quadrant %s (and regains strength).") % q)
394 # handle local matters related to escape
395 game.quad[game.ks[loccom].x][game.ks[loccom].y] = IHDOT
396 game.ks[loccom] = game.ks[game.nenhere]
397 game.kavgd[loccom] = game.kavgd[game.nenhere]
398 game.kpower[loccom] = game.kpower[game.nenhere]
399 game.kdist[loccom] = game.kdist[game.nenhere]
402 if game.condition != docked:
404 # Handle global matters related to escape
405 game.state.galaxy[game.quadrant.x][game.quadrant.y].klingons -= 1
406 game.state.galaxy[iq.x][iq.y].klingons += 1
412 schedule(FSCMOVE, 0.2777)
416 for n in range(1, game.state.remcom+1):
417 if same(game.state.kcmdr[n], game.quadrant):
418 game.state.kcmdr[n]=iq
421 return True; # success
424 # The bad-guy movement algorithm:
426 # 1. Enterprise has "force" based on condition of phaser and photon torpedoes.
427 # If both are operating full strength, force is 1000. If both are damaged,
428 # force is -1000. Having shields down subtracts an additional 1000.
430 # 2. Enemy has forces equal to the energy of the attacker plus
431 # 100*(K+R) + 500*(C+S) - 400 for novice through good levels OR
432 # 346*K + 400*R + 500*(C+S) - 400 for expert and emeritus.
434 # Attacker Initial energy levels (nominal):
435 # Klingon Romulan Commander Super-Commander
436 # Novice 400 700 1200
438 # Good 450 800 1300 1750
439 # Expert 475 850 1350 1875
440 # Emeritus 500 900 1400 2000
441 # VARIANCE 75 200 200 200
443 # Enemy vessels only move prior to their attack. In Novice - Good games
444 # only commanders move. In Expert games, all enemy vessels move if there
445 # is a commander present. In Emeritus games all enemy vessels move.
447 # 3. If Enterprise is not docked, an aggressive action is taken if enemy
448 # forces are 1000 greater than Enterprise.
450 # Agressive action on average cuts the distance between the ship and
451 # the enemy to 1/4 the original.
453 # 4. At lower energy advantage, movement units are proportional to the
454 # advantage with a 650 advantage being to hold ground, 800 to move forward
455 # 1, 950 for two, 150 for back 4, etc. Variance of 100.
457 # If docked, is reduced by roughly 1.75*game.skill, generally forcing a
458 # retreat, especially at high skill levels.
460 # 5. Motion is limited to skill level, except for SC hi-tailing it out.
463 def movebaddy(com, loccom, ienm):
464 # tactical movement for the bad guys
465 next = coord(); look = coord()
467 # This should probably be just game.comhere + game.ishere
468 if game.skill >= SKILL_EXPERT:
469 nbaddys = ((game.comhere*2 + game.ishere*2+game.klhere*1.23+game.irhere*1.5)/2.0)
471 nbaddys = game.comhere + game.ishere
473 dist1 = game.kdist[loccom]
474 mdist = int(dist1 + 0.5); # Nearest integer distance
476 # If SC, check with spy to see if should hi-tail it
478 (game.kpower[loccom] <= 500.0 or (game.condition=="docked" and not damaged(DPHOTON))):
482 # decide whether to advance, retreat, or hold position
483 forces = game.kpower[loccom]+100.0*game.nenhere+400*(nbaddys-1)
485 forces += 1000; # Good for enemy if shield is down!
486 if not damaged(DPHASER) or not damaged(DPHOTON):
487 if damaged(DPHASER): # phasers damaged
490 forces -= 0.2*(game.energy - 2500.0)
491 if damaged(DPHOTON): # photon torpedoes damaged
494 forces -= 50.0*game.torps
496 # phasers and photon tubes both out!
499 if forces <= 1000.0 and game.condition != "docked": # Typical situation
500 motion = ((forces+200.0*Rand())/150.0) - 5.0
502 if forces > 1000.0: # Very strong -- move in for kill
503 motion = (1.0-square(Rand()))*dist1 + 1.0
504 if game.condition=="docked" and (game.options & OPTION_BASE): # protected by base -- back off !
505 motion -= game.skill*(2.0-square(Rand()))
507 proutn("=== MOTION = %d, FORCES = %1.2f, " % (motion, forces))
508 # don't move if no motion
511 # Limit motion according to skill
512 if abs(motion) > game.skill:
517 # calculate preferred number of steps
522 if motion > 0 and nsteps > mdist:
523 nsteps = mdist; # don't overshoot
524 if nsteps > QUADSIZE:
525 nsteps = QUADSIZE; # This shouldn't be necessary
527 nsteps = 1; # This shouldn't be necessary
529 proutn("NSTEPS = %d:" % nsteps)
530 # Compute preferred values of delta X and Y
531 mx = game.sector.x - com.x
532 my = game.sector.y - com.y
533 if 2.0 * abs(mx) < abs(my):
535 if 2.0 * abs(my) < abs(game.sector.x-com.x):
549 for ll in range(nsteps):
551 proutn(" %d" % (ll+1))
552 # Check if preferred position available
564 attempts = 0; # Settle mysterious hang problem
565 while attempts < 20 and not success:
567 if look.x < 1 or look.x > QUADSIZE:
568 if motion < 0 and tryexit(look, ienm, loccom, irun):
570 if krawlx == mx or my == 0:
572 look.x = next.x + krawlx
574 elif look.y < 1 or look.y > QUADSIZE:
575 if motion < 0 and tryexit(look, ienm, loccom, irun):
577 if krawly == my or mx == 0:
579 look.y = next.y + krawly
581 elif (game.options & OPTION_RAMMING) and game.quad[look.x][look.y] != IHDOT:
582 # See if we should ram ship
583 if game.quad[look.x][look.y] == game.ship and \
584 (ienm == IHC or ienm == IHS):
587 if krawlx != mx and my != 0:
588 look.x = next.x + krawlx
590 elif krawly != my and mx != 0:
591 look.y = next.y + krawly
594 break; # we have failed
606 # Put commander in place within same quadrant
607 game.quad[com.x][com.y] = IHDOT
608 game.quad[next.x][next.y] = ienm
609 if not same(next, com):
611 game.ks[loccom] = next
612 game.kdist[loccom] = game.kavgd[loccom] = distance(game.sector, next)
613 if not damaged(DSRSENS) or game.condition == docked:
616 proutn(_(" from Sector %s") % com)
617 if game.kdist[loccom] < dist1:
618 proutn(_(" advances to "))
620 proutn(_(" retreats to "))
621 prout("Sector %s." % next)
624 # Klingon tactical movement
627 # Figure out which Klingon is the commander (or Supercommander)
630 for i in range(1, game.nenhere+1):
632 if game.quad[w.x][w.y] == IHC:
636 for i in range(1, game.nenhere+1):
638 if game.quad[w.x][w.y] == IHS:
641 # If skill level is high, move other Klingons and Romulans too!
642 # Move these last so they can base their actions on what the
644 if game.skill >= SKILL_EXPERT and (game.options & OPTION_MVBADDY):
645 for i in range(1, game.nenhere+1):
647 if game.quad[w.x][w.y] == IHK or game.quad[w.x][w.y] == IHR:
648 movebaddy(w, i, game.quad[w.x][w.y])
651 def movescom(iq, avoid):
652 # commander movement helper
653 if same(iq, game.quadrant) or not VALID_QUADRANT(iq.x, iq.y) or \
654 game.state.galaxy[iq.x][iq.y].supernova or \
655 game.state.galaxy[iq.x][iq.y].klingons > MAXKLQUAD-1:
658 # Avoid quadrants with bases if we want to avoid Enterprise
659 for i in range(1, game.state.rembase+1):
660 if same(game.state.baseq[i], iq):
662 if game.justin and not game.iscate:
665 game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].klingons -= 1
666 game.state.kscmdr = iq
667 game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].klingons += 1
669 # SC has scooted, Remove him from current quadrant
675 for i in range(1, game.nenhere+1):
676 if game.quad[game.ks[i].x][game.ks[i].y] == IHS:
678 game.quad[game.ks[i].x][game.ks[i].y] = IHDOT
679 game.ks[i] = game.ks[game.nenhere]
680 game.kdist[i] = game.kdist[game.nenhere]
681 game.kavgd[i] = game.kavgd[game.nenhere]
682 game.kpower[i] = game.kpower[game.nenhere]
685 if game.condition!=docked:
688 # check for a helpful planet
689 for i in range(game.inplan):
690 if same(game.state.planets[i].w, game.state.kscmdr) and \
691 game.state.planets[i].crystals == present:
693 game.state.planets[i].pclass = destroyed
694 game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].planet = NOPLANET
695 if not damaged(DRADIO) or game.condition == docked:
697 prout(_("Lt. Uhura- \"Captain, Starfleet Intelligence reports"))
698 proutn(_(" a planet in Quadrant %s has been destroyed") % game.state.kscmdr)
699 prout(_(" by the Super-commander.\""))
701 return False; # looks good!
703 def supercommander():
704 # move the Super Commander
705 iq = coord(); sc = coord(); ibq = coord(); idelta = coord()
708 prout("== SUPERCOMMANDER")
709 # Decide on being active or passive
710 avoid = ((game.incom - game.state.remcom + game.inkling - game.state.remkl)/(game.state.date+0.01-game.indate) < 0.1*game.skill*(game.skill+1.0) or \
711 (game.state.date-game.indate) < 3.0)
712 if not game.iscate and avoid:
713 # compute move away from Enterprise
714 idelta = game.state.kscmdr-game.quadrant
715 if math.sqrt(idelta.x*idelta.x+idelta.y*idelta.y) > 2.0:
717 idelta.x = game.state.kscmdr.y-game.quadrant.y
718 idelta.y = game.quadrant.x-game.state.kscmdr.x
720 # compute distances to starbases
721 if game.state.rembase <= 0:
725 sc = game.state.kscmdr
726 for i in range(1, game.state.rembase+1):
727 basetbl.append((i, distance(game.state.baseq[i], sc)))
728 if game.state.rembase > 1:
729 basetbl.sort(lambda x, y: cmp(x[1]. y[1]))
730 # look for nearest base without a commander, no Enterprise, and
731 # without too many Klingons, and not already under attack.
732 ifindit = iwhichb = 0
733 for i2 in range(1, game.state.rembase+1):
734 i = basetbl[i2][0]; # bug in original had it not finding nearest
735 ibq = game.state.baseq[i]
736 if same(ibq, game.quadrant) or same(ibq, game.battle) or \
737 game.state.galaxy[ibq.x][ibq.y].supernova or \
738 game.state.galaxy[ibq.x][ibq.y].klingons > MAXKLQUAD-1:
740 # if there is a commander, and no other base is appropriate,
741 # we will take the one with the commander
742 for j in range(1, game.state.remcom+1):
743 if same(ibq, game.state.kcmdr[j]) and ifindit!= 2:
747 if j > game.state.remcom: # no commander -- use this one
752 return; # Nothing suitable -- wait until next time
753 ibq = game.state.baseq[iwhichb]
754 # decide how to move toward base
755 idelta = ibq - game.state.kscmdr
756 # Maximum movement is 1 quadrant in either or both axes
757 idelta = idelta.sgn()
758 # try moving in both x and y directions
759 # there was what looked like a bug in the Almy C code here,
760 # but it might be this translation is just wrong.
761 iq = game.state.kscmdr + idelta
762 if movescom(iq, avoid):
763 # failed -- try some other maneuvers
764 if idelta.x==0 or idelta.y==0:
767 iq.y = game.state.kscmdr.y + 1
768 if movescom(iq, avoid):
769 iq.y = game.state.kscmdr.y - 1
772 iq.x = game.state.kscmdr.x + 1
773 if movescom(iq, avoid):
774 iq.x = game.state.kscmdr.x - 1
777 # try moving just in x or y
778 iq.y = game.state.kscmdr.y
779 if movescom(iq, avoid):
780 iq.y = game.state.kscmdr.y + idelta.y
781 iq.x = game.state.kscmdr.x
784 if game.state.rembase == 0:
787 for i in range(1, game.state.rembase+1):
788 ibq = game.state.baseq[i]
789 if same(ibq, game.state.kscmdr) and same(game.state.kscmdr, game.battle):
792 return; # no, don't attack base!
795 schedule(FSCDBAS, 1.0 +2.0*Rand())
796 if is_scheduled(FCDBAS):
797 postpone(FSCDBAS, scheduled(FCDBAS)-game.state.date)
798 if damaged(DRADIO) and game.condition != docked:
802 prout(_("Lt. Uhura- \"Captain, the starbase in Quadrant %s") \
804 prout(_(" reports that it is under attack from the Klingon Super-commander."))
805 proutn(_(" It can survive until stardate %d.\"") \
806 % int(scheduled(FSCDBAS)))
809 prout(_("Mr. Spock- \"Captain, shall we cancel the rest period?\""))
813 game.optime = 0.0; # actually finished
815 # Check for intelligence report
818 (damaged(DRADIO) and game.condition != docked) or \
819 not game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].charted):
822 prout(_("Lt. Uhura- \"Captain, Starfleet Intelligence reports"))
823 proutn(_(" the Super-commander is in Quadrant %s,") % game.state.kscmdr)
828 if not game.ithere or game.justin:
831 if game.tholian.x == 1 and game.tholian.y == 1:
832 idx = 1; idy = QUADSIZE
833 elif game.tholian.x == 1 and game.tholian.y == QUADSIZE:
834 idx = QUADSIZE; idy = QUADSIZE
835 elif game.tholian.x == QUADSIZE and game.tholian.y == QUADSIZE:
836 idx = QUADSIZE; idy = 1
837 elif game.tholian.x == QUADSIZE and game.tholian.y == 1:
840 # something is wrong!
844 # do nothing if we are blocked
845 if game.quad[idx][idy]!= IHDOT and game.quad[idx][idy]!= IHWEB:
847 game.quad[game.tholian.x][game.tholian.y] = IHWEB
849 if game.tholian.x != idx:
851 im = math.fabs(idx - game.tholian.x)*1.0/(idx - game.tholian.x)
852 while game.tholian.x != idx:
854 if game.quad[game.tholian.x][game.tholian.y]==IHDOT:
855 game.quad[game.tholian.x][game.tholian.y] = IHWEB
856 elif game.tholian.y != idy:
858 im = math.fabs(idy - game.tholian.y)*1.0/(idy - game.tholian.y)
859 while game.tholian.y != idy:
861 if game.quad[game.tholian.x][game.tholian.y]==IHDOT:
862 game.quad[game.tholian.x][game.tholian.y] = IHWEB
863 game.quad[game.tholian.x][game.tholian.y] = IHT
864 game.ks[game.nenhere] = game.tholian
866 # check to see if all holes plugged
867 for i in range(1, QUADSIZE+1):
868 if game.quad[1][i]!=IHWEB and game.quad[1][i]!=IHT:
870 if game.quad[QUADSIZE][i]!=IHWEB and game.quad[QUADSIZE][i]!=IHT:
872 if game.quad[i][1]!=IHWEB and game.quad[i][1]!=IHT:
874 if game.quad[i][QUADSIZE]!=IHWEB and game.quad[i][QUADSIZE]!=IHT:
876 # All plugged up -- Tholian splits
877 game.quad[game.tholian.x][game.tholian.y]=IHWEB
879 crmena(True, IHT, "sector", game.tholian)
880 prout(_(" completes web."))
885 # Code from battle.c begins here
887 def doshield(shraise):
888 # change shield status
901 prout(_("Shields damaged and down."))
908 proutn(_("Do you wish to change shield energy? "))
910 proutn(_("Energy to transfer to shields- "))
912 elif damaged(DSHIELD):
913 prout(_("Shields damaged and down."))
916 proutn(_("Shields are up. Do you want them down? "))
923 proutn(_("Shields are down. Do you want them up? "))
929 if action == "SHUP": # raise shields
931 prout(_("Shields already up."))
935 if game.condition != "docked":
937 prout(_("Shields raised."))
940 prout(_("Shields raising uses up last of energy."))
945 elif action == "SHDN":
947 prout(_("Shields already down."))
951 prout(_("Shields lowered."))
954 elif action == "NRG":
955 while scan() != IHREAL:
957 proutn(_("Energy to transfer to shields- "))
961 if aaitem > game.energy:
962 prout(_("Insufficient ship energy."))
965 if game.shield+aaitem >= game.inshld:
966 prout(_("Shield energy maximized."))
967 if game.shield+aaitem > game.inshld:
968 prout(_("Excess energy requested returned to ship energy"))
969 game.energy -= game.inshld-game.shield
970 game.shield = game.inshld
972 if aaitem < 0.0 and game.energy-aaitem > game.inenrg:
973 # Prevent shield drain loophole
975 prout(_("Engineering to bridge--"))
976 prout(_(" Scott here. Power circuit problem, Captain."))
977 prout(_(" I can't drain the shields."))
980 if game.shield+aaitem < 0:
981 prout(_("All shield energy transferred to ship."))
982 game.energy += game.shield
985 proutn(_("Scotty- \""))
987 prout(_("Transferring energy to shields.\""))
989 prout(_("Draining energy from shields.\""))
990 game.shield += aaitem
991 game.energy -= aaitem
995 # choose a device to damage, at random.
997 # Quoth Eric Allman in the code of BSD-Trek:
998 # "Under certain conditions you can get a critical hit. This
999 # sort of hit damages devices. The probability that a given
1000 # device is damaged depends on the device. Well protected
1001 # devices (such as the computer, which is in the core of the
1002 # ship and has considerable redundancy) almost never get
1003 # damaged, whereas devices which are exposed (such as the
1004 # warp engines) or which are particularly delicate (such as
1005 # the transporter) have a much higher probability of being
1008 # This is one place where OPTION_PLAIN does not restore the
1009 # original behavior, which was equiprobable damage across
1010 # all devices. If we wanted that, we'd return NDEVICES*Rand()
1011 # and have done with it. Also, in the original game, DNAVYS
1012 # and DCOMPTR were the same device.
1014 # Instead, we use a table of weights similar to the one from BSD Trek.
1015 # BSD doesn't have the shuttle, shield controller, death ray, or probes.
1016 # We don't have a cloaking device. The shuttle got the allocation
1017 # for the cloaking device, then we shaved a half-percent off
1018 # everything to have some weight to give DSHCTRL/DDRAY/DDSP.
1021 105, # DSRSENS: short range scanners 10.5%
1022 105, # DLRSENS: long range scanners 10.5%
1023 120, # DPHASER: phasers 12.0%
1024 120, # DPHOTON: photon torpedoes 12.0%
1025 25, # DLIFSUP: life support 2.5%
1026 65, # DWARPEN: warp drive 6.5%
1027 70, # DIMPULS: impulse engines 6.5%
1028 145, # DSHIELD: deflector shields 14.5%
1029 30, # DRADIO: subspace radio 3.0%
1030 45, # DSHUTTL: shuttle 4.5%
1031 15, # DCOMPTR: computer 1.5%
1032 20, # NAVCOMP: navigation system 2.0%
1033 75, # DTRANSP: transporter 7.5%
1034 20, # DSHCTRL: high-speed shield controller 2.0%
1035 10, # DDRAY: death ray 1.0%
1036 30, # DDSP: deep-space probes 3.0%
1038 idx = Rand() * 1000.0 # weights must sum to 1000
1040 for (i, w) in enumerate(weights):
1044 return None; # we should never get here
1046 def ram(ibumpd, ienm, w):
1047 # make our ship ram something
1048 prouts(_("***RED ALERT! RED ALERT!"))
1050 prout(_("***COLLISION IMMINENT."))
1054 hardness = {IHR:1.5, IHC:2.0, IHS:2.5, IHT:0.5, IHQUEST:4.0}.get(ienm, 1.0)
1056 proutn(_(" rammed by "))
1059 crmena(False, ienm, sector, w)
1061 proutn(_(" (original position)"))
1063 deadkl(w, ienm, game.sector)
1066 prout(_(" heavily damaged."))
1067 icas = 10.0+20.0*Rand()
1068 prout(_("***Sickbay reports %d casualties"), icas)
1070 game.state.crew -= icas
1072 # In the pre-SST2K version, all devices got equiprobably damaged,
1073 # which was silly. Instead, pick up to half the devices at
1074 # random according to our weighting table,
1076 ncrits = Rand() * (NDEVICES/2)
1077 for m in range(ncrits):
1079 if game.damage[dev] < 0:
1081 extradm = (10.0*hardness*Rand()+1.0)*game.damfac
1082 # Damage for at least time of travel!
1083 game.damage[dev] += game.optime + extradm
1085 prout(_("***Shields are down."))
1086 if game.state.remkl + game.state.remcom + game.state.nscrem:
1093 def torpedo(course, r, incoming, i, n):
1094 # let a photon torpedo fly
1097 ac = course + 0.25*r
1098 angle = (15.0-ac)*0.5235988
1099 bullseye = (15.0 - course)*0.5235988
1100 deltax = -math.sin(angle);
1101 deltay = math.cos(angle);
1102 x = incoming.x; y = incoming.y
1103 w = coord(); jw = coord()
1104 w.x = w.y = jw.x = jw.y = 0
1105 bigger = max(math.fabs(deltax), math.fabs(deltay))
1108 if not damaged(DSRSENS) or game.condition=="docked":
1109 setwnd(srscan_window)
1111 setwnd(message_window)
1112 # Loop to move a single torpedo
1113 for l in range(1, 15+1):
1118 if not VALID_SECTOR(w.x, w.y):
1120 iquad=game.quad[w.x][w.y]
1121 tracktorpedo(w, l, i, n, iquad)
1125 setwnd(message_window)
1126 if damaged(DSRSENS) and not game.condition=="docked":
1127 skip(1); # start new line after text track
1128 if iquad in (IHE, IHF): # Hit our ship
1130 proutn(_("Torpedo hits "))
1133 hit = 700.0 + 100.0*Rand() - \
1134 1000.0 * distance(w, incoming) * math.fabs(math.sin(bullseye-angle))
1135 newcnd(); # we're blown out of dock
1136 # We may be displaced.
1137 if game.landed or game.condition=="docked":
1138 return hit # Cheat if on a planet
1139 ang = angle + 2.5*(Rand()-0.5)
1140 temp = math.fabs(math.sin(ang))
1141 if math.fabs(math.cos(ang)) > temp:
1142 temp = math.fabs(math.cos(ang))
1143 xx = -math.sin(ang)/temp
1144 yy = math.cos(ang)/temp
1147 if not VALID_SECTOR(jw.x, jw.y):
1149 if game.quad[jw.x][jw.y]==IHBLANK:
1152 if game.quad[jw.x][jw.y]!=IHDOT:
1153 # can't move into object
1158 elif iquad in (IHC, IHS): # Hit a commander
1160 crmena(True, iquad, sector, w)
1161 prout(_(" uses anti-photon device;"))
1162 prout(_(" torpedo neutralized."))
1164 elif iquad in (IHR, IHK): # Hit a regular enemy
1166 for ll in range(1, game.nenhere+1):
1167 if same(w, game.ks[ll]):
1169 kp = math.fabs(game.kpower[ll])
1170 h1 = 700.0 + 100.0*Rand() - \
1171 1000.0 * distance(w, incoming) * math.fabs(math.sin(bullseye-angle))
1175 if game.kpower[ll] < 0:
1176 game.kpower[ll] -= -h1
1178 game.kpower[ll] -= h1
1179 if game.kpower[ll] == 0:
1182 crmena(True, iquad, "sector", w)
1183 # If enemy damaged but not destroyed, try to displace
1184 ang = angle + 2.5*(Rand()-0.5)
1185 temp = math.fabs(math.sin(ang))
1186 if math.fabs(math.cos(ang)) > temp:
1187 temp = math.fabs(math.cos(ang))
1188 xx = -math.sin(ang)/temp
1189 yy = math.cos(ang)/temp
1192 if not VALID_SECTOR(jw.x, jw.y):
1193 prout(_(" damaged but not destroyed."))
1195 if game.quad[jw.x][jw.y]==IHBLANK:
1196 prout(_(" buffeted into black hole."))
1197 deadkl(w, iquad, jw)
1199 if game.quad[jw.x][jw.y]!=IHDOT:
1200 # can't move into object
1201 prout(_(" damaged but not destroyed."))
1203 proutn(_(" damaged--"))
1207 elif iquad == IHB: # Hit a base
1209 prout(_("***STARBASE DESTROYED.."))
1210 for ll in range(1, game.state.rembase+1):
1211 if same(game.state.baseq[ll], game.quadrant):
1212 game.state.baseq[ll]=game.state.baseq[game.state.rembase]
1214 game.quad[w.x][w.y]=IHDOT
1215 game.state.rembase -= 1
1216 game.base.x=game.base.y=0
1217 game.state.galaxy[game.quadrant.x][game.quadrant.y].starbase -= 1
1218 game.state.chart[game.quadrant.x][game.quadrant.y].starbase -= 1
1219 game.state.basekl += 1
1222 elif iquad == IHP: # Hit a planet
1223 crmena(True, iquad, sector, w)
1224 prout(_(" destroyed."))
1225 game.state.nplankl += 1
1226 game.state.galaxy[game.quadrant.x][game.quadrant.y].planet = NOPLANET
1227 game.state.planets[game.iplnet].pclass = destroyed
1229 invalidate(game.plnet)
1230 game.quad[w.x][w.y] = IHDOT
1232 # captain perishes on planet
1235 elif iquad == IHW: # Hit an inhabited world -- very bad!
1236 crmena(True, iquad, sector, w)
1237 prout(_(" destroyed."))
1238 game.state.nworldkl += 1
1239 game.state.galaxy[game.quadrant.x][game.quadrant.y].planet = NOPLANET
1240 game.state.planets[game.iplnet].pclass = destroyed
1242 invalidate(game.plnet)
1243 game.quad[w.x][w.y] = IHDOT
1245 # captain perishes on planet
1247 prout(_("You have just destroyed an inhabited planet."))
1248 prout(_("Celebratory rallies are being held on the Klingon homeworld."))
1250 elif iquad == IHSTAR: # Hit a star
1254 crmena(True, IHSTAR, sector, w)
1255 prout(_(" unaffected by photon blast."))
1257 elif iquad == IHQUEST: # Hit a thingy
1258 if not (game.options & OPTION_THINGY) or Rand()>0.7:
1260 prouts(_("AAAAIIIIEEEEEEEEAAAAAAAAUUUUUGGGGGHHHHHHHHHHHH!!!"))
1262 prouts(_(" HACK! HACK! HACK! *CHOKE!* "))
1264 proutn(_("Mr. Spock-"))
1265 prouts(_(" \"Fascinating!\""))
1270 # Stas Sergeev added the possibility that
1271 # you can shove the Thingy and piss it off.
1272 # It then becomes an enemy and may fire at you.
1277 elif iquad == IHBLANK: # Black hole
1279 crmena(True, IHBLANK, sector, w)
1280 prout(_(" swallows torpedo."))
1282 elif iquad == IHWEB: # hit the web
1284 prout(_("***Torpedo absorbed by Tholian web."))
1286 elif iquad == IHT: # Hit a Tholian
1287 h1 = 700.0 + 100.0*Rand() - \
1288 1000.0 * distance(w, incoming) * math.fabs(math.sin(bullseye-angle))
1291 game.quad[w.x][w.y] = IHDOT
1296 crmena(True, IHT, sector, w)
1298 prout(_(" survives photon blast."))
1300 prout(_(" disappears."))
1301 game.quad[w.x][w.y] = IHWEB
1308 proutn("Don't know how to handle torpedo collision with ")
1309 crmena(True, iquad, sector, w)
1313 if curwnd!=message_window:
1314 setwnd(message_window)
1316 game.quad[w.x][w.y]=IHDOT
1317 game.quad[jw.x][jw.y]=iquad
1318 prout(_(" displaced by blast to %s "), cramlc(sector, jw))
1319 for ll in range(1, game.nenhere+1):
1320 game.kdist[ll] = game.kavgd[ll] = distance(game.sector,game.ks[ll])
1324 prout(_("Torpedo missed."))
1328 # critical-hit resolution
1330 # a critical hit occured
1331 if hit < (275.0-25.0*game.skill)*(1.0+0.5*Rand()):
1334 ncrit = 1.0 + hit/(500.0+100.0*Rand())
1335 proutn(_("***CRITICAL HIT--"))
1336 # Select devices and cause damage
1338 for loop1 in range(ncrit):
1341 # Cheat to prevent shuttle damage unless on ship
1342 if not (game.damage[j]<0.0 or (j==DSHUTTL and game.iscraft != "onship")):
1345 extradm = (hit*game.damfac)/(ncrit*(75.0+25.0*Rand()))
1346 game.damage[j] += extradm
1348 for loop2 in range(loop1):
1349 if j == cdam[loop2]:
1358 prout(_(" damaged."))
1359 if damaged(DSHIELD) and game.shldup:
1360 prout(_("***Shields knocked down."))
1363 def attack(torps_ok):
1364 # bad guy attacks us
1365 # torps_ok == false forces use of phasers in an attack
1366 atackd = False; attempt = False; ihurt = False;
1367 hitmax=0.0; hittot=0.0; chgfac=1.0
1371 # game could be over at this point, check
1376 prout("=== ATTACK!")
1378 # Tholian gewts to move before attacking
1382 # if you have just entered the RNZ, you'll get a warning
1383 if game.neutz: # The one chance not to be attacked
1387 # commanders get a chance to tac-move towards you
1388 if (((game.comhere or game.ishere) and not game.justin) or game.skill == SKILL_EMERITUS) and torps_ok:
1391 # if no enemies remain after movement, we're done
1392 if game.nenhere==0 or (game.nenhere==1 and iqhere and not iqengry):
1395 # set up partial hits if attack happens during shield status change
1396 pfac = 1.0/game.inshld
1398 chgfac = 0.25+0.5*Rand()
1402 # message verbosity control
1403 if game.skill <= SKILL_FAIR:
1406 for loop in range(1, game.nenhere+1):
1407 if game.kpower[loop] < 0:
1408 continue; # too weak to attack
1409 # compute hit strength and diminish shield power
1411 # Increase chance of photon torpedos if docked or enemy energy low
1412 if game.condition == "docked":
1414 if game.kpower[loop] < 500:
1417 iquad = game.quad[jay.x][jay.y]
1418 if iquad==IHT or (iquad==IHQUEST and not iqengry):
1420 # different enemies have different probabilities of throwing a torp
1421 usephasers = not torps_ok or \
1422 (iquad == IHK and r > 0.0005) or \
1423 (iquad==IHC and r > 0.015) or \
1424 (iquad==IHR and r > 0.3) or \
1425 (iquad==IHS and r > 0.07) or \
1426 (iquad==IHQUEST and r > 0.05)
1427 if usephasers: # Enemy uses phasers
1428 if game.condition == "docked":
1429 continue; # Don't waste the effort!
1430 attempt = True; # Attempt to attack
1431 dustfac = 0.8+0.05*Rand()
1432 hit = game.kpower[loop]*math.pow(dustfac,game.kavgd[loop])
1433 game.kpower[loop] *= 0.75
1434 else: # Enemy uses photon torpedo
1435 course = 1.90985*math.atan2(game.sector.y-jay.y, jay.x-game.sector.x)
1437 proutn(_("***TORPEDO INCOMING"))
1438 if not damaged(DSRSENS):
1440 crmena(False, iquad, where, jay)
1443 r = (Rand()+Rand())*0.5 -0.5
1444 r += 0.002*game.kpower[loop]*r
1445 hit = torpedo(course, r, jay, 1, 1)
1446 if (game.state.remkl + game.state.remcom + game.state.nscrem)==0:
1447 finish(FWON); # Klingons did themselves in!
1448 if game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova or game.alldone:
1449 return; # Supernova or finished
1452 # incoming phaser or torpedo, shields may dissipate it
1453 if game.shldup or game.shldchg or game.condition=="docked":
1454 # shields will take hits
1455 propor = pfac * game.shield
1456 if game.condition =="docked":
1460 hitsh = propor*chgfac*hit+1.0
1462 if absorb > game.shield:
1463 absorb = game.shield
1464 game.shield -= absorb
1466 # taking a hit blasts us out of a starbase dock
1467 if game.condition == "docked":
1469 # but the shields may take care of it
1470 if propor > 0.1 and hit < 0.005*game.energy:
1472 # hit from this opponent got through shields, so take damage
1474 proutn(_("%d unit hit") % int(hit))
1475 if (damaged(DSRSENS) and usephasers) or game.skill<=SKILL_FAIR:
1476 proutn(_(" on the "))
1478 if not damaged(DSRSENS) and usephasers:
1480 crmena(False, iquad, where, jay)
1482 # Decide if hit is critical
1488 if game.energy <= 0:
1489 # Returning home upon your shield, not with it...
1492 if not attempt and game.condition == "docked":
1493 prout(_("***Enemies decide against attacking your ship."))
1496 percent = 100.0*pfac*game.shield+0.5
1498 # Shields fully protect ship
1499 proutn(_("Enemy attack reduces shield strength to "))
1501 # Print message if starship suffered hit(s)
1503 proutn(_("Energy left %2d shields ") % int(game.energy))
1506 elif not damaged(DSHIELD):
1509 proutn(_("damaged, "))
1510 prout(_("%d%%, torpedoes left %d"), percent, game.torps)
1511 # Check if anyone was hurt
1512 if hitmax >= 200 or hittot >= 500:
1513 icas= hittot*Rand()*0.015
1516 prout(_("Mc Coy- \"Sickbay to bridge. We suffered %d casualties") % icas)
1517 prout(_(" in that last attack.\""))
1519 game.state.crew -= icas
1520 # After attack, reset average distance to enemies
1521 for loop in range(1, game.nenhere+1):
1522 game.kavgd[loop] = game.kdist[loop]
1526 def deadkl(w, type, mv):
1527 # kill a Klingon, Tholian, Romulan, or Thingy
1528 # Added mv to allow enemy to "move" before dying
1530 crmena(True, type, sector, mv)
1531 # Decide what kind of enemy it is and update appropriately
1533 # chalk up a Romulan
1534 game.state.galaxy[game.quadrant.x][game.quadrant.y].romulans -= 1
1536 game.state.nromrem -= 1
1540 elif type == IHQUEST:
1542 iqhere = iqengry = False
1545 # Some type of a Klingon
1546 game.state.galaxy[game.quadrant.x][game.quadrant.y].klingons -= 1
1549 game.comhere = False
1550 for i in range(1, game.state.remcom+1):
1551 if same(game.state.kcmdr[i], game.quadrant):
1553 game.state.kcmdr[i] = game.state.kcmdr[game.state.remcom]
1554 game.state.kcmdr[game.state.remcom].x = 0
1555 game.state.kcmdr[game.state.remcom].y = 0
1556 game.state.remcom -= 1
1558 if game.state.remcom != 0:
1559 schedule(FTBEAM, expran(1.0*game.incom/game.state.remcom))
1561 game.state.remkl -= 1
1563 game.state.nscrem -= 1
1565 game.state.kscmdr.x = game.state.kscmdr.y = game.isatb = 0
1570 prout("*** Internal error, deadkl() called on %s\n" % type)
1572 # For each kind of enemy, finish message to player
1573 prout(_(" destroyed."))
1574 game.quad[w.x][w.y] = IHDOT
1575 if (game.state.remkl + game.state.remcom + game.state.nscrem)==0:
1578 game.state.remtime = game.state.remres/(game.state.remkl + 4*game.state.remcom)
1580 # Remove enemy ship from arrays describing local conditions
1581 if is_scheduled(FCDBAS) and same(game.battle, game.quadrant) and type==IHC:
1583 for i in range(1, game.nenhere+1):
1584 if same(game.ks[i], w):
1587 if i <= game.nenhere:
1588 for j in range(i, game.nenhere+1):
1589 game.ks[j] = game.ks[j+1]
1590 game.kpower[j] = game.kpower[j+1]
1591 game.kavgd[j] = game.kdist[j] = game.kdist[j+1]
1592 game.ks[game.nenhere+1].x = 0
1593 game.ks[game.nenhere+1].x = 0
1594 game.kdist[game.nenhere+1] = 0
1595 game.kavgd[game.nenhere+1] = 0
1596 game.kpower[game.nenhere+1] = 0
1599 def targetcheck(x, y):
1600 # Return None if target is invalid
1601 if not VALID_SECTOR(x, y):
1604 deltx = 0.1*(y - game.sector.y)
1605 delty = 0.1*(x - game.sector.x)
1606 if deltx==0 and delty== 0:
1608 prout(_("Spock- \"Bridge to sickbay. Dr. McCoy,"))
1609 prout(_(" I recommend an immediate review of"))
1610 prout(_(" the Captain's psychological profile.\""))
1613 return 1.90985932*math.atan2(deltx, delty)
1616 # launch photon torpedo
1618 if damaged(DPHOTON):
1619 prout(_("Photon tubes damaged."))
1623 prout(_("No torpedoes left."))
1632 prout(_("%d torpedoes left."), game.torps)
1633 proutn(_("Number of torpedoes to fire- "))
1635 else: # key == IHREAL {
1637 if n <= 0: # abort command
1642 prout(_("Maximum of 3 torpedoes per burst."))
1649 for i in range(1, n+1):
1651 if i==1 and key == IHEOL:
1652 break; # we will try prompting
1653 if i==2 and key == IHEOL:
1654 # direct all torpedoes at one target
1656 targ[i][1] = targ[1][1]
1657 targ[i][2] = targ[1][2]
1658 course[i] = course[1]
1670 course[i] = targetcheck(targ[i][1], targ[i][2])
1671 if course[i] == None:
1674 if i == 1 and key == IHEOL:
1675 # prompt for each one
1676 for i in range(1, n+1):
1677 proutn(_("Target sector for torpedo number %d- "), i)
1689 course[i] = targetcheck(targ[i][1], targ[i][2])
1690 if course[i] == None:
1693 # Loop for moving <n> torpedoes
1694 for i in range(1, n+1):
1695 if game.condition != "docked":
1697 r = (Rand()+Rand())*0.5 -0.5
1698 if math.fabs(r) >= 0.47:
1700 r = (Rand()+1.2) * r
1702 prouts(_("***TORPEDO NUMBER %d MISFIRES"), i)
1704 prouts(_("***TORPEDO MISFIRES."))
1707 prout(_(" Remainder of burst aborted."))
1709 prout(_("***Photon tubes damaged by misfire."))
1710 game.damage[DPHOTON] = game.damfac*(1.0+2.0*Rand())
1712 if game.shldup or game.condition == "docked":
1713 r *= 1.0 + 0.0001*game.shield
1714 torpedo(course[i], r, game.sector, i, n)
1715 if game.alldone or game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova:
1717 if (game.state.remkl + game.state.remcom + game.state.nscrem)==0:
1721 # check for phasers overheating
1723 chekbrn = (rpow-1500.)*0.00038
1724 if Rand() <= chekbrn:
1725 prout(_("Weapons officer Sulu- \"Phasers overheated, sir.\""))
1726 game.damage[DPHASER] = game.damfac*(1.0 + Rand()) * (1.0+chekbrn)
1728 def checkshctrl(rpow):
1729 # check shield control
1733 prout(_("Shields lowered."))
1735 # Something bad has happened
1736 prouts(_("***RED ALERT! RED ALERT!"))
1738 hit = rpow*game.shield/game.inshld
1739 game.energy -= rpow+hit*0.8
1740 game.shield -= hit*0.2
1741 if game.energy <= 0.0:
1742 prouts(_("Sulu- \"Captain! Shield malf***********************\""))
1747 prouts(_("Sulu- \"Captain! Shield malfunction! Phaser fire contained!\""))
1749 prout(_("Lt. Uhura- \"Sir, all decks reporting damage.\""))
1750 icas = hit*Rand()*0.012
1755 prout(_("McCoy to bridge- \"Severe radiation burns, Jim."))
1756 prout(_(" %d casualties so far.\""), icas)
1758 game.state.crew -= icas
1760 prout(_("Phaser energy dispersed by shields."))
1761 prout(_("Enemy unaffected."))
1765 def hittem(doublehits):
1766 # register a phaser hit on Klingons and Romulans
1767 nenhr2=game.nenhere; kk=1
1770 for k in range(1, nenhr2+1):
1774 dustfac = 0.9 + 0.01*Rand()
1775 hit = wham*math.pow(dustfac,game.kdist[kk])
1776 kpini = game.kpower[kk]
1777 kp = math.fabs(kpini)
1778 if PHASEFAC*hit < kp:
1780 if game.kpower[kk] < 0:
1781 game.kpower[kk] -= -kp
1783 game.kpower[kk] -= kp
1784 kpow = game.kpower[kk]
1787 if not damaged(DSRSENS):
1789 proutn(_("%d unit hit on ") % int(hit))
1791 proutn(_("Very small hit on "))
1792 ienm = game.quad[w.x][w.y]
1795 crmena(False, ienm, "sector", w)
1799 if (game.state.remkl + game.state.remcom + game.state.nscrem)==0:
1803 kk -= 1; # don't do the increment
1804 else: # decide whether or not to emasculate klingon
1805 if kpow > 0 and Rand() >= 0.9 and \
1806 kpow <= ((0.4 + 0.4*Rand())*kpini):
1807 prout(_("***Mr. Spock- \"Captain, the vessel at %s"),
1809 prout(_(" has just lost its firepower.\""))
1810 game.kpower[kk] = -kpow
1817 kz = 0; k = 1; irec=0 # Cheating inhibitor
1818 ifast = False; no = False; itarg = True; msgflag = True
1823 # SR sensors and Computer are needed fopr automode
1824 if damaged(DSRSENS) or damaged(DCOMPTR):
1826 if game.condition == "docked":
1827 prout(_("Phasers can't be fired through base shields."))
1830 if damaged(DPHASER):
1831 prout(_("Phaser control damaged."))
1835 if damaged(DSHCTRL):
1836 prout(_("High speed shield control damaged."))
1839 if game.energy <= 200.0:
1840 prout(_("Insufficient energy to activate high-speed shield control."))
1843 prout(_("Weapons Officer Sulu- \"High-speed shield control enabled, sir.\""))
1846 # Original code so convoluted, I re-did it all
1847 while automode=="NOTSET":
1852 prout(_("There is no enemy present to select."))
1855 automode="AUTOMATIC"
1859 elif isit("automatic"):
1860 if (not itarg) and game.nenhere != 0:
1861 automode = "FORCEMAN"
1864 prout(_("Energy will be expended into space."))
1865 automode = "AUTOMATIC"
1874 prout(_("Energy will be expended into space."))
1875 automode = "AUTOMATIC"
1877 automode = "FORCEMAN"
1879 automode = "AUTOMATIC"
1883 prout(_("Energy will be expended into space."))
1884 automode = "AUTOMATIC"
1886 automode = "FORCEMAN"
1888 proutn(_("Manual or automatic? "))
1892 if automode == "AUTOMATIC":
1893 if key == IHALPHA and isit("no"):
1896 if key != IHREAL and game.nenhere != 0:
1897 prout(_("Phasers locked on target. Energy available: %.2f"),
1903 for i in range(1, game.nenhere+1):
1904 irec += math.fabs(game.kpower[i])/(PHASEFAC*math.pow(0.90,game.kdist[i]))*(1.01+0.05*Rand()) + 1.0
1906 proutn(_("%d units required. "), irec)
1908 proutn(_("Units to fire= "))
1914 proutn(_("Energy available= %.2f") % avail)
1917 if not rpow > avail:
1924 if key == IHALPHA and isit("no"):
1927 game.energy -= 200; # Go and do it!
1928 if checkshctrl(rpow):
1936 for i in range(1, game.nenhere+1):
1940 hits[i] = math.fabs(game.kpower[i])/(PHASEFAC*math.pow(0.90,game.kdist[i]))
1941 over = (0.01 + 0.05*Rand())*hits[i]
1943 powrem -= hits[i] + over
1944 if powrem <= 0 and temp < hits[i]:
1953 if extra > 0 and not game.alldone:
1955 proutn(_("*** Tholian web absorbs "))
1957 proutn(_("excess "))
1958 prout(_("phaser energy."))
1960 prout(_("%d expended on empty space."), int(extra))
1961 elif automode == "FORCEMAN":
1964 if damaged(DCOMPTR):
1965 prout(_("Battle computer damaged, manual fire only."))
1968 prouts(_("---WORKING---"))
1970 prout(_("Short-range-sensors-damaged"))
1971 prout(_("Insufficient-data-for-automatic-phaser-fire"))
1972 prout(_("Manual-fire-must-be-used"))
1974 elif automode == "MANUAL":
1976 for k in range(1, game.nenhere+1):
1978 ienm = game.quad[aim.x][aim.y]
1980 proutn(_("Energy available= %.2f") % avail-0.006)
1984 if damaged(DSRSENS) and not (abs(game.sector.x-aim.x) < 2 and abs(game.sector.y-aim.y) < 2) and \
1985 (ienm == IHC or ienm == IHS):
1987 prout(_(" can't be located without short range scan."))
1990 hits[k] = 0; # prevent overflow -- thanks to Alexei Voitenko
1995 if itarg and k > kz:
1996 irec=(abs(game.kpower[k])/(PHASEFAC*math.pow(0.9,game.kdist[k]))) * (1.01+0.05*Rand()) + 1.0
1999 if not damaged(DCOMPTR):
2004 proutn(_("units to fire at "))
2005 crmena(False, ienm, sector, aim)
2008 if key == IHALPHA and isit("no"):
2016 if k==1: # Let me say I'm baffled by this
2025 # If total requested is too much, inform and start over
2027 prout(_("Available energy exceeded -- try again."))
2030 key = scan(); # scan for next value
2033 # zero energy -- abort
2036 if key == IHALPHA and isit("no"):
2041 game.energy -= 200.0
2042 if checkshctrl(rpow):
2046 # Say shield raised or malfunction, if necessary
2053 prout(_("Sulu- \"Sir, the high-speed shield control has malfunctioned . . ."))
2054 prouts(_(" CLICK CLICK POP . . ."))
2055 prout(_(" No response, sir!"))
2058 prout(_("Shields raised."))