コード例 #1
0
	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)
コード例 #2
0
ファイル: game.py プロジェクト: nealholt/wild-black-yonder
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)
コード例 #3
0
	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
コード例 #4
0
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)
コード例 #5
0
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
コード例 #6
0
	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
コード例 #7
0
	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
コード例 #8
0
	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
コード例 #9
0
	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)
コード例 #10
0
	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)
コード例 #11
0
	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)
コード例 #12
0
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
コード例 #13
0
	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)
コード例 #14
0
	def update(self):
		''' '''
		#Distance to target
		self.dtt = cygeometry.distance(globalvars.player.rect.center, self.target.rect.center)
コード例 #15
0
	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.
コード例 #16
0
ファイル: game.py プロジェクト: nealholt/wild-black-yonder
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)
コード例 #17
0
ファイル: game.py プロジェクト: nealholt/wild-black-yonder
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()