merge 0.9.14.1 to trunk
authornemo
Sun, 14 Nov 2010 15:06:02 -0500
changeset 4337 85e02b1a8e8f
parent 4331 6b946b93761f (diff)
parent 4335 c279aeb615df (current diff)
child 4339 58d4733c9cad
merge 0.9.14.1 to trunk
CMakeLists.txt
QTfrontend/gamecfgwidget.cpp
QTfrontend/gamecfgwidget.h
QTfrontend/hwconsts.cpp.in
QTfrontend/mapContainer.cpp
QTfrontend/mapContainer.h
gameServer/Actions.hs
gameServer/CoreTypes.hs
gameServer/HWProtoCore.hs
gameServer/HWProtoInRoomState.hs
gameServer/HWProtoLobbyState.hs
gameServer/HWProtoNEState.hs
gameServer/OfficialServer/DBInteraction.hs
gameServer/Opts.hs
gameServer/Utils.hs
hedgewars/GSHandlers.inc
hedgewars/uConsts.pas
hedgewars/uGears.pas
--- a/CMakeLists.txt	Sun Nov 14 20:37:55 2010 +0100
+++ b/CMakeLists.txt	Sun Nov 14 15:06:02 2010 -0500
@@ -4,38 +4,38 @@
 cmake_policy(SET CMP0003 NEW)
 
 IF(POLICY CMP0012)
-    cmake_policy(SET CMP0012 NEW)
+	cmake_policy(SET CMP0012 NEW)
 ENDIF()
 
-#detect subversion revision (if present)
-#set(version_suffix "-dev") #UNSET THIS VARIABLE AT RELEASE TIME
+#detect Mercurial revision (if present)
+set(version_suffix "-dev") #UNSET THIS VARIABLE AT RELEASE TIME
 IF(version_suffix MATCHES "-dev")
-    set(HW_DEV true)
-    IF (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.hg)
-        FIND_PROGRAM(HGCOMMAND hg)
-        IF(HGCOMMAND)
-            exec_program(${HGCOMMAND}
-                    ARGS identify -in ${CMAKE_CURRENT_SOURCE_DIR}
-                    OUTPUT_VARIABLE version_suffix
-                    )
-            STRING(REGEX REPLACE "(.*) +(.*)" "\\2:\\1" version_suffix ${version_suffix})
-            MESSAGE(STATUS "Building revision ${version_suffix}")
-            set(version_suffix ".${version_suffix}")
+	set(HW_DEV true)
+	IF (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.hg)
+		FIND_PROGRAM(HGCOMMAND hg)
+		IF(HGCOMMAND)
+			exec_program(${HGCOMMAND}
+				     ARGS identify -in ${CMAKE_CURRENT_SOURCE_DIR}
+				     OUTPUT_VARIABLE version_suffix
+				     )
+			STRING(REGEX REPLACE "(.*) +(.*)" "\\2:\\1" version_suffix ${version_suffix})
+			MESSAGE(STATUS "Building revision ${version_suffix}")
+			set(version_suffix ".${version_suffix}")
 #			#truncate to numbers only - trying to fix a problem described in http://www.hedgewars.org/node/2019
 #			STRING(REGEX REPLACE "^\\.(\\d+)" ".\\1" version_suffix ${version_suffix})
 #			# screw whole suffix if there's no number
 #			STRING(REGEX REPLACE "^\\.([a-z]+.*)" "-dev" version_suffix ${version_suffix})
-        ENDIF()
-    ENDIF()
+		ENDIF()
+	ENDIF()
 ELSE()
-    set(HW_DEV false)
+	set(HW_DEV false)
 ENDIF()
 
 set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules)
 
 set(CPACK_PACKAGE_VERSION_MAJOR "0")
 set(CPACK_PACKAGE_VERSION_MINOR "9")
-set(CPACK_PACKAGE_VERSION_PATCH "14.1${version_suffix}")
+set(CPACK_PACKAGE_VERSION_PATCH "15${version_suffix}")
 
 #forbid in-tree building
 #IF (${CMAKE_SOURCE_DIR} MATCHES ${CMAKE_BINARY_DIR})
@@ -46,80 +46,80 @@
 
 #set some safe values
 IF(NOT WITH_SERVER)
-    SET(WITH_SERVER 0)
+	SET(WITH_SERVER 0)
 ENDIF(NOT WITH_SERVER)
 IF(NOT BUILD_ENGINE_LIBRARY)
-    SET(BUILD_ENGINE_LIBRARY 0)
+	SET(BUILD_ENGINE_LIBRARY 0)
 ENDIF(NOT BUILD_ENGINE_LIBRARY)
 
 
 if(APPLE)
-    set(CMAKE_FIND_FRAMEWORK "FIRST")
+	set(CMAKE_FIND_FRAMEWORK "FIRST")
 
-    #paths for creating the bundle
-    set(bundle_name Hedgewars.app)
-    set(CMAKE_INSTALL_PREFIX ${bundle_name}/Contents/MacOS/)
-    set(DATA_INSTALL_DIR "../Resources/")
-    set(target_dir ".")
+	#paths for creating the bundle
+	set(bundle_name Hedgewars.app)
+	set(CMAKE_INSTALL_PREFIX ${bundle_name}/Contents/MacOS/)
+	set(DATA_INSTALL_DIR "../Resources/")
+	set(target_dir ".")
 
-    #what system are we building for
-    set(minimum_macosx $ENV{MACOSX_DEPLOYMENT_TARGET})
+	#what system are we building for
+	set(minimum_macosx $ENV{MACOSX_DEPLOYMENT_TARGET})
 
-    #detect on which system are we
-    EXEC_PROGRAM("/usr/bin/sw_vers" OUTPUT_VARIABLE MACOSX_VERSION_TMP)
-    STRING(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+" MACOSX_VERSION_TMP "${MACOSX_VERSION_TMP}")
-    STRING(REGEX REPLACE "([0-9]+.[0-9]+).[0-9]+" "\\1" current_macosx_version ${MACOSX_VERSION_TMP})
+	#detect on which system are we
+	EXEC_PROGRAM("/usr/bin/sw_vers" OUTPUT_VARIABLE MACOSX_VERSION_TMP)
+	STRING(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+" MACOSX_VERSION_TMP "${MACOSX_VERSION_TMP}")
+	STRING(REGEX REPLACE "([0-9]+.[0-9]+).[0-9]+" "\\1" current_macosx_version ${MACOSX_VERSION_TMP})
 
-    if(NOT minimum_macosx)
-        #if nothing is set, we deploy only for the current system
-        set(minimum_macosx ${current_macosx_version})
-    endif()
+	if(NOT minimum_macosx)
+		#if nothing is set, we deploy only for the current system
+		set(minimum_macosx ${current_macosx_version})
+	endif()
 
-    if (minimum_macosx LESS "10.4")
-        set(FATAL "Hedgewars is not supported for pre-10.4 systems")
-    endif()
+	if (minimum_macosx LESS "10.4")
+		set(FATAL "Hedgewars is not supported for pre-10.4 systems")
+	endif()
 
-    set(CMAKE_OSX_ARCHITECTURES "i386;ppc7400")
+	set(CMAKE_OSX_ARCHITECTURES "i386;ppc7400")
 
 
-    #create universal binaries only when it's time to bundle the application, also build server
-    IF(BUNDLE)
-        set(WITH_SERVER true)
-        if(NOT minimum_macosx MATCHES "10.6")
-            set(CMAKE_C_COMPILER "gcc-4.0")
-            set(CMAKE_CXX_COMPILER "g++-4.0")
-        else()
-            if(current_macosx_version MATCHES "10.6")
-                set(CMAKE_OSX_ARCHITECTURES "x86_64")
-            endif()
-        endif()
-    ELSE()
-        if(current_macosx_version MATCHES "10.6")
-            set(CMAKE_OSX_ARCHITECTURES "x86_64")
-        endif()
-    ENDIF()
+	#create universal binaries only when it's time to bundle the application, also build server
+	IF(BUNDLE)
+		set(WITH_SERVER true)
+		if(NOT minimum_macosx MATCHES "10.6")
+			set(CMAKE_C_COMPILER "gcc-4.0")
+			set(CMAKE_CXX_COMPILER "g++-4.0")
+		else()
+			if(current_macosx_version MATCHES "10.6")
+				set(CMAKE_OSX_ARCHITECTURES "x86_64")
+			endif()
+		endif()
+	ELSE()
+		if(current_macosx_version MATCHES "10.6")
+			set(CMAKE_OSX_ARCHITECTURES "x86_64")
+		endif()
+	ENDIF()
 
-    message(STATUS "Target system: Mac OS X ${minimum_macosx} ${CMAKE_OSX_ARCHITECTURES}")
+	message(STATUS "Target system: Mac OS X ${minimum_macosx} ${CMAKE_OSX_ARCHITECTURES}")
 
-    if(minimum_macosx MATCHES "10.4")
-        set(CMAKE_OSX_SYSROOT "/Developer/SDKs/MacOSX10.4u.sdk/")
-        if(current_macosx_version MATCHES "10.4")
-            find_package(SDL_mixer REQUIRED)
-            set(pascal_compiler_flags_cmn "-k-dylib_file @loader_path/Frameworks/smpeg.framework/Versions/A/smpeg:${SDLMIXER_LIBRARY}/Versions/A/Frameworks/smpeg.framework/Versions/A/smpeg" "-k-dylib_file @loader_path/Frameworks/mikmod.framework/Versions/A/mikmod:${SDLMIXER_LIBRARY}/Versions/A/Frameworks/mikmod.framework/Versions/A/mikmod" ${pascal_compiler_flags_cmn})
-            set(CMAKE_C_FLAGS "-dylib_file @loader_path/Frameworks/smpeg.framework/Versions/A/smpeg:${SDLMIXER_LIBRARY}/Versions/A/Frameworks/smpeg.framework/Versions/A/smpeg -dylib_file @loader_path/Frameworks/mikmod.framework/Versions/A/mikmod:${SDLMIXER_LIBRARY}/Versions/A/Frameworks/mikmod.framework/Versions/A/mikmod")
-        endif()
-    else()
-        set(CMAKE_OSX_SYSROOT "/Developer/SDKs/MacOSX${minimum_macosx}.sdk/")
-    endif()
+	if(minimum_macosx MATCHES "10.4")
+		set(CMAKE_OSX_SYSROOT "/Developer/SDKs/MacOSX10.4u.sdk/")
+		if(current_macosx_version MATCHES "10.4")
+			find_package(SDL_mixer REQUIRED)
+			set(pascal_compiler_flags_cmn "-k-dylib_file @loader_path/Frameworks/smpeg.framework/Versions/A/smpeg:${SDLMIXER_LIBRARY}/Versions/A/Frameworks/smpeg.framework/Versions/A/smpeg" "-k-dylib_file @loader_path/Frameworks/mikmod.framework/Versions/A/mikmod:${SDLMIXER_LIBRARY}/Versions/A/Frameworks/mikmod.framework/Versions/A/mikmod" ${pascal_compiler_flags_cmn})
+			set(CMAKE_C_FLAGS "-dylib_file @loader_path/Frameworks/smpeg.framework/Versions/A/smpeg:${SDLMIXER_LIBRARY}/Versions/A/Frameworks/smpeg.framework/Versions/A/smpeg -dylib_file @loader_path/Frameworks/mikmod.framework/Versions/A/mikmod:${SDLMIXER_LIBRARY}/Versions/A/Frameworks/mikmod.framework/Versions/A/mikmod")
+		endif()
+	else()
+		set(CMAKE_OSX_SYSROOT "/Developer/SDKs/MacOSX${minimum_macosx}.sdk/")
+	endif()
 
-    #1.set deployment target; 2.link with libsdlmain.a (when building an executable); 3.link with liblua.a (which requires readline)
-    set(pascal_compiler_flags_cmn "-k-macosx_version_min" "-k${minimum_macosx}" "-XR${CMAKE_OSX_SYSROOT}" ${pascal_compiler_flags_cmn})
-    if(NOT BUILD_ENGINE_LIBRARY)
-        set(pascal_compiler_flags_cmn "-k${CMAKE_BINARY_DIR}/bin/libSDLmain.a" ${pascal_compiler_flags_cmn})
-    endif()
-    set(pascal_compiler_flags_cmn "-k${CMAKE_BINARY_DIR}/bin/liblua.a" "-k-lreadline" ${pascal_compiler_flags_cmn})
+	#1.set deployment target; 2.link with libsdlmain.a (when building an executable); 3.link with liblua.a (which requires readline)
+	set(pascal_compiler_flags_cmn "-k-macosx_version_min" "-k${minimum_macosx}" "-XR${CMAKE_OSX_SYSROOT}" ${pascal_compiler_flags_cmn})
+	if(NOT BUILD_ENGINE_LIBRARY)
+		set(pascal_compiler_flags_cmn "-k${CMAKE_BINARY_DIR}/bin/libSDLmain.a" ${pascal_compiler_flags_cmn})
+	endif()
+	set(pascal_compiler_flags_cmn "-k${CMAKE_BINARY_DIR}/bin/liblua.a" "-k-lreadline" ${pascal_compiler_flags_cmn})
 else(APPLE)
-    set(target_dir "bin")
+	set(target_dir "bin")
 endif(APPLE)
 
 
@@ -128,16 +128,16 @@
 #	SET(CMAKE_BUILD_TYPE "Release")
 #ENDIF (NOT CMAKE_BUILD_TYPE)
 if (NOT CMAKE_BUILD_TYPE)
-    set (CMAKE_BUILD_TYPE RELEASE CACHE STRING "Choose the type of build, options are: None Debug Release." FORCE)
+	set (CMAKE_BUILD_TYPE RELEASE CACHE STRING "Choose the type of build, options are: None Debug Release." FORCE)
 endif (NOT CMAKE_BUILD_TYPE)
 
 if(CMAKE_BUILD_TYPE MATCHES RELEASE OR CMAKE_BUILD_TYPE MATCHES "Release")
-    message(STATUS "Building Release")
-    set(Optz true)
+	message(STATUS "Building Release")
+	set(Optz true)
 else()
-    message(STATUS "Building Debug")
-    #set(CMAKE_VERBOSE_MAKEFILE true)
-    set(Optz false)
+	message(STATUS "Building Debug")
+	#set(CMAKE_VERBOSE_MAKEFILE true)
+	set(Optz false)
 endif()
 
 
@@ -153,40 +153,40 @@
 
 if(Optz)
 #	set(pascal_compiler_flags_cmn "-O3" "-OpPENTIUM4" "-CfSSE3" "-Xs" "-Si" ${pascal_compiler_flags_cmn})
-    set(pascal_compiler_flags_cmn "-O2" "-Xs" "-Si" ${pascal_compiler_flags_cmn})
-    set(haskell_compiler_flags_cmn "-O2" "-w")
+	set(pascal_compiler_flags_cmn "-O2" "-Xs" "-Si" ${pascal_compiler_flags_cmn})
+	set(haskell_compiler_flags_cmn "-O2" "-w")
 else(Optz)
-    set(pascal_compiler_flags_cmn "-O-" "-g" "-gh" "-gl" "-dDEBUGFILE" ${pascal_compiler_flags_cmn})
-    set(haskell_compiler_flags_cmn "-Wall" "-debug" "-dcore-lint")
+	set(pascal_compiler_flags_cmn "-O-" "-g" "-gh" "-gl" "-dDEBUGFILE" ${pascal_compiler_flags_cmn})
+	set(haskell_compiler_flags_cmn "-Wall" "-debug" "-dcore-lint")
 endif(Optz)
 
 
 if(DEFINED DATA_INSTALL_DIR)
-    set(SHAREPATH ${DATA_INSTALL_DIR}/hedgewars/)
+	set(SHAREPATH ${DATA_INSTALL_DIR}/hedgewars/)
 else()
-    set(SHAREPATH share/hedgewars/)
+	set(SHAREPATH share/hedgewars/)
 endif()
 set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
 
 set(HEDGEWARS_VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}")
-set(HEDGEWARS_PROTO_VER 35)
+set(HEDGEWARS_PROTO_VER 34)
 
 if(WITH_SERVER)
-    message(STATUS "Server is going to be built! Make sure you have GHC installed")
-    set(HAVE_NETSERVER true)
-    add_subdirectory(gameServer)
+	message(STATUS "Server is going to be built! Make sure you have GHC installed")
+	set(HAVE_NETSERVER true)
+	add_subdirectory(gameServer)
 else(WITH_SERVER)
-    set(HAVE_NETSERVER false)
+	set(HAVE_NETSERVER false)
 endif(WITH_SERVER)
 
 add_subdirectory(misc/liblua)
 add_subdirectory(hedgewars)
 #add_subdirectory(misc/libopenalbridge)
 if(NOT BUILD_ENGINE_LIBRARY)
-    add_subdirectory(bin)
-    add_subdirectory(QTfrontend)
-    add_subdirectory(share)
-    add_subdirectory(tools)
+	add_subdirectory(bin)
+	add_subdirectory(QTfrontend)
+	add_subdirectory(share)
+	add_subdirectory(tools)
 endif()
 
 # CPack vars
@@ -201,70 +201,70 @@
 set(CPACK_PACKAGE_INSTALL_DIRECTORY "Hedgewars ${HEDGEWARS_VERSION}")
 
 if(WIN32 AND NOT UNIX)
-    set(CPACK_NSIS_DISPLAY_NAME "Hedgewars")
-    set(CPACK_NSIS_HELP_LINK "http://www.hedgewars.org/")
-    set(CPACK_NSIS_URL_INFO_ABOUT "http://www.hedgewars.org/")
-    set(CPACK_NSIS_CONTACT "unC0Rr@gmail.com")
-    set(CPACK_NSIS_MODIFY_PATH OFF)
-    set(CPACK_GENERATOR "ZIP;NSIS")
-    set(CPACK_PACKAGE_INSTALL_REGISTRY_KEY "hedgewars")
+	set(CPACK_NSIS_DISPLAY_NAME "Hedgewars")
+	set(CPACK_NSIS_HELP_LINK "http://www.hedgewars.org/")
+	set(CPACK_NSIS_URL_INFO_ABOUT "http://www.hedgewars.org/")
+	set(CPACK_NSIS_CONTACT "unC0Rr@gmail.com")
+	set(CPACK_NSIS_MODIFY_PATH OFF)
+	set(CPACK_GENERATOR "ZIP;NSIS")
+	set(CPACK_PACKAGE_INSTALL_REGISTRY_KEY "hedgewars")
 else(WIN32 AND NOT UNIX)
-    set(CPACK_STRIP_FILES "bin/hedgewars;bin/hwengine")
+	set(CPACK_STRIP_FILES "bin/hedgewars;bin/hwengine")
 endif(WIN32 AND NOT UNIX)
 
 set(CPACK_SOURCE_IGNORE_FILES
-    "~"
-    "\\\\.hg"
-    "\\\\.svn"
-    "\\\\.exe$"
-    "\\\\.a$"
-    "\\\\.dll$"
-    "\\\\.xcf$"
-    "\\\\.cxx$"
-    "\\\\.db$"
-    "\\\\.dof$"
-    "\\\\.layout$"
-    "\\\\.zip$"
-    "\\\\.gz$"
-    "\\\\.bz2$"
-    "\\\\.tmp$"
-    "\\\\.core$"
-    "\\\\.sh$"
-    "\\\\.sifz$"
-    "\\\\.svg$"
-    "\\\\.svgz$"
-    "\\\\.ppu$"
-    "\\\\.psd$"
-    "\\\\.o$"
-    "Makefile"
-    "Doxyfile"
-    "CMakeFiles"
-    "debug"
-    "release$"
-    "Debug$"
-    "Release$"
-    "proto.inc$"
-    "hwconsts.cpp$"
-    "playlist.inc$"
-    "CPack"
-    "cmake_install.cmake$"
-    "config.inc$"
-    "hwengine.desktop$"
+	"~"
+	"\\\\.hg"
+	"\\\\.svn"
+	"\\\\.exe$"
+	"\\\\.a$"
+	"\\\\.dll$"
+	"\\\\.xcf$"
+	"\\\\.cxx$"
+	"\\\\.db$"
+	"\\\\.dof$"
+	"\\\\.layout$"
+	"\\\\.zip$"
+	"\\\\.gz$"
+	"\\\\.bz2$"
+	"\\\\.tmp$"
+	"\\\\.core$"
+	"\\\\.sh$"
+	"\\\\.sifz$"
+	"\\\\.svg$"
+	"\\\\.svgz$"
+	"\\\\.ppu$"
+	"\\\\.psd$"
+	"\\\\.o$"
+	"Makefile"
+	"Doxyfile"
+	"CMakeFiles"
+	"debug"
+	"release$"
+	"Debug$"
+	"Release$"
+	"proto.inc$"
+	"hwconsts.cpp$"
+	"playlist.inc$"
+	"CPack"
+	"cmake_install.cmake$"
+	"config.inc$"
+	"hwengine.desktop$"
 #   "^${CMAKE_CURRENT_SOURCE_DIR}/misc/libopenalbridge"
-    "^${CMAKE_CURRENT_SOURCE_DIR}/project_files/HedgewarsMobile/"
-    "^${CMAKE_CURRENT_SOURCE_DIR}/bin/[a-z]"
-    "^${CMAKE_CURRENT_SOURCE_DIR}/tools/templates"
-    "^${CMAKE_CURRENT_SOURCE_DIR}/doc"
-    "^${CMAKE_CURRENT_SOURCE_DIR}/templates"
-    "^${CMAKE_CURRENT_SOURCE_DIR}/Graphics"
-    "^${CMAKE_CURRENT_SOURCE_DIR}/realtest"
-    "^${CMAKE_CURRENT_SOURCE_DIR}/tmp"
-    "^${CMAKE_CURRENT_SOURCE_DIR}/utils"
-    "^${CMAKE_CURRENT_SOURCE_DIR}/share/hedgewars/Data/Maps/test"
-    "^${CMAKE_CURRENT_SOURCE_DIR}/share/hedgewars/Data/Themes/ethereal"
-    "^${CMAKE_CURRENT_SOURCE_DIR}/install_manifest.txt"
-    "^${CMAKE_CURRENT_SOURCE_DIR}/CMakeCache.txt"
-    "^${CMAKE_CURRENT_SOURCE_DIR}/hedgewars\\\\."
+	"^${CMAKE_CURRENT_SOURCE_DIR}/project_files/HedgewarsMobile/"
+	"^${CMAKE_CURRENT_SOURCE_DIR}/bin/[a-z]"
+	"^${CMAKE_CURRENT_SOURCE_DIR}/tools/templates"
+	"^${CMAKE_CURRENT_SOURCE_DIR}/doc"
+	"^${CMAKE_CURRENT_SOURCE_DIR}/templates"
+	"^${CMAKE_CURRENT_SOURCE_DIR}/Graphics"
+	"^${CMAKE_CURRENT_SOURCE_DIR}/realtest"
+	"^${CMAKE_CURRENT_SOURCE_DIR}/tmp"
+	"^${CMAKE_CURRENT_SOURCE_DIR}/utils"
+	"^${CMAKE_CURRENT_SOURCE_DIR}/share/hedgewars/Data/Maps/test"
+	"^${CMAKE_CURRENT_SOURCE_DIR}/share/hedgewars/Data/Themes/ethereal"
+	"^${CMAKE_CURRENT_SOURCE_DIR}/install_manifest.txt"
+	"^${CMAKE_CURRENT_SOURCE_DIR}/CMakeCache.txt"
+	"^${CMAKE_CURRENT_SOURCE_DIR}/hedgewars\\\\."
 )
 
 include(CPack)
--- a/ChangeLog.txt	Sun Nov 14 20:37:55 2010 +0100
+++ b/ChangeLog.txt	Sun Nov 14 15:06:02 2010 -0500
@@ -11,14 +11,14 @@
  + New maps: ShoppaKing, Sticks, TrophyRace (Mission)
  + New utilities: Portal Gun, Resurrector
  + New weapons: Flamethrower, Hammer, Old Limburger, Piano Strike, Sticky Mines
- + Weapons' projectiles will how be launched from their barrels instead of the hog's center
+ + Weapons' projectiles will now be launched from their barrels instead of the hog's center
  + Flying Saucers may be used for moving below the water surface
  + New default game schemes: Clean Slate, Fort Mode, Timeless, Thinking with Portals, King Mode
  + New default weapon set: Clean Slate, Thinking with Portals
  + Bomb clusters/Melon parts inherit some of the original bomb's speed
  + Extended game statistics
  + Improved health bar updating
- + Hogs that blow themselves up will use triggers in they team color
+ + Hogs that blow themselves up will use triggers in their team color
  + Settings allow better control over the level of details/effects
  + Improved Lua support
  + On empty ammo switch to no weapon rather than the first available one (to avoid shooting by accident)
--- a/QTfrontend/ammoSchemeModel.cpp	Sun Nov 14 20:37:55 2010 +0100
+++ b/QTfrontend/ammoSchemeModel.cpp	Sun Nov 14 15:06:02 2010 -0500
@@ -45,19 +45,21 @@
         << QVariant(false)         // inf. attack    19
         << QVariant(false)         // reset weps     20
         << QVariant(false)         // per hog ammo   21
-        << QVariant(100)           // damage modfier 22
-        << QVariant(45)            // turn time      23
-        << QVariant(100)           // init health    24
-        << QVariant(15)            // sudden death   25
-        << QVariant(5)             // case prob      26
-        << QVariant(3)             // mines time     27
-        << QVariant(4)             // mines number   28
-        << QVariant(0)             // mine dud pct   29
-        << QVariant(2)             // explosives     30
-        << QVariant(35)            // health case pct 31
-        << QVariant(25)            // health case amt 32
-        << QVariant(47)            // water rise amt 33
-        << QVariant(5)             // health dec amt 34
+        << QVariant(false)         // no wind        22
+        << QVariant(false)         // more wind      23
+        << QVariant(100)           // damage modfier 24
+        << QVariant(45)            // turn time      25
+        << QVariant(100)           // init health    26
+        << QVariant(15)            // sudden death   27
+        << QVariant(5)             // case prob      28
+        << QVariant(3)             // mines time     29
+        << QVariant(4)             // mines number   30
+        << QVariant(0)             // mine dud pct   31
+        << QVariant(2)             // explosives     32
+        << QVariant(35)            // health case pct 33
+        << QVariant(25)            // health case amt 34
+        << QVariant(47)            // water rise amt 35
+        << QVariant(5)             // health dec amt 36
         ;
 
 AmmoSchemeModel::AmmoSchemeModel(QObject* parent, const QString & fileName) :
@@ -103,19 +105,21 @@
         << "infattack"        // 19
         << "resetweps"        // 20
         << "perhogammo"       // 21
-        << "damagefactor"     // 22
-        << "turntime"         // 23
-        << "health"           // 24
-        << "suddendeath"      // 25
-        << "caseprobability"  // 26
-        << "minestime"        // 27
-        << "minesnum"         // 28
-        << "minedudpct"       // 29
-        << "explosives"       // 30
-        << "healthprobability" // 31
-        << "healthcaseamount" // 32
-        << "waterrise"        // 33
-        << "healthdecrease"   // 34
+        << "disablewind"      // 22
+        << "morewind"         // 23
+        << "damagefactor"     // 24
+        << "turntime"         // 25
+        << "health"           // 26
+        << "suddendeath"      // 27
+        << "caseprobability"  // 28
+        << "minestime"        // 29
+        << "minesnum"         // 30
+        << "minedudpct"       // 31
+        << "explosives"       // 32
+        << "healthprobability" // 33
+        << "healthcaseamount" // 34
+        << "waterrise"        // 35
+        << "healthdecrease"   // 36
         ;
 
     QList<QVariant> proMode;
@@ -142,19 +146,21 @@
         << QVariant(false)         // inf. attack    19
         << QVariant(false)         // reset weps     20
         << QVariant(false)         // per hog ammo   21
-        << QVariant(100)           // damage modfier 22
-        << QVariant(15)            // turn time      23
-        << QVariant(100)           // init health    24
-        << QVariant(15)            // sudden death   25
-        << QVariant(0)             // case prob      26
-        << QVariant(3)             // mines time     27
-        << QVariant(0)             // mines number   28
-        << QVariant(0)             // mine dud pct   29
-        << QVariant(2)             // explosives     30
-        << QVariant(35)            // health case pct 31
-        << QVariant(25)            // health case amt 32
-        << QVariant(47)            // water rise amt 33
-        << QVariant(5)             // health dec amt 34
+        << QVariant(false)         // no wind        22
+        << QVariant(false)         // more wind      23
+        << QVariant(100)           // damage modfier 24
+        << QVariant(15)            // turn time      25
+        << QVariant(100)           // init health    26
+        << QVariant(15)            // sudden death   27
+        << QVariant(0)             // case prob      28
+        << QVariant(3)             // mines time     29
+        << QVariant(0)             // mines number   30
+        << QVariant(0)             // mine dud pct   31
+        << QVariant(2)             // explosives     32
+        << QVariant(35)            // health case pct 33
+        << QVariant(25)            // health case amt 34
+        << QVariant(47)            // water rise amt 35
+        << QVariant(5)             // health dec amt 36
         ;
 
     QList<QVariant> shoppa;
@@ -181,19 +187,21 @@
         << QVariant(false)         // inf. attack    19
         << QVariant(false)         // reset weps     20
         << QVariant(false)         // per hog ammo   21
-        << QVariant(100)           // damage modfier 22
-        << QVariant(30)            // turn time      23
-        << QVariant(100)           // init health    24
-        << QVariant(50)            // sudden death   25
-        << QVariant(1)             // case prob      26
-        << QVariant(3)             // mines time     27
-        << QVariant(0)             // mines number   28
-        << QVariant(0)             // mine dud pct   29
-        << QVariant(0)             // explosives     30
-        << QVariant(0)             // health case pct 31
-        << QVariant(25)            // health case amt 32
-        << QVariant(47)            // water rise amt 33
-        << QVariant(5)             // health dec amt 34
+        << QVariant(false)         // no wind        22
+        << QVariant(false)         // more wind      23
+        << QVariant(100)           // damage modfier 24
+        << QVariant(30)            // turn time      25
+        << QVariant(100)           // init health    26
+        << QVariant(50)            // sudden death   27
+        << QVariant(1)             // case prob      28
+        << QVariant(3)             // mines time     29
+        << QVariant(0)             // mines number   30
+        << QVariant(0)             // mine dud pct   31
+        << QVariant(0)             // explosives     32
+        << QVariant(0)             // health case pct 33
+        << QVariant(25)            // health case amt 34
+        << QVariant(47)            // water rise amt 35
+        << QVariant(5)             // health dec amt 36
         ;
 
     QList<QVariant> cleanslate;
@@ -220,19 +228,21 @@
         << QVariant(true)          // inf. attack    19
         << QVariant(true)          // reset weps     20
         << QVariant(false)         // per hog ammo   21
-        << QVariant(100)           // damage modfier 22
-        << QVariant(45)            // turn time      23
-        << QVariant(100)           // init health    24
-        << QVariant(15)            // sudden death   25
-        << QVariant(5)             // case prob      26
-        << QVariant(3)             // mines time     27
-        << QVariant(4)             // mines number   28
-        << QVariant(0)             // mine dud pct   29
-        << QVariant(2)             // explosives     30
-        << QVariant(35)            // health case pct 31
-        << QVariant(25)            // health case amt 32
-        << QVariant(47)            // water rise amt 33
-        << QVariant(5)             // health dec amt 34
+        << QVariant(false)         // no wind        22
+        << QVariant(false)         // more wind      23
+        << QVariant(100)           // damage modfier 24
+        << QVariant(45)            // turn time      25
+        << QVariant(100)           // init health    26
+        << QVariant(15)            // sudden death   27
+        << QVariant(5)             // case prob      28
+        << QVariant(3)             // mines time     29
+        << QVariant(4)             // mines number   30
+        << QVariant(0)             // mine dud pct   31
+        << QVariant(2)             // explosives     32
+        << QVariant(35)            // health case pct 33
+        << QVariant(25)            // health case amt 34
+        << QVariant(47)            // water rise amt 35
+        << QVariant(5)             // health dec amt 36
         ;
 
     QList<QVariant> minefield;
@@ -259,19 +269,21 @@
         << QVariant(false)         // inf. attack    19
         << QVariant(false)         // reset weps     20
         << QVariant(false)         // per hog ammo   21
-        << QVariant(150)           // damage modfier 22
-        << QVariant(30)            // turn time      23
-        << QVariant(50)            // init health    24
-        << QVariant(15)            // sudden death   25
-        << QVariant(0)             // case prob      26
-        << QVariant(0)             // mines time     27
-        << QVariant(80)            // mines number   28
-        << QVariant(0)             // mine dud pct   29
-        << QVariant(0)             // explosives     30
-        << QVariant(35)            // health case pct 31
-        << QVariant(25)            // health case amt 32
-        << QVariant(47)            // water rise amt 33
-        << QVariant(5)             // health dec amt 34
+        << QVariant(false)         // no wind        22
+        << QVariant(false)         // more wind      23
+        << QVariant(150)           // damage modfier 24
+        << QVariant(30)            // turn time      25
+        << QVariant(50)            // init health    26
+        << QVariant(15)            // sudden death   27
+        << QVariant(0)             // case prob      28
+        << QVariant(0)             // mines time     29
+        << QVariant(80)            // mines number   30
+        << QVariant(0)             // mine dud pct   31
+        << QVariant(0)             // explosives     32
+        << QVariant(35)            // health case pct 33
+        << QVariant(25)            // health case amt 34
+        << QVariant(47)            // water rise amt 35
+        << QVariant(5)             // health dec amt 36
         ;
 
     QList<QVariant> barrelmayhem;
@@ -298,19 +310,21 @@
         << QVariant(false)         // inf. attack    19
         << QVariant(false)         // reset weps     20
         << QVariant(false)         // per hog ammo   21
-        << QVariant(100)           // damage modfier 22
-        << QVariant(30)            // turn time      23
-        << QVariant(100)           // init health    24
-        << QVariant(15)            // sudden death   25
-        << QVariant(0)             // case prob      26
-        << QVariant(0)             // mines time     27
-        << QVariant(0)             // mines number   28
-        << QVariant(0)             // mine dud pct   29
-        << QVariant(80)            // explosives     30
-        << QVariant(35)            // health case pct 31
-        << QVariant(25)            // health case amt 32
-        << QVariant(47)            // water rise amt 33
-        << QVariant(5)             // health dec amt 34
+        << QVariant(false)         // no wind        22
+        << QVariant(false)         // more wind      23
+        << QVariant(100)           // damage modfier 24
+        << QVariant(30)            // turn time      25
+        << QVariant(100)           // init health    26
+        << QVariant(15)            // sudden death   27
+        << QVariant(0)             // case prob      28
+        << QVariant(0)             // mines time     29
+        << QVariant(0)             // mines number   30
+        << QVariant(0)             // mine dud pct   31
+        << QVariant(80)            // explosives     32
+        << QVariant(35)            // health case pct 33
+        << QVariant(25)            // health case amt 34
+        << QVariant(47)            // water rise amt 35
+        << QVariant(5)             // health dec amt 36
         ;
 
     QList<QVariant> tunnelhogs;
@@ -337,19 +351,21 @@
         << QVariant(false)         // inf. attack    19
         << QVariant(false)         // reset weps     20
         << QVariant(false)         // per hog ammo   21
-        << QVariant(100)           // damage modfier 22
-        << QVariant(30)            // turn time      23
-        << QVariant(100)           // init health    24
-        << QVariant(15)            // sudden death   25
-        << QVariant(5)             // case prob      26
-        << QVariant(3)             // mines time     27
-        << QVariant(10)            // mines number   28
-        << QVariant(10)            // mine dud pct   29
-        << QVariant(10)            // explosives     30
-        << QVariant(35)            // health case pct 31
-        << QVariant(25)            // health case amt 32
-        << QVariant(47)            // water rise amt 33
-        << QVariant(5)             // health dec amt 34
+        << QVariant(false)         // no wind        22
+        << QVariant(false)         // more wind      23
+        << QVariant(100)           // damage modfier 24
+        << QVariant(30)            // turn time      25
+        << QVariant(100)           // init health    26
+        << QVariant(15)            // sudden death   27
+        << QVariant(5)             // case prob      28
+        << QVariant(3)             // mines time     29
+        << QVariant(10)            // mines number   30
+        << QVariant(10)            // mine dud pct   31
+        << QVariant(10)            // explosives     32
+        << QVariant(35)            // health case pct 33
+        << QVariant(25)            // health case amt 34
+        << QVariant(47)            // water rise amt 35
+        << QVariant(5)             // health dec amt 36
         ;
 
     QList<QVariant> forts;
@@ -376,19 +392,21 @@
         << QVariant(false)         // inf. attack    19
         << QVariant(false)         // reset weps     20
         << QVariant(false)         // per hog ammo   21
-        << QVariant(100)           // damage modfier 22
-        << QVariant(45)            // turn time      23
-        << QVariant(100)           // init health    24
-        << QVariant(15)            // sudden death   25
-        << QVariant(5)             // case prob      26
-        << QVariant(3)             // mines time     27
-        << QVariant(0)             // mines number   28
-        << QVariant(0)             // mine dud pct   29
-        << QVariant(0)             // explosives     30
-        << QVariant(35)            // health case pct 31
-        << QVariant(25)            // health case amt 32
-        << QVariant(47)            // water rise amt 33
-        << QVariant(5)             // health dec amt 34
+        << QVariant(false)         // no wind        22
+        << QVariant(false)         // more wind      23
+        << QVariant(100)           // damage modfier 24
+        << QVariant(45)            // turn time      25
+        << QVariant(100)           // init health    26
+        << QVariant(15)            // sudden death   27
+        << QVariant(5)             // case prob      28
+        << QVariant(3)             // mines time     29
+        << QVariant(0)             // mines number   30
+        << QVariant(0)             // mine dud pct   31
+        << QVariant(0)             // explosives     32
+        << QVariant(35)            // health case pct 33
+        << QVariant(25)            // health case amt 34
+        << QVariant(47)            // water rise amt 35
+        << QVariant(5)             // health dec amt 36
         ;
 
     QList<QVariant> timeless;
@@ -415,19 +433,21 @@
         << QVariant(false)         // inf. attack    19
         << QVariant(false)         // reset weps     20
         << QVariant(true)          // per hog ammo   21
-        << QVariant(100)           // damage modfier 22
-        << QVariant(9999)          // turn time      23
-        << QVariant(100)           // init health    24
-        << QVariant(15)            // sudden death   25
-        << QVariant(5)             // case prob      26
-        << QVariant(3)             // mines time     27
-        << QVariant(5)             // mines number   28
-        << QVariant(10)            // mine dud pct   29
-        << QVariant(2)             // explosives     30
-        << QVariant(35)            // health case pct 31
-        << QVariant(30)            // health case amt 32
-        << QVariant(0)             // water rise amt 33
-        << QVariant(0)             // health dec amt 34
+        << QVariant(false)         // no wind        22
+        << QVariant(false)         // more wind      23
+        << QVariant(100)           // damage modfier 24
+        << QVariant(9999)          // turn time      25
+        << QVariant(100)           // init health    26
+        << QVariant(15)            // sudden death   27
+        << QVariant(5)             // case prob      28
+        << QVariant(3)             // mines time     29
+        << QVariant(5)             // mines number   30
+        << QVariant(10)            // mine dud pct   31
+        << QVariant(2)             // explosives     32
+        << QVariant(35)            // health case pct 33
+        << QVariant(30)            // health case amt 34
+        << QVariant(0)             // water rise amt 35
+        << QVariant(0)             // health dec amt 36
         ;
 
     QList<QVariant> thinkingportals;
@@ -454,19 +474,21 @@
         << QVariant(false)         // inf. attack    19
         << QVariant(false)         // reset weps     20
         << QVariant(false)         // per hog ammo   21
-        << QVariant(100)           // damage modfier 22
-        << QVariant(45)            // turn time      23
-        << QVariant(100)           // init health    24
-        << QVariant(15)            // sudden death   25
-        << QVariant(2)             // case prob      26
-        << QVariant(3)             // mines time     27
-        << QVariant(5)             // mines number   28
-        << QVariant(0)             // mine dud pct   29
-        << QVariant(5)             // explosives     30
-        << QVariant(25)            // health case pct 31
-        << QVariant(25)            // health case amt 32
-        << QVariant(47)            // water rise amt 33
-        << QVariant(5)             // health dec amt 34
+        << QVariant(false)         // no wind        22
+        << QVariant(false)         // more wind      23
+        << QVariant(100)           // damage modfier 24
+        << QVariant(45)            // turn time      25
+        << QVariant(100)           // init health    26
+        << QVariant(15)            // sudden death   27
+        << QVariant(2)             // case prob      28
+        << QVariant(3)             // mines time     29
+        << QVariant(5)             // mines number   30
+        << QVariant(0)             // mine dud pct   31
+        << QVariant(5)             // explosives     32
+        << QVariant(25)            // health case pct 33
+        << QVariant(25)            // health case amt 34
+        << QVariant(47)            // water rise amt 35
+        << QVariant(5)             // health dec amt 36
         ;
 
     QList<QVariant> kingmode;
@@ -493,19 +515,21 @@
         << QVariant(false)         // inf. attack    19
         << QVariant(false)         // reset weps     20
         << QVariant(false)         // per hog ammo   21
-        << QVariant(100)           // damage modfier 22
-        << QVariant(45)            // turn time      23
-        << QVariant(100)           // init health    24
-        << QVariant(15)            // sudden death   25
-        << QVariant(5)             // case prob      26
-        << QVariant(3)             // mines time     27
-        << QVariant(3)             // mines number   28
-        << QVariant(20)            // mine dud pct   29
-        << QVariant(3)             // explosives     30
-        << QVariant(35)            // health case pct 31
-        << QVariant(30)            // health case amt 32
-        << QVariant(30)            // water rise amt 33
-        << QVariant(5)             // health dec amt 34
+        << QVariant(false)         // no wind        22
+        << QVariant(false)         // more wind      23
+        << QVariant(100)           // damage modfier 24
+        << QVariant(45)            // turn time      25
+        << QVariant(100)           // init health    26
+        << QVariant(15)            // sudden death   27
+        << QVariant(5)             // case prob      28
+        << QVariant(3)             // mines time     29
+        << QVariant(3)             // mines number   30
+        << QVariant(20)            // mine dud pct   31
+        << QVariant(3)             // explosives     32
+        << QVariant(35)            // health case pct 33
+        << QVariant(30)            // health case amt 34
+        << QVariant(30)            // water rise amt 35
+        << QVariant(5)             // health dec amt 36
         ;
 
 
--- a/QTfrontend/gamecfgwidget.cpp	Sun Nov 14 20:37:55 2010 +0100
+++ b/QTfrontend/gamecfgwidget.cpp	Sun Nov 14 15:06:02 2010 -0500
@@ -78,6 +78,13 @@
 
     connect(goToWeaponPage, SIGNAL(clicked()), this, SLOT(jumpToWeapons()));
 
+    GBoxOptionsLayout->addWidget(new QLabel(QLabel::tr("Bind schemes and weapons"), GBoxOptions), 2, 0);
+
+    bindEntries = new QCheckBox(GBoxOptions);
+    bindEntries->setToolTip(tr("When this option is enabled selecting a game scheme will auto-select a weapon (and viceversa)"));
+    bindEntries->setChecked(true);
+    GBoxOptionsLayout->addWidget(bindEntries, 2, 2);
+
     connect(pMapContainer, SIGNAL(seedChanged(const QString &)), this, SLOT(seedChanged(const QString &)));
     connect(pMapContainer, SIGNAL(mapChanged(const QString &)), this, SLOT(mapChanged(const QString &)));
     connect(pMapContainer, SIGNAL(mapgenChanged(MapGenerator)), this, SLOT(mapgenChanged(MapGenerator)));
@@ -142,13 +149,17 @@
         result |= 0x00200000;       // reset weaps
     if (schemeData(21).toBool())
         result |= 0x00400000;       // per hog ammo
+    if (schemeData(22).toBool())
+        result |= 0x00800000;       // no wind
+    if (schemeData(23).toBool())
+        result |= 0x01000000;       // more wind
 
     return result;
 }
 
 quint32 GameCFGWidget::getInitHealth() const
 {
-    return schemeData(24).toInt();
+    return schemeData(26).toInt();
 }
 
 QStringList GameCFGWidget::getFullConfig() const
@@ -156,18 +167,18 @@
     QStringList sl;
     sl.append("eseed " + pMapContainer->getCurrentSeed());
     sl.append(QString("e$gmflags %1").arg(getGameFlags()));
-    sl.append(QString("e$damagepct %1").arg(schemeData(22).toInt()));
-    sl.append(QString("e$turntime %1").arg(schemeData(23).toInt() * 1000));
-    sl.append(QString("e$minestime %1").arg(schemeData(27).toInt() * 1000));
-    sl.append(QString("e$minesnum %1").arg(schemeData(28).toInt()));
-    sl.append(QString("e$sd_turns %1").arg(schemeData(25).toInt()));
-    sl.append(QString("e$casefreq %1").arg(schemeData(26).toInt()));
-    sl.append(QString("e$minedudpct %1").arg(schemeData(29).toInt()));
-    sl.append(QString("e$explosives %1").arg(schemeData(30).toInt()));
-    sl.append(QString("e$healthprob %1").arg(schemeData(31).toInt()));
-    sl.append(QString("e$hcaseamount %1").arg(schemeData(32).toInt()));
-    sl.append(QString("e$waterrise %1").arg(schemeData(33).toInt()));
-    sl.append(QString("e$healthdec %1").arg(schemeData(34).toInt()));
+    sl.append(QString("e$damagepct %1").arg(schemeData(24).toInt()));
+    sl.append(QString("e$turntime %1").arg(schemeData(25).toInt() * 1000));
+    sl.append(QString("e$sd_turns %1").arg(schemeData(27).toInt()));
+    sl.append(QString("e$casefreq %1").arg(schemeData(28).toInt()));
+    sl.append(QString("e$minestime %1").arg(schemeData(29).toInt()));
+    sl.append(QString("e$minesnum %1").arg(schemeData(30).toInt()));
+    sl.append(QString("e$minedudpct %1").arg(schemeData(31).toInt()));
+    sl.append(QString("e$explosives %1").arg(schemeData(32).toInt()));
+    sl.append(QString("e$healthprob %1").arg(schemeData(33).toInt()));
+    sl.append(QString("e$hcaseamount %1").arg(schemeData(34).toInt()));
+    sl.append(QString("e$waterrise %1").arg(schemeData(35).toInt()));
+    sl.append(QString("e$healthdec %1").arg(schemeData(36).toInt()));
     sl.append(QString("e$template_filter %1").arg(pMapContainer->getTemplateFilter()));
     sl.append(QString("e$mapgen %1").arg(pMapContainer->get_mapgen()));
     sl.append(QString("e$maze_size %1").arg(pMapContainer->get_maze_size()));
@@ -263,11 +274,23 @@
 
 void GameCFGWidget::ammoChanged(int index)
 {
-    if (index >= 0)
+    if (index >= 0) {
         emit paramChanged(
             "AMMO",
             QStringList() << WeaponsName->itemText(index) << WeaponsName->itemData(index).toString()
         );
+        if (bindEntries->isChecked() == true) {
+            QString weapName = WeaponsName->itemText(index);
+            for (int i = 0; i < GameSchemes->count(); i++) {
+                 QString schemeName = GameSchemes->itemText(i);
+                 int res = QString::compare(weapName, schemeName, Qt::CaseSensitive);
+                 if (0 == res) {
+                     GameSchemes->setCurrentIndex(i);
+                     break;
+                 }
+            }
+        }
+    }
 }
 
 void GameCFGWidget::mapChanged(const QString & value)
@@ -276,6 +299,7 @@
     {
         GameSchemes->setEnabled(false);
         WeaponsName->setEnabled(false);
+        bindEntries->setEnabled(false);
         GameSchemes->setCurrentIndex(GameSchemes->findText("Default"));
         WeaponsName->setCurrentIndex(WeaponsName->findText("Default"));
     }
@@ -283,6 +307,7 @@
     {
         GameSchemes->setEnabled(true);
         WeaponsName->setEnabled(true);
+        bindEntries->setEnabled(true);
     }
     emit paramChanged("MAP", QStringList(value));
 }
