update webgl branch webgl
authorkoda
Tue, 02 Apr 2013 21:00:57 +0200
branchwebgl
changeset 8833 c13ebed437cb
parent 8450 404ddce27b23 (current diff)
parent 8830 343d3f0d6a86 (diff)
child 8836 7a474fcc343d
update webgl branch
.hgignore
CMakeLists.txt
QTfrontend/CMakeLists.txt
QTfrontend/game.cpp
QTfrontend/gameuiconfig.cpp
QTfrontend/hwform.cpp
QTfrontend/hwform.h
QTfrontend/net/newnetclient.cpp
QTfrontend/net/newnetclient.h
QTfrontend/ui/dialog/input_password.cpp
QTfrontend/ui/page/pagemain.cpp
QTfrontend/ui/widget/about.cpp
QTfrontend/ui/widget/flowlayout.cpp
QTfrontend/ui/widget/flowlayout.h
cmake_modules/FindFreepascal.cmake
cmake_modules/FindSDL_Extras.cmake
gameServer/Actions.hs
gameServer/CMakeLists.txt
gameServer/HWProtoInRoomState.hs
hedgewars/CMakeLists.txt
hedgewars/GSHandlers.inc
hedgewars/SDLh.pas
hedgewars/hwengine.pas
hedgewars/uAI.pas
hedgewars/uAIAmmoTests.pas
hedgewars/uAIMisc.pas
hedgewars/uChat.pas
hedgewars/uCommandHandlers.pas
hedgewars/uConsts.pas
hedgewars/uGame.pas
hedgewars/uGears.pas
hedgewars/uGearsHedgehog.pas
hedgewars/uGearsList.pas
hedgewars/uGearsRender.pas
hedgewars/uGearsUtils.pas
hedgewars/uIO.pas
hedgewars/uInputHandler.pas
hedgewars/uLandGraphics.pas
hedgewars/uLandObjects.pas
hedgewars/uPhysFSLayer.pas
hedgewars/uRender.pas
hedgewars/uScript.pas
hedgewars/uSound.pas
hedgewars/uStats.pas
hedgewars/uTeams.pas
hedgewars/uTypes.pas
hedgewars/uUtils.pas
hedgewars/uVariables.pas
hedgewars/uWorld.pas
misc/hedgewars.desktop
misc/libphysfs/CMakeLists.txt
misc/physfs/Android.mk
misc/physfs/CMakeLists.txt
misc/physfs/Xcode/Physfs.xcodeproj/project.pbxproj
misc/physfs/Xcode/Physfs_Prefix.pch
misc/physfs/extras/hwpacksmounter.c
misc/physfs/extras/hwpacksmounter.h
misc/physfs/extras/physfslualoader.c
misc/physfs/extras/physfsrwops.c
misc/physfs/extras/physfsrwops.h
misc/physfs/src/Android.mk
misc/physfs/src/archiver_dir.c
misc/physfs/src/archiver_grp.c
misc/physfs/src/archiver_hog.c
misc/physfs/src/archiver_iso9660.c
misc/physfs/src/archiver_lzma.c
misc/physfs/src/archiver_mvl.c
misc/physfs/src/archiver_qpak.c
misc/physfs/src/archiver_unpacked.c
misc/physfs/src/archiver_wad.c
misc/physfs/src/archiver_zip.c
misc/physfs/src/physfs.c
misc/physfs/src/physfs.h
misc/physfs/src/physfs_byteorder.c
misc/physfs/src/physfs_casefolding.h
misc/physfs/src/physfs_internal.h
misc/physfs/src/physfs_miniz.h
misc/physfs/src/physfs_platforms.h
misc/physfs/src/physfs_unicode.c
misc/physfs/src/platform_beos.cpp
misc/physfs/src/platform_macosx.c
misc/physfs/src/platform_posix.c
misc/physfs/src/platform_unix.c
misc/physfs/src/platform_windows.c
project_files/Android-build/SDL-android-project/libs/armeabi/libjnidispatch.so
project_files/Android-build/SDL-android-project/libs/jna.jar
share/CMakeLists.txt
share/hedgewars/CMakeLists.txt
--- a/.hgignore	Wed Feb 20 02:21:58 2013 +0100
+++ b/.hgignore	Tue Apr 02 21:00:57 2013 +0200
@@ -31,7 +31,8 @@
 glob:misc/liblua/Xcode/build/
 glob:misc/libfreetype/Xcode/build/
 glob:misc/libfreetype/Xcode-iOS/build/
-glob:misc/physfs/Xcode/build/
+glob:misc/libphysfs/Xcode/build/
+glob:misc/libphyslayer/Xcode/build/
 glob:moc_*.cxx_parameters
 relre:^release\/
 glob:*.log
@@ -60,4 +61,7 @@
 glob:*.depends
 glob:tools/build_windows_koda.bat
 glob:share/hedgewars/Data/misc/hwengine.desktop
-
+glob:*.exe
+glob:_CPack_Packages/
+glob:version_info.txt
+glob:*.tar.*
--- a/CMakeLists.txt	Wed Feb 20 02:21:58 2013 +0100
+++ b/CMakeLists.txt	Tue Apr 02 21:00:57 2013 +0200
@@ -2,77 +2,97 @@
 
 #initialise cmake environment
 cmake_minimum_required(VERSION 2.6.0)
+if(CMAKE_VERSION VERSION_LESS "2.8")
+    set(WARNING "WARNING: ")
+    set(allow_parse_args FALSE)
+else()
+    set(WARNING WARNING)
+    set(allow_parse_args TRUE)
+endif()
 foreach(hwpolicy CMP0003 CMP0012 CMP0017)
     if(POLICY ${hwpolicy})
         cmake_policy(SET ${hwpolicy} NEW)
     endif()
 endforeach()
-#use available modules, fallback to ours if not present (CMP0017 helps)
-set(CMAKE_MODULE_PATH "${CMAKE_ROOT}/Modules" "${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules")
+
+set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake_modules")
 
 
-#usually this is set at release time
-option(NOREVISION "Build Hedgewars without revision information [default: off]" OFF)
+#possible cmake configuration
+option(NOSERVER "Disable gameServer build (off)]" OFF)
+option(NOPNG "Disable screenshoot compression (off)" OFF)
+option(NOVIDEOREC "Disable video recording (off)" OFF)
+
+#set this to ON when 2.1.0 becomes more widespread (and only for linux)
+option(SYSTEM_PHYSFS "Use system physfs (off)" OFF)
 
-#set other default values
-option(NOSERVER "Disable gameServer build [default: auto]" OFF)
-option(NOPNG "Disable screenshoot compression [default: auto]" OFF)
-option(NOVIDEOREC "Disable video recording [default: auto]" OFF)
+option(LIBENGINE "Enable hwengine library (off)" OFF)
+option(ANDROID "Enable Android build (off)" OFF)
 
+if(UNIX AND NOT APPLE)
+    option(MINIMAL_FLAGS "Respect system flags as much as possible (off)" OFF)
+else()
+    option(NOAUTOUPDATE "Disable OS X Sparkle update checking" OFF)
+endif()
 
 option(WEBGL "Enable WebGL build (implies NOPASCAL) [default: off]" OFF)
 option(NOPASCAL "Compile hwengine as native C [default: off]" ${WEBGL})
-option(LIBENGINE "Enable hwengine library [default: off]" OFF)
+option(GL2 "Enable OpenGL 2 rendering [default: off]" OFF)
 
-option(ANDROID "Enable Android build [default: off]" OFF)
-option(NOAUTOUPDATE "Disable OS X Sparkle update checking" OFF)
-option(MINIMAL_FLAGS "Respect system flags as much as possible [default: off]" OFF)
-option(GL2 "Enable OpenGL 2 rendering [default: off]" OFF)
 set(FPFLAGS "" CACHE STRING "Additional Freepascal flags")
 set(GHFLAGS "" CACHE STRING "Additional Haskell flags")
 if(UNIX AND NOT APPLE)
     set(DATA_INSTALL_DIR "share/hedgewars" CACHE STRING "Resource folder path")
 endif()
 
-#detect Mercurial revision (if present)
-if(NOT ${NOREVISION})
-    set(default_build_type "DEBUG")
-    set(version_suffix "-development_version")
-    set(HW_DEV true)
-    find_program(HGCOMMAND hg)
-    if(HGCOMMAND AND (EXISTS ${CMAKE_SOURCE_DIR}/.hg))
-        execute_process(COMMAND ${HGCOMMAND} identify -in
-                        WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
-                        OUTPUT_VARIABLE internal_version
-                        ERROR_QUIET
-                    )
-        #check local repo status
-        string(REGEX REPLACE "[^+]" "" HGCHANGED ${internal_version})
+
+#detect Mercurial revision and init rev/hash information
+find_program(HGCOMMAND hg)
+if(HGCOMMAND AND (EXISTS ${CMAKE_SOURCE_DIR}/.hg))
+    execute_process(COMMAND ${HGCOMMAND} identify -in
+                    WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
+                    OUTPUT_VARIABLE internal_version
+                    ERROR_QUIET
+                )
+    #check local repo status
+    string(REGEX REPLACE "[^+]" "" HGCHANGED ${internal_version})
+    string(REGEX REPLACE "[0-9a-zA-Z]+(.*) ([0-9]+)(.*)" "\\2" HEDGEWARS_REVISION ${internal_version})
+    string(REGEX REPLACE "([0-9a-zA-Z]+)(.*) [0-9]+(.*)" "\\1" HEDGEWARS_HASH ${internal_version})
 
-        string(REGEX REPLACE "[0-9a-zA-Z]+(.*) ([0-9]+)(.*)" "\\2" revision_number ${internal_version})
-        string(REGEX REPLACE "([0-9a-zA-Z]+)(.*) [0-9]+(.*)" "\\1" revision_hash ${internal_version})
-
-        message(STATUS "Building revision ${revision_number} from hash ${revision_hash} ${HGCHANGED}")
-        if(HGCHANGED)
-            MESSAGE(WARNING "Notice: you have uncommitted changes in your repository")
-        endif()
-        set(version_suffix "-${revision_number}${HGCHANGED}")
+    if(HGCHANGED)
+        message(${WARNING} "You have uncommitted changes in your repository!")
     endif()
-else(NOT ${NOREVISION})
+    #let's assume that if you have hg you might be interested in debugging
+    set(default_build_type "DEBUG")
+    #write down hash and rev for easy picking should hg be missing
+    file(WRITE "${CMAKE_SOURCE_DIR}/share/version_info.txt" "Hedgewars versioning information, do not modify\nrev ${HEDGEWARS_REVISION}\nhash ${HEDGEWARS_HASH}\n")
+else()
     set(default_build_type "RELEASE")
-    set(HWDEV false)
-    message(STATUS "Building distributable version")
-endif(NOT ${NOREVISION})
+    # when compiling outside rev control, fetch revision and hash information from version_info.txt
+    find_file(version_info version_info.txt PATH ${CMAKE_SOURCE_DIR}/share)
+    if(version_info)
+        file(STRINGS ${version_info} internal_version REGEX "rev")
+        string(REGEX REPLACE "rev ([0-9]*)" "\\1" HEDGEWARS_REVISION ${internal_version})
+        file(STRINGS ${version_info} internal_version REGEX "hash")
+        string(REGEX REPLACE "hash ([a-zA-Z0-9]*)" "\\1" HEDGEWARS_HASH ${internal_version})
+    else()
+        message(${WARNING} "${CMAKE_SOURCE_DIR}/share/version_info.txt not found, revision information "
+                           "will be incorrect!!! Contact your source provider to fix this!")
+        set(HEDGEWARS_REVISION "0000")
+        set(HEDGEWARS_HASH "unknown")
+    endif()
+endif()
 
 
 #versioning
 set(CPACK_PACKAGE_VERSION_MAJOR 0)
 set(CPACK_PACKAGE_VERSION_MINOR 9)
-set(CPACK_PACKAGE_VERSION_PATCH 19${version_suffix})
+set(CPACK_PACKAGE_VERSION_PATCH 19)
 set(HEDGEWARS_PROTO_VER 44)
 set(HEDGEWARS_VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}")
 set(required_clang_version 3.0)
 
+message(STATUS "Building ${HEDGEWARS_VERSION}-r${HEDGEWARS_REVISION} (${HEDGEWARS_HASH})")
 
 if (${NOPASCAL})
     find_package(Clang)
@@ -85,27 +105,36 @@
 endif(${NOPASCAL})
 
 
+
+#where to build libs and bins
 set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
 set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
 
+#resource paths
 if(UNIX AND NOT APPLE)
     set(target_binary_install_dir "bin")
     set(target_library_install_dir "lib")
-    set(SHAREPATH "${DATA_INSTALL_DIR}/")
+
+    string(SUBSTRING "${DATA_INSTALL_DIR}" 0 1 sharepath_start)
+    if (NOT (${sharepath_start} MATCHES "/"))
+        set(HEDGEWARS_DATADIR "${CMAKE_INSTALL_PREFIX}/${DATA_INSTALL_DIR}/")
+    else()
+        set(HEDGEWARS_DATADIR "${DATA_INSTALL_DIR}/")
+    endif()
+    set(HEDGEWARS_FULL_DATADIR "${HEDGEWARS_DATADIR}")
 else()
     set(target_binary_install_dir "./")
 
     if(APPLE)
+        set(target_library_install_dir "../Frameworks/")
         set(CMAKE_INSTALL_PREFIX "Hedgewars.app/Contents/MacOS/")
-        set(SHAREPATH "../Resources/")
-        set(target_library_install_dir "../Frameworks/")
-    else()
-        if(WIN32)
-            set(target_library_install_dir "./")
-            set(SHAREPATH "./")
-            set(CMAKE_PREFIX_PATH "${CMAKE_SOURCE_DIR}/misc/winutils/")
-            link_directories("${EXECUTABLE_OUTPUT_PATH}" "${CMAKE_SOURCE_DIR}/misc/winutils/bin")
-        endif(WIN32)
+        set(HEDGEWARS_DATADIR "../Resources/")
+        set(HEDGEWARS_FULL_DATADIR "/Applications/${CMAKE_INSTALL_PREFIX}/${HEDGEWARS_DATADIR}")
+    elseif(WIN32)
+        set(target_library_install_dir "./")
+        set(HEDGEWARS_DATADIR "./")
+        set(HEDGEWARS_FULL_DATADIR "${CMAKE_INSTALL_PREFIX}/")
+        link_directories("${EXECUTABLE_OUTPUT_PATH}" "${CMAKE_SOURCE_DIR}/misc/winutils/bin")
     endif()
 endif()
 
@@ -127,7 +156,7 @@
         if(NOT minimum_macosx_version)
             message(FATAL_ERROR "sw_vers not found! Need explicit MACOSX_DEPLOYMENT_TARGET variable set")
         else()
-            message(WARNING "sw_vers not found! Fallback to MACOSX_DEPLOYMENT_TARGET variable")
+            message(${WARNING} "sw_vers not found! Fallback to MACOSX_DEPLOYMENT_TARGET variable")
             set(current_macosx_version ${minimum_macosx_version})
         endif()
     endif()
@@ -181,11 +210,6 @@
     list(APPEND pascal_flags "-Ff~/Library/Frameworks")
     #set deployment target
     list(APPEND pascal_flags "-k-macosx_version_min" "-k${minimum_macosx_version}" "-XR${CMAKE_OSX_SYSROOT}")
-
-    #silly libav that always brings in VideoDecoderAcceleration, avaible only from 10.6.3
-    if(NOT NOVIDEOREC AND ${minimum_macosx_version} VERSION_LESS "10.6")
-        set(WARNING "Video recording support before OS X 10.6 is experimental")
-    endif()
 endif(APPLE)
 
 
@@ -193,13 +217,14 @@
 if (CMAKE_BUILD_TYPE)
     string (TOUPPER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE)
     if ( NOT( (CMAKE_BUILD_TYPE MATCHES "RELEASE") OR (CMAKE_BUILD_TYPE MATCHES "DEBUG") ) )
-        set (CMAKE_BUILD_TYPE ${default_build_type} CACHE STRING "Choose the build type, options are: Debug Release." FORCE)
+        set (CMAKE_BUILD_TYPE ${default_build_type} CACHE STRING "Build type (Debug/Release)" FORCE)
         message (STATUS "Unknown build type, using default (${default_build_type})")
     endif ()
 else (CMAKE_BUILD_TYPE)
-    set (CMAKE_BUILD_TYPE ${default_build_type} CACHE STRING "Choose the build type, options are: Debug Release." FORCE)
+    set (CMAKE_BUILD_TYPE ${default_build_type} CACHE STRING "Build type (Debug/Release)" FORCE)
 endif (CMAKE_BUILD_TYPE)
 
+
 #set default flags values for all projects (unless MINIMAL_FLAGS is true)
 if(NOT ${MINIMAL_FLAGS})
     set(CMAKE_C_FLAGS "-pipe ${CMAKE_C_FLAGS}")
@@ -216,39 +241,67 @@
     set(CMAKE_CXX_FLAGS_DEBUG "-Wall -DDEBUG")
 endif()
 
+#TODO: find out why we need this...
+include(CheckCCompilerFlag)
+set(CMAKE_REQUIRED_FLAGS "-Wl,-z -Wl,noexecstack")
+check_c_compiler_flag("" HAVE_NOEXECSTACK) #empty because we are testing a linker flag
+if(HAVE_NOEXECSTACK)
+    list(APPEND pascal_flags "-k-z" "-knoexecstack")
+    if(NOT ${MINIMAL_FLAGS})
+        set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${CMAKE_REQUIRED_FLAGS}")
+    endif()
+endif()
+unset(CMAKE_REQUIRED_FLAGS)
+
 #parse additional parameters
 if(FPFLAGS OR GHFLAGS)
-    set(cmake_version "${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}.${CMAKE_PATCH_VERSION}")
-    if(cmake_version VERSION_LESS "2.8")
-        message(WARNING "FPFLAGS and GHFLAGS are available only when using CMake >= 2.8")
+    if(${allow_parse_args})
+        message(${WARNING} "FPFLAGS and GHFLAGS are available only when using CMake >= 2.8")
     else()
         separate_arguments(fpflags_parsed UNIX_COMMAND ${FPFLAGS})
         separate_arguments(ghflags_parsed UNIX_COMMAND ${GHFLAGS})
     endif()
 endif()
 
-list(APPEND pascal_flags ${fpflags_parsed}              # user flags
-                 "-vm4079,4080,4081"            # fpc output format
-                 "-B"                           # compile all units
-                 "-FE${PROJECT_BINARY_DIR}/bin" # fpc output directory
-                 "-Fl${PROJECT_BINARY_DIR}/bin" # fpc linking directory
-                 "-Cs2000000"                   # stack size
-                 "-vewnq"                       # fpc output verbosity
-                 "-dDEBUGFILE"                  # macro for engine output
+list(APPEND pascal_flags ${fpflags_parsed}            # user flags
+                 "-B"                                 # compile all units
+                 "-vm4079,4080,4081"                  # fpc verbosity output format
+                 "-FE${PROJECT_BINARY_DIR}/bin"       # fpc binaries output directory
+                 "-FU${PROJECT_BINARY_DIR}/hedgewars" # fpc units output directory
+                 "-Fl${PROJECT_BINARY_DIR}/bin"       # fpc linking directory (win/unix)
+                 "-Fi${PROJECT_BINARY_DIR}/hedgewars" # fpc .inc path (for out of source builds)
+                 "-k-L${PROJECT_BINARY_DIR}/bin"      # ld linking directory (unix/osx)
+                 "-Cs2000000"                         # stack size
+                 "-vewnq"                             # fpc output verbosity
+                 "-dDEBUGFILE"                        # macro for engine output
                  )
-list(APPEND haskell_flags "-O2" ${ghflags_parsed})
+list(APPEND haskell_flags ${ghflags_parsed} # user flags
+                 "-O2"                      # optimise for faster code
+                 )
+
 
 #get BUILD_TYPE and enable/disable optimisation
 message(STATUS "Using ${CMAKE_BUILD_TYPE} configuration")
 if(CMAKE_BUILD_TYPE MATCHES "DEBUG")
-    list(APPEND pascal_flags "-O-" "-g" "-gl" "-gv")
-    list(APPEND haskell_flags "-Wall" "-debug" "-dcore-lint" "-fno-warn-unused-do-bind")
+    list(APPEND pascal_flags "-O-" # disable all optimisations
+                             "-g"  # enable debug symbols
+                             "-gl" # add line info to bt
+                             "-gv" # allow valgrind
+                             )
+    list(APPEND haskell_flags "-Wall"       # all warnings
+                              "-debug"      # debug mode
+                              "-dcore-lint" # internal sanity check
+                              )
 else()
-#    set(pascal_flags "-O3" "-OpPENTIUM4" "-CfSSE3" "-Xs" "-Si" ${pascal_flags})
-    list(APPEND pascal_flags "-Os" "-Xs" "-Si")
-    list(APPEND haskell_flags "-w" "-fno-warn-unused-do-bind")
+    list(APPEND pascal_flags "-Os" # optimise for size
+                             "-Xs" # strip binary
+                             "-Si" # turn on inlining
+                             )
+    list(APPEND haskell_flags "-w" # no warnings
+                              )
 endif()
 
+include(${CMAKE_MODULE_PATH}/utils.cmake)
 
 #Haskell compiler discovery (for server and engine in c)
 if((NOT NOSERVER) OR NOPASCAL)
@@ -290,16 +343,45 @@
 endif()
 
 
-#physfs library (static on unix, dll on win32)
-add_subdirectory(misc/physfs)
-if(NOT WIN32)
-    list(APPEND pascal_flags "-k${LIBRARY_OUTPUT_PATH}/libphysfs.a")
+#physfs discovery
+if (${SYSTEM_PHYSFS})
+    if (NOT PHYSFS_LIBRARY OR NOT PHYSFS_INCLUDE_DIR)
+        find_package(PhysFS)
+    endif()
+
+    find_file(physfs_h physfs.h ${PHYSFS_INCLUDE_DIR})
+    if(physfs_h)
+        file(STRINGS ${physfs_h} physfs_majorversion REGEX "PHYSFS_VER_MAJOR[\t' ']+[0-9]+")
+        file(STRINGS ${physfs_h} physfs_minorversion REGEX "PHYSFS_VER_MINOR[\t' ']+[0-9]+")
+        file(STRINGS ${physfs_h} physfs_patchversion REGEX "PHYSFS_VER_PATCH[\t' ']+[0-9]+")
+        string(REGEX MATCH "([0-9]+)" physfs_majorversion "${physfs_majorversion}")
+        string(REGEX MATCH "([0-9]+)" physfs_minorversion "${physfs_minorversion}")
+        string(REGEX MATCH "([0-9]+)" physfs_patchversion "${physfs_patchversion}")
+        set(physfs_detected_ver "${physfs_majorversion}.${physfs_minorversion}.${physfs_patchversion}")
+
+        if (physfs_detected_ver VERSION_LESS "2.1.0")
+            message(FATAL_ERROR "PhysFS version is too old (dected ${physfs_detected_ver}, required 2.1.0)")
+            set(physfs_too_old true)
+        endif()
+    endif()
+
+    if (NOT PHYSFS_LIBRARY OR NOT PHYSFS_INCLUDE_DIR)
+        message(FATAL_ERROR "Missing PhysFS! Rerun cmake with -DPHYSFS_SYSTEM=off to build the internal version")
+    endif()
+else()
+    message(STATUS "PhysFS will be provided by the bundled sources")
+    set(physfs_output_name "hw_physfs")
+    add_subdirectory(misc/libphysfs)
+    #-XLA is a beta fpc flag that renames libraries before passing them to the linker
+    #we also have to pass PHYSFS_INTERNAL to satisfy windows runtime requirements
+    #(should be harmless on other platforms)
+    list(APPEND pascal_flags "-XLAphysfs=${physfs_output_name}" "-dPHYSFS_INTERNAL")
 endif()
 
+find_package_or_disable_msg(FFMPEG NOVIDEOREC "Video recording will not be built")
 
-#frontend library
-add_subdirectory(project_files/frontlib)
-
+#physfs helper library
+add_subdirectory(misc/libphyslayer)
 
 if(NOPASCAL)
     if (NOT ghc_executable)
@@ -317,102 +399,16 @@
     #WEBGL deps
 else(WEBGL)
     #Android related build scripts
+    #TODO: when ANDROID, LIBENGINE should be set
     if(ANDROID)
         add_subdirectory(project_files/Android-build)
-    endif()
-
-    #TODO: when ANDROID, LIBENGINE should be set
-    if(NOT ANDROID)
+    else(ANDROID)
         add_subdirectory(bin)
         add_subdirectory(QTfrontend)
         add_subdirectory(share)
         add_subdirectory(tools)
-    endif()
+    endif(ANDROID)
 endif(WEBGL)
 
-
-
-# CPack variables
-set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Hedgewars, a free turn-based strategy")
-set(CPACK_PACKAGE_VENDOR "Hedgewars Project")
-set(CPACK_PACKAGE_FILE_NAME "hedgewars-${HEDGEWARS_VERSION}")
-set(CPACK_SOURCE_PACKAGE_FILE_NAME "hedgewars-src-${HEDGEWARS_VERSION}")
-set(CPACK_SOURCE_GENERATOR "TBZ2")
-set(CPACK_PACKAGE_EXECUTABLES "hedgewars" "hedgewars")
-set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/COPYING")
-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_NSIS_EXECUTABLES_DIRECTORY "${target_binary_install_dir}")
-    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")
-endif(WIN32 AND NOT UNIX)
+include(${CMAKE_MODULE_PATH}/CPackConfig.cmake)
 
-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$"
-    "CMakeCache\\\\.txt$"
-#    "^${CMAKE_CURRENT_SOURCE_DIR}/misc/libopenalbridge"
-#    "^${CMAKE_CURRENT_SOURCE_DIR}/misc/libfreetype"
-    "^${CMAKE_CURRENT_SOURCE_DIR}/misc/liblua"
-#    "^${CMAKE_CURRENT_SOURCE_DIR}/misc/libtremor"
-    "^${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/CREDITS	Wed Feb 20 02:21:58 2013 +0100
+++ b/CREDITS	Tue Apr 02 21:00:57 2013 +0200
@@ -6,6 +6,13 @@
 - see Fonts_LICENSE.txt
 
 ==========
+= FORTS
+==========
+- Carlos Vives -> Tank (2010)
+- Dragonfly -> EvilChicken (2010)
+- Randy Broda -> SteelTower (2013)
+
+==========
 = HATS
 ==========
 - Robinator -> Terminator (2010)
@@ -66,4 +73,4 @@
      http://www.freesound.org/people/Jovica/sounds/38317/
 
 
-ALL OTHER CONTENT IS PROPERTY OF Andrey Korotaev <unC0Rr@gmail.com> UNLESS OTHERWISE SPECIFIED
\ No newline at end of file
+ALL OTHER CONTENT IS PROPERTY OF Andrey Korotaev <unC0Rr@gmail.com> UNLESS OTHERWISE SPECIFIED
--- a/INSTALL	Wed Feb 20 02:21:58 2013 +0100
+++ b/INSTALL	Tue Apr 02 21:00:57 2013 +0200
@@ -8,7 +8,7 @@
  - SDL_image >= 1.2
  - SDL_ttf >= 2.0
  - Lua >= 5.1.0
- - Physfs >= 2.1
+ - Physfs >= 2.1.0
 For server:
  - Glasgow Haskell Compiler >= 6.10
  - bytestring-show package
@@ -22,6 +22,8 @@
 
 Lua will be automatically built if not found.
 
+PhysFS will internally built unless -DPHYSFS_SYSTEM=on is passed to cmake
+(also allows to set PHYSFS_LIBRARY and PHYSFS_INCLUDE_DIR if needed).
 
 1. Configure:
 $ cmake .
--- a/QTfrontend/CMakeLists.txt	Wed Feb 20 02:21:58 2013 +0100
+++ b/QTfrontend/CMakeLists.txt	Tue Apr 02 21:00:57 2013 +0200
@@ -15,12 +15,11 @@
 
 find_package(SDL REQUIRED)       #video in SDLInteraction
 find_package(SDL_mixer REQUIRED) #audio in SDLInteraction
-find_package(SDL_net REQUIRED)   #network frontlib
-if(NOT NOVIDEOREC)
-    find_package(FFMPEG)
-    if(${FFMPEG_FOUND})
-        add_definitions(-DVIDEOREC -D__STDC_CONSTANT_MACROS)
-    endif()
+
+if(${FFMPEG_FOUND})
+    add_definitions(-DVIDEOREC -D__STDC_CONSTANT_MACROS)
+    include_directories(${FFMPEG_INCLUDE_DIR})
+    list(APPEND HW_LINK_LIBS ${FFMPEG_LIBRARIES})
 endif()
 
 # server messages localization
@@ -56,22 +55,15 @@
 include_directories(${CMAKE_CURRENT_SOURCE_DIR}/util/platform)
 include_directories(${SDL_INCLUDE_DIR})
 include_directories(${SDLMIXER_INCLUDE_DIR})
-include_directories(${FFMPEG_INCLUDE_DIR})
-include_directories(${CMAKE_SOURCE_DIR}/misc/physfs/src)
-include_directories(${CMAKE_SOURCE_DIR}/misc/physfs/extras)
+include_directories(${PHYSFS_INCLUDE_DIR})
+include_directories(${PHYSLAYER_INCLUDE_DIR})
+
+
 if(UNIX)
     # HACK: in freebsd cannot find iconv.h included via SDL.h
     include_directories("/usr/local/include")
 endif(UNIX)
 
-#directory for resources, relative to bindir (on linux an absolute path is always used)
-string(SUBSTRING "${SHAREPATH}" 0 1 sharepath_start)
-if(APPLE OR WIN32 OR ${sharepath_start} MATCHES "/")
-    set(HEDGEWARS_DATADIR ${SHAREPATH})
-else()
-    set(HEDGEWARS_DATADIR ${CMAKE_INSTALL_PREFIX}/${SHAREPATH})
-endif()
-
 #only the cocoa version of qt supports building 64 bit apps
 if(APPLE AND (CMAKE_OSX_ARCHITECTURES MATCHES "x86_64*") AND (NOT QT_MAC_USE_COCOA))
     message(FATAL_ERROR "Building the 64 bit version of Hedgewars *requires* the Cocoa variant of QT on Mac OS X")
@@ -138,7 +130,6 @@
     team.h
     util/DataManager.h
     util/LibavInteraction.h
-    util/MessageDialog.h
     )
 
 set(hwfr_hdrs
@@ -175,14 +166,13 @@
                          util/platform/M3InstallController.m
                          util/platform/NSWorkspace_RBAdditions.m
                          )
-    if(NOT NOAUTOUPDATE)
-        find_package(Sparkle)
-        if(SPARKLE_FOUND)
-            add_definitions(-DSPARKLE_ENABLED)
-            list(APPEND hwfr_src util/platform/AutoUpdater.cpp
-                                 util/platform/SparkleAutoUpdater.mm)
-            list(APPEND HW_LINK_LIBS ${SPARKLE_LIBRARY})
-        endif()
+    include(${CMAKE_MODULE_PATH}/utils.cmake)
+    find_package_or_disable_msg(Sparkle NOAUTOUPDATE "Autoupdater will not be built.")
+    if(SPARKLE_FOUND)
+        add_definitions(-DSPARKLE_ENABLED)
+        list(APPEND hwfr_src util/platform/AutoUpdater.cpp
+                             util/platform/SparkleAutoUpdater.mm)
+        list(APPEND HW_LINK_LIBS ${SPARKLE_LIBRARY})
     endif()
 endif()
 
@@ -209,11 +199,11 @@
 endif()
 
 list(APPEND HW_LINK_LIBS
-    physfs
+    ${PHYSFS_LIBRARY}
+    ${PHYSLAYER_LIBRARY}
     ${QT_LIBRARIES}
     ${SDL_LIBRARY}
     ${SDLMIXER_LIBRARY}
-    ${FFMPEG_LIBRARIES}
     )
 
 if(WIN32 AND NOT UNIX)
--- a/QTfrontend/HWApplication.cpp	Wed Feb 20 02:21:58 2013 +0100
+++ b/QTfrontend/HWApplication.cpp	Tue Apr 02 21:00:57 2013 +0200
@@ -1,6 +1,6 @@
 /*
  * Hedgewars, a free turn based strategy game
- * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
+ * Copyright (c) 2012 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
@@ -18,30 +18,84 @@
 
 #include "HWApplication.h"
 #include <QFileOpenEvent>
+#include <QEvent>
 
 #include "hwform.h"
+#include "MessageDialog.h"
 
-HWApplication::HWApplication(int &argc,  char **argv):
+#if !defined(Q_WS_WIN)
+#include "signal.h"
+#endif
+
+#if !defined(Q_WS_WIN)
+void terminateFrontend(int signal)
+{
+    Q_UNUSED(signal);
+    QCoreApplication::exit(0);
+}
+#endif
+
+HWApplication::HWApplication(int &argc, char **argv) :
     QApplication(argc, argv)
 {
+#if !defined(Q_WS_WIN)
+    signal(SIGINT, &terminateFrontend);
+#endif
+#if 0
+    qDebug("%s called with", argv[0]);
+    for (int i = 1; i < argc; i++)
+        qDebug("%d: %s", i, argv[i]);
+#endif
+    // on Windows, sending an event right away leads to a segfault
+    // so we use urlString to save the data and send the event just before the app.exec()
+    urlString = NULL;
+    if (argc > 1) {
+        urlString = new QString(argv[1]);
+        if (urlString->contains("//", Qt::CaseInsensitive) == false) {
+            delete urlString;
+            urlString = NULL;
+        }
+    }
+}
 
+void HWApplication::fakeEvent()
+{
+    QUrl parsedUrl(*urlString);
+    delete urlString;
+    urlString = NULL;
+    QFileOpenEvent *openEvent = new QFileOpenEvent(parsedUrl);
+    QCoreApplication::sendEvent(QCoreApplication::instance(), openEvent);
 }
 
 bool HWApplication::event(QEvent *event)
 {
     QFileOpenEvent *openEvent;
+    QString scheme, path, address;
 
-    switch (event->type())
-    {
-        case QEvent::FileOpen:
-            openEvent = (QFileOpenEvent *)event;
-            if (form) form->PlayDemoQuick(openEvent->file());
+    if (event->type() == QEvent::FileOpen) {
+        openEvent = (QFileOpenEvent *)event;
+        scheme = openEvent->url().scheme();
+        path = openEvent->url().path();
+        address = openEvent->url().host();
+
+        QFile file(path);
+        if (scheme == "file" && file.exists()) {
+            form->PlayDemoQuick(path);
             return true;
-            break;
-        default:
-            return QApplication::event(event);
-            break;
+        } else if (scheme == "hwplay") {
+            int port = openEvent->url().port(NETGAME_DEFAULT_PORT);
+            if (address == "")
+                address = NETGAME_DEFAULT_SERVER;
+            form->NetConnectQuick(address, (quint16) port);
+            return true;
+        } else {
+            const QString errmsg = tr("Scheme '%1' not supported").arg(scheme);
+            MessageDialog::ShowErrorMessage(errmsg, form);
+            return false;
+        }
     }
+
+    return QApplication::event(event);
 }
 
 
--- a/QTfrontend/HWApplication.h	Wed Feb 20 02:21:58 2013 +0100
+++ b/QTfrontend/HWApplication.h	Tue Apr 02 21:00:57 2013 +0200
@@ -1,6 +1,6 @@
 /*
  * Hedgewars, a free turn based strategy game
- * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
+ * Copyright (c) 2012 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
@@ -20,10 +20,9 @@
 #define HWAPP_H
 
 #include <QApplication>
-#include <QString>
-#include <QEvent>
 
 class HWForm;
+class QEvent;
 
 /**
  * @brief Main class of the Qt application.
@@ -41,6 +40,8 @@
         ~HWApplication() {};
 
         HWForm *form;
+        QString *urlString;
+        void fakeEvent();
     protected:
         bool event(QEvent *);
 };
--- a/QTfrontend/game.cpp	Wed Feb 20 02:21:58 2013 +0100
+++ b/QTfrontend/game.cpp	Tue Apr 02 21:00:57 2013 +0200
@@ -313,9 +313,8 @@
         default:
         {
             if (gameType == gtNet && !netSuspend)
-            {
-                emit SendNet(msg);
-            }
+                m_netSendBuffer.append(msg);
+
             demo.append(msg);
         }
     }
@@ -344,6 +343,18 @@
         readbuffer.remove(0, msglen + 1);
         ParseMessage(msg);
     }
+    
+    flushNetBuffer();
+}
+
+void HWGame::flushNetBuffer()
+{
+    if(m_netSendBuffer.size())
+    {
+        emit SendNet(m_netSendBuffer);
+        
+        m_netSendBuffer.clear();
+    }
 }
 
 QStringList HWGame::getArguments()
@@ -478,7 +489,7 @@
 void HWGame::sendCampaignVar(const QByteArray &varToSend)
 {
     QString varToFind(varToSend);
-    QSettings teamfile(cfgdir->absolutePath() + "/Teams/" + campaignTeam + ".hwt", QSettings::IniFormat, 0);
+    QSettings teamfile(QString("physfs://Teams/%1.hwt").arg(campaignTeam), QSettings::IniFormat, 0);
     teamfile.setIniCodec("UTF-8");
     QString varValue = teamfile.value("Campaign " + campaign + "/" + varToFind, "").toString();
     QByteArray command;
@@ -495,7 +506,7 @@
     QString varToWrite = QString::fromUtf8(varVal.left(i));
     QString varValue = QString::fromUtf8(varVal.mid(i + 1));
 
-    QSettings teamfile(cfgdir->absolutePath() + "/Teams/" + campaignTeam + ".hwt", QSettings::IniFormat, 0);
+    QSettings teamfile(QString("physfs://Teams/%1.hwt").arg(campaignTeam), QSettings::IniFormat, 0);
     teamfile.setIniCodec("UTF-8");
     teamfile.setValue("Campaign " + campaign + "/" + varToWrite, varValue);
 }
--- a/QTfrontend/game.h	Wed Feb 20 02:21:58 2013 +0100
+++ b/QTfrontend/game.h	Tue Apr 02 21:00:57 2013 +0200
@@ -102,6 +102,7 @@
         GameCFGWidget * gamecfg;
         TeamSelWidget* m_pTeamSelWidget;
         GameType gameType;
+        QByteArray m_netSendBuffer;
 
         void addKeyBindings(QByteArray * buf);
         void commonConfig();
@@ -114,6 +115,7 @@
         void SetGameState(GameState state);
         void sendCampaignVar(const QByteArray & varToSend);
         void writeCampaignVar(const QByteArray &varVal);
+        void flushNetBuffer();
 };
 
 #endif
--- a/QTfrontend/gameuiconfig.cpp	Wed Feb 20 02:21:58 2013 +0100
+++ b/QTfrontend/gameuiconfig.cpp	Tue Apr 02 21:00:57 2013 +0200
@@ -130,10 +130,10 @@
 
     delete netHost;
     netHost = new QString(value("net/ip", "").toString());
-    netPort = value("net/port", 46631).toUInt();
+    netPort = value("net/port", NETGAME_DEFAULT_PORT).toUInt();
 
     Form->ui.pageNetServer->leServerDescr->setText(value("net/servername", "hedgewars server").toString());
-    Form->ui.pageNetServer->sbPort->setValue(value("net/serverport", 46631).toUInt());
+    Form->ui.pageNetServer->sbPort->setValue(value("net/serverport", NETGAME_DEFAULT_PORT).toUInt());
 
     Form->ui.pageOptions->CBShowFPS->setChecked(value("fps/show", false).toBool());
     Form->ui.pageOptions->fpsedit->setValue(value("fps/limit", 27).toUInt());
@@ -217,12 +217,12 @@
 void GameUIConfig::resizeToConfigValues()
 {
     // fill 2/3 of the screen desktop
-    const QRect deskSize = QApplication::desktop()->screenGeometry(-1);
+    const QRect deskSize = HWApplication::desktop()->screenGeometry(-1);
     Form->resize(value("frontend/width", qMin(qMax(deskSize.width()*2/3,800),deskSize.width())).toUInt(),
                  value("frontend/height", qMin(qMax(deskSize.height()*2/3,600),deskSize.height())).toUInt());
 
     // move the window to the center of the screen
-    QPoint center = QApplication::desktop()->availableGeometry(-1).center();
+    QPoint center = HWApplication::desktop()->availableGeometry(-1).center();
     center.setX(center.x() - (Form->width()/2));
     center.setY(center.y() - (Form->height()/2));
     Form->move(center);
--- a/QTfrontend/hwconsts.cpp.in	Wed Feb 20 02:21:58 2013 +0100
+++ b/QTfrontend/hwconsts.cpp.in	Tue Apr 02 21:00:57 2013 +0200
@@ -19,10 +19,15 @@
 #include <QStandardItemModel>
 
 #include "hwconsts.h"
+#include "weapons.h"
 
+// cDataDir gets 'Data' appended later (in main.cpp)
+QString * cDataDir = new QString("${HEDGEWARS_DATADIR}");
 QString * cProtoVer = new QString("${HEDGEWARS_PROTO_VER}");
-QString * cDataDir = new QString("${HEDGEWARS_DATADIR}");
 QString * cVersionString = new QString("${HEDGEWARS_VERSION}");
+QString * cRevisionString = new QString("${HEDGEWARS_REVISION}");
+QString * cHashString = new QString("${HEDGEWARS_HASH}");
+
 
 QDir * bindir = new QDir();
 QDir * cfgdir = new QDir();
@@ -59,6 +64,9 @@
         << qMakePair(QString("Thinking with Portals"), QString(
             AMMOLINE_PORTALS_QT AMMOLINE_PORTALS_PROB
             AMMOLINE_PORTALS_DELAY AMMOLINE_PORTALS_CRATE ))
+        << qMakePair(QString("One of Everything"), QString(
+            AMMOLINE_ONEEVERY_QT AMMOLINE_ONEEVERY_PROB
+            AMMOLINE_ONEEVERY_DELAY AMMOLINE_ONEEVERY_CRATE ))
         ;
 
 unsigned int colors[] = HW_TEAMCOLOR_ARRAY;
@@ -66,8 +74,5 @@
 QString * netHost = new QString();
 quint16 netPort = NETGAME_DEFAULT_PORT;
 
-bool haveServer = ${HAVE_NETSERVER};
-bool isDevBuild = ${HW_DEV};
-
 int season = SEASON_NONE;
 int years_since_foundation = 0;
--- a/QTfrontend/hwconsts.h	Wed Feb 20 02:21:58 2013 +0100
+++ b/QTfrontend/hwconsts.h	Tue Apr 02 21:00:57 2013 +0200
@@ -25,6 +25,8 @@
 
 extern QString * cProtoVer;
 extern QString * cVersionString;
+extern QString * cRevisionString;
+extern QString * cHashString;
 extern QString * cDataDir;
 
 extern QDir * bindir;
@@ -48,8 +50,6 @@
 extern QString * netHost;
 extern quint16 netPort;
 
-extern bool haveServer;
-extern bool isDevBuild;
 
 //Current season, SEASON_NONE by default
 extern int season;
@@ -60,42 +60,6 @@
 
 #endif
 
-#define HEDGEHOGS_PER_TEAM           8
-
-#define AMMOLINE_DEFAULT_QT     "9391929422199121032235111001201000000211110101011111101"
-#define AMMOLINE_DEFAULT_PROB   "0405040541600655546554464776576666666155510101115411101"
-#define AMMOLINE_DEFAULT_DELAY  "0000000000000205500000040007004000000000220000000600000"
-#define AMMOLINE_DEFAULT_CRATE  "1311110312111111123114111111111111111211111101111111101"
-
-#define AMMOLINE_CRAZY_QT       "9999999999999999992999999999999999299999999909999992909"
-#define AMMOLINE_CRAZY_PROB     "1111110111111111111111111111111111111111111101111111101"
-#define AMMOLINE_CRAZY_DELAY    "0000000000000000000000000000000000000000000000000000000"
-#define AMMOLINE_CRAZY_CRATE    "1311110312111111123114111111111111111211110101111111101"
-
-#define AMMOLINE_PROMODE_QT     "9090009000000000000009000000000000000000000000000000000"
-#define AMMOLINE_PROMODE_PROB   "0000000000000000000000000000000000000000000000000000000"
-#define AMMOLINE_PROMODE_DELAY  "0000000000000205500000040007004000000000200000000000002"
-#define AMMOLINE_PROMODE_CRATE  "1111111111111111111111111111111111111111100101111111101"
-
-#define AMMOLINE_SHOPPA_QT      "0000009900000000000000000000000000000000000000000000000"
-#define AMMOLINE_SHOPPA_PROB    "4444410044244402210112121222422000000002000400010011001"
-#define AMMOLINE_SHOPPA_DELAY   "0000000000000000000000000000000000000000000000000000000"
-#define AMMOLINE_SHOPPA_CRATE   "1111111111111111111111111111111111111111101101111111001"
-
-#define AMMOLINE_CLEAN_QT       "1010009000010000011000000000000000000000000000001000000"
-#define AMMOLINE_CLEAN_PROB     "0405040541600655546554464776576666666155510101115411101"
-#define AMMOLINE_CLEAN_DELAY    "0000000000000000000000000000000000000000000000000000000"
-#define AMMOLINE_CLEAN_CRATE    "1311110312111111123114111111111111111211111101111111101"
-
-#define AMMOLINE_MINES_QT       "0000009900090000000300000000000000000000000000000000000"
-#define AMMOLINE_MINES_PROB     "0000000000000000000000000000000000000000000000000000000"
-#define AMMOLINE_MINES_DELAY    "0000000000000205500000040007004000000000200000000600000"
-#define AMMOLINE_MINES_CRATE    "1111111111111111111111111111111111111111111101111111101"
-
-#define AMMOLINE_PORTALS_QT     "9000009002000000002100000000000000110000090000000000000"
-#define AMMOLINE_PORTALS_PROB   "0405040541600655546554464776576666666155510101115411101"
-#define AMMOLINE_PORTALS_DELAY  "0000000000000205500000040007004000000000200000000600000"
-#define AMMOLINE_PORTALS_CRATE  "1311110312111111123114111111111111111211111101111111101"
 
 //Different seasons; assigned to season (int)
 #define SEASON_NONE 0
@@ -103,7 +67,9 @@
 #define SEASON_HWBDAY 4
 #define SEASON_EASTER 8
 
+#define NETGAME_DEFAULT_SERVER "netserver.hedgewars.org"
 #define NETGAME_DEFAULT_PORT 46631
+#define HEDGEHOGS_PER_TEAM 8
 
 
 // see http://en.wikipedia.org/wiki/List_of_colors
--- a/QTfrontend/hwform.cpp	Wed Feb 20 02:21:58 2013 +0100
+++ b/QTfrontend/hwform.cpp	Tue Apr 02 21:00:57 2013 +0200
@@ -155,6 +155,7 @@
     playerHash = QString(QCryptographicHash::hash(config->value("net/nick","").toString().toUtf8(), QCryptographicHash::Md5).toHex());
 
     ui.pageRoomsList->setSettings(config);
+    ui.pageNetGame->setSettings(config);
     ui.pageNetGame->chatWidget->setSettings(config);
     ui.pageRoomsList->chatWidget->setSettings(config);
     ui.pageOptions->setConfig(config);
@@ -349,6 +350,7 @@
         }
     }
 
+    ui.Pages->setCurrentIndex(ID_PAGE_INFO);
     PagesStack.push(ID_PAGE_MAIN);
     ((AbstractPage*)ui.Pages->widget(ID_PAGE_MAIN))->triggerPageEnter();
     GoBack();
@@ -359,7 +361,7 @@
 {
     if(hwnet && (hwnet->clientState() != HWNewNet::Disconnected))
     {
-        xfire_setvalue(XFIRE_SERVER, !hwnet->getHost().compare("netserver.hedgewars.org:46631") ? "Official server" : hwnet->getHost().toAscii());
+        xfire_setvalue(XFIRE_SERVER, !hwnet->getHost().compare(QString("%1:%2").arg(NETGAME_DEFAULT_SERVER).arg(NETGAME_DEFAULT_PORT)) ? "Official server" : hwnet->getHost().toAscii());
         switch(hwnet->clientState())
         {
             case HWNewNet::Connecting: // Connecting
@@ -992,21 +994,27 @@
 
 void HWForm::PlayDemoQuick(const QString & demofilename)
 {
-    if (game && game->gameState == gsStarted) return;
-    GoBack(); //needed to cleanly disconnect from netgame
     GoToPage(ID_PAGE_MAIN);
+    //GoBack() <- don't or you'll close the socket
     CreateGame(0, 0, 0);
     game->PlayDemo(demofilename, false);
 }
 
+void HWForm::NetConnectQuick(const QString & host, quint16 port)
+{
+    GoToPage(ID_PAGE_MAIN);
+    NetConnectServer(host, port);
+}
+
 void HWForm::NetConnectServer(const QString & host, quint16 port)
 {
+    qDebug("connecting to %s:%d", qPrintable(host), port);
     _NetConnect(host, port, ui.pageOptions->editNetNick->text().trimmed());
 }
 
 void HWForm::NetConnectOfficialServer()
 {
-    NetConnectServer("netserver.hedgewars.org", 46631);
+    NetConnectServer(NETGAME_DEFAULT_SERVER, NETGAME_DEFAULT_PORT);
 }
 
 void HWForm::NetPassword(const QString & nick)
@@ -1096,10 +1104,14 @@
     if (!ok || newNick.isEmpty())
     {
         //ForcedDisconnect(tr("No nickname supplied."));
-    bool retry = RetryDialog(tr("Hedgewars - Empty nickname"), tr("No nickname supplied."));
-    GoBack();
+        bool retry = RetryDialog(tr("Hedgewars - Empty nickname"), tr("No nickname supplied."));
+        GoBack();
         if (retry) {
-            NetConnectOfficialServer();
+            if (hwnet->m_private_game) {
+                QStringList list = hwnet->getHost().split(":");
+                NetConnectServer(list.at(0), list.at(1).toShort());
+            } else
+                NetConnectOfficialServer();
         }
         return;
     }
@@ -1182,18 +1194,18 @@
 {
     Q_UNUSED(nick);
 
-    if(hwnet)
-    {
+    if (hwnet) {
+        // destroy old connection
         hwnet->Disconnect();
         delete hwnet;
-        hwnet=0;
+        hwnet = NULL;
     }
 
     hwnet = new HWNewNet();
 
     GoToPage(ID_PAGE_CONNECTING);
 
-    connect(hwnet, SIGNAL(AskForRunGame()), this, SLOT(CreateNetGame()));
+    connect(hwnet, SIGNAL(AskForRunGame()), this, SLOT(CreateNetGame()), Qt::QueuedConnection);
     connect(hwnet, SIGNAL(connected()), this, SLOT(NetConnected()), Qt::QueuedConnection);
     connect(hwnet, SIGNAL(Error(const QString&)), this, SLOT(NetError(const QString&)), Qt::QueuedConnection);
     connect(hwnet, SIGNAL(Warning(const QString&)), this, SLOT(NetWarning(const QString&)), Qt::QueuedConnection);
@@ -1237,6 +1249,8 @@
             this, SLOT(NetGameChangeStatus(bool)), Qt::QueuedConnection);
 
 // net page stuff
+    connect(hwnet, SIGNAL(roomNameUpdated(const QString &)),
+            ui.pageNetGame, SLOT(setRoomName(const QString &)), Qt::QueuedConnection);
     connect(hwnet, SIGNAL(chatStringFromNet(const QString&)),
             ui.pageNetGame->chatWidget, SLOT(onChatString(const QString&)), Qt::QueuedConnection);
 
@@ -1278,7 +1292,7 @@
     connect(hwnet, SIGNAL(chatStringLobby(const QString&)),
             ui.pageRoomsList->chatWidget, SLOT(onChatString(const QString&)), Qt::QueuedConnection);
     connect(hwnet, SIGNAL(chatStringLobby(const QString&, const QString&)),
-            ui.pageRoomsList->chatWidget, SLOT(onChatString(const QString&, const QString&)));
+            ui.pageRoomsList->chatWidget, SLOT(onChatString(const QString&, const QString&)), Qt::QueuedConnection);
     connect(hwnet, SIGNAL(chatStringFromMeLobby(const QString&)),
             ui.pageRoomsList->chatWidget, SLOT(onChatString(const QString&)), Qt::QueuedConnection);
 
@@ -1328,8 +1342,22 @@
     connect(ui.pageNetGame->pGameCFG, SIGNAL(paramChanged(const QString &, const QStringList &)), hwnet, SLOT(onParamChanged(const QString &, const QStringList &)));
     connect(hwnet, SIGNAL(configAsked()), ui.pageNetGame->pGameCFG, SLOT(fullNetConfig()));
 
-//nick and pass stuff
+    //nick and pass stuff
+    QString nickname = config->value("net/nick", "").toString();
+
+    hwnet->m_private_game = !(hostName == NETGAME_DEFAULT_SERVER && port == NETGAME_DEFAULT_PORT);
+    if (hwnet->m_private_game == false)
+        if (AskForNickAndPwd() != 0)
+            return;
 
+    ui.pageRoomsList->setUser(nickname);
+    ui.pageNetGame->setUser(nickname);
+
+    hwnet->Connect(hostName, port, nickname);
+}
+
+int HWForm::AskForNickAndPwd(void)
+{
     //remove temppasswordhash just in case
     config->clearTempHash();
 
@@ -1360,7 +1388,7 @@
             if (pwDialog->exec() != QDialog::Accepted) {
                 delete pwDialog;
                 GoBack();
-                return;
+                return -1;
             }
 
             //set nick and pass from the dialog
@@ -1373,9 +1401,13 @@
                 GoBack();
                 delete pwDialog;
                 if (retry) {
-                    NetConnectOfficialServer();
-                }
-                return;
+                    if (hwnet->m_private_game) {
+                        QStringList list = hwnet->getHost().split(":");
+                        NetConnectServer(list.at(0), list.at(1).toShort());
+                    } else
+                        NetConnectOfficialServer();
+                    }
+                return -1;
             }
 
             if (!password.isEmpty()) {
@@ -1412,15 +1444,9 @@
             nickname = config->value("net/nick", "").toString();
         }
     }
-
-    ui.pageRoomsList->setUser(nickname);
-    ui.pageNetGame->setUser(nickname);
-
-    hwnet->Connect(hostName, port, nickname);
+    return 0;
 }
 
-
-
 void HWForm::NetConnect()
 {
     HWHostPortDialog * hpd = new HWHostPortDialog(this);
@@ -1488,7 +1514,11 @@
     if (reason == "Reconnected too fast") { //TODO: this is a hack, which should be remade
         bool retry = RetryDialog(tr("Hedgewars - Connection error"), tr("You reconnected too fast.\nPlease wait a few seconds and try again."));
         if (retry) {
-            NetConnectOfficialServer();
+            if (hwnet->m_private_game) {
+                QStringList list = hwnet->getHost().split(":");
+                NetConnectServer(list.at(0), list.at(1).toShort());
+            } else
+                NetConnectOfficialServer();
         }
         else {
             while (ui.Pages->currentIndex() != ID_PAGE_NET
@@ -1501,8 +1531,7 @@
     }
     if (pnetserver)
         return; // we have server - let it care of all things
-    if (hwnet)
-    {
+    if (hwnet) {
         QString errorStr = QMessageBox::tr("Connection to server is lost") + (reason.isEmpty()?"":("\n\n" + HWNewNet::tr("Quit reason: ") + '"' + reason +'"'));
         MessageDialog::ShowErrorMessage(errorStr, this);
     }
@@ -1621,9 +1650,7 @@
             QDateTime::currentDateTime().toString("yyyy-MM-dd_hh-mm") :
             "LastRound";
 
-        QStringList versionParts = cVersionString->split('-');
-        if ( (versionParts.size() == 2) && (!versionParts[1].isEmpty()) && (versionParts[1].contains(':')) )
-            recordFileName = recordFileName + "_" + versionParts[1].replace(':','-');
+        recordFileName += "_" + *cRevisionString + "-" + *cHashString;
 
         if (type == rtDemo)
         {
@@ -1727,6 +1754,7 @@
     ui.pageNetGame->setMasterMode(true);
     ui.pageNetGame->restrictJoins->setChecked(false);
     ui.pageNetGame->restrictTeamAdds->setChecked(false);
+    ui.pageNetGame->restrictUnregistered->setChecked(false);
     ui.pageNetGame->pGameCFG->GameSchemes->setModel(ammoSchemeModel);
     ui.pageNetGame->pGameCFG->setMaster(true);
     ui.pageNetGame->pNetTeamsWidget->setInteractivity(true);
@@ -1739,6 +1767,7 @@
         ui.pageNetGame->leRoomName->disconnect(hwnet);
         ui.pageNetGame->restrictJoins->disconnect(hwnet);
         ui.pageNetGame->restrictTeamAdds->disconnect(hwnet);
+        ui.pageNetGame->restrictUnregistered->disconnect(hwnet);
         ui.pageNetGame->disconnect(hwnet, SLOT(updateRoomName(const QString&)));
 
         ui.pageNetGame->setRoomName(hwnet->getRoom());
@@ -1747,6 +1776,7 @@
         connect(ui.pageNetGame, SIGNAL(askForUpdateRoomName(const QString &)), hwnet, SLOT(updateRoomName(const QString &)));
         connect(ui.pageNetGame->restrictJoins, SIGNAL(triggered()), hwnet, SLOT(toggleRestrictJoins()));
         connect(ui.pageNetGame->restrictTeamAdds, SIGNAL(triggered()), hwnet, SLOT(toggleRestrictTeamAdds()));
+        connect(ui.pageNetGame->restrictUnregistered, SIGNAL(triggered()), hwnet, SLOT(toggleRegisteredOnly()));
         connect(ui.pageNetGame->pGameCFG->GameSchemes->model(),
                 SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)),
                 ui.pageNetGame->pGameCFG,
@@ -1855,8 +1885,8 @@
 QString HWForm::getDemoArguments()
 {
 
-    QString prefix = datadir->absolutePath();
-    QString userPrefix = cfgdir->absolutePath();
+    QString prefix = "\"" + datadir->absolutePath() + "\"";
+    QString userPrefix = "\"" + cfgdir->absolutePath() + "\"";
 #ifdef Q_WS_WIN
     prefix = prefix.replace("/","\\");
     userPrefix = userPrefix.replace("/","\\");
@@ -1886,14 +1916,22 @@
     QString arguments = getDemoArguments();
 #ifdef _WIN32
     QSettings registry_hkcr("HKEY_CLASSES_ROOT", QSettings::NativeFormat);
+
+    // file extension(s)
     registry_hkcr.setValue(".hwd/Default", "Hedgewars.Demo");
     registry_hkcr.setValue(".hws/Default", "Hedgewars.Save");
     registry_hkcr.setValue("Hedgewars.Demo/Default", tr("Hedgewars Demo File", "File Types"));
     registry_hkcr.setValue("Hedgewars.Save/Default", tr("Hedgewars Save File", "File Types"));
     registry_hkcr.setValue("Hedgewars.Demo/DefaultIcon/Default", "\"" + bindir->absolutePath().replace("/", "\\") + "\\hwdfile.ico\",0");
     registry_hkcr.setValue("Hedgewars.Save/DefaultIcon/Default", "\"" + bindir->absolutePath().replace("/", "\\") + "\\hwsfile.ico\",0");
-    registry_hkcr.setValue("Hedgewars.Demo/Shell/Open/Command/Default", "\"" + bindir->absolutePath().replace("/", "\\") + "\\hwengine.exe\" \"%1\" "+arguments);
-    registry_hkcr.setValue("Hedgewars.Save/Shell/Open/Command/Default", "\"" + bindir->absolutePath().replace("/", "\\") + "\\hwengine.exe\" \"%1\" "+arguments);
+    registry_hkcr.setValue("Hedgewars.Demo/Shell/Open/Command/Default", "\"" + bindir->absolutePath().replace("/", "\\") + "\\hwengine.exe\" " + arguments + " %1");
+    registry_hkcr.setValue("Hedgewars.Save/Shell/Open/Command/Default", "\"" + bindir->absolutePath().replace("/", "\\") + "\\hwengine.exe\" " + arguments + " %1");
+
+    // custom url scheme(s)
+    registry_hkcr.setValue("hwplay/Default", "\"URL:Hedgewars ServerAccess Scheme\"");
+    registry_hkcr.setValue("hwplay/URL Protocol", "");
+    registry_hkcr.setValue("hwplay/DefaultIcon/Default", "\"" + bindir->absolutePath().replace("/", "\\") + "\\hedgewars.exe\",0");
+    registry_hkcr.setValue("hwplay/Shell/Open/Command/Default", "\"" + bindir->absolutePath().replace("/", "\\") + "\\hedgewars.exe\"  %1");
 #elif defined __APPLE__
     // only useful when other apps have taken precedence over our file extensions and you want to reset it
     system("defaults write com.apple.LaunchServices LSHandlers -array-add '<dict><key>LSHandlerContentTag</key><string>hwd</string><key>LSHandlerContentTagClass</key><string>public.filename-extension</string><key>LSHandlerRoleAll</key><string>org.hedgewars.desktop</string></dict>'");
@@ -1909,12 +1947,14 @@
     if (success) success = checkForDir(QDir::home().absolutePath() + "/.local/share");
     if (success) success = checkForDir(QDir::home().absolutePath() + "/.local/share/applications");
     if (success) success = system(("cp "+datadir->absolutePath()+"/misc/hedgewars-mimeinfo.xml "+QDir::home().absolutePath()+"/.local/share/mime/packages").toLocal8Bit().constData())==0;
+    if (success) success = system(("cp "+datadir->absolutePath()+"/misc/hedgewars.desktop "+QDir::home().absolutePath()+"/.local/share/applications").toLocal8Bit().constData())==0;
     if (success) success = system(("cp "+datadir->absolutePath()+"/misc/hwengine.desktop "+QDir::home().absolutePath()+"/.local/share/applications").toLocal8Bit().constData())==0;
     if (success) success = system(("update-mime-database "+QDir::home().absolutePath()+"/.local/share/mime").toLocal8Bit().constData())==0;
+    if (success) success = system("xdg-mime default hedgewars.desktop x-scheme-handler/hwplay")==0;
     if (success) success = system("xdg-mime default hwengine.desktop application/x-hedgewars-demo")==0;
     if (success) success = system("xdg-mime default hwengine.desktop application/x-hedgewars-save")==0;
     // hack to add user's settings to hwengine. might be better at this point to read in the file, append it, and write it out to its new home.  This assumes no spaces in the data dir path
-    if (success) success = system(("sed -i 's/^\\(Exec=.*\\) \\([^ ]* %f\\)/\\1 \\2 "+arguments+"/' "+QDir::home().absolutePath()+"/.local/share/applications/hwengine.desktop").toLocal8Bit().constData())==0;
+    if (success) success = system(("sed -i 's|^\\(Exec=.*\\) \\(%f\\)|\\1 \\2 "+arguments+"|' "+QDir::home().absolutePath()+"/.local/share/applications/hwengine.desktop").toLocal8Bit().constData())==0;
 #endif
     if (success)
     {
--- a/QTfrontend/hwform.h	Wed Feb 20 02:21:58 2013 +0100
+++ b/QTfrontend/hwform.h	Tue Apr 02 21:00:57 2013 +0200
@@ -62,12 +62,14 @@
         Ui_HWForm ui;
         static GameUIConfig * config;
         void updateXfire();
-        void PlayDemoQuick(const QString & demofilename);
         void exit();
         void setButtonDescription(QString desc);
         void backDescription();
         void GoToVideos();
 
+        void NetConnectQuick(const QString & host, quint16 port);
+        void PlayDemoQuick(const QString & demofilename);
+
     private slots:
         void GoToSaves();
         void GoToDemos();
@@ -143,6 +145,7 @@
 
     private:
         void _NetConnect(const QString & hostName, quint16 port, QString nick);
+        int  AskForNickAndPwd(void);
         void UpdateTeamsLists();
         void CreateGame(GameCFGWidget * gamecfg, TeamSelWidget* pTeamSelWidget, QString ammo);
         void closeEvent(QCloseEvent *event);
--- a/QTfrontend/main.cpp	Wed Feb 20 02:21:58 2013 +0100
+++ b/QTfrontend/main.cpp	Tue Apr 02 21:00:57 2013 +0200
@@ -20,7 +20,6 @@
 
 #include <QTranslator>
 #include <QLocale>
-#include <QMessageBox>
 #include <QPlastiqueStyle>
 #include <QRegExp>
 #include <QMap>
@@ -36,15 +35,13 @@
 
 #include "DataManager.h"
 #include "FileEngine.h"
+#include "MessageDialog.h"
 
 #ifdef _WIN32
 #include <Shlobj.h>
 #elif defined __APPLE__
 #include "CocoaInitializer.h"
 #endif
-#ifndef _WIN32
-#include <signal.h>
-#endif
 
 // Program resources
 #ifdef __APPLE__
@@ -94,14 +91,7 @@
     else
         season = SEASON_NONE;
 }
-#ifndef _WIN32
-void terminateFrontend(int signal)
-{
-    Q_UNUSED(signal);
 
-    QCoreApplication::exit(0);
-}
-#endif
 
 bool checkForDir(const QString & dir)
 {
@@ -109,26 +99,12 @@
     if (!tmpdir.exists())
         if (!tmpdir.mkpath(dir))
         {
-            QMessageBox directoryMsg(QApplication::activeWindow());
-            directoryMsg.setIcon(QMessageBox::Warning);
-            directoryMsg.setWindowTitle(QMessageBox::tr("Main - Error"));
-            directoryMsg.setText(QMessageBox::tr("Cannot create directory %1").arg(dir));
-            directoryMsg.setWindowModality(Qt::WindowModal);
-            directoryMsg.exec();
+            MessageDialog::ShowErrorMessage(HWApplication::tr("Cannot create directory %1").arg(dir));
             return false;
         }
     return true;
 }
 
-bool checkForFile(const QString & file)
-{
-    QFile tmpfile(file);
-    if (!tmpfile.exists())
-        return tmpfile.open(QFile::WriteOnly);
-    else
-        return true;
-}
-
 // Guaranteed to be the last thing ran in the application's life time.
 // Closes resources that need to exist as long as possible.
 void closeResources(void)
@@ -156,10 +132,6 @@
     cocoaInit = new CocoaInitializer(); // Creates the autoreleasepool preventing cocoa object leaks on OS X.
 #endif
 
-#ifndef _WIN32
-    signal(SIGINT, &terminateFrontend);
-#endif
-
     HWApplication app(argc, argv);
 
     QLabel *splash = NULL;
@@ -167,7 +139,7 @@
     QPixmap pixmap(":res/splash.png");
     splash = new QLabel(0, Qt::FramelessWindowHint|Qt::WindowStaysOnTopHint);
     splash->setAttribute(Qt::WA_TranslucentBackground);
-    const QRect deskSize = QApplication::desktop()->screenGeometry(-1);
+    const QRect deskSize = HWApplication::desktop()->screenGeometry(-1);
     QPoint splashCenter = QPoint( (deskSize.width() - pixmap.width())/2,
                                   (deskSize.height() - pixmap.height())/2 );
     splash->move(splashCenter);
@@ -276,16 +248,9 @@
 
     datadir->cd(bindir->absolutePath());
     datadir->cd(*cDataDir);
-    if(!datadir->cd("Data"))
+    if (!datadir->cd("Data"))
     {
-        QMessageBox missingMsg(QApplication::activeWindow());
-        missingMsg.setIcon(QMessageBox::Critical);
-        missingMsg.setWindowTitle(QMessageBox::tr("Main - Error"));
-        missingMsg.setText(QMessageBox::tr("Failed to open data directory:\n%1\n\n"
-                                           "Please check your installation!").
-                                            arg(datadir->absolutePath()+"/Data"));
-        missingMsg.setWindowModality(Qt::WindowModal);
-        missingMsg.exec();
+        MessageDialog::ShowFatalMessage(HWApplication::tr("Failed to open data directory:\n%1\n\nPlease check your installation!").arg(datadir->absolutePath()+"/Data"));
         return 1;
     }
 
@@ -296,7 +261,7 @@
     engine->setWriteDir(cfgdir->absolutePath());
     engine->mountPacks();
 
-    checkForFile("physfs://hedgewars.ini");
+    DataManager::ensureFileExists("physfs://hedgewars.ini");
 
     QTranslator Translator;
     {
@@ -355,9 +320,13 @@
     if (file.open(QIODevice::ReadOnly | QIODevice::Text))
         style.append(file.readAll());
 
+    qWarning("Starting Hedgewars %s-r%d (%s)", qPrintable(*cVersionString), cRevisionString->toInt(), qPrintable(*cHashString));
+
     app.form = new HWForm(NULL, style);
     app.form->show();
     if(splash)
         splash->close();
+    if (app.urlString)
+        app.fakeEvent();
     return app.exec();
 }
--- a/QTfrontend/model/GameStyleModel.cpp	Wed Feb 20 02:21:58 2013 +0100
+++ b/QTfrontend/model/GameStyleModel.cpp	Tue Apr 02 21:00:57 2013 +0200
@@ -25,12 +25,11 @@
 
 #include "physfs.h"
 #include "GameStyleModel.h"
+#include "hwconsts.h"
 
 
 void GameStyleModel::loadGameStyles()
 {
-    const QString appDir = QString(PHYSFS_getBaseDir());
-
     beginResetModel();
 
     // empty list, so that we can (re)fill it
@@ -81,7 +80,7 @@
 
         // detect if script is dlc
         QString scriptPath = PHYSFS_getRealDir(QString("Scripts/Multiplayer/%1.lua").arg(script).toLocal8Bit().data());
-        bool isDLC = !scriptPath.startsWith(appDir);
+        bool isDLC = !scriptPath.startsWith(datadir->absolutePath());
 
         QStandardItem * item = new QStandardItem((isDLC ? "*" : "") + name);
 
--- a/QTfrontend/model/MapModel.cpp	Wed Feb 20 02:21:58 2013 +0100
+++ b/QTfrontend/model/MapModel.cpp	Tue Apr 02 21:00:57 2013 +0200
@@ -26,6 +26,7 @@
 #include "physfs.h"
 #include "MapModel.h"
 #include "HWApplication.h"
+#include "hwconsts.h"
 
 MapModel::MapInfo MapModel::MapInfoRandom = {MapModel::GeneratedMap, "+rnd+", "", 0, "", "", ""};
 MapModel::MapInfo MapModel::MapInfoMaze = {MapModel::GeneratedMaze, "+maze+", "", 0, "", "", ""};
@@ -33,8 +34,6 @@
 
 void MapModel::loadMaps(MapType maptype)
 {
-    const QString appDir = QString(PHYSFS_getBaseDir());
-
     // this method resets the contents of this model (important to know for views).
     beginResetModel();
 
@@ -99,7 +98,7 @@
 
             // detect if map is dlc
             QString mapDir = PHYSFS_getRealDir(QString("Maps/%1/map.cfg").arg(map).toLocal8Bit().data());
-            dlc = !mapDir.startsWith(appDir);
+            dlc = !mapDir.startsWith(datadir->absolutePath());
 
             // let's use some semi-sane hedgehog limit, rather than none
             if (limit == 0)
--- a/QTfrontend/model/ThemeModel.cpp	Wed Feb 20 02:21:58 2013 +0100
+++ b/QTfrontend/model/ThemeModel.cpp	Tue Apr 02 21:00:57 2013 +0200
@@ -23,6 +23,7 @@
 
 #include "physfs.h"
 #include "ThemeModel.h"
+#include "hwconsts.h"
 
 ThemeModel::ThemeModel(QObject *parent) :
     QAbstractListModel(parent)
@@ -50,8 +51,6 @@
 
 void ThemeModel::loadThemes()
 {
-    const QString appDir = QString(PHYSFS_getBaseDir());
-
     beginResetModel();
 
     DataManager & datamgr = DataManager::instance();
@@ -77,24 +76,21 @@
 
         // detect if theme is dlc
         QString themeDir = PHYSFS_getRealDir(QString("Themes/%1/icon.png").arg(theme).toLocal8Bit().data());
-        dataset.insert(Qt::UserRole + 2, !themeDir.startsWith(appDir));
+        bool isDLC = !themeDir.startsWith(datadir->absolutePath());
+        dataset.insert(IsDlcRole, isDLC);
 
         // set icon path
-        dataset.insert(Qt::UserRole + 1, iconpath);
+        dataset.insert(IconPathRole, iconpath);
 
         // set name
-        dataset.insert(Qt::DisplayRole, theme);
+        dataset.insert(ActualNameRole, theme);
 
-        // load and set icon
-        QIcon icon;
-        icon.addPixmap(QPixmap(iconpath), QIcon::Normal);
-        icon.addPixmap(QPixmap(iconpath), QIcon::Disabled);
-
-        dataset.insert(Qt::DecorationRole, icon);
+        // set displayed name
+        dataset.insert(Qt::DisplayRole, (isDLC ? "*" : "") + theme);
 
         // load and set preview icon
         QIcon preview(QString("physfs://Themes/%1/icon@2x.png").arg(theme));
-        dataset.insert(Qt::UserRole, preview);
+        dataset.insert(Qt::DecorationRole, preview);
 
         m_data.append(dataset);
     }
--- a/QTfrontend/model/ThemeModel.h	Wed Feb 20 02:21:58 2013 +0100
+++ b/QTfrontend/model/ThemeModel.h	Tue Apr 02 21:00:57 2013 +0200
@@ -39,6 +39,7 @@
         Q_OBJECT
 
     public:
+        enum Roles { ActualNameRole = Qt::UserRole, IsDlcRole, IconPathRole };
         explicit ThemeModel(QObject *parent = 0);
 
         int rowCount(const QModelIndex &parent = QModelIndex()) const;
--- a/QTfrontend/net/netudpserver.cpp	Wed Feb 20 02:21:58 2013 +0100
+++ b/QTfrontend/net/netudpserver.cpp	Tue Apr 02 21:00:57 2013 +0200
@@ -20,13 +20,14 @@
 #include <QUdpSocket>
 
 #include "netudpserver.h"
+#include "hwconsts.h"
 
 HWNetUdpServer::HWNetUdpServer(QObject *parent, const QString & descr, quint16 port) :
     HWNetRegisterServer(parent, descr, port),
     m_descr(descr)
 {
     pUdpSocket = new QUdpSocket(this);
-    pUdpSocket->bind(46631);
+    pUdpSocket->bind(NETGAME_DEFAULT_PORT);
     connect(pUdpSocket, SIGNAL(readyRead()), this, SLOT(onClientRead()));
 }
 
--- a/QTfrontend/net/netudpwidget.cpp	Wed Feb 20 02:21:58 2013 +0100
+++ b/QTfrontend/net/netudpwidget.cpp	Tue Apr 02 21:00:57 2013 +0200
@@ -20,6 +20,7 @@
 #include <QUdpSocket>
 
 #include "netudpwidget.h"
+#include "hwconsts.h"
 
 HWNetUdpModel::HWNetUdpModel(QObject* parent) :
     HWNetServersModel(parent)
@@ -36,7 +37,7 @@
 
     reset();
 
-    pUdpSocket->writeDatagram("hedgewars client", QHostAddress::Broadcast, 46631);
+    pUdpSocket->writeDatagram("hedgewars client", QHostAddress::Broadcast, NETGAME_DEFAULT_PORT);
 }
 
 void HWNetUdpModel::onClientRead()
@@ -54,7 +55,7 @@
         if(packet.startsWith("hedgewars server"))
         {
             QStringList sl;
-            sl << packet.remove(0, 17) << clientAddr.toString() << "46631";
+            sl << packet.remove(0, 17) << clientAddr.toString() << QString::number(NETGAME_DEFAULT_PORT);
             games.append(sl);
         }
     }
--- a/QTfrontend/net/newnetclient.cpp	Wed Feb 20 02:21:58 2013 +0100
+++ b/QTfrontend/net/newnetclient.cpp	Tue Apr 02 21:00:57 2013 +0200
@@ -63,6 +63,8 @@
     connect(&NetSocket, SIGNAL(disconnected()), this, SLOT(OnDisconnect()));
     connect(&NetSocket, SIGNAL(error(QAbstractSocket::SocketError)), this,
             SLOT(displayError(QAbstractSocket::SocketError)));
+
+    connect(this, SIGNAL(messageProcessed()), this, SLOT(ClientRead()), Qt::QueuedConnection);
 }
 
 HWNewNet::~HWNewNet()
@@ -186,6 +188,8 @@
         {
             ParseCmd(cmdbuf);
             cmdbuf.clear();
+            emit messageProcessed();
+            return ;
         }
         else
             cmdbuf << s;
@@ -307,7 +311,7 @@
         QStringList tmp = lst;
         tmp.removeFirst();
         m_roomsListModel->setRoomsList(tmp);
-        if (m_nick_registered == false)
+        if (m_private_game == false && m_nick_registered == false)
         {
             emit NickNotRegistered(mynick);
         }
@@ -486,9 +490,9 @@
                 emit connected();
             }
 
+            m_playersModel->addPlayer(lst[i]);
             emit nickAddedLobby(lst[i], false);
             emit chatStringLobby(lst[i], tr("%1 *** %2 has joined").arg('\x03').arg("|nick|"));
-            m_playersModel->addPlayer(lst[i]);
         }
         return;
     }
@@ -512,9 +516,12 @@
         QString roomName = tmp.takeFirst();
         m_roomsListModel->updateRoom(roomName, tmp);
 
-        // keep track of room name so correct name is displayed when you become room admin
+        // keep track of room name so correct name is displayed
         if(myroom == roomName)
+        {
             myroom = tmp[1];
+            emit roomNameUpdated(myroom);
+        }
 
         return;
     }
@@ -598,6 +605,18 @@
         return;
     }
 
+    if(lst[0] == "JOINING")
+    {
+        if(lst.size() < 2)
+        {
+            qWarning("Net: Bad JOINING message");
+            return;
+        }
+
+        myroom = lst[1];
+        emit roomNameUpdated(myroom);
+    }
+
     if(netClientState == InLobby && lst[0] == "JOINED")
     {
         if(lst.size() < 2 || lst[1] != mynick)
@@ -617,9 +636,9 @@
                     emit configAsked();
             }
 
+            m_playersModel->playerJoinedRoom(lst[i]);
             emit nickAdded(lst[i], isChief && (lst[i] != mynick));
             emit chatStringFromNet(tr("%1 *** %2 has joined the room").arg('\x03').arg(lst[i]));
-            m_playersModel->playerJoinedRoom(lst[i]);
         }
         return;
     }
@@ -963,6 +982,11 @@
     RawSendNet(QString("TOGGLE_RESTRICT_TEAMS"));
 }
 
+void HWNewNet::toggleRegisteredOnly()
+{
+    RawSendNet(QString("TOGGLE_REGISTERED_ONLY"));
+}
+
 void HWNewNet::clearAccountsCache()
 {
     RawSendNet(QString("CLEAR_ACCOUNTS_CACHE"));
--- a/QTfrontend/net/newnetclient.h	Wed Feb 20 02:21:58 2013 +0100
+++ b/QTfrontend/net/newnetclient.h	Tue Apr 02 21:00:57 2013 +0200
@@ -61,6 +61,7 @@
         QAbstractItemModel * lobbyPlayersModel();
         QAbstractItemModel * roomPlayersModel();
         bool allPlayersReady();
+        bool m_private_game;
 
     private:
         bool isChief;
@@ -105,6 +106,7 @@
         void FromNet(const QByteArray & buf);
         void adminAccess(bool);
         void roomMaster(bool);
+        void roomNameUpdated(const QString & name);
 
         void netSchemeConfig(QStringList &);
         void paramChanged(const QString & param, const QStringList & value);
@@ -130,6 +132,8 @@
 
         void setMyReadyStatus(bool isReady);
 
+        void messageProcessed();
+
     public slots:
         void ToggleReady();
         void chatLineToNet(const QString& str);
@@ -160,6 +164,7 @@
         void startGame();
         void toggleRestrictJoins();
         void toggleRestrictTeamAdds();
+        void toggleRegisteredOnly();
         void partRoom();
         void clearAccountsCache();
         void getBanList();
--- a/QTfrontend/net/tcpBase.cpp	Wed Feb 20 02:21:58 2013 +0100
+++ b/QTfrontend/net/tcpBase.cpp	Tue Apr 02 21:00:57 2013 +0200
@@ -19,13 +19,12 @@
 
 #include "tcpBase.h"
 
-#include <QMessageBox>
 #include <QList>
-#include <QApplication>
 #include <QImage>
 #include <QThread>
 
 #include "hwconsts.h"
+#include "MessageDialog.h"
 
 #ifdef HWLIBRARY
 extern "C" void Game(char**arguments);
@@ -89,13 +88,7 @@
         IPCServer->setMaxPendingConnections(1);
         if (!IPCServer->listen(QHostAddress::LocalHost))
         {
-            QMessageBox deniedMsg(QApplication::activeWindow());
-            deniedMsg.setIcon(QMessageBox::Critical);
-            deniedMsg.setWindowTitle(QMessageBox::tr("TCP - Error"));
-            deniedMsg.setText(QMessageBox::tr("Unable to start the server: %1.").arg(IPCServer->errorString()));
-            deniedMsg.setWindowModality(Qt::WindowModal);
-            deniedMsg.exec();
-
+            MessageDialog::ShowFatalMessage(tr("Unable to start server at %1.").arg(IPCServer->errorString()));
             exit(0); // FIXME - should be graceful exit here (lower Critical -> Warning above when implemented)
         }
     }
@@ -141,9 +134,10 @@
     connect(process, SIGNAL(error(QProcess::ProcessError)), this, SLOT(StartProcessError(QProcess::ProcessError)));
     QStringList arguments=getArguments();
 
+#ifdef DEBUG
     // redirect everything written on stdout/stderr
-    if(isDevBuild)
-        process->setProcessChannelMode(QProcess::ForwardedChannels);
+    process->setProcessChannelMode(QProcess::ForwardedChannels);
+#endif
 
     process->start(bindir->absolutePath() + "/hwengine", arguments);
 #endif
@@ -171,14 +165,7 @@
 
 void TCPBase::StartProcessError(QProcess::ProcessError error)
 {
-    QMessageBox deniedMsg(QApplication::activeWindow());
-    deniedMsg.setIcon(QMessageBox::Critical);
-    deniedMsg.setWindowTitle(QMessageBox::tr("TCP - Error"));
-    deniedMsg.setText(QMessageBox::tr("Unable to run engine at ") + bindir->absolutePath() + "/hwengine\n" +
-                      QMessageBox::tr("Error code: %1").arg(error));
-    deniedMsg.setWindowModality(Qt::WindowModal);
-    deniedMsg.exec();
-
+    MessageDialog::ShowFatalMessage(tr("Unable to run engine at %1\nError code: %2").arg(bindir->absolutePath() + "/hwengine").arg(error));
     ClientDisconnect();
 }
 
--- a/QTfrontend/res/css/qt.css	Wed Feb 20 02:21:58 2013 +0100
+++ b/QTfrontend/res/css/qt.css	Tue Apr 02 21:00:57 2013 +0200
@@ -275,21 +275,21 @@
 border-radius: 3px;
 }
 
-HatButton {
+HatButton, ThemeButton {
 text-align: left;
 }
 
-#hatList, #hatList:hover {
+#hatList, #hatList:hover, #themeList, #themeList:hover {
 border-color: #F6CB1C;
 }
 
-#hatList QScrollBar {
+#hatList QScrollBar, #themeList QScrollBar {
 background-color: #130F2A;
 border-top-right-radius: 10px;
 border-bottom-right-radius: 10px;
 }
 
-#hatList {
+#hatList, #themeList {
 border-color: #F6CB1C;
 border-width: 3px;
 border-style: solid;
@@ -297,7 +297,7 @@
 border-top-left-radius: 0px;
 }
 
-#hatList::item {
+#hatList::item, #themeList::item {
 background-color: #11084A;
 padding: 4px;
 border-radius: 10px;
@@ -307,11 +307,11 @@
 border-color: #11084A;
 }
 
-#hatList::item:hover {
+#hatList::item:hover, #themeList::item:hover {
 background-color: #150A61;
 }
 
-#hatList::item:selected {
+#hatList::item:selected, #themeList::item:selected {
 background-color: #150A61;
 }
 
--- a/QTfrontend/res/html/about.html	Wed Feb 20 02:21:58 2013 +0100
+++ b/QTfrontend/res/html/about.html	Tue Apr 02 21:00:57 2013 +0200
@@ -4,7 +4,9 @@
 <title>Hedgewars - Authors</title>
 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
 <style type="text/css">
-     { color: #ffcc00; }
+     body { color: orange; }
+     a { color: #ffe270; }
+     a:hover { color: yellow; }
 </style>
 </head>
 <body>
@@ -30,6 +32,9 @@
         WebGL port, some pas2c and GLES2 work: Meng Xiangyun &lt;<a href="mailto:xymengxy@gmail.com">xymengxy@gmail.com</a>&gt;<br>
         Video recording: Stepan Podoskin &lt;<a href="mailto:stepik-777@mail.ru">stepik-777@mail.ru</a>&gt;<br>
         Campaign support, first campaign: Szabolcs Orb&agrave;n &lt;<a href="mailto:szabibibi@gmail.com">szabibibi@gmail.com</a>&gt;<br>
+        Keybinds, feedback, maps and hats interfaces: Drew Gottlieb &lt;<a href="mailto:gottlieb.drew@gmail.com">gottlieb.drew@gmail.com</a>&gt;<br>
+        Login dialogs, frontend improvements: Ondrej Skopek &lt;<a href="mailto:skopekondrej@gmail.com">skopekondrej@gmail.com</a>&gt;<br>
+        Icegun weapon: Julia Struchenko &lt;<a href="mailto:urbertar@gmail.com">urbertar@gmail.com</a>&gt;<br>
         </p>
 
         <h2>Art:</h2>
--- a/QTfrontend/team.cpp	Wed Feb 20 02:21:58 2013 +0100
+++ b/QTfrontend/team.cpp	Tue Apr 02 21:00:57 2013 +0200
@@ -226,7 +226,9 @@
         OldTeamName = m_name;
     }
 
-    QSettings teamfile(QString("physfs://Teams/%1.hwt").arg(m_name), QSettings::IniFormat, 0);
+    QString fileName = QString("physfs://Teams/%1.hwt").arg(m_name);
+    DataManager::ensureFileExists(fileName);
+    QSettings teamfile(fileName, QSettings::IniFormat, 0);
     teamfile.setIniCodec("UTF-8");
     teamfile.setValue("Team/Name", m_name);
     teamfile.setValue("Team/Grave", m_grave);
--- a/QTfrontend/ui/dialog/input_ip.cpp	Wed Feb 20 02:21:58 2013 +0100
+++ b/QTfrontend/ui/dialog/input_ip.cpp	Tue Apr 02 21:00:57 2013 +0200
@@ -23,6 +23,7 @@
 #include <QLabel>
 
 #include "input_ip.h"
+#include "hwconsts.h"
 
 HWHostPortDialog::HWHostPortDialog(QWidget* parent) : QDialog(parent)
 {
@@ -66,5 +67,5 @@
 
 void HWHostPortDialog::setDefaultPort()
 {
-    sbPort->setValue(46631);
+    sbPort->setValue(NETGAME_DEFAULT_PORT);
 }
--- a/QTfrontend/ui/dialog/input_password.cpp	Wed Feb 20 02:21:58 2013 +0100
+++ b/QTfrontend/ui/dialog/input_password.cpp	Tue Apr 02 21:00:57 2013 +0200
@@ -29,18 +29,14 @@
 {
     setWindowTitle(tr("Login"));
 
-    QString titleLabelText = "To connect to the server, please log in.\n\nIf you don't have an account on www.hedgewars.org,\njust enter your nickname.";
-    QString nickLabelText = "Nickname:";
-    QString passLabelText = "Password:";
-
     QGridLayout * layout = new QGridLayout(this);
 
     QLabel * titleLabel = new QLabel(this);
-    titleLabel->setText(titleLabelText);
+    titleLabel->setText(tr("To connect to the server, please log in.\n\nIf you don't have an account on www.hedgewars.org,\njust enter your nickname."));
     layout->addWidget(titleLabel, 0, 0);
 
     QLabel * nickLabel = new QLabel(this);
-    nickLabel->setText(nickLabelText);
+    nickLabel->setText(tr("Nickname:"));
     layout->addWidget(nickLabel, 1, 0);
 
     leNickname = new QLineEdit(this);
@@ -48,7 +44,7 @@
     layout->addWidget(leNickname, 2, 0);
 
     QLabel * passLabel = new QLabel(this);
-    passLabel->setText(passLabelText);
+    passLabel->setText(tr("Password:"));
     layout->addWidget(passLabel, 3, 0);
 
     lePassword = new QLineEdit(this);
--- a/QTfrontend/ui/mouseoverfilter.cpp	Wed Feb 20 02:21:58 2013 +0100
+++ b/QTfrontend/ui/mouseoverfilter.cpp	Tue Apr 02 21:00:57 2013 +0200
@@ -5,6 +5,7 @@
 #include <QLabel>
 #include <QLineEdit>
 #include <QCheckBox>
+#include <QListView>
 
 #include "mouseoverfilter.h"
 #include "ui/page/AbstractPage.h"
@@ -31,6 +32,10 @@
             abstractpage->setButtonDescription(widget->whatsThis());
         else if (widget->toolTip() != NULL)
             abstractpage->setButtonDescription(widget->toolTip());
+    }
+    else if (event->type() == QEvent::FocusIn)
+    {
+        abstractpage = qobject_cast<AbstractPage*>(ui->Pages->currentWidget());
 
         // play a sound when mouse hovers certain ui elements
         QPushButton * button = dynamic_cast<QPushButton*>(dist);
@@ -39,7 +44,8 @@
         QComboBox * droplist = dynamic_cast<QComboBox*>(dist);
         QSlider * slider = dynamic_cast<QSlider*>(dist);
         QTabWidget * tab = dynamic_cast<QTabWidget*>(dist);
-        if (button || textfield || checkbox || droplist || slider || tab)
+        QListView * listview = dynamic_cast<QListView*>(dist);
+        if (button || textfield || checkbox || droplist || slider || tab || listview)
         {
             SDLInteraction::instance().playSoundFile("/Sounds/steps.ogg");
         }
@@ -50,9 +56,9 @@
     {
         abstractpage = qobject_cast<AbstractPage*>(ui->Pages->currentWidget());
 
-        if (abstractpage->getDefautDescription() != NULL)
+        if (abstractpage->getDefaultDescription() != NULL)
         {
-            abstractpage->setButtonDescription( * abstractpage->getDefautDescription());
+            abstractpage->setButtonDescription( * abstractpage->getDefaultDescription());
         }
         else
             abstractpage->setButtonDescription("");
--- a/QTfrontend/ui/page/AbstractPage.cpp	Wed Feb 20 02:21:58 2013 +0100
+++ b/QTfrontend/ui/page/AbstractPage.cpp	Tue Apr 02 21:00:57 2013 +0200
@@ -53,7 +53,7 @@
     // add back/exit button
     btnBack = formattedButton(":/res/Exit.png", true);
     btnBack->setWhatsThis(tr("Go back"));
-    bottomLeftLayout->addWidget(btnBack, 0);
+    bottomLeftLayout->addWidget(btnBack, 0, Qt::AlignBottom);
 
     // add body layout as defined by the subclass
     pageLayout->addLayout(bodyLayoutDefinition(), 0, 0, 1, 3);
@@ -158,13 +158,13 @@
     descLabel->setText(desc);
 }
 
-void AbstractPage::setDefautDescription(QString text)
+void AbstractPage::setDefaultDescription(QString text)
 {
     *defautDesc = text;
     descLabel->setText(text);
 }
 
-QString * AbstractPage::getDefautDescription()
+QString * AbstractPage::getDefaultDescription()
 {
     return defautDesc;
 }
--- a/QTfrontend/ui/page/AbstractPage.h	Wed Feb 20 02:21:58 2013 +0100
+++ b/QTfrontend/ui/page/AbstractPage.h	Tue Apr 02 21:00:57 2013 +0200
@@ -70,12 +70,12 @@
         *
         * @param text the defaut desc
         */
-        void setDefautDescription(QString text);
+        void setDefaultDescription(QString text);
 
         /**
         * @brief Get the desc defaut text
         */
-        QString * getDefautDescription();
+        QString * getDefaultDescription();
 
     signals:
 
--- a/QTfrontend/ui/page/pagemain.cpp	Wed Feb 20 02:21:58 2013 +0100
+++ b/QTfrontend/ui/page/pagemain.cpp	Tue Apr 02 21:00:57 2013 +0200
@@ -61,12 +61,12 @@
     netLayout->setSpacing(20);
     netLayout->setAlignment(Qt::AlignHCenter);
 
-    BtnNetLocal = addButton("Play local network game", (QBoxLayout*)netLayout, 0, false);
+    BtnNetLocal = addButton(tr("Play local network game"), (QBoxLayout*)netLayout, 0, false);
     BtnNetLocal->setWhatsThis(tr("Play a game across a local area network"));
     BtnNetLocal->setFixedSize(BtnNet->width() - 50, 60);
     BtnNetLocal->setVisible(false);
 
-    BtnNetOfficial = addButton("Play official network game", (QBoxLayout*)netLayout, 0, false);
+    BtnNetOfficial = addButton(tr("Play official network game"), (QBoxLayout*)netLayout, 0, false);
     BtnNetOfficial->setWhatsThis(tr("Play a game on an official server"));
     BtnNetOfficial->setFixedSize(BtnNet->width() - 50, 60);
     BtnNetOfficial->setVisible(false);
@@ -77,13 +77,13 @@
     BtnInfo->setWhatsThis(tr("Read about who is behind the Hedgewars Project"));
     pageLayout->setAlignment(BtnInfo, Qt::AlignHCenter);
 
-    BtnFeedback = addButton("Feedback", pageLayout, 4, 0, 1, 4, false);
-    BtnFeedback->setFixedSize(86, 27);
+    BtnFeedback = addButton(tr("Feedback"), pageLayout, 4, 0, 1, 4, false);
+    BtnFeedback->setStyleSheet("padding: 5px 10px");
     BtnFeedback->setWhatsThis(tr("Leave a feedback here reporting issues, suggesting features or just saying how you like Hedgewars"));
     pageLayout->setAlignment(BtnFeedback, Qt::AlignHCenter);
 
     BtnDataDownload = addButton(tr("Downloadable Content"), pageLayout, 5, 0, 1, 4, false);
-    BtnDataDownload->setFixedSize(176, 27);
+    BtnDataDownload->setStyleSheet("padding: 5px 10px");
     BtnDataDownload->setWhatsThis(tr("Access the user created content downloadable from our website"));
     pageLayout->setAlignment(BtnDataDownload, Qt::AlignHCenter);
 
@@ -120,6 +120,8 @@
 void PageMain::connectSignals()
 {
     connect(BtnNet, SIGNAL(clicked()), this, SLOT(toggleNetworkChoice()));
+    connect(BtnNetLocal, SIGNAL(clicked()), this, SLOT(toggleNetworkChoice()));
+    connect(BtnNetOfficial, SIGNAL(clicked()), this, SLOT(toggleNetworkChoice()));
     // TODO: add signal-forwarding required by (currently missing) encapsulation
 }
 
@@ -127,17 +129,15 @@
 {
     initPage();
 
-    if(frontendEffects) setAttribute(Qt::WA_NoSystemBackground, true);
+    if(frontendEffects)
+        setAttribute(Qt::WA_NoSystemBackground, true);
     mainNote->setOpenExternalLinks(true);
 
-    if(!isDevBuild)
-    {
-        setDefautDescription(QLabel::tr("Tip: ") + randomTip());
-    }
-    else
-    {
-        setDefautDescription(QLabel::tr("This development build is 'work in progress' and may not be compatible with other versions of the game, while some features might be broken or incomplete!"));
-    }
+#ifdef DEBUG
+    setDefaultDescription(QLabel::tr("This development build is 'work in progress' and may not be compatible with other versions of the game, while some features might be broken or incomplete!"));
+#else
+    setDefaultDescription(QLabel::tr("Tip: ") + randomTip());
+#endif
 
 }
 
--- a/QTfrontend/ui/page/pagenet.cpp	Wed Feb 20 02:21:58 2013 +0100
+++ b/QTfrontend/ui/page/pagenet.cpp	Tue Apr 02 21:00:57 2013 +0200
@@ -72,7 +72,12 @@
 
     BtnNetSvrStart = formattedButton(QPushButton::tr("Start server"));
     BtnNetSvrStart->setMinimumWidth(180);
-    BtnNetSvrStart->setVisible(haveServer);
+    QString serverPath = bindir->absolutePath() + "/hedgewars-server";
+#ifdef Q_WS_WIN
+    serverPath += + ".exe";
+#endif
+    QFile server(serverPath);
+    BtnNetSvrStart->setVisible(server.exists());
 
     footerLayout->addStretch();
     footerLayout->addWidget(BtnNetSvrStart);
--- a/QTfrontend/ui/page/pagenetgame.cpp	Wed Feb 20 02:21:58 2013 +0100
+++ b/QTfrontend/ui/page/pagenetgame.cpp	Tue Apr 02 21:00:57 2013 +0200
@@ -22,6 +22,7 @@
 #include <QAction>
 #include <QMenu>
 #include <QMessageBox>
+#include <QSettings>
 
 #include "pagenetgame.h"
 #include "gamecfgwidget.h"
@@ -53,13 +54,9 @@
     leRoomName->setMaximumWidth(600);
     leRoomName->setFixedHeight(30);
     leRoomName->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
+    leRoomName->setStyleSheet("border-right: 0; padding-left: 4px; border-top-right-radius: 0px; border-bottom-right-radius: 0px;");
     roomConfigLayout->addWidget(leRoomName, 100);
 
-    QLabel * lblRoomName = new QLabel(tr("Room name: "), leRoomName);
-    lblRoomName->setStyleSheet("font: 12px; font-weight: bold;");
-    lblRoomName->setStyleSheet(QString("font: 12px; font-weight: bold; background: none; margin-left: -%1px; margin-top: 8px;").arg(lblRoomName->width() - 20));
-    leRoomName->setStyleSheet(QString("font: 12px; border-right: 0; padding-left: %1px; padding-bottom: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px;").arg(lblRoomName->width() - 14));
-
     BtnUpdate = new QPushButton();
     BtnUpdate->setEnabled(false);
     BtnUpdate->setText(tr("Update"));
@@ -173,8 +170,11 @@
     restrictJoins->setCheckable(true);
     restrictTeamAdds = new QAction(QAction::tr("Restrict Team Additions"), menu);
     restrictTeamAdds->setCheckable(true);
+    restrictUnregistered = new QAction(QAction::tr("Restrict Unregistered Players Join"), menu);
+    restrictUnregistered->setCheckable(true);
     menu->addAction(restrictJoins);
     menu->addAction(restrictTeamAdds);
+    menu->addAction(restrictUnregistered);
 
     BtnMaster->setMenu(menu);
 
@@ -188,9 +188,13 @@
     int newHeight = event->size().height();
 
     if (newHeight < cutoffHeight && oldHeight >= cutoffHeight)
+    {
         pGameCFG->setTabbed(true);
+    }
     else if (newHeight >= cutoffHeight && oldHeight < cutoffHeight)
+    {
         pGameCFG->setTabbed(false);
+    }
 }
 
 void PageNetGame::displayError(const QString & message)
@@ -227,9 +231,10 @@
 {
     if (!leRoomName->text().trimmed().isEmpty())
     {
-        emit askForUpdateRoomName(leRoomName->text());
+        m_gameSettings->setValue("frontend/lastroomname", leRoomName->text());
         leRoomName->rememberCurrentText();
         BtnUpdate->setEnabled(false);
+        emit askForUpdateRoomName(leRoomName->text());
     }
     else
     {
@@ -240,6 +245,7 @@
         roomMsg.setText(QMessageBox::tr("Please enter room name"));
         roomMsg.setWindowModality(Qt::WindowModal);
         roomMsg.exec();
+        leRoomName->setFocus();
     }
 }
 
@@ -259,9 +265,16 @@
     BtnUpdate->setVisible(isMaster);
     leRoomName->setVisible(isMaster);
     lblRoomNameReadOnly->setVisible(!isMaster);
+    pGameCFG->setMaster(isMaster);
+    repaint();
 }
 
 void PageNetGame::setUser(const QString & nickname)
 {
     chatWidget->setUser(nickname);
 }
+
+void PageNetGame::setSettings(QSettings *settings)
+{
+    m_gameSettings = settings;
+}
--- a/QTfrontend/ui/page/pagenetgame.h	Wed Feb 20 02:21:58 2013 +0100
+++ b/QTfrontend/ui/page/pagenetgame.h	Tue Apr 02 21:00:57 2013 +0200
@@ -26,6 +26,7 @@
 class HWChatWidget;
 class TeamSelWidget;
 class GameCFGWidget;
+class QSettings;
 
 class PageNetGame : public AbstractPage
 {
@@ -34,11 +35,7 @@
     public:
         PageNetGame(QWidget* parent);
 
-        /**
-         * Sets the room name to display.
-         * @param roomName room name to be displayed.
-         */
-        void setRoomName(const QString & roomName);
+        void setSettings(QSettings * settings);
 
         void displayError(const QString & message);
         void displayNotice(const QString & message);
@@ -52,6 +49,7 @@
 
         QAction * restrictJoins;
         QAction * restrictTeamAdds;
+        QAction * restrictUnregistered;
 
         HWChatWidget* chatWidget;
 
@@ -59,6 +57,7 @@
         GameCFGWidget* pGameCFG;
 
     public slots:
+        void setRoomName(const QString & roomName);
         void setReadyStatus(bool isReady);
         void setUser(const QString & nickname);
         void onUpdateClick();
@@ -80,6 +79,7 @@
         QLayout * footerLayoutLeftDefinition();
         void connectSignals();
 
+        QSettings * m_gameSettings;
         QPushButton * btnSetup;
         QLabel * lblRoomNameReadOnly;
 };
--- a/QTfrontend/ui/page/pagenetserver.cpp	Wed Feb 20 02:21:58 2013 +0100
+++ b/QTfrontend/ui/page/pagenetserver.cpp	Tue Apr 02 21:00:57 2013 +0200
@@ -24,8 +24,13 @@
 #include <QLabel>
 #include <QLineEdit>
 #include <QSpinBox>
+#include <QTcpSocket>
+#include <QHostAddress>
+#include <QClipboard>
 
 #include "pagenetserver.h"
+#include "hwconsts.h"
+#include "HWApplication.h"
 
 QLayout * PageNetServer::bodyLayoutDefinition()
 {
@@ -59,14 +64,21 @@
     gbLayout->addWidget(labelPort, 1, 0);
 
     sbPort = new QSpinBox(gb);
-    sbPort->setMinimum(0);
+    sbPort->setMinimum(1024);
     sbPort->setMaximum(65535);
     gbLayout->addWidget(sbPort, 1, 1);
 
     BtnDefault = new QPushButton(gb);
-    BtnDefault->setText(QPushButton::tr("default"));
+    BtnDefault->setMinimumWidth(50);
+    BtnDefault->setText(QPushButton::tr("Reset"));
+    BtnDefault->setWhatsThis(QPushButton::tr("Set the default server port for Hedgewars"));
     gbLayout->addWidget(BtnDefault, 1, 2);
 
+    BtnShare = new QPushButton(gb);
+    BtnShare->setText(QPushButton::tr("Invite your friends to your server in just 1 click!"));
+    BtnShare->setWhatsThis(QPushButton::tr("Click to copy your unique server URL in your clipboard. Send this link to your friends ands and they will be able to join you."));
+    gbLayout->addWidget(BtnShare, 2, 1);
+
     return pageLayout;
 }
 
@@ -75,6 +87,7 @@
     QHBoxLayout * bottomLayout = new QHBoxLayout();
 
     BtnStart = formattedButton(QPushButton::tr("Start"));
+    BtnStart->setWhatsThis(QPushButton::tr("Start private server"));
     BtnStart->setMinimumWidth(180);
 
     bottomLayout->addStretch();
@@ -86,6 +99,7 @@
 void PageNetServer::connectSignals()
 {
     connect(BtnDefault, SIGNAL(clicked()), this, SLOT(setDefaultPort()));
+    connect(BtnShare, SIGNAL(clicked()), this, SLOT(copyUrl()));
 }
 
 PageNetServer::PageNetServer(QWidget* parent) : AbstractPage(parent)
@@ -95,5 +109,29 @@
 
 void PageNetServer::setDefaultPort()
 {
-    sbPort->setValue(46631);
+    sbPort->setValue(NETGAME_DEFAULT_PORT);
 }
+
+// This function assumes that the user wants to share his server while connected to
+// the Internet and that he/she is using direct access (eg no NATs). To determine the
+// IP we briefly connect to Hedgewars website and fallback to user intervention 
+// after 4 seconds of timeout.
+void PageNetServer::copyUrl()
+{
+    QString address = "hwplay://";
+
+    QTcpSocket socket;
+    socket.connectToHost("www.hedgewars.org", 80);
+    if (socket.waitForConnected(4000))
+        address += socket.localAddress().toString();
+    else
+        address += "<" + tr("Insert your address here") + ">";
+
+    if (sbPort->value() != NETGAME_DEFAULT_PORT)
+        address += ":" + QString::number(sbPort->value());
+
+    QClipboard *clipboard = HWApplication::clipboard();
+    clipboard->setText(address);
+    qDebug() << address << "copied to clipboard";
+}
+
--- a/QTfrontend/ui/page/pagenetserver.h	Wed Feb 20 02:21:58 2013 +0100
+++ b/QTfrontend/ui/page/pagenetserver.h	Tue Apr 02 21:00:57 2013 +0200
@@ -30,6 +30,7 @@
 
         QPushButton *BtnStart;
         QPushButton *BtnDefault;
+        QPushButton *BtnShare;
         QLabel *labelSD;
         QLineEdit *leServerDescr;
         QLabel *labelPort;
@@ -42,6 +43,7 @@
 
     private slots:
         void setDefaultPort();
+        void copyUrl();
 };
 
 #endif
--- a/QTfrontend/ui/page/pageroomslist.cpp	Wed Feb 20 02:21:58 2013 +0100
+++ b/QTfrontend/ui/page/pageroomslist.cpp	Tue Apr 02 21:00:57 2013 +0200
@@ -16,102 +16,187 @@
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
  */
 
+#include <QVBoxLayout>
+#include <QHBoxLayout>
 #include <QGridLayout>
-#include <QHBoxLayout>
 #include <QPushButton>
 #include <QComboBox>
 #include <QLabel>
 #include <QLineEdit>
 #include <QMessageBox>
 #include <QHeaderView>
-#include <QTableView>
+#include <QGroupBox>
+#include <QMenu>
+#include <QDebug>
 
 #include <QSortFilterProxyModel>
 
 #include "roomslistmodel.h"
 
 #include "ammoSchemeModel.h"
-#include "pageroomslist.h"
 #include "hwconsts.h"
 #include "chatwidget.h"
+#include "roomnameprompt.h"
+#include "lineeditcursor.h"
+#include "pageroomslist.h"
+
+void RoomTableView::moveDown()
+{
+    setCurrentIndex(moveCursor(QAbstractItemView::MoveDown, Qt::NoModifier));
+}
+
+void RoomTableView::moveUp()
+{
+    setCurrentIndex(moveCursor(QAbstractItemView::MoveUp, Qt::NoModifier));
+}
 
 QLayout * PageRoomsList::bodyLayoutDefinition()
 {
-    QGridLayout * pageLayout = new QGridLayout();
+    QVBoxLayout * pageLayout = new QVBoxLayout();
+    pageLayout->setSpacing(0);
+
+    QGridLayout * topLayout = new QGridLayout();
+    topLayout->setSpacing(0);
+    pageLayout->addLayout(topLayout, 0);
+
+    // Help/prompt message at top
+    QLabel * lblDesc = new QLabel(tr("Search for a room:"));
+    lblDesc->setObjectName("lblDesc");
+    lblDesc->setStyleSheet("#lblDesc { color: #130F2A; background: #F6CB1C; border: solid 4px #F6CB1C; border-top-left-radius: 10px; padding: 4px 10px;}");
+    lblDesc->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
+    lblDesc->setFixedHeight(24);
+    lblDesc->setMinimumWidth(0);
+
+    // Search text box
+    QWidget * searchContainer = new QWidget();
+    searchContainer->setFixedHeight(24);
+    searchContainer->setObjectName("searchContainer");
+    searchContainer->setStyleSheet("#searchContainer { background: #F6CB1C; border-top-right-radius: 10px; padding: 3px; }");
+    searchContainer->setFixedWidth(200);
+    searchText = new LineEditCursor(searchContainer);
+    searchText->setFixedWidth(200);
+    searchText->setMaxLength(60);
+    searchText->setFixedHeight(22);
+    searchText->setStyleSheet("LineEditCursor { border-width: 0px; border-radius: 6px; margin-top: 3px; margin-right: 3px; padding-left: 4px; padding-bottom: 2px; background-color: rgb(23, 11, 54); } LineEditCursor:hover, LineEditCursor:focus { background-color: rgb(13, 5, 68); }");
 
-    QHBoxLayout * newRoomLayout = new QHBoxLayout();
-    QLabel * roomNameLabel = new QLabel(this);
-    roomNameLabel->setText(tr("Room Name:"));
-    roomName = new QLineEdit(this);
-    roomName->setMaxLength(60);
-    newRoomLayout->addWidget(roomNameLabel);
-    newRoomLayout->addWidget(roomName);
-    pageLayout->addLayout(newRoomLayout, 0, 0, 1, 2);
+    // Corner widget
+    QLabel * corner = new QLabel();
+    corner->setPixmap(QPixmap(QString::fromUtf8(":/res/inverse-corner-bl.png")));
+    corner->setFixedSize(10, 10);
+
+    const QIcon& lp = QIcon(":/res/new.png");
+    //QSize sz = lp.actualSize(QSize(65535, 65535));
+    BtnCreate = new QPushButton();
+    BtnCreate->setText(tr("Create room"));
+    BtnCreate->setIcon(lp);
+    BtnCreate->setStyleSheet("padding: 4px 8px; margin-bottom: 6px;");
+
+    BtnJoin = new QPushButton(tr("Join room"));
+    BtnJoin->setStyleSheet("padding: 4px 8px; margin-bottom: 6px; margin-left: 6px;");
+    BtnJoin->setEnabled(false);
 
-    roomsList = new QTableView(this);
+    // Add widgets to top layout
+    topLayout->addWidget(lblDesc, 1, 0);
+    topLayout->addWidget(searchContainer, 1, 1);
+    topLayout->addWidget(corner, 1, 2, Qt::AlignBottom);
+    topLayout->addWidget(BtnCreate, 0, 4, 2, 1);
+    topLayout->addWidget(BtnJoin, 0, 5, 2, 1);
+
+    // Top layout stretch
+    topLayout->setRowStretch(0, 1);
+    topLayout->setRowStretch(1, 0);
+    topLayout->setColumnStretch(3, 1);
+
+    // Room list
+
+    roomsList = new RoomTableView(this);
     roomsList->setSelectionBehavior(QAbstractItemView::SelectRows);
     roomsList->verticalHeader()->setVisible(false);
     roomsList->horizontalHeader()->setResizeMode(QHeaderView::Interactive);
     roomsList->setAlternatingRowColors(true);
     roomsList->setShowGrid(false);
     roomsList->setSelectionMode(QAbstractItemView::SingleSelection);
-    pageLayout->addWidget(roomsList, 1, 0, 3, 2);
-    pageLayout->setRowStretch(2, 100);
+    roomsList->setStyleSheet("QTableView { border-top-left-radius: 0px; }");
+    roomsList->setFocusPolicy(Qt::NoFocus);
+    pageLayout->addWidget(roomsList, 200);
+
+    // Room filters container
+
+    QWidget * filtersContainer = new QWidget();
+    filtersContainer->setMaximumWidth(800);
+    filtersContainer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
 
-    QHBoxLayout * filterLayout = new QHBoxLayout();
+    pageLayout->addSpacing(7);
+    pageLayout->addWidget(filtersContainer, 0, Qt::AlignHCenter);
+    pageLayout->addSpacing(7);
+    
+    QHBoxLayout * filterLayout = new QHBoxLayout(filtersContainer);
+    filterLayout->setSpacing(0);
+    filterLayout->setMargin(0);
 
-    QLabel * stateLabel = new QLabel(this);
-    CBState = new QComboBox(this);
+    const int filterSpacing = 20;
+
+    // State button
 
-    filterLayout->addWidget(stateLabel);
-    filterLayout->addWidget(CBState);
-    filterLayout->addStretch(1);
+    QPushButton * btnState = new QPushButton(tr("Room state"));
+    btnState->setStyleSheet("QPushButton { padding: 2px 4px; } QPushButton:pressed { background-color: #ffcc00; border-color: #ffcc00; border-bottom-left-radius: 0px; border-bottom-right-radius: 0px; color: #11084A; }");
+    btnState->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
+    filterLayout->addWidget(btnState);
+    filterLayout->addSpacing(filterSpacing);
+
+    // State menu
 
-    QLabel * ruleLabel = new QLabel(this);
-    ruleLabel->setText(tr("Rules:"));
+    QMenu * stateMenu = new QMenu(btnState);
+    showGamesInLobby = new QAction(QAction::tr("Show games in lobby"), stateMenu);
+    showGamesInLobby->setCheckable(true);
+    showGamesInLobby->setChecked(true);
+    showGamesInProgress = new QAction(QAction::tr("Show games in-progress"), stateMenu);
+    showGamesInProgress->setCheckable(true);
+    showGamesInProgress->setChecked(true);
+    stateMenu->addAction(showGamesInLobby);
+    stateMenu->addAction(showGamesInProgress);
+    btnState->setMenu(stateMenu);
+
+    // Rules dropdown
+
     CBRules = new QComboBox(this);
+    CBRules->setStyleSheet("QComboBox { border-top-left-radius: 0px; border-bottom-left-radius: 0px; border-left-width: 2px; }");
+
+    QLabel * ruleLabel = new QLabel(tr("Rules:"), this);
+    ruleLabel->setFixedHeight(CBRules->height());
+    ruleLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+    ruleLabel->setStyleSheet("border: solid; border-width: 3px; border-right-width: 0px; border-color: #ffcc00; border-top-left-radius: 10px; border-bottom-left-radius: 10px; background-color: rgba(13, 5, 68, 70%);");
 
     filterLayout->addWidget(ruleLabel);
     filterLayout->addWidget(CBRules);
-    filterLayout->addStretch(1);
+    filterLayout->addSpacing(filterSpacing);
+
+    // Weapons dropdown
 
-    QLabel * weaponLabel = new QLabel(this);
-    weaponLabel->setText(tr("Weapons:"));
     CBWeapons = new QComboBox(this);
+    CBWeapons->setStyleSheet("QComboBox { border-top-left-radius: 0px; border-bottom-left-radius: 0px; border-left-width: 2px; }");
+
+    QLabel * weaponLabel = new QLabel(tr("Weapons:"), this);
+    weaponLabel->setFixedHeight(CBWeapons->height());
+    weaponLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+    weaponLabel->setStyleSheet("border: solid; border-width: 3px; border-right-width: 0px; border-color: #ffcc00; border-top-left-radius: 10px; border-bottom-left-radius: 10px; background-color: rgba(13, 5, 68, 70%);");
 
     filterLayout->addWidget(weaponLabel);
     filterLayout->addWidget(CBWeapons);
-    filterLayout->addStretch(1);
+    filterLayout->addSpacing(filterSpacing);
+
+    // Clear filters button
 
-    QLabel * searchLabel = new QLabel(this);
-    searchLabel->setText(tr("Search:"));
-    searchText = new QLineEdit(this);
-    searchText->setMaxLength(60);
-    searchText->setMinimumWidth(100);
-    searchText->setMaximumWidth(360);
-    filterLayout->addWidget(searchLabel);
-    filterLayout->addWidget(searchText);
-    filterLayout->setStretchFactor(searchText, 2);
+    BtnClear = addButton(tr("Clear filters"), filterLayout, 0);
+    weaponLabel->setFixedHeight(CBWeapons->height());
+    BtnClear->setStyleSheet("padding: 4px;");
 
-    pageLayout->addLayout(filterLayout, 4, 0, 1, 2);
+    // Lobby chat
 
     chatWidget = new HWChatWidget(this, false);
-    pageLayout->addWidget(chatWidget, 5, 0, 1, 3);
-    pageLayout->setRowStretch(5, 350);
-
-    BtnCreate = addButton(tr("Create"), pageLayout, 0, 2);
-    BtnJoin = addButton(tr("Join"), pageLayout, 1, 2);
-    BtnClear = addButton(tr("Clear"), pageLayout, 4, 2);
-
-    // strech all but the buttons column
-    pageLayout->setColumnStretch(0, 1);
-    pageLayout->setColumnStretch(1, 1);
-    pageLayout->setColumnStretch(2, 0);
+    pageLayout->addWidget(chatWidget, 350);
 
     CBRules->addItem(QComboBox::tr("Any"));
-    CBState->addItem(QComboBox::tr("Any"));
-    CBState->addItem(QComboBox::tr("In lobby"));
-    CBState->addItem(QComboBox::tr("In progress"));
 
     return pageLayout;
 }
@@ -120,18 +205,9 @@
 {
     QHBoxLayout * bottomLayout = new QHBoxLayout();
 
-    lblCount = new QLabel(this);
-    bottomLayout->addWidget(lblCount, 0, Qt::AlignHCenter);
-    bottomLayout->setStretchFactor(lblCount, 1);
-    lblCount->setText("?");
-    lblCount->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
-
-    BtnAdmin = addButton(tr("Admin features"), bottomLayout, 1);
-    BtnAdmin->setMinimumWidth(160);
-
-    // strech left part
-    bottomLayout->setStretch(0, 1);
-    bottomLayout->setStretch(1, 0);
+    BtnAdmin = addButton(tr("Admin features"), bottomLayout, 0);
+    BtnAdmin->setStyleSheet("padding: 4px auto;");
+    BtnAdmin->setWhatsThis(tr("Open server administration page"));
 
     return bottomLayout;
 }
@@ -143,18 +219,40 @@
     connect(BtnCreate, SIGNAL(clicked()), this, SLOT(onCreateClick()));
     connect(BtnJoin, SIGNAL(clicked()), this, SLOT(onJoinClick()));
     connect(BtnClear, SIGNAL(clicked()), this, SLOT(onClearClick()));
+    connect(searchText, SIGNAL(moveUp()), this, SLOT(moveSelectionUp()));
+    connect(searchText, SIGNAL(moveDown()), this, SLOT(moveSelectionDown()));
+    connect(searchText, SIGNAL(returnPressed()), this, SLOT(onJoinClick()));
     connect(roomsList, SIGNAL(doubleClicked (const QModelIndex &)), this, SLOT(onJoinClick()));
-    connect(CBState, SIGNAL(currentIndexChanged (int)), this, SLOT(onFilterChanged()));
+    connect(roomsList, SIGNAL(clicked (const QModelIndex &)), searchText, SLOT(setFocus()));
+    connect(showGamesInLobby, SIGNAL(triggered()), this, SLOT(onFilterChanged()));
+    connect(showGamesInProgress, SIGNAL(triggered()), this, SLOT(onFilterChanged()));
     connect(CBRules, SIGNAL(currentIndexChanged (int)), this, SLOT(onFilterChanged()));
     connect(CBWeapons, SIGNAL(currentIndexChanged (int)), this, SLOT(onFilterChanged()));
     connect(searchText, SIGNAL(textChanged (const QString &)), this, SLOT(onFilterChanged()));
     connect(this, SIGNAL(askJoinConfirmation (const QString &)), this, SLOT(onJoinConfirmation(const QString &)), Qt::QueuedConnection);
 
+    // Set focus on search box
+    connect(this, SIGNAL(pageEnter()), searchText, SLOT(setFocus()));
+
     // sorting
     connect(roomsList->horizontalHeader(), SIGNAL(sortIndicatorChanged(int, Qt::SortOrder)),
             this, SLOT(onSortIndicatorChanged(int, Qt::SortOrder)));
 }
 
+void PageRoomsList::moveSelectionUp()
+{
+    roomsList->moveUp();
+}
+
+void PageRoomsList::moveSelectionDown()
+{
+    roomsList->moveDown();
+}
+
+void PageRoomsList::roomSelectionChanged(const QModelIndex & current, const QModelIndex & previous)
+{
+    BtnJoin->setEnabled(current.isValid());
+}
 
 PageRoomsList::PageRoomsList(QWidget* parent) :
     AbstractPage(parent)
@@ -419,16 +517,21 @@
 
 void PageRoomsList::onCreateClick()
 {
-    if (roomName->text().size())
-        emit askForCreateRoom(roomName->text());
+    RoomNamePrompt prompt(parentWidget()->parentWidget(), m_gameSettings->value("frontend/lastroomname", QString()).toString());
+    connect(&prompt, SIGNAL(roomNameChosen(const QString &)), this, SLOT(onRoomNameChosen(const QString &)));
+    prompt.exec();
+}
+
+void PageRoomsList::onRoomNameChosen(const QString & roomName)
+{
+    if (!roomName.trimmed().isEmpty())
+    {
+        m_gameSettings->setValue("frontend/lastroomname", roomName);
+        emit askForCreateRoom(roomName);
+    }
     else
     {
-        QMessageBox roomNameMsg(this);
-        roomNameMsg.setIcon(QMessageBox::Warning);
-        roomNameMsg.setWindowTitle(QMessageBox::tr("Room Name - Error"));
-        roomNameMsg.setText(QMessageBox::tr("Please enter room name"));
-        roomNameMsg.setWindowModality(Qt::WindowModal);
-        roomNameMsg.exec();
+        onCreateClick();
     }
 }
 
@@ -463,10 +566,12 @@
 
 void PageRoomsList::onClearClick()
 {
-    CBState->setCurrentIndex(0);
+    showGamesInLobby->setChecked(true);
+    showGamesInProgress->setChecked(true);
     CBRules->setCurrentIndex(0);
     CBWeapons->setCurrentIndex(0);
     searchText->clear();
+    searchText->setFocus();
 }
 
 void PageRoomsList::onJoinConfirmation(const QString & room)
@@ -487,7 +592,7 @@
 
 void PageRoomsList::updateNickCounter(int cnt)
 {
-    lblCount->setText(tr("%1 players online", 0, cnt).arg(cnt));
+    setDefaultDescription(tr("%1 players online", 0, cnt).arg(cnt));
 }
 
 void PageRoomsList::setUser(const QString & nickname)
@@ -531,6 +636,12 @@
 
         // let the table view display the last model in the filter chain
         roomsList->setModel(roomsModel);
+
+        // When the data changes
+        connect(roomsModel, SIGNAL(layoutChanged()), roomsList, SLOT(repaint()));
+
+        // When a selection changes
+        connect(roomsList->selectionModel(), SIGNAL(currentRowChanged(const QModelIndex &, const QModelIndex &)), this, SLOT(roomSelectionChanged(const QModelIndex &, const QModelIndex &)));
     }
 
     stateFilteredModel->setSourceModel(model);
@@ -559,6 +670,8 @@
             this, SLOT(saveHeaderState()));
     connect(roomsList->horizontalHeader(), SIGNAL(sectionResized(int, int, int)),
             this, SLOT(saveHeaderState()));
+
+    roomsList->repaint();
 }
 
 
@@ -589,13 +702,15 @@
 
     roomsModel->setFilterWildcard(QString("*%1*").arg(searchText->text()));
 
-    int stateIdx = CBState->currentIndex();
-    // any = 0, in lobby/false = 1, in progress/true = 2
+    bool stateLobby = showGamesInLobby->isChecked();
+    bool stateProgress = showGamesInProgress->isChecked();
 
-    if (stateIdx == 0)
+    if (stateLobby && stateProgress)
         stateFilteredModel->setFilterWildcard("*"); // "any"
+    else if (stateLobby != stateProgress)
+        stateFilteredModel->setFilterFixedString(QString(stateProgress));
     else
-        stateFilteredModel->setFilterFixedString(QString(stateIdx == 2));
+        stateFilteredModel->setFilterFixedString(QString("none")); // Basically, none.
 
     if (CBRules->currentIndex() == 0)
         schemeFilteredModel->setFilterWildcard("*"); // "any"
--- a/QTfrontend/ui/page/pageroomslist.h	Wed Feb 20 02:21:58 2013 +0100
+++ b/QTfrontend/ui/page/pageroomslist.h	Tue Apr 02 21:00:57 2013 +0200
@@ -19,6 +19,7 @@
 #ifndef PAGE_ROOMLIST_H
 #define PAGE_ROOMLIST_H
 
+#include <QTableView>
 #include "AbstractPage.h"
 
 class HWChatWidget;
@@ -27,6 +28,16 @@
 class RoomsListModel;
 class QSortFilterProxyModel;
 
+class RoomTableView : public QTableView
+{
+    friend class PageRoomsList;
+
+    public:
+        RoomTableView(QWidget* parent = 0) : QTableView(parent){}
+        void moveUp();
+        void moveDown();
+};
+
 class PageRoomsList : public AbstractPage
 {
         Q_OBJECT
@@ -38,9 +49,8 @@
         void displayWarning(const QString & message);
         void setSettings(QSettings * settings);
 
-        QLineEdit * roomName;
         QLineEdit * searchText;
-        QTableView * roomsList;
+        RoomTableView * roomsList;
         QPushButton * BtnCreate;
         QPushButton * BtnJoin;
         QPushButton * BtnAdmin;
@@ -78,6 +88,10 @@
         void onSortIndicatorChanged(int logicalIndex, Qt::SortOrder order);
         void onFilterChanged();
         void saveHeaderState();
+        void onRoomNameChosen(const QString &);
+        void roomSelectionChanged(const QModelIndex &, const QModelIndex &);
+        void moveSelectionUp();
+        void moveSelectionDown();
 
     private:
         QSettings * m_gameSettings;
@@ -85,6 +99,8 @@
         QSortFilterProxyModel * stateFilteredModel;
         QSortFilterProxyModel * schemeFilteredModel;
         QSortFilterProxyModel * weaponsFilteredModel;
+        QAction * showGamesInLobby;
+        QAction * showGamesInProgress;
 
         AmmoSchemeModel * ammoSchemeModel;
 
--- a/QTfrontend/ui/widget/about.cpp	Wed Feb 20 02:21:58 2013 +0100
+++ b/QTfrontend/ui/widget/about.cpp	Tue Apr 02 21:00:57 2013 +0200
@@ -67,10 +67,11 @@
         "a { color: #ffcc00; }"
 //            "a:hover { color: yellow; }"
         "</style>"
-        "<div align=\"center\"><h1>Hedgewars</h1>"
-        "<h3>" + QLabel::tr("Version") + " " + *cVersionString + "</h3>"
+        "<div align=\"center\"><h1>Hedgewars " + *cVersionString + "</h1>"
+        "<h3>" + QLabel::tr("Revision") + " " + *cRevisionString + " (" + *cHashString + ")</h3>"
         "<p><a href=\"http://www.hedgewars.org/\">http://www.hedgewars.org/</a></p>" +
-        QLabel::tr("This program is distributed under the GNU General Public License v2") +
+        QLabel::tr("This program is distributed under the %1").arg("<a \
+        href=\"http://www.gnu.org/licenses/gpl-2.0.html\">GNU GPL v2</a>") +
         "</div>"
     );
     lbl1->setWordWrap(true);
@@ -87,9 +88,9 @@
     QString libinfo = "<style type=text/css>a:link { color: #FFFF6E; }</style>";
 
 #ifdef __GNUC__
-    libinfo.append(QString("Compiler: <a href=\"http://gcc.gnu.org\">GCC</a> %1<br>").arg(__VERSION__));
+    libinfo.append(QString("<a href=\"http://gcc.gnu.org\">GCC</a> %1<br>").arg(__VERSION__));
 #else
-    libinfo.append(QString("Compiler: Unknown<br>").arg(__VERSION__));
+    libinfo.append(QString(tr("Unknown Compiler")).arg(__VERSION__) + QString("<br>"));
 #endif
 
     libinfo.append(QString("<a href=\"http://www.libsdl.org/\">SDL</a> version: %1.%2.%3<br>")
@@ -112,10 +113,11 @@
         .arg(PHYSFS_VER_PATCH));
 
     QLabel * lblLibInfo = new QLabel();
+    lblLibInfo->setOpenExternalLinks(true);
     lblLibInfo->setText(libinfo);
     lblLibInfo->setWordWrap(true);
     lblLibInfo->setMaximumWidth(280);
-    leftLayout->addWidget(lblLibInfo, 0, Qt::AlignTop | Qt::AlignHCenter);
+    leftLayout->addWidget(lblLibInfo, 0, Qt::AlignHCenter);
     leftLayout->addStretch(1);
 
     setAcceptDrops(true);
--- a/QTfrontend/ui/widget/chatwidget.cpp	Wed Feb 20 02:21:58 2013 +0100
+++ b/QTfrontend/ui/widget/chatwidget.cpp	Tue Apr 02 21:00:57 2013 +0200
@@ -31,12 +31,13 @@
 #include <QModelIndexList>
 #include <QSortFilterProxyModel>
 #include <QMenu>
+#include <QScrollBar>
 
 #include "DataManager.h"
 #include "hwconsts.h"
 #include "gameuiconfig.h"
 #include "playerslistmodel.h"
-
+#include "HWApplication.h"
 #include "chatwidget.h"
 
 
@@ -156,24 +157,18 @@
 void HWChatWidget::displayError(const QString & message)
 {
     addLine("msg_Error", " !!! " + message);
-    // scroll to the end
-    chatText->moveCursor(QTextCursor::End);
 }
 
 
 void HWChatWidget::displayNotice(const QString & message)
 {
     addLine("msg_Notice", " *** " + message);
-    // scroll to the end
-    chatText->moveCursor(QTextCursor::End);
 }
 
 
 void HWChatWidget::displayWarning(const QString & message)
 {
     addLine("msg_Warning", " *!* " + message);
-    // scroll to the end
-    chatText->moveCursor(QTextCursor::End);
 }
 
 
@@ -451,6 +446,8 @@
     if (s_displayNone->contains(cssClass))
         return; // the css forbids us to display this line
 
+    beforeContentAdd();
+
     if (chatStrings.size() > 250)
         chatStrings.removeFirst();
 
@@ -467,17 +464,20 @@
     {
         line = QString("<span class=\"highlight\">%1</span>").arg(line);
         SDLInteraction::instance().playSoundFile(m_hilightSound);
+        HWApplication::alert(this, 800);
     }
 
     chatStrings.append(line);
 
     chatText->setHtml("<html><body>"+chatStrings.join("<br>")+"</body></html>");
 
-    chatText->moveCursor(QTextCursor::End);
+    afterContentAdd();
 }
 
 void HWChatWidget::onServerMessage(const QString& str)
 {
+    beforeContentAdd();
+
     if (chatStrings.size() > 250)
         chatStrings.removeFirst();
 
@@ -485,7 +485,7 @@
 
     chatText->setHtml("<html><body>"+chatStrings.join("<br>")+"</body></html>");
 
-    chatText->moveCursor(QTextCursor::End);
+    afterContentAdd();
 }
 
 
@@ -901,3 +901,21 @@
 
     m_nicksMenu->popup(chatNicks->mapToGlobal(pos));
 }
+
+void HWChatWidget::beforeContentAdd()
+{
+    m_scrollBarPos = chatText->verticalScrollBar()->value();
+    m_scrollToBottom = m_scrollBarPos == chatText->verticalScrollBar()->maximum();
+}
+
+void HWChatWidget::afterContentAdd()
+{
+    if(m_scrollToBottom)
+    {
+        chatText->verticalScrollBar()->setValue(chatText->verticalScrollBar()->maximum());
+        chatText->moveCursor(QTextCursor::End);
+    } else
+    {
+        chatText->verticalScrollBar()->setValue(m_scrollBarPos);
+    }
+}
--- a/QTfrontend/ui/widget/chatwidget.h	Wed Feb 20 02:21:58 2013 +0100
+++ b/QTfrontend/ui/widget/chatwidget.h	Tue Apr 02 21:00:57 2013 +0200
@@ -83,6 +83,8 @@
         void discardStyleSheet();
         void saveStyleSheet();
         QString linkedNick(const QString & nickname);
+        void beforeContentAdd();
+        void afterContentAdd();
 
     public slots:
         void onChatString(const QString& str);
@@ -124,6 +126,8 @@
         QList<QRegExp> m_highlights; ///< regular expressions used for highlighting
         bool notify;
         bool m_autoKickEnabled;
+        bool m_scrollToBottom;
+        int m_scrollBarPos;
 
     private slots:
         void returnPressed();
--- a/QTfrontend/ui/widget/feedbackdialog.cpp	Wed Feb 20 02:21:58 2013 +0100
+++ b/QTfrontend/ui/widget/feedbackdialog.cpp	Tue Apr 02 21:00:57 2013 +0200
@@ -30,6 +30,7 @@
 #include <QProcess>
 #include <QMessageBox>
 #include <QCheckBox>
+#include <QByteArray>
 
 #include <string>
 
@@ -73,16 +74,22 @@
     QHBoxLayout * systemLayout = new QHBoxLayout();
 
     info = new QLabel();
-    info->setText(
+    info->setText(QString(
         "<style type=\"text/css\">"
         "a { color: #fc0; }"
         "b { color: #0df; }"
         "</style>"
-        "<div align=\"center\"><h1>Please give us feedback!</h1>"
-        "<h3>We are always happy about suggestions, ideas, or bug reports.<h3>"
-        "<h4>Your email address is optional, but we may want to contact you.<h4>"
-        "</div>"
+        "<div align=\"center\"><h1>%1</h1>"
+        "<h3>%2<h3>"
+        "<h4>%3 <a href=\"http://code.google.com/p/hedgewars/wiki/KnownBugs\">known bugs</a><h4>"
+        "<h4>%4<h4>"
+        "</div>")
+        .arg(tr("Please give us feedback!"))
+        .arg(tr("We are always happy about suggestions, ideas, or bug reports."))
+        .arg(tr("If you found a bug, you can see if it's already known here (english): "))
+        .arg(tr("Your email address is optional, but we may want to contact you."))
     );
+    info->setOpenExternalLinks(true);
     pageLayout->addWidget(info);
 
     QVBoxLayout * summaryEmailLayout = new QVBoxLayout();
@@ -194,7 +201,7 @@
     QString screen_size = "Size of the screen(s): " +
         QString::number(screen->width()) + "x" + QString::number(screen->height()) + "\n";
     QString number_of_screens = "Number of screens: " + QString::number(screen->screenCount()) + "\n";
-    std::string processor_name = "Processor: ";
+    QString processor_name = "Processor: ";
 
     // platform specific code
 #ifdef Q_WS_MACX
@@ -237,7 +244,7 @@
     MEMORYSTATUSEX status;
     status.dwLength = sizeof(status);
     GlobalMemoryStatusEx(&status);
-    total_ram += QString::number(status.ullTotalPhys);
+    total_ram += QString::number(status.ullTotalPhys) + "\n";
 
     switch(QSysInfo::WinVersion())
     {
@@ -245,6 +252,7 @@
         case QSysInfo::WV_XP: os_version += "Windows XP\n"; break;
         case QSysInfo::WV_VISTA: os_version += "Windows Vista\n"; break;
         case QSysInfo::WV_WINDOWS7: os_version += "Windows 7\n"; break;
+        //case QSysInfo::WV_WINDOWS8: os_version += "Windows 8\n"; break; //QT 5+
         default: os_version += "Windows (Unknown version)\n"; break;
     }
     kernel_line += "Windows kernel\n";
@@ -273,6 +281,7 @@
     delete process;
 #endif
 
+#if defined(__i386__) || defined(__x86_64__)
     // cpu info
     quint32 registers[4];
     quint32 i;
@@ -281,26 +290,30 @@
     asm volatile
       ("cpuid" : "=a" (registers[0]), "=b" (registers[1]), "=c" (registers[2]), "=d" (registers[3])
        : "a" (i), "c" (0));
-    processor_name += std::string((const char *)&registers[0], 4);
-    processor_name += std::string((const char *)&registers[1], 4);
-    processor_name += std::string((const char *)&registers[2], 4);
-    processor_name += std::string((const char *)&registers[3], 4);
+    processor_name += QByteArray(reinterpret_cast<char*>(&registers[0]), 4);
+    processor_name += QByteArray(reinterpret_cast<char*>(&registers[1]), 4);
+    processor_name += QByteArray(reinterpret_cast<char*>(&registers[2]), 4);
+    processor_name += QByteArray(reinterpret_cast<char*>(&registers[3]), 4);
     i = 0x80000003;
     asm volatile
       ("cpuid" : "=a" (registers[0]), "=b" (registers[1]), "=c" (registers[2]), "=d" (registers[3])
        : "a" (i), "c" (0));
-    processor_name += std::string((const char *)&registers[0], 4);
-    processor_name += std::string((const char *)&registers[1], 4);
-    processor_name += std::string((const char *)&registers[2], 4);
-    processor_name += std::string((const char *)&registers[3], 4);
+    processor_name += QByteArray(reinterpret_cast<char*>(&registers[0]), 4);
+    processor_name += QByteArray(reinterpret_cast<char*>(&registers[1]), 4);
+    processor_name += QByteArray(reinterpret_cast<char*>(&registers[2]), 4);
+    processor_name += QByteArray(reinterpret_cast<char*>(&registers[3]), 4);
     i = 0x80000004;
     asm volatile
       ("cpuid" : "=a" (registers[0]), "=b" (registers[1]), "=c" (registers[2]), "=d" (registers[3])
        : "a" (i), "c" (0));
-    processor_name += std::string((const char *)&registers[0], 4);
-    processor_name += std::string((const char *)&registers[1], 4);
-    processor_name += std::string((const char *)&registers[2], 4);
-    processor_name += std::string((const char *)&registers[3], 3);
+    processor_name += QByteArray(reinterpret_cast<char*>(&registers[0]), 4);
+    processor_name += QByteArray(reinterpret_cast<char*>(&registers[1]), 4);
+    processor_name += QByteArray(reinterpret_cast<char*>(&registers[2]), 4);
+    processor_name += QByteArray(reinterpret_cast<char*>(&registers[3]), 4);
+    processor_name += "\n";
+#else
+    processor_name += "Unknown";
+#endif
 
     // compiler
 #ifdef __GNUC__
@@ -320,7 +333,7 @@
         + total_ram
         + screen_size
         + number_of_screens
-        + QString::fromStdString(processor_name + "\n")
+        + processor_name
         + number_of_cores
         + compiler_version
         + compiler_bits
@@ -443,7 +456,8 @@
     QString email = this->email->text();
     QString captchaCode = this->captcha_code->text();
     QString captchaID = QString::number(this->captchaID);
-    QString version = "HedgewarsFoundation-Hedgewars-" + (cVersionString?(*cVersionString):QString(""));
+    QString version = "HedgewarsFoundation-Hedgewars-v" + *cVersionString + "_r" + 
+                       *cRevisionString + "|" + *cHashString;
 
     if (summary.isEmpty() || description.isEmpty())
     {
--- a/QTfrontend/ui/widget/flowlayout.cpp	Wed Feb 20 02:21:58 2013 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,190 +0,0 @@
- /****************************************************************************
- **
- ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
- ** All rights reserved.
- ** Contact: Nokia Corporation (qt-info@nokia.com)
- **
- ** This file is part of the examples of the Qt Toolkit.
- **
- ** $QT_BEGIN_LICENSE:BSD$
- ** You may use this file under the terms of the BSD license as follows:
- **
- ** "Redistribution and use in source and binary forms, with or without
- ** modification, are permitted provided that the following conditions are
- ** met:
- **   * Redistributions of source code must retain the above copyright
- **     notice, this list of conditions and the following disclaimer.
- **   * Redistributions in binary form must reproduce the above copyright
- **     notice, this list of conditions and the following disclaimer in
- **     the documentation and/or other materials provided with the
- **     distribution.
- **   * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
- **     the names of its contributors may be used to endorse or promote
- **     products derived from this software without specific prior written
- **     permission.
- **
- ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
- ** $QT_END_LICENSE$
- **
- ****************************************************************************/
-
- #include <QtGui>
-
- #include "flowlayout.h"
-
-FlowLayout::FlowLayout(QWidget *parent, int margin, int hSpacing, int vSpacing)
-    : QLayout(parent), m_hSpace(hSpacing), m_vSpace(vSpacing)
-{
-    setContentsMargins(margin, margin, margin, margin);
-}
-
-FlowLayout::FlowLayout(int margin, int hSpacing, int vSpacing)
-    : m_hSpace(hSpacing), m_vSpace(vSpacing)
-{
-    setContentsMargins(margin, margin, margin, margin);
-}
-
-FlowLayout::~FlowLayout()
-{
-    QLayoutItem *item;
-    while ((item = takeAt(0)))
-        delete item;
-}
-
-void FlowLayout::addItem(QLayoutItem *item)
-{
-    itemList.append(item);
-}
-
-int FlowLayout::horizontalSpacing() const
-{
-    if (m_hSpace >= 0) {
-        return m_hSpace;
-    } else {
-        return smartSpacing(QStyle::PM_LayoutHorizontalSpacing);
-    }
-}
-
-int FlowLayout::verticalSpacing() const
-{
-    if (m_vSpace >= 0) {
-        return m_vSpace;
-    } else {
-        return smartSpacing(QStyle::PM_LayoutVerticalSpacing);
-    }
-}
-
-int FlowLayout::count() const
-{
-    return itemList.size();
-}
-
-QLayoutItem *FlowLayout::itemAt(int index) const
-{
-    return itemList.value(index);
-}
-
-QLayoutItem *FlowLayout::takeAt(int index)
-{
-    if (index >= 0 && index < itemList.size())
-        return itemList.takeAt(index);
-    else
-        return 0;
-}
-
-Qt::Orientations FlowLayout::expandingDirections() const
-{
-    return 0;
-}
-
-bool FlowLayout::hasHeightForWidth() const
-{
-    return true;
-}
-
-int FlowLayout::heightForWidth(int width) const
-{
-    int height = doLayout(QRect(0, 0, width, 0), true);
-    return height;
-}
-
-void FlowLayout::setGeometry(const QRect &rect)
-{
-    QLayout::setGeometry(rect);
-    doLayout(rect, false);
-}
-
-QSize FlowLayout::sizeHint() const
-{
-    return minimumSize();
-}
-
-QSize FlowLayout::minimumSize() const
-{
-    QSize size;
-    QLayoutItem *item;
-    foreach (item, itemList)
-        size = size.expandedTo(item->minimumSize());
-
-    size += QSize(2*margin(), 2*margin());
-    return size;
-}
-
-int FlowLayout::doLayout(const QRect &rect, bool testOnly) const
-{
-    int left, top, right, bottom;
-    getContentsMargins(&left, &top, &right, &bottom);
-    QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom);
-    int x = effectiveRect.x();
-    int y = effectiveRect.y();
-    int lineHeight = 0;
-
-    QLayoutItem *item;
-    foreach (item, itemList) {
-        QWidget *wid = item->widget();
-        int spaceX = horizontalSpacing();
-        if (spaceX == -1)
-            spaceX = wid->style()->layoutSpacing(
-                QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal);
-        int spaceY = verticalSpacing();
-        if (spaceY == -1)
-            spaceY = wid->style()->layoutSpacing(
-                QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical);
-        int nextX = x + item->sizeHint().width() + spaceX;
-        if (nextX - spaceX > effectiveRect.right() && lineHeight > 0) {
-            x = effectiveRect.x();
-            y = y + lineHeight + spaceY;
-            nextX = x + item->sizeHint().width() + spaceX;
-            lineHeight = 0;
-        }
-
-        if (!testOnly)
-            item->setGeometry(QRect(QPoint(x, y), item->sizeHint()));
-
-        x = nextX;
-        lineHeight = qMax(lineHeight, item->sizeHint().height());
-    }
-    return y + lineHeight - rect.y() + bottom;
-}
-int FlowLayout::smartSpacing(QStyle::PixelMetric pm) const
-{
-    QObject *parent = this->parent();
-    if (!parent) {
-        return -1;
-    } else if (parent->isWidgetType()) {
-        QWidget *pw = static_cast<QWidget *>(parent);
-        return pw->style()->pixelMetric(pm, 0, pw);
-    } else {
-        return static_cast<QLayout *>(parent)->spacing();
-    }
-}
--- a/QTfrontend/ui/widget/flowlayout.h	Wed Feb 20 02:21:58 2013 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,77 +0,0 @@
- /****************************************************************************
- **
- ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
- ** All rights reserved.
- ** Contact: Nokia Corporation (qt-info@nokia.com)
- **
- ** This file is part of the examples of the Qt Toolkit.
- **
- ** $QT_BEGIN_LICENSE:BSD$
- ** You may use this file under the terms of the BSD license as follows:
- **
- ** "Redistribution and use in source and binary forms, with or without
- ** modification, are permitted provided that the following conditions are
- ** met:
- **   * Redistributions of source code must retain the above copyright
- **     notice, this list of conditions and the following disclaimer.
- **   * Redistributions in binary form must reproduce the above copyright
- **     notice, this list of conditions and the following disclaimer in
- **     the documentation and/or other materials provided with the
- **     distribution.
- **   * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
- **     the names of its contributors may be used to endorse or promote
- **     products derived from this software without specific prior written
- **     permission.
- **
- ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
- ** $QT_END_LICENSE$
- **
- ****************************************************************************/
-
- #ifndef FLOWLAYOUT_H
- #define FLOWLAYOUT_H
-
- #include <QLayout>
- #include <QRect>
- #include <QStyle>
- #include <QWidgetItem>
- class FlowLayout : public QLayout
- {
- public:
-     FlowLayout(QWidget *parent, int margin = -1, int hSpacing = -1, int vSpacing = -1);
-     FlowLayout(int margin = -1, int hSpacing = -1, int vSpacing = -1);
-     ~FlowLayout();
-
-     void addItem(QLayoutItem *item);
-     int horizontalSpacing() const;
-     int verticalSpacing() const;
-     Qt::Orientations expandingDirections() const;
-     bool hasHeightForWidth() const;
-     int heightForWidth(int) const;
-     int count() const;
-     QLayoutItem *itemAt(int index) const;
-     QSize minimumSize() const;
-     void setGeometry(const QRect &rect);
-     QSize sizeHint() const;
-     QLayoutItem *takeAt(int index);
-
- private:
-     int doLayout(const QRect &rect, bool testOnly) const;
-     int smartSpacing(QStyle::PixelMetric pm) const;
-
-     QList<QLayoutItem *> itemList;
-     int m_hSpace;
-     int m_vSpace;
- };
-
- #endif
--- a/QTfrontend/ui/widget/gamecfgwidget.cpp	Wed Feb 20 02:21:58 2013 +0100
+++ b/QTfrontend/ui/widget/gamecfgwidget.cpp	Tue Apr 02 21:00:57 2013 +0200
@@ -49,6 +49,7 @@
     setMaximumHeight(447);
     setMinimumWidth(470);
     setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+    m_master = true;
 
     // Easy containers for the map/game options in either stacked or tabbed mode
 
--- a/QTfrontend/ui/widget/hatprompt.cpp	Wed Feb 20 02:21:58 2013 +0100
+++ b/QTfrontend/ui/widget/hatprompt.cpp	Tue Apr 02 21:00:57 2013 +0200
@@ -35,6 +35,26 @@
 #include "HatModel.h"
 #include "hatprompt.h"
 
+void HatListView::moveUp()
+{
+    setCurrentIndex(moveCursor(QAbstractItemView::MoveUp, Qt::NoModifier));
+}
+
+void HatListView::moveDown()
+{
+    setCurrentIndex(moveCursor(QAbstractItemView::MoveDown, Qt::NoModifier));
+}
+
+void HatListView::moveLeft()
+{
+    setCurrentIndex(moveCursor(QAbstractItemView::MoveLeft, Qt::NoModifier));
+}
+
+void HatListView::moveRight()
+{
+    setCurrentIndex(moveCursor(QAbstractItemView::MoveRight, Qt::NoModifier));
+}
+
 HatPrompt::HatPrompt(int currentIndex, QWidget* parent) : QDialog(parent)
 {
     setModal(true);
@@ -60,7 +80,7 @@
     QHBoxLayout * topLayout = new QHBoxLayout();
 
     // Help/prompt message at top
-    QLabel * lblDesc = new QLabel(tr("Select a hat"));
+    QLabel * lblDesc = new QLabel(tr("Search for a hat:"));
     lblDesc->setObjectName("lblDesc");
     lblDesc->setStyleSheet("#lblDesc { color: #130F2A; background: #F6CB1C; border: solid 4px #F6CB1C; border-top-left-radius: 10px; padding: 4px 10px;}");
     lblDesc->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
@@ -72,22 +92,18 @@
     filterContainer->setFixedHeight(24);
     filterContainer->setObjectName("filterContainer");
     filterContainer->setStyleSheet("#filterContainer { background: #F6CB1C; border-top-right-radius: 10px; padding: 3px; }");
-    filterContainer->setFixedWidth(250);
+    filterContainer->setFixedWidth(150);
     txtFilter = new LineEditCursor(filterContainer);
-    txtFilter->setFixedWidth(250);
+    txtFilter->setFixedWidth(150);
     txtFilter->setFocus();
     txtFilter->setFixedHeight(22);
+    txtFilter->setStyleSheet("LineEditCursor { border-width: 0px; border-radius: 6px; margin-top: 3px; margin-right: 3px; padding-left: 4px; padding-bottom: 2px; background-color: rgb(23, 11, 54); } LineEditCursor:hover, LineEditCursor:focus { background-color: rgb(13, 5, 68); }");
     connect(txtFilter, SIGNAL(textChanged(const QString &)), this, SLOT(filterChanged(const QString &)));
     connect(txtFilter, SIGNAL(moveUp()), this, SLOT(moveUp()));
     connect(txtFilter, SIGNAL(moveDown()), this, SLOT(moveDown()));
     connect(txtFilter, SIGNAL(moveLeft()), this, SLOT(moveLeft()));
     connect(txtFilter, SIGNAL(moveRight()), this, SLOT(moveRight()));
 
-    // Filter label
-    QLabel * lblFilter = new QLabel(tr("Filter: "), txtFilter);
-    lblFilter->setStyleSheet(QString("background: none; margin-left: -%1px; margin-top: 4px;").arg(lblFilter->width() / 2 - 3));
-    txtFilter->setStyleSheet(QString("border-width: 0px; background-color: rgb(13, 5, 68); border-radius: 6px; margin-top: 3px; margin-right: 3px; padding-left: %1px; padding-bottom: 2px;").arg(lblFilter->width() / 2));
-
     // Corner widget
     QLabel * corner = new QLabel();
     corner->setPixmap(QPixmap(QString::fromUtf8(":/res/inverse-corner-bl.png")));
@@ -132,22 +148,22 @@
 
 void HatPrompt::moveUp()
 {
-    list->setCurrentIndex(list->moveCursor(QAbstractItemView::MoveUp, Qt::NoModifier));
+    list->moveUp();
 }
 
 void HatPrompt::moveDown()
 {
-    list->setCurrentIndex(list->moveCursor(QAbstractItemView::MoveDown, Qt::NoModifier));
+    list->moveDown();
 }
 
 void HatPrompt::moveLeft()
 {
-    list->setCurrentIndex(list->moveCursor(QAbstractItemView::MoveLeft, Qt::NoModifier));
+    list->moveLeft();
 }
 
 void HatPrompt::moveRight()
 {
-    list->setCurrentIndex(list->moveCursor(QAbstractItemView::MoveRight, Qt::NoModifier));
+    list->moveRight();
 }
 
 void HatPrompt::onAccepted()
--- a/QTfrontend/ui/widget/hatprompt.h	Wed Feb 20 02:21:58 2013 +0100
+++ b/QTfrontend/ui/widget/hatprompt.h	Tue Apr 02 21:00:57 2013 +0200
@@ -34,6 +34,10 @@
 
     public:
         HatListView(QWidget* parent = 0) : QListView(parent){}
+        void moveUp();
+        void moveDown();
+        void moveLeft();
+        void moveRight();
 };
 
 class HatPrompt : public QDialog
--- a/QTfrontend/ui/widget/mapContainer.cpp	Wed Feb 20 02:21:58 2013 +0100
+++ b/QTfrontend/ui/widget/mapContainer.cpp	Tue Apr 02 21:00:57 2013 +0200
@@ -132,14 +132,15 @@
     QLabel * lblMapPreviewText = new QLabel(this);
     lblMapPreviewText->setText(tr("Map preview:"));
     leftLayout->addWidget(lblMapPreviewText, 0);
-    leftLayout->addSpacing(2);
 
     /* Map Preview */
 
-    mapPreview = new QLabel(this);
+    mapPreview = new QPushButton(this);
     mapPreview->setObjectName("mapPreview");
     mapPreview->setFixedSize(256, 128);
+    mapPreview->setContentsMargins(0, 0, 0, 0);
     leftLayout->addWidget(mapPreview, 0);
+    connect(mapPreview, SIGNAL(clicked()), this, SLOT(previewClicked()));
 
     /* Bottom-Left layout */
 
@@ -215,7 +216,7 @@
     mazeStyles = new QListWidget();
     new QListWidgetItem(tr("Small tunnels"), mazeStyles);
     new QListWidgetItem(tr("Medium tunnels"), mazeStyles);
-    new QListWidgetItem(tr("Largetunnels"), mazeStyles);
+    new QListWidgetItem(tr("Large tunnels"), mazeStyles);
     new QListWidgetItem(tr("Small islands"), mazeStyles);
     new QListWidgetItem(tr("Medium islands"), mazeStyles);
     new QListWidgetItem(tr("Large islands"), mazeStyles);
@@ -230,7 +231,7 @@
     lblDesc->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
     lblDesc->setAlignment(Qt::AlignTop | Qt::AlignLeft);
     lblDesc->setStyleSheet("font: 10px;");
-    bottomLeftLayout->addWidget(lblDesc, 1);
+    bottomLeftLayout->addWidget(lblDesc, 100);
 
     /* Add stretch above theme button */
 
@@ -249,14 +250,13 @@
 
     /* Set defaults */
 
-    setRandomTheme();
     setRandomSeed();
     setMazeSize(0);
     setTemplateFilter(0);
     staticMapChanged(m_staticMapModel->index(0, 0));
     missionMapChanged(m_missionMapModel->index(0, 0));
-    updateTheme(m_themeModel->index(0, 0));
     changeMapType(MapModel::GeneratedMap);
+    setRandomTheme();
 }
 
 void HWMapContainer::setImage(const QImage newImage)
@@ -270,7 +270,7 @@
     px.setMask(bm);
 
     p.fillRect(pxres.rect(), linearGrad);
-    p.drawPixmap(QPoint(0, 0), px);
+    p.drawPixmap(0, 0, px);
 
     addInfoToPreview(pxres);
     pMap = 0;
@@ -300,7 +300,14 @@
     p.drawText(image.rect().width() - hhSmall.rect().width() - 14 - (hhLimit > 9 ? 10 : 0), 18, text);
     p.drawPixmap(image.rect().width() - hhSmall.rect().width() - 5, 5, hhSmall.rect().width(), hhSmall.rect().height(), hhSmall);
 
-    mapPreview->setPixmap(finalImage);
+    // Shrink, crop, and center preview image
+    QPixmap centered(QSize(m_previewSize.width() - 6, m_previewSize.height() - 6));
+    QPainter pc(&centered);
+    pc.fillRect(centered.rect(), linearGrad);
+    pc.drawPixmap(-3, -3, finalImage);
+
+    mapPreview->setIcon(QIcon(centered));
+    mapPreview->setIconSize(centered.size());
 }
 
 void HWMapContainer::askForGeneratedPreview()
@@ -333,6 +340,19 @@
     cType->setEnabled(false);
 }
 
+void HWMapContainer::previewClicked()
+{
+    switch (m_mapInfo.type)
+    {
+        case MapModel::HandDrawnMap:
+            emit drawMapRequested();
+            break;
+        default:
+            setRandomMap();
+            break;
+    }
+}
+
 QString HWMapContainer::getCurrentSeed() const
 {
     return m_seed;
@@ -432,7 +452,7 @@
 
 void HWMapContainer::setTheme(const QString & theme)
 {
-    QModelIndexList mdl = m_themeModel->match(m_themeModel->index(0), Qt::DisplayRole, theme);
+    QModelIndexList mdl = m_themeModel->match(m_themeModel->index(0), ThemeModel::ActualNameRole, theme);
 
     if(mdl.size())
         updateTheme(mdl.at(0));
@@ -442,6 +462,8 @@
 
 void HWMapContainer::setRandomMap()
 {
+    if (!m_master) return;
+    
     setRandomSeed();
     switch(m_mapInfo.type)
     {
@@ -471,6 +493,7 @@
     if(!m_themeModel->rowCount()) return;
     quint32 themeNum = rand() % m_themeModel->rowCount();
     updateTheme(m_themeModel->index(themeNum));
+    emit themeChanged(m_theme);
 }
 
 void HWMapContainer::intSetTemplateFilter(int filter)
@@ -594,7 +617,8 @@
     {
         case MapModel::Invalid:
             failIcon = QPixmap(":/res/btnDisabled.png");
-            mapPreview->setPixmap(failIcon);
+            mapPreview->setIcon(QIcon(failIcon));
+            mapPreview->setIconSize(failIcon.size());
             break;
         case MapModel::GeneratedMap:
             askForGeneratedPreview();
@@ -611,7 +635,7 @@
 
             if(!success)
             {
-                mapPreview->setPixmap(QPixmap());
+                mapPreview->setIcon(QIcon());
                 return;
             }
 
@@ -730,6 +754,8 @@
         }
     }
 
+    repaint();
+
     emit mapgenChanged(mapgen);
 }
 
@@ -752,7 +778,7 @@
 
 void HWMapContainer::showThemePrompt()
 {
-    ThemePrompt prompt(this);
+    ThemePrompt prompt(m_themeID, this);
     int theme = prompt.exec() - 1; // Since 0 means canceled, so all indexes are +1'd
     if (theme < 0) return;
 
@@ -763,8 +789,9 @@
 
 void HWMapContainer::updateTheme(const QModelIndex & current)
 {
-    m_theme = selectedTheme = current.data().toString();
-    QIcon icon = qVariantValue<QIcon>(current.data(Qt::UserRole));
+    m_theme = selectedTheme = current.data(ThemeModel::ActualNameRole).toString();
+    m_themeID = current.row();
+    QIcon icon = qVariantValue<QIcon>(current.data(Qt::DecorationRole));
     QSize iconSize = icon.actualSize(QSize(65535, 65535));
     btnTheme->setFixedHeight(64);
     btnTheme->setIconSize(iconSize);
--- a/QTfrontend/ui/widget/mapContainer.h	Wed Feb 20 02:21:58 2013 +0100
+++ b/QTfrontend/ui/widget/mapContainer.h	Tue Apr 02 21:00:57 2013 +0200
@@ -107,13 +107,14 @@
         void missionMapChanged(const QModelIndex & map, const QModelIndex & old = QModelIndex());
         void loadDrawing();
         void showSeedPrompt();
+        void previewClicked();
 
     protected:
         virtual void resizeEvent ( QResizeEvent * event );
 
     private:
         QVBoxLayout mainLayout;
-        QLabel* mapPreview;
+        QPushButton* mapPreview;
         QComboBox* chooseMap;
         MapModel * m_staticMapModel;
         MapModel * m_missionMapModel;
@@ -162,6 +163,7 @@
         void updateThemeButtonSize();
 
         MapModel::MapInfo m_mapInfo;
+        int m_themeID;
         QString m_theme;
         QString m_curMap;
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/QTfrontend/ui/widget/roomnameprompt.cpp	Tue Apr 02 21:00:57 2013 +0200
@@ -0,0 +1,82 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@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
+ */
+
+#include <QDialog>
+#include <QVBoxLayout>
+#include <QHBoxLayout>
+#include <QPushButton>
+#include <QLineEdit>
+#include <QLabel>
+#include <QDebug>
+
+#include "roomnameprompt.h"
+
+RoomNamePrompt::RoomNamePrompt(QWidget* parent, const QString & roomName) : QDialog(parent)
+{
+    setModal(true);
+    setWindowFlags(Qt::Sheet);
+    setWindowModality(Qt::WindowModal);
+    setMinimumSize(360, 130);
+    resize(360, 130);
+    setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
+
+    // Layout
+    QVBoxLayout * dialogLayout = new QVBoxLayout(this);
+
+    // Label
+    label = new QLabel(tr("Enter a name for your room."));
+    label->setWordWrap(true);
+    dialogLayout->addWidget(label, 0);
+
+    // Input box
+    editBox = new QLineEdit();
+    editBox->setText(roomName);
+    editBox->setMaxLength(59); // It didn't like 60 :(
+    editBox->setStyleSheet("QLineEdit { padding: 3px; }");
+    editBox->selectAll();
+    dialogLayout->addWidget(editBox, 1);
+
+    dialogLayout->addStretch(1);
+
+    // Buttons
+    QHBoxLayout * buttonLayout = new QHBoxLayout();
+    buttonLayout->addStretch(1);
+    dialogLayout->addLayout(buttonLayout);
+
+    QPushButton * btnCancel = new QPushButton(tr("Cancel"));
+    QPushButton * btnOkay = new QPushButton(tr("Create room"));
+    connect(btnCancel, SIGNAL(clicked()), this, SLOT(reject()));
+    connect(btnOkay, SIGNAL(clicked()), this, SLOT(accept()));
+#ifdef Q_WS_MAC
+        buttonLayout->addWidget(btnCancel);
+        buttonLayout->addWidget(btnOkay);
+#else
+        buttonLayout->addWidget(btnOkay);
+        buttonLayout->addWidget(btnCancel);
+#endif
+    btnOkay->setDefault(true);
+
+    setStyleSheet("QPushButton { padding: 5px; }");
+
+    connect(btnOkay, SIGNAL(clicked()), this, SLOT(setRoomName()));
+}
+
+void RoomNamePrompt::setRoomName()
+{
+    emit roomNameChosen(editBox->text());
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/QTfrontend/ui/widget/roomnameprompt.h	Tue Apr 02 21:00:57 2013 +0200
@@ -0,0 +1,45 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@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
+ */
+
+#ifndef ROOMNAMEPROMPT_H
+#define ROOMNAMEPROMPT_H
+
+#include <QDialog>
+
+class QLineEdit;
+class QLabel;
+
+class RoomNamePrompt : public QDialog
+{
+        Q_OBJECT
+
+    public:
+        RoomNamePrompt(QWidget* parent, const QString & roomName);
+
+    signals:
+        void roomNameChosen(const QString & roomName);
+
+    private slots:
+        void setRoomName();
+
+    private:
+        QLineEdit * editBox;
+        QLabel * label;
+};
+
+#endif // ROOMNAMEPROMPT_H
--- a/QTfrontend/ui/widget/seedprompt.cpp	Wed Feb 20 02:21:58 2013 +0100
+++ b/QTfrontend/ui/widget/seedprompt.cpp	Tue Apr 02 21:00:57 2013 +0200
@@ -62,8 +62,13 @@
         QPushButton * btnOkay = new QPushButton(tr("Set seed"));
         connect(btnCancel, SIGNAL(clicked()), this, SLOT(reject()));
         connect(btnOkay, SIGNAL(clicked()), this, SLOT(accept()));
+#ifdef Q_WS_MAC
         buttonLayout->addWidget(btnCancel);
         buttonLayout->addWidget(btnOkay);
+#else
+        buttonLayout->addWidget(btnOkay);
+        buttonLayout->addWidget(btnCancel);
+#endif
         btnOkay->setDefault(true);
     }
     else
--- a/QTfrontend/ui/widget/teamselect.cpp	Wed Feb 20 02:21:58 2013 +0100
+++ b/QTfrontend/ui/widget/teamselect.cpp	Tue Apr 02 21:00:57 2013 +0200
@@ -257,7 +257,7 @@
     frameDontPlaying = new FrameTeams();
 
     // Add notice about number of required teams.
-    numTeamNotice = new QLabel("Two teams are required to play!");
+    numTeamNotice = new QLabel(tr("At least two teams are required to play!"));
     mainLayout.addWidget(numTeamNotice);
 
     QPalette p;
--- a/QTfrontend/ui/widget/themeprompt.cpp	Wed Feb 20 02:21:58 2013 +0100
+++ b/QTfrontend/ui/widget/themeprompt.cpp	Tue Apr 02 21:00:57 2013 +0200
@@ -17,20 +17,46 @@
  */
 
 #include <QDialog>
-#include <QVBoxLayout>
+#include <QGridLayout>
+#include <QHBoxLayout>
 #include <QScrollArea>
 #include <QPushButton>
 #include <QToolButton>
 #include <QWidgetItem>
 #include <QModelIndex>
+#include <QListView>
+#include <QLineEdit>
 #include <QLabel>
+#include <QSortFilterProxyModel>
+#include <QDebug>
 
-#include "flowlayout.h"
 #include "DataManager.h"
+#include "lineeditcursor.h"
 #include "ThemeModel.h"
 #include "themeprompt.h"
 
-ThemePrompt::ThemePrompt(QWidget* parent) : QDialog(parent)
+
+void ThemeListView::moveUp()
+{
+    setCurrentIndex(moveCursor(QAbstractItemView::MoveUp, Qt::NoModifier));
+}
+
+void ThemeListView::moveDown()
+{
+    setCurrentIndex(moveCursor(QAbstractItemView::MoveDown, Qt::NoModifier));
+}
+
+void ThemeListView::moveLeft()
+{
+    setCurrentIndex(moveCursor(QAbstractItemView::MoveLeft, Qt::NoModifier));
+}
+
+void ThemeListView::moveRight()
+{
+    setCurrentIndex(moveCursor(QAbstractItemView::MoveRight, Qt::NoModifier));
+}
+
+ThemePrompt::ThemePrompt(int currentIndex, QWidget* parent) : QDialog(parent)
 {
     setModal(true);
     setWindowFlags(Qt::Sheet);
@@ -39,63 +65,122 @@
     resize(550, 430);
     setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
 
+    setStyleSheet("QPushButton { padding: 5px; margin-top: 10px; }");
+
+    // Theme model, and a model for setting a filter
+    ThemeModel * themeModel = DataManager::instance().themeModel();
+    filterModel = new QSortFilterProxyModel();
+    filterModel->setSourceModel(themeModel);
+    filterModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
+
     // Grid
-    QVBoxLayout * dialogLayout = new QVBoxLayout(this);
+    QGridLayout * dialogLayout = new QGridLayout(this);
     dialogLayout->setSpacing(0);
+    dialogLayout->setColumnStretch(1, 1);
+
+    QHBoxLayout * topLayout = new QHBoxLayout();
 
     // Help/prompt message at top
-    QLabel * lblDesc = new QLabel(tr("Select a theme for this map"));
-    lblDesc->setStyleSheet("color: #130F2A; background: #F6CB1C; border: solid 4px #F6CB1C; border-top-left-radius: 10px; border-top-right-radius: 10px; padding: auto 20px;");
+    QLabel * lblDesc = new QLabel(tr("Search for a theme:"));
+    lblDesc->setObjectName("lblDesc");
+    lblDesc->setStyleSheet("#lblDesc { color: #130F2A; background: #F6CB1C; border: solid 4px #F6CB1C; border-top-left-radius: 10px; padding: 4px 10px;}");
     lblDesc->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
     lblDesc->setFixedHeight(24);
     lblDesc->setMinimumWidth(0);
 
-    // Scroll area and container for theme icons
-    QWidget * themesContainer = new QWidget();
-    FlowLayout * themesGrid = new FlowLayout();
-    themesContainer->setLayout(themesGrid);
-    QScrollArea * scrollArea = new QScrollArea();
-    scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
-    scrollArea->setObjectName("scrollArea");
-    scrollArea->setStyleSheet("QScrollBar, #scrollArea { background-color: #130F2A; } #scrollArea { border-color: #F6CB1C; border-width: 3px; border-top-width: 0; border-style: solid; border-bottom-left-radius: 10px; border-bottom-right-radius: 10px; }");
-    scrollArea->setWidgetResizable(true);
-    scrollArea->setFrameShape(QFrame::NoFrame);
-    scrollArea->setWidget(themesContainer);
+    // Filter text box
+    QWidget * filterContainer = new QWidget();
+    filterContainer->setFixedHeight(24);
+    filterContainer->setObjectName("filterContainer");
+    filterContainer->setStyleSheet("#filterContainer { background: #F6CB1C; border-top-right-radius: 10px; padding: 3px; }");
+    filterContainer->setFixedWidth(150);
+    txtFilter = new LineEditCursor(filterContainer);
+    txtFilter->setFixedWidth(150);
+    txtFilter->setFocus();
+    txtFilter->setFixedHeight(22);
+    txtFilter->setStyleSheet("LineEditCursor { border-width: 0px; border-radius: 6px; margin-top: 3px; margin-right: 3px; padding-left: 4px; padding-bottom: 2px; background-color: rgb(23, 11, 54); } LineEditCursor:hover, LineEditCursor:focus { background-color: rgb(13, 5, 68); }");
+    connect(txtFilter, SIGNAL(textChanged(const QString &)), this, SLOT(filterChanged(const QString &)));
+    connect(txtFilter, SIGNAL(moveUp()), this, SLOT(moveUp()));
+    connect(txtFilter, SIGNAL(moveDown()), this, SLOT(moveDown()));
+    connect(txtFilter, SIGNAL(moveLeft()), this, SLOT(moveLeft()));
+    connect(txtFilter, SIGNAL(moveRight()), this, SLOT(moveRight()));
+
+    // Corner widget
+    QLabel * corner = new QLabel();
+    corner->setPixmap(QPixmap(QString::fromUtf8(":/res/inverse-corner-bl.png")));
+    corner->setFixedSize(10, 10);
+
+    // Add widgets to top layout
+    topLayout->addWidget(lblDesc);
+    topLayout->addWidget(filterContainer);
+    topLayout->addWidget(corner, 0, Qt::AlignBottom);
+    topLayout->addStretch(1);
 
     // Cancel button (closes dialog)
     QPushButton * btnCancel = new QPushButton(tr("Cancel"));
-    btnCancel->setStyleSheet("padding: 5px; margin-top: 10px;");
     connect(btnCancel, SIGNAL(clicked()), this, SLOT(reject()));
 
+    // Select button
+    QPushButton * btnSelect = new QPushButton(tr("Use selected theme"));
+    btnSelect->setDefault(true);
+    connect(btnSelect, SIGNAL(clicked()), this, SLOT(onAccepted()));
+
+    // Add themes
+    list = new ThemeListView();
+    list->setModel(filterModel);
+    list->setViewMode(QListView::IconMode);
+    list->setResizeMode(QListView::Adjust);
+    list->setMovement(QListView::Static);
+    list->setEditTriggers(QAbstractItemView::NoEditTriggers);
+    list->setSpacing(8);
+    list->setWordWrap(true);
+    list->setSelectionMode(QAbstractItemView::SingleSelection);
+    list->setObjectName("themeList");
+    list->setCurrentIndex(filterModel->index(currentIndex, 0));
+    connect(list, SIGNAL(activated(const QModelIndex &)), this, SLOT(themeChosen(const QModelIndex &)));
+    connect(list, SIGNAL(clicked(const QModelIndex &)), this, SLOT(themeChosen(const QModelIndex &)));
+
     // Add elements to layouts
-    dialogLayout->addWidget(lblDesc, 0);
-    dialogLayout->addWidget(scrollArea, 1);
-    dialogLayout->addWidget(btnCancel, 0, Qt::AlignLeft);
+    dialogLayout->addLayout(topLayout, 0, 0, 1, 3);
+    dialogLayout->addWidget(list, 1, 0, 1, 3);
+    dialogLayout->addWidget(btnCancel, 2, 0, 1, 1, Qt::AlignLeft);
+    dialogLayout->addWidget(btnSelect, 2, 2, 1, 1, Qt::AlignRight);
+}
 
-    // Tooltip label for theme name
-    lblToolTip = new QLabel(this);
+void ThemePrompt::moveUp()
+{
+    list->moveUp();
+}
 
-    // Add theme buttons
-    ThemeModel * themes = DataManager::instance().themeModel();
-    for (int i = 0; i < themes->rowCount(); i++)
-    {
-        QModelIndex index = themes->index(i, 0);
-        QToolButton * btn = new QToolButton();
-        bool dlc = themes->data(index, Qt::UserRole + 2).toBool();
-        btn->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
-        btn->setIcon(qVariantValue<QIcon>(themes->data(index, Qt::UserRole)));
-        btn->setText((dlc ? "*" : "") + themes->data(index, Qt::DisplayRole).toString());
-        btn->setIconSize(QSize(60, 60));
-        btn->setProperty("themeID", QVariant(i));
-        btn->setStyleSheet("padding: 2px;");
-        connect(btn, SIGNAL(clicked()), this, SLOT(themeClicked()));
-        themesGrid->addWidget(btn);
-    }
+void ThemePrompt::moveDown()
+{
+    list->moveDown();
+}
+
+void ThemePrompt::moveLeft()
+{
+    list->moveLeft();
+}
+
+void ThemePrompt::moveRight()
+{
+    list->moveRight();
+}
+
+void ThemePrompt::onAccepted()
+{
+    themeChosen(list->currentIndex());
 }
 
 // When a theme is selected
-void ThemePrompt::themeClicked()
+void ThemePrompt::themeChosen(const QModelIndex & index)
 {
-    QWidget * btn = (QWidget*)sender();
-    done(btn->property("themeID").toInt() + 1); // Since returning 0 means canceled
+    done(filterModel->mapToSource(index).row() + 1); // Since returning 0 means canceled
 }
+
+// When the text in the filter text box is changed
+void ThemePrompt::filterChanged(const QString & text)
+{
+    filterModel->setFilterFixedString(text);
+    list->setCurrentIndex(filterModel->index(0, 0));
+}
--- a/QTfrontend/ui/widget/themeprompt.h	Wed Feb 20 02:21:58 2013 +0100
+++ b/QTfrontend/ui/widget/themeprompt.h	Tue Apr 02 21:00:57 2013 +0200
@@ -21,21 +21,45 @@
 
 #include <QWidget>
 #include <QDialog>
+#include <QListView>
 
-class QLabel;
+class QLineEdit;
+class QModelIndex;
+class QSortFilterProxyModel;
+class LineEditCursor;
+
+class ThemeListView : public QListView
+{
+    friend class ThemePrompt;
+
+    public:
+        ThemeListView(QWidget* parent = 0) : QListView(parent){}
+        void moveUp();
+        void moveDown();
+        void moveLeft();
+        void moveRight();
+};
 
 class ThemePrompt : public QDialog
 {
         Q_OBJECT
 
     public:
-        ThemePrompt(QWidget* parent);
+        ThemePrompt(int currentIndex = 0, QWidget* parent = 0);
 
     private:
-        QLabel * lblToolTip;
+        LineEditCursor * txtFilter;
+        ThemeListView * list;
+        QSortFilterProxyModel * filterModel;
 
     private slots:
-        void themeClicked();
+        void onAccepted();
+        void themeChosen(const QModelIndex & index);
+        void filterChanged(const QString & text);
+        void moveUp();
+        void moveDown();
+        void moveLeft();
+        void moveRight();
 };
 
 #endif // THEMEPROMPT_H
--- a/QTfrontend/ui_hwform.cpp	Wed Feb 20 02:21:58 2013 +0100
+++ b/QTfrontend/ui_hwform.cpp	Tue Apr 02 21:00:57 2013 +0200
@@ -54,7 +54,11 @@
     HWForm->setObjectName(QString::fromUtf8("HWForm"));
     HWForm->resize(QSize(640, 480).expandedTo(HWForm->minimumSizeHint()));
     HWForm->setMinimumSize(QSize(720, 450));
-    HWForm->setWindowTitle(QMainWindow::tr("Hedgewars %1").arg(*cVersionString));
+    QString title = QMainWindow::tr("Hedgewars %1").arg(*cVersionString);
+#ifdef DEBUG
+    title += QMainWindow::tr("-r%1 (%2)").arg(*cRevisionString, *cHashString);
+#endif
+    HWForm->setWindowTitle(title);
     centralWidget = new QWidget(HWForm);
     centralWidget->setObjectName(QString::fromUtf8("centralWidget"));
 
--- a/QTfrontend/util/DataManager.cpp	Wed Feb 20 02:21:58 2013 +0100
+++ b/QTfrontend/util/DataManager.cpp	Tue Apr 02 21:00:57 2013 +0200
@@ -176,3 +176,12 @@
         m_colorsModel->item(i)->setData(QColor(colors[i]));
     }
 }
+
+bool DataManager::ensureFileExists(const QString &fileName)
+{
+    QFile tmpfile(fileName);
+    if (!tmpfile.exists())
+        return tmpfile.open(QFile::WriteOnly);
+    else
+        return true;
+}
--- a/QTfrontend/util/DataManager.h	Wed Feb 20 02:21:58 2013 +0100
+++ b/QTfrontend/util/DataManager.h	Tue Apr 02 21:00:57 2013 +0200
@@ -117,6 +117,8 @@
         QStandardItemModel * colorsModel();
         QStandardItemModel * bindsModel();
 
+        static bool ensureFileExists(const QString & fileName);
+
     public slots:
         /// Reloads data from storage.
         void reload();
--- a/QTfrontend/util/FileEngine.cpp	Wed Feb 20 02:21:58 2013 +0100
+++ b/QTfrontend/util/FileEngine.cpp	Tue Apr 02 21:00:57 2013 +0200
@@ -327,12 +327,12 @@
 
 void FileEngineHandler::mount(const QString &path)
 {
-    PHYSFS_mount(path.toUtf8().constData(), NULL, 1);
+    PHYSFS_mount(path.toUtf8().constData(), NULL, 0);
 }
 
 void FileEngineHandler::mount(const QString & path, const QString & mountPoint)
 {
-    PHYSFS_mount(path.toUtf8().constData(), mountPoint.toUtf8().constData(), 1);
+    PHYSFS_mount(path.toUtf8().constData(), mountPoint.toUtf8().constData(), 0);
 }
 
 void FileEngineHandler::setWriteDir(const QString &path)
--- a/QTfrontend/util/MessageDialog.cpp	Wed Feb 20 02:21:58 2013 +0100
+++ b/QTfrontend/util/MessageDialog.cpp	Tue Apr 02 21:00:57 2013 +0200
@@ -17,23 +17,39 @@
  */
 
 #include "MessageDialog.h"
+#include "HWApplication.h"
+
+int MessageDialog::ShowFatalMessage(const QString & msg, QWidget * parent)
+{
+    return ShowMessage(QMessageBox::tr("Hedgewars - Error"),
+                       msg,
+                       QMessageBox::Critical,
+                       parent);
+}
 
 int MessageDialog::ShowErrorMessage(const QString & msg, QWidget * parent)
 {
-    return ShowMessage(msg, QMessageBox::tr("Hedgewars - Warning"), QMessageBox::Warning, parent);
+    return ShowMessage(QMessageBox::tr("Hedgewars - Warning"),
+                       msg,
+                       QMessageBox::Warning,
+                       parent);
 }
 
 int MessageDialog::ShowInfoMessage(const QString & msg, QWidget * parent)
 {
-    return ShowMessage(msg, QMessageBox::tr("Hedgewars - Information"), QMessageBox::Information, parent);
+    return ShowMessage(QMessageBox::tr("Hedgewars - Information"),
+                       msg,
+                       QMessageBox::Information,
+                       parent);
 }
 
-int MessageDialog::ShowMessage(const QString & msg, const QString & title, QMessageBox::Icon icon, QWidget * parent)
+int MessageDialog::ShowMessage(const QString & title, const QString & msg, QMessageBox::Icon icon, QWidget * parent)
 {
-    QMessageBox msgMsg(parent);
+    QMessageBox msgMsg(parent ? parent : HWApplication::activeWindow());
+    msgMsg.setWindowTitle(title != NULL ? title : "Hedgewars");
+    msgMsg.setText(msg);
     msgMsg.setIcon(icon);
-    msgMsg.setWindowTitle(title.isEmpty() ? QMessageBox::tr("Hedgewars") : title);
-    msgMsg.setText(msg);
     msgMsg.setWindowModality(Qt::WindowModal);
+
     return msgMsg.exec();
 }
--- a/QTfrontend/util/MessageDialog.h	Wed Feb 20 02:21:58 2013 +0100
+++ b/QTfrontend/util/MessageDialog.h	Tue Apr 02 21:00:57 2013 +0200
@@ -19,7 +19,6 @@
 #ifndef MESSAGEDIALOG_H
 #define MESSAGEDIALOG_H
 
-#include <QString>
 #include <QMessageBox>
 
 class QWidget;
@@ -27,9 +26,18 @@
 class MessageDialog
 {
     public:
+        static int ShowFatalMessage(const QString & msg, QWidget * parent = 0);
         static int ShowErrorMessage(const QString & msg, QWidget * parent = 0);
         static int ShowInfoMessage(const QString & msg, QWidget * parent = 0);
-        static int ShowMessage(const QString & msg, const QString & title = QString(), QMessageBox::Icon icon = QMessageBox::NoIcon, QWidget * parent = 0);
+        /**
+         * @brief Displays a message.
+         * @param title message title or <code>NULL</code> if no/default title
+         * @param msg message to display
+         * @param icon (optional) icon to be displayed next to the message
+         * @param parent parent Widget
+         * @return a QMessageBox::StandardButton value indicating which button was clicked
+         */
+        static int ShowMessage(const QString & title, const QString & msg, QMessageBox::Icon icon = QMessageBox::NoIcon, QWidget * parent = 0);
 };
 
 #endif
--- a/QTfrontend/util/platform/M3InstallController.m	Wed Feb 20 02:21:58 2013 +0100
+++ b/QTfrontend/util/platform/M3InstallController.m	Tue Apr 02 21:00:57 2013 +0200
@@ -70,21 +70,43 @@
 
     //Delete the app that is installed
     if ([[NSFileManager defaultManager] fileExistsAtPath:appsPath]) {
-        [[NSFileManager defaultManager] removeFileAtPath:appsPath handler:nil];
+        if ([NSFileManager instancesRespondToSelector:@selector(removeItemAtPath:error:)])
+            [[NSFileManager defaultManager] removeItemAtPath:appsPath error:nil];
+        else
+            //casting hides the deprecation warning
+            [(id)[NSFileManager defaultManager] removeFileAtPath:appsPath handler:nil];
     }
     //Delete the app that is installed
-    if ([[NSFileManager defaultManager] copyPath:[[NSBundle mainBundle] bundlePath] toPath:appsPath
-                                          handler:nil]) {
+    BOOL success = NO;
+    if ([NSFileManager instancesRespondToSelector:@selector(copyItemAtPath:toPath:error:)])
+        success = [[NSFileManager defaultManager] copyItemAtPath:[[NSBundle mainBundle] bundlePath]
+                                                          toPath:appsPath
+                                                           error:nil];
+    else
+        success = [(id)[NSFileManager defaultManager] copyPath:[[NSBundle mainBundle] bundlePath]
+                                                        toPath:appsPath
+                                                       handler:nil];
+    if (success) {
         NSRunAlertPanel([NSString stringWithFormat:NSLocalizedString(@"%@ installed successfully", @"App Name installed successfully"), appName],
                         [NSString stringWithFormat:NSLocalizedString(@"%@ was installed in /Applications", @"App Name was installed in /Applications"), appName],
                         NSLocalizedString(@"Quit", @"Quit"), nil, nil);
     } else {
         if ([[NSFileManager defaultManager] fileExistsAtPath:userAppsPath]) {
-            [[NSFileManager defaultManager] removeFileAtPath:userAppsPath handler:nil];
+            if ([NSFileManager instancesRespondToSelector:@selector(removeItemAtPath:error:)])
+                [[NSFileManager defaultManager] removeItemAtPath:userAppsPath error:nil];
+            else
+                [(id)[NSFileManager defaultManager] removeFileAtPath:userAppsPath handler:nil];
         }
-        if ([[NSFileManager defaultManager] copyPath:[[NSBundle mainBundle] bundlePath] toPath:userAppsPath
-                                                handler:nil]) {
-        NSRunAlertPanel([NSString stringWithFormat:NSLocalizedString(@"%@ installed successfully", @"AppName installed successfully"), appName],
+        if ([NSFileManager instancesRespondToSelector:@selector(copyItemAtPath:toPath:error:)])
+            success = [[NSFileManager defaultManager] copyItemAtPath:[[NSBundle mainBundle] bundlePath]
+                                                              toPath:userAppsPath
+                                                               error:nil];
+        else
+            success = [(id)[NSFileManager defaultManager] copyPath:[[NSBundle mainBundle] bundlePath]
+                                                            toPath:userAppsPath
+                                                           handler:nil];
+        if (success) {
+            NSRunAlertPanel([NSString stringWithFormat:NSLocalizedString(@"%@ installed successfully", @"AppName installed successfully"), appName],
                 [NSString stringWithFormat:NSLocalizedString(@"%@ was installed in %@", @"App Name was installed in %@"), appName, [[NSString stringWithString:@"~/Applications"] stringByExpandingTildeInPath]],
                         NSLocalizedString(@"Quit", @"Quit"), nil, nil);
         } else {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/QTfrontend/weapons.h	Tue Apr 02 21:00:57 2013 +0200
@@ -0,0 +1,66 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (c) 2013 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
+ */
+
+
+// TODO: keep on documenting all the weapons
+//skip---------------------------------|
+//structure------------------------------------------------------------------|
+
+#define AMMOLINE_DEFAULT_QT     "9391929422199121032235111001201000000211110101011111121"
+#define AMMOLINE_DEFAULT_PROB   "0405040541600655546554464776576666666155510101115411121"
+#define AMMOLINE_DEFAULT_DELAY  "0000000000000205500000040007004000000000220000000600020"
+#define AMMOLINE_DEFAULT_CRATE  "1311110312111111123114111111111111111211111101111111121"
+
+#define AMMOLINE_CRAZY_QT       "9999999999999999992999999999999999299999999909999992999"
+#define AMMOLINE_CRAZY_PROB     "1111110111111111111111111111111111111111111101111111111"
+#define AMMOLINE_CRAZY_DELAY    "0000000000000000000000000000000000000000000000000000000"
+#define AMMOLINE_CRAZY_CRATE    "1311110312111111123114111111111111111211110101111111121"
+
+#define AMMOLINE_PROMODE_QT     "9090009000000000000009000000000000000000000000000000000"
+#define AMMOLINE_PROMODE_PROB   "0000000000000000000000000000000000000000000000000000000"
+#define AMMOLINE_PROMODE_DELAY  "0000000000000205500000040007004000000000200000000000020"
+#define AMMOLINE_PROMODE_CRATE  "1111110111111111111111111111111111111111100101111111121"
+
+#define AMMOLINE_SHOPPA_QT      "0000009900000000000000000000000000000000000000000000000"
+#define AMMOLINE_SHOPPA_PROB    "4444410044244402210112121222422000000002000400010011001"
+#define AMMOLINE_SHOPPA_DELAY   "0000000000000000000000000000000000000000000000000000000"
+#define AMMOLINE_SHOPPA_CRATE   "1111110111111111111111111111111111111111101101111111121"
+
+#define AMMOLINE_CLEAN_QT       "1010009000010000011000000000000000000000000000001000000"
+#define AMMOLINE_CLEAN_PROB     "0405040541600655546554464776576666666155510101115411121"
+#define AMMOLINE_CLEAN_DELAY    "0000000000000000000000000000000000000000000000000000020"
+#define AMMOLINE_CLEAN_CRATE    "1311110312111111123114111111111111111211111101111111121"
+
+#define AMMOLINE_MINES_QT       "0000009900090000000300000000000000000000000000000000000"
+#define AMMOLINE_MINES_PROB     "0000000000000000000000000000000000000000000000000000000"
+#define AMMOLINE_MINES_DELAY    "0000000000000205500000040007004000000000200000000600020"
+#define AMMOLINE_MINES_CRATE    "1111110111111111111111111111111111111111111101111111121"
+
+#define AMMOLINE_PORTALS_QT     "9000009002000000002100000000000000110000090000000000000"
+#define AMMOLINE_PORTALS_PROB   "0405040541600655546554464776576666666155510101115411121"
+#define AMMOLINE_PORTALS_DELAY  "0000000000000205500000040007004000000000200000000600020"
+#define AMMOLINE_PORTALS_CRATE  "1311110312111111123114111111111111111211111101111111121"
+
+#define AMMOLINE_ONEEVERY_QT    "1111119111111111111111111111111111111111111111111111111"
+#define AMMOLINE_ONEEVERY_PROB  "1111110111111111111111111111111111111111111111111111111"
+#define AMMOLINE_ONEEVERY_DELAY "0000000000000205500000040007004000000000220000000600020"
+#define AMMOLINE_ONEEVERY_CRATE "1111110111111111111111111111111111111111111111111111111"
+
+//When adding new weapons also inster one element in cDefaultAmmos list (hwconsts.cpp.in)
+
+
--- a/README	Wed Feb 20 02:21:58 2013 +0100
+++ b/README	Tue Apr 02 21:00:57 2013 +0200
@@ -12,3 +12,6 @@
 - http://code.google.com/p/hedgewars/wiki/BuildingOnWindows
 - http://code.google.com/p/hedgewars/wiki/BuildingOnMac
 
+Dependencies:
+you can find an outline of the necessary dependencies in the INSTALL file.
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cmake_modules/CPackConfig.cmake	Tue Apr 02 21:00:57 2013 +0200
@@ -0,0 +1,99 @@
+
+# revision information in cpack-generated names
+if(CMAKE_BUILD_TYPE MATCHES DEBUG)
+    set(full_suffix "${HEDGEWARS_VERSION}-r${HEDGEWARS_REVISION}")
+else()
+    set(full_suffix "${HEDGEWARS_VERSION}")
+endif()
+
+# CPack variables
+set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Hedgewars, a free turn-based strategy game")
+set(CPACK_PACKAGE_VENDOR "Hedgewars Project")
+set(CPACK_PACKAGE_FILE_NAME "Hedgewars-${full_suffix}")
+set(CPACK_SOURCE_PACKAGE_FILE_NAME "hedgewars-src-${full_suffix}")
+set(CPACK_SOURCE_GENERATOR "TBZ2")
+set(CPACK_PACKAGE_EXECUTABLES "hedgewars" "Hedgewars")
+set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/COPYING")
+set(CPACK_PACKAGE_INSTALL_DIRECTORY "Hedgewars ${full_suffix}")
+set(CPACK_STRIP_FILES true)
+
+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_NSIS_EXECUTABLES_DIRECTORY "${target_binary_install_dir}")
+    set(CPACK_NSIS_MUI_FINISHPAGE_RUN "hedgewars${CMAKE_EXECUTABLE_SUFFIX}")
+    set(CPACK_GENERATOR "ZIP;NSIS")
+    set(CPACK_PACKAGE_INSTALL_REGISTRY_KEY "hedgewars")
+endif(WIN32 AND NOT UNIX)
+
+set(CPACK_SOURCE_IGNORE_FILES
+    #temporary files
+    "~"
+    ".swp"
+    #version control
+    "\\\\.hg"
+    #output binary/library
+    "\\\\.exe$"
+    "\\\\.a$"
+    "\\\\.so$"
+    "\\\\.dylib$"
+    "\\\\.dll$"
+    "\\\\.ppu$"
+    "\\\\.o$"
+    "\\\\.cxx$"
+    #graphics
+    "\\\\.xcf$"
+    "\\\\.svg$"
+    "\\\\.svgz$"
+    "\\\\.psd$"
+    "\\\\.sifz$"
+    #misc
+    "\\\\.core$"
+    "\\\\.sh$"
+    "\\\\.orig$"
+    "\\\\.layout$"
+    "\\\\.db$"
+    "\\\\.dof$"
+    #archives
+    "\\\\.zip$"
+    "\\\\.gz$"
+    "\\\\.bz2$"
+    "\\\\.tmp$"
+    #cmake-configured files
+    "hwconsts\\\\.cpp$"
+    "config\\\\.inc$"
+    "hwengine\\\\.desktop$"
+    "Info\\\\.plist$"
+    #other cmake generated files
+    "Makefile"
+    "Doxyfile"
+    "CMakeFiles"
+    "[dD]ebug$"
+    "[rR]elease$"
+    "CPack"
+    "cmake_install\\\\.cmake$"
+    "CMakeCache\\\\.txt$"
+#    "^${CMAKE_CURRENT_SOURCE_DIR}/misc/libtremor"
+#    "^${CMAKE_CURRENT_SOURCE_DIR}/misc/libfreetype"
+#    "^${CMAKE_CURRENT_SOURCE_DIR}/misc/liblua"
+    "^${CMAKE_CURRENT_SOURCE_DIR}/misc/libopenalbridge"
+    "^${CMAKE_CURRENT_SOURCE_DIR}/project_files/frontlib"
+    "^${CMAKE_CURRENT_SOURCE_DIR}/project_files/promotional_art"
+    "^${CMAKE_CURRENT_SOURCE_DIR}/project_files/cmdlineClient"
+    "^${CMAKE_CURRENT_SOURCE_DIR}/tools/templates"
+    "^${CMAKE_CURRENT_SOURCE_DIR}/bin/checkstack*"
+    "^${CMAKE_CURRENT_SOURCE_DIR}/doc"
+    "^${CMAKE_CURRENT_SOURCE_DIR}/templates"
+    "^${CMAKE_CURRENT_SOURCE_DIR}/tmp"
+    "^${CMAKE_CURRENT_SOURCE_DIR}/utils"
+    "^${CMAKE_CURRENT_SOURCE_DIR}/share/hedgewars/Data/Maps/test"
+    "^${CMAKE_CURRENT_SOURCE_DIR}/install_manifest.txt"
+    "^${CMAKE_CURRENT_SOURCE_DIR}/CMakeCache.txt"
+    "^${CMAKE_CURRENT_SOURCE_DIR}/hedgewars\\\\."
+)
+
+include(CPack)
+
--- a/cmake_modules/FindFFMPEG.cmake	Wed Feb 20 02:21:58 2013 +0100
+++ b/cmake_modules/FindFFMPEG.cmake	Tue Apr 02 21:00:57 2013 +0200
@@ -1,96 +1,83 @@
-# - Try to find ffmpeg libraries (libavcodec, libavformat and libavutil)
+# Find ffmpeg/libav libraries (libavcodec, libavformat and libavutil)
 # Once done this will define
 #
-#  FFMPEG_FOUND - system has ffmpeg or libav
-#  FFMPEG_INCLUDE_DIR - the ffmpeg include directory
-#  FFMPEG_LIBRARIES - Link these to use ffmpeg
-#  FFMPEG_LIBAVCODEC
-#  FFMPEG_LIBAVFORMAT
-#  FFMPEG_LIBAVUTIL
+#  FFMPEG_FOUND             - system has libavcodec, libavformat, libavutil
+#  FFMPEG_INCLUDE_DIR       - the libav include directories
+#  FFMPEG_LIBRARIES         - the libav libraries
+#
+#  LIBAVCODEC_LIBRARY      - the libavcodec library
+#  LIBAVCODEC_INCLUDE_DIR  - the libavcodec include directory
+#  LIBAVFORMAT_LIBRARY     - the libavformat library
+#  LIBAVUTIL_LIBRARY       - the libavutil library
 #
 #  Copyright (c) 2008 Andreas Schneider <mail@cynapses.org>
 #  Modified for other libraries by Lasse Kärkkäinen <tronic>
 #  Modified for Hedgewars by Stepik777
+#  Copyright (c) 2013 Vittorio Giovara <vittorio.giovara@gmail.com>
 #
 #  Redistribution and use is allowed according to the terms of the New
 #  BSD license.
 #
 
-set(FFMPEG_FOUND FALSE)
+include(FindPackageHandleStandardArgs)
+
 
-if (FFMPEG_LIBRARIES AND FFMPEG_INCLUDE_DIR)
-  # in cache already
-  set(FFMPEG_FOUND TRUE)
-else (FFMPEG_LIBRARIES AND FFMPEG_INCLUDE_DIR)
-  # silence output option
-  if (FFMPEG_FIND_QUIETLY)
-    set(VERBOSITY "QUIET")
-  endif ()
-  # use pkg-config to get the directories and then use these values
-  # in the FIND_PATH() and FIND_LIBRARY() calls
-  find_package(PkgConfig)
-  if (PKG_CONFIG_FOUND)
+# use pkg-config to get the directories and then use these values
+# in the FIND_PATH() and FIND_LIBRARY() calls
+find_package(PkgConfig)
+if (PKG_CONFIG_FOUND)
     pkg_check_modules(_FFMPEG_AVCODEC libavcodec ${VERBOSITY})
     pkg_check_modules(_FFMPEG_AVFORMAT libavformat ${VERBOSITY})
     pkg_check_modules(_FFMPEG_AVUTIL libavutil ${VERBOSITY})
-  endif (PKG_CONFIG_FOUND)
+endif (PKG_CONFIG_FOUND)
 
-  find_path(FFMPEG_AVCODEC_INCLUDE_DIR
+find_path(LIBAVCODEC_INCLUDE_DIR
     NAMES libavcodec/avcodec.h
-    PATHS ${_FFMPEG_AVCODEC_INCLUDE_DIRS}
+    PATHS ${_AVCODEC_INCLUDE_DIRS}
         /usr/include /usr/local/include #system level
         /opt/local/include #macports
         /sw/include #fink
-    PATH_SUFFIXES ffmpeg libav
-  )
+    PATH_SUFFIXES libav ffmpeg
+)
 
-  find_library(FFMPEG_LIBAVCODEC
+#TODO: add other include paths
+
+find_library(LIBAVCODEC_LIBRARY
     NAMES avcodec
-    PATHS ${_FFMPEG_AVCODEC_LIBRARY_DIRS}
+    PATHS ${_AVCODEC_LIBRARY_DIRS}
         /usr/lib /usr/local/lib #system level
         /opt/local/lib #macports
         /sw/lib #fink
-  )
+)
 
-  find_library(FFMPEG_LIBAVFORMAT
+find_library(LIBAVFORMAT_LIBRARY
     NAMES avformat
-    PATHS ${_FFMPEG_AVFORMAT_LIBRARY_DIRS}
+    PATHS ${_AVFORMAT_LIBRARY_DIRS}
         /usr/lib /usr/local/lib #system level
         /opt/local/lib #macports
         /sw/lib #fink
-  )
+)
 
-  find_library(FFMPEG_LIBAVUTIL
+find_library(LIBAVUTIL_LIBRARY
     NAMES avutil
-    PATHS ${_FFMPEG_AVUTIL_LIBRARY_DIRS}
+    PATHS ${_AVUTIL_LIBRARY_DIRS}
         /usr/lib /usr/local/lib #system level
         /opt/local/lib #macports
         /sw/lib #fink
-  )
-
-  if (FFMPEG_LIBAVCODEC AND FFMPEG_LIBAVFORMAT)
-    set(FFMPEG_FOUND TRUE)
-  endif()
-
-  if (FFMPEG_FOUND)
-    set(FFMPEG_INCLUDE_DIR ${FFMPEG_AVCODEC_INCLUDE_DIR})
+)
 
-    set(FFMPEG_LIBRARIES
-      ${FFMPEG_LIBAVCODEC}
-      ${FFMPEG_LIBAVFORMAT}
-      ${FFMPEG_LIBAVUTIL}
-    )
-  endif (FFMPEG_FOUND)
+find_package_handle_standard_args(FFMPEG DEFAULT_MSG LIBAVCODEC_LIBRARY LIBAVCODEC_INCLUDE_DIR
+                                                     LIBAVFORMAT_LIBRARY
+                                                     LIBAVUTIL_LIBRARY
+                                                     )
+set(FFMPEG_INCLUDE_DIR ${LIBAVCODEC_INCLUDE_DIR}
+                       #TODO: add other include paths
+                       )
+set(FFMPEG_LIBRARIES ${LIBAVCODEC_LIBRARY}
+                     ${LIBAVFORMAT_LIBRARY}
+                     ${LIBAVUTIL_LIBRARY}
+                     )
 
-  if (FFMPEG_FOUND)
-    if (NOT FFMPEG_FIND_QUIETLY)
-      message(STATUS "Found FFMPEG/LibAV: ${FFMPEG_LIBRARIES}, ${FFMPEG_INCLUDE_DIR}")
-    endif (NOT FFMPEG_FIND_QUIETLY)
-  else (FFMPEG_FOUND)
-    if (FFMPEG_FIND_REQUIRED)
-      message(FATAL_ERROR "Could NOT find libavcodec or libavformat or libavutil")
-    endif (FFMPEG_FIND_REQUIRED)
-  endif (FFMPEG_FOUND)
+mark_as_advanced(FFMPEG_INCLUDE_DIR FFMPEG_LIBRARIES LIBAVCODEC_LIBRARY LIBAVCODEC_INCLUDE_DIR LIBAVFORMAT_LIBRARY LIBAVUTIL_LIBRARY)
 
-endif (FFMPEG_LIBRARIES AND FFMPEG_INCLUDE_DIR)
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cmake_modules/FindFreePascal.cmake	Tue Apr 02 21:00:57 2013 +0200
@@ -0,0 +1,37 @@
+# - Try to find the FreePascal executable
+# Once done this will define
+#
+#  FREEPASCAL_FOUND       - system has Freepascal
+#  FREEPASCAL_VERSION     - Freepascal version
+#  FREEPASCAL_EXECUTABLE  - Freepascal executable
+#
+# Copyright (c) 2012, Bryan Dunsmore <dunsmoreb@gmail.com>
+# Copyright (c) 2013, Vittorio Giovara <vittorio.giovara@gmail.com>
+#
+# Redistribution and use is allowed according to the terms of the BSD license.
+# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
+
+
+find_program(FREEPASCAL_EXECUTABLE
+    NAMES fpc
+    PATHS /opt/local/bin /usr/local/bin /usr/bin
+    )
+
+if (FREEPASCAL_EXECUTABLE)
+    # check Freepascal version
+    execute_process(COMMAND ${FREEPASCAL_EXECUTABLE} -iV
+                    OUTPUT_VARIABLE FREEPASCAL_VERSION
+                    ERROR_VARIABLE FREEPASCAL_VERSION_ERROR
+                    RESULT_VARIABLE FREEPASCAL_VERSION_RESULT
+                    OUTPUT_STRIP_TRAILING_WHITESPACE
+                    )
+
+    if(NOT ${FREEPASCAL_VERSION_RESULT} EQUAL 0)
+        message(SEND_ERROR "Command \"${FREEPASCAL_EXECUTABLE} -iV\" failed with output: ${FREEPASCAL_VERSION_ERROR}")
+    endif()
+endif()
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(FreePascal DEFAULT_MSG FREEPASCAL_EXECUTABLE FREEPASCAL_VERSION)
+mark_as_advanced(FREEPASCAL_VERSION)
+
--- a/cmake_modules/FindFreepascal.cmake	Wed Feb 20 02:21:58 2013 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,37 +0,0 @@
-# Load Freepascal
-if (FPC)
-    set(FPC_EXECUTABLE ${FPC})
-else()
-    find_program(FPC_EXECUTABLE
-        NAMES fpc
-        PATHS /opt/local/bin /usr/local/bin /usr/bin)
-endif()
-
-# Check Freepascal version
-if (FPC_EXECUTABLE)
-    exec_program(${FPC_EXECUTABLE} ARGS "-v" OUTPUT_VARIABLE FPC_VERSION_FULL)
-
-    string(REGEX MATCH "[0-9]+\\.[0-9]+" FPC_VERSION_LONG "${FPC_VERSION_FULL}")
-    string(REGEX REPLACE "([0-9]+\\.[0-9]+)" "\\1" FPC_VERSION "${FPC_VERSION_LONG}")
-    message(STATUS "Found Freepascal: ${FPC_EXECUTABLE} (version ${FPC_VERSION})")
-else()
-    message(FATAL_ERROR "Could NOT find Freepascal")
-endif()
-
-# Check for noexecstack flag support
-message(STATUS "Checking whether linker needs explicit noexecstack")
-set(NOEXECSTACK_FLAGS "-k-z" "-knoexecstack")
-file(WRITE ${EXECUTABLE_OUTPUT_PATH}/checkstack.pas "begin end.")
-
-execute_process(COMMAND ${FPC_EXECUTABLE} ${NOEXECSTACK_FLAGS} checkstack.pas
-    WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}
-    RESULT_VARIABLE TEST_NOEXECSTACK
-    OUTPUT_QUIET ERROR_QUIET)
-
-if (TEST_NOEXECSTACK)
-    set(NOEXECSTACK_FLAGS "")
-    message(STATUS "Checking whether linker needs explicit noexecstack -- no")
-else(TEST_NOEXECSTACK)
-    message(STATUS "Checking whether linker needs explicit noexecstack -- yes")
-endif(TEST_NOEXECSTACK)
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cmake_modules/FindGHC.cmake	Tue Apr 02 21:00:57 2013 +0200
@@ -0,0 +1,38 @@
+# - Try to find the Glasgow Haskell Compiler executable
+# Once done this will define
+#
+#  GHC_FOUND       - system has GHC
+#  GHC_VERSION     - GHC version
+#  GHC_EXECUTABLE  - GHC executable
+#
+# Copyright (c) 2013, Vittorio Giovara <vittorio.giovara@gmail.com>
+#
+# Redistribution and use is allowed according to the terms of the BSD license.
+# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
+
+
+find_program(GHC_EXECUTABLE
+    NAMES ghc
+    PATHS /opt/local/bin /usr/local/bin /usr/bin
+    )
+
+if (GHC_EXECUTABLE)
+    # check Freepascal version
+    execute_process(COMMAND ${GHC_EXECUTABLE} -V
+                    OUTPUT_VARIABLE GHC_VERSION_OUTPUT
+                    ERROR_VARIABLE GHC_VERSION_ERROR
+                    RESULT_VARIABLE GHC_VERSION_RESULT
+                    OUTPUT_STRIP_TRAILING_WHITESPACE
+                    )
+
+    if(${GHC_VERSION_RESULT} EQUAL 0)
+        string(REGEX MATCH "([0-9]+)" GHC_VERSION ${GHC_VERSION_OUTPUT})
+    else()
+        message(SEND_ERROR "Command \"${GHC_EXECUTABLE} -V\" failed with output: ${GHC_VERSION_ERROR}")
+    endif()
+endif()
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(GHC DEFAULT_MSG GHC_EXECUTABLE GHC_VERSION)
+mark_as_advanced(GHC_VERSION)
+
--- a/cmake_modules/FindOggVorbis.cmake	Wed Feb 20 02:21:58 2013 +0100
+++ b/cmake_modules/FindOggVorbis.cmake	Tue Apr 02 21:00:57 2013 +0200
@@ -1,61 +1,68 @@
-### SuperTux - Removed unused vorbisenc library
-
 # - Try to find the OggVorbis libraries
 # Once done this will define
 #
-#  OGGVORBIS_FOUND - system has OggVorbis
-#  OGGVORBIS_VERSION - set either to 1 or 2
+#  OGGVORBIS_FOUND       - system has both Ogg and Vorbis
+#  OGGVORBIS_VERSION     - set either to 1 or 2
 #  OGGVORBIS_INCLUDE_DIR - the OggVorbis include directory
-#  OGGVORBIS_LIBRARIES - The libraries needed to use OggVorbis
-#  OGG_LIBRARY         - The Ogg library
-#  VORBIS_LIBRARY      - The Vorbis library
-#  VORBISFILE_LIBRARY  - The VorbisFile library
-# Copyright (c) 2006, Richard Laerkaeng, <richard@goteborg.utfors.se>
+#  OGGVORBIS_LIBRARIES   - the libraries needed to use OggVorbis
+#
+#  OGG_LIBRARY           - the Ogg library
+#  OGG_INCLUDE_DIR       - the Ogg include directory
+#  VORBIS_LIBRARY        - the Vorbis library
+#  VORBIS_INCLUDE_DIR    - the Vorbis include directory
+#  VORBISFILE_LIBRARY    - the VorbisFile library
+#
+# Copyright (c) 2006, Richard Laerkaeng <richard@goteborg.utfors.se>
+# Copyright (c) 2013, Vittorio Giovara <vittorio.giovara@gmail.com>
 #
 # Redistribution and use is allowed according to the terms of the BSD license.
 # For details see the accompanying COPYING-CMAKE-SCRIPTS file.
 
+### sommer [SuperTux]
+##  - Removed unused vorbisenc library
+##  - reversed order of libraries, so that cmake 2.4.5 for Windows generates an MSYS Makefile that will link correctly
+
+### koda [Hedgewars]
+##  - split ogg and vorbis lookup
+##  - special case for framework handling
+##  - standard variables handling
+
 
 include (CheckLibraryExists)
-find_path(VORBIS_INCLUDE_DIR vorbis/vorbisfile.h)
+include (FindPackageHandleStandardArgs)
+
+find_path(OGG_INCLUDE_DIR ogg.h PATH_SUFFIXES ogg)
+find_path(VORBIS_INCLUDE_DIR vorbisfile.h PATH_SUFFIXES vorbis)
+
+find_library(OGG_LIBRARY NAMES Ogg ogg)
+find_library(VORBIS_LIBRARY NAMES Vorbis vorbis)
+find_library(VORBISFILE_LIBRARY NAMES vorbisfile)
 
-find_library(OGG_LIBRARY NAMES ogg)
-find_library(VORBIS_LIBRARY NAMES vorbis)
-find_library(VORBISFILE_LIBRARY NAMES vorbisfile)
-if(APPLE AND NOT VORBISFILE_LIBRARY)
-#  [koda] (for Hedgewars) frameworks don't come with libvorbisfile
-   set(VORBISFILE_LIBRARY "${VORBIS_LIBRARY}")
+set(_CMAKE_REQUIRED_LIBRARIES_TMP ${CMAKE_REQUIRED_LIBRARIES})
+set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} ${OGGVORBIS_LIBRARIES})
+check_library_exists(${VORBIS_LIBRARY} vorbis_bitrate_addblock "" HAVE_LIBVORBISENC2)
+set(CMAKE_REQUIRED_LIBRARIES ${_CMAKE_REQUIRED_LIBRARIES_TMP})
+
+if(HAVE_LIBVORBISENC2)
+    set(OGGVORBIS_VERSION 2)
+else(HAVE_LIBVORBISENC2)
+    set(OGGVORBIS_VERSION 1)
+endif(HAVE_LIBVORBISENC2)
+
+if(${OGG_LIBRARY} MATCHES ".framework" AND ${VORBIS_LIBRARY} MATCHES ".framework")
+    set(VORBISFILE_LIBRARY "") #vorbisfile will appear as NOTFOUND and discarded
+    set(fphsa_vorbis_list VORBIS_LIBRARY)
+else()
+    set(fphsa_vorbis_list VORBISFILE_LIBRARY VORBIS_LIBRARY)
 endif()
 
-if (OGG_LIBRARY AND VORBIS_LIBRARY AND VORBISFILE_LIBRARY)
-   set(OGGVORBIS_FOUND TRUE)
-#  [sommer] (for SuperTux) reversed order of libraries, so that cmake 2.4.5 for Windows generates an MSYS Makefile that will link correctly
-#  set(OGGVORBIS_LIBRARIES ${OGG_LIBRARY} ${VORBIS_LIBRARY} ${VORBISFILE_LIBRARY})
-   set(OGGVORBIS_LIBRARIES ${VORBISFILE_LIBRARY} ${VORBIS_LIBRARY} ${OGG_LIBRARY})
-   set(_CMAKE_REQUIRED_LIBRARIES_TMP ${CMAKE_REQUIRED_LIBRARIES})
-   set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} ${OGGVORBIS_LIBRARIES})
-   check_library_exists(vorbis vorbis_bitrate_addblock "" HAVE_LIBVORBISENC2)
-   set(CMAKE_REQUIRED_LIBRARIES ${_CMAKE_REQUIRED_LIBRARIES_TMP})
-   if (HAVE_LIBVORBISENC2)
-      set (OGGVORBIS_VERSION 2)
-   else (HAVE_LIBVORBISENC2)
-      set (OGGVORBIS_VERSION 1)
-   endif (HAVE_LIBVORBISENC2)
-else ()
-   set(OGGVORBIS_VERSION)
-   set(OGGVORBIS_FOUND FALSE)
-endif ()
-if (OGGVORBIS_FOUND)
-   if (NOT OggVorbis_FIND_QUIETLY)
-      message(STATUS "Found OggVorbis: ${OGGVORBIS_LIBRARIES}")
-   endif (NOT OggVorbis_FIND_QUIETLY)
-else (OGGVORBIS_FOUND)
-   if (OggVorbis_FIND_REQUIRED)
-      message(FATAL_ERROR "Could NOT find OggVorbis libraries")
-   else (OggVorbis_FIND_REQUIRED)
-      if (NOT OggVorbis_FIND_QUIETLY)
-         message(STATUS "Could NOT find OggVorbis libraries")
-      endif (NOT OggVorbis_FIND_QUIETLY)
-   endif(OggVorbis_FIND_REQUIRED)
-endif (OGGVORBIS_FOUND)
+find_package_handle_standard_args(OggVorbis DEFAULT_MSG ${fphsa_vorbis_list} OGG_LIBRARY
+                                                        OGG_INCLUDE_DIR VORBIS_INCLUDE_DIR)
+unset(fphsa_vorbis_list)
 
+set(OGGVORBIS_LIBRARIES ${VORBISFILE_LIBRARY} ${VORBIS_LIBRARY} ${OGG_LIBRARY})
+set(OGGVORBIS_INCLUDE_DIR ${VORBIS_INCLUDE_DIR} ${OGG_INCLUDE_DIR})
+
+mark_as_advanced(OGGVORBIS_VERSION OGGVORBIS_INCLUDE_DIR OGGVORBIS_LIBRARIES
+                 OGG_LIBRARY OGG_INCLUDE_DIR VORBIS_LIBRARY VORBIS_INCLUDE_DIR VORBISFILE_LIBRARY)
+
--- a/cmake_modules/FindSDL_Extras.cmake	Wed Feb 20 02:21:58 2013 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,36 +0,0 @@
-#if the headers are not installed, the newer apis won't be activated
-
-#find which version of SDL_mixer we have (for Mix_Init)
-find_file(sdlmixer_h SDL_mixer.h ${SDLMIXER_INCLUDE_DIR})
-if(sdlmixer_h)
-    file(STRINGS ${sdlmixer_h} sdlmixer_majorversion_tmp REGEX "SDL_MIXER_MAJOR_VERSION[\t' ']+[0-9]+")
-    file(STRINGS ${sdlmixer_h} sdlmixer_minorversion_tmp REGEX "SDL_MIXER_MINOR_VERSION[\t' ']+[0-9]+")
-    file(STRINGS ${sdlmixer_h} sdlmixer_patchversion_tmp REGEX "SDL_MIXER_PATCHLEVEL[\t' ']+[0-9]+")
-    string(REGEX MATCH ".([0-9]+)" sdlmixer_majorversion "${sdlmixer_majorversion_tmp}")
-    string(REGEX MATCH ".([0-9]+)" sdlmixer_minorversion "${sdlmixer_minorversion_tmp}")
-    string(REGEX MATCH ".([0-9]+)" sdlmixer_patchversion "${sdlmixer_patchversion_tmp}")
-    math(EXPR sdlmixer_version "${sdlmixer_majorversion}*10000 + ${sdlmixer_minorversion}*100 + ${sdlmixer_patchversion}")
-
-    if(sdlmixer_version GREATER "10209")
-        message(STATUS "Mix_Init() is present")
-        list(APPEND pascal_flags "-dSDL_MIXER_NEWER")
-    endif()
-endif()
-
-#find which version of SDL_image we have (for IMG_Init)
-find_file(sdlimage_h SDL_image.h ${SDLIMAGE_INCLUDE_DIR})
-if(sdlimage_h)
-    file(STRINGS ${sdlimage_h} sdlimage_majorversion_tmp REGEX "SDL_IMAGE_MAJOR_VERSION[\t' ']+[0-9]+")
-    file(STRINGS ${sdlimage_h} sdlimage_minorversion_tmp REGEX "SDL_IMAGE_MINOR_VERSION[\t' ']+[0-9]+")
-    file(STRINGS ${sdlimage_h} sdlimage_patchversion_tmp REGEX "SDL_IMAGE_PATCHLEVEL[\t' ']+[0-9]+")
-    string(REGEX MATCH ".([0-9]+)" sdlimage_majorversion "${sdlimage_majorversion_tmp}")
-    string(REGEX MATCH ".([0-9]+)" sdlimage_minorversion "${sdlimage_minorversion_tmp}")
-    string(REGEX MATCH ".([0-9]+)" sdlimage_patchversion "${sdlimage_patchversion_tmp}")
-    math(EXPR sdlimage_version "${sdlimage_majorversion}*10000 + ${sdlimage_minorversion}*100 + ${sdlimage_patchversion}")
-
-    if(sdlimage_version GREATER "010207")
-        message(STATUS "IMG_Init() is present")
-        list(APPEND pascal_flags "-dSDL_IMAGE_NEWER")
-    endif()
-endif()
-
--- a/cmake_modules/FindSparkle.cmake	Wed Feb 20 02:21:58 2013 +0100
+++ b/cmake_modules/FindSparkle.cmake	Tue Apr 02 21:00:57 2013 +0200
@@ -1,39 +1,23 @@
-### Hedgewars
-
-# - Try to find the Sparkle framework
+# Find Sparkle.framework
+#
 # Once done this will define
-#
 #  SPARKLE_FOUND - system has Sparkle
 #  SPARKLE_INCLUDE_DIR - the Sparkle include directory
 #  SPARKLE_LIBRARY - The library needed to use Sparkle
-# Copyright (c) 2009, Vittorio Giovara, <vittorio.giovara@gmail.com>
+# Copyright (c) 2009, Vittorio Giovara <vittorio.giovara@gmail.com>
+#
+# Distributed under the OSI-approved BSD License (the "License");
+# see accompanying file Copyright.txt for details.
 #
-# Redistribution and use is allowed according to the terms of a Creative Commons license.
-# For details see http://creativecommons.org/licenses/by-sa/3.0/
-# original version of this module was derived from Richard Laerkaeng, <richard@goteborg.utfors.se>
+# This software is distributed WITHOUT ANY WARRANTY; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the License for more information.
 
+include(FindPackageHandleStandardArgs)
 
-include (CheckLibraryExists)
 find_path(SPARKLE_INCLUDE_DIR Sparkle.h)
 find_library(SPARKLE_LIBRARY NAMES Sparkle)
 
-if (SPARKLE_INCLUDE_DIR AND SPARKLE_LIBRARY)
-   set(SPARKLE_FOUND TRUE)
-else ()
-   set(SPARKLE_FOUND FALSE)
-endif ()
+find_package_handle_standard_args(Sparkle DEFAULT_MSG SPARKLE_INCLUDE_DIR SPARKLE_LIBRARY)
+mark_as_advanced(SPARKLE_INCLUDE_DIR SPARKLE_LIBRARY)
 
-if (SPARKLE_FOUND)
-   if (NOT SPARKLE_FIND_QUIETLY)
-      message(STATUS "Found Sparkle: ${SPARKLE_LIBRARY}")
-   endif ()
-else ()
-   if (SPARKLE_FIND_REQUIRED)
-      message(FATAL_ERROR "Could NOT find Sparkle framework")
-   else ()
-      if (NOT SPARKLE_FIND_QUIETLY)
-         message(STATUS "Could NOT find Sparkle framework, autoupdate feature will be disabled")
-      endif()
-   endif ()
-endif ()
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cmake_modules/utils.cmake	Tue Apr 02 21:00:57 2013 +0200
@@ -0,0 +1,30 @@
+
+macro(find_package_or_fail _PKG_NAME)
+    find_package(${_PKG_NAME})
+    string(TOUPPER ${_PKG_NAME} _PKG_NAME_UP)
+    if(NOT ${_PKG_NAME_UP}_FOUND)
+        message(SEND_ERROR "Missing ${_PKG_NAME}! Please install it and rerun cmake.")
+    endif(NOT ${_PKG_NAME_UP}_FOUND)
+endmacro(find_package_or_fail _PKG_NAME)
+
+macro(find_package_or_disable _PKG_NAME _VAR_NAME)
+    find_package(${_PKG_NAME})
+    string(TOUPPER ${_PKG_NAME} _PKG_NAME_UP)
+    if(NOT ${_PKG_NAME_UP}_FOUND)
+        message(SEND_ERROR "Missing ${_PKG_NAME}! Rerun cmake with -D${_VAR_NAME}=1 to build without it.")
+    endif(NOT ${_PKG_NAME_UP}_FOUND)
+endmacro(find_package_or_disable _PKG_NAME _VAR_NAME)
+
+macro(find_package_or_disable_msg _PKG_NAME _VAR_NAME _MSG)
+    if(NOT ${_VAR_NAME})
+        find_package_or_disable(${_PKG_NAME} ${_VAR_NAME})
+    else(NOT ${_VAR_NAME})
+        message(STATUS "${_PKG_NAME} disabled. ${_MSG}")
+        string(TOUPPER ${_PKG_NAME} _PKG_NAME_UP)
+        set(${_PKG_NAME_UP}_FOUND false)
+    endif(NOT ${_VAR_NAME})
+endmacro(find_package_or_disable_msg _PKG_NAME _VAR_NAME _MSG)
+
+
+#TODO: find_package_or_bundle
+
--- a/gameServer/Actions.hs	Wed Feb 20 02:21:58 2013 +0100
+++ b/gameServer/Actions.hs	Tue Apr 02 21:00:57 2013 +0200
@@ -32,62 +32,9 @@
 import ConfigFile
 import EngineInteraction
 
-data Action =
-    AnswerClients ![ClientChan] ![B.ByteString]
-    | SendServerMessage
-    | SendServerVars
-    | MoveToRoom RoomIndex
-    | MoveToLobby B.ByteString
-    | RemoveTeam B.ByteString
-    | SendTeamRemovalMessage B.ByteString
-    | RemoveRoom
-    | FinishGame
-    | UnreadyRoomClients
-    | JoinLobby
-    | ProtocolError B.ByteString
-    | Warning B.ByteString
-    | NoticeMessage Notice
-    | ByeClient B.ByteString
-    | KickClient ClientIndex
-    | KickRoomClient ClientIndex
-    | BanClient NominalDiffTime B.ByteString ClientIndex
-    | BanIP B.ByteString NominalDiffTime B.ByteString
-    | BanNick B.ByteString NominalDiffTime B.ByteString
-    | BanList
-    | Unban B.ByteString
-    | ChangeMaster (Maybe ClientIndex)
-    | RemoveClientTeams
-    | ModifyClient (ClientInfo -> ClientInfo)
-    | ModifyClient2 ClientIndex (ClientInfo -> ClientInfo)
-    | ModifyRoomClients (ClientInfo -> ClientInfo)
-    | ModifyRoom (RoomInfo -> RoomInfo)
-    | ModifyServerInfo (ServerInfo -> ServerInfo)
-    | AddRoom B.ByteString B.ByteString
-    | SendUpdateOnThisRoom
-    | CheckRegistered
-    | ClearAccountsCache
-    | ProcessAccountInfo AccountInfo
-    | AddClient ClientInfo
-    | DeleteClient ClientIndex
-    | PingAll
-    | StatsAction
-    | RestartServer
-    | AddNick2Bans B.ByteString B.ByteString UTCTime
-    | AddIP2Bans B.ByteString B.ByteString UTCTime
-    | CheckBanned Bool
-    | SaveReplay
-    | Stats
-
 
 type CmdHandler = [B.ByteString] -> Reader (ClientIndex, IRnC) [Action]
 
-instance NFData Action where
-    rnf (AnswerClients chans msg) = chans `deepseq` msg `deepseq` ()
-    rnf a = a `seq` ()
-
---instance NFData B.ByteString
-instance NFData (Chan a)
-
 
 othersChans :: StateT ServerState IO [ClientChan]
 othersChans = do
@@ -214,7 +161,7 @@
     rnc <- gets roomsClients
 
     io $ do
-        modifyClient rnc (\cl -> cl{teamsInGame = 0, isReady = False, isMaster = False, isInGame = False}) ci
+        modifyClient rnc (\cl -> cl{teamsInGame = 0, isReady = False, isMaster = False, isInGame = False, clientClan = Nothing}) ci
         modifyRoom rnc (\r -> r{playersIn = playersIn r + 1}) ri
         moveClientToRoom rnc ri ci
 
@@ -430,7 +377,7 @@
     uid <- client's clUID
     -- allow multiple checker logins
     haveSameNick <- liftM (not . null . tail . filter (\c -> (not $ isChecker c) && caseInsensitiveCompare (nick c) n)) allClientsS
-    if haveSameNick && (not checker) then
+    if (not checker) && haveSameNick then
         if p < 38 then
             processAction $ ByeClient $ loc "Nickname is already in use"
             else
@@ -455,8 +402,12 @@
             when (not b) $ (if c then checkerLogin else playerLogin) passwd isAdmin
         Guest -> do
             b <- isBanned
+            c <- client's isChecker
             when (not b) $
-                processAction JoinLobby
+                if c then
+                    checkerLogin "" False
+                    else
+                    processAction JoinLobby
         Admin -> do
             mapM_ processAction [ModifyClient (\cl -> cl{isAdministrator = True}), JoinLobby]
             chan <- client's sendChan
@@ -588,6 +539,7 @@
     when (not $ ci `Set.member` rc)
         $ processAction $ ModifyServerInfo (\s -> s{bans = BanByIP ip reason expiring : bans s})
 
+
 processAction (CheckBanned byIP) = do
     clTime <- client's connectTime
     clNick <- client's nick
@@ -607,6 +559,7 @@
         getBanReason (BanByIP _ msg _) = msg
         getBanReason (BanByNick _ msg _) = msg
 
+
 processAction PingAll = do
     rnc <- gets roomsClients
     io (allClientsM rnc) >>= mapM_ (kickTimeouted rnc)
@@ -648,12 +601,19 @@
 
 processAction Stats = do
     cls <- allClientsS
-    let stats = versions cls
-    processAction $ Warning stats
-    where
-        versions = B.concat . ((:) "<table border=1>") . (flip (++) ["</table>"])
-            . concatMap (\(p, n :: Int) -> ["<tr><td>", protoNumber2ver p, "</td><td>", showB n, "</td></tr>"])
-            . Map.toList . Map.fromListWith (+) . map (\c -> (clientProto c, 1))
+    rms <- allRoomsS
+    let clientsMap = Map.fromListWith (+) . map (\c -> (clientProto c, 1 :: Int)) $ cls
+    let roomsMap = Map.fromListWith (+) . map (\c -> (roomProto c, 1 :: Int)) . filter ((/=) 0 . roomProto) $ rms
+    let keys = Map.keysSet clientsMap `Set.union` Map.keysSet roomsMap
+    let versionsStats = B.concat . ((:) "<table border=1>") . (flip (++) ["</table>"])
+            . concatMap (\p -> [
+                    "<tr><td>", protoNumber2ver p
+                    , "</td><td>", showB $ Map.findWithDefault 0 p clientsMap
+                    , "</td><td>", showB $ Map.findWithDefault 0 p roomsMap
+                    , "</td></tr>"])
+            . Set.toList $ keys
+    processAction $ Warning versionsStats
+
 
 #if defined(OFFICIAL_SERVER)
 processAction SaveReplay = do
@@ -663,6 +623,29 @@
     io $ do
         r <- room'sM rnc id ri
         saveReplay r
+
+
+processAction CheckRecord = do
+    p <- client's clientProto
+    c <- client's sendChan
+    (cinfo, l) <- io $ loadReplay (fromIntegral p)
+    when (not . null $ l) $
+        mapM_ processAction [
+            AnswerClients [c] ("REPLAY" : l)
+            , ModifyClient $ \c -> c{checkInfo = cinfo}
+            ]
+
+processAction (CheckFailed msg) = do
+    Just (CheckInfo fileName _) <- client's checkInfo
+    io $ moveFailedRecord fileName
+
+processAction (CheckSuccess info) = do
+    Just (CheckInfo fileName _) <- client's checkInfo
+    io $ moveCheckedRecord fileName
+
 #else
 processAction SaveReplay = return ()
+processAction CheckRecord = return ()
+processAction (CheckFailed _) = return ()
+processAction (CheckSuccess _) = return ()
 #endif
--- a/gameServer/CMakeLists.txt	Wed Feb 20 02:21:58 2013 +0100
+++ b/gameServer/CMakeLists.txt	Tue Apr 02 21:00:57 2013 +0200
@@ -1,4 +1,7 @@
 
+include(${CMAKE_MODULE_PATH}/utils.cmake)
+
+find_package_or_disable(GHC NOSERVER)
 
 set(hwserver_sources
     OfficialServer/DBInteraction.hs
@@ -34,7 +37,7 @@
     ${haskell_flags})
 
 add_custom_command(OUTPUT "${EXECUTABLE_OUTPUT_PATH}/hedgewars-server${CMAKE_EXECUTABLE_SUFFIX}"
-        COMMAND "${ghc_executable}"
+        COMMAND "${GHC_EXECUTABLE}"
         ARGS ${ghc_flags}
         MAIN_DEPENDENCY ${hwserv_main}
         DEPENDS ${hwserver_sources}
--- a/gameServer/ClientIO.hs	Wed Feb 20 02:21:58 2013 +0100
+++ b/gameServer/ClientIO.hs	Tue Apr 02 21:00:57 2013 +0200
@@ -37,7 +37,7 @@
             unless (B.null recvBS) $ do
                 let (packets, newrecvBuf) = bs2Packets $ B.append recvBuf recvBS
                 forM_ packets sendPacket
-                receiveWithBufferLoop newrecvBuf
+                receiveWithBufferLoop $ B.copy newrecvBuf
 
         sendPacket packet = writeChan chan $ ClientMessage (ci, packet)
 
--- a/gameServer/CoreTypes.hs	Wed Feb 20 02:21:58 2013 +0100
+++ b/gameServer/CoreTypes.hs	Tue Apr 02 21:00:57 2013 +0200
@@ -1,4 +1,4 @@
-{-# LANGUAGE OverloadedStrings, DeriveDataTypeable #-}
+{-# LANGUAGE CPP, OverloadedStrings, DeriveDataTypeable #-}
 module CoreTypes where
 
 import Control.Concurrent
@@ -12,11 +12,79 @@
 import Control.Exception
 import Data.Typeable
 import Data.TConfig
+import Control.DeepSeq
 -----------------------
 import RoomsAndClients
 
+
+#if __GLASGOW_HASKELL__ < 706
+instance NFData B.ByteString
+#endif
+
+instance NFData (Chan a)
+
+instance NFData Action where
+    rnf (AnswerClients chans msg) = chans `deepseq` msg `deepseq` ()
+    rnf a = a `seq` ()
+
+data Action =
+    AnswerClients ![ClientChan] ![B.ByteString]
+    | SendServerMessage
+    | SendServerVars
+    | MoveToRoom RoomIndex
+    | MoveToLobby B.ByteString
+    | RemoveTeam B.ByteString
+    | SendTeamRemovalMessage B.ByteString
+    | RemoveRoom
+    | FinishGame
+    | UnreadyRoomClients
+    | JoinLobby
+    | ProtocolError B.ByteString
+    | Warning B.ByteString
+    | NoticeMessage Notice
+    | ByeClient B.ByteString
+    | KickClient ClientIndex
+    | KickRoomClient ClientIndex
+    | BanClient NominalDiffTime B.ByteString ClientIndex
+    | BanIP B.ByteString NominalDiffTime B.ByteString
+    | BanNick B.ByteString NominalDiffTime B.ByteString
+    | BanList
+    | Unban B.ByteString
+    | ChangeMaster (Maybe ClientIndex)
+    | RemoveClientTeams
+    | ModifyClient (ClientInfo -> ClientInfo)
+    | ModifyClient2 ClientIndex (ClientInfo -> ClientInfo)
+    | ModifyRoomClients (ClientInfo -> ClientInfo)
+    | ModifyRoom (RoomInfo -> RoomInfo)
+    | ModifyServerInfo (ServerInfo -> ServerInfo)
+    | AddRoom B.ByteString B.ByteString
+    | SendUpdateOnThisRoom
+    | CheckRegistered
+    | ClearAccountsCache
+    | ProcessAccountInfo AccountInfo
+    | AddClient ClientInfo
+    | DeleteClient ClientIndex
+    | PingAll
+    | StatsAction
+    | RestartServer
+    | AddNick2Bans B.ByteString B.ByteString UTCTime
+    | AddIP2Bans B.ByteString B.ByteString UTCTime
+    | CheckBanned Bool
+    | SaveReplay
+    | Stats
+    | CheckRecord
+    | CheckFailed B.ByteString
+    | CheckSuccess [B.ByteString]
+
 type ClientChan = Chan [B.ByteString]
 
+data CheckInfo =
+    CheckInfo
+    {
+        recordFileName :: String,
+        recordTeams :: [TeamInfo]
+    }
+
 data ClientInfo =
     ClientInfo
     {
@@ -38,8 +106,10 @@
         isAdministrator :: Bool,
         isChecker :: Bool,
         isKickedFromServer :: Bool,
-        clientClan :: Maybe B.ByteString,
-        teamsInGame :: Word
+        clientClan :: !(Maybe B.ByteString),
+        checkInfo :: Maybe CheckInfo,
+        teamsInGame :: Word,
+        actionsPending :: [Action]
     }
 
 instance Eq ClientInfo where
@@ -66,6 +136,9 @@
     }
     deriving (Show, Read)
 
+instance Eq TeamInfo where
+    (==) = (==) `on` teamname
+
 data GameInfo =
     GameInfo
     {
--- a/gameServer/EngineInteraction.hs	Wed Feb 20 02:21:58 2013 +0100
+++ b/gameServer/EngineInteraction.hs	Tue Apr 02 21:00:57 2013 +0200
@@ -1,3 +1,5 @@
+{-# LANGUAGE OverloadedStrings #-}
+
 module EngineInteraction where
 
 import qualified Data.Set as Set
@@ -5,8 +7,14 @@
 import qualified Codec.Binary.Base64 as Base64
 import qualified Data.ByteString.Char8 as B
 import qualified Data.ByteString as BW
+import qualified Data.Map as Map
+import qualified Data.List as L
+import Data.Word
+import Data.Bits
+import Control.Arrow
 -------------
 import CoreTypes
+import Utils
 
 
 toEngineMsg :: B.ByteString -> B.ByteString
@@ -20,19 +28,130 @@
         removeLength _ = Nothing
 
 
-checkNetCmd :: B.ByteString -> (Bool, Bool)
+splitMessages :: B.ByteString -> [B.ByteString]
+splitMessages = L.unfoldr (\b -> if B.null b then Nothing else Just $ B.splitAt (1 + fromIntegral (BW.head b)) b)
+
+
+checkNetCmd :: B.ByteString -> (B.ByteString, B.ByteString)
 checkNetCmd msg = check decoded
     where
-        decoded = fromEngineMsg msg
-        check Nothing = (False, False)
-        check (Just ms) | B.length ms > 0 = let m = B.head ms in (m `Set.member` legalMessages, m == '+')
-                        | otherwise        = (False, False)
+        decoded = liftM (splitMessages . BW.pack) $ Base64.decode $ B.unpack msg
+        check Nothing = (B.empty, B.empty)
+        check (Just msgs) = let (a, b) = (filter isLegal msgs, filter isNonEmpty a) in (encode a, encode b)
+        encode = B.pack . Base64.encode . BW.unpack . B.concat
+        isLegal m = (B.length m > 1) && (flip Set.member legalMessages . B.head . B.tail $ m)
+        isNonEmpty = (/=) '+' . B.head . B.tail
         legalMessages = Set.fromList $ "M#+LlRrUuDdZzAaSjJ,sNpPwtghbc12345" ++ slotMessages
         slotMessages = "\128\129\130\131\132\133\134\135\136\137\138"
 
 
-gameInfo2Replay :: GameInfo -> B.ByteString
-gameInfo2Replay GameInfo{roundMsgs = rm,
-        teamsAtStart = teams,
-        giMapParams = params1,
-        giParams = params2} = undefined
+replayToDemo :: [TeamInfo]
+        -> Map.Map B.ByteString B.ByteString
+        -> Map.Map B.ByteString [B.ByteString]
+        -> [B.ByteString]
+        -> [B.ByteString]
+replayToDemo teams mapParams params msgs = concat [
+        [em "TD"]
+        , maybeScript
+        , maybeMap
+        , [eml ["etheme ", head $ params Map.! "THEME"]]
+        , [eml ["eseed ", mapParams Map.! "SEED"]]
+        , [eml ["e$gmflags ", showB gameFlags]]
+        , schemeFlags
+        , [eml ["e$template_filter ", mapParams Map.! "TEMPLATE"]]
+        , [eml ["e$mapgen ", mapgen]]
+        , mapgenSpecific
+        , concatMap teamSetup teams
+        , msgs
+        , [em "!"]
+        ]
+    where
+        em = toEngineMsg
+        eml = em . B.concat
+        mapGenTypes = ["+rnd+", "+maze+", "+drawn+"]
+        maybeScript = let s = head $ params Map.! "SCRIPT" in if s == "Normal" then [] else [eml ["escript Scripts/Multiplayer/", s, ".lua"]]
+        maybeMap = let m = mapParams Map.! "MAP" in if m `elem` mapGenTypes then [] else [eml ["emap ", m]]
+        scheme = tail $ params Map.! "SCHEME"
+        mapgen = mapParams Map.! "MAPGEN"
+        mapgenSpecific = case mapgen of
+            "+maze+" -> [eml ["e$maze_size ", head $ params Map.! "MAZE_SIZE"]]
+            "+drawn" -> drawnMapData . head $ params Map.! "DRAWNMAP"
+            _ -> []
+        gameFlags :: Word32
+        gameFlags = foldl (\r (b, f) -> if b == "false" then r else r .|. f) 0 $ zip scheme gameFlagConsts
+        schemeFlags = map (\(v, (n, m)) -> eml [n, " ", showB $ (readInt_ v) * m])
+            $ filter (\(_, (n, _)) -> not $ B.null n)
+            $ zip (drop (length gameFlagConsts) scheme) schemeParams
+        ammoStr :: B.ByteString
+        ammoStr = head . tail $ params Map.! "AMMO"
+        ammo = let l = B.length ammoStr `div` 4; ((a, b), (c, d)) = (B.splitAt l . fst &&& B.splitAt l . snd) . B.splitAt (l * 2) $ ammoStr in
+                   (map (\(x, y) -> eml [x, " ", y]) $ zip ["eammloadt", "eammprob", "eammdelay", "eammreinf"] [a, b, c, d])
+                   ++ [em "eammstore" | scheme !! 14 == "true" || scheme !! 20 == "false"]
+        initHealth = scheme !! 27
+        teamSetup :: TeamInfo -> [B.ByteString]
+        teamSetup t = (++) ammo $
+                eml ["eaddteam <hash> ", showB $ (1 + (readInt_ $ teamcolor t) :: Int) * 2113696, " ", teamname t]
+                : em "erdriven"
+                : eml ["efort ", teamfort t]
+                : take (2 * hhnum t) (
+                    concatMap (\(HedgehogInfo hname hhat) -> [
+                            eml ["eaddhh ", showB $ difficulty t, " ", initHealth, " ", hname]
+                            , eml ["ehat ", hhat]
+                            ])
+                        $ hedgehogs t
+                        )
+
+drawnMapData :: B.ByteString -> [B.ByteString]
+drawnMapData = error "drawnMapData"
+
+schemeParams :: [(B.ByteString, Int)]
+schemeParams = [
+      ("e$damagepct", 1)
+    , ("e$turntime", 1000)
+    , ("", 0)
+    , ("e$sd_turns", 1)
+    , ("e$casefreq", 1)
+    , ("e$minestime", 1000)
+    , ("e$minesnum", 1)
+    , ("e$minedudpct", 1)
+    , ("e$explosives", 1)
+    , ("e$healthprob", 1)
+    , ("e$hcaseamount", 1)
+    , ("e$waterrise", 1)
+    , ("e$healthdec", 1)
+    , ("e$ropepct", 1)
+    , ("e$getawaytime", 1)
+    ]
+
+
+gameFlagConsts :: [Word32]
+gameFlagConsts = [
+          0x00001000
+        , 0x00000010
+        , 0x00000004
+        , 0x00000008
+        , 0x00000020
+        , 0x00000040
+        , 0x00000080
+        , 0x00000100
+        , 0x00000200
+        , 0x00000400
+        , 0x00000800
+        , 0x00002000
+        , 0x00004000
+        , 0x00008000
+        , 0x00010000
+        , 0x00020000
+        , 0x00040000
+        , 0x00080000
+        , 0x00100000
+        , 0x00200000
+        , 0x00400000
+        , 0x00800000
+        , 0x01000000
+        , 0x02000000
+        , 0x04000000
+        ]
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gameServer/HWProtoChecker.hs	Tue Apr 02 21:00:57 2013 +0200
@@ -0,0 +1,36 @@
+{-# LANGUAGE OverloadedStrings #-}
+module HWProtoChecker where
+
+import qualified Data.Map as Map
+import Data.Maybe
+import Data.List
+import Control.Monad.Reader
+--------------------------------------
+import CoreTypes
+import Actions
+import Utils
+import HandlerUtils
+import RoomsAndClients
+import EngineInteraction
+
+
+handleCmd_checker :: CmdHandler
+
+handleCmd_checker ["READY"] = return [CheckRecord]
+
+handleCmd_checker ["CHECKED", "FAIL", msg] = do
+    isChecking <- liftM (isJust . checkInfo) thisClient
+    if not isChecking then
+        return []
+        else
+        return [CheckFailed msg, ModifyClient $ \c -> c{checkInfo = Nothing}]
+
+
+handleCmd_checker ("CHECKED" : "OK" : info) = do
+    isChecking <- liftM (isJust . checkInfo) thisClient
+    if not isChecking then
+        return []
+        else
+        return [CheckSuccess info, ModifyClient $ \c -> c{checkInfo = Nothing}]
+
+handleCmd_checker _ = return [ProtocolError "Unknown command"]
--- a/gameServer/HWProtoCore.hs	Wed Feb 20 02:21:58 2013 +0100
+++ b/gameServer/HWProtoCore.hs	Tue Apr 02 21:00:57 2013 +0200
@@ -11,6 +11,7 @@
 import HWProtoNEState
 import HWProtoLobbyState
 import HWProtoInRoomState
+import HWProtoChecker
 import HandlerUtils
 import RoomsAndClients
 import Utils
@@ -29,12 +30,12 @@
 handleCmd ["PONG"] = do
     cl <- thisClient
     if pingsQueue cl == 0 then
-        return [ProtocolError "Protocol violation"]
+        return $ actionsPending cl ++ [ModifyClient (\c -> c{actionsPending = []})]
         else
         return [ModifyClient (\c -> c{pingsQueue = pingsQueue c - 1})]
 
-handleCmd ("CMD" : params) =
-    let c = concatMap B.words params in
+handleCmd ("CMD" : parameters) =
+    let c = concatMap B.words parameters in
         if not $ null c then
             h $ (upperCase . head $ c) : tail c
             else
@@ -42,12 +43,22 @@
     where
         h ["DELEGATE", n] = handleCmd ["DELEGATE", n]
         h ["STATS"] = handleCmd ["STATS"]
+        h ["PART", msg] = handleCmd ["PART", msg]
+        h ["QUIT", msg] = handleCmd ["QUIT", msg]
+        h ["GLOBAL", msg] = do
+            rnc <- liftM snd ask
+            let chans = map (sendChan . client rnc) $ allClients rnc
+            return [AnswerClients chans ["CHAT", "[global notice]", msg]]
         h c = return [Warning . B.concat . L.intersperse " " $ "Unknown cmd" : c]
 
 handleCmd cmd = do
     (ci, irnc) <- ask
-    if logonPassed (irnc `client` ci) then
-        handleCmd_loggedin cmd
+    let cl = irnc `client` ci
+    if logonPassed cl then
+        if isChecker cl then
+            handleCmd_checker cmd
+            else
+            handleCmd_loggedin cmd
         else
         handleCmd_NotEntered cmd
 
--- a/gameServer/HWProtoInRoomState.hs	Wed Feb 20 02:21:58 2013 +0100
+++ b/gameServer/HWProtoInRoomState.hs	Tue Apr 02 21:00:57 2013 +0200
@@ -77,9 +77,12 @@
                 SendUpdateOnThisRoom,
                 ModifyClient (\c -> c{teamsInGame = teamsInGame c + 1, clientClan = Just teamColor}),
                 AnswerClients clChan ["TEAM_ACCEPTED", tName],
-                AnswerClients clChan ["HH_NUM", tName, showB $ hhnum newTeam],
                 AnswerClients othChans $ teamToNet $ newTeam,
-                AnswerClients roomChans ["TEAM_COLOR", tName, teamColor]
+                AnswerClients roomChans ["TEAM_COLOR", tName, teamColor],
+                ModifyClient $ \c -> c{actionsPending = actionsPending cl
+                    ++ [AnswerClients clChan ["HH_NUM", tName, showB $ hhnum newTeam]]
+                    },
+                AnswerClients [sendChan cl] ["PING"]
                 ]
         where
         canAddNumber rt = (48::Int) - (sum $ map hhnum rt)
@@ -97,7 +100,6 @@
 handleCmd_inRoom ["REMOVE_TEAM", tName] = do
         (ci, _) <- ask
         r <- thisRoom
-        clNick <- clientNick
 
         let maybeTeam = findTeam r
         let team = fromJust maybeTeam
@@ -105,18 +107,18 @@
         return $
             if isNothing $ maybeTeam then
                 [Warning $ loc "REMOVE_TEAM: no such team"]
-            else if clNick /= teamowner team then
+            else if ci /= teamownerId team then
                 [ProtocolError $ loc "Not team owner!"]
             else
                 [RemoveTeam tName,
                 ModifyClient
                     (\c -> c{
                         teamsInGame = teamsInGame c - 1,
-                        clientClan = if teamsInGame c == 1 then Nothing else Just $ anotherTeamClan ci r
+                        clientClan = if teamsInGame c == 1 then Nothing else Just $ anotherTeamClan ci team r
                     })
                 ]
     where
-        anotherTeamClan ci = teamcolor . fromJust . find (\t -> teamownerId t == ci) . teams
+        anotherTeamClan ci team = teamcolor . fromMaybe (error "CHECKPOINT 011") . find (\t -> (teamownerId t == ci) && (t /= team)) . teams
         findTeam = find (\t -> tName == teamname t) . teams
 
 
@@ -124,7 +126,7 @@
     cl <- thisClient
     r <- thisRoom
     clChan <- thisClientChans
-    roomChans <- roomClientsChans
+    others <- roomOthersChans
 
     let maybeTeam = findTeam r
     let team = fromJust maybeTeam
@@ -138,7 +140,7 @@
             [AnswerClients clChan ["HH_NUM", teamName, showB $ hhnum team]]
         else
             [ModifyRoom $ modifyTeam team{hhnum = hhNumber},
-            AnswerClients roomChans ["HH_NUM", teamName, showB hhNumber]]
+            AnswerClients others ["HH_NUM", teamName, showB hhNumber]]
     where
         hhNumber = readInt_ numberStr
         findTeam = find (\t -> teamName == teamname t) . teams
@@ -182,6 +184,7 @@
                     ["CLIENT_FLAGS", if isReady cl then "-r" else "+r", nick cl]
             ]
 
+
 handleCmd_inRoom ["START_GAME"] = do
     (ci, rnc) <- ask
     cl <- thisClient
@@ -217,16 +220,16 @@
     rm <- thisRoom
     chans <- roomOthersChans
 
-    if teamsInGame cl > 0 && (isJust $ gameInfo rm) && isLegal then
-        return $ AnswerClients chans ["EM", msg]
-            : [ModifyRoom (\r -> r{gameInfo = liftM (\g -> g{roundMsgs = msg : roundMsgs g}) $ gameInfo r}) | not isKeepAlive]
+    if teamsInGame cl > 0 && (isJust $ gameInfo rm) && (not $ B.null legalMsgs) then
+        return $ AnswerClients chans ["EM", legalMsgs]
+            : [ModifyRoom (\r -> r{gameInfo = liftM (\g -> g{roundMsgs = nonEmptyMsgs : roundMsgs g}) $ gameInfo r}) | not $ B.null nonEmptyMsgs]
         else
         return []
     where
-        (isLegal, isKeepAlive) = checkNetCmd msg
+        (legalMsgs, nonEmptyMsgs) = checkNetCmd msg
 
 
-handleCmd_inRoom ["ROUNDFINISHED", correctly] = do
+handleCmd_inRoom ["ROUNDFINISHED", _] = do
     cl <- thisClient
     rm <- thisRoom
     chans <- roomClientsChans
@@ -242,7 +245,7 @@
         else
         return [] -- don't accept this message twice
     where
-        isCorrect = correctly == "1"
+--        isCorrect = correctly == "1"
 
 -- compatibility with clients with protocol < 38
 handleCmd_inRoom ["ROUNDFINISHED"] =
@@ -274,6 +277,7 @@
         else
             [ModifyRoom (\r -> r{isRegisteredOnly = not $ isRegisteredOnly r})]
 
+
 handleCmd_inRoom ["ROOM_NAME", newName] = do
     cl <- thisClient
     rs <- allRoomInfos
@@ -297,10 +301,19 @@
     (thisClientId, rnc) <- ask
     maybeClientId <- clientByNick kickNick
     master <- liftM isMaster thisClient
+    rm <- thisRoom
     let kickId = fromJust maybeClientId
+    let kickCl = rnc `client` kickId
     let sameRoom = clientRoom rnc thisClientId == clientRoom rnc kickId
+    let notOnly2Players = (length . group . sort . map teamowner . teams $ rm) > 2
     return
-        [KickRoomClient kickId | master && isJust maybeClientId && (kickId /= thisClientId) && sameRoom]
+        [KickRoomClient kickId |
+            master
+            && isJust maybeClientId
+            && (kickId /= thisClientId)
+            && sameRoom
+            && ((isNothing $ gameInfo rm) || notOnly2Players || teamsInGame kickCl == 0)
+        ]
 
 
 handleCmd_inRoom ["DELEGATE", newAdmin] = do
@@ -323,7 +336,8 @@
     chans <- roomSameClanChans
     return [AnswerClients chans ["EM", engineMsg cl]]
     where
-        engineMsg cl = toEngineMsg $ B.concat ["b", nick cl, "(team): ", msg, "\x20\x20"]
+        engineMsg cl = toEngineMsg $ B.concat ["b", nick cl, " (team): ", msg, "\x20\x20"]
+
 
 handleCmd_inRoom ["BAN", banNick] = do
     (thisClientId, rnc) <- ask
--- a/gameServer/HWProtoLobbyState.hs	Wed Feb 20 02:21:58 2013 +0100
+++ b/gameServer/HWProtoLobbyState.hs	Tue Apr 02 21:00:57 2013 +0200
@@ -92,9 +92,12 @@
                 , AnswerClients [sendChan cl] $ ["CLIENT_FLAGS", "+h", ownerNick]
             ]
             ++ (if clientProto cl < 38 then map (readynessMessage cl) jRoomClients else [sendStateFlags cl jRoomClients])
-            ++ answerFullConfig cl (mapParams jRoom) (params jRoom)
-            ++ answerTeams cl jRoom
-            ++ watchRound cl jRoom chans
+            ++ [AnswerClients [sendChan cl] ["PING"]
+                , ModifyClient $ \c -> c{actionsPending = actionsPending cl
+                    ++ answerFullConfig cl (mapParams jRoom) (params jRoom)
+                    ++ answerTeams cl jRoom
+                    ++ watchRound cl jRoom chans}
+                ]
 
         where
         readynessMessage cl c = AnswerClients [sendChan cl] [if isReady c then "READY" else "NOT_READY", nick c]
@@ -135,13 +138,14 @@
 
 handleCmd_lobby ["FOLLOW", asknick] = do
     (_, rnc) <- ask
+    clChan <- liftM sendChan thisClient
     ci <- clientByNick asknick
     let ri = clientRoom rnc $ fromJust ci
-    let clRoom = room rnc ri
+    let roomName = name $ room rnc ri
     if isNothing ci || ri == lobbyId then
         return []
         else
-        handleCmd_lobby ["JOIN_ROOM", name clRoom]
+        liftM ((:) (AnswerClients [clChan] ["JOINING", roomName])) $ handleCmd_lobby ["JOIN_ROOM", roomName]
 
     ---------------------------
     -- Administrator's stuff --
--- a/gameServer/NetRoutines.hs	Wed Feb 20 02:21:58 2013 +0100
+++ b/gameServer/NetRoutines.hs	Tue Apr 02 21:00:57 2013 +0200
@@ -45,7 +45,9 @@
                     False
                     False
                     Nothing
+                    Nothing
                     0
+                    []
                     )
 
         writeChan chan $ Accept newClient
--- a/gameServer/OfficialServer/GameReplayStore.hs	Wed Feb 20 02:21:58 2013 +0100
+++ b/gameServer/OfficialServer/GameReplayStore.hs	Tue Apr 02 21:00:57 2013 +0200
@@ -9,18 +9,57 @@
 import Data.Maybe
 import Data.Unique
 import Control.Monad
+import Data.List
+import qualified Data.ByteString as B
+import System.Directory
 ---------------
 import CoreTypes
+import EngineInteraction
 
 
+pickReplayFile :: Int -> IO String
+pickReplayFile p = do
+    files <- liftM (filter (isSuffixOf ('.' : show p))) $ getDirectoryContents "replays"
+    if (not $ null files) then
+        return $ "replays/" ++ head files
+        else
+        return ""
+
 saveReplay :: RoomInfo -> IO ()
 saveReplay r = do
     let gi = fromJust $ gameInfo r
     when (allPlayersHaveRegisteredAccounts gi) $ do
         time <- getCurrentTime
         u <- liftM hashUnique newUnique
-        let fileName = "replays/" ++ show time ++ "-" ++ show u
+        let fileName = "replays/" ++ show time ++ "-" ++ show u ++ "." ++ show (roomProto r)
         let replayInfo = (teamsAtStart gi, Map.toList $ mapParams r, Map.toList $ params r, roundMsgs gi)
         E.catch
             (writeFile fileName (show replayInfo))
             (\(e :: IOException) -> warningM "REPLAYS" $ "Couldn't write to " ++ fileName ++ ": " ++ show e)
+
+
+loadReplay :: Int -> IO (Maybe CheckInfo, [B.ByteString])
+loadReplay p = E.handle (\(e :: SomeException) -> warningM "REPLAYS" "Problems reading replay" >> return (Nothing, [])) $ do
+    fileName <- pickReplayFile p
+    if (not $ null fileName) then
+        loadFile fileName
+        else
+        return (Nothing, [])
+    where
+        loadFile :: String -> IO (Maybe CheckInfo, [B.ByteString])
+        loadFile fileName = E.handle (\(e :: SomeException) ->
+                    warningM "REPLAYS" ("Problems reading " ++ fileName ++ ": " ++ show e) >> return (Nothing, [])) $ do
+            (teams, params1, params2, roundMsgs) <- liftM read $ readFile fileName
+            return $ (
+                Just (CheckInfo fileName teams)
+                , replayToDemo teams (Map.fromList params1) (Map.fromList params2) (reverse roundMsgs)
+                )
+
+moveFailedRecord :: String -> IO ()
+moveFailedRecord fn = E.handle (\(e :: SomeException) -> warningM "REPLAYS" $ show e) $
+    renameFile fn ("failed/" ++ drop 8 fn)
+
+
+moveCheckedRecord :: String -> IO ()
+moveCheckedRecord fn = E.handle (\(e :: SomeException) -> warningM "REPLAYS" $ show e) $
+    renameFile fn ("checked/" ++ drop 8 fn)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gameServer/OfficialServer/checker.hs	Tue Apr 02 21:00:57 2013 +0200
@@ -0,0 +1,183 @@
+{-# LANGUAGE CPP, ScopedTypeVariables, OverloadedStrings #-}
+module Main where
+
+import qualified Control.Exception as Exception
+import System.IO
+import System.Log.Logger
+import qualified Data.ConfigFile as CF
+import Control.Monad.Error
+import System.Directory
+import Control.Monad.State
+import Control.Concurrent.Chan
+import Control.Concurrent
+import Network
+import Network.BSD
+import Network.Socket hiding (recv)
+import Network.Socket.ByteString
+import qualified Data.ByteString.Char8 as B
+import qualified Data.ByteString as BW
+import qualified Codec.Binary.Base64 as Base64
+import System.Process
+import Data.Maybe
+import qualified Data.List as L
+#if !defined(mingw32_HOST_OS)
+import System.Posix
+#endif
+
+data Message = Packet [B.ByteString]
+             | CheckFailed B.ByteString
+             | CheckSuccess [B.ByteString]
+    deriving Show
+
+serverAddress = "netserver.hedgewars.org"
+protocolNumber = "43"
+
+getLines :: Handle -> IO [String]
+getLines h = g
+    where
+        g = do
+            l <- liftM Just (hGetLine h) `Exception.catch` (\(_ :: Exception.IOException) -> return Nothing)
+            if isNothing l then
+                return []
+                else
+                do
+                lst <- g
+                return $ fromJust l : lst
+
+
+engineListener :: Chan Message -> Handle -> IO ()
+engineListener coreChan h = do
+    output <- getLines h
+    debugM "Engine" $ show output
+    if isNothing $ L.find start output then
+        writeChan coreChan $ CheckFailed "No stats msg"
+        else
+        writeChan coreChan $ CheckSuccess []
+    where
+        start = flip L.elem ["WINNERS", "DRAW"]
+
+
+checkReplay :: Chan Message -> [B.ByteString] -> IO ()
+checkReplay coreChan msgs = do
+    tempDir <- getTemporaryDirectory
+    (fileName, h) <- openBinaryTempFile tempDir "checker-demo"
+    B.hPut h . BW.pack . concat . map (fromJust . Base64.decode . B.unpack) $ msgs
+    hFlush h
+    hClose h
+
+    (_, Just hOut, _, _) <- createProcess (proc "/usr/home/unC0Rr/Sources/Hedgewars/Releases/0.9.18/bin/hwengine"
+                ["/usr/home/unC0Rr/.hedgewars"
+                , "/usr/home/unC0Rr/Sources/Hedgewars/Releases/0.9.18/share/hedgewars/Data"
+                , fileName
+                , "--set-audio"
+                , "0"
+                , "0"
+                , "0"
+                ])
+            {std_out = CreatePipe}
+    hSetBuffering hOut LineBuffering
+    void $ forkIO $ engineListener coreChan hOut
+
+
+takePacks :: State B.ByteString [[B.ByteString]]
+takePacks = do
+    modify (until (not . B.isPrefixOf pDelim) (B.drop 2))
+    packet <- state $ B.breakSubstring pDelim
+    buf <- get
+    if B.null buf then put packet >> return [] else
+        if B.null packet then return [] else do
+            packets <- takePacks
+            return (B.splitWith (== '\n') packet : packets)
+    where
+    pDelim = "\n\n"
+
+
+recvLoop :: Socket -> Chan Message -> IO ()
+recvLoop s chan =
+        ((receiveWithBufferLoop B.empty >> return "Connection closed")
+            `Exception.catch` (\(e :: Exception.SomeException) -> return . B.pack . show $ e)
+        )
+        >>= disconnected
+    where
+        disconnected msg = writeChan chan $ Packet ["BYE", msg]
+        receiveWithBufferLoop recvBuf = do
+            recvBS <- recv s 4096
+            unless (B.null recvBS) $ do
+                let (packets, newrecvBuf) = runState takePacks $ B.append recvBuf recvBS
+                forM_ packets sendPacket
+                receiveWithBufferLoop $ B.copy newrecvBuf
+
+        sendPacket packet = writeChan chan $ Packet packet
+
+
+session :: B.ByteString -> B.ByteString -> Socket -> IO ()
+session l p s = do
+    noticeM "Core" "Connected"
+    coreChan <- newChan
+    forkIO $ recvLoop s coreChan
+    forever $ do
+        p <- readChan coreChan
+        case p of
+            Packet p -> do
+                debugM "Network" $ "Recv: " ++ show p
+                onPacket coreChan p
+            CheckFailed msg -> do
+                warningM "Check" "Check failed"
+                answer ["CHECKED", "FAIL", msg]
+                answer ["READY"]
+            CheckSuccess msgs -> do
+                warningM "Check" "Check succeeded"
+                answer ("CHECKED" : "OK" : msgs)
+                answer ["READY"]
+    where
+    answer :: [B.ByteString] -> IO ()
+    answer p = do
+        debugM "Network" $ "Send: " ++ show p
+        sendAll s $ B.unlines p `B.snoc` '\n'
+    onPacket :: Chan Message -> [B.ByteString] -> IO ()
+    onPacket _ ("CONNECTED":_) = do
+        answer ["CHECKER", protocolNumber, l, p]
+        answer ["READY"]
+    onPacket _ ["PING"] = answer ["PONG"]
+    onPacket chan ("REPLAY":msgs) = do
+        checkReplay chan msgs
+        warningM "Check" "Started check"
+    onPacket _ ("BYE" : xs) = error $ show xs
+    onPacket _ _ = return ()
+
+
+main :: IO ()
+main = withSocketsDo $ do
+#if !defined(mingw32_HOST_OS)
+    installHandler sigPIPE Ignore Nothing
+    installHandler sigCHLD Ignore Nothing
+#endif
+
+    updateGlobalLogger "Core" (setLevel DEBUG)
+    updateGlobalLogger "Network" (setLevel DEBUG)
+    updateGlobalLogger "Check" (setLevel DEBUG)
+    updateGlobalLogger "Engine" (setLevel DEBUG)
+
+    Right (login, password) <- runErrorT $ do
+        d <- liftIO $ getHomeDirectory
+        conf <- join . liftIO . CF.readfile CF.emptyCP $ d ++ "/.hedgewars/hedgewars.ini"
+        l <- CF.get conf "net" "nick"
+        p <- CF.get conf "net" "passwordhash"
+        return (B.pack l, B.pack p)
+
+
+    Exception.bracket
+        setupConnection
+        (\s -> noticeM "Core" "Shutting down" >> sClose s)
+        (session login password)
+    where
+        setupConnection = do
+            noticeM "Core" "Connecting to the server..."
+
+            proto <- getProtocolNumber "tcp"
+            let hints = defaultHints { addrFlags = [AI_ADDRCONFIG, AI_CANONNAME] }
+            (addr:_) <- getAddrInfo (Just hints) (Just serverAddress) Nothing
+            let (SockAddrInet _ host) = addrAddress addr
+            sock <- socket AF_INET Stream proto
+            connect sock (SockAddrInet 46631 host)
+            return sock
--- a/gameServer/RoomsAndClients.hs	Wed Feb 20 02:21:58 2013 +0100
+++ b/gameServer/RoomsAndClients.hs	Tue Apr 02 21:00:57 2013 +0200
@@ -23,6 +23,7 @@
     room'sM,
     allClientsM,
     clientsM,
+    roomsM,
     roomClientsM,
     roomClientsIndicesM,
     withRoomsAndClients,
@@ -160,6 +161,9 @@
 clientsM :: MRoomsAndClients r c -> IO [c]
 clientsM (MRoomsAndClients (_, clients)) = indicesM clients >>= mapM (liftM client' . readElem clients)
 
+roomsM :: MRoomsAndClients r c -> IO [r]
+roomsM (MRoomsAndClients (rooms, _)) = indicesM rooms >>= mapM (liftM room' . readElem rooms)
+
 roomClientsIndicesM :: MRoomsAndClients r c -> RoomIndex -> IO [ClientIndex]
 roomClientsIndicesM (MRoomsAndClients (rooms, _)) (RoomIndex ri) = liftM roomClients' (rooms `readElem` ri)
 
--- a/gameServer/ServerState.hs	Wed Feb 20 02:21:58 2013 +0100
+++ b/gameServer/ServerState.hs	Tue Apr 02 21:00:57 2013 +0200
@@ -5,6 +5,7 @@
     ServerState(..),
     client's,
     allClientsS,
+    allRoomsS,
     roomClientsS,
     sameProtoClientsS,
     io
@@ -40,6 +41,9 @@
 allClientsS :: StateT ServerState IO [ClientInfo]
 allClientsS = gets roomsClients >>= liftIO . clientsM
 
+allRoomsS :: StateT ServerState IO [RoomInfo]
+allRoomsS = gets roomsClients >>= liftIO . roomsM
+
 roomClientsS :: RoomIndex -> StateT ServerState IO [ClientInfo]
 roomClientsS ri = do
     rnc <- gets roomsClients
--- a/gameServer/Utils.hs	Wed Feb 20 02:21:58 2013 +0100
+++ b/gameServer/Utils.hs	Tue Apr 02 21:00:57 2013 +0200
@@ -56,7 +56,7 @@
             t : replaceTeam tm ts
 
 illegalName :: B.ByteString -> Bool
-illegalName s = B.null s || B.all isSpace s || isSpace (B.head s) || isSpace (B.last s) || B.any isIllegalChar s
+illegalName s = B.null s || B.length s > 40 || B.all isSpace s || isSpace (B.head s) || isSpace (B.last s) || B.any isIllegalChar s
     where
         isIllegalChar c = c `List.elem` "$()*+?[]^{|}"
 
--- a/hedgewars/CMakeLists.txt	Wed Feb 20 02:21:58 2013 +0100
+++ b/hedgewars/CMakeLists.txt	Tue Apr 02 21:00:57 2013 +0200
@@ -4,17 +4,28 @@
 find_package(SDL_ttf)
 find_package(SDL_mixer)
 
-include(${CMAKE_SOURCE_DIR}/cmake_modules/FindSDL_Extras.cmake)
+include (CheckLibraryExists)
+#Mix_Init/Mix_Quit from SDL_mixer 1.2.10
+check_library_exists(${SDLMIXER_LIBRARY} Mix_Init "" HAVE_MIXINIT)
+if(HAVE_MIXINIT)
+    list(APPEND pascal_flags "-dSDL_MIXER_NEWER")
+endif()
+#IMG_Init/IMG_Quit from SDL_image 1.2.8
+check_library_exists(${SDLIMAGE_LIBRARY} IMG_Init "" HAVE_IMGINIT)
+if(HAVE_IMGINIT)
+    list(APPEND pascal_flags "-dSDL_IMAGE_NEWER")
+endif()
+
 
 configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.inc.in ${CMAKE_CURRENT_BINARY_DIR}/config.inc)
 
 #SOURCE AND PROGRAMS SECTION
 if(${LIBENGINE})
     set(engine_output_name "${CMAKE_SHARED_LIBRARY_PREFIX}hwengine${CMAKE_SHARED_LIBRARY_SUFFIX}")
-    set(hwengine_project ${CMAKE_CURRENT_SOURCE_DIR}/hwLibrary.pas)
+    set(hwengine_project hwLibrary.pas)
 else()
     set(engine_output_name "hwengine${CMAKE_EXECUTABLE_SUFFIX}")
-    set(hwengine_project ${CMAKE_CURRENT_SOURCE_DIR}/hwengine.pas)
+    set(hwengine_project hwengine.pas)
 endif()
 
 if (APPLE)
@@ -92,7 +103,7 @@
     )
 
 if(${LIBENGINE})
-    message(WARNING "Engine will be built as library (experimental)")
+    message(${WARNING} "Engine will be built as library (experimental)")
     list(APPEND pascal_flags "-dHWLIBRARY")
 
     # create position independent code, only required for x68_64 builds, similar to -fPIC
@@ -110,16 +121,18 @@
 endif(${LIBENGINE})
 
 
+include(${CMAKE_MODULE_PATH}/utils.cmake)
 #opengl 2
 IF(${GL2})
     set(pascal_flags "-dGL2" ${pascal_flags})
     message(STATUS "Building using OpenGL 2")
 ENDIF(${GL2})
-# Check Freepascal version
-find_package(Freepascal)
+
+find_package_or_fail(FreePascal)
 
-if (FPC_VERSION VERSION_LESS required_fpc_version)
-    message(FATAL_ERROR "Freepascal is too old, minimum version required is ${required_fpc_version}")
+#when cmake-2.6 support is dropped, this ought to be inside FindFreePascal.cmake
+if (FREEPASCAL_VERSION VERSION_LESS required_fpc_version)
+    message(FATAL_ERROR "Freepascal ${FREEPASCAL_VERSION} is too old, minimum version required is ${required_fpc_version}")
 endif()
 
 
@@ -152,82 +165,71 @@
 
         list(APPEND pascal_flags "-k${SDLMAIN_LIB}")
     endif()
+
+    #when you have multiple ld installation make sure you get the one bundled with the compiler
+    get_filename_component(compiler_dir ${CMAKE_C_COMPILER} PATH)
+    list(APPEND pascal_flags "-FD${compiler_dir}")
 endif(APPLE)
 
-if(NOT NOPNG)
-    find_package(PNG)
-    if(${PNG_FOUND})
-        list(APPEND pascal_flags "-dPNG_SCREENSHOTS")
-        if(APPLE)  # fpc png unit doesn't pull the library (see bug 21833)
-            list(APPEND pascal_flags "-k${PNG_LIBRARY}")
-        endif()
-    else()
-        message(WARNING "Screenshots will be in BMP format because libpng was not found")
-    endif()
-else()
-    message(STATUS "Screenshots will be in BMP format per user request")
+find_package_or_disable_msg(PNG NOPNG "Screenshots will be saved in BMP")
+if(PNG_FOUND)
+    list(REMOVE_AT PNG_LIBRARIES 1) #removing the zlib library path
+    get_filename_component(PNG_LIB_DIR ${PNG_LIBRARIES} PATH)
+    list(APPEND pascal_flags "-dPNG_SCREENSHOTS" "-Fl${PNG_LIB_DIR}")
 endif()
 
 
 #this command is a workaround to some inlining issues present in older FreePascal versions and fixed in 2.6
-if(FPC_VERSION VERSION_LESS "2.6")
+if(FREEPASCAL_VERSION VERSION_LESS "2.6")
     #under some configurations CMAKE_BUILD_TOOL fails to pass on the jobserver, breaking parallel compilation
     if(UNIX)
         set(SAFE_BUILD_TOOL $(MAKE))
     else()
         set(SAFE_BUILD_TOOL ${CMAKE_BUILD_TOOL})
     endif()
-    add_custom_target(ENGINECLEAN COMMAND ${SAFE_BUILD_TOOL} "clean" "${PROJECT_BINARY_DIR}" "${CMAKE_SOURCE_DIR}/hedgewars")
+    add_custom_target(ENGINECLEAN COMMAND ${SAFE_BUILD_TOOL} "clean" "${PROJECT_BINARY_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}")
 endif()
 
 
-if(NOT NOVIDEOREC)
-    set(FFMPEG_FIND_QUIETLY true)
-    find_package(FFMPEG)
-    if(${FFMPEG_FOUND})
-        # TODO: this check is only for SDL < 2
-        # fpc will take care of linking but we need to have this library installed
-        find_package(GLUT REQUIRED)
+if(${FFMPEG_FOUND})
+    # TODO: this check is only for SDL < 2
+    # fpc will take care of linking but we need to have this library installed
+    find_package(GLUT REQUIRED)
 
-        #TODO: convert avwrapper to .pas unit so we can skip this step
-        include_directories(${FFMPEG_INCLUDE_DIR})
+    #TODO: convert avwrapper to .pas unit so we can skip this step
+    include_directories(${FFMPEG_INCLUDE_DIR})
+    list(APPEND pascal_flags "-dUSE_VIDEO_RECORDING")
+    if(WIN32)
+        # there are some problems with linking our avwrapper as static lib, so link it as shared
+        add_library(avwrapper SHARED videorec/avwrapper.c)
+        target_link_libraries(avwrapper ${FFMPEG_LIBRARIES})
+        install(PROGRAMS "${EXECUTABLE_OUTPUT_PATH}/${CMAKE_SHARED_LIBRARY_PREFIX}avwrapper${CMAKE_SHARED_LIBRARY_SUFFIX}" DESTINATION ${target_library_install_dir})
         list(APPEND pascal_flags "-dUSE_VIDEO_RECORDING")
-        IF (WIN32)
-            # there are some problems with linking our avwrapper as static lib, so link it as shared
-            add_library(avwrapper SHARED videorec/avwrapper.c)
-            target_link_libraries(avwrapper ${FFMPEG_LIBRARIES})
-            install(PROGRAMS "${EXECUTABLE_OUTPUT_PATH}/${CMAKE_SHARED_LIBRARY_PREFIX}avwrapper${CMAKE_SHARED_LIBRARY_SUFFIX}" DESTINATION ${target_library_install_dir})
-        ELSE()
-            add_library(avwrapper STATIC videorec/avwrapper.c)
-            list(APPEND pascal_flags "-k${FFMPEG_LIBAVCODEC}" "-k${FFMPEG_LIBAVFORMAT}" "-k${FFMPEG_LIBAVUTIL}")
-        ENDIF()
     else()
-        message(WARNING "Could NOT find FFMPEG/LibAV, video recording will be disabled")
+        add_library(avwrapper STATIC videorec/avwrapper.c)
     endif()
-else()
-    message(STATUS "Video recording disabled by user")
 endif()
 
 
-set(fpc_flags ${NOEXECSTACK_FLAGS} ${pascal_flags} ${hwengine_project})
+set(fpc_flags ${pascal_flags} ${hwengine_project})
 
 if(NOT APPLE)
     #here is the command for standard executables or for shared library
     add_custom_command(OUTPUT "${EXECUTABLE_OUTPUT_PATH}/${engine_output_name}"
-        COMMAND "${FPC_EXECUTABLE}"
+        COMMAND "${FREEPASCAL_EXECUTABLE}"
         ARGS ${fpc_flags} -o${engine_output_name}
-        MAIN_DEPENDENCY ${hwengine_project}
         DEPENDS ${engine_sources}
+        WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
         )
 else()
     #these are the dependencies for building a universal binary on Mac OS X
     foreach (build_arch ${powerpc_build} ${i386_build} ${x86_64_build})
         list(APPEND lipo_args_list "${EXECUTABLE_OUTPUT_PATH}/hwengine.${build_arch}")
         add_custom_command(OUTPUT "${EXECUTABLE_OUTPUT_PATH}/hwengine.${build_arch}"
-            COMMAND "${FPC_EXECUTABLE}"
+            COMMAND "${FREEPASCAL_EXECUTABLE}"
             ARGS ${fpc_flags} -ohwengine.${build_arch} -P${build_arch}
-            MAIN_DEPENDENCY ${hwengine_project}
             DEPENDS ${engine_sources}
+            WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
             )
         add_custom_target(hwengine.${build_arch} ALL DEPENDS "${EXECUTABLE_OUTPUT_PATH}/hwengine.${build_arch}")
         add_custom_command(TARGET hwengine.${build_arch} POST_BUILD
@@ -252,8 +254,12 @@
     add_dependencies(hwengine lua)
 endif()
 
-# compile physfs before engine
-add_dependencies(hwengine physfs)
+# same for physfs
+if(NOT PHYSFS_FOUND)
+    add_dependencies(hwengine physfs)
+endif()
+
+add_dependencies(hwengine physlayer)
 
 #when ffmpeg/libav is found we need to compile it before engine
 #TODO: convert avwrapper to .pas unit so we can skip this step
@@ -262,7 +268,7 @@
 endif()
 
 #this command is a workaround to some inlining issues present in older FreePascal versions and fixed in 2.6
-if((FPC_VERSION VERSION_LESS "2.6") AND (NOVIDEOREC OR NOT ${FFMPEG_FOUND}))
+if((FREEPASCAL_VERSION VERSION_LESS "2.6") AND (NOT ${FFMPEG_FOUND}))
     add_dependencies(hwengine ENGINECLEAN)
 endif()
 
--- a/hedgewars/GSHandlers.inc	Wed Feb 20 02:21:58 2013 +0100
+++ b/hedgewars/GSHandlers.inc	Tue Apr 02 21:00:57 2013 +0200
@@ -92,7 +92,7 @@
 
                 else
                     begin
-                    if (gi^.State and gstMoving) = 0 then
+                    if ((gi^.State and gstMoving) = 0) and (gi^.Hedgehog^.Effects[heFrozen] = 0) then
                         begin
                         gi^.dX.isNegative:= X<gi^.X;
                         gi^.State := gi^.State or gstLoser;
@@ -616,6 +616,7 @@
         else if ((yy and LAND_HEIGHT_MASK) = 0) and ((xx and LAND_WIDTH_MASK) = 0) and (Land[yy, xx] <> 0) then
             begin
             lf:= Land[yy, xx] and (lfObject or lfBasic or lfIndestructible);
+            if lf = 0 then lf:= lfObject;
             // If there's room below keep falling
             if (((yy-1) and LAND_HEIGHT_MASK) = 0) and (Land[yy-1, xx] = 0) then
                 begin
@@ -679,7 +680,7 @@
                             begin
                             rx:= rx div 2;ry:= ry div 2;
                             end;
-                        if Land[yy + py, xx + px] and $FF00 = 0 then
+                        if Land[yy + py, xx + px] <= lfAllObjMask then
                             if gun then
                                 begin
                                 LandDirty[yy div 32, xx div 32]:= 1;
@@ -1020,7 +1021,7 @@
         if ((y and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0) and (Land[y, x] <> 0) then
             inc(Gear^.Damage);
         // let's interrupt before a collision to give portals a chance to catch the bullet
-        if (Gear^.Damage = 1) and (Gear^.Tag = 0) and (Land[y, x] > 255) then
+        if (Gear^.Damage = 1) and (Gear^.Tag = 0) and not(CheckLandValue(x, y, lfLandMask)) then
             begin
             Gear^.Tag := 1;
             Gear^.Damage := 0;
@@ -1153,15 +1154,15 @@
 dec(Gear^.Timer);
 case Gear^.Kind of
     gtATStartGame:
-    begin
+        begin
         AllInactive := false;
         if Gear^.Timer = 0 then
             begin
             AddCaption(trmsg[sidStartFight], cWhiteColor, capgrpGameState);
             end
-    end;
+        end;
     gtATFinishGame:
-    begin
+        begin
         AllInactive := false;
         if Gear^.Timer = 1000 then
             begin
@@ -1175,8 +1176,8 @@
             SendIPC(_S'q');
             GameState := gsExit
             end
+        end;
     end;
-end;
 if Gear^.Timer = 0 then
     DeleteGear(Gear)
 end;
@@ -1242,7 +1243,7 @@
         end
     else
         begin
-        if CheckLandValue(hwRound(Gear^.X), hwRound(Gear^.Y + Gear^.dY + cGravity), $FF00) then
+        if CheckLandValue(hwRound(Gear^.X), hwRound(Gear^.Y + Gear^.dY + cGravity), lfLandMask) then
             begin
             Gear^.dY := Gear^.dY + cGravity;
             Gear^.Y := Gear^.Y + Gear^.dY
@@ -1252,7 +1253,7 @@
         end;
 
     Gear^.X := Gear^.X + HHGear^.dX;
-    if CheckLandValue(hwRound(Gear^.X), hwRound(Gear^.Y)-cHHRadius, $FF00) then
+    if CheckLandValue(hwRound(Gear^.X), hwRound(Gear^.Y)-cHHRadius, lfLandMask) then
         begin
         HHGear^.X := Gear^.X;
         HHGear^.Y := Gear^.Y - int2hwFloat(cHHRadius)
@@ -1401,6 +1402,14 @@
     BTPrevAngle := High(LongInt);
     BTSteps := 0;
     HHGear := Gear^.Hedgehog^.Gear;
+    HedgehogChAngle(HHGear);
+    Gear^.dX := SignAs(AngleSin(HHGear^.Angle) * _0_5, Gear^.dX);
+    Gear^.dY := AngleCos(HHGear^.Angle) * ( - _0_5);
+    DrawTunnel(HHGear^.X,
+        HHGear^.Y + Gear^.dY * cHHRadius - _1 -
+        ((hwAbs(Gear^.dX) / (hwAbs(Gear^.dX) + hwAbs(Gear^.dY))) * _0_5 * 7),
+        Gear^.dX, Gear^.dY,
+        cHHStepTicks, cHHRadius * 2 + 7);
     HHGear^.Message := 0;
     HHGear^.State := HHGear^.State or gstNotKickable;
     Gear^.doStep := @doStepBlowTorchWork
@@ -1784,7 +1793,7 @@
         Gear^.Y := Gear^.Y + Gear^.dY;
 
         if (not Gear^.dY.isNegative) and (Gear^.dY > _0_001) then
-            SetAllHHToActive;
+            SetAllHHToActive(false);
 
         if (not Gear^.dY.isNegative) and (TestCollisionYwithGear(Gear, 1) <> 0) then
             begin
@@ -2385,7 +2394,9 @@
 
         repeat
             CurrentTeam^.CurrHedgehog := Succ(CurrentTeam^.CurrHedgehog) mod (CurrentTeam^.HedgehogsNumber);
-        until (CurrentTeam^.Hedgehogs[CurrentTeam^.CurrHedgehog].Gear <> nil) and (CurrentTeam^.Hedgehogs[CurrentTeam^.CurrHedgehog].Gear^.Damage = 0);
+        until (CurrentTeam^.Hedgehogs[CurrentTeam^.CurrHedgehog].Gear <> nil) and
+              (CurrentTeam^.Hedgehogs[CurrentTeam^.CurrHedgehog].Gear^.Damage = 0) and
+              (CurrentTeam^.Hedgehogs[CurrentTeam^.CurrHedgehog].Effects[heFrozen]=0);
 
         SwitchCurrentHedgehog(@CurrentTeam^.Hedgehogs[CurrentTeam^.CurrHedgehog]);
         AmmoMenuInvalidated:= true;
@@ -2418,7 +2429,7 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 procedure doStepMortar(Gear: PGear);
-var 
+var
     dX, dY, gdX, gdY: hwFloat;
     i: LongInt;
     dxn, dyn: boolean;
@@ -2745,7 +2756,7 @@
 
     HHGear := Gear^.Hedgehog^.Gear;
     HHGear^.Message := HHGear^.Message and (not gmAttack);
-    Gear^.CollisionMask:= $FF7F;
+    Gear^.CollisionMask:= lfNotCurrentMask;
 
     FollowGear := Gear;
 
@@ -2879,30 +2890,32 @@
 procedure doStepDrillDrilling(Gear: PGear);
 var
     t: PGearArray;
-    ox, oy: hwFloat;
+    tempColl: Word;
 begin
     AllInactive := false;
-
-    if (Gear^.Timer > 0) and ((Gear^.Timer mod 10) = 0) then
-    begin
-        ox := Gear^.X;
-        oy := Gear^.Y;
-        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
-            StopSoundChan(Gear^.SoundChannel);
-            exit
-        end
+    if (Gear^.Timer > 0) and (Gear^.Timer mod 10 <> 0) then
+        begin
+        dec(Gear^.Timer);
+        exit;
+        end;
+
+    DrawTunnel(Gear^.X, Gear^.Y, Gear^.dX, Gear^.dY, 2, 6);
+    Gear^.X := Gear^.X + Gear^.dX;
+    Gear^.Y := Gear^.Y + Gear^.dY;
+    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
+        StopSoundChan(Gear^.SoundChannel);
+        exit
     end;
 
-    if GameTicks > Gear^.FlightTime then
+    tempColl:= Gear^.CollisionMask;
+    Gear^.CollisionMask:= $007F;
+    if (TestCollisionYWithGear(Gear, hwSign(Gear^.dY)) <> 0) or TestCollisionXWithGear(Gear, hwSign(Gear^.dX)) or (GameTicks > Gear^.FlightTime) then
         t := CheckGearsCollision(Gear)
-
     else t := nil;
+    Gear^.CollisionMask:= tempColl;
     //fixes drill not exploding when touching HH bug
 
     if (Gear^.Timer = 0) or ((t <> nil) and (t^.Count <> 0))
@@ -3016,7 +3029,7 @@
         ry := rndSign(getRandomf * _0_1);
 
         ball:= AddGear(gx, gy, gtBall, 0, SignAs(AngleSin(HHGear^.Angle) * _0_8, HHGear^.dX) + rx, AngleCos(HHGear^.Angle) * ( - _0_8) + ry, 0);
-        ball^.CollisionMask:= $FF7F;
+        ball^.CollisionMask:= lfNotCurrentMask;
 
         PlaySound(sndGun);
         end;
@@ -3588,7 +3601,7 @@
     doPortalColorSwitch();
 
     // destroy portal if ground it was attached too is gone
-    if ((Land[hwRound(Gear^.Y), hwRound(Gear^.X)] and $FF00) = 0)
+    if (Land[hwRound(Gear^.Y), hwRound(Gear^.X)] <= lfAllObjMask)
     or (Gear^.Timer < 1)
     or (Gear^.Hedgehog^.Team <> CurrentHedgehog^.Team)
     or (hwRound(Gear^.Y) > cWaterLine) then
@@ -3628,7 +3641,7 @@
             break;
 
         // don't port portals or other gear that wouldn't make sense
-        if (iterator^.Kind in [gtPortal, gtRope, gtAirAttack])
+        if (iterator^.Kind in [gtPortal, gtRope, gtAirAttack, gtIceGun])
         or (iterator^.PortalCounter > 32) then
             continue;
 
@@ -4371,14 +4384,14 @@
             flame:= AddGear(gx, gy, gtFlame, gstTmpFlag,
                     SignAs(AngleSin(HHGear^.Angle) * speed, HHGear^.dX) + rx,
                     AngleCos(HHGear^.Angle) * ( - speed) + ry, 0);
-            flame^.CollisionMask:= $FF7F;
+            flame^.CollisionMask:= lfNotCurrentMask;
 
             if (Gear^.Health mod 30) = 0 then
                 begin
                 flame:= AddGear(gx, gy, gtFlame, 0,
                         SignAs(AngleSin(HHGear^.Angle) * speed, HHGear^.dX) + rx,
                         AngleCos(HHGear^.Angle) * ( - speed) + ry, 0);
-                flame^.CollisionMask:= $FF7F;
+                flame^.CollisionMask:= lfNotCurrentMask;
                 end
             end;
         Gear^.Timer:= Gear^.Tag
@@ -4455,7 +4468,7 @@
         land:= AddGear(gx, gy, gtFlake, gstTmpFlag,
                 SignAs(AngleSin(HHGear^.Angle) * speed, HHGear^.dX) + rx,
                 AngleCos(HHGear^.Angle) * ( - speed) + ry, 0);
-        land^.CollisionMask:= $FF7F;
+        land^.CollisionMask:= lfNotCurrentMask;
 
         Gear^.Timer:= Gear^.Tag
         end;
@@ -4579,8 +4592,8 @@
         if CheckLandValue(hwRound(Gear^.X + Gear^.dX + SignAs(_6,Gear^.dX)), hwRound(Gear^.Y + _1_9)
            , lfIndestructible) then
             begin
-            Gear^.X := Gear^.X + Gear^.dX;
-            Gear^.Y := Gear^.Y + _1_9;
+            //Gear^.X := Gear^.X + Gear^.dX;
+            Gear^.Y := Gear^.Y + _1_9
             end;
         end;
     if TestCollisionYwithGear(Gear, 1) <> 0 then
@@ -4591,14 +4604,15 @@
         end
     else
         begin
-        Gear^.dY := Gear^.dY + cGravity;
-        Gear^.Y := Gear^.Y + Gear^.dY;
+        //Gear^.dY := Gear^.dY + cGravity;
+        //Gear^.Y := Gear^.Y + Gear^.dY;
         if hwRound(Gear^.Y) > cWaterLine then
             Gear^.Timer := 1
         end;
 
-    Gear^.X := Gear^.X + HitGear^.dX;
+    //Gear^.X := Gear^.X + HitGear^.dX;
     HitGear^.X := Gear^.X;
+    HitGear^.Y := Gear^.Y;
     SetLittle(HitGear^.dY);
     HitGear^.Active:= true;
 end;
@@ -4727,6 +4741,7 @@
 procedure doStepResurrector(Gear: PGear);
 var
     graves: PGearArrayS;
+    hh: PHedgehog;
     i: LongInt;
     len: Integer;
 begin
@@ -4735,12 +4750,24 @@
 
     if graves.size > 0 then
         begin
+        hh := Gear^.Hedgehog;
         for i:= 0 to graves.size - 1 do
             begin
             PHedgehog(graves.ar^[i]^.Hedgehog)^.Gear := nil;
             graves.ar^[i]^.Health := 0;
             end;
         Gear^.doStep := @doStepResurrectorWork;
+        if ((Gear^.Message and gmAttack) <> 0) and (hh^.Gear^.Health > 0) and (TurnTimeLeft > 0) then
+            begin
+            if LongInt(graves.size) <= Gear^.Tag then Gear^.Tag:= 0;
+            dec(hh^.Gear^.Health);
+            if (hh^.Gear^.Health = 0) and (hh^.Gear^.Damage = 0) then
+                hh^.Gear^.Damage:= 1;
+            RenderHealth(hh^);
+            RecountTeamHealth(hh^.Team);
+            inc(graves.ar^[Gear^.Tag]^.Health);
+            inc(Gear^.Tag)
+            end
         end
     else
         begin
@@ -4953,7 +4980,7 @@
         end;
     Gear^.Pos:= 4;
     // This condition might need tweaking
-    Gear^.Timer:= GetRandom(cHedgehogTurnTime*TeamsCount*2)+cHedgehogTurnTime*2
+    Gear^.Timer:= GetRandom(cHedgehogTurnTime*TeamsCount)+cHedgehogTurnTime
     end;
 
 if (Gear^.Pos = 4) then
@@ -4989,7 +5016,7 @@
             Gear^.Power:= 0;
             end
         end
-    else dec(Gear^.Timer);
+    else if (CurrentHedgehog^.Team^.Clan = Gear^.Hedgehog^.Team^.Clan) then dec(Gear^.Timer)
     end;
 
 end;
@@ -5047,39 +5074,83 @@
 For now we assume a "ray" like a deagle projected out from the gun.
 All these effects assume the ray's angle is not changed and that the target type was unchanged over a number of ticks.  This is a simplifying assumption for "gun was applying freezing effect to the same target".
   * When fired at water a layer of ice textured land is added above the water.
-  * When fired at non-ice land (land and $FF00 and not lfIce) the land is overlaid with a thin layer of ice textured land around that point (say, 1 or 2px into land, 1px above). For attractiveness, a slope would probably be needed.
+  * When fired at non-ice land (land and lfLandMask and not lfIce) the land is overlaid with a thin layer of ice textured land around that point (say, 1 or 2px into land, 1px above). For attractiveness, a slope would probably be needed.
   * When fired at a hog (land and $00FF <> 0), while the hog is targetted, the hog's state is set to frozen.  As long as the gun is on the hog, a frozen hog sprite creeps up from the feet to the head.  If the effect is interrupted before reaching the top, the freezing state is cleared.
 A frozen hog will animate differently.  To be decided, but possibly in a similar fashion to a grave when it comes to explosions.  The hog might (possibly) not be damaged by explosions.  This might make freezing potentially useful for friendlies in a bad position.  It might be better to allow damage though.
 A frozen hog stays frozen for a certain number of turns. Each turn the frozen overlay becomes fainter, until it fades and the hog animates normally again.
 *)
 
+
+procedure updateFuel(Gear: PGear);
+var
+  t:LongInt;
+begin
+    t:= Gear^.Health div 10;
+    if (t <> Gear^.Damage) and ((GameTicks and $3F) = 0) then
+    begin
+    Gear^.Damage:= t;
+    FreeTexture(Gear^.Tex);
+    Gear^.Tex := RenderStringTex(trmsg[sidFuel] + ': ' + inttostr(t) +
+              '%', cWhiteColor, fntSmall)
+    end;
+    if GameTicks mod 10 = 0 then dec(Gear^.Health);
+end;
+
+
+procedure updateTarget(Gear:PGear; newX, newY:HWFloat);
+//    var
+//    iter:PGear;
+begin
+  with Gear^ do
+  begin
+    dX:= newX;
+    dY:= newY;
+    Pos:= 0;
+    Target.X:= NoPointX;
+    LastDamage:= nil;
+    X:= Hedgehog^.Gear^.X;
+    Y:= Hedgehog^.Gear^.Y;
+    //unfreeze all semifrozen hogs - make this generic hog cleanup
+(*
+    iter := GearsList;
+    while iter <> nil do
+        begin
+        if (iter^.Kind = gtHedgehog) and
+        (iter^.Hedgehog^.Effects[heFrozen] and $FF = 0) then
+        iter^.Hedgehog^.Effects[heFrozen]:= 0;
+        iter:= iter^.NextGear
+        end
+*)
+  end;
+end;
+
 procedure doStepIceGun(Gear: PGear);
+const iceWaitCollision:Longint = 0;
+const iceCollideWithGround:Longint = 1;
+//const iceWaitNextTarget:Longint = 2;
+//const iceCollideWithHog:Longint = 4;
+const iceCollideWithWater:Longint = 5;
+//const waterFreezingTime:Longint = 500;
+const groundFreezingTime:Longint = 1000;
+const iceRadius = 32;
+const iceHeight = 40;
 var
     HHGear: PGear;
+    landRect: TSDL_Rect;
     ndX, ndY: hwFloat;
     i, t, gX, gY: LongInt;
     hogs: PGearArrayS;
     len: Integer;
 begin
     HHGear := Gear^.Hedgehog^.Gear;
-    if (Gear^.Health = 0) or (HHGear = nil) or (HHGear^.Damage <> 0) then
+    if (Gear^.Message and gmAttack <> 0) or (Gear^.Health = 0) or (HHGear = nil) or (HHGear^.Damage <> 0) then
         begin
         DeleteGear(Gear);
         AfterAttack;
         exit
         end
-    else
-        begin
-        t:= Gear^.Health div 10;
-        if (t <> Gear^.Damage) and ((GameTicks and $3F) = 0) then
-            begin
-            Gear^.Damage:= t;
-            FreeTexture(Gear^.Tex);
-            Gear^.Tex := RenderStringTex(trmsg[sidFuel] + ': ' + inttostr(t) +
-                         '%', cWhiteColor, fntSmall)
-            end
-        end;
-    if GameTicks mod 10 = 0 then dec(Gear^.Health);
+    else if Gear^.Message and (gmUp or gmDown) = 0 then updateFuel(Gear);
+
     with Gear^ do
         begin
         HedgehogChAngle(HHGear);
@@ -5089,22 +5160,8 @@
            ((Target.X <> NoPointX) and (Target.X and LAND_WIDTH_MASK = 0) and
              (Target.Y and LAND_HEIGHT_MASK = 0) and ((Land[Target.Y, Target.X] = 0))) then
             begin
-            dX:= ndX;
-            dY:= ndY;
-            Pos:= 0;
-            Target.X:= NoPointX;
-            LastDamage:= nil;
-            X:= HHGear^.X;
-            Y:= HHGear^.Y;
-(* unfreeze all semifrozen hogs - make this generic hog cleanup
-            iter := GearsList;
-            while iter <> nil do
-                begin
-                if (iter^.Kind = gtHedgehog) and
-                   (iter^.Hedgehog^.Effects[heFrozen] < 0) then
-                    iter^.Hedgehog^.Effects[heFrozen]:= 0;
-                iter:= iter^.NextGear
-                end *)
+            updateTarget(Gear, ndX, ndY);
+            Timer := iceWaitCollision;
             end
         else
             begin
@@ -5113,22 +5170,67 @@
             gX:= hwRound(X);
             gY:= hwRound(Y);
             if Target.X = NoPointX then t:= hwRound(hwSqr(X-HHGear^.X)+hwSqr(Y-HHGear^.Y));
+
             if Target.X <> NoPointX then
                 begin
+                CheckCollisionWithLand(Gear);
+                if (State and gstCollision) <> 0 then
+                    begin
+                    if Timer = iceWaitCollision then
+                        begin
+                        Timer := iceCollideWithGround;
+                        Power := GameTicks;
+                        end
+                    end
+                else if (target.y >= cWaterLine) then
+                    begin
+                    if Timer = iceWaitCollision then
+                        begin
+                        Timer := iceCollideWithWater;
+                        Power := GameTicks;
+                        end;
+                    end;
+
                 if (abs(gX-Target.X) < 2) and (abs(gY-Target.Y) < 2) then
                     begin
                     X:= HHGear^.X;
                     Y:= HHGear^.Y
                     end;
+
+                if (Timer = iceCollideWithGround) and ((GameTicks - Power) > groundFreezingTime) then
+                    begin
+                    FillRoundInLand(target.x, target.y, iceRadius, icePixel);
+                    landRect.x := min(max(target.x - iceRadius, 0), LAND_WIDTH - 1);
+                    landRect.y := min(max(target.y - iceRadius, 0), LAND_HEIGHT - 1);
+                    landRect.w := min(2*iceRadius, LAND_WIDTH - landRect.x - 1);
+                    landRect.h := min(2*iceRadius, LAND_HEIGHT - landRect.y - 1);
+                    UpdateLandTexture(landRect.x, landRect.w, landRect.y, landRect.h, true);
+
+                    // FillRoundInLandWithIce(Target.X, Target.Y, iceRadius);
+                    SetAllHHToActive;
+                    Timer := iceWaitCollision;
+                    end;
+
+                if (Timer = iceCollideWithWater) and ((GameTicks - Power) > groundFreezingTime) then
+                    begin
+                    DrawIceBreak(Target.X, cWaterLine - iceHeight, iceRadius, iceHeight);
+                    SetAllHHToActive;
+                    Timer := iceWaitCollision;
+                    end;
+
 // freeze nearby hogs
-                if GameTicks mod 10 = 0 then dec(Gear^.Health);
-                hogs := GearsNear(Gear^.X, Gear^.Y, gtHedgehog, Gear^.Radius);
+                hogs := GearsNear(int2hwFloat(Target.X), int2hwFloat(Target.Y), gtHedgehog, Gear^.Radius*2);
                 if hogs.size > 0 then
                     for i:= 0 to hogs.size - 1 do
                         if hogs.ar^[i] <> HHGear then
-                            begin
-                            //if Gear^.Hedgehog^.Effects[heFrozen]:= 0;
-                            end;
+                            if GameTicks mod 5 = 0 then
+                                begin
+                                hogs.ar^[i]^.Active:= true;
+                                if hogs.ar^[i]^.Hedgehog^.Effects[heFrozen] < 256 then
+                                    hogs.ar^[i]^.Hedgehog^.Effects[heFrozen] := hogs.ar^[i]^.Hedgehog^.Effects[heFrozen] + 1
+                                else if hogs.ar^[i]^.Hedgehog^.Effects[heFrozen] = 256 then
+                                    hogs.ar^[i]^.Hedgehog^.Effects[heFrozen]:= 200000;//cHedgehogTurnTime + cReadyDelay
+                                end;
                 inc(Pos)
                 end
             else if (t > 400) and ((gY > cWaterLine) or
@@ -5140,14 +5242,14 @@
                 X:= HHGear^.X;
                 Y:= HHGear^.Y
                 end;
-            if (gX > max(LAND_WIDTH,4096)*2) or
+            {if (gX > max(LAND_WIDTH,4096)*2) or
                     (gX < -max(LAND_WIDTH,4096)) or
                     (gY < -max(LAND_HEIGHT,4096)) or
                     (gY > max(LAND_HEIGHT,4096)+512) then
-                begin
+            begin
                 X:= HHGear^.X;
                 Y:= HHGear^.Y
-                end
+            end}
         end
     end;
 end;
@@ -5288,7 +5390,7 @@
 var   a: real;
 begin
     // Gear is shrunk so it can actually escape the hog without carving into the terrain
-    if (Gear^.Radius = 6) and (Gear^.CollisionMask = $FFFF) then Gear^.Radius:= 16;
+    if (Gear^.Radius = 4) and (Gear^.CollisionMask = $FFFF) then Gear^.Radius:= 7;
     if Gear^.Damage > 100 then Gear^.CollisionMask:= 0
     else if Gear^.Damage > 30 then
         if GetRandom(max(4,18-Gear^.Damage div 10)) < 3 then Gear^.CollisionMask:= 0;
@@ -5297,6 +5399,7 @@
     if (Gear^.State and gstMoving <> 0) and (Gear^.State and gstCollision = 0) then
         begin
         DeleteCI(Gear);
+        Gear^.Radius:= 7;
         // used for damage and impact calc. needs balancing I think
         Gear^.Health:= hwRound(hwSqr((hwAbs(Gear^.dY)+hwAbs(Gear^.dX))*_4));
         doStepFallingGear(Gear);
@@ -5332,9 +5435,8 @@
         Gear^.dX:= _0;
         Gear^.dY:= _0;
         Gear^.State:= Gear^.State and (not gstMoving) or gstCollision;
-        Gear^.Radius:= 20;
+        Gear^.Radius:= 16;
         if Gear^.Health > 0 then AmmoShove(Gear, Gear^.Health, 0);
-        Gear^.Radius:= 16;
         Gear^.Health:= 0;
         Gear^.Timer:= 500;
         AddGearCI(Gear)
@@ -5373,7 +5475,7 @@
     if Gear^.State and gstDrowning <> 0 then exit;
     with Gear^ do
         begin
-        if CheckLandValue(gx, gy, $FF00) then
+        if CheckLandValue(gx, gy, lfLandMask) then
             begin
             t:= Angle + hwRound((hwAbs(dX)+hwAbs(dY)) * _10);
 
--- a/hedgewars/PNGh.pas	Wed Feb 20 02:21:58 2013 +0100
+++ b/hedgewars/PNGh.pas	Tue Apr 02 21:00:57 2013 +0200
@@ -23,15 +23,14 @@
 
 uses png;
 
-{$IFDEF FPC}
-    {$PACKRECORDS C}
-{$ELSE}
-    {$DEFINE cdecl attribute(cdecl)}
+
+{$IFDEF DARWIN}
+    {$linklib png}
 {$ENDIF}
 
 const
     // Constants for libpng, they are not defined in png unit.
-    // We actually don't need all of them.
+    // We actually do not need all of them.
 
     // These describe the color_type field in png_info.
     // color type masks
--- a/hedgewars/SDLh.pas	Wed Feb 20 02:21:58 2013 +0100
+++ b/hedgewars/SDLh.pas	Tue Apr 02 21:00:57 2013 +0200
@@ -36,7 +36,10 @@
 
 {$IFDEF UNIX}
     {$IFNDEF DARWIN}
-        {$linklib c}
+        {necessary for statically linking physfs (divdi3 undefined on 32 bit)}
+        {$IFDEF CPU32}
+            {$linklib stdc++}
+        {$ENDIF}
     {$ENDIF}
     {$IFDEF HAIKU}
         {$linklib root}
@@ -315,6 +318,22 @@
     IMG_INIT_PNG = $00000002;
     IMG_INIT_TIF = $00000004;
 
+    {* SDL_keysym *}
+    SDLK_BACKSPACE = 8;
+    SDLK_RETURN    = 13;
+    SDLK_ESCAPE    = 27;
+    SDLK_q         = 113;
+    SDLK_w         = 119;
+    SDLK_DELETE    = 127;
+    SDLK_UP        = 273;
+    SDLK_DOWN      = 274;
+    SDLK_RIGHT     = 275;
+    SDLK_LEFT      = 276;
+    SDLK_HOME      = 278;
+    SDLK_END       = 279;
+    SDLK_PAGEUP    = 280;
+    SDLK_PAGEDOWN  = 281;
+
 
 /////////////////////////////////////////////////////////////////
 ///////////////////////  TYPE DEFINITIONS ///////////////////////
--- a/hedgewars/config.inc.in	Wed Feb 20 02:21:58 2013 +0100
+++ b/hedgewars/config.inc.in	Tue Apr 02 21:00:57 2013 +0200
@@ -23,5 +23,7 @@
  *)
 const cNetProtoVersion = ${HEDGEWARS_PROTO_VER};
       cVersionString = '${HEDGEWARS_VERSION}';
+      cRevisionString = '${HEDGEWARS_REVISION}';
+      cHashString = '${HEDGEWARS_HASH}';
       cLuaLibrary = '${LUA_LIBRARY}';
-      cDefaultPathPrefix = '${CMAKE_INSTALL_PREFIX}/${SHAREPATH}/Data';
+      cDefaultPathPrefix = '${HEDGEWARS_FULL_DATADIR}/Data';
--- a/hedgewars/hwengine.pas	Wed Feb 20 02:21:58 2013 +0100
+++ b/hedgewars/hwengine.pas	Tue Apr 02 21:00:57 2013 +0200
@@ -196,8 +196,10 @@
 {$IFDEF SDL13}
                 SDL_KEYDOWN:
                     if GameState = gsChat then
+                        begin
                     // sdl on iphone supports only ashii keyboards and the unicode field is deprecated in sdl 1.3
-                        KeyPressChat(SDL_GetKeyFromScancode(event.key.keysym.sym))//TODO correct for keymodifiers
+                        KeyPressChat(SDL_GetKeyFromScancode(event.key.keysym.sym, event.key.keysym.sym)//TODO correct for keymodifiers
+                        end
                     else
                         ProcessKey(event.key);
                 SDL_KEYUP:
@@ -241,7 +243,7 @@
 {$ELSE}
                 SDL_KEYDOWN:
                     if GameState = gsChat then
-                        KeyPressChat(event.key.keysym.unicode)
+                        KeyPressChat(event.key.keysym.unicode, event.key.keysym.sym)
                     else
                         ProcessKey(event.key);
                 SDL_KEYUP:
@@ -384,8 +386,11 @@
     parseCommandLine(argc, argv);
 {$ENDIF}
     initEverything(true);
-    WriteLnToConsole('Hedgewars ' + cVersionString + ' engine (network protocol: ' + inttostr(cNetProtoVersion) + ')');
-
+    WriteLnToConsole('Hedgewars engine ' + cVersionString + '-r' + cRevisionString +
+                     ' (' + cHashString + ') with protocol #' + inttostr(cNetProtoVersion));
+    AddFileLog('Prefix: "' + PathPrefix +'"');
+    AddFileLog('UserPrefix: "' + UserPathPrefix +'"');
+    
     for i:= 0 to ParamCount do
         AddFileLog(inttostr(i) + ': ' + ParamStr(i));
 
--- a/hedgewars/uAI.pas	Wed Feb 20 02:21:58 2013 +0100
+++ b/hedgewars/uAI.pas	Tue Apr 02 21:00:57 2013 +0200
@@ -434,7 +434,7 @@
             // find another hog in team
             repeat
                 itHedgehog:= Succ(itHedgehog) mod CurrentTeam^.HedgehogsNumber;
-            until (itHedgehog = currHedgehogIndex) or (CurrentTeam^.Hedgehogs[itHedgehog].Gear <> nil);
+            until (itHedgehog = currHedgehogIndex) or ((CurrentTeam^.Hedgehogs[itHedgehog].Gear <> nil) and (CurrentTeam^.Hedgehogs[itHedgehog].Effects[heFrozen]=0));
 
 
             inc(switchesNum);
--- a/hedgewars/uAIAmmoTests.pas	Wed Feb 20 02:21:58 2013 +0100
+++ b/hedgewars/uAIAmmoTests.pas	Tue Apr 02 21:00:57 2013 +0200
@@ -197,7 +197,11 @@
     x, y, dX, dY: real;
     t: LongInt;
     value: LongInt;
+    t2: real;
+    timer: Longint;
 begin
+    if (Level > 3) then exit(BadTurn);
+
     mX:= hwFloat2Float(Me^.X);
     mY:= hwFloat2Float(Me^.Y);
     ap.Time:= 0;
@@ -225,13 +229,29 @@
             until (((Me = CurrentHedgehog^.Gear) and TestColl(trunc(x), trunc(y), 5)) or
                    ((Me <> CurrentHedgehog^.Gear) and TestCollExcludingMe(Me, trunc(x), trunc(y), 5))) or (y > cWaterLine);
 
+            if TestCollWithLand(trunc(x), trunc(y), 5) and (Abs(Targ.X - trunc(x)) + Abs(Targ.Y - trunc(y)) > 21) then
+                begin
+                timer := 500;
+                t2 := 0.5 / sqrt(sqr(dX) + sqr(dY));
+                dX := dX * t2;
+                dY := dY * t2;
+                repeat
+                    x:= x + dX;
+                    y:= y + dY;
+                    dec(timer);
+                until (Abs(Targ.X - trunc(x)) + Abs(Targ.Y - trunc(y)) < 22)
+                    or (x < 0)
+                    or (y < 0)
+                    or (trunc(x) > LAND_WIDTH)
+                    or (trunc(y) > LAND_HEIGHT)
+                    or not TestCollWithLand(trunc(x), trunc(y), 5)
+                    or (timer = 0)
+                end;
             EX:= trunc(x);
             EY:= trunc(y);
             if Level = 1 then
                 value:= RateExplosion(Me, EX, EY, 101, afTrackFall or afErasesLand)
             else value:= RateExplosion(Me, EX, EY, 101);
-            if value = 0 then
-                value:= 1024 - Metric(Targ.X, Targ.Y, EX, EY) div 64;
             if valueResult <= value then
                 begin
                 ap.Angle:= DxDy2AttackAnglef(Vx, Vy) + AIrndSign(random((Level - 1) * 9));
@@ -239,7 +259,7 @@
                 ap.ExplR:= 100;
                 ap.ExplX:= EX;
                 ap.ExplY:= EY;
-                valueResult:= value
+                valueResult:= value-2500 // trying to make it slightly less attractive than a bazooka, to prevent waste.  AI could use awareness of weapon count
                 end;
             end
     until rTime > 4250;
@@ -1171,7 +1191,7 @@
 
     //FillChar(cake, sizeof(cake), 0);
     cake.Radius:= 7;
-    cake.CollisionMask:= $FF7F;
+    cake.CollisionMask:= lfNotCurrentMask;
     cake.Hedgehog:= Me^.Hedgehog;
 
     // check left direction
--- a/hedgewars/uAIMisc.pas	Wed Feb 20 02:21:58 2013 +0100
+++ b/hedgewars/uAIMisc.pas	Tue Apr 02 21:00:57 2013 +0200
@@ -74,6 +74,7 @@
 function  TestCollExcludingObjects(x, y, r: LongInt): boolean; inline;
 function  TestCollExcludingMe(Me: PGear; x, y, r: LongInt): boolean; inline;
 function  TraceShoveFall(x, y, dX, dY: Real): LongInt;
+function TestCollWithLand(x, y, r: LongInt): boolean; inline;
 
 function  RateExplosion(Me: PGear; x, y, r: LongInt): LongInt; inline;
 function  RateExplosion(Me: PGear; x, y, r: LongInt; Flags: LongWord): LongInt;
@@ -269,7 +270,7 @@
         MeX:= hwRound(Me^.X);
         MeY:= hwRound(Me^.Y);
         // We are still inside the hog. Skip radius test
-        if ((((x-MeX)*(x-MeX)) + ((y-MeY)*(y-MeY))) < 256) and ((Land[y, x] and $FF00) = 0) then
+        if ((((x-MeX)*(x-MeX)) + ((y-MeY)*(y-MeY))) < 256) and (Land[y, x] <= lfAllObjMask) and ((Land[y, x] and lfObjMask) < 2) then
             exit(false);
     end;
     TestCollExcludingMe:= TestColl(x, y, r)
@@ -278,19 +279,19 @@
 function TestCollExcludingObjects(x, y, r: LongInt): boolean; inline;
 var b: boolean;
 begin
-    b:= (((x-r) and LAND_WIDTH_MASK) = 0) and (((y-r) and LAND_HEIGHT_MASK) = 0) and (Land[y-r, x-r] and $FF00 <> 0);
+    b:= (((x-r) and LAND_WIDTH_MASK) = 0) and (((y-r) and LAND_HEIGHT_MASK) = 0) and (Land[y-r, x-r] > lfAllObjMask);
     if b then
         exit(true);
     
-    b:= (((x-r) and LAND_WIDTH_MASK) = 0) and (((y+r) and LAND_HEIGHT_MASK) = 0) and (Land[y+r, x-r] and $FF00 <> 0);
+    b:= (((x-r) and LAND_WIDTH_MASK) = 0) and (((y+r) and LAND_HEIGHT_MASK) = 0) and (Land[y+r, x-r] > lfAllObjMask);
     if b then
         exit(true);
     
-    b:= (((x+r) and LAND_WIDTH_MASK) = 0) and (((y-r) and LAND_HEIGHT_MASK) = 0) and (Land[y-r, x+r] and $FF00 <> 0);
+    b:= (((x+r) and LAND_WIDTH_MASK) = 0) and (((y-r) and LAND_HEIGHT_MASK) = 0) and (Land[y-r, x+r] > lfAllObjMask);
     if b then
         exit(true);
     
-    b:= (((x+r) and LAND_WIDTH_MASK) = 0) and (((y+r) and LAND_HEIGHT_MASK) = 0) and (Land[y+r, x+r] and $FF00 <> 0);
+    b:= (((x+r) and LAND_WIDTH_MASK) = 0) and (((y+r) and LAND_HEIGHT_MASK) = 0) and (Land[y+r, x+r] > lfAllObjMask);
     if b then
         exit(true);
     
@@ -300,19 +301,19 @@
 function TestColl(x, y, r: LongInt): boolean; inline;
 var b: boolean;
 begin
-    b:= (((x-r) and LAND_WIDTH_MASK) = 0) and (((y-r) and LAND_HEIGHT_MASK) = 0) and (Land[y-r, x-r] and $FF7F <> 0);
+    b:= (((x-r) and LAND_WIDTH_MASK) = 0) and (((y-r) and LAND_HEIGHT_MASK) = 0) and (Land[y-r, x-r] and lfNotCurrentMask <> 0);
     if b then
         exit(true);
     
-    b:= (((x-r) and LAND_WIDTH_MASK) = 0) and (((y+r) and LAND_HEIGHT_MASK) = 0) and (Land[y+r, x-r] and $FF7F <> 0);
+    b:= (((x-r) and LAND_WIDTH_MASK) = 0) and (((y+r) and LAND_HEIGHT_MASK) = 0) and (Land[y+r, x-r] and lfNotCurrentMask <> 0);
     if b then
         exit(true);
     
-    b:= (((x+r) and LAND_WIDTH_MASK) = 0) and (((y-r) and LAND_HEIGHT_MASK) = 0) and (Land[y-r, x+r] and $FF7F <> 0);
+    b:= (((x+r) and LAND_WIDTH_MASK) = 0) and (((y-r) and LAND_HEIGHT_MASK) = 0) and (Land[y-r, x+r] and lfNotCurrentMask <> 0);
     if b then
         exit(true);
     
-    b:= (((x+r) and LAND_WIDTH_MASK) = 0) and (((y+r) and LAND_HEIGHT_MASK) = 0) and (Land[y+r, x+r] and $FF7F <> 0);
+    b:= (((x+r) and LAND_WIDTH_MASK) = 0) and (((y+r) and LAND_HEIGHT_MASK) = 0) and (Land[y+r, x+r] and lfNotCurrentMask <> 0);
     if b then
         exit(true);
     
@@ -322,19 +323,19 @@
 function TestCollWithLand(x, y, r: LongInt): boolean; inline;
 var b: boolean;
 begin
-    b:= (((x-r) and LAND_WIDTH_MASK) = 0) and (((y-r) and LAND_HEIGHT_MASK) = 0) and (Land[y-r, x-r] > 255);
+    b:= (((x-r) and LAND_WIDTH_MASK) = 0) and (((y-r) and LAND_HEIGHT_MASK) = 0) and (Land[y-r, x-r] > lfAllObjMask);
     if b then
         exit(true);
         
-    b:= (((x-r) and LAND_WIDTH_MASK) = 0) and (((y+r) and LAND_HEIGHT_MASK) = 0) and (Land[y+r, x-r] > 255);
+    b:= (((x-r) and LAND_WIDTH_MASK) = 0) and (((y+r) and LAND_HEIGHT_MASK) = 0) and (Land[y+r, x-r] > lfAllObjMask);
     if b then
         exit(true);
         
-    b:= (((x+r) and LAND_WIDTH_MASK) = 0) and (((y-r) and LAND_HEIGHT_MASK) = 0) and (Land[y-r, x+r] > 255);
+    b:= (((x+r) and LAND_WIDTH_MASK) = 0) and (((y-r) and LAND_HEIGHT_MASK) = 0) and (Land[y-r, x+r] > lfAllObjMask);
     if b then
         exit(true);
         
-    b:= (((x+r) and LAND_WIDTH_MASK) = 0) and (((y+r) and LAND_HEIGHT_MASK) = 0) and (Land[y+r, x+r] > 255);
+    b:= (((x+r) and LAND_WIDTH_MASK) = 0) and (((y+r) and LAND_HEIGHT_MASK) = 0) and (Land[y+r, x+r] > lfAllObjMask);
     if b then
         exit(true);
 
@@ -716,7 +717,7 @@
 var pX, pY, tY: LongInt;
 begin
 HHGo:= false;
-Gear^.CollisionMask:= $FF7F;
+Gear^.CollisionMask:= lfNotCurrentMask;
 AltGear^:= Gear^;
 
 GoInfo.Ticks:= 0;
--- a/hedgewars/uChat.pas	Wed Feb 20 02:21:58 2013 +0100
+++ b/hedgewars/uChat.pas	Tue Apr 02 21:00:57 2013 +0200
@@ -25,10 +25,10 @@
 procedure initModule;
 procedure freeModule;
 procedure ReloadLines;
-
+procedure CleanupInput;
 procedure AddChatString(s: shortstring);
 procedure DrawChat;
-procedure KeyPressChat(Key: Longword);
+procedure KeyPressChat(Key, Sym: Longword);
 
 implementation
 uses SDLh, uInputHandler, uTypes, uVariables, uCommands, uUtils, uTextures, uRender, uIO;
@@ -45,8 +45,11 @@
 
 var Strs: array[0 .. MaxStrIndex] of TChatLine;
     MStrs: array[0 .. MaxStrIndex] of shortstring;
+    LocalStrs: array[0 .. MaxStrIndex] of shortstring;
     missedCount: LongWord;
     lastStr: LongWord;
+    localLastStr: LongWord;
+    history: LongWord;
     visibleCount: LongWord;
     InputStr: TChatLine;
     InputStrL: array[0..260] of char; // for full str + 4-byte utf-8 char
@@ -291,41 +294,66 @@
         ParseCommand('/say ' + s, true);
 end;
 
-procedure KeyPressChat(Key: Longword);
-const firstByteMark: array[0..3] of byte = (0, $C0, $E0, $F0);
-var i, btw: integer;
-    utf8: shortstring;
+procedure CleanupInput;
 begin
-if Key <> 0 then
-    case Key of
-        {Backspace}
-        8, 127: if Length(InputStr.s) > 0 then
+    FreezeEnterKey;
+    history:= 0;
+    SDL_EnableKeyRepeat(0,0);
+    GameState:= gsGame;
+    ResetKbd;
+end;
+
+procedure KeyPressChat(Key, Sym: Longword);
+const firstByteMark: array[0..3] of byte = (0, $C0, $E0, $F0);
+var i, btw, index: integer;
+    utf8: shortstring;
+    action: boolean;
+begin
+    action:= true;
+    case Sym of
+        SDLK_BACKSPACE:
+            begin
+            if Length(InputStr.s) > 0 then
                 begin
                 InputStr.s[0]:= InputStrL[byte(InputStr.s[0])];
                 SetLine(InputStr, InputStr.s, true)
-                end;
-        {Esc}
-        27: if Length(InputStr.s) > 0 then SetLine(InputStr, '', true)
-            else
-                begin
-                FreezeEnterKey;
-                SDL_EnableKeyRepeat(0,0);
-                GameState:= gsGame;
-                ResetKbd;
-                end;
-        {Return}
-        3, 13, 271: begin
+                end
+            end;
+        SDLK_ESCAPE:
+            begin
+            if Length(InputStr.s) > 0 then
+                SetLine(InputStr, '', true)
+            else CleanupInput
+            end;
+        SDLK_RETURN:
+            begin
             if Length(InputStr.s) > 0 then
                 begin
                 AcceptChatString(InputStr.s);
                 SetLine(InputStr, '', false)
                 end;
-            FreezeEnterKey;
-            SDL_EnableKeyRepeat(0,0);
-            GameState:= gsGame;
-            ResetKbd;
+            CleanupInput
+            end;
+        SDLK_UP, SDLK_DOWN:
+            begin
+            if (Sym = SDLK_UP) and (history < localLastStr) then inc(history);
+            if (Sym = SDLK_DOWN) and (history > 0) then dec(history);
+            index:= localLastStr - history + 1;
+            if (index > localLastStr) then
+                 SetLine(InputStr, '', true)
+            else SetLine(InputStr, LocalStrs[index], true)
             end;
-    else
+        SDLK_RIGHT, SDLK_LEFT, SDLK_DELETE,
+        SDLK_HOME, SDLK_END,
+        SDLK_PAGEUP, SDLK_PAGEDOWN:
+            begin
+            // ignore me!!!
+            end;
+        else
+            action:= false;
+        end;
+    if not action and (Key <> 0) then
+        begin
         if (Key < $80) then
             btw:= 1
         else if (Key < $800) then
@@ -335,22 +363,22 @@
         else
             btw:= 4;
 
-    utf8:= '';
+        utf8:= '';
 
-    for i:= btw downto 2 do
-        begin
-        utf8:= char((Key or $80) and $BF) + utf8;
-        Key:= Key shr 6
-        end;
+        for i:= btw downto 2 do
+            begin
+            utf8:= char((Key or $80) and $BF) + utf8;
+            Key:= Key shr 6
+            end;
 
-    utf8:= char(Key or firstByteMark[Pred(btw)]) + utf8;
+        utf8:= char(Key or firstByteMark[Pred(btw)]) + utf8;
 
-    if byte(InputStr.s[0]) + btw > 240 then
-        exit;
+        if byte(InputStr.s[0]) + btw > 240 then
+            exit;
 
-    InputStrL[byte(InputStr.s[0]) + btw]:= InputStr.s[0];
-    SetLine(InputStr, InputStr.s + utf8, true)
-    end
+        InputStrL[byte(InputStr.s[0]) + btw]:= InputStr.s[0];
+        SetLine(InputStr, InputStr.s + utf8, true)
+        end
 end;
 
 procedure chChatMessage(var s: shortstring);
@@ -365,7 +393,11 @@
     if copy(s, 1, 4) = '/me ' then
         s:= #2 + '* ' + UserNick + ' ' + copy(s, 5, Length(s) - 4)
     else
+        begin
+        localLastStr:= (localLastStr + 1) mod MaxStrIndex;
+        LocalStrs[localLastStr]:= s;
         s:= #1 + UserNick + ': ' + s;
+        end;
 
     AddChatString(s)
 end;
@@ -393,17 +425,7 @@
     if length(s) = 0 then
         SetLine(InputStr, '', true)
     else
-        begin
-        // err, does anyone have any documentation on this sequence?
-        // ^^ isn't it obvious? 27 is esc, 32 is space, inbetween is "/team"
-        KeyPressChat(27);
-        KeyPressChat(47);
-        KeyPressChat(116);
-        KeyPressChat(101);
-        KeyPressChat(97);
-        KeyPressChat(109);
-        KeyPressChat(32)
-        end
+        SetLine(InputStr, '/team ', true)
 end;
 
 procedure initModule;
@@ -416,6 +438,8 @@
     RegisterVariable('chat', @chChat, true );
 
     lastStr:= 0;
+    localLastStr:= 0;
+    history:= 0;
     visibleCount:= 0;
     showAll:= false;
     ChatReady:= false;
--- a/hedgewars/uCollisions.pas	Wed Feb 20 02:21:58 2013 +0100
+++ b/hedgewars/uCollisions.pas	Tue Apr 02 21:00:57 2013 +0200
@@ -139,7 +139,7 @@
 var x, y, i: LongInt;
 begin
 // Special case to emulate the old intersect gear clearing, but with a bit of slop for pixel overlap
-if (Gear^.CollisionMask = $FF7F) and (Gear^.Kind <> gtHedgehog) and (Gear^.Hedgehog <> nil) and (Gear^.Hedgehog^.Gear <> nil) and
+if (Gear^.CollisionMask = lfNotCurrentMask) and (Gear^.Kind <> gtHedgehog) and (Gear^.Hedgehog <> nil) and (Gear^.Hedgehog^.Gear <> nil) and
     ((hwRound(Gear^.Hedgehog^.Gear^.X) + Gear^.Hedgehog^.Gear^.Radius + 16 < hwRound(Gear^.X) - Gear^.Radius) or
      (hwRound(Gear^.Hedgehog^.Gear^.X) - Gear^.Hedgehog^.Gear^.Radius - 16 > hwRound(Gear^.X) + Gear^.Radius)) then
     Gear^.CollisionMask:= $FFFF;
@@ -169,7 +169,7 @@
 var x, y, i: LongInt;
 begin
 // Special case to emulate the old intersect gear clearing, but with a bit of slop for pixel overlap
-if (Gear^.CollisionMask = $FF7F) and (Gear^.Kind <> gtHedgehog) and (Gear^.Hedgehog <> nil) and (Gear^.Hedgehog^.Gear <> nil) and
+if (Gear^.CollisionMask = lfNotCurrentMask) and (Gear^.Kind <> gtHedgehog) and (Gear^.Hedgehog <> nil) and (Gear^.Hedgehog^.Gear <> nil) and
     ((hwRound(Gear^.Hedgehog^.Gear^.Y) + Gear^.Hedgehog^.Gear^.Radius + 16 < hwRound(Gear^.Y) - Gear^.Radius) or
      (hwRound(Gear^.Hedgehog^.Gear^.Y) - Gear^.Hedgehog^.Gear^.Radius - 16 > hwRound(Gear^.Y) + Gear^.Radius)) then
     Gear^.CollisionMask:= $FFFF;
--- a/hedgewars/uCommandHandlers.pas	Wed Feb 20 02:21:58 2013 +0100
+++ b/hedgewars/uCommandHandlers.pas	Tue Apr 02 21:00:57 2013 +0200
@@ -595,7 +595,6 @@
             if bShowAmmoMenu then
                 bShowAmmoMenu:= false
             else if not(CurrentTeam^.Extdriven) and (((Gear^.State and (gstAttacking or gstAttacked)) <> 0)
-            or ((MultiShootAttacks > 0) and ((Ammoz[CurAmmoType].Ammo.Propz and ammoprop_NoRoundEnd) = 0))
             or ((Gear^.State and gstHHDriven) = 0)) then
                 begin
                 end
--- a/hedgewars/uCommands.pas	Wed Feb 20 02:21:58 2013 +0100
+++ b/hedgewars/uCommands.pas	Tue Apr 02 21:00:57 2013 +0200
@@ -88,12 +88,14 @@
 //WriteLnToConsole(CmdStr);
 if CmdStr[0]=#0 then
     exit;
+
+AddFileLog('[Cmd] ' + sanitizeForLog(CmdStr));
+
 c:= CmdStr[1];
 if (c = '/') or (c = '$') then
     Delete(CmdStr, 1, 1);
 s:= '';
 SplitBySpace(CmdStr, s);
-AddFileLog('[Cmd] ' + CmdStr + ' (' + inttostr(length(s)) + ')');
 
 t:= Variables;
 while t <> nil do
--- a/hedgewars/uConsts.pas	Wed Feb 20 02:21:58 2013 +0100
+++ b/hedgewars/uConsts.pas	Tue Apr 02 21:00:57 2013 +0200
@@ -97,6 +97,16 @@
     lfDamaged        = $1000;  //
     lfIce            = $0800;  // blue
     lfBouncy         = $0400;  // green
+    lfLandMask       = $FF00;  // upper byte is used for terrain, not objects.
+
+    lfCurrentHog     = $0080;  // CurrentHog.  It is also used to flag crates, for convenience of AI.  Since an active hog would instantly collect the crate, this doesn't impact play
+    lfNotCurrentMask = $FF7F;  // inverse of above. frequently used
+    lfObjMask        = $007F;  // lower 7 bits used for hogs
+    lfNotObjMask     = $FF80;  // inverse of above.
+    // lower byte is for objects. 
+    // consists of 0-127 counted for object checkins and $80 as a bit flag for current hog. 
+    lfAllObjMask     = $00FF;  // lfCurrentHog or lfObjMask
+
 
     cMaxPower     = 1500;
     cMaxAngle     = 2048;
--- a/hedgewars/uGame.pas	Wed Feb 20 02:21:58 2013 +0100
+++ b/hedgewars/uGame.pas	Tue Apr 02 21:00:57 2013 +0200
@@ -27,20 +27,24 @@
     implementation
 ////////////////////
 uses uInputHandler, uTeams, uIO, uAI, uGears, uSound,
+    uLocale, uCaptions,
     uVisualGears, uTypes, uVariables, uCommands, uConsts
     {$IFDEF USE_TOUCH_INTERFACE}, uTouch{$ENDIF};
 
 procedure DoGameTick(Lag: LongInt);
-var i: LongInt;
+var i,j : LongInt;
+    s: shortstring;
 begin
 if isPaused then
     exit;
+
 if (not CurrentTeam^.ExtDriven) then
     begin
     NetGetNextCmd; // its for the case of receiving "/say" message
     isInLag:= false;
-    SendKeepAliveMessage(Lag)
+    FlushMessages(Lag)
     end;
+
 if GameType <> gmtRecord then
     begin
     if Lag > 100 then
@@ -61,6 +65,23 @@
         else if cOnlyStats then
             Lag:= High(LongInt)
     end;
+inc(SoundTimerTicks, Lag);
+if SoundTimerTicks >= 50 then
+    begin
+    SoundTimerTicks:= 0;
+    if cVolumeDelta <> 0 then
+        begin
+        j:= Volume;
+        i:= ChangeVolume(cVolumeDelta);
+        if isAudioMuted and (j<>i) then
+            AddCaption(trmsg[sidMute], cWhiteColor, capgrpVolume)
+        else if not isAudioMuted then
+            begin
+            str(i, s);
+            AddCaption(Format(trmsg[sidVolume], s), cWhiteColor, capgrpVolume)
+            end
+        end;
+    end;
 PlayNextVoice;
 i:= 1;
 while (GameState <> gsExit) and (i <= Lag) do
--- a/hedgewars/uGears.pas	Wed Feb 20 02:21:58 2013 +0100
+++ b/hedgewars/uGears.pas	Tue Apr 02 21:00:57 2013 +0200
@@ -46,7 +46,8 @@
 procedure ProcessGears;
 procedure EndTurnCleanup;
 procedure SetAllToActive;
-procedure SetAllHHToActive;
+procedure SetAllHHToActive; inline;
+procedure SetAllHHToActive(Ice: boolean);
 procedure DrawGears;
 procedure FreeGearsList;
 procedure AddMiscGears;
@@ -441,8 +442,8 @@
 
 if TurnTimeLeft > 0 then
     if CurrentHedgehog^.Gear <> nil then
-        if ((CurrentHedgehog^.Gear^.State and gstAttacking) = 0)
-            and (not isInMultiShoot) then
+        if ((CurrentHedgehog^.Gear^.State and gstAttacking) = 0) and 
+            not(isInMultiShoot and (CurrentHedgehog^.CurAmmoType in [amShotgun, amDEagle, amSniperRifle])) then
                 begin
                 if (TurnTimeLeft = 5000)
                 and (cHedgehogTurnTime >= 10000)
@@ -561,7 +562,12 @@
     end
 end;
 
-procedure SetAllHHToActive;
+procedure SetAllHHToActive; inline;
+begin
+SetAllHHToActive(true)
+end;
+
+procedure SetAllHHToActive(Ice: boolean);
 var t: PGear;
 begin
 AllInactive:= false;
@@ -569,12 +575,14 @@
 while t <> nil do
     begin
     if (t^.Kind = gtHedgehog) or (t^.Kind = gtExplosives) then
-        t^.Active:= true;
+        begin
+        if (t^.Kind = gtHedgehog) and Ice then CheckIce(t);
+        t^.Active:= true
+        end;
     t:= t^.NextGear
     end
 end;
 
-
 procedure DrawGears;
 var Gear: PGear;
     x, y: LongInt;
@@ -766,6 +774,8 @@
     begin
     dec(i);
     Gear:= t^.ar[i];
+    if (Ammo^.Kind = gtFlame) and (Gear^.Kind = gtHedgehog) and (Gear^.Hedgehog^.Effects[heFrozen] > 255) then
+        Gear^.Hedgehog^.Effects[heFrozen]:= max(255,Gear^.Hedgehog^.Effects[heFrozen]-10000);
     tmpDmg:= ModifyDamage(Damage, Gear);
     if (Gear^.State and gstNoDamage) = 0 then
         begin
@@ -780,6 +790,7 @@
         if (Gear^.Kind = gtHedgehog) and (Ammo^.State and gsttmpFlag <> 0) and (Ammo^.Kind = gtShover) then
             Gear^.FlightTime:= 1;
 
+
         case Gear^.Kind of
             gtHedgehog,
             gtMine,
@@ -827,7 +838,7 @@
                 ApplyDamage(Gear, Ammo^.Hedgehog, tmpDmg * 100, dsUnknown); // crank up damage for explosives + blowtorch
                 end;
 
-            if (Gear^.Kind = gtHedgehog) and Gear^.Hedgehog^.King then
+            if (Gear^.Kind = gtHedgehog) and (Gear^.Hedgehog^.King or (Gear^.Hedgehog^.Effects[heFrozen] > 0)) then
                 begin
                 Gear^.dX:= Ammo^.dX * Power * _0_005;
                 Gear^.dY:= Ammo^.dY * Power * _0_005
--- a/hedgewars/uGearsHandlersRope.pas	Wed Feb 20 02:21:58 2013 +0100
+++ b/hedgewars/uGearsHandlersRope.pas	Tue Apr 02 21:00:57 2013 +0200
@@ -33,6 +33,14 @@
     HHGear: PGear;
 begin
     HHGear := Gear^.Hedgehog^.Gear;
+    if (HHGear^.Hedgehog^.CurAmmoType = amParachute) and (HHGear^.dY > _0_39) then
+        begin
+        DeleteGear(Gear);
+        ApplyAmmoChanges(HHGear^.Hedgehog^);
+        HHGear^.Message:= HHGear^.Message or gmLJump;
+        exit
+        end;
+
     if ((HHGear^.State and gstHHDriven) = 0)
     or (CheckGearDrowning(HHGear))
     or (TestCollisionYwithGear(HHGear, 1) <> 0) then
@@ -102,7 +110,7 @@
     lx, ly, cd: LongInt;
     haveCollision,
     haveDivided: boolean;
-
+    wrongSide: boolean;
 begin
     if GameTicks mod 4 <> 0 then exit;
 
@@ -165,12 +173,12 @@
 
     if ((Gear^.Message and gmDown) <> 0) and (Gear^.Elasticity < Gear^.Friction) then
         if not (TestCollisionXwithGear(HHGear, hwSign(ropeDx))
-        or (TestCollisionYwithGear(HHGear, hwSign(ropeDy)) <> 0)) then
+        or ((ropeDy.QWordValue <> 0) and TestCollisionYwithXYShift(HHGear, 0, 1, hwSign(ropeDy)))) then
             Gear^.Elasticity := Gear^.Elasticity + _1_2;
 
     if ((Gear^.Message and gmUp) <> 0) and (Gear^.Elasticity > _30) then
         if not (TestCollisionXwithGear(HHGear, -hwSign(ropeDx))
-        or (TestCollisionYwithGear(HHGear, -hwSign(ropeDy)) <> 0)) then
+        or ((ropeDy.QWordValue <> 0) and TestCollisionYwithXYShift(HHGear, 0, 1, -hwSign(ropeDy)))) then
             Gear^.Elasticity := Gear^.Elasticity - _1_2;
 
     HHGear^.X := Gear^.X + mdX * Gear^.Elasticity;
@@ -194,7 +202,7 @@
         begin
         lx := hwRound(nx);
         ly := hwRound(ny);
-        if ((ly and LAND_HEIGHT_MASK) = 0) and ((lx and LAND_WIDTH_MASK) = 0) and ((Land[ly, lx] and $FF00) <> 0) then
+        if ((ly and LAND_HEIGHT_MASK) = 0) and ((lx and LAND_WIDTH_MASK) = 0) and (Land[ly, lx] > lfAllObjMask) then
             begin
             tx := _1 / Distance(ropeDx, ropeDy);
             // old rope pos
@@ -208,6 +216,9 @@
                 if RopePoints.Count = 0 then
                     RopePoints.HookAngle := DxDy2Angle(Gear^.dY, Gear^.dX);
                 b := (nx * HHGear^.dY) > (ny * HHGear^.dX);
+                sx:= Gear^.dX.isNegative;
+                sy:= Gear^.dY.isNegative;
+                sb:= Gear^.dX.QWordValue < Gear^.dY.QWordValue;
                 dLen := len
                 end;
 
@@ -240,21 +251,45 @@
             ty := RopePoints.ar[Pred(RopePoints.Count)].Y;
             mdX := tx - Gear^.X;
             mdY := ty - Gear^.Y;
-            if RopePoints.ar[Pred(RopePoints.Count)].b xor (mdX * (ty - HHGear^.Y) > (tx - HHGear^.X) * mdY) then
+            ropeDx:= tx - HHGear^.X;
+            ropeDy:= ty - HHGear^.Y;
+            if RopePoints.ar[Pred(RopePoints.Count)].b xor (mdX * ropeDy > ropeDx * mdY) then
                 begin
                 dec(RopePoints.Count);
-                Gear^.X := RopePoints.ar[RopePoints.Count].X;
-                Gear^.Y := RopePoints.ar[RopePoints.Count].Y;
-                Gear^.Elasticity := Gear^.Elasticity + RopePoints.ar[RopePoints.Count].dLen;
-                Gear^.Friction := Gear^.Friction + RopePoints.ar[RopePoints.Count].dLen;
+                Gear^.X := tx;
+                Gear^.Y := ty;
+
+                // oops, opposite quadrant, don't restore hog position in such case, just remove the point
+                wrongSide:= (ropeDx.isNegative = RopePoints.ar[RopePoints.Count].sx)
+                    and (ropeDy.isNegative = RopePoints.ar[RopePoints.Count].sy);
+
+                // previous check could be inaccurate in vertical/horizontal rope positions,
+                // so perform this check also, even though odds are 1 to 415927 to hit this
+                if (not wrongSide)
+                    and ((ropeDx.isNegative = RopePoints.ar[RopePoints.Count].sx)
+                      <> (ropeDy.isNegative = RopePoints.ar[RopePoints.Count].sy)) then
+                    if RopePoints.ar[RopePoints.Count].sb then
+                        wrongSide:= ropeDy.isNegative = RopePoints.ar[RopePoints.Count].sy
+                        else
+                        wrongSide:= ropeDx.isNegative = RopePoints.ar[RopePoints.Count].sx;
 
-                // restore hog position
-                len := _1 / Distance(mdX, mdY);
-                mdX := mdX * len;
-                mdY := mdY * len;
+                if wrongSide then
+                    begin
+                    Gear^.Elasticity := Gear^.Elasticity - RopePoints.ar[RopePoints.Count].dLen;
+                    Gear^.Friction := Gear^.Friction - RopePoints.ar[RopePoints.Count].dLen;
+                    end else
+                    begin
+                    Gear^.Elasticity := Gear^.Elasticity + RopePoints.ar[RopePoints.Count].dLen;
+                    Gear^.Friction := Gear^.Friction + RopePoints.ar[RopePoints.Count].dLen;
 
-                HHGear^.X := Gear^.X - mdX * Gear^.Elasticity;
-                HHGear^.Y := Gear^.Y - mdY * Gear^.Elasticity;
+                    // restore hog position
+                    len := _1 / Distance(mdX, mdY);
+                    mdX := mdX * len;
+                    mdY := mdY * len;
+
+                    HHGear^.X := Gear^.X - mdX * Gear^.Elasticity;
+                    HHGear^.Y := Gear^.Y - mdY * Gear^.Elasticity;
+                    end;
                 end
             end;
 
@@ -264,7 +299,7 @@
         HHGear^.dX := -_0_6 * HHGear^.dX;
         haveCollision := true
         end;
-    if TestCollisionYwithGear(HHGear, hwSign(HHGear^.dY)) <> 0 then
+    if TestCollisionYwithXYShift(HHGear, 0, 1, hwSign(HHGear^.dY)) then
         begin
         HHGear^.dY := -_0_6 * HHGear^.dY;
         haveCollision := true
@@ -390,7 +425,7 @@
         ty := _0;
         while tt > _20 do
             begin
-            if ((hwRound(Gear^.Y+ty) and LAND_HEIGHT_MASK) = 0) and ((hwRound(Gear^.X+tx) and LAND_WIDTH_MASK) = 0) and ((Land[hwRound(Gear^.Y+ty), hwRound(Gear^.X+tx)] and $FF00) <> 0) then
+            if ((hwRound(Gear^.Y+ty) and LAND_HEIGHT_MASK) = 0) and ((hwRound(Gear^.X+tx) and LAND_WIDTH_MASK) = 0) and (Land[hwRound(Gear^.Y+ty), hwRound(Gear^.X+tx)] > lfAllObjMask) then
                 begin
                 Gear^.X := Gear^.X + tx;
                 Gear^.Y := Gear^.Y + ty;
@@ -414,8 +449,8 @@
             end;
         end;
 
-    if Gear^.Elasticity < _20 then Gear^.CollisionMask:= $FF00
-    else Gear^.CollisionMask:= $FF7F;
+    if Gear^.Elasticity < _20 then Gear^.CollisionMask:= lfLandMask
+    else Gear^.CollisionMask:= lfNotCurrentMask;
     CheckCollision(Gear);
 
     if (Gear^.State and gstCollision) <> 0 then
--- a/hedgewars/uGearsHedgehog.pas	Wed Feb 20 02:21:58 2013 +0100
+++ b/hedgewars/uGearsHedgehog.pas	Tue Apr 02 21:00:57 2013 +0200
@@ -29,6 +29,7 @@
 procedure HedgehogChAngle(HHGear: PGear);
 procedure PickUp(HH, Gear: PGear);
 procedure AddPickup(HH: THedgehog; ammo: TAmmoType; cnt, X, Y: LongWord);
+procedure CheckIce(Gear: PGear); inline;
 
 implementation
 uses uConsts, uVariables, uFloat, uAmmos, uSound, uCaptions,
@@ -53,7 +54,6 @@
     prevAmmo:= CurAmmoType;
     ammoidx:= 0;
     if ((HHGear^.State and (gstAttacking or gstAttacked)) <> 0)
-    or ((MultiShootAttacks > 0) and ((Ammoz[CurAmmoType].Ammo.Propz and ammoprop_NoRoundEnd) = 0))
     or ((HHGear^.State and gstHHDriven) = 0) then
         exit;
     ChangeAmmo:= true;
@@ -61,8 +61,17 @@
     while (ammoidx < cMaxSlotAmmoIndex) and (Ammo^[slot, ammoidx].AmmoType <> CurAmmoType) do
         inc(ammoidx);
 
-    if ((Ammoz[CurAmmoType].Ammo.Propz and ammoprop_NoRoundEnd) <> 0) and (MultiShootAttacks > 0) then
-        OnUsedAmmo(HHGear^.Hedgehog^);
+    if (MultiShootAttacks > 0) then
+        begin
+        if (CurAmmoType = amSniperRifle) and ((GameFlags and gfArtillery) = 0) then
+            cArtillery := false;
+        if (Ammoz[CurAmmoType].Ammo.Propz and ammoprop_NoRoundEnd) = 0 then
+            begin
+            MultiShootAttacks:= Ammoz[CurAmmoType].Ammo.NumPerTurn;
+            AfterAttack
+            end
+        else OnUsedAmmo(HHGear^.Hedgehog^)
+        end;
 
     MultiShootAttacks:= 0;
     HHGear^.Message:= HHGear^.Message and (not (gmLJump or gmHJump));
@@ -211,9 +220,7 @@
         and ((TargetPoint.X <> NoPointX) or ((Ammoz[CurAmmoType].Ammo.Propz and ammoprop_NeedTarget) = 0)) then
             begin
             State:= State or gstAttacking;
-            if Power = cMaxPower then
-                Message:= Message and (not gmAttack)
-            else if (Ammoz[CurAmmoType].Ammo.Propz and ammoprop_Power) = 0 then
+            if (Power = cMaxPower) or ((Ammoz[CurAmmoType].Ammo.Propz and ammoprop_Power) = 0) then
                 Message:= Message and (not gmAttack)
             else
                 begin
@@ -224,44 +231,44 @@
                     end;
                 inc(Power)
                 end;
-        if ((Message and gmAttack) <> 0) then
-            exit;
+            if ((Message and gmAttack) <> 0) then
+                exit;
 
-        if (Ammoz[CurAmmoType].Ammo.Propz and ammoprop_Power) <> 0 then
-            begin
-            StopSound(sndThrowPowerUp);
-            PlaySound(sndThrowRelease);
-            end;
+            if (Ammoz[CurAmmoType].Ammo.Propz and ammoprop_Power) <> 0 then
+                begin
+                StopSound(sndThrowPowerUp);
+                PlaySound(sndThrowRelease);
+                end;
 
-        xx:= SignAs(AngleSin(Angle), dX);
-        yy:= -AngleCos(Angle);
+            xx:= SignAs(AngleSin(Angle), dX);
+            yy:= -AngleCos(Angle);
 
-        lx:= X + int2hwfloat(round(GetLaunchX(CurAmmoType, hwSign(dX), Angle)));
-        ly:= Y + int2hwfloat(round(GetLaunchY(CurAmmoType, Angle)));
+            lx:= X + int2hwfloat(round(GetLaunchX(CurAmmoType, hwSign(dX), Angle)));
+            ly:= Y + int2hwfloat(round(GetLaunchY(CurAmmoType, Angle)));
 
-        if ((Gear^.State and gstHHHJump) <> 0) and (not cArtillery) then
-            xx:= - xx;
-        if Ammoz[CurAmmoType].Ammo.AttackVoice <> sndNone then
-            AddVoice(Ammoz[CurAmmoType].Ammo.AttackVoice, CurrentTeam^.voicepack);
+            if ((Gear^.State and gstHHHJump) <> 0) and (not cArtillery) then
+                xx:= - xx;
+            if Ammoz[CurAmmoType].Ammo.AttackVoice <> sndNone then
+                AddVoice(Ammoz[CurAmmoType].Ammo.AttackVoice, CurrentTeam^.voicepack);
 
 // Initiating alt attack
-        if  (CurAmmoGear <> nil)
-        and ((Ammoz[CurAmmoGear^.AmmoType].Ammo.Propz and ammoprop_AltAttack) <> 0)
-        and ((Gear^.Message and gmLJump) <> 0)
-        and ((Ammoz[CurAmmoType].Ammo.Propz and ammoprop_AltUse) <> 0) then
-            begin
-            newDx:= dX;
-            newDy:= dY;
-            altUse:= true
-            end
-        else
-            begin
-            newDx:= xx*Power/cPowerDivisor;
-            newDy:= yy*Power/cPowerDivisor;
-            altUse:= false
-            end;
+            if  (CurAmmoGear <> nil)
+            and ((Ammoz[CurAmmoGear^.AmmoType].Ammo.Propz and ammoprop_AltAttack) <> 0)
+            and ((Gear^.Message and gmLJump) <> 0)
+            and ((Ammoz[CurAmmoType].Ammo.Propz and ammoprop_AltUse) <> 0) then
+                begin
+                newDx:= dX;
+                newDy:= dY;
+                altUse:= true
+                end
+            else
+                begin
+                newDx:= xx*Power/cPowerDivisor;
+                newDy:= yy*Power/cPowerDivisor;
+                altUse:= false
+                end;
 
-             case CurAmmoType of
+            case CurAmmoType of
                       amGrenade: newGear:= AddGear(hwRound(lx), hwRound(ly), gtGrenade,         0, newDx, newDy, CurWeapon^.Timer);
                       amMolotov: newGear:= AddGear(hwRound(lx), hwRound(ly), gtMolotov,      0, newDx, newDy, 0);
                   amClusterBomb: newGear:= AddGear(hwRound(lx), hwRound(ly), gtClusterBomb,  0, newDx, newDy, CurWeapon^.Timer);
@@ -281,7 +288,7 @@
                         amKnife: begin
                                  newGear:= AddGear(hwRound(lx), hwRound(ly), gtKnife,    0, xx*Power/cPowerDivisor, yy*Power/cPowerDivisor, 0);
                                  newGear^.State:= newGear^.State or gstMoving;
-                                 newGear^.Radius:= 6 // temporarily shrink so it doesn't instantly embed in the ground
+                                 newGear^.Radius:= 4 // temporarily shrink so it doesn't instantly embed in the ground
                                  end;
                        amDEagle: newGear:= AddGear(hwRound(lx + xx * cHHRadius), hwRound(ly + yy * cHHRadius), gtDEagleShot, 0, xx * _0_5, yy * _0_5, 0);
                       amSineGun: newGear:= AddGear(hwRound(lx + xx * cHHRadius), hwRound(ly + yy * cHHRadius), gtSineGunShot, 0, xx * _0_5, yy * _0_5, 0);
@@ -377,56 +384,56 @@
                     //amStructure: newGear:= AddGear(hwRound(lx) + hwSign(dX) * 7, hwRound(ly), gtStructure, gstWait, SignAs(_0_02, dX), _0, 3000);
                        amTardis: newGear:= AddGear(hwRound(X), hwRound(Y), gtTardis, 0, _0, _0, 5000);
                        amIceGun: newGear:= AddGear(hwRound(X), hwRound(Y), gtIceGun, 0, _0, _0, 0);
-             end;
-             if altUse and (newGear <> nil) then
-                begin
-                newGear^.dX:= newDx / newGear^.Density;
-                newGear^.dY:= newDY / newGear^.Density
-                end;
+            end;
+            if altUse and (newGear <> nil) then
+               begin
+               newGear^.dX:= newDx / newGear^.Density;
+               newGear^.dY:= newDY / newGear^.Density
+               end;
 
-             case CurAmmoType of
-                      amGrenade, amMolotov,
-                  amClusterBomb, amGasBomb,
-                      amBazooka, amSnowball,
-                          amBee, amSMine,
-                       amMortar, amWatermelon,
-                  amHellishBomb, amDrill: FollowGear:= newGear;
+            case CurAmmoType of
+                     amGrenade, amMolotov,
+                 amClusterBomb, amGasBomb,
+                     amBazooka, amSnowball,
+                         amBee, amSMine,
+                      amMortar, amWatermelon,
+                 amHellishBomb, amDrill: FollowGear:= newGear;
 
-                      amShotgun, amPickHammer,
-                         amRope, amDEagle,
-                      amSineGun, amSniperRifle,
-                    amFirePunch, amWhip,
-                       amHammer, amBaseballBat,
-                    amParachute, amBlowTorch,
-                       amGirder, amTeleport,
-                       amSwitch, amRCPlane,
-                     amKamikaze, amCake,
-                    amSeduction, amBallgun,
-                      amJetpack, amBirdy,
-                 amFlamethrower, amLandGun,
-                  amResurrector, //amStructure,
-                       amTardis, amPiano,
-                       amIceGun: CurAmmoGear:= newGear;
-             end;
+                     amShotgun, amPickHammer,
+                        amRope, amDEagle,
+                     amSineGun, amSniperRifle,
+                   amFirePunch, amWhip,
+                      amHammer, amBaseballBat,
+                   amParachute, amBlowTorch,
+                      amGirder, amTeleport,
+                      amSwitch, amRCPlane,
+                    amKamikaze, amCake,
+                   amSeduction, amBallgun,
+                     amJetpack, amBirdy,
+                amFlamethrower, amLandGun,
+                 amResurrector, //amStructure,
+                      amTardis, amPiano,
+                      amIceGun: CurAmmoGear:= newGear;
+            end;
 
             if ((CurAmmoType = amMine) or (CurAmmoType = amSMine)) and (GameFlags and gfInfAttack <> 0) then
                 newGear^.FlightTime:= GameTicks + 1000
             else if CurAmmoType = amDrill then
                 newGear^.FlightTime:= GameTicks + 250;
-        if Ammoz[CurAmmoType].Ammo.Propz and ammoprop_NeedTarget <> 0 then
-            begin
-            newGear^.Target.X:= TargetPoint.X;
-            newGear^.Target.Y:= TargetPoint.Y
-            end;
-        if (newGear <> nil) and (newGear^.CollisionMask and $80 <> 0) then newGear^.CollisionMask:= newGear^.CollisionMask and (not $80);
+            if Ammoz[CurAmmoType].Ammo.Propz and ammoprop_NeedTarget <> 0 then
+                begin
+                newGear^.Target.X:= TargetPoint.X;
+                newGear^.Target.Y:= TargetPoint.Y
+                end;
+            if (newGear <> nil) and (newGear^.CollisionMask and lfCurrentHog <> 0) then newGear^.CollisionMask:= newGear^.CollisionMask and (not lfCurrentHog);
 
-        // Clear FollowGear if using on a rope/parachute/saucer etc so focus stays with the hog's movement
-        if altUse then
-            FollowGear:= nil;
+            // Clear FollowGear if using on a rope/parachute/saucer etc so focus stays with the hog's movement
+            if altUse then
+                FollowGear:= nil;
 
-        if (newGear <> nil) and ((Ammoz[newGear^.AmmoType].Ammo.Propz and ammoprop_SetBounce) <> 0) then
-            begin
-            elastic:=  int2hwfloat(CurWeapon^.Bounciness) / _1000;
+            if (newGear <> nil) and ((Ammoz[newGear^.AmmoType].Ammo.Propz and ammoprop_SetBounce) <> 0) then
+                begin
+                elastic:=  int2hwfloat(CurWeapon^.Bounciness) / _1000;
 
             if elastic < _1 then
                 newGear^.Elasticity:= newGear^.Elasticity * elastic
@@ -439,37 +446,36 @@
             end;
 
 
-        uStats.AmmoUsed(CurAmmoType);
+            uStats.AmmoUsed(CurAmmoType);
 
-        if not (SpeechText = '') then
-            begin
-            speech:= AddVisualGear(0, 0, vgtSpeechBubble);
-            if speech <> nil then
+            if not (SpeechText = '') then
                 begin
-                speech^.Text:= SpeechText;
-                speech^.Hedgehog:= Gear^.Hedgehog;
-                speech^.FrameTicks:= SpeechType;
+                speech:= AddVisualGear(0, 0, vgtSpeechBubble);
+                if speech <> nil then
+                    begin
+                    speech^.Text:= SpeechText;
+                    speech^.Hedgehog:= Gear^.Hedgehog;
+                    speech^.FrameTicks:= SpeechType;
+                    end;
+                SpeechText:= ''
                 end;
-            SpeechText:= ''
-            end;
 
-        Power:= 0;
-        if (CurAmmoGear <> nil)
-            and ((Ammoz[CurAmmoType].Ammo.Propz and ammoprop_AltUse) = 0){check for dropping ammo from rope} then
-            begin
-            Message:= Message or gmAttack;
-            CurAmmoGear^.Message:= Message
+            Power:= 0;
+            if (CurAmmoGear <> nil) and ((Ammoz[CurAmmoType].Ammo.Propz and ammoprop_AltUse) = 0){check for dropping ammo from rope} then
+                begin
+                if CurAmmoType in [amRope,amResurrector] then
+                    Message:= Message or gmAttack;
+                CurAmmoGear^.Message:= Message
+                end
+            else
+                begin
+                if (not CurrentTeam^.ExtDriven) and ((Ammoz[CurAmmoType].Ammo.Propz and ammoprop_Power) <> 0) then
+                    SendIPC(_S'a');
+                AfterAttack;
+                end
             end
         else
-            begin
-            if not CurrentTeam^.ExtDriven
-            and ((Ammoz[CurAmmoType].Ammo.Propz and ammoprop_Power) <> 0) then
-                SendIPC(_S'a');
-            AfterAttack;
-            end
-        end
-    else
-        Message:= Message and (not gmAttack);
+            Message:= Message and (not gmAttack);
     end;
     TargetPoint.X := NoPointX;
     ScriptCall('onHogAttack');
@@ -777,7 +783,7 @@
     if (not cArtillery) and ((Gear^.Message and gmPrecise) = 0) then
         MakeHedgehogsStep(Gear);
 
-    SetAllHHToActive;
+    SetAllHHToActive(false);
     AddGearCI(Gear)
     end
 end;
@@ -786,7 +792,7 @@
 var da: LongWord;
 begin
 with HHGear^.Hedgehog^ do
-    if ((CurAmmoType = amRope) and ((HHGear^.State and (gstMoving or gstHHJumping)) = gstMoving))
+    if ((CurAmmoGear <> nil) and (CurAmmoGear^.AmmoType = amRope) and ((HHGear^.State and (gstMoving or gstHHJumping)) = gstMoving))
     or ((CurAmmoType = amPortalGun) and ((HHGear^.State and gstMoving) <> 0)) then
         da:= 2
     else da:= 1;
@@ -887,35 +893,40 @@
     if TestCollisionXKick(Gear, hwSign(Gear^.dX)) then
         if not isFalling then
             if hwAbs(Gear^.dX) > _0_01 then
-                if not TestCollisionXwithXYShift(Gear, int2hwFloat(hwSign(Gear^.dX)) - Gear^.dX, -1, hwSign(Gear^.dX)) then
+                if not (TestCollisionXwithXYShift(Gear, int2hwFloat(hwSign(Gear^.dX)) - Gear^.dX, -1, hwSign(Gear^.dX)) or
+                (TestCollisionYwithXYShift(Gear, hwSign(Gear^.dX) - hwRound(Gear^.dX), -1, -1))) then
                     begin
                     Gear^.X:= Gear^.X + Gear^.dX;
                     Gear^.dX:= Gear^.dX * _0_96;
                     Gear^.Y:= Gear^.Y - _1
                     end
                 else
-                    if not TestCollisionXwithXYShift(Gear, int2hwFloat(hwSign(Gear^.dX)) - Gear^.dX, -2, hwSign(Gear^.dX)) then
+                    if not (TestCollisionXwithXYShift(Gear, int2hwFloat(hwSign(Gear^.dX)) - Gear^.dX, -2, hwSign(Gear^.dX)) or
+                        (TestCollisionYwithXYShift(Gear, hwSign(Gear^.dX) - hwRound(Gear^.dX), -1, -1))) then
                         begin
                         Gear^.X:= Gear^.X + Gear^.dX;
                         Gear^.dX:= Gear^.dX * _0_93;
                         Gear^.Y:= Gear^.Y - _2
                         end
                     else
-                        if not TestCollisionXwithXYShift(Gear, int2hwFloat(hwSign(Gear^.dX)) - Gear^.dX, -3, hwSign(Gear^.dX)) then
+                    if not (TestCollisionXwithXYShift(Gear, int2hwFloat(hwSign(Gear^.dX)) - Gear^.dX, -3, hwSign(Gear^.dX)) or
+                        (TestCollisionYwithXYShift(Gear, hwSign(Gear^.dX) - hwRound(Gear^.dX), -1, -1))) then
                         begin
                         Gear^.X:= Gear^.X + Gear^.dX;
                         Gear^.dX:= Gear^.dX * _0_9 ;
                         Gear^.Y:= Gear^.Y - _3
                         end
                     else
-                        if not TestCollisionXwithXYShift(Gear, int2hwFloat(hwSign(Gear^.dX)) - Gear^.dX, -4, hwSign(Gear^.dX)) then
+                        if not (TestCollisionXwithXYShift(Gear, int2hwFloat(hwSign(Gear^.dX)) - Gear^.dX, -4, hwSign(Gear^.dX)) or
+                        (TestCollisionYwithXYShift(Gear, hwSign(Gear^.dX) - hwRound(Gear^.dX), -1, -1))) then
                             begin
                             Gear^.X:= Gear^.X + Gear^.dX;
                             Gear^.dX:= Gear^.dX * _0_87;
                             Gear^.Y:= Gear^.Y - _4
                             end
                     else
-                        if not TestCollisionXwithXYShift(Gear, int2hwFloat(hwSign(Gear^.dX)) - Gear^.dX, -5, hwSign(Gear^.dX)) then
+                        if not (TestCollisionXwithXYShift(Gear, int2hwFloat(hwSign(Gear^.dX)) - Gear^.dX, -5, hwSign(Gear^.dX)) or
+                        (TestCollisionYwithXYShift(Gear, hwSign(Gear^.dX) - hwRound(Gear^.dX), -1, -1))) then
                             begin
                             Gear^.X:= Gear^.X + Gear^.dX;
                             Gear^.dX:= Gear^.dX * _0_84;
@@ -1000,7 +1011,9 @@
     Hedgehog: PHedgehog;
 begin
 Hedgehog:= HHGear^.Hedgehog;
-if isInMultiShoot then
+if not isInMultiShoot then
+    AllInactive:= false
+else if Hedgehog^.CurAmmoType in [amShotgun, amDEagle, amSniperRifle] then
     HHGear^.Message:= 0;
 
 if ((Ammoz[CurrentHedgehog^.CurAmmoType].Ammo.Propz and ammoprop_Utility) <> 0) and isInMultiShoot then
@@ -1010,6 +1023,8 @@
 
 if (TurnTimeLeft = 0) or (HHGear^.Damage > 0) then
     begin
+    if (Hedgehog^.CurAmmoType = amKnife) then
+       LoadHedgehogHat(Hedgehog^, Hedgehog^.Hat);
     if TagTurnTimeLeft = 0 then
         TagTurnTimeLeft:= TurnTimeLeft;
     TurnTimeLeft:= 0;
@@ -1086,7 +1101,6 @@
     exit
     end;
 
-if not isInMultiShoot then
     HedgehogChAngle(HHGear);
 
 if (HHGear^.State and gstMoving) <> 0 then
@@ -1122,7 +1136,7 @@
     exit
     end;
 
-    if (not isInMultiShoot) and (Hedgehog^.Gear <> nil) then
+    if not(isInMultiShoot and (Hedgehog^.CurAmmoType in [amShotgun, amDEagle, amSniperRifle])) and (Hedgehog^.Gear <> nil) then
         begin
         if GHStepTicks > 0 then
             dec(GHStepTicks);
@@ -1194,7 +1208,7 @@
     if Gear^.Timer = 0 then
         begin
         Gear^.State:= Gear^.State and (not (gstWait or gstLoser or gstWinner or gstAttacked or gstNotKickable or gstHHChooseTarget));
-        Gear^.Active:= false;
+        if Gear^.Hedgehog^.Effects[heFrozen] = 0 then Gear^.Active:= false;
         AddGearCI(Gear);
         exit
         end
@@ -1204,40 +1218,25 @@
 AllInactive:= false
 end;
 
-////////////////////////////////////////////////////////////////////////////////
-procedure doStepHedgehog(Gear: PGear);
+procedure CheckIce(Gear: PGear); inline;
 (*
 var x,y,tx,ty: LongInt;
     tdX, tdY, slope: hwFloat;
     land: Word; *)
 var slope: hwFloat;
 begin
-CheckSum:= CheckSum xor Gear^.Hedgehog^.BotLevel;
-if (Gear^.Message and gmDestroy) <> 0 then
-    begin
-    DeleteGear(Gear);
-    exit
-    end;
-
-if (Gear^.State and gstHHDriven) = 0 then
-    doStepHedgehogFree(Gear)
-else
-    begin
-    with Gear^.Hedgehog^ do
-        if Team^.hasGone then
-            TeamGoneEffect(Team^)
-        else
-            doStepHedgehogDriven(Gear)
-    end;
-if (Gear^.Message and (gmAllStoppable or gmLJump or gmHJump) = 0)
-and (Gear^.State and (gstHHJumping or gstHHHJump or gstAttacking) = 0)
-and (not Gear^.dY.isNegative) and (GameTicks mod (100*LongWOrd(hwRound(cMaxWindSpeed*2/cGravity))) = 0)
-and (TestCollisionYwithGear(Gear, 1) and lfIce <> 0) then
-    begin
-    slope:= CalcSlopeBelowGear(Gear);
-    Gear^.dX:=Gear^.dX+slope*_0_07;
-    if slope.QWordValue <> 0 then
-        Gear^.State:= Gear^.State or gstMoving;
+    if (Gear^.Message and (gmAllStoppable or gmLJump or gmHJump) = 0)
+    and (Gear^.State and (gstHHJumping or gstHHHJump or gstAttacking) = 0)
+    and ((Gear^.Hedgehog = nil) or ((Gear^.Hedgehog^.Effects[heFrozen] = 0) or (Gear^.Hedgehog^.Effects[heFrozen] > 255)))
+    and (not Gear^.dY.isNegative) and (TurnTimeLeft > 0) and (TestCollisionYwithGear(Gear, 1) and lfIce <> 0) then
+        begin
+        slope:= CalcSlopeBelowGear(Gear);
+        if slope.QWordValue > 730144440 then // ignore mild slopes
+            begin
+            Gear^.dX:=Gear^.dX+slope*cGravity*_256;
+            Gear^.State:= Gear^.State or gstMoving
+            end
+        end;
 (*
     x:= hwRound(Gear^.X);
     y:= hwRound(Gear^.Y);
@@ -1252,7 +1251,39 @@
     AddVisualGear(x + hwRound(_40 * slope), y - hwRound(_40 * slope), vgtSmokeTrace);
     AddVisualGear(x - hwRound(_50 * slope), y + hwRound(_50 * slope), vgtSmokeTrace);
     AddVisualGear(x + hwRound(_50 * slope), y - hwRound(_50 * slope), vgtSmokeTrace); *)
-    end
+end;
+
+////////////////////////////////////////////////////////////////////////////////
+procedure doStepHedgehog(Gear: PGear);
+begin
+CheckSum:= CheckSum xor Gear^.Hedgehog^.BotLevel;
+if (Gear^.Message and gmDestroy) <> 0 then
+    begin
+    DeleteGear(Gear);
+    exit
+    end;
+if GameTicks mod 100 = 0 then CheckIce(Gear);
+(*
+if Gear^.Hedgehog^.Effects[heFrozen] > 0 then
+    begin
+    if (Gear^.Hedgehog^.Effects[heFrozen] > 256) and (CurrentHedgehog^.Team^.Clan <> Gear^.Hedgehog^.Team^.Clan) then
+        dec(Gear^.Hedgehog^.Effects[heFrozen])
+    else if GameTicks mod 10 = 0 then
+        dec(Gear^.Hedgehog^.Effects[heFrozen])
+    end;
+*)
+if (GameTicks mod 10 = 0) and (Gear^.Hedgehog^.Effects[heFrozen] > 0) and (Gear^.Hedgehog^.Effects[heFrozen] < 256) then
+    dec(Gear^.Hedgehog^.Effects[heFrozen]);
+if (Gear^.State and gstHHDriven) = 0 then
+    doStepHedgehogFree(Gear)
+else
+    begin
+    with Gear^.Hedgehog^ do
+        if Team^.hasGone then
+            TeamGoneEffect(Team^)
+        else
+            doStepHedgehogDriven(Gear)
+    end;
 end;
 
 end.
--- a/hedgewars/uGearsList.pas	Wed Feb 20 02:21:58 2013 +0100
+++ b/hedgewars/uGearsList.pas	Tue Apr 02 21:00:57 2013 +0200
@@ -187,7 +187,7 @@
     begin
     gear^.Hedgehog:= CurrentHedgehog;
     if (CurrentHedgehog^.Gear <> nil) and (hwRound(CurrentHedgehog^.Gear^.X) = X) and (hwRound(CurrentHedgehog^.Gear^.Y) = Y) then
-        gear^.CollisionMask:= $FF7F
+        gear^.CollisionMask:= lfNotCurrentMask
     end;
 
 if (Ammoz[Gear^.AmmoType].Ammo.Propz and ammoprop_NeedTarget <> 0) then
@@ -329,7 +329,7 @@
                 end;
        gtKnife: begin
                 gear^.Density:= _4;
-                gear^.Radius:= 16
+                gear^.Radius:= 7
                 end;
         gtCase: begin
                 gear^.ImpactSound:= sndGraveImpact;
@@ -550,7 +550,10 @@
                 gear^.Pos:= 1;
                 end;
 }
-      gtIceGun: gear^.Health:= 1000;
+      gtIceGun: begin
+                gear^.Health:= 1000;
+                gear^.Radius:= 8;
+                end;
 gtGenericFaller:begin
                 gear^.AdvBounce:= 1;
                 gear^.Radius:= 1;
--- a/hedgewars/uGearsRender.pas	Wed Feb 20 02:21:58 2013 +0100
+++ b/hedgewars/uGearsRender.pas	Tue Apr 02 21:00:57 2013 +0200
@@ -37,7 +37,17 @@
          end;
 procedure RenderGear(Gear: PGear; x, y: LongInt);
 
-var RopePoints: TRopePoints;
+var RopePoints: record
+                Count: Longword;
+                HookAngle: GLfloat;
+                ar: array[0..MAXROPEPOINTS] of record
+                                X, Y: hwFloat;
+                                dLen: hwFloat;
+                                b: boolean;
+                                sx, sy, sb: boolean;
+                                end;
+                rounded: array[0..MAXROPEPOINTS + 2] of TVertex2f;
+                end;
 
 implementation
 uses uRender, uUtils, uVariables, uAmmos, Math, uVisualGears;
@@ -215,6 +225,8 @@
     defaultPos, HatVisible: boolean;
     HH: PHedgehog;
     CurWeapon: PAmmo;
+    iceOffset:Longint;
+    r:TSDL_Rect;
 begin
     HH:= Gear^.Hedgehog;
     if HH^.Unplaced then
@@ -243,6 +255,30 @@
     defaultPos:= true;
     HatVisible:= false;
 
+    if HH^.Effects[heFrozen] > 0 then
+        if HH^.Effects[heFrozen] < 150000 then
+            begin
+            DrawHedgehog(sx, sy,
+                    sign,
+                    0,
+                    0,
+                    0);
+            defaultPos:= false;
+            if HH^.Effects[heFrozen] < 256 then
+                 HatVisible:= true
+            else HatVisible:= false
+            end
+        else
+            begin
+            DrawHedgehog(sx, sy,
+                    sign,
+                    2,
+                    4,
+                    0);
+            defaultPos:= false;
+            HatVisible:= false
+            end;
+
 
     if HH^.Effects[hePoisoned] <> 0 then
         begin
@@ -251,6 +287,7 @@
         Tint($FF, $FF, $FF, $FF)
         end;
 
+
     if ((Gear^.State and gstWinner) <> 0) and
     ((CurAmmoGear = nil) or (CurAmmoGear^.Kind <> gtPickHammer)) then
         begin
@@ -542,7 +579,7 @@
                         DrawTextureCentered(sx, sy - 40, CurAmmoGear^.Tex)
                     end;
                 gtIceGun:
-                    begin DrawSpriteRotated(sprHandBallgun, hx, hy, sign, aangle);
+                    begin DrawSpriteRotated(sprIceGun, hx, hy, sign, aangle);
                     if CurAmmoGear^.Tex <> nil then
                         DrawTextureCentered(sx, sy - 40, CurAmmoGear^.Tex)
                     end;
@@ -673,7 +710,7 @@
                 amBee: DrawSpriteRotatedF(sprHandBee, hx, hy, (RealTicks div 125) mod 4, sign, aangle);
                 amFlamethrower: DrawSpriteRotatedF(sprHandFlamethrower, hx, hy, (RealTicks div 125) mod 4, sign, aangle);
                 amLandGun: DrawSpriteRotated(sprHandBallgun, hx, hy, sign, aangle);
-                amIceGun: DrawSpriteRotated(sprHandBallgun, hx, hy, sign, aangle);
+                amIceGun: DrawSpriteRotated(sprIceGun, hx, hy, sign, aangle);
                 amResurrector: DrawCircle(ox, oy, 98, 4, $F5, $DB, $35, $AA); // I'd rather not like to hardcode 100 here
             end;
 
@@ -736,7 +773,7 @@
 
     end else // not gstHHDriven
         begin
-        if (Gear^.Damage > 0)
+        if (Gear^.Damage > 0) and (HH^.Effects[heFrozen] = 0)
         and (hwSqr(Gear^.dX) + hwSqr(Gear^.dY) > _0_003) then
             begin
             DrawHedgehog(sx, sy,
@@ -921,6 +958,28 @@
         Tint($FF, $FF, $FF, max($40, round($FF * abs(1 - ((RealTicks div 2 + Gear^.uid * 491) mod 1500) / 750))));
         DrawSprite(sprInvulnerable, sx - 24, sy - 24, 0);
         end;
+
+    if HH^.Effects[heFrozen] < 150000 then
+        begin
+        if HH^.Effects[heFrozen] < 150000 then
+            Tint($FF, $FF, $FF, min(255,127+HH^.Effects[heFrozen] div 800));
+
+        iceOffset:= min(32, HH^.Effects[heFrozen] div 8);
+        r.x := 128;
+        r.y := 96 - iceOffset;
+        r.w := 32;
+        r.h := iceOffset;
+        if sign = -1 then
+        DrawTextureFromRectDir(sx + sign*2, sy+16-iceoffset, r.w, r.h, @r, HHTexture, sign)
+        else
+        DrawTextureFromRectDir(sx-16 + sign*2, sy+16-iceoffset, r.w, r.h, @r, HHTexture, sign);
+
+
+        if HH^.Effects[heFrozen] < 150000 then
+            Tint($FF, $FF, $FF, $FF);
+        end;
+
+
     if cVampiric and
     (CurrentHedgehog^.Gear <> nil) and
     (CurrentHedgehog^.Gear = Gear) then
@@ -1232,8 +1291,13 @@
                                 begin
                                 i:= random(100)+100;
                                 if Gear^.Target.X <> NoPointX then
-                                    DrawLine(Gear^.Target.X, Gear^.Target.Y, hwRound(HHGear^.X), hwRound(HHGear^.Y), 4.0, i, i, $FF, $40)
-                                else DrawLine(hwRound(HHGear^.X), hwRound(HHGear^.Y), hwRound(Gear^.X), hwRound(Gear^.Y), 4.0, i, i, $FF, $40);
+                                    begin
+                                    DrawLine(Gear^.Target.X, Gear^.Target.Y, hwRound(HHGear^.X), hwRound(HHGear^.Y), 4.0, i, i, $FF, $40);
+                                    end
+                                else
+                                    begin
+                                    DrawLine(hwRound(HHGear^.X), hwRound(HHGear^.Y), hwRound(Gear^.X), hwRound(Gear^.Y), 4.0, i, i, $FF, $40);
+                                    end;
                                 end
                           end
                       end;
--- a/hedgewars/uGearsUtils.pas	Wed Feb 20 02:21:58 2013 +0100
+++ b/hedgewars/uGearsUtils.pas	Tue Apr 02 21:00:57 2013 +0200
@@ -137,7 +137,7 @@
                                 Gear^.Active:= true;
                                 if Gear^.Kind <> gtFlame then FollowGear:= Gear
                                 end;
-                            if ((Mask and EXPLPoisoned) <> 0) and (Gear^.Kind = gtHedgehog) and (not Gear^.Invulnerable) then
+                            if ((Mask and EXPLPoisoned) <> 0) and (Gear^.Kind = gtHedgehog) and (not Gear^.Invulnerable) and ((Gear^.State and gstHHDeath) = 0) then
                                 Gear^.Hedgehog^.Effects[hePoisoned] := 1;
                             end;
 
@@ -182,7 +182,7 @@
 i:= _1;
 if (CurrentHedgehog <> nil) and CurrentHedgehog^.King then
     i:= _1_5;
-if (Gear^.Hedgehog <> nil) and (Gear^.Hedgehog^.King) then
+if (Gear^.Hedgehog <> nil) and (Gear^.Hedgehog^.King or (Gear^.Hedgehog^.Effects[heFrozen] > 0)) then
     ModifyDamage:= hwRound(_0_01 * cDamageModifier * dmg * i * cDamagePercent * _0_5)
 else
     ModifyDamage:= hwRound(_0_01 * cDamageModifier * dmg * i * cDamagePercent)
@@ -580,16 +580,16 @@
                 repeat
                     inc(y, 2);
                 until (y >= cWaterLine) or
-                        ((not ignoreOverlap) and (CountNonZeroz(x, y, Gear^.Radius - 1, 1, $FFFF) = 0)) or
-                        (ignoreOverlap and (CountNonZeroz(x, y, Gear^.Radius - 1, 1, $FF00) = 0));
+                        ((not ignoreOverlap) and (CountNonZeroz(x, y, Gear^.Radius - 1, 1, $FFFF) = 0)) or 
+                        (ignoreOverlap and (CountNonZeroz(x, y, Gear^.Radius - 1, 1, lfLandMask) = 0));
 
                 sy:= y;
 
                 repeat
                     inc(y);
                 until (y >= cWaterLine) or
-                        ((not ignoreOverlap) and (CountNonZeroz(x, y, Gear^.Radius - 1, 1, $FFFF) <> 0)) or
-                        (ignoreOverlap and (CountNonZeroz(x, y, Gear^.Radius - 1, 1, $FF00) <> 0));
+                        ((not ignoreOverlap) and (CountNonZeroz(x, y, Gear^.Radius - 1, 1, $FFFF) <> 0)) or 
+                        (ignoreOverlap and (CountNonZeroz(x, y, Gear^.Radius - 1, 1, lfLandMask) <> 0)); 
 
                 if (y - sy > Gear^.Radius * 2)
                     and (((Gear^.Kind = gtExplosives)
--- a/hedgewars/uIO.pas	Wed Feb 20 02:21:58 2013 +0100
+++ b/hedgewars/uIO.pas	Tue Apr 02 21:00:57 2013 +0200
@@ -30,7 +30,7 @@
 procedure SendIPCXY(cmd: char; X, Y: LongInt);
 procedure SendIPCRaw(p: pointer; len: Longword);
 procedure SendIPCAndWaitReply(s: shortstring);
-procedure SendKeepAliveMessage(Lag: Longword);
+procedure FlushMessages(Lag: Longword);
 procedure LoadRecordFromFile(fileName: shortstring);
 procedure SendStat(sit: TStatInfoType; s: shortstring);
 procedure IPCWaitPongEvent;
@@ -43,6 +43,7 @@
 
 const
     cSendEmptyPacketTime = 1000;
+    cSendBufferSize = 1024;
 
 type PCmd = ^TCmd;
      TCmd = packed record
@@ -63,7 +64,11 @@
     headcmd: PCmd;
     lastcmd: PCmd;
 
-    SendEmptyPacketTicks: LongWord;
+    flushDelayTicks: LongWord;
+    sendBuffer: record
+                buf: array[0..Pred(cSendBufferSize)] of byte;
+                count: Word;
+                end;
 
 function AddCmd(Time: Word; str: shortstring): PCmd;
 var command: PCmd;
@@ -140,7 +145,7 @@
      else
      loTicks:= SDLNet_Read16(@s[byte(s[0]) - 1]);
      AddCmd(loTicks, s);
-     AddFileLog('[IPC in] '+s[1]+' ticks '+IntToStr(lastcmd^.loTime));
+     AddFileLog('[IPC in] ' + sanitizeCharForLog(s[1]) + ' ticks ' + IntToStr(lastcmd^.loTime));
      end
 end;
 
@@ -215,20 +220,45 @@
 SendIPCRaw(@buf[0], length(buf) + 1)
 end;
 
+function isSyncedCommand(c: char): boolean;
+begin
+    isSyncedCommand:= (c in ['+', '#', 'L', 'l', 'R', 'r', 'U', 'u', 'D', 'd', 'Z', 'z', 'A', 'a', 'S', 'j', 'J', ',', 'c', 'N', 'p', 'P', 'w', 't', '1', '2', '3', '4', '5']) or ((c >= #128) and (c <= char(128 + cMaxSlotIndex)))
+end;
+
+procedure flushBuffer();
+begin
+    if IPCSock <> nil then
+        begin
+        SDLNet_TCP_Send(IPCSock, @sendBuffer.buf, sendBuffer.count);
+        flushDelayTicks:= 0;
+        sendBuffer.count:= 0
+        end
+end;
 
 procedure SendIPC(s: shortstring);
 begin
 if IPCSock <> nil then
     begin
-    SendEmptyPacketTicks:= 0;
-    if s[0]>#251 then
+    if s[0] > #251 then
         s[0]:= #251;
 
     SDLNet_Write16(GameTicks, @s[Succ(byte(s[0]))]);
-    AddFileLog('[IPC out] '+ s[1]);
+    
+    AddFileLog('[IPC out] '+ sanitizeCharForLog(s[1]));
     inc(s[0], 2);
-       SDLNet_TCP_Send(IPCSock, @s, Succ(byte(s[0])));
-       //log('SendIPC');
+    
+    if isSyncedCommand(s[1]) then
+        begin
+        if sendBuffer.count + byte(s[0]) >= cSendBufferSize then
+            flushBuffer();
+            
+        Move(s, sendBuffer.buf[sendBuffer.count], byte(s[0]) + 1);
+        inc(sendBuffer.count, byte(s[0]) + 1);
+        
+        if (s[1] = 'N') or (s[1] = '#') then
+            flushBuffer();
+        end else
+        SDLNet_TCP_Send(IPCSock, @s, Succ(byte(s[0])))
     end
 end;
 
@@ -266,11 +296,16 @@
 IPCWaitPongEvent
 end;
 
-procedure SendKeepAliveMessage(Lag: Longword);
+procedure FlushMessages(Lag: Longword);
 begin
-inc(SendEmptyPacketTicks, Lag);
-if (SendEmptyPacketTicks >= cSendEmptyPacketTime) then
-    SendIPC(_S'+')
+inc(flushDelayTicks, Lag);
+if (flushDelayTicks >= cSendEmptyPacketTime) then
+    begin
+    if sendBuffer.count = 0 then
+        SendIPC(_S'+');
+        
+     flushBuffer()    
+    end
 end;
 
 procedure NetGetNextCmd;
@@ -436,8 +471,8 @@
     SocketString:= '';
 
     hiTicks:= 0;
-    SendEmptyPacketTicks:= 0;
-
+    flushDelayTicks:= 0;
+    sendBuffer.count:= 0;
 end;
 
 procedure freeModule;
--- a/hedgewars/uInputHandler.pas	Wed Feb 20 02:21:58 2013 +0100
+++ b/hedgewars/uInputHandler.pas	Tue Apr 02 21:00:57 2013 +0200
@@ -58,7 +58,6 @@
     RCTRL  = $4000;
 
 var tkbd: array[0..cKbdMaxIndex] of boolean;
-    quitKeyCode, closeKeyCode: Byte;
     KeyNames: array [0..cKeyMaxIndex] of string[15];
     CurrentBinds: TBinds;
     ControllerNumControllers: Integer;
@@ -138,7 +137,7 @@
           and (CurrentHedgehog^.BotLevel = 0);
 
 // ctrl/cmd + q to close engine and frontend
-if(KeyDown and (code = quitKeyCode)) then
+if(KeyDown and (code = SDLK_q)) then
     begin
 {$IFDEF DARWIN}
     if tkbd[KeyNameToCode('left_meta')] or tkbd[KeyNameToCode('right_meta')] then
@@ -149,7 +148,7 @@
     end;
 
 // ctrl/cmd + w to close engine
-if(KeyDown and (code = closeKeyCode)) then
+if(KeyDown and (code = SDLK_w)) then
     begin
 {$IFDEF DARWIN}
     // on OS X it this is expected behaviour
@@ -159,7 +158,7 @@
     if tkbd[KeyNameToCode('left_ctrl')] or tkbd[KeyNameToCode('right_ctrl')] then
         if ((CurrentBinds[KeyNameToCode('left_ctrl')] = '') or
             (CurrentBinds[KeyNameToCode('right_ctrl')] = '')) and
-            (CurrentBinds[closeKeyCode] = '') then
+            (CurrentBinds[SDLK_w] = '') then
 {$ENDIF}
         ParseCommand('forcequit', true);
     end;
@@ -242,8 +241,6 @@
         end;
     end;
 
-quitKeyCode:= KeyNameToCode(_S'q');
-closeKeyCode:= KeyNameToCode(_S'w');
 
 // get the size of keyboard array
 SDL_GetKeyState(@k);
--- a/hedgewars/uLandGraphics.pas	Wed Feb 20 02:21:58 2013 +0100
+++ b/hedgewars/uLandGraphics.pas	Tue Apr 02 21:00:57 2013 +0200
@@ -22,10 +22,14 @@
 interface
 uses uFloat, uConsts, uTypes;
 
+type
+    fillType = (nullPixel, backgroundPixel, ebcPixel, icePixel, setNotCurrentMask, changePixelSetNotCurrent, setCurrentHog, changePixelNotSetNotCurrent);
+
 type TRangeArray = array[0..31] of record
                                    Left, Right: LongInt;
                                    end;
      PRangeArray = ^TRangeArray;
+TLandCircleProcedure = procedure (landX, landY, pixelX, pixelY: Longint);
 
 function  addBgColor(OldColor, NewColor: LongWord): LongWord;
 function  SweepDirty: boolean;
@@ -36,17 +40,230 @@
 procedure DrawHLinesExplosions(ar: PRangeArray; Radius: LongInt; y, dY: LongInt; Count: Byte);
 procedure DrawTunnel(X, Y, dX, dY: hwFloat; ticks, HalfWidth: LongInt);
 procedure FillRoundInLand(X, Y, Radius: LongInt; Value: Longword);
+function FillRoundInLand(X, Y, Radius: LongInt; fill: fillType): LongWord;
 procedure ChangeRoundInLand(X, Y, Radius: LongInt; doSet, isCurrent: boolean);
 function  LandBackPixel(x, y: LongInt): LongWord;
 procedure DrawLine(X1, Y1, X2, Y2: LongInt; Color: Longword);
 procedure DrawThickLine(X1, Y1, X2, Y2, radius: LongInt; color: Longword);
 procedure DumpLandToLog(x, y, r: LongInt);
-
+procedure DrawIceBreak(x, y, iceRadius, iceHeight: Longint);
 function TryPlaceOnLand(cpX, cpY: LongInt; Obj: TSprite; Frame: LongInt; doPlace: boolean; indestructible: boolean): boolean;
 
 implementation
 uses SDLh, uLandTexture, uVariables, uUtils, uDebug;
 
+
+procedure calculatePixelsCoordinates(landX, landY: Longint; var pixelX, pixelY: Longint); inline;
+begin
+if (cReducedQuality and rqBlurryLand) = 0 then
+    begin
+    pixelX := landX;
+    pixelY := landY;
+    end
+else
+    begin
+    pixelX := LandX div 2;
+    pixelY := LandY div 2;
+    end;
+end;
+
+function drawPixelBG(landX, landY, pixelX, pixelY: Longint): Longword; inline;
+begin
+drawPixelBG := 0;
+if (Land[LandY, landX] and lfIndestructible) = 0 then
+    begin
+        if ((Land[landY, landX] and lfBasic) <> 0) and (((LandPixels[pixelY, pixelX] and AMask) shr AShift) = 255) and (not disableLandBack) then
+        begin
+            LandPixels[pixelY, pixelX]:= LandBackPixel(landX, landY);
+            inc(drawPixelBG);
+        end
+        else if ((Land[landY, landX] and lfObject) <> 0) or (((LandPixels[pixelY, pixelX] and AMask) shr AShift) < 255) then
+            LandPixels[pixelY, pixelX]:= 0
+    end;
+end;
+
+procedure drawPixelEBC(landX, landY, pixelX, pixelY: Longint); inline;
+begin
+if ((Land[landY, landX] and lfBasic) <> 0) or ((Land[landY, landX] and lfObject) <> 0) then
+    begin
+    LandPixels[pixelY, pixelX]:= ExplosionBorderColor;
+    Land[landY, landX]:= (Land[landY, landX] or lfDamaged) and not lfIce;
+    LandDirty[landY div 32, landX div 32]:= 1;
+    end;
+end;
+
+function isLandscapeEdge(weight:Longint):boolean; inline;
+begin
+result := (weight < 8) and (weight >= 2);
+end;
+
+function getPixelWeight(x, y:Longint): Longint;
+var
+    i, j:Longint;
+begin
+result := 0;
+for i := x - 1 to x + 1 do
+    for j := y - 1 to y + 1 do
+    begin
+    if (i < 0) or
+       (i > LAND_WIDTH - 1) or
+       (j < 0) or
+       (j > LAND_HEIGHT -1) then
+       begin
+       result := 9;
+       exit;
+       end;
+    if Land[j, i] and lfLandMask and not lfIce = 0 then
+       result := result + 1;
+    end;
+end;
+
+
+procedure fillPixelFromIceSprite(pixelX, pixelY:Longint); inline;
+var
+    iceSurface: PSDL_Surface;
+    icePixels: PLongwordArray;
+    w: LongWord;
+begin
+    // So. 3 parameters here. Ice colour, Ice opacity, and a bias on the greyscaled pixel towards lightness
+    iceSurface:= SpritesData[sprIceTexture].Surface;
+    icePixels := iceSurface^.pixels;
+    w:= LandPixels[pixelY, pixelX];
+    if w > 0 then
+        begin
+        w:= round(((w shr RShift and $FF) * RGB_LUMINANCE_RED +
+              (w shr BShift and $FF) * RGB_LUMINANCE_GREEN +
+              (w shr GShift and $FF) * RGB_LUMINANCE_BLUE));
+        if w < 128 then w:= w+128;
+        if w > 255 then w:= 255;
+        w:= (w shl RShift) or (w shl BShift) or (w shl GShift) or (LandPixels[pixelY, pixelX] and AMask);
+        LandPixels[pixelY, pixelX]:= addBgColor(w, IceColor);
+        LandPixels[pixelY, pixelX]:= addBgColor(LandPixels[pixelY, pixelX], icePixels^[iceSurface^.w * (pixelY mod iceSurface^.h) + (pixelX mod iceSurface^.w)])
+        end
+    else
+        begin
+        LandPixels[pixelY, pixelX]:= IceColor and not AMask or $E8 shl AShift;
+        LandPixels[pixelY, pixelX]:= addBgColor(LandPixels[pixelY, pixelX], icePixels^[iceSurface^.w * (pixelY mod iceSurface^.h) + (pixelX mod iceSurface^.w)]);
+        // silly workaround to avoid having to make background erasure a tadb it smarter about sea ice
+        if LandPixels[pixelY, pixelX] and AMask shr AShift = 255 then
+            LandPixels[pixelY, pixelX]:= LandPixels[pixelY, pixelX] and not AMask or 254 shl AShift;
+        end;
+end;
+
+
+procedure DrawPixelIce(landX, landY, pixelX, pixelY: Longint); inline;
+begin
+if ((Land[landY, landX] and lfIce) <> 0) then exit;
+if isLandscapeEdge(getPixelWeight(landX, landY)) then
+    begin
+    if (LandPixels[pixelY, pixelX] and AMask < 255) and (LandPixels[pixelY, pixelX] and AMask > 0) then
+        LandPixels[pixelY, pixelX] := (IceEdgeColor and not AMask) or (LandPixels[pixelY, pixelX] and AMask)
+    else if (LandPixels[pixelY, pixelX] and AMask < 255) or (Land[landY, landX] > 255) then
+        LandPixels[pixelY, pixelX] := IceEdgeColor
+    end
+else if Land[landY, landX] > 255 then
+    begin
+        fillPixelFromIceSprite(pixelX, pixelY);
+    end;
+if Land[landY, landX] > 255 then Land[landY, landX] := Land[landY, landX] or lfIce and not lfDamaged;
+end;
+
+
+function FillLandCircleLine(y, fromPix, toPix: LongInt; fill : fillType): Longword;
+var px, py, i: LongInt;
+begin
+//get rid of compiler warning
+    px := 0;
+    py := 0;
+    FillLandCircleLine := 0;
+    case fill of
+    backgroundPixel:
+    for i:= fromPix to toPix do
+        begin
+        calculatePixelsCoordinates(i, y, px, py);
+        inc(FillLandCircleLine, drawPixelBG(i, y, px, py));
+        end;
+    ebcPixel:
+    for i:= fromPix to toPix do
+        begin
+        calculatePixelsCoordinates(i, y, px, py);
+        drawPixelEBC(i, y, px, py);
+        end;
+    nullPixel:
+    for i:= fromPix to toPix do
+        begin
+        calculatePixelsCoordinates(i, y, px, py);
+        if ((Land[y, i] and lfIndestructible) = 0) and (not disableLandBack or (Land[y, i] > 255))  then
+            LandPixels[py, px]:= 0
+        end;
+    icePixel:
+    for i:= fromPix to toPix do
+        begin
+        calculatePixelsCoordinates(i, y, px, py);
+        DrawPixelIce(i, y, px, py);
+        end;
+    setNotCurrentMask:
+    for i:= fromPix to toPix do
+        begin
+        Land[y, i]:= Land[y, i] and lfNotCurrentMask;
+        end;
+    changePixelSetNotCurrent:
+    for i:= fromPix to toPix do
+        begin
+        if Land[y, i] and lfObjMask > 0 then
+            Land[y, i]:= (Land[y, i] and lfNotObjMask) or ((Land[y, i] and lfObjMask) - 1);
+        end;
+    setCurrentHog:
+    for i:= fromPix to toPix do
+        begin
+        Land[y, i]:= Land[y, i] or lfCurrentHog
+        end;
+    changePixelNotSetNotCurrent:
+    for i:= fromPix to toPix do
+        begin
+        if Land[y, i] and lfObjMask < lfObjMask then
+            Land[y, i]:= (Land[y, i] and lfNotObjMask) or ((Land[y, i] and lfObjMask) + 1)
+        end;
+    end;
+end;
+
+function FillLandCircleSegment(x, y, dx, dy: LongInt; fill : fillType): Longword; inline;
+begin
+    FillLandCircleSegment := 0;
+if ((y + dy) and LAND_HEIGHT_MASK) = 0 then
+    inc(FillLandCircleSegment, FillLandCircleLine(y + dy, Max(x - dx, 0), Min(x + dx, LAND_WIDTH - 1), fill));
+if ((y - dy) and LAND_HEIGHT_MASK) = 0 then
+    inc(FillLandCircleSegment, FillLandCircleLine(y - dy, Max(x - dx, 0), Min(x + dx, LAND_WIDTH - 1), fill));
+if ((y + dx) and LAND_HEIGHT_MASK) = 0 then
+    inc(FillLandCircleSegment, FillLandCircleLine(y + dx, Max(x - dy, 0), Min(x + dy, LAND_WIDTH - 1), fill));
+if ((y - dx) and LAND_HEIGHT_MASK) = 0 then
+    inc(FillLandCircleSegment, FillLandCircleLine(y - dx, Max(x - dy, 0), Min(x + dy, LAND_WIDTH - 1), fill));
+end;
+
+function FillRoundInLand(X, Y, Radius: LongInt; fill: fillType): Longword; inline;
+var dx, dy, d: LongInt;
+begin
+dx:= 0;
+dy:= Radius;
+d:= 3 - 2 * Radius;
+FillRoundInLand := 0;
+while (dx < dy) do
+    begin
+    inc(FillRoundInLand, FillLandCircleSegment(x, y, dx, dy, fill));
+    if (d < 0) then
+        d:= d + 4 * dx + 6
+    else
+        begin
+        d:= d + 4 * (dx - dy) + 10;
+        dec(dy)
+        end;
+    inc(dx)
+    end;
+if (dx = dy) then
+    inc (FillRoundInLand, FillLandCircleSegment(x, y, dx, dy, fill));
+end;
+
+
 function addBgColor(OldColor, NewColor: LongWord): LongWord;
 // Factor ranges from 0 to 100% NewColor
 var
@@ -99,65 +316,6 @@
             Land[y - dx, i]:= Value;
 end;
 
-procedure ChangeCircleLines(x, y, dx, dy: LongInt; doSet, isCurrent: boolean);
-var i: LongInt;
-begin
-if not doSet then
-    begin
-    if ((y + dy) and LAND_HEIGHT_MASK) = 0 then
-        for i:= Max(x - dx, 0) to Min(x + dx, LAND_WIDTH - 1) do
-            if isCurrent then
-                Land[y + dy, i]:= Land[y + dy, i] and $FF7F
-            else if Land[y + dy, i] and $007F > 0 then
-                Land[y + dy, i]:= (Land[y + dy, i] and $FF80) or ((Land[y + dy, i] and $7F) - 1);
-    if ((y - dy) and LAND_HEIGHT_MASK) = 0 then
-        for i:= Max(x - dx, 0) to Min(x + dx, LAND_WIDTH - 1) do
-            if isCurrent then
-                Land[y - dy, i]:= Land[y - dy, i] and $FF7F
-            else if Land[y - dy, i] and $007F > 0 then
-                Land[y - dy, i]:= (Land[y - dy, i] and $FF80) or ((Land[y - dy, i] and $7F) - 1);
-    if ((y + dx) and LAND_HEIGHT_MASK) = 0 then
-        for i:= Max(x - dy, 0) to Min(x + dy, LAND_WIDTH - 1) do
-            if isCurrent then
-                Land[y + dx, i]:= Land[y + dx, i] and $FF7F
-            else if Land[y + dx, i] and $007F > 0 then
-                Land[y + dx, i]:= (Land[y + dx, i] and $FF80) or ((Land[y + dx, i] and $7F) - 1);
-    if ((y - dx) and LAND_HEIGHT_MASK) = 0 then
-        for i:= Max(x - dy, 0) to Min(x + dy, LAND_WIDTH - 1) do
-            if isCurrent then
-                Land[y - dx, i]:= Land[y - dx, i] and $FF7F
-            else if Land[y - dx, i] and $007F > 0 then
-                Land[y - dx, i]:= (Land[y - dx, i] and $FF80) or ((Land[y - dx, i] and $7F) - 1)
-    end
-else
-    begin
-    if ((y + dy) and LAND_HEIGHT_MASK) = 0 then
-        for i:= Max(x - dx, 0) to Min(x + dx, LAND_WIDTH - 1) do
-            if isCurrent then
-                Land[y + dy, i]:= Land[y + dy, i] or $80
-            else if Land[y + dy, i] and $007F < 127 then
-                Land[y + dy, i]:= (Land[y + dy, i] and $FF80) or ((Land[y + dy, i] and $7F) + 1);
-    if ((y - dy) and LAND_HEIGHT_MASK) = 0 then
-        for i:= Max(x - dx, 0) to Min(x + dx, LAND_WIDTH - 1) do
-            if isCurrent then
-                Land[y - dy, i]:= Land[y - dy, i] or $80
-            else if Land[y - dy, i] and $007F < 127 then
-                Land[y - dy, i]:= (Land[y - dy, i] and $FF80) or ((Land[y - dy, i] and $7F) + 1);
-    if ((y + dx) and LAND_HEIGHT_MASK) = 0 then
-        for i:= Max(x - dy, 0) to Min(x + dy, LAND_WIDTH - 1) do
-            if isCurrent then
-                Land[y + dx, i]:= Land[y + dx, i] or $80
-            else if Land[y + dx, i] and $007F < 127 then
-                Land[y + dx, i]:= (Land[y + dx, i] and $FF80) or ((Land[y + dx, i] and $7F) + 1);
-    if ((y - dx) and LAND_HEIGHT_MASK) = 0 then
-        for i:= Max(x - dy, 0) to Min(x + dy, LAND_WIDTH - 1) do
-            if isCurrent then
-                Land[y - dx, i]:= Land[y - dx, i] or $80
-            else if Land[y - dx, i] and $007F < 127 then
-                Land[y - dx, i]:= (Land[y - dx, i] and $FF80) or ((Land[y - dx, i] and $7F) + 1)
-    end
-end;
-
 procedure FillRoundInLand(X, Y, Radius: LongInt; Value: Longword);
 var dx, dy, d: LongInt;
 begin
@@ -181,307 +339,54 @@
 end;
 
 procedure ChangeRoundInLand(X, Y, Radius: LongInt; doSet, isCurrent: boolean);
-var dx, dy, d: LongInt;
 begin
-dx:= 0;
-dy:= Radius;
-d:= 3 - 2 * Radius;
-while (dx < dy) do
-    begin
-    ChangeCircleLines(x, y, dx, dy, doSet, isCurrent);
-    if (d < 0) then
-        d:= d + 4 * dx + 6
-    else
-        begin
-        d:= d + 4 * (dx - dy) + 10;
-        dec(dy)
-        end;
-    inc(dx)
-    end;
-if (dx = dy) then
-    ChangeCircleLines(x, y, dx, dy, doSet, isCurrent)
-end;
-
-procedure FillLandCircleLines0(x, y, dx, dy: LongInt);
-var i, t: LongInt;
-begin
-t:= y + dy;
-if (t and LAND_HEIGHT_MASK) = 0 then
-    for i:= Max(x - dx, 0) to Min(x + dx, LAND_WIDTH - 1) do
-        if ((Land[t, i] and lfIndestructible) = 0) and ((not disableLandBack) or (Land[t, i] > 255))  then
-            if (cReducedQuality and rqBlurryLand) = 0 then
-                LandPixels[t, i]:= 0
-            else
-                LandPixels[t div 2, i div 2]:= 0;
-
-t:= y - dy;
-if (t and LAND_HEIGHT_MASK) = 0 then
-    for i:= Max(x - dx, 0) to Min(x + dx, LAND_WIDTH - 1) do
-        if ((Land[t, i] and lfIndestructible) = 0) and ((not disableLandBack) or (Land[t, i] > 255))  then
-            if (cReducedQuality and rqBlurryLand) = 0 then
-                LandPixels[t, i]:= 0
-            else
-                LandPixels[t div 2, i div 2]:= 0;
-
-t:= y + dx;
-if (t and LAND_HEIGHT_MASK) = 0 then
-    for i:= Max(x - dy, 0) to Min(x + dy, LAND_WIDTH - 1) do
-        if ((Land[t, i] and lfIndestructible) = 0) and ((not disableLandBack) or (Land[t, i] > 255))  then
-            if (cReducedQuality and rqBlurryLand) = 0 then
-                LandPixels[t, i]:= 0
-            else
-                LandPixels[t div 2, i div 2]:= 0;
-
-t:= y - dx;
-if (t and LAND_HEIGHT_MASK) = 0 then
-    for i:= Max(x - dy, 0) to Min(x + dy, LAND_WIDTH - 1) do
-        if ((Land[t, i] and lfIndestructible) = 0) and ((not disableLandBack) or (Land[t, i] > 255))  then
-            if (cReducedQuality and rqBlurryLand) = 0 then
-                LandPixels[t, i]:= 0
-            else
-                LandPixels[t div 2, i div 2]:= 0;
-
+if not doSet and isCurrent then
+    FillRoundInLand(X, Y, Radius, setNotCurrentMask)
+else if not doSet and not IsCurrent then
+    FillRoundInLand(X, Y, Radius, changePixelSetNotCurrent)
+else if doSet and IsCurrent then
+    FillRoundInLand(X, Y, Radius, setCurrentHog)
+else if doSet and not IsCurrent then
+    FillRoundInLand(X, Y, Radius, changePixelNotSetNotCurrent);
 end;
 
-function FillLandCircleLinesBG(x, y, dx, dy: LongInt): Longword;
-var i, t, by, bx: LongInt;
-    cnt: Longword;
+procedure DrawIceBreak(x, y, iceRadius, iceHeight: Longint);
+var
+    i, j: integer;
+    landRect: TSDL_Rect;
 begin
-cnt:= 0;
-t:= y + dy;
-if (t and LAND_HEIGHT_MASK) = 0 then
-    for i:= Max(x - dx, 0) to Min(x + dx, LAND_WIDTH - 1) do
-        if (Land[t, i] and lfIndestructible) = 0 then
-            begin
-            if (cReducedQuality and rqBlurryLand) = 0 then
-                begin
-                by:= t; bx:= i;
-                end
-            else
-                begin
-                by:= t div 2; bx:= i div 2;
-                end;
-            if ((Land[t, i] and lfBasic) <> 0) and (((LandPixels[by,bx] and AMask) shr AShift) = 255) and (not disableLandBack) then
-                begin
-                inc(cnt);
-                LandPixels[by, bx]:= LandBackPixel(i, t)
-                end
-            else if ((Land[t, i] and lfObject) <> 0) or (((LandPixels[by,bx] and AMask) shr AShift) < 255) then
-                LandPixels[by, bx]:= 0
-            end;
-
-t:= y - dy;
-if (t and LAND_HEIGHT_MASK) = 0 then
-    for i:= Max(x - dx, 0) to Min(x + dx, LAND_WIDTH - 1) do
-        if (Land[t, i] and lfIndestructible) = 0 then
-            begin
-            if (cReducedQuality and rqBlurryLand) = 0 then
-                begin
-                by:= t; bx:= i;
-                end
-            else
-                begin
-                by:= t div 2; bx:= i div 2;
-                end;
-            if ((Land[t, i] and lfBasic) <> 0) and (((LandPixels[by,bx] and AMask) shr AShift) = 255) and (not disableLandBack) then
-                begin
-                inc(cnt);
-                LandPixels[by, bx]:= LandBackPixel(i, t)
-                end
-            else if ((Land[t, i] and lfObject) <> 0) or (((LandPixels[by,bx] and AMask) shr AShift) < 255) then
-                LandPixels[by, bx]:= 0
-            end;
-
-t:= y + dx;
-if (t and LAND_HEIGHT_MASK) = 0 then
-    for i:= Max(x - dy, 0) to Min(x + dy, LAND_WIDTH - 1) do
-        if (Land[t, i] and lfIndestructible) = 0 then
-            begin
-            if (cReducedQuality and rqBlurryLand) = 0 then
-                begin
-                by:= t; bx:= i;
-                end
-            else
-                begin
-                by:= t div 2; bx:= i div 2;
-                end;
-            if ((Land[t, i] and lfBasic) <> 0) and (((LandPixels[by,bx] and AMask) shr AShift) = 255) and (not disableLandBack) then
-                begin
-                inc(cnt);
-                LandPixels[by, bx]:= LandBackPixel(i, t)
-                end
-            else if ((Land[t, i] and lfObject) <> 0) or (((LandPixels[by,bx] and AMask) shr AShift) < 255) then
-                LandPixels[by, bx]:= 0
-            end;
-t:= y - dx;
-if (t and LAND_HEIGHT_MASK) = 0 then
-    for i:= Max(x - dy, 0) to Min(x + dy, LAND_WIDTH - 1) do
-        if (Land[t, i] and lfIndestructible) = 0 then
+for i := min(max(x - iceRadius, 0), LAND_WIDTH - 1) to min(max(x + iceRadius, 0), LAND_WIDTH - 1) do
+    begin
+    for j := min(max(y, 0), LAND_HEIGHT - 1) to min(max(y + iceHeight, 0), LAND_HEIGHT - 1) do
+        begin
+        if Land[j, i] = 0 then
             begin
-            if (cReducedQuality and rqBlurryLand) = 0 then
-                begin
-                by:= t; bx:= i;
-                end
-            else
-                begin
-                by:= t div 2; bx:= i div 2;
-                end;
-            if ((Land[t, i] and lfBasic) <> 0) and (((LandPixels[by,bx] and AMask) shr AShift) = 255) and (not disableLandBack) then
-                begin
-                inc(cnt);
-                LandPixels[by, bx]:= LandBackPixel(i, t)
-                end
-            else if ((Land[t, i] and lfObject) <> 0) or (((LandPixels[by,bx] and AMask) shr AShift) < 255) then
-                LandPixels[by, bx]:= 0
-            end;
-FillLandCircleLinesBG:= cnt;
-end;
-
-procedure FillLandCircleLinesEBC(x, y, dx, dy: LongInt);
-var i, t: LongInt;
-begin
-t:= y + dy;
-if (t and LAND_HEIGHT_MASK) = 0 then
-    for i:= Max(x - dx, 0) to Min(x + dx, LAND_WIDTH - 1) do
-        if ((Land[t, i] and lfBasic) <> 0) or ((Land[t, i] and lfObject) <> 0) then
-            begin
-            if (cReducedQuality and rqBlurryLand) = 0 then
-                LandPixels[t, i]:= ExplosionBorderColor
-            else
-                LandPixels[t div 2, i div 2]:= ExplosionBorderColor;
-
-            Land[t, i]:= Land[t, i] or lfDamaged;
-            //Despeckle(i, t);
-            LandDirty[t div 32, i div 32]:= 1;
+            Land[j, i] := lfIce;
+            fillPixelFromIceSprite(i, j);
             end;
-
-t:= y - dy;
-if (t and LAND_HEIGHT_MASK) = 0 then
-    for i:= Max(x - dx, 0) to Min(x + dx, LAND_WIDTH - 1) do
-        if ((Land[t, i] and lfBasic) <> 0) or ((Land[t, i] and lfObject) <> 0) then
-            begin
-            if (cReducedQuality and rqBlurryLand) = 0 then
-                LandPixels[t, i]:= ExplosionBorderColor
-            else
-                LandPixels[t div 2, i div 2]:= ExplosionBorderColor;
-            Land[t, i]:= Land[t, i] or lfDamaged;
-            //Despeckle(i, t);
-            LandDirty[t div 32, i div 32]:= 1;
-            end;
-
-t:= y + dx;
-if (t and LAND_HEIGHT_MASK) = 0 then
-    for i:= Max(x - dy, 0) to Min(x + dy, LAND_WIDTH - 1) do
-        if ((Land[t, i] and lfBasic) <> 0) or ((Land[t, i] and lfObject) <> 0) then
-            begin
-            if (cReducedQuality and rqBlurryLand) = 0 then
-                LandPixels[t, i]:= ExplosionBorderColor
-            else
-               LandPixels[t div 2, i div 2]:= ExplosionBorderColor;
-
-            Land[t, i]:= Land[t, i] or lfDamaged;
-            //Despeckle(i, t);
-            LandDirty[t div 32, i div 32]:= 1;
-            end;
-
-t:= y - dx;
-if (t and LAND_HEIGHT_MASK) = 0 then
-    for i:= Max(x - dy, 0) to Min(x + dy, LAND_WIDTH - 1) do
-        if ((Land[t, i] and lfBasic) <> 0) or ((Land[t, i] and lfObject) <> 0) then
-            begin
-            if (cReducedQuality and rqBlurryLand) = 0 then
-                LandPixels[t, i]:= ExplosionBorderColor
-            else
-                LandPixels[t div 2, i div 2]:= ExplosionBorderColor;
-
-            Land[t, i]:= Land[t, i] or lfDamaged;
-            //Despeckle(i, y - dy);
-            LandDirty[t div 32, i div 32]:= 1;
-            end;
+        end;
+    end;
+landRect.x := min(max(x - iceRadius, 0), LAND_WIDTH - 1);
+landRect.y := min(max(y, 0), LAND_HEIGHT - 1);
+landRect.w := min(2*iceRadius, LAND_WIDTH - landRect.x - 1);
+landRect.h := min(iceHeight, LAND_HEIGHT - landRect.y - 1);
+UpdateLandTexture(landRect.x, landRect.w, landRect.y, landRect.h, true);
 end;
 
 function DrawExplosion(X, Y, Radius: LongInt): Longword;
-var dx, dy, ty, tx, d: LongInt;
-    cnt: Longword;
+var
+    tx, ty, dx, dy: Longint;
 begin
-
-// draw background land texture
-    begin
-    cnt:= 0;
-    dx:= 0;
-    dy:= Radius;
-    d:= 3 - 2 * Radius;
-
-    while (dx < dy) do
-        begin
-        inc(cnt, FillLandCircleLinesBG(x, y, dx, dy));
-        if (d < 0) then
-            d:= d + 4 * dx + 6
-        else
-            begin
-            d:= d + 4 * (dx - dy) + 10;
-            dec(dy)
-            end;
-        inc(dx)
-        end;
-    if (dx = dy) then
-        inc(cnt, FillLandCircleLinesBG(x, y, dx, dy));
-    end;
-
-// draw a hole in land
-if Radius > 20 then
-    begin
-    dx:= 0;
-    dy:= Radius - 15;
-    d:= 3 - 2 * dy;
-
-    while (dx < dy) do
-        begin
-        FillLandCircleLines0(x, y, dx, dy);
-        if (d < 0) then
-            d:= d + 4 * dx + 6
-        else
-            begin
-            d:= d + 4 * (dx - dy) + 10;
-            dec(dy)
-            end;
-        inc(dx)
-        end;
-    if (dx = dy) then
-        FillLandCircleLines0(x, y, dx, dy);
-    end;
-
-  // FillRoundInLand after erasing land pixels to allow Land 0 check for mask.png to function
+    DrawExplosion := FillRoundInLand(x, y, Radius, backgroundPixel);
+    if Radius > 20 then
+        FillRoundInLand(x, y, Radius - 15, nullPixel);
     FillRoundInLand(X, Y, Radius, 0);
-
-// draw explosion border
-    begin
-    inc(Radius, 4);
-    dx:= 0;
-    dy:= Radius;
-    d:= 3 - 2 * Radius;
-    while (dx < dy) do
-        begin
-        FillLandCircleLinesEBC(x, y, dx, dy);
-        if (d < 0) then
-            d:= d + 4 * dx + 6
-        else
-            begin
-            d:= d + 4 * (dx - dy) + 10;
-            dec(dy)
-            end;
-        inc(dx)
-        end;
-    if (dx = dy) then
-        FillLandCircleLinesEBC(x, y, dx, dy);
-    end;
-
-tx:= Max(X - Radius - 1, 0);
-dx:= Min(X + Radius + 1, LAND_WIDTH) - tx;
-ty:= Max(Y - Radius - 1, 0);
-dy:= Min(Y + Radius + 1, LAND_HEIGHT) - ty;
-UpdateLandTexture(tx, dx, ty, dy, false);
-DrawExplosion:= cnt
+    FillRoundInLand(x, y, Radius + 4, ebcPixel);
+    tx:= Max(X - Radius - 5, 0);
+    dx:= Min(X + Radius + 5, LAND_WIDTH) - tx;
+    ty:= Max(Y - Radius - 5, 0);
+    dy:= Min(Y + Radius + 5, LAND_HEIGHT) - ty;
+    UpdateLandTexture(tx, dx, ty, dy, false);
 end;
 
 procedure DrawHLinesExplosions(ar: PRangeArray; Radius: LongInt; y, dY: LongInt; Count: Byte);
@@ -525,7 +430,7 @@
                 else
                     LandPixels[ty div 2, tx div 2]:= ExplosionBorderColor;
 
-                Land[ty, tx]:= Land[ty, tx] or lfDamaged;
+                Land[ty, tx]:= (Land[ty, tx] or lfDamaged) and not lfIce;
                 LandDirty[ty div 32, tx div 32]:= 1;
                 end;
     inc(y, dY)
@@ -535,6 +440,33 @@
 UpdateLandTexture(0, LAND_WIDTH, 0, LAND_HEIGHT, false)
 end;
 
+
+
+procedure DrawExplosionBorder(X, Y, dx, dy:hwFloat;  despeckle : Boolean);
+var
+    t, tx, ty :Longint;
+begin
+for t:= 0 to 7 do
+    begin
+    X:= X + dX;
+    Y:= Y + dY;
+    tx:= hwRound(X);
+    ty:= hwRound(Y);
+    if ((ty and LAND_HEIGHT_MASK) = 0) and ((tx and LAND_WIDTH_MASK) = 0) and (((Land[ty, tx] and lfBasic) <> 0)
+    or ((Land[ty, tx] and lfObject) <> 0)) then
+        begin
+        Land[ty, tx]:= (Land[ty, tx] or lfDamaged) and not lfIce;
+        if despeckle then
+            LandDirty[ty div 32, tx div 32]:= 1;
+        if (cReducedQuality and rqBlurryLand) = 0 then
+            LandPixels[ty, tx]:= ExplosionBorderColor
+        else
+            LandPixels[ty div 2, tx div 2]:= ExplosionBorderColor
+        end
+    end;
+end;
+
+
 //
 //  - (dX, dY) - direction, vector of length = 0.5
 //
@@ -567,6 +499,7 @@
     and ((tx and LAND_WIDTH_MASK) = 0)
     and (((Land[ty, tx] and lfBasic) <> 0) or ((Land[ty, tx] and lfObject) <> 0)) then
         begin
+        Land[ty, tx]:= Land[ty, tx] and not lfIce;
         if despeckle then
             begin
             Land[ty, tx]:= Land[ty, tx] or lfDamaged;
@@ -586,24 +519,7 @@
     begin
     X:= nx - dX8;
     Y:= ny - dY8;
-    for t:= 0 to 7 do
-        begin
-        X:= X + dX;
-        Y:= Y + dY;
-        tx:= hwRound(X);
-        ty:= hwRound(Y);
-        if ((ty and LAND_HEIGHT_MASK) = 0) and ((tx and LAND_WIDTH_MASK) = 0) and (((Land[ty, tx] and lfBasic) <> 0)
-        or ((Land[ty, tx] and lfObject) <> 0)) then
-            begin
-            Land[ty, tx]:= Land[ty, tx] or lfDamaged;
-            if despeckle then
-                LandDirty[ty div 32, tx div 32]:= 1;
-            if (cReducedQuality and rqBlurryLand) = 0 then
-                LandPixels[ty, tx]:= ExplosionBorderColor
-            else
-                LandPixels[ty div 2, tx div 2]:= ExplosionBorderColor
-            end
-        end;
+    DrawExplosionBorder(X, Y, dx, dy, despeckle);
     X:= nx;
     Y:= ny;
     for t:= 0 to ticks do
@@ -629,24 +545,7 @@
             Land[ty, tx]:= 0;
             end
         end;
-    for t:= 0 to 7 do
-    begin
-    X:= X + dX;
-    Y:= Y + dY;
-    tx:= hwRound(X);
-    ty:= hwRound(Y);
-    if ((ty and LAND_HEIGHT_MASK) = 0) and ((tx and LAND_WIDTH_MASK) = 0) and (((Land[ty, tx] and lfBasic) <> 0)
-    or ((Land[ty, tx] and lfObject) <> 0)) then
-        begin
-        Land[ty, tx]:= Land[ty, tx] or lfDamaged;
-        if despeckle then
-            LandDirty[ty div 32, tx div 32]:= 1;
-        if (cReducedQuality and rqBlurryLand) = 0 then
-            LandPixels[ty, tx]:= ExplosionBorderColor
-        else
-            LandPixels[ty div 2, tx div 2]:= ExplosionBorderColor
-        end
-        end;
+    DrawExplosionBorder(X, Y, dx, dy, despeckle);
     nx:= nx - dY;
     ny:= ny + dX;
     end;
@@ -664,7 +563,7 @@
     if ((ty and LAND_HEIGHT_MASK) = 0) and ((tx and LAND_WIDTH_MASK) = 0) and (((Land[ty, tx] and lfBasic) <> 0)
     or ((Land[ty, tx] and lfObject) <> 0)) then
         begin
-        Land[ty, tx]:= Land[ty, tx] or lfDamaged;
+        Land[ty, tx]:= (Land[ty, tx] or lfDamaged) and not lfIce;
         if despeckle then
             LandDirty[ty div 32, tx div 32]:= 1;
         if (cReducedQuality and rqBlurryLand) = 0 then
@@ -790,7 +689,7 @@
         yy:= Y div 2;
     end;
 
-    pixelsweep:= ((Land[Y, X] and $FF00) = 0) and (LandPixels[yy, xx] <> 0);
+    pixelsweep:= (Land[Y, X] <= lfAllObjMask) and (LandPixels[yy, xx] <> 0);
     if (((Land[Y, X] and lfDamaged) <> 0) and ((Land[Y, X] and lfIndestructible) = 0)) or pixelsweep then
     begin
         c:= 0;
@@ -891,7 +790,7 @@
         end
     end
 else if ((cReducedQuality and rqBlurryLand) = 0) and (LandPixels[Y, X] and AMask = 255)
-and ((Land[Y, X] and (lfDamaged or lfBasic) = lfBasic) or (Land[Y, X] and (lfDamaged or lfBasic) = lfBasic))
+and (Land[Y, X] and (lfDamaged or lfBasic) = lfBasic)
 and (Y > LongInt(topY) + 1) and (Y < LAND_HEIGHT-2) and (X > LongInt(leftX) + 1) and (X < LongInt(rightX) - 1) then
     begin
     if ((((Land[y, x-1] and lfDamaged) <> 0) and (((Land[y+1,x] and lfDamaged) <> 0)) or ((Land[y-1,x] and lfDamaged) <> 0))
--- a/hedgewars/uLandObjects.pas	Wed Feb 20 02:21:58 2013 +0100
+++ b/hedgewars/uLandObjects.pas	Tue Apr 02 21:00:57 2013 +0200
@@ -107,7 +107,7 @@
                 if LandPixels[(cpY + y) div 2, (cpX + x) div 2] = 0 then
                     LandPixels[(cpY + y) div 2, (cpX + x) div 2]:= p^[x];
 
-            if ((Land[cpY + y, cpX + x] and $FF00) = 0) and ((p^[x] and AMask) <> 0) then
+            if (Land[cpY + y, cpX + x] <= lfAllObjMask) and ((p^[x] and AMask) <> 0) then
                 begin
                 Land[cpY + y, cpX + x]:= lfObject;
                 Land[cpY + y, cpX + x]:= Land[cpY + y, cpX + x] or extraFlags
--- a/hedgewars/uPhysFSLayer.pas	Wed Feb 20 02:21:58 2013 +0100
+++ b/hedgewars/uPhysFSLayer.pas	Tue Apr 02 21:00:57 2013 +0200
@@ -3,21 +3,17 @@
 interface
 uses SDLh, LuaPas;
 
-{$IFDEF ANDROID}
+const PhysfsLibName = {$IFDEF PHYSFS_INTERNAL}'libhw_physfs'{$ELSE}'libphysfs'{$ENDIF};
+const PhyslayerLibName = 'libphyslayer';
+
+{$IFNDEF WIN32}
     {$linklib physfs}
-{$ELSE}
+    {$linklib physlayer}
     {$IFDEF DARWIN}
-        {$LINKFRAMEWORK IOKit}
+        {$linkframework IOKit}
     {$ENDIF}
 {$ENDIF}
 
-const
-{$IFDEF WIN32}
-    PhysfsLibName = 'libphysfs';
-{$ELSE}
-    PhysfsLibName = 'physfs';
-{$ENDIF}
-
 procedure initModule;
 procedure freeModule;
 
@@ -36,8 +32,8 @@
 
 function pfsExists(fname: shortstring): boolean;
 
-function  physfsReader(L: Plua_State; f: PFSFile; sz: Psize_t) : PChar; cdecl; external PhysfsLibName;
-procedure physfsReaderSetBuffer(buf: pointer); cdecl; external PhysfsLibName;
+function  physfsReader(L: Plua_State; f: PFSFile; sz: Psize_t) : PChar; cdecl; external PhyslayerLibName;
+procedure physfsReaderSetBuffer(buf: pointer); cdecl; external PhyslayerLibName;
 
 {$IFNDEF PAS2C}
 //apparently pas2c doesn't render the functions below if it finds 'implementation' first
@@ -45,10 +41,10 @@
 uses uUtils, uVariables, sysutils;
 {$ENDIF}
 
-function PHYSFS_init(argv: PChar): LongInt; cdecl; external PhysfsLibName;
-function PHYSFS_deinit: LongInt; cdecl; external PhysfsLibName;
-function PHYSFSRWOPS_openRead(fname: PChar): PSDL_RWops; cdecl; external PhysfsLibName;
-function PHYSFSRWOPS_openWrite(fname: PChar): PSDL_RWops; cdecl; external PhysfsLibName;
+function PHYSFS_init(argv0: PChar) : LongInt; cdecl; external PhysfsLibName;
+function PHYSFS_deinit() : LongInt; cdecl; external PhysfsLibName;
+function PHYSFSRWOPS_openRead(fname: PChar): PSDL_RWops; cdecl ; external PhyslayerLibName;
+function PHYSFSRWOPS_openWrite(fname: PChar): PSDL_RWops; cdecl; external PhyslayerLibName;
 
 function PHYSFS_mount(newDir, mountPoint: PChar; appendToPath: LongBool) : LongInt; cdecl; external PhysfsLibName;
 function PHYSFS_openRead(fname: PChar): PFSFile; cdecl; external PhysfsLibName;
@@ -57,7 +53,7 @@
 function PHYSFS_close(f: PFSFile): LongBool; cdecl; external PhysfsLibName;
 function PHYSFS_exists(fname: PChar): LongBool; cdecl; external PhysfsLibName;
 
-procedure hedgewarsMountPackages; cdecl; external PhysfsLibName;
+procedure hedgewarsMountPackages(); cdecl; external PhyslayerLibName;
 
 {$IFDEF PAS2C}
 implementation
@@ -157,9 +153,9 @@
     i:= PHYSFS_init(Str2PChar(cPhysfsId));
     AddFileLog('[PhysFS] init: ' + inttostr(i));
 
-    i:= PHYSFS_mount(Str2PChar(PathPrefix), nil, true);
+    i:= PHYSFS_mount(Str2PChar(PathPrefix), nil, false);
     AddFileLog('[PhysFS] mount ' + PathPrefix + ': ' + inttostr(i));
-    i:= PHYSFS_mount(Str2PChar(UserPathPrefix + '/Data'), nil, true);
+    i:= PHYSFS_mount(Str2PChar(UserPathPrefix + '/Data'), nil, false);
     AddFileLog('[PhysFS] mount ' + UserPathPrefix + '/Data: ' + inttostr(i));
 
     hedgewarsMountPackages;
--- a/hedgewars/uRender.pas	Wed Feb 20 02:21:58 2013 +0100
+++ b/hedgewars/uRender.pas	Tue Apr 02 21:00:57 2013 +0200
@@ -27,15 +27,16 @@
 
 procedure DrawSprite            (Sprite: TSprite; X, Y, Frame: LongInt);
 procedure DrawSprite            (Sprite: TSprite; X, Y, FrameX, FrameY: LongInt);
-procedure DrawSpriteFromRect    (Sprite: TSprite; r: TSDL_Rect; X, Y, Height, Position: LongInt);
+procedure DrawSpriteFromRect    (Sprite: TSprite; r: TSDL_Rect; X, Y, Height, Position: LongInt); inline;
 procedure DrawSpriteClipped     (Sprite: TSprite; X, Y, TopY, RightX, BottomY, LeftX: LongInt);
 procedure DrawSpriteRotated     (Sprite: TSprite; X, Y, Dir: LongInt; Angle: real);
 procedure DrawSpriteRotatedF    (Sprite: TSprite; X, Y, Frame, Dir: LongInt; Angle: real);
 
 procedure DrawTexture           (X, Y: LongInt; Texture: PTexture); inline;
 procedure DrawTexture           (X, Y: LongInt; Texture: PTexture; Scale: GLfloat);
-procedure DrawTextureFromRect   (X, Y: LongInt; r: PSDL_Rect; SourceTexture: PTexture);
-procedure DrawTextureFromRect   (X, Y, W, H: LongInt; r: PSDL_Rect; SourceTexture: PTexture);
+procedure DrawTextureFromRect   (X, Y: LongInt; r: PSDL_Rect; SourceTexture: PTexture); inline;
+procedure DrawTextureFromRect   (X, Y, W, H: LongInt; r: PSDL_Rect; SourceTexture: PTexture); inline;
+procedure DrawTextureFromRectDir(X, Y, W, H: LongInt; r: PSDL_Rect; SourceTexture: PTexture; Dir: LongInt);
 procedure DrawTextureCentered   (X, Top: LongInt; Source: PTexture);
 procedure DrawTextureF          (Texture: PTexture; Scale: GLfloat; X, Y, Frame, Dir, w, h: LongInt);
 procedure DrawTextureRotated    (Texture: PTexture; hw, hh, X, Y, Dir: LongInt; Angle: real);
@@ -63,19 +64,24 @@
 
 var LastTint: LongWord = 0;
 
-procedure DrawSpriteFromRect(Sprite: TSprite; r: TSDL_Rect; X, Y, Height, Position: LongInt);
+procedure DrawSpriteFromRect(Sprite: TSprite; r: TSDL_Rect; X, Y, Height, Position: LongInt); inline;
 begin
 r.y:= r.y + Height * Position;
 r.h:= Height;
 DrawTextureFromRect(X, Y, @r, SpritesData[Sprite].Texture)
 end;
 
-procedure DrawTextureFromRect(X, Y: LongInt; r: PSDL_Rect; SourceTexture: PTexture);
+procedure DrawTextureFromRect(X, Y: LongInt; r: PSDL_Rect; SourceTexture: PTexture); inline;
 begin
-DrawTextureFromRect(X, Y, r^.w, r^.h, r, SourceTexture)
+DrawTextureFromRectDir(X, Y, r^.w, r^.h, r, SourceTexture, 1)
 end;
-{
-procedure DrawTextureFromRect(X, Y, W, H: LongInt; r: PSDL_Rect; SourceTexture: PTexture);
+
+procedure DrawTextureFromRect(X, Y, W, H: LongInt; r: PSDL_Rect; SourceTexture: PTexture); inline;
+begin
+DrawTextureFromRectDir(X, Y, W, H, r, SourceTexture, 1)
+end;
+
+procedure DrawTextureFromRectDir(X, Y, W, H: LongInt; r: PSDL_Rect; SourceTexture: PTexture; Dir: LongInt);
 var rr: TSDL_Rect;
     _l, _r, _t, _b: real;
     VertexBuffer, TextureBuffer: array [0..3] of TVertex2f;
@@ -101,14 +107,28 @@
 
 glBindTexture(GL_TEXTURE_2D, SourceTexture^.id);
 
-VertexBuffer[0].X:= X;
-VertexBuffer[0].Y:= Y;
-VertexBuffer[1].X:= rr.w + X;
-VertexBuffer[1].Y:= Y;
-VertexBuffer[2].X:= rr.w + X;
-VertexBuffer[2].Y:= rr.h + Y;
-VertexBuffer[3].X:= X;
-VertexBuffer[3].Y:= rr.h + Y;
+if Dir < 0 then
+    begin
+    VertexBuffer[0].X:= X + rr.w/2;
+    VertexBuffer[0].Y:= Y;
+    VertexBuffer[1].X:= X - rr.w/2;
+    VertexBuffer[1].Y:= Y;
+    VertexBuffer[2].X:= X - rr.w/2;
+    VertexBuffer[2].Y:= rr.h + Y;
+    VertexBuffer[3].X:= X + rr.w/2;
+    VertexBuffer[3].Y:= rr.h + Y;
+    end
+else
+    begin
+    VertexBuffer[0].X:= X;
+    VertexBuffer[0].Y:= Y;
+    VertexBuffer[1].X:= rr.w + X;
+    VertexBuffer[1].Y:= Y;
+    VertexBuffer[2].X:= rr.w + X;
+    VertexBuffer[2].Y:= rr.h + Y;
+    VertexBuffer[3].X:= X;
+    VertexBuffer[3].Y:= rr.h + Y;
+    end;
 
 TextureBuffer[0].X:= _l;
 TextureBuffer[0].Y:= _t;
@@ -123,9 +143,9 @@
 glTexCoordPointer(2, GL_FLOAT, 0, @TextureBuffer[0]);
 glDrawArrays(GL_TRIANGLE_FAN, 0, High(VertexBuffer) - Low(VertexBuffer) + 1);
 end;
-}
+
 
-procedure DrawTextureFromRect(X, Y, W, H: LongInt; r: PSDL_Rect; SourceTexture: PTexture);
+procedure DrawTextureFromRectDir(X, Y, W, H: LongInt; r: PSDL_Rect; SourceTexture: PTexture);
 var
     rr: TSDL_Rect;
     VertexBuffer, TextureBuffer: array [0..3] of TVertex2f;
--- a/hedgewars/uScript.pas	Wed Feb 20 02:21:58 2013 +0100
+++ b/hedgewars/uScript.pas	Tue Apr 02 21:00:57 2013 +0200
@@ -1280,10 +1280,8 @@
 
 function lc_endgame(L : Plua_State) : LongInt; Cdecl;
 begin
-   {$IFNDEF PAS2C}
     L:= L; // avoid compiler hint
-   {$ENDIF}
-    GameState:= gsExit;
+    AddGear(0, 0, gtATFinishGame, 0, _0, _0, 3000);
     lc_endgame:= 0
 end;
 
@@ -2048,7 +2046,6 @@
 begin
 ScriptSetInteger('TurnTimeLeft', TurnTimeLeft);
 ScriptSetInteger('GameTime', GameTicks);
-ScriptSetInteger('RealTime', RealTicks);
 ScriptSetInteger('TotalRounds', TotalRounds);
 ScriptSetInteger('WaterLine', cWaterLine);
 if GameTicks = 0 then
--- a/hedgewars/uSound.pas	Wed Feb 20 02:21:58 2013 +0100
+++ b/hedgewars/uSound.pas	Tue Apr 02 21:00:57 2013 +0200
@@ -105,12 +105,13 @@
 function  AskForVoicepack(name: shortstring): Pointer;
 
 
+var Volume: LongInt;
+    SoundTimerTicks: Longword;
 implementation
 uses uVariables, uConsole, uCommands, uDebug, uPhysFSLayer;
 
 const chanTPU = 32;
-var Volume: LongInt;
-    cInitVolume: LongInt;
+var cInitVolume: LongInt;
     previousVolume: LongInt; // cached volume value
     lastChan: array [TSound] of LongInt;
     voicepacks: array[0..cMaxTeams] of TVoicepack;
@@ -262,14 +263,14 @@
         begin
         locName:= name+'_'+cLocale;
         path:= cPathz[ptVoices] + '/' + locName;
-        if DirectoryExists(path) then
+        if pfsExists(path) then
             name:= locName
         else
             if Length(cLocale) > 3 then
                 begin
                 locName:= name+'_'+Copy(cLocale,1,2);
                 path:= cPathz[ptVoices] + '/' + locName;
-                if DirectoryExists(path) then
+                if pfsExists(path) then
                     name:= locName
                 end
         end;
@@ -712,6 +713,7 @@
     isAudioMuted:= false;
     isSEBackup:= isSoundEnabled;
     Volume:= 0;
+    SoundTimerTicks:= 0;
     defVoicepack:= AskForVoicepack('Default');
 
     for i:= Low(TSound) to High(TSound) do
--- a/hedgewars/uStats.pas	Wed Feb 20 02:21:58 2013 +0100
+++ b/hedgewars/uStats.pas	Tue Apr 02 21:00:57 2013 +0200
@@ -40,6 +40,7 @@
 
 var DamageClan  : Longword = 0;
     DamageTotal : Longword = 0;
+    DamageTurn  : Longword = 0;
     KillsClan   : LongWord = 0;
     Kills       : LongWord = 0;
     KillsTotal  : LongWord = 0;
@@ -82,7 +83,8 @@
         inc(KillsClan);
     end;
 
-inc(DamageTotal, Damage)
+inc(DamageTotal, Damage);
+inc(DamageTurn, Damage)
 end;
 
 procedure Skipped;
@@ -112,7 +114,7 @@
         end
 
     else if DamageClan <> 0 then
-        if DamageTotal > DamageClan then
+        if DamageTurn > DamageClan then
             if random(2) = 0 then
                 AddVoice(sndNutter, CurrentTeam^.voicepack)
             else
@@ -170,6 +172,7 @@
 Kills:= 0;
 KillsClan:= 0;
 DamageClan:= 0;
+DamageTurn:= 0;
 AmmoUsedCount:= 0;
 AmmoDamagingUsed:= false;
 isTurnSkipped:= false
--- a/hedgewars/uTeams.pas	Wed Feb 20 02:21:58 2013 +0100
+++ b/hedgewars/uTeams.pas	Tue Apr 02 21:00:57 2013 +0200
@@ -110,7 +110,7 @@
 end;
 
 procedure SwitchHedgehog;
-var c: LongWord;
+var c, i, j: LongWord;
     PrevHH, PrevTeam : LongWord;
 begin
 TargetPoint.X:= NoPointX;
@@ -173,7 +173,15 @@
     if c = ClansCount then
         begin
         if not PlacingHogs then
+            begin
             inc(TotalRounds);
+            for i:= 0 to Pred(TeamsCount) do
+                with TeamsArray[i]^ do
+                    for j:= 0 to Pred(HedgehogsNumber) do
+                        with Hedgehogs[j] do
+                            if Effects[heFrozen] > 255 then
+                                Effects[heFrozen]:= max(255,Effects[heFrozen]-50000)
+            end;
         c:= 0
         end;
 
@@ -188,11 +196,11 @@
                 PrevHH:= CurrHedgehog mod HedgehogsNumber; // prevent infinite loop when CurrHedgehog = 7, but HedgehogsNumber < 8 (team is destroyed before its first turn)
                 repeat
                     CurrHedgehog:= Succ(CurrHedgehog) mod HedgehogsNumber;
-                until (Hedgehogs[CurrHedgehog].Gear <> nil) or (CurrHedgehog = PrevHH)
+                until ((Hedgehogs[CurrHedgehog].Gear <> nil) and (Hedgehogs[CurrHedgehog].Effects[heFrozen] = 0)) or (CurrHedgehog = PrevHH)
                 end
         until (CurrentTeam^.Hedgehogs[CurrentTeam^.CurrHedgehog].Gear <> nil) or (PrevTeam = CurrTeam) or ((CurrTeam = TagTeamIndex) and ((GameFlags and gfTagTeam) <> 0));
         end
-until (CurrentTeam^.Hedgehogs[CurrentTeam^.CurrHedgehog].Gear <> nil);
+until (CurrentTeam^.Hedgehogs[CurrentTeam^.CurrHedgehog].Gear <> nil) and (CurrentTeam^.Hedgehogs[CurrentTeam^.CurrHedgehog].Effects[heFrozen] = 0);
 
 SwitchCurrentHedgehog(@(CurrentTeam^.Hedgehogs[CurrentTeam^.CurrHedgehog]));
 {$IFDEF USE_TOUCH_INTERFACE}
--- a/hedgewars/uTypes.pas	Wed Feb 20 02:21:58 2013 +0100
+++ b/hedgewars/uTypes.pas	Tue Apr 02 21:00:57 2013 +0200
@@ -87,7 +87,7 @@
             sprHandResurrector, sprCross, sprAirDrill, sprNapalmBomb,
             sprBulletHit, sprSnowball, sprHandSnowball, sprSnow,
             sprSDFlake, sprSDWater, sprSDCloud, sprSDSplash, sprSDDroplet, sprTardis,
-            sprSlider, sprBotlevels, sprHandKnife, sprKnife, sprStar
+            sprSlider, sprBotlevels, sprHandKnife, sprKnife, sprStar, sprIceTexture, sprIceGun, sprFrozenHog
             );
 
     // Gears that interact with other Gears and/or Land
@@ -223,46 +223,56 @@
     PClan     = ^TClan;
 
     TGearStepProcedure = procedure (Gear: PGear);
+// So, you're here looking for variables you can (ab)use to store some gear state?
+// Not all members of this structure are created equal. Comments below are my take on what can be used for what in the gear structure.
     TGear = record
-            NextGear, PrevGear: PGear;
-            Active: Boolean;
-            AdvBounce: Longword;
-            Invulnerable: Boolean;
-            RenderTimer: Boolean;
-            AmmoType : TAmmoType;
-            State : Longword;
-            X : hwFloat;
+// Don't ever override these.
+            NextGear, PrevGear: PGear;  // Linked list
+            Z: Longword;                // Z index. For rendering. Sets order in list
+            Active: Boolean;            // Is gear Active (running step code)
+            Kind: TGearType;
+            doStep: TGearStepProcedure; // Code the gear is running
+            AmmoType : TAmmoType;       // Ammo type associated with this kind of gear
+            RenderTimer: Boolean;       // Will visually display Timer if true
+            Target : TPoint;            // Gear target. Will render in uGearsRender unless a special case is added
+            AIHints: LongWord;          // hints for ai.
+            LastDamage: PHedgehog;      // Used to track damage source for stats
+            CollisionIndex: LongInt;    // Position in collision array
+            Message: LongWord;          // Game messages are stored here. See gm bitmasks in uConsts
+            uid: Longword;              // Lua use this to reference gears
+// Strongly recommended not to override these.  Will mess up generic operations like portaling
+            X : hwFloat;              // X/Y/dX/dY are position/velocity. People count on these having semi-normal values
             Y : hwFloat;
             dX: hwFloat;
             dY: hwFloat;
-            Target : TPoint;
-            Kind: TGearType;
-            Pos: Longword;
-            doStep: TGearStepProcedure;
-            Radius: LongInt;
-            Angle, Power : Longword;
-            DirAngle: real;
-            Timer : LongWord;
+            State : Longword;        // See gst bitmask values in uConsts
+            PortalCounter: LongWord; // Necessary to interrupt portal loops.  Not possible to avoid infinite loops without it.
+// Don't use these if you're using generic movement like doStepFallingGear and explosion shoves. Generally recommended not to use.
+            Radius: LongInt;     // Radius. If not using uCollisions, is usually used to indicate area of effect
+            CollisionMask: Word; // Masking off Land impact  FF7F for example ignores current hog and crates
+            AdvBounce: Longword; // Triggers 45° bounces. Is a counter to avoid edge cases
             Elasticity: hwFloat;
             Friction  : hwFloat;
-            Density   : hwFloat;
-            Message, MsgParam : Longword;
-            Hedgehog: PHedgehog;
+            Density   : hwFloat; // Density is kind of a mix of size and density. Impacts distance thrown, wind.
+            ImpactSound: TSound; // first sound, others have to be after it in the sounds def.
+            nImpactSounds: Word; // count of ImpactSounds.
+// Don't use these if you want to take damage normally, otherwise health/damage are commonly used for other purposes
+            Invulnerable: Boolean;
             Health, Damage, Karma: LongInt;
-            CollisionIndex: LongInt;
-            Tag: LongInt;
-            Tex: PTexture;
-            Z: Longword;
-            CollisionMask: Word;
-            LinkedGear: PGear;
-            FlightTime: Longword;
-            uid: Longword;
-            ImpactSound: TSound; // first sound, others have to be after it in the sounds def.
-            nImpactSounds: Word; // count of ImpactSounds
-            SoundChannel: LongInt;
-            PortalCounter: LongWord;  // Hopefully temporary, but avoids infinite portal loops in a guaranteed fashion.
-            AIHints: LongWord; // hints for ai. haha ^^^^^^ temporary, sure
-            LastDamage: PHedgehog;
+// DirAngle is a "real" - if you don't need it for rotation of sprite in uGearsRender, you can use it for any visual-only value
+            DirAngle: real;
+// These are frequently overridden to serve some other purpose
+            Pos: Longword;           // Commonly overridden.  Example use is posCase values in uConsts.
+            Angle, Power : Longword; // Used for hog aiming/firing.  Angle is rarely used as an Angle otherwise.
+            Timer : LongWord;        // Typically used for some sort of gear timer. Time to explosion, remaining fuel...
+            Tag: LongInt;            // Quite generic. Variety of uses.
+            FlightTime: Longword;    // Initially added for batting of hogs to determine homerun. Used for some firing delays
+            MsgParam: LongWord;      // Initially stored a set of messages. So usually gm values like Message. Frequently overriden
+// These are not used generically, but should probably be used for purpose intended. Definitely shouldn't override pointer type
+            Tex: PTexture;          // A texture created by the gear. Shouldn't use for anything but textures
+            LinkedGear: PGear;      // Used to track a related gear. Portal pairs for example.
+            Hedgehog: PHedgehog;    // set to CurrentHedgehog on gear creation
+            SoundChannel: LongInt;  // Used to track a sound the gear started
             end;
     TPGearArray = array of PGear;
     PGearArrayS = record
--- a/hedgewars/uUtils.pas	Wed Feb 20 02:21:58 2013 +0100
+++ b/hedgewars/uUtils.pas	Tue Apr 02 21:00:57 2013 +0200
@@ -87,6 +87,9 @@
 procedure AudioServicesPlaySystemSound(num: LongInt); cdecl; external;
 {$ENDIF}