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
Binary file data/maps/default/map.png has changed
Binary file data/maps/default/map.xcf has changed
--- /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
Binary file maps/default/map.png has changed
Binary file maps/default/map.xcf has changed
--- 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
--- a/planes/B747.info	Fri Jun 01 02:08:43 2007 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,8 +0,0 @@
-FullName: Boeing 747-400
-Name: B747
-Heavy: yes
-Jet: yes
-StallSpeed: 66.878
-ClimbNormal: 10.16
-MaxAltitude: 13000
-
--- a/planes/PA28.info	Fri Jun 01 02:08:43 2007 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,7 +0,0 @@
-FullName: Piper PA28-140 Cherokee
-Name: PA28
-Heavy: no
-Jet: no
-StallSpeed: 16.667
-ClimbNormal: 2.54
-MaxAltitude: 6500