@@ -302,7 +327,7 @@
     emit paramChanged("THEME", QStringList(value));
 }
 
-void GameCFGWidget::schemeChanged(int value)
+void GameCFGWidget::schemeChanged(int index)
 {
     QStringList sl;
 
@@ -311,6 +336,18 @@
         sl << schemeData(i).toString();
 
     emit paramChanged("SCHEME", sl);
+
+    if (bindEntries->isChecked() == true) {
+        QString schemeName = GameSchemes->itemText(index);
+        for (int i = 0; i < WeaponsName->count(); i++) {
+             QString weapName = WeaponsName->itemText(i);
+             int res = QString::compare(weapName, schemeName, Qt::CaseSensitive);
+             if (0 == res) {
+                 WeaponsName->setCurrentIndex(i);
+                 break;
+             }
+        }
+    }
 }
 
 void GameCFGWidget::mapgenChanged(MapGenerator m)
--- a/QTfrontend/gamecfgwidget.h	Sun Nov 14 20:37:55 2010 +0100
+++ b/QTfrontend/gamecfgwidget.h	Sun Nov 14 15:06:02 2010 -0500
@@ -68,7 +68,7 @@
 
 private:
     QGridLayout mainLayout;
-
+    QCheckBox * bindEntries;
     QString curNetAmmoName;
     QString curNetAmmo;
 
--- a/QTfrontend/hedgewars.qrc	Sun Nov 14 20:37:55 2010 +0100
+++ b/QTfrontend/hedgewars.qrc	Sun Nov 14 15:06:02 2010 -0500
@@ -71,6 +71,8 @@
     <file>res/btnInfAttack.png</file>
     <file>res/btnResetWeps.png</file>
     <file>res/btnPerHogAmmo.png</file>
+    <file>res/btnNoWind.png</file>
+    <file>res/btnMoreWind.png</file>
     <file>res/iconBox.png</file>
     <file>res/iconHealth.png</file>
     <file>res/iconSuddenDeath.png</file>
--- a/QTfrontend/hwconsts.cpp.in	Sun Nov 14 20:37:55 2010 +0100
+++ b/QTfrontend/hwconsts.cpp.in	Sun Nov 14 15:06:02 2010 -0500
@@ -36,10 +36,10 @@
 int cMaxTeams = 6;
 
 QString * cDefaultAmmoStore = new QString(
-        "9391929422199121032235111001201000000211110101011"
-        "0405040541600655546554464776576666666155510101115"
-        "0000000000000205500000040007004000000000200000000"
-        "1311110312111111123114111111111111111211111101111"
+        "93919294221991210322351110012010000002111101010111"
+        "04050405416006555465544647765766666661555101011154"
+        "00000000000002055000000400070040000000002000000006"
+        "13111103121111111231141111111111111112111111011111"
         );
 int cAmmoNumber = cDefaultAmmoStore->size() / 4;
 
@@ -48,40 +48,40 @@
         << qMakePair(QString("Default"), *cDefaultAmmoStore)
         << qMakePair(QString("Crazy"),     QString(
         // TODO: Remove Piano's unlimited uses!
-        "9999999999999999992999999999999999299999999909999"
-        "1111110111111111111111111111111111111111111101111"
-        "0000000000000000000000000000000000000000000000000"
-        "1311110312111111123114111111111111111211110101111"
+        "99999999999999999929999999999999992999999999099999"
+        "11111101111111111111111111111111111111111111011111"
+        "00000000000000000000000000000000000000000000000000"
+        "13111103121111111231141111111111111112111101011111"
         ))
         << qMakePair(QString("Pro Mode"),  QString(
-        "9090009000000000000009000000000000000000000000000"
-        "0000000000000000000000000000000000000000000000000"
-        "0000000000000205500000040007004000000000200000000"
-        "1111111111111111111111111111111111111111100101111"
+        "90900090000000000000090000000000000000000000000000"
+        "00000000000000000000000000000000000000000000000000"
+        "00000000000002055000000400070040000000002000000000"
+        "11111111111111111111111111111111111111111001011111"
         ))
         << qMakePair(QString("Shoppa"),    QString(
-        "0000009900000000000000000000000000000000000000000"
-        "4444410044244402210112121222422000000002000400010"
-        "0000000000000000000000000000000000000000000000000"
-        "1111111111111111111111111111111111111111101101111"
+        "00000099000000000000000000000000000000000000000000"
+        "44444100442444022101121212224220000000020004000100"
+        "00000000000000000000000000000000000000000000000000"
+        "11111111111111111111111111111111111111111011011111"
         ))
         << qMakePair(QString("Clean Slate"),QString(
-        "1010009000010000011000000000000000000000000000001"
-        "0405040541600655546554464776576666666155510101115"
-        "0000000000000000000000000000000000000000000000000"
-        "1311110312111111123114111111111111111211111101111"
+        "10100090000100000110000000000000000000000000000010"
+        "04050405416006555465544647765766666661555101011154"
+        "00000000000000000000000000000000000000000000000000"
+        "13111103121111111231141111111111111112111111011111"
         ))
         << qMakePair(QString("Minefield"), QString(
-        "0000009900090000000300000000000000000000000000000"
-        "0000000000000000000000000000000000000000000000000"
-        "0000000000000205500000040007004000000000200000000"
-        "1111111111111111111111111111111111111111111101111"
+        "00000099000900000003000000000000000000000000000000"
+        "00000000000000000000000000000000000000000000000000"
+        "00000000000002055000000400070040000000002000000006"
+        "11111111111111111111111111111111111111111111011111"
         ))
         << qMakePair(QString("Thinking with Portals"), QString(
-        "9000009002000000002100000000000000110000090000000"
-        "0405040541600655546554464776576666666155510101115"
-        "0000000000000205500000040007004000000000200000000"
-        "1311110312111111123114111111111111111211111101111"
+        "90000090020000000021000000000000001100000900000000"
+        "04050405416006555465544647765766666661555101011154"
+        "00000000000002055000000400070040000000002000000006"
+        "13111103121111111231141111111111111112111111011111"
         ));
 
 QColor *colors[] = {
--- a/QTfrontend/mapContainer.cpp	Sun Nov 14 20:37:55 2010 +0100
+++ b/QTfrontend/mapContainer.cpp	Sun Nov 14 15:06:02 2010 -0500
@@ -56,8 +56,9 @@
     imageButt->setFlat(true);
     imageButt->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);//QSizePolicy::Minimum, QSizePolicy::Minimum);
     mainLayout.addWidget(imageButt, 0, 0, 1, 2);
-    connect(imageButt, SIGNAL(clicked()), this, SLOT(setRandomSeed()));
-    connect(imageButt, SIGNAL(clicked()), this, SLOT(setRandomTheme()));
+    //connect(imageButt, SIGNAL(clicked()), this, SLOT(setRandomSeed()));
+    //connect(imageButt, SIGNAL(clicked()), this, SLOT(setRandomTheme()));
+    connect(imageButt, SIGNAL(clicked()), this, SLOT(setRandomMap()));
 
     chooseMap = new QComboBox(this);
     chooseMap->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
@@ -72,6 +73,7 @@
     chooseMap->insertSeparator(chooseMap->count()); // separator between generators and missions
 
     int missionindex = chooseMap->count();
+    numMissions = 0;
     for (int i = 0; i < mapList->size(); ++i) {
         QString map = (*mapList)[i];
         QFile mapCfgFile(
@@ -98,15 +100,18 @@
                 mapInfo.push_back(18);
             mapInfo.push_back(mapLuaFile.exists());
             if(mapLuaFile.exists())
+            {
                 chooseMap->insertItem(missionindex++, 
 // FIXME - need real icons. Disabling until then
 //QIcon(":/res/mapMission.png"), 
 QComboBox::tr("Mission") + ": " + map, mapInfo);
+                numMissions++;
+            }
             else
                 chooseMap->addItem(
 // FIXME - need real icons. Disabling until then
-//QIcon(":/res/mapCustom.png"),
- map, mapInfo);
+//QIcon(":/res/mapCustom.png"), 
+map, mapInfo);
             mapCfgFile.close();
         }
     }
@@ -402,6 +407,36 @@
     if(items.size())
         lwThemes->setCurrentItem(items.at(0));
 }
+#include <QMessageBox>
+void HWMapContainer::setRandomMap()
+{
+    switch(chooseMap->currentIndex())
+    {
+    case MAPGEN_REGULAR:
+    case MAPGEN_MAZE:
+        setRandomSeed();
+        setRandomTheme();
+        break;
+    default:
+        if(chooseMap->currentIndex() < numMissions + 3)
+            setRandomMission();
+        else
+            setRandomStatic();
+        break;
+    }
+}
+
+void HWMapContainer::setRandomStatic()
+{
+    chooseMap->setCurrentIndex(4 + numMissions + rand() % (chooseMap->count() - 4 - numMissions));
+    m_seed = QUuid::createUuid().toString();
+}
+
+void HWMapContainer::setRandomMission()
+{
+    chooseMap->setCurrentIndex(3 + rand() % numMissions);
+    m_seed = QUuid::createUuid().toString();
+}
 
 void HWMapContainer::setRandomSeed()
 {
--- a/QTfrontend/mapContainer.h	Sun Nov 14 20:37:55 2010 +0100
+++ b/QTfrontend/mapContainer.h	Sun Nov 14 15:06:02 2010 -0500
@@ -72,6 +72,9 @@
   void mapChanged(int index);
   void setRandomSeed();
   void setRandomTheme();
+  void setRandomMap();
+  void setRandomStatic();
+  void setRandomMission();
   void themeSelected(int currentRow);
   void addInfoToPreview(QPixmap image);
   void templateFilterChanged(int filter);
@@ -95,6 +98,7 @@
   QLabel *maze_size_label;
   QComboBox *maze_size_selection;
   MapGenerator mapgen;
+  int numMissions;
   int maze_size;
 
   void loadMap(int index);
--- a/QTfrontend/pages.cpp	Sun Nov 14 20:37:55 2010 +0100
+++ b/QTfrontend/pages.cpp	Sun Nov 14 15:06:02 2010 -0500
@@ -1562,6 +1562,14 @@
     TBW_perhogammo->setToolTip("<b>" + ToggleButtonWidget::tr("Per Hedgehog Ammo") + "</b>:<br />" + tr("Each hedgehog has its own ammo. It does not share with the team."));
     glGMLayout->addWidget(TBW_perhogammo,4,0,1,1);
 
+    TBW_nowind = new ToggleButtonWidget(gbGameModes, ":/res/btnNoWind.png");
+    TBW_nowind->setToolTip("<b>" + ToggleButtonWidget::tr("Disable Wind") + "</b>:<br />" + tr("You will not have to worry about wind anymore."));
+    glGMLayout->addWidget(TBW_nowind,4,1,1,1);
+
+    TBW_morewind = new ToggleButtonWidget(gbGameModes, ":/res/btnMoreWind.png");
+    TBW_morewind->setToolTip("<b>" + ToggleButtonWidget::tr("More Wind") + "</b>:<br />" + tr("Wind will affect almost everything."));
+    glGMLayout->addWidget(TBW_morewind,4,2,1,1);
+
     // Right
     QLabel * l;
 
@@ -1800,19 +1808,21 @@
     mapper->addMapping(TBW_infattack, 19);
     mapper->addMapping(TBW_resetweps, 20);
     mapper->addMapping(TBW_perhogammo, 21);
-    mapper->addMapping(SB_DamageModifier, 22);
-    mapper->addMapping(SB_TurnTime, 23);
-    mapper->addMapping(SB_InitHealth, 24);
-    mapper->addMapping(SB_SuddenDeath, 25);
-    mapper->addMapping(SB_CaseProb, 26);
-    mapper->addMapping(SB_MinesTime, 27);
-    mapper->addMapping(SB_Mines, 28);
-    mapper->addMapping(SB_MineDuds, 29);
-    mapper->addMapping(SB_Explosives, 30);
-    mapper->addMapping(SB_HealthCrates, 31);
-    mapper->addMapping(SB_CrateHealth, 32);
-    mapper->addMapping(SB_WaterRise, 33);
-    mapper->addMapping(SB_HealthDecrease, 34);
+    mapper->addMapping(TBW_nowind, 22);
+    mapper->addMapping(TBW_morewind, 23);
+    mapper->addMapping(SB_DamageModifier, 24);
+    mapper->addMapping(SB_TurnTime, 25);
+    mapper->addMapping(SB_InitHealth, 26);
+    mapper->addMapping(SB_SuddenDeath, 27);
+    mapper->addMapping(SB_CaseProb, 28);
+    mapper->addMapping(SB_MinesTime, 29);
+    mapper->addMapping(SB_Mines, 30);
+    mapper->addMapping(SB_MineDuds, 31);
+    mapper->addMapping(SB_Explosives, 32);
+    mapper->addMapping(SB_HealthCrates, 33);
+    mapper->addMapping(SB_CrateHealth, 34);
+    mapper->addMapping(SB_WaterRise, 35);
+    mapper->addMapping(SB_HealthDecrease, 36);
 
     mapper->toFirst();
 }
--- a/QTfrontend/pages.h	Sun Nov 14 20:37:55 2010 +0100
+++ b/QTfrontend/pages.h	Sun Nov 14 15:06:02 2010 -0500
@@ -481,6 +481,8 @@
     ToggleButtonWidget * TBW_infattack;
     ToggleButtonWidget * TBW_resetweps;
     ToggleButtonWidget * TBW_perhogammo;
+    ToggleButtonWidget * TBW_nowind;
+    ToggleButtonWidget * TBW_morewind;
 
     QSpinBox * SB_DamageModifier;
     QSpinBox * SB_TurnTime;
Binary file QTfrontend/res/btnMoreWind.png has changed
Binary file QTfrontend/res/btnNoWind.png has changed
--- a/gameServer/Actions.hs	Sun Nov 14 20:37:55 2010 +0100
+++ b/gameServer/Actions.hs	Sun Nov 14 15:06:02 2010 -0500
@@ -1,171 +1,134 @@
+{-# LANGUAGE OverloadedStrings #-}
 module Actions where
 
-import Control.Concurrent.STM
+import Control.Concurrent
 import Control.Concurrent.Chan
-import Data.IntMap
 import qualified Data.IntSet as IntSet
+import qualified Data.Set as Set
 import qualified Data.Sequence as Seq
 import System.Log.Logger
 import Control.Monad
 import Data.Time
 import Data.Maybe
+import Control.Monad.Reader
+import Control.Monad.State.Strict
+import qualified Data.ByteString.Char8 as B
 -----------------------------
 import CoreTypes
 import Utils
+import ClientIO
+import ServerState
 
 data Action =
-    AnswerThisClient [String]
-    | AnswerAll [String]
-    | AnswerAllOthers [String]
-    | AnswerThisRoom [String]
-    | AnswerOthersInRoom [String]
-    | AnswerSameClan [String]
-    | AnswerLobby [String]
+    AnswerClients ![ClientChan] ![B.ByteString]
     | SendServerMessage
     | SendServerVars
-    | RoomAddThisClient Int -- roomID
-    | RoomRemoveThisClient String
-    | RemoveTeam String
+    | MoveToRoom RoomIndex
+    | MoveToLobby B.ByteString
+    | RemoveTeam B.ByteString
     | RemoveRoom
     | UnreadyRoomClients
-    | MoveToLobby
-    | ProtocolError String
-    | Warning String
-    | ByeClient String
-    | KickClient Int -- clID
-    | KickRoomClient Int -- clID
-    | BanClient String -- nick
-    | RemoveClientTeams Int -- clID
+    | JoinLobby
+    | ProtocolError B.ByteString
+    | Warning B.ByteString
+    | ByeClient B.ByteString
+    | KickClient ClientIndex
+    | KickRoomClient ClientIndex
+    | BanClient B.ByteString -- nick
+    | RemoveClientTeams ClientIndex
     | ModifyClient (ClientInfo -> ClientInfo)
-    | ModifyClient2 Int (ClientInfo -> ClientInfo)
+    | ModifyClient2 ClientIndex (ClientInfo -> ClientInfo)
     | ModifyRoom (RoomInfo -> RoomInfo)
     | ModifyServerInfo (ServerInfo -> ServerInfo)
-    | AddRoom String String
+    | AddRoom B.ByteString B.ByteString
     | CheckRegistered
     | ClearAccountsCache
     | ProcessAccountInfo AccountInfo
     | Dump
     | AddClient ClientInfo
+    | DeleteClient ClientIndex
     | PingAll
     | StatsAction
 
-type CmdHandler = Int -> Clients -> Rooms -> [String] -> [Action]
-
-replaceID a (b, c, d, e) = (a, c, d, e)
-
-processAction :: (Int, ServerInfo, Clients, Rooms) -> Action -> IO (Int, ServerInfo, Clients, Rooms)
-
-
-processAction (clID, serverInfo, clients, rooms) (AnswerThisClient msg) = do
-    writeChan (sendChan $ clients ! clID) msg
-    return (clID, serverInfo, clients, rooms)
+type CmdHandler = [B.ByteString] -> Reader (ClientIndex, IRnC) [Action]
 
 
-processAction (clID, serverInfo, clients, rooms) (AnswerAll msg) = do
-    mapM_ (\cl -> writeChan (sendChan cl) msg) (elems clients)
-    return (clID, serverInfo, clients, rooms)
-
-
-processAction (clID, serverInfo, clients, rooms) (AnswerAllOthers msg) = do
-    mapM_ (\id' -> writeChan (sendChan $ clients ! id') msg) $
-        Prelude.filter (\id' -> (id' /= clID) && logonPassed (clients ! id')) (keys clients)
-    return (clID, serverInfo, clients, rooms)
-
-
-processAction (clID, serverInfo, clients, rooms) (AnswerThisRoom msg) = do
-    mapM_ (\id' -> writeChan (sendChan $ clients ! id') msg) roomClients
-    return (clID, serverInfo, clients, rooms)
-    where
-        roomClients = IntSet.elems $ playersIDs room
-        room = rooms ! rID
-        rID = roomID client
-        client = clients ! clID
+processAction :: Action -> StateT ServerState IO ()
 
 
-processAction (clID, serverInfo, clients, rooms) (AnswerOthersInRoom msg) = do
-    mapM_ (\id' -> writeChan (sendChan $ clients ! id') msg) $ Prelude.filter (/= clID) roomClients
-    return (clID, serverInfo, clients, rooms)
-    where
-        roomClients = IntSet.elems $ playersIDs room
-        room = rooms ! rID
-        rID = roomID client
-        client = clients ! clID
-
-
-processAction (clID, serverInfo, clients, rooms) (AnswerLobby msg) = do
-    mapM_ (\id' -> writeChan (sendChan $ clients ! id') msg) roomClients
-    return (clID, serverInfo, clients, rooms)
-    where
-        roomClients = IntSet.elems $ playersIDs room
-        room = rooms ! 0
+processAction (AnswerClients chans msg) = do
+    liftIO $ map (flip seq ()) chans `seq` map (flip seq ()) msg `seq` mapM_ (flip writeChan msg) chans
 
 
-processAction (clID, serverInfo, clients, rooms) (AnswerSameClan msg) = do
-    mapM_ (\cl -> writeChan (sendChan cl) msg) sameClanOrSpec
-    return (clID, serverInfo, clients, rooms)
-    where
-        otherRoomClients = Prelude.map ((!) clients) $ IntSet.elems $ clID `IntSet.delete` (playersIDs room)
-        sameClanOrSpec = if teamsInGame client > 0 then sameClanClients else spectators
-        spectators = Prelude.filter (\cl -> teamsInGame cl == 0) otherRoomClients
-        sameClanClients = Prelude.filter (\cl -> teamsInGame cl > 0 && clientClan cl == thisClan) otherRoomClients
-        thisClan = clientClan client
-        room = rooms ! rID
-        rID = roomID client
-        client = clients ! clID
-
-
-processAction (clID, serverInfo, clients, rooms) SendServerMessage = do
-    writeChan (sendChan $ clients ! clID) ["SERVER_MESSAGE", message serverInfo]
-    return (clID, serverInfo, clients, rooms)
-    where
-        client = clients ! clID
-        message si = if clientProto client < latestReleaseVersion si then
+processAction SendServerMessage = do
+    chan <- client's sendChan
+    protonum <- client's clientProto
+    si <- liftM serverInfo get
+    let message = if protonum < latestReleaseVersion si then
             serverMessageForOldVersions si
             else
             serverMessage si
+    processAction $ AnswerClients [chan] ["SERVER_MESSAGE", message]
+{-
 
-processAction (clID, serverInfo, clients, rooms) SendServerVars = do
+processAction (clID, serverInfo, rnc) SendServerVars = do
     writeChan (sendChan $ clients ! clID) ("SERVER_VARS" : vars)
-    return (clID, serverInfo, clients, rooms)
+    return (clID, serverInfo, rnc)
     where
         client = clients ! clID
         vars = [
-            "MOTD_NEW", serverMessage serverInfo, 
-            "MOTD_OLD", serverMessageForOldVersions serverInfo, 
+            "MOTD_NEW", serverMessage serverInfo,
+            "MOTD_OLD", serverMessageForOldVersions serverInfo,
             "LATEST_PROTO", show $ latestReleaseVersion serverInfo
             ]
 
 
-processAction (clID, serverInfo, clients, rooms) (ProtocolError msg) = do
-    writeChan (sendChan $ clients ! clID) ["ERROR", msg]
-    return (clID, serverInfo, clients, rooms)
+-}
 
-
-processAction (clID, serverInfo, clients, rooms) (Warning msg) = do
-    writeChan (sendChan $ clients ! clID) ["WARNING", msg]
-    return (clID, serverInfo, clients, rooms)
+processAction (ProtocolError msg) = do
+    chan <- client's sendChan
+    processAction $ AnswerClients [chan] ["ERROR", msg]
 
 
-processAction (clID, serverInfo, clients, rooms) (ByeClient msg) = do
-    infoM "Clients" (show (clientUID client) ++ " quits: " ++ msg)
-    (_, _, newClients, newRooms) <-
-            if roomID client /= 0 then
-                processAction  (clID, serverInfo, clients, rooms) $ RoomRemoveThisClient "quit"
-                else
-                    return (clID, serverInfo, clients, rooms)
+processAction (Warning msg) = do
+    chan <- client's sendChan
+    processAction $ AnswerClients [chan] ["WARNING", msg]
+
+processAction (ByeClient msg) = do
+    (Just ci) <- gets clientIndex
+    rnc <- gets roomsClients
+    ri <- clientRoomA
+
+    chan <- client's sendChan
+    ready <- client's isReady
 
-    mapM_ (processAction (clID, serverInfo, newClients, newRooms)) $ answerOthersQuit ++ answerInformRoom
-    writeChan (sendChan $ clients ! clID) ["BYE", msg]
-    return (
-            0,
-            serverInfo,
-            delete clID newClients,
-            adjust (\r -> r{
-                    playersIDs = IntSet.delete clID (playersIDs r),
-                    playersIn = (playersIn r) - 1,
-                    readyPlayers = if isReady client then readyPlayers r - 1 else readyPlayers r
-                    }) (roomID $ newClients ! clID) newRooms
-            )
+    when (ri /= lobbyId) $ do
+        processAction $ MoveToLobby ("quit: " `B.append` msg)
+        liftIO $ modifyRoom rnc (\r -> r{
+                        --playersIDs = IntSet.delete ci (playersIDs r)
+                        playersIn = (playersIn r) - 1,
+                        readyPlayers = if ready then readyPlayers r - 1 else readyPlayers r
+                        }) ri
+        return ()
+
+    liftIO $ do
+        infoM "Clients" (show ci ++ " quits: " ++ (B.unpack msg))
+
+        --mapM_ (processAction (ci, serverInfo, rnc)) $ answerOthersQuit ++ answerInformRoom
+
+    processAction $ AnswerClients [chan] ["BYE", msg]
+
+    s <- get
+    put $! s{removedClients = ci `Set.insert` removedClients s}
+
+processAction (DeleteClient ci) = do
+    rnc <- gets roomsClients
+    liftIO $ removeClient rnc ci
+
+    s <- get
+    put $! s{removedClients = ci `Set.delete` removedClients s}
+
+{-
     where
         client = clients ! clID
         clientNick = nick client
@@ -184,46 +147,57 @@
                 else
                     [AnswerAll ["LOBBY:LEFT", clientNick]]
             else
-                []
-
-
-processAction (clID, serverInfo, clients, rooms) (ModifyClient func) =
-    return (clID, serverInfo, adjust func clID clients, rooms)
-
+            [] 
+-}
 
