Mercurial > atc
changeset 47:d2efdc4ff196
Initial reorganization of the code
author | Josef "Jeff" Sipek <jeffpc@josefsipek.net> |
---|---|
date | Fri, 01 Jun 2007 02:32:48 -0400 |
parents | ca661c5a267b |
children | 955cd24d7a05 |
files | atc atc_beacon.py atc_colors.py atc_config.py atc_game.py atc_maps.py atc_message.py atc_plane.py atc_plane_specs.py atc_single.py atc_utils.py atc_weather.py atcgame/__init__.py atcgame/beacon.py atcgame/colors.py atcgame/config.py atcgame/game.py atcgame/mainmenu.py atcgame/maps.py atcgame/message.py atcgame/plane.py atcgame/plane_specs.py atcgame/single.py atcgame/utils.py atcgame/weather.py data/maps/default/map.info data/maps/default/map.png data/maps/default/map.xcf data/maps/default/navaid.info data/planes/B747.info data/planes/PA28.info maps/default/map.info maps/default/map.png maps/default/map.xcf maps/default/navaid.info planes/B747.info planes/PA28.info |
diffstat | 36 files changed, 1187 insertions(+), 1162 deletions(-) [+] |
line wrap: on
line diff
--- a/atc Fri Jun 01 02:08:43 2007 -0400 +++ b/atc Fri Jun 01 02:32:48 2007 -0400 @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python #/* # * ATC - Air Traffic Controller simulation game @@ -15,84 +15,13 @@ # * GNU General Public License for more details. # */ -version = "0.10-pre2" +import os, sys -try: - import sys - import threading - import pygame - - import atc_config - - import atc_colors - import atc_utils - import atc_plane - import atc_message - - import atc_single -except ImportError, err: - print "Couldn't load module %s" % (err) - sys.exit() - -size = width, height = 1024, 768 - -def main(): - """ Main fn to run the main thread """ - - # Init - pygame.init() - screen = pygame.display.set_mode(size) - pygame.display.set_caption('Air Traffic Controller (' + version + ')') - - # background music - try: - pygame.mixer.init() - except pygame.error: - print "Could not init sound" - sys.exit(1) - #atc_utils.playmusic("some.mp3"); +basedir = os.path.dirname(sys.argv[0]) - # Set background - background = pygame.Surface(screen.get_size()).convert() - background.fill(atc_colors.black) - - (splash_image, splash_rect) = atc_utils.load_png('data/image/splash.png') - background.blit(splash_image, (0, 0)) - - #blit! - screen.blit(background, (0, 0)) - pygame.display.flip() +import atcgame.mainmenu - optsel = 0 - menustr = ('single', 'multi', 'quit') - - while 1: - event = pygame.event.wait() - if event.type == pygame.QUIT: - sys.exit() - if event.type == pygame.KEYDOWN: - if event.key == pygame.K_UP: - optsel = (optsel + 2) % 3 - if event.key == pygame.K_DOWN: - optsel = (optsel + 1) % 3 - if event.key == pygame.K_RETURN: - optsel = optsel | 0x10 - if optsel == 0x12: - sys.exit() - - screen.blit(background, (0, 0)) - - (menu_img, menu_rect) = atc_utils.load_png('data/image/menu_' + menustr[optsel & 3] + '.png') - screen.blit(menu_img, ((width - 334)/2.0, (height - 225)/2.0)) - - pygame.display.flip() - - if (optsel & 0x10): - optsel = optsel & 3 - if (optsel == 0): # single player - atc_single.startgame(screen) - if (optsel == 1): # multi player - pass # FIXME: implement +# a hack to make things run from current dir without having to install +sys.path.insert(0, os.path.join(basedir,"atcgame")) -if (__name__ == '__main__'): - main() +atcgame.mainmenu.main(os.path.join(basedir,"data"))
--- a/atc_beacon.py Fri Jun 01 02:08:43 2007 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,44 +0,0 @@ -#/* -# * ATC - Air Traffic Controller simulation game -# * -# * Copyright (C) 2004-2007 Josef "Jeff" Sipek <jeffpc@josefsipek.net> -# * -# * This program is free software; you can redistribute it and/or modify -# * it under the terms of the GNU General Public License version 2 as -# * published by the Free Software Foundation. -# * -# * This program is distributed in the hope that it will be useful, -# * but WITHOUT ANY WARRANTY; without even the implied warranty of -# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# * GNU General Public License for more details. -# */ - -import os -import sys -import time -import pygame -from math import * - -import atc_config - -import atc_colors -import atc_utils - -# FIXME: power output/range? -class Beacon: - """ Class to manage a generic beacon """ - def __init__(self, name, freq, loc): - self.freq = freq - self.loc = loc - self.name = name - -class ILS_Beacon(Beacon): - """ Class to manage ILS beacons """ - def __init__(self, name, freq, loc, marker): - Beacon.__init__(self, name, freq, loc) - self.marker = marker - -class VOR_Beacon(Beacon): - """ Class to manage VOR beacons """ - def __init__(self, name, freq, loc): - Beacon.__init__(self, name, freq, loc)
--- a/atc_colors.py Fri Jun 01 02:08:43 2007 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,32 +0,0 @@ -#/* -# * ATC - Air Traffic Controller simulation game -# * -# * Copyright (C) 2004-2007 Josef "Jeff" Sipek <jeffpc@josefsipek.net> -# * -# * This program is free software; you can redistribute it and/or modify -# * it under the terms of the GNU General Public License version 2 as -# * published by the Free Software Foundation. -# * -# * This program is distributed in the hope that it will be useful, -# * but WITHOUT ANY WARRANTY; without even the implied warranty of -# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# * GNU General Public License for more details. -# */ - -black = 0, 0, 0 -blue1 = 0, 182, 227 -blue2 = 57, 108, 149 -brown = 199, 103, 0 -gray = 131, 131, 131 -green1 = 0, 227, 0 -green2 = 0, 149, 0 -red = 255, 0, 0 -snow = 227, 227, 227 -white = 255, 255, 255 - -transparent = 0, 0, 0, 0 - -planeinfo_ok = green1 -planeinfo_crashed = brown -planeinfo_emergency = red -background = black
--- a/atc_config.py Fri Jun 01 02:08:43 2007 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,13 +0,0 @@ -# type of labels used to display plane info -# -# values: -# 1 (default) = realistic -# 2 = very detailed - -plane_label = 1 - -# relative location of the plane specs directory -plane_spec_dir = "planes" - -# relative location of the maps directory -maps_dir = "maps"
--- a/atc_game.py Fri Jun 01 02:08:43 2007 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,279 +0,0 @@ -#/* -# * ATC - Air Traffic Controller simulation game -# * -# * Copyright (C) 2004-2007 Josef "Jeff" Sipek <jeffpc@josefsipek.net> -# * -# * This program is free software; you can redistribute it and/or modify -# * it under the terms of the GNU General Public License version 2 as -# * published by the Free Software Foundation. -# * -# * This program is distributed in the hope that it will be useful, -# * but WITHOUT ANY WARRANTY; without even the implied warranty of -# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# * GNU General Public License for more details. -# */ - -import pygame -import sys - -import atc_colors -import atc_utils -import atc_plane -import atc_message -import atc_weather - -from atc_maps import * - -class Game: - def __init__(self, screen): - # background music - #atc_utils.playmusic("some.mp3"); - - self.displays = { - "map": 1, - "weather": 0, - "navaids": 0, - "menus": 0, - "planes": 1} - - self.map = MAPS[0] # FIXME: add param to chose map - self.map.bootstrap() - - # set up the display layers as much as possible - self.screen = screen - self.screen_void = pygame.Surface(self.screen.get_size()).convert_alpha() - self.screen_map = pygame.Surface(self.screen.get_size()).convert_alpha() - self.screen_weather = pygame.Surface(self.screen.get_size()).convert_alpha() - self.screen_navaids = pygame.Surface(self.screen.get_size()).convert_alpha() - self.screen_menus = pygame.Surface(self.screen.get_size()).convert_alpha() - self.screen_planes = pygame.Surface(self.screen.get_size()).convert_alpha() - self.screen_cmd = pygame.Surface(self.screen.get_size()).convert_alpha() - - self.screen_void.fill (atc_colors.black) - self.screen_map.fill (atc_colors.transparent) - self.screen_weather.fill(atc_colors.transparent) - self.screen_navaids.fill(atc_colors.transparent) - self.screen_menus.fill (atc_colors.transparent) - self.screen_cmd.fill (atc_colors.transparent) - - (back_image, back_rect) = atc_utils.load_png(self.map.image) - self.screen_map.blit(back_image, (0, 0)) - - #blit! - self.screen.blit(self.screen_map, (0, 0)) - pygame.display.flip() - - self.planes = [] - self.planes.append(self.__random_plane()) - - self.weather = atc_weather.Weather() - - self.mess = atc_message.Message() - - self.cmd = "" - - def __del__(self): - self.map.cleanup() - - def __proc_cmd(self): - flno = cmd_plane(self.cmd) - com = cmd_cmd(self.cmd) - - flag = None - if (com!=None) and (flno!=None): - print "plane = " + flno + ", cmd = " + com - for plane in self.planes: - if plane.flightno.upper() == flno.upper(): - print "Found match!" - plane.process(com) - flag = 1 - break - - if (flag == None): - # either not the right syntax, or we - # didn't find the right plane - print "Error, could not process command" - - self.cmd = "" - - def __proc_events(self): - for event in pygame.event.get(): - if event.type == pygame.QUIT: - self.mess.pickup_kill = 1 - sys.exit() - if event.type == pygame.KEYDOWN: - if event.key == pygame.K_a: - self.cmd += 'A' - if event.key == pygame.K_b: - self.cmd += 'B' - if event.key == pygame.K_c: - self.cmd += 'C' - if event.key == pygame.K_d: - self.cmd += 'D' - if event.key == pygame.K_e: - self.cmd += 'E' - if event.key == pygame.K_f: - self.cmd += 'F' - if event.key == pygame.K_g: - self.cmd += 'G' - if event.key == pygame.K_h: - self.cmd += 'H' - if event.key == pygame.K_i: - self.cmd += 'I' - if event.key == pygame.K_j: - self.cmd += 'J' - if event.key == pygame.K_k: - self.cmd += 'K' - if event.key == pygame.K_l: - self.cmd += 'L' - if event.key == pygame.K_m: - self.cmd += 'M' - if event.key == pygame.K_n: - self.cmd += 'N' - if event.key == pygame.K_o: - self.cmd += 'O' - if event.key == pygame.K_p: - self.cmd += 'P' - if event.key == pygame.K_q: - self.cmd += 'Q' - if event.key == pygame.K_r: - self.cmd += 'R' - if event.key == pygame.K_s: - self.cmd += 'S' - if event.key == pygame.K_t: - self.cmd += 'T' - if event.key == pygame.K_u: - self.cmd += 'U' - if event.key == pygame.K_v: - self.cmd += 'V' - if event.key == pygame.K_w: - self.cmd += 'W' - if event.key == pygame.K_x: - self.cmd += 'X' - if event.key == pygame.K_y: - self.cmd += 'Y' - if event.key == pygame.K_z: - self.cmd += 'Z' - - if (event.key == pygame.K_0) or (event.key == pygame.K_KP0): - self.cmd += '0' - if (event.key == pygame.K_1) or (event.key == pygame.K_KP1): - self.cmd += '1' - if (event.key == pygame.K_2) or (event.key == pygame.K_KP2): - self.cmd += '2' - if (event.key == pygame.K_3) or (event.key == pygame.K_KP3): - self.cmd += '3' - if (event.key == pygame.K_4) or (event.key == pygame.K_KP4): - self.cmd += '4' - if (event.key == pygame.K_5) or (event.key == pygame.K_KP5): - self.cmd += '5' - if (event.key == pygame.K_6) or (event.key == pygame.K_KP6): - self.cmd += '6' - if (event.key == pygame.K_7) or (event.key == pygame.K_KP7): - self.cmd += '7' - if (event.key == pygame.K_8) or (event.key == pygame.K_KP8): - self.cmd += '8' - if (event.key == pygame.K_9) or (event.key == pygame.K_KP9): - self.cmd += '9' - - if (event.key == pygame.K_COLON) or (event.key == pygame.K_SEMICOLON): - self.cmd += ': ' - - if event.key == pygame.K_SPACE: - self.cmd += " " - - if event.key == pygame.K_ESCAPE: - self.cmd = "" - if event.key == pygame.K_BACKSPACE: - self.cmd = self.cmd[:-1] - - if event.key == pygame.K_TAB: - pass # FIXME, tab complete call signs/flight numbers, commands, etc. - - if event.key == pygame.K_F1: - self.displays["map"] = 1 - self.displays["map"] - if event.key == pygame.K_F2: - self.displays["weather"] = 1 - self.displays["weather"] - if event.key == pygame.K_F3: - self.displays["navaids"] = 1 - self.displays["navaids"] - if event.key == pygame.K_F4: - self.displays["menus"] = 1 - self.displays["menus"] - if event.key == pygame.K_F5: - self.displays["planes"] = 1 - self.displays["planes"] - - if (event.key == pygame.K_KP_ENTER) or (event.key == pygame.K_RETURN): - if (self.cmd == "QUIT"): - self.mess.pickup_kill = 1 - return False - - if (self.cmd.__len__()): - self.__proc_cmd() - - return True - - def __random_plane(self): - callsign = "N1499H" - flightno = "Delta 79" - squawk = "1200" - type = "B747" - vel = (20, 20, 20) # FIXME: use the specs - - return atc_plane.Plane(type=type, callsign=callsign, flightno=flightno, squawk=squawk, vel=vel) - - def run(self): - if not self.__proc_events(): return False - - # planes - self.screen_planes.fill(atc_colors.transparent) - for plane in self.planes: - plane.display(self.screen_planes) - plane.update() - if (plane.status == atc_plane.plane_CRASHED): - mess.write(self.screen_planes, plane.callsign + ": PLANE CRASHED") - plane.status = atc_plane.plane_DEAD - - # weather - self.weather.display(self.screen_weather) - self.weather.update() - - # command - self.screen_cmd.fill(atc_colors.transparent) - if (self.cmd): - font = pygame.font.Font(None, 18) - text = font.render(self.cmd, 1, atc_colors.white) - self.screen_cmd.blit(text, (25, 735)) - - # blit what's needed - self.screen.blit(self.screen_void, (0, 0)) - if (self.displays["map"]): - self.screen.blit(self.screen_map, (0, 0)) - if (self.displays["weather"]): - self.screen.blit(self.screen_weather, (0, 0)) - if (self.displays["navaids"]): - self.screen.blit(self.screen_navaids, (0, 0)) - if (self.displays["menus"]): - self.screen.blit(self.screen_menus, (0, 0)) - if (self.displays["planes"]): - self.screen.blit(self.screen_planes, (0, 0)) - self.screen.blit(self.screen_cmd, (0, 0)) - - pygame.display.flip() - - return True - -def cmd_plane(cmd): - parts = cmd.split(': ') - - if (parts.__len__()>1): - return parts[0] - return None - -def cmd_cmd(cmd): - parts = cmd.split(': ') - - if (parts.__len__()>1): - return parts[1] - return None - -if (__name__ == '__main__'): - print "Don't do it!"
--- a/atc_maps.py Fri Jun 01 02:08:43 2007 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,106 +0,0 @@ -#/* -# * ATC - Air Traffic Controller simulation game -# * -# * Copyright (C) 2004-2007 Josef "Jeff" Sipek <jeffpc@josefsipek.net> -# * -# * This program is free software; you can redistribute it and/or modify -# * it under the terms of the GNU General Public License version 2 as -# * published by the Free Software Foundation. -# * -# * This program is distributed in the hope that it will be useful, -# * but WITHOUT ANY WARRANTY; without even the implied warranty of -# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# * GNU General Public License for more details. -# */ - -import os -import sys -import atc_config -import atc_beacon - -def get_map_info(file): - fd = open(file, 'r') - s = fd.read() - fd.close() - - s = s.split("\n") - - return Map( float(s[0]), # centerx - float(s[1]), # centery - float(s[2]), # mperpix - float(s[3]), # atcx - float(s[4])) # atcy - -def get_navaids(file, m): - fd = open(file, 'r') - s = fd.read() - fd.close() - - s = s.split("\n") - - for line in s: - if (line.__len__() == 0): - continue - - line = line.split(" ") - - m.ninfo.append({ - "xoff": float(line[0]), - "yoff": float(line[1]), - "freq": float(line[2]), - "name": line[3], - "type": line[4], - "output": float(line[5])}) - -class Map: - def __init__(self, centerx, centery, mperpix, atcx, atcy): - self.centerx = centerx - self.centery = centery - self.mperpix = mperpix - self.atcx = atcx - self.atcy = atcy - - self.ninfo = [] - self.navs = [] - - self.image = "" - - def bootstrap(self): - for nav in self.ninfo: - if nav["type"] == "VOR": - self.navs.append(atc_beacon.VOR_Beacon(nav["name"], nav["freq"], (nav["xoff"], nav["yoff"]))) - elif nav["type"] == "ILS": - pass # FIXME: ILS beacons are little complicated - - def cleanup(self): - while self.navs.__len__(): - self.navs.pop() - -MAPS = [] - -maps_dir = os.listdir(atc_config.maps_dir) - -for map_dir in maps_dir: - files = os.listdir(atc_config.maps_dir + "/" + map_dir) - - if (files.count("map.info") == 0): - print "Missing " + atc_config.maps_dir + "/" + map_dir + "/map.info" - sys.exit(1) - - if (files.count("map.png") == 0): - print "Missing " + atc_config.maps_dir + "/" + map_dir + "/map.png" - sys.exit(1) - - if (files.count("navaid.info") == 0): - print "Missing " + atc_config.maps_dir + "/" + map_dir + "/navaid.info" - sys.exit(1) - - map_info = get_map_info(atc_config.maps_dir + "/" + map_dir + "/map.info") - get_navaids(atc_config.maps_dir + "/" + map_dir + "/navaid.info", map_info) - - map_info.image = atc_config.maps_dir + "/" + map_dir + "/map.png" - - MAPS.append(map_info) - print "Parsed and added '" + map_dir + "' map (" + str(map_info.ninfo.__len__()) + " navaids)" - -print "Added " + str(MAPS.__len__()) + " maps"
--- a/atc_message.py Fri Jun 01 02:08:43 2007 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,118 +0,0 @@ -#/* -# * ATC - Air Traffic Controller simulation game -# * -# * Copyright (C) 2004-2007 Josef "Jeff" Sipek <jeffpc@josefsipek.net> -# * -# * This program is free software; you can redistribute it and/or modify -# * it under the terms of the GNU General Public License version 2 as -# * published by the Free Software Foundation. -# * -# * This program is distributed in the hope that it will be useful, -# * but WITHOUT ANY WARRANTY; without even the implied warranty of -# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# * GNU General Public License for more details. -# */ - -import pygame -import threading - -class Message: - """ Interface to display a message and beep it off in morse code """ - def __init__(self): - """ Set up everything """ - self.snd = {} - self.snd["."] = pygame.mixer.Sound('data/sound/sbeep.wav') - self.snd["."].set_volume(0.4) - self.snd["-"] = pygame.mixer.Sound('data/sound/lbeep.wav') - self.snd["-"].set_volume(0.4) - self.snd[" "] = pygame.mixer.Sound('data/sound/nosnd.wav') - self.snd[" "].set_volume(0.4) - - self.delay = {} - self.delay["."] = 250 - self.delay["-"] = 400 - self.delay[" "] = 330 - - self.morse = {} - self.morse["A"] = ".-" - self.morse["B"] = "-..." - self.morse["C"] = "-.-." - self.morse["D"] = "-.." - self.morse["E"] = "." - self.morse["F"] = "..-." - self.morse["G"] = "--." - self.morse["H"] = "...." - self.morse["I"] = ".." - self.morse["J"] = ".---" - self.morse["K"] = "-.-" - self.morse["L"] = ".-.." - self.morse["M"] = "--" - self.morse["N"] = "-." - self.morse["O"] = "---" - self.morse["P"] = ".--." - self.morse["Q"] = "--.-" - self.morse["R"] = ".-." - self.morse["S"] = "..." - self.morse["T"] = "-" - self.morse["U"] = "..-" - self.morse["V"] = "...-" - self.morse["W"] = ".--" - self.morse["X"] = "-..-" - self.morse["Y"] = "-.--" - self.morse["Z"] = "--.." - self.morse["1"] = ".----" - self.morse["2"] = "..---" - self.morse["3"] = "...--" - self.morse["4"] = "....-" - self.morse["5"] = "....." - self.morse["6"] = "-...." - self.morse["7"] = "--..." - self.morse["8"] = "---.." - self.morse["9"] = "----." - self.morse["0"] = "-----" - self.morse[" "] = " " - self.morse[":"] = "" - - self.statusmess = "" - - self.messages = [] - - self.pickup_kill = 0 - self.pickup_thread = threading.Timer(0, self.__pickup) - self.pickup_thread.start() - - def write(self,screen,mess): - """ Add message to the queue """ - code = "" - for letter in mess: - code += self.morse[letter] + " " - - self.messages.append(code) - - def __beep(self,char): - """ Beep one . or - """ - self.snd[char].play() - - def __beeper(self,mess): - """ Beep out all . and - in a message """ - for char in mess: - self.__beep(char) - pygame.time.delay(self.delay[char]) - if (self.pickup_kill): - return - - def __pickup(self): - """ The message pickup thread code, it picks up new messages - and calls the appropreate functions to beep them out """ - while(not self.pickup_kill): - try: - message = self.messages.pop() - self.statusmess = message - self.__beeper(message) - pygame.time.delay(1400) - self.statusmess = "" - except IndexError: - pass - - pygame.time.delay(100) -
--- a/atc_plane.py Fri Jun 01 02:08:43 2007 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,266 +0,0 @@ -#/* -# * ATC - Air Traffic Controller simulation game -# * -# * Copyright (C) 2004-2007 Josef "Jeff" Sipek <jeffpc@josefsipek.net> -# * -# * This program is free software; you can redistribute it and/or modify -# * it under the terms of the GNU General Public License version 2 as -# * published by the Free Software Foundation. -# * -# * This program is distributed in the hope that it will be useful, -# * but WITHOUT ANY WARRANTY; without even the implied warranty of -# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# * GNU General Public License for more details. -# */ - -import os -import sys -import time -import pygame -from math import * - -import atc_config - -import atc_colors -import atc_utils -from atc_plane_specs import * -from atc_maps import * - -mperpix = MAPS[0].mperpix -mperdeg = 1852.0*60.0 # == 1852 m/min * 60 min/deg - -def m2pix(m): - """ Meters to pixels conversion fn """ - return m/mperpix - -def pix2m(pix): - """ Pixels to meters conversion fn """ - return pix*mperpix - -def pix2geo(pix): - return pix2m(pix)/mperdeg - -plane_OK = 0 -plane_RANGE = 1 -plane_CRASHED = 2 -plane_STALL = 3 -plane_DEAD = 4 -plane_PROXY = 5 - -class Plane(pygame.sprite.Sprite): - """ Class to manage one plane's motion """ - def __init__(self,type,callsign,flightno,squawk="1200",pos=(0.0, 0.0, 0.0),vel=(0.0, 0.0, 0.0)): - """ Set up everything """ - pygame.sprite.Sprite.__init__(self) - - # screen - screen = pygame.display.get_surface() - self.area = screen.get_rect() - - # plane image - (self.image, self.rect) = atc_utils.load_png('data/image/plane.png') - - # call sign - self.callsign = callsign - - # flight number - self.flightno = flightno - - # squawk code (shoud be octal number digits 0-7 only) - self.squawk = squawk - - # position (X, Y, Z) - self.pos = { \ - "X":pos[0], \ - "Y":pos[1], \ - "Z":pos[2]} - - # target position - self.targetPos = { \ - "X":None, \ - "Y":None, \ - "Z":None} - - # velocity - self.vel = { \ - "i":vel[0], \ - "j":vel[1], \ - "k":vel[2], \ - - "heading":0, \ - "speed":0, \ - "climb":0} - self.complete_vel(ijk=1) - - self.timer = 0 # this is timeout the next time we try to recalculate position - - # plane specs - self.specs = None - for p in plane_SPECS: - if p['name'] == type: - self.specs= p - break - - if self.specs == None: - self.specs = plane_SPECS[0] - - # status flag - self.status = plane_OK - - def complete_vel(self, ijk=None, hac=None): - """ Recalculate all the velocity variables """ - if ijk == None and hac == None: - return - - if ijk == None: - self.vel["i"] = sin(atc_utils.torad(self.vel["heading"])) * sqrt(self.vel["speed"]**2 - self.vel["climb"]**2) - self.vel["j"] = cos(atc_utils.torad(self.vel["heading"])) * sqrt(self.vel["speed"]**2 - self.vel["climb"]**2) - self.vel["k"] = self.vel["climb"] - else: - self.vel["heading"] = self.calc_head() - self.vel["speed"] = sqrt(self.vel["i"]**2 + self.vel["j"]**2 + self.vel["k"]**2) - self.vel["climb"] = self.vel["k"] - - def update(self): - """ Move the plane, and check for collisions """ - if (self.timer > time.time()): - return - - if (self.status != plane_OK): - return - - self.pos["X"] += self.vel["i"]/10.0 - self.pos["Y"] += self.vel["j"]/10.0 - self.pos["Z"] += self.vel["k"]/10.0 - - # FIXME: need physics - if (self.targetPos["Z"] != None) and (self.pos["Z"] >= (self.targetPos["Z"] - 25)) and (self.pos["Z"] <= (self.targetPos["Z"] + 25)): - self.vel["climb"] = 0 - self.targetPos["Z"] = None - - self.complete_vel(hac=1) - - #if (self.pos["X"] < 0) or (self.pos["Y"] < 0): # FIXME: max values - # self.status = plane_RANGE - - if (self.pos["Z"] < 0): - self.vel["i"] = self.vel["j"] = self.vel["k"] = 0 - self.status = plane_CRASHED - - if (self.vel["speed"]<m2pix(self.specs["stall_speed"])) and (not self.status==plane_CRASHED): - self.status = plane_STALL - - self.rect.move(m2pix(self.vel["i"])/10.0,m2pix(self.vel["j"])/10.0) - - self.timer = time.time() + m2pix(self.vel["speed"])**-1/10.0 - - def calc_head(self): - """ Calculate heading of the airplane (in degrees)""" - if (self.vel["i"] == self.vel["j"] == 0): # no movement - return 0 - - try: - head = fabs(atan(self.vel["i"]/self.vel["j"])) # yes this is "backwards" - except ZeroDivisionError: - if (self.vel["i"]>0): - return 90 # east - return 270 # west - - head = abs(head*180/pi) - if self.vel["i"]>0 and self.vel["j"]>0: # quad I - return head - if self.vel["i"]<0 and self.vel["j"]>0: # quad II - return 360-head - if self.vel["i"]<0 and self.vel["j"]<0: # quad III - return 270-head - if self.vel["i"]>0 and self.vel["j"]<0: # quad IV - return 180-head - - if self.vel["i"]==0 and self.vel["j"]>0: # north - return 0 - return 180 # south - - def calc_geo(self, xy, prefix): - """ Return a geographic coodrinate-formated xy """ - df = m2pix(pix2geo(xy)) - minus = prefix[0] - if df<0: - df *= -1 - minus = prefix[1] - d = floor(df) - mf = (df-d) * 60.0 - m = floor(mf) - s = (mf-m) * 60.0 - - return "%s %dd %02dm %02ds" % (minus, int(d), int(m), int(s)) - - def process(self,cmd): - """ Process a user command """ - - # FIXME: convert multiple spaces to single - parts = cmd.split(' ') - - if (parts[0] == "ALT"): - print "Changing altitude to " + parts[1] + "m" - self.targetPos["Z"] = int(parts[1]) - self.vel["climb"] = self.specs["climb_normal"] - if (self.pos["Z"] > self.targetPos["Z"]): - self.vel["climb"] *= -1 - self.complete_vel(hac=1) - - if (parts[0] == "HEAD"): - self.vel["heading"] = int(parts[1]) - while(True): - if self.vel["heading"]>=360: - self.vel["heading"] -= 360 - elif self.vel["heading"]<0: - self.vel["heading"] += 360 - else: - break - - print "Changing heading to " + str(self.vel["heading"]) - self.complete_vel(hac=1) - - if (parts[0] == "SQUAWK"): - self.squawk = parts[1] - - def display(self,screen): - """ Put everything onto the screen """ - rotimage = pygame.transform.rotate(self.image, 360.0-self.vel["heading"]) - screen.blit(rotimage, (int(1024/2+m2pix(self.pos["X"])), int(768/2-m2pix(self.pos["Y"])))) - - # Plane info - font = pygame.font.Font(None, 16) - - color = atc_colors.planeinfo_ok - if (self.status == plane_CRASHED) or (self.status == plane_DEAD): - color = atc_colors.planeinfo_crashed - - if (self.squawk == "7700") or \ - (self.squawk == "7600") or \ - (self.squawk == "7500") or \ - (self.squawk == "0000"): - color = atc_colors.planeinfo_emergency - - x = int(1024/2+m2pix(self.pos["X"])) + 10 - y = int(768/2-m2pix(self.pos["Y"])) - 5 - - if (atc_config.plane_label == 2): - strings = ( self.flightno, - self.callsign + " " + str(self.status), - "Alt: " + str(int(self.pos["Z"])), - "Lat: " + self.calc_geo(self.pos["Y"],"NS"), - "Long: " + self.calc_geo(self.pos["X"],"EW"), - "Head: " + str(int(self.vel["heading"])), - "AS: " + str(int(self.vel["speed"]*3.6))) - else: - strings = ( self.flightno, - "%03d %03d" % (int(round(self.vel["heading"])), int(round(self.vel["speed"]*3.6))), - self.specs["name"]) - - for stri in strings: - alt = font.render(stri, 1, color) - altpos = alt.get_rect() - altpos.topleft = (x,y) - screen.blit(alt, altpos) - (x,y) = (x,y+10)
--- a/atc_plane_specs.py Fri Jun 01 02:08:43 2007 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,73 +0,0 @@ -#/* -# * ATC - Air Traffic Controller simulation game -# * -# * Copyright (C) 2004-2007 Josef "Jeff" Sipek <jeffpc@josefsipek.net> -# * -# * This program is free software; you can redistribute it and/or modify -# * it under the terms of the GNU General Public License version 2 as -# * published by the Free Software Foundation. -# * -# * This program is distributed in the hope that it will be useful, -# * but WITHOUT ANY WARRANTY; without even the implied warranty of -# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# * GNU General Public License for more details. -# */ - -import os -import sys -import atc_config - -# all measurements in meters/sec or meters -plane_SPECS = [] - -plane_dir = os.listdir(atc_config.plane_spec_dir) - -for f in plane_dir: - plane = {"fullname": str(None), - "name": str(None), - "heavy": False, - "jet": False, - "stall_speed": 0.0, - "climb_normal": 0.0, - "max_altitude": 0.0} - - fd = open(atc_config.plane_spec_dir + "/" + f, 'r') - s = fd.read() - fd.close() - - s = s.split("\n") - for line in s: - if (line.__len__()==0): - continue - - cmd = line[:line.find(' ')-1] - param = line[line.find(' ')+1:] - - if cmd=="FullName": - plane["fullname"] = param - elif cmd=="Name": - plane["name"] = param - elif cmd=="Heavy": - if (param.upper() == "YES"): - plane["heavy"] = True - else: - plane["heavy"] = False - elif cmd=="Jet": - if (param.upper() == "YES"): - plane["jet"] = True - else: - plane["jet"] = False - elif cmd=="StallSpeed": - plane["stall_speed"] = float(param) - elif cmd=="ClimbNormal": - plane["climb_normal"] = float(param) - elif cmd=="MaxAltitude": - plane["max_altitude"] = float(param) - else: - print "Error parsing " + f + ": Unknown statement" - sys.exit(1) - - plane_SPECS.append(plane) - print "Parsed and added " + plane["fullname"] + " aka. " + plane["name"] - -print "Added " + str(plane_SPECS.__len__()) + " plane types"
--- a/atc_single.py Fri Jun 01 02:08:43 2007 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,49 +0,0 @@ -#/* -# * ATC - Air Traffic Controller simulation game -# * -# * Copyright (C) 2004-2007 Josef "Jeff" Sipek <jeffpc@josefsipek.net> -# * -# * This program is free software; you can redistribute it and/or modify -# * it under the terms of the GNU General Public License version 2 as -# * published by the Free Software Foundation. -# * -# * This program is distributed in the hope that it will be useful, -# * but WITHOUT ANY WARRANTY; without even the implied warranty of -# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# * GNU General Public License for more details. -# */ - -import pygame -import sys - -import atc_colors -import atc_utils -import atc_plane -import atc_message -import atc_game - -from atc_maps import * - -def startgame(screen): - """ Main fn to run the single player game """ - g = atc_game.Game(screen) - - while g.run(): pass - - -def cmd_plane(cmd): - parts = cmd.split(': ') - - if (parts.__len__()>1): - return parts[0] - return None - -def cmd_cmd(cmd): - parts = cmd.split(': ') - - if (parts.__len__()>1): - return parts[1] - return None - -if (__name__ == '__main__'): - print "Don't do it!"
--- a/atc_utils.py Fri Jun 01 02:08:43 2007 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,46 +0,0 @@ -#/* -# * ATC - Air Traffic Controller simulation game -# * -# * Copyright (C) 2004-2007 Josef "Jeff" Sipek <jeffpc@josefsipek.net> -# * -# * This program is free software; you can redistribute it and/or modify -# * it under the terms of the GNU General Public License version 2 as -# * published by the Free Software Foundation. -# * -# * This program is distributed in the hope that it will be useful, -# * but WITHOUT ANY WARRANTY; without even the implied warranty of -# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# * GNU General Public License for more details. -# */ - -import pygame -import os -from math import * - -def load_png(name): - """ Load image and return image object""" - fullname = os.path.join('', name) - try: - image = pygame.image.load(fullname) - if image.get_alpha() is None: - image = image.convert() - else: - image = image.convert_alpha() - except pygame.error, message: - print 'Cannot load image:', fullname - raise SystemExit, message - return image, image.get_rect() - -def todeg(rad): - """ Convert radians to degrees """ - return rad*180.0/pi - -def torad(deg): - """ Convert degrees to radians """ - return deg*pi/180.0 - -def playmusic(name,vol=0.1): - """ Start playing background music """ - pygame.mixer.music.load('data/sound/' + name) - pygame.mixer.music.set_volume(vol) - pygame.mixer.music.play()
--- a/atc_weather.py Fri Jun 01 02:08:43 2007 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,36 +0,0 @@ -#/* -# * ATC - Air Traffic Controller simulation game -# * -# * Copyright (C) 2004-2007 Josef "Jeff" Sipek <jeffpc@josefsipek.net> -# * -# * This program is free software; you can redistribute it and/or modify -# * it under the terms of the GNU General Public License version 2 as -# * published by the Free Software Foundation. -# * -# * This program is distributed in the hope that it will be useful, -# * but WITHOUT ANY WARRANTY; without even the implied warranty of -# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# * GNU General Public License for more details. -# */ - -import os -import sys -import time -import pygame -from math import * - -import atc_config - -import atc_colors -import atc_utils - -class Weather: - """ Class to manage the weather """ - def __init__(self): - pass - - def update(self): - pass - - def display(self, screen): - pass
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/atcgame/beacon.py Fri Jun 01 02:32:48 2007 -0400 @@ -0,0 +1,44 @@ +#/* +# * ATC - Air Traffic Controller simulation game +# * +# * Copyright (C) 2004-2007 Josef "Jeff" Sipek <jeffpc@josefsipek.net> +# * +# * This program is free software; you can redistribute it and/or modify +# * it under the terms of the GNU General Public License version 2 as +# * published by the Free Software Foundation. +# * +# * This program is distributed in the hope that it will be useful, +# * but WITHOUT ANY WARRANTY; without even the implied warranty of +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# * GNU General Public License for more details. +# */ + +import os +import sys +import time +import pygame +from math import * + +import config + +import colors +import utils + +# FIXME: power output/range? +class Beacon: + """ Class to manage a generic beacon """ + def __init__(self, name, freq, loc): + self.freq = freq + self.loc = loc + self.name = name + +class ILS_Beacon(Beacon): + """ Class to manage ILS beacons """ + def __init__(self, name, freq, loc, marker): + Beacon.__init__(self, name, freq, loc) + self.marker = marker + +class VOR_Beacon(Beacon): + """ Class to manage VOR beacons """ + def __init__(self, name, freq, loc): + Beacon.__init__(self, name, freq, loc)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/atcgame/colors.py Fri Jun 01 02:32:48 2007 -0400 @@ -0,0 +1,32 @@ +#/* +# * ATC - Air Traffic Controller simulation game +# * +# * Copyright (C) 2004-2007 Josef "Jeff" Sipek <jeffpc@josefsipek.net> +# * +# * This program is free software; you can redistribute it and/or modify +# * it under the terms of the GNU General Public License version 2 as +# * published by the Free Software Foundation. +# * +# * This program is distributed in the hope that it will be useful, +# * but WITHOUT ANY WARRANTY; without even the implied warranty of +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# * GNU General Public License for more details. +# */ + +black = 0, 0, 0 +blue1 = 0, 182, 227 +blue2 = 57, 108, 149 +brown = 199, 103, 0 +gray = 131, 131, 131 +green1 = 0, 227, 0 +green2 = 0, 149, 0 +red = 255, 0, 0 +snow = 227, 227, 227 +white = 255, 255, 255 + +transparent = 0, 0, 0, 0 + +planeinfo_ok = green1 +planeinfo_crashed = brown +planeinfo_emergency = red +background = black
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/atcgame/config.py Fri Jun 01 02:32:48 2007 -0400 @@ -0,0 +1,13 @@ +# type of labels used to display plane info +# +# values: +# 1 (default) = realistic +# 2 = very detailed + +plane_label = 1 + +# relative location of the plane specs directory +plane_spec_dir = "data/planes" + +# relative location of the maps directory +maps_dir = "data/maps"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/atcgame/game.py Fri Jun 01 02:32:48 2007 -0400 @@ -0,0 +1,279 @@ +#/* +# * ATC - Air Traffic Controller simulation game +# * +# * Copyright (C) 2004-2007 Josef "Jeff" Sipek <jeffpc@josefsipek.net> +# * +# * This program is free software; you can redistribute it and/or modify +# * it under the terms of the GNU General Public License version 2 as +# * published by the Free Software Foundation. +# * +# * This program is distributed in the hope that it will be useful, +# * but WITHOUT ANY WARRANTY; without even the implied warranty of +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# * GNU General Public License for more details. +# */ + +import pygame +import sys + +import colors +import utils +import plane +import message +import weather + +from maps import * + +class Game: + def __init__(self, screen): + # background music + #utils.playmusic("some.mp3"); + + self.displays = { + "map": 1, + "weather": 0, + "navaids": 0, + "menus": 0, + "planes": 1} + + self.map = MAPS[0] # FIXME: add param to chose map + self.map.bootstrap() + + # set up the display layers as much as possible + self.screen = screen + self.screen_void = pygame.Surface(self.screen.get_size()).convert_alpha() + self.screen_map = pygame.Surface(self.screen.get_size()).convert_alpha() + self.screen_weather = pygame.Surface(self.screen.get_size()).convert_alpha() + self.screen_navaids = pygame.Surface(self.screen.get_size()).convert_alpha() + self.screen_menus = pygame.Surface(self.screen.get_size()).convert_alpha() + self.screen_planes = pygame.Surface(self.screen.get_size()).convert_alpha() + self.screen_cmd = pygame.Surface(self.screen.get_size()).convert_alpha() + + self.screen_void.fill (colors.black) + self.screen_map.fill (colors.transparent) + self.screen_weather.fill(colors.transparent) + self.screen_navaids.fill(colors.transparent) + self.screen_menus.fill (colors.transparent) + self.screen_cmd.fill (colors.transparent) + + (back_image, back_rect) = utils.load_png(self.map.image) + self.screen_map.blit(back_image, (0, 0)) + + #blit! + self.screen.blit(self.screen_map, (0, 0)) + pygame.display.flip() + + self.planes = [] + self.planes.append(self.__random_plane()) + + self.weather = weather.Weather() + + self.mess = message.Message() + + self.cmd = "" + + def __del__(self): + self.map.cleanup() + + def __proc_cmd(self): + flno = cmd_plane(self.cmd) + com = cmd_cmd(self.cmd) + + flag = None + if (com!=None) and (flno!=None): + print "plane = " + flno + ", cmd = " + com + for p in self.planes: + if p.flightno.upper() == flno.upper(): + print "Found match!" + p.process(com) + flag = 1 + break + + if (flag == None): + # either not the right syntax, or we + # didn't find the right plane + print "Error, could not process command" + + self.cmd = "" + + def __proc_events(self): + for event in pygame.event.get(): + if event.type == pygame.QUIT: + self.mess.pickup_kill = 1 + sys.exit() + if event.type == pygame.KEYDOWN: + if event.key == pygame.K_a: + self.cmd += 'A' + if event.key == pygame.K_b: + self.cmd += 'B' + if event.key == pygame.K_c: + self.cmd += 'C' + if event.key == pygame.K_d: + self.cmd += 'D' + if event.key == pygame.K_e: + self.cmd += 'E' + if event.key == pygame.K_f: + self.cmd += 'F' + if event.key == pygame.K_g: + self.cmd += 'G' + if event.key == pygame.K_h: + self.cmd += 'H' + if event.key == pygame.K_i: + self.cmd += 'I' + if event.key == pygame.K_j: + self.cmd += 'J' + if event.key == pygame.K_k: + self.cmd += 'K' + if event.key == pygame.K_l: + self.cmd += 'L' + if event.key == pygame.K_m: + self.cmd += 'M' + if event.key == pygame.K_n: + self.cmd += 'N' + if event.key == pygame.K_o: + self.cmd += 'O' + if event.key == pygame.K_p: + self.cmd += 'P' + if event.key == pygame.K_q: + self.cmd += 'Q' + if event.key == pygame.K_r: + self.cmd += 'R' + if event.key == pygame.K_s: + self.cmd += 'S' + if event.key == pygame.K_t: + self.cmd += 'T' + if event.key == pygame.K_u: + self.cmd += 'U' + if event.key == pygame.K_v: + self.cmd += 'V' + if event.key == pygame.K_w: + self.cmd += 'W' + if event.key == pygame.K_x: + self.cmd += 'X' + if event.key == pygame.K_y: + self.cmd += 'Y' + if event.key == pygame.K_z: + self.cmd += 'Z' + + if (event.key == pygame.K_0) or (event.key == pygame.K_KP0): + self.cmd += '0' + if (event.key == pygame.K_1) or (event.key == pygame.K_KP1): + self.cmd += '1' + if (event.key == pygame.K_2) or (event.key == pygame.K_KP2): + self.cmd += '2' + if (event.key == pygame.K_3) or (event.key == pygame.K_KP3): + self.cmd += '3' + if (event.key == pygame.K_4) or (event.key == pygame.K_KP4): + self.cmd += '4' + if (event.key == pygame.K_5) or (event.key == pygame.K_KP5): + self.cmd += '5' + if (event.key == pygame.K_6) or (event.key == pygame.K_KP6): + self.cmd += '6' + if (event.key == pygame.K_7) or (event.key == pygame.K_KP7): + self.cmd += '7' + if (event.key == pygame.K_8) or (event.key == pygame.K_KP8): + self.cmd += '8' + if (event.key == pygame.K_9) or (event.key == pygame.K_KP9): + self.cmd += '9' + + if (event.key == pygame.K_COLON) or (event.key == pygame.K_SEMICOLON): + self.cmd += ': ' + + if event.key == pygame.K_SPACE: + self.cmd += " " + + if event.key == pygame.K_ESCAPE: + self.cmd = "" + if event.key == pygame.K_BACKSPACE: + self.cmd = self.cmd[:-1] + + if event.key == pygame.K_TAB: + pass # FIXME, tab complete call signs/flight numbers, commands, etc. + + if event.key == pygame.K_F1: + self.displays["map"] = 1 - self.displays["map"] + if event.key == pygame.K_F2: + self.displays["weather"] = 1 - self.displays["weather"] + if event.key == pygame.K_F3: + self.displays["navaids"] = 1 - self.displays["navaids"] + if event.key == pygame.K_F4: + self.displays["menus"] = 1 - self.displays["menus"] + if event.key == pygame.K_F5: + self.displays["planes"] = 1 - self.displays["planes"] + + if (event.key == pygame.K_KP_ENTER) or (event.key == pygame.K_RETURN): + if (self.cmd == "QUIT"): + self.mess.pickup_kill = 1 + return False + + if (self.cmd.__len__()): + self.__proc_cmd() + + return True + + def __random_plane(self): + callsign = "N1499H" + flightno = "Delta 79" + squawk = "1200" + type = "B747" + vel = (20, 20, 20) # FIXME: use the specs + + return plane.Plane(type=type, callsign=callsign, flightno=flightno, squawk=squawk, vel=vel) + + def run(self): + if not self.__proc_events(): return False + + # planes + self.screen_planes.fill(colors.transparent) + for p in self.planes: + p.display(self.screen_planes) + p.update() + if (p.status == plane.plane_CRASHED): + mess.write(self.screen_planes, p.callsign + ": PLANE CRASHED") + p.status = plane.plane_DEAD + + # weather + self.weather.display(self.screen_weather) + self.weather.update() + + # command + self.screen_cmd.fill(colors.transparent) + if (self.cmd): + font = pygame.font.Font(None, 18) + text = font.render(self.cmd, 1, colors.white) + self.screen_cmd.blit(text, (25, 735)) + + # blit what's needed + self.screen.blit(self.screen_void, (0, 0)) + if (self.displays["map"]): + self.screen.blit(self.screen_map, (0, 0)) + if (self.displays["weather"]): + self.screen.blit(self.screen_weather, (0, 0)) + if (self.displays["navaids"]): + self.screen.blit(self.screen_navaids, (0, 0)) + if (self.displays["menus"]): + self.screen.blit(self.screen_menus, (0, 0)) + if (self.displays["planes"]): + self.screen.blit(self.screen_planes, (0, 0)) + self.screen.blit(self.screen_cmd, (0, 0)) + + pygame.display.flip() + + return True + +def cmd_plane(cmd): + parts = cmd.split(': ') + + if (parts.__len__()>1): + return parts[0] + return None + +def cmd_cmd(cmd): + parts = cmd.split(': ') + + if (parts.__len__()>1): + return parts[1] + return None + +if (__name__ == '__main__'): + print "Don't do it!"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/atcgame/mainmenu.py Fri Jun 01 02:32:48 2007 -0400 @@ -0,0 +1,95 @@ +#/* +# * ATC - Air Traffic Controller simulation game +# * +# * Copyright (C) 2004-2007 Josef "Jeff" Sipek <jeffpc@josefsipek.net> +# * +# * This program is free software; you can redistribute it and/or modify +# * it under the terms of the GNU General Public License version 2 as +# * published by the Free Software Foundation. +# * +# * This program is distributed in the hope that it will be useful, +# * but WITHOUT ANY WARRANTY; without even the implied warranty of +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# * GNU General Public License for more details. +# */ + +version = "0.10-pre2" + +try: + import os + import sys + import threading + import pygame + + import config + + import colors + import utils + import plane + import message + + import single +except ImportError, err: + print "Couldn't load module %s" % (err) + sys.exit() + +size = width, height = 1024, 768 + +def main(datadir): + """ Main fn to run the main thread """ + + # Init + pygame.init() + screen = pygame.display.set_mode(size) + pygame.display.set_caption('Air Traffic Controller (' + version + ')') + + # background music + try: + pygame.mixer.init() + except pygame.error: + print "Could not init sound" + sys.exit(1) + #utils.playmusic("some.mp3"); + + # Set background + background = pygame.Surface(screen.get_size()).convert() + background.fill(colors.black) + + (splash_image, splash_rect) = utils.load_png(datadir+'/image/splash.png') + background.blit(splash_image, (0, 0)) + + #blit! + screen.blit(background, (0, 0)) + pygame.display.flip() + + optsel = 0 + menustr = ('single', 'multi', 'quit') + + while 1: + event = pygame.event.wait() + if event.type == pygame.QUIT: + sys.exit() + if event.type == pygame.KEYDOWN: + if event.key == pygame.K_UP: + optsel = (optsel + 2) % 3 + if event.key == pygame.K_DOWN: + optsel = (optsel + 1) % 3 + if event.key == pygame.K_RETURN: + optsel = optsel | 0x10 + if optsel == 0x12: + sys.exit() + + screen.blit(background, (0, 0)) + + (menu_img, menu_rect) = utils.load_png(datadir+'/image/menu_' + menustr[optsel & 3] + '.png') + screen.blit(menu_img, ((width - 334)/2.0, (height - 225)/2.0)) + + pygame.display.flip() + + if (optsel & 0x10): + optsel = optsel & 3 + if (optsel == 0): # single player + single.startgame(screen) + if (optsel == 1): # multi player + pass # FIXME: implement +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/atcgame/maps.py Fri Jun 01 02:32:48 2007 -0400 @@ -0,0 +1,107 @@ +#/* +# * ATC - Air Traffic Controller simulation game +# * +# * Copyright (C) 2004-2007 Josef "Jeff" Sipek <jeffpc@josefsipek.net> +# * +# * This program is free software; you can redistribute it and/or modify +# * it under the terms of the GNU General Public License version 2 as +# * published by the Free Software Foundation. +# * +# * This program is distributed in the hope that it will be useful, +# * but WITHOUT ANY WARRANTY; without even the implied warranty of +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# * GNU General Public License for more details. +# */ + +import os +import sys + +import config +import beacon + +def get_map_info(file): + fd = open(file, 'r') + s = fd.read() + fd.close() + + s = s.split("\n") + + return Map( float(s[0]), # centerx + float(s[1]), # centery + float(s[2]), # mperpix + float(s[3]), # atcx + float(s[4])) # atcy + +def get_navaids(file, m): + fd = open(file, 'r') + s = fd.read() + fd.close() + + s = s.split("\n") + + for line in s: + if (line.__len__() == 0): + continue + + line = line.split(" ") + + m.ninfo.append({ + "xoff": float(line[0]), + "yoff": float(line[1]), + "freq": float(line[2]), + "name": line[3], + "type": line[4], + "output": float(line[5])}) + +class Map: + def __init__(self, centerx, centery, mperpix, atcx, atcy): + self.centerx = centerx + self.centery = centery + self.mperpix = mperpix + self.atcx = atcx + self.atcy = atcy + + self.ninfo = [] + self.navs = [] + + self.image = "" + + def bootstrap(self): + for nav in self.ninfo: + if nav["type"] == "VOR": + self.navs.append(beacon.VOR_Beacon(nav["name"], nav["freq"], (nav["xoff"], nav["yoff"]))) + elif nav["type"] == "ILS": + pass # FIXME: ILS beacons are little complicated + + def cleanup(self): + while self.navs.__len__(): + self.navs.pop() + +MAPS = [] + +maps_dir = os.listdir(config.maps_dir) + +for map_dir in maps_dir: + files = os.listdir(config.maps_dir + "/" + map_dir) + + if (files.count("map.info") == 0): + print "Missing " + config.maps_dir + "/" + map_dir + "/map.info" + sys.exit(1) + + if (files.count("map.png") == 0): + print "Missing " + config.maps_dir + "/" + map_dir + "/map.png" + sys.exit(1) + + if (files.count("navaid.info") == 0): + print "Missing " + config.maps_dir + "/" + map_dir + "/navaid.info" + sys.exit(1) + + map_info = get_map_info(config.maps_dir + "/" + map_dir + "/map.info") + get_navaids(config.maps_dir + "/" + map_dir + "/navaid.info", map_info) + + map_info.image = config.maps_dir + "/" + map_dir + "/map.png" + + MAPS.append(map_info) + print "Parsed and added '" + map_dir + "' map (" + str(map_info.ninfo.__len__()) + " navaids)" + +print "Added " + str(MAPS.__len__()) + " maps"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/atcgame/message.py Fri Jun 01 02:32:48 2007 -0400 @@ -0,0 +1,118 @@ +#/* +# * ATC - Air Traffic Controller simulation game +# * +# * Copyright (C) 2004-2007 Josef "Jeff" Sipek <jeffpc@josefsipek.net> +# * +# * This program is free software; you can redistribute it and/or modify +# * it under the terms of the GNU General Public License version 2 as +# * published by the Free Software Foundation. +# * +# * This program is distributed in the hope that it will be useful, +# * but WITHOUT ANY WARRANTY; without even the implied warranty of +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# * GNU General Public License for more details. +# */ + +import pygame +import threading + +class Message: + """ Interface to display a message and beep it off in morse code """ + def __init__(self): + """ Set up everything """ + self.snd = {} + self.snd["."] = pygame.mixer.Sound('data/sound/sbeep.wav') + self.snd["."].set_volume(0.4) + self.snd["-"] = pygame.mixer.Sound('data/sound/lbeep.wav') + self.snd["-"].set_volume(0.4) + self.snd[" "] = pygame.mixer.Sound('data/sound/nosnd.wav') + self.snd[" "].set_volume(0.4) + + self.delay = {} + self.delay["."] = 250 + self.delay["-"] = 400 + self.delay[" "] = 330 + + self.morse = {} + self.morse["A"] = ".-" + self.morse["B"] = "-..." + self.morse["C"] = "-.-." + self.morse["D"] = "-.." + self.morse["E"] = "." + self.morse["F"] = "..-." + self.morse["G"] = "--." + self.morse["H"] = "...." + self.morse["I"] = ".." + self.morse["J"] = ".---" + self.morse["K"] = "-.-" + self.morse["L"] = ".-.." + self.morse["M"] = "--" + self.morse["N"] = "-." + self.morse["O"] = "---" + self.morse["P"] = ".--." + self.morse["Q"] = "--.-" + self.morse["R"] = ".-." + self.morse["S"] = "..." + self.morse["T"] = "-" + self.morse["U"] = "..-" + self.morse["V"] = "...-" + self.morse["W"] = ".--" + self.morse["X"] = "-..-" + self.morse["Y"] = "-.--" + self.morse["Z"] = "--.." + self.morse["1"] = ".----" + self.morse["2"] = "..---" + self.morse["3"] = "...--" + self.morse["4"] = "....-" + self.morse["5"] = "....." + self.morse["6"] = "-...." + self.morse["7"] = "--..." + self.morse["8"] = "---.." + self.morse["9"] = "----." + self.morse["0"] = "-----" + self.morse[" "] = " " + self.morse[":"] = "" + + self.statusmess = "" + + self.messages = [] + + self.pickup_kill = 0 + self.pickup_thread = threading.Timer(0, self.__pickup) + self.pickup_thread.start() + + def write(self,screen,mess): + """ Add message to the queue """ + code = "" + for letter in mess: + code += self.morse[letter] + " " + + self.messages.append(code) + + def __beep(self,char): + """ Beep one . or - """ + self.snd[char].play() + + def __beeper(self,mess): + """ Beep out all . and - in a message """ + for char in mess: + self.__beep(char) + pygame.time.delay(self.delay[char]) + if (self.pickup_kill): + return + + def __pickup(self): + """ The message pickup thread code, it picks up new messages + and calls the appropreate functions to beep them out """ + while(not self.pickup_kill): + try: + message = self.messages.pop() + self.statusmess = message + self.__beeper(message) + pygame.time.delay(1400) + self.statusmess = "" + except IndexError: + pass + + pygame.time.delay(100) +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/atcgame/plane.py Fri Jun 01 02:32:48 2007 -0400 @@ -0,0 +1,266 @@ +#/* +# * ATC - Air Traffic Controller simulation game +# * +# * Copyright (C) 2004-2007 Josef "Jeff" Sipek <jeffpc@josefsipek.net> +# * +# * This program is free software; you can redistribute it and/or modify +# * it under the terms of the GNU General Public License version 2 as +# * published by the Free Software Foundation. +# * +# * This program is distributed in the hope that it will be useful, +# * but WITHOUT ANY WARRANTY; without even the implied warranty of +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# * GNU General Public License for more details. +# */ + +import os +import sys +import time +import pygame +from math import * + +import config + +import colors +import utils +from plane_specs import * +from maps import * + +mperpix = MAPS[0].mperpix +mperdeg = 1852.0*60.0 # == 1852 m/min * 60 min/deg + +def m2pix(m): + """ Meters to pixels conversion fn """ + return m/mperpix + +def pix2m(pix): + """ Pixels to meters conversion fn """ + return pix*mperpix + +def pix2geo(pix): + return pix2m(pix)/mperdeg + +plane_OK = 0 +plane_RANGE = 1 +plane_CRASHED = 2 +plane_STALL = 3 +plane_DEAD = 4 +plane_PROXY = 5 + +class Plane(pygame.sprite.Sprite): + """ Class to manage one plane's motion """ + def __init__(self,type,callsign,flightno,squawk="1200",pos=(0.0, 0.0, 0.0),vel=(0.0, 0.0, 0.0)): + """ Set up everything """ + pygame.sprite.Sprite.__init__(self) + + # screen + screen = pygame.display.get_surface() + self.area = screen.get_rect() + + # plane image + (self.image, self.rect) = utils.load_png('data/image/plane.png') + + # call sign + self.callsign = callsign + + # flight number + self.flightno = flightno + + # squawk code (shoud be octal number digits 0-7 only) + self.squawk = squawk + + # position (X, Y, Z) + self.pos = { \ + "X":pos[0], \ + "Y":pos[1], \ + "Z":pos[2]} + + # target position + self.targetPos = { \ + "X":None, \ + "Y":None, \ + "Z":None} + + # velocity + self.vel = { \ + "i":vel[0], \ + "j":vel[1], \ + "k":vel[2], \ + + "heading":0, \ + "speed":0, \ + "climb":0} + self.complete_vel(ijk=1) + + self.timer = 0 # this is timeout the next time we try to recalculate position + + # plane specs + self.specs = None + for p in plane_SPECS: + if p['name'] == type: + self.specs= p + break + + if self.specs == None: + self.specs = plane_SPECS[0] + + # status flag + self.status = plane_OK + + def complete_vel(self, ijk=None, hac=None): + """ Recalculate all the velocity variables """ + if ijk == None and hac == None: + return + + if ijk == None: + self.vel["i"] = sin(utils.torad(self.vel["heading"])) * sqrt(self.vel["speed"]**2 - self.vel["climb"]**2) + self.vel["j"] = cos(utils.torad(self.vel["heading"])) * sqrt(self.vel["speed"]**2 - self.vel["climb"]**2) + self.vel["k"] = self.vel["climb"] + else: + self.vel["heading"] = self.calc_head() + self.vel["speed"] = sqrt(self.vel["i"]**2 + self.vel["j"]**2 + self.vel["k"]**2) + self.vel["climb"] = self.vel["k"] + + def update(self): + """ Move the plane, and check for collisions """ + if (self.timer > time.time()): + return + + if (self.status != plane_OK): + return + + self.pos["X"] += self.vel["i"]/10.0 + self.pos["Y"] += self.vel["j"]/10.0 + self.pos["Z"] += self.vel["k"]/10.0 + + # FIXME: need physics + if (self.targetPos["Z"] != None) and (self.pos["Z"] >= (self.targetPos["Z"] - 25)) and (self.pos["Z"] <= (self.targetPos["Z"] + 25)): + self.vel["climb"] = 0 + self.targetPos["Z"] = None + + self.complete_vel(hac=1) + + #if (self.pos["X"] < 0) or (self.pos["Y"] < 0): # FIXME: max values + # self.status = plane_RANGE + + if (self.pos["Z"] < 0): + self.vel["i"] = self.vel["j"] = self.vel["k"] = 0 + self.status = plane_CRASHED + + if (self.vel["speed"]<m2pix(self.specs["stall_speed"])) and (not self.status==plane_CRASHED): + self.status = plane_STALL + + self.rect.move(m2pix(self.vel["i"])/10.0,m2pix(self.vel["j"])/10.0) + + self.timer = time.time() + m2pix(self.vel["speed"])**-1/10.0 + + def calc_head(self): + """ Calculate heading of the airplane (in degrees)""" + if (self.vel["i"] == self.vel["j"] == 0): # no movement + return 0 + + try: + head = fabs(atan(self.vel["i"]/self.vel["j"])) # yes this is "backwards" + except ZeroDivisionError: + if (self.vel["i"]>0): + return 90 # east + return 270 # west + + head = abs(head*180/pi) + if self.vel["i"]>0 and self.vel["j"]>0: # quad I + return head + if self.vel["i"]<0 and self.vel["j"]>0: # quad II + return 360-head + if self.vel["i"]<0 and self.vel["j"]<0: # quad III + return 270-head + if self.vel["i"]>0 and self.vel["j"]<0: # quad IV + return 180-head + + if self.vel["i"]==0 and self.vel["j"]>0: # north + return 0 + return 180 # south + + def calc_geo(self, xy, prefix): + """ Return a geographic coodrinate-formated xy """ + df = m2pix(pix2geo(xy)) + minus = prefix[0] + if df<0: + df *= -1 + minus = prefix[1] + d = floor(df) + mf = (df-d) * 60.0 + m = floor(mf) + s = (mf-m) * 60.0 + + return "%s %dd %02dm %02ds" % (minus, int(d), int(m), int(s)) + + def process(self,cmd): + """ Process a user command """ + + # FIXME: convert multiple spaces to single + parts = cmd.split(' ') + + if (parts[0] == "ALT"): + print "Changing altitude to " + parts[1] + "m" + self.targetPos["Z"] = int(parts[1]) + self.vel["climb"] = self.specs["climb_normal"] + if (self.pos["Z"] > self.targetPos["Z"]): + self.vel["climb"] *= -1 + self.complete_vel(hac=1) + + if (parts[0] == "HEAD"): + self.vel["heading"] = int(parts[1]) + while(True): + if self.vel["heading"]>=360: + self.vel["heading"] -= 360 + elif self.vel["heading"]<0: + self.vel["heading"] += 360 + else: + break + + print "Changing heading to " + str(self.vel["heading"]) + self.complete_vel(hac=1) + + if (parts[0] == "SQUAWK"): + self.squawk = parts[1] + + def display(self,screen): + """ Put everything onto the screen """ + rotimage = pygame.transform.rotate(self.image, 360.0-self.vel["heading"]) + screen.blit(rotimage, (int(1024/2+m2pix(self.pos["X"])), int(768/2-m2pix(self.pos["Y"])))) + + # Plane info + font = pygame.font.Font(None, 16) + + color = colors.planeinfo_ok + if (self.status == plane_CRASHED) or (self.status == plane_DEAD): + color = colors.planeinfo_crashed + + if (self.squawk == "7700") or \ + (self.squawk == "7600") or \ + (self.squawk == "7500") or \ + (self.squawk == "0000"): + color = colors.planeinfo_emergency + + x = int(1024/2+m2pix(self.pos["X"])) + 10 + y = int(768/2-m2pix(self.pos["Y"])) - 5 + + if (config.plane_label == 2): + strings = ( self.flightno, + self.callsign + " " + str(self.status), + "Alt: " + str(int(self.pos["Z"])), + "Lat: " + self.calc_geo(self.pos["Y"],"NS"), + "Long: " + self.calc_geo(self.pos["X"],"EW"), + "Head: " + str(int(self.vel["heading"])), + "AS: " + str(int(self.vel["speed"]*3.6))) + else: + strings = ( self.flightno, + "%03d %03d" % (int(round(self.vel["heading"])), int(round(self.vel["speed"]*3.6))), + self.specs["name"]) + + for stri in strings: + alt = font.render(stri, 1, color) + altpos = alt.get_rect() + altpos.topleft = (x,y) + screen.blit(alt, altpos) + (x,y) = (x,y+10)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/atcgame/plane_specs.py Fri Jun 01 02:32:48 2007 -0400 @@ -0,0 +1,73 @@ +#/* +# * ATC - Air Traffic Controller simulation game +# * +# * Copyright (C) 2004-2007 Josef "Jeff" Sipek <jeffpc@josefsipek.net> +# * +# * This program is free software; you can redistribute it and/or modify +# * it under the terms of the GNU General Public License version 2 as +# * published by the Free Software Foundation. +# * +# * This program is distributed in the hope that it will be useful, +# * but WITHOUT ANY WARRANTY; without even the implied warranty of +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# * GNU General Public License for more details. +# */ + +import os +import sys +import config + +# all measurements in meters/sec or meters +plane_SPECS = [] + +plane_dir = os.listdir(config.plane_spec_dir) + +for f in plane_dir: + plane = {"fullname": str(None), + "name": str(None), + "heavy": False, + "jet": False, + "stall_speed": 0.0, + "climb_normal": 0.0, + "max_altitude": 0.0} + + fd = open(config.plane_spec_dir + "/" + f, 'r') + s = fd.read() + fd.close() + + s = s.split("\n") + for line in s: + if (line.__len__()==0): + continue + + cmd = line[:line.find(' ')-1] + param = line[line.find(' ')+1:] + + if cmd=="FullName": + plane["fullname"] = param + elif cmd=="Name": + plane["name"] = param + elif cmd=="Heavy": + if (param.upper() == "YES"): + plane["heavy"] = True + else: + plane["heavy"] = False + elif cmd=="Jet": + if (param.upper() == "YES"): + plane["jet"] = True + else: + plane["jet"] = False + elif cmd=="StallSpeed": + plane["stall_speed"] = float(param) + elif cmd=="ClimbNormal": + plane["climb_normal"] = float(param) + elif cmd=="MaxAltitude": + plane["max_altitude"] = float(param) + else: + print "Error parsing " + f + ": Unknown statement" + sys.exit(1) + + plane_SPECS.append(plane) + print "Parsed and added " + plane["fullname"] + " aka. " + plane["name"] + +print "Added " + str(plane_SPECS.__len__()) + " plane types"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/atcgame/single.py Fri Jun 01 02:32:48 2007 -0400 @@ -0,0 +1,49 @@ +#/* +# * ATC - Air Traffic Controller simulation game +# * +# * Copyright (C) 2004-2007 Josef "Jeff" Sipek <jeffpc@josefsipek.net> +# * +# * This program is free software; you can redistribute it and/or modify +# * it under the terms of the GNU General Public License version 2 as +# * published by the Free Software Foundation. +# * +# * This program is distributed in the hope that it will be useful, +# * but WITHOUT ANY WARRANTY; without even the implied warranty of +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# * GNU General Public License for more details. +# */ + +import pygame +import sys + +import colors +import utils +import plane +import message +import game + +from maps import * + +def startgame(screen): + """ Main fn to run the single player game """ + g = game.Game(screen) + + while g.run(): pass + + +def cmd_plane(cmd): + parts = cmd.split(': ') + + if (parts.__len__()>1): + return parts[0] + return None + +def cmd_cmd(cmd): + parts = cmd.split(': ') + + if (parts.__len__()>1): + return parts[1] + return None + +if (__name__ == '__main__'): + print "Don't do it!"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/atcgame/utils.py Fri Jun 01 02:32:48 2007 -0400 @@ -0,0 +1,46 @@ +#/* +# * ATC - Air Traffic Controller simulation game +# * +# * Copyright (C) 2004-2007 Josef "Jeff" Sipek <jeffpc@josefsipek.net> +# * +# * This program is free software; you can redistribute it and/or modify +# * it under the terms of the GNU General Public License version 2 as +# * published by the Free Software Foundation. +# * +# * This program is distributed in the hope that it will be useful, +# * but WITHOUT ANY WARRANTY; without even the implied warranty of +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# * GNU General Public License for more details. +# */ + +import pygame +import os +from math import * + +def load_png(name): + """ Load image and return image object""" + fullname = os.path.join('', name) + try: + image = pygame.image.load(fullname) + if image.get_alpha() is None: + image = image.convert() + else: + image = image.convert_alpha() + except pygame.error, message: + print 'Cannot load image:', fullname + raise SystemExit, message + return image, image.get_rect() + +def todeg(rad): + """ Convert radians to degrees """ + return rad*180.0/pi + +def torad(deg): + """ Convert degrees to radians """ + return deg*pi/180.0 + +def playmusic(name,vol=0.1): + """ Start playing background music """ + pygame.mixer.music.load('data/sound/' + name) + pygame.mixer.music.set_volume(vol) + pygame.mixer.music.play()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/atcgame/weather.py Fri Jun 01 02:32:48 2007 -0400 @@ -0,0 +1,36 @@ +#/* +# * ATC - Air Traffic Controller simulation game +# * +# * Copyright (C) 2004-2007 Josef "Jeff" Sipek <jeffpc@josefsipek.net> +# * +# * This program is free software; you can redistribute it and/or modify +# * it under the terms of the GNU General Public License version 2 as +# * published by the Free Software Foundation. +# * +# * This program is distributed in the hope that it will be useful, +# * but WITHOUT ANY WARRANTY; without even the implied warranty of +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# * GNU General Public License for more details. +# */ + +import os +import sys +import time +import pygame +from math import * + +import config + +import colors +import utils + +class Weather: + """ Class to manage the weather """ + def __init__(self): + pass + + def update(self): + pass + + def display(self, screen): + pass
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/maps/default/map.info Fri Jun 01 02:32:48 2007 -0400 @@ -0,0 +1,5 @@ +0 +0 +37.5 +323 +324
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/maps/default/navaid.info Fri Jun 01 02:32:48 2007 -0400 @@ -0,0 +1,2 @@ +100 100 112.825 N1 VOR 7500 +-50 200 112.225 N2 VOR 7500
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/planes/B747.info Fri Jun 01 02:32:48 2007 -0400 @@ -0,0 +1,8 @@ +FullName: Boeing 747-400 +Name: B747 +Heavy: yes +Jet: yes +StallSpeed: 66.878 +ClimbNormal: 10.16 +MaxAltitude: 13000 +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/planes/PA28.info Fri Jun 01 02:32:48 2007 -0400 @@ -0,0 +1,7 @@ +FullName: Piper PA28-140 Cherokee +Name: PA28 +Heavy: no +Jet: no +StallSpeed: 16.667 +ClimbNormal: 2.54 +MaxAltitude: 6500
--- a/maps/default/map.info Fri Jun 01 02:08:43 2007 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,5 +0,0 @@ -0 -0 -37.5 -323 -324
--- a/maps/default/navaid.info Fri Jun 01 02:08:43 2007 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ -100 100 112.825 N1 VOR 7500 --50 200 112.225 N2 VOR 7500