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 # Stas thinks this should be (C expression):
300 # game.state.remkl + game.state.remcom > 0 ?
301 # game.state.remres/(game.state.remkl + 4*game.state.remcom) : 99
302 # He says the existing expression is prone to divide-by-zero errors.
303 game.state.remtime = game.state.remres/(game.state.remkl + 4*game.state.remcom)
304 # From enumerated type 'feature'
325 # From enumerated type 'FINTYPE'
349 # From enumerated type 'COLORS'
368 # Code from ai.c begins here
370 def tryexit(look, ienm, loccom, irun):
371 # a bad guy attempts to bug out
373 iq.x = game.quadrant.x+(look.x+(QUADSIZE-1))/QUADSIZE - 1
374 iq.y = game.quadrant.y+(look.y+(QUADSIZE-1))/QUADSIZE - 1
375 if not VALID_QUADRANT(iq.x,iq.y) or \
376 game.state.galaxy[iq.x][iq.y].supernova or \
377 game.state.galaxy[iq.x][iq.y].klingons > MAXKLQUAD-1:
378 return False; # no can do -- neg energy, supernovae, or >MAXKLQUAD-1 Klingons
380 return False; # Romulans cannot escape!
382 # avoid intruding on another commander's territory
384 for n in range(1, game.state.remcom+1):
385 if same(game.state.kcmdr[n],iq):
387 # refuse to leave if currently attacking starbase
388 if same(game.battle, game.quadrant):
390 # don't leave if over 1000 units of energy
391 if game.kpower[loccom] > 1000.0:
393 # print escape message and move out of quadrant.
394 # we know this if either short or long range sensors are working
395 if not damaged(DSRSENS) or not damaged(DLRSENS) or \
396 game.condition == docked:
397 crmena(True, ienm, "sector", game.ks[loccom])
398 prout(_(" escapes to Quadrant %s (and regains strength).") % q)
399 # handle local matters related to escape
400 game.quad[game.ks[loccom].x][game.ks[loccom].y] = IHDOT
401 game.ks[loccom] = game.ks[game.nenhere]
402 game.kavgd[loccom] = game.kavgd[game.nenhere]
403 game.kpower[loccom] = game.kpower[game.nenhere]
404 game.kdist[loccom] = game.kdist[game.nenhere]
407 if game.condition != docked:
409 # Handle global matters related to escape
410 game.state.galaxy[game.quadrant.x][game.quadrant.y].klingons -= 1
411 game.state.galaxy[iq.x][iq.y].klingons += 1
417 schedule(FSCMOVE, 0.2777)
421 for n in range(1, game.state.remcom+1):
422 if same(game.state.kcmdr[n], game.quadrant):
423 game.state.kcmdr[n]=iq
426 return True; # success
429 # The bad-guy movement algorithm:
431 # 1. Enterprise has "force" based on condition of phaser and photon torpedoes.
432 # If both are operating full strength, force is 1000. If both are damaged,
433 # force is -1000. Having shields down subtracts an additional 1000.
435 # 2. Enemy has forces equal to the energy of the attacker plus
436 # 100*(K+R) + 500*(C+S) - 400 for novice through good levels OR
437 # 346*K + 400*R + 500*(C+S) - 400 for expert and emeritus.
439 # Attacker Initial energy levels (nominal):
440 # Klingon Romulan Commander Super-Commander
441 # Novice 400 700 1200
443 # Good 450 800 1300 1750
444 # Expert 475 850 1350 1875
445 # Emeritus 500 900 1400 2000
446 # VARIANCE 75 200 200 200
448 # Enemy vessels only move prior to their attack. In Novice - Good games
449 # only commanders move. In Expert games, all enemy vessels move if there
450 # is a commander present. In Emeritus games all enemy vessels move.
452 # 3. If Enterprise is not docked, an aggressive action is taken if enemy
453 # forces are 1000 greater than Enterprise.
455 # Agressive action on average cuts the distance between the ship and
456 # the enemy to 1/4 the original.
458 # 4. At lower energy advantage, movement units are proportional to the
459 # advantage with a 650 advantage being to hold ground, 800 to move forward
460 # 1, 950 for two, 150 for back 4, etc. Variance of 100.
462 # If docked, is reduced by roughly 1.75*game.skill, generally forcing a
463 # retreat, especially at high skill levels.
465 # 5. Motion is limited to skill level, except for SC hi-tailing it out.
468 def movebaddy(com, loccom, ienm):
469 # tactical movement for the bad guys
470 next = coord(); look = coord()
472 # This should probably be just game.comhere + game.ishere
473 if game.skill >= SKILL_EXPERT:
474 nbaddys = ((game.comhere*2 + game.ishere*2+game.klhere*1.23+game.irhere*1.5)/2.0)
476 nbaddys = game.comhere + game.ishere
478 dist1 = game.kdist[loccom]
479 mdist = int(dist1 + 0.5); # Nearest integer distance
481 # If SC, check with spy to see if should hi-tail it
483 (game.kpower[loccom] <= 500.0 or (game.condition=="docked" and not damaged(DPHOTON))):
487 # decide whether to advance, retreat, or hold position
488 forces = game.kpower[loccom]+100.0*game.nenhere+400*(nbaddys-1)
490 forces += 1000; # Good for enemy if shield is down!
491 if not damaged(DPHASER) or not damaged(DPHOTON):
492 if damaged(DPHASER): # phasers damaged
495 forces -= 0.2*(game.energy - 2500.0)
496 if damaged(DPHOTON): # photon torpedoes damaged
499 forces -= 50.0*game.torps
501 # phasers and photon tubes both out!
504 if forces <= 1000.0 and game.condition != "docked": # Typical situation
505 motion = ((forces+200.0*Rand())/150.0) - 5.0
507 if forces > 1000.0: # Very strong -- move in for kill
508 motion = (1.0-square(Rand()))*dist1 + 1.0
509 if game.condition=="docked" and (game.options & OPTION_BASE): # protected by base -- back off !
510 motion -= game.skill*(2.0-square(Rand()))
512 proutn("=== MOTION = %d, FORCES = %1.2f, " % (motion, forces))
513 # don't move if no motion
516 # Limit motion according to skill
517 if abs(motion) > game.skill:
522 # calculate preferred number of steps
527 if motion > 0 and nsteps > mdist:
528 nsteps = mdist; # don't overshoot
529 if nsteps > QUADSIZE:
530 nsteps = QUADSIZE; # This shouldn't be necessary
532 nsteps = 1; # This shouldn't be necessary
534 proutn("NSTEPS = %d:" % nsteps)
535 # Compute preferred values of delta X and Y
536 mx = game.sector.x - com.x
537 my = game.sector.y - com.y
538 if 2.0 * abs(mx) < abs(my):
540 if 2.0 * abs(my) < abs(game.sector.x-com.x):
554 for ll in range(nsteps):
556 proutn(" %d" % (ll+1))
557 # Check if preferred position available
569 attempts = 0; # Settle mysterious hang problem
570 while attempts < 20 and not success:
572 if look.x < 1 or look.x > QUADSIZE:
573 if motion < 0 and tryexit(look, ienm, loccom, irun):
575 if krawlx == mx or my == 0:
577 look.x = next.x + krawlx
579 elif look.y < 1 or look.y > QUADSIZE:
580 if motion < 0 and tryexit(look, ienm, loccom, irun):
582 if krawly == my or mx == 0:
584 look.y = next.y + krawly
586 elif (game.options & OPTION_RAMMING) and game.quad[look.x][look.y] != IHDOT:
587 # See if we should ram ship
588 if game.quad[look.x][look.y] == game.ship and \
589 (ienm == IHC or ienm == IHS):
592 if krawlx != mx and my != 0:
593 look.x = next.x + krawlx
595 elif krawly != my and mx != 0:
596 look.y = next.y + krawly
599 break; # we have failed
611 # Put commander in place within same quadrant
612 game.quad[com.x][com.y] = IHDOT
613 game.quad[next.x][next.y] = ienm
614 if not same(next, com):
616 game.ks[loccom] = next
617 game.kdist[loccom] = game.kavgd[loccom] = distance(game.sector, next)
618 if not damaged(DSRSENS) or game.condition == docked:
621 proutn(_(" from Sector %s") % com)
622 if game.kdist[loccom] < dist1:
623 proutn(_(" advances to "))
625 proutn(_(" retreats to "))
626 prout("Sector %s." % next)
629 # Klingon tactical movement
632 # Figure out which Klingon is the commander (or Supercommander)
635 for i in range(1, game.nenhere+1):
637 if game.quad[w.x][w.y] == IHC:
641 for i in range(1, game.nenhere+1):
643 if game.quad[w.x][w.y] == IHS:
646 # If skill level is high, move other Klingons and Romulans too!
647 # Move these last so they can base their actions on what the
649 if game.skill >= SKILL_EXPERT and (game.options & OPTION_MVBADDY):
650 for i in range(1, game.nenhere+1):
652 if game.quad[w.x][w.y] == IHK or game.quad[w.x][w.y] == IHR:
653 movebaddy(w, i, game.quad[w.x][w.y])
656 def movescom(iq, avoid):
657 # commander movement helper
658 if same(iq, game.quadrant) or not VALID_QUADRANT(iq.x, iq.y) or \
659 game.state.galaxy[iq.x][iq.y].supernova or \
660 game.state.galaxy[iq.x][iq.y].klingons > MAXKLQUAD-1:
663 # Avoid quadrants with bases if we want to avoid Enterprise
664 for i in range(1, game.state.rembase+1):
665 if same(game.state.baseq[i], iq):
667 if game.justin and not game.iscate:
670 game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].klingons -= 1
671 game.state.kscmdr = iq
672 game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].klingons += 1
674 # SC has scooted, Remove him from current quadrant
680 for i in range(1, game.nenhere+1):
681 if game.quad[game.ks[i].x][game.ks[i].y] == IHS:
683 game.quad[game.ks[i].x][game.ks[i].y] = IHDOT
684 game.ks[i] = game.ks[game.nenhere]
685 game.kdist[i] = game.kdist[game.nenhere]
686 game.kavgd[i] = game.kavgd[game.nenhere]
687 game.kpower[i] = game.kpower[game.nenhere]
690 if game.condition!=docked:
693 # check for a helpful planet
694 for i in range(game.inplan):
695 if same(game.state.planets[i].w, game.state.kscmdr) and \
696 game.state.planets[i].crystals == present:
698 game.state.planets[i].pclass = destroyed
699 game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].planet = NOPLANET
700 if not damaged(DRADIO) or game.condition == docked:
702 prout(_("Lt. Uhura- \"Captain, Starfleet Intelligence reports"))
703 proutn(_(" a planet in Quadrant %s has been destroyed") % game.state.kscmdr)
704 prout(_(" by the Super-commander.\""))
706 return False; # looks good!
708 def supercommander():
709 # move the Super Commander
710 iq = coord(); sc = coord(); ibq = coord(); idelta = coord()
713 prout("== SUPERCOMMANDER")
714 # Decide on being active or passive
715 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 \
716 (game.state.date-game.indate) < 3.0)
717 if not game.iscate and avoid:
718 # compute move away from Enterprise
719 idelta = game.state.kscmdr-game.quadrant
720 if math.sqrt(idelta.x*idelta.x+idelta.y*idelta.y) > 2.0:
722 idelta.x = game.state.kscmdr.y-game.quadrant.y
723 idelta.y = game.quadrant.x-game.state.kscmdr.x
725 # compute distances to starbases
726 if game.state.rembase <= 0:
730 sc = game.state.kscmdr
731 for i in range(1, game.state.rembase+1):
732 basetbl.append((i, distance(game.state.baseq[i], sc)))
733 if game.state.rembase > 1:
734 basetbl.sort(lambda x, y: cmp(x[1]. y[1]))
735 # look for nearest base without a commander, no Enterprise, and
736 # without too many Klingons, and not already under attack.
737 ifindit = iwhichb = 0
738 for i2 in range(1, game.state.rembase+1):
739 i = basetbl[i2][0]; # bug in original had it not finding nearest
740 ibq = game.state.baseq[i]
741 if same(ibq, game.quadrant) or same(ibq, game.battle) or \
742 game.state.galaxy[ibq.x][ibq.y].supernova or \
743 game.state.galaxy[ibq.x][ibq.y].klingons > MAXKLQUAD-1:
745 # if there is a commander, and no other base is appropriate,
746 # we will take the one with the commander
747 for j in range(1, game.state.remcom+1):
748 if same(ibq, game.state.kcmdr[j]) and ifindit!= 2:
752 if j > game.state.remcom: # no commander -- use this one
757 return; # Nothing suitable -- wait until next time
758 ibq = game.state.baseq[iwhichb]
759 # decide how to move toward base
760 idelta = ibq - game.state.kscmdr
761 # Maximum movement is 1 quadrant in either or both axes
762 idelta = idelta.sgn()
763 # try moving in both x and y directions
764 # there was what looked like a bug in the Almy C code here,
765 # but it might be this translation is just wrong.
766 iq = game.state.kscmdr + idelta
767 if movescom(iq, avoid):
768 # failed -- try some other maneuvers
769 if idelta.x==0 or idelta.y==0:
772 iq.y = game.state.kscmdr.y + 1
773 if movescom(iq, avoid):
774 iq.y = game.state.kscmdr.y - 1
777 iq.x = game.state.kscmdr.x + 1
778 if movescom(iq, avoid):
779 iq.x = game.state.kscmdr.x - 1
782 # try moving just in x or y
783 iq.y = game.state.kscmdr.y
784 if movescom(iq, avoid):
785 iq.y = game.state.kscmdr.y + idelta.y
786 iq.x = game.state.kscmdr.x
789 if game.state.rembase == 0:
792 for i in range(1, game.state.rembase+1):
793 ibq = game.state.baseq[i]
794 if same(ibq, game.state.kscmdr) and same(game.state.kscmdr, game.battle):
797 return; # no, don't attack base!
800 schedule(FSCDBAS, 1.0 +2.0*Rand())
801 if is_scheduled(FCDBAS):
802 postpone(FSCDBAS, scheduled(FCDBAS)-game.state.date)
803 if damaged(DRADIO) and game.condition != docked:
807 prout(_("Lt. Uhura- \"Captain, the starbase in Quadrant %s") \
809 prout(_(" reports that it is under attack from the Klingon Super-commander."))
810 proutn(_(" It can survive until stardate %d.\"") \
811 % int(scheduled(FSCDBAS)))
814 prout(_("Mr. Spock- \"Captain, shall we cancel the rest period?\""))
818 game.optime = 0.0; # actually finished
820 # Check for intelligence report
823 (damaged(DRADIO) and game.condition != docked) or \
824 not game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].charted):
827 prout(_("Lt. Uhura- \"Captain, Starfleet Intelligence reports"))
828 proutn(_(" the Super-commander is in Quadrant %s,") % game.state.kscmdr)
833 if not game.ithere or game.justin:
836 if game.tholian.x == 1 and game.tholian.y == 1:
837 idx = 1; idy = QUADSIZE
838 elif game.tholian.x == 1 and game.tholian.y == QUADSIZE:
839 idx = QUADSIZE; idy = QUADSIZE
840 elif game.tholian.x == QUADSIZE and game.tholian.y == QUADSIZE:
841 idx = QUADSIZE; idy = 1
842 elif game.tholian.x == QUADSIZE and game.tholian.y == 1:
845 # something is wrong!
849 # do nothing if we are blocked
850 if game.quad[idx][idy]!= IHDOT and game.quad[idx][idy]!= IHWEB:
852 game.quad[game.tholian.x][game.tholian.y] = IHWEB
854 if game.tholian.x != idx:
856 im = math.fabs(idx - game.tholian.x)*1.0/(idx - game.tholian.x)
857 while game.tholian.x != idx:
859 if game.quad[game.tholian.x][game.tholian.y]==IHDOT:
860 game.quad[game.tholian.x][game.tholian.y] = IHWEB
861 elif game.tholian.y != idy:
863 im = math.fabs(idy - game.tholian.y)*1.0/(idy - game.tholian.y)
864 while game.tholian.y != idy:
866 if game.quad[game.tholian.x][game.tholian.y]==IHDOT:
867 game.quad[game.tholian.x][game.tholian.y] = IHWEB
868 game.quad[game.tholian.x][game.tholian.y] = IHT
869 game.ks[game.nenhere] = game.tholian
871 # check to see if all holes plugged
872 for i in range(1, QUADSIZE+1):
873 if game.quad[1][i]!=IHWEB and game.quad[1][i]!=IHT:
875 if game.quad[QUADSIZE][i]!=IHWEB and game.quad[QUADSIZE][i]!=IHT:
877 if game.quad[i][1]!=IHWEB and game.quad[i][1]!=IHT:
879 if game.quad[i][QUADSIZE]!=IHWEB and game.quad[i][QUADSIZE]!=IHT:
881 # All plugged up -- Tholian splits
882 game.quad[game.tholian.x][game.tholian.y]=IHWEB
884 crmena(True, IHT, "sector", game.tholian)
885 prout(_(" completes web."))
890 # Code from battle.c begins here
892 def doshield(shraise):
893 # change shield status
906 prout(_("Shields damaged and down."))
913 proutn(_("Do you wish to change shield energy? "))
915 proutn(_("Energy to transfer to shields- "))
917 elif damaged(DSHIELD):
918 prout(_("Shields damaged and down."))
921 proutn(_("Shields are up. Do you want them down? "))
928 proutn(_("Shields are down. Do you want them up? "))
934 if action == "SHUP": # raise shields
936 prout(_("Shields already up."))
940 if game.condition != "docked":
942 prout(_("Shields raised."))
945 prout(_("Shields raising uses up last of energy."))
950 elif action == "SHDN":
952 prout(_("Shields already down."))
956 prout(_("Shields lowered."))
959 elif action == "NRG":
960 while scan() != IHREAL:
962 proutn(_("Energy to transfer to shields- "))
966 if aaitem > game.energy:
967 prout(_("Insufficient ship energy."))
970 if game.shield+aaitem >= game.inshld:
971 prout(_("Shield energy maximized."))
972 if game.shield+aaitem > game.inshld:
973 prout(_("Excess energy requested returned to ship energy"))
974 game.energy -= game.inshld-game.shield
975 game.shield = game.inshld
977 if aaitem < 0.0 and game.energy-aaitem > game.inenrg:
978 # Prevent shield drain loophole
980 prout(_("Engineering to bridge--"))
981 prout(_(" Scott here. Power circuit problem, Captain."))
982 prout(_(" I can't drain the shields."))
985 if game.shield+aaitem < 0:
986 prout(_("All shield energy transferred to ship."))
987 game.energy += game.shield
990 proutn(_("Scotty- \""))
992 prout(_("Transferring energy to shields.\""))
994 prout(_("Draining energy from shields.\""))
995 game.shield += aaitem
996 game.energy -= aaitem
1000 # choose a device to damage, at random.
1002 # Quoth Eric Allman in the code of BSD-Trek:
1003 # "Under certain conditions you can get a critical hit. This
1004 # sort of hit damages devices. The probability that a given
1005 # device is damaged depends on the device. Well protected
1006 # devices (such as the computer, which is in the core of the
1007 # ship and has considerable redundancy) almost never get
1008 # damaged, whereas devices which are exposed (such as the
1009 # warp engines) or which are particularly delicate (such as
1010 # the transporter) have a much higher probability of being
1013 # This is one place where OPTION_PLAIN does not restore the
1014 # original behavior, which was equiprobable damage across
1015 # all devices. If we wanted that, we'd return NDEVICES*Rand()
1016 # and have done with it. Also, in the original game, DNAVYS
1017 # and DCOMPTR were the same device.
1019 # Instead, we use a table of weights similar to the one from BSD Trek.
1020 # BSD doesn't have the shuttle, shield controller, death ray, or probes.
1021 # We don't have a cloaking device. The shuttle got the allocation
1022 # for the cloaking device, then we shaved a half-percent off
1023 # everything to have some weight to give DSHCTRL/DDRAY/DDSP.
1026 105, # DSRSENS: short range scanners 10.5%
1027 105, # DLRSENS: long range scanners 10.5%
1028 120, # DPHASER: phasers 12.0%
1029 120, # DPHOTON: photon torpedoes 12.0%
1030 25, # DLIFSUP: life support 2.5%
1031 65, # DWARPEN: warp drive 6.5%
1032 70, # DIMPULS: impulse engines 6.5%
1033 145, # DSHIELD: deflector shields 14.5%
1034 30, # DRADIO: subspace radio 3.0%
1035 45, # DSHUTTL: shuttle 4.5%
1036 15, # DCOMPTR: computer 1.5%
1037 20, # NAVCOMP: navigation system 2.0%
1038 75, # DTRANSP: transporter 7.5%
1039 20, # DSHCTRL: high-speed shield controller 2.0%
1040 10, # DDRAY: death ray 1.0%
1041 30, # DDSP: deep-space probes 3.0%
1043 idx = Rand() * 1000.0 # weights must sum to 1000
1045 for (i, w) in enumerate(weights):
1049 return None; # we should never get here
1051 def ram(ibumpd, ienm, w):
1052 # make our ship ram something
1053 prouts(_("***RED ALERT! RED ALERT!"))
1055 prout(_("***COLLISION IMMINENT."))
1059 hardness = {IHR:1.5, IHC:2.0, IHS:2.5, IHT:0.5, IHQUEST:4.0}.get(ienm, 1.0)
1061 proutn(_(" rammed by "))
1064 crmena(False, ienm, sector, w)
1066 proutn(_(" (original position)"))
1068 deadkl(w, ienm, game.sector)
1071 prout(_(" heavily damaged."))
1072 icas = 10.0+20.0*Rand()
1073 prout(_("***Sickbay reports %d casualties"), icas)
1075 game.state.crew -= icas
1077 # In the pre-SST2K version, all devices got equiprobably damaged,
1078 # which was silly. Instead, pick up to half the devices at
1079 # random according to our weighting table,
1081 ncrits = Rand() * (NDEVICES/2)
1082 for m in range(ncrits):
1084 if game.damage[dev] < 0:
1086 extradm = (10.0*hardness*Rand()+1.0)*game.damfac
1087 # Damage for at least time of travel!
1088 game.damage[dev] += game.optime + extradm
1090 prout(_("***Shields are down."))
1091 if game.state.remkl + game.state.remcom + game.state.nscrem:
1098 def torpedo(course, r, incoming, i, n):
1099 # let a photon torpedo fly
1102 ac = course + 0.25*r
1103 angle = (15.0-ac)*0.5235988
1104 bullseye = (15.0 - course)*0.5235988
1105 deltax = -math.sin(angle);
1106 deltay = math.cos(angle);
1107 x = incoming.x; y = incoming.y
1108 w = coord(); jw = coord()
1109 w.x = w.y = jw.x = jw.y = 0
1110 bigger = max(math.fabs(deltax), math.fabs(deltay))
1113 if not damaged(DSRSENS) or game.condition=="docked":
1114 setwnd(srscan_window)
1116 setwnd(message_window)
1117 # Loop to move a single torpedo
1118 for l in range(1, 15+1):
1123 if not VALID_SECTOR(w.x, w.y):
1125 iquad=game.quad[w.x][w.y]
1126 tracktorpedo(w, l, i, n, iquad)
1130 setwnd(message_window)
1131 if damaged(DSRSENS) and not game.condition=="docked":
1132 skip(1); # start new line after text track
1133 if iquad in (IHE, IHF): # Hit our ship
1135 proutn(_("Torpedo hits "))
1138 hit = 700.0 + 100.0*Rand() - \
1139 1000.0 * distance(w, incoming) * math.fabs(math.sin(bullseye-angle))
1140 newcnd(); # we're blown out of dock
1141 # We may be displaced.
1142 if game.landed or game.condition=="docked":
1143 return hit # Cheat if on a planet
1144 ang = angle + 2.5*(Rand()-0.5)
1145 temp = math.fabs(math.sin(ang))
1146 if math.fabs(math.cos(ang)) > temp:
1147 temp = math.fabs(math.cos(ang))
1148 xx = -math.sin(ang)/temp
1149 yy = math.cos(ang)/temp
1152 if not VALID_SECTOR(jw.x, jw.y):
1154 if game.quad[jw.x][jw.y]==IHBLANK:
1157 if game.quad[jw.x][jw.y]!=IHDOT:
1158 # can't move into object
1163 elif iquad in (IHC, IHS): # Hit a commander
1165 crmena(True, iquad, sector, w)
1166 prout(_(" uses anti-photon device;"))
1167 prout(_(" torpedo neutralized."))
1169 elif iquad in (IHR, IHK): # Hit a regular enemy
1171 for ll in range(1, game.nenhere+1):
1172 if same(w, game.ks[ll]):
1174 kp = math.fabs(game.kpower[ll])
1175 h1 = 700.0 + 100.0*Rand() - \
1176 1000.0 * distance(w, incoming) * math.fabs(math.sin(bullseye-angle))
1180 if game.kpower[ll] < 0:
1181 game.kpower[ll] -= -h1
1183 game.kpower[ll] -= h1
1184 if game.kpower[ll] == 0:
1187 crmena(True, iquad, "sector", w)
1188 # If enemy damaged but not destroyed, try to displace
1189 ang = angle + 2.5*(Rand()-0.5)
1190 temp = math.fabs(math.sin(ang))
1191 if math.fabs(math.cos(ang)) > temp:
1192 temp = math.fabs(math.cos(ang))
1193 xx = -math.sin(ang)/temp
1194 yy = math.cos(ang)/temp
1197 if not VALID_SECTOR(jw.x, jw.y):
1198 prout(_(" damaged but not destroyed."))
1200 if game.quad[jw.x][jw.y]==IHBLANK:
1201 prout(_(" buffeted into black hole."))
1202 deadkl(w, iquad, jw)
1204 if game.quad[jw.x][jw.y]!=IHDOT:
1205 # can't move into object
1206 prout(_(" damaged but not destroyed."))
1208 proutn(_(" damaged--"))
1212 elif iquad == IHB: # Hit a base
1214 prout(_("***STARBASE DESTROYED.."))
1215 for ll in range(1, game.state.rembase+1):
1216 if same(game.state.baseq[ll], game.quadrant):
1217 game.state.baseq[ll]=game.state.baseq[game.state.rembase]
1219 game.quad[w.x][w.y]=IHDOT
1220 game.state.rembase -= 1
1221 game.base.x=game.base.y=0
1222 game.state.galaxy[game.quadrant.x][game.quadrant.y].starbase -= 1
1223 game.state.chart[game.quadrant.x][game.quadrant.y].starbase -= 1
1224 game.state.basekl += 1
1227 elif iquad == IHP: # Hit a planet
1228 crmena(True, iquad, sector, w)
1229 prout(_(" destroyed."))
1230 game.state.nplankl += 1
1231 game.state.galaxy[game.quadrant.x][game.quadrant.y].planet = NOPLANET
1232 game.state.planets[game.iplnet].pclass = destroyed
1234 invalidate(game.plnet)
1235 game.quad[w.x][w.y] = IHDOT
1237 # captain perishes on planet
1240 elif iquad == IHW: # Hit an inhabited world -- very bad!
1241 crmena(True, iquad, sector, w)
1242 prout(_(" destroyed."))
1243 game.state.nworldkl += 1
1244 game.state.galaxy[game.quadrant.x][game.quadrant.y].planet = NOPLANET
1245 game.state.planets[game.iplnet].pclass = destroyed
1247 invalidate(game.plnet)
1248 game.quad[w.x][w.y] = IHDOT
1250 # captain perishes on planet
1252 prout(_("You have just destroyed an inhabited planet."))
1253 prout(_("Celebratory rallies are being held on the Klingon homeworld."))
1255 elif iquad == IHSTAR: # Hit a star
1259 crmena(True, IHSTAR, sector, w)
1260 prout(_(" unaffected by photon blast."))
1262 elif iquad == IHQUEST: # Hit a thingy
1263 if not (game.options & OPTION_THINGY) or Rand()>0.7:
1265 prouts(_("AAAAIIIIEEEEEEEEAAAAAAAAUUUUUGGGGGHHHHHHHHHHHH!!!"))
1267 prouts(_(" HACK! HACK! HACK! *CHOKE!* "))
1269 proutn(_("Mr. Spock-"))
1270 prouts(_(" \"Fascinating!\""))
1275 # Stas Sergeev added the possibility that
1276 # you can shove the Thingy and piss it off.
1277 # It then becomes an enemy and may fire at you.
1282 elif iquad == IHBLANK: # Black hole
1284 crmena(True, IHBLANK, sector, w)
1285 prout(_(" swallows torpedo."))
1287 elif iquad == IHWEB: # hit the web
1289 prout(_("***Torpedo absorbed by Tholian web."))
1291 elif iquad == IHT: # Hit a Tholian
1292 h1 = 700.0 + 100.0*Rand() - \
1293 1000.0 * distance(w, incoming) * math.fabs(math.sin(bullseye-angle))
1296 game.quad[w.x][w.y] = IHDOT
1301 crmena(True, IHT, sector, w)
1303 prout(_(" survives photon blast."))
1305 prout(_(" disappears."))
1306 game.quad[w.x][w.y] = IHWEB
1313 proutn("Don't know how to handle torpedo collision with ")
1314 crmena(True, iquad, sector, w)
1318 if curwnd!=message_window:
1319 setwnd(message_window)
1321 game.quad[w.x][w.y]=IHDOT
1322 game.quad[jw.x][jw.y]=iquad
1323 prout(_(" displaced by blast to %s "), cramlc(sector, jw))
1324 for ll in range(1, game.nenhere+1):
1325 game.kdist[ll] = game.kavgd[ll] = distance(game.sector,game.ks[ll])
1329 prout(_("Torpedo missed."))
1333 # critical-hit resolution
1335 # a critical hit occured
1336 if hit < (275.0-25.0*game.skill)*(1.0+0.5*Rand()):
1339 ncrit = 1.0 + hit/(500.0+100.0*Rand())
1340 proutn(_("***CRITICAL HIT--"))
1341 # Select devices and cause damage
1343 for loop1 in range(ncrit):
1346 # Cheat to prevent shuttle damage unless on ship
1347 if not (game.damage[j]<0.0 or (j==DSHUTTL and game.iscraft != "onship")):
1350 extradm = (hit*game.damfac)/(ncrit*(75.0+25.0*Rand()))
1351 game.damage[j] += extradm
1353 for loop2 in range(loop1):
1354 if j == cdam[loop2]:
1363 prout(_(" damaged."))
1364 if damaged(DSHIELD) and game.shldup:
1365 prout(_("***Shields knocked down."))
1368 def attack(torps_ok):
1369 # bad guy attacks us
1370 # torps_ok == false forces use of phasers in an attack
1371 atackd = False; attempt = False; ihurt = False;
1372 hitmax=0.0; hittot=0.0; chgfac=1.0
1376 # game could be over at this point, check
1381 prout("=== ATTACK!")
1383 # Tholian gewts to move before attacking
1387 # if you have just entered the RNZ, you'll get a warning
1388 if game.neutz: # The one chance not to be attacked
1392 # commanders get a chance to tac-move towards you
1393 if (((game.comhere or game.ishere) and not game.justin) or game.skill == SKILL_EMERITUS) and torps_ok:
1396 # if no enemies remain after movement, we're done
1397 if game.nenhere==0 or (game.nenhere==1 and iqhere and not iqengry):
1400 # set up partial hits if attack happens during shield status change
1401 pfac = 1.0/game.inshld
1403 chgfac = 0.25+0.5*Rand()
1407 # message verbosity control
1408 if game.skill <= SKILL_FAIR:
1411 for loop in range(1, game.nenhere+1):
1412 if game.kpower[loop] < 0:
1413 continue; # too weak to attack
1414 # compute hit strength and diminish shield power
1416 # Increase chance of photon torpedos if docked or enemy energy low
1417 if game.condition == "docked":
1419 if game.kpower[loop] < 500:
1422 iquad = game.quad[jay.x][jay.y]
1423 if iquad==IHT or (iquad==IHQUEST and not iqengry):
1425 # different enemies have different probabilities of throwing a torp
1426 usephasers = not torps_ok or \
1427 (iquad == IHK and r > 0.0005) or \
1428 (iquad==IHC and r > 0.015) or \
1429 (iquad==IHR and r > 0.3) or \
1430 (iquad==IHS and r > 0.07) or \
1431 (iquad==IHQUEST and r > 0.05)
1432 if usephasers: # Enemy uses phasers
1433 if game.condition == "docked":
1434 continue; # Don't waste the effort!
1435 attempt = True; # Attempt to attack
1436 dustfac = 0.8+0.05*Rand()
1437 hit = game.kpower[loop]*math.pow(dustfac,game.kavgd[loop])
1438 game.kpower[loop] *= 0.75
1439 else: # Enemy uses photon torpedo
1440 course = 1.90985*math.atan2(game.sector.y-jay.y, jay.x-game.sector.x)
1442 proutn(_("***TORPEDO INCOMING"))
1443 if not damaged(DSRSENS):
1445 crmena(False, iquad, where, jay)
1448 r = (Rand()+Rand())*0.5 -0.5
1449 r += 0.002*game.kpower[loop]*r
1450 hit = torpedo(course, r, jay, 1, 1)
1451 if (game.state.remkl + game.state.remcom + game.state.nscrem)==0:
1452 finish(FWON); # Klingons did themselves in!
1453 if game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova or game.alldone:
1454 return; # Supernova or finished
1457 # incoming phaser or torpedo, shields may dissipate it
1458 if game.shldup or game.shldchg or game.condition=="docked":
1459 # shields will take hits
1460 propor = pfac * game.shield
1461 if game.condition =="docked":
1465 hitsh = propor*chgfac*hit+1.0
1467 if absorb > game.shield:
1468 absorb = game.shield
1469 game.shield -= absorb
1471 # taking a hit blasts us out of a starbase dock
1472 if game.condition == "docked":
1474 # but the shields may take care of it
1475 if propor > 0.1 and hit < 0.005*game.energy:
1477 # hit from this opponent got through shields, so take damage
1479 proutn(_("%d unit hit") % int(hit))
1480 if (damaged(DSRSENS) and usephasers) or game.skill<=SKILL_FAIR:
1481 proutn(_(" on the "))
1483 if not damaged(DSRSENS) and usephasers:
1485 crmena(False, iquad, where, jay)
1487 # Decide if hit is critical
1493 if game.energy <= 0:
1494 # Returning home upon your shield, not with it...
1497 if not attempt and game.condition == "docked":
1498 prout(_("***Enemies decide against attacking your ship."))
1501 percent = 100.0*pfac*game.shield+0.5
1503 # Shields fully protect ship
1504 proutn(_("Enemy attack reduces shield strength to "))
1506 # Print message if starship suffered hit(s)
1508 proutn(_("Energy left %2d shields ") % int(game.energy))
1511 elif not damaged(DSHIELD):
1514 proutn(_("damaged, "))
1515 prout(_("%d%%, torpedoes left %d"), percent, game.torps)
1516 # Check if anyone was hurt
1517 if hitmax >= 200 or hittot >= 500:
1518 icas= hittot*Rand()*0.015
1521 prout(_("Mc Coy- \"Sickbay to bridge. We suffered %d casualties") % icas)
1522 prout(_(" in that last attack.\""))
1524 game.state.crew -= icas
1525 # After attack, reset average distance to enemies
1526 for loop in range(1, game.nenhere+1):
1527 game.kavgd[loop] = game.kdist[loop]
1531 def deadkl(w, type, mv):
1532 # kill a Klingon, Tholian, Romulan, or Thingy
1533 # Added mv to allow enemy to "move" before dying
1535 crmena(True, type, sector, mv)
1536 # Decide what kind of enemy it is and update appropriately
1538 # chalk up a Romulan
1539 game.state.galaxy[game.quadrant.x][game.quadrant.y].romulans -= 1
1541 game.state.nromrem -= 1
1545 elif type == IHQUEST:
1547 iqhere = iqengry = False
1550 # Some type of a Klingon
1551 game.state.galaxy[game.quadrant.x][game.quadrant.y].klingons -= 1
1554 game.comhere = False
1555 for i in range(1, game.state.remcom+1):
1556 if same(game.state.kcmdr[i], game.quadrant):
1558 game.state.kcmdr[i] = game.state.kcmdr[game.state.remcom]
1559 game.state.kcmdr[game.state.remcom].x = 0
1560 game.state.kcmdr[game.state.remcom].y = 0
1561 game.state.remcom -= 1
1563 if game.state.remcom != 0:
1564 schedule(FTBEAM, expran(1.0*game.incom/game.state.remcom))
1566 game.state.remkl -= 1
1568 game.state.nscrem -= 1
1570 game.state.kscmdr.x = game.state.kscmdr.y = game.isatb = 0
1575 prout("*** Internal error, deadkl() called on %s\n" % type)
1577 # For each kind of enemy, finish message to player
1578 prout(_(" destroyed."))
1579 game.quad[w.x][w.y] = IHDOT
1580 if (game.state.remkl + game.state.remcom + game.state.nscrem)==0:
1583 # Remove enemy ship from arrays describing local conditions
1584 if is_scheduled(FCDBAS) and same(game.battle, game.quadrant) and type==IHC:
1586 for i in range(1, game.nenhere+1):
1587 if same(game.ks[i], w):
1590 if i <= game.nenhere:
1591 for j in range(i, game.nenhere+1):
1592 game.ks[j] = game.ks[j+1]
1593 game.kpower[j] = game.kpower[j+1]
1594 game.kavgd[j] = game.kdist[j] = game.kdist[j+1]
1595 game.ks[game.nenhere+1].x = 0
1596 game.ks[game.nenhere+1].x = 0
1597 game.kdist[game.nenhere+1] = 0
1598 game.kavgd[game.nenhere+1] = 0
1599 game.kpower[game.nenhere+1] = 0
1602 def targetcheck(x, y):
1603 # Return None if target is invalid
1604 if not VALID_SECTOR(x, y):
1607 deltx = 0.1*(y - game.sector.y)
1608 delty = 0.1*(x - game.sector.x)
1609 if deltx==0 and delty== 0:
1611 prout(_("Spock- \"Bridge to sickbay. Dr. McCoy,"))
1612 prout(_(" I recommend an immediate review of"))
1613 prout(_(" the Captain's psychological profile.\""))
1616 return 1.90985932*math.atan2(deltx, delty)
1619 # launch photon torpedo
1621 if damaged(DPHOTON):
1622 prout(_("Photon tubes damaged."))
1626 prout(_("No torpedoes left."))
1635 prout(_("%d torpedoes left."), game.torps)
1636 proutn(_("Number of torpedoes to fire- "))
1638 else: # key == IHREAL {
1640 if n <= 0: # abort command
1645 prout(_("Maximum of 3 torpedoes per burst."))
1652 for i in range(1, n+1):
1654 if i==1 and key == IHEOL:
1655 break; # we will try prompting
1656 if i==2 and key == IHEOL:
1657 # direct all torpedoes at one target
1659 targ[i][1] = targ[1][1]
1660 targ[i][2] = targ[1][2]
1661 course[i] = course[1]
1673 course[i] = targetcheck(targ[i][1], targ[i][2])
1674 if course[i] == None:
1677 if i == 1 and key == IHEOL:
1678 # prompt for each one
1679 for i in range(1, n+1):
1680 proutn(_("Target sector for torpedo number %d- "), i)
1692 course[i] = targetcheck(targ[i][1], targ[i][2])
1693 if course[i] == None:
1696 # Loop for moving <n> torpedoes
1697 for i in range(1, n+1):
1698 if game.condition != "docked":
1700 r = (Rand()+Rand())*0.5 -0.5
1701 if math.fabs(r) >= 0.47:
1703 r = (Rand()+1.2) * r
1705 prouts(_("***TORPEDO NUMBER %d MISFIRES"), i)
1707 prouts(_("***TORPEDO MISFIRES."))
1710 prout(_(" Remainder of burst aborted."))
1712 prout(_("***Photon tubes damaged by misfire."))
1713 game.damage[DPHOTON] = game.damfac*(1.0+2.0*Rand())
1715 if game.shldup or game.condition == "docked":
1716 r *= 1.0 + 0.0001*game.shield
1717 torpedo(course[i], r, game.sector, i, n)
1718 if game.alldone or game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova:
1720 if (game.state.remkl + game.state.remcom + game.state.nscrem)==0:
1724 # check for phasers overheating
1726 chekbrn = (rpow-1500.)*0.00038
1727 if Rand() <= chekbrn:
1728 prout(_("Weapons officer Sulu- \"Phasers overheated, sir.\""))
1729 game.damage[DPHASER] = game.damfac*(1.0 + Rand()) * (1.0+chekbrn)
1731 def checkshctrl(rpow):
1732 # check shield control
1736 prout(_("Shields lowered."))
1738 # Something bad has happened
1739 prouts(_("***RED ALERT! RED ALERT!"))
1741 hit = rpow*game.shield/game.inshld
1742 game.energy -= rpow+hit*0.8
1743 game.shield -= hit*0.2
1744 if game.energy <= 0.0:
1745 prouts(_("Sulu- \"Captain! Shield malf***********************\""))
1750 prouts(_("Sulu- \"Captain! Shield malfunction! Phaser fire contained!\""))
1752 prout(_("Lt. Uhura- \"Sir, all decks reporting damage.\""))
1753 icas = hit*Rand()*0.012
1758 prout(_("McCoy to bridge- \"Severe radiation burns, Jim."))
1759 prout(_(" %d casualties so far.\""), icas)
1761 game.state.crew -= icas
1763 prout(_("Phaser energy dispersed by shields."))
1764 prout(_("Enemy unaffected."))
1768 def hittem(doublehits):
1769 # register a phaser hit on Klingons and Romulans
1770 nenhr2=game.nenhere; kk=1
1773 for k in range(1, nenhr2+1):
1777 dustfac = 0.9 + 0.01*Rand()
1778 hit = wham*math.pow(dustfac,game.kdist[kk])
1779 kpini = game.kpower[kk]
1780 kp = math.fabs(kpini)
1781 if PHASEFAC*hit < kp:
1783 if game.kpower[kk] < 0:
1784 game.kpower[kk] -= -kp
1786 game.kpower[kk] -= kp
1787 kpow = game.kpower[kk]
1790 if not damaged(DSRSENS):
1792 proutn(_("%d unit hit on ") % int(hit))
1794 proutn(_("Very small hit on "))
1795 ienm = game.quad[w.x][w.y]
1798 crmena(False, ienm, "sector", w)
1802 if (game.state.remkl + game.state.remcom + game.state.nscrem)==0:
1806 kk -= 1; # don't do the increment
1807 else: # decide whether or not to emasculate klingon
1808 if kpow > 0 and Rand() >= 0.9 and \
1809 kpow <= ((0.4 + 0.4*Rand())*kpini):
1810 prout(_("***Mr. Spock- \"Captain, the vessel at %s"),
1812 prout(_(" has just lost its firepower.\""))
1813 game.kpower[kk] = -kpow
1820 kz = 0; k = 1; irec=0 # Cheating inhibitor
1821 ifast = False; no = False; itarg = True; msgflag = True
1826 # SR sensors and Computer are needed fopr automode
1827 if damaged(DSRSENS) or damaged(DCOMPTR):
1829 if game.condition == "docked":
1830 prout(_("Phasers can't be fired through base shields."))
1833 if damaged(DPHASER):
1834 prout(_("Phaser control damaged."))
1838 if damaged(DSHCTRL):
1839 prout(_("High speed shield control damaged."))
1842 if game.energy <= 200.0:
1843 prout(_("Insufficient energy to activate high-speed shield control."))
1846 prout(_("Weapons Officer Sulu- \"High-speed shield control enabled, sir.\""))
1849 # Original code so convoluted, I re-did it all
1850 while automode=="NOTSET":
1855 prout(_("There is no enemy present to select."))
1858 automode="AUTOMATIC"
1862 elif isit("automatic"):
1863 if (not itarg) and game.nenhere != 0:
1864 automode = "FORCEMAN"
1867 prout(_("Energy will be expended into space."))
1868 automode = "AUTOMATIC"
1877 prout(_("Energy will be expended into space."))
1878 automode = "AUTOMATIC"
1880 automode = "FORCEMAN"
1882 automode = "AUTOMATIC"
1886 prout(_("Energy will be expended into space."))
1887 automode = "AUTOMATIC"
1889 automode = "FORCEMAN"
1891 proutn(_("Manual or automatic? "))
1895 if automode == "AUTOMATIC":
1896 if key == IHALPHA and isit("no"):
1899 if key != IHREAL and game.nenhere != 0:
1900 prout(_("Phasers locked on target. Energy available: %.2f"),
1906 for i in range(1, game.nenhere+1):
1907 irec += math.fabs(game.kpower[i])/(PHASEFAC*math.pow(0.90,game.kdist[i]))*(1.01+0.05*Rand()) + 1.0
1909 proutn(_("%d units required. "), irec)
1911 proutn(_("Units to fire= "))
1917 proutn(_("Energy available= %.2f") % avail)
1920 if not rpow > avail:
1927 if key == IHALPHA and isit("no"):
1930 game.energy -= 200; # Go and do it!
1931 if checkshctrl(rpow):
1939 for i in range(1, game.nenhere+1):
1943 hits[i] = math.fabs(game.kpower[i])/(PHASEFAC*math.pow(0.90,game.kdist[i]))
1944 over = (0.01 + 0.05*Rand())*hits[i]
1946 powrem -= hits[i] + over
1947 if powrem <= 0 and temp < hits[i]:
1956 if extra > 0 and not game.alldone:
1958 proutn(_("*** Tholian web absorbs "))
1960 proutn(_("excess "))
1961 prout(_("phaser energy."))
1963 prout(_("%d expended on empty space."), int(extra))
1964 elif automode == "FORCEMAN":
1967 if damaged(DCOMPTR):
1968 prout(_("Battle computer damaged, manual fire only."))
1971 prouts(_("---WORKING---"))
1973 prout(_("Short-range-sensors-damaged"))
1974 prout(_("Insufficient-data-for-automatic-phaser-fire"))
1975 prout(_("Manual-fire-must-be-used"))
1977 elif automode == "MANUAL":
1979 for k in range(1, game.nenhere+1):
1981 ienm = game.quad[aim.x][aim.y]
1983 proutn(_("Energy available= %.2f") % avail-0.006)
1987 if damaged(DSRSENS) and not (abs(game.sector.x-aim.x) < 2 and abs(game.sector.y-aim.y) < 2) and \
1988 (ienm == IHC or ienm == IHS):
1990 prout(_(" can't be located without short range scan."))
1993 hits[k] = 0; # prevent overflow -- thanks to Alexei Voitenko
1998 if itarg and k > kz:
1999 irec=(abs(game.kpower[k])/(PHASEFAC*math.pow(0.9,game.kdist[k]))) * (1.01+0.05*Rand()) + 1.0
2002 if not damaged(DCOMPTR):
2007 proutn(_("units to fire at "))
2008 crmena(False, ienm, sector, aim)
2011 if key == IHALPHA and isit("no"):
2019 if k==1: # Let me say I'm baffled by this
2028 # If total requested is too much, inform and start over
2030 prout(_("Available energy exceeded -- try again."))
2033 key = scan(); # scan for next value
2036 # zero energy -- abort
2039 if key == IHALPHA and isit("no"):
2044 game.energy -= 200.0
2045 if checkshctrl(rpow):
2049 # Say shield raised or malfunction, if necessary
2056 prout(_("Sulu- \"Sir, the high-speed shield control has malfunctioned . . ."))
2057 prouts(_(" CLICK CLICK POP . . ."))
2058 prout(_(" No response, sir!"))
2061 prout(_("Shields raised."))
2066 # Code from events,c begins here.
2068 # This isn't a real event queue a la BSD Trek yet -- you can only have one
2069 # event of each type active at any given time. Mostly these means we can
2070 # only have one FDISTR/FENSLV/FREPRO sequence going at any given time
2071 # BSD Trek, from which we swiped the idea, can have up to 5.
2075 def unschedule(evtype):
2076 # remove an event from the schedule
2077 game.future[evtype].date = FOREVER
2078 return game.future[evtype]
2080 def is_scheduled(evtype):
2081 # is an event of specified type scheduled
2082 return game.future[evtype].date != FOREVER
2084 def scheduled(evtype):
2085 # when will this event happen?
2086 return game.future[evtype].date
2088 def schedule(evtype, offset):
2089 # schedule an event of specified type
2090 game.future[evtype].date = game.state.date + offset
2091 return game.future[evtype]
2093 def postpone(evtype, offset):
2094 # postpone a scheduled event
2095 game.future[evtype].date += offset
2098 # rest period is interrupted by event
2101 proutn(_("Mr. Spock- \"Captain, shall we cancel the rest period?\""))
2103 game.resting = False
2110 # run through the event queue looking for things to do
2112 fintim = game.state.date + game.optime; yank=0
2113 ictbeam = False; istract = False
2114 w = coord(); hold = coord()
2115 ev = event(); ev2 = event()
2118 # tractor beaming cases merge here
2119 yank = math.sqrt(yank)
2121 game.optime = (10.0/(7.5*7.5))*yank # 7.5 is yank rate (warp 7.5)
2125 prout(_(" caught in long range tractor beam--"))
2126 # If Kirk & Co. screwing around on planet, handle
2127 atover(True) # atover(true) is Grab
2130 if game.icraft: # Caught in Galileo?
2133 # Check to see if shuttle is aboard
2134 if game.iscraft == "offship":
2137 prout(_("Galileo, left on the planet surface, is captured"))
2138 prout(_("by aliens and made into a flying McDonald's."))
2139 game.damage[DSHUTTL] = -10
2140 game.iscraft = "removed"
2142 prout(_("Galileo, left on the planet surface, is well hidden."))
2144 game.quadrant = game.state.kscmdr
2146 game.quadrant = game.state.kcmdr[i]
2147 game.sector = randplace(QUADSIZE)
2149 proutn(_(" is pulled to "))
2150 proutn(cramlc(quadrant, game.quadrant))
2152 prout(cramlc(sector, game.sector))
2154 prout(_("(Remainder of rest/repair period cancelled.)"))
2155 game.resting = False
2157 if not damaged(DSHIELD) and game.shield > 0:
2158 doshield(True) # raise shields
2161 prout(_("(Shields not currently useable.)"))
2163 # Adjust finish time to time of tractor beaming
2164 fintim = game.state.date+game.optime
2166 if game.state.remcom <= 0:
2169 schedule(FTBEAM, game.optime+expran(1.5*game.intime/game.state.remcom))
2172 # Code merges here for any commander destroying base
2173 # Not perfect, but will have to do
2174 # Handle case where base is in same quadrant as starship
2175 if same(game.battle, game.quadrant):
2176 game.state.chart[game.battle.x][game.battle.y].starbase = False
2177 game.quad[game.base.x][game.base.y] = IHDOT
2178 game.base.x=game.base.y=0
2181 prout(_("Spock- \"Captain, I believe the starbase has been destroyed.\""))
2182 elif game.state.rembase != 1 and \
2183 (not damaged(DRADIO) or game.condition == "docked"):
2184 # Get word via subspace radio
2187 prout(_("Lt. Uhura- \"Captain, Starfleet Command reports that"))
2188 proutn(_(" the starbase in "))
2189 proutn(cramlc(quadrant, game.battle))
2190 prout(_(" has been destroyed by"))
2192 prout(_("the Klingon Super-Commander"))
2194 prout(_("a Klingon Commander"))
2195 game.state.chart[game.battle.x][game.battle.y].starbase = False
2196 # Remove Starbase from galaxy
2197 game.state.galaxy[game.battle.x][game.battle.y].starbase = False
2198 for i in range(1, game.state.rembase+1):
2199 if same(game.state.baseq[i], game.battle):
2200 game.state.baseq[i] = game.state.baseq[game.state.rembase]
2201 game.state.rembase -= 1
2203 # reinstate a commander's base attack
2207 invalidate(game.battle)
2210 prout("=== EVENTS from %.2f to %.2f:" % (game.state.date, fintim))
2211 for i in range(1, NEVENTS):
2212 if i == FSNOVA: proutn("=== Supernova ")
2213 elif i == FTBEAM: proutn("=== T Beam ")
2214 elif i == FSNAP: proutn("=== Snapshot ")
2215 elif i == FBATTAK: proutn("=== Base Attack ")
2216 elif i == FCDBAS: proutn("=== Base Destroy ")
2217 elif i == FSCMOVE: proutn("=== SC Move ")
2218 elif i == FSCDBAS: proutn("=== SC Base Destroy ")
2219 elif i == FDSPROB: proutn("=== Probe Move ")
2220 elif i == FDISTR: proutn("=== Distress Call ")
2221 elif i == FENSLV: proutn("=== Enslavement ")
2222 elif i == FREPRO: proutn("=== Klingon Build ")
2224 prout("%.2f" % (scheduled(i)))
2227 radio_was_broken = damaged(DRADIO)
2230 # Select earliest extraneous event, evcode==0 if no events
2235 for l in range(1, NEVENTS):
2236 if game.future[l].date < datemin:
2239 prout("== Event %d fires" % (evcode))
2240 datemin = game.future[l].date
2241 xtime = datemin-game.state.date
2242 game.state.date = datemin
2243 # Decrement Federation resources and recompute remaining time
2244 game.state.remres -= (game.state.remkl+4*game.state.remcom)*xtime
2246 if game.state.remtime <=0:
2249 # Any crew left alive?
2250 if game.state.crew <=0:
2253 # Is life support adequate?
2254 if damaged(DLIFSUP) and game.condition != "docked":
2255 if game.lsupres < xtime and game.damage[DLIFSUP] > game.lsupres:
2258 game.lsupres -= xtime
2259 if game.damage[DLIFSUP] <= xtime:
2260 game.lsupres = game.inlsr
2263 if game.condition == "docked":
2264 repair /= game.docfac
2265 # Don't fix Deathray here
2266 for l in range(0, NDEVICES):
2267 if game.damage[l] > 0.0 and l != DDRAY:
2268 if game.damage[l]-repair > 0.0:
2269 game.damage[l] -= repair
2271 game.damage[l] = 0.0
2272 # If radio repaired, update star chart and attack reports
2273 if radio_was_broken and not damaged(DRADIO):
2274 prout(_("Lt. Uhura- \"Captain, the sub-space radio is working and"))
2275 prout(_(" surveillance reports are coming in."))
2277 if not game.iseenit:
2281 prout(_(" The star chart is now up to date.\""))
2283 # Cause extraneous event EVCODE to occur
2284 game.optime -= xtime
2285 if evcode == FSNOVA: # Supernova
2288 schedule(FSNOVA, expran(0.5*game.intime))
2289 if game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova:
2291 elif evcode == FSPY: # Check with spy to see if SC should tractor beam
2292 if game.state.nscrem == 0 or \
2293 ictbeam or istract or \
2294 game.condition=="docked" or game.isatb==1 or game.iscate:
2296 if game.ientesc or \
2297 (game.energy<2000 and game.torps<4 and game.shield < 1250) or \
2298 (damaged(DPHASER) and (damaged(DPHOTON) or game.torps<4)) or \
2299 (damaged(DSHIELD) and \
2300 (game.energy < 2500 or damaged(DPHASER)) and \
2301 (game.torps < 5 or damaged(DPHOTON))):
2304 yank = distance(game.state.kscmdr, game.quadrant)
2309 elif evcode == FTBEAM: # Tractor beam
2310 if game.state.remcom == 0:
2313 i = Rand()*game.state.remcom+1.0
2314 yank = square(game.state.kcmdr[i].x-game.quadrant.x) + square(game.state.kcmdr[i].y-game.quadrant.y)
2315 if istract or game.condition == "docked" or yank == 0:
2316 # Drats! Have to reschedule
2318 game.optime + expran(1.5*game.intime/game.state.remcom))
2322 elif evcode == FSNAP: # Snapshot of the universe (for time warp)
2323 game.snapsht = game.state
2324 game.state.snap = True
2325 schedule(FSNAP, expran(0.5 * game.intime))
2326 elif evcode == FBATTAK: # Commander attacks starbase
2327 if game.state.remcom==0 or game.state.rembase==0:
2333 for j in range(1, game.state.rembase+1):
2334 for k in range(1, game.state.remcom+1):
2335 if same(game.state.baseq[j], game.state.kcmdr[k]) and \
2336 not same(game.state.baseq[j], game.quadrant) and \
2337 not same(game.state.baseq[j], game.state.kscmdr):
2341 if j>game.state.rembase:
2342 # no match found -- try later
2343 schedule(FBATTAK, expran(0.3*game.intime))
2346 # commander + starbase combination found -- launch attack
2347 game.battle = game.state.baseq[j]
2348 schedule(FCDBAS, 1.0+3.0*Rand())
2349 if game.isatb: # extra time if SC already attacking
2350 postpone(FCDBAS, scheduled(FSCDBAS)-game.state.date)
2351 game.future[FBATTAK].date = game.future[FCDBAS].date + expran(0.3*game.intime)
2352 game.iseenit = False
2353 if damaged(DRADIO) and game.condition != "docked":
2354 continue # No warning :-(
2358 proutn(_("Lt. Uhura- \"Captain, the starbase in Quadrant %s") % game.battle)
2359 prout(_(" reports that it is under attack and that it can"))
2360 proutn(_(" hold out only until stardate %d" % (int(scheduled(FCDBAS)))))
2364 elif evcode == FSCDBAS: # Supercommander destroys base
2367 if not game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].starbase:
2368 continue # WAS RETURN!
2370 game.battle = game.state.kscmdr
2372 elif evcode == FCDBAS: # Commander succeeds in destroying base
2375 # find the lucky pair
2376 for i in range(1, game.state.remcom+1):
2377 if same(game.state.kcmdr[i], game.battle):
2379 if i > game.state.remcom or game.state.rembase == 0 or \
2380 not game.state.galaxy[game.battle.x][game.battle.y].starbase:
2381 # No action to take after all
2382 invalidate(game.battle)
2385 elif evcode == FSCMOVE: # Supercommander moves
2386 schedule(FSCMOVE, 0.2777)
2387 if not game.ientesc and not istract and game.isatb != 1 and \
2388 (not game.iscate or not game.justin):
2390 elif evcode == FDSPROB: # Move deep space probe
2391 schedule(FDSPROB, 0.01)
2392 game.probex += game.probeinx
2393 game.probey += game.probeiny
2394 i = (int)(game.probex/QUADSIZE +0.05)
2395 j = (int)(game.probey/QUADSIZE + 0.05)
2396 if game.probec.x != i or game.probec.y != j:
2399 if not VALID_QUADRANT(i, j) or \
2400 game.state.galaxy[game.probec.x][game.probec.y].supernova:
2401 # Left galaxy or ran into supernova
2402 if not damaged(DRADIO) or game.condition == "docked":
2405 proutn(_("Lt. Uhura- \"The deep space probe "))
2406 if not VALID_QUADRANT(j, i):
2407 proutn(_("has left the galaxy"))
2409 proutn(_("is no longer transmitting"))
2413 if not damaged(DRADIO) or game.condition == "docked":
2416 proutn(_("Lt. Uhura- \"The deep space probe is now in "))
2417 proutn(cramlc(quadrant, game.probec))
2419 pdest = game.state.galaxy[game.probec.x][game.probec.y]
2420 # Update star chart if Radio is working or have access to radio
2421 if not damaged(DRADIO) or game.condition == "docked":
2422 chp = game.state.chart[game.probec.x][game.probec.y]
2423 chp.klingons = pdest.klingons
2424 chp.starbase = pdest.starbase
2425 chp.stars = pdest.stars
2426 pdest.charted = True
2427 game.proben -= 1 # One less to travel
2428 if game.proben == 0 and game.isarmed and pdest.stars:
2429 # lets blow the sucker!
2430 supernova(True, game.probec)
2432 if game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova:
2434 elif evcode == FDISTR: # inhabited system issues distress call
2436 # try a whole bunch of times to find something suitable
2437 for i in range(100):
2438 # need a quadrant which is not the current one,
2439 # which has some stars which are inhabited and
2440 # not already under attack, which is not
2441 # supernova'ed, and which has some Klingons in it
2442 w = randplace(GALSIZE)
2443 q = game.state.galaxy[w.x][w.y]
2444 if not (same(game.quadrant, w) or q.planet == NOPLANET or \
2445 game.state.planets[q.planet].inhabited == UNINHABITED or \
2446 q.supernova or q.status!=secure or q.klingons<=0):
2449 # can't seem to find one; ignore this call
2451 prout("=== Couldn't find location for distress event.")
2453 # got one!! Schedule its enslavement
2454 ev = schedule(FENSLV, expran(game.intime))
2456 q.status = distressed
2458 # tell the captain about it if we can
2459 if not damaged(DRADIO) or game.condition == "docked":
2460 prout(_("Uhura- Captain, %s in Quadrant %s reports it is under attack" \
2461 % (systnames[q.planet], `w`)))
2462 prout(_("by a Klingon invasion fleet."))
2465 elif evcode == FENSLV: # starsystem is enslaved
2466 ev = unschedule(FENSLV)
2467 # see if current distress call still active
2468 q = game.state.galaxy[ev.quadrant.x][ev.quadrant.y]
2472 q.status = "enslaved"
2474 # play stork and schedule the first baby
2475 ev2 = schedule(FREPRO, expran(2.0 * game.intime))
2476 ev2.quadrant = ev.quadrant
2478 # report the disaster if we can
2479 if not damaged(DRADIO) or game.condition == "docked":
2480 prout(_("Uhura- We've lost contact with starsystem %s" % \
2481 systnames[q.planet]))
2482 prout(_("in Quadrant %s.\n" % ev.quadrant))
2483 elif evcode == FREPRO: # Klingon reproduces
2484 # If we ever switch to a real event queue, we'll need to
2485 # explicitly retrieve and restore the x and y.
2486 ev = schedule(FREPRO, expran(1.0 * game.intime))
2487 # see if current distress call still active
2488 q = game.state.galaxy[ev.quadrant.x][ev.quadrant.y]
2492 if game.state.remkl >=MAXKLGAME:
2493 continue # full right now
2494 # reproduce one Klingon
2496 if game.klhere >= MAXKLQUAD:
2498 # this quadrant not ok, pick an adjacent one
2499 for i in range(w.x - 1, w.x + 2):
2500 for j in range(w.y - 1, w.y + 2):
2501 if not VALID_QUADRANT(i, j):
2503 q = game.state.galaxy[w.x][w.y]
2504 # check for this quad ok (not full & no snova)
2505 if q.klingons >= MAXKLQUAD or q.supernova:
2509 continue # search for eligible quadrant failed
2514 game.state.remkl += 1
2516 if same(game.quadrant, w):
2517 newkling(++game.klhere)
2519 # recompute time left
2521 # report the disaster if we can
2522 if not damaged(DRADIO) or game.condition == "docked":
2523 if same(game.quadrant, w):
2524 prout(_("Spock- sensors indicate the Klingons have"))
2525 prout(_("launched a warship from %s." \
2526 % systnames[q.planet]))
2528 prout(_("Uhura- Starfleet reports increased Klingon activity"))
2529 if q.planet != NOPLANET:
2530 proutn(_("near %s" % systnames[q.planet]))
2531 prout(_("in %s.\n" % cramlc(quadrant, w)))
2540 proutn(_("How long? "))
2545 origTime = delay = aaitem
2548 if delay >= game.state.remtime or game.nenhere != 0:
2549 proutn(_("Are you sure? "))
2553 # Alternate resting periods (events) with attacks
2558 game.resting = False
2559 if not game.resting:
2560 prout(_("%d stardates left." % int(game.state.remtime)))
2562 temp = game.optime = delay
2564 rtime = 1.0 + Rand()
2568 if game.optime < delay:
2577 # Repair Deathray if long rest at starbase
2578 if origTime-delay >= 9.99 and game.condition == "docked":
2579 game.damage[DDRAY] = 0.0
2580 # leave if quadrant supernovas
2581 if game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova:
2583 game.resting = False
2586 # A nova occurs. It is the result of having a star hit with a
2587 # photon torpedo, or possibly of a probe warhead going off.
2588 # Stars that go nova cause stars which surround them to undergo
2589 # the same probabilistic process. Klingons next to them are
2590 # destroyed. And if the starship is next to it, it gets zapped.
2591 # If the zap is too much, it gets destroyed.
2595 course = (0.0, 10.5, 12.0, 1.5, 9.0, 0.0, 3.0, 7.5, 6.0, 4.5)
2596 newc = coord(); scratch = coord()
2599 # Wow! We've supernova'ed
2600 supernova(False, nov)
2603 # handle initial nova
2604 game.quad[nov.x][nov.y] = IHDOT
2605 crmena(False, IHSTAR, sector, nov)
2607 game.state.galaxy[game.quadrant.x][game.quadrant.y].stars -= 1
2608 game.state.starkl += 1
2610 # Set up stack to recursively trigger adjacent stars
2611 bot = top = top2 = 1
2617 for mm in range(bot, top+1):
2618 for nn in range(1, 3+1): # nn,j represents coordinates around current
2619 for j in range(1, 3+1):
2622 scratch.x = hits[mm][1]+nn-2
2623 scratch.y = hits[mm][2]+j-2
2624 if not VALID_SECTOR(scratch.y, scratch.x):
2626 iquad = game.quad[scratch.x][scratch.y]
2627 # Empty space ends reaction
2628 if iquad in (IHDOT, IHQUEST, IHBLANK, IHT, IHWEB):
2630 elif iquad == IHSTAR: # Affect another star
2632 # This star supernovas
2633 scratch = supernova(False)
2636 hits[top2][1]=scratch.x
2637 hits[top2][2]=scratch.y
2638 game.state.galaxy[game.quadrant.x][game.quadrant.y].stars -= 1
2639 game.state.starkl += 1
2640 crmena(True, IHSTAR, sector, scratch)
2642 game.quad[scratch.x][scratch.y] = IHDOT
2643 elif iquad == IHP: # Destroy planet
2644 game.state.galaxy[game.quadrant.x][game.quadrant.y].planet = NOPLANET
2645 game.state.nplankl += 1
2646 crmena(True, IHP, sector, scratch)
2647 prout(_(" destroyed."))
2648 game.state.planets[game.iplnet].pclass = destroyed
2650 invalidate(game.plnet)
2654 game.quad[scratch.x][scratch.y] = IHDOT
2655 elif iquad == IHB: # Destroy base
2656 game.state.galaxy[game.quadrant.x][game.quadrant.y].starbase = False
2657 for i in range(1, game.state.rembase+1):
2658 if same(game.state.baseq[i], game.quadrant):
2660 game.state.baseq[i] = game.state.baseq[game.state.rembase]
2661 game.state.rembase -= 1
2662 invalidate(game.base)
2663 game.state.basekl += 1
2665 crmena(True, IHB, sector, scratch)
2666 prout(_(" destroyed."))
2667 game.quad[scratch.x][scratch.y] = IHDOT
2668 elif iquad in (IHE, IHF): # Buffet ship
2669 prout(_("***Starship buffeted by nova."))
2671 if game.shield >= 2000.0:
2672 game.shield -= 2000.0
2674 diff = 2000.0 - game.shield
2678 prout(_("***Shields knocked out."))
2679 game.damage[DSHIELD] += 0.005*game.damfac*Rand()*diff
2681 game.energy -= 2000.0
2682 if game.energy <= 0:
2685 # add in course nova contributes to kicking starship
2686 icx += game.sector.x-hits[mm][1]
2687 icy += game.sector.y-hits[mm][2]
2689 elif iquad == IHK: # kill klingon
2690 deadkl(scratch,iquad, scratch)
2691 elif iquad in (IHC,IHS,IHR): # Damage/destroy big enemies
2692 for ll in range(1, game.nenhere+1):
2693 if same(game.ks[ll], scratch):
2695 game.kpower[ll] -= 800.0 # If firepower is lost, die
2696 if game.kpower[ll] <= 0.0:
2697 deadkl(scratch, iquad, scratch)
2699 newc.x = scratch.x + scratch.x - hits[mm][1]
2700 newc.y = scratch.y + scratch.y - hits[mm][2]
2701 crmena(True, iquad, sector, scratch)
2702 proutn(_(" damaged"))
2703 if not VALID_SECTOR(newc.x, newc.y):
2704 # can't leave quadrant
2707 iquad1 = game.quad[newc.x][newc.y]
2708 if iquad1 == IHBLANK:
2709 proutn(_(", blasted into "))
2710 crmena(False, IHBLANK, sector, newc)
2712 deadkl(scratch, iquad, newc)
2715 # can't move into something else
2718 proutn(_(", buffeted to "))
2719 proutn(cramlc(sector, newc))
2720 game.quad[scratch.x][scratch.y] = IHDOT
2721 game.quad[newc.x][newc.y] = iquad
2723 game.kdist[ll] = game.kavgd[ll] = distance(game.sector, newc)
2732 # Starship affected by nova -- kick it away.
2733 game.dist = kount*0.1
2736 game.direc = course[3*(icx+1)+icy+2]
2737 if game.direc == 0.0:
2739 if game.dist == 0.0:
2741 game.optime = 10.0*game.dist/16.0
2743 prout(_("Force of nova displaces starship."))
2745 game.optime = 10.0*game.dist/16.0
2748 def supernova(induced, w=None):
2749 # star goes supernova
2757 # Scheduled supernova -- select star
2758 # logic changed here so that we won't favor quadrants in top
2760 for nq.x in range(1, GALSIZE+1):
2761 for nq.y in range(1, GALSIZE+1):
2762 stars += game.state.galaxy[nq.x][nq.y].stars
2764 return # nothing to supernova exists
2765 num = Rand()*stars + 1
2766 for nq.x in range(1, GALSIZE+1):
2767 for nq.y in range(1, GALSIZE+1):
2768 num -= game.state.galaxy[nq.x][nq.y].stars
2774 proutn("=== Super nova here?")
2778 if not same(nq, game.quadrant) or game.justin:
2779 # it isn't here, or we just entered (treat as enroute)
2780 if not damaged(DRADIO) or game.condition == "docked":
2782 prout(_("Message from Starfleet Command Stardate %.2f" % game.state.date))
2783 prout(_(" Supernova in Quadrant %s; caution advised." % nq))
2786 # we are in the quadrant!
2787 num = Rand()* game.state.galaxy[nq.x][nq.y].stars + 1
2788 for ns.x in range(1, QUADSIZE+1):
2789 for ns.y in range(1, QUADSIZE+1):
2790 if game.quad[ns.x][ns.y]==IHSTAR:
2798 prouts(_("***RED ALERT! RED ALERT!"))
2800 prout(_("***Incipient supernova detected at Sector %s" % ns))
2801 if square(ns.x-game.sector.x) + square(ns.y-game.sector.y) <= 2.1:
2802 proutn(_("Emergency override attempts t"))
2803 prouts("***************")
2808 # destroy any Klingons in supernovaed quadrant
2809 kldead = game.state.galaxy[nq.x][nq.y].klingons
2810 game.state.galaxy[nq.x][nq.y].klingons = 0
2811 if same(nq, game.state.kscmdr):
2812 # did in the Supercommander!
2813 game.state.nscrem = game.state.kscmdr.x = game.state.kscmdr.y = game.isatb = 0
2817 if game.state.remcom:
2818 maxloop = game.state.remcom
2819 for l in range(1, maxloop+1):
2820 if same(game.state.kcmdr[l], nq):
2821 game.state.kcmdr[l] = game.state.kcmdr[game.state.remcom]
2822 invalidate(game.state.kcmdr[game.state.remcom])
2823 game.state.remcom -= 1
2825 if game.state.remcom==0:
2828 game.state.remkl -= kldead
2829 # destroy Romulans and planets in supernovaed quadrant
2830 nrmdead = game.state.galaxy[nq.x][nq.y].romulans
2831 game.state.galaxy[nq.x][nq.y].romulans = 0
2832 game.state.nromrem -= nrmdead
2834 for loop in range(game.inplan):
2835 if same(game.state.planets[loop].w, nq):
2836 game.state.planets[loop].pclass = destroyed
2838 # Destroy any base in supernovaed quadrant
2839 if game.state.rembase:
2840 maxloop = game.state.rembase
2841 for loop in range(1, maxloop+1):
2842 if same(game.state.baseq[loop], nq):
2843 game.state.baseq[loop] = game.state.baseq[game.state.rembase]
2844 invalidate(game.state.baseq[game.state.rembase])
2845 game.state.rembase -= 1
2847 # If starship caused supernova, tally up destruction
2849 game.state.starkl += game.state.galaxy[nq.x][nq.y].stars
2850 game.state.basekl += game.state.galaxy[nq.x][nq.y].starbase
2851 game.state.nplankl += npdead
2852 # mark supernova in galaxy and in star chart
2853 if same(game.quadrant, nq) or not damaged(DRADIO) or game.condition == "docked":
2854 game.state.galaxy[nq.x][nq.y].supernova = True
2855 # If supernova destroys last Klingons give special message
2856 if (game.state.remkl + game.state.remcom + game.state.nscrem)==0 and not same(nq, game.quadrant):
2859 prout(_("Lucky you!"))
2860 proutn(_("A supernova in %s has just destroyed the last Klingons." % nq))
2863 # if some Klingons remain, continue or die in supernova