-processAction (clID, serverInfo, clients, rooms) (ModifyClient2 cl2ID func) =
-    return (clID, serverInfo, adjust func cl2ID clients, rooms)
-
+processAction (ModifyClient f) = do
+    (Just ci) <- gets clientIndex
+    rnc <- gets roomsClients
+    liftIO $ modifyClient rnc f ci
+    return ()
 
-processAction (clID, serverInfo, clients, rooms) (ModifyRoom func) =
-    return (clID, serverInfo, clients, adjust func rID rooms)
-    where
-        rID = roomID $ clients ! clID
+processAction (ModifyClient2 ci f) = do
+    rnc <- gets roomsClients
+    liftIO $ modifyClient rnc f ci
+    return ()
 
 
-processAction (clID, serverInfo, clients, rooms) (ModifyServerInfo func) =
-    return (clID, func serverInfo, clients, rooms)
+processAction (ModifyRoom f) = do
+    rnc <- gets roomsClients
+    ri <- clientRoomA
+    liftIO $ modifyRoom rnc f ri
+    return ()
 
+{-
+
+processAction (clID, serverInfo, rnc) (ModifyServerInfo func) =
+    return (clID, func serverInfo, rnc)
+
+-}
 
-processAction (clID, serverInfo, clients, rooms) (RoomAddThisClient rID) =
-    processAction (
-        clID,
-        serverInfo,
-        adjust (\cl -> cl{roomID = rID, teamsInGame = if rID == 0 then teamsInGame cl else 0}) clID clients,
-        adjust (\r -> r{playersIDs = IntSet.insert clID (playersIDs r), playersIn = (playersIn r) + 1}) rID $
-            adjust (\r -> r{playersIDs = IntSet.delete clID (playersIDs r)}) 0 rooms
-        ) joinMsg
-    where
-        client = clients ! clID
-        joinMsg = if rID == 0 then
-                AnswerAllOthers ["LOBBY:JOINED", nick client]
-            else
-                AnswerThisRoom ["JOINED", nick client]
+processAction (MoveToRoom ri) = do
+    (Just ci) <- gets clientIndex
+    rnc <- gets roomsClients
+    liftIO $ do
+        modifyClient rnc (\cl -> cl{teamsInGame = 0}) ci
+        modifyRoom rnc (\r -> r{playersIn = (playersIn r) + 1}) ri
+
+    liftIO $ moveClientToRoom rnc ri ci
+
+    chans <- liftM (map sendChan) $ roomClientsS ri
+    clNick <- client's nick
 
+    processAction $ AnswerClients chans ["JOINED", clNick]
 
-processAction (clID, serverInfo, clients, rooms) (RoomRemoveThisClient msg) = do
+processAction (MoveToLobby msg) = do
+    (Just ci) <- gets clientIndex
+    --ri <- clientRoomA
+    rnc <- gets roomsClients
+
+    liftIO $ moveClientToLobby rnc ci
+
+{-
     (_, _, newClients, newRooms) <-
-        if roomID client /= 0 then
             if isMaster client then
                 if (gameinprogress room) && (playersIn room > 1) then
                     (changeMaster >>= (\state -> foldM processAction state
@@ -231,16 +205,15 @@
                         AnswerOthersInRoom ["WARNING", "Admin left the room"],
                         RemoveClientTeams clID]))
                 else -- not in game
-                    processAction (clID, serverInfo, clients, rooms) RemoveRoom
+                    processAction (clID, serverInfo, rnc) RemoveRoom
             else -- not master
                 foldM
                     processAction
-                        (clID, serverInfo, clients, rooms)
+                        (clID, serverInfo, rnc)
                         [AnswerOthersInRoom ["LEFT", nick client, msg],
                         RemoveClientTeams clID]
-        else -- in lobby
-            return (clID, serverInfo, clients, rooms)
-    
+
+
     return (
         clID,
         serverInfo,
@@ -259,7 +232,7 @@
                 }
         insertClientToRoom r = r{playersIDs = IntSet.insert clID (playersIDs r)}
         changeMaster = do
-            processAction (newMasterId, serverInfo, clients, rooms) $ AnswerThisClient ["ROOM_CONTROL_ACCESS", "1"]
+            processAction (newMasterId, serverInfo, rnc) $ AnswerThisClient ["ROOM_CONTROL_ACCESS", "1"]
             return (
                 clID,
                 serverInfo,
@@ -270,34 +243,35 @@
         otherPlayersSet = IntSet.delete clID (playersIDs room)
         newMasterId = IntSet.findMin otherPlayersSet
         newMasterClient = clients ! newMasterId
-
+-}
 
-processAction (clID, serverInfo, clients, rooms) (AddRoom roomName roomPassword) = do
-    let newServerInfo = serverInfo {nextRoomID = newID}
+processAction (AddRoom roomName roomPassword) = do
+    Just clId <- gets clientIndex
+    rnc <- gets roomsClients
+    proto <- liftIO $ client'sM rnc clientProto clId
+
     let room = newRoom{
-            roomUID = newID,
-            masterID = clID,
+            masterID = clId,
             name = roomName,
             password = roomPassword,
-            roomProto = (clientProto client)
+            roomProto = proto
             }
 
-    processAction (clID, serverInfo, clients, rooms) $ AnswerLobby ["ROOM", "ADD", roomName]
+    rId <- liftIO $ addRoom rnc room
+
+    processAction $ MoveToRoom rId
+
+    chans <- liftM (map sendChan) $! roomClientsS lobbyId
 
-    processAction (
-        clID,
-        newServerInfo,
-        adjust (\cl -> cl{isMaster = True}) clID clients,
-        insert newID room rooms
-        ) $ RoomAddThisClient newID
-    where
-        newID = (nextRoomID serverInfo) - 1
-        client = clients ! clID
+    mapM_ processAction [
+        AnswerClients chans ["ROOM", "ADD", roomName]
+        , ModifyClient (\cl -> cl{isMaster = True})
+        ]
 
-
-processAction (clID, serverInfo, clients, rooms) (RemoveRoom) = do
-    processAction (clID, serverInfo, clients, rooms) $ AnswerLobby ["ROOM", "DEL", name room]
-    processAction (clID, serverInfo, clients, rooms) $ AnswerOthersInRoom ["ROOMABANDONED", name room]
+{-
+processAction (clID, serverInfo, rnc) (RemoveRoom) = do
+    processAction (clID, serverInfo, rnc) $ AnswerLobby ["ROOM", "DEL", name room]
+    processAction (clID, serverInfo, rnc) $ AnswerOthersInRoom ["ROOMABANDONED", name room]
     return (clID,
         serverInfo,
         Data.IntMap.map (\cl -> if roomID cl == rID then cl{roomID = 0, isMaster = False, isReady = False, teamsInGame = undefined} else cl) clients,
@@ -308,139 +282,163 @@
         rID = roomID client
         client = clients ! clID
 
-
-processAction (clID, serverInfo, clients, rooms) (UnreadyRoomClients) = do
-    processAction (clID, serverInfo, clients, rooms) $ AnswerThisRoom ("NOT_READY" : roomPlayers)
-    return (clID,
-        serverInfo,
-        Data.IntMap.map (\cl -> if roomID cl == rID then cl{isReady = False} else cl) clients,
-        adjust (\r -> r{readyPlayers = 0}) rID rooms)
-    where
-        room = rooms ! rID
-        rID = roomID client
-        client = clients ! clID
-        roomPlayers = Prelude.map (nick . (clients !)) roomPlayersIDs
-        roomPlayersIDs = IntSet.elems $ playersIDs room
+-}
+processAction (UnreadyRoomClients) = do
+    rnc <- gets roomsClients
+    ri <- clientRoomA
+    roomPlayers <- roomClientsS ri
+    roomClIDs <- liftIO $ roomClientsIndicesM rnc ri
+    processAction $ AnswerClients (map sendChan roomPlayers) ("NOT_READY" : map nick roomPlayers)
+    liftIO $ mapM_ (modifyClient rnc (\cl -> cl{isReady = False})) roomClIDs
+    processAction $ ModifyRoom (\r -> r{readyPlayers = 0})
 
 
-processAction (clID, serverInfo, clients, rooms) (RemoveTeam teamName) = do
-    newRooms <- if not $ gameinprogress room then
-            do
-            processAction (clID, serverInfo, clients, rooms) $ AnswerOthersInRoom ["REMOVE_TEAM", teamName]
-            return $
-                adjust (\r -> r{teams = Prelude.filter (\t -> teamName /= teamname t) $ teams r}) rID rooms
+processAction (RemoveTeam teamName) = do
+    rnc <- gets roomsClients
+    cl <- client's id
+    ri <- clientRoomA
+    inGame <- liftIO $ room'sM rnc gameinprogress ri
+    chans <- liftM (map sendChan . filter (/= cl)) $ roomClientsS ri
+    if inGame then
+            mapM_ processAction [
+                AnswerClients chans ["REMOVE_TEAM", teamName],
+                ModifyRoom (\r -> r{teams = Prelude.filter (\t -> teamName /= teamname t) $ teams r})
+                ]
         else
-            do
-            processAction (clID, serverInfo, clients, rooms) $ AnswerOthersInRoom ["EM", rmTeamMsg]
-            return $
-                adjust (\r -> r{
-                teams = Prelude.filter (\t -> teamName /= teamname t) $ teams r,
-                leftTeams = teamName : leftTeams r,
-                roundMsgs = roundMsgs r Seq.|> rmTeamMsg
-                }) rID rooms
-    return (clID, serverInfo, clients, newRooms)
+            mapM_ processAction [
+                AnswerClients chans ["EM", rmTeamMsg],
+                ModifyRoom (\r -> r{
+                    teams = Prelude.filter (\t -> teamName /= teamname t) $ teams r,
+                    leftTeams = teamName : leftTeams r,
+                    roundMsgs = roundMsgs r Seq.|> rmTeamMsg
+                    })
+                ]
     where
-        room = rooms ! rID
-        rID = roomID client
-        client = clients ! clID
-        rmTeamMsg = toEngineMsg $ 'F' : teamName
+        rmTeamMsg = toEngineMsg $ (B.singleton 'F') `B.append` teamName
 
+processAction CheckRegistered = do
+    (Just ci) <- gets clientIndex
+    n <- client's nick
+    h <- client's host
+    db <- gets (dbQueries . serverInfo)
+    liftIO $ writeChan db $ CheckAccount ci n h
+    return ()
 
-processAction (clID, serverInfo, clients, rooms) (CheckRegistered) = do
-    writeChan (dbQueries serverInfo) $ CheckAccount (clientUID client) (nick client) (host client)
-    return (clID, serverInfo, clients, rooms)
+{-
+processAction (clID, serverInfo, rnc) (ClearAccountsCache) = do
+    writeChan (dbQueries serverInfo) ClearCache
+    return (clID, serverInfo, rnc)
     where
         client = clients ! clID
 
 
-processAction (clID, serverInfo, clients, rooms) (ClearAccountsCache) = do
-    writeChan (dbQueries serverInfo) ClearCache
-    return (clID, serverInfo, clients, rooms)
-    where
-        client = clients ! clID
-
+processAction (clID, serverInfo, rnc) (Dump) = do
+    writeChan (sendChan $ clients ! clID) ["DUMP", show serverInfo, showTree clients, showTree rooms]
+    return (clID, serverInfo, rnc)
+-}
 
-processAction (clID, serverInfo, clients, rooms) (Dump) = do
-    writeChan (sendChan $ clients ! clID) ["DUMP", show serverInfo, showTree clients, showTree rooms]
-    return (clID, serverInfo, clients, rooms)
-
-
-processAction (clID, serverInfo, clients, rooms) (ProcessAccountInfo info) =
+processAction (ProcessAccountInfo info) =
     case info of
         HasAccount passwd isAdmin -> do
-            infoM "Clients" $ show clID ++ " has account"
-            writeChan (sendChan $ clients ! clID) ["ASKPASSWORD"]
-            return (clID, serverInfo, adjust (\cl -> cl{webPassword = passwd, isAdministrator = isAdmin}) clID clients, rooms)
+            chan <- client's sendChan
+            processAction $ AnswerClients [chan] ["ASKPASSWORD"]
         Guest -> do
-            infoM "Clients" $ show clID ++ " is guest"
-            processAction (clID, serverInfo, adjust (\cl -> cl{logonPassed = True}) clID clients, rooms) MoveToLobby
+            processAction JoinLobby
         Admin -> do
-            infoM "Clients" $ show clID ++ " is admin"
-            foldM processAction (clID, serverInfo, adjust (\cl -> cl{logonPassed = True, isAdministrator = True}) clID clients, rooms) [MoveToLobby, AnswerThisClient ["ADMIN_ACCESS"]]
+            mapM processAction [ModifyClient (\cl -> cl{isAdministrator = True}), JoinLobby]
+            chan <- client's sendChan
+            processAction $ AnswerClients [chan] ["ADMIN_ACCESS"]
 
 
-processAction (clID, serverInfo, clients, rooms) (MoveToLobby) =
-    foldM processAction (clID, serverInfo, clients, rooms) $
-        (RoomAddThisClient 0)
-        : answerLobbyNicks
-        ++ [SendServerMessage]
+processAction JoinLobby = do
+    chan <- client's sendChan
+    clientNick <- client's nick
+    (lobbyNicks, clientsChans) <- liftM (unzip . Prelude.map (\c -> (nick c, sendChan c)) . Prelude.filter logonPassed) $! allClientsS
+    mapM_ processAction $
+        (AnswerClients clientsChans ["LOBBY:JOINED", clientNick])
+        : [AnswerClients [chan] ("LOBBY:JOINED" : clientNick : lobbyNicks)]
+        ++ [ModifyClient (\cl -> cl{logonPassed = True}), SendServerMessage]
 
-        -- ++ (answerServerMessage client clients)
+{-
+processAction (clID, serverInfo, rnc) (RoomAddThisClient rID) =
+    processAction (
+        clID,
+        serverInfo,
+        adjust (\cl -> cl{roomID = rID, teamsInGame = if rID == 0 then teamsInGame cl else 0}) clID clients,
+        adjust (\r -> r{playersIDs = IntSet.insert clID (playersIDs r), playersIn = (playersIn r) + 1}) rID $
+            adjust (\r -> r{playersIDs = IntSet.delete clID (playersIDs r)}) 0 rooms
+        ) joinMsg
     where
-        lobbyNicks = Prelude.map nick $ Prelude.filter logonPassed $ elems clients
-        answerLobbyNicks = [AnswerThisClient ("LOBBY:JOINED": lobbyNicks) | not $ Prelude.null lobbyNicks]
+        client = clients ! clID
+        joinMsg = if rID == 0 then
+                AnswerAllOthers ["LOBBY:JOINED", nick client]
+            else
+                AnswerThisRoom ["JOINED", nick client]
+
+processAction (clID, serverInfo, rnc) (KickClient kickID) =
+    liftM2 replaceID (return clID) (processAction (kickID, serverInfo, rnc) $ ByeClient "Kicked")
 
 
-processAction (clID, serverInfo, clients, rooms) (KickClient kickID) =
-    liftM2 replaceID (return clID) (processAction (kickID, serverInfo, clients, rooms) $ ByeClient "Kicked")
-
-
-processAction (clID, serverInfo, clients, rooms) (BanClient banNick) =
-    return (clID, serverInfo, clients, rooms)
+processAction (clID, serverInfo, rnc) (BanClient banNick) =
+    return (clID, serverInfo, rnc)
 
 
-processAction (clID, serverInfo, clients, rooms) (KickRoomClient kickID) = do
+processAction (clID, serverInfo, rnc) (KickRoomClient kickID) = do
     writeChan (sendChan $ clients ! kickID) ["KICKED"]
-    liftM2 replaceID (return clID) (processAction (kickID, serverInfo, clients, rooms) $ RoomRemoveThisClient "kicked")
+    liftM2 replaceID (return clID) (processAction (kickID, serverInfo, rnc) $ RoomRemoveThisClient "kicked")
 
 
-processAction (clID, serverInfo, clients, rooms) (RemoveClientTeams teamsClID) =
+processAction (clID, serverInfo, rnc) (RemoveClientTeams teamsClID) =
     liftM2 replaceID (return clID) $
-        foldM processAction (teamsClID, serverInfo, clients, rooms) removeTeamsActions
+        foldM processAction (teamsClID, serverInfo, rnc) removeTeamsActions
     where
         client = clients ! teamsClID
         room = rooms ! (roomID client)
         teamsToRemove = Prelude.filter (\t -> teamowner t == nick client) $ teams room
         removeTeamsActions = Prelude.map (RemoveTeam . teamname) teamsToRemove
-
+-}
 
-processAction (clID, serverInfo, clients, rooms) (AddClient client) = do
-    let updatedClients = insert (clientUID client) client clients
-    infoM "Clients" (show (clientUID client) ++ ": New client. Time: " ++ show (connectTime client))
-    writeChan (sendChan client) ["CONNECTED", "Hedgewars server http://www.hedgewars.org/"]
+processAction (AddClient client) = do
+    rnc <- gets roomsClients
+    si <- gets serverInfo
+    liftIO $ do
+        ci <- addClient rnc client
+        forkIO $ clientRecvLoop (clientSocket client) (coreChan si) ci
+        forkIO $ clientSendLoop (clientSocket client) (sendChan client) ci
 
-    let newLogins = takeWhile (\(_ , time) -> (connectTime client) `diffUTCTime` time <= 11) $ lastLogins serverInfo
+        infoM "Clients" (show ci ++ ": New client. Time: " ++ show (connectTime client))
+
+    processAction $ AnswerClients [sendChan client] ["CONNECTED", "Hedgewars server http://www.hedgewars.org/"]
+{-        let newLogins = takeWhile (\(_ , time) -> (connectTime client) `diffUTCTime` time <= 11) $ lastLogins serverInfo
 
-    if isJust $ host client `Prelude.lookup` newLogins then
-        processAction (clID, serverInfo{lastLogins = newLogins}, updatedClients, rooms) $ ByeClient "Reconnected too fast"
-        else
-        return (clID, serverInfo{lastLogins = (host client, connectTime client) : newLogins}, updatedClients, rooms)
+        if False && (isJust $ host client `Prelude.lookup` newLogins) then
+            processAction (ci, serverInfo{lastLogins = newLogins}, rnc) $ ByeClient "Reconnected too fast"
+            else
+            return (ci, serverInfo)
+-}
+
 
 
-processAction (clID, serverInfo, clients, rooms) PingAll = do
-    (_, _, newClients, newRooms) <- foldM kickTimeouted (clID, serverInfo, clients, rooms) $ elems clients
-    processAction (clID,
-        serverInfo,
-        Data.IntMap.map (\cl -> cl{pingsQueue = pingsQueue cl + 1}) newClients,
-        newRooms) $ AnswerAll ["PING"]
+processAction PingAll = do
+    rnc <- gets roomsClients
+    liftIO (allClientsM rnc) >>= mapM_ (kickTimeouted rnc)
+    cis <- liftIO $ allClientsM rnc
+    chans <- liftIO $ mapM (client'sM rnc sendChan) cis
+    liftIO $ mapM_ (modifyClient rnc (\cl -> cl{pingsQueue = pingsQueue cl + 1})) cis
+    processAction $ AnswerClients chans ["PING"]
     where
-        kickTimeouted (clID, serverInfo, clients, rooms) client =
-            if pingsQueue client > 0 then
-                processAction (clientUID client, serverInfo, clients, rooms) $ ByeClient "Ping timeout"
-                else
-                return (clID, serverInfo, clients, rooms)
+        kickTimeouted rnc ci = do
+            pq <- liftIO $ client'sM rnc pingsQueue ci
+            when (pq > 0) $
+                withStateT (\as -> as{clientIndex = Just ci}) $
+                    processAction (ByeClient "Ping timeout")
 
 
-processAction (clID, serverInfo, clients, rooms) (StatsAction) = do
-    writeChan (dbQueries serverInfo) $ SendStats (size clients) (size rooms - 1)
-    return (clID, serverInfo, clients, rooms)
+processAction (StatsAction) = do
+    rnc <- gets roomsClients
+    si <- gets serverInfo
+    (roomsNum, clientsNum) <- liftIO $ withRoomsAndClients rnc stats
+    liftIO $ writeChan (dbQueries si) $ SendStats clientsNum (roomsNum - 1)
+    where
+          stats irnc = (length $ allRooms irnc, length $ allClients irnc)
+
--- a/gameServer/CMakeLists.txt	Sun Nov 14 20:37:55 2010 +0100
+++ b/gameServer/CMakeLists.txt	Sun Nov 14 15:06:02 2010 -0500
@@ -1,43 +1,48 @@
 find_program(ghc_executable ghc)
 
 if(NOT ghc_executable)
-	message(FATAL_ERROR "Cannot find GHC")
+    message(FATAL_ERROR "Cannot find GHC")
 endif(NOT ghc_executable)
 
 
 set(hwserver_sources
-	OfficialServer/DBInteraction.hs
-	Actions.hs
-	ClientIO.hs
-	CoreTypes.hs
-	HWProtoCore.hs
-	HWProtoInRoomState.hs
-	HWProtoLobbyState.hs
-	HWProtoNEState.hs
-	NetRoutines.hs
-	Opts.hs
-	ServerCore.hs
-	Utils.hs
-	hedgewars-server.hs
-	)
+    OfficialServer/DBInteraction.hs
+    Actions.hs
+    ClientIO.hs
+    CoreTypes.hs
+    HWProtoCore.hs
+    HWProtoInRoomState.hs
+    HWProtoLobbyState.hs
+    HWProtoNEState.hs
+    HandlerUtils.hs
+    NetRoutines.hs
+    Opts.hs
+    RoomsAndClients.hs
+    ServerCore.hs
+    ServerState.hs
+    Store.hs
+    Utils.hs
+    hedgewars-server.hs
+    )
 
 set(hwserv_main ${hedgewars_SOURCE_DIR}/gameServer/hedgewars-server.hs)
 
 set(ghc_flags
-	--make ${hwserv_main}
-	-i${hedgewars_SOURCE_DIR}/gameServer
-	-o ${EXECUTABLE_OUTPUT_PATH}/hedgewars-server${CMAKE_EXECUTABLE_SUFFIX}
-	-odir ${CMAKE_CURRENT_BINARY_DIR}
-	-hidir ${CMAKE_CURRENT_BINARY_DIR})
+    -Wall
+    --make ${hwserv_main}
+    -i${hedgewars_SOURCE_DIR}/gameServer
+    -o ${EXECUTABLE_OUTPUT_PATH}/hedgewars-server${CMAKE_EXECUTABLE_SUFFIX}
+    -odir ${CMAKE_CURRENT_BINARY_DIR}
+    -hidir ${CMAKE_CURRENT_BINARY_DIR})
 
 set(ghc_flags ${haskell_compiler_flags_cmn} ${ghc_flags})
 
 add_custom_command(OUTPUT "${EXECUTABLE_OUTPUT_PATH}/hedgewars-server${CMAKE_EXECUTABLE_SUFFIX}"
-		COMMAND "${ghc_executable}"
-		ARGS ${ghc_flags}
-		MAIN_DEPENDENCY ${hwserv_main}
-		DEPENDS ${hwserver_sources}
-		)
+        COMMAND "${ghc_executable}"
+        ARGS ${ghc_flags}
+        MAIN_DEPENDENCY ${hwserv_main}
+        DEPENDS ${hwserver_sources}
+        )
 
 add_custom_target(hedgewars-server ALL DEPENDS "${EXECUTABLE_OUTPUT_PATH}/hedgewars-server${CMAKE_EXECUTABLE_SUFFIX}")
 
