def update(self): self.image = self.bkground.copy() fuel_msg = 'FUEL: %s (%s)' % (str(U.rint(self.plane.fuel)).zfill(3), str(U.rint(self.plane.fuel_delta)).zfill(3)) color = S.DARK_GREEN if self.plane.fuel > 100 else S.KO_COLOUR img = self.render_text('small', color, fuel_msg) self.image.blit(img, self.fuel_data_position) self.rect.y += cmp(self.target_y, self.rect.y) * \ min(3, abs(self.rect.y - self.target_y)) # Master alarm if self.plane.flags.collision or self.plane.tcas.state \ or not self.plane.fuel: img = self.master_alarm_on else: img = self.master_alarm_off self.image.blit(img, self.master_alarm_position) # Expedite arrow if self.plane.pilot.status['haste'] in ('expedite', 'emergency'): img = self.expedite_on else: img = self.expedite_off self.image.blit(img, self.expedite_position) # Drop if self.plane.fuel_delta > 0: img = self.drop_green elif self.plane.fuel: img = self.drop_yellow else: img = self.drop_red self.image.blit(img, self.drop_position) # Last order text = self.plane.pilot.order_being_processed if text: text = self.render_text('small', S.GRAY, text) self.image.blit(text, self.order_being_processed_position)
def testRint(self): ''' rint - round to integer and typecast to int ''' self.assertEqual(U.rint(0.35), 0) self.assertEqual(U.rint(0.835), 1) self.assertIsInstance(U.rint(3465657.4545), int)
def place(self): ''' Place the tag according to the angle and radius properties. Return True if the tag is entirely on radar, False otherwise. ''' cx, cy = self.plane.trail[0] rad = radians(self.angle) ox = U.rint(cos(rad) * self.radius) oy = -U.rint(sin(rad) * self.radius) #minus because of screen coordinates x, y = cx + ox, cy + oy self.rect = U.get_rect_at_centered_pos(self.image, (x, y)) return self.radar_rect.contains(self.rect)
def __generate_flight_plan(self): ''' Return intial position and velocity 3D vectors plus the origin and destination identifiers (airport or gate) for a plane. It also returns the initial amount of onboard fuel and the fuel_efficiency values. ''' #TODO: foresee port to port and gate to gate #TODO: foresee a configurable ratio between ports and air #FIXME: consider passing gate and airport objects directly as orig/dest # Establish type of origin if self.__check_grounded_is_ok(): options = ['gates', 'airports'] random.shuffle(options) type_ = options.pop() else: type_ = 'gates' if type_ == 'gates': entry_data_gates = self.__entry_data['gates'][:] random.shuffle(entry_data_gates) # Attempt to make planes enter the aerospace without making them # collide with each other while entry_data_gates: orig, pos, vel, levels = entry_data_gates.pop() levels = levels[:] while levels: # Prevent in-place modification on __entry_data pos = pos.copy() pos.z = levels.pop() if not self.gamelogic.aerospace.check_proximity(pos): vel = vel.copy() tmp = random.choice(self.scenario.airports) dest = tmp.iata fuel = U.rint(U.ground_distance(pos, tmp.location)* 4*self.fuel_per_metre) return dict(origin=orig, position=pos, velocity=vel, destination=dest, fuel=fuel, fuel_efficiency=self.fuel_per_metre) elif type_ == 'airports': random.shuffle(self.__entry_data['airports']) orig, pos, vel = self.__entry_data['airports'][0] pos = pos.copy() vel = vel.copy() tmp = random.choice(self.scenario.gates) dest = tmp.name fuel = U.rint(U.ground_distance(pos, Vector3(*tmp.location))* 4*self.fuel_per_metre) return dict(origin=orig, position=pos, velocity=vel, destination=dest, fuel=fuel, fuel_efficiency=self.fuel_per_metre) return False
def check(self, commands): """ Check if the requested commands can be performed or not. Return True or an error message. ``commands`` is a dictionary in the form: {command_name = (args, flags)}. Return True or a message error. """ pi = self.pilot pl = self.plane aspace = self.plane.aerospace cnames = set(commands.keys()) # Reject orders if imminent collision if pl.tcas.state == True: return "Mayday, mayday!!!" # Reject orders other than SQUAWK if plane is locked if pl.flags.locked and cnames - set("SQUAWK"): return "You can only SQUAWK this plane." # Reject orders if busy if pl.flags.busy == True and cnames - set(["ABORT", "SQUAWK"]): return "We are still performing the previous command." # Reject orders other than TAKEOFF or SQUAWK if plane is on ground if pl.flags.on_ground and not ("TAKEOFF" in cnames or cnames == set(["SQUAWK"])): return "We should probably TAKEOFF first, uh?" # Reject orders if they contain an altitude beyond max_altitude if "ALTITUDE" in cnames and commands["ALTITUDE"][0][0] > pl.max_altitude: return "The target altitude is above the maximum one for our " + "aircraft." # Reject orders if they contain a speed out of speed limits (min/max) if "SPEED" in cnames and not self.plane.min_speed <= commands["SPEED"][0][0] <= self.plane.max_speed: mi = U.rint(self.plane.min_speed * 3.6) ma = U.rint(self.plane.max_speed * 3.6) return "Our speed must be between %d and %d kph!" % (mi, ma) # Reject LAND order if unexisting airport or runway if "LAND" in cnames: check = pi.navigator.check_existing_runway(*commands["LAND"][0]) if check != True: return check # Reject TAKEOFF if one of the various no-go conditions is true if "TAKEOFF" in cnames: args, flags = commands["TAKEOFF"] if not pl.flags.on_ground: return "We can't take off if we are already airborne!" port = aspace.airports[pl.origin] if args[0] not in port.runways.keys(): return "Uh? What runway did you say we should taxi to?" runway = port.runways[args[0]] twin = port.runways[runway["twin"]] if not aspace.runways_manager.check_runway_free(port, twin): return "Negative, that runway is currently in use." # If nothing else have stopped us... return True
def draw(self, surface): ''' Blit self on radar surface. ''' x, y = U.sc(self.location) # GATE # In order to facilitate blitting information on the orientation of the # gate, we create the image already rotated 90° clockwise by swapping # width and height... gate_width_px = U.rint(self.width / S.METRES_PER_PIXEL) gate_length_px = S.RADAR_RECT.h / 4 aaf = 5 #anti-alias factor g_img = pygame.surface.Surface((gate_length_px*aaf, gate_width_px*aaf), SRCALPHA) # BOUNDARIES OF THE GATE pygame.draw.line( g_img, S.GRAY, (0, aaf), (gate_length_px*aaf, aaf), aaf) pygame.draw.line( g_img, S.GRAY, (0, gate_width_px*aaf-aaf), (gate_length_px*aaf, gate_width_px*aaf-aaf), aaf) # INFO ON ORIENTATION and FLIGHT LEVELS fl = lambda x : str(x/100).zfill(2) lines = ['H:' + str(self.heading).zfill(3), 'B:' + fl(self.bottom), 'T:' + fl(self.top)] fontobj = pygame.font.Font(S.MAIN_FONT, S.HUD_INFO_FONT_SIZE * aaf) label = U.render_lines(fontobj, lines, S.GRAY) label = label.subsurface(label.get_bounding_rect()) w, h = label.get_size() ypsilon = U.rint(gate_width_px*aaf/2.0-h/2) g_img.blit(label, (0, ypsilon)) g_img.blit(label, (gate_length_px*aaf-w, ypsilon)) # tranformation and blitting rotang = 90 if 0<= self.heading < 180 else 270 g_img = pygame.transform.rotate(g_img, rotang-self.heading) g_img = g_img.subsurface(g_img.get_bounding_rect()).copy() r = g_img.get_rect() g_img = pygame.transform.smoothscale(g_img, (U.rint(r.w*1.0/aaf), U.rint(r.h*1.0/aaf))) g_rect = g_img.get_rect() surface.blit(g_img, (x-g_rect.centerx, y-g_rect.centery)) # LABEL fontobj = pygame.font.Font(S.MAIN_FONT, S.HUD_INFO_FONT_SIZE) label = fontobj.render(self.name, True, S.RED) w, h = label.get_size() signed_offset = lambda n : cmp(1,n)*w x += (signed_offset(x) if self.side <=0 else 0) - w/2 y += (signed_offset(y) if self.side >=0 else 0) - h/2 surface.blit(label, (x,y))
def __init__(self, gamelogic): super(Score, self).__init__() self.gamelogic = gamelogic self.image = pygame.surface.Surface(S.SCORE_RECT.size, SRCALPHA) self.rect = self.image.get_rect() self.score = self.gamelogic.score self.fontobj = pygame.font.Font(S.MAIN_FONT, U.rint(self.rect.h * 0.8))
def draw(self, surface): pos = U.sc(self.location) pygame.draw.circle(surface, S.GRAY, pos, 2) pygame.draw.circle(surface, S.GRAY, pos, 6, 1) fontobj = pygame.font.Font(S.MAIN_FONT, S.HUD_INFO_FONT_SIZE) label = fontobj.render(self.id, True, S.BLUE) label = label.subsurface(label.get_bounding_rect()).copy() w, h = label.get_size() x, y = pos # In order to keep the crammed central space free, beacons labels are # always placed towards the edges of the radar screen, if possible. offsets = [U.rint(6+w/3), -U.rint(6+w/3)-w] index = x < S.RADAR_RECT.w/2 if not (0 < x+offsets[index] and x+offsets[index]+w < S.RADAR_RECT.w): index = not index surface.blit(label, (x+offsets[index], y-h/2))
def __draw_radar_aid(self): ''' Draw the radar aid. ''' if not S.RADAR_AID: return centre = U.sc((S.RADAR_RANGE, S.RADAR_RANGE)) # Find how many metres a step consist of, making sure the final value # is sensible (not 12735.5, for example...) sensibles = [n*1000 for n in (1, 5, 10, 20, 25, 50, 100)] attempts = [S.RADAR_RANGE * 2 / n for n in sensibles] closest = min(attempts, key = lambda x : abs(x-S.RADAR_AID_STEPS)) metres_per_step = sensibles[attempts.index(closest)] if S.RADAR_AID == 'circles': step_range = range(metres_per_step, U.rint(S.RADAR_RANGE*2**0.5), metres_per_step) for radius in step_range: pygame.draw.circle(self.surface, S.RADAR_AID_COLOUR, centre, U.rint(radius/S.METRES_PER_PIXEL), 1) elif S.RADAR_AID in ('grid', 'crosses', 'dots'): # In the following line: since division is integer division, this # will ensure that on marking will pass from the radar position first = S.RADAR_RANGE - S.RADAR_RANGE/metres_per_step*metres_per_step step_range = range(first, S.RADAR_RANGE * 2 + metres_per_step, metres_per_step) draw = lambda fm, to : pygame.draw.aaline(self.surface, S.RADAR_AID_COLOUR, U.sc(fm), U.sc(to)) for step in step_range: if S.RADAR_AID == 'grid': draw((step, 0), (step, S.RADAR_RANGE*2)) draw((0, step), (S.RADAR_RANGE*2, step)) elif S.RADAR_AID in ('dots', 'crosses'): for step2 in step_range: if S.RADAR_AID == 'dots': pygame.draw.circle(self.surface, S.RADAR_AID_COLOUR, U.sc((step, step2)), 2) elif S.RADAR_AID == 'crosses': x, y = step, step2 offset = U.rint(metres_per_step / 16.0) draw((x-offset, y), (x+offset, y)) draw((x, y-offset), (x, y+offset)) else: msg = 'Wrong value of `RADAR_AID` in config file!' raise BaseException(msg) S.RADAR_MARKING = metres_per_step
def update(self): pl = self.plane render = self.fontobj.render lines = [] # LINE 1 = Airplane code lines.append(pl.icao.upper()) # LINE 2 = Altitude, speed # Remove last digit, add variometer alt = str(U.rint(pl.altitude/100.0)) alt += pl.variometer # Convert m/s to kph AND remove last digit, add accelerometer spd = str(U.rint(pl.speed*3.6)) spd += pl.accelerometer lines.append('%s%s' % (alt,spd)) self.image = self.render_lines(lines) self.rect = self.image.get_rect() self.angle = self.default_angle self.radius = self.default_radius
def update(self): STEP = U.rint(S.PING_PERIOD / 1000.0 + 1) #arbitrary: ping in sec + 1 self.image.fill(S.BLACK) delta = U.rint(self.gamelogic.score) - self.score if abs(delta) < STEP: colour = S.WHITE variation = delta elif delta > 0: colour = S.OK_COLOUR variation = STEP else: colour = S.KO_COLOUR variation = -STEP self.score += variation score = str(self.score).zfill(6) score_img = self.fontobj.render(score, True, colour) score_img.subsurface(score_img.get_bounding_rect()).copy() pos = U.get_rect_at_centered_pos(score_img, self.rect.center) self.image.blit(score_img, pos)
def process_commands(self, commands): ''' Process commands. This is a "subroutine" of ``execute`` which is also called by some of the procedures. This is such that is possible to process commands without triggering score events and setting flags (as it happens with ``execute()``). This method will silently pass if the command name has not been recognised. This is to allow the method to process set of commands that also contains *procedures* (such LAND, TAKEOFF, etc...). ''' pi = self.pilot pl = self.plane tc = self.pilot.target_conf for cname, (args, flags) in commands.items(): log.info('%s executes: %s' % (pl.icao, ' '.join((cname, repr(args), repr(flags))))) # PROCESS COMMANDS # Since flags are "universal" across commands (they all do the same # thing if they are called the same), it is possible to process # them separately. if cname == 'HEADING': assert len(args) == 1 tc.heading = args[0] pi.status['veer_dir'] = \ pi.navigator.get_shortest_veering_direction() elif cname == 'ALTITUDE': tc.altitude = args[0] elif cname == 'SPEED': tc.speed = args[0] elif cname == 'ABORT': self.abort() elif cname == 'SQUAWK': if pl.flags.on_ground: pi.say('Currently at airport %s, our destination is %s' % (pl.origin, pl.destination), S.OK_COLOUR) else: pi.say('Currently heading %s, our destination is %s' % (U.rint(pl.heading), pl.destination), S.OK_COLOUR) else: log.debug('process_commands() ignored: %s' % cname) # PROCESS FLAGS # Flags with the same name have the same meaning and therefore # can be processed independently from the command they are # associated with. Since they can modify the value set by their # command, they must follow the command processing if 'EXPEDITE' in flags: pi.status['haste'] = 'expedite' if 'LONG' in flags: pi.status['veer_dir'] *= -1 #invert direction
def __get_fontobj(cls, size_str): ''' Return the appropriate font object. ''' if size_str not in cls.font_objects.keys(): # set the big font size = 1 while True: fontobj = pygame.font.Font(S.MAIN_FONT, size) w,h = fontobj.render('XXX0000', True, S.WHITE).get_size() if w > S.STRIPS_RECT.w/2 - cls.offset - cls.margin: break last_ok = fontobj size += 1 cls.font_objects['large'] = last_ok # set the small font cls.font_objects['small'] = pygame.font.Font(S.MAIN_FONT, U.rint(size/3.0)) return cls.font_objects[size_str]