def kill_escaped(self): ''' Kill all sprites related to a plane that left the aerospace. ''' for plane in self.__planes.values(): s = self.surface.get_rect() x, y = plane['sprites'][0].position if x < 0 or x > s.width or y < 0 or y > s.height: # This is the worst scenario msg = 'Tower? ... Tower? ... Aaaaahhhh!' event = S.PLANE_LEAVES_RANDOM colour = S.KO_COLOUR crossed = self.__get_crossed_gates(plane['plane']) # This might be better for gate in crossed: msg = 'Tower? It doesn\'t seem we are where we should...' event = S.PLANE_LEAVES_WRONG_GATE #little better! if gate.name == plane['plane'].destination and \ plane['plane'].altitude % 1000 == 0: msg = 'Thank you tower, and good bye!' colour = S.OK_COLOUR event = S.PLANE_LEAVES_CORRECT_GATE #yay! :) plane['plane'].pilot.say(msg, colour) self.gamelogic.remove_plane(plane['plane'], event) log.info('%s left aerospace under event %s' % (plane['plane'].icao, event)) log.debug('Data at exit was: %s' % plane['plane'].get_current_configuration())
def _abort_clear(self, msg): ''' Abort clear, generating all events of the case and resetting relevant variables. ''' log.info('%s aborts: %s' % (self.plane.icao, msg)) self.pilot.say('Aborting clear command: %s' % msg, S.ALERT_COLOUR) self.pilot.status['procedure'] = None self.pilot.set_target_conf_to_current() self.pilot.adjust_to_valid_FL()
def remove_plane(self, plane, event): ''' Remove a plane from the game. ''' log.info('%s removed, event is %s' % (plane.icao, event)) self.score_event(event, plane=plane) self.aerospace.remove_plane(plane) self.strips.remove_strip(plane) if event in (S.PLANE_CRASHES, S.PLANE_LEAVES_RANDOM): self.fatalities += 1
def update(self, pings): ''' Update the plane status according to the elapsed time. Pings = number of radar pings from last update. ''' burning_speed = 1 if self.pilot.status['haste'] == 'normal' else 2 initial = self.position.copy() for i in range(pings): # Pilot's updates self.pilot.update() # Compute waiting time score if not airborne # FIXME: distinguish between just landed and waiting to takeoff if self.flags.on_ground: mult = pings * S.PING_IN_SECONDS self.aerospace.gamelogic.score_event(S.PLANE_WAITS_ONE_SECOND, multiplier=mult) # Decrease fuel amount if airborne elif self.fuel > 0: dist = U.ground_distance(initial, self.position) burnt = burning_speed * dist * self.fuel_efficiency self.fuel -= burnt self.aerospace.gamelogic.score_event(S.PLANE_BURNS_FUEL_UNIT, multiplier=burnt) # Check if a fuel emergency has to be triggered. # FIXME: this is goo reason to use objects intstead of IATA/NAME try: dest_point = self.aerospace.airports[self.destination].location except KeyError: tmp = self.aerospace.gates[self.destination].location dest_point = Vector3(tmp[0], tmp[1], self.altitude) dist = U.ground_distance(dest_point, self.position) self.fuel_delta = self.fuel - (2 * dist * self.fuel_efficiency) self.dist_to_target = dist if not self.flags.fuel_emergency and self.fuel_delta < 0: log.info('%s is declaring fuel emergency' % self.icao) msg = 'Pan-Pan, Pan-Pan, Pan-Pan... We are low on fuel, ' \ 'requesting priority landing!' self.pilot.say(msg, S.KO_COLOUR) self.aerospace.gamelogic.score_event(S.EMERGENCY_FUEL) self.flags.fuel_emergency = True # Fuel has ran out if self.fuel < 0: msg = 'Mayday! Mayday! Mayday! All engines have flamed out, we ' \ 'are going down!' self.pilot.say(msg, S.KO_COLOUR) log.info('%s has ran out of fuel' % self.icao) self.fuel = 0 self.max_speed = self.min_speed * 2 max_down = self.climb_rate_limits[0] self.climb_rate_limits = [max_down, max_down / 2.0] # Update sprite self.rect = U.sc(self.position.xy) self.trail.appendleft(U.sc(self.position.xy))
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 main(): try: version = __version__ #set when package is buit @UndefinedVariable except NameError: version = '<unknown>' try: log.info('### NEW MATCH - Game version: %s ################' % version) MainWindow().main_loop() except: trace = traceback.format_exc() log.critical(trace) print trace sys.exit(1)
def update(self): ''' Operations that must be performed when the radar pings. ''' assert self.phase in (self.ACCELERATING, self.CLIMBING, self.HEADING) pl = self.plane pi = self.pilot if self.phase == self.ACCELERATING: if pl.speed > pl.landing_speed: pi.target_conf.altitude = self.target_altitude pl.flags.on_ground = False self.phase = self.CLIMBING log.info('%s is lifting off' % pl.icao) else: if pi.navigator.check_overshot(self.end_of_runway): log.info('%s crashed due to too short runway' % pl.icao) pl.terminate(S.PLANE_CRASHES) if self.phase == self.CLIMBING: if pi.navigator.check_overshot(self.end_of_runway): pi.target_conf.heading = self.target_heading self.phase = self.HEADING log.info('%s is setting post-lift-off course' % pl.icao) if self.timer <= 0 and self.phase == self.HEADING: pl.aerospace.runways_manager.release_runway(pl) pl.flags.locked = False pi.status['procedure'] = None log.info('%s has terminated takeoff, runway free' % pl.icao) self.timer -= S.PING_IN_SECONDS
def _abort_landing(self, msg): ''' Abort landing, generating all events of the case and resetting relevant variables. ''' # TODO: Introducing abort codes would simplify testing! log.info('%s aborts: %s' % (self.plane.icao, msg)) self.pilot.say('Aborting landing: %s' % msg, S.ALERT_COLOUR) # Marked runway as free if self.lander and self.lander.taxiing_data: self.pilot.aerospace.runways_manger.release_runway(self.plane) self.pilot.status['procedure'] = None self.pilot.set_target_conf_to_current() self.pilot.adjust_to_valid_FL()
def _initiate(self, commands): ''' Automatically called by ancestor class upon ``__init__`` ''' self.pilot.executer.process_commands(commands) st = self.pilot.status param = commands['CIRCLE'][0][0] if param in ('L', 'LEFT', 'CCW'): st['veer_dir'] = S.LEFT elif param in ('R', 'RIGHT', 'CW'): st['veer_dir'] = S.RIGHT else: msg = 'Unknown parameter for circle command.' raise BaseException(msg) if self._check_expedite(commands): st['haste'] = 'expedite' log.info('%s is circling %s' % (self.plane.icao, st['veer_dir']))
def _initiate(self, commands): ''' Automatically called by ancestor class upon ``__init__`` ''' # Preliminary check: is the beacon in range? point = commands['CLEAR'][0][0] veer_type = 'expedite' if 'EXPEDITE' in commands['CLEAR'][1] else None if not self.pilot.navigator.check_reachable(point, veer_type) \ and 'SPEED' not in commands \ or ('SPEED' in commands \ and commands['SPEED'][0][0] > self.plane.speed): msg = 'The target waypoint is too close for us to fly over it!' return self._abort_clear(msg) if self._check_expedite(commands): self.pilot.status['haste'] = 'expedite' # Head towards the point commands['HEADING'] = commands['CLEAR'] self.pilot.executer.process_commands(commands) self.target = point log.info('%s is clearing towards %s' % (self.plane.icao, point))
def update(self): ''' Perform actions (typically making a new aeroplane to appear) based on the kind of challenge. ''' now = time() if now - self.last_entry > self.frequency: self.last_entry = now if self.plane_counter == 0: for i in range(self.PLANE_NUMBER_START): self.__add_plane() else: self.__add_plane() if self.frequency != self.FREQ_LIMIT and \ now - self.last_freq_increase > self.MOD_PERIOD: self.last_freq_increase = now self.frequency += self.FREQ_STEP # If there have been 3 (or more) destroyed planes, terminate the match if self.gamelogic.fatalities >= self.MAX_LOST: msg = ('THREE_STRIKES_OUT: Match si over after %s planes ' \ 'entered the aerospace' % self.plane_counter, S.KO_COLOUR) log.info(msg) self.gamelogic.game_commander.display([msg])
def _initiate(self, commands): ''' Automatically called by ancestor class upon ``__init__`` ''' pl = self.plane aspace = pl.aerospace r_name = commands['TAKEOFF'][0][0] port = aspace.airports[pl.origin] runway = port.runways[r_name] twin = port.runways[runway['twin']] start_point = runway['location'] + port.location vector = Vector3(*(-twin['ils']).normalized().xy) self.end_of_runway = twin['location'] + port.location # SAVE PROCEDURE' PERSISTENT DATA h = commands['HEADING'][0][0] if 'HEADING' in commands \ else U.v3_to_heading(vector) a = commands['ALTITUDE'][0][0] if 'ALTITUDE' in commands \ else pl.max_altitude self.target_heading = h self.target_altitude = a self.timer = S.RUNWAY_BUSY_TIME # LET'S ROLL!! pl.position = start_point.copy() aspace.runways_manager.use_runway(port, twin, pl) pl.flags.locked = True # Give 1 m/s speed and update target to set the heading/sprite icon pl.velocity = vector.copy() self.pilot.set_target_conf_to_current() # Set max acceleration self.pilot.target_conf.speed = \ commands['SPEED'][0][0] if 'SPEED' in commands else pl.max_speed self.phase = self.ACCELERATING # LOG log.info('%s is taking off from %s %s' % (pl.icao, pl.origin, runway['name'])) if self._check_expedite(commands): self.pilot.status['haste'] = 'expedite'
def _initiate(self, commands): ''' Automatically called by ancestor class upon ``__init__`` ''' pl = self.plane pi = self.pilot port_name, rnwy_name = commands['LAND'][0] self.lander = Lander(self.pilot, port_name, rnwy_name) l = self.lander # EARLY RETURN - Maximum incidence angle into the ILS is 60° ils_heading = U.v3_to_heading(l.ils) boundaries = [(ils_heading - S.ILS_TOLERANCE)%360, (ils_heading + S.ILS_TOLERANCE)%360] if not U.heading_in_between(boundaries, pl.heading): msg = 'ILS heading must be within %s degrees ' \ 'from current heading' % S.ILS_TOLERANCE return self._abort_landing(msg) # EARLY RETURN - No possible intersection (the RADAR_RANGE*3 # ensures that the segments to test for intersection are long enough. if not l.set_intersection_point(): msg = 'The ILS does not intersect the plane current heading' return self._abort_landing(msg) # EARLY RETURN - Although the intersection is ahead of the plane, # it's too late to merge into the ILS vector type_ = pi.status['haste'] if l.set_merge_point(pi.navigator.get_veering_radius(type_)) < 0: msg = 'We are too close to the ILS to merge into it' return self._abort_landing(msg) # LANDING IS NOT EXCLUDED A PRIORI... log.info('%s started landing procedure, destination: %s %s' % (self.plane.icao, port_name, rnwy_name)) # SETTING PERSISTENT DATA if self._check_expedite(commands): pi.status['haste'] = 'expedite' self.phase = self.INTERCEPTING self.lander = l