--- a/gameServer/ClientIO.hs	Sun Nov 14 20:37:55 2010 +0100
+++ b/gameServer/ClientIO.hs	Sun Nov 14 15:06:02 2010 -0500
@@ -1,4 +1,4 @@
-{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE ScopedTypeVariables, OverloadedStrings #-}
 module ClientIO where
 
 import qualified Control.Exception as Exception
@@ -6,45 +6,71 @@
 import Control.Concurrent
 import Control.Monad
 import System.IO
-import qualified Data.ByteString.UTF8 as BUTF8
-import qualified Data.ByteString as B
+import Network
+import Network.Socket.ByteString
+import qualified Data.ByteString.Char8 as B
 ----------------
 import CoreTypes
+import RoomsAndClients
+import Utils
 
-listenLoop :: Handle -> Int -> [String] -> Chan CoreMessage -> Int -> IO ()
-listenLoop handle linesNumber buf chan clientID = do
-    str <- liftM BUTF8.toString $ B.hGetLine handle
-    if (linesNumber > 50) || (length str > 450) then
-        writeChan chan $ ClientMessage (clientID, ["QUIT", "Protocol violation"])
-        else
-        if str == "" then do
-            writeChan chan $ ClientMessage (clientID, buf)
-            yield
-            listenLoop handle 0 [] chan clientID
-            else
-            listenLoop handle (linesNumber + 1) (buf ++ [str]) chan clientID
+
+pDelim :: B.ByteString
+pDelim = B.pack "\n\n"
+
+bs2Packets :: B.ByteString -> ([[B.ByteString]], B.ByteString)
+bs2Packets buf = unfoldrE extractPackets buf
+    where
+    extractPackets :: B.ByteString -> Either B.ByteString ([B.ByteString], B.ByteString)
+    extractPackets buf = 
+        let buf' = until (not . B.isPrefixOf pDelim) (B.drop 2) buf in
+            let (bsPacket, bufTail) = B.breakSubstring pDelim buf' in
+                if B.null bufTail then
+                    Left bsPacket
+                    else
+                    if B.null bsPacket then 
+                        Left bufTail
+                        else
+                        Right (B.splitWith (== '\n') bsPacket, bufTail)
+
 
-clientRecvLoop :: Handle -> Chan CoreMessage -> Int -> IO ()
-clientRecvLoop handle chan clientID =
-    listenLoop handle 0 [] chan clientID
-        `catch` (\e -> clientOff (show e) >> return ())
-    where clientOff msg = writeChan chan $ ClientMessage (clientID, ["QUIT", msg]) -- if the client disconnects, we perform as if it sent QUIT message
+listenLoop :: Socket -> Chan CoreMessage -> ClientIndex -> IO ()
+listenLoop sock chan ci = recieveWithBufferLoop B.empty
+    where
+        recieveWithBufferLoop recvBuf = do
+            recvBS <- recv sock 4096
+--            putStrLn $ show sock ++ " got smth: " ++ (show $ B.length recvBS)
+            unless (B.null recvBS) $ do
+                let (packets, newrecvBuf) = bs2Packets $ B.append recvBuf recvBS
+                forM_ packets sendPacket
+                recieveWithBufferLoop newrecvBuf
+
+        sendPacket packet = writeChan chan $ ClientMessage (ci, packet)
 
-clientSendLoop :: Handle -> Chan CoreMessage -> Chan [String] -> Int -> IO()
-clientSendLoop handle coreChan chan clientID = do
+
+clientRecvLoop :: Socket -> Chan CoreMessage -> ClientIndex -> IO ()
+clientRecvLoop s chan ci = do
+    msg <- (listenLoop s chan ci >> return "Connection closed") `catch` (return . B.pack . show)
+    clientOff msg
+    where 
+        clientOff msg = mapM_ (writeChan chan) [ClientMessage (ci, ["QUIT", msg]), Remove ci]
+
+
+
+clientSendLoop :: Socket -> Chan [B.ByteString] -> ClientIndex -> IO ()
+clientSendLoop s chan ci = do
     answer <- readChan chan
-    doClose <- Exception.handle
-        (\(e :: Exception.IOException) -> if isQuit answer then return True else sendQuit e >> return False) $ do
-            B.hPutStrLn handle $ BUTF8.fromString $ unlines answer
-            hFlush handle
-            return $ isQuit answer
+    Exception.handle
+        (\(e :: Exception.IOException) -> when (not $ isQuit answer) $ sendQuit e) $ do
+            sendAll s $ (B.unlines answer) `B.append` (B.singleton '\n')
 
-    if doClose then
-        Exception.handle (\(_ :: Exception.IOException) -> putStrLn "error on hClose") $ hClose handle
+    if (isQuit answer) then
+        Exception.handle (\(_ :: Exception.IOException) -> putStrLn "error on sClose") $ sClose s
         else
-        clientSendLoop handle coreChan chan clientID
+        clientSendLoop s chan ci
 
     where
-        sendQuit e = writeChan coreChan $ ClientMessage (clientID, ["QUIT", show e])
+        --sendQuit e = writeChan coreChan $ ClientMessage (ci, ["QUIT", B.pack $ show e])
+        sendQuit e = putStrLn $ show e
         isQuit ("BYE":xs) = True
         isQuit _ = False
--- a/gameServer/CoreTypes.hs	Sun Nov 14 20:37:55 2010 +0100
+++ b/gameServer/CoreTypes.hs	Sun Nov 14 15:06:02 2010 -0500
@@ -1,3 +1,4 @@
+{-# LANGUAGE OverloadedStrings #-}
 module CoreTypes where
 
 import System.IO
@@ -5,102 +6,95 @@
 import Control.Concurrent.STM
 import Data.Word
 import qualified Data.Map as Map
-import qualified Data.IntMap as IntMap
 import qualified Data.IntSet as IntSet
 import Data.Sequence(Seq, empty)
 import Data.Time
 import Network
 import Data.Function
+import Data.ByteString.Char8 as B
 
+import RoomsAndClients
+
+type ClientChan = Chan [B.ByteString]
 
 data ClientInfo =
     ClientInfo
     {
-        clientUID :: !Int,
-        sendChan :: Chan [String],
-        clientHandle :: Handle,
-        host :: String,
+        sendChan :: ClientChan,
+        clientSocket :: Socket,
+        host :: B.ByteString,
         connectTime :: UTCTime,
-        nick :: String,
-        webPassword :: String,
+        nick :: B.ByteString,
+        webPassword :: B.ByteString,
         logonPassed :: Bool,
         clientProto :: !Word16,
-        roomID :: !Int,
+        roomID :: RoomIndex,
         pingsQueue :: !Word,
         isMaster :: Bool,
-        isReady :: Bool,
+        isReady :: !Bool,
         isAdministrator :: Bool,
-        clientClan :: String,
+        clientClan :: B.ByteString,
         teamsInGame :: Word
     }
 
 instance Show ClientInfo where
-    show ci = show (clientUID ci)
-            ++ " nick: " ++ (nick ci)
-            ++ " host: " ++ (host ci)
+    show ci = " nick: " ++ (unpack $ nick ci) ++ " host: " ++ (unpack $ host ci)
 
 instance Eq ClientInfo where
-    (==) = (==) `on` clientHandle
+    (==) = (==) `on` clientSocket
 
 data HedgehogInfo =
-    HedgehogInfo String String
+    HedgehogInfo B.ByteString B.ByteString
 
 data TeamInfo =
     TeamInfo
     {
-        teamownerId :: !Int,
-        teamowner :: String,
-        teamname :: String,
-        teamcolor :: String,
-        teamgrave :: String,
-        teamfort :: String,
-        teamvoicepack :: String,
-        teamflag :: String,
+        teamownerId :: ClientIndex,
+        teamowner :: B.ByteString,
+        teamname :: B.ByteString,
+        teamcolor :: B.ByteString,
+        teamgrave :: B.ByteString,
+        teamfort :: B.ByteString,
+        teamvoicepack :: B.ByteString,
+        teamflag :: B.ByteString,
         difficulty :: Int,
         hhnum :: Int,
         hedgehogs :: [HedgehogInfo]
     }
 
 instance Show TeamInfo where
-    show ti = "owner: " ++ (teamowner ti)
-            ++ "name: " ++ (teamname ti)
-            ++ "color: " ++ (teamcolor ti)
+    show ti = "owner: " ++ (unpack $ teamowner ti)
+            ++ "name: " ++ (unpack $ teamname ti)
+            ++ "color: " ++ (unpack $ teamcolor ti)
 
 data RoomInfo =
     RoomInfo
     {
-        roomUID :: !Int,
-        masterID :: !Int,
-        name :: String,
-        password :: String,
+        masterID :: ClientIndex,
+        name :: B.ByteString,
+        password :: B.ByteString,
         roomProto :: Word16,
         teams :: [TeamInfo],
         gameinprogress :: Bool,
         playersIn :: !Int,
         readyPlayers :: !Int,
-        playersIDs :: IntSet.IntSet,
         isRestrictedJoins :: Bool,
         isRestrictedTeams :: Bool,
-        roundMsgs :: Seq String,
-        leftTeams :: [String],
+        roundMsgs :: Seq B.ByteString,
+        leftTeams :: [B.ByteString],
         teamsAtStart :: [TeamInfo],
-        params :: Map.Map String [String]
+        params :: Map.Map B.ByteString [B.ByteString]
     }
 
 instance Show RoomInfo where
-    show ri = show (roomUID ri)
-            ++ ", players ids: " ++ show (IntSet.size $ playersIDs ri)
-            ++ ", players: " ++ show (playersIn ri)
+    show ri = ", players: " ++ show (playersIn ri)
             ++ ", ready: " ++ show (readyPlayers ri)
             ++ ", teams: " ++ show (teams ri)
 
-instance Eq RoomInfo where
-    (==) = (==) `on` roomUID
-
+newRoom :: RoomInfo
 newRoom = (
     RoomInfo
-        0
-        0
+        undefined
         ""
         ""
         0
@@ -108,7 +102,6 @@
         False
         0
         0
-        IntSet.empty
         False
         False
         Data.Sequence.empty
@@ -128,23 +121,24 @@
     ServerInfo
     {
         isDedicated :: Bool,
-        serverMessage :: String,
-        serverMessageForOldVersions :: String,
+        serverMessage :: B.ByteString,
+        serverMessageForOldVersions :: B.ByteString,
         latestReleaseVersion :: Word16,
         listenPort :: PortNumber,
         nextRoomID :: Int,
-        dbHost :: String,
-        dbLogin :: String,
-        dbPassword :: String,
-        lastLogins :: [(String, UTCTime)],
+        dbHost :: B.ByteString,
+        dbLogin :: B.ByteString,
+        dbPassword :: B.ByteString,
+        lastLogins :: [(B.ByteString, UTCTime)],
         stats :: TMVar StatisticsInfo,
         coreChan :: Chan CoreMessage,
         dbQueries :: Chan DBQuery
     }
 
 instance Show ServerInfo where
-    show si = "Server Info"
+    show _ = "Server Info"
 
+newServerInfo :: TMVar StatisticsInfo -> Chan CoreMessage -> Chan DBQuery -> ServerInfo
 newServerInfo = (
     ServerInfo
         True
@@ -160,29 +154,31 @@
     )
 
 data AccountInfo =
-    HasAccount String Bool
+    HasAccount B.ByteString Bool
     | Guest
     | Admin
     deriving (Show, Read)
 
 data DBQuery =
-    CheckAccount Int String String
+    CheckAccount ClientIndex B.ByteString B.ByteString
     | ClearCache
     | SendStats Int Int
     deriving (Show, Read)
 
 data CoreMessage =
     Accept ClientInfo
-    | ClientMessage (Int, [String])
-    | ClientAccountInfo (Int, AccountInfo)
+    | ClientMessage (ClientIndex, [B.ByteString])
+    | ClientAccountInfo (ClientIndex, AccountInfo)
     | TimerAction Int
-
-type Clients = IntMap.IntMap ClientInfo
-type Rooms = IntMap.IntMap RoomInfo
+    | Remove ClientIndex
 
---type ClientsTransform = [ClientInfo] -> [ClientInfo]
---type RoomsTransform = [RoomInfo] -> [RoomInfo]
---type HandlesSelector = ClientInfo -> [ClientInfo] -> [RoomInfo] -> [ClientInfo]
---type Answer = ServerInfo -> (HandlesSelector, [String])
+instance Show CoreMessage where
+    show (Accept _) = "Accept"
+    show (ClientMessage _) = "ClientMessage"
+    show (ClientAccountInfo _) = "ClientAccountInfo"
+    show (TimerAction _) = "TimerAction"
+    show (Remove _) = "Remove"
+    
+type MRnC = MRoomsAndClients RoomInfo ClientInfo
+type IRnC = IRoomsAndClients RoomInfo ClientInfo
 
-type ClientsSelector = Clients -> Rooms -> [Int]
--- a/gameServer/HWProtoCore.hs	Sun Nov 14 20:37:55 2010 +0100
+++ b/gameServer/HWProtoCore.hs	Sun Nov 14 15:06:02 2010 -0500
@@ -1,8 +1,10 @@
+{-# LANGUAGE OverloadedStrings #-}
 module HWProtoCore where
 
 import qualified Data.IntMap as IntMap
 import Data.Foldable
 import Data.Maybe
+import Control.Monad.Reader
 --------------------------------------
 import CoreTypes
 import Actions
@@ -10,35 +12,37 @@
 import HWProtoNEState
 import HWProtoLobbyState
 import HWProtoInRoomState
+import HandlerUtils
+import RoomsAndClients
 
 handleCmd, handleCmd_loggedin :: CmdHandler
 
-handleCmd clID _ _ ["PING"] = [AnswerThisClient ["PONG"]]
+
+handleCmd ["PING"] = answerClient ["PONG"]
 
-handleCmd clID clients rooms ("QUIT" : xs) =
-    [ByeClient msg]
+
+handleCmd ("QUIT" : xs) = return [ByeClient msg]
     where
         msg = if not $ null xs then head xs else ""
 
-
-handleCmd clID clients _ ["PONG"] =
+{-
+handleCmd ["PONG"] =
     if pingsQueue client == 0 then
         [ProtocolError "Protocol violation"]
     else
         [ModifyClient (\cl -> cl{pingsQueue = pingsQueue cl - 1})]
     where
         client = clients IntMap.! clID
-
+-}
 
-handleCmd clID clients rooms cmd =
-    if not $ logonPassed client then
-        handleCmd_NotEntered clID clients rooms cmd
-    else
-        handleCmd_loggedin clID clients rooms cmd
-    where
-        client = clients IntMap.! clID
+handleCmd cmd = do
+    (ci, irnc) <- ask
+    if logonPassed (irnc `client` ci) then
+        handleCmd_loggedin cmd
+        else
+        handleCmd_NotEntered cmd
 
-
+{-
 handleCmd_loggedin clID clients rooms ["INFO", asknick] =
     if noSuchClient then
         []
@@ -62,11 +66,12 @@
             then if teamsInGame client > 0 then "(playing)" else "(spectating)"
             else ""
 
+-}
 
-handleCmd_loggedin clID clients rooms cmd =
-    if roomID client == 0 then
-        handleCmd_lobby clID clients rooms cmd
-    else
-        handleCmd_inRoom clID clients rooms cmd
-    where
-        client = clients IntMap.! clID
+
+handleCmd_loggedin cmd = do
+    (ci, rnc) <- ask
+    if clientRoom rnc ci == lobbyId then
+        handleCmd_lobby cmd
+        else
+        handleCmd_inRoom cmd
--- a/gameServer/HWProtoInRoomState.hs	Sun Nov 14 20:37:55 2010 +0100
+++ b/gameServer/HWProtoInRoomState.hs	Sun Nov 14 15:06:02 2010 -0500
@@ -1,182 +1,240 @@
+{-# LANGUAGE OverloadedStrings #-}
 module HWProtoInRoomState where
 
 import qualified Data.Foldable as Foldable
-import qualified Data.IntMap as IntMap
 import qualified Data.Map as Map
 import Data.Sequence(Seq, (|>), (><), fromList, empty)
 import Data.List
 import Data.Maybe
+import qualified Data.ByteString.Char8 as B
+import Control.Monad
+import Control.Monad.Reader
 --------------------------------------
 import CoreTypes
 import Actions
 import Utils
-
+import HandlerUtils
+import RoomsAndClients
 
 handleCmd_inRoom :: CmdHandler
 
-handleCmd_inRoom clID clients _ ["CHAT", msg] =
-    [AnswerOthersInRoom ["CHAT", clientNick, msg]]
-    where
-        clientNick = nick $ clients IntMap.! clID
+handleCmd_inRoom ["CHAT", msg] = do
+    n <- clientNick
+    s <- roomOthersChans
+    return [AnswerClients s ["CHAT", n, msg]]
 
-handleCmd_inRoom clID clients rooms ["PART"] =
-    [RoomRemoveThisClient "part"]
-    where
-        client = clients IntMap.! clID
+handleCmd_inRoom ["PART"] = return [MoveToLobby "part"]
+handleCmd_inRoom ["PART", msg] = return [MoveToLobby $ "part: " `B.append` msg]
 
 
-handleCmd_inRoom clID clients rooms ("CFG" : paramName : paramStrs)
-    | null paramStrs = [ProtocolError "Empty config entry"]
-    | isMaster client =
-        [ModifyRoom (\r -> r{params = Map.insert paramName paramStrs (params r)}),
-        AnswerOthersInRoom ("CFG" : paramName : paramStrs)]
-    | otherwise = [ProtocolError "Not room master"]
-    where
-        client = clients IntMap.! clID
+handleCmd_inRoom ("CFG" : paramName : paramStrs)
+    | null paramStrs = return [ProtocolError "Empty config entry"]
+    | otherwise = do
+        chans <- roomOthersChans
+        cl <- thisClient
+        if isMaster cl then
+           return [
+                ModifyRoom (\r -> r{params = Map.insert paramName paramStrs (params r)}),
+                AnswerClients chans ("CFG" : paramName : paramStrs)]
+            else
+            return [ProtocolError "Not room master"]
 
-handleCmd_inRoom clID clients rooms ("ADD_TEAM" : name : color : grave : fort : voicepack : flag : difStr : hhsInfo)
-    | length hhsInfo == 15 && clientProto client < 30 = handleCmd_inRoom clID clients rooms ("ADD_TEAM" : name : color : grave : fort : voicepack : " " : flag : difStr : hhsInfo)
-    | length hhsInfo /= 16 = [ProtocolError "Corrupted hedgehogs info"]
-    | length (teams room) == 6 = [Warning "too many teams"]
-    | canAddNumber <= 0 = [Warning "too many hedgehogs"]
-    | isJust findTeam = [Warning "There's already a team with same name in the list"]
-    | gameinprogress room = [Warning "round in progress"]
-    | isRestrictedTeams room = [Warning "restricted"]
-    | otherwise =
-        [ModifyRoom (\r -> r{teams = teams r ++ [newTeam]}),
-        ModifyClient (\c -> c{teamsInGame = teamsInGame c + 1, clientClan = color}),
-        AnswerThisClient ["TEAM_ACCEPTED", name],
-        AnswerOthersInRoom $ teamToNet (clientProto client) newTeam,
-        AnswerOthersInRoom ["TEAM_COLOR", name, color]
-        ]
+handleCmd_inRoom ("ADD_TEAM" : name : color : grave : fort : voicepack : flag : difStr : hhsInfo)
+    | length hhsInfo /= 16 = return [ProtocolError "Corrupted hedgehogs info"]
+    | otherwise = do
+        (ci, rnc) <- ask
+        r <- thisRoom
+        clNick <- clientNick
+        clChan <- thisClientChans
+        othersChans <- roomOthersChans
+        return $
+            if not . null . drop 5 $ teams r then
+                [Warning "too many teams"]
+            else if canAddNumber r <= 0 then
+                [Warning "too many hedgehogs"]
+            else if isJust $ findTeam r then
+                [Warning "There's already a team with same name in the list"]
+            else if gameinprogress r then
+                [Warning "round in progress"]
+            else if isRestrictedTeams r then
+                [Warning "restricted"]
+            else
+                [ModifyRoom (\r -> r{teams = teams r ++ [newTeam ci clNick r]}),
+                ModifyClient (\c -> c{teamsInGame = teamsInGame c + 1, clientClan = color}),
+                AnswerClients clChan ["TEAM_ACCEPTED", name],
+                AnswerClients othersChans $ teamToNet $ newTeam ci clNick r,
+                AnswerClients othersChans ["TEAM_COLOR", name, color]
+                ]
+        where
+        canAddNumber r = 48 - (sum . map hhnum $ teams r)
+        findTeam = find (\t -> name == teamname t) . teams
+        newTeam ci clNick r = (TeamInfo ci clNick name color grave fort voicepack flag difficulty (newTeamHHNum r) (hhsList hhsInfo))
+        difficulty = case B.readInt difStr of
+                           Just (i, t) | B.null t -> fromIntegral i
+                           otherwise -> 0
+        hhsList [] = []
+        hhsList [_] = error "Hedgehogs list with odd elements number"
+        hhsList (n:h:hhs) = HedgehogInfo n h : hhsList hhs
+        newTeamHHNum r = min 4 (canAddNumber r)
+
+handleCmd_inRoom ["REMOVE_TEAM", name] = do
+        (ci, rnc) <- ask
+        r <- thisRoom
+        clNick <- clientNick
+
+        let maybeTeam = findTeam r
+        let team = fromJust maybeTeam
+
+        return $
+            if isNothing $ findTeam r then
+                [Warning "REMOVE_TEAM: no such team"]
+            else if clNick /= teamowner team then
+                [ProtocolError "Not team owner!"]
+            else
+                [RemoveTeam name,
+                ModifyClient
+                    (\c -> c{
+                        teamsInGame = teamsInGame c - 1,
+                        clientClan = if teamsInGame c == 1 then undefined else anotherTeamClan ci r
+                        })
+                ]
     where
-        client = clients IntMap.! clID
-        room = rooms IntMap.! (roomID client)
-        canAddNumber = 48 - (sum . map hhnum $ teams room)
-        findTeam = find (\t -> name == teamname t) $ teams room
-        newTeam = (TeamInfo clID (nick client) name color grave fort voicepack flag difficulty newTeamHHNum (hhsList hhsInfo))
-        difficulty = fromMaybe 0 (maybeRead difStr :: Maybe Int)
-        hhsList [] = []
-        hhsList (n:h:hhs) = HedgehogInfo n h : hhsList hhs
-        newTeamHHNum = min 4 canAddNumber
-
-handleCmd_inRoom clID clients rooms ["REMOVE_TEAM", teamName]
-    | noSuchTeam = [Warning "REMOVE_TEAM: no such team"]
-    | nick client /= teamowner team = [ProtocolError "Not team owner!"]
-    | otherwise =
-            [RemoveTeam teamName,
-            ModifyClient (\c -> c{teamsInGame = teamsInGame c - 1, clientClan = if teamsInGame client == 1 then undefined else anotherTeamClan})
-            ]
-    where
-        client = clients IntMap.! clID
-        room = rooms IntMap.! (roomID client)
-        noSuchTeam = isNothing findTeam
-        team = fromJust findTeam
-        findTeam = find (\t -> teamName == teamname t) $ teams room
-        anotherTeamClan = teamcolor $ fromJust $ find (\t -> teamownerId t == clID) $ teams room
+        anotherTeamClan ci = teamcolor . fromJust . find (\t -> teamownerId t == ci) . teams
+        findTeam = find (\t -> name == teamname t) . teams
 
 
-handleCmd_inRoom clID clients rooms ["HH_NUM", teamName, numberStr]
-    | not $ isMaster client = [ProtocolError "Not room master"]
-    | hhNumber < 1 || hhNumber > 8 || noSuchTeam || hhNumber > (canAddNumber + (hhnum team)) = []
-    | otherwise =
-        [ModifyRoom $ modifyTeam team{hhnum = hhNumber},
-        AnswerOthersInRoom ["HH_NUM", teamName, show hhNumber]]
+handleCmd_inRoom ["HH_NUM", teamName, numberStr] = do
+    cl <- thisClient
+    others <- roomOthersChans
+    r <- thisRoom
+
+    let maybeTeam = findTeam r
+    let team = fromJust maybeTeam
+
+    return $
+        if not $ isMaster cl then
+            [ProtocolError "Not room master"]
+        else if hhNumber < 1 || hhNumber > 8 || isNothing maybeTeam || hhNumber > (canAddNumber r) + (hhnum team) then
+            []
+        else
+            [ModifyRoom $ modifyTeam team{hhnum = hhNumber},
+            AnswerClients others ["HH_NUM", teamName, B.pack $ show hhNumber]]
     where
-        client = clients IntMap.! clID
-        room = rooms IntMap.! (roomID client)
-        hhNumber = fromMaybe 0 (maybeRead numberStr :: Maybe Int)
-        noSuchTeam = isNothing findTeam
-        team = fromJust findTeam
-        findTeam = find (\t -> teamName == teamname t) $ teams room
-        canAddNumber = 48 - (sum . map hhnum $ teams room)
+        hhNumber = case B.readInt numberStr of
+                           Just (i, t) | B.null t -> fromIntegral i
+                           otherwise -> 0
+        findTeam = find (\t -> teamName == teamname t) . teams
+        canAddNumber = (-) 48 . sum . map hhnum . teams
+
 
 
-handleCmd_inRoom clID clients rooms ["TEAM_COLOR", teamName, newColor]
-    | not $ isMaster client = [ProtocolError "Not room master"]
-    | noSuchTeam = []
-    | otherwise = [ModifyRoom $ modifyTeam team{teamcolor = newColor},
-            AnswerOthersInRoom ["TEAM_COLOR", teamName, newColor],
+handleCmd_inRoom ["TEAM_COLOR", teamName, newColor] = do
+    cl <- thisClient
+    others <- roomOthersChans
+    r <- thisRoom
+
+    let maybeTeam = findTeam r
+    let team = fromJust maybeTeam
+
+    return $
+        if not $ isMaster cl then
+            [ProtocolError "Not room master"]
+        else if isNothing maybeTeam then
+            []
+        else
+            [ModifyRoom $ modifyTeam team{teamcolor = newColor},
+            AnswerClients others ["TEAM_COLOR", teamName, newColor],
             ModifyClient2 (teamownerId team) (\c -> c{clientClan = newColor})]
     where
-        noSuchTeam = isNothing findTeam
-        team = fromJust findTeam
-        findTeam = find (\t -> teamName == teamname t) $ teams room
-        client = clients IntMap.! clID
-        room = rooms IntMap.! (roomID client)
+        findTeam = find (\t -> teamName == teamname t) . teams
 
 
-handleCmd_inRoom clID clients rooms ["TOGGLE_READY"] =
-    [ModifyClient (\c -> c{isReady = not $ isReady client}),
-    ModifyRoom (\r -> r{readyPlayers = readyPlayers r + (if isReady client then -1 else 1)}),
-    AnswerThisRoom [if isReady client then "NOT_READY" else "READY", nick client]]
-    where
-        client = clients IntMap.! clID
+handleCmd_inRoom ["TOGGLE_READY"] = do
+    cl <- thisClient
+    chans <- roomClientsChans
+    return [
+        ModifyClient (\c -> c{isReady = not $ isReady cl}),
+        ModifyRoom (\r -> r{readyPlayers = readyPlayers r + (if isReady cl then -1 else 1)}),
+        AnswerClients chans [if isReady cl then "NOT_READY" else "READY", nick cl]
+        ]
 
+handleCmd_inRoom ["START_GAME"] = do
+    cl <- thisClient
+    r <- thisRoom
+    chans <- roomClientsChans
 
-handleCmd_inRoom clID clients rooms ["START_GAME"] =
-    if isMaster client && (playersIn room == readyPlayers room) && (not . gameinprogress) room then
-        if enoughClans then
-            [ModifyRoom
+    if isMaster cl && (playersIn r == readyPlayers r) && (not $ gameinprogress r) then
+        if enoughClans r then
+            return [
+                ModifyRoom
                     (\r -> r{
                         gameinprogress = True,
                         roundMsgs = empty,
                         leftTeams = [],
                         teamsAtStart = teams r}
                     ),
-            AnswerThisRoom ["RUN_GAME"]]
+                AnswerClients chans ["RUN_GAME"]
+                ]
+            else
+            return [Warning "Less than two clans!"]
         else
-            [Warning "Less than two clans!"]
-    else
-        []
+        return []
     where
-        client = clients IntMap.! clID
-        room = rooms IntMap.! (roomID client)
-        enoughClans = not $ null $ drop 1 $ group $ map teamcolor $ teams room
+        enoughClans = not . null . drop 1 . group . map teamcolor . teams
 
 
-handleCmd_inRoom clID clients rooms ["EM", msg] =
-    if (teamsInGame client > 0) && isLegal then
-        (AnswerOthersInRoom ["EM", msg]) : [ModifyRoom (\r -> r{roundMsgs = roundMsgs r |> msg}) | not isKeepAlive]
-    else
-        []
+handleCmd_inRoom ["EM", msg] = do
+    cl <- thisClient
+    r <- thisRoom
+    chans <- roomOthersChans
+    
+    if (teamsInGame cl > 0) && isLegal then
+        return $ (AnswerClients chans ["EM", msg]) : [ModifyRoom (\r -> r{roundMsgs = roundMsgs r |> msg}) | not isKeepAlive]
+        else
+        return []
     where
-        client = clients IntMap.! clID
         (isLegal, isKeepAlive) = checkNetCmd msg
 
-handleCmd_inRoom clID clients rooms ["ROUNDFINISHED"] =
-    if isMaster client then
-        [ModifyRoom
+
+handleCmd_inRoom ["ROUNDFINISHED"] = do
+    cl <- thisClient
+    r <- thisRoom
+    chans <- roomClientsChans
+
+    if isMaster cl && (gameinprogress r) then
+        return $ (ModifyRoom
                 (\r -> r{
                     gameinprogress = False,
                     readyPlayers = 0,
                     roundMsgs = empty,
                     leftTeams = [],
                     teamsAtStart = []}
-                ),
-        UnreadyRoomClients
-        ] ++ answerRemovedTeams
-    else
-        []
+                ))
+            : UnreadyRoomClients
+            : answerRemovedTeams chans r
+        else
+        return []
     where
-        client = clients IntMap.! clID
-        room = rooms IntMap.! (roomID client)
-        answerRemovedTeams = map (\t -> AnswerThisRoom ["REMOVE_TEAM", t]) $ leftTeams room
+        answerRemovedTeams chans = map (\t -> AnswerClients chans ["REMOVE_TEAM", t]) . leftTeams
+
+handleCmd_inRoom ["TOGGLE_RESTRICT_JOINS"] = do
+    cl <- thisClient
+    return $
+        if not $ isMaster cl then
+            [ProtocolError "Not room master"]
+        else
+            [ModifyRoom (\r -> r{isRestrictedJoins = not $ isRestrictedJoins r})]
 
 
-handleCmd_inRoom clID clients _ ["TOGGLE_RESTRICT_JOINS"]
-    | isMaster client = [ModifyRoom (\r -> r{isRestrictedJoins = not $ isRestrictedJoins r})]
-    | otherwise = [ProtocolError "Not room master"]
-    where
-        client = clients IntMap.! clID
-
+handleCmd_inRoom ["TOGGLE_RESTRICT_TEAMS"] = do
+    cl <- thisClient
+    return $
+        if not $ isMaster cl then
+            [ProtocolError "Not room master"]
+        else
+            [ModifyRoom (\r -> r{isRestrictedTeams = not $ isRestrictedTeams r})]
 
-handleCmd_inRoom clID clients _ ["TOGGLE_RESTRICT_TEAMS"]
-    | isMaster client = [ModifyRoom (\r -> r{isRestrictedTeams = not $ isRestrictedTeams r})]
-    | otherwise = [ProtocolError "Not room master"]
-    where
-        client = clients IntMap.! clID
-
+{-
 handleCmd_inRoom clID clients rooms ["KICK", kickNick] =
     [KickRoomClient kickID | isMaster client && not noSuchClient && (kickID /= clID) && (roomID client == roomID kickClient)]
     where
@@ -192,5 +250,5 @@
     where
         client = clients IntMap.! clID
         engineMsg = toEngineMsg $ 'b' : ((nick client) ++ "(team): " ++ msg ++ "\x20\x20")
-
-handleCmd_inRoom clID _ _ _ = [ProtocolError "Incorrect command (state: in room)"]
+-}
+handleCmd_inRoom _ = return [ProtocolError "Incorrect command (state: in room)"]
--- a/gameServer/HWProtoLobbyState.hs	Sun Nov 14 20:37:55 2010 +0100
+++ b/gameServer/HWProtoLobbyState.hs	Sun Nov 14 15:06:02 2010 -0500
@@ -1,73 +1,102 @@
+{-# LANGUAGE OverloadedStrings #-}
 module HWProtoLobbyState where
 
 import qualified Data.Map as Map
-import qualified Data.IntMap as IntMap
 import qualified Data.IntSet as IntSet
 import qualified Data.Foldable as Foldable
 import Data.Maybe
 import Data.List
 import Data.Word
+import Control.Monad.Reader
+import qualified Data.ByteString.Char8 as B
 --------------------------------------
 import CoreTypes
 import Actions
 import Utils
+import HandlerUtils
+import RoomsAndClients
 
-answerAllTeams protocol teams = concatMap toAnswer teams
+{-answerAllTeams protocol teams = concatMap toAnswer teams
     where
         toAnswer team =
             [AnswerThisClient $ teamToNet protocol team,
             AnswerThisClient ["TEAM_COLOR", teamname team, teamcolor team],
             AnswerThisClient ["HH_NUM", teamname team, show $ hhnum team]]
-
+-}
 handleCmd_lobby :: CmdHandler
 
-handleCmd_lobby clID clients rooms ["LIST"] =
-    [AnswerThisClient ("ROOMS" : roomsInfoList)]
+
+handleCmd_lobby ["LIST"] = do
+    (ci, irnc) <- ask
+    let cl = irnc `client` ci
+    rooms <- allRoomInfos
+    let roomsInfoList = concatMap (roomInfo irnc) . filter (\r -> (roomProto r == clientProto cl) && not (isRestrictedJoins r))
+    return [AnswerClients [sendChan cl] ("ROOMS" : roomsInfoList rooms)]
     where
-        roomsInfoList = concatMap roomInfo sameProtoRooms
-        sameProtoRooms = filter (\r -> (roomProto r == protocol) && not (isRestrictedJoins r)) roomsList
-        roomsList = IntMap.elems rooms
-        protocol = clientProto client
-        client = clients IntMap.! clID
-        roomInfo room
-            | clientProto client < 28 = [
+        roomInfo irnc room = [
+                showB $ gameinprogress room,
                 name room,
-                show (playersIn room) ++ "(" ++ show (length $ teams room) ++ ")",
-                show $ gameinprogress room
-                ]
-            | otherwise = [
-                show $ gameinprogress room,
-                name room,
-                show $ playersIn room,
-                show $ length $ teams room,
-                nick $ clients IntMap.! (masterID room),
+                showB $ playersIn room,
+                showB $ length $ teams room,
+                nick $ irnc `client` masterID room,
                 head (Map.findWithDefault ["+gen+"] "MAP" (params room)),
                 head (Map.findWithDefault ["Default"] "SCHEME" (params room)),
                 head (Map.findWithDefault ["Default"] "AMMO" (params room))
                 ]
 
-handleCmd_lobby clID clients _ ["CHAT", msg] =
-    [AnswerOthersInRoom ["CHAT", clientNick, msg]]
-    where
-        clientNick = nick $ clients IntMap.! clID
+
+handleCmd_lobby ["CHAT", msg] = do
+    n <- clientNick
+    s <- roomOthersChans
+    return [AnswerClients s ["CHAT", n, msg]]
+
+handleCmd_lobby ["CREATE_ROOM", newRoom, roomPassword]
+    | illegalName newRoom = return [Warning "Illegal room name"]
+    | otherwise = do
+        rs <- allRoomInfos
+        cl <- thisClient
+        return $ if isJust $ find (\room -> newRoom == name room) rs then 
+            [Warning "Room exists"]
+            else
+            [
+                AddRoom newRoom roomPassword,
+                AnswerClients [sendChan cl] ["NOT_READY", nick cl]
+            ]
+
+
+handleCmd_lobby ["CREATE_ROOM", newRoom] =
+    handleCmd_lobby ["CREATE_ROOM", newRoom, ""]
 
 
-handleCmd_lobby clID clients rooms ["CREATE_ROOM", newRoom, roomPassword]
-    | haveSameRoom = [Warning "Room exists"]
-    | illegalName newRoom = [Warning "Illegal room name"]
-    | otherwise =
-        [RoomRemoveThisClient "", -- leave lobby
-        AddRoom newRoom roomPassword,
-        AnswerThisClient ["NOT_READY", clientNick]
-        ]
+handleCmd_lobby ["JOIN_ROOM", roomName, roomPassword] = do
+    (ci, irnc) <- ask
+    let ris = allRooms irnc
+    cl <- thisClient
+    let maybeRI = find (\ri -> roomName == name (irnc `room` ri)) ris
+    let jRI = fromJust maybeRI
+    let jRoom = irnc `room` jRI
+    let jRoomClients = map (client irnc) $! roomClients irnc jRI -- no lazyness here!
+    return $
+        if isNothing maybeRI then 
+            [Warning "No such rooms"]
+            else if isRestrictedJoins jRoom then
+            [Warning "Joining restricted"]
+            else if roomPassword /= password jRoom then
+            [Warning "Wrong password"]
+            else
+            [
+                MoveToRoom jRI,
+                AnswerClients (map sendChan $ cl : jRoomClients) ["NOT_READY", nick cl]
+            ]
+            ++ [ AnswerClients [sendChan cl] $ "JOINED" : map nick jRoomClients | playersIn jRoom /= 0]
+            ++ (map (readynessMessage cl) jRoomClients)
+
     where
-        clientNick = nick $ clients IntMap.! clID
-        haveSameRoom = isJust $ find (\room -> newRoom == name room) $ IntMap.elems rooms
+        readynessMessage cl c = AnswerClients [sendChan cl] [if isReady c then "READY" else "NOT_READY", nick c]
 
 
-handleCmd_lobby clID clients rooms ["CREATE_ROOM", newRoom] =
-    handleCmd_lobby clID clients rooms ["CREATE_ROOM", newRoom, ""]
 
+{-
 
 handleCmd_lobby clID clients rooms ["JOIN_ROOM", roomName, roomPassword]
     | noSuchRoom = [Warning "No such room"]
@@ -83,12 +112,6 @@
         ++ answerTeams
         ++ watchRound
     where
-        noSuchRoom = isNothing mbRoom
-        mbRoom = find (\r -> roomName == name r && roomProto r == clientProto client) $ IntMap.elems rooms
-        jRoom = fromJust mbRoom
-        rID = roomUID jRoom
-        client = clients IntMap.! clID
-        roomClientsIDs = IntSet.elems $ playersIDs jRoom
         answerNicks =
             [AnswerThisClient $ "JOINED" :
             map (\clID -> nick $ clients IntMap.! clID) roomClientsIDs | playersIn jRoom /= 0]
@@ -100,7 +123,7 @@
             roomClientsIDs
 
         toAnswer (paramName, paramStrs) = AnswerThisClient $ "CFG" : paramName : paramStrs
-        
+
         answerFullConfig = map toAnswer (leftConfigPart ++ rightConfigPart)
         (leftConfigPart, rightConfigPart) = partition (\(p, _) -> p /= "MAP") (Map.toList $ params jRoom)
 
@@ -114,12 +137,12 @@
                 answerAllTeams (clientProto client) (teamsAtStart jRoom)
             else
                 answerAllTeams (clientProto client) (teams jRoom)
-
+-}
 
-handleCmd_lobby clID clients rooms ["JOIN_ROOM", roomName] =
-    handleCmd_lobby clID clients rooms ["JOIN_ROOM", roomName, ""]
-    
+handleCmd_lobby ["JOIN_ROOM", roomName] =
+    handleCmd_lobby ["JOIN_ROOM", roomName, ""]
 
+{-
 handleCmd_lobby clID clients rooms ["FOLLOW", asknick] =
     if noSuchClient || roomID followClient == 0 then
         []
@@ -180,6 +203,7 @@
         [ClearAccountsCache | isAdministrator client]
     where
         client = clients IntMap.! clID
+-}
 
 
-handleCmd_lobby clID _ _ _ = [ProtocolError "Incorrect command (state: in lobby)"]
+handleCmd_lobby _ = return [ProtocolError "Incorrect command (state: in lobby)"]
--- a/gameServer/HWProtoNEState.hs	Sun Nov 14 20:37:55 2010 +0100
+++ b/gameServer/HWProtoNEState.hs	Sun Nov 14 15:06:02 2010 -0500
@@ -1,54 +1,66 @@
+{-# LANGUAGE OverloadedStrings #-}
 module HWProtoNEState where
 
 import qualified Data.IntMap as IntMap
 import Data.Maybe
 import Data.List
 import Data.Word
+import Control.Monad.Reader
+import qualified Data.ByteString.Char8 as B
 --------------------------------------
 import CoreTypes
 import Actions
 import Utils
+import RoomsAndClients
 
 handleCmd_NotEntered :: CmdHandler
 
-handleCmd_NotEntered clID clients _ ["NICK", newNick]
-    | not . null $ nick client = [ProtocolError "Nickname already chosen"]
-    | haveSameNick = [AnswerThisClient ["WARNING", "Nickname already in use"], ByeClient ""]
-    | illegalName newNick = [ByeClient "Illegal nickname"]
-    | otherwise =
-        ModifyClient (\c -> c{nick = newNick}) :
-        AnswerThisClient ["NICK", newNick] :
-        [CheckRegistered | clientProto client /= 0]
+handleCmd_NotEntered ["NICK", newNick] = do
+    (ci, irnc) <- ask
+    let cl = irnc `client` ci
+    if not . B.null $ nick cl then return [ProtocolError "Nickname already chosen"]
+        else
+        if haveSameNick irnc (nick cl) then return [AnswerClients [sendChan cl] ["WARNING", "Nickname already in use"], ByeClient ""]
+            else
+            if illegalName newNick then return [ByeClient "Illegal nickname"]
+                else
+                return $
+                    ModifyClient (\c -> c{nick = newNick}) :
+                    AnswerClients [sendChan cl] ["NICK", newNick] :
+                    [CheckRegistered | clientProto cl /= 0]
     where
-        client = clients IntMap.! clID
-        haveSameNick = isJust $ find (\cl -> newNick == nick cl) $ IntMap.elems clients
+    haveSameNick irnc clNick = isJust $ find (\cl -> newNick == clNick) $ map (client irnc) $ allClients irnc
+
+handleCmd_NotEntered ["PROTO", protoNum] = do
+    (ci, irnc) <- ask
+    let cl = irnc `client` ci
+    if clientProto cl > 0 then return [ProtocolError "Protocol already known"]
+        else
+        if parsedProto == 0 then return [ProtocolError "Bad number"]
+            else
+            return $
+                ModifyClient (\c -> c{clientProto = parsedProto}) :
+                AnswerClients [sendChan cl] ["PROTO", B.pack $ show parsedProto] :
+                [CheckRegistered | not . B.null $ nick cl]
+    where
+        parsedProto = case B.readInt protoNum of
+                           Just (i, t) | B.null t -> fromIntegral i
+                           otherwise -> 0
 
 
-handleCmd_NotEntered clID clients _ ["PROTO", protoNum]
-    | clientProto client > 0 = [ProtocolError "Protocol already known"]
-    | parsedProto == 0 = [ProtocolError "Bad number"]
-    | otherwise =
-        ModifyClient (\c -> c{clientProto = parsedProto}) :
-        AnswerThisClient ["PROTO", show parsedProto] :
-        [CheckRegistered | (not . null) (nick client)]
-    where
-        client = clients IntMap.! clID
-        parsedProto = fromMaybe 0 (maybeRead protoNum :: Maybe Word16)
+handleCmd_NotEntered ["PASSWORD", passwd] = do
+    (ci, irnc) <- ask
+    let cl = irnc `client` ci
 
+    if passwd == webPassword cl then
+        return $ JoinLobby : [AnswerClients [sendChan cl] ["ADMIN_ACCESS"] | isAdministrator cl]
+        else
+        return [ByeClient "Authentication failed"]
 
-handleCmd_NotEntered clID clients _ ["PASSWORD", passwd] =
-    if passwd == webPassword client then
-        [ModifyClient (\cl -> cl{logonPassed = True}),
-        MoveToLobby] ++ adminNotice
-    else
-        [ByeClient "Authentication failed"]
-    where
-        client = clients IntMap.! clID
-        adminNotice = [AnswerThisClient ["ADMIN_ACCESS"] | isAdministrator client]
-
+{-
 
 handleCmd_NotEntered clID clients _ ["DUMP"] =
     if isAdministrator (clients IntMap.! clID) then [Dump] else []
-
+-}
 
-handleCmd_NotEntered clID _ _ _ = [ProtocolError "Incorrect command (state: not entered)"]
+handleCmd_NotEntered _ = return [ProtocolError "Incorrect command (state: not entered)"]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gameServer/HandlerUtils.hs	Sun Nov 14 15:06:02 2010 -0500
@@ -0,0 +1,45 @@
+module HandlerUtils where
+
+import Control.Monad.Reader
+import qualified Data.ByteString.Char8 as B
+
+import RoomsAndClients
+import CoreTypes
+import Actions
+
+thisClient :: Reader (ClientIndex, IRnC) ClientInfo
+thisClient = do
+    (ci, rnc) <- ask
+    return $ rnc `client` ci
+
+thisRoom :: Reader (ClientIndex, IRnC) RoomInfo
+thisRoom = do
+    (ci, rnc) <- ask
+    let ri = clientRoom rnc ci
+    return $ rnc `room` ri
+
+clientNick :: Reader (ClientIndex, IRnC) B.ByteString
+clientNick = liftM nick thisClient
+
+roomOthersChans :: Reader (ClientIndex, IRnC) [ClientChan]
+roomOthersChans = do
+    (ci, rnc) <- ask
+    let ri = clientRoom rnc ci
+    return $ map (sendChan . client rnc) $ filter (/= ci) (roomClients rnc ri)
+
+roomClientsChans :: Reader (ClientIndex, IRnC) [ClientChan]
+roomClientsChans = do
+    (ci, rnc) <- ask
+    let ri = clientRoom rnc ci
+    return $ map (sendChan . client rnc) (roomClients rnc ri)
+
+thisClientChans :: Reader (ClientIndex, IRnC) [ClientChan]
+thisClientChans = do
+    (ci, rnc) <- ask
+    return $ [sendChan (rnc `client` ci)]
+
+answerClient :: [B.ByteString] -> Reader (ClientIndex, IRnC) [Action]
+answerClient msg = thisClientChans >>= return . (: []) . flip AnswerClients msg
+
+allRoomInfos :: Reader (a, IRnC) [RoomInfo]
+allRoomInfos = liftM ((\irnc -> map (room irnc) $ allRooms irnc) . snd) ask
--- a/gameServer/NetRoutines.hs	Sun Nov 14 20:37:55 2010 +0100
+++ b/gameServer/NetRoutines.hs	Sun Nov 14 15:06:02 2010 -0500
@@ -1,46 +1,41 @@
-{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE ScopedTypeVariables, OverloadedStrings #-}
 module NetRoutines where
 
-import Network
 import Network.Socket
 import System.IO
-import Control.Concurrent
 import Control.Concurrent.Chan
-import Control.Concurrent.STM
 import qualified Control.Exception as Exception
 import Data.Time
+import Control.Monad
 -----------------------------
 import CoreTypes
-import ClientIO
 import Utils
+import RoomsAndClients
 
-acceptLoop :: Socket -> Chan CoreMessage -> Int -> IO ()
-acceptLoop servSock coreChan clientCounter = do
+acceptLoop :: Socket -> Chan CoreMessage -> IO ()
+acceptLoop servSock chan = forever $ do
     Exception.handle
         (\(_ :: Exception.IOException) -> putStrLn "exception on connect") $
         do
-        (socket, sockAddr) <- Network.Socket.accept servSock
+        (sock, sockAddr) <- Network.Socket.accept servSock
 
-        cHandle <- socketToHandle socket ReadWriteMode
-        hSetBuffering cHandle LineBuffering
         clientHost <- sockAddr2String sockAddr
 
         currentTime <- getCurrentTime
-        
-        sendChan <- newChan
+
+        sendChan' <- newChan
 
         let newClient =
                 (ClientInfo
-                    nextID
-                    sendChan
-                    cHandle
+                    sendChan'
+                    sock
                     clientHost
                     currentTime
                     ""
                     ""
                     False
                     0
-                    0
+                    lobbyId
                     0
                     False
                     False
@@ -49,12 +44,5 @@
                     undefined
                     )
 
-        writeChan coreChan $ Accept newClient
-
-        forkIO $ clientRecvLoop cHandle coreChan nextID
-        forkIO $ clientSendLoop cHandle coreChan sendChan nextID
+        writeChan chan $ Accept newClient
         return ()
-
-    acceptLoop servSock coreChan nextID
-    where
-        nextID = clientCounter + 1
--- a/gameServer/OfficialServer/DBInteraction.hs	Sun Nov 14 20:37:55 2010 +0100
+++ b/gameServer/OfficialServer/DBInteraction.hs	Sun Nov 14 15:06:02 2010 -0500
@@ -1,4 +1,4 @@
-{-# LANGUAGE CPP, ScopedTypeVariables #-}
+{-# LANGUAGE CPP, ScopedTypeVariables, OverloadedStrings #-}
 module OfficialServer.DBInteraction
 (
     startDBConnection
@@ -20,7 +20,7 @@
 
 localAddressList = ["127.0.0.1", "0:0:0:0:0:0:0:1", "0:0:0:0:0:ffff:7f00:1"]
 
-fakeDbConnection serverInfo = do
+fakeDbConnection serverInfo = forever $ do
     q <- readChan $ dbQueries serverInfo
     case q of
         CheckAccount clUid _ clHost -> do
@@ -29,8 +29,6 @@
         ClearCache -> return ()
         SendStats {} -> return ()
 
-    fakeDbConnection serverInfo
-
 
 #if defined(OFFICIAL_SERVER)
 pipeDbConnectionLoop queries coreChan hIn hOut accountsCache =
--- a/gameServer/OfficialServer/extdbinterface.hs	Sun Nov 14 20:37:55 2010 +0100
+++ b/gameServer/OfficialServer/extdbinterface.hs	Sun Nov 14 15:06:02 2010 -0500
@@ -1,4 +1,4 @@
-{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE ScopedTypeVariables, OverloadedStrings #-}
 
 module Main where
 
@@ -26,7 +26,7 @@
     case q of
         CheckAccount clUid clNick _ -> do
                 statement <- prepare dbConn dbQueryAccount
-                execute statement [SqlString $ clNick]
+                execute statement [SqlByteString $ clNick]
                 passAndRole <- fetchRow statement
                 finish statement
                 let response = 
@@ -47,7 +47,7 @@
 
 
 dbConnectionLoop mySQLConnectionInfo =
-    Control.Exception.handle (\(_ :: IOException) -> return ()) $ handleSqlError $
+    Control.Exception.handle (\(e :: IOException) -> hPutStrLn stderr $ show e) $ handleSqlError $
         bracket
             (connectMySQL mySQLConnectionInfo)
             (disconnect)
--- a/gameServer/Opts.hs	Sun Nov 14 20:37:55 2010 +0100
+++ b/gameServer/Opts.hs	Sun Nov 14 15:06:02 2010 -0500
@@ -3,10 +3,12 @@
     getOpts,
 ) where
 
-import System.Environment ( getArgs )
+import System.Environment
 import System.Console.GetOpt
 import Network
 import Data.Maybe ( fromMaybe )
+import qualified Data.ByteString.Char8 as B
+
 import CoreTypes
 import Utils
 
@@ -30,9 +32,9 @@
     where
         readDedicated = fromMaybe True (maybeRead str :: Maybe Bool)
 
-readDbLogin str opts = opts{dbLogin = str}
-readDbPassword str opts = opts{dbPassword = str}
-readDbHost str opts = opts{dbHost = str}
+readDbLogin str opts = opts{dbLogin = B.pack str}
+readDbPassword str opts = opts{dbPassword = B.pack str}
+readDbHost str opts = opts{dbHost = B.pack str}
 
 getOpts :: ServerInfo -> IO ServerInfo
 getOpts opts = do
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gameServer/RoomsAndClients.hs	Sun Nov 14 15:06:02 2010 -0500
@@ -0,0 +1,196 @@
+module RoomsAndClients(
+    RoomIndex(),
+    ClientIndex(),
+    MRoomsAndClients(),
+    IRoomsAndClients(),
+    newRoomsAndClients,
+    addRoom,
+    addClient,
+    removeRoom,
+    removeClient,
+    modifyRoom,
+    modifyClient,
+    lobbyId,
+    moveClientToLobby,
+    moveClientToRoom,
+    clientRoomM,
+    clientExists,
+    client,
+    room,
+    client'sM,
+    room'sM,
+    allClientsM,
+    clientsM,
+    roomClientsM,
+    roomClientsIndicesM,
+    withRoomsAndClients,
+    allRooms,
+    allClients,
+    clientRoom,
+    showRooms,
+    roomClients
+    ) where
+
+
+import Store
+import Control.Monad
+
+
+data Room r = Room {
+    roomClients' :: [ClientIndex],
+    room' :: r
+    }
+
+
+data Client c = Client {
+    clientRoom' :: RoomIndex,
+    client' :: c
+    }
+
+
+newtype RoomIndex = RoomIndex ElemIndex
+    deriving (Eq)
+newtype ClientIndex = ClientIndex ElemIndex
+    deriving (Eq, Show, Read, Ord)
+
+instance Show RoomIndex where
+    show (RoomIndex i) = 'r' : show i
+
+unRoomIndex :: RoomIndex -> ElemIndex
+unRoomIndex (RoomIndex r) = r
+
+unClientIndex :: ClientIndex -> ElemIndex
+unClientIndex (ClientIndex c) = c
+
+
+newtype MRoomsAndClients r c = MRoomsAndClients (MStore (Room r), MStore (Client c))
+newtype IRoomsAndClients r c = IRoomsAndClients (IStore (Room r), IStore (Client c))
+
+
+lobbyId :: RoomIndex
+lobbyId = RoomIndex firstIndex
+
+
+newRoomsAndClients :: r -> IO (MRoomsAndClients r c)
+newRoomsAndClients r = do
+    rooms <- newStore
+    clients <- newStore
+    let rnc = MRoomsAndClients (rooms, clients)
+    ri <- addRoom rnc r
+    when (ri /= lobbyId) $ error "Empty struct inserts not at firstIndex index"
+    return rnc
+
+
+roomAddClient :: ClientIndex -> Room r -> Room r
+roomAddClient cl room = let cls = cl : roomClients' room; nr = room{roomClients' = cls} in cls `seq` nr `seq` nr
+
+roomRemoveClient :: ClientIndex -> Room r -> Room r
+roomRemoveClient cl room = let cls = filter (/= cl) $ roomClients' room; nr = room{roomClients' = cls} in cls `seq` nr `seq` nr
+
+
+addRoom :: MRoomsAndClients r c -> r -> IO RoomIndex
+addRoom (MRoomsAndClients (rooms, _)) room = do
+    i <- addElem rooms (Room  [] room)
+    return $ RoomIndex i
+
+
+addClient :: MRoomsAndClients r c -> c -> IO ClientIndex
+addClient (MRoomsAndClients (rooms, clients)) client = do
+    i <- addElem clients (Client lobbyId client)
+    modifyElem rooms (roomAddClient (ClientIndex i)) (unRoomIndex lobbyId)
+    return $ ClientIndex i
+
+removeRoom :: MRoomsAndClients r c -> RoomIndex -> IO ()
+removeRoom rnc@(MRoomsAndClients (rooms, _)) room@(RoomIndex ri) 
+    | room == lobbyId = error "Cannot delete lobby"
+    | otherwise = do
+        clIds <- liftM roomClients' $ readElem rooms ri
+        forM_ clIds (moveClientToLobby rnc)
+        removeElem rooms ri
+
+
+removeClient :: MRoomsAndClients r c -> ClientIndex -> IO ()
+removeClient (MRoomsAndClients (rooms, clients)) cl@(ClientIndex ci) = do
+    RoomIndex ri <- liftM clientRoom' $ readElem clients ci
+    modifyElem rooms (roomRemoveClient cl) ri
+    removeElem clients ci
+
+
+modifyRoom :: MRoomsAndClients r c -> (r -> r) -> RoomIndex -> IO ()
+modifyRoom (MRoomsAndClients (rooms, _)) f (RoomIndex ri) = modifyElem rooms (\r -> r{room' = f $ room' r}) ri
+
+modifyClient :: MRoomsAndClients r c -> (c -> c) -> ClientIndex -> IO ()
+modifyClient (MRoomsAndClients (_, clients)) f (ClientIndex ci) = modifyElem clients (\c -> c{client' = f $ client' c}) ci
+
+moveClientInRooms :: MRoomsAndClients r c -> RoomIndex -> RoomIndex -> ClientIndex -> IO ()
+moveClientInRooms (MRoomsAndClients (rooms, clients)) (RoomIndex riFrom) rt@(RoomIndex riTo) cl@(ClientIndex ci) = do
+    modifyElem rooms (roomRemoveClient cl) riFrom
+    modifyElem rooms (roomAddClient cl) riTo
+    modifyElem clients (\c -> c{clientRoom' = rt}) ci
+
+
+moveClientToLobby :: MRoomsAndClients r c -> ClientIndex -> IO ()
+moveClientToLobby rnc ci = do
+    room <- clientRoomM rnc ci
+    moveClientInRooms rnc room lobbyId ci
+
+
+moveClientToRoom :: MRoomsAndClients r c -> RoomIndex -> ClientIndex -> IO ()
+moveClientToRoom rnc ri ci = moveClientInRooms rnc lobbyId ri ci
+
+
+clientExists :: MRoomsAndClients r c -> ClientIndex -> IO Bool
+clientExists (MRoomsAndClients (_, clients)) (ClientIndex ci) = elemExists clients ci
+
+clientRoomM :: MRoomsAndClients r c -> ClientIndex -> IO RoomIndex
+clientRoomM (MRoomsAndClients (_, clients)) (ClientIndex ci) = liftM clientRoom' (clients `readElem` ci)
+
+client'sM :: MRoomsAndClients r c -> (c -> a) -> ClientIndex -> IO a
+client'sM (MRoomsAndClients (_, clients)) f (ClientIndex ci) = liftM (f . client') (clients `readElem` ci)
+
+room'sM :: MRoomsAndClients r c -> (r -> a) -> RoomIndex -> IO a
+room'sM (MRoomsAndClients (rooms, _)) f (RoomIndex ri) = liftM (f . room') (rooms `readElem` ri)
+
+allClientsM :: MRoomsAndClients r c -> IO [ClientIndex]
+allClientsM (MRoomsAndClients (_, clients)) = liftM (map ClientIndex) $ indicesM clients
+
+clientsM :: MRoomsAndClients r c -> IO [c]
+clientsM (MRoomsAndClients (_, clients)) = indicesM clients >>= mapM (\ci -> liftM client' $ readElem clients ci)
+
+roomClientsIndicesM :: MRoomsAndClients r c -> RoomIndex -> IO [ClientIndex]
+roomClientsIndicesM (MRoomsAndClients (rooms, clients)) (RoomIndex ri) = liftM roomClients' (rooms `readElem` ri)
+
+roomClientsM :: MRoomsAndClients r c -> RoomIndex -> IO [c]
+roomClientsM (MRoomsAndClients (rooms, clients)) (RoomIndex ri) = liftM roomClients' (rooms `readElem` ri) >>= mapM (\(ClientIndex ci) -> liftM client' $ readElem clients ci)
+
+withRoomsAndClients :: MRoomsAndClients r c -> (IRoomsAndClients r c -> a) -> IO a
+withRoomsAndClients (MRoomsAndClients (rooms, clients)) f =
+    withIStore2 rooms clients (\r c -> f $ IRoomsAndClients (r, c))
+
+----------------------------------------
+----------- IRoomsAndClients -----------
+
+showRooms :: (Show r, Show c) => IRoomsAndClients r c -> String
+showRooms rnc@(IRoomsAndClients (rooms, clients)) = concatMap showRoom (allRooms rnc)
+    where
+    showRoom r = unlines $ ((show r) ++ ": " ++ (show $ room' $ rooms ! (unRoomIndex r))) : (map showClient (roomClients' $ rooms ! (unRoomIndex r)))
+    showClient c = "    " ++ (show c) ++ ": " ++ (show $ client' $ clients ! (unClientIndex c))
+
+
+allRooms :: IRoomsAndClients r c -> [RoomIndex]
+allRooms (IRoomsAndClients (rooms, _)) = map RoomIndex $ indices rooms
+
+allClients :: IRoomsAndClients r c -> [ClientIndex]
+allClients (IRoomsAndClients (_, clients)) = map ClientIndex $ indices clients
+
+clientRoom :: IRoomsAndClients r c -> ClientIndex -> RoomIndex
+clientRoom (IRoomsAndClients (_, clients)) (ClientIndex ci) = clientRoom' (clients ! ci)
+
+client :: IRoomsAndClients r c -> ClientIndex -> c
+client (IRoomsAndClients (_, clients)) (ClientIndex ci) = client' (clients ! ci)
+
+room :: IRoomsAndClients r c -> RoomIndex -> r
+room (IRoomsAndClients (rooms, _)) (RoomIndex ri) = room' (rooms ! ri)
+
+roomClients :: IRoomsAndClients r c -> RoomIndex -> [ClientIndex]
+roomClients (IRoomsAndClients (rooms, _)) (RoomIndex ri) = roomClients' $ (rooms ! ri)
--- a/gameServer/ServerCore.hs	Sun Nov 14 20:37:55 2010 +0100
+++ b/gameServer/ServerCore.hs	Sun Nov 14 15:06:02 2010 -0500
@@ -2,69 +2,75 @@
 
 import Network
 import Control.Concurrent
-import Control.Concurrent.STM
 import Control.Concurrent.Chan
 import Control.Monad
 import qualified Data.IntMap as IntMap
 import System.Log.Logger
+import Control.Monad.Reader
+import Control.Monad.State.Strict
+import Data.Set as Set
+import qualified Data.ByteString.Char8 as B
 --------------------------------------
 import CoreTypes
 import NetRoutines
-import Utils
 import HWProtoCore
 import Actions
 import OfficialServer.DBInteraction
+import ServerState
 
 
-timerLoop :: Int -> Chan CoreMessage -> IO()
+timerLoop :: Int -> Chan CoreMessage -> IO ()
 timerLoop tick messagesChan = threadDelay (30 * 10^6) >> writeChan messagesChan (TimerAction tick) >> timerLoop (tick + 1) messagesChan
 
-firstAway (_, a, b, c) = (a, b, c)
 
-reactCmd :: ServerInfo -> Int -> [String] -> Clients -> Rooms -> IO (ServerInfo, Clients, Rooms)
-reactCmd serverInfo clID cmd clients rooms =
-    liftM firstAway $ foldM processAction (clID, serverInfo, clients, rooms) $ handleCmd clID clients rooms cmd
+reactCmd :: [B.ByteString] -> StateT ServerState IO ()
+reactCmd cmd = do
+    (Just ci) <- gets clientIndex
+    rnc <- gets roomsClients
+    actions <- liftIO $ withRoomsAndClients rnc (\irnc -> runReader (handleCmd cmd) (ci, irnc))
+    forM_ actions processAction
 
-mainLoop :: ServerInfo -> Clients -> Rooms -> IO ()
-mainLoop serverInfo clients rooms = do
-    r <- readChan $ coreChan serverInfo
-    
-    (newServerInfo, mClients, mRooms) <-
-        case r of
-            Accept ci ->
-                liftM firstAway $ processAction
-                    (clientUID ci, serverInfo, clients, rooms) (AddClient ci)
+mainLoop :: StateT ServerState IO ()
+mainLoop = forever $ do
+    get >>= \s -> put $! s
+
+    si <- gets serverInfo
+    r <- liftIO $ readChan $ coreChan si
+
+    case r of
+        Accept ci -> processAction (AddClient ci)
+
+        ClientMessage (ci, cmd) -> do
+            liftIO $ debugM "Clients" $ (show ci) ++ ": " ++ (show cmd)
 
-            ClientMessage (clID, cmd) -> do
-                debugM "Clients" $ (show clID) ++ ": " ++ (show cmd)
-                if clID `IntMap.member` clients then
-                    reactCmd serverInfo clID cmd clients rooms
-                    else
-                    do
-                    debugM "Clients" "Message from dead client"
-                    return (serverInfo, clients, rooms)
+            removed <- gets removedClients
+            when (not $ ci `Set.member` removed) $ do
+                as <- get
+                put $! as{clientIndex = Just ci}
+                reactCmd cmd
+
+        Remove ci -> do
+            liftIO $ debugM "Clients"  $ "DeleteClient: " ++ show ci
+            processAction (DeleteClient ci)
 
-            ClientAccountInfo (clID, info) ->
-                if clID `IntMap.member` clients then
-                    liftM firstAway $ processAction
-                        (clID, serverInfo, clients, rooms)
-                        (ProcessAccountInfo info)
-                    else
-                    do
-                    debugM "Clients" "Got info for dead client"
-                    return (serverInfo, clients, rooms)
+                --else
+                --do
+                --debugM "Clients" "Message from dead client"
+                --return (serverInfo, rnc)
 
-            TimerAction tick ->
-                liftM firstAway $
-                    foldM processAction (0, serverInfo, clients, rooms) $
-                        PingAll : [StatsAction | even tick]
-
+        ClientAccountInfo (ci, info) -> do
+            rnc <- gets roomsClients
+            exists <- liftIO $ clientExists rnc ci
+            when (exists) $ do
+                as <- get
+                put $! as{clientIndex = Just ci}
+                processAction (ProcessAccountInfo info)
+                return ()
 
-    {-          let hadRooms = (not $ null rooms) && (null mrooms)
-                    in unless ((not $ isDedicated serverInfo) && ((null clientsIn) || hadRooms)) $
-                        mainLoop serverInfo acceptChan messagesChan clientsIn mrooms -}
+        TimerAction tick ->
+                mapM_ processAction $
+                    PingAll : [StatsAction | even tick]
 
-    mainLoop newServerInfo mClients mRooms
 
 startServer :: ServerInfo -> Socket -> IO ()
 startServer serverInfo serverSocket = do
@@ -74,14 +80,15 @@
         acceptLoop
             serverSocket
             (coreChan serverInfo)
-            0
 
     return ()
-    
-    forkIO $ timerLoop 0 $ coreChan serverInfo
+
+    --forkIO $ timerLoop 0 $ coreChan serverInfo
 
     startDBConnection serverInfo
 
-    forkIO $ mainLoop serverInfo IntMap.empty (IntMap.singleton 0 newRoom)
+    rnc <- newRoomsAndClients newRoom
 
-    forever $ threadDelay (60 * 60 * 10^6) >> putStrLn "***"
\ No newline at end of file
+    forkIO $ evalStateT mainLoop (ServerState Nothing serverInfo Set.empty rnc)
+
+    forever $ threadDelay (60 * 60 * 10^6)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gameServer/ServerState.hs	Sun Nov 14 15:06:02 2010 -0500
@@ -0,0 +1,43 @@
+module ServerState
+    (
+    module RoomsAndClients,
+    clientRoomA,
+    ServerState(..),
+    client's,
+    allClientsS,
+    roomClientsS
+    ) where
+
+import Control.Monad.State.Strict
+import Data.Set as Set
+----------------------
+import RoomsAndClients
+import CoreTypes
+
+data ServerState = ServerState {
+        clientIndex :: !(Maybe ClientIndex),
+        serverInfo :: !ServerInfo,
+        removedClients :: !(Set.Set ClientIndex),
+        roomsClients :: !MRnC
+    }
+
+
+clientRoomA :: StateT ServerState IO RoomIndex
+clientRoomA = do
+    (Just ci) <- gets clientIndex
+    rnc <- gets roomsClients
+    liftIO $ clientRoomM rnc ci
+
+client's :: (ClientInfo -> a) -> StateT ServerState IO a
+client's f = do
+    (Just ci) <- gets clientIndex
+    rnc <- gets roomsClients
+    liftIO $ client'sM rnc f ci
+
+allClientsS :: StateT ServerState IO [ClientInfo]
+allClientsS = gets roomsClients >>= liftIO . clientsM
+
+roomClientsS :: RoomIndex -> StateT ServerState IO [ClientInfo]
+roomClientsS ri = do
+    rnc <- gets roomsClients
+    liftIO $ roomClientsM rnc ri
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gameServer/Store.hs	Sun Nov 14 15:06:02 2010 -0500
@@ -0,0 +1,145 @@
+module Store(
+    ElemIndex(),
+    MStore(),
+    IStore(),
+    newStore,
+    addElem,
+    removeElem,
+    readElem,
+    writeElem,
+    modifyElem,
+    elemExists,
+    firstIndex,
+    indicesM,
+    withIStore,
+    withIStore2,
+    (!),
+    indices
+    ) where
+
+import qualified Data.Array.IArray as IA
+import qualified Data.Array.IO as IOA
+import qualified Data.IntSet as IntSet
+import Data.IORef
+import Control.Monad
+
+
+newtype ElemIndex = ElemIndex Int
+    deriving (Eq, Show, Read, Ord)
+newtype MStore e = MStore (IORef (IntSet.IntSet, IntSet.IntSet, IOA.IOArray Int e))
+newtype IStore e = IStore (IntSet.IntSet, IA.Array Int e)
+
+
+firstIndex :: ElemIndex
+firstIndex = ElemIndex 0
+
+-- MStore code
+initialSize :: Int
+initialSize = 10
+
+
+growFunc :: Int -> Int
+growFunc a = a * 3 `div` 2
+
+
+newStore :: IO (MStore e)
+newStore = do
+    newar <- IOA.newArray_ (0, initialSize - 1)
+    new <- newIORef (IntSet.empty, IntSet.fromAscList [0..initialSize - 1], newar)
+    return (MStore new)
+
+
+growStore :: MStore e -> IO ()
+growStore (MStore ref) = do
+    (busyElems, freeElems, arr) <- readIORef ref
+    (_, m') <- IOA.getBounds arr
+    let newM' = growFunc (m' + 1) - 1
+    newArr <- IOA.newArray_ (0, newM')
+    sequence_ [IOA.readArray arr i >>= IOA.writeArray newArr i | i <- [0..m']]
+    writeIORef ref (busyElems, freeElems `IntSet.union` (IntSet.fromAscList [m'+1..newM']), newArr)
+
+
+growIfNeeded :: MStore e -> IO ()
+growIfNeeded m@(MStore ref) = do
+    (_, freeElems, _) <- readIORef ref
+    when (IntSet.null freeElems) $ growStore m
+
+
+addElem :: MStore e -> e -> IO ElemIndex
+addElem m@(MStore ref) element = do
+    growIfNeeded m
+    (busyElems, freeElems, arr) <- readIORef ref
+    let (n, freeElems') = IntSet.deleteFindMin freeElems
+    IOA.writeArray arr n element
+    writeIORef ref (IntSet.insert n busyElems, freeElems', arr)
+    return $ ElemIndex n
+
+
+removeElem :: MStore e -> ElemIndex -> IO ()
+removeElem (MStore ref) (ElemIndex n) = do
+    (busyElems, freeElems, arr) <- readIORef ref
+    IOA.writeArray arr n (error $ "Store: no element " ++ show n)
+    writeIORef ref (IntSet.delete n busyElems, IntSet.insert n freeElems, arr)
+
+
+readElem :: MStore e -> ElemIndex -> IO e
+readElem (MStore ref) (ElemIndex n) = readIORef ref >>= \(_, _, arr) -> IOA.readArray arr n
+
+
+writeElem :: MStore e -> ElemIndex -> e -> IO ()
+writeElem (MStore ref) (ElemIndex n) el = readIORef ref >>= \(_, _, arr) -> IOA.writeArray arr n el
+
+
+modifyElem :: MStore e -> (e -> e) -> ElemIndex -> IO ()
+modifyElem (MStore ref) f (ElemIndex n) = do
+    (_, _, arr) <- readIORef ref
+    IOA.readArray arr n >>= IOA.writeArray arr n . f
+
+elemExists :: MStore e -> ElemIndex -> IO Bool
+elemExists (MStore ref) (ElemIndex n) = do
+    (_, free, _) <- readIORef ref
+    return $ n `IntSet.notMember` free
+
+indicesM :: MStore e -> IO [ElemIndex]
+indicesM (MStore ref) = do
+    (busy, _, _) <- readIORef ref
+    return $ map ElemIndex $ IntSet.toList busy
+
+
+-- A way to see MStore elements in pure code via IStore
+m2i :: MStore e -> IO (IStore e)
+m2i (MStore ref) = do
+    (a, _, c') <- readIORef ref
+    c <- IOA.unsafeFreeze c'
+    return $ IStore (a, c)
+
+i2m :: (MStore e) -> IStore e -> IO ()
+i2m (MStore ref) (IStore (_, arr)) = do
+    (b, e, _) <- readIORef ref
+    a <- IOA.unsafeThaw arr
+    writeIORef ref (b, e, a)
+
+withIStore :: MStore e -> (IStore e -> a) -> IO a
+withIStore m f = do
+    i <- m2i m
+    let res = f i
+    res `seq` i2m m i
+    return res
+
+
+withIStore2 :: MStore e1 -> MStore e2 -> (IStore e1 -> IStore e2 -> a) -> IO a
+withIStore2 m1 m2 f = do
+    i1 <- m2i m1
+    i2 <- m2i m2
+    let res = f i1 i2
+    res `seq` i2m m1 i1
+    i2m m2 i2
+    return res
+
+
+-- IStore code
+(!) :: IStore e -> ElemIndex -> e
+(!) (IStore (_, arr)) (ElemIndex i) = (IA.!) arr i
+
+indices :: IStore e -> [ElemIndex]
+indices (IStore (busy, _)) = map ElemIndex $ IntSet.toList busy
--- a/gameServer/Utils.hs	Sun Nov 14 20:37:55 2010 +0100
+++ b/gameServer/Utils.hs	Sun Nov 14 15:06:02 2010 -0500
@@ -1,3 +1,4 @@
+{-# LANGUAGE OverloadedStrings #-}
 module Utils where
 
 import Control.Concurrent
@@ -16,33 +17,30 @@
 import Data.Maybe
 -------------------------------------------------
 import qualified Codec.Binary.Base64 as Base64
-import qualified Data.ByteString.UTF8 as BUTF8
-import qualified Data.ByteString as B
+import qualified Data.ByteString.Char8 as B
+import qualified Data.ByteString as BW
 import CoreTypes
 
 
-sockAddr2String :: SockAddr -> IO String
-sockAddr2String (SockAddrInet _ hostAddr) = inet_ntoa hostAddr
+sockAddr2String :: SockAddr -> IO B.ByteString
+sockAddr2String (SockAddrInet _ hostAddr) = liftM B.pack $ inet_ntoa hostAddr
 sockAddr2String (SockAddrInet6 _ _ (a, b, c, d) _) =
-    return $ (foldr1 (.)
+    return $ B.pack $ (foldr1 (.)
         $ List.intersperse (\a -> ':':a)
         $ concatMap (\n -> (\(a, b) -> [showHex a, showHex b]) $ divMod n 65536) [a, b, c, d]) []
 
-toEngineMsg :: String -> String
-toEngineMsg msg = Base64.encode (fromIntegral (B.length encodedMsg) : (B.unpack encodedMsg))
-    where
-    encodedMsg = BUTF8.fromString msg
+toEngineMsg :: B.ByteString -> B.ByteString
+toEngineMsg msg = B.pack $ Base64.encode (fromIntegral (BW.length msg) : (BW.unpack msg))
 
-fromEngineMsg :: String -> Maybe String
-fromEngineMsg msg = liftM (map w2c) (Base64.decode msg >>= removeLength)
+fromEngineMsg :: B.ByteString -> Maybe B.ByteString
+fromEngineMsg msg = Base64.decode (B.unpack msg) >>= removeLength >>= return . BW.pack
     where
         removeLength (x:xs) = if length xs == fromIntegral x then Just xs else Nothing
         removeLength _ = Nothing
 
-checkNetCmd :: String -> (Bool, Bool)
-checkNetCmd msg = check decoded
+checkNetCmd :: B.ByteString -> (Bool, Bool)
+checkNetCmd = check . liftM B.unpack . fromEngineMsg
     where
-        decoded = fromEngineMsg msg
         check Nothing = (False, False)
         check (Just (m:ms)) = (m `Set.member` legalMessages, m == '+')
         check _ = (False, False)
@@ -54,29 +52,17 @@
     [(x, rest)] | all isSpace rest -> Just x
     _         -> Nothing
 
-teamToNet :: Word16 -> TeamInfo -> [String]
-teamToNet protocol team 
-    | protocol < 30 = [
-        "ADD_TEAM",
-        teamname team,
-        teamgrave team,
-        teamfort team,
-        teamvoicepack team,
-        teamowner team,
-        show $ difficulty team
-        ]
-        ++ hhsInfo
-    | otherwise = [
-        "ADD_TEAM",
-        teamname team,
-        teamgrave team,
-        teamfort team,
-        teamvoicepack team,
-        teamflag team,
-        teamowner team,
-        show $ difficulty team
-        ]
-        ++ hhsInfo
+teamToNet :: TeamInfo -> [B.ByteString]
+teamToNet team =
+        "ADD_TEAM"
+        : teamname team
+        : teamgrave team
+        : teamfort team
+        : teamvoicepack team
+        : teamflag team
+        : teamowner team
+        : (B.pack $ show $ difficulty team)
+        : hhsInfo
     where
         hhsInfo = concatMap (\(HedgehogInfo name hat) -> [name, hat]) $ hedgehogs team
 
@@ -90,10 +76,10 @@
         else
             t : replaceTeam team teams
 
-illegalName :: String -> Bool
-illegalName = all isSpace
+illegalName :: B.ByteString -> Bool
+illegalName = all isSpace . B.unpack
 
-protoNumber2ver :: Word16 -> String
+protoNumber2ver :: Word16 -> B.ByteString
 protoNumber2ver 17 = "0.9.7-dev"
 protoNumber2ver 19 = "0.9.7"
 protoNumber2ver 20 = "0.9.8-dev"
@@ -119,3 +105,13 @@
     putStr msg
     hFlush stdout
     getLine
+
+
+unfoldrE :: (b -> Either b (a, b)) -> b -> ([a], b)
+unfoldrE f b  =
+    case f b of
+        Right (a, new_b) -> let (a', b') = unfoldrE f new_b in (a : a', b')
+        Left new_b       -> ([], new_b)
+
+showB :: Show a => a -> B.ByteString
+showB = B.pack .show
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gameServer/hedgewars-server.cabal	Sun Nov 14 15:06:02 2010 -0500
@@ -0,0 +1,32 @@
+Name:                hedgewars-server
+Version:             0.1
+Synopsis:            hedgewars server
+Description:         hedgewars server
+Homepage:            http://www.hedgewars.org/
+License:             GPL-2
+Author:              unC0Rr
+Maintainer:          unC0Rr@hedgewars.org
+Category:            Game
+Build-type:          Simple
+Cabal-version:       >=1.2
+
+
+Executable hedgewars-server
+  main-is: hedgewars-server.hs
+
+  Build-depends:
+    base >= 4,
+    unix,
+    containers,
+    array,
+    bytestring,
+    network-bytestring,
+    network,
+    time,
+    stm,
+    mtl,
+    dataenc,
+    hslogger,
+    process
+  
+  ghc-options: -O2
\ No newline at end of file
--- a/gameServer/hedgewars-server.hs	Sun Nov 14 20:37:55 2010 +0100
+++ b/gameServer/hedgewars-server.hs	Sun Nov 14 15:06:02 2010 -0500
@@ -2,23 +2,15 @@
 
 module Main where
 
-import Network.Socket
-import qualified Network
-import Network.BSD
+import Network
 import Control.Concurrent.STM
 import Control.Concurrent.Chan
-#if defined(NEW_EXCEPTIONS)
-import qualified Control.OldException as Exception
-#else
 import qualified Control.Exception as Exception
-#endif
 import System.Log.Logger
 -----------------------------------
 import Opts
 import CoreTypes
-import OfficialServer.DBInteraction
 import ServerCore
-import Utils
 
 
 #if !defined(mingw32_HOST_OS)
@@ -26,10 +18,12 @@
 #endif
 
 
+setupLoggers :: IO ()
 setupLoggers =
     updateGlobalLogger "Clients"
         (setLevel INFO)
 
+main :: IO ()
 main = withSocketsDo $ do
 #if !defined(mingw32_HOST_OS)
     installHandler sigPIPE Ignore Nothing;
@@ -38,11 +32,11 @@
 
     setupLoggers
 
-    stats <- atomically $ newTMVar (StatisticsInfo 0 0)
+    stats' <- atomically $ newTMVar (StatisticsInfo 0 0)
     dbQueriesChan <- newChan
-    coreChan <- newChan
-    serverInfo' <- getOpts $ newServerInfo stats coreChan dbQueriesChan
-    
+    coreChan' <- newChan
+    serverInfo' <- getOpts $ newServerInfo stats' coreChan' dbQueriesChan
+
 #if defined(OFFICIAL_SERVER)
     dbHost' <- askFromConsole "DB host: "
     dbLogin' <- askFromConsole "login: "
@@ -52,14 +46,7 @@
     let serverInfo = serverInfo'
 #endif
 
-
-    proto <- getProtocolNumber "tcp"
     Exception.bracket
-        (socket AF_INET Stream proto)
+        (Network.listenOn $ Network.PortNumber $ listenPort serverInfo)
         sClose
-        (\sock -> do
-            setSocketOption sock ReuseAddr 1
-            bindSocket sock (SockAddrInet (listenPort serverInfo) iNADDR_ANY)
-            listen sock maxListenQueue
-            startServer serverInfo sock
-        )
+        (startServer serverInfo)
--- a/gameServer/stresstest.hs	Sun Nov 14 20:37:55 2010 +0100
+++ b/gameServer/stresstest.hs	Sun Nov 14 15:06:02 2010 -0500
@@ -6,7 +6,7 @@
 import System.IO
 import Control.Concurrent
 import Network
-import Control.Exception
+import Control.OldException
 import Control.Monad
 import System.Random
 
@@ -14,24 +14,24 @@
 import System.Posix
 #endif
 
-session1 nick room = ["NICK", nick, "", "PROTO", "24", "", "CHAT", "lobby 1", "", "CREATE", room, "", "CHAT", "room 1", "", "QUIT", "bye-bye", ""]
-session2 nick room = ["NICK", nick, "", "PROTO", "24", "", "LIST", "", "JOIN", room, "", "CHAT", "room 2", "", "PART", "", "CHAT", "lobby after part", "", "QUIT", "bye-bye", ""]
-session3 nick room = ["NICK", nick, "", "PROTO", "24", "", "LIST", "", "JOIN", room, "", "CHAT", "room 2", "", "QUIT", "bye-bye", ""]
+session1 nick room = ["NICK", nick, "", "PROTO", "32", "", "PING", "", "CHAT", "lobby 1", "", "CREATE_ROOM", room, "", "CHAT", "room 1", "", "QUIT", "creator", ""]
+session2 nick room = ["NICK", nick, "", "PROTO", "32", "", "LIST", "", "JOIN_ROOM", room, "", "CHAT", "room 2", "", "PART", "", "CHAT", "lobby after part", "", "QUIT", "part-quit", ""]
+session3 nick room = ["NICK", nick, "", "PROTO", "32", "", "LIST", "", "JOIN_ROON", room, "", "CHAT", "room 2", "", "QUIT", "quit", ""]
 
 emulateSession sock s = do
-    mapM_ (\x -> hPutStrLn sock x >> hFlush sock >> randomRIO (50000::Int, 90000) >>= threadDelay) s
+    mapM_ (\x -> hPutStrLn sock x >> hFlush sock >> randomRIO (30000::Int, 59000) >>= threadDelay) s
     hFlush sock
     threadDelay 225000
 
-testing = Control.Exception.handle print $ do
+testing = Control.OldException.handle print $ do
     putStrLn "Start"
     sock <- connectTo "127.0.0.1" (PortNumber 46631)
 
     num1 <- randomRIO (70000::Int, 70100)
     num2 <- randomRIO (0::Int, 2)
     num3 <- randomRIO (0::Int, 5)
-    let nick1 = show num1
-    let room1 = show num2
+    let nick1 = 'n' : show num1
+    let room1 = 'r' : show num2
     case num2 of 
         0 -> emulateSession sock $ session1 nick1 room1
         1 -> emulateSession sock $ session2 nick1 room1
@@ -40,7 +40,7 @@
     putStrLn "Finish"
 
 forks = forever $ do
-    delay <- randomRIO (10000::Int, 19000)
+    delay <- randomRIO (30000::Int, 59000)
     threadDelay delay
     forkIO testing
 
--- a/gameServer/stresstest2.hs	Sun Nov 14 20:37:55 2010 +0100
+++ b/gameServer/stresstest2.hs	Sun Nov 14 15:06:02 2010 -0500
@@ -6,7 +6,7 @@
 import System.IO
 import Control.Concurrent
 import Network
-import Control.Exception
+import Control.OldException
 import Control.Monad
 import System.Random
 
@@ -14,22 +14,28 @@
 import System.Posix
 #endif
 
-testing = Control.Exception.handle print $ do
-    delay <- randomRIO (100::Int, 300)
-    threadDelay delay
+session1 nick room = ["NICK", nick, "", "PROTO", "32", ""]
+
+
+
+testing = Control.OldException.handle print $ do
+    putStrLn "Start"
     sock <- connectTo "127.0.0.1" (PortNumber 46631)
-    hClose sock
 
-forks i = do
-    delay <- randomRIO (50::Int, 190)
-    if i `mod` 10 == 0 then putStr (show i) else putStr "."
-    hFlush stdout
-    threadDelay delay
-    forkIO testing
-    forks (i + 1)
+    num1 <- randomRIO (70000::Int, 70100)
+    num2 <- randomRIO (0::Int, 2)
+    num3 <- randomRIO (0::Int, 5)
+    let nick1 = 'n' : show num1
+    let room1 = 'r' : show num2
+    mapM_ (\x -> hPutStrLn sock x >> hFlush sock >> randomRIO (300::Int, 590) >>= threadDelay) $ session1 nick1 room1
+    mapM_ (\x -> hPutStrLn sock x >> hFlush sock) $ concatMap (\x -> ["CHAT_MSG", show x, ""]) [1..]
+    hClose sock
+    putStrLn "Finish"
+
+forks = testing
 
 main = withSocketsDo $ do
 #if !defined(mingw32_HOST_OS)
     installHandler sigPIPE Ignore Nothing;
 #endif
-    forks 1
+    forks
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gameServer/stresstest3.hs	Sun Nov 14 15:06:02 2010 -0500
@@ -0,0 +1,75 @@
+{-# LANGUAGE CPP #-}
+
+module Main where
+
+import IO
+import System.IO
+import Control.Concurrent
+import Network
+import Control.OldException
+import Control.Monad
+import System.Random
+import Control.Monad.State
+import Data.List
+
+#if !defined(mingw32_HOST_OS)
+import System.Posix
+#endif
+
+type SState = Handle
+io = liftIO
+
+readPacket :: StateT SState IO [String]
+readPacket = do
+    h <- get
+    p <- io $ hGetPacket h []
+    return p
+    where
+    hGetPacket h buf = do
+        l <- hGetLine h
+        if (not $ null l) then hGetPacket h (buf ++ [l]) else return buf
+
+waitPacket :: String -> StateT SState IO Bool
+waitPacket s = do
+    p <- readPacket
+    return $ head p == s
+
+sendPacket :: [String] -> StateT SState IO ()
+sendPacket s = do
+    h <- get
+    io $ do
+        mapM_ (hPutStrLn h) s
+        hPutStrLn h ""
+        hFlush h
+
+emulateSession :: StateT SState IO ()
+emulateSession = do
+    n <- io $ randomRIO (100000::Int, 100100)
+    waitPacket "CONNECTED"
+    sendPacket ["NICK", "test" ++ (show n)]
+    waitPacket "NICK"
+    sendPacket ["PROTO", "31"]
+    waitPacket "PROTO"
+    b <- waitPacket "LOBBY:JOINED"
+    --io $ print b
+    sendPacket ["QUIT", "BYE"]
+    return ()
+
+testing = Control.OldException.handle print $ do
+    putStr "+"
+    sock <- connectTo "127.0.0.1" (PortNumber 46631)
+    evalStateT emulateSession sock
+    --hClose sock
+    putStr "-"
+    hFlush stdout
+
+forks = forM_ [1..100] $ const $ do
+    delay <- randomRIO (10000::Int, 30000)
+    threadDelay delay
+    forkIO testing
+
+main = withSocketsDo $ do
+#if !defined(mingw32_HOST_OS)
+    installHandler sigPIPE Ignore Nothing;
+#endif
+    forks
--- a/hedgewars/GSHandlers.inc	Sun Nov 14 20:37:55 2010 +0100
+++ b/hedgewars/GSHandlers.inc	Sun Nov 14 15:06:02 2010 -0500
@@ -317,7 +317,11 @@
 
     if Gear^.AdvBounce > 1 then dec(Gear^.AdvBounce);
 
-    if isFalling then Gear^.dY := Gear^.dY + cGravity;
+    if isFalling then 
+        begin
+        Gear^.dY := Gear^.dY + cGravity;
+        if (GameFlags and gfMoreWind) <> 0 then Gear^.dX := Gear^.dX + cWindSpeed * _16 / max(12,sqr(Gear^.Radius))
+        end;
 
     Gear^.X := Gear^.X + Gear^.dX;
     Gear^.Y := Gear^.Y + Gear^.dY;
@@ -512,7 +516,7 @@
 procedure doStepShell(Gear: PGear);
 begin
     AllInactive := false;
-    Gear^.dX := Gear^.dX + cWindSpeed;
+    if (GameFlags and gfMoreWind) = 0 then Gear^.dX := Gear^.dX + cWindSpeed;
     doStepFallingGear(Gear);
     if (Gear^.State and gstCollision) <> 0 then
     begin
@@ -704,6 +708,7 @@
 var 
     i, x, y: LongWord;
     oX, oY: hwFloat;
+    VGear: PVisualGear;
 begin
     AllInactive := false;
     inc(Gear^.Timer);
@@ -750,6 +755,41 @@
             cLaserSighting := false;
         if (Ammoz[Gear^.AmmoType].Ammo.NumPerTurn <= CurrentHedgehog^.MultiShootAttacks) and
            ((GameFlags and gfArtillery) = 0) then cArtillery := false;
+        
+        // Bullet Hit
+        if (hwRound(Gear^.X) and LAND_WIDTH_MASK = 0) 
+            and (hwRound(Gear^.Y) and LAND_HEIGHT_MASK = 0) then
+        begin
+            VGear := AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtBulletHit);
+            if VGear <> nil then
+            begin
+                VGear^.Angle := DxDy2Angle(-Gear^.dX, Gear^.dY);
+            end;
+        end;
+        
+        // Bullet trail
+        VGear := AddVisualGear(
+            hwround(CurrentHedgehog^.Gear^.X) + GetLaunchX(CurrentHedgehog^.CurAmmoType, hwSign(CurrentHedgehog^.Gear^.dX), CurrentHedgehog^.Gear^.Angle), 
+            hwround(CurrentHedgehog^.Gear^.Y) + GetLaunchY(CurrentHedgehog^.CurAmmoType, CurrentHedgehog^.Gear^.Angle),
+            vgtLineTrail
+        );
+        if VGear <> nil then
+        begin
+            // http://mantis.freepascal.org/view.php?id=17714 hits again
+            VGear^.dX := Gear^.X.QWordValue / SignAs(_1,_1).QWordValue;
+            VGear^.dY := Gear^.Y.QWordValue / SignAs(_1,_1).QWordValue;
+            
+            // reached edge of land. assume infinite beam. Extend it way out past camera
+            if (hwRound(Gear^.X) and LAND_WIDTH_MASK <> 0) 
+                or (hwRound(Gear^.Y) and LAND_HEIGHT_MASK <> 0) then
+            begin
+                VGear^.dX := VGear^.dX + (CurrentHedgehog^.Gear^.dX * LAND_WIDTH).QWordValue / SignAs(_1,_1).QWordValue;
+                VGear^.dY := VGear^.dY + (CurrentHedgehog^.Gear^.dY * LAND_WIDTH).QWordValue / SignAs(_1,_1).QWordValue;
+            end;
+            
+            VGear^.Timer := 200;
+        end;
+        
         Gear^.doStep := @doStepShotIdle
     end;
 end;
@@ -1081,6 +1121,7 @@
     HHGear^.X := HHGear^.X + HHGear^.dX;
     HHGear^.Y := HHGear^.Y + HHGear^.dY;
     HHGear^.dY := HHGear^.dY + cGravity;
+    if (GameFlags and gfMoreWind) <> 0 then HHGear^.dX := HHGear^.dX + cWindSpeed * _0_2;
 
     if (Gear^.Message and gmAttack) <> 0 then
     begin
@@ -1143,7 +1184,11 @@
     else
         if (Gear^.Message and gmRight <> 0) then HHGear^.dX := HHGear^.dX + _0_0002;
 
-    if not TestCollisionYwithGear(HHGear, 1) then HHGear^.dY := HHGear^.dY + cGravity;
+    if not TestCollisionYwithGear(HHGear, 1) then
+        begin
+        HHGear^.dY := HHGear^.dY + cGravity;
+        if (GameFlags and gfMoreWind) <> 0 then HHGear^.dX := HHGear^.dX + cWindSpeed * _0_2
+        end;
 
     ropeDx := HHGear^.X - Gear^.X;
     // vector between hedgehog and rope attaching point
@@ -1343,7 +1388,8 @@
             begin
             HHGear^.Y := HHGear^.Y + HHGear^.dY;
             Gear^.Y := Gear^.Y + HHGear^.dY;
-            HHGear^.dY := HHGear^.dY + cGravity
+            HHGear^.dY := HHGear^.dY + cGravity;
+            if (GameFlags and gfMoreWind) <> 0 then HHGear^.dX := HHGear^.dX + cWindSpeed * _0_2
             end;
 
         tt := Gear^.Elasticity;
@@ -2041,8 +2087,6 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 procedure doStepAirAttackWork(Gear: PGear);
-var 
-    i: Longint;
 begin
     AllInactive := false;
     Gear^.X := Gear^.X + cAirPlaneSpeed * Gear^.Tag;
@@ -2055,9 +2099,12 @@
                              Gear^.Tag, _0, 0);
             1: FollowGear := AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtMine,    0, cBombsSpeed *
                              Gear^.Tag, _0, 0);
-            2: for i:= -19 to 19 do
-                   FollowGear := AddGear(hwRound(Gear^.X) + i div 3, hwRound(Gear^.Y), gtFlame, 0,
-                                 _0_001 * i, _0, 0);
+            2: FollowGear := AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtNapalmBomb, 0, cBombsSpeed *
+                             Gear^.Tag, _0, 0);
+            3: FollowGear := AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtDrill, gsttmpFlag, cBombsSpeed *
+                             Gear^.Tag, _0, 0);
+            //4: FollowGear := AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtWaterMelon, 0, cBombsSpeed *
+            //                 Gear^.Tag, _0, 5000);
         end;
         Gear^.dX := Gear^.dX + int2hwFloat(30 * Gear^.Tag)
     end;
@@ -2086,7 +2133,7 @@
     Gear^.Y := int2hwFloat(topY-300);
     Gear^.dX := int2hwFloat(TargetPoint.X - 5 * Gear^.Tag * 15);
 
-    if (int2hwFloat(TargetPoint.Y) - Gear^.Y > _0) and (Gear^.State <> 2) then
+    if (int2hwFloat(TargetPoint.Y) - Gear^.Y > _0) then
         Gear^.dX := Gear^.dX - cBombsSpeed * hwSqrt((int2hwFloat(TargetPoint.Y) - Gear^.Y) * 2 /
                     cGravity) * Gear^.Tag;
 
@@ -2699,6 +2746,8 @@
         Gear^.X := Gear^.X + Gear^.dX;
         Gear^.Y := Gear^.Y + Gear^.dY;
         DrawTunnel(oX, oY, Gear^.dX, Gear^.dY, 2, 6);
+        if (Gear^.Timer mod 30) = 0 then
+            AddVisualGear(hwRound(Gear^.X + _20 * Gear^.dX), hwRound(Gear^.Y + _20 * Gear^.dY), vgtDust);
         if (CheckGearDrowning(Gear)) then
         begin
             StopSound(Gear^.SoundChannel);
@@ -2717,7 +2766,10 @@
     begin
         //out of time or exited ground
         StopSound(Gear^.SoundChannel);
-        doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, EXPLAutoSound);
+        if (Gear^.State and gsttmpFlag) <> 0 then
+            doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 30, EXPLAutoSound)
+        else
+            doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, EXPLAutoSound);
         DeleteGear(Gear);
         exit
     end;
@@ -2733,7 +2785,9 @@
 begin
     AllInactive := false;
 
-    Gear^.dX := Gear^.dX + cWindSpeed;
+    if (Gear^.State and gsttmpFlag) = 0 then
+        Gear^.dX := Gear^.dX + cWindSpeed;
+
     oldDx := Gear^.dX;
     oldDy := Gear^.dY;
 
@@ -2759,7 +2813,10 @@
         else
         begin
             //explode right on contact with HH
-            doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, EXPLAutoSound);
+            if (Gear^.State and gsttmpFlag) <> 0 then
+                doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 30, EXPLAutoSound)
+            else
+                doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, EXPLAutoSound);
             DeleteGear(Gear);
             exit;
         end;
@@ -3163,19 +3220,19 @@
         Gear^.Tag := 1;
 
     if (HHGear^.Message and gmUp) <> 0 then
-    begin
+        begin
         if (not HHGear^.dY.isNegative) or (HHGear^.Y > -_256) then
             HHGear^.dY := HHGear^.dY - move;
         dec(Gear^.Health, fuel);
         Gear^.MsgParam := Gear^.MsgParam or gmUp;
-    end;
+        end;
     if (HHGear^.Message and gmLeft) <> 0 then move.isNegative := true;
     if (HHGear^.Message and (gmLeft or gmRight)) <> 0 then
-    begin
+        begin
         HHGear^.dX := HHGear^.dX + (move * _0_1);
         dec(Gear^.Health, fuel div 5);
         Gear^.MsgParam := Gear^.MsgParam or (HHGear^.Message and (gmLeft or gmRight));
-    end;
+        end;
 
     if Gear^.Health < 0 then Gear^.Health := 0;
     if ((GameTicks and $FF) = 0) and (Gear^.Health < 500) then
@@ -3183,16 +3240,15 @@
             AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtFeather);
 
     if (HHGear^.Message and gmAttack <> 0) then
-    begin
+        begin
         HHGear^.Message := HHGear^.Message and not gmAttack;
         if Gear^.FlightTime > 0 then
-        begin
-            AddGear(hwRound(Gear^.X), hwRound(Gear^.Y) + 32, gtEgg, 0, Gear^.dX * _0_5, Gear^.dY, 0)
-            ;
+            begin
+            AddGear(hwRound(Gear^.X), hwRound(Gear^.Y) + 32, gtEgg, 0, Gear^.dX * _0_5, Gear^.dY, 0);
             PlaySound(sndBirdyLay);
             dec(Gear^.FlightTime)
+            end;
         end;
-    end;
 
     if HHGear^.Message and (gmUp or gmPrecise or gmLeft or gmRight) <> 0 then 
         Gear^.State := Gear^.State and not gsttmpFlag;
@@ -3214,25 +3270,25 @@
        or (((GameTicks and $1FF) = 0) and (not HHGear^.dY.isNegative) and TestCollisionYwithGear(
        HHGear, 1))
        or ((Gear^.Message and gmAttack) <> 0) then
-    begin
+        begin
         with HHGear^ do
-        begin
+            begin
             Message := 0;
             Active := true;
             State := State or gstMoving
-        end;
+            end;
         Gear^.State := Gear^.State or gstAnimation or gstTmpFlag;
         if HHGear^.dY < _0 then
-        begin
+            begin
             Gear^.dX := HHGear^.dX;
             Gear^.dY := HHGear^.dY;
-        end;
+            end;
         Gear^.Timer := 0;
         Gear^.doStep := @doStepBirdyDisappear;
         CurAmmoGear := nil;
         isCursorVisible := false;
         AfterAttack;
-    end
+        end
 end;
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -4197,7 +4253,7 @@
     Gear^.doStep := @doStepHammerHitWork
 end;
 
-
+////////////////////////////////////////////////////////////////////////////////
 procedure doStepResurrectorWork(Gear: PGear);
 var
     graves: TPGearArray;
@@ -4312,3 +4368,43 @@
         end
 end;
 
+////////////////////////////////////////////////////////////////////////////////
+procedure doStepNapalmBomb(Gear: PGear);
+var
+    i, gX, gY: LongInt;
+    dX, dY: hwFloat;
+begin
+    AllInactive := false;
+    doStepFallingGear(Gear);
+    if (Gear^.Timer > 0) and ((Gear^.State and gstCollision) <> 0) then
+    begin
+        doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 10, EXPLAutoSound);
+        gX := hwRound(Gear^.X);
+        gY := hwRound(Gear^.Y); 
+        for i:= 0 to 10 do
+        begin
+            dX := AngleCos(i * 2) * ((_0_1*(i div 5))) * (GetRandom + _1);
+            dY := AngleSin(i * 8) * _0_5 * (GetRandom + _1);
+            AddGear(gX, gY, gtFlame, 0, dX, dY, 0);
+            AddGear(gX, gY, gtFlame, 0, dX, -dY, 0);
+            AddGear(gX, gY, gtFlame, 0, -dX, dY, 0);
+            AddGear(gX, gY, gtFlame, 0, -dX, -dY, 0);
+        end;
+        DeleteGear(Gear);
+        exit
+    end;
+    if (Gear^.Timer = 0) then
+    begin
+        doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 10, EXPLAutoSound);
+        for i:= -19 to 19 do
+           FollowGear := AddGear(hwRound(Gear^.X) + i div 3, hwRound(Gear^.Y), gtFlame, 0, _0_001 * i, _0, 0);
+        DeleteGear(Gear);
+        exit
+    end;
+    if (GameTicks and $3F) = 0 then
+        AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace);
+    dec(Gear^.Timer)
+end;
+
+////////////////////////////////////////////////////////////////////////////////
+
--- a/hedgewars/GearDrawing.inc	Sun Nov 14 20:37:55 2010 +0100
+++ b/hedgewars/GearDrawing.inc	Sun Nov 14 15:06:02 2010 -0500
@@ -4,7 +4,6 @@
     sign, hx, hy, cx, cy, tx, ty, sx, sy, m: LongInt;  // hedgehog, crosshair, temp, sprite, direction
     dx, dy, ax, ay, aAngle, dAngle, hAngle, lx, ly: real;  // laser, change
     defaultPos, HatVisible: boolean;
-    VertexBuffer: array [0..1] of TVertex2f;
     HH: PHedgehog;
     CurWeapon: PAmmo;
 begin
@@ -126,22 +125,7 @@
 
             //if (abs(lx-tx)>8) or (abs(ly-ty)>8) then
                 begin
-                glDisable(GL_TEXTURE_2D);
-                glEnable(GL_LINE_SMOOTH);
-
-                glLineWidth(1.0);
-
-                Tint($FF, $00, $00, $C0);
-                VertexBuffer[0].X:= hx + WorldDx;
-                VertexBuffer[0].Y:= hy + WorldDy;
-                VertexBuffer[1].X:= tx + WorldDx;
-                VertexBuffer[1].Y:= ty + WorldDy;
-
-                glVertexPointer(2, GL_FLOAT, 0, @VertexBuffer[0]);
-                glDrawArrays(GL_LINES, 0, Length(VertexBuffer));
-                Tint($FF, $FF, $FF, $FF);
-                glEnable(GL_TEXTURE_2D);
-                glDisable(GL_LINE_SMOOTH);
+                DrawLine(hx, hy, tx, ty, 1.0, $FF, $00, $00, $C0);
                 end;
             end;
         // draw crosshair
@@ -438,7 +422,8 @@
 
         case amt of
             amAirAttack,
-            amMineStrike: DrawRotated(sprHandAirAttack, sx, oy, sign, 0);
+            amMineStrike,
+            amDrillStrike: DrawRotated(sprHandAirAttack, sx, oy, sign, 0);
             amPickHammer: DrawHedgehog(sx, sy,
                         sign,
                         1,
@@ -711,7 +696,10 @@
                       DrawRotatedf(sprPortal, x, y, Gear^.Tag, hwSign(Gear^.dX), Gear^.DirAngle)
                  else DrawRotatedf(sprPortal, x, y, 4 + Gear^.Tag div 2, hwSign(Gear^.dX), Gear^.DirAngle);
 
-       gtDrill: DrawRotated(sprDrill, x, y, 0, DxDy2Angle(Gear^.dY, Gear^.dX));
+           gtDrill: if (Gear^.State and gsttmpFlag) <> 0 then
+                        DrawRotated(sprAirDrill, x, y, 0, DxDy2Angle(Gear^.dY, Gear^.dX))
+                    else
+                        DrawRotated(sprDrill, x, y, 0, DxDy2Angle(Gear^.dY, Gear^.dX));
 
         gtHedgehog: DrawHH(Gear, x, y);
 
@@ -728,9 +716,7 @@
                         Tint($FF, $FF, $FF, $FF)
                         end
                     end;
-
              gtBee: DrawRotatedF(sprBee, x, y, (GameTicks shr 5) mod 2, 0, DxDy2Angle(Gear^.dY, Gear^.dX));
-
       gtPickHammer: DrawSprite(sprPHammer, x - 16, y - 50 + LongInt(((GameTicks shr 5) and 1) * 2), 0);
             gtRope: DrawRope(Gear);
             gtMine: if (((Gear^.State and gstAttacking) = 0)or((Gear^.Timer and $3FF) < 420)) and (Gear^.Health <> 0) then
@@ -860,6 +846,7 @@
                     DrawTexture(x - 108, y - 108, SpritesData[sprVampiric].Texture, 4.5);
                     Tint($FF, $FF, $FF, $FF);
                     end;
+      gtNapalmBomb: DrawRotated(sprNapalmBomb, x, y, 0, DxDy2Angle(Gear^.dY, Gear^.dX));
          end;
       if Gear^.RenderTimer and (Gear^.Tex <> nil) then DrawCentered(x + 8, y + 8, Gear^.Tex);
       Gear:= Gear^.NextGear
--- a/hedgewars/HHHandlers.inc	Sun Nov 14 20:37:55 2010 +0100
+++ b/hedgewars/HHHandlers.inc	Sun Nov 14 15:06:02 2010 -0500
@@ -305,6 +305,8 @@
                                 gtResurrector, 0, _0, _0, 0);
                         CurAmmoGear^.SoundChannel := LoopSound(sndResurrector);
                     end;
+                   amDrillStrike: AddGear(CurWeapon^.Pos, 0, gtAirAttack, 3, _0, _0, 0);
+                   //amMelonStrike: AddGear(CurWeapon^.Pos, 0, gtAirAttack, 4, _0, _0, 0);
                   end;
 
         uStats.AmmoUsed(CurAmmoType);
@@ -660,11 +662,22 @@
    if (Gear^.dY.isNegative) and TestCollisionYKick(Gear, -1) then Gear^.dY:= _0;
    Gear^.State:= Gear^.State or gstMoving;
    if isUnderwater then Gear^.dY:= Gear^.dY + cGravity / _2
-   else Gear^.dY:= Gear^.dY + cGravity
+   else
+       begin
+       Gear^.dY:= Gear^.dY + cGravity;
+// this set of circumstances could be less complex if jumping was more clearly identified
+       if ((GameFlags and gfMoreWind) <> 0) and 
+          (Gear^.Damage <> 0) or
+          ((CurAmmoGear <> nil) and
+            ((CurAmmoGear^.AmmoType = amJetpack) or
+            (CurAmmoGear^.AmmoType = amBirdy))) or
+          ((Gear^.dY.QWordValue + Gear^.dX.QWordValue) > _0_55.QWordValue)
+          then Gear^.dX := Gear^.dX + cWindSpeed * _0_2
+       end
    end 
 else
    begin
-   if ((hwAbs(Gear^.dX) + hwAbs(Gear^.dY)) < _0_55)
+   if ((Gear^.dX.QWordValue + Gear^.dY.QWordValue) < _0_55.QWordValue)
       and ((Gear^.State and gstHHJumping) <> 0) then SetLittle(Gear^.dX);
 
    if not Gear^.dY.isNegative then
--- a/hedgewars/VGSHandlers.inc	Sun Nov 14 20:37:55 2010 +0100
+++ b/hedgewars/VGSHandlers.inc	Sun Nov 14 15:06:02 2010 -0500
@@ -133,6 +133,16 @@
 end;
 
 ////////////////////////////////////////////////////////////////////////////////
+procedure doStepLineTrail(Gear: PVisualGear; Steps: Longword);
+begin
+Steps := Steps;
+if Gear^.Timer <= Steps then
+    DeleteVisualGear(Gear)
+else
+    dec(Gear^.Timer, Steps)
+end;
+
+////////////////////////////////////////////////////////////////////////////////
 procedure doStepEgg(Gear: PVisualGear; Steps: Longword);
 begin
 Gear^.X:= Gear^.X + Gear^.dX * Steps;
@@ -608,3 +618,12 @@
     DeleteVisualGear(Gear);
     end
 end;
+
+////////////////////////////////////////////////////////////////////////////////
+procedure doStepBulletHit(Gear: PVisualGear; Steps: Longword);
+begin
+  if Gear^.FrameTicks <= Steps then
+      DeleteVisualGear(Gear)
+  else
+      dec(Gear^.FrameTicks, Steps);
+end;
--- a/hedgewars/uAIAmmoTests.pas	Sun Nov 14 20:37:55 2010 +0100
+++ b/hedgewars/uAIAmmoTests.pas	Sun Nov 14 15:06:02 2010 -0500
@@ -101,7 +101,8 @@
             (proc: nil;              flags: 0), // amFlamethrower
             (proc: @TestGrenade;     flags: 0), // amSMine
             (proc: @TestFirePunch;   flags: 0), // amHammer
-            (proc: nil;              flags: 0) // amResurrector
+            (proc: nil;              flags: 0), // amResurrector
+            (proc: nil;              flags: 0) // amDrillStrike
             );
 
 const BadTurn = Low(LongInt) div 4;
--- a/hedgewars/uConsts.pas	Sun Nov 14 20:37:55 2010 +0100
+++ b/hedgewars/uConsts.pas	Sun Nov 14 15:06:02 2010 -0500
@@ -75,7 +75,8 @@
             sprFeather, sprPiano, sprHandSineGun, sprPortalGun, sprPortal,
             sprCheese, sprHandCheese, sprHandFlamethrower, sprChunk, sprNote,
             sprSMineOff, sprSMineOn, sprHandSMine, sprHammer,
-            sprHandResurrector, sprCross
+            sprHandResurrector, sprCross, sprAirDrill, sprNapalmBomb,
+            sprBulletHit
             );
 
     // Gears that interact with other Gears and/or Land
@@ -89,7 +90,8 @@
             gtHellishBomb, gtWaterUp, gtDrill, gtBallGun, gtBall, gtRCPlane, // 40
             gtSniperRifleShot, gtJetpack, gtMolotov, gtExplosives, gtBirdy, // 45
             gtEgg, gtPortal, gtPiano, gtGasBomb, gtSineGunShot, gtFlamethrower, // 51
-            gtSMine, gtPoisonCloud, gtHammer, gtHammerHit, gtResurrector);
+            gtSMine, gtPoisonCloud, gtHammer, gtHammerHit, gtResurrector, // 56
+            gtNapalmBomb); // 57
 
     // Gears that are _only_ of visual nature (e.g. background stuff, visual effects, speechbubbles, etc.)
     TVisualGearType = (vgtFlake, vgtCloud, vgtExplPart, vgtExplPart2, vgtFire,
@@ -97,7 +99,8 @@
             vgtSteam, vgtAmmo, vgtSmoke, vgtSmokeWhite, vgtHealth, vgtShell,
             vgtDust, vgtSplash, vgtDroplet, vgtSmokeRing, vgtBeeTrace, vgtEgg,
             vgtFeather, vgtHealthTag, vgtSmokeTrace, vgtEvilTrace, vgtExplosion,
-            vgtBigExplosion, vgtChunk, vgtNote);
+            vgtBigExplosion, vgtChunk, vgtNote, vgtLineTrail,
+            vgtBulletHit);
 
     TGearsType = set of TGearType;
 
