def getRecommendedVector(self): """This was copied out of scripts.py in stardog and modified slightly. PRE: The following code MUST MUST MUST be set in order to make self.angle_to_target current before calling this method: self.angle_to_target = self.getAngleToTarget() POST: Returns recommended speed and direction for approaching target. """ dtheta = 0.0 speed = self.maxSpeed #If we need to turn more towards the target... abs_angle = abs(self.angle_to_target) if abs_angle > self.acceptableError: #Get the amount to turn. It may be less than the #amount this object is capable of turning. capable_dtheta = self.calculateDTheta() #Turn counter clockwise if that's the direction of our target if self.angle_to_target < 0: dtheta = max(self.angle_to_target, -capable_dtheta) #In all other cases turn clockwise else: dtheta = min(self.angle_to_target, capable_dtheta) #Now determine the best speed #Get distance to target. Cap the distance at one screen width. d = min(globalvars.WIDTH, cygeometry.distance(self.rect.center, self.destination)) if d / max(abs_angle, 1.0) < self.distance_angle_ratio: speed = self.maxTurnSpeed return (speed, dtheta)
def setClosestSprites(): """Pre: Post: For all ships in the whiskerables sprite list, the closest sprite and the distance to that sprite is set. This is used for helping NPC ships avoid collisions.""" # Get all the whiskerable sprites in an array sprite_list = globalvars.whiskerables.sprites() # TODO I'd like to make use of sorting like I do for collision checking with this, but recently it was gumming up the works, so I'm simplifying it for now. # Sort them by their top point as is done when checking for collisions. # sprite_list = sorted(sprite_list, \ # key=lambda c: c.rect.topleft[1]+c.rect.height,\ # reverse=True) # For each sprite... for i in xrange(len(sprite_list)): # Get the next sprite to deal with. A = sprite_list[i] # only ships can avoid objects. if ( A.is_a != globalvars.SHIP ): # TODO this could be more efficient by keeping another group that is just ships. Of course there is a cost there. It might be worth profiling at some point to see if this is better or another group that is just non-player ships is better. continue # Reset closest sprite and the distance to that sprite. Sprites # further than this distance will be ignored. closest_sprite = None least_dist = globalvars.MINSAFEDIST # search for too close sprites for j in xrange(len(sprite_list)): if j != i: B = sprite_list[j] dist = cygeometry.distance(A.rect.center, B.rect.center) - B.collisionradius - A.collisionradius if dist < least_dist: least_dist = dist closest_sprite = B # Set sprite A's closest sprite and the distance to that sprite. A.setClosest(closest_sprite, least_dist)
def inCollision(self, other): '''Pre: Other is a physical object Post: returns true if self and other are colliding.''' #treat both objects as rectangles if either object wants to be #treated as a rectangle. #collision detect based on rectangles: if self.useRectangular or other.useRectangular: #If not that self's right most point is left of other's left most point #or other's right most point is left of self's left most point #then the two objects collided. selfRight = self.rect.topleft[0]+self.collisionwidth+self.collisiontopleft[0] otherLeft = other.rect.topleft[0]+other.collisiontopleft[0] otherRight = other.rect.topleft[0]+other.collisionwidth+other.collisiontopleft[0] selfLeft = self.rect.topleft[0]+self.collisiontopleft[0] selfBottom = self.rect.topleft[1]+self.collisionheight+self.collisiontopleft[1] otherTop = other.rect.topleft[1]+other.collisiontopleft[1] otherBottom = other.rect.topleft[1]+other.collisionheight+other.collisiontopleft[1] selfTop = self.rect.topleft[1]+self.collisiontopleft[1] return not(selfRight < otherLeft or otherRight < selfLeft)\ and not(selfBottom < otherTop or otherBottom < selfTop) #collision detect based on circles: #If the distance between our centers is less than our #summed radii, then we have collided. else: return cygeometry.distance(self.rect.center, other.rect.center) < self.collisionradius+other.collisionradius
def nudgeApart(physical_objs, TESTING=False): """Takes a list of physical objects and modifies their locations so they don't collide.""" collisions = 0 # Used for testing # Prevent collisions. # The following copied from collisionHandling() physical_objs = sorted(physical_objs, key=lambda c: c.rect.bottom, reverse=True) # Nudge objects that collide apart. # This code is related to the code used in setClosestSprites and collisionHandling for i in xrange(len(physical_objs)): A = physical_objs[i] for j in xrange(i + 1, len(physical_objs)): B = physical_objs[j] if A.rect.top > B.rect.bottom: break else: if cygeometry.distance(A.rect.center, B.rect.center) > A.collisionradius + B.collisionradius: pass else: # They collide. Move them apart. if TESTING: collisions += 1 magnitude = max(A.collisionradius, B.collisionradius) * 2 angle = A.getAngleToTarget(target=B) B.translate(angle, magnitude) if TESTING: print "Initial collisions: " + str(collisions)
def inSights(shooter, target_loc, weapon_range, angle_min): '''Pre: shooter is a ship. target_loc is a tuple x,y. weapon_range is an int or float. angle_min is a float or int representing the minimal angle to the target. Post: returns true it the target is roughly in the sights of the shooter.''' angle = shooter.getAngleToTarget(target=target_loc) #Check angle to target. #Also check that the target is roughly within this weapon's range. return abs(angle) < angle_min and \ cygeometry.distance(shooter.rect.center, target_loc) < weapon_range
def park(self): '''Slow to a stop near target destination.''' itersToStop = self.speed / self.dv if not self.speed == 0 and \ itersToStop >= cygeometry.distance(self.rect.center, self.destination) / self.speed: #Decelerate self.speed = max(0, self.speed - self.dv) self.targetSpeed = self.speed return True return False
def getNearestEnemy(self, location, team_index): '''Returns the enemy nearest location.''' enemy_sprite_list = self.getAllEnemies(team_index) nearest = None min_dist = 1000000.0 for enemy in enemy_sprite_list: dist = cygeometry.distance(location, enemy.rect.center) if dist < min_dist: min_dist = dist nearest = enemy return nearest
def getClosestLeadIndicator(self, location, team_index): '''Returns the nearest enemy by that enemy's lead indicator or None.''' enemy_sprite_list = self.getAllEnemies(team_index) nearest = None min_dist = 1000000.0 for enemy in enemy_sprite_list: dist = cygeometry.distance(location, enemy.lead_indicator) if dist < min_dist: min_dist = dist nearest = enemy return nearest
def draw(self, _): #elapsed time elapsed = time.time() - self.start_time #Distance to target dtt = cygeometry.distance(globalvars.player.rect.center, self.target) #Write the elapsed time to the top of the screen. string = 'Time: '+formatTime(elapsed)+\ '. Distance: '+trunc(dtt,0) text = self.font.render(string, 1, colors.white) self.rect = text.get_rect() self.rect.topleft = (250,10) globalvars.screen.blit(text, self.rect.topleft)
def shoot(self, force_shot=False): #Force shot tells this to shoot even if a target #is not obviously in view. NPC's will not take such wild shots. #Capital ship initially has 3 guns. one on top, one in front, and one on bottom. for i in range(len(self.weapons)): #If weapon is cool and nearest target is in range then fire. if not self.targets[i] is None and \ self.weapons[i].cooldown == 0 and \ cygeometry.distance(self.targets[i].lead_indicator, self.gunlocs[i]) < \ self.weapons[i].weapon_range: angle = angleFromPosition(self.gunlocs[i], self.targets[i].lead_indicator) self.weapons[i].shoot(angle)
def resetWarps(self): self.warps = [] #Place warp portals for c in self.connections: node_id, location = c #Get the slope of the line from self.loc to this other warp angle = angleFromPosition(self.loc, location) scaledDistance = cygeometry.distance(self.loc, location) * WARP_PORTAL_SCALING #print scaledDistance #TESTING x,y = translate(self.loc, angle, scaledDistance) temp = objInstances.WarpPortal(x=x, y=y, destinationNode=node_id, method=globalvars.scenario_manager.goToInfiniteSpace) self.warps.append(temp)
def getCoordsNearLoc(loc, mindist, xmax, ymax): '''Returns random coordinates in a rectangle centered at loc where x is between -xmax and xmax and y is between -ymax and ymax but not within mindist of the loc. This is used for the asteroids, gemwild, and race scenarios.''' x = rd.randint(-xmax, xmax)+loc[0] y = rd.randint(-ymax, ymax)+loc[1] #Select a random direction in which to push away any x,y that is too close to loc adjust = adjustments[ rd.randint(0,len(adjustments)-1) ] dist = cygeometry.distance((x,y), loc) if dist < mindist: #Push x,y away from loc x = x+adjust[0]*(mindist-int(dist)) y = y+adjust[1]*(mindist-int(dist)) return x,y
def generateGalaxy(self, seed=0, nodecount=10, minimumNodeDist=40): '''Post: randomly populates self.nodes.''' maxNeighbors = 6 #Reset nodes self.nodes = [] rd.seed(seed) #Randomly create locations for each node. for i in range(nodecount): x = rd.randint(globalvars.MENU_PADDING+globalvars.MENU_BORDER_PADDING, globalvars.WIDTH-globalvars.MENU_PADDING-globalvars.MENU_BORDER_PADDING) y = rd.randint(globalvars.MENU_PADDING+globalvars.MENU_BORDER_PADDING, globalvars.HEIGHT-globalvars.MENU_PADDING-globalvars.MENU_BORDER_PADDING) self.nodes.append(Node(i, x, y)) #Sort the nodes by x sortednodes = sorted(self.nodes, key=lambda n: n.x, reverse=True) #Remove nodes that are too close anyTooClose = True while anyTooClose: anyTooClose = False #Check for pairs of nodes that are too close together and remove one of them. for i in xrange(nodecount-1): for j in xrange(i+1, nodecount): if cygeometry.distance(sortednodes[i].loc, sortednodes[j].loc) < minimumNodeDist: anyTooClose = True sortednodes.pop(j) nodecount -= 1 break if anyTooClose: break #print 'Final node count is '+str(nodecount) #TESTING #Re-id the nodes to ensure that a node with id 0 exists. #This must be done before connecting the nodes. for i in xrange(len(sortednodes)): sortednodes[i].id = i #Connect each node only to its closest maxNeighbors neighbors. closestsNeighbors = [] #Store pairs of neighbor ids and distances for i in xrange(len(sortednodes)): #Populate closestsNeighbors with #maxNeighbors - len(sortednodes[i].connections) connections closestNeighbors = [] for j in xrange(len(sortednodes)): #Get the distance between the nodes dist = cygeometry.distance(sortednodes[i].loc, sortednodes[j].loc) #Determine how full of connections the current node is vacancies = maxNeighbors - len(sortednodes[i].connections) if vacancies == 0: break #If node i is not the same as node j and #node i is not already connected to node j and #(closestsNeighbors is not yet full or #node j is closer to node i than any other node in closetsNeighbors) #Then replace the furthest node in closestNeighbors with node j #and sort closestsNeighbors. if i != j and\ not sortednodes[i].alreadyConnected(sortednodes[i].id) and\ ((len(closestNeighbors) < vacancies) or\ (dist < closestNeighbors[0][1])): #If closestNeighbors is not yet full if len(closestNeighbors) < vacancies: closestNeighbors.append((j, dist)) else: #Then replace the furthest node in closestNeighbors with node j closestNeighbors[0] = (j, dist) #and sort closestsNeighbors by distance. closestNeighbors = sorted(closestNeighbors, reverse=True, key=lambda neighbor: neighbor[1]) #Connect all the closest neighbors for cn in closestNeighbors: sortednodes[i].addConnection(sortednodes[cn[0]].id, sortednodes[cn[0]].loc) sortednodes[cn[0]].addConnection(sortednodes[i].id, sortednodes[i].loc) #Copy all the final nodes to the self.nodes self.nodes = sortednodes #Randomly remove between 0 and (the number of connections -2) connections for sn in self.nodes: toRemove = rd.randint(0, len(sn.connections)-2) for _ in range(toRemove): removeIndex = rd.randint(0, len(sn.connections)-1) removed = sn.connections.pop(removeIndex) otherEndOfConnection = self.getNode(removed[0]) otherEndOfConnection.removeConnection(sn.id) #Create list of connections without duplicates. self.connections = [] for n in self.nodes: for c in n.connections: #Only add connections in one direction to prevent duplicates. if c[0] > n.id: otherend = self.getNode(c[0]) self.connections.append([n.x, n.y, otherend.x, otherend.y]) #Scale pirate difficulty with distance from node 0 where the player starts. #Go through all nodes and get their distance from node 0 where the player will start #Set the pirate strength of the node based on its distance from the player's start node. #Treat globalvars.WIDTH as roughly the maximum distance from node 0. for n in self.nodes: d = cygeometry.distance(n.loc, self.nodes[0].loc) percent = d / float(globalvars.WIDTH) n.setPiracyStrength(percent/2.0, min(1.0, percent)) for n in self.nodes: n.resetWarps() self.player_node = self.getNode(0)
def update(self): ''' ''' #Distance to target self.dtt = cygeometry.distance(globalvars.player.rect.center, self.target.rect.center)
def setDestinationNode(self, nodeid): '''Chart a shortest path using breadth first search to the node with the given id. Set the player's destination to be the path. Get the very first node in the path and point an arrow to it to lead the player in the right direction.''' #End conditions failure = False #But this can be true if we run out of destinations to append. #There are disconnects in the graph. success = False #This is set true when we find the destination #Append as tuples all nodes reachable from player's current node along with the distance to them. visited_node_ids = [globalvars.player.nodeid] old_bfs_array = [] new_bfs_array = [] current_node = globalvars.galaxy.player_node for connectid,location in current_node.connections: #If connectid is the destination then we can shortcircuit here if connectid == nodeid: globalvars.player.destinationNode = [nodeid] new_bfs_array = [] success = True break if not connectid in visited_node_ids: #Calculate the distance (aka cost) cost = cygeometry.distance(current_node.loc, location) visited_node_ids.append(connectid) #Append a tuple where the first element is a path in the form of #an array of node ids and the second element is the cost of the path. new_bfs_array.append(([connectid], cost)) while not success and not failure: #Sort by shortest path when updating the arrays old_bfs_array = sorted(new_bfs_array, key=lambda pair: pair[1]) new_bfs_array = [] #We may exhaust all the connections if the destination is not #connected to the player's location. if len(old_bfs_array) == 0: failure = True continue #Then for each node by id in the current list for path, cost in old_bfs_array: if success == True: break #Get the last node in the path current_node = globalvars.galaxy.getNode(path[-1]) #For each of the node's neighbors for connectid,location in current_node.connections: #If connectid is the destination then we can shortcircuit here if connectid == nodeid: path.append(nodeid) globalvars.player.destinationNode = path success = True break #skip if the neighbor has already been considered #otherwise add node+neighbor + the sum of the distances to a new list elif not connectid in visited_node_ids: #Calculate the distance (aka cost) extra_cost = cygeometry.distance(current_node.loc, location) visited_node_ids.append(connectid) #Append a tuple where the first element is a path in the form of #an array of node ids and the second element is the cost of #the path. new_bfs_array.append((path+[connectid], cost+extra_cost)) #If we failed to find a path, return false if failure: return False #Otherwise, make a new arrow point to the first warp point on the path and remove any old arrows. self.resetArrow() return True #Indicate that the destination was successfully set.
def setClosestSpritesAlt(): """Pre: Post: For all ships in the whiskerables sprite list, the closest sprite and the distance to that sprite is set. This is used for helping NPC ships avoid collisions.""" # Get all the whiskerable sprites in an array sprite_list = globalvars.whiskerables.sprites() # Sort them by their bottom point sprite_list = sorted(sprite_list, key=lambda c: c.rect.topleft[1]) # For each sprite... for i in xrange(len(sprite_list)): # Call the sprite A A = sprite_list[i] # only ships can avoid objects. so if A is not a ship then skip it. if ( A.is_a != globalvars.SHIP ): # TODO this could be more efficient by keeping another group that is just ships. Of course there is a cost there. It might be worth profiling at some point to see if this is better or another group that is just non-player ships is better. continue # Reset closest sprite and the distance to that sprite. Sprites # further than this distance will be ignored. closest_sprite = None danger_factor = -10000000.0 dist_to_sprite = 10000000.0 # Look forward through the sprite list. # once the y-coordinates are more different than minsafedistance then we can stop # looking through the list because it is sorted by y values of the sprites. # Update sprite that has ... # high radius - big and hard to avoid # low distance - close # narrow angle from our current heading - on a collision course # get sprite with largest radius of sprite - (angle to sprite + distance to sprite) # TODO you might want to change this formula later on. if i + 1 < len(sprite_list): for j in xrange(i + 1, len(sprite_list)): B = sprite_list[j] # Get angle to other sprite's center angle = abs(A.getAngleToTarget(target=B.rect.center)) # If angle to other sprite is over 90 degrees, then ignore it if angle >= 90: continue # Get distance to other sprite dist = cygeometry.distance(A.rect.center, B.rect.center) - (B.collisionradius + A.collisionradius) # Calculate the danger factor for this sprite temp = B.collisionradius - (angle * dist) # TODO # If this sprite represents the biggest danger so far, then update if temp > danger_factor: danger_factor = temp closest_sprite = B dist_to_sprite = dist # If distance is the smallest we've seen so far, then update least dist. # if the difference between the y values is already larger than # minsafedistance then stop this loop if globalvars.MINSAFEDIST < B.rect.topleft[1] - A.rect.topleft[1]: break # Then do the same looking backward through the sprite list. if i - 1 > 0: for j in xrange(i - 1, 0, -1): B = sprite_list[j] # Get angle to other sprite's center angle = abs(A.getAngleToTarget(target=B.rect.center)) # If angle to other sprite is over 90 degrees, then ignore it if angle >= 90: continue # Get distance to other sprite dist = cygeometry.distance(A.rect.center, B.rect.center) - (B.collisionradius + A.collisionradius) # Calculate the danger factor for this sprite temp = B.collisionradius - (angle * dist) # TODO # If this sprite represents the biggest danger so far, then update if temp > danger_factor: danger_factor = temp closest_sprite = B dist_to_sprite = dist # If distance is the smallest we've seen so far, then update least dist. # if the difference between the y values is already larger than # minsafedistance then stop this loop. # In this loop we reverse the subtraction order because we sorted ascending. if globalvars.MINSAFEDIST < A.rect.topleft[1] - B.rect.topleft[1]: break # Set sprite A's closest sprite and the distance to that sprite. A.setClosest(closest_sprite, dist_to_sprite)
def run(countdown=-1, track_FPS=False, track_efficiency=False): """Runs the game.""" take_screenshot = False # For more efficient animations dirty_rects = [] # Places where we are drawing new images dirty_covers = [] # Places where we are covering old images with background start_time = datetime.datetime.now() tick_length = 0 offsetx = 0 offsety = 0 offset = offsetx, offsety # key polling: # Use this to keep track of which keys are up and which # are down at any given point in time. keys = [] for _i in range(322): keys.append(False) # mouse is [pos, button1, button2, button3,..., button6]. # new Apple mice think they have 6 buttons. # Use this to keep track of which mouse buttons are up and which # are down at any given point in time. # Also the tuple, mouse[0], is the current location of the mouse. mouse = [(0, 0), 0, 0, 0, 0, 0, 0] # pygame setup: clock = pygame.time.Clock() running = True # Gather some data on efficiency by measuring the relationship between time burned in the ticks method and the number of objects to update on the screen globalvars.time_lapses = [0 for _ in xrange(1900)] globalvars.dirty_rect_size = [0 for _ in xrange(1900)] efficiency_index = 0 # The in-round loop (while player is alive): while running: # Use this for more accurate profiling: if countdown != -1: countdown -= 1 if countdown < 0: exit() # Draw everything on the screen. Do so either using dirty rects or just by # redrawing the whole screen. Dirty rects are usually more efficient. if update_mechanism == DIRTY: pygame.display.update(dirty_rects) pygame.display.update(dirty_covers) elif update_mechanism == FLIP: pygame.display.flip() else: above_threshold = len(dirty_rects) + len(dirty_covers) > UPDATE_THRESHOLD if above_threshold: pygame.display.flip() else: pygame.display.update(dirty_rects) pygame.display.update(dirty_covers) # Cover previous dirty rectangles and empty them out. if update_mechanism != FLIP: # Cover the dirty rectangles with background coverAll(dirty_rects) # Copy dirty rects into dirty covers dirty_covers = dirty_rects[:] # Empty dirty rects dirty_rects = [] if take_screenshot: pygame.image.save(globalvars.screen, "screenshots/" + str(rd.randint(0, 999999999)) + ".jpeg") take_screenshot = False if track_FPS: # Calculate how long we took in the above loop to estimate the number of frames per second. # We want time_lapse to be stable. time_lapse = datetime.datetime.now() - start_time # Used for calculating actual frames per second in # order to determine when we are dropping frames # so that efficiency improvements can be made. start_time = datetime.datetime.now() # Alert user if fraps drops below half the desired threshold. if float(time_lapse.microseconds) / 1000000.0 > (2.0 / float(globalvars.FPS)): print "\nWarning: frames dropping." print "Goal frames per second is " + str(globalvars.FPS) + ". Current is " + str( 1.0 / (float(time_lapse.microseconds) / 1000000.0) )[ :2 ] # Cut off decimal because I don't care. print "Sizes of Sprite Groups follows:" print "Tangibles: " + str(len(globalvars.tangibles)) print "Intangibles_bottom: " + str(len(globalvars.intangibles_bottom)) print "Intangibles_top: " + str(len(globalvars.intangibles_top)) print "Whiskerables: " + str(len(globalvars.whiskerables)) if track_efficiency: tick_length = datetime.datetime.now() # frame maintainance: # aim for globalvars.FPS frames per second. clock.tick(globalvars.FPS) if track_efficiency: # We want this value, tick_length, to be large because that means the processor # is taking long rests because everything else is happening so efficiently. tick_length = datetime.datetime.now() - tick_length # Gather some efficiency data globalvars.time_lapses[efficiency_index] = tick_length.microseconds globalvars.dirty_rect_size[efficiency_index] = len(dirty_rects) + len(dirty_covers) efficiency_index = (efficiency_index + 1) % len(globalvars.time_lapses) # Display the panel if not globalvars.menu.main_panel is None: globalvars.menu.main_panel.draw() pygame.display.flip() # Check for another key press to remove the panel. for event in pygame.event.get(): # Check for event m key pressed to remove the menu. if event.type == pygame.KEYDOWN and event.key == 109: globalvars.menu.main_panel = None redrawWholeBackground() pygame.display.flip() break # Panel event handeling can make the panel itself None so we have # to check if the panel has become None for every event. If the # panel has become None we break and ignore further input events. elif globalvars.menu.main_panel is None: redrawWholeBackground() pygame.display.flip() break # Pass all other events to the panel else: globalvars.menu.main_panel.handleEvent(event) # Skip all the rest while displaying the menu. # This effectively pauses the game. continue # event polling: # See what buttons may or may not have been pushed. for event in pygame.event.get(): if event.type == pygame.QUIT: running = False elif event.type == pygame.MOUSEBUTTONDOWN: mouse[event.button] = 1 mouse[0] = event.pos # Also causes shooting globalvars.player.shoot(force_shot=True) elif event.type == pygame.MOUSEBUTTONUP: mouse[event.button] = 0 # elif event.type == pygame.MOUSEMOTION: # mouse[0] = event.pos elif event.type == pygame.KEYDOWN: keys[event.key % 322] = 1 # print "TODO TESTING: key press "+str(event.key) # Respond to key taps. # Keys that we want to respond to holding them down # will be dealt with below. if event.key == 273 or event.key == 119: # Pressed up arrow or w key # increase speed by a fraction of max up to max. globalvars.player.targetSpeed = min( globalvars.player.maxSpeed, globalvars.player.targetSpeed + globalvars.player.maxSpeed * globalvars.player.speedIncrements, ) elif event.key == 274 or event.key == 115: # Pressed down arrow or s key # decrease speed by a fraction of # max down to zero. globalvars.player.targetSpeed = max( 0, globalvars.player.targetSpeed - globalvars.player.maxSpeed * globalvars.player.speedIncrements, ) elif event.key == 27: # escape key or red button running = False elif event.key == 109: # m key # If player is dead, access the restart panel, not the testing panel. if globalvars.player.isDead(): globalvars.menu.setRestartPanel() elif globalvars.disable_menu: globalvars.menu.setPausePanel() else: globalvars.menu.setShipPanel() continue elif event.key == 98: # b key globalvars.player.parkingBrake() elif event.key == 113: # q key # Obliterate destination. # Change to free flight. globalvars.player.killDestination() elif event.key == 116: # t key # shoot a bunch of hit box testers # in towards the player print "Width: " + str(globalvars.player.image.get_width()) + " vs " + str( globalvars.player.rect.width ) print "Height: " + str(globalvars.player.image.get_height()) + " vs " + str( globalvars.player.rect.height ) test.hitBoxTest(globalvars.player.rect.center) elif event.key == 108: # l key import profilingObject temp = profilingObject.CollisionAvoidanceTester() globalvars.intangibles_bottom.add(temp) elif event.key == 111: # o key import profilingObject temp = profilingObject.ProfilingObject() globalvars.intangibles_bottom.add(temp) # Begin profiling import cProfile cProfile.runctx("run(track_efficiency=True)", globals(), None, "profiling/game.run.profile") exit() elif event.key == 121: # y key # Profile lots of methods, but not game.run() profileEverything(offset) elif event.key == 117: # u key # Profile game.run() import cProfile print "Profiling game.run(countdown=1800). " + "Press escape to quit early. " + "Profiling will stop automatically after 30 seconds." # Run for 1800 frames (or 30 seconds assuming 60 frames per second. cProfile.runctx("run(countdown=1800)", globals(), None, "profiling/game.run.profile") exit() elif event.key == 105: # i key # Record and display efficiency data. # We want time_lapses to be large. # Write efficiency data to file. # filehandle = open('profiling/efficiency_data.txt', 'w') # for i in xrange(len(globalvars.time_lapses)): # filehandle.write(str(globalvars.time_lapses[i])+\ # ', '+str(globalvars.dirty_rect_size[i])+'\n') # filehandle.close() # Plot the data then exit import matplotlib.pyplot as plt plt.plot(globalvars.time_lapses, "ro") plt.plot(globalvars.dirty_rect_size) plt.show() plt.plot(globalvars.dirty_rect_size) plt.show() exit() elif event.key == 47: # forward slash (question mark # without shift) key. # Useful for querying one time info. print "Print player destination: " + str(globalvars.player.destx) + "," + str( globalvars.player.desty ) elif event.key == 104 or event.key == 304: # "h key" lower or upper case. # Display help menu. globalvars.menu.setHelpPanel() elif event.key == 112: # p key - takes a screenshot take_screenshot = True if event.key == 120: # Pressed x key globalvars.player.shoot(force_shot=True, weapon=globalvars.player.missile) if event.key == 122: # Pressed z key globalvars.player.shoot(force_shot=True, weapon=globalvars.player.mine) elif event.type == pygame.KEYUP: # Keep track of which keys are no longer # being pushed. keys[event.key % 322] = 0 ##This will make the player move towards the mouse ##without any clicking involved. ##Set player destination to current mouse coordinates. if mouse[1]: x, y = pygame.mouse.get_pos() x += offsetx y += offsety globalvars.player.setDestination((x, y)) # Respond to key holds. # Keys that we want to respond to tapping them # will be dealt with above. if keys[276] or keys[97]: # Pressed left arrow or a key globalvars.player.turnCounterClockwise() elif keys[275] or keys[100]: # Pressed right arrow or d key globalvars.player.turnClockwise() # This is not part of the above else if. # You can shoot and turn at the same time. if keys[32] or keys[99]: # Pressed space bar or c key # Force shot tells this to shoot even if a target # is not obviously in view. NPC's will not take such wild shots. globalvars.player.shoot(force_shot=True) # Check all collisions collisionHandling() # If arena is non-zero, then make sure player and all # whiskerables are within it if globalvars.arena > 0: # The inner concentric ring bounces the player back # towards center (don't actually bounce, just change # angle directly towards center. The outer # concentric ring, defined by distance from center # plus object radius will bounce asteroids in a # semi random direction towards the center-ish area. # Make sure player is within arena. # If not, change player's heading to be towards 0,0 if cygeometry.distance(globalvars.player.rect.center, (0.0, 0.0)) > globalvars.arena: globalvars.player.theta = geometry.angleFromPosition(globalvars.player.rect.center, (0.0, 0.0)) globalvars.player.updateImageAngle() # Check each whiskerable and if it is more than # arena + diameter from center, then change its # angle to point somewhere within the arena too. for w in globalvars.whiskerables: if cygeometry.distance(w.rect.center, (0.0, 0.0)) > globalvars.arena + w.collisionradius: # Whiskerables reflect randomly # off arena boundaries towards a # point somewhere within 3/4 the # center of the arena. limit = 3 * globalvars.arena / 4 x = rd.randint(-limit, limit) y = rd.randint(-limit, limit) if not w.direction is None: # Asteroids distinguish between their orientation, # theta, and direction of movement, direction. # Thus we want direction changed, not theta. w.direction = geometry.angleFromPosition(w.rect.center, (x, y)) else: w.theta = geometry.angleFromPosition(w.rect.center, (x, y)) # update all sprites: # First tell the ships what is closest to them # so that they can avoid collisions setClosestSprites() # setClosestSpritesAlt() #TODO - This one should be more efficient and superior to the other, but it's not. # Update all the sprites globalvars.intangibles_bottom.update() globalvars.tangibles.update() globalvars.intangibles_top.update() # Get the offset based on the player location. offsetx = globalvars.player.rect.centerx - globalvars.CENTERX offsety = globalvars.player.rect.centery - globalvars.CENTERY offset = offsetx, offsety if update_mechanism == FLIP: # Draw the background over the screen. redrawWholeBackground() # Draw all the things that are on the screen for x in globalvars.intangibles_bottom: if x.isOnScreen(offset): x.draw(offset) for x in globalvars.tangibles: if x.isOnScreen(offset): x.draw(offset) for x in globalvars.intangibles_top: if x.isOnScreen(offset): x.draw(offset) else: # Put on screen rects in dirty rects for x in globalvars.intangibles_bottom: if x.isOnScreen(offset): dirty_rects.append(x.getDirtyRect(offset)) x.draw(offset) for x in globalvars.tangibles: if x.isOnScreen(offset): dirty_rects.append(x.getDirtyRect(offset)) x.draw(offset) for x in globalvars.intangibles_top: if x.isOnScreen(offset): dirty_rects.append(x.getDirtyRect(offset)) x.draw(offset) # Draw player last so the background isn't drawn overtop of the player. globalvars.player.playerUpdate() if not globalvars.player.isDead() and globalvars.player.fuel > 0: if update_mechanism == DIRTY: dirty_rects.append(globalvars.player.getDirtyRect(offset)) elif update_mechanism == FLIP: globalvars.player.drawAt((globalvars.CENTERX, globalvars.CENTERY)) else: dirty_rects.append(globalvars.player.getDirtyRect(offset)) if above_threshold: globalvars.player.drawAt((globalvars.CENTERX, globalvars.CENTERY)) else: # Make player death kick the player back to a menu where player # can choose to restart. Display a death screen then. Reset the # scenario and everything else. # Countdown before kicking player back to menu globalvars.deathcountdown -= 1 if globalvars.deathcountdown < 0: globalvars.menu.setRestartPanel()