@@ -134,7 +137,7 @@
             amRCPlane, amLowGravity, amExtraDamage, amInvulnerable, amExtraTime, // 35
             amLaserSight, amVampiric, amSniperRifle, amJetpack, amMolotov, amBirdy, amPortalGun, // 42
             amPiano, amGasBomb, amSineGun, amFlamethrower, amSMine, amHammer, // 48
-            amResurrector);
+            amResurrector, amDrillStrike);
 
     TCrateType = (HealthCrate, AmmoCrate, UtilityCrate);
 
@@ -355,6 +358,7 @@
     gfResetWeps          = $00200000;
     gfPerHogAmmo         = $00400000;
     gfDisableWind        = $00800000;           // only lua for now
+    gfMoreWind           = $01000000;
     // NOTE: When adding new game flags, ask yourself
     // if a "game start notice" would be useful. If so,
     // add one in uWorld.pas - look for "AddGoal".
@@ -824,8 +828,23 @@
             (FileName: 'Cross'; Path: ptGraphics; altPath: ptNone;
                 Texture: nil; Surface: nil; Width: 108; Height: 138;
                 imageWidth: 0; imageHeight: 0; saveSurf: false; priority:
+                tpMedium; getDimensions: false; getImageDimensions: true),
+            //sprCross
+            (FileName:  'AirDrill'; Path: ptGraphics; AltPath: ptNone;
+                Texture: nil; Surface: nil; Width:  16; Height: 16;
+                imageWidth: 0; imageHeight: 0; saveSurf: false; priority:
+                tpMedium; getDimensions: false; getImageDimensions: true),
+            // sprAirDrill
+            (FileName:  'NapalmBomb'; Path: ptGraphics; AltPath: ptNone;
+                Texture: nil; Surface: nil; Width:  16; Height: 16;
+                imageWidth: 0; imageHeight: 0; saveSurf: false; priority:
+                tpMedium; getDimensions: false; getImageDimensions: true),
+            // sprNapalmBomb
+            (FileName:  'BulletHit'; Path: ptGraphics; AltPath: ptNone;
+                Texture: nil; Surface: nil; Width:  32; Height: 32;
+                imageWidth: 0; imageHeight: 0; saveSurf: false; priority:
                 tpMedium; getDimensions: false; getImageDimensions: true)
-            //sprCross
+            // sprNapalmBomb
             );
 
 
@@ -2183,6 +2202,7 @@
             ejectX: 0;
             ejectY: 0),
 
+// Ressurrector
         (NameId: sidResurrector;
             NameTex: nil;
             Probability: 0;
@@ -2205,6 +2225,33 @@
             PosCount: 1;
             PosSprite: sprWater;
             ejectX: 0;
+            ejectY: 0),
+
+// DrillStrike
+            (NameId: sidDrillStrike;
+            NameTex: nil;
+            Probability: 200;
+            NumberInCase: 1;
+            Ammo: (Propz: ammoprop_NoCrosshair or
+                            ammoprop_NeedTarget or
+                            ammoprop_AttackingPut or
+                            ammoprop_DontHold or
+                            ammoprop_NotBorder;
+                Count: 1;
+                NumPerTurn: 0;
+                Timer: 0;
+                Pos: 0;
+                AmmoType: amDrillStrike;
+                AttackVoice: sndIncoming);
+            Slot: 5;
+            TimeAfterTurn: 0;
+            minAngle: 0;
+            maxAngle: 0;
+            isDamaging: true;
+            SkipTurns: 6;
+            PosCount: 2;
+            PosSprite: sprAmAirplane;
+            ejectX: 0;
             ejectY: 0)
         );
 
--- a/hedgewars/uGears.pas	Sun Nov 14 20:37:55 2010 +0100
+++ b/hedgewars/uGears.pas	Sun Nov 14 15:06:02 2010 -0500
@@ -135,24 +135,18 @@
 
 function GetLaunchX(at: TAmmoType; dir: LongInt; angle: LongInt): LongInt;
 begin
-    GetLaunchX:= 0
-(*
     if (Ammoz[at].ejectX <> 0) or (Ammoz[at].ejectY <> 0) then
         GetLaunchX:= sign(dir) * (8 + hwRound(AngleSin(angle) * Ammoz[at].ejectX) + hwRound(AngleCos(angle) * Ammoz[at].ejectY))
     else
         GetLaunchX:= 0
-*)
 end;
 
 function GetLaunchY(at: TAmmoType; angle: LongInt): LongInt;
 begin
-    GetLaunchY:= 0
-(*
     if (Ammoz[at].ejectX <> 0) or (Ammoz[at].ejectY <> 0) then
         GetLaunchY:= hwRound(AngleSin(angle) * Ammoz[at].ejectY) - hwRound(AngleCos(angle) * Ammoz[at].ejectX) - 2
     else
         GetLaunchY:= 0
-*)
 end;
 
 {$INCLUDE "GSHandlers.inc"}
@@ -215,7 +209,8 @@
             @doStepPoisonCloud,
             @doStepHammer,
             @doStepHammerHit,
-            @doStepResurrector
+            @doStepResurrector,
+            @doStepNapalmBomb
             );
 
 procedure InsertGearToList(Gear: PGear);
@@ -304,7 +299,7 @@
                 gear^.ImpactSound:= sndGrenadeImpact;
                 gear^.nImpactSounds:= 1;
                 gear^.AdvBounce:= 1;
-                gear^.Radius:= 6;
+                gear^.Radius:= 5;
                 gear^.Elasticity:= _0_8;
                 gear^.Friction:= _0_8;
                 gear^.RenderTimer:= true;
@@ -314,7 +309,7 @@
                 gear^.ImpactSound:= sndMelonImpact;
                 gear^.nImpactSounds:= 1;
                 gear^.AdvBounce:= 1;
-                gear^.Radius:= 4;
+                gear^.Radius:= 6;
                 gear^.Elasticity:= _0_8;
                 gear^.Friction:= _0_995;
                 gear^.RenderTimer:= true;
@@ -371,9 +366,9 @@
                 gear^.Elasticity:= _0_55;
                 gear^.Friction:= _0_995;
                 if cMinesTime < 0 then
-                    gear^.Timer:= getrandom(4)*1000
+                    gear^.Timer:= getrandom(51)*100
                 else
-                    gear^.Timer:= cMinesTime*1;
+                    gear^.Timer:= cMinesTime*1000;
                 end;
        gtSMine: begin
                 gear^.Health:= 10;
@@ -551,6 +546,10 @@
      gtWaterUp: begin
                 gear^.Tag := 47;
                 end;
+  gtNapalmBomb: begin
+                gear^.Timer:= 1000;
+                gear^.Radius:= 5;
+                end;
     end;
 
 InsertGearToList(gear);
@@ -1425,6 +1424,7 @@
 var t: PGearArray;
     Gear: PGear;
     i, tmpDmg: LongInt;
+    VGear: PVisualGear;
 begin
 t:= CheckGearsCollision(Ammo);
 // Just to avoid hogs on rope dodging fire.
@@ -1446,6 +1446,16 @@
     tmpDmg:= ModifyDamage(Damage, Gear);
     if (Gear^.State and gstNoDamage) = 0 then
         begin
+        
+        if (Ammo^.Kind = gtDEagleShot) or (Ammo^.Kind = gtSniperRifleShot) then 
+        begin
+            VGear := AddVisualGear(hwround(Ammo^.X), hwround(Ammo^.Y), vgtBulletHit);
+            if VGear <> nil then
+            begin
+                VGear^.Angle := DxDy2Angle(-Ammo^.dX, Ammo^.dY);
+            end;
+        end;
+        
         if (Gear^.Kind = gtHedgehog) and (Ammo^.State and gsttmpFlag <> 0) and (Ammo^.Kind = gtShover) then Gear^.FlightTime:= 1;
 
         case Gear^.Kind of
--- a/hedgewars/uLocale.pas	Sun Nov 14 20:37:55 2010 +0100
+++ b/hedgewars/uLocale.pas	Sun Nov 14 15:06:02 2010 -0500
@@ -30,7 +30,7 @@
             sidLowGravity, sidExtraDamage, sidInvulnerable, sidExtraTime,
             sidLaserSight, sidVampiric, sidSniperRifle, sidJetpack,
             sidMolotov, sidBirdy, sidPortalGun, sidPiano, sidGasBomb, sidSineGun, sidFlamethrower,
-            sidSMine, sidHammer, sidResurrector);
+            sidSMine, sidHammer, sidResurrector, sidDrillStrike);
 
     TMsgStrId = (sidStartFight, sidDraw, sidWinner, sidVolume, sidPaused,
             sidConfirm, sidSuddenDeath, sidRemaining, sidFuel, sidSync,
--- a/hedgewars/uMisc.pas	Sun Nov 14 20:37:55 2010 +0100
+++ b/hedgewars/uMisc.pas	Sun Nov 14 15:06:02 2010 -0500
@@ -758,7 +758,7 @@
     cMapGen             := 0;   // MAPGEN_REGULAR
     cMazeSize           := 0;
     cHedgehogTurnTime   := 45000;
-    cMinesTime          := 3000;
+    cMinesTime          := 3;
     cMaxAIThinkTime     := 9000;
     cCloudsNumber       := 9;
     cHealthCaseProb     := 35;
--- a/hedgewars/uScript.pas	Sun Nov 14 20:37:55 2010 +0100
+++ b/hedgewars/uScript.pas	Sun Nov 14 15:06:02 2010 -0500
@@ -1126,6 +1126,7 @@
 ScriptSetInteger('gfResetWeps', gfResetWeps);
 ScriptSetInteger('gfPerHogAmmo', gfPerHogAmmo);
 ScriptSetInteger('gfDisableWind', gfDisableWind);
+ScriptSetInteger('gfMoreWind', gfMoreWind);
 
 ScriptSetInteger('gmLeft', gmLeft);
 ScriptSetInteger('gmRight', gmRight);
@@ -1184,6 +1185,7 @@
 lua_register(luaState, 'AddTeam', @lc_addteam);
 lua_register(luaState, 'AddHog', @lc_addhog);
 lua_register(luaState, 'SetHealth', @lc_sethealth);
+lua_register(luaState, 'GetHealth', @lc_gethealth);
 lua_register(luaState, 'SetEffect', @lc_seteffect);
 lua_register(luaState, 'GetHogClan', @lc_gethogclan);
 lua_register(luaState, 'GetHogTeamName', @lc_gethogteamname);
@@ -1200,7 +1202,6 @@
 lua_register(luaState, 'SetTag', @lc_settag);
 lua_register(luaState, 'SetTimer', @lc_settimer);
 lua_register(luaState, 'GetTimer', @lc_gettimer);
-lua_register(luaState, 'GetHealth', @lc_gethealth);
 lua_register(luaState, 'SetZoom', @lc_setzoom);
 lua_register(luaState, 'GetZoom', @lc_getzoom);
 lua_register(luaState, 'HogSay', @lc_hogsay);
--- a/hedgewars/uStore.pas	Sun Nov 14 20:37:55 2010 +0100
+++ b/hedgewars/uStore.pas	Sun Nov 14 15:06:02 2010 -0500
@@ -57,6 +57,7 @@
 procedure DrawFromRect(X, Y, W, H: LongInt; r: PSDL_Rect; SourceTexture: PTexture);
 procedure DrawFromRect(X, Y: LongInt; r: PSDL_Rect; SourceTexture: PTexture);
 procedure DrawHedgehog(X, Y: LongInt; Dir: LongInt; Pos, Step: LongWord; Angle: real);
+procedure DrawLine(X0, Y0, X1, Y1, Width: Single; r, g, b, a: Byte); 
 procedure DrawFillRect(r: TSDL_Rect);
 procedure DrawCircle(X, Y, Radius: LongInt; Width: Single; r, g, b, a: Byte); 
 procedure DrawRoundRect(rect: PSDL_Rect; BorderColor, FillColor: Longword; Surface: PSDL_Surface; Clear: boolean);
@@ -791,6 +792,32 @@
 glPopMatrix
 end;
 
+procedure DrawLine(X0, Y0, X1, Y1, Width: Single; r, g, b, a: Byte);
+var VertexBuffer: array [0..3] of TVertex2f;
+begin
+    glDisable(GL_TEXTURE_2D);
+    glEnable(GL_LINE_SMOOTH);
+
+    glPushMatrix;
+    glTranslatef(WorldDx, WorldDy, 0);
+    glLineWidth(Width);
+
+    Tint(r, g, b, a);
+    VertexBuffer[0].X:= X0;
+    VertexBuffer[0].Y:= Y0;
+    VertexBuffer[1].X:= X1;
+    VertexBuffer[1].Y:= Y1;
+
+    glVertexPointer(2, GL_FLOAT, 0, @VertexBuffer[0]);
+    glDrawArrays(GL_LINES, 0, Length(VertexBuffer));
+    Tint($FF, $FF, $FF, $FF);
+    
+    glPopMatrix;
+    
+    glEnable(GL_TEXTURE_2D);
+    glDisable(GL_LINE_SMOOTH);
+end;
+
 procedure DrawFillRect(r: TSDL_Rect);
 var VertexBuffer: array [0..3] of TVertex2f;
 begin
--- a/hedgewars/uVisualGears.pas	Sun Nov 14 20:37:55 2010 +0100
+++ b/hedgewars/uVisualGears.pas	Sun Nov 14 15:06:02 2010 -0500
@@ -117,7 +117,9 @@
             @doStepExplosion,
             @doStepBigExplosion,
             @doStepChunk,
-            @doStepNote
+            @doStepNote,
+            @doStepLineTrail,
+            @doStepBulletHit
         );
 
 function  AddVisualGear(X, Y: LongInt; Kind: TVisualGearType; State: LongWord = 0): PVisualGear;
@@ -316,6 +318,13 @@
                 Frame:= random(4);
                 FrameTicks:= random(2000) + 1500;
                 end;
+  vgtBulletHit: begin
+                dx:= 0;
+                dy:= 0;
+                FrameTicks:= 350;
+                Frame:= 7;
+                Angle := 0;
+                end;
         end;
 
 if State <> 0 then gear^.State:= State;
@@ -406,6 +415,7 @@
         case Gear^.Kind of
             vgtSmokeTrace: if Gear^.State < 8 then DrawSprite(sprSmokeTrace, round(Gear^.X) + WorldDx, round(Gear^.Y) + WorldDy, Gear^.State);
             vgtEvilTrace: if Gear^.State < 8 then DrawSprite(sprEvilTrace, round(Gear^.X) + WorldDx, round(Gear^.Y) + WorldDy, Gear^.State);
+            vgtLineTrail: DrawLine(Gear^.X, Gear^.Y, Gear^.dX, Gear^.dY, 1.0, $FF, min(Gear^.Timer, $C0), min(Gear^.Timer, $80), min(Gear^.Timer, $FF));
         end;
             if (cReducedQuality and rqFancyBoom) = 0 then
                 case Gear^.Kind of
@@ -477,6 +487,7 @@
                             end;
                 vgtChunk: DrawRotatedF(sprChunk, round(Gear^.X) + WorldDx, round(Gear^.Y) + WorldDy, Gear^.Frame, 1, Gear^.Angle);
                  vgtNote: DrawRotatedF(sprNote, round(Gear^.X) + WorldDx, round(Gear^.Y) + WorldDy, Gear^.Frame, 1, Gear^.Angle);
+                vgtBulletHit: DrawRotatedF(sprBulletHit, round(Gear^.X) + WorldDx - 0, round(Gear^.Y) + WorldDy - 0, 7 - (Gear^.FrameTicks div 50), 1, Gear^.Angle);
             end;
         case Gear^.Kind of
             vgtSmallDamageTag: DrawCentered(round(Gear^.X) + WorldDx, round(Gear^.Y) + WorldDy, Gear^.Tex);
--- a/hedgewars/uWorld.pas	Sun Nov 14 20:37:55 2010 +0100
+++ b/hedgewars/uWorld.pas	Sun Nov 14 15:06:02 2010 -0500
@@ -149,14 +149,14 @@
 ScreenFadeSpeed:= 1;
 
 // modified mine timers?
-if cMinesTime <> 3000 then
+if cMinesTime <> 3 then
     begin
     if cMinesTime = 0 then
         g:= AddGoal(g, gfAny, gidNoMineTimer)
     else if cMinesTime < 0 then
         g:= AddGoal(g, gfAny, gidRandomMineTimer)
     else
-        g:= AddGoal(g, gfAny, gidMineTimer, cMinesTime div 1000);
+        g:= AddGoal(g, gfAny, gidMineTimer, cMinesTime);
     end;
 
 // if the string has been set, show it for (default timeframe) seconds
--- a/project_files/HedgewarsMobile/Classes/CommodityFunctions.h	Sun Nov 14 20:37:55 2010 +0100
+++ b/project_files/HedgewarsMobile/Classes/CommodityFunctions.h	Sun Nov 14 15:06:02 2010 -0500
@@ -60,9 +60,6 @@
 #define DEFAULT_NETGAME_PORT    46631
 
 
-void createTeamNamed (NSString *nameWithoutExt);
-void createWeaponNamed (NSString *nameWithoutExt, int type);
-void createSchemeNamed (NSString *nameWithoutExt);
 void print_free_memory (void);
 void playSound (NSString *snd);
 void popError (const char *title, const char *message);
@@ -73,3 +70,5 @@
 NSArray *getAvailableColors (void);
 UILabel *createBlueLabel (NSString *title, CGRect frame);
 UILabel *createLabelWithParams (NSString *title, CGRect frame, CGFloat borderWidth, UIColor *borderColor, UIColor *backgroundColor);
+
+CGSize PSPNGSizeFromMetaData (NSString *aFileName);
--- a/project_files/HedgewarsMobile/Classes/CommodityFunctions.m	Sun Nov 14 20:37:55 2010 +0100
+++ b/project_files/HedgewarsMobile/Classes/CommodityFunctions.m	Sun Nov 14 15:06:02 2010 -0500
@@ -28,169 +28,6 @@
 #import "AudioToolbox/AudioToolbox.h"
 #import "PascalImports.h"
 
-void createTeamNamed (NSString *nameWithoutExt) {
-    NSString *teamsDirectory = TEAMS_DIRECTORY();
-
-    if (![[NSFileManager defaultManager] fileExistsAtPath: teamsDirectory]) {
-        [[NSFileManager defaultManager] createDirectoryAtPath:teamsDirectory
-                                  withIntermediateDirectories:NO
-                                                   attributes:nil
-                                                        error:NULL];
-    }
-
-    NSMutableArray *hedgehogs = [[NSMutableArray alloc] initWithCapacity: HW_getMaxNumberOfHogs()];
-
-    for (int i = 0; i < HW_getMaxNumberOfHogs(); i++) {
-        NSString *hogName = [[NSString alloc] initWithFormat:@"hedgehog %d",i];
-        NSDictionary *hog = [[NSDictionary alloc] initWithObjectsAndKeys: [NSNumber numberWithInt:0],@"level",
-                             hogName,@"hogname", @"NoHat",@"hat", nil];
-        [hogName release];
-        [hedgehogs addObject:hog];
-        [hog release];
-    }
-
-    NSDictionary *theTeam = [[NSDictionary alloc] initWithObjectsAndKeys:@"0",@"hash",
-                             @"Statue",@"grave", @"Plane",@"fort", @"Default",@"voicepack",
-                             @"hedgewars",@"flag", hedgehogs,@"hedgehogs", nil];
-    [hedgehogs release];
-
-    NSString *teamFile = [[NSString alloc] initWithFormat:@"%@/%@.plist", teamsDirectory, nameWithoutExt];
-
-    [theTeam writeToFile:teamFile atomically:YES];
-    [teamFile release];
-    [theTeam release];
-}
-
-void createWeaponNamed (NSString *nameWithoutExt, int type) {
-    NSString *weaponsDirectory = WEAPONS_DIRECTORY();
-
-    if (![[NSFileManager defaultManager] fileExistsAtPath: weaponsDirectory]) {
-        [[NSFileManager defaultManager] createDirectoryAtPath:weaponsDirectory
-                                  withIntermediateDirectories:NO
-                                                   attributes:nil
-                                                        error:NULL];
-    }
-
-    NSDictionary *theWeapon = nil;
-    switch (type) {
-        case 0: //default
-            theWeapon = [[NSDictionary alloc] initWithObjectsAndKeys:
-                         @"9391929422199121032235111001201000000211110101011",@"ammostore_initialqt",
-                         @"0405040541600655546554464776576666666155510101117",@"ammostore_probability",
-                         @"0000000000000205500000040007004000000000200000000",@"ammostore_delay",
-                         @"1311110312111111123114111111111111111211111101111",@"ammostore_crate", nil];
-            break;
-        case 1: //crazy
-            theWeapon = [[NSDictionary alloc] initWithObjectsAndKeys:
-                         @"9999999999999999992999999999999999299999999909999",@"ammostore_initialqt",
-                         @"1111110111111111111111111111111111111111111101111",@"ammostore_probability",
-                         @"0000000000000000000000000000000000000000000000000",@"ammostore_delay",
-                         @"1311110312111111123114111111111111111211110101111",@"ammostore_crate", nil];
-            break;
-        case 2: //pro mode
-            theWeapon = [[NSDictionary alloc] initWithObjectsAndKeys:
-                         @"9090009000000000000009000000000000000000000000000",@"ammostore_initialqt",
-                         @"0000000000000000000000000000000000000000000000000",@"ammostore_probability",
-                         @"0000000000000205500000040007004000000000200000000",@"ammostore_delay",
-                         @"1111111111111111111111111111111111111111100101111",@"ammostore_crate", nil];
-            break;
-        case 3: //shoppa
-            theWeapon = [[NSDictionary alloc] initWithObjectsAndKeys:
-                         @"0000009900000000000000000000000000000000000000000",@"ammostore_initialqt",
-                         @"4444410044244402210112121222422000000002000400010",@"ammostore_probability",
-                         @"0000000000000000000000000000000000000000000000000",@"ammostore_delay",
-                         @"1111111111111111111111111111111111111111101101111",@"ammostore_crate", nil];
-            break;
-        case 4: //clean slate
-            theWeapon = [[NSDictionary alloc] initWithObjectsAndKeys:
-                         @"1010009000010000011000000000000000000000000000001",@"ammostore_initialqt",
-                         @"0405040541600655546554464776576666666155510101117",@"ammostore_probability",
-                         @"0000000000000205500000040007004000000000200000000",@"ammostore_delay",
-                         @"1311110312111111123114111111111111111211111101111",@"ammostore_crate", nil];
-            break;
-        case 5: //minefield
-            theWeapon = [[NSDictionary alloc] initWithObjectsAndKeys:
-                         @"0000009900090000000300000000000000000000000000000",@"ammostore_initialqt",
-                         @"0000000000000000000000000000000000000000000000000",@"ammostore_probability",
-                         @"0000000000000205500000040007004000000000200000000",@"ammostore_delay",
-                         @"1111111111111111111111111111111111111111111101111",@"ammostore_crate", nil];
-            break;
-        default:
-            NSLog(@"Nope");
-            break;
-    }
-    
-    NSString *weaponFile = [[NSString alloc] initWithFormat:@"%@/%@.plist", weaponsDirectory, nameWithoutExt];
-
-    [theWeapon writeToFile:weaponFile atomically:YES];
-    [weaponFile release];
-    [theWeapon release];
-}
-
-void createSchemeNamed (NSString *nameWithoutExt) {
-    NSString *schemesDirectory = SCHEMES_DIRECTORY();
-
-    if (![[NSFileManager defaultManager] fileExistsAtPath: schemesDirectory]) {
-        [[NSFileManager defaultManager] createDirectoryAtPath:schemesDirectory
-                                  withIntermediateDirectories:NO
-                                                   attributes:nil
-                                                        error:NULL];
-    }
-    
-    NSMutableArray *basicArray  = [[NSMutableArray alloc] initWithObjects:
-                                   [NSNumber numberWithInt:100],      //initialhealth
-                                   [NSNumber numberWithInt:45],       //turntime
-                                   [NSNumber numberWithInt:100],      //damagemodifier
-                                   [NSNumber numberWithInt:15],       //suddendeathtimeout
-                                   [NSNumber numberWithInt:47],       //waterrise
-                                   [NSNumber numberWithInt:5],        //healthdecrease
-                                   [NSNumber numberWithInt:5],        //cratedrops
-                                   [NSNumber numberWithInt:35],       //healthprob
-                                   [NSNumber numberWithInt:25],       //healthamount
-                                   [NSNumber numberWithInt:3],        //minestime
-                                   [NSNumber numberWithInt:4],        //minesnumber
-                                   [NSNumber numberWithInt:0],        //dudmines
-                                   [NSNumber numberWithInt:2],        //explosives
-                                   nil];
-    
-    NSMutableArray *gamemodArray= [[NSMutableArray alloc] initWithObjects:
-                                   [NSNumber numberWithBool:NO],      //fortmode
-                                   [NSNumber numberWithBool:NO],      //divideteam
-                                   [NSNumber numberWithBool:NO],      //solidland
-                                   [NSNumber numberWithBool:NO],      //addborder
-                                   [NSNumber numberWithBool:NO],      //lowgravity
-                                   [NSNumber numberWithBool:NO],      //lasersight
-                                   [NSNumber numberWithBool:NO],      //invulnerable
-                                   [NSNumber numberWithBool:NO],      //resethealth
-                                   [NSNumber numberWithBool:NO],      //vampirism
-                                   [NSNumber numberWithBool:NO],      //karma
-                                   [NSNumber numberWithBool:NO],      //artillery
-                                   [NSNumber numberWithBool:YES],     //randomorder
-                                   [NSNumber numberWithBool:NO],      //king
-                                   [NSNumber numberWithBool:NO],      //placehedgehogs
-                                   [NSNumber numberWithBool:NO],      //clansharesammo
-                                   [NSNumber numberWithBool:NO],      //disablegirders
-                                   [NSNumber numberWithBool:NO],      //disablelandobjects
-                                   [NSNumber numberWithBool:NO],      //aisurvival
-                                   [NSNumber numberWithBool:NO],      //infattack
-                                   [NSNumber numberWithBool:NO],      //resetweaps
-                                   [NSNumber numberWithBool:NO],      //perhogammo
-                                   nil];
-    
-    NSMutableDictionary *theScheme = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
-                                      basicArray,@"basic",
-                                      gamemodArray,@"gamemod",
-                                      nil];
-    [gamemodArray release];
-    [basicArray release];
-    
-    NSString *schemeFile = [[NSString alloc] initWithFormat:@"%@/%@.plist", schemesDirectory, nameWithoutExt];
-    
-    [theScheme writeToFile:schemeFile atomically:YES];
-    [schemeFile release];
-    [theScheme release];
-}
-
 BOOL inline rotationManager (UIInterfaceOrientation interfaceOrientation) {
     if (IS_IPAD())
         return (interfaceOrientation == UIInterfaceOrientationLandscapeRight) ||
@@ -305,3 +142,47 @@
     
     return theLabel;
 }
+
+// this routine checks for the PNG size without loading it in memory
+// https://github.com/steipete/PSFramework/blob/master/PSFramework%20Version%200.3/PhotoshopFramework/PSMetaDataFunctions.m
+CGSize PSPNGSizeFromMetaData (NSString *aFileName) {
+    // File Name to C String.
+    const char *fileName = [aFileName UTF8String];
+    // source file
+    FILE *infile = fopen(fileName, "rb");
+    if (infile == NULL) {
+        DLog(@"Can't open the file: %@", aFileName);
+        return CGSizeZero;
+    }
+
+    // Bytes Buffer.
+    unsigned char buffer[30];
+    // Grab Only First Bytes.
+    fread(buffer, 1, 30, infile);
+    // Close File.
+    fclose(infile);
+
+    // PNG Signature.
+    unsigned char png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10};
+
+    // Compare File signature.
+    if ((int)(memcmp(&buffer[0], &png_signature[0], 8))) {
+        DLog(@"The file (%@) is not a PNG file", aFileName);
+        return CGSizeZero;
+    }
+
+    // Calc Sizes. Isolate only four bytes of each size (width, height).
+    int width[4];
+    int height[4];
+    for (int d = 16; d < (16 + 4); d++) {
+        width[d-16] = buffer[d];
+        height[d-16] = buffer[d+4];
+    }
+
+    // Convert bytes to Long (Integer)
+    long resultWidth = (width[0] << (int)24) | (width[1] << (int)16) | (width[2] << (int)8) | width[3];
+    long resultHeight = (height[0] << (int)24) | (height[1] << (int)16) | (height[2] << (int)8) | height[3];
+
+    // Return Size.
+    return CGSizeMake(resultWidth,resultHeight);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/HedgewarsMobile/Classes/CreationChamber.h	Sun Nov 14 15:06:02 2010 -0500
@@ -0,0 +1,26 @@
+/*
+ * Hedgewars-iOS, a Hedgewars port for iOS devices
+ * Copyright (c) 2009-2010 Vittorio Giovara <vittorio.giovara@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * File created on 12/11/2010.
+ */
+
+
+#import <Foundation/Foundation.h>
+
+void createTeamNamed (NSString *nameWithoutExt);
+void createWeaponNamed (NSString *nameWithoutExt, int type);
+void createSchemeNamed (NSString *nameWithoutExt);
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/HedgewarsMobile/Classes/CreationChamber.m	Sun Nov 14 15:06:02 2010 -0500
@@ -0,0 +1,201 @@
+/*
+ * Hedgewars-iOS, a Hedgewars port for iOS devices
+ * Copyright (c) 2009-2010 Vittorio Giovara <vittorio.giovara@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * File created on 12/11/2010.
+ */
+
+
+#import "CreationChamber.h"
+
+void createTeamNamed (NSString *nameWithoutExt) {
+    NSString *teamsDirectory = TEAMS_DIRECTORY();
+
+    if (![[NSFileManager defaultManager] fileExistsAtPath: teamsDirectory]) {
+        [[NSFileManager defaultManager] createDirectoryAtPath:teamsDirectory
+                                  withIntermediateDirectories:NO
+                                                   attributes:nil
+                                                        error:NULL];
+    }
+
+    NSMutableArray *hedgehogs = [[NSMutableArray alloc] initWithCapacity: HW_getMaxNumberOfHogs()];
+
+    for (int i = 0; i < HW_getMaxNumberOfHogs(); i++) {
+        NSString *hogName = [[NSString alloc] initWithFormat:@"hedgehog %d",i];
+        NSDictionary *hog = [[NSDictionary alloc] initWithObjectsAndKeys:
+                             [NSNumber numberWithInt:0],@"level",
+                             hogName,@"hogname",
+                             @"NoHat",@"hat",
+                             nil];
+        [hogName release];
+        [hedgehogs addObject:hog];
+        [hog release];
+    }
+
+    NSDictionary *theTeam = [[NSDictionary alloc] initWithObjectsAndKeys:
+                             @"0",@"hash",
+                             @"Statue",@"grave",
+                             @"Plane",@"fort",
+                             @"Default",@"voicepack",
+                             @"hedgewars",@"flag",
+                             hedgehogs,@"hedgehogs",
+                             nil];
+    [hedgehogs release];
+
+    NSString *teamFile = [[NSString alloc] initWithFormat:@"%@/%@.plist", teamsDirectory, nameWithoutExt];
+
+    [theTeam writeToFile:teamFile atomically:YES];
+    [teamFile release];
+    [theTeam release];
+}
+
+void createWeaponNamed (NSString *nameWithoutExt, int type) {
+    NSString *weaponsDirectory = WEAPONS_DIRECTORY();
+
+    if (![[NSFileManager defaultManager] fileExistsAtPath: weaponsDirectory]) {
+        [[NSFileManager defaultManager] createDirectoryAtPath:weaponsDirectory
+                                  withIntermediateDirectories:NO
+                                                   attributes:nil
+                                                        error:NULL];
+    }
+
+    NSDictionary *theWeapon = nil;
+    switch (type) {
+        case 0: //default
+            theWeapon = [[NSDictionary alloc] initWithObjectsAndKeys:
+                         @"93919294221991210322351110012010000002111101010111",@"ammostore_initialqt",
+                         @"04050405416006555465544647765766666661555101011154",@"ammostore_probability",
+                         @"00000000000002055000000400070040000000002000000006",@"ammostore_delay",
+                         @"13111103121111111231141111111111111112111111011111",@"ammostore_crate", nil];
+            break;
+        case 1: //crazy
+            theWeapon = [[NSDictionary alloc] initWithObjectsAndKeys:
+                         @"99999999999999999929999999999999992999999999099999",@"ammostore_initialqt",
+                         @"11111101111111111111111111111111111111111111011111",@"ammostore_probability",
+                         @"00000000000000000000000000000000000000000000000000",@"ammostore_delay",
+                         @"13111103121111111231141111111111111112111101011111",@"ammostore_crate", nil];
+            break;
+        case 2: //pro mode
+            theWeapon = [[NSDictionary alloc] initWithObjectsAndKeys:
+                         @"90900090000000000000090000000000000000000000000000",@"ammostore_initialqt",
+                         @"00000000000000000000000000000000000000000000000000",@"ammostore_probability",
+                         @"00000000000002055000000400070040000000002000000000",@"ammostore_delay",
+                         @"11111111111111111111111111111111111111111001011111",@"ammostore_crate", nil];
+            break;
+        case 3: //shoppa
+            theWeapon = [[NSDictionary alloc] initWithObjectsAndKeys:
+                         @"00000099000000000000000000000000000000000000000000",@"ammostore_initialqt",
+                         @"44444100442444022101121212224220000000020004000100",@"ammostore_probability",
+                         @"00000000000000000000000000000000000000000000000000",@"ammostore_delay",
+                         @"11111111111111111111111111111111111111111011011111",@"ammostore_crate", nil];
+            break;
+        case 4: //clean slate
+            theWeapon = [[NSDictionary alloc] initWithObjectsAndKeys:
+                         @"10100090000100000110000000000000000000000000000010",@"ammostore_initialqt",
+                         @"04050405416006555465544647765766666661555101011154",@"ammostore_probability",
+                         @"00000000000002055000000400070040000000002000000000",@"ammostore_delay",
+                         @"13111103121111111231141111111111111112111111011111",@"ammostore_crate", nil];
+            break;
+        case 5: //minefield
+            theWeapon = [[NSDictionary alloc] initWithObjectsAndKeys:
+                         @"00000099000900000003000000000000000000000000000000",@"ammostore_initialqt",
+                         @"00000000000000000000000000000000000000000000000000",@"ammostore_probability",
+                         @"00000000000002055000000400070040000000002000000000",@"ammostore_delay",
+                         @"11111111111111111111111111111111111111111111011111",@"ammostore_crate", nil];
+            break;
+        case 6: //thinking with portals
+            theWeapon = [[NSDictionary alloc] initWithObjectsAndKeys:
+                         @"90000090020000000021000000000000001100000900000000",@"ammostore_initialqt",
+                         @"04050405416006555465544647765766666661555101011154",@"ammostore_probability",
+                         @"00000000000002055000000400070040000000002000000006",@"ammostore_delay",
+                         @"13111103121111111231141111111111111112111111011111",@"ammostore_crate", nil];
+            break;
+        default:
+            NSLog(@"Nope");
+            break;
+    }
+
+    NSString *weaponFile = [[NSString alloc] initWithFormat:@"%@/%@.plist", weaponsDirectory, nameWithoutExt];
+
+    [theWeapon writeToFile:weaponFile atomically:YES];
+    [weaponFile release];
+    [theWeapon release];
+}
+
+void createSchemeNamed (NSString *nameWithoutExt) {
+    NSString *schemesDirectory = SCHEMES_DIRECTORY();
+
+    if (![[NSFileManager defaultManager] fileExistsAtPath: schemesDirectory]) {
+        [[NSFileManager defaultManager] createDirectoryAtPath:schemesDirectory
+                                  withIntermediateDirectories:NO
+                                                   attributes:nil
+                                                        error:NULL];
+    }
+
+    NSMutableArray *basicArray  = [[NSMutableArray alloc] initWithObjects:
+                                   [NSNumber numberWithInt:100],      //initialhealth
+                                   [NSNumber numberWithInt:45],       //turntime
+                                   [NSNumber numberWithInt:100],      //damagemodifier
+                                   [NSNumber numberWithInt:15],       //suddendeathtimeout
+                                   [NSNumber numberWithInt:47],       //waterrise
+                                   [NSNumber numberWithInt:5],        //healthdecrease
+                                   [NSNumber numberWithInt:5],        //cratedrops
+                                   [NSNumber numberWithInt:35],       //healthprob
+                                   [NSNumber numberWithInt:25],       //healthamount
+                                   [NSNumber numberWithInt:3],        //minestime
+                                   [NSNumber numberWithInt:4],        //minesnumber
+                                   [NSNumber numberWithInt:0],        //dudmines
+                                   [NSNumber numberWithInt:2],        //explosives
+                                   nil];
+
+    NSMutableArray *gamemodArray= [[NSMutableArray alloc] initWithObjects:
+                                   [NSNumber numberWithBool:NO],      //fortmode
+                                   [NSNumber numberWithBool:NO],      //divideteam
+                                   [NSNumber numberWithBool:NO],      //solidland
+                                   [NSNumber numberWithBool:NO],      //addborder
+                                   [NSNumber numberWithBool:NO],      //lowgravity
+                                   [NSNumber numberWithBool:NO],      //lasersight
+                                   [NSNumber numberWithBool:NO],      //invulnerable
+                                   [NSNumber numberWithBool:NO],      //resethealth
+                                   [NSNumber numberWithBool:NO],      //vampirism
+                                   [NSNumber numberWithBool:NO],      //karma
+                                   [NSNumber numberWithBool:NO],      //artillery
+                                   [NSNumber numberWithBool:YES],     //randomorder
+                                   [NSNumber numberWithBool:NO],      //king
+                                   [NSNumber numberWithBool:NO],      //placehedgehogs
+                                   [NSNumber numberWithBool:NO],      //clansharesammo
+                                   [NSNumber numberWithBool:NO],      //disablegirders
+                                   [NSNumber numberWithBool:NO],      //disablelandobjects
+                                   [NSNumber numberWithBool:NO],      //aisurvival
+                                   [NSNumber numberWithBool:NO],      //infattack
+                                   [NSNumber numberWithBool:NO],      //resetweaps
+                                   [NSNumber numberWithBool:NO],      //perhogammo
+                                   [NSNumber numberWithBool:NO],      //nowind
+                                   nil];
+    
+    NSMutableDictionary *theScheme = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
+                                      basicArray,@"basic",
+                                      gamemodArray,@"gamemod",
+                                      nil];
+    [gamemodArray release];
+    [basicArray release];
+    
+    NSString *schemeFile = [[NSString alloc] initWithFormat:@"%@/%@.plist", schemesDirectory, nameWithoutExt];
+    
+    [theScheme writeToFile:schemeFile atomically:YES];
+    [schemeFile release];
+    [theScheme release];
+}
--- a/project_files/HedgewarsMobile/Classes/EditableCellView.m	Sun Nov 14 20:37:55 2010 +0100
+++ b/project_files/HedgewarsMobile/Classes/EditableCellView.m	Sun Nov 14 15:06:02 2010 -0500
@@ -37,6 +37,7 @@
         textField.returnKeyType = UIReturnKeyDone;
         textField.adjustsFontSizeToFitWidth = YES;
         textField.userInteractionEnabled = YES;
+        textField.font = [UIFont boldSystemFontOfSize:[UIFont labelFontSize]];
         [textField addTarget:self action:@selector(save:) forControlEvents:UIControlEventEditingDidEndOnExit];
 
         [self.contentView addSubview:textField];
@@ -76,8 +77,6 @@
         skew +=2;
     }
 
-    // sometimes the bold property gets lost
-    textField.font = [UIFont boldSystemFontOfSize:[UIFont labelFontSize]];
     textField.frame = CGRectMake(boundsX+offset+10, skew+10, 300, [UIFont labelFontSize] + 4);
 }
 
--- a/project_files/HedgewarsMobile/Classes/LevelViewController.m	Sun Nov 14 20:37:55 2010 +0100
+++ b/project_files/HedgewarsMobile/Classes/LevelViewController.m	Sun Nov 14 15:06:02 2010 -0500
@@ -144,6 +144,7 @@
         [self.tableView deleteSections:sections withRowAnimation:UITableViewRowAnimationFade];
         level = 0;
     }
+    [sections release];
 
     DLog(@"New level is %d",level);
     for (NSMutableDictionary *hog in hogs)
@@ -151,8 +152,6 @@
 
     [self.tableView reloadData];
     [[NSNotificationCenter defaultCenter] postNotificationName:@"setWriteNeedTeams" object:nil];
-
-    [sections release];
 }
 
 
@@ -166,8 +165,10 @@
         if (newRow != oldRow) {
             NSMutableArray *hogs = [self.teamDictionary objectForKey:@"hedgehogs"];
             
+            NSInteger level = newRow + 1;
             for (NSMutableDictionary *hog in hogs)
-                [hog setObject:[NSNumber numberWithInt:newRow+1] forKey:@"level"];
+                [hog setObject:[NSNumber numberWithInt:level] forKey:@"level"];
+            DLog(@"New level is %d",level);
             
             // tell our boss to write this new stuff on disk
             [[NSNotificationCenter defaultCenter] postNotificationName:@"setWriteNeedTeams" object:nil];
--- a/project_files/HedgewarsMobile/Classes/MainMenuViewController.m	Sun Nov 14 20:37:55 2010 +0100
+++ b/project_files/HedgewarsMobile/Classes/MainMenuViewController.m	Sun Nov 14 15:06:02 2010 -0500
@@ -20,7 +20,7 @@
 
 
 #import "MainMenuViewController.h"
-#import "CommodityFunctions.h"
+#import "CreationChamber.h"
 #import "SDL_uikitappdelegate.h"
 #import "PascalImports.h"
 #import "GameConfigViewController.h"
@@ -35,95 +35,81 @@
     return rotationManager(interfaceOrientation);
 }
 
-// using a different thread for audio 'cos it's slow
--(void) initAudioThread {
-    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-    // do somthing in the future
-    [pool release];
-}
-
 // check if some configuration files are already set; if they are present it means that the current copy must be updated
 -(void) createNecessaryFiles {
-    NSError *err = nil;
-    NSString *directoryToCheck, *fileToCheck, *fileToUpdate;
-    NSString *resDir = [[NSBundle mainBundle] resourcePath];
+    NSString *sourceFile, *destinationFile;
+    NSString *resourcesDir = [[NSBundle mainBundle] resourcePath];
     DLog(@"Creating necessary files");
     
-    // create an empty saves directory by deleting the previous one (saves are incompatible between releases)
+    // SAVES - just delete and overwrite
     if ([[NSFileManager defaultManager] fileExistsAtPath:SAVES_DIRECTORY()])
         [[NSFileManager defaultManager] removeItemAtPath:SAVES_DIRECTORY() error:NULL];
     [[NSFileManager defaultManager] createDirectoryAtPath:SAVES_DIRECTORY() withIntermediateDirectories:NO attributes:nil error:NULL];
     
-    // if the settings file is already present, we merge current preferences with the update
-    fileToCheck = [NSString stringWithFormat:@"%@/Settings/settings.plist",resDir];
+    // SETTINGS FILE - merge when present
+    NSString *baseSettingsFile = [[NSString alloc] initWithFormat:@"%@/Settings/settings.plist",resourcesDir];
     if ([[NSFileManager defaultManager] fileExistsAtPath:SETTINGS_FILE()]) {
         NSDictionary *settings = [[NSDictionary alloc] initWithContentsOfFile:SETTINGS_FILE()];
-        NSMutableDictionary *update = [[NSMutableDictionary alloc] initWithContentsOfFile:fileToCheck];
+        NSMutableDictionary *update = [[NSMutableDictionary alloc] initWithContentsOfFile:baseSettingsFile];
+        // the order of what adds what is important
         [update addEntriesFromDictionary:settings];
         [settings release];
         [update writeToFile:SETTINGS_FILE() atomically:YES];
         [update release];
     } else 
-        [[NSFileManager defaultManager] copyItemAtPath:fileToCheck toPath:SETTINGS_FILE() error:&err];
-    
-    // TODO: scrap this and always copy the bundled files; update exisising ones in some way
-    // if the teams are already present we merge the old teams, else we copy new teams
-    directoryToCheck = [NSString stringWithFormat:@"%@/Settings/Teams",resDir];
-    if ([[NSFileManager defaultManager] fileExistsAtPath:TEAMS_DIRECTORY()]) {
-        for (NSString *str in [[NSFileManager defaultManager] contentsOfDirectoryAtPath:directoryToCheck error:&err]) {
-            fileToCheck = [NSString stringWithFormat:@"%@/%@",TEAMS_DIRECTORY(),str];
-            fileToUpdate = [NSString stringWithFormat:@"%@/Settings/Teams/%@",resDir,str];
-            if ([[NSFileManager defaultManager] fileExistsAtPath:fileToCheck]) {
-                NSDictionary *team = [[NSDictionary alloc] initWithContentsOfFile:fileToCheck];
-                NSMutableDictionary *update = [[NSMutableDictionary alloc] initWithContentsOfFile:fileToUpdate];
-                [update addEntriesFromDictionary:team];
-                [team release];
-                [update writeToFile:fileToCheck atomically:YES];
-                [update release];
-            } else
-                [[NSFileManager defaultManager] copyItemAtPath:fileToUpdate toPath:fileToCheck error:&err];
+        [[NSFileManager defaultManager] copyItemAtPath:baseSettingsFile toPath:SETTINGS_FILE() error:NULL];
+    [baseSettingsFile release];
+
+    // TEAMS - update exisiting teams with new format
+    if ([[NSFileManager defaultManager] fileExistsAtPath:TEAMS_DIRECTORY()] == NO) {
+        [[NSFileManager defaultManager] createDirectoryAtPath:TEAMS_DIRECTORY()
+                                  withIntermediateDirectories:YES
+                                                   attributes:nil
+                                                        error:NULL];
+        // we copy teams only the first time because it's unlikely that newer ones are going to be added
+        NSString *baseTeamsDir = [[NSString alloc] initWithFormat:@"%@/Settings/Teams/",resourcesDir];
+        for (NSString *str in [[NSFileManager defaultManager] contentsOfDirectoryAtPath:baseTeamsDir error:NULL]) {
+            sourceFile = [baseTeamsDir stringByAppendingString:str];
+            destinationFile = [TEAMS_DIRECTORY() stringByAppendingString:str];
+            [[NSFileManager defaultManager] removeItemAtPath:destinationFile error:NULL];
+            [[NSFileManager defaultManager] copyItemAtPath:sourceFile toPath:destinationFile error:NULL];
         }
-    } else
-        [[NSFileManager defaultManager] copyItemAtPath:directoryToCheck toPath:TEAMS_DIRECTORY() error:&err];
-    
-    // TODO: scrap this and always copy the bundled files; update exisising ones in some way
-    // the same holds for schemes (but they're dictionaries containing arrays)
-    directoryToCheck = [NSString stringWithFormat:@"%@/Settings/Schemes",resDir];
-    if ([[NSFileManager defaultManager] fileExistsAtPath:SCHEMES_DIRECTORY()]) {
-        for (NSString *str in [[NSFileManager defaultManager] contentsOfDirectoryAtPath:directoryToCheck error:nil]) {
-            fileToCheck = [NSString stringWithFormat:@"%@/%@",SCHEMES_DIRECTORY(),str];
-            fileToUpdate = [NSString stringWithFormat:@"%@/Settings/Schemes/%@",resDir,str];
-            if ([[NSFileManager defaultManager] fileExistsAtPath:fileToCheck]) {
-                NSDictionary *scheme = [[NSDictionary alloc] initWithContentsOfFile:fileToCheck];
-                NSDictionary *update = [[NSDictionary alloc] initWithContentsOfFile:fileToUpdate];
-                if ([[update objectForKey:@"basic"] count] > [[scheme objectForKey:@"basic"] count] ||
-                    [[update objectForKey:@"gamemod"] count] > [[scheme objectForKey:@"gamemod"] count])
-                    [update writeToFile:fileToCheck atomically:YES];
-                [update release];
-                [scheme release];
-            } else
-                [[NSFileManager defaultManager] copyItemAtPath:fileToUpdate toPath:fileToCheck error:&err];
-        }
-    } else
-        [[NSFileManager defaultManager] copyItemAtPath:directoryToCheck toPath:SCHEMES_DIRECTORY() error:&err];
-    
-    // weapons are autoupdated at runtime but it's better to update then every new version
+        [baseTeamsDir release];
+    }
+    // TODO: is merge needed?
+
+    // SCHEMES - update old stuff and add new stuff
+    if ([[NSFileManager defaultManager] fileExistsAtPath:SCHEMES_DIRECTORY()] == NO)
+        [[NSFileManager defaultManager] createDirectoryAtPath:SCHEMES_DIRECTORY()
+                                  withIntermediateDirectories:YES
+                                                   attributes:nil
+                                                        error:NULL];
+    // TODO: do the merge if necessary
+    // we overwrite the default ones because it is likely that new modes are added every release
+    NSString *baseSchemesDir = [[NSString alloc] initWithFormat:@"%@/Settings/Schemes/",resourcesDir];
+    for (NSString *str in [[NSFileManager defaultManager] contentsOfDirectoryAtPath:baseSchemesDir error:NULL]) {
+        sourceFile = [baseSchemesDir stringByAppendingString:str];
+        destinationFile = [SCHEMES_DIRECTORY() stringByAppendingString:str];
+        [[NSFileManager defaultManager] removeItemAtPath:destinationFile error:NULL];
+        [[NSFileManager defaultManager] copyItemAtPath:sourceFile toPath:destinationFile error:NULL];
+    }
+    [baseSchemesDir release];
+
+    // WEAPONS - always overwrite
     if ([[NSFileManager defaultManager] fileExistsAtPath:WEAPONS_DIRECTORY()] == NO)
         [[NSFileManager defaultManager] createDirectoryAtPath:WEAPONS_DIRECTORY()
                                   withIntermediateDirectories:YES
                                                    attributes:nil
-                                                        error:&err];
+                                                        error:NULL];
     createWeaponNamed(@"Default", 0);
     createWeaponNamed(@"Crazy", 1);
-    createWeaponNamed(@"Pro mode", 2);
+    createWeaponNamed(@"Pro Mode", 2);
     createWeaponNamed(@"Shoppa", 3);
-    createWeaponNamed(@"Clean slate", 4);
+    createWeaponNamed(@"Clean Slate", 4);
     createWeaponNamed(@"Minefield", 5);
+    createWeaponNamed(@"Thinking with Portals", 6);
 
-    if (err != nil) 
-        DLog(@"%@", err);
-    else
-        DLog(@"Success");
+    DLog(@"Success");
 }
 
 #pragma mark -
--- a/project_files/HedgewarsMobile/Classes/MapConfigViewController.m	Sun Nov 14 20:37:55 2010 +0100
+++ b/project_files/HedgewarsMobile/Classes/MapConfigViewController.m	Sun Nov 14 15:06:02 2010 -0500
@@ -72,7 +72,7 @@
     // perform as if user clicked on an entry
     [self tableView:self.tableView didSelectRowAtIndexPath:theIndex];
     if (IS_NOT_POWERFUL() == NO)
-        [self.tableView scrollToRowAtIndexPath:theIndex atScrollPosition:UITableViewScrollPositionNone animated:YES];
+        [self.tableView scrollToRowAtIndexPath:theIndex atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
 }
 
 -(void) turnOffWidgets {
@@ -350,19 +350,6 @@
     self.staticMapCommand = staticmap;
     self.missionCommand = mission;
 
-    // nice animation for updating the table when appropriate (on iphone)
-    /*
-    if (IS_IPAD() == NO)
-        if (((oldPage == 0 || oldPage == 2) && (newPage == 1 || newPage == 3)) ||
-            ((oldPage == 1 || oldPage == 3) && (newPage == 0 || newPage == 2)) ||
-            ((oldPage == 1 && newPage == 3) || (oldPage == 3 || newPage == 1))) {
-            self.tableView.frame = CGRectMake(480, 0, 185, 276);
-            [UIView beginAnimations:@"moving in table" context:NULL];
-            self.tableView.frame = CGRectMake(295, 0, 185, 276);
-            [UIView commitAnimations];
-        }
-    */
-
     [self.tableView reloadData];
     [self updatePreview];
     oldPage = newPage;
@@ -385,13 +372,36 @@
     [string release];
     // remove a trailing "" element
     [themeArray removeLastObject];
-    NSArray *mapArray = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:MAPS_DIRECTORY() error:NULL];
-    NSArray *missionArray = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:MISSIONS_DIRECTORY() error:NULL];
+    NSArray *mapArrayFull = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:MAPS_DIRECTORY() error:NULL];
+    NSMutableArray *mapArray = [[NSMutableArray alloc] init];
+    for (NSString *str in mapArrayFull) {
+        CGSize imgSize = PSPNGSizeFromMetaData([MAPS_DIRECTORY() stringByAppendingFormat:@"%@/map.png",str]);
+        //DLog(@"%@ %f %f", str, imgSize.width, imgSize.height);
+        if (IS_NOT_POWERFUL() && imgSize.height > 1024.0f)
+            continue;
+        if (IS_IPAD() && imgSize.height > 1280.0f)
+            continue;
+        [mapArray addObject:str];
+    }
     
+    NSArray *missionArrayFull = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:MISSIONS_DIRECTORY() error:NULL];
+    NSMutableArray *missionArray = [[NSMutableArray alloc] init];
+    for (NSString *str in missionArrayFull) {
+        CGSize imgSize = PSPNGSizeFromMetaData([MISSIONS_DIRECTORY() stringByAppendingFormat:@"%@/map.png",str]);
+        //DLog(@"%@ %f %f", str, imgSize.width, imgSize.height);
+        if (IS_NOT_POWERFUL() && imgSize.height > 1024.0f)
+            continue;
+        if (IS_IPAD() && imgSize.height > 1280.0f)
+            continue;
+        [missionArray addObject:str];
+    }
     NSArray *array = [[NSArray alloc] initWithObjects:themeArray,mapArray,themeArray,missionArray,nil];
+    [missionArray release];
+    [themeArray release];
+    [mapArray release];
+
     self.dataSourceArray = array;
     [array release];
-    [themeArray release];
 }
 
 -(void) viewDidLoad {
@@ -469,11 +479,9 @@
 -(void) didReceiveMemoryWarning {
     self.dataSourceArray = nil;
 
-    self.previewButton = nil;
     self.tableView = nil;
     self.maxLabel = nil;
     self.sizeLabel = nil;
-    self.segmentedControl = nil;
     self.slider = nil;
 
     MSG_MEMCLEAN();
--- a/project_files/HedgewarsMobile/Classes/SchemeSettingsViewController.m	Sun Nov 14 20:37:55 2010 +0100
+++ b/project_files/HedgewarsMobile/Classes/SchemeSettingsViewController.m	Sun Nov 14 15:06:02 2010 -0500
@@ -20,7 +20,7 @@
 
 
 #import "SchemeSettingsViewController.h"
-#import "CommodityFunctions.h"
+#import "CreationChamber.h"
 #import "SingleSchemeViewController.h"
 
 @implementation SchemeSettingsViewController
@@ -81,12 +81,14 @@
     createSchemeNamed([fileName stringByDeletingPathExtension]);
 
     [self.listOfSchemes addObject:fileName];
-    [fileName release];
 
     // order the array alphabetically, so schemes will keep their position
     [self.listOfSchemes sortUsingSelector:@selector(compare:)];
+    [self.tableView reloadData];
 
-    [self.tableView reloadData];
+    NSInteger index = [self.listOfSchemes indexOfObject:fileName];
+    [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:index inSection:0] atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
+    [fileName release];
 }
 
 #pragma mark -
--- a/project_files/HedgewarsMobile/Classes/SchemeWeaponConfigViewController.h	Sun Nov 14 20:37:55 2010 +0100
+++ b/project_files/HedgewarsMobile/Classes/SchemeWeaponConfigViewController.h	Sun Nov 14 15:06:02 2010 -0500
@@ -31,13 +31,16 @@
 
     NSString *selectedScheme;
     NSString *selectedWeapon;
+
+    UISwitch *syncSwitch;
 }
 
-@property (nonatomic, retain) NSArray *listOfSchemes;
-@property (nonatomic, retain) NSArray *listOfWeapons;
+@property (nonatomic,retain) NSArray *listOfSchemes;
+@property (nonatomic,retain) NSArray *listOfWeapons;
 @property (nonatomic,retain) NSIndexPath *lastIndexPath_sc;
 @property (nonatomic,retain) NSIndexPath *lastIndexPath_we;
 @property (nonatomic,retain) NSString *selectedScheme;
 @property (nonatomic,retain) NSString *selectedWeapon;
+@property (nonatomic,retain) UISwitch *syncSwitch;
 
 @end
--- a/project_files/HedgewarsMobile/Classes/SchemeWeaponConfigViewController.m	Sun Nov 14 20:37:55 2010 +0100
+++ b/project_files/HedgewarsMobile/Classes/SchemeWeaponConfigViewController.m	Sun Nov 14 15:06:02 2010 -0500
@@ -24,7 +24,7 @@
 #import "SDL_uikitappdelegate.h"
 
 @implementation SchemeWeaponConfigViewController
-@synthesize listOfSchemes, listOfWeapons, lastIndexPath_sc, lastIndexPath_we, selectedScheme, selectedWeapon;
+@synthesize listOfSchemes, listOfWeapons, lastIndexPath_sc, lastIndexPath_we, selectedScheme, selectedWeapon, syncSwitch;
 
 -(BOOL) shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
     return rotationManager(interfaceOrientation);
@@ -81,27 +81,30 @@
 #pragma mark -
 #pragma mark Table view data source
 -(NSInteger) numberOfSectionsInTableView:(UITableView *)tableView {
-    return 2;
+    return 3;
 }
 
 -(NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
     if (section == 0)
         return [self.listOfSchemes count];
+    else if (section == 1)
+        return [self.listOfWeapons count];
     else
-        return [self.listOfWeapons count];
+        return 1;
 }
 
 // Customize the appearance of table view cells.
 -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
     static NSString *CellIdentifier = @"Cell";
     NSInteger row = [indexPath row];
+    NSInteger section = [indexPath section];
 
     UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
     if (cell == nil)
         cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
 
     cell.accessoryView = nil;
-    if ([indexPath section] == 0) {
+    if (0 == section) {
         cell.textLabel.text = [[self.listOfSchemes objectAtIndex:row] stringByDeletingPathExtension];
         NSString *str = [NSString stringWithFormat:@"%@/%@",SCHEMES_DIRECTORY(),[self.listOfSchemes objectAtIndex:row]];
         NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:str];
@@ -113,7 +116,7 @@
             [checkbox release];
             self.lastIndexPath_sc = indexPath;
         }
-    } else {
+    } else if (1 == section) {
         cell.textLabel.text = [[self.listOfWeapons objectAtIndex:row] stringByDeletingPathExtension];
         NSString *str = [NSString stringWithFormat:@"%@/%@",WEAPONS_DIRECTORY(),[self.listOfWeapons objectAtIndex:row]];
         NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:str];
@@ -125,8 +128,19 @@
             [checkbox release];
             self.lastIndexPath_we = indexPath;
         }
+    } else {
+        if (self.syncSwitch == nil) {
+            UISwitch *theSwitch = [[UISwitch alloc] init];
+            [theSwitch setOn:YES];
+            self.syncSwitch = theSwitch;
+            [theSwitch release];
+        }
+        cell.textLabel.text = IS_IPAD() ? NSLocalizedString(@"Sync Schemes",@"") : NSLocalizedString(@"Sync Schemes and Weapons",@"");
+        cell.detailTextLabel.text = IS_IPAD() ? nil : NSLocalizedString(@"Choosing a Scheme will select its associated Weapon",@"");
+        cell.detailTextLabel.adjustsFontSizeToFitWidth = YES;
+        cell.accessoryView = self.syncSwitch;
     }
-    
+
     cell.backgroundColor = [UIColor blackColor];
     cell.textLabel.textColor = UICOLOR_HW_YELLOW_TEXT;
     cell.detailTextLabel.textColor = [UIColor whiteColor];
@@ -142,8 +156,11 @@
     NSString *text;
     if (section == 0) 
         text = NSLocalizedString(@"Schemes",@"");
+    else if (section == 1)
+        text = NSLocalizedString(@"Weapons",@"");
     else
-        text = NSLocalizedString(@"Weapons",@"");
+        text = NSLocalizedString(@"Options",@"");
+
     UILabel *theLabel = createBlueLabel(text, frame);
     theLabel.center = CGPointMake(self.view.frame.size.width/2, 20);
 
@@ -177,6 +194,17 @@
         if ([indexPath section] == 0) {
             self.lastIndexPath_sc = indexPath;
             self.selectedScheme = [self.listOfSchemes objectAtIndex:newRow];
+            if (self.syncSwitch.on) {
+                for (NSString *str in self.listOfWeapons) {
+                    if ([str isEqualToString:self.selectedScheme]) {
+                        int index = [self.listOfSchemes indexOfObject:str];
+                        self.selectedWeapon = str;
+                        self.lastIndexPath_we = [NSIndexPath indexPathForRow:index inSection:1];
+                        [self.tableView reloadData];
+                        break;
+                    }
+                }
+            }
         } else {
             self.lastIndexPath_we = indexPath;
             self.selectedWeapon = [self.listOfWeapons objectAtIndex:newRow];
@@ -195,6 +223,7 @@
         self.lastIndexPath_we = nil;
         self.listOfSchemes = nil;
         self.listOfWeapons = nil;
+        self.syncSwitch = nil;
         MSG_MEMCLEAN();
     }
     [super didReceiveMemoryWarning];
@@ -207,6 +236,7 @@
     self.lastIndexPath_we = nil;
     self.selectedScheme = nil;
     self.selectedWeapon = nil;
+    self.syncSwitch = nil;
     MSG_DIDUNLOAD();
     [super viewDidUnload];
 }
@@ -219,6 +249,7 @@
     [lastIndexPath_we release];
     [selectedScheme release];
     [selectedWeapon release];
+    [syncSwitch release];
     [super dealloc];
 }
 
--- a/project_files/HedgewarsMobile/Classes/SingleSchemeViewController.m	Sun Nov 14 20:37:55 2010 +0100
+++ b/project_files/HedgewarsMobile/Classes/SingleSchemeViewController.m	Sun Nov 14 15:06:02 2010 -0500
@@ -153,6 +153,7 @@
          
             if (row == 0) {
                 editableCell.textField.text = self.schemeName;
+                editableCell.textField.font = [UIFont boldSystemFontOfSize:[UIFont labelFontSize]];
             } else {
                 editableCell.minimumCharacters = 0;
                 editableCell.textField.font = [UIFont systemFontOfSize:[UIFont labelFontSize]];
--- a/project_files/HedgewarsMobile/Classes/SingleWeaponViewController.m	Sun Nov 14 20:37:55 2010 +0100
+++ b/project_files/HedgewarsMobile/Classes/SingleWeaponViewController.m	Sun Nov 14 15:06:02 2010 -0500
@@ -150,6 +150,7 @@
         
         if (row == 0) {
             editableCell.textField.text = self.weaponName;
+            editableCell.textField.font = [UIFont boldSystemFontOfSize:[UIFont labelFontSize]];
         } else {
             editableCell.minimumCharacters = 0;
             editableCell.textField.font = [UIFont systemFontOfSize:[UIFont labelFontSize]];
--- a/project_files/HedgewarsMobile/Classes/TeamConfigViewController.m	Sun Nov 14 20:37:55 2010 +0100
+++ b/project_files/HedgewarsMobile/Classes/TeamConfigViewController.m	Sun Nov 14 15:06:02 2010 -0500
@@ -125,7 +125,12 @@
             [cell addSubview:squareButton];
             [squareButton release];
 
-            UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(12+88+6+36, 10, 103, 25)];
+            NSInteger length;
+            if (IS_IPAD())
+                length = 103;
+            else
+                length = 285;
+            UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(12+88+6+36, 10, length, 25)];
             label.textAlignment = UITextAlignmentLeft;
             label.minimumFontSize = 11;
             label.adjustsFontSizeToFitWidth = YES;
--- a/project_files/HedgewarsMobile/Classes/TeamSettingsViewController.m	Sun Nov 14 20:37:55 2010 +0100
+++ b/project_files/HedgewarsMobile/Classes/TeamSettingsViewController.m	Sun Nov 14 15:06:02 2010 -0500
@@ -20,8 +20,8 @@
 
 
 #import "TeamSettingsViewController.h"
+#import "CreationChamber.h"
 #import "SingleTeamViewController.h"
-#import "CommodityFunctions.h"
 
 @implementation TeamSettingsViewController
 @synthesize listOfTeams;
@@ -85,12 +85,14 @@
     createTeamNamed([fileName stringByDeletingPathExtension]);
 
     [self.listOfTeams addObject:fileName];
-    [fileName release];
 
     // order the array alphabetically, so teams will keep their position
     [self.listOfTeams sortUsingSelector:@selector(compare:)];
+    [self.tableView reloadData];
 
-    [self.tableView reloadData];
+    NSInteger index = [self.listOfTeams indexOfObject:fileName];
+    [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:index inSection:0] atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
+    [fileName release];
 }
 
 #pragma mark -
--- a/project_files/HedgewarsMobile/Classes/WeaponSettingsViewController.m	Sun Nov 14 20:37:55 2010 +0100
+++ b/project_files/HedgewarsMobile/Classes/WeaponSettingsViewController.m	Sun Nov 14 15:06:02 2010 -0500
@@ -20,7 +20,7 @@
 
 
 #import "WeaponSettingsViewController.h"
-#import "CommodityFunctions.h"
+#import "CreationChamber.h"
 #import "SingleWeaponViewController.h"
 
 @implementation WeaponSettingsViewController
@@ -82,12 +82,14 @@
     createWeaponNamed([fileName stringByDeletingPathExtension], 0);
 
     [self.listOfWeapons addObject:fileName];
-    [fileName release];
 
     // order the array alphabetically, so schemes will keep their position
     [self.listOfWeapons sortUsingSelector:@selector(compare:)];
+    [self.tableView reloadData];
 
-    [self.tableView reloadData];
+    NSInteger index = [self.listOfWeapons indexOfObject:fileName];
+    [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:index inSection:0] atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
+    [fileName release];
 }
 
 #pragma mark -
--- a/project_files/HedgewarsMobile/Hedgewars.xcodeproj/project.pbxproj	Sun Nov 14 20:37:55 2010 +0100
+++ b/project_files/HedgewarsMobile/Hedgewars.xcodeproj/project.pbxproj	Sun Nov 14 15:06:02 2010 -0500
@@ -25,6 +25,7 @@
 		1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
 		28FD15000DC6FC520079059D /* OpenGLES.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 28FD14FF0DC6FC520079059D /* OpenGLES.framework */; };
 		28FD15080DC6FC5B0079059D /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 28FD15070DC6FC5B0079059D /* QuartzCore.framework */; settings = {ATTRIBUTES = (Required, ); }; };
+		61006F95128DE31F00EBA7F7 /* CreationChamber.m in Sources */ = {isa = PBXBuildFile; fileRef = 61006F94128DE31F00EBA7F7 /* CreationChamber.m */; };
 		610D5FB21270E2660033333A /* Icon-Small@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 61F7A43411E290650040BA66 /* Icon-Small@2x.png */; };
 		610D5FB31270E26C0033333A /* Icon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 61F7A43611E290650040BA66 /* Icon@2x.png */; };
 		611D9BFB12497E9800008271 /* SavedGamesViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 611D9BF912497E9800008271 /* SavedGamesViewController.m */; };
@@ -730,6 +731,8 @@
 		28FD14FF0DC6FC520079059D /* OpenGLES.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGLES.framework; path = System/Library/Frameworks/OpenGLES.framework; sourceTree = SDKROOT; };
 		28FD15070DC6FC5B0079059D /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
 		32CA4F630368D1EE00C91783 /* Hedgewars_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Hedgewars_Prefix.pch; sourceTree = "<group>"; };
+		61006F93128DE31F00EBA7F7 /* CreationChamber.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CreationChamber.h; path = Classes/CreationChamber.h; sourceTree = "<group>"; };
+		61006F94128DE31F00EBA7F7 /* CreationChamber.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CreationChamber.m; path = Classes/CreationChamber.m; sourceTree = "<group>"; };
 		611D9BF812497E9800008271 /* SavedGamesViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SavedGamesViewController.h; sourceTree = "<group>"; };
 		611D9BF912497E9800008271 /* SavedGamesViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SavedGamesViewController.m; sourceTree = "<group>"; };
 		611D9BFA12497E9800008271 /* SavedGamesViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = SavedGamesViewController.xib; path = ../Resources/SavedGamesViewController.xib; sourceTree = "<group>"; };
@@ -1023,6 +1026,8 @@
 				6165922D11CA9BD500D6E256 /* UIImageExtra.m */,
 				61D2059F127CDD1100ABD83E /* ObjcExports.h */,
 				61D205A0127CDD1100ABD83E /* ObjcExports.m */,
+				61006F93128DE31F00EBA7F7 /* CreationChamber.h */,
+				61006F94128DE31F00EBA7F7 /* CreationChamber.m */,
 			);
 			name = "Other Sources";
 			sourceTree = "<group>";
@@ -2284,6 +2289,7 @@
 				61DE8F221257EB1100B80214 /* AmmoMenuViewController.m in Sources */,
 				61399013125D19C0003C2DC0 /* uMobile.pas in Sources */,
 				61D205A1127CDD1100ABD83E /* ObjcExports.m in Sources */,
+				61006F95128DE31F00EBA7F7 /* CreationChamber.m in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
--- a/project_files/HedgewarsMobile/Resources/Settings/Schemes/Barrel Mayhem.plist	Sun Nov 14 20:37:55 2010 +0100
+++ b/project_files/HedgewarsMobile/Resources/Settings/Schemes/Barrel Mayhem.plist	Sun Nov 14 15:06:02 2010 -0500
@@ -41,6 +41,7 @@
 		<false/>
 		<false/>
 		<false/>
+		<false/>
 	</array>
 </dict>
 </plist>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/HedgewarsMobile/Resources/Settings/Schemes/Clean Slate.plist	Sun Nov 14 15:06:02 2010 -0500
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>basic</key>
+	<array>
+		<integer>100</integer>
+		<integer>45</integer>
+		<integer>100</integer>
+		<integer>15</integer>
+		<integer>47</integer>
+		<integer>5</integer>
+		<integer>5</integer>
+		<integer>35</integer>
+		<integer>25</integer>
+		<integer>3</integer>
+		<integer>4</integer>
+		<integer>0</integer>
+		<integer>2</integer>
+	</array>
+	<key>gamemod</key>
+	<array>
+		<false/>
+		<false/>
+		<false/>
+		<false/>
+		<false/>
+		<false/>
+		<true/>
+		<false/>
+		<false/>
+		<false/>
+		<false/>
+		<true/>
+		<false/>
+		<false/>
+		<false/>
+		<false/>
+		<false/>
+		<false/>
+		<true/>
+		<true/>
+		<false/>
+		<false/>
+	</array>
+</dict>
+</plist>
--- a/project_files/HedgewarsMobile/Resources/Settings/Schemes/Default.plist	Sun Nov 14 20:37:55 2010 +0100
+++ b/project_files/HedgewarsMobile/Resources/Settings/Schemes/Default.plist	Sun Nov 14 15:06:02 2010 -0500
@@ -41,6 +41,7 @@
 		<false/>
 		<false/>
 		<false/>
+		<false/>
 	</array>
 </dict>
 </plist>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/HedgewarsMobile/Resources/Settings/Schemes/Fort Mode.plist	Sun Nov 14 15:06:02 2010 -0500
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>basic</key>
+	<array>
+		<integer>100</integer>
+		<integer>45</integer>
+		<integer>100</integer>
+		<integer>15</integer>
+		<integer>47</integer>
+		<integer>5</integer>
+		<integer>5</integer>
+		<integer>35</integer>
+		<integer>25</integer>
+		<integer>3</integer>
+		<integer>0</integer>
+		<integer>0</integer>
+		<integer>0</integer>
+	</array>
+	<key>gamemod</key>
+	<array>
+		<false/>
+		<false/>
+		<true/>
+		<true/>
+		<false/>
+		<false/>
+		<false/>
+		<false/>
+		<false/>
+		<false/>
+		<true/>
+		<true/>
+		<false/>
+		<false/>
+		<false/>
+		<false/>
+		<false/>
+		<false/>
+		<false/>
+		<false/>
+		<false/>
+		<false/>
+	</array>
+</dict>
+</plist>
--- a/project_files/HedgewarsMobile/Resources/Settings/Schemes/Minefield.plist	Sun Nov 14 20:37:55 2010 +0100
+++ b/project_files/HedgewarsMobile/Resources/Settings/Schemes/Minefield.plist	Sun Nov 14 15:06:02 2010 -0500
@@ -41,6 +41,7 @@
 		<false/>
 		<false/>
 		<false/>
+		<false/>
 	</array>
 </dict>
 </plist>
--- a/project_files/HedgewarsMobile/Resources/Settings/Schemes/Pro Mode.plist	Sun Nov 14 20:37:55 2010 +0100
+++ b/project_files/HedgewarsMobile/Resources/Settings/Schemes/Pro Mode.plist	Sun Nov 14 15:06:02 2010 -0500
@@ -41,6 +41,7 @@
 		<false/>
 		<false/>
 		<false/>
+		<false/>
 	</array>
 </dict>
 </plist>
--- a/project_files/HedgewarsMobile/Resources/Settings/Schemes/Shoppa.plist	Sun Nov 14 20:37:55 2010 +0100
+++ b/project_files/HedgewarsMobile/Resources/Settings/Schemes/Shoppa.plist	Sun Nov 14 15:06:02 2010 -0500
@@ -41,6 +41,7 @@
 		<false/>
 		<false/>
 		<false/>
+		<false/>
 	</array>
 </dict>
 </plist>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/HedgewarsMobile/Resources/Settings/Schemes/Thinking with Portals.plist	Sun Nov 14 15:06:02 2010 -0500
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>basic</key>
+	<array>
+		<integer>100</integer>
+		<integer>45</integer>
+		<integer>100</integer>
+		<integer>15</integer>
+		<integer>47</integer>
+		<integer>5</integer>
+		<integer>2</integer>
+		<integer>25</integer>
+		<integer>25</integer>
+		<integer>4</integer>
+		<integer>5</integer>
+		<integer>0</integer>
+		<integer>5</integer>
+	</array>
+	<key>gamemod</key>
+	<array>
+		<false/>
+		<false/>
+		<false/>
+		<false/>
+		<false/>
+		<false/>
+		<false/>
+		<false/>
+		<false/>
+		<false/>
+		<true/>
+		<true/>
+		<false/>
+		<false/>
+		<false/>
+		<false/>
+		<false/>
+		<false/>
+		<false/>
+		<false/>
+		<false/>
+		<false/>
+	</array>
+</dict>
+</plist>
--- a/project_files/HedgewarsMobile/Resources/Settings/Schemes/Timeless.plist	Sun Nov 14 20:37:55 2010 +0100
+++ b/project_files/HedgewarsMobile/Resources/Settings/Schemes/Timeless.plist	Sun Nov 14 15:06:02 2010 -0500
@@ -41,6 +41,7 @@
 		<false/>
 		<false/>
 		<true/>
+		<false/>
 	</array>
 </dict>
 </plist>
--- a/project_files/HedgewarsMobile/Resources/Settings/Schemes/Tunnel Hogs.plist	Sun Nov 14 20:37:55 2010 +0100
+++ b/project_files/HedgewarsMobile/Resources/Settings/Schemes/Tunnel Hogs.plist	Sun Nov 14 15:06:02 2010 -0500
@@ -41,6 +41,7 @@
 		<false/>
 		<false/>
 		<false/>
+		<false/>
 	</array>
 </dict>
 </plist>
--- a/project_files/HedgewarsMobile/Resources/Settings/iFrontend/gameFlags_en.plist	Sun Nov 14 20:37:55 2010 +0100
+++ b/project_files/HedgewarsMobile/Resources/Settings/iFrontend/gameFlags_en.plist	Sun Nov 14 15:06:02 2010 -0500
@@ -170,5 +170,13 @@
 		<key>title</key>
 		<string>Per Hedgehog Ammo</string>
 	</dict>
+	<dict>
+		<key>description</key>
+		<string>Wind will not affect weapons</string>
+		<key>image</key>
+		<string>NoWind</string>
+		<key>title</key>
+		<string>Disable Wind</string>
+	</dict>
 </array>
 </plist>
Binary file share/hedgewars/Data/Graphics/AirDrill.png has changed
Binary file share/hedgewars/Data/Graphics/BulletHit.png has changed
Binary file share/hedgewars/Data/Graphics/BulletHit.sifz has changed
Binary file share/hedgewars/Data/Graphics/NapalmBomb.png has changed
--- a/share/hedgewars/Data/Locale/en.txt	Sun Nov 14 20:37:55 2010 +0100
+++ b/share/hedgewars/Data/Locale/en.txt	Sun Nov 14 15:06:02 2010 -0500
@@ -50,6 +50,7 @@
 00:47=Sticky Mine
 00:48=Hammer
 00:49=Resurrector
+00:50=Drill Strike
 
 01:00=Let's fight!
 01:01=Round draw
@@ -431,6 +432,7 @@
 03:47=Stick these somewhere useful!
 03:48=It's Hammer time!
 03:49=Does what you guess
+03:50=Moles fan
 
 ; Weapon Descriptions (use | as line breaks)
 04:00=Attack your enemies using a simple grenade.|It will explode once its timer reaches zero.|1-5: Set grenade's timer|Attack: Hold to throw with more power
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/share/hedgewars/Data/Maps/FlightJoust/map.cfg	Sun Nov 14 15:06:02 2010 -0500
@@ -0,0 +1,2 @@
+Nature
+4
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/share/hedgewars/Data/Maps/FlightJoust/map.lua	Sun Nov 14 15:06:02 2010 -0500
@@ -0,0 +1,79 @@
+local hogs = {}
+local spawncrate = 0
+
+function mapM_(func, tbl)
+    for i,v in pairs(tbl) do
+        func(v)
+    end 
+end
+
+function map(func, tbl)
+    local newtbl = {}
+    for i,v in pairs(tbl) do
+        newtbl[i] = func(v)
+    end 
+    return newtbl
+end
+
+function filter(func, tbl)
+    local newtbl = {}
+    for i,v in pairs(tbl) do
+        if func(v) then
+            table.insert(newtbl, v)
+        end
+    end
+    return newtbl
+end
+
+function onGameInit()
+    GameFlags = gfSolidLand + gfDivideTeams
+    TurnTime = 10000
+    CaseFreq = 0 
+    MinesNum = 0 
+    Explosives = 0 
+    Delay = 500 
+    SuddenDeathTurns = 99999 -- "disable" sudden death
+    Theme = Compost
+end
+
+function onGameStart()
+    local offset = 50
+    local team1hh = filter(function(h) return GetHogClan(h) == 0 end, hogs)
+    local team2hh = filter(function(h) return GetHogClan(h) == 1 end, hogs)
+
+    for i,h in ipairs(team1hh) do
+        SetGearPosition(h, 250+(i-1)*offset, 1000)
+    end
+    for i,h in ipairs(team2hh) do
+        SetGearPosition(h, 3500-(i-1)*offset, 1000)
+    end
+
+    SpawnHealthCrate(1800, 1150)
+end
+
+function onAmmoStoreInit()
+    SetAmmo(amRCPlane, 9, 0, 0, 0)
+    SetAmmo(amSkip, 9, 0, 0, 0)
+end
+
+function onGearAdd(gear)
+    if GetGearType(gear) == gtRCPlane then
+        SetTimer(gear,60000)
+    end 
+    if GetGearType(gear) == gtHedgehog then
+        table.insert(hogs, gear)
+    end 
+end
+
+function onGameTick()
+    if (TurnTimeLeft == 9999 and spawncrate == 1) then
+        SpawnHealthCrate(1800, 1150)
+        spawncrate = 0
+    end
+end
+
+function onGearDelete(gear)
+    if GetGearType(gear) == gtCase then
+        spawncrate = 1
+    end
+end
Binary file share/hedgewars/Data/Maps/FlightJoust/map.png has changed
Binary file share/hedgewars/Data/Maps/FlightJoust/preview.png has changed