Merge default to ios-develop ios-develop
authorantonc27 <antonc27@mail.ru>
Sun, 10 Jun 2018 19:12:26 +0200
branchios-develop
changeset 13413 ba39a1d396c0
parent 13411 6e8b807bda4b (current diff)
parent 13412 236cc4cf2448 (diff)
Merge default to ios-develop
QTfrontend/model/ammoSchemeModel.cpp
QTfrontend/model/ammoSchemeModel.h
QTfrontend/ui/dialog/upload_video.cpp
QTfrontend/ui/dialog/upload_video.h
cmake_modules/CheckHaskellModuleExists.cmake
share/hedgewars/Data/Graphics/Hats/Luigi.svg
share/hedgewars/Data/Graphics/Hats/Mario.svg
share/hedgewars/Data/Graphics/Hats/PrincessPeach.svg
share/hedgewars/Data/Graphics/Hats/Toad.svg
share/hedgewars/Data/Graphics/Hats/Wario.svg
share/hedgewars/Data/Graphics/Hats/pikachu.svg
share/hedgewars/Data/Missions/Campaign/01-Boot_Camp.lua
--- a/.hgignore	Sun Jun 10 18:56:51 2018 +0200
+++ b/.hgignore	Sun Jun 10 19:12:26 2018 +0200
@@ -5,8 +5,8 @@
 .git
 CMakeCache.txt
 CMakeFiles
-moc_*.cxx
-qrc_*.cxx
+moc_*.*
+qrc_*.*
 *.o
 *.a
 *.qm
@@ -26,7 +26,7 @@
 tools/cmake_uninstall.cmake
 install_manifest.txt
 .DS_Store
-*.swp
+*.sw?
 *.orig
 vittorio.*
 project_files/HedgewarsMobile/Data/
@@ -35,7 +35,7 @@
 misc/liblua/Xcode/build/
 misc/libphysfs/Xcode/build/
 misc/libphyslayer/Xcode/build/
-moc_*.cxx_parameters
+moc_*.cpp_parameters
 *.log
 *.cmd
 *.patch
--- a/.hgtags	Sun Jun 10 18:56:51 2018 +0200
+++ b/.hgtags	Sun Jun 10 19:12:26 2018 +0200
@@ -77,4 +77,14 @@
 0000000000000000000000000000000000000000 0.9.22-RC
 8cc070640fd1f4affb3c5dd5652b4d75a075c200 0.9.23-alpha
 8610462e3d336e112bffcd7c1530d271242216f1 0.9.23-release
+a4e7e62f2c0dcf67ff2181550085475b09455377 0.9.24-release
+3f2ee2f9b51d53f3de50c812aef87f3a681c0328 0.9.24.1-release
+3f2ee2f9b51d53f3de50c812aef87f3a681c0328 0.9.24.1-release
+0000000000000000000000000000000000000000 0.9.24.1-release
+0000000000000000000000000000000000000000 0.9.24.1-release
+ea98a45452e512f691c4866520849d69777398ef 0.9.24.1-release
+ea98a45452e512f691c4866520849d69777398ef 0.9.24.1-release
+0000000000000000000000000000000000000000 0.9.24.1-release
+0000000000000000000000000000000000000000 0.9.24.1-release
+afc089c39556bdd895892fa36ed47aaec83c3825 0.9.24.1-release
 195208deff1dd3e22d303d4a92c2ba14be3b6623 Hedgewars-iOS-2.1
--- a/.travis.yml	Sun Jun 10 18:56:51 2018 +0200
+++ b/.travis.yml	Sun Jun 10 19:12:26 2018 +0200
@@ -1,5 +1,5 @@
 sudo: required
-dist: trusty
+dist: xenial
 language: c
 sudo: true
 os:
@@ -52,7 +52,6 @@
 
 before_install: |
   if [ "$TRAVIS_OS_NAME" == "linux" ]; then
-    sudo add-apt-repository -y ppa:zoogie/sdl2-snapshots
     sudo apt-get update -qq
   elif [ "$TRAVIS_OS_NAME" == "osx" ]; then
     brew update --all
@@ -66,11 +65,9 @@
 
 install: |
   if [ "$TRAVIS_OS_NAME" == "linux" ]; then
-    sudo apt-get install -y debhelper cmake dpkg-dev libqt4-dev qt4-qmake libphysfs-dev libsdl2-dev libsdl2-ttf-dev libsdl2-mixer-dev libsdl2-image-dev libsdl2-net-dev bzip2 ghc libghc-mtl-dev libghc-parsec3-dev libghc-vector-dev libghc-zlib-dev libghc-random-dev libghc-stm-dev libghc-network-dev libghc-sandi-dev libghc-hslogger-dev libghc-utf8-string-dev libghc-sha-dev libghc-entropy-dev libghc-regex-tdfa-dev liblua5.1-0-dev imagemagick fpc fp-compiler fp-units-misc libpng-dev fp-units-gfx libavcodec-dev libavformat-dev libglew1.6-dev
+    sudo apt-get install -y debhelper cmake dpkg-dev qtbase5-dev qtbase5-private-dev qttools5-dev-tools qttools5-dev libphysfs-dev libsdl2-dev libsdl2-ttf-dev libsdl2-mixer-dev libsdl2-image-dev libsdl2-net-dev bzip2 ghc libghc-mtl-dev libghc-vector-dev libghc-zlib-dev libghc-random-dev libghc-network-dev libghc-sandi-dev libghc-hslogger-dev libghc-utf8-string-dev libghc-sha-dev libghc-entropy-dev libghc-regex-tdfa-dev liblua5.1-0-dev fpc fp-compiler fp-units-misc libpng-dev fp-units-gfx libavcodec-dev libavformat-dev libglew1.6-dev
   elif [ "$TRAVIS_OS_NAME" == "osx" ]; then
-    brew tap cartr/qt4
-    brew tap-pin cartr/qt4
-    brew install qt@4
+    brew install qt5
     brew install fpc glew physfs lua51 sdl2 sdl2_image sdl2_net sdl2_ttf ffmpeg ghc cabal-install
     brew install sdl2_mixer --with-libvorbis
     # use cabal install haskell deps, pas2c ones are covered by server
--- a/CMakeLists.txt	Sun Jun 10 18:56:51 2018 +0200
+++ b/CMakeLists.txt	Sun Jun 10 19:12:26 2018 +0200
@@ -80,8 +80,8 @@
 #versioning
 set(CPACK_PACKAGE_VERSION_MAJOR 0)
 set(CPACK_PACKAGE_VERSION_MINOR 9)
-set(CPACK_PACKAGE_VERSION_PATCH 23)
-set(HEDGEWARS_PROTO_VER 53)
+set(CPACK_PACKAGE_VERSION_PATCH 25)
+set(HEDGEWARS_PROTO_VER 56)
 set(HEDGEWARS_VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}")
 include(${CMAKE_MODULE_PATH}/revinfo.cmake)
 
--- a/CREDITS	Sun Jun 10 18:56:51 2018 +0200
+++ b/CREDITS	Sun Jun 10 19:12:26 2018 +0200
@@ -92,6 +92,7 @@
 = VOICES
 ==========
 - Stephen Alexander <ArmagonNo1@gmail.com>
+- mtg90pl <mtg90pl@gmail.com>: Default_pl, Russian_pl
 
 ==========
 = MISSIONS
@@ -130,6 +131,11 @@
 - Invulnerable sound: remix based on a sound by pepingrillin (CC-0)
      https://www.freesound.org/people/pepingrillin/sounds/252079/
 
+==================
+= OTHER GRAPHICS =
+==================
+- Custom ammo images for Continental supplies: KarBoy2314PL
+
 ======================
 = LICENSE REFERENCES =
 ======================
--- a/ChangeLog.txt	Sun Jun 10 18:56:51 2018 +0200
+++ b/ChangeLog.txt	Sun Jun 10 19:12:26 2018 +0200
@@ -1,5 +1,170 @@
 + features
 * bugfixes
+====================== 0.9.25 ======================
+Game:
+ * Fix extreme amounts of droplets when shooting with minigun into ocean world edge
+ * Fix hog being unable to walk after using sniper rifle without firing both shots
+
+Continental supplies:
+ + Continents are now selected before the game starts
+ + Continents give hog different start health
+ + Add Antarctica special: Upside-Down World (teleport to top of map)
+ + Major rewrite of ALL texts for better usability
+ + Add custom weapon tooltips
+ + Improve audiovisual effects
+ + Show message when hog receives new continent ammo
+ + Sabotaged hedgehogs also emit smoke when it's not their turn
+ + Can switch continent in reverse order with [Precise]+[Switch]
+ * Invulnerability now protects from sabotage damage
+ * Sabotage kills hog instantly when health reaches 0
+ * Reliably prevent using of Lonely Cries and baseball bat specials when usage not allowed
+ * Don't explode Anno 1302, Medicine and Bouncy Boomerang if drowning
+ * Don't play “Missed” and “Laugh” taunt when those don't make sense
+ * Fix retreat timer not turning red for some weapons
+
+Lua API:
+ + New call: Retreat(time [, respectGetAwayTimeFactor): Force current turn into retreating mode
+ + New call: GetAmmoTimer(gearUid, ammoType): Returns current set timer for given ammoType and hog gear in ms. Returns nil for non-timerable ammo
+ + New call: EnableSwitchHog(): Enable hog switching
+ + New parameter: SetAmmoTexts: 5th param. showExtra: Set to false to hide texts like “Not yet available”
+
+====================== 0.9.24.1 ====================
+ * Fix crash when portable portal device is fired at reduced graphics quality
+ * Fix possible crash when starting Hedgewars frontend in fullscreen mode
+
+====================== 0.9.24 ======================
+Game:
+ + New weapon: Minigun
+ + New game modifier: Enable switch hedgehog at start of turn
+ + Change weapon icons: Mudball, air mine
+ + Add healing visual effect in Paramedics mode and a few scripts
+ + Show remaining ammo when using Birdy egg or RC Plane bomb
+ + Add separate control for team chat (default: U)
+ + Add separate control for changing hedgehog tags (default: Home)
+ + Toggle all hedgehog tags at once with [Precise] + [Change hedgehog tags]
+ + Hedgehog tag translucency is now changed with [Switch] + [Change hedgehog tags]
+ + When using flying saucer shortly after a jump, stay in-mid air
+ + No longer cut off team/hedgehog names when not playing online
+ * Fix possible network desynchronization and crash when players rejoin an active game
+ * Fix time box being usable in Sudden Death with 0 health decrease
+ * Fix chat input key being sometimes registered twice
+ * Fix not displaying current volume status after pressing mute key
+ * Fix many effects not being correctly recorded by the video recorder
+ * Fix quit menu showing incorrect keys if using non-default key bindings
+ * Fix script translations not being loaded for Portuguese and Chinese
+ * When using seduction, only show “CENSORED” text in English locale
+
+Frontend:
+ + Migrate to Qt 5
+ + Schemes are now stored in separate files under Schemes
+ + Add default directory DrawnMaps for hand-drawn maps
+ + Lead player to training missions when starting Hedgewars the first time
+ * Fix map preview, map name, style name not being displayed if host selects map or style you don't have
+ * Remove “Upload to YouTube” functionality (it was broken for years)
+ * Fix broken preview of team hats (e.g. cap_team)
+ * Fix chart in stats screen not supporting negative numbers
+
+Content:
+ + New scenarios: Bazooka Battlefield, Tentacle Terror
+ + New basic training mission: Movement
+ + Completely redo basic training missions: Bazooka, Grenade, Rope
+ + Major overhaul of Sudden Death visuals in most themes
+ + New flag: uk_scotland
+ + New voice: Default_pl (Polish)
+ + New voice: Russian_pl (Polish with a Russian accent)
+ + Add theme icons for: Blox, CrazyMission, Deepspace, Eyes, Planes
+
+Styles and game modes:
+ + Various styles: Add current score/time next to team bars (where applicable)
+ + Remove rubber duck from most weapon schemes and Random Weapon and Balanced Random Weapon
+ + Construction Mode: Add a few shortcuts
+ + Construction Mode: Show selected object at cursor and a crate preview icon
+ + Construction Mode: Remember all selections
+ + Construction Mode: Add subtle glow to healing station
+ + Construction Mode: Allow to change "Switch Hedgehog" in weapon scheme
+ + Construction Mode: Hide selected sub-modes from crate/object placer from enemies over the net
+ + Highlander: Replace undocumented script param “mode=orig” to “multiuse=true”
+ + Highlander: Display the meaning of “multiuse=true” (or lack thereof) in mission panel
+ + Continental supplies: Custom weapon icons for the special weapons
+ + Racer, Tech Racer: Before starting a round, make first waypoint flash and center camera to it
+ + Racer, Tech Racer: Various other waypoint appearance improvements
+ + Racer: Use dark waypoints in bright themes like Bath
+ + Racer, HedgeEditor: Show waypoint outline around cursor when placing waypoints
+ + Battalion: Minor message and visual improvements
+ + Battalion: Mutate hog hats by default, but not the names
+ + Battalion: Script parameter “mutatenames=true” to also change the hog names (default: false)
+ + Battalion (King Mode): Kings always wear crowns and non-kings don't, regardless of settings
+ + Battalion (King Mode): If the king dies, the hogs die normally instead of disappearing
+ + Space Invasion: Add a default Space Invasion game scheme
+ + Capture the Flag: Can now be played with more than 2 clans
+ + Capture the Flag: Show team scores and score graph in stats screen
+ + HedgeEditor: Show cursor coordinates in top left corner
+ + Control: Always remove TimeBox and Resurrector
+ + Climb Home: Show messages when a fire cake is nearby and when you're inside home
+ + ShoppaMap: Map complexity can now be changed with the slider
+ + ShoppaMap: Remove script parameter “pad”
+ * The Specialists: Less buggy hog switching at turn start
+ * Mutant: When game ends due to all land being gone, declare the highest-scoring team the winner
+ * Battalion: Some texts in the mission panel were wrong and misleading
+ * Construction Mode: Remove drill strike if added by weapon scheme (it's broken)
+ * Construction Mode, Racer, HedgeEditor: No longer play Incoming voice for building stuff, fix other sound problems
+ * Capture the Flag: Fix many bugs caused by playing with >2 teams
+ * Capture the Flag: Properly place flag when first hog uses kamikaze or TimeBox
+ * Capture the Flag: Fix flag not being dropped when carrier uses piano strike
+ * CTF_Blizzard: Don't allow more than 2 clans. Excess hogs will be removed
+
+A Space Adventure:
+ + Precise Shooting: Display collected ammo
+ + Hard Flying: Display personal best at mission start
+ + Killing the Specialists: Add event messages and graphical effects
+ * Killing the Specialists: Fix very misleading hints in mission panel
+ * Killing the Specialists: Fix number of rounds measured being way too low
+
+A Classic Fairytale:
+ + Mission 3: Display number of turns left at timed parcours
+ + Mission 2, Mission 5: Reduce computer hog levels
+ + Mission 6: Change mines
+ + Mission 7: Add one additional pick hammer in a crate
+ * Fix incorrect storytelling in mission descriptions
+ * Missions now work even with corrupted team file, by using a default behaviour
+ * Mission 1: Fix error message spam when Leaks a Lot drowns
+ * Mission 3: Fix cannibals sometimes doing nothing for a full turn
+ * Mission 6: Fix cyborgs sometimes doing nothing for a full turn
+ * Mission 8: Fix invisible cyborg sometimes blocking the way
+ * Mission 9: Fix cannibal talking to himself in cut scene if mission starts with 3 natives
+
+Missions:
+ + Target Practice: Re-center camera to hog when projectile is destroyed or drowns
+
+Theme customization:
+ + Add fallback music with new keys “fallback-music” and “fallback-sd-music”
+ + Allow to hide themes explicitly with new key “hidden”. Using this key it's possible for hidden themes to have icons
+ * Fix green color channel on themes with key “sd-tint”
+
+Lua API:
+ + New library: SimpleMission: Allows to create missions more easily
+ + New call: WriteLnToChat(string): Add a line in the chat
+ + New call: SetVampiric(bool): Toggle vampirism
+ + New call: SetLaserSight(bool): Toggle laser sight
+ + New call: GetWind(): Returns current wind (approximation) from -100 to 100
+ + New call: GetTeamName(teamIdx): Returns name of team with given index (starts at 0)
+ + New call: GetTeamIndex(teamname): Returns index of team with given name
+ + New call: GetTeamClan(teamname): Returns clan of team with given name
+ + New call: SpawnSupplyCrate(x, y, content, [, amount]): Spawn ammo or utility crate, depending on content
+ + New call: HealHog(gearUid, healthBoost[, showMessage[, tint]]): Heal hedgehog with graphical effects and message
+ + New call: SetTeamLabel(teamname[, label]): Set an arbitrary label for a team, will be displayed next to the team bar
+ + New call: SetSoundMask(soundId, isMasked): Allows to disable playing a sound effect from engine
+ + New call: SkipTurn(): Force current hedgehog to skip turn
+ + New call: Explode(x, y, radius[, options]): Cause an explosion
+ + New param: PlaySound accepts 3rd parameter for voices: instaVoice: If true, sound plays instantly instead of being queued
+ + New callback: onEndTurn(): Called at the end of a turn (when gears have settled)
+ + New callback: onSkipTurn(): Called when a hog skips turn
+ + New hedgehog effect: heArtillery: Per-hedgehog artillery mode (can't walk). Values: 1 = permanently active. 2 = temporarily active (sniper rifle). 0 = not active
+ * AddAmmo now automatically unselects weapon if it would remove current ammo from current hedgehog
+ * Fix call: SetWeapon(amNothing) now unselects weapon
+ * Fix call: SetWind did not update flake flying direction
+ * Fix global: TotalRounds was stuck at -1 for several turns
+ * Fix CursorX, CursorY often not being updated when moving cursor at screen border
 
 ====================== 0.9.23 ======================
 HIGHLIGHTS:
@@ -38,7 +203,7 @@
  * Redo flags: cm_pentagram, cm_soviet, armenia, ireland, nepal, suisse, sweden, turkey
 
 Game engine (weapons):
- + New weapon: rubber duck
+ + New weapon: Rubber duck
  + Freezer can now freeze sticky mines (they get disabled and fall into the water)
  + Keep rope / parachute / flying saucer selected when destroyed and secondary ammo selected
  * Gameplay fix: Hammer damage is now rounded down. This means it will cause NO DAMAGE to a hedgehog with less than 3 hp.
--- a/INSTALL.md	Sun Jun 10 18:56:51 2018 +0200
+++ b/INSTALL.md	Sun Jun 10 19:12:26 2018 +0200
@@ -16,22 +16,29 @@
 - CMake >= 2.6.0
 - A make program (e.g. GNU Make)
 - Free Pascal Compiler (FPC) >= 2.2.4
-- Qt = 4.7.0
+- Qt 5
 - SDL >= 2.0
 - SDL\_net >= 2.0
 - SDL\_mixer >= 2.0
 - SDL\_image >= 2.0
 - SDL\_ttf >= 2.0
 
+### Recommended optional dependencies
+
+These are not strictly required to build Hedgewars, but it's
+usually better to have them installed. Hedgewars has fallback mechanisms
+in if these are not found on your system.
+
+- qtstyleplugins (for Qt 5)
+- PhysFS >= 2.0.0
+- Lua = 5.1.0
+
 ### Optional dependencies
 
 For some additional features, you can optionally install these dependencies:
 
-- For Hedgewars:
-    - PhysFS >= 2.0.0 (recommended)
-    - Lua = 5.1.0 (recommended)
 - For PNG screenshots:
-    - libpng >= 1.2 (recommended)
+    - libpng >= 1.2
 - For video recording:
     - FFmpeg or Libav
 - For the Hedgewars Server:
--- a/QTfrontend/CMakeLists.txt	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/CMakeLists.txt	Sun Jun 10 19:12:26 2018 +0200
@@ -1,24 +1,17 @@
-# Configure for Qt4
-set(QT_MIN_VERSION "4.7.0")
-include(CheckLibraryExists)
+if(APPLE AND EXISTS /usr/local/opt/qt5)
+	# Special treatment for OS X users who
+	# install Qt5 via Homebrew.
+	# Homebrew installs Qt5 (up to at least 5.9.1) in
+	# /usr/local/qt5, ensure it can be found by CMake since
+	# it is not in the default /usr/local prefix.
+	list(APPEND CMAKE_PREFIX_PATH "/usr/local/opt/qt5")
+endif()
 
-set(QT_USE_QTCORE TRUE)
-set(QT_USE_QTGUI TRUE)
-set(QT_USE_QTNETWORK TRUE)
-set(QT_USE_QTSVG FALSE)
-set(QT_USE_QTXML FALSE)
-set(QT_USE_QTOPENGL FALSE)
-set(QT_USE_QTMAIN TRUE)
+find_package(Qt5 COMPONENTS Core Widgets Gui Network)
 
-find_package(Qt4 REQUIRED)
-include(${QT_USE_FILE})
+include_directories(${Qt5Core_PRIVATE_INCLUDE_DIRS})
 
-# https://bugreports.qt-project.org/browse/QTBUG-17333
-if(APPLE AND
-   ${QTVERSION} VERSION_GREATER "4.7.0" AND
-   ${QTVERSION} VERSION_LESS "4.7.4")
-   message(FATAL_ERROR "This version of Qt is known *not* to work, please update or use a lower version")
-endif()
+include(CheckLibraryExists)
 
 find_package(SDL2 REQUIRED)
 find_package(SDL2_mixer 2 REQUIRED) #audio in SDLInteraction
@@ -159,9 +152,9 @@
     endif()
 endif()
 
-qt4_add_resources(hwfr_rez_src ${hwfr_rez})
+qt5_add_resources(hwfr_rez_src ${hwfr_rez})
 
-qt4_wrap_cpp(hwfr_moc_srcs ${hwfr_moc_hdrs})
+qt5_wrap_cpp(hwfr_moc_srcs ${hwfr_moc_hdrs})
 
 
 if(APPLE)
@@ -198,14 +191,14 @@
 
 list(APPEND HW_LINK_LIBS
     physfs physlayer
-    ${QT_LIBRARIES}
+    Qt5::Core Qt5::Widgets Qt5::Gui Qt5::Network
     )
 
 list(APPEND HW_LINK_LIBS
     ${SDL2_LIBRARY}
     ${SDL2_MIXER_LIBRARIES}
     )
-    
+
 if(WIN32 AND NOT UNIX)
     if(NOT SDL2_LIBRARY)
         list(APPEND HW_LINK_LIBS SDL2)
--- a/QTfrontend/binds.cpp	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/binds.cpp	Sun Jun 10 19:12:26 2018 +0200
@@ -47,17 +47,18 @@
     {"+attack",   "space",      QT_TRANSLATE_NOOP("binds", "attack"),          NULL, QT_TRANSLATE_NOOP("binds (descriptions)", "Fire your selected weapon or trigger an utility item:")},
     {"put",       "mousel",     QT_TRANSLATE_NOOP("binds", "put"),             NULL, QT_TRANSLATE_NOOP("binds (descriptions)", "Pick a weapon or a target location under the cursor:")},
     {"findhh",    "h",          QT_TRANSLATE_NOOP("binds", "autocam / find hedgehog"),QT_TRANSLATE_NOOP("binds (categories)", "Camera"), QT_TRANSLATE_NOOP("binds (descriptions)", "Toggle automatic camera / refocus on active hedgehog:")},
-    {"+cur_u",    "[8]",        QT_TRANSLATE_NOOP("binds", "up"),              NULL, QT_TRANSLATE_NOOP("binds (descriptions)", "Move the cursor or camera without using the mouse:")},
-    {"+cur_l",    "[4]",        QT_TRANSLATE_NOOP("binds", "left"),            NULL, NULL},
-    {"+cur_r",    "[6]",        QT_TRANSLATE_NOOP("binds", "right"),           NULL, NULL},
-    {"+cur_d",    "[2]",        QT_TRANSLATE_NOOP("binds", "down"),            NULL, NULL},
+    {"+cur_u",    "keypad_8",   QT_TRANSLATE_NOOP("binds", "up"),              NULL, QT_TRANSLATE_NOOP("binds (descriptions)", "Move the cursor or camera without using the mouse:")},
+    {"+cur_l",    "keypad_4",   QT_TRANSLATE_NOOP("binds", "left"),            NULL, NULL},
+    {"+cur_r",    "keypad_6",   QT_TRANSLATE_NOOP("binds", "right"),           NULL, NULL},
+    {"+cur_d",    "keypad_2",   QT_TRANSLATE_NOOP("binds", "down"),            NULL, NULL},
 //  {"+cur_m",    "",           QT_TRANSLATE_NOOP("binds", "movement key modifier"),    NULL, QT_TRANSLATE_NOOP("binds (descriptions)", "Specify a modifier key to move camera and cursor using your default hog movement keys:")},
     {"zoomin",    "wheelup",    QT_TRANSLATE_NOOP("binds", "zoom in"),         NULL, QT_TRANSLATE_NOOP("binds (descriptions)", "Modify the camera's zoom level:")},
     {"zoomout",   "wheeldown",  QT_TRANSLATE_NOOP("binds", "zoom out"),        NULL, NULL},
     {"zoomreset", "mousem",     QT_TRANSLATE_NOOP("binds", "reset zoom"),      NULL, NULL},
     {"chat",      "t",          QT_TRANSLATE_NOOP("binds", "chat"),            QT_TRANSLATE_NOOP("binds (categories)", "Miscellaneous"), QT_TRANSLATE_NOOP("binds (descriptions)", "Talk to your team or all participants:")},
+    {"chat team", "u",          QT_TRANSLATE_NOOP("binds", "team chat"),       NULL, NULL},
     {"history",   "`",          QT_TRANSLATE_NOOP("binds", "chat history"),    NULL, NULL},
-    {"pause",     "p",          QT_TRANSLATE_NOOP("binds", "pause"),           NULL, QT_TRANSLATE_NOOP("binds (descriptions)", "Pause, continue or leave your game:")},
+    {"pause",     "p",          QT_TRANSLATE_NOOP("binds", "pause / auto skip"),NULL, QT_TRANSLATE_NOOP("binds (descriptions)", "Pause, continue or leave your game:")},
     {"quit",      "escape",     QT_TRANSLATE_NOOP("binds", "quit"),            NULL, NULL},
     {"confirm",   "y",          QT_TRANSLATE_NOOP("binds", "confirmation"),    NULL, NULL},
     {"+voldown",  "9",          QT_TRANSLATE_NOOP("binds", "volume down"),     NULL, QT_TRANSLATE_NOOP("binds (descriptions)", "Modify the game's volume while playing:")},
@@ -68,6 +69,7 @@
     {"+speedup",  "s",          QT_TRANSLATE_NOOP("binds", "speed up replay"),         NULL, QT_TRANSLATE_NOOP("binds (descriptions)", "Demo replay:")},
     //: This refers to the team info bars (name/flag/health) of all teams. These are shown at the bottom center of the screen
     {"rotmask",   "delete",     QT_TRANSLATE_NOOP("binds", "toggle team bars"), NULL, QT_TRANSLATE_NOOP("binds (descriptions)", "Heads-up display:")},
+    {"rottags",   "home",       QT_TRANSLATE_NOOP("binds", "toggle hedgehog tags"), NULL, NULL},
 #ifdef VIDEOREC
     {"record",    "r",          QT_TRANSLATE_NOOP("binds", "record"),          NULL, QT_TRANSLATE_NOOP("binds (descriptions)", "Record video:")}
 #endif
--- a/QTfrontend/binds.h	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/binds.h	Sun Jun 10 19:12:26 2018 +0200
@@ -22,9 +22,9 @@
 #include <QString>
 
 #ifdef VIDEOREC
-#define BINDS_NUMBER 47
+#define BINDS_NUMBER 49
 #else
-#define BINDS_NUMBER 46
+#define BINDS_NUMBER 48
 #endif
 
 struct BindAction
--- a/QTfrontend/campaign.cpp	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/campaign.cpp	Sun Jun 10 19:12:26 2018 +0200
@@ -88,9 +88,7 @@
     // get locale
     QSettings settings(dataMgr.settingsFileName(),
     QSettings::IniFormat);
-    QString loc = settings.value("misc/locale", "").toString();
-    if (loc.isEmpty())
-        loc = QLocale::system().name();
+    QString loc = QLocale().name();
     QString campaignDescFile = QString("physfs://Locale/campaigns_" + loc + ".txt");
     // if file is non-existant try with language only
     if (!QFile::exists(campaignDescFile))
--- a/QTfrontend/drawmapscene.cpp	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/drawmapscene.cpp	Sun Jun 10 19:12:26 2018 +0200
@@ -25,6 +25,10 @@
 
 #include "drawmapscene.h"
 
+#define DRAWN_MAP_COLOR_LAND (Qt::yellow)
+#define DRAWN_MAP_COLOR_CURSOR_PEN (Qt::green)
+#define DRAWN_MAP_COLOR_CURSOR_ERASER (Qt::red)
+
 template <class T> T sqr(const T & x)
 {
     return x*x;
@@ -32,9 +36,9 @@
 
 DrawMapScene::DrawMapScene(QObject *parent) :
     QGraphicsScene(parent),
-    m_pen(Qt::yellow),
-    m_brush(Qt::yellow),
-    m_cursor(new QGraphicsEllipseItem(-0.5, -0.5, 1, 1))
+    m_pen(DRAWN_MAP_COLOR_LAND),
+    m_brush(DRAWN_MAP_COLOR_LAND),
+    m_cursor(new QGraphicsEllipseItem(-5, -5, 5, 5))
 {
     setSceneRect(0, 0, 4096, 2048);
 
@@ -48,15 +52,18 @@
 
     m_pathType = Polyline;
 
-    m_pen.setWidth(76);
+    m_pen.setWidth(DRAWN_MAP_BRUSH_SIZE_START);
     m_pen.setJoinStyle(Qt::RoundJoin);
     m_pen.setCapStyle(Qt::RoundCap);
     m_currPath = 0;
 
     m_isCursorShown = false;
-    m_cursor->setPen(QPen(Qt::green));
+    QPen cursorPen = QPen(DRAWN_MAP_COLOR_CURSOR_PEN);
+    cursorPen.setJoinStyle(Qt::RoundJoin);
+    cursorPen.setCapStyle(Qt::RoundCap);
+    cursorPen.setWidth(brushSize());
+    m_cursor->setPen(cursorPen);
     m_cursor->setZValue(1);
-    m_cursor->setScale(m_pen.width());
 }
 
 void DrawMapScene::mouseMoveEvent(QGraphicsSceneMouseEvent * mouseEvent)
@@ -124,7 +131,7 @@
     path.lineTo(mouseEvent->scenePos());
 
     PathParams params;
-    params.width = serializePenWidth(m_pen.width());
+    params.width = serializePenWidth(brushSize());
     params.erasing = m_isErasing;
     params.initialPoint = mouseEvent->scenePos().toPoint();
     params.points = QList<QPoint>() << params.initialPoint;
@@ -178,20 +185,38 @@
     }
 }
 
-void DrawMapScene::wheelEvent(QGraphicsSceneWheelEvent * wheelEvent)
+void DrawMapScene::setBrushSize(int newBrushSize)
 {
-    if(wheelEvent->delta() > 0 && m_pen.width() < 516)
-        m_pen.setWidth(m_pen.width() + 10);
-    else if(wheelEvent->delta() < 0 && m_pen.width() >= 16)
-        m_pen.setWidth(m_pen.width() - 10);
+    if(newBrushSize > DRAWN_MAP_BRUSH_SIZE_MAX)
+        newBrushSize = DRAWN_MAP_BRUSH_SIZE_MAX;
+    if(newBrushSize < DRAWN_MAP_BRUSH_SIZE_MIN)
+        newBrushSize = DRAWN_MAP_BRUSH_SIZE_MIN;
 
-    m_cursor->setScale(m_pen.width());
-
+    m_pen.setWidth(newBrushSize);
+    QPen cursorPen = m_cursor->pen();
+    cursorPen.setWidth(m_pen.width());
+    m_cursor->setPen(cursorPen);
     if(m_currPath)
     {
         m_currPath->setPen(m_pen);
         paths.first().width = serializePenWidth(m_pen.width());
     }
+
+    emit brushSizeChanged(newBrushSize);
+}
+
+int DrawMapScene::brushSize()
+{
+    return m_pen.width();
+}
+
+void DrawMapScene::wheelEvent(QGraphicsSceneWheelEvent * wheelEvent)
+{
+    int b = brushSize();
+    if(wheelEvent->delta() > 0)
+        setBrushSize(b + DRAWN_MAP_BRUSH_SIZE_STEP);
+    else if(wheelEvent->delta() < 0 && b >= DRAWN_MAP_BRUSH_SIZE_MIN)
+        setBrushSize(b - DRAWN_MAP_BRUSH_SIZE_STEP);
 }
 
 void DrawMapScene::showCursor()
@@ -266,10 +291,15 @@
 void DrawMapScene::setErasing(bool erasing)
 {
     m_isErasing = erasing;
-    if(erasing)
+    QPen cursorPen = m_cursor->pen();
+    if(erasing) {
         m_pen.setBrush(m_eraser);
-    else
+        cursorPen.setColor(DRAWN_MAP_COLOR_CURSOR_ERASER);
+    } else {
         m_pen.setBrush(m_brush);
+        cursorPen.setColor(DRAWN_MAP_COLOR_CURSOR_PEN);
+    }
+    m_cursor->setPen(cursorPen);
 }
 
 QByteArray DrawMapScene::encode()
@@ -306,8 +336,12 @@
 {
     hideCursor();
 
+    // Remember erasing mode
     bool erasing = m_isErasing;
 
+    // Use seperate for decoding the map, don't mess with the user pen
+    QPen load_pen = QPen(m_pen);
+
     oldItems.clear();
     oldPaths.clear();
     clear();
@@ -333,7 +367,7 @@
 
             if(params.points.size())
             {
-                addPath(pointsToPath(params.points), m_pen);
+                addPath(pointsToPath(params.points), load_pen);
 
                 paths.prepend(params);
 
@@ -341,12 +375,12 @@
             }
 
             quint8 penWidth = flags & 0x3f;
-            m_pen.setWidth(deserializePenWidth(penWidth));
+            load_pen.setWidth(deserializePenWidth(penWidth));
             params.erasing = flags & 0x40;
             if(params.erasing)
-                m_pen.setBrush(m_eraser);
+                load_pen.setBrush(m_eraser);
             else
-                m_pen.setBrush(m_brush);
+                load_pen.setBrush(m_brush);
             params.width = penWidth;
         } else
             if(isSpecial)
@@ -369,12 +403,13 @@
 
     if(params.points.size())
     {
-        addPath(pointsToPath(params.points), m_pen);
+        addPath(pointsToPath(params.points), load_pen);
         paths.prepend(params);
     }
 
     emit pathChanged();
 
+    // Restore erasing mode
     setErasing(erasing);
 }
 
--- a/QTfrontend/drawmapscene.h	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/drawmapscene.h	Sun Jun 10 19:12:26 2018 +0200
@@ -23,6 +23,11 @@
 #include <QPainterPath>
 #include <QGraphicsEllipseItem>
 
+#define DRAWN_MAP_BRUSH_SIZE_STEP (10)
+#define DRAWN_MAP_BRUSH_SIZE_MAX (516)
+#define DRAWN_MAP_BRUSH_SIZE_MIN (16)
+#define DRAWN_MAP_BRUSH_SIZE_START (76)
+
 class QGraphicsPathItem;
 
 struct PathParams
@@ -50,9 +55,11 @@
         QByteArray encode();
         void decode(QByteArray data);
         int pointsCount();
+        int brushSize();
 
     signals:
         void pathChanged();
+        void brushSizeChanged(int brushSize);
 
     public slots:
         void undo();
@@ -63,6 +70,7 @@
         void showCursor();
         void hideCursor();
         void setPathType(PathType pathType);
+        void setBrushSize(int brushSize);
 
     private:
         QPen m_pen;
--- a/QTfrontend/game.cpp	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/game.cpp	Sun Jun 10 19:12:26 2018 +0200
@@ -140,11 +140,11 @@
 void HWGame::SendQuickConfig()
 {
     QByteArray teamscfg;
-    ThemeModel * themeModel = DataManager::instance().themeModel();
+    QAbstractItemModel * themeModel = DataManager::instance().themeModel()->withoutHidden();
 
     HWProto::addStringToBuffer(teamscfg, "TL");
     HWProto::addStringToBuffer(teamscfg, QString("etheme %1")
-                               .arg((themeModel->rowCount() > 0) ? themeModel->index(rand() % themeModel->rowCount()).data(ThemeModel::ActualNameRole).toString() : "steel"));
+                               .arg((themeModel->rowCount() > 0) ? themeModel->index(rand() % themeModel->rowCount(), 0).data(ThemeModel::ActualNameRole).toString() : "Nature"));
     HWProto::addStringToBuffer(teamscfg, "eseed " + QUuid::createUuid().toString());
 
     HWProto::addStringToBuffer(teamscfg, "e$template_filter 2");
@@ -255,11 +255,10 @@
         {
             int size = msg.size();
             emit ErrorMessage(
-                tr("A Fatal ERROR occured! - The game engine had to stop.\n\n"
-                "We are very sorry for the inconvenience :(\n\n"
-                "If this keeps happening, please click the '%1' button in the main menu!\n\n"
-                "Last two engine messages:\n%2")
-                .arg("Feedback")
+                tr("A fatal ERROR occured! The game engine had to stop.\n\n"
+                "We are very sorry for the inconvenience. :-(\n\n"
+                "If this keeps happening, please click the 'Feedback' button in the main menu!\n\n"
+                "Last engine message:\n%1")
                 .arg(QString::fromUtf8(msg.mid(2).left(size - 4))));
             return;
         }
@@ -537,7 +536,7 @@
 void HWGame::sendCampaignVar(const QByteArray &varToSend)
 {
     QString varToFind = QString::fromUtf8(varToSend);
-    QSettings teamfile(QString("physfs://Teams/%1.hwt").arg(campaignTeam), QSettings::IniFormat, 0);
+    QSettings teamfile(QString(cfgdir->absolutePath() + "/Teams/%1.hwt").arg(campaignTeam), QSettings::IniFormat, 0);
     teamfile.setIniCodec("UTF-8");
     QString varValue = teamfile.value("Campaign " + campaign + "/" + varToFind, "").toString();
     QByteArray command;
@@ -554,7 +553,7 @@
     QString varToWrite = QString::fromUtf8(varVal.left(i));
     QString varValue = QString::fromUtf8(varVal.mid(i + 1));
 
-    QSettings teamfile(QString("physfs://Teams/%1.hwt").arg(campaignTeam), QSettings::IniFormat, 0);
+    QSettings teamfile(QString(cfgdir->absolutePath() + "/Teams/%1.hwt").arg(campaignTeam), QSettings::IniFormat, 0);
     teamfile.setIniCodec("UTF-8");
     teamfile.setValue("Campaign " + campaign + "/" + varToWrite, varValue);
 }
--- a/QTfrontend/gameuiconfig.cpp	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/gameuiconfig.cpp	Sun Jun 10 19:12:26 2018 +0200
@@ -134,7 +134,7 @@
     netHost = new QString(value("net/ip", "").toString());
     netPort = value("net/port", NETGAME_DEFAULT_PORT).toUInt();
 
-    Form->ui.pageNetServer->leServerDescr->setText(value("net/servername", "hedgewars server").toString());
+    Form->ui.pageNetServer->leServerDescr->setText(value("net/servername", "Hedgewars Server").toString());
     Form->ui.pageNetServer->sbPort->setValue(value("net/serverport", NETGAME_DEFAULT_PORT).toUInt());
 
     Form->ui.pageOptions->CBShowFPS->setChecked(value("fps/show", false).toBool());
--- a/QTfrontend/hedgewars.qrc	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/hedgewars.qrc	Sun Jun 10 19:12:26 2018 +0200
@@ -4,6 +4,7 @@
         <file>res/css/qt.css</file>
         <file>res/css/chat.css</file>
         <file>res/css/christmas.css</file>
+        <file>res/css/april1.css</file>
         <file>res/css/easter.css</file>
         <file>res/css/birthday.css</file>
         <file>res/hh25x25.png</file>
@@ -73,6 +74,7 @@
         <file>res/BackgroundBirthday.png</file>
         <file>res/Exit.png</file>
         <file>res/HedgewarsTitle.png</file>
+        <file>res/TomatowarsTitle.png</file>
         <file>res/LocalPlay.png</file>
         <file>res/NetworkPlay.png</file>
         <file>res/NetworkPlayDisabled.png</file>
@@ -80,13 +82,27 @@
         <file>res/camera.png</file>
         <file>res/Settings.png</file>
         <file>res/dropdown.png</file>
+        <file>res/dropdown_disabled.png</file>
+        <file>res/dropdown_selected.png</file>
         <file>res/new.png</file>
         <file>res/edit.png</file>
         <file>res/delete.png</file>
         <file>res/checked.png</file>
+        <file>res/checkedHover.png</file>
+        <file>res/checkedPressed.png</file>
         <file>res/unchecked.png</file>
+        <file>res/uncheckedHover.png</file>
+        <file>res/uncheckedPressed.png</file>
+        <file>res/radioButtonChecked.png</file>
+        <file>res/radioButtonCheckedHover.png</file>
+        <file>res/radioButtonCheckedPressed.png</file>
+        <file>res/radioButtonUnchecked.png</file>
+        <file>res/radioButtonUncheckedHover.png</file>
+        <file>res/radioButtonUncheckedPressed.png</file>
         <file>res/missionFinished.png</file>
         <file>res/missionFinishedSelected.png</file>
+        <file>res/missingMap.png</file>
+        <file>res/missingTheme@2x.png</file>
         <file>res/dlcMarker.png</file>
         <file>res/dlcMarkerSelected.png</file>
         <file>res/graphicsicon.png</file>
@@ -105,11 +121,16 @@
         <file>res/lightbulb_on.png</file>
         <file>res/lightbulb_off.png</file>
         <file>res/spin_up.png</file>
+        <file>res/spin_up_disabled.png</file>
         <file>res/spin_down.png</file>
+        <file>res/spin_down_disabled.png</file>
+        <file>res/sort_up.png</file>
+        <file>res/sort_down.png</file>
         <file>res/PlaySound.png</file>
         <file>res/hh_small.png</file>
         <file>res/btnDisabled.png</file>
         <file>res/btnBorder@2x.png</file>
+        <file>res/btnSwitchHog@2x.png</file>
         <file>res/btnInvulnerable@2x.png</file>
         <file>res/btnLaserSight@2x.png</file>
         <file>res/btnLowGravity@2x.png</file>
@@ -171,7 +192,10 @@
         <file>res/info.png</file>
         <file>res/kick.png</file>
         <file>res/lock.png</file>
+        <file>res/lock_disabled.png</file>
         <file>res/unlock.png</file>
+        <file>res/unlock_disabled.png</file>
+        <file>res/splitter.png</file>
         <file>res/StatsMedal1.png</file>
         <file>res/StatsMedal2.png</file>
         <file>res/StatsMedal3.png</file>
--- a/QTfrontend/hwconsts.h	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/hwconsts.h	Sun Jun 10 19:12:26 2018 +0200
@@ -16,7 +16,7 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 
-#if !defined(TARGET_OS_IPHONE)
+#if !(TARGET_OS_IPHONE)
 #include <QString>
 #include <QDir>
 #include <QStringList>
@@ -67,6 +67,7 @@
 #define SEASON_CHRISTMAS 2
 #define SEASON_HWBDAY 4
 #define SEASON_EASTER 8
+#define SEASON_APRIL1 16
 
 #define NETGAME_DEFAULT_SERVER "netserver.hedgewars.org"
 #define NETGAME_DEFAULT_PORT 46631
--- a/QTfrontend/hwform.cpp	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/hwform.cpp	Sun Jun 10 19:12:26 2018 +0200
@@ -90,7 +90,7 @@
 #include "chatwidget.h"
 #include "input_ip.h"
 #include "input_password.h"
-#include "ammoSchemeModel.h"
+#include "gameSchemeModel.h"
 #include "bgwidget.h"
 #include "drawmapwidget.h"
 #include "mouseoverfilter.h"
@@ -231,10 +231,7 @@
 
     pageSwitchMapper = new QSignalMapper(this);
     connect(pageSwitchMapper, SIGNAL(mapped(int)), this, SLOT(GoToPage(int)));
-
-    connect(config, SIGNAL(frontendFullscreen(bool)), this, SLOT(onFrontendFullscreen(bool)));
-    onFrontendFullscreen(config->isFrontendFullscreen());
-
+    
     connect(ui.pageMain->BtnSinglePlayer, SIGNAL(clicked()), pageSwitchMapper, SLOT(map()));
     pageSwitchMapper->setMapping(ui.pageMain->BtnSinglePlayer, ID_PAGE_SINGLEPLAYER);
 
@@ -347,10 +344,10 @@
 
     connect(ui.pageVideos, SIGNAL(goBack()), config, SLOT(SaveVideosOptions()));
 
-    ammoSchemeModel = new AmmoSchemeModel(this, cfgdir->absolutePath() + "/schemes.ini");
-    ui.pageScheme->setModel(ammoSchemeModel);
-    ui.pageMultiplayer->gameCFG->GameSchemes->setModel(ammoSchemeModel);
-    ui.pageOptions->SchemesName->setModel(ammoSchemeModel);
+    gameSchemeModel = new GameSchemeModel(this, cfgdir->absolutePath() + "/Schemes/Game");
+    ui.pageScheme->setModel(gameSchemeModel);
+    ui.pageMultiplayer->gameCFG->GameSchemes->setModel(gameSchemeModel);
+    ui.pageOptions->SchemesName->setModel(gameSchemeModel);
 
     wBackground = new BGWidget(this);
     wBackground->setFixedSize(this->width(), this->height());
@@ -380,6 +377,9 @@
     PagesStack.push(ID_PAGE_MAIN);
     ((AbstractPage*)ui.Pages->widget(ID_PAGE_MAIN))->triggerPageEnter();
     GoBack();
+
+    connect(config, SIGNAL(frontendFullscreen(bool)), this, SLOT(onFrontendFullscreen(bool)));
+    onFrontendFullscreen(config->isFrontendFullscreen());
 }
 
 void HWForm::onFrontendFullscreen(bool value)
@@ -646,6 +646,11 @@
     GoToPage(ID_PAGE_VIDEOS);
 }
 
+void HWForm::GoToTraining()
+{
+    GoToPage(ID_PAGE_TRAINING);
+}
+
 //TODO: maybe find a better place for this?
 QString HWForm::stringifyPageId(quint32 id)
 {
@@ -914,7 +919,7 @@
     //if (curid == ID_PAGE_NETGAME && (!game || game->gameState != gsStarted)) hwnet->partRoom();
 
     if (curid == ID_PAGE_SCHEME)
-        ammoSchemeModel->Save();
+        gameSchemeModel->Save();
 
 #if (QT_VERSION >= 0x040600)
     /**Start animation :**/
@@ -1074,14 +1079,14 @@
 void HWForm::DeleteScheme()
 {
     ui.pageScheme->selectScheme->setCurrentIndex(ui.pageOptions->SchemesName->currentIndex());
-    if (ui.pageOptions->SchemesName->currentIndex() < ammoSchemeModel->numberOfDefaultSchemes)
+    if (ui.pageOptions->SchemesName->currentIndex() < gameSchemeModel->numberOfDefaultSchemes)
     {
         MessageDialog::ShowErrorMessage(QMessageBox::tr("Cannot delete default scheme '%1'!").arg(ui.pageOptions->SchemesName->currentText()), this);
     }
     else
     {
         ui.pageScheme->deleteRow();
-        ammoSchemeModel->Save();
+        gameSchemeModel->Save();
     }
 }
 
@@ -1894,7 +1899,7 @@
     ui.pageNetGame->restrictJoins->setChecked(false);
     ui.pageNetGame->restrictTeamAdds->setChecked(false);
     ui.pageNetGame->restrictUnregistered->setChecked(false);
-    ui.pageNetGame->pGameCFG->GameSchemes->setModel(ammoSchemeModel);
+    ui.pageNetGame->pGameCFG->GameSchemes->setModel(gameSchemeModel);
     ui.pageNetGame->pGameCFG->setMaster(true);
     ui.pageNetGame->pNetTeamsWidget->setInteractivity(true);
 
@@ -1931,7 +1936,7 @@
 
     if (hwnet)
     {
-        NetAmmoSchemeModel * netAmmo = new NetAmmoSchemeModel(hwnet);
+        NetGameSchemeModel * netAmmo = new NetGameSchemeModel(hwnet);
         connect(hwnet, SIGNAL(netSchemeConfig(QStringList)), netAmmo, SLOT(setNetSchemeConfig(QStringList)));
 
         ui.pageNetGame->pGameCFG->GameSchemes->setModel(netAmmo);
--- a/QTfrontend/hwform.h	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/hwform.h	Sun Jun 10 19:12:26 2018 +0200
@@ -47,7 +47,7 @@
 class GameUIConfig;
 class HWNetRegisterServer;
 class QCloseEvent;
-class AmmoSchemeModel;
+class GameSchemeModel;
 class QSettings;
 class QSignalMapper;
 
@@ -66,6 +66,7 @@
         void setButtonDescription(QString desc);
         void backDescription();
         void GoToVideos();
+        void GoToTraining();
 
         void NetConnectQuick(const QString & host, quint16 port);
         void PlayDemoQuick(const QString & demofilename);
@@ -196,7 +197,7 @@
         QPointer<HWTeam> editedTeam;
         QPointer<HWNewNet> hwnet;
         HWNamegen * namegen;
-        AmmoSchemeModel * ammoSchemeModel;
+        GameSchemeModel * gameSchemeModel;
         QStack<int> PagesStack;
         QString previousCampaignName;
         QString previousTeamName;
--- a/QTfrontend/main.cpp	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/main.cpp	Sun Jun 10 19:12:26 2018 +0200
@@ -20,7 +20,6 @@
 
 #include <QTranslator>
 #include <QLocale>
-#include <QPlastiqueStyle>
 #include <QRegExp>
 #include <QMap>
 #include <QSettings>
@@ -29,6 +28,8 @@
 #include <QDesktopWidget>
 #include <QLabel>
 #include <QLibraryInfo>
+#include <QStyle>
+#include <QStyleFactory>
 
 #include "hwform.h"
 #include "hwconsts.h"
@@ -51,6 +52,8 @@
 #include <QSplashScreen>
 #endif
 
+#include <QMessageBox>
+
 // Program resources
 #ifdef __APPLE__
 static CocoaInitializer * cocoaInit = NULL;
@@ -93,6 +96,10 @@
         season = SEASON_HWBDAY;
         years_since_foundation = date.year() - 2004;
     }
+    else if (date.month() == 4 && date.day() == 1)
+    {
+        season = SEASON_APRIL1;
+    }
     //Easter?
     else if (calculateEaster(date.year()) == date)
         season = SEASON_EASTER;
@@ -153,8 +160,30 @@
 .arg(HWApplication::tr("Hedgewars can use a %1 (e.g. \"%2\") to connect on start.", "command-line").arg(HWApplication::tr("CONNECTSTRING", "command-line")).arg(QString("hwplay://") + NETGAME_DEFAULT_SERVER));
 }
 
-int main(int argc, char *argv[])
-{
+int main(int argc, char *argv[]) {
+    /* Qt5 Base removed Motif, Plastique. These are now in the Qt style plugins
+    (Ubuntu: qt5-style-plugins, which was NOT backported by Debian/Ubuntu to stable/LTS).
+    Windows appears to render best of the remaining options but still isn't quite right. */
+
+    // Try setting Plastique if available
+    QStyle* coreStyle;
+    coreStyle = QStyleFactory::create("Plastique");
+    if(coreStyle != 0) {
+        QApplication::setStyle(coreStyle);
+        qDebug("Qt style set: Plastique");
+    } else {
+        // Use Windows as fallback.
+        // FIXME: Under Windows style, some widgets like scrollbars don't render as nicely
+        coreStyle = QStyleFactory::create("Windows");
+        if(coreStyle != 0) {
+            QApplication::setStyle(coreStyle);
+            qDebug("Qt style set: Windows");
+        } else {
+            // Windows style should not be missing in Qt5 Base. If it does, something went terribly wrong!
+            qWarning("No Qt style could be set! Using the default one.");
+        }
+    }
+
     // Since we're calling this first, closeResources() will be the last thing called after main() returns.
     atexit(closeResources);
 
@@ -252,8 +281,6 @@
     splash.show();
 #endif
 
-    app.setStyle(new QPlastiqueStyle());
-
     QDateTime now = QDateTime::currentDateTime();
     srand(now.toTime_t());
     rand();
@@ -295,6 +322,7 @@
 
         // config/save paths
         checkForDir(cfgdir->absolutePath() + "/Demos");
+        checkForDir(cfgdir->absolutePath() + "/DrawnMaps");
         checkForDir(cfgdir->absolutePath() + "/Saves");
         checkForDir(cfgdir->absolutePath() + "/Screenshots");
         checkForDir(cfgdir->absolutePath() + "/Teams");
@@ -311,6 +339,8 @@
         return 1;
     }
 
+    bool isProbablyNewPlayer = false;
+
     // setup PhysFS
     engine = new FileEngineHandler(argv[0]);
     engine->mount(datadir->absolutePath());
@@ -325,24 +355,52 @@
         QSettings settings(DataManager::instance().settingsFileName(), QSettings::IniFormat);
         settings.setIniCodec("UTF-8");
 
+        // Heuristic to figure out if the user is (probably) a first-time player.
+        // If nickname is not set, then probably yes.
+        // The hidden setting firstLaunch is, if present, used to force HW to
+        // treat iself as if it were launched the first time.
+        QString nick = settings.value("net/nick", QString()).toString();
+        if (settings.contains("frontend/firstLaunch"))
+        {
+            isProbablyNewPlayer = settings.value("frontend/firstLaunch").toBool();
+        }
+        else
+        {
+            isProbablyNewPlayer = nick.isNull();
+        }
+
+        // Set firstLaunch to false to make sure we remember we have been launched before.
+        settings.setValue("frontend/firstLaunch", false);
+
         QString cc = settings.value("misc/locale", QString()).toString();
         if (cc.isEmpty())
         {
             cc = QLocale::system().name();
+            qDebug("Detected system locale: %s", qPrintable(cc));
 
             // Fallback to current input locale if "C" locale is returned
             if(cc == "C")
-                cc = HWApplication::keyboardInputLocale().name();
+                cc = HWApplication::inputMethod()->locale().name();
         }
+        else
+        {
+            qDebug("Configured frontend locale: %s", qPrintable(cc));
+        }
+        QLocale::setDefault(cc);
+        QString defaultLocaleName = QLocale().name();
+        qDebug("Frontend uses locale: %s", qPrintable(defaultLocaleName));
 
-        // Load locale files into translators
-        if (!TranslatorHedgewars.load(QString("physfs://Locale/hedgewars_%1").arg(cc)))
-            qWarning("Failed to install Hedgewars translation (%s)", qPrintable(cc));
-        if (!TranslatorQt.load(QString("%1/qt_%2").arg(QLibraryInfo::location(QLibraryInfo::TranslationsPath), cc)))
-            qWarning("Failed to install Qt translation (%s)", qPrintable(cc));
-        app.installTranslator(&TranslatorHedgewars);
-        app.installTranslator(&TranslatorQt);
-        app.setLayoutDirection(QLocale(cc).textDirection());
+        if (defaultLocaleName != "C")
+        {
+            // Load locale files into translators
+            if (!TranslatorHedgewars.load(QLocale(), "hedgewars", "_", QString("physfs://Locale")))
+                qWarning("Failed to install Hedgewars translation (%s)", qPrintable(defaultLocaleName));
+            if (!TranslatorQt.load(QLocale(), "qt", "_", QString(QLibraryInfo::location(QLibraryInfo::TranslationsPath))))
+                qWarning("Failed to install Qt translation (%s)", qPrintable(defaultLocaleName));
+            app.installTranslator(&TranslatorHedgewars);
+            app.installTranslator(&TranslatorQt);
+        }
+        app.setLayoutDirection(QLocale().textDirection());
     }
 
 #ifdef _WIN32
@@ -369,6 +427,9 @@
         case SEASON_CHRISTMAS :
             fname = "christmas.css";
             break;
+        case SEASON_APRIL1 :
+            fname = "april1.css";
+            break;
         case SEASON_EASTER :
             fname = "easter.css";
             break;
@@ -398,6 +459,23 @@
 #endif
     app.form->show();
 
+    // Show welcome message for (suspected) first-time player and
+    // point towards the Training menu.
+    if(isProbablyNewPlayer) {
+        QMessageBox questionTutorialMsg(app.form);
+        questionTutorialMsg.setIcon(QMessageBox::Question);
+        questionTutorialMsg.setWindowTitle(QMessageBox::tr("Welcome to Hedgewars"));
+        questionTutorialMsg.setText(QMessageBox::tr("Welcome to Hedgewars!\n\nYou seem to be new around here. Would you like to play some training missions first to learn the basics of Hedgewars?"));
+        questionTutorialMsg.setWindowModality(Qt::WindowModal);
+        questionTutorialMsg.addButton(QMessageBox::Yes);
+        questionTutorialMsg.addButton(QMessageBox::No);
+
+        int answer = questionTutorialMsg.exec();
+        if (answer == QMessageBox::Yes) {
+            app.form->GoToTraining();
+        }
+    }
+
     if (app.urlString)
         app.fakeEvent();
     return app.exec();
--- a/QTfrontend/model/HatModel.cpp	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/model/HatModel.cpp	Sun Jun 10 19:12:26 2018 +0200
@@ -78,7 +78,7 @@
 
         QString str = hatsList.at(i);
         str = str.remove(QRegExp("\\.png$"));
-        QPixmap pix(
+        QPixmap hatpix(
                 "physfs://Graphics/Hats/" + QString(isReserved?"Reserved/":"") + str +
                 ".png"
         );
@@ -87,20 +87,51 @@
         if (isReserved)
             str = "Reserved "+str.remove(0,32);
 
-        QPixmap tmppix(32, 37);
-        tmppix.fill(QColor(Qt::transparent));
+        // Color for team hats. We use the default color of the first team.
+        QColor overlay_color = QColor(colors[0]);
+
+        QPixmap ppix(32, 37);
+        ppix.fill(QColor(Qt::transparent));
+        QPainter painter(&ppix);
+
+        QPixmap opix(32, 37);
+        opix.fill(QColor(Qt::transparent));
+        QPainter overlay_painter(&opix);
+
+        // The hat is drawn in reverse: First the color overlay, then the hat, then the hedgehog.
 
-        QPainter painter(&tmppix);
+        // draw hat's color layer, if present
+        int overlay_offset = -1;
+        if((hatpix.height() == 32) && (hatpix.width() == 64)) {
+            overlay_offset = 32;
+        } else if(hatpix.width() > 64) {
+            overlay_offset = 64;
+        }
+        if(overlay_offset > -1) {
+            // colorized layer
+            overlay_painter.drawPixmap(QPoint(0, 0), hatpix.copy(overlay_offset, 0, 32, 32));
+            overlay_painter.setCompositionMode(QPainter::CompositionMode_Multiply);
+            overlay_painter.fillRect(0, 0, 32, 32, overlay_color);
+
+            // uncolorized layer and combine
+            painter.drawPixmap(QPoint(0, 0), hatpix.copy(overlay_offset, 0, 32, 32));
+            painter.setCompositionMode(QPainter::CompositionMode_SourceAtop);
+            painter.drawPixmap(QPoint(0, 0), opix.copy(0, 0, 32, 32));
+        }
+
+        // draw hat below the color layer
+        painter.setCompositionMode(QPainter::CompositionMode_DestinationOver);
+        painter.drawPixmap(QPoint(0, 0), hatpix.copy(0, 0, 32, 32));
+
+        // draw hedgehog below the hat
         painter.drawPixmap(QPoint(0, 5), hhpix);
-        painter.drawPixmap(QPoint(0, 0), pix.copy(0, 0, 32, 32));
-        if(pix.width() > 32)
-            painter.drawPixmap(QPoint(0, 0), pix.copy(32, 0, 32, 32));
+
         painter.end();
 
         if (str == "NoHat")
-            hats.prepend(new QStandardItem(QIcon(tmppix), str));
+            hats.prepend(new QStandardItem(QIcon(ppix), str));
         else
-            hats.append(new QStandardItem(QIcon(tmppix), str));
+            hats.append(new QStandardItem(QIcon(ppix), str));
     }
 
     QStandardItemModel::appendColumn(hats);
--- a/QTfrontend/model/MapModel.cpp	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/model/MapModel.cpp	Sun Jun 10 19:12:26 2018 +0200
@@ -129,18 +129,19 @@
             {
                 // get locale
                 QSettings settings(datamgr.settingsFileName(), QSettings::IniFormat);
-                QString locale = settings.value("misc/locale", "").toString();
-                if (locale.isEmpty())
-                    locale = QLocale::system().name();
+                QString locale = QLocale().name();
 
                 QSettings descSettings(QString("physfs://Maps/%1/desc.txt").arg(map), QSettings::IniFormat);
                 descSettings.setIniCodec("UTF-8");
                 desc = descSettings.value(locale, QString()).toString();
-                // If not found, try with lanague-only code
+                // If not found, try with language-only code
                 if (desc.isEmpty())
                 {
                     QString localeSimple = locale.remove(QRegExp("_.*$"));
                     desc = descSettings.value(localeSimple, QString()).toString();
+                    // If still not found, use English
+                    if (desc.isEmpty())
+                        desc = descSettings.value("en", QString()).toString();
                 }
                 desc = desc.replace("_n", "\n").replace("_c", ",").replace("__", "_");
             }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/QTfrontend/model/ThemeFilterProxyModel.cpp	Sun Jun 10 19:12:26 2018 +0200
@@ -0,0 +1,71 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (c) 2004-2018 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+/**
+ * @file
+ * @brief ThemeFilterProxyModel class implementation
+ */
+
+#include "ThemeModel.h"
+#include "ThemeFilterProxyModel.h"
+
+ThemeFilterProxyModel::ThemeFilterProxyModel(QObject *parent)
+    : QSortFilterProxyModel(parent)
+{
+    isFilteringDLC = false;
+    isFilteringHidden = false;
+}
+
+bool ThemeFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex & sourceParent) const
+{
+    QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
+    bool searchOkay = true;
+    if(!filterRegExp().isEmpty())
+    {
+        // Check regular expression set by the theme chooser search
+        QString name = index.data(ThemeModel::ActualNameRole).toString();
+        int in = filterRegExp().indexIn(name);
+        searchOkay = in != -1;
+    }
+
+    if(isFilteringDLC || isFilteringHidden)
+    {
+        bool isDLC = index.data(ThemeModel::IsDlcRole).toBool();
+        bool isHidden = index.data(ThemeModel::IsHiddenRole).toBool();
+
+        return ( ((isFilteringDLC && !isDLC) || !isFilteringDLC) &&
+                 ((isFilteringHidden && !isHidden) || !isFilteringHidden) ) &&
+               searchOkay;
+    }
+    else
+    {
+        return searchOkay;
+    }
+}
+
+void ThemeFilterProxyModel::setFilterDLC(bool enable)
+{
+    isFilteringDLC = enable;
+    invalidateFilter();
+}
+
+void ThemeFilterProxyModel::setFilterHidden(bool enable)
+{
+    isFilteringHidden = enable;
+    invalidateFilter();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/QTfrontend/model/ThemeFilterProxyModel.h	Sun Jun 10 19:12:26 2018 +0200
@@ -0,0 +1,49 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (c) 2004-2018 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+/**
+ * @file
+ * @brief Class definition of ThemeFilterProxyModel
+ */
+
+#ifndef HEDGEWARS_THEMEFILTERPROXYMODEL_H
+#define HEDGEWARS_THEMEFILTERPROXYMODEL_H
+
+#include <QSortFilterProxyModel>
+
+/**
+ * @brief A filter model for filtering DLC themes
+ */
+class ThemeFilterProxyModel : public QSortFilterProxyModel
+{
+        Q_OBJECT
+
+    public:
+        ThemeFilterProxyModel(QObject *parent = 0);
+        void setFilterDLC(bool enabled);
+        void setFilterHidden(bool enabled);
+
+    protected:
+        bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const;
+
+    private:
+        bool isFilteringDLC;
+        bool isFilteringHidden;
+};
+
+#endif // HEDGEWARS_THEMEFILTERPROXYMODEL_H
--- a/QTfrontend/model/ThemeModel.cpp	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/model/ThemeModel.cpp	Sun Jun 10 19:12:26 2018 +0200
@@ -33,21 +33,48 @@
     m_themesLoaded = false;
 
     m_filteredNoDLC = NULL;
+    m_filteredNoHidden = NULL;
+    m_filteredNoDLCOrHidden = NULL;
 }
 
-QSortFilterProxyModel * ThemeModel::withoutDLC()
+// Filters out DLC themes, e.g. themes which do not come by default
+ThemeFilterProxyModel * ThemeModel::withoutDLC()
 {
     if (m_filteredNoDLC == NULL)
     {
-        m_filteredNoDLC = new QSortFilterProxyModel(this);
+        m_filteredNoDLC = new ThemeFilterProxyModel(this);
         m_filteredNoDLC->setSourceModel(this);
-        // filtering based on IsDlcRole would be nicer
-        // but seems this model can only do string-based filtering :|
-        m_filteredNoDLC->setFilterRegExp(QRegExp("^[^*]"));
+        m_filteredNoDLC->setFilterDLC(true);
     }
     return m_filteredNoDLC;
 }
 
+// Filters out hidden themes, these are themes which are not supposed to be
+// seen by the user.
+ThemeFilterProxyModel * ThemeModel::withoutHidden()
+{
+    if (m_filteredNoHidden == NULL)
+    {
+        m_filteredNoHidden = new ThemeFilterProxyModel(this);
+        m_filteredNoHidden->setSourceModel(this);
+        m_filteredNoHidden->setFilterHidden(true);
+    }
+    return m_filteredNoHidden;
+}
+
+// Combination of the two above for convenience
+ThemeFilterProxyModel * ThemeModel::withoutDLCOrHidden()
+{
+    if (m_filteredNoDLCOrHidden == NULL)
+    {
+        m_filteredNoDLCOrHidden = new ThemeFilterProxyModel(this);
+        m_filteredNoDLCOrHidden->setSourceModel(this);
+        m_filteredNoDLCOrHidden->setFilterDLC(true);
+        m_filteredNoDLCOrHidden->setFilterHidden(true);
+    }
+    return m_filteredNoDLCOrHidden;
+}
+
 int ThemeModel::rowCount(const QModelIndex &parent) const
 {
     if(parent.isValid())
@@ -95,16 +122,42 @@
 
     foreach (QString theme, themes)
     {
+        QMap<int, QVariant> dataset;
+
         // themes without icon are supposed to be hidden
         QString iconpath = QString("physfs://Themes/%1/icon.png").arg(theme);
 
         if (!QFile::exists(iconpath))
-            continue;
-
-        QMap<int, QVariant> dataset;
+        {
+            dataset.insert(IsHiddenRole, true);
+        }
+        else
+        {
+            // themes with the key “hidden” in theme.cfg are hidden, too
+            QFile themeCfgFile(QString("physfs://Themes/%1/theme.cfg").arg(theme));
+            if (themeCfgFile.open(QFile::ReadOnly))
+            {
+                QTextStream stream(&themeCfgFile);
+                QString line = stream.readLine();
+                QString key;
+                while (!line.isNull())
+                {
+                    key = QString(line);
+                    int equalsPos = line.indexOf('=');
+                    key.truncate(equalsPos - 1);
+                    key = key.simplified();
+                    if (!line.startsWith(';') && key == "hidden")
+                    {
+                        dataset.insert(IsHiddenRole, true);
+                        break;
+                    }
+                    line = stream.readLine();
+                }
+            }
+        }
 
         // detect if theme is dlc
-        QString themeDir = PHYSFS_getRealDir(QString("Themes/%1/icon.png").arg(theme).toLocal8Bit().data());
+        QString themeDir = PHYSFS_getRealDir(QString("Themes/%1").arg(theme).toLocal8Bit().data());
         bool isDLC = !themeDir.startsWith(datadir->absolutePath());
         dataset.insert(IsDlcRole, isDLC);
 
@@ -118,8 +171,12 @@
         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::DecorationRole, preview);
+        iconpath = QString("physfs://Themes/%1/icon@2x.png").arg(theme);
+        if (QFile::exists(iconpath))
+        {
+            QIcon preview(QString("physfs://Themes/%1/icon@2x.png").arg(theme));
+            dataset.insert(Qt::DecorationRole, preview);
+        }
 
         m_data.append(dataset);
     }
--- a/QTfrontend/model/ThemeModel.h	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/model/ThemeModel.h	Sun Jun 10 19:12:26 2018 +0200
@@ -25,11 +25,12 @@
 #define HEDGEWARS_THEMEMODEL_H
 
 #include <QAbstractListModel>
-#include <QSortFilterProxyModel>
 #include <QStringList>
 #include <QMap>
 #include <QIcon>
+#include <QTextStream>
 
+#include "ThemeFilterProxyModel.h"
 #include "DataManager.h"
 
 /**
@@ -40,17 +41,21 @@
         Q_OBJECT
 
     public:
-        enum Roles { ActualNameRole = Qt::UserRole, IsDlcRole, IconPathRole };
+        enum Roles { ActualNameRole = Qt::UserRole, IsDlcRole, IconPathRole, IsHiddenRole };
         explicit ThemeModel(QObject *parent = 0);
 
         int rowCount(const QModelIndex &parent = QModelIndex()) const;
         QVariant data(const QModelIndex &index, int role) const;
-        QSortFilterProxyModel * withoutDLC();
+        ThemeFilterProxyModel * withoutDLC();
+        ThemeFilterProxyModel * withoutHidden();
+        ThemeFilterProxyModel * withoutDLCOrHidden();
 
     private:
         mutable QList<QMap<int, QVariant> > m_data;
         mutable bool m_themesLoaded;
-        mutable QSortFilterProxyModel * m_filteredNoDLC;
+        mutable ThemeFilterProxyModel * m_filteredNoDLC;
+        mutable ThemeFilterProxyModel * m_filteredNoHidden;
+        mutable ThemeFilterProxyModel * m_filteredNoDLCOrHidden;
 
         void loadThemes() const;
 };
--- a/QTfrontend/model/ammoSchemeModel.cpp	Sun Jun 10 18:56:51 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,905 +0,0 @@
-/*
- * Hedgewars, a free turn based strategy game
- * Copyright (c) 2004-2015 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- */
-
-#include <QDebug>
-#include <QModelIndex>
-
-#include "ammoSchemeModel.h"
-#include "hwconsts.h"
-
-QList<QVariant> defaultScheme = QList<QVariant>()
-                                << QVariant("Default")     // name           0
-                                << QVariant(false)         // fortsmode      1
-                                << QVariant(false)         // team divide    2
-                                << QVariant(false)         // solid land     3
-                                << QVariant(false)         // border         4
-                                << QVariant(false)         // low gravity    5
-                                << QVariant(false)         // laser sight    6
-                                << QVariant(false)         // invulnerable   7
-                                << QVariant(false)         // reset health   8
-                                << QVariant(false)         // vampiric       9
-                                << QVariant(false)         // karma          10
-                                << QVariant(false)         // artillery      11
-                                << QVariant(true)          // random order   12
-                                << QVariant(false)         // king           13
-                                << QVariant(false)         // place hog      14
-                                << QVariant(false)         // shared ammo    15
-                                << QVariant(false)         // disable girders 16
-                                << QVariant(false)         // disable land objects 17
-                                << QVariant(false)         // AI survival    18
-                                << QVariant(false)         // inf. attack    19
-                                << QVariant(false)         // reset weps     20
-                                << QVariant(false)         // per hog ammo   21
-                                << QVariant(false)         // no wind        22
-                                << QVariant(false)         // more wind      23
-                                << QVariant(false)         // tag team       24
-                                << QVariant(false)         // bottom border  25
-                                << QVariant(100)           // damage modfier 26
-                                << QVariant(45)            // turn time      27
-                                << QVariant(100)           // init health    28
-                                << QVariant(15)            // sudden death   29
-                                << QVariant(5)             // case prob      30
-                                << QVariant(3)             // mines time     31
-                                << QVariant(4)             // mines number   32
-                                << QVariant(0)             // mine dud pct   33
-                                << QVariant(2)             // explosives     34
-                                << QVariant(0)             // air mines      35
-                                << QVariant(35)            // health case pct 36
-                                << QVariant(25)            // health case amt 37
-                                << QVariant(47)            // water rise amt 38
-                                << QVariant(5)             // health dec amt 39
-                                << QVariant(100)           // rope modfier   40
-                                << QVariant(100)           // get away time  41
-                                << QVariant(0)             // world edge     42
-                                << QVariant()              // scriptparam    43
-                                ;
-
-AmmoSchemeModel::AmmoSchemeModel(QObject* parent, const QString & fileName) :
-    QAbstractTableModel(parent),
-    fileConfig(fileName, QSettings::IniFormat)
-{
-    predefSchemesNames = QStringList()
-                         << "Default"
-                         << "Pro Mode"
-                         << "Shoppa"
-                         << "Clean Slate"
-                         << "Minefield"
-                         << "Barrel Mayhem"
-                         << "Tunnel Hogs"
-                         << "Timeless"
-                         << "Thinking with Portals"
-                         << "King Mode"
-                         << "Construction Mode"
-                         << "HedgeEditor"
-                         ;
-
-    numberOfDefaultSchemes = predefSchemesNames.size();
-
-    spNames = QStringList()
-              << "name"                //  0
-              << "fortsmode"           //  1
-              << "divteams"            //  2
-              << "solidland"           //  3
-              << "border"              //  4
-              << "lowgrav"             //  5
-              << "laser"               //  6
-              << "invulnerability"     //  7
-              << "resethealth"         //  8
-              << "vampiric"            //  9
-              << "karma"               // 10
-              << "artillery"           // 11
-              << "randomorder"         // 12
-              << "king"                // 13
-              << "placehog"            // 14
-              << "sharedammo"          // 15
-              << "disablegirders"      // 16
-              << "disablelandobjects"  // 17
-              << "aisurvival"          // 18
-              << "infattack"           // 19
-              << "resetweps"           // 20
-              << "perhogammo"          // 21
-              << "disablewind"         // 22
-              << "morewind"            // 23
-              << "tagteam"             // 24
-              << "bottomborder"        // 25
-              << "damagefactor"        // 26
-              << "turntime"            // 27
-              << "health"              // 28
-              << "suddendeath"         // 29
-              << "caseprobability"     // 30
-              << "minestime"           // 31
-              << "minesnum"            // 32
-              << "minedudpct"          // 33
-              << "explosives"          // 34
-              << "airmines"            // 35
-              << "healthprobability"   // 36
-              << "healthcaseamount"    // 37
-              << "waterrise"           // 38
-              << "healthdecrease"      // 39
-              << "ropepct"             // 40
-              << "getawaytime"         // 41
-              << "worldedge"           // 42
-              << "scriptparam"         // scriptparam    43
-              ;
-
-    QList<QVariant> proMode;
-    proMode
-            << predefSchemesNames[1]   // name           0
-            << QVariant(false)         // fortsmode      1
-            << QVariant(false)         // team divide    2
-            << QVariant(false)         // solid land     3
-            << QVariant(false)         // border         4
-            << QVariant(false)         // low gravity    5
-            << QVariant(false)         // laser sight    6
-            << QVariant(false)         // invulnerable   7
-            << QVariant(false)         // reset health   8
-            << QVariant(false)         // vampiric       9
-            << QVariant(false)         // karma          10
-            << QVariant(false)         // artillery      11
-            << QVariant(true)          // random order   12
-            << QVariant(false)         // king           13
-            << QVariant(false)         // place hog      14
-            << QVariant(true)          // shared ammo    15
-            << QVariant(false)         // disable girders 16
-            << QVariant(false)         // disable land objects 17
-            << QVariant(false)         // AI survival    18
-            << QVariant(false)         // inf. attack    19
-            << QVariant(false)         // reset weps     20
-            << QVariant(false)         // per hog ammo   21
-            << QVariant(false)         // no wind        22
-            << QVariant(false)         // more wind      23
-            << QVariant(false)         // tag team       24
-            << QVariant(false)         // bottom border  25
-            << QVariant(100)           // damage modfier 26
-            << QVariant(15)            // turn time      27
-            << QVariant(100)           // init health    28
-            << QVariant(15)            // sudden death   29
-            << QVariant(0)             // case prob      30
-            << QVariant(3)             // mines time     31
-            << QVariant(0)             // mines number   32
-            << QVariant(0)             // mine dud pct   33
-            << QVariant(2)             // explosives     34
-            << QVariant(0)             // air mines      35
-            << QVariant(35)            // health case pct 36
-            << QVariant(25)            // health case amt 37
-            << QVariant(47)            // water rise amt 38
-            << QVariant(5)             // health dec amt 39
-            << QVariant(100)           // rope modfier   40
-            << QVariant(100)           // get away time  41
-            << QVariant(0)             // world edge     42
-            << QVariant()              // scriptparam    43
-            ;
-
-    QList<QVariant> shoppa;
-    shoppa
-            << predefSchemesNames[2]   // name           0
-            << QVariant(false)         // fortsmode      1
-            << QVariant(false)         // team divide    2
-            << QVariant(true)          // solid land     3
-            << QVariant(true)          // border         4
-            << QVariant(false)         // low gravity    5
-            << QVariant(false)         // laser sight    6
-            << QVariant(false)         // invulnerable   7
-            << QVariant(false)         // reset health   8
-            << QVariant(false)         // vampiric       9
-            << QVariant(false)         // karma          10
-            << QVariant(false)         // artillery      11
-            << QVariant(true)          // random order   12
-            << QVariant(false)         // king           13
-            << QVariant(false)         // place hog      14
-            << QVariant(true)          // shared ammo    15
-            << QVariant(true)          // disable girders 16
-            << QVariant(true)         // disable land objects 17
-            << QVariant(false)         // AI survival    18
-            << QVariant(false)         // inf. attack    19
-            << QVariant(true)          // reset weps     20
-            << QVariant(false)         // per hog ammo   21
-            << QVariant(false)         // no wind        22
-            << QVariant(false)         // more wind      23
-            << QVariant(false)         // tag team       24
-            << QVariant(false)         // bottom border  25
-            << QVariant(100)           // damage modfier 26
-            << QVariant(30)            // turn time      27
-            << QVariant(100)           // init health    28
-            << QVariant(50)            // sudden death   29
-            << QVariant(1)             // case prob      30
-            << QVariant(0)             // mines time     31
-            << QVariant(0)             // mines number   32
-            << QVariant(0)             // mine dud pct   33
-            << QVariant(0)             // explosives     34
-            << QVariant(0)             // air mines      35
-            << QVariant(0)             // health case pct 36
-            << QVariant(25)            // health case amt 37
-            << QVariant(0)             // water rise amt 38
-            << QVariant(0)             // health dec amt 39
-            << QVariant(100)           // rope modfier   40
-            << QVariant(100)           // get away time  41
-            << QVariant(0)             // world edge     42
-            << QVariant()              // scriptparam    43
-            ;
-
-    QList<QVariant> cleanslate;
-    cleanslate
-            << predefSchemesNames[3]   // name           0
-            << QVariant(false)         // fortsmode      1
-            << QVariant(false)         // team divide    2
-            << QVariant(false)         // solid land     3
-            << QVariant(false)         // border         4
-            << QVariant(false)         // low gravity    5
-            << QVariant(false)         // laser sight    6
-            << QVariant(false)         // invulnerable   7
-            << QVariant(true)          // reset health   8
-            << QVariant(false)         // vampiric       9
-            << QVariant(false)         // karma          10
-            << QVariant(false)         // artillery      11
-            << QVariant(true)          // random order   12
-            << QVariant(false)         // king           13
-            << QVariant(false)         // place hog      14
-            << QVariant(false)         // shared ammo    15
-            << QVariant(false)         // disable girders 16
-            << QVariant(false)         // disable land objects 17
-            << QVariant(false)         // AI survival    18
-            << QVariant(true)          // inf. attack    19
-            << QVariant(true)          // reset weps     20
-            << QVariant(false)         // per hog ammo   21
-            << QVariant(false)         // no wind        22
-            << QVariant(false)         // more wind      23
-            << QVariant(false)         // tag team       24
-            << QVariant(false)         // bottom border  25
-            << QVariant(100)           // damage modfier 26
-            << QVariant(45)            // turn time      27
-            << QVariant(100)           // init health    28
-            << QVariant(15)            // sudden death   29
-            << QVariant(5)             // case prob      30
-            << QVariant(3)             // mines time     31
-            << QVariant(4)             // mines number   32
-            << QVariant(0)             // mine dud pct   33
-            << QVariant(2)             // explosives     34
-            << QVariant(0)             // air mines      35
-            << QVariant(35)            // health case pct 36
-            << QVariant(25)            // health case amt 37
-            << QVariant(47)            // water rise amt 38
-            << QVariant(5)             // health dec amt 39
-            << QVariant(100)           // rope modfier   40
-            << QVariant(100)           // get away time  41
-            << QVariant(0)             // world edge     42
-            << QVariant()              // scriptparam    43
-            ;
-
-    QList<QVariant> minefield;
-    minefield
-            << predefSchemesNames[4]   // name           0
-            << QVariant(false)         // fortsmode      1
-            << QVariant(false)         // team divide    2
-            << QVariant(false)         // solid land     3
-            << QVariant(false)         // border         4
-            << QVariant(false)         // low gravity    5
-            << QVariant(false)         // laser sight    6
-            << QVariant(false)         // invulnerable   7
-            << QVariant(false)         // reset health   8
-            << QVariant(false)         // vampiric       9
-            << QVariant(false)         // karma          10
-            << QVariant(false)         // artillery      11
-            << QVariant(true)          // random order   12
-            << QVariant(false)         // king           13
-            << QVariant(false)         // place hog      14
-            << QVariant(true)          // shared ammo    15
-            << QVariant(true)          // disable girders 16
-            << QVariant(false)         // disable land objects 17
-            << QVariant(false)         // AI survival    18
-            << QVariant(false)         // inf. attack    19
-            << QVariant(false)         // reset weps     20
-            << QVariant(false)         // per hog ammo   21
-            << QVariant(false)         // no wind        22
-            << QVariant(false)         // more wind      23
-            << QVariant(false)         // tag team       24
-            << QVariant(false)         // bottom border  25
-            << QVariant(100)           // damage modfier 26
-            << QVariant(30)            // turn time      27
-            << QVariant(50)            // init health    28
-            << QVariant(15)            // sudden death   29
-            << QVariant(0)             // case prob      30
-            << QVariant(0)             // mines time     31
-            << QVariant(200)           // mines number   32
-            << QVariant(0)             // mine dud pct   33
-            << QVariant(0)             // explosives     34
-            << QVariant(0)             // air mines      35
-            << QVariant(35)            // health case pct 36
-            << QVariant(25)            // health case amt 37
-            << QVariant(47)            // water rise amt 38
-            << QVariant(5)             // health dec amt 39
-            << QVariant(100)           // rope modfier   40
-            << QVariant(100)           // get away time  41
-            << QVariant(0)             // world edge     42
-            << QVariant()              // scriptparam    43
-            ;
-
-    QList<QVariant> barrelmayhem;
-    barrelmayhem
-            << predefSchemesNames[5]   // name           0
-            << QVariant(false)         // fortsmode      1
-            << QVariant(false)         // team divide    2
-            << QVariant(false)         // solid land     3
-            << QVariant(false)         // border         4
-            << QVariant(false)         // low gravity    5
-            << QVariant(false)         // laser sight    6
-            << QVariant(false)         // invulnerable   7
-            << QVariant(false)         // reset health   8
-            << QVariant(false)         // vampiric       9
-            << QVariant(false)         // karma          10
-            << QVariant(false)         // artillery      11
-            << QVariant(true)          // random order   12
-            << QVariant(false)         // king           13
-            << QVariant(false)         // place hog      14
-            << QVariant(true)          // shared ammo    15
-            << QVariant(false)         // disable girders 16
-            << QVariant(false)         // disable land objects 17
-            << QVariant(false)         // AI survival    18
-            << QVariant(false)         // inf. attack    19
-            << QVariant(false)         // reset weps     20
-            << QVariant(false)         // per hog ammo   21
-            << QVariant(false)         // no wind        22
-            << QVariant(false)         // more wind      23
-            << QVariant(false)         // tag team       24
-            << QVariant(false)         // bottom border  25
-            << QVariant(100)           // damage modfier 26
-            << QVariant(30)            // turn time      27
-            << QVariant(100)           // init health    28
-            << QVariant(15)            // sudden death   29
-            << QVariant(0)             // case prob      30
-            << QVariant(0)             // mines time     31
-            << QVariant(0)             // mines number   32
-            << QVariant(0)             // mine dud pct   33
-            << QVariant(200)           // explosives     34
-            << QVariant(0)             // air mines      35
-            << QVariant(35)            // health case pct 36
-            << QVariant(25)            // health case amt 37
-            << QVariant(47)            // water rise amt 38
-            << QVariant(5)             // health dec amt 39
-            << QVariant(100)           // rope modfier   40
-            << QVariant(100)           // get away time  41
-            << QVariant(0)             // world edge     42
-            << QVariant()              // scriptparam    43
-            ;
-
-    QList<QVariant> tunnelhogs;
-    tunnelhogs
-            << predefSchemesNames[6]   // name           0
-            << QVariant(false)         // fortsmode      1
-            << QVariant(false)         // team divide    2
-            << QVariant(false)         // solid land     3
-            << QVariant(true)          // border         4
-            << QVariant(false)         // low gravity    5
-            << QVariant(false)         // laser sight    6
-            << QVariant(false)         // invulnerable   7
-            << QVariant(false)         // reset health   8
-            << QVariant(false)         // vampiric       9
-            << QVariant(false)         // karma          10
-            << QVariant(false)         // artillery      11
-            << QVariant(true)          // random order   12
-            << QVariant(false)         // king           13
-            << QVariant(false)         // place hog      14
-            << QVariant(true)          // shared ammo    15
-            << QVariant(true)          // disable girders 16
-            << QVariant(true)          // disable land objects 17
-            << QVariant(false)         // AI survival    18
-            << QVariant(false)         // inf. attack    19
-            << QVariant(false)         // reset weps     20
-            << QVariant(false)         // per hog ammo   21
-            << QVariant(false)         // no wind        22
-            << QVariant(false)         // more wind      23
-            << QVariant(false)         // tag team       24
-            << QVariant(false)         // bottom border  25
-            << QVariant(100)           // damage modfier 26
-            << QVariant(30)            // turn time      27
-            << QVariant(100)           // init health    28
-            << QVariant(15)            // sudden death   29
-            << QVariant(5)             // case prob      30
-            << QVariant(3)             // mines time     31
-            << QVariant(10)            // mines number   32
-            << QVariant(10)            // mine dud pct   33
-            << QVariant(10)            // explosives     34
-            << QVariant(4)             // air mines      35
-            << QVariant(35)            // health case pct 36
-            << QVariant(25)            // health case amt 37
-            << QVariant(47)            // water rise amt 38
-            << QVariant(5)             // health dec amt 39
-            << QVariant(100)           // rope modfier   40
-            << QVariant(100)           // get away time  41
-            << QVariant(0)             // world edge     42
-            << QVariant()              // scriptparam    43
-            ;
-
-    QList<QVariant> timeless;
-    timeless
-            << predefSchemesNames[7]   // name           0
-            << QVariant(false)         // fortsmode      1
-            << QVariant(false)         // team divide    2
-            << QVariant(false)         // solid land     3
-            << QVariant(false)         // border         4
-            << QVariant(false)         // low gravity    5
-            << QVariant(false)         // laser sight    6
-            << QVariant(false)         // invulnerable   7
-            << QVariant(false)         // reset health   8
-            << QVariant(false)         // vampiric       9
-            << QVariant(false)         // karma          10
-            << QVariant(false)         // artillery      11
-            << QVariant(true)          // random order   12
-            << QVariant(false)         // king           13
-            << QVariant(false)         // place hog      14
-            << QVariant(false)         // shared ammo    15
-            << QVariant(false)         // disable girders 16
-            << QVariant(false)         // disable land objects 17
-            << QVariant(false)         // AI survival    18
-            << QVariant(false)         // inf. attack    19
-            << QVariant(false)         // reset weps     20
-            << QVariant(true)          // per hog ammo   21
-            << QVariant(false)         // no wind        22
-            << QVariant(false)         // more wind      23
-            << QVariant(false)         // tag team       24
-            << QVariant(false)         // bottom border  25
-            << QVariant(100)           // damage modfier 26
-            << QVariant(9999)          // turn time      27
-            << QVariant(100)           // init health    28
-            << QVariant(15)            // sudden death   29
-            << QVariant(5)             // case prob      30
-            << QVariant(3)             // mines time     31
-            << QVariant(5)             // mines number   32
-            << QVariant(10)            // mine dud pct   33
-            << QVariant(2)             // explosives     34
-            << QVariant(0)             // air mines      35
-            << QVariant(35)            // health case pct 36
-            << QVariant(30)            // health case amt 37
-            << QVariant(0)             // water rise amt 38
-            << QVariant(0)             // health dec amt 39
-            << QVariant(100)           // rope modfier   40
-            << QVariant(100)           // get away time  41
-            << QVariant(0)             // world edge     42
-            << QVariant()              // scriptparam    43
-            ;
-
-    QList<QVariant> thinkingportals;
-    thinkingportals
-            << predefSchemesNames[8]   // name           0
-            << QVariant(false)         // fortsmode      1
-            << QVariant(false)         // team divide    2
-            << QVariant(false)         // solid land     3
-            << QVariant(false)         // border         4
-            << QVariant(false)         // low gravity    5
-            << QVariant(false)         // laser sight    6
-            << QVariant(false)         // invulnerable   7
-            << QVariant(false)         // reset health   8
-            << QVariant(false)         // vampiric       9
-            << QVariant(false)         // karma          10
-            << QVariant(true)          // artillery      11
-            << QVariant(true)          // random order   12
-            << QVariant(false)         // king           13
-            << QVariant(false)         // place hog      14
-            << QVariant(false)         // shared ammo    15
-            << QVariant(false)         // disable girders 16
-            << QVariant(false)         // disable land objects 17
-            << QVariant(false)         // AI survival    18
-            << QVariant(false)         // inf. attack    19
-            << QVariant(false)         // reset weps     20
-            << QVariant(false)         // per hog ammo   21
-            << QVariant(false)         // no wind        22
-            << QVariant(false)         // more wind      23
-            << QVariant(false)         // tag team       24
-            << QVariant(false)         // bottom border  25
-            << QVariant(100)           // damage modfier 26
-            << QVariant(45)            // turn time      27
-            << QVariant(100)           // init health    28
-            << QVariant(15)            // sudden death   29
-            << QVariant(2)             // case prob      30
-            << QVariant(3)             // mines time     31
-            << QVariant(5)             // mines number   32
-            << QVariant(0)             // mine dud pct   33
-            << QVariant(5)             // explosives     34
-            << QVariant(4)             // air mines      35
-            << QVariant(25)            // health case pct 36
-            << QVariant(25)            // health case amt 37
-            << QVariant(47)            // water rise amt 38
-            << QVariant(5)             // health dec amt 39
-            << QVariant(100)           // rope modfier   40
-            << QVariant(100)           // get away time  41
-            << QVariant(0)             // world edge     42
-            << QVariant()              // scriptparam    43
-            ;
-
-    QList<QVariant> kingmode;
-    kingmode
-            << predefSchemesNames[9]  // name           0
-            << QVariant(false)         // fortsmode      1
-            << QVariant(false)         // team divide    2
-            << QVariant(false)         // solid land     3
-            << QVariant(false)         // border         4
-            << QVariant(false)         // low gravity    5
-            << QVariant(false)         // laser sight    6
-            << QVariant(false)         // invulnerable   7
-            << QVariant(false)         // reset health   8
-            << QVariant(false)         // vampiric       9
-            << QVariant(false)         // karma          10
-            << QVariant(false)         // artillery      11
-            << QVariant(true)          // random order   12
-            << QVariant(true)          // king           13
-            << QVariant(false)         // place hog      14
-            << QVariant(false)         // shared ammo    15
-            << QVariant(false)         // disable girders 16
-            << QVariant(false)         // disable land objects 17
-            << QVariant(false)         // AI survival    18
-            << QVariant(false)         // inf. attack    19
-            << QVariant(false)         // reset weps     20
-            << QVariant(false)         // per hog ammo   21
-            << QVariant(false)         // no wind        22
-            << QVariant(false)         // more wind      23
-            << QVariant(false)         // tag team       24
-            << QVariant(false)         // bottom border  25
-            << QVariant(100)           // damage modfier 26
-            << QVariant(45)            // turn time      27
-            << QVariant(100)           // init health    28
-            << QVariant(15)            // sudden death   29
-            << QVariant(5)             // case prob      30
-            << QVariant(3)             // mines time     31
-            << QVariant(4)             // mines number   32
-            << QVariant(0)             // mine dud pct   33
-            << QVariant(2)             // explosives     34
-            << QVariant(0)             // air mines      35
-            << QVariant(35)            // health case pct 36
-            << QVariant(25)            // health case amt 37
-            << QVariant(47)            // water rise amt 38
-            << QVariant(5)             // health dec amt 39
-            << QVariant(100)           // rope modfier   40
-            << QVariant(100)           // get away time  41
-            << QVariant(0)             // world edge     42
-            << QVariant()              // scriptparam    43
-            ;
-
-	QList<QVariant> construction;
-    construction
-            << predefSchemesNames[10]  // name           0
-            << QVariant(false)         // fortsmode      1
-            << QVariant(false)         // team divide    2
-            << QVariant(false)         // solid land     3
-            << QVariant(false)         // border         4
-            << QVariant(false)         // low gravity    5
-            << QVariant(false)         // laser sight    6
-            << QVariant(false)         // invulnerable   7
-            << QVariant(false)         // reset health   8
-            << QVariant(false)         // vampiric       9
-            << QVariant(false)         // karma          10
-            << QVariant(false)         // artillery      11
-            << QVariant(true)          // random order   12
-            << QVariant(false)          // king           13
-            << QVariant(false)         // place hog      14
-            << QVariant(false)         // shared ammo    15
-            << QVariant(true)         // disable girders 16
-            << QVariant(true)         // disable land objects 17
-            << QVariant(false)         // AI survival    18
-            << QVariant(true)         // inf. attack    19
-            << QVariant(false)         // reset weps     20
-            << QVariant(true)         // per hog ammo   21
-            << QVariant(false)         // no wind        22
-            << QVariant(false)         // more wind      23
-            << QVariant(false)         // tag team       24
-            << QVariant(false)         // bottom border  25
-            << QVariant(100)           // damage modfier 26
-            << QVariant(45)            // turn time      27
-            << QVariant(100)           // init health    28
-            << QVariant(15)            // sudden death   29
-            << QVariant(5)             // case prob      30
-            << QVariant(3)             // mines time     31
-            << QVariant(0)             // mines number   32
-            << QVariant(0)             // mine dud pct   33
-            << QVariant(0)             // explosives     34
-            << QVariant(0)             // air mines      35
-            << QVariant(35)            // health case pct 36
-            << QVariant(25)            // health case amt 37
-            << QVariant(47)            // water rise amt 38
-            << QVariant(5)             // health dec amt 39
-            << QVariant(100)           // rope modfier   40
-            << QVariant(100)           // get away time  41
-            << QVariant(0)             // world edge     42
-            << QVariant()              // scriptparam    43
-            ;
-
-	QList<QVariant> hedgeeditor;
-    hedgeeditor
-            << predefSchemesNames[11]  // name           0
-            << QVariant(false)         // fortsmode      1
-            << QVariant(false)         // team divide    2
-            << QVariant(false)         // solid land     3
-            << QVariant(false)         // border         4
-            << QVariant(false)         // low gravity    5
-            << QVariant(false)         // laser sight    6
-            << QVariant(false)         // invulnerable   7
-            << QVariant(false)         // reset health   8
-            << QVariant(false)         // vampiric       9
-            << QVariant(false)         // karma          10
-            << QVariant(false)         // artillery      11
-            << QVariant(false)         // random order   12
-            << QVariant(false)         // king           13
-            << QVariant(false)         // place hog      14
-            << QVariant(false)         // shared ammo    15
-            << QVariant(false)         // disable girders 16
-            << QVariant(false)         // disable land objects 17
-            << QVariant(false)         // AI survival    18
-            << QVariant(false)         // inf. attack    19
-            << QVariant(false)         // reset weps     20
-            << QVariant(true)          // per hog ammo   21
-            << QVariant(false)         // no wind        22
-            << QVariant(false)         // more wind      23
-            << QVariant(false)         // tag team       24
-            << QVariant(false)         // bottom border  25
-            << QVariant(100)           // damage modfier 26
-            << QVariant(9999)          // turn time      27
-            << QVariant(100)           // init health    28
-            << QVariant(50)            // sudden death   29
-            << QVariant(5)             // case prob      30
-            << QVariant(3)             // mines time     31
-            << QVariant(0)             // mines number   32
-            << QVariant(0)             // mine dud pct   33
-            << QVariant(0)             // explosives     34
-            << QVariant(0)             // air mines      35
-            << QVariant(35)            // health case pct 36
-            << QVariant(25)            // health case amt 37
-            << QVariant(0)            // water rise amt 38
-            << QVariant(0)             // health dec amt 39
-            << QVariant(100)           // rope modfier   40
-            << QVariant(100)           // get away time  41
-            << QVariant(0)             // world edge     42
-            << QVariant()              // scriptparam    43
-            ;
-			
- 
-			
-    schemes.append(defaultScheme);
-    schemes.append(proMode);
-    schemes.append(shoppa);
-    schemes.append(cleanslate);
-    schemes.append(minefield);
-    schemes.append(barrelmayhem);
-    schemes.append(tunnelhogs);
-    schemes.append(timeless);
-    schemes.append(thinkingportals);
-    schemes.append(kingmode);
-    schemes.append(construction);
-    schemes.append(hedgeeditor);
-
-
-    int size = fileConfig.beginReadArray("schemes");
-    for (int i = 0; i < size; ++i)
-    {
-        fileConfig.setArrayIndex(i);
-
-        if (!predefSchemesNames.contains(fileConfig.value(spNames[0]).toString()))
-        {
-            QList<QVariant> scheme;
-
-            for (int k = 0; k < spNames.size(); ++k)
-                scheme << fileConfig.value(spNames[k], defaultScheme[k]);
-
-            schemes.append(scheme);
-        }
-    }
-    fileConfig.endArray();
-}
-
-QVariant AmmoSchemeModel::headerData(int section, Qt::Orientation orientation, int role) const
-{
-    Q_UNUSED(section);
-    Q_UNUSED(orientation);
-    Q_UNUSED(role);
-
-    return QVariant();
-}
-
-int AmmoSchemeModel::rowCount(const QModelIndex &parent) const
-{
-    if (parent.isValid())
-        return 0;
-    else
-        return schemes.size();
-}
-
-int AmmoSchemeModel::columnCount(const QModelIndex & parent) const
-{
-    if (parent.isValid())
-        return 0;
-    else
-        return defaultScheme.size();
-}
-
-bool AmmoSchemeModel::hasScheme(QString name)
-{
-    for(int i=0; i<schemes.size(); i++)
-    {
-        if(schemes[i][0] == name)
-        {
-            return true;
-        }
-    }
-    return false;
-}
-
-Qt::ItemFlags AmmoSchemeModel::flags(const QModelIndex & index) const
-{
-    Q_UNUSED(index);
-
-    return
-        Qt::ItemIsEnabled
-        | Qt::ItemIsSelectable
-        | Qt::ItemIsEditable;
-}
-
-bool AmmoSchemeModel::setData(const QModelIndex & index, const QVariant & value, int role)
-{
-    if (!index.isValid() || index.row() < numberOfDefaultSchemes
-            || index.row() >= schemes.size()
-            || index.column() >= defaultScheme.size()
-            || role != Qt::EditRole)
-        return false;
-
-    schemes[index.row()][index.column()] = value;
-
-    emit dataChanged(index, index);
-    return true;
-}
-
-bool AmmoSchemeModel::insertRows(int row, int count, const QModelIndex & parent)
-{
-    Q_UNUSED(count);
-
-    beginInsertRows(parent, schemes.size(), schemes.size());
-
-    if (row == -1)
-    {
-        QList<QVariant> newScheme = defaultScheme;
-
-        QString newName = tr("New");
-        if(hasScheme(newName))
-        {
-            //name already used -> look for an appropriate name:
-            int i=2;
-            while(hasScheme(newName = tr("New (%1)").arg(i++))) ;
-        }
-        newScheme[0] = QVariant(newName);
-        schemes.insert(schemes.size(), newScheme);
-    }
-    else
-    {
-        QList<QVariant> newScheme = schemes[row];
-        QString oldName = newScheme[0].toString();
-        QString newName = tr("Copy of %1").arg(oldName);
-        if(hasScheme(newName))
-        {
-            //name already used -> look for an appropriate name:
-            int i=2;
-            while(hasScheme(newName = tr("Copy of %1 (%2)").arg(oldName).arg(i++)));
-        }
-        newScheme[0] = QVariant(newName);
-        schemes.insert(schemes.size(), newScheme);
-    }
-
-    endInsertRows();
-
-    return true;
-}
-
-bool AmmoSchemeModel::removeRows(int row, int count, const QModelIndex & parent)
-{
-    if(count != 1
-            || row < numberOfDefaultSchemes
-            || row >= schemes.size())
-        return false;
-
-    beginRemoveRows(parent, row, row);
-
-    schemes.removeAt(row);
-
-    endRemoveRows();
-
-    return true;
-}
-
-QVariant AmmoSchemeModel::data(const QModelIndex &index, int role) const
-{
-    if (!index.isValid() || index.row() < 0
-            || index.row() >= schemes.size()
-            || index.column() >= defaultScheme.size()
-            || (role != Qt::EditRole && role != Qt::DisplayRole)
-       )
-        return QVariant();
-
-    return schemes[index.row()][index.column()];
-}
-
-void AmmoSchemeModel::Save()
-{
-    fileConfig.beginWriteArray("schemes", schemes.size() - numberOfDefaultSchemes);
-
-    for (int i = 0; i < schemes.size() - numberOfDefaultSchemes; ++i)
-    {
-        fileConfig.setArrayIndex(i);
-
-        QList<QVariant> scheme = schemes[i + numberOfDefaultSchemes];
-
-        for (int k = 0; k < scheme.size(); ++k)
-            fileConfig.setValue(spNames[k], scheme[k]);
-    }
-    fileConfig.endArray();
-}
-
-
-NetAmmoSchemeModel::NetAmmoSchemeModel(QObject * parent) :
-    QAbstractTableModel(parent)
-{
-    netScheme = defaultScheme;
-}
-
-QVariant NetAmmoSchemeModel::headerData(int section, Qt::Orientation orientation, int role) const
-{
-    Q_UNUSED(section);
-    Q_UNUSED(orientation);
-    Q_UNUSED(role);
-
-    return QVariant();
-}
-
-int NetAmmoSchemeModel::rowCount(const QModelIndex & parent) const
-{
-    if (parent.isValid())
-        return 0;
-    else
-        return 1;
-}
-
-int NetAmmoSchemeModel::columnCount(const QModelIndex & parent) const
-{
-    if (parent.isValid())
-        return 0;
-    else
-        return defaultScheme.size();
-}
-
-QVariant NetAmmoSchemeModel::data(const QModelIndex &index, int role) const
-{
-    if (!index.isValid() || index.row() < 0
-            || index.row() > 1
-            || index.column() >= defaultScheme.size()
-            || (role != Qt::EditRole && role != Qt::DisplayRole)
-       )
-        return QVariant();
-
-    return netScheme[index.column()];
-}
-
-void NetAmmoSchemeModel::setNetSchemeConfig(QStringList cfg)
-{
-    if(cfg.size() != netScheme.size())
-    {
-        qWarning("Incorrect scheme cfg size");
-        return;
-    }
-
-    cfg[cfg.size()-1] = cfg[cfg.size()-1].mid(1);
-
-    for(int i = 0; i < cfg.size(); ++i)
-        netScheme[i] = QVariant(cfg[i]);
-
-    reset();
-}
--- a/QTfrontend/model/ammoSchemeModel.h	Sun Jun 10 18:56:51 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,80 +0,0 @@
-/*
- * Hedgewars, a free turn based strategy game
- * Copyright (c) 2004-2015 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- */
-
-#ifndef _AMMO_SCHEME_MODEL_INCLUDED
-#define _AMMO_SCHEME_MODEL_INCLUDED
-
-#include <QSettings>
-#include <QAbstractTableModel>
-#include <QStringList>
-#include <QList>
-
-class AmmoSchemeModel : public QAbstractTableModel
-{
-        Q_OBJECT
-
-    public:
-        AmmoSchemeModel(QObject * parent, const QString & fileName);
-
-        QVariant headerData(int section, Qt::Orientation orientation, int role) const;
-        int rowCount(const QModelIndex & parent) const;
-        int columnCount(const QModelIndex & parent) const;
-        bool hasScheme(QString name);
-        Qt::ItemFlags flags(const QModelIndex & index) const;
-        bool setData(const QModelIndex & index, const QVariant & value, int role = Qt::EditRole);
-        bool insertRows(int row, int count, const QModelIndex & parent = QModelIndex());
-        bool removeRows(int row, int count, const QModelIndex & parent = QModelIndex());
-        QVariant data(const QModelIndex &index, int role) const;
-
-        int numberOfDefaultSchemes;
-        QStringList predefSchemesNames;
-        QStringList spNames;
-
-    public slots:
-        void Save();
-
-//    signals:
-//        void dataChanged(const QModelIndex & topLeft, const QModelIndex & bottomRight);
-
-    protected:
-        QList< QList<QVariant> > schemes;
-
-    private:
-        QSettings fileConfig;
-};
-
-class NetAmmoSchemeModel : public QAbstractTableModel
-{
-        Q_OBJECT
-
-    public:
-        NetAmmoSchemeModel(QObject * parent);
-
-        QVariant headerData(int section, Qt::Orientation orientation, int role) const;
-        int rowCount(const QModelIndex & parent) const;
-        int columnCount(const QModelIndex & parent) const;
-        QVariant data(const QModelIndex &index, int role) const;
-
-    public slots:
-        void setNetSchemeConfig(QStringList cfg);
-
-    private:
-        QList<QVariant> netScheme;
-};
-
-#endif // _AMMO_SCHEME_MODEL_INCLUDED
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/QTfrontend/model/gameSchemeModel.cpp	Sun Jun 10 19:12:26 2018 +0200
@@ -0,0 +1,1049 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (c) 2004-2015 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <QDebug>
+#include <QModelIndex>
+#include <QFile>
+#include <QSettings>
+#include <QTextStream>
+#include <QHash>
+
+#include "gameSchemeModel.h"
+#include "hwconsts.h"
+
+QList<QVariant> defaultScheme = QList<QVariant>()
+                                << QVariant("Default")     // name           0
+                                << QVariant(false)         // switchhog      1
+                                << QVariant(false)         // team divide    2
+                                << QVariant(false)         // solid land     3
+                                << QVariant(false)         // border         4
+                                << QVariant(false)         // low gravity    5
+                                << QVariant(false)         // laser sight    6
+                                << QVariant(false)         // invulnerable   7
+                                << QVariant(false)         // reset health   8
+                                << QVariant(false)         // vampiric       9
+                                << QVariant(false)         // karma          10
+                                << QVariant(false)         // artillery      11
+                                << QVariant(true)          // random order   12
+                                << QVariant(false)         // king           13
+                                << QVariant(false)         // place hog      14
+                                << QVariant(false)         // shared ammo    15
+                                << QVariant(false)         // disable girders 16
+                                << QVariant(false)         // disable land objects 17
+                                << QVariant(false)         // AI survival    18
+                                << QVariant(false)         // inf. attack    19
+                                << QVariant(false)         // reset weps     20
+                                << QVariant(false)         // per hog ammo   21
+                                << QVariant(false)         // no wind        22
+                                << QVariant(false)         // more wind      23
+                                << QVariant(false)         // tag team       24
+                                << QVariant(false)         // bottom border  25
+                                << QVariant(100)           // damage modfier 26
+                                << QVariant(45)            // turn time      27
+                                << QVariant(100)           // init health    28
+                                << QVariant(15)            // sudden death   29
+                                << QVariant(5)             // case prob      30
+                                << QVariant(3)             // mines time     31
+                                << QVariant(4)             // mines number   32
+                                << QVariant(0)             // mine dud pct   33
+                                << QVariant(2)             // explosives     34
+                                << QVariant(0)             // air mines      35
+                                << QVariant(35)            // health case pct 36
+                                << QVariant(25)            // health case amt 37
+                                << QVariant(47)            // water rise amt 38
+                                << QVariant(5)             // health dec amt 39
+                                << QVariant(100)           // rope modfier   40
+                                << QVariant(100)           // get away time  41
+                                << QVariant(0)             // world edge     42
+                                << QVariant()              // scriptparam    43
+                                ;
+
+GameSchemeModel::GameSchemeModel(QObject* parent, const QString & directory) :
+    QAbstractTableModel(parent)
+{
+    predefSchemesNames = QStringList()
+                         << "Default"
+                         << "Pro Mode"
+                         << "Shoppa"
+                         << "Clean Slate"
+                         << "Minefield"
+                         << "Barrel Mayhem"
+                         << "Tunnel Hogs"
+                         << "Timeless"
+                         << "Thinking with Portals"
+                         << "King Mode"
+                         << "Construction Mode"
+                         << "Space Invasion"
+                         << "HedgeEditor"
+                         ;
+
+    numberOfDefaultSchemes = predefSchemesNames.size();
+
+    spNames = QStringList()
+              << "name"                //  0 | Name should be first forever
+              << "switchhog"           //  1
+              << "divteams"            //  2
+              << "solidland"           //  3
+              << "border"              //  4
+              << "lowgrav"             //  5
+              << "laser"               //  6
+              << "invulnerability"     //  7
+              << "resethealth"         //  8
+              << "vampiric"            //  9
+              << "karma"               // 10
+              << "artillery"           // 11
+              << "randomorder"         // 12
+              << "king"                // 13
+              << "placehog"            // 14
+              << "sharedammo"          // 15
+              << "disablegirders"      // 16
+              << "disablelandobjects"  // 17
+              << "aisurvival"          // 18
+              << "infattack"           // 19
+              << "resetweps"           // 20
+              << "perhogammo"          // 21
+              << "disablewind"         // 22
+              << "morewind"            // 23
+              << "tagteam"             // 24
+              << "bottomborder"        // 25
+              << "damagefactor"        // 26
+              << "turntime"            // 27
+              << "health"              // 28
+              << "suddendeath"         // 29
+              << "caseprobability"     // 30
+              << "minestime"           // 31
+              << "minesnum"            // 32
+              << "minedudpct"          // 33
+              << "explosives"          // 34
+              << "airmines"            // 35
+              << "healthprobability"   // 36
+              << "healthcaseamount"    // 37
+              << "waterrise"           // 38
+              << "healthdecrease"      // 39
+              << "ropepct"             // 40
+              << "getawaytime"         // 41
+              << "worldedge"           // 42
+              << "scriptparam"         // scriptparam    43
+              ;
+
+    QList<QVariant> proMode;
+    proMode
+            << predefSchemesNames[1]   // name           0
+            << QVariant(false)         // switchhog      1
+            << QVariant(false)         // team divide    2
+            << QVariant(false)         // solid land     3
+            << QVariant(false)         // border         4
+            << QVariant(false)         // low gravity    5
+            << QVariant(false)         // laser sight    6
+            << QVariant(false)         // invulnerable   7
+            << QVariant(false)         // reset health   8
+            << QVariant(false)         // vampiric       9
+            << QVariant(false)         // karma          10
+            << QVariant(false)         // artillery      11
+            << QVariant(true)          // random order   12
+            << QVariant(false)         // king           13
+            << QVariant(false)         // place hog      14
+            << QVariant(true)          // shared ammo    15
+            << QVariant(false)         // disable girders 16
+            << QVariant(false)         // disable land objects 17
+            << QVariant(false)         // AI survival    18
+            << QVariant(false)         // inf. attack    19
+            << QVariant(false)         // reset weps     20
+            << QVariant(false)         // per hog ammo   21
+            << QVariant(false)         // no wind        22
+            << QVariant(false)         // more wind      23
+            << QVariant(false)         // tag team       24
+            << QVariant(false)         // bottom border  25
+            << QVariant(100)           // damage modfier 26
+            << QVariant(15)            // turn time      27
+            << QVariant(100)           // init health    28
+            << QVariant(15)            // sudden death   29
+            << QVariant(0)             // case prob      30
+            << QVariant(3)             // mines time     31
+            << QVariant(0)             // mines number   32
+            << QVariant(0)             // mine dud pct   33
+            << QVariant(2)             // explosives     34
+            << QVariant(0)             // air mines      35
+            << QVariant(35)            // health case pct 36
+            << QVariant(25)            // health case amt 37
+            << QVariant(47)            // water rise amt 38
+            << QVariant(5)             // health dec amt 39
+            << QVariant(100)           // rope modfier   40
+            << QVariant(100)           // get away time  41
+            << QVariant(0)             // world edge     42
+            << QVariant()              // scriptparam    43
+            ;
+
+    QList<QVariant> shoppa;
+    shoppa
+            << predefSchemesNames[2]   // name           0
+            << QVariant(false)         // switchhog      1
+            << QVariant(false)         // team divide    2
+            << QVariant(true)          // solid land     3
+            << QVariant(true)          // border         4
+            << QVariant(false)         // low gravity    5
+            << QVariant(false)         // laser sight    6
+            << QVariant(false)         // invulnerable   7
+            << QVariant(false)         // reset health   8
+            << QVariant(false)         // vampiric       9
+            << QVariant(false)         // karma          10
+            << QVariant(false)         // artillery      11
+            << QVariant(true)          // random order   12
+            << QVariant(false)         // king           13
+            << QVariant(false)         // place hog      14
+            << QVariant(true)          // shared ammo    15
+            << QVariant(true)          // disable girders 16
+            << QVariant(true)         // disable land objects 17
+            << QVariant(false)         // AI survival    18
+            << QVariant(false)         // inf. attack    19
+            << QVariant(true)          // reset weps     20
+            << QVariant(false)         // per hog ammo   21
+            << QVariant(false)         // no wind        22
+            << QVariant(false)         // more wind      23
+            << QVariant(false)         // tag team       24
+            << QVariant(false)         // bottom border  25
+            << QVariant(100)           // damage modfier 26
+            << QVariant(30)            // turn time      27
+            << QVariant(100)           // init health    28
+            << QVariant(50)            // sudden death   29
+            << QVariant(1)             // case prob      30
+            << QVariant(0)             // mines time     31
+            << QVariant(0)             // mines number   32
+            << QVariant(0)             // mine dud pct   33
+            << QVariant(0)             // explosives     34
+            << QVariant(0)             // air mines      35
+            << QVariant(0)             // health case pct 36
+            << QVariant(25)            // health case amt 37
+            << QVariant(0)             // water rise amt 38
+            << QVariant(0)             // health dec amt 39
+            << QVariant(100)           // rope modfier   40
+            << QVariant(100)           // get away time  41
+            << QVariant(0)             // world edge     42
+            << QVariant()              // scriptparam    43
+            ;
+
+    QList<QVariant> cleanslate;
+    cleanslate
+            << predefSchemesNames[3]   // name           0
+            << QVariant(false)         // switchhog      1
+            << QVariant(false)         // team divide    2
+            << QVariant(false)         // solid land     3
+            << QVariant(false)         // border         4
+            << QVariant(false)         // low gravity    5
+            << QVariant(false)         // laser sight    6
+            << QVariant(false)         // invulnerable   7
+            << QVariant(true)          // reset health   8
+            << QVariant(false)         // vampiric       9
+            << QVariant(false)         // karma          10
+            << QVariant(false)         // artillery      11
+            << QVariant(true)          // random order   12
+            << QVariant(false)         // king           13
+            << QVariant(false)         // place hog      14
+            << QVariant(false)         // shared ammo    15
+            << QVariant(false)         // disable girders 16
+            << QVariant(false)         // disable land objects 17
+            << QVariant(false)         // AI survival    18
+            << QVariant(true)          // inf. attack    19
+            << QVariant(true)          // reset weps     20
+            << QVariant(false)         // per hog ammo   21
+            << QVariant(false)         // no wind        22
+            << QVariant(false)         // more wind      23
+            << QVariant(false)         // tag team       24
+            << QVariant(false)         // bottom border  25
+            << QVariant(100)           // damage modfier 26
+            << QVariant(45)            // turn time      27
+            << QVariant(100)           // init health    28
+            << QVariant(15)            // sudden death   29
+            << QVariant(5)             // case prob      30
+            << QVariant(3)             // mines time     31
+            << QVariant(4)             // mines number   32
+            << QVariant(0)             // mine dud pct   33
+            << QVariant(2)             // explosives     34
+            << QVariant(0)             // air mines      35
+            << QVariant(35)            // health case pct 36
+            << QVariant(25)            // health case amt 37
+            << QVariant(47)            // water rise amt 38
+            << QVariant(5)             // health dec amt 39
+            << QVariant(100)           // rope modfier   40
+            << QVariant(100)           // get away time  41
+            << QVariant(0)             // world edge     42
+            << QVariant()              // scriptparam    43
+            ;
+
+    QList<QVariant> minefield;
+    minefield
+            << predefSchemesNames[4]   // name           0
+            << QVariant(false)         // switchhog      1
+            << QVariant(false)         // team divide    2
+            << QVariant(false)         // solid land     3
+            << QVariant(false)         // border         4
+            << QVariant(false)         // low gravity    5
+            << QVariant(false)         // laser sight    6
+            << QVariant(false)         // invulnerable   7
+            << QVariant(false)         // reset health   8
+            << QVariant(false)         // vampiric       9
+            << QVariant(false)         // karma          10
+            << QVariant(false)         // artillery      11
+            << QVariant(true)          // random order   12
+            << QVariant(false)         // king           13
+            << QVariant(false)         // place hog      14
+            << QVariant(true)          // shared ammo    15
+            << QVariant(true)          // disable girders 16
+            << QVariant(false)         // disable land objects 17
+            << QVariant(false)         // AI survival    18
+            << QVariant(false)         // inf. attack    19
+            << QVariant(false)         // reset weps     20
+            << QVariant(false)         // per hog ammo   21
+            << QVariant(false)         // no wind        22
+            << QVariant(false)         // more wind      23
+            << QVariant(false)         // tag team       24
+            << QVariant(false)         // bottom border  25
+            << QVariant(100)           // damage modfier 26
+            << QVariant(30)            // turn time      27
+            << QVariant(50)            // init health    28
+            << QVariant(15)            // sudden death   29
+            << QVariant(0)             // case prob      30
+            << QVariant(0)             // mines time     31
+            << QVariant(200)           // mines number   32
+            << QVariant(0)             // mine dud pct   33
+            << QVariant(0)             // explosives     34
+            << QVariant(0)             // air mines      35
+            << QVariant(35)            // health case pct 36
+            << QVariant(25)            // health case amt 37
+            << QVariant(47)            // water rise amt 38
+            << QVariant(5)             // health dec amt 39
+            << QVariant(100)           // rope modfier   40
+            << QVariant(100)           // get away time  41
+            << QVariant(0)             // world edge     42
+            << QVariant()              // scriptparam    43
+            ;
+
+    QList<QVariant> barrelmayhem;
+    barrelmayhem
+            << predefSchemesNames[5]   // name           0
+            << QVariant(false)         // switchhog      1
+            << QVariant(false)         // team divide    2
+            << QVariant(false)         // solid land     3
+            << QVariant(false)         // border         4
+            << QVariant(false)         // low gravity    5
+            << QVariant(false)         // laser sight    6
+            << QVariant(false)         // invulnerable   7
+            << QVariant(false)         // reset health   8
+            << QVariant(false)         // vampiric       9
+            << QVariant(false)         // karma          10
+            << QVariant(false)         // artillery      11
+            << QVariant(true)          // random order   12
+            << QVariant(false)         // king           13
+            << QVariant(false)         // place hog      14
+            << QVariant(true)          // shared ammo    15
+            << QVariant(false)         // disable girders 16
+            << QVariant(false)         // disable land objects 17
+            << QVariant(false)         // AI survival    18
+            << QVariant(false)         // inf. attack    19
+            << QVariant(false)         // reset weps     20
+            << QVariant(false)         // per hog ammo   21
+            << QVariant(false)         // no wind        22
+            << QVariant(false)         // more wind      23
+            << QVariant(false)         // tag team       24
+            << QVariant(false)         // bottom border  25
+            << QVariant(100)           // damage modfier 26
+            << QVariant(30)            // turn time      27
+            << QVariant(100)           // init health    28
+            << QVariant(15)            // sudden death   29
+            << QVariant(0)             // case prob      30
+            << QVariant(0)             // mines time     31
+            << QVariant(0)             // mines number   32
+            << QVariant(0)             // mine dud pct   33
+            << QVariant(200)           // explosives     34
+            << QVariant(0)             // air mines      35
+            << QVariant(35)            // health case pct 36
+            << QVariant(25)            // health case amt 37
+            << QVariant(47)            // water rise amt 38
+            << QVariant(5)             // health dec amt 39
+            << QVariant(100)           // rope modfier   40
+            << QVariant(100)           // get away time  41
+            << QVariant(0)             // world edge     42
+            << QVariant()              // scriptparam    43
+            ;
+
+    QList<QVariant> tunnelhogs;
+    tunnelhogs
+            << predefSchemesNames[6]   // name           0
+            << QVariant(false)         // switchhog      1
+            << QVariant(false)         // team divide    2
+            << QVariant(false)         // solid land     3
+            << QVariant(true)          // border         4
+            << QVariant(false)         // low gravity    5
+            << QVariant(false)         // laser sight    6
+            << QVariant(false)         // invulnerable   7
+            << QVariant(false)         // reset health   8
+            << QVariant(false)         // vampiric       9
+            << QVariant(false)         // karma          10
+            << QVariant(false)         // artillery      11
+            << QVariant(true)          // random order   12
+            << QVariant(false)         // king           13
+            << QVariant(false)         // place hog      14
+            << QVariant(true)          // shared ammo    15
+            << QVariant(true)          // disable girders 16
+            << QVariant(true)          // disable land objects 17
+            << QVariant(false)         // AI survival    18
+            << QVariant(false)         // inf. attack    19
+            << QVariant(false)         // reset weps     20
+            << QVariant(false)         // per hog ammo   21
+            << QVariant(false)         // no wind        22
+            << QVariant(false)         // more wind      23
+            << QVariant(false)         // tag team       24
+            << QVariant(false)         // bottom border  25
+            << QVariant(100)           // damage modfier 26
+            << QVariant(30)            // turn time      27
+            << QVariant(100)           // init health    28
+            << QVariant(15)            // sudden death   29
+            << QVariant(5)             // case prob      30
+            << QVariant(3)             // mines time     31
+            << QVariant(10)            // mines number   32
+            << QVariant(10)            // mine dud pct   33
+            << QVariant(10)            // explosives     34
+            << QVariant(4)             // air mines      35
+            << QVariant(35)            // health case pct 36
+            << QVariant(25)            // health case amt 37
+            << QVariant(47)            // water rise amt 38
+            << QVariant(5)             // health dec amt 39
+            << QVariant(100)           // rope modfier   40
+            << QVariant(100)           // get away time  41
+            << QVariant(0)             // world edge     42
+            << QVariant()              // scriptparam    43
+            ;
+
+    QList<QVariant> timeless;
+    timeless
+            << predefSchemesNames[7]   // name           0
+            << QVariant(false)         // switchhog      1
+            << QVariant(false)         // team divide    2
+            << QVariant(false)         // solid land     3
+            << QVariant(false)         // border         4
+            << QVariant(false)         // low gravity    5
+            << QVariant(false)         // laser sight    6
+            << QVariant(false)         // invulnerable   7
+            << QVariant(false)         // reset health   8
+            << QVariant(false)         // vampiric       9
+            << QVariant(false)         // karma          10
+            << QVariant(false)         // artillery      11
+            << QVariant(true)          // random order   12
+            << QVariant(false)         // king           13
+            << QVariant(false)         // place hog      14
+            << QVariant(false)         // shared ammo    15
+            << QVariant(false)         // disable girders 16
+            << QVariant(false)         // disable land objects 17
+            << QVariant(false)         // AI survival    18
+            << QVariant(false)         // inf. attack    19
+            << QVariant(false)         // reset weps     20
+            << QVariant(true)          // per hog ammo   21
+            << QVariant(false)         // no wind        22
+            << QVariant(false)         // more wind      23
+            << QVariant(false)         // tag team       24
+            << QVariant(false)         // bottom border  25
+            << QVariant(100)           // damage modfier 26
+            << QVariant(9999)          // turn time      27
+            << QVariant(100)           // init health    28
+            << QVariant(15)            // sudden death   29
+            << QVariant(5)             // case prob      30
+            << QVariant(3)             // mines time     31
+            << QVariant(5)             // mines number   32
+            << QVariant(10)            // mine dud pct   33
+            << QVariant(2)             // explosives     34
+            << QVariant(0)             // air mines      35
+            << QVariant(35)            // health case pct 36
+            << QVariant(30)            // health case amt 37
+            << QVariant(0)             // water rise amt 38
+            << QVariant(0)             // health dec amt 39
+            << QVariant(100)           // rope modfier   40
+            << QVariant(100)           // get away time  41
+            << QVariant(0)             // world edge     42
+            << QVariant()              // scriptparam    43
+            ;
+
+    QList<QVariant> thinkingportals;
+    thinkingportals
+            << predefSchemesNames[8]   // name           0
+            << QVariant(false)         // switchhog      1
+            << QVariant(false)         // team divide    2
+            << QVariant(false)         // solid land     3
+            << QVariant(false)         // border         4
+            << QVariant(false)         // low gravity    5
+            << QVariant(false)         // laser sight    6
+            << QVariant(false)         // invulnerable   7
+            << QVariant(false)         // reset health   8
+            << QVariant(false)         // vampiric       9
+            << QVariant(false)         // karma          10
+            << QVariant(true)          // artillery      11
+            << QVariant(true)          // random order   12
+            << QVariant(false)         // king           13
+            << QVariant(false)         // place hog      14
+            << QVariant(false)         // shared ammo    15
+            << QVariant(false)         // disable girders 16
+            << QVariant(false)         // disable land objects 17
+            << QVariant(false)         // AI survival    18
+            << QVariant(false)         // inf. attack    19
+            << QVariant(false)         // reset weps     20
+            << QVariant(false)         // per hog ammo   21
+            << QVariant(false)         // no wind        22
+            << QVariant(false)         // more wind      23
+            << QVariant(false)         // tag team       24
+            << QVariant(false)         // bottom border  25
+            << QVariant(100)           // damage modfier 26
+            << QVariant(45)            // turn time      27
+            << QVariant(100)           // init health    28
+            << QVariant(15)            // sudden death   29
+            << QVariant(2)             // case prob      30
+            << QVariant(3)             // mines time     31
+            << QVariant(5)             // mines number   32
+            << QVariant(0)             // mine dud pct   33
+            << QVariant(5)             // explosives     34
+            << QVariant(4)             // air mines      35
+            << QVariant(25)            // health case pct 36
+            << QVariant(25)            // health case amt 37
+            << QVariant(47)            // water rise amt 38
+            << QVariant(5)             // health dec amt 39
+            << QVariant(100)           // rope modfier   40
+            << QVariant(100)           // get away time  41
+            << QVariant(0)             // world edge     42
+            << QVariant()              // scriptparam    43
+            ;
+
+    QList<QVariant> kingmode;
+    kingmode
+            << predefSchemesNames[9]  // name           0
+            << QVariant(false)         // switchhog      1
+            << QVariant(false)         // team divide    2
+            << QVariant(false)         // solid land     3
+            << QVariant(false)         // border         4
+            << QVariant(false)         // low gravity    5
+            << QVariant(false)         // laser sight    6
+            << QVariant(false)         // invulnerable   7
+            << QVariant(false)         // reset health   8
+            << QVariant(false)         // vampiric       9
+            << QVariant(false)         // karma          10
+            << QVariant(false)         // artillery      11
+            << QVariant(true)          // random order   12
+            << QVariant(true)          // king           13
+            << QVariant(false)         // place hog      14
+            << QVariant(false)         // shared ammo    15
+            << QVariant(false)         // disable girders 16
+            << QVariant(false)         // disable land objects 17
+            << QVariant(false)         // AI survival    18
+            << QVariant(false)         // inf. attack    19
+            << QVariant(false)         // reset weps     20
+            << QVariant(false)         // per hog ammo   21
+            << QVariant(false)         // no wind        22
+            << QVariant(false)         // more wind      23
+            << QVariant(false)         // tag team       24
+            << QVariant(false)         // bottom border  25
+            << QVariant(100)           // damage modfier 26
+            << QVariant(45)            // turn time      27
+            << QVariant(100)           // init health    28
+            << QVariant(15)            // sudden death   29
+            << QVariant(5)             // case prob      30
+            << QVariant(3)             // mines time     31
+            << QVariant(4)             // mines number   32
+            << QVariant(0)             // mine dud pct   33
+            << QVariant(2)             // explosives     34
+            << QVariant(0)             // air mines      35
+            << QVariant(35)            // health case pct 36
+            << QVariant(25)            // health case amt 37
+            << QVariant(47)            // water rise amt 38
+            << QVariant(5)             // health dec amt 39
+            << QVariant(100)           // rope modfier   40
+            << QVariant(100)           // get away time  41
+            << QVariant(0)             // world edge     42
+            << QVariant()              // scriptparam    43
+            ;
+
+    QList<QVariant> construction;
+    construction
+            << predefSchemesNames[10]  // name           0
+            << QVariant(false)         // switchhog      1
+            << QVariant(false)         // team divide    2
+            << QVariant(false)         // solid land     3
+            << QVariant(false)         // border         4
+            << QVariant(false)         // low gravity    5
+            << QVariant(false)         // laser sight    6
+            << QVariant(false)         // invulnerable   7
+            << QVariant(false)         // reset health   8
+            << QVariant(false)         // vampiric       9
+            << QVariant(false)         // karma          10
+            << QVariant(false)         // artillery      11
+            << QVariant(true)          // random order   12
+            << QVariant(false)          // king           13
+            << QVariant(false)         // place hog      14
+            << QVariant(false)         // shared ammo    15
+            << QVariant(true)         // disable girders 16
+            << QVariant(true)         // disable land objects 17
+            << QVariant(false)         // AI survival    18
+            << QVariant(true)         // inf. attack    19
+            << QVariant(false)         // reset weps     20
+            << QVariant(true)         // per hog ammo   21
+            << QVariant(false)         // no wind        22
+            << QVariant(false)         // more wind      23
+            << QVariant(false)         // tag team       24
+            << QVariant(false)         // bottom border  25
+            << QVariant(100)           // damage modfier 26
+            << QVariant(45)            // turn time      27
+            << QVariant(100)           // init health    28
+            << QVariant(15)            // sudden death   29
+            << QVariant(5)             // case prob      30
+            << QVariant(3)             // mines time     31
+            << QVariant(0)             // mines number   32
+            << QVariant(0)             // mine dud pct   33
+            << QVariant(0)             // explosives     34
+            << QVariant(0)             // air mines      35
+            << QVariant(35)            // health case pct 36
+            << QVariant(25)            // health case amt 37
+            << QVariant(47)            // water rise amt 38
+            << QVariant(5)             // health dec amt 39
+            << QVariant(100)           // rope modfier   40
+            << QVariant(100)           // get away time  41
+            << QVariant(0)             // world edge     42
+            // NOTE: If you change this, also change the defaults in the Construction Mode script
+            << QVariant("initialenergy=550, energyperround=50, maxenergy=1000, cratesperround=5") // scriptparam    43
+            ;
+
+    QList<QVariant> spaceinvasion;
+    spaceinvasion
+            << predefSchemesNames[11]  // name           0
+            << QVariant(false)         // switchhog      1
+            << QVariant(false)         // team divide    2
+            << QVariant(false)         // solid land     3
+            << QVariant(false)         // border         4
+            << QVariant(false)         // low gravity    5
+            << QVariant(false)         // laser sight    6
+            << QVariant(false)         // invulnerable   7
+            << QVariant(false)         // reset health   8
+            << QVariant(false)         // vampiric       9
+            << QVariant(false)         // karma          10
+            << QVariant(false)         // artillery      11
+            << QVariant(true)          // random order   12
+            << QVariant(false)         // king           13
+            << QVariant(false)         // place hog      14
+            << QVariant(false)         // shared ammo    15
+            << QVariant(true)          // disable girders 16
+            << QVariant(false)         // disable land objects 17
+            << QVariant(false)         // AI survival    18
+            << QVariant(false)         // inf. attack    19
+            << QVariant(false)         // reset weps     20
+            << QVariant(false)         // per hog ammo   21
+            << QVariant(false)         // no wind        22
+            << QVariant(false)         // more wind      23
+            << QVariant(false)         // tag team       24
+            << QVariant(false)         // bottom border  25
+            << QVariant(100)           // damage modfier 26
+            << QVariant(45)            // turn time      27
+            << QVariant(100)           // init health    28
+            << QVariant(50)            // sudden death   29
+            << QVariant(0)             // case prob      30
+            << QVariant(3)             // mines time     31
+            << QVariant(0)             // mines number   32
+            << QVariant(0)             // mine dud pct   33
+            << QVariant(0)             // explosives     34
+            << QVariant(0)             // air mines      35
+            << QVariant(0)             // health case pct 36
+            << QVariant(25)            // health case amt 37
+            << QVariant(0)             // water rise amt 38
+            << QVariant(0)             // health dec amt 39
+            << QVariant(100)           // rope modfier   40
+            << QVariant(100)           // get away time  41
+            << QVariant(0)             // world edge     42
+            // NOTE: If you change this, also change the defaults in the Space Invasion script
+            << QVariant("rounds=3, shield=30, barrels=5, pings=2, barrelbonus=3, shieldbonus=30, timebonus=4") // scriptparam    43
+            ;
+
+    QList<QVariant> hedgeeditor;
+    hedgeeditor
+            << predefSchemesNames[12]  // name           0
+            << QVariant(false)         // switchhog      1
+            << QVariant(false)         // team divide    2
+            << QVariant(false)         // solid land     3
+            << QVariant(false)         // border         4
+            << QVariant(false)         // low gravity    5
+            << QVariant(false)         // laser sight    6
+            << QVariant(false)         // invulnerable   7
+            << QVariant(false)         // reset health   8
+            << QVariant(false)         // vampiric       9
+            << QVariant(false)         // karma          10
+            << QVariant(false)         // artillery      11
+            << QVariant(false)         // random order   12
+            << QVariant(false)         // king           13
+            << QVariant(false)         // place hog      14
+            << QVariant(false)         // shared ammo    15
+            << QVariant(false)         // disable girders 16
+            << QVariant(false)         // disable land objects 17
+            << QVariant(false)         // AI survival    18
+            << QVariant(false)         // inf. attack    19
+            << QVariant(false)         // reset weps     20
+            << QVariant(true)          // per hog ammo   21
+            << QVariant(false)         // no wind        22
+            << QVariant(false)         // more wind      23
+            << QVariant(false)         // tag team       24
+            << QVariant(false)         // bottom border  25
+            << QVariant(100)           // damage modfier 26
+            << QVariant(9999)          // turn time      27
+            << QVariant(100)           // init health    28
+            << QVariant(50)            // sudden death   29
+            << QVariant(0)             // case prob      30
+            << QVariant(3)             // mines time     31
+            << QVariant(0)             // mines number   32
+            << QVariant(0)             // mine dud pct   33
+            << QVariant(0)             // explosives     34
+            << QVariant(0)             // air mines      35
+            << QVariant(35)            // health case pct 36
+            << QVariant(25)            // health case amt 37
+            << QVariant(0)            // water rise amt 38
+            << QVariant(0)             // health dec amt 39
+            << QVariant(100)           // rope modfier   40
+            << QVariant(100)           // get away time  41
+            << QVariant(0)             // world edge     42
+            << QVariant()              // scriptparam    43
+            ;
+
+
+
+    schemes.append(defaultScheme);
+    schemes.append(proMode);
+    schemes.append(shoppa);
+    schemes.append(cleanslate);
+    schemes.append(minefield);
+    schemes.append(barrelmayhem);
+    schemes.append(tunnelhogs);
+    schemes.append(timeless);
+    schemes.append(thinkingportals);
+    schemes.append(kingmode);
+    schemes.append(construction);
+    schemes.append(spaceinvasion);
+    schemes.append(hedgeeditor);
+
+    if (!QDir(cfgdir->absolutePath() + "/Schemes").exists()) {
+        QDir().mkdir(cfgdir->absolutePath() + "/Schemes");
+    }
+    if (!QDir(directory).exists()) {
+        QDir().mkdir(directory);
+
+        qDebug("No /Schemes/Game directory found. Trying to import game schemes from schemes.ini.");
+
+        QSettings legacyFileConfig(cfgdir->absolutePath() + "/schemes.ini", QSettings::IniFormat);
+        int size = legacyFileConfig.beginReadArray("schemes");
+        int imported = 0;
+        for (int i = 0; i < size; ++i)
+        {
+            legacyFileConfig.setArrayIndex(i);
+
+            QString schemeName = legacyFileConfig.value(spNames[0]).toString();
+            if (!schemeName.isNull() && !predefSchemesNames.contains(schemeName))
+            {
+                QList<QVariant> scheme;
+                QFile file(directory + "/" + schemeName + ".hwg");
+
+                // Add keys to scheme info and create file
+                if (file.open(QIODevice::WriteOnly)) {
+                    QTextStream stream(&file);
+
+                    for (int k = 0; k < spNames.size(); ++k) {
+                        scheme << legacyFileConfig.value(spNames[k], defaultScheme[k]);
+
+                        // File handling
+                        // We skip the name key (k==0), it is not stored redundantly in file.
+                        // The file name is used for that already.
+                        if(k != 0) {
+                            // The file is just a list of key=value pairs
+                            stream << spNames[k] << "=" << legacyFileConfig.value(spNames[k], defaultScheme[k]).toString();
+                            stream << endl;
+                        }
+                    }
+                    file.close();
+                }
+                imported++;
+
+                schemes.append(scheme);
+            }
+        }
+        qDebug("%d game scheme(s) imported.", imported);
+        legacyFileConfig.endArray();
+    } else {
+        QStringList scheme_dir = QDir(directory).entryList(QDir::Files);
+
+        for(int i = 0; i < scheme_dir.size(); i++)
+        {
+            QList<QVariant> scheme;
+            QFile file(directory + "/" + scheme_dir[i]);
+
+            // Chop off file name suffix
+            QString schemeName = scheme_dir[i];
+            if (schemeName.endsWith(".hwg", Qt::CaseInsensitive)) {
+                schemeName.chop(4);
+            }
+            // Parse game scheme file
+            if (file.open(QIODevice::ReadOnly)) {
+                QTextStream stream(&file);
+                QString line, key, value;
+                QHash<QString, QString> fileKeyValues;
+                do {
+                    // Read line and get key and value
+                    line = stream.readLine();
+                    key = line.section(QChar('='), 0, 0);
+                    value = line.section(QChar('='), 1);
+                    if(!key.isNull() && !value.isNull()) {
+                        fileKeyValues[key] = value;
+                    }
+                } while (!line.isNull());
+
+                // Add scheme name manually
+                scheme << schemeName;
+                // Add other keys from the QHash.
+                for (int k = 1; k < spNames.size(); ++k) {
+                    key = spNames[k];
+                    if (fileKeyValues.contains(key)) {
+                        scheme << fileKeyValues.value(key);
+                    } else {
+                        // Use default value in case the key is not set
+                        scheme << defaultScheme[k];
+                    }
+                }
+                schemes.append(scheme);
+
+                file.close();
+            }
+        }
+    }
+}
+
+QVariant GameSchemeModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+    Q_UNUSED(section);
+    Q_UNUSED(orientation);
+    Q_UNUSED(role);
+
+    return QVariant();
+}
+
+int GameSchemeModel::rowCount(const QModelIndex &parent) const
+{
+    if (parent.isValid())
+        return 0;
+    else
+        return schemes.size();
+}
+
+int GameSchemeModel::columnCount(const QModelIndex & parent) const
+{
+    if (parent.isValid())
+        return 0;
+    else
+        return defaultScheme.size();
+}
+
+bool GameSchemeModel::hasScheme(QString name)
+{
+    for(int i=0; i<schemes.size(); i++)
+    {
+        if(schemes[i][0] == name)
+        {
+            return true;
+        }
+    }
+    return false;
+}
+
+Qt::ItemFlags GameSchemeModel::flags(const QModelIndex & index) const
+{
+    Q_UNUSED(index);
+
+    return
+        Qt::ItemIsEnabled
+        | Qt::ItemIsSelectable
+        | Qt::ItemIsEditable;
+}
+
+bool GameSchemeModel::setData(const QModelIndex & index, const QVariant & value, int role)
+{
+    if (!index.isValid() || index.row() < numberOfDefaultSchemes
+            || index.row() >= schemes.size()
+            || index.column() >= defaultScheme.size()
+            || role != Qt::EditRole)
+        return false;
+
+    schemes[index.row()][index.column()] = value;
+
+    emit dataChanged(index, index);
+    return true;
+}
+
+bool GameSchemeModel::insertRows(int row, int count, const QModelIndex & parent)
+{
+    Q_UNUSED(count);
+
+    beginInsertRows(parent, schemes.size(), schemes.size());
+
+    if (row == -1)
+    {
+        QList<QVariant> newScheme = defaultScheme;
+
+        QString newName = tr("New");
+        if(hasScheme(newName))
+        {
+            //name already used -> look for an appropriate name:
+            int i=2;
+            while(hasScheme(newName = tr("New (%1)").arg(i++))) ;
+        }
+        newScheme[0] = QVariant(newName);
+        schemes.insert(schemes.size(), newScheme);
+    }
+    else
+    {
+        QList<QVariant> newScheme = schemes[row];
+        QString oldName = newScheme[0].toString();
+        QString newName = tr("Copy of %1").arg(oldName);
+        if(hasScheme(newName))
+        {
+            //name already used -> look for an appropriate name:
+            int i=2;
+            while(hasScheme(newName = tr("Copy of %1 (%2)").arg(oldName).arg(i++)));
+        }
+        newScheme[0] = QVariant(newName);
+        schemes.insert(schemes.size(), newScheme);
+    }
+
+    endInsertRows();
+
+    return true;
+}
+
+bool GameSchemeModel::removeRows(int row, int count, const QModelIndex & parent)
+{
+    if(count != 1
+            || row < numberOfDefaultSchemes
+            || row >= schemes.size())
+        return false;
+
+    beginRemoveRows(parent, row, row);
+
+    QList<QVariant> scheme = schemes[row];
+    int j = spNames.indexOf("name");
+    QFile(cfgdir->absolutePath() + "/Schemes/Game/" + scheme[j].toString() + ".hwg").remove();
+    schemes.removeAt(row);
+
+    endRemoveRows();
+
+    return true;
+}
+
+QVariant GameSchemeModel::data(const QModelIndex &index, int role) const
+{
+    if (!index.isValid() || index.row() < 0
+            || index.row() >= schemes.size()
+            || index.column() >= defaultScheme.size()
+            || (role != Qt::EditRole && role != Qt::DisplayRole)
+       )
+        return QVariant();
+
+    return schemes[index.row()][index.column()];
+}
+
+void GameSchemeModel::Save()
+{
+    for (int i = 0; i < schemes.size() - numberOfDefaultSchemes; ++i)
+    {
+        QList<QVariant> scheme = schemes[i + numberOfDefaultSchemes];
+        int j = spNames.indexOf("name");
+
+        QString schemeName = scheme[j].toString();
+        QFile file(cfgdir->absolutePath() + "/Schemes/Game/" + schemeName + ".hwg");
+
+        if (file.open(QIODevice::WriteOnly)) {
+            QTextStream stream(&file);
+            for (int k = 0; k < spNames.size(); ++k) {
+                // We skip the name key
+                if(k != j) {
+                    // The file is just a list of key=value pairs
+                    stream << spNames[k] << "=" << scheme[k].toString();
+                    stream << endl;
+                }
+            }
+            file.close();
+        }
+    }
+}
+
+
+NetGameSchemeModel::NetGameSchemeModel(QObject * parent) :
+    QAbstractTableModel(parent)
+{
+    netScheme = defaultScheme;
+}
+
+QVariant NetGameSchemeModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+    Q_UNUSED(section);
+    Q_UNUSED(orientation);
+    Q_UNUSED(role);
+
+    return QVariant();
+}
+
+int NetGameSchemeModel::rowCount(const QModelIndex & parent) const
+{
+    if (parent.isValid())
+        return 0;
+    else
+        return 1;
+}
+
+int NetGameSchemeModel::columnCount(const QModelIndex & parent) const
+{
+    if (parent.isValid())
+        return 0;
+    else
+        return defaultScheme.size();
+}
+
+QVariant NetGameSchemeModel::data(const QModelIndex &index, int role) const
+{
+    if (!index.isValid() || index.row() < 0
+            || index.row() > 1
+            || index.column() >= defaultScheme.size()
+            || (role != Qt::EditRole && role != Qt::DisplayRole)
+       )
+        return QVariant();
+
+    return netScheme[index.column()];
+}
+
+void NetGameSchemeModel::setNetSchemeConfig(QStringList cfg)
+{
+    if(cfg.size() != netScheme.size())
+    {
+        qWarning("Incorrect scheme cfg size");
+        return;
+    }
+
+    beginResetModel();
+
+    cfg[cfg.size()-1] = cfg[cfg.size()-1].mid(1);
+
+    for(int i = 0; i < cfg.size(); ++i)
+        netScheme[i] = QVariant(cfg[i]);
+
+    endResetModel();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/QTfrontend/model/gameSchemeModel.h	Sun Jun 10 19:12:26 2018 +0200
@@ -0,0 +1,73 @@
+/*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (c) 2004-2015 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef _GAME_SCHEME_MODEL_INCLUDED
+#define _GAME_SCHEME_MODEL_INCLUDED
+
+#include <QAbstractTableModel>
+#include <QStringList>
+#include <QList>
+
+class GameSchemeModel : public QAbstractTableModel
+{
+        Q_OBJECT
+
+    public:
+        GameSchemeModel(QObject * parent, const QString & fileName);
+
+        QVariant headerData(int section, Qt::Orientation orientation, int role) const;
+        int rowCount(const QModelIndex & parent) const;
+        int columnCount(const QModelIndex & parent) const;
+        bool hasScheme(QString name);
+        Qt::ItemFlags flags(const QModelIndex & index) const;
+        bool setData(const QModelIndex & index, const QVariant & value, int role = Qt::EditRole);
+        bool insertRows(int row, int count, const QModelIndex & parent = QModelIndex());
+        bool removeRows(int row, int count, const QModelIndex & parent = QModelIndex());
+        QVariant data(const QModelIndex &index, int role) const;
+
+        int numberOfDefaultSchemes;
+        QStringList predefSchemesNames;
+        QStringList spNames;
+
+    public slots:
+        void Save();
+
+    protected:
+        QList< QList<QVariant> > schemes;
+};
+
+class NetGameSchemeModel : public QAbstractTableModel
+{
+        Q_OBJECT
+
+    public:
+        NetGameSchemeModel(QObject * parent);
+
+        QVariant headerData(int section, Qt::Orientation orientation, int role) const;
+        int rowCount(const QModelIndex & parent) const;
+        int columnCount(const QModelIndex & parent) const;
+        QVariant data(const QModelIndex &index, int role) const;
+
+    public slots:
+        void setNetSchemeConfig(QStringList cfg);
+
+    private:
+        QList<QVariant> netScheme;
+};
+
+#endif // _GAME_SCHEME_MODEL_INCLUDED
--- a/QTfrontend/model/playerslistmodel.cpp	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/model/playerslistmodel.cpp	Sun Jun 10 19:12:26 2018 +0200
@@ -309,10 +309,10 @@
     }
 
     if(index.data(Ignore).toBool())
-        setData(index, Qt::gray, Qt::ForegroundRole);
+        setData(index, QColor(Qt::gray), Qt::ForegroundRole);
     else
     if(index.data(Friend).toBool())
-        setData(index, Qt::green, Qt::ForegroundRole);
+        setData(index, QColor(Qt::green), Qt::ForegroundRole);
     else
         setData(index, QBrush(QColor(0xff, 0xcc, 0x00)), Qt::ForegroundRole);
 }
--- a/QTfrontend/net/hwmap.cpp	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/net/hwmap.cpp	Sun Jun 10 19:12:26 2018 +0200
@@ -79,7 +79,7 @@
     {
         quint8 *buf = (quint8*) readbuffer.constData();
         QImage im(buf, 256, 128, QImage::Format_Mono);
-        im.setNumColors(2);
+        im.setColorCount(2);
 
         QPixmap px(QSize(256, 128));
         QPixmap pxres(px.size());
--- a/QTfrontend/net/netudpwidget.cpp	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/net/netudpwidget.cpp	Sun Jun 10 19:12:26 2018 +0200
@@ -33,15 +33,19 @@
 
 void HWNetUdpModel::updateList()
 {
-    games.clear();
+  beginResetModel();
+
+  games.clear();
 
-    reset();
+  endResetModel();
 
-    pUdpSocket->writeDatagram("hedgewars client", QHostAddress::Broadcast, NETGAME_DEFAULT_PORT);
+  pUdpSocket->writeDatagram("hedgewars client", QHostAddress::Broadcast, NETGAME_DEFAULT_PORT);
 }
 
 void HWNetUdpModel::onClientRead()
 {
+    beginResetModel();
+
     while (pUdpSocket->hasPendingDatagrams())
     {
         QByteArray datagram;
@@ -60,7 +64,7 @@
         }
     }
 
-    reset();
+    endResetModel();
 }
 
 QVariant HWNetUdpModel::data(const QModelIndex &index,
--- a/QTfrontend/net/newnetclient.cpp	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/net/newnetclient.cpp	Sun Jun 10 19:12:26 2018 +0200
@@ -99,7 +99,7 @@
 
 void HWNewNet::CreateRoom(const QString & room, const QString & password)
 {
-    if(netClientState != InLobby)
+    if(netClientState != InLobby || !ByteLength(room))
     {
         qWarning("Illegal try to create room!");
         return;
@@ -176,6 +176,11 @@
     RawSendNet(QString("EM%1%2").arg(delimiter).arg(msg));
 }
 
+int HWNewNet::ByteLength(const QString & str)
+{
+	return str.toUtf8().size();
+}
+
 void HWNewNet::RawSendNet(const QString & str)
 {
     RawSendNet(str.toUtf8());
@@ -242,7 +247,7 @@
 void HWNewNet::SendPasswordHash(const QString & hash)
 {
     // don't send it immediately, only store and check if server asked us for a password
-    m_passwordHash = hash.toAscii();
+    m_passwordHash = hash.toLatin1();
 
     maybeSendPassword();
 }
@@ -271,7 +276,7 @@
     if (lst[0] == "ERROR")
     {
         if (lst.size() == 2)
-            emit Error(HWApplication::translate("server", lst[1].toAscii().constData()));
+            emit Error(HWApplication::translate("server", lst[1].toLatin1().constData()));
         else
             emit Error("Unknown error");
         return;
@@ -280,7 +285,7 @@
     if (lst[0] == "WARNING")
     {
         if (lst.size() == 2)
-            emit Warning(HWApplication::translate("server", lst[1].toAscii().constData()));
+            emit Warning(HWApplication::translate("server", lst[1].toLatin1().constData()));
         else
             emit Warning("Unknown warning");
         return;
@@ -447,7 +452,7 @@
         while(flags.size() > 1)
         {
             flags.remove(0, 1);
-            char c = flags[0].toAscii();
+            char c = flags[0].toLatin1();
             bool inRoom = (netClientState == InRoom || netClientState == InGame);
 
             switch(c)
@@ -673,7 +678,7 @@
         }
         m_game_connected = false;
         Disconnect();
-        emit disconnected(HWApplication::translate("server", lst[1].toAscii().constData()));
+        emit disconnected(HWApplication::translate("server", lst[1].toLatin1().constData()));
         return;
     }
 
@@ -727,7 +732,7 @@
             }
             for(int i = 1; i < lst.size(); ++i)
             {
-                QByteArray em = QByteArray::fromBase64(lst[i].toAscii());
+                QByteArray em = QByteArray::fromBase64(lst[i].toLatin1());
                 emit FromNet(em);
             }
             return;
@@ -909,7 +914,7 @@
 
 void HWNewNet::chatLineToNet(const QString& str)
 {
-    if(str != "")
+    if(ByteLength(str))
     {
         RawSendNet(QString("CHAT") + delimiter + str);
         QString action = HWProto::chatStringToAction(str);
@@ -922,7 +927,7 @@
 
 void HWNewNet::chatLineToLobby(const QString& str)
 {
-    if(str != "")
+    if(ByteLength(str))
     {
         RawSendNet(QString("CHAT") + delimiter + str);
         QString action = HWProto::chatStringToAction(str);
@@ -1155,18 +1160,18 @@
         return;
 
     QString hash = QCryptographicHash::hash(
-                m_clientSalt.toAscii()
-                .append(m_serverSalt.toAscii())
+                m_clientSalt.toLatin1()
+                .append(m_serverSalt.toLatin1())
                 .append(m_passwordHash)
-                .append(cProtoVer->toAscii())
+                .append(cProtoVer->toLatin1())
                 .append("!hedgewars")
                 , QCryptographicHash::Sha1).toHex();
 
     m_serverHash = QCryptographicHash::hash(
-                m_serverSalt.toAscii()
-                .append(m_clientSalt.toAscii())
+                m_serverSalt.toLatin1()
+                .append(m_clientSalt.toLatin1())
                 .append(m_passwordHash)
-                .append(cProtoVer->toAscii())
+                .append(cProtoVer->toLatin1())
                 .append("!hedgewars")
                 , QCryptographicHash::Sha1).toHex();
 
--- a/QTfrontend/net/newnetclient.h	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/net/newnetclient.h	Sun Jun 10 19:12:26 2018 +0200
@@ -84,6 +84,7 @@
 
         QStringList cmdbuf;
 
+        int  ByteLength(const QString & str);
         void RawSendNet(const QString & buf);
         void RawSendNet(const QByteArray & buf);
         void ParseCmd(const QStringList & lst);
--- a/QTfrontend/net/proto.h	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/net/proto.h	Sun Jun 10 19:12:26 2018 +0200
@@ -22,7 +22,7 @@
 #include <QByteArray>
 #include <QString>
 #include <QStringList>
-
+#include <QObject>
 
 class HWProto : public QObject
 {
--- a/QTfrontend/net/tcpBase.cpp	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/net/tcpBase.cpp	Sun Jun 10 19:12:26 2018 +0200
@@ -228,8 +228,10 @@
 {
     Q_UNUSED(exitStatus);
 
-    if(m_connected)
-      ClientDisconnect();
+    if(!m_connected) { // yes, it is intended to be like this
+      ClientDisconnect(); // need to do cleanup in case no connection occured,
+      //if m_connected is true, it is done automatically in socket disconnect handler
+    }
 
     // show error message if there was an error that was not an engine's
     // fatal error - because that one already sent a info via IPC
Binary file QTfrontend/res/TomatowarsTitle.png has changed
Binary file QTfrontend/res/btnDisabled.png has changed
Binary file QTfrontend/res/btnKarma.png has changed
Binary file QTfrontend/res/btnKarma@2x.png has changed
Binary file QTfrontend/res/btnKing.png has changed
Binary file QTfrontend/res/btnKing@2x.png has changed
Binary file QTfrontend/res/btnPerHogAmmo.png has changed
Binary file QTfrontend/res/btnPerHogAmmo@2x.png has changed
Binary file QTfrontend/res/btnResetHealth.png has changed
Binary file QTfrontend/res/btnResetHealth@2x.png has changed
Binary file QTfrontend/res/btnSharedAmmo.png has changed
Binary file QTfrontend/res/btnSharedAmmo@2x.png has changed
Binary file QTfrontend/res/btnSwitchHog.png has changed
Binary file QTfrontend/res/btnSwitchHog@2x.png has changed
Binary file QTfrontend/res/btnTeamsDivide.png has changed
Binary file QTfrontend/res/btnTeamsDivide@2x.png has changed
Binary file QTfrontend/res/checked.png has changed
Binary file QTfrontend/res/checkedHover.png has changed
Binary file QTfrontend/res/checkedPressed.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/QTfrontend/res/css/april1.css	Sun Jun 10 19:12:26 2018 +0200
@@ -0,0 +1,455 @@
+/******************************************************************************
+ *
+ * CSS-like definition of Qt frontend appearance
+ *
+ ******************************************************************************
+ *
+ * see http://doc.qt.nokia.com/4.5/stylesheet.html
+ *
+ ******************************************************************************
+ *
+ * This file can be stored at different locations, but it will be read only
+ * once, based on first file found in this order:
+ *
+ *    <userdir>/Data/css/qt.css
+ *    <datadir>/css/qt.css
+ *    <internal default style-sheet> (:/res/css/qt.css)
+ *
+ *****************************************************************************/
+
+#infoButton 
+{
+border: transparent;
+background: transparent; 
+width: 800px;
+min-width: 800px;
+qproperty-icon: url(":/res/TomatowarsTitle.png");
+qproperty-iconSize: 800px 165px;
+}
+HWForm,QDialog {
+background-image: url(":/res/Background.png");
+background-position: bottom center;
+background-repeat: repeat-x;
+background-color: #141250;
+}
+
+* {
+color: #ffcc00;
+selection-background-color: #ffcc00;
+selection-color: #00351d;
+}
+
+a { color:#c8c8ff; }
+
+QLineEdit, QListWidget, QListView, QTableView, QTableWidget, QTextBrowser, QSpinBox, QComboBox,
+QComboBox QAbstractItemView, QPlainTextEdit, QMenu::item, #labelLikeLineEdit {
+background-color: rgba(13, 5, 68, 70%);
+}
+
+VertScrArea, QGraphicsView {
+border-style: solid; border-width: 2px; border-color: #cca300; border-radius: 3px;
+}
+#gameStatsView {
+border-color: #332816;
+}
+
+QSplitter::handle {
+background-image: url(":/res/splitter.png");
+background-clip: content;
+}
+QSplitter::handle:horizontal {
+width: 7px;
+background-repeat: repeat-y;
+}
+QSplitter::handle:vertical {
+height: 7px;
+background-repeat: repeat-x;
+}
+
+QComboBox::separator {
+border: solid; border-width: 3px; border-color: #ffcc00;
+}
+
+QPushButton, QListWidget, QListView, QTableView, QTableWidget, QLineEdit,
+QTextBrowser, QSpinBox, QToolBox, QComboBox, QPlainTextEdit,
+QComboBox QAbstractItemView, IconedGroupBox,
+.QGroupBox, #gameStackContainer, TeamSelWidget, SelWeaponWidget,
+QTabWidget::pane, QTabBar::tab, #mapPreview, #labelLikeLineEdit {
+border: solid;
+border-width: 3px;
+border-color: #ffcc00;
+}
+
+QPushButton:hover, QLineEdit:hover, QListWidget:hover, QListView:hover,
+QSpinBox:hover, QToolBox:hover, QComboBox:hover {
+border-color: yellow;
+}
+
+TeamShowWidget QPushButton {
+icon-size: 48px;
+text-align: left;
+background-color: #0d0544;
+color: orange;
+font: bold;
+border-width: 2px;
+margin: 6px 0px 6px 0px;
+}
+TeamShowWidget QPushButton:disabled {
+color: #a0a0a0;
+}
+
+QToolButton {
+background-color: #11084A;
+}
+
+QToolButton:hover {
+background-color: #150A61;
+}
+
+QToolButton:pressed {
+background-color: #100744;
+}
+
+QLineEdit, QListWidget, QListView, QTableView, QTableWidget, QTextBrowser,
+QSpinBox, QToolBox, QPlainTextEdit, QToolButton, #mapPreview, #labelLikeLineEdit {
+border-radius: 10px;
+}
+
+#mapPreview {
+background-color: #0d0544;
+}
+#mapPreview:disabled{
+border-color: #a0a0a0;
+background-color: #0d0544;
+color: #ffffff;
+}
+
+QLineEdit, QLabel, QHeaderView, QListWidget, QListView, QTableView,
+QTableWidget, QSpinBox, QToolBox::tab, QComboBox, QComboBox QAbstractItemView,
+IconedGroupBox, .QGroupBox, #gameStackContainer, TeamSelWidget,
+SelWeaponWidget, QCheckBox, QRadioButton, QPushButton, QPlainTextEdit,
+#mapName {
+font: bold 13px;
+}
+.QLabel{
+background-color: transparent;
+}
+SelWeaponWidget QTabWidget::pane, SelWeaponWidget QTabBar::tab:selected {
+background-position: bottom center;
+background-repeat: repeat-x;
+background-color: #000000;
+}
+.QGroupBox, #gameStackContainer, TeamSelWidget, SelWeaponWidget {
+background-position: bottom center;
+background-repeat: repeat-x;
+border-radius: 16px;
+background-color: rgba(13, 5, 68, 70%);
+padding: 6px;
+margin-top: 4px;
+}
+/*  Experimenting with PaintOnScreen and border-radius on IconedGroupBox children didn't work out well
+IconedGroupBox QComboBox, IconedGroupBox QPushButton, IconedGroupBox QLineEdit,
+IconedGroupBox QSpinBox {
+border-radius: 0;
+}
+IconedGroupBox, IconedGroupBox *, QTabWidget::pane, QTabBar::tab:selected, QToolBox::tab QWidget{" */
+IconedGroupBox, QTabWidget::pane, QTabBar::tab:selected, QToolBox::tab QWidget{
+background-color: #130f2c;
+}
+
+QTabWidget::pane {
+border-radius: 8px;
+border-top-left-radius: 0px;
+}
+
+GameCFGWidget {
+border: none;
+}
+
+QPushButton {
+border-radius: 8px;
+background-origin: margin;
+background-position: top left;
+background-color: rgba(18, 42, 5, 70%);
+}
+
+QPushButton:pressed, QToolButton:pressed {
+border-color: white;
+}
+
+QPushButton:focus {
+outline: none;
+}
+
+QHeaderView {
+background-color: #00351d;
+border: solid;
+border-bottom-width: 3px;
+border-top-width: 0px;
+border-left-width: 0px;
+border-right-width: 0px;
+border-color: #ffcc00;
+}
+QHeaderView::section {
+border-left-width: 1px;
+border-right-width: 1px;
+border-top-width: 0;
+border-bottom-width: 0px;
+border-color: #001d10;
+border-style: solid;
+background-color: #00351d;
+padding: 4px;
+}
+QHeaderView::section:pressed {
+background-color: #00250d;
+}
+QHeaderView::up-arrow {
+image: url(":/res/sort_up.png");
+}
+QHeaderView::down-arrow{
+image: url(":/res/sort_down.png");
+}
+
+QTableView, QTableWidget {
+alternate-background-color: #2f213a;
+gridline-color: transparent;
+}
+QTabWidget::pane { top: -2px; }
+QTabBar::tab {
+border-radius: 0;
+border-top-left-radius: 6px;
+border-top-right-radius: 6px;
+padding: 3px;
+background-color: #00351d;
+color: #ffcc00;
+}
+QTabBar::tab:selected {
+border-bottom-color: #0d0544;
+border-bottom-width: 0;
+}
+QSpinBox::up-button{
+background: transparent;
+width: 16px;
+height: 10px;
+}
+
+QSpinBox::up-arrow {
+image: url(":/res/spin_up.png");
+}
+QSpinBox::up-arrow:disabled {
+image: url(":/res/spin_up_disabled.png");
+}
+
+QSpinBox::down-arrow {
+image: url(":/res/spin_down.png");
+}
+QSpinBox::down-arrow:disabled {
+image: url(":/res/spin_down_disabled.png");
+}
+
+QSpinBox::down-button {
+background: transparent;
+width: 16px;
+height: 10px;
+}
+
+QComboBox {
+border-radius: 10px;
+padding: 3px;
+height: 18px;
+}
+QComboBox:pressed{
+border-color: white;
+}
+QComboBox::drop-down{
+border: transparent;
+width: 25px;
+}
+QComboBox::down-arrow {
+image: url(":/res/dropdown.png");
+}
+QComboBox::down-arrow:disabled {
+image: url(":/res/dropdown_disabled.png");
+}
+
+VertScrArea {
+background-position: bottom center;
+background-repeat: repeat-x;
+}
+
+IconedGroupBox {
+border-radius: 16px;
+padding: 2px;
+}
+
+QGroupBox::title{
+subcontrol-origin: margin;
+subcontrol-position: top left;
+text-align: left;
+left: 15px;
+}
+
+QCheckBox::indicator:checked{
+image: url(":/res/checked.png");
+}
+QCheckBox::indicator:checked:hover{
+image: url(":/res/checkedHover.png");
+}
+QCheckBox::indicator:checked:pressed{
+image: url(":/res/checkedPressed.png");
+}
+QCheckBox::indicator:unchecked{
+image: url(":/res/unchecked.png");
+}
+QCheckBox::indicator:unchecked:hover{
+image: url(":/res/uncheckedHover.png");
+}
+QCheckBox::indicator:unchecked:pressed{
+image: url(":/res/uncheckedPressed.png");
+}
+
+QRadioButton::indicator:checked{
+image: url(":/res/radioButtonChecked.png");
+}
+QRadioButton::indicator:checked:hover{
+image: url(":/res/radioButtonCheckedHover.png");
+}
+QRadioButton::indicator:checked:pressed{
+image: url(":/res/radioButtonCheckedPressed.png");
+}
+QRadioButton::indicator:unchecked{
+image: url(":/res/radioButtonUnchecked.png");
+}
+QRadioButton::indicator:unchecked:hover{
+image: url(":/res/radioButtonUncheckedHover.png");
+}
+QRadioButton::indicator:unchecked:pressed{
+image: url(":/res/radioButtonUncheckedPressed.png");
+}
+
+.QWidget{
+background: transparent;
+}
+
+QTabWidget::pane {
+border-top-width: 2px;
+}
+
+QMenu{
+background-color: #ffcc00;
+margin: 3px;
+}
+QMenu::item {
+background-color: #0d0544;
+border: 1px solid transparent;
+font: bold;
+padding: 2px 25px 2px 20px;
+}
+QMenu::item:selected {
+background-color: #2d2564;
+}
+QMenu::indicator {
+width: 16px;
+height: 16px;
+}
+QMenu::indicator:non-exclusive:checked{
+image: url(":/res/checked.png");
+}
+QMenu::indicator:non-exclusive:unchecked{
+image: url(":/res/unchecked.png");
+}
+
+QToolTip{
+background-color: #0d0544;
+border: 1px solid #ffcc00;
+}
+
+:disabled{
+color: #a0a0a0;
+border-color: #a0a0a0;
+}
+QListWidget:item:selected:disabled, QListView:item:selected:disabled{
+color: rgba(13, 5, 68, 70%);
+background-color: #a0a0a0;
+}
+SquareLabel, ItemNum {
+background-color: #000000;
+}
+
+QSlider::groove::horizontal {
+height: 2px;
+margin: 2px 0px;
+background-color: #ffcc00;
+}
+QSlider::groove::horizontal:disabled {
+background-color: #a0a0a0;
+}
+
+QSlider::handle::horizontal {
+border: 0px;
+margin: -8px 0px;
+background-color: #ffcc00;
+width: 12px;
+height: 6px;
+border-radius: 3px;
+}
+QSlider::handle::horizontal:disabled {
+background-color: #a0a0a0;
+}
+
+HatButton, ThemeButton {
+text-align: left;
+}
+
+#hatList, #hatList:hover, #themeList, #themeList:hover {
+border-color: #F6CB1C;
+}
+
+#hatList QScrollBar, #themeList QScrollBar {
+background-color: #130F2A;
+border-top-right-radius: 10px;
+border-bottom-right-radius: 10px;
+}
+
+#hatList, #themeList {
+border-color: #F6CB1C;
+border-width: 3px;
+border-style: solid;
+border-radius: 10px;
+border-top-left-radius: 0px;
+}
+
+#hatList::item, #themeList::item {
+background-color: #11084A;
+padding: 4px;
+border-radius: 10px;
+color: #ffcc00 !important;
+font: 8px;
+border-width: 2px;
+border-color: #11084A;
+}
+
+#hatList::item:hover, #themeList::item:hover {
+background-color: #150A61;
+}
+
+#hatList::item:selected, #themeList::item:selected {
+background-color: #150A61;
+}
+
+QDialogButtonBox QPushButton {
+padding: 3px 5px;
+}
+
+#gameCfgWidgetTabs {
+border-radius: 16px;
+border-top-left-radius: 0px;
+}
+
+TeamSelWidget, #gameStackContainer, #GBoxOptions {
+border-radius: 10px;
+}
+
+PageMultiplayer TeamSelWidget {
+min-height: 500px;
+}
--- a/QTfrontend/res/css/qt.css	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/res/css/qt.css	Sun Jun 10 19:12:26 2018 +0200
@@ -17,6 +17,11 @@
  *
  *****************************************************************************/
 
+#infoButton 
+{
+border: transparent;
+background: transparent; 
+}
 HWForm,QDialog {
 background-image: url(":/res/Background.png");
 background-position: bottom center;
@@ -32,16 +37,36 @@
 
 a { color:#c8c8ff; }
 
-QLineEdit, QListWidget, QListView, QTableView, QTextBrowser, QSpinBox, QComboBox,
+QLineEdit, QListWidget, QListView, QTableView, QTableWidget, QTextBrowser, QSpinBox, QComboBox,
 QComboBox QAbstractItemView, QPlainTextEdit, QMenu::item, #labelLikeLineEdit {
 background-color: rgba(13, 5, 68, 70%);
 }
 
+VertScrArea, QGraphicsView {
+border-style: solid; border-width: 2px; border-color: #cca300; border-radius: 3px;
+}
+#gameStatsView {
+border-color: #332816;
+}
+
+QSplitter::handle {
+background-image: url(":/res/splitter.png");
+background-clip: content;
+}
+QSplitter::handle:horizontal {
+width: 7px;
+background-repeat: repeat-y;
+}
+QSplitter::handle:vertical {
+height: 7px;
+background-repeat: repeat-x;
+}
+
 QComboBox::separator {
 border: solid; border-width: 3px; border-color: #ffcc00;
 }
 
-QPushButton, QListWidget, QListView, QTableView, QLineEdit, QHeaderView,
+QPushButton, QListWidget, QListView, QTableView, QTableWidget, QLineEdit,
 QTextBrowser, QSpinBox, QToolBox, QComboBox, QPlainTextEdit,
 QComboBox QAbstractItemView, IconedGroupBox,
 .QGroupBox, #gameStackContainer, TeamSelWidget, SelWeaponWidget,
@@ -56,6 +81,19 @@
 border-color: yellow;
 }
 
+TeamShowWidget QPushButton {
+icon-size: 48px;
+text-align: left;
+background-color: #0d0544;
+color: orange;
+font: bold;
+border-width: 2px;
+margin: 6px 0px 6px 0px;
+}
+TeamShowWidget QPushButton:disabled {
+color: #a0a0a0;
+}
+
 QToolButton {
 background-color: #11084A;
 }
@@ -68,7 +106,7 @@
 background-color: #100744;
 }
 
-QLineEdit, QListWidget, QListView, QTableView, QTextBrowser,
+QLineEdit, QListWidget, QListView, QTableView, QTableWidget, QTextBrowser,
 QSpinBox, QToolBox, QPlainTextEdit, QToolButton, #mapPreview, #labelLikeLineEdit {
 border-radius: 10px;
 }
@@ -76,13 +114,22 @@
 #mapPreview {
 background-color: #0d0544;
 }
+#mapPreview:disabled{
+border-color: #a0a0a0;
+background-color: #0d0544;
+color: #ffffff;
+}
 
 QLineEdit, QLabel, QHeaderView, QListWidget, QListView, QTableView,
-QSpinBox, QToolBox::tab, QComboBox, QComboBox QAbstractItemView,
+QTableWidget, QSpinBox, QToolBox::tab, QComboBox, QComboBox QAbstractItemView,
 IconedGroupBox, .QGroupBox, #gameStackContainer, TeamSelWidget,
-SelWeaponWidget, QCheckBox, QRadioButton, QPushButton, QPlainTextEdit {
+SelWeaponWidget, QCheckBox, QRadioButton, QPushButton, QPlainTextEdit,
+#mapName {
 font: bold 13px;
 }
+.QLabel{
+background-color: transparent;
+}
 SelWeaponWidget QTabWidget::pane, SelWeaponWidget QTabBar::tab:selected {
 background-position: bottom center;
 background-repeat: repeat-x;
@@ -111,10 +158,6 @@
 border-top-left-radius: 0px;
 }
 
-QLineEdit:disabled, QSpinBox:disabled {
-border-color: gray;
-}
-
 GameCFGWidget {
 border: none;
 }
@@ -135,12 +178,35 @@
 }
 
 QHeaderView {
-border-radius: 0;
-border-width: 0;
+background-color: #00351d;
+border: solid;
 border-bottom-width: 3px;
-background-color: #00351d;
+border-top-width: 0px;
+border-left-width: 0px;
+border-right-width: 0px;
+border-color: #ffcc00;
 }
-QTableView {
+QHeaderView::section {
+border-left-width: 1px;
+border-right-width: 1px;
+border-top-width: 0;
+border-bottom-width: 0px;
+border-color: #001d10;
+border-style: solid;
+background-color: #00351d;
+padding: 4px;
+}
+QHeaderView::section:pressed {
+background-color: #00250d;
+}
+QHeaderView::up-arrow {
+image: url(":/res/sort_up.png");
+}
+QHeaderView::down-arrow{
+image: url(":/res/sort_down.png");
+}
+
+QTableView, QTableWidget {
 alternate-background-color: #2f213a;
 gridline-color: transparent;
 }
@@ -166,10 +232,16 @@
 QSpinBox::up-arrow {
 image: url(":/res/spin_up.png");
 }
+QSpinBox::up-arrow:disabled {
+image: url(":/res/spin_up_disabled.png");
+}
 
 QSpinBox::down-arrow {
 image: url(":/res/spin_down.png");
 }
+QSpinBox::down-arrow:disabled {
+image: url(":/res/spin_down_disabled.png");
+}
 
 QSpinBox::down-button {
 background: transparent;
@@ -192,6 +264,9 @@
 QComboBox::down-arrow {
 image: url(":/res/dropdown.png");
 }
+QComboBox::down-arrow:disabled {
+image: url(":/res/dropdown_disabled.png");
+}
 
 VertScrArea {
 background-position: bottom center;
@@ -213,9 +288,40 @@
 QCheckBox::indicator:checked{
 image: url(":/res/checked.png");
 }
+QCheckBox::indicator:checked:hover{
+image: url(":/res/checkedHover.png");
+}
+QCheckBox::indicator:checked:pressed{
+image: url(":/res/checkedPressed.png");
+}
 QCheckBox::indicator:unchecked{
 image: url(":/res/unchecked.png");
 }
+QCheckBox::indicator:unchecked:hover{
+image: url(":/res/uncheckedHover.png");
+}
+QCheckBox::indicator:unchecked:pressed{
+image: url(":/res/uncheckedPressed.png");
+}
+
+QRadioButton::indicator:checked{
+image: url(":/res/radioButtonChecked.png");
+}
+QRadioButton::indicator:checked:hover{
+image: url(":/res/radioButtonCheckedHover.png");
+}
+QRadioButton::indicator:checked:pressed{
+image: url(":/res/radioButtonCheckedPressed.png");
+}
+QRadioButton::indicator:unchecked{
+image: url(":/res/radioButtonUnchecked.png");
+}
+QRadioButton::indicator:unchecked:hover{
+image: url(":/res/radioButtonUncheckedHover.png");
+}
+QRadioButton::indicator:unchecked:pressed{
+image: url(":/res/radioButtonUncheckedPressed.png");
+}
 
 .QWidget{
 background: transparent;
@@ -256,6 +362,11 @@
 
 :disabled{
 color: #a0a0a0;
+border-color: #a0a0a0;
+}
+QListWidget:item:selected:disabled, QListView:item:selected:disabled{
+color: rgba(13, 5, 68, 70%);
+background-color: #a0a0a0;
 }
 SquareLabel, ItemNum {
 background-color: #000000;
@@ -266,6 +377,9 @@
 margin: 2px 0px;
 background-color: #ffcc00;
 }
+QSlider::groove::horizontal:disabled {
+background-color: #a0a0a0;
+}
 
 QSlider::handle::horizontal {
 border: 0px;
@@ -275,6 +389,9 @@
 height: 6px;
 border-radius: 3px;
 }
+QSlider::handle::horizontal:disabled {
+background-color: #a0a0a0;
+}
 
 HatButton, ThemeButton {
 text-align: left;
Binary file QTfrontend/res/dropdown_disabled.png has changed
Binary file QTfrontend/res/dropdown_selected.png has changed
--- a/QTfrontend/res/html/about.html	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/res/html/about.html	Sun Jun 10 19:12:26 2018 +0200
@@ -80,7 +80,7 @@
             Chinese: Jie Luo &lt;<a href="mailto:lililjlj@gmail.com">lililjlj@gmail.com</a>&gt;<br />
             English: Andrey Korotaev &lt;<a href="mailto:unC0Rr@gmail.com">unC0Rr@gmail.com</a>&gt;<br />
             Finnish: Nina Kuisma &lt;<a href="mailto:ninnnu@gmail.com">ninnnu@gmail.com</a>&gt;, Janne Uusitupa<br />
-            French: Antoine Turmel &lt;<a href="mailto:geekshadow@gmail.com">geekshadow@gmail.com</a>&gt;, Clement Woitrain &lt;<a href="mailto:sphrixclement@gmail.com">sphrixclement@gmail.com</a>&gt;, Matisumi<br />
+            French: Antoine Turmel &lt;<a href="mailto:geekshadow@gmail.com">geekshadow@gmail.com</a>&gt;, Clement Woitrain &lt;<a href="mailto:sphrixclement@gmail.com">sphrixclement@gmail.com</a>&gt;, Matisumi, Case_Of<br />
             German: Peter Hüwe &lt;<a href="mailto:PeterHuewe@gmx.de">PeterHuewe@gmx.de</a>&gt;, Mario Liebisch &lt;<a href="mailto:mario.liebisch@gmail.com">mario.liebisch@gmail.com</a>&gt;, Richard Karolyi &lt;<a href="mailto:sheepluva@ercatec.net">sheepluva@ercatec.net</a>&gt;, Wuzzy &lt;<a href="mailto:Wuzzy2@mail.ru">Wuzzy2@mail.ru</a>&gt;<br />
             Greek: &lt;<a href="mailto:talos_kriti@yahoo.gr">talos_kriti@yahoo.gr</a>&gt;<br />
             Italian: Luca Bonora &lt;<a href="mailto:bonora.luca@gmail.com">bonora.luca@gmail.com</a>&gt;, Marco Bresciani &lt;<a href="mailto:m.bresciani@email.it">m.bresciani@email.it</a>&gt;, Gianfranco Costamagna &lt;<a href="mailto:costamagnagianfranco@yahoo.it">costamagnagianfranco@yahoo.it</a>&gt;, Enrico &lt;<a href="mailto:enricobe@hotmail.com">enricobe@hotmail.com</a>&gt;<br />
@@ -89,7 +89,7 @@
             Lithuanian: Lukas Urbonas &lt;<a href="mailto:lukasu08@gmail.com">lukasu08@gmail.com</a>&gt;<br />
             Polish: Maciej Mroziński &lt;<a href="mailto:mynick2@o2.pl">mynick2@o2.pl</a>&gt;, Wojciech Latkowski &lt;<a href="mailto:magik17l@gmail.com">magik17l@gmail.com</a>&gt;, Piotr Mitana, Maciej Górny, KoBeWi<br />
             Portuguese: Fábio Canário &lt;<a href="mailto:inufabie@gmail.com">inufabie@gmail.com</a>&gt;<br />
-            Russian: Andrey Korotaev &lt;<a href="mailto:unC0Rr@gmail.com">unC0Rr@gmail.com</a>&gt;, Vitaly Novichkov &lt;<a href="mailto:admin@wohlnet.ru">admin@wohlnet.ru</a>&gt;, Anton Malmygin &lt;<a href="mailto:antonc27@mail.ru">antonc27@mail.ru</a>&gt;<br />
+            Russian: Andrey Korotaev &lt;<a href="mailto:unC0Rr@gmail.com">unC0Rr@gmail.com</a>&gt;, Vitaly Novichkov &lt;<a href="mailto:admin@wohlnet.ru">admin@wohlnet.ru</a>&gt;, Anton Malmygin &lt;<a href="mailto:antonc27@mail.ru">antonc27@mail.ru</a>&gt;, greno4ka<br />
             Slovak: Jose Riha<br />
             Spanish: Carlos Vives &lt;<a href="mailto:mail@carlosvives.es">mail@carlosvives.es</a>&gt;<br />
             Swedish: Niklas Grahn &lt;<a href="mailto:raewolusjoon@yaoo.com">raewolusjoon@yaoo.com</a>&gt;, Henrik Rostedt &lt;<a href="mailto:henrik.rostedt@gmail.com">henrik.rostedt@gmail.com</a>&gt;<br />
Binary file QTfrontend/res/lock_disabled.png has changed
Binary file QTfrontend/res/missingMap.png has changed
Binary file QTfrontend/res/missingTheme.png has changed
Binary file QTfrontend/res/missingTheme@2x.png has changed
Binary file QTfrontend/res/radioButtonChecked.png has changed
Binary file QTfrontend/res/radioButtonCheckedHover.png has changed
Binary file QTfrontend/res/radioButtonCheckedPressed.png has changed
Binary file QTfrontend/res/radioButtonUnchecked.png has changed
Binary file QTfrontend/res/radioButtonUncheckedHover.png has changed
Binary file QTfrontend/res/radioButtonUncheckedPressed.png has changed
Binary file QTfrontend/res/radioChecked.png has changed
Binary file QTfrontend/res/sort_down.png has changed
Binary file QTfrontend/res/sort_up.png has changed
Binary file QTfrontend/res/spin_down_disabled.png has changed
Binary file QTfrontend/res/spin_up_disabled.png has changed
Binary file QTfrontend/res/splitter.png has changed
Binary file QTfrontend/res/uncheckedHover.png has changed
Binary file QTfrontend/res/uncheckedPressed.png has changed
Binary file QTfrontend/res/unlock_disabled.png has changed
--- a/QTfrontend/sdlkeys.h	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/sdlkeys.h	Sun Jun 10 19:12:26 2018 +0200
@@ -94,21 +94,21 @@
     {"y", "Y"},
     {"z", "Z"},
     {"delete", QT_TRANSLATE_NOOP("binds (keys)", "Delete")},
-    {"[0]", QT_TRANSLATE_NOOP("binds (keys)", "Numpad 0")},
-    {"[1]", QT_TRANSLATE_NOOP("binds (keys)", "Numpad 1")},
-    {"[2]", QT_TRANSLATE_NOOP("binds (keys)", "Numpad 2")},
-    {"[3]", QT_TRANSLATE_NOOP("binds (keys)", "Numpad 3")},
-    {"[4]", QT_TRANSLATE_NOOP("binds (keys)", "Numpad 4")},
-    {"[5]", QT_TRANSLATE_NOOP("binds (keys)", "Numpad 5")},
-    {"[6]", QT_TRANSLATE_NOOP("binds (keys)", "Numpad 6")},
-    {"[7]", QT_TRANSLATE_NOOP("binds (keys)", "Numpad 7")},
-    {"[8]", QT_TRANSLATE_NOOP("binds (keys)", "Numpad 8")},
-    {"[9]", QT_TRANSLATE_NOOP("binds (keys)", "Numpad 9")},
-    {"[.]", QT_TRANSLATE_NOOP("binds (keys)", "Numpad .")},
-    {"[/]", QT_TRANSLATE_NOOP("binds (keys)", "Numpad /")},
-    {"[*]", QT_TRANSLATE_NOOP("binds (keys)", "Numpad *")},
-    {"[-]", QT_TRANSLATE_NOOP("binds (keys)", "Numpad -")},
-    {"[+]", QT_TRANSLATE_NOOP("binds (keys)", "Numpad +")},
+    {"keypad_0", QT_TRANSLATE_NOOP("binds (keys)", "Numpad 0")},
+    {"keypad_1", QT_TRANSLATE_NOOP("binds (keys)", "Numpad 1")},
+    {"keypad_2", QT_TRANSLATE_NOOP("binds (keys)", "Numpad 2")},
+    {"keypad_3", QT_TRANSLATE_NOOP("binds (keys)", "Numpad 3")},
+    {"keypad_4", QT_TRANSLATE_NOOP("binds (keys)", "Numpad 4")},
+    {"keypad_5", QT_TRANSLATE_NOOP("binds (keys)", "Numpad 5")},
+    {"keypad_6", QT_TRANSLATE_NOOP("binds (keys)", "Numpad 6")},
+    {"keypad_7", QT_TRANSLATE_NOOP("binds (keys)", "Numpad 7")},
+    {"keypad_8", QT_TRANSLATE_NOOP("binds (keys)", "Numpad 8")},
+    {"keypad_9", QT_TRANSLATE_NOOP("binds (keys)", "Numpad 9")},
+    {"keypad_.", QT_TRANSLATE_NOOP("binds (keys)", "Numpad .")},
+    {"keypad_/", QT_TRANSLATE_NOOP("binds (keys)", "Numpad /")},
+    {"keypad_*", QT_TRANSLATE_NOOP("binds (keys)", "Numpad *")},
+    {"keypad_-", QT_TRANSLATE_NOOP("binds (keys)", "Numpad -")},
+    {"keypad_+", QT_TRANSLATE_NOOP("binds (keys)", "Numpad +")},
     {"enter", QT_TRANSLATE_NOOP("binds (keys)", "Enter")},
     {"equals", QT_TRANSLATE_NOOP("binds (keys)", "Equals")},
     {"up", QT_TRANSLATE_NOOP("binds (keys)", "Up")},
--- a/QTfrontend/team.cpp	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/team.cpp	Sun Jun 10 19:12:26 2018 +0200
@@ -41,7 +41,7 @@
     for (int i = 0; i < HEDGEHOGS_PER_TEAM; i++)
     {
         m_hedgehogs.append(HWHog());
-        m_hedgehogs[i].Name = (QLineEdit::tr("hedgehog %1").arg(i+1));
+        m_hedgehogs[i].Name = (QLineEdit::tr("Hedgehog %1").arg(i+1));
         m_hedgehogs[i].Hat = "NoHat";
     }
     m_grave = "Statue";
@@ -171,7 +171,7 @@
 
 bool HWTeam::loadFromFile()
 {
-    QSettings teamfile(QString("physfs://Teams/%1.hwt").arg(DataManager::safeFileName(m_name)), QSettings::IniFormat, 0);
+    QSettings teamfile(QString(cfgdir->absolutePath() + "/Teams/%1.hwt").arg(DataManager::safeFileName(m_name)), QSettings::IniFormat, 0);
     teamfile.setIniCodec("UTF-8");
     m_name = teamfile.value("Team/Name", m_name).toString();
     m_grave = teamfile.value("Team/Grave", "Statue").toString();
@@ -185,7 +185,7 @@
     for(int i = 0; i < HEDGEHOGS_PER_TEAM; i++)
     {
         QString hh = QString("Hedgehog%1/").arg(i);
-        m_hedgehogs[i].Name = teamfile.value(hh + "Name", QString("hedgehog %1").arg(i+1)).toString();
+        m_hedgehogs[i].Name = teamfile.value(hh + "Name", QString("Hedgehog %1").arg(i+1)).toString();
         m_hedgehogs[i].Hat = teamfile.value(hh + "Hat", "NoHat").toString();
         m_hedgehogs[i].Rounds = teamfile.value(hh + "Rounds", 0).toInt();
         m_hedgehogs[i].Kills = teamfile.value(hh + "Kills", 0).toInt();
@@ -204,7 +204,7 @@
 
 bool HWTeam::fileExists()
 {
-    QFile f(QString("physfs://Teams/%1.hwt").arg(DataManager::safeFileName(m_name)));
+    QFile f(QString(cfgdir->absolutePath() + "/Teams/%1.hwt").arg(DataManager::safeFileName(m_name)));
     return f.exists();
 }
 
@@ -220,7 +220,7 @@
 {
     if(m_isNetTeam)
         return false;
-    QFile cfgfile(QString("physfs://Teams/%1.hwt").arg(DataManager::safeFileName(m_name)));
+    QFile cfgfile(QString(cfgdir->absolutePath() + "/Teams/%1.hwt").arg(DataManager::safeFileName(m_name)));
     cfgfile.remove();
     return true;
 }
@@ -229,12 +229,12 @@
 {
     if (OldTeamName != m_name)
     {
-        QFile cfgfile(QString("physfs://Teams/%1.hwt").arg(DataManager::safeFileName(OldTeamName)));
+        QFile cfgfile(QString(cfgdir->absolutePath() + "/Teams/%1.hwt").arg(DataManager::safeFileName(OldTeamName)));
         cfgfile.remove();
         OldTeamName = m_name;
     }
 
-    QString fileName = QString("physfs://Teams/%1.hwt").arg(DataManager::safeFileName(m_name));
+    QString fileName = QString(cfgdir->absolutePath() + "/Teams/%1.hwt").arg(DataManager::safeFileName(m_name));
     DataManager::ensureFileExists(fileName);
     QSettings teamfile(fileName, QSettings::IniFormat, 0);
     teamfile.setIniCodec("UTF-8");
--- a/QTfrontend/ui/dialog/upload_video.cpp	Sun Jun 10 18:56:51 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,312 +0,0 @@
-/*
- * Hedgewars, a free turn based strategy game
- * Copyright (c) 2004-2015 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- */
-
-#include <QLineEdit>
-#include <QDialogButtonBox>
-#include <QPushButton>
-#include <QGridLayout>
-#include <QCheckBox>
-#include <QLabel>
-#include <QFrame>
-#include <QPlainTextEdit>
-#include <QSslError>
-#include <QUrl>
-#include <QNetworkAccessManager>
-#include <QNetworkRequest>
-#include <QNetworkReply>
-#include <QMessageBox>
-#include <QRegExp>
-#include <QRegExpValidator>
-
-#include "upload_video.h"
-#include "hwconsts.h"
-
-// User-agent string used in http requests.
-// Don't make it a global varibale - crash on linux because of cVersionString
-#define USER_AGENT ("Hedgewars-QtFrontend/" + *cVersionString).toAscii()
-
-// This is developer key obtained from http://code.google.com/apis/youtube/dashboard/
-// If you are reusing this code outside Hedgewars, don't use this developer key,
-// obtain you own at http://code.google.com/apis/youtube/dashboard/
-static const QByteArray devKey = "AI39si5pKjxR0XgNIlmrEFF-LyYD31rps4g2O5dZTxLgD0fvJ2rHxrMrNFY8FYTZrzeI3VlaFVQLKfFnSBugvdZmy8vFzRDefQ";
-
-HWUploadVideoDialog::HWUploadVideoDialog(QWidget* parent, const QString &filename, QNetworkAccessManager* netManager) : QDialog(parent)
-{
-    this->filename = filename;
-    this->netManager = netManager;
-
-    setWindowTitle(tr("Upload video"));
-
-    // Google requires us to display this, see https://developers.google.com/youtube/terms
-    QString GoogleNotice =
-        "<p>By clicking 'upload,' you certify that you own all rights to the content or that "
-        "you are authorized by the owner to make the content publicly available on YouTube, "
-        "and that it otherwise complies with the YouTube Terms of Service located at "
-        "<a href=\"https://www.youtube.com/t/terms\" style=\"color: white;\">https://www.youtube.com/t/terms</a>.</p>";
-
-    // youtube doesn't understand this characters, even when they are properly escaped
-    // (either with CDATA or with &lt or &gt)
-    QRegExp rx("[^<>]*");
-
-    int row = 0;
-
-    QGridLayout * layout = new QGridLayout(this);
-    layout->setColumnStretch(0, 1);
-    layout->setColumnStretch(1, 2);
-
-    QLabel * lbLabel = new QLabel(this);
-    lbLabel->setWordWrap(true);
-    lbLabel->setText(QLabel::tr(
-                         "Please provide either the YouTube account name "
-                         "or the email address associated with the Google Account."));
-    layout->addWidget(lbLabel, row++, 0, 1, 2);
-
-    lbLabel = new QLabel(this);
-    lbLabel->setText(QLabel::tr("Account name (or email): "));
-    layout->addWidget(lbLabel, row, 0);
-
-    leAccount = new QLineEdit(this);
-    layout->addWidget(leAccount, row++, 1);
-
-    lbLabel = new QLabel(this);
-    lbLabel->setText(QLabel::tr("Password: "));
-    layout->addWidget(lbLabel, row, 0);
-
-    lePassword = new QLineEdit(this);
-    lePassword->setEchoMode(QLineEdit::Password);
-    layout->addWidget(lePassword, row++, 1);
-
-    cbSave = new QCheckBox(this);
-    cbSave->setText(QCheckBox::tr("Save account name and password"));
-    layout->addWidget(cbSave, row++, 0, 1, 2);
-
-    QFrame * hr = new QFrame(this);
-    hr->setFrameStyle(QFrame::HLine);
-    hr->setLineWidth(3);
-    hr->setFixedHeight(10);
-    layout->addWidget(hr, row++, 0, 1, 2);
-
-    lbLabel = new QLabel(this);
-    lbLabel->setText(QLabel::tr("Video title: "));
-    layout->addWidget(lbLabel, row, 0);
-
-    leTitle = new QLineEdit(this);
-    leTitle->setText(filename);
-    leTitle->setValidator(new QRegExpValidator(rx, leTitle));
-    layout->addWidget(leTitle, row++, 1);
-
-    lbLabel = new QLabel(this);
-    lbLabel->setText(QLabel::tr("Video description: "));
-    layout->addWidget(lbLabel, row++, 0, 1, 2);
-
-    teDescription = new QPlainTextEdit(this);
-    layout->addWidget(teDescription, row++, 0, 1, 2);
-
-    lbLabel = new QLabel(this);
-    lbLabel->setText(QLabel::tr("Tags (comma separated): "));
-    layout->addWidget(lbLabel, row, 0);
-
-    leTags = new QLineEdit(this);
-    leTags->setText("hedgewars");
-    leTags->setMaxLength(500);
-    leTags->setValidator(new QRegExpValidator(rx, leTags));
-    layout->addWidget(leTags, row++, 1);
-
-    cbPrivate = new QCheckBox(this);
-    cbPrivate->setText(QCheckBox::tr("Video is private"));
-    layout->addWidget(cbPrivate, row++, 0, 1, 2);
-
-    hr = new QFrame(this);
-        hr->setFrameStyle(QFrame::HLine);
-        hr->setLineWidth(3);
-        hr->setFixedHeight(10);
-        layout->addWidget(hr, row++, 0, 1, 2);
-
-    lbLabel = new QLabel(this);
-    lbLabel->setWordWrap(true);
-    lbLabel->setTextInteractionFlags(Qt::LinksAccessibleByMouse);
-    lbLabel->setTextFormat(Qt::RichText);
-    lbLabel->setOpenExternalLinks(true);
-    lbLabel->setText(GoogleNotice);
-    layout->addWidget(lbLabel, row++, 0, 1, 2);
-
-    QDialogButtonBox* dbbButtons = new QDialogButtonBox(this);
-    btnUpload = dbbButtons->addButton(tr("Upload"), QDialogButtonBox::ActionRole);
-    QPushButton * pbCancel = dbbButtons->addButton(QDialogButtonBox::Cancel);
-    layout->addWidget(dbbButtons, row++, 0, 1, 2);
-
-   /* hr = new QFrame(this);
-        hr->setFrameStyle(QFrame::HLine);
-        hr->setLineWidth(3);
-        hr->setFixedHeight(10);
-        layout->addWidget(hr, row++, 0, 1, 2);*/
-
-    connect(btnUpload, SIGNAL(clicked()), this, SLOT(upload()));
-    connect(pbCancel, SIGNAL(clicked()), this, SLOT(reject()));
-
-    this->setWindowModality(Qt::WindowModal);
-}
-
-void HWUploadVideoDialog::showEvent(QShowEvent * event)
-{
-    QDialog::showEvent(event);
-
-    // set width to the same value as height (otherwise dialog has too small width)
-    QSize s = size();
-    QPoint p = pos();
-    resize(s.height(), s.height());
-    move(p.x() - (s.height() - s.width())/2, p.y());
-}
-
-void HWUploadVideoDialog::setEditable(bool editable)
-{
-    leTitle->setEnabled(editable);
-    leAccount->setEnabled(editable);
-    lePassword->setEnabled(editable);
-    btnUpload->setEnabled(editable);
-}
-
-void HWUploadVideoDialog::upload()
-{
-    setEditable(false);
-
-    // Documentation is at https://developers.google.com/youtube/2.0/developers_guide_protocol_clientlogin#ClientLogin_Authentication
-    QNetworkRequest request;
-    request.setUrl(QUrl("https://www.google.com/accounts/ClientLogin"));
-    request.setRawHeader("User-Agent", USER_AGENT);
-    request.setRawHeader("Content-Type", "application/x-www-form-urlencoded");
-
-    QString account(QUrl::toPercentEncoding(leAccount->text()));
-    QString pass(QUrl::toPercentEncoding(lePassword->text()));
-    QByteArray data = QString("Email=%1&Passwd=%2&service=youtube&source=Hedgewars").arg(account).arg(pass).toAscii();
-
-    QNetworkReply *reply = netManager->post(request, data);
-    connect(reply, SIGNAL(finished()), this, SLOT(authFinished()));
-}
-
-static QString XmlEscape(const QString& str)
-{
-    QString str2 = str;
-    // youtube doesn't understand this characters, even when they are properly escaped
-    // (either with CDATA or with &lt; &gt;)
-    str2.replace('<', ' ').replace('>', ' ');
-    return "<![CDATA[" + str2.replace("]]>", "]]]]><![CDATA[>") + "]]>";
-}
-
-void HWUploadVideoDialog::authFinished()
-{
-    QNetworkReply *reply = (QNetworkReply*)sender();
-    reply->deleteLater();
-
-    int HttpCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
-
-    QByteArray answer = reply->readAll();
-    QString authToken = "";
-    QList<QByteArray> lines = answer.split('\n');
-    foreach (const QByteArray& line, lines)
-    {
-        QString str(line);
-        if (!str.startsWith("Auth=", Qt::CaseInsensitive))
-            continue;
-        str.remove(0, 5);
-        authToken = str;
-        break;
-    }
-    if (authToken.isEmpty())
-    {
-        QString errorStr = QMessageBox::tr("Error while authenticating at google.com:\n");
-        if (HttpCode == 403)
-            errorStr += QMessageBox::tr("Login or password is incorrect");
-        else
-            errorStr += reply->errorString();
-
-        QMessageBox deniedMsg(this);
-        deniedMsg.setIcon(QMessageBox::Warning);
-        deniedMsg.setWindowTitle(QMessageBox::tr("Video upload - Error"));
-        deniedMsg.setText(errorStr);
-        deniedMsg.setWindowModality(Qt::WindowModal);
-        deniedMsg.exec();
-
-        setEditable(true);
-        return;
-    }
-
-    QByteArray auth = ("GoogleLogin auth=" + authToken).toAscii();
-
-    // We have authenticated, now we can send metadata and start upload
-    // Documentation is here: https://developers.google.com/youtube/2.0/developers_guide_protocol_resumable_uploads#Resumable_uploads
-    QByteArray body =
-            "<?xml version=\"1.0\"?>"
-            "<entry xmlns=\"http://www.w3.org/2005/Atom\" "
-                "xmlns:media=\"http://search.yahoo.com/mrss/\" "
-                "xmlns:yt=\"http://gdata.youtube.com/schemas/2007\">"
-                "<media:group>"
-                  //  "<yt:incomplete/>"
-                    "<media:category "
-                        "scheme=\"http://gdata.youtube.com/schemas/2007/categories.cat\">Games"
-                    "</media:category>"
-                    "<media:title type=\"plain\">"
-                        + XmlEscape(leTitle->text()).toUtf8() +
-                    "</media:title>"
-                    "<media:description type=\"plain\">"
-                        + XmlEscape(teDescription->toPlainText()).toUtf8() +
-                    "</media:description>"
-                    "<media:keywords type=\"plain\">"
-                        + XmlEscape(leTags->text()).toUtf8() +
-                    "</media:keywords>"
-                    + (cbPrivate->isChecked()? "<yt:private/>" : "") +
-                "</media:group>"
-            "</entry>";
-
-    QNetworkRequest request;
-    request.setUrl(QUrl("http://uploads.gdata.youtube.com/resumable/feeds/api/users/default/uploads"));
-    request.setRawHeader("User-Agent", USER_AGENT);
-    request.setRawHeader("Authorization", auth);
-    request.setRawHeader("GData-Version", "2");
-    request.setRawHeader("X-GData-Key", "key=" + devKey);
-    request.setRawHeader("Slug", filename.toUtf8());
-    request.setRawHeader("Content-Type", "application/atom+xml; charset=UTF-8");
-
-    reply = netManager->post(request, body);
-    connect(reply, SIGNAL(finished()), this, SLOT(startUpload()));
-}
-
-void HWUploadVideoDialog::startUpload()
-{
-    QNetworkReply *reply = (QNetworkReply*)sender();
-    reply->deleteLater();
-
-    location = QString::fromLatin1(reply->rawHeader("Location"));
-    if (location.isEmpty())
-    {
-        QString errorStr = QMessageBox::tr("Error while sending metadata to youtube.com:\n");
-        errorStr += reply->errorString();
-
-        QMessageBox deniedMsg(this);
-        deniedMsg.setIcon(QMessageBox::Warning);
-        deniedMsg.setWindowTitle(QMessageBox::tr("Video upload - Error"));
-        deniedMsg.setText(errorStr);
-        deniedMsg.setWindowModality(Qt::WindowModal);
-        deniedMsg.exec();
-
-        setEditable(true);
-        return;
-    }
-
-    accept();
-}
--- a/QTfrontend/ui/dialog/upload_video.h	Sun Jun 10 18:56:51 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,65 +0,0 @@
-/*
- * Hedgewars, a free turn based strategy game
- * Copyright (c) 2004-2015 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- */
-
-#ifndef UPLOAD_VIDEO_H
-#define UPLOAD_VIDEO_H
-
-#include <QDialog>
-
-class QLineEdit;
-class QCheckBox;
-class QPlainTextEdit;
-class QLabel;
-class QNetworkAccessManager;
-
-class HWUploadVideoDialog : public QDialog
-{
-        Q_OBJECT
-    public:
-    HWUploadVideoDialog(QWidget* parent, const QString& filename, QNetworkAccessManager* netManager);
-
-        QLineEdit* leAccount;
-        QLineEdit* lePassword;
-        QCheckBox* cbSave;
-
-        QLineEdit* leTitle;
-        QPlainTextEdit* teDescription;
-        QLineEdit* leTags;
-        QCheckBox* cbPrivate;
-
-        QPushButton* btnUpload;
-
-        QString location;
-
-    private:
-        QNetworkAccessManager* netManager;
-        QString filename;
-
-        void setEditable(bool editable);
-
-    protected:
-        // virtual from QWidget
-        void showEvent(QShowEvent * event);
-
-    private slots:
-        void upload();
-        void authFinished();
-        void startUpload();
-};
-
-#endif // UPLOAD_VIDEO_H
--- a/QTfrontend/ui/page/pageadmin.cpp	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/ui/page/pageadmin.cpp	Sun Jun 10 19:12:26 2018 +0200
@@ -98,7 +98,7 @@
                               << tr("Expiration")
                               << tr("Reason")
                     );
-        twBans->horizontalHeader()->setResizeMode(2, QHeaderView::Stretch);
+        twBans->horizontalHeader()->setSectionResizeMode(2, QHeaderView::Stretch);
         twBans->setEditTriggers(QAbstractItemView::NoEditTriggers);
         twBans->setSelectionBehavior(QAbstractItemView::SelectRows);
         twBans->setSelectionMode(QAbstractItemView::SingleSelection);
--- a/QTfrontend/ui/page/pagedata.cpp	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/ui/page/pagedata.cpp	Sun Jun 10 19:12:26 2018 +0200
@@ -31,8 +31,6 @@
 #include "pagedata.h"
 #include "databrowser.h"
 #include "hwconsts.h"
-#include "DataManager.h"
-#include "FileEngine.h"
 
 QLayout * PageDataDownload::bodyLayoutDefinition()
 {
@@ -197,9 +195,6 @@
         out.write(reply->readAll());
 
         out.close();
-
-        // now mount it
-        FileEngineHandler::mount(fileName);
     }
 }
 
@@ -229,7 +224,6 @@
     if (m_contentDownloaded)
     {
         m_contentDownloaded = false;
-        //DataManager::instance().reload();
     }
 }
 
--- a/QTfrontend/ui/page/pagedrawmap.cpp	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/ui/page/pagedrawmap.cpp	Sun Jun 10 19:12:26 2018 +0200
@@ -21,9 +21,12 @@
 #include <QFileDialog>
 #include <QCheckBox>
 #include <QRadioButton>
+#include <QSpinBox>
+#include <QDir>
 
 #include "pagedrawmap.h"
 #include "drawmapwidget.h"
+#include "hwconsts.h"
 
 
 QLayout * PageDrawMap::bodyLayoutDefinition()
@@ -32,22 +35,30 @@
 
     cbEraser = new QCheckBox(tr("Eraser"), this);
     pageLayout->addWidget(cbEraser, 0, 0);
-    pbUndo = addButton(tr("Undo"), pageLayout, 1, 0);
 
     rbPolyline = new QRadioButton(tr("Polyline"), this);
-    pageLayout->addWidget(rbPolyline, 2, 0);
+    pageLayout->addWidget(rbPolyline, 1, 0);
     rbRectangle = new QRadioButton(tr("Rectangle"), this);
-    pageLayout->addWidget(rbRectangle, 3, 0);
+    pageLayout->addWidget(rbRectangle, 2, 0);
     rbEllipse = new QRadioButton(tr("Ellipse"), this);
-    pageLayout->addWidget(rbEllipse, 4, 0);
+    pageLayout->addWidget(rbEllipse, 3, 0);
 
     rbPolyline->setChecked(true);
 
-    pbClear = addButton(tr("Clear"), pageLayout, 5, 0);
-    pbOptimize = addButton(tr("Optimize"), pageLayout, 6, 0);
+    sbBrushSize = new QSpinBox(this);
+    sbBrushSize->setWhatsThis(tr("Brush size"));
+    sbBrushSize->setRange(DRAWN_MAP_BRUSH_SIZE_MIN, DRAWN_MAP_BRUSH_SIZE_MAX);
+    sbBrushSize->setValue(DRAWN_MAP_BRUSH_SIZE_START);
+    sbBrushSize->setSingleStep(DRAWN_MAP_BRUSH_SIZE_STEP);
+    pageLayout->addWidget(sbBrushSize, 4, 0);
+
+    pbUndo = addButton(tr("Undo"), pageLayout, 5, 0);
+    pbClear = addButton(tr("Clear"), pageLayout, 6, 0);
+
+    pbOptimize = addButton(tr("Optimize"), pageLayout, 7, 0);
+    // The optimize button is quite buggy, so we disable it for now.
+    // TODO: Re-enable optimize button when it's finished.
     pbOptimize->setVisible(false);
-    pbLoad = addButton(tr("Load"), pageLayout, 7, 0);
-    pbSave = addButton(tr("Save"), pageLayout, 8, 0);
 
     drawMapWidget = new DrawMapWidget(this);
     pageLayout->addWidget(drawMapWidget, 0, 1, 10, 1);
@@ -55,12 +66,33 @@
     return pageLayout;
 }
 
+QLayout * PageDrawMap::footerLayoutDefinition()
+{
+    QHBoxLayout * bottomLayout = new QHBoxLayout();
+
+    bottomLayout->addStretch();
+
+    pbLoad = addButton(":/res/Load.png", bottomLayout, 0, true, Qt::AlignBottom);
+    pbLoad ->setWhatsThis(tr("Load"));
+    pbLoad->setStyleSheet("QPushButton{margin: 24px 0 0 0;}");
+
+    pbSave = addButton(":/res/Save.png", bottomLayout, 0, true, Qt::AlignBottom);
+    pbSave ->setWhatsThis(tr("Save"));
+    pbSave->setStyleSheet("QPushButton{margin: 24px 0 0 0;}");
+
+    return bottomLayout;
+}
+
 void PageDrawMap::connectSignals()
 {
     connect(cbEraser, SIGNAL(toggled(bool)), drawMapWidget, SLOT(setErasing(bool)));
     connect(pbUndo, SIGNAL(clicked()), drawMapWidget, SLOT(undo()));
     connect(pbClear, SIGNAL(clicked()), drawMapWidget, SLOT(clear()));
     connect(pbOptimize, SIGNAL(clicked()), drawMapWidget, SLOT(optimize()));
+    connect(sbBrushSize, SIGNAL(valueChanged(int)), drawMapWidget, SLOT(setBrushSize(int)));
+
+    connect(drawMapWidget, SIGNAL(brushSizeChanged(int)), this, SLOT(brushSizeChanged(int)));
+
     connect(pbLoad, SIGNAL(clicked()), this, SLOT(load()));
     connect(pbSave, SIGNAL(clicked()), this, SLOT(save()));
 
@@ -76,7 +108,8 @@
 
 void PageDrawMap::load()
 {
-    QString fileName = QFileDialog::getOpenFileName(NULL, tr("Load drawn map"), ".", tr("Drawn Maps") + " (*.hwmap);;" + tr("All files") + " (*)");
+    QString loadDir = QDir(cfgdir->absolutePath() + "/DrawnMaps").absolutePath();
+    QString fileName = QFileDialog::getOpenFileName(this, tr("Load drawn map"), loadDir, tr("Drawn Maps") + " (*.hwmap);;" + tr("All files") + " (*)");
 
     if(!fileName.isEmpty())
         drawMapWidget->load(fileName);
@@ -84,7 +117,8 @@
 
 void PageDrawMap::save()
 {
-    QString fileName = QFileDialog::getSaveFileName(NULL, tr("Save drawn map"), "./map.hwmap", tr("Drawn Maps") + " (*.hwmap);;" + tr("All files") + " (*)");
+    QString saveDir = QDir(cfgdir->absolutePath() + "/DrawnMaps/map.hwmap").absolutePath();
+    QString fileName = QFileDialog::getSaveFileName(this, tr("Save drawn map"), saveDir, tr("Drawn Maps") + " (*.hwmap);;" + tr("All files") + " (*)");
 
     if(!fileName.isEmpty())
         drawMapWidget->save(fileName);
@@ -99,3 +133,8 @@
         else if(rbEllipse->isChecked()) drawMapWidget->setPathType(DrawMapScene::Ellipse);
     }
 }
+
+void PageDrawMap::brushSizeChanged(int brushSize)
+{
+    sbBrushSize->setValue(brushSize);
+}
--- a/QTfrontend/ui/page/pagedrawmap.h	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/ui/page/pagedrawmap.h	Sun Jun 10 19:12:26 2018 +0200
@@ -35,6 +35,7 @@
 
     protected:
         QLayout * bodyLayoutDefinition();
+        QLayout * footerLayoutDefinition();
         void connectSignals();
 
     private:
@@ -47,6 +48,10 @@
         QRadioButton * rbPolyline;
         QRadioButton * rbRectangle;
         QRadioButton * rbEllipse;
+        QSpinBox * sbBrushSize;
+
+    public slots:
+        void brushSizeChanged(int brushSize);
 
     private slots:
         void load();
--- a/QTfrontend/ui/page/pageeditteam.cpp	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/ui/page/pageeditteam.cpp	Sun Jun 10 19:12:26 2018 +0200
@@ -77,7 +77,7 @@
         HHNameEdit[i]->setMinimumWidth(120);
         HHNameEdit[i]->setFixedHeight(36);
         HHNameEdit[i]->setWhatsThis(tr("This hedgehog's name"));
-        HHNameEdit[i]->setStyleSheet("padding: 6px;");
+        HHNameEdit[i]->setStyleSheet("QLineEdit { padding: 6px; }");
         GBHLayout->addWidget(HHNameEdit[i], i + 1, 1, 1, 2);
 
         btnRandomHogName[i] = addButton(":/res/dice.png", GBHLayout, i + 1, 5, 1, 1, true);
@@ -128,6 +128,7 @@
     TeamNameEdit = new QLineEdit(GBoxTeam);
     TeamNameEdit->setMaxLength(64);
     TeamNameEdit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
+    TeamNameEdit->setStyleSheet("QLineEdit { padding: 6px; }");
     GBTLayout->addWidget(TeamNameEdit, 0, 1, 1, 2);
     vbox2->addWidget(GBoxTeam);
 
--- a/QTfrontend/ui/page/pagegamestats.cpp	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/ui/page/pagegamestats.cpp	Sun Jun 10 19:12:26 2018 +0200
@@ -70,6 +70,7 @@
 
     // graph
     graphic = new FitGraphicsView(gb);
+    graphic->setObjectName("gameStatsView");
     labelGraphTitle = new QLabel(this);
     labelGraphTitle->setTextFormat(Qt::RichText);
     labelGraphTitle->setText("<br><h1><img src=\":/res/StatsH.png\"> " + PageGameStats::tr("Health graph") + "</h1>");
@@ -78,6 +79,7 @@
     gbl->addWidget(graphic);
     graphic->scale(1.0, -1.0);
     graphic->setBackgroundBrush(QBrush(Qt::black));
+    graphic->setRenderHint(QPainter::Antialiasing, true);
 
     labelGameWin = new QLabel(this);
     labelGameWin->setTextFormat(Qt::RichText);
@@ -160,8 +162,6 @@
 
 void PageGameStats::renderStats()
 {
-    graphic->show();
-    labelGraphTitle-> show();
     if(defaultGraphTitle) {
         labelGraphTitle->setText("<br><h1><img src=\":/res/StatsH.png\"> " + PageGameStats::tr("Health graph") + "</h1>");
     } else {
@@ -172,28 +172,81 @@
         labelGraphTitle->hide();
         graphic->hide();
     } else {
-        QGraphicsScene * scene = new QGraphicsScene();
+        graphic->setScene(Q_NULLPTR);
+        m_scene.reset(new QGraphicsScene(this));
+
+        // min and max value across the entire chart
+        qint32 minValue = 0;
+        qint32 maxValue = 0;
+        bool minMaxValuesInitialized = false;
 
-        QMap<quint32, QVector<quint32> >::const_iterator i = healthPoints.constBegin();
+        // max data points per clan
+        int maxDataPoints = 0;
+        for(QMap<qint32, QVector<qint32> >::const_iterator i = healthPoints.constBegin(); i != healthPoints.constEnd(); ++i)
+        {
+            maxDataPoints = qMax(maxDataPoints, i.value().size());
+        }
+
+        /* There must be at least 2 data points for any clan,
+           otherwise there's not much to look at. ;-) */
+        if(maxDataPoints < 2) {
+            labelGraphTitle->hide();
+            graphic->hide();
+            return;
+        }
+
+        QMap<qint32, QVector<qint32> >::const_iterator i = healthPoints.constBegin();
         while (i != healthPoints.constEnd())
         {
-            quint32 c = i.key();
-            //QColor clanColor = QColor(qRgb((c >> 16) & 255, (c >> 8) & 255, c & 255));
-            QVector<quint32> hps = i.value();
+            qint32 c = i.key();
+            const QVector<qint32>& hps = i.value();
 
             QPainterPath path;
-            if (hps.size())
-                path.moveTo(0, hps[0]);
 
-            for(int t = 1; t < hps.size(); ++t)
+            if (hps.size()) {
+                path.moveTo(0, hps[0]);
+                if(minMaxValuesInitialized) {
+                    minValue = qMin(minValue, hps[0]);
+                    maxValue = qMax(maxValue, hps[0]);
+                } else {
+                    minValue = hps[0];
+                    maxValue = hps[0];
+                    minMaxValuesInitialized = true;
+                }
+            }
+
+            for(int t = 0; t < hps.size(); ++t) {
                 path.lineTo(t, hps[t]);
+                maxValue = qMax(maxValue, hps[t]);
+                minValue = qMin(minValue, hps[t]);
+            }
 
-            scene->addPath(path, QPen(c));
+            QPen pen(c);
+            pen.setWidth(2);
+            pen.setCosmetic(true);
+
+            m_scene->addPath(path, pen);
             ++i;
         }
 
-        graphic->setScene(scene);
+        graphic->setScene(m_scene.data());
+
+        // Calculate the bounding box of the final chart
+        qint32 sceneMinY = minValue;
+        qint32 sceneMaxY = maxValue;
+        // If all values are 0 or greater, make sure to include 0 at the bottom.
+        if(sceneMinY >= 0 && sceneMaxY >= 0)
+            sceneMinY = 0;
+        // If all values are equal, we must increase sceneMaxY, otherwise the scene rect
+        // would have a height of 0 and will screw up
+        if(sceneMinY == sceneMaxY)
+            sceneMaxY++;
+        graphic->setSceneRect(0, sceneMinY, maxDataPoints-1, sceneMaxY - sceneMinY);
+
         graphic->fitInView(graphic->sceneRect());
+
+        graphic->show();
+        labelGraphTitle->show();
     }
 }
 
@@ -233,7 +286,7 @@
         {
             int i = info.indexOf(' ');
             quint32 clan = info.left(i).toInt();
-            quint32 hp = info.mid(i + 1).toUInt();
+            qint32 hp = info.mid(i + 1).toInt();
             healthPoints[clan].append(hp);
             break;
         }
--- a/QTfrontend/ui/page/pagegamestats.h	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/ui/page/pagegamestats.h	Sun Jun 10 19:12:26 2018 +0200
@@ -66,10 +66,11 @@
     private:
         void AddStatText(const QString & msg);
 
-        QMap<quint32, QVector<quint32> > healthPoints;
+        QMap<qint32, QVector<qint32> > healthPoints;
         unsigned int playerPosition;
         quint32 lastColor;
         bool defaultGraphTitle;
+        QScopedPointer<QGraphicsScene> m_scene;
 
     protected:
         QLayout * bodyLayoutDefinition();
--- a/QTfrontend/ui/page/pagemain.cpp	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/ui/page/pagemain.cpp	Sun Jun 10 19:12:26 2018 +0200
@@ -75,7 +75,7 @@
 
     // button order matters for overlapping (what's on top and what isn't)
     BtnInfo = addButton(":/res/HedgewarsTitle.png", pageLayout, 0, 0, 1, 4, true);
-    BtnInfo->setStyleSheet("border: transparent;background: transparent;");
+	BtnInfo->setObjectName("infoButton");
     BtnInfo->setWhatsThis(tr("Read about who is behind the Hedgewars Project"));
     pageLayout->setAlignment(BtnInfo, Qt::AlignHCenter);
 
@@ -166,9 +166,7 @@
         QSettings settings(dataMgr.settingsFileName(),
                            QSettings::IniFormat);
 
-        QString loc = settings.value("misc/locale", "").toString();
-        if (loc.isEmpty())
-            loc = QLocale::system().name();
+        QString loc = QLocale().name();
 
         QString tipFile = QString("physfs://Locale/tips_" + loc + ".xml");
 
--- a/QTfrontend/ui/page/pagenet.cpp	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/ui/page/pagenet.cpp	Sun Jun 10 19:12:26 2018 +0200
@@ -99,7 +99,7 @@
 {
     tvServersList->setModel(new HWNetUdpModel(tvServersList));
 
-    tvServersList->horizontalHeader()->setResizeMode(0, QHeaderView::Stretch);
+    tvServersList->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch);
 
     static_cast<HWNetServersModel *>(tvServersList->model())->updateList();
 
--- a/QTfrontend/ui/page/pageoptions.cpp	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/ui/page/pageoptions.cpp	Sun Jun 10 19:12:26 2018 +0200
@@ -134,6 +134,7 @@
 
             CBTeamName = new QComboBox(groupTeams);
             CBTeamName->setMaxVisibleItems(50);
+            CBTeamName->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
             groupTeams->layout()->addWidget(CBTeamName, 0, 0);
 
             BtnNewTeam = new QPushButton(groupTeams);
@@ -175,6 +176,7 @@
 
             SchemesName = new QComboBox(groupSchemes);
             SchemesName->setMaxVisibleItems(50);
+            SchemesName->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
             groupSchemes->layout()->addWidget(SchemesName, 0, 0);
 
             SchemeNew = new QPushButton(groupSchemes);
@@ -207,6 +209,7 @@
 
             WeaponsName = new QComboBox(groupWeapons);
             WeaponsName->setMaxVisibleItems(50);
+            WeaponsName->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
             groupWeapons->layout()->addWidget(WeaponsName, 0, 0);
 
             WeaponNew = new QPushButton(groupWeapons);
@@ -652,17 +655,8 @@
                 // Fallback code, if language name is empty for some reason. This should normally not happen
                 if(entryName.isEmpty())
                 {
-                    if(lname == "gd")
-                    {
-                        /* Workaround for Qt4: nativeLanguageName does not return correct name for Scottish Gaelic (QTBUG-59929),
-                           so we have to add it ourselves :-/ */
-                        entryName = QString::fromUtf8("Gàidhlig");
-                    }
-                    else
-                    {
-                        // If all else fails, show error and the locale identifier
-                        entryName = tr("MISSING LANGUAGE NAME [%1]").arg(lname);
-                    }
+                    // Show error and the locale identifier
+                    entryName = tr("MISSING LANGUAGE NAME [%1]").arg(lname);
                 }
                 CBLanguage->addItem(entryName, lname);
             }
--- a/QTfrontend/ui/page/pageroomslist.cpp	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/ui/page/pageroomslist.cpp	Sun Jun 10 19:12:26 2018 +0200
@@ -29,12 +29,13 @@
 #include <QMenu>
 #include <QDebug>
 #include <QSplitter>
+#include <QSettings>
 
 #include <QSortFilterProxyModel>
 
 #include "roomslistmodel.h"
 
-#include "ammoSchemeModel.h"
+#include "gameSchemeModel.h"
 #include "hwconsts.h"
 #include "chatwidget.h"
 #include "roomnameprompt.h"
@@ -154,7 +155,8 @@
     roomsList = new RoomTableView(this);
     roomsList->setSelectionBehavior(QAbstractItemView::SelectRows);
     roomsList->verticalHeader()->setVisible(false);
-    roomsList->horizontalHeader()->setResizeMode(QHeaderView::Interactive);
+    roomsList->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive);
+	roomsList->horizontalHeader()->stretchLastSection();
     roomsList->setAlternatingRowColors(true);
     roomsList->setShowGrid(false);
     roomsList->setSelectionMode(QAbstractItemView::SingleSelection);
@@ -587,27 +589,18 @@
 
     h->setSortIndicatorShown(true);
     h->setSortIndicator(RoomsListModel::StateColumn, Qt::AscendingOrder);
-    h->setResizeMode(RoomsListModel::NameColumn, QHeaderView::Stretch);
+    h->setSectionResizeMode(RoomsListModel::NameColumn, QHeaderView::Stretch);
 
-    if (!restoreHeaderState())
-    {
-        h->resizeSection(RoomsListModel::PlayerCountColumn, 32);
-        h->resizeSection(RoomsListModel::TeamCountColumn, 32);
-        h->resizeSection(RoomsListModel::OwnerColumn, 100);
-        h->resizeSection(RoomsListModel::MapColumn, 100);
-        h->resizeSection(RoomsListModel::SchemeColumn, 100);
-        h->resizeSection(RoomsListModel::WeaponsColumn, 100);
-    }
+	h->resizeSection(RoomsListModel::PlayerCountColumn, 32);
+	h->resizeSection(RoomsListModel::TeamCountColumn, 32);
+	h->resizeSection(RoomsListModel::OwnerColumn, 100);
+	h->resizeSection(RoomsListModel::MapColumn, 100);
+	h->resizeSection(RoomsListModel::SchemeColumn, 100);
+	h->resizeSection(RoomsListModel::WeaponsColumn, 100);
 
     // hide column used for filtering
     roomsList->hideColumn(RoomsListModel::StateColumn);
 
-    // save header state on change
-    connect(roomsList->horizontalHeader(), SIGNAL(sortIndicatorChanged(int, Qt::SortOrder)),
-            this, SLOT(saveHeaderState()));
-    connect(roomsList->horizontalHeader(), SIGNAL(sectionResized(int, int, int)),
-            this, SLOT(saveHeaderState()));
-
     roomsList->repaint();
 }
 
@@ -670,27 +663,3 @@
 {
     m_gameSettings = settings;
 }
-
-bool PageRoomsList::restoreHeaderState()
-{
-    if (m_gameSettings->contains("frontend/roomslist_splitter"))
-    {
-        m_splitter->restoreState(QByteArray::fromBase64(
-            (m_gameSettings->value("frontend/roomslist_splitter").toByteArray())));
-    }
-
-    if (m_gameSettings->contains("frontend/roomslist_header"))
-    {
-        return roomsList->horizontalHeader()->restoreState(QByteArray::fromBase64(
-            (m_gameSettings->value("frontend/roomslist_header").toByteArray())));
-    } else return false;
-}
-
-void PageRoomsList::saveHeaderState()
-{
-    m_gameSettings->setValue("frontend/roomslist_header",
-        QString(roomsList->horizontalHeader()->saveState().toBase64()));
-
-    m_gameSettings->setValue("frontend/roomslist_splitter",
-        QString(m_splitter->saveState().toBase64()));
-}
--- a/QTfrontend/ui/page/pageroomslist.h	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/ui/page/pageroomslist.h	Sun Jun 10 19:12:26 2018 +0200
@@ -23,7 +23,7 @@
 #include "AbstractPage.h"
 
 class HWChatWidget;
-class AmmoSchemeModel;
+class GameSchemeModel;
 class QTableView;
 class RoomsListModel;
 class QSortFilterProxyModel;
@@ -84,7 +84,6 @@
         void onJoinConfirmation(const QString &);
         void onSortIndicatorChanged(int logicalIndex, Qt::SortOrder order);
         void onFilterChanged();
-        void saveHeaderState();
         void onRoomNameChosen(const QString &, const QString &password);
         void roomSelectionChanged(const QModelIndex &, const QModelIndex &);
         void moveSelectionUp();
@@ -100,7 +99,7 @@
         QAction * showJoinRestricted;
         QSplitter * m_splitter;
 
-        AmmoSchemeModel * ammoSchemeModel;
+        GameSchemeModel * gameSchemeModel;
 
         bool restoreHeaderState();
 };
--- a/QTfrontend/ui/page/pagescheme.cpp	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/ui/page/pagescheme.cpp	Sun Jun 10 19:12:26 2018 +0200
@@ -27,7 +27,7 @@
 #include <QDataWidgetMapper>
 #include <QSpinBox>
 
-#include "ammoSchemeModel.h"
+#include "gameSchemeModel.h"
 #include "pagescheme.h"
 #include "FreqSpinBox.h"
 #include "MinesTimeSpinBox.h"
@@ -84,6 +84,10 @@
     TBW_bottomborder->setWhatsThis(tr("Add an indestructible border along the bottom"));
     glGMLayout->addWidget(TBW_bottomborder,0,3,1,1);
 
+    TBW_switchhog = new ToggleButtonWidget(gbGameModes, ":/res/btnSwitchHog@2x.png");
+    TBW_switchhog->setWhatsThis(tr("Select a hedgehog at the beginning of a turn"));
+    glGMLayout->addWidget(TBW_switchhog,0,4,1,1);
+
     TBW_solid = new ToggleButtonWidget(gbGameModes, ":/res/btnSolid@2x.png");
     TBW_solid->setWhatsThis(tr("Land can not be destroyed!"));
     glGMLayout->addWidget(TBW_solid,1,0,1,1);
@@ -494,15 +498,14 @@
     LE_ScriptParam->setMaxLength(240);
     glBSLayout->addWidget(LE_ScriptParam,17,2,1,1);
 
-
-    l = new QLabel(gbBasicSettings);
-    l->setText(QLabel::tr("Scheme Name:"));
+    L_name = new QLabel(gbBasicSettings);
+    L_name->setText(QLabel::tr("Scheme Name:"));
 
     LE_name = new QLineEdit(this);
     LE_name->setWhatsThis(tr("Name of this scheme"));
 
     gl->addWidget(LE_name,15,1,1,5);
-    gl->addWidget(l,15,0,1,1);
+    gl->addWidget(L_name,15,0,1,1);
 
     return pageLayout;
 }
@@ -546,6 +549,7 @@
     selectScheme->setModel(model);
 
     mapper->addMapping(LE_name, 0);
+    mapper->addMapping(TBW_switchhog, 1);
     mapper->addMapping(TBW_teamsDivide, 2);
     mapper->addMapping(TBW_solid, 3);
     mapper->addMapping(TBW_border, 4);
@@ -608,7 +612,7 @@
 
 void PageScheme::deleteRow()
 {
-    int numberOfDefaultSchemes = ((AmmoSchemeModel*)mapper->model())->numberOfDefaultSchemes;
+    int numberOfDefaultSchemes = ((GameSchemeModel*)mapper->model())->numberOfDefaultSchemes;
     if (selectScheme->currentIndex() < numberOfDefaultSchemes)
     {
         QMessageBox deniedMsg(this);
@@ -637,10 +641,11 @@
 
 void PageScheme::schemeSelected(int n)
 {
-    int c = ((AmmoSchemeModel*)mapper->model())->numberOfDefaultSchemes;
+    int c = ((GameSchemeModel*)mapper->model())->numberOfDefaultSchemes;
     gbGameModes->setEnabled(n >= c);
     gbBasicSettings->setEnabled(n >= c);
     LE_name->setEnabled(n >= c);
+    L_name->setEnabled(n >= c);
 }
 
 
--- a/QTfrontend/ui/page/pagescheme.h	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/ui/page/pagescheme.h	Sun Jun 10 19:12:26 2018 +0200
@@ -75,6 +75,7 @@
         ToggleButtonWidget * TBW_morewind;
         ToggleButtonWidget * TBW_tagteam;
         ToggleButtonWidget * TBW_bottomborder;
+        ToggleButtonWidget * TBW_switchhog;
 
         QSpinBox * SB_DamageModifier;
         QSpinBox * SB_TurnTime;
@@ -94,6 +95,7 @@
         QSpinBox * SB_GetAwayTime;
         QComboBox * CB_WorldEdge;
         QLineEdit * LE_name;
+        QLabel * L_name;
         QLineEdit * LE_ScriptParam;
 
         QGroupBox * gbGameModes;
--- a/QTfrontend/ui/page/pagetraining.cpp	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/ui/page/pagetraining.cpp	Sun Jun 10 19:12:26 2018 +0200
@@ -23,6 +23,7 @@
 #include <QListWidgetItem>
 #include <QPushButton>
 
+#include <QTextStream>
 #include <QFile>
 #include <QLocale>
 #include <QSettings>
@@ -156,9 +157,7 @@
     QSettings settings(dataMgr.settingsFileName(),
                        QSettings::IniFormat);
 
-    QString loc = settings.value("misc/locale", "").toString();
-    if (loc.isEmpty())
-        loc = QLocale::system().name();
+    QString loc = QLocale().name();
 
     QString infoFile = QString("physfs://Locale/missions_" + loc + ".txt");
 
@@ -194,14 +193,46 @@
                 m_widget = lstScenarios;
                 break;
         }
+        // scripts to load
+        // first, load scripts in order specified in order.cfg (if present)
+        QFile orderFile(QString("physfs://Missions/%1/order.cfg").arg(subFolder));
+        QStringList orderedMissions;
+        if (orderFile.open(QFile::ReadOnly))
+        {
+            QString m_id;
+            QTextStream input(&orderFile);
+            while(true)
+            {
+                m_id = input.readLine();
+                if(m_id.isNull() || m_id.isEmpty())
+                {
+                    break;
+                }
+                QListWidgetItem * item = new QListWidgetItem(m_id);
+                QString name = item->text().replace("_", " ");
+                name = m_info->value(m_id + ".name", name).toString();
+                item->setText(name);
+                item->setData(Qt::UserRole, m_id);
+                m_widget->addItem(item);
+
+                orderedMissions << m_id;
+            }
+        }
+
+        // then, just load anything else in no particular order
         m_list = dataMgr.entryList(
                     "Missions/" + subFolder,
                     QDir::Files, QStringList("*.lua")).
                replaceInStrings(QRegExp("\\.lua$"), "");
 
-        // scripts to load - TODO: model?
         foreach (const QString & m_id, m_list)
         {
+            // Disallow duplicates from order.cfg
+            if (orderedMissions.contains(m_id))
+            {
+                continue;
+            }
+
             QListWidgetItem * item = new QListWidgetItem(m_id);
 
             // fallback name: replace underscores in mission name with spaces
--- a/QTfrontend/ui/page/pagevideos.cpp	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/ui/page/pagevideos.cpp	Sun Jun 10 19:12:26 2018 +0200
@@ -39,9 +39,6 @@
 #include <QFileSystemWatcher>
 #include <QDateTime>
 #include <QRegExp>
-#include <QNetworkAccessManager>
-#include <QNetworkRequest>
-#include <QNetworkReply>
 #include <QXmlStreamReader>
 
 #include "hwconsts.h"
@@ -51,7 +48,6 @@
 #include "gameuiconfig.h"
 #include "recorder.h"
 #include "ask_quit.h"
-#include "upload_video.h"
 
 static const QSize ThumbnailSize(350, 350*3/5);
 
@@ -60,7 +56,7 @@
 {
     vcName,
     vcSize,
-    vcProgress, // either encoding or uploading
+    vcProgress, // for encoding
 
     vcNumColumns,
 };
@@ -77,9 +73,7 @@
         QString name;
         QString prefix; // original filename without extension
         QString desc;   // description (duration, resolution, etc...)
-        QString uploadUrl; // https://youtu.be/???????
         HWRecorder    * pRecorder; // non NULL if file is being encoded
-        QNetworkReply * pUploading; // non NULL if file is being uploaded
         bool seen; // used when updating directory
         float lastSizeUpdate;
         float progress;
@@ -96,7 +90,6 @@
 {
     this->name = name;
     pRecorder = NULL;
-    pUploading = NULL;
     lastSizeUpdate = 0;
     progress = 0;
     seen = false;
@@ -133,8 +126,8 @@
         filesTable->setMinimumWidth(400);
 
         QHeaderView * header = filesTable->horizontalHeader();
-        header->setResizeMode(vcName, QHeaderView::ResizeToContents);
-        header->setResizeMode(vcSize, QHeaderView::Fixed);
+        header->setSectionResizeMode(vcName, QHeaderView::ResizeToContents);
+        header->setSectionResizeMode(vcSize, QHeaderView::Fixed);
         header->resizeSection(vcSize, 100);
         header->setStretchLastSection(true);
 
@@ -180,8 +173,6 @@
         labelDesc->setTextFormat(Qt::RichText);
         labelDesc->setWordWrap(true);
         labelDesc->setOpenExternalLinks(true);
-        //labelDesc->setMinimumSize(ThumbnailSize);
-        //pTopDescLayout->addWidget(labelDesc, 1);
 
         // buttons: play and delete
         btnPlay = new QPushButton(QPushButton::tr("Play"), pDescGroup);
@@ -192,10 +183,6 @@
         btnDelete->setEnabled(false);
         btnDelete->setWhatsThis(QPushButton::tr("Delete this video"));
         pBottomDescLayout->addWidget(btnDelete);
-        btnToYouTube = new QPushButton(QPushButton::tr("Upload to YouTube"), pDescGroup);
-        btnToYouTube->setEnabled(false);
-        btnToYouTube->setWhatsThis(QPushButton::tr("Upload this video to your YouTube account"));
-        pBottomDescLayout->addWidget(btnToYouTube);
 
         pDescLayout->addWidget(labelThumbnail, 0);
         pDescLayout->addWidget(labelDesc, 0);
@@ -220,16 +207,14 @@
     connect(filesTable, SIGNAL(currentCellChanged(int,int,int,int)), this, SLOT(currentCellChanged()));
     connect(btnPlay,   SIGNAL(clicked()), this, SLOT(playSelectedFile()));
     connect(btnDelete, SIGNAL(clicked()), this, SLOT(deleteSelectedFiles()));
-    connect(btnToYouTube, SIGNAL(clicked()), this, SLOT(uploadToYouTube()));
     connect(btnOpenDir, SIGNAL(clicked()), this, SLOT(openVideosDirectory()));
 }
 
 PageVideos::PageVideos(QWidget* parent) : AbstractPage(parent),
-    config(0), netManager(0)
+    config(0)
 {
     nameChangedFromCode = false;
     numRecorders = 0;
-    numUploads = 0;
     initPage();
 }
 
@@ -249,19 +234,24 @@
 // get file size as string
 static QString FileSizeStr(const QString & path)
 {
-    quint64 size = QFileInfo(path).size();
+    qint64 size = QFileInfo(path).size();
 
-    quint64 KiB = 1024;
-    quint64 MiB = 1024*KiB;
-    quint64 GiB = 1024*MiB;
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
+    return QLocale().formattedDataSize(size);
+#else
+    qint64 KiB = 1024;
+    qint64 MiB = 1024*KiB;
+    qint64 GiB = 1024*MiB;
     QString sizeStr;
+    float fsize = (float) size;
     if (size >= GiB)
-        return QString("%1 GiB").arg(QString::number(float(size)/GiB, 'f', 2));
+        return QString("%1 GiB").arg(QLocale().toString(fsize/GiB, 'f', 2));
     if (size >= MiB)
-        return QString("%1 MiB").arg(QString::number(float(size)/MiB, 'f', 2));
-     if (size >= KiB)
-        return QString("%1 KiB").arg(QString::number(float(size)/KiB, 'f', 2));
+        return QString("%1 MiB").arg(QLocale().toString(fsize/MiB, 'f', 2));
+    if (size >= KiB)
+        return QString("%1 KiB").arg(QLocale().toString(fsize/KiB, 'f', 2));
     return PageVideos::tr("%1 bytes", "", size).arg(QString::number(size));
+#endif
 }
 
 // set file size in file list in specified row
@@ -334,7 +324,8 @@
 {
     QProgressBar * progressBar = (QProgressBar*)filesTable->cellWidget(row, vcProgress);
     progressBar->setValue(value*10000);
-    progressBar->setFormat(QString("%1%").arg(value*100, 0, 'f', 2));
+    //: Video encoding progress. %1 = number
+    progressBar->setFormat(QString(tr("%1%")).arg(QLocale().toString(value*100, 'f', 2)));
     item->progress = value;
 }
 
@@ -491,15 +482,12 @@
         clearThumbnail();
         btnPlay->setEnabled(false);
         btnDelete->setEnabled(false);
-        btnToYouTube->setEnabled(false);
         return;
     }
 
     btnPlay->setEnabled(item->ready());
-    btnToYouTube->setEnabled(item->ready());
     btnDelete->setEnabled(true);
     btnDelete->setText(item->ready()? QPushButton::tr("Delete") :  QPushButton::tr("Cancel"));
-    btnToYouTube->setText(item->pUploading? QPushButton::tr("Cancel uploading") :  QPushButton::tr("Upload to YouTube"));
 
     // construct string with desctiption of this file to display it
     QString desc = item->name + "\n\n";
@@ -543,21 +531,6 @@
             item->prefix.truncate(pt);
     }
 
-    if (item->ready() && item->uploadUrl.isEmpty())
-    {
-        // try to load url from file
-        QFile * file = new QFile(cfgdir->absoluteFilePath("VideoTemp/" + item->prefix + "-url.txt"), this);
-        if (!file->open(QIODevice::ReadOnly))
-            item->uploadUrl = "no";
-        else
-        {
-            QByteArray data = file->readAll();
-            file->close();
-            item->uploadUrl = QString::fromUtf8(data.data());
-        }
-    }
-    if (item->uploadUrl != "no")
-        desc += QString("<a href=\"%1\" style=\"color: white;\">%1</a>").arg(item->uploadUrl);
     desc.replace("\n", "<br/>");
 
     labelDesc->setText(desc);
@@ -688,14 +661,14 @@
     QDesktopServices::openUrl(QUrl("file:///" + path));
 }
 
-// clear VideoTemp directory (except for thumbnails and upload links)
+// clear VideoTemp directory (except for thumbnails)
 void PageVideos::clearTemp()
 {
     QDir temp(cfgdir->absolutePath() + "/VideoTemp");
     QStringList files = temp.entryList(QDir::Files);
     foreach (const QString& file, files)
     {
-        if (!file.endsWith(".bmp") && !file.endsWith(".png") && !file.endsWith("-url.txt"))
+        if (!file.endsWith(".bmp") && !file.endsWith(".png"))
             temp.remove(file);
     }
 }
@@ -703,7 +676,7 @@
 bool PageVideos::tryQuit(HWForm * form)
 {
     bool quit = true;
-    if (numRecorders != 0 || numUploads != 0)
+    if (numRecorders != 0)
     {
         // ask user what to do - abort or wait
         HWAskQuitDialog * askd = new HWAskQuitDialog(this, form);
@@ -718,7 +691,6 @@
 // returns multi-line string with list of videos in progress
 /* it will look like this:
 foo.avi (15.21% - encoding)
-bar.avi (18.21% - uploading)
 */
 QString PageVideos::getVideosInProgress()
 {
@@ -728,16 +700,17 @@
     {
         VideoItem * item = nameItem(i);
         QString process;
-        if (!item->ready())
-            process = tr("encoding");
-        else if (item->pUploading)
-            process = tr("uploading");
-        else
+        if (item->ready())
             continue;
         float progress = 100*item->progress;
         if (progress > 99.99)
             progress = 99.99; // displaying 100% may be confusing
-        list += item->name + " (" + QString::number(progress, 'f', 2) + "% - " + process + ")\n";
+        //: Video encoding list entry. %1 = file name, %2 = percent complete, %3 = video operation type (e.g. “encoding”)
+        list += QString(tr("%1 (%2%) - %3"))
+            .arg(item->name)
+            .arg(QLocale().toString(progress, 'f', 2))
+            .arg(tr("encoding"))
+            + "\n";
     }
     return list;
 }
@@ -771,168 +744,3 @@
     }
 }
 
-VideoItem * PageVideos::itemFromReply(QNetworkReply* reply, int & row)
-{
-    VideoItem * item = NULL;
-    int count = filesTable->rowCount();
-    // find corresponding item (maybe there is a better way to implement this?)
-    for (int i = 0; i < count; i++)
-    {
-        item = nameItem(i);
-        if (item->pUploading == reply)
-        {
-            row = i;
-            break;
-        }
-    }
-    return item;
-}
-
-void PageVideos::uploadProgress(qint64 bytesSent, qint64 bytesTotal)
-{
-    QNetworkReply* reply = (QNetworkReply*)sender();
-    int row;
-    VideoItem * item = itemFromReply(reply, row);
-    setProgress(row, item, bytesSent*1.0/bytesTotal);
-}
-
-void PageVideos::uploadFinished()
-{
-    QNetworkReply* reply = (QNetworkReply*)sender();
-    reply->deleteLater();
-
-    int row;
-    VideoItem * item = itemFromReply(reply, row);
-    if (!item)
-        return;
-
-    item->pUploading = NULL;
-
-    // extract video id from reply
-    QString videoid;
-    QXmlStreamReader xml(reply);
-    while (!xml.atEnd())
-    {
-        xml.readNext();
-        if (xml.qualifiedName() == "yt:videoid")
-        {
-            videoid = xml.readElementText();
-            break;
-        }
-    }
-
-    if (!videoid.isEmpty())
-    {
-        item->uploadUrl = "https://youtu.be/" + videoid;
-        updateDescription();
-
-        // save url in file
-        QFile * file = new QFile(cfgdir->absoluteFilePath("VideoTemp/" + item->prefix + "-url.txt"), this);
-        if (file->open(QIODevice::WriteOnly))
-        {
-            file->write(item->uploadUrl.toUtf8());
-            file->close();
-        }
-    }
-
-    filesTable->setCellWidget(row, vcProgress, NULL); // remove progress bar
-    numUploads--;
-}
-
-// this will protect saved youtube password from those who cannot read source code
-static QString protectPass(QString str)
-{
-    QByteArray array = str.toUtf8();
-    for (int i = 0; i < array.size(); i++)
-        array[i] = array[i] ^ 0xC4 ^ i;
-    array = array.toBase64();
-    return QString::fromLatin1(array.data());
-}
-
-static QString unprotectPass(QString str)
-{
-    QByteArray array = QByteArray::fromBase64(str.toAscii());
-    for (int i = 0; i < array.size(); i++)
-        array[i] = array[i] ^ 0xC4 ^ i;
-    return QString::fromUtf8(array);
-}
-
-void PageVideos::uploadToYouTube()
-{
-    int row = filesTable->currentRow();
-    VideoItem * item = nameItem(row);
-
-    if (item->pUploading) //Act as 'cancel uploading' button
-    {
-        // ask user if (s)he is serious
-        QMessageBox reallyStopMsg(this);
-        reallyStopMsg.setIcon(QMessageBox::Question);
-        reallyStopMsg.setWindowTitle(QMessageBox::tr("Videos - Are you sure?"));
-        reallyStopMsg.setText(QMessageBox::tr("Do you really want to cancel uploading %1?").arg(item->name));
-        reallyStopMsg.setWindowModality(Qt::WindowModal);
-        reallyStopMsg.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
-
-        if (reallyStopMsg.exec() != QMessageBox::Ok)
-            return;
-        item->pUploading->abort();
-        btnToYouTube->setText(QPushButton::tr("Upload to YouTube"));
-        filesTable->setCellWidget(row, vcProgress, NULL); // remove progress bar
-        //numUploads--;
-        return;
-    }
-
-    if (!netManager)
-        netManager = new QNetworkAccessManager(this);
-
-    HWUploadVideoDialog* dlg = new HWUploadVideoDialog(this, item->name, netManager);
-    dlg->deleteLater();
-    if (config->value("youtube/save").toBool())
-    {
-        dlg->cbSave->setChecked(true);
-        dlg->leAccount->setText(config->value("youtube/name").toString());
-        dlg->lePassword->setText(unprotectPass(config->value("youtube/pswd").toString()));
-    }
-
-    bool result = dlg->exec();
-
-    if (dlg->cbSave->isChecked())
-    {
-        config->setValue("youtube/save", true);
-        config->setValue("youtube/name", dlg->leAccount->text());
-        config->setValue("youtube/pswd", protectPass(dlg->lePassword->text()));
-    }
-    else
-    {
-        config->setValue("youtube/save", false);
-        config->setValue("youtube/name", "");
-        config->setValue("youtube/pswd", "");
-    }
-
-    if (!result)
-        return;
-
-    QNetworkRequest request(QUrl(dlg->location));
-    request.setRawHeader("Content-Type", "application/octet-stream");
-
-    QFile * file = new QFile(item->path(), this);
-    if (!file->open(QIODevice::ReadOnly))
-        return;
-
-    // add progress bar
-    QProgressBar * progressBar = new QProgressBar(filesTable);
-    progressBar->setMinimum(0);
-    progressBar->setMaximum(10000);
-    progressBar->setValue(0);
-    // make it different from progress-bar used during encoding (use blue color)
-    progressBar->setStyleSheet("* {color: #00ccff; selection-background-color: #00ccff;}" );
-    filesTable->setCellWidget(row, vcProgress, progressBar);
-
-    QNetworkReply* reply = netManager->put(request, file);
-    file->setParent(reply); // automatically close file when needed
-    item->pUploading = reply;
-    connect(reply, SIGNAL(uploadProgress(qint64, qint64)), this, SLOT(uploadProgress(qint64, qint64)));
-    connect(reply, SIGNAL(finished()), this, SLOT(uploadFinished()));
-    numUploads++;
-
-    updateDescription();
-}
--- a/QTfrontend/ui/page/pagevideos.h	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/ui/page/pagevideos.h	Sun Jun 10 19:12:26 2018 +0200
@@ -22,8 +22,6 @@
 
 #include "AbstractPage.h"
 
-class QNetworkAccessManager;
-class QNetworkReply;
 class GameUIConfig;
 class HWRecorder;
 class VideoItem;
@@ -60,17 +58,15 @@
         void clearTemp();
         void clearThumbnail();
         void setProgress(int row, VideoItem* item, float value);
-        VideoItem * itemFromReply(QNetworkReply* reply, int & row);
 
         GameUIConfig * config;
-        QNetworkAccessManager* netManager;
 
         // file list group
         QTableWidget *filesTable;
         QPushButton *btnOpenDir;
 
         // description group
-        QPushButton *btnPlay, *btnDelete, *btnToYouTube;
+        QPushButton *btnPlay, *btnDelete;
         QLabel *labelDesc;
         QLabel *labelThumbnail;
 
@@ -78,7 +74,7 @@
         // (in signal cellChanged)
         bool nameChangedFromCode;
 
-        int numRecorders, numUploads;
+        int numRecorders;
 
     private slots:
         void encodingFinished(bool success);
@@ -90,9 +86,6 @@
         void deleteSelectedFiles();
         void openVideosDirectory();
         void updateFileList(const QString & path);
-        void uploadToYouTube();
-        void uploadProgress(qint64 bytesSent, qint64 bytesTotal);
-        void uploadFinished();
 };
 
 #endif // PAGE_VIDEOS_H
--- a/QTfrontend/ui/widget/about.cpp	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/ui/widget/about.cpp	Sun Jun 10 19:12:26 2018 +0200
@@ -26,6 +26,7 @@
 #include <QMessageBox>
 #include <QNetworkReply>
 #include <QDebug>
+#include <QMimeData>
 #include "hwconsts.h"
 #include "SDLInteraction.h"
 #include "SDL.h"
--- a/QTfrontend/ui/widget/chatwidget.cpp	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/ui/widget/chatwidget.cpp	Sun Jun 10 19:12:26 2018 +0200
@@ -32,6 +32,7 @@
 #include <QSortFilterProxyModel>
 #include <QMenu>
 #include <QScrollBar>
+#include <QMimeData>
 
 #include "DataManager.h"
 #include "hwconsts.h"
@@ -312,7 +313,7 @@
     else if (link.scheme() == "hwnick")
     {
         // decode nick
-        QString nick = QString::fromUtf8(QByteArray::fromBase64(link.encodedQuery()));
+        QString nick = QString::fromUtf8(QByteArray::fromBase64(link.query(QUrl::FullyDecoded).toLatin1()));
         QModelIndexList mil = chatNicks->model()->match(chatNicks->model()->index(0, 0), Qt::DisplayRole, nick);
 
         bool isOffline = (mil.size() < 1);
@@ -374,10 +375,10 @@
 {
     if (nickname != m_userNick)
         return QString("<a href=\"hwnick://?%1\" class=\"nick\">%2</a>").arg(
-                   QString(nickname.toUtf8().toBase64())).arg(Qt::escape(nickname));
+                   QString(nickname.toUtf8().toBase64())).arg(nickname.toHtmlEscaped());
 
     // unlinked nick (if own one)
-    return QString("<span class=\"nick\">%1</span>").arg(Qt::escape(nickname));
+    return QString("<span class=\"nick\">%1</span>").arg(nickname.toHtmlEscaped());
 }
 
 const QRegExp HWChatWidget::URLREGEXP = QRegExp("(http(s)?://)?(www\\.)?((([^/:?&#]+\\.)?hedgewars\\.org|code\\.google\\.com|googlecode\\.com|hh\\.unit22\\.org)(/[^ ]*)?)");
@@ -399,7 +400,7 @@
 
 QString HWChatWidget::messageToHTML(const QString & message)
 {
-    QString formattedStr = Qt::escape(message);
+    QString formattedStr = message.toHtmlEscaped();
     // link some urls
     formattedStr = formattedStr.replace(URLREGEXP, "<a href=\"http\\2://\\4\">\\4</a>");
     return formattedStr;
@@ -606,9 +607,9 @@
 {
     addLine("msg_PlayerInfo", QString(" >>> %1 - <span class=\"ipaddress\">%2</span> <span class=\"version\">%3</span> <span class=\"location\">%4</span>")
         .arg(linkedNick(nick))
-        .arg(Qt::escape(ip == "[]"?"":ip))
-        .arg(Qt::escape(version))
-        .arg(Qt::escape(roomInfo))
+        .arg(QString(ip == "[]"?"":ip).toHtmlEscaped())
+        .arg(version.toHtmlEscaped())
+        .arg(roomInfo.toHtmlEscaped())
     );
 }
 
--- a/QTfrontend/ui/widget/colorwidget.cpp	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/ui/widget/colorwidget.cpp	Sun Jun 10 19:12:26 2018 +0200
@@ -30,7 +30,7 @@
 
     QStandardItem * item = m_colorsModel->item(m_color);
 
-    setStyleSheet(QString("border: 2px solid orange; border-radius: 8px; background: %1").arg(item->data().value<QColor>().name()));
+    setStyleSheet(QString("* { border: 2px solid #ffcc00; border-radius: 8px; background: %1 } :disabled { border-color: #a0a0a0; }").arg(item->data().value<QColor>().name()));
     /*
     QPalette p = palette();
     p.setColor(QPalette::Window, item->data().value<QColor>());
--- a/QTfrontend/ui/widget/drawmapwidget.cpp	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/ui/widget/drawmapwidget.cpp	Sun Jun 10 19:12:26 2018 +0200
@@ -56,12 +56,16 @@
 
     ui->graphicsView->setScene(scene);
     connect(scene, SIGNAL(pathChanged()), this, SLOT(pathChanged()));
+    connect(scene, SIGNAL(brushSizeChanged(int)), this, SLOT(brushSizeChanged_slot(int)));
 }
 
 void DrawMapWidget::resizeEvent(QResizeEvent * event)
 {
     Q_UNUSED(event);
 
+	if(!m_scene)
+		return;
+		
     int height = this->height();
     int width = this->width();
 
@@ -133,6 +137,11 @@
     if(m_scene) m_scene->setPathType(pathType);
 }
 
+void DrawMapWidget::setBrushSize(int brushSize)
+{
+    if(m_scene) m_scene->setBrushSize(brushSize);
+}
+
 void DrawMapWidget::save(const QString & fileName)
 {
     if(m_scene)
@@ -179,7 +188,10 @@
     ui->lblPoints->setNum(m_scene->pointsCount());
 }
 
-
+void DrawMapWidget::brushSizeChanged_slot(int brushSize)
+{
+    emit brushSizeChanged(brushSize);
+}
 
 DrawMapView::DrawMapView(QWidget *parent) :
     QGraphicsView(parent)
--- a/QTfrontend/ui/widget/drawmapwidget.h	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/ui/widget/drawmapwidget.h	Sun Jun 10 19:12:26 2018 +0200
@@ -24,6 +24,7 @@
 #include <QPushButton>
 #include <QGraphicsView>
 #include <QLabel>
+#include <QSizePolicy>
 
 #include "drawmapscene.h"
 
@@ -59,14 +60,19 @@
             {
                 QVBoxLayout * vbox = new QVBoxLayout(drawMapWidget);
                 vbox->setMargin(0);
-                lblPoints = new QLabel("0", drawMapWidget);
                 QLayout * arLayout = new QVBoxLayout();
                 arLayout->setAlignment(Qt::AlignCenter);
                 vbox->addLayout(arLayout);
 
+                lblPoints = new QLabel("0", drawMapWidget);
+                lblPoints->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
+                arLayout->addWidget(lblPoints);
+
                 graphicsView = new DrawMapView(drawMapWidget);
                 graphicsView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
                 graphicsView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+                graphicsView->setRenderHint(QPainter::Antialiasing, true);
+                graphicsView->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);
                 arLayout->addWidget(graphicsView);
 
                 retranslateUi(drawMapWidget);
@@ -102,6 +108,10 @@
         void save(const QString & fileName);
         void load(const QString & fileName);
         void setPathType(DrawMapScene::PathType pathType);
+        void setBrushSize(int brushSize);
+
+    signals:
+        void brushSizeChanged(int brushSize);
 
     protected:
         void changeEvent(QEvent *e);
@@ -115,6 +125,7 @@
 
     private slots:
         void pathChanged();
+        void brushSizeChanged_slot(int brushSize);
 };
 
 #endif // DRAWMAPWIDGET_H
--- a/QTfrontend/ui/widget/feedbackdialog.cpp	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/ui/widget/feedbackdialog.cpp	Sun Jun 10 19:12:26 2018 +0200
@@ -20,7 +20,7 @@
 #include <QLineEdit>
 #include <QTextBrowser>
 #include <QLabel>
-#include <QHttp>
+#include <QNetworkAccessManager>
 #include <QSysInfo>
 #include <QDebug>
 #include <QBuffer>
@@ -109,7 +109,8 @@
 
     CheckSendSpecs = new QCheckBox();
     CheckSendSpecs->setText(QLabel::tr("Send system information"));
-    CheckSendSpecs->setChecked(true);
+    CheckSendSpecs->setChecked(false);
+    CheckSendSpecs->setToolTip(tr("This is optional, but this information might help us to resolve bugs and other technical problems."));
     BtnViewInfo = new QPushButton(tr("View"));
     BtnViewInfo->setFixedHeight(40);
     feedbackLayout->addWidget(CheckSendSpecs, 0, 2, 2, 1);
@@ -136,7 +137,7 @@
 
     label_captcha = new QLabel();
     label_captcha->setStyleSheet("border: 3px solid #ffcc00; border-radius: 4px");
-    label_captcha->setText("loading<br>captcha");
+    label_captcha->setText(QLabel::tr("Loading<br>CAPTCHA ..."));
     label_captcha->setFixedSize(200, 50);
     captchaLayout->addWidget(label_captcha);
 
@@ -474,7 +475,7 @@
             this, SLOT(finishedSlot(QNetworkReply*)));
 
     QNetworkRequest header(QUrl("https://hedgewars.org/feedback/?submit"));
-    header.setRawHeader("Content-Length", QString::number(body.size()).toAscii());
+    header.setRawHeader("Content-Length", QString::number(body.size()).toLatin1());
     header.setRawHeader("Content-Type", "application/x-www-form-urlencoded");
 
     nam->post(header, body);
--- a/QTfrontend/ui/widget/frameTeam.cpp	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/ui/widget/frameTeam.cpp	Sun Jun 10 19:12:26 2018 +0200
@@ -28,7 +28,8 @@
 #include "DataManager.h"
 
 FrameTeams::FrameTeams(QWidget* parent) :
-    QFrame(parent), mainLayout(this), nonInteractive(false)
+    QFrame(parent), mainLayout(this), nonInteractive(false),
+    hasDecoFrame(false)
 {
     QPalette newPalette = palette();
     newPalette.setColor(QPalette::Window, QColor(0x00, 0x00, 0x00));
@@ -72,6 +73,7 @@
     mainLayout.addWidget(pTeamShowWidget);
     teamToWidget.insert(team, pTeamShowWidget);
     QResizeEvent* pevent=new QResizeEvent(parentWidget()->size(), parentWidget()->size());
+    updateDecoFrame();
     QCoreApplication::postEvent(parentWidget(), pevent);
 }
 
@@ -83,6 +85,7 @@
     it.value()->deleteLater();
     teamToWidget.erase(it);
     QResizeEvent* pevent=new QResizeEvent(parentWidget()->size(), parentWidget()->size());
+    updateDecoFrame();
     QCoreApplication::postEvent(parentWidget(), pevent);
 }
 
@@ -95,6 +98,7 @@
         teamToWidget.erase(it++);
     }
     QResizeEvent* pevent=new QResizeEvent(parentWidget()->size(), parentWidget()->size());
+    updateDecoFrame();
     QCoreApplication::postEvent(parentWidget(), pevent);
 }
 
@@ -134,3 +138,28 @@
 {
     return QSize(-1, teamToWidget.size() * 39 + 9);
 }
+
+void FrameTeams::setDecoFrameEnabled(bool enabled)
+{
+    hasDecoFrame = enabled;
+    updateDecoFrame();
+}
+
+void FrameTeams::updateDecoFrame()
+{
+    if (hasDecoFrame && teamToWidget.size() >= 1)
+    {
+        setStyleSheet(
+            "FrameTeams{"
+            "border: solid;"
+            "border-width: 1px;"
+            "border-radius: 16px;"
+            "border-color: #ffcc00;"
+            "}"
+        );
+    }
+    else
+    {
+        setStyleSheet("FrameTeams{ border: transparent }");
+    }
+}
--- a/QTfrontend/ui/widget/frameTeam.h	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/ui/widget/frameTeam.h	Sun Jun 10 19:12:26 2018 +0200
@@ -44,6 +44,7 @@
         void setInteractivity(bool interactive);
         int getNextColor();
         QSize sizeHint() const;
+        void setDecoFrameEnabled(bool enabled);
 
     signals:
         void teamColorChanged(const HWTeam&);
@@ -61,6 +62,9 @@
         typedef QMap<HWTeam, QWidget*> tmapTeamToWidget;
         tmapTeamToWidget teamToWidget;
         bool nonInteractive;
+
+        bool hasDecoFrame;
+        void updateDecoFrame();
 };
 
 #endif // _FRAME_TAM_INCLUDED
--- a/QTfrontend/ui/widget/gamecfgwidget.cpp	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/ui/widget/gamecfgwidget.cpp	Sun Jun 10 19:12:26 2018 +0200
@@ -34,7 +34,7 @@
 #include "igbox.h"
 #include "DataManager.h"
 #include "hwconsts.h"
-#include "ammoSchemeModel.h"
+#include "gameSchemeModel.h"
 #include "proto.h"
 #include "GameStyleModel.h"
 #include "themeprompt.h"
@@ -106,14 +106,20 @@
     OptionsInnerContainer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
     GBoxOptionsLayout = new QGridLayout(OptionsInnerContainer);
 
-    GBoxOptionsLayout->addWidget(new QLabel(QLabel::tr("Style"), this), 1, 0);
+    lblScript = new QLabel(QLabel::tr("Style"), this);
+    GBoxOptionsLayout->addWidget(lblScript, 1, 0);
 
     Scripts = new QComboBox(this);
     Scripts->setMaxVisibleItems(30);
     GBoxOptionsLayout->addWidget(Scripts, 1, 1);
-
     Scripts->setModel(DataManager::instance().gameStyleModel());
     m_curScript = Scripts->currentText();
+
+    ScriptsLabel = new QLabel(this);
+    ScriptsLabel->setHidden(true);
+    ScriptsLabel->setTextFormat(Qt::PlainText);
+    GBoxOptionsLayout->addWidget(ScriptsLabel, 1, 1);
+
     connect(Scripts, SIGNAL(currentIndexChanged(int)), this, SLOT(scriptChanged(int)));
 
     QWidget *SchemeWidget = new QWidget(this);
@@ -125,29 +131,43 @@
     GameSchemes = new QComboBox(SchemeWidget);
     GameSchemes->setMaxVisibleItems(30);
     SchemeWidgetLayout->addWidget(GameSchemes, 0, 2);
+
+    GameSchemesLabel = new QLabel(SchemeWidget);
+    GameSchemesLabel->setHidden(true);
+    GameSchemesLabel->setTextFormat(Qt::PlainText);
+    SchemeWidgetLayout->addWidget(GameSchemesLabel, 0, 2);
+
     connect(GameSchemes, SIGNAL(currentIndexChanged(int)), this, SLOT(schemeChanged(int)));
 
-    SchemeWidgetLayout->addWidget(new QLabel(QLabel::tr("Scheme"), SchemeWidget), 0, 0);
+    lblScheme = new QLabel(QLabel::tr("Scheme"), SchemeWidget);
+    SchemeWidgetLayout->addWidget(lblScheme, 0, 0);
 
     QPixmap pmEdit(":/res/edit.png");
+    QIcon iconEdit = QIcon(pmEdit);
 
-    QPushButton * goToSchemePage = new QPushButton(SchemeWidget);
+    goToSchemePage = new QPushButton(SchemeWidget);
     goToSchemePage->setWhatsThis(tr("Edit schemes"));
     goToSchemePage->setIconSize(pmEdit.size());
-    goToSchemePage->setIcon(pmEdit);
+    goToSchemePage->setIcon(iconEdit);
     goToSchemePage->setMaximumWidth(pmEdit.width() + 6);
     SchemeWidgetLayout->addWidget(goToSchemePage, 0, 3);
     connect(goToSchemePage, SIGNAL(clicked()), this, SLOT(jumpToSchemes()));
 
-    SchemeWidgetLayout->addWidget(new QLabel(QLabel::tr("Weapons"), SchemeWidget), 1, 0);
+    lblWeapons = new QLabel(QLabel::tr("Weapons"), SchemeWidget);
+    SchemeWidgetLayout->addWidget(lblWeapons, 1, 0);
 
     WeaponsName = new QComboBox(SchemeWidget);
     WeaponsName->setMaxVisibleItems(30);
     SchemeWidgetLayout->addWidget(WeaponsName, 1, 2);
 
+    WeaponsNameLabel = new QLabel(SchemeWidget);
+    WeaponsNameLabel->setHidden(true);
+    WeaponsNameLabel->setTextFormat(Qt::PlainText);
+    SchemeWidgetLayout->addWidget(WeaponsNameLabel, 1, 2);
+
     connect(WeaponsName, SIGNAL(currentIndexChanged(int)), this, SLOT(ammoChanged(int)));
 
-    QPushButton * goToWeaponPage = new QPushButton(SchemeWidget);
+    goToWeaponPage = new QPushButton(SchemeWidget);
     goToWeaponPage->setWhatsThis(tr("Edit weapons"));
     goToWeaponPage->setIconSize(pmEdit.size());
     goToWeaponPage->setIcon(pmEdit);
@@ -159,8 +179,10 @@
     bindEntries->setWhatsThis(tr("Game scheme will auto-select a weapon"));
     bindEntries->setChecked(true);
     bindEntries->setMaximumWidth(42);
-    bindEntries->setStyleSheet( "QCheckBox::indicator:checked   { image: url(\":/res/lock.png\"); }"
-                                "QCheckBox::indicator:unchecked { image: url(\":/res/unlock.png\");   }" );
+    bindEntries->setStyleSheet( "QCheckBox::indicator:checked:enabled    { image: url(\":/res/lock.png\"); }"
+                                "QCheckBox::indicator:checked:disabled   { image: url(\":/res/lock_disabled.png\"); }"
+                                "QCheckBox::indicator:unchecked:enabled  { image: url(\":/res/unlock.png\");   }"
+                                "QCheckBox::indicator:unchecked:disabled { image: url(\":/res/unlock_disabled.png\");   }" );
     SchemeWidgetLayout->addWidget(bindEntries, 0, 1, 0, 1, Qt::AlignVCenter);
 
     connect(pMapContainer, SIGNAL(seedChanged(const QString &)), this, SLOT(seedChanged(const QString &)));
@@ -377,7 +399,7 @@
         QMessageBox illegalMsg(parentWidget());
         illegalMsg.setIcon(QMessageBox::Warning);
         illegalMsg.setWindowTitle(QMessageBox::tr("Error"));
-        illegalMsg.setText(QMessageBox::tr("Cannot use the ammo '%1'!").arg(name));
+        illegalMsg.setText(QMessageBox::tr("Cannot use the weapon scheme '%1'!").arg(name));
         illegalMsg.setWindowModality(Qt::WindowModal);
         illegalMsg.exec();
     }
@@ -464,7 +486,9 @@
         }
         if (param == "SCRIPT")
         {
-            Scripts->setCurrentIndex(Scripts->findText(value));
+            int in = Scripts->findText(value);
+            Scripts->setCurrentIndex(in);
+            ScriptsLabel->setText(value);
             pMapContainer->setScript(Scripts->itemData(Scripts->currentIndex(), GameStyleModel::ScriptRole).toString().toUtf8(), schemeData(43).toString());
             return;
         }
@@ -509,11 +533,16 @@
 {
     if (index >= 0)
     {
+        WeaponsNameLabel->setText(WeaponsName->currentText());
         emit paramChanged(
             "AMMO",
             QStringList() << WeaponsName->itemText(index) << WeaponsName->itemData(index).toString()
         );
     }
+    else
+    {
+        WeaponsNameLabel->setText("");
+    }
 }
 
 void GameCFGWidget::mapChanged(const QString & value)
@@ -521,16 +550,21 @@
     if(isEnabled() && pMapContainer->getCurrentIsMission())
     {
         Scripts->setEnabled(false);
+        lblScript->setEnabled(false);
         Scripts->setCurrentIndex(0);
 
         if (pMapContainer->getCurrentScheme() == "locked")
         {
             GameSchemes->setEnabled(false);
+            goToSchemePage->setEnabled(false);
+            lblScheme->setEnabled(false);
             GameSchemes->setCurrentIndex(GameSchemes->findText("Default"));
         }
         else
         {
             GameSchemes->setEnabled(true);
+            goToSchemePage->setEnabled(true);
+            lblScheme->setEnabled(true);
             int num = GameSchemes->findText(pMapContainer->getCurrentScheme());
             if (num != -1)
                 GameSchemes->setCurrentIndex(num);
@@ -541,11 +575,15 @@
         if (pMapContainer->getCurrentWeapons() == "locked")
         {
             WeaponsName->setEnabled(false);
+            goToWeaponPage->setEnabled(false);
+            lblWeapons->setEnabled(false);
             WeaponsName->setCurrentIndex(WeaponsName->findText("Default"));
         }
         else
         {
             WeaponsName->setEnabled(true);
+            goToWeaponPage->setEnabled(true);
+            lblWeapons->setEnabled(true);
             int num = WeaponsName->findText(pMapContainer->getCurrentWeapons());
             if (num != -1)
                 WeaponsName->setCurrentIndex(num);
@@ -561,8 +599,13 @@
     else
     {
         Scripts->setEnabled(true);
+        lblScript->setEnabled(true);
         GameSchemes->setEnabled(true);
+        goToSchemePage->setEnabled(true);
+        lblScheme->setEnabled(true);
         WeaponsName->setEnabled(true);
+        goToWeaponPage->setEnabled(true);
+        lblWeapons->setEnabled(true);
         bindEntries->setEnabled(true);
     }
     emit paramChanged("MAP", QStringList(value));
@@ -612,6 +655,12 @@
             }
         }
     }
+
+    if(index == -1)
+        GameSchemesLabel->setText("");
+    else
+        GameSchemesLabel->setText(GameSchemes->currentText());
+
     pMapContainer->setScript(Scripts->itemData(Scripts->currentIndex(), GameStyleModel::ScriptRole).toString().toUtf8(), schemeData(43).toString());
 }
 
@@ -628,11 +677,15 @@
         if (scheme == "locked")
         {
             GameSchemes->setEnabled(false);
+            goToSchemePage->setEnabled(false);
+            lblScheme->setEnabled(false);
             GameSchemes->setCurrentIndex(GameSchemes->findText("Default"));
         }
         else if (m_master)
         {
             GameSchemes->setEnabled(true);
+            goToSchemePage->setEnabled(true);
+            lblScheme->setEnabled(true);
             int num = GameSchemes->findText(scheme);
             if (num != -1)
                 GameSchemes->setCurrentIndex(num);
@@ -643,11 +696,15 @@
         if (weapons == "locked")
         {
             WeaponsName->setEnabled(false);
+            goToWeaponPage->setEnabled(false);
+            lblWeapons->setEnabled(false);
             WeaponsName->setCurrentIndex(WeaponsName->findText("Default"));
         }
         else if (m_master)
         {
             WeaponsName->setEnabled(true);
+            goToWeaponPage->setEnabled(true);
+            lblWeapons->setEnabled(true);
             int num = WeaponsName->findText(weapons);
             if (num != -1)
                 WeaponsName->setCurrentIndex(num);
@@ -663,17 +720,25 @@
     else
     {
         GameSchemes->setEnabled(true);
+        goToSchemePage->setEnabled(true);
+        lblScheme->setEnabled(true);
         WeaponsName->setEnabled(true);
+        goToWeaponPage->setEnabled(true);
+        lblWeapons->setEnabled(true);
         bindEntries->setEnabled(true);
     }
-    if (!index)
+    if (index == -1)
     {
         pMapContainer->setScript(QString(""), QString(""));
+        ScriptsLabel->setStyleSheet("color: #b50000;");
     }
     else
     {
         pMapContainer->setScript(Scripts->itemData(index, GameStyleModel::ScriptRole).toString().toUtf8(), schemeData(43).toString());
+        ScriptsLabel->setText(Scripts->currentText());
+        ScriptsLabel->setStyleSheet("");
     }
+
     emit paramChanged("SCRIPT", QStringList(name));
 }
 
@@ -731,8 +796,28 @@
     if (master == m_master) return;
     m_master = master;
 
+    if (master)
+    {
+        // Reset script if not found
+        if (Scripts->currentIndex() == -1)
+        {
+            Scripts->setCurrentIndex(Scripts->findText("Normal"));
+        }
+    }
+
     pMapContainer->setMaster(master);
 
+    GameSchemes->setHidden(!master);
+    WeaponsName->setHidden(!master);
+    Scripts->setHidden(!master);
+    goToSchemePage->setHidden(!master);
+    goToWeaponPage->setHidden(!master);
+    bindEntries->setHidden(!master);
+
+    GameSchemesLabel->setHidden(master);
+    WeaponsNameLabel->setHidden(master);
+    ScriptsLabel->setHidden(master);
+
     foreach (QWidget *widget, m_childWidgets)
         widget->setEnabled(master);
 }
--- a/QTfrontend/ui/widget/gamecfgwidget.h	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/ui/widget/gamecfgwidget.h	Sun Jun 10 19:12:26 2018 +0200
@@ -47,6 +47,11 @@
         QComboBox * Scripts;
         QComboBox * GameSchemes;
         QComboBox * WeaponsName;
+        QPushButton * goToSchemePage;
+        QPushButton * goToWeaponPage;
+        QLabel * ScriptsLabel;
+        QLabel * GameSchemesLabel;
+        QLabel * WeaponsNameLabel;
         HWMapContainer* pMapContainer;
         QVariant schemeData(int column) const;
         bool isMaster();
@@ -93,6 +98,9 @@
         QGridLayout * GBoxOptionsLayout;
         QWidget * OptionsInnerContainer;
         QWidget * StackContainer;
+        QLabel * lblScript;
+        QLabel * lblScheme;
+        QLabel * lblWeapons;
 
         QWidget * mapContainerFree;
         QWidget * mapContainerTabbed;
--- a/QTfrontend/ui/widget/hedgehogerWidget.cpp	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/ui/widget/hedgehogerWidget.cpp	Sun Jun 10 19:12:26 2018 +0200
@@ -63,6 +63,7 @@
 void CHedgehogerWidget::setNonInteractive()
 {
     nonInteractive=true;
+    repaint();
 }
 
 void CHedgehogerWidget::setHHNum(unsigned int num)
@@ -105,6 +106,8 @@
     }
 
     QPainter painter(this);
+    if(nonInteractive)
+        painter.setPen(QPen(QColor("#FFA0A0A0")));
     painter.setFont(QFont("MS Shell Dlg", 10, QFont::Bold));
     painter.drawText(this->width() - 12, 23, QString::number(numItems));
 
--- a/QTfrontend/ui/widget/keybinder.cpp	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/ui/widget/keybinder.cpp	Sun Jun 10 19:12:26 2018 +0200
@@ -154,7 +154,7 @@
             curTable = new QTableWidget(0, 2);
             curTable->verticalHeader()->setVisible(false);
             curTable->horizontalHeader()->setVisible(false);
-            curTable->horizontalHeader()->setResizeMode(QHeaderView::Stretch);
+            curTable->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
             curTable->verticalHeader()->setDefaultSectionSize(rowHeight);
             curTable->setShowGrid(false);
             curTable->setStyleSheet("QTableWidget { border: none; } ");
@@ -180,6 +180,12 @@
         curTable->insertRow(row);
         curTable->setItem(row, 0, nameCell);
         QTableWidgetItem * bindCell = new QTableWidgetItem(comboBox->currentText());
+        QIcon dropDownIcon = QIcon();
+        QPixmap dd1 = QPixmap(":/res/dropdown.png");
+        QPixmap dd2 = QPixmap(":/res/dropdown_selected.png");
+        dropDownIcon.addPixmap(dd1, QIcon::Normal);
+        dropDownIcon.addPixmap(dd2, QIcon::Selected);
+        bindCell->setIcon(dropDownIcon);
         bindCell->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
         curTable->setItem(row, 1, bindCell);
         curTable->resizeColumnsToContents();
--- a/QTfrontend/ui/widget/mapContainer.cpp	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/ui/widget/mapContainer.cpp	Sun Jun 10 19:12:26 2018 +0200
@@ -64,15 +64,28 @@
     m_prevMapFeatureSize = 12;
     m_mapFeatureSize = 12;
     m_withoutDLC = false;
+    m_missingMap = false;
 
     hhSmall.load(":/res/hh_small.png");
     hhLimit = 18;
     templateFilter = 0;
     m_master = true;
 
-    linearGrad = QLinearGradient(QPoint(128, 0), QPoint(128, 128));
-    linearGrad.setColorAt(1, QColor(0, 0, 192));
-    linearGrad.setColorAt(0, QColor(66, 115, 225));
+    linearGradNormal = QLinearGradient(QPoint(128, 0), QPoint(128, 128));
+    linearGradNormal.setColorAt(1, QColor(0, 0, 192));
+    linearGradNormal.setColorAt(0, QColor(66, 115, 225));
+
+    linearGradLoading = QLinearGradient(QPoint(128, 0), QPoint(128, 128));
+    linearGradLoading.setColorAt(1, QColor(58, 58, 137));
+    linearGradLoading.setColorAt(0, QColor(90, 109, 153));
+
+    linearGradMapError = QLinearGradient(QPoint(128, 0), QPoint(128, 128));
+    linearGradMapError.setColorAt(1, QColor(255, 1, 0));
+    linearGradMapError.setColorAt(0, QColor(255, 119, 0));
+
+    linearGradNoPreview = QLinearGradient(QPoint(128, 0), QPoint(128, 128));
+    linearGradNoPreview.setColorAt(1, QColor(15, 9, 72));
+    linearGradNoPreview.setColorAt(0, QColor(15, 9, 72));
 
     mainLayout.setContentsMargins(HWApplication::style()->pixelMetric(QStyle::PM_LayoutLeftMargin),
                                   10,
@@ -90,18 +103,23 @@
     topWidget->setContentsMargins(0, 0, 0, 0);
     topLayout->setContentsMargins(0, 0, 0, 0);
 
-    QHBoxLayout * twoColumnLayout = new QHBoxLayout();
+    twoColumnLayout = new QHBoxLayout();
     QVBoxLayout * leftLayout = new QVBoxLayout();
+    leftLayout->setAlignment(Qt::AlignLeft);
     QVBoxLayout * rightLayout = new QVBoxLayout();
     twoColumnLayout->addLayout(leftLayout, 0);
-    twoColumnLayout->addStretch(1);
     twoColumnLayout->addLayout(rightLayout, 0);
     QVBoxLayout * drawnControls = new QVBoxLayout();
 
+    /* Map type label */
+
+    QLabel* lblMapType = new QLabel(tr("Map type:"));
+    topLayout->setSpacing(10);
+    topLayout->addWidget(lblMapType, 0);
+    m_childWidgets << lblMapType;
+
     /* Map type combobox */
 
-    topLayout->setSpacing(10);
-    topLayout->addWidget(new QLabel(tr("Map type:")), 0);
     cType = new QComboBox(this);
     topLayout->addWidget(cType, 1);
     cType->insertItem(0, tr("Image map"), MapModel::StaticMap);
@@ -148,6 +166,7 @@
     QLabel * lblMapPreviewText = new QLabel(this);
     lblMapPreviewText->setText(tr("Map preview:"));
     leftLayout->addWidget(lblMapPreviewText, 0);
+    m_childWidgets << lblMapPreviewText;
 
     /* Map Preview */
 
@@ -158,6 +177,7 @@
     mapPreview->setContentsMargins(0, 0, 0, 0);
     leftLayout->addWidget(mapPreview, 0);
     connect(mapPreview, SIGNAL(clicked()), this, SLOT(previewClicked()));
+    m_childWidgets << mapPreview;
 
     /* Bottom-Left layout */
 
@@ -167,7 +187,10 @@
     /* Map list label */
 
     lblMapList = new QLabel(this);
+    lblMapList->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
+    lblMapList->setAlignment(Qt::AlignTop | Qt::AlignLeft);
     rightLayout->addWidget(lblMapList, 0);
+    m_childWidgets << lblMapList;
 
     /* Static maps list */
 
@@ -181,6 +204,26 @@
     rightLayout->addWidget(missionMapList, 1);
     m_childWidgets << missionMapList;
 
+    /* Map name (when not room master) */
+    /* We use a QTextEdit instead of QLabel because it is able
+       to wrap at any character. */
+    teMapName = new QTextEdit(this);
+    teMapName->setObjectName("mapName");
+    teMapName->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum);
+    teMapName->setAlignment(Qt::AlignTop | Qt::AlignLeft);
+
+    /* Boilerplate to emulate a QLabel */
+    teMapName->setReadOnly(true);
+    teMapName->setAcceptRichText(false);
+    teMapName->setFrameStyle(QFrame::NoFrame);
+    teMapName->setStyleSheet("background-color: transparent");
+
+    teMapName->setLineWrapMode(QTextEdit::WidgetWidth);
+    teMapName->setWordWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
+
+    rightLayout->addWidget(teMapName, 1);
+    m_childWidgets << teMapName;
+
     /* Map load and edit buttons */
 
     drawnControls->addStretch(1);
@@ -234,7 +277,7 @@
     mapFeatureSize->setMinimum(1);
     //mapFeatureSize->setFixedWidth(259);
     mapFeatureSize->setValue(m_mapFeatureSize);
-    mapFeatureSize->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+    mapFeatureSize->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
     bottomLeftLayout->addWidget(mapFeatureSize, 0);
     connect(mapFeatureSize, SIGNAL(valueChanged(int)), this, SLOT(setFeatureSize(int)));
     m_childWidgets << mapFeatureSize;
@@ -244,7 +287,7 @@
     lblDesc = new QLabel();
     lblDesc->setWordWrap(true);
     lblDesc->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
-    lblDesc->setAlignment(Qt::AlignTop | Qt::AlignLeft);
+    lblDesc->setAlignment(Qt::AlignBottom | Qt::AlignLeft);
     lblDesc->setStyleSheet("font: 10px;");
     bottomLeftLayout->addWidget(lblDesc, 100);
 
@@ -292,6 +335,24 @@
     changeMapType(MapModel::GeneratedMap);
 }
 
+void HWMapContainer::onImageReceived(const QPixmap &newImage)
+{
+    // When image received from the engine.
+    switch (m_mapInfo.type)
+    {
+        case MapModel::GeneratedMap:
+        case MapModel::GeneratedMaze:
+        case MapModel::GeneratedPerlin:
+        case MapModel::HandDrawnMap:
+        case MapModel::FortsMap:
+            setImage(newImage);
+            break;
+        // Throw away image if we have switched the map mode in the meantime
+        default:
+            return;
+    }
+}
+
 void HWMapContainer::setImage(const QPixmap &newImage)
 {
     addInfoToPreview(newImage);
@@ -300,43 +361,61 @@
     cType->setEnabled(isMaster());
 }
 
+void HWMapContainer::setImage(const QPixmap &newImage, const QLinearGradient &linearGrad, bool showHHLimit)
+{
+    addInfoToPreview(newImage, linearGrad, showHHLimit);
+
+    pMap = 0;
+
+    cType->setEnabled(isMaster());
+}
+
+
 void HWMapContainer::setHHLimit(int newHHLimit)
 {
     hhLimit = newHHLimit;
 }
 
+void HWMapContainer::addInfoToPreview(const QPixmap &image)
+{
+    addInfoToPreview(image, linearGradNormal, true);
+}
+
 // Should this add text to identify map size?
-void HWMapContainer::addInfoToPreview(const QPixmap &image)
+void HWMapContainer::addInfoToPreview(const QPixmap &image, const QLinearGradient &linearGrad, bool drawHHLimit)
 {
     QPixmap finalImage = QPixmap(image.size());
-//finalImage.fill(QColor(0, 0, 0, 0));
+    QPainter p(&finalImage);
 
-    QPainter p(&finalImage);
     p.fillRect(finalImage.rect(), linearGrad);
     p.drawPixmap(finalImage.rect(), image);
-    //p.setPen(QColor(0xf4,0x9e,0xe9));
-    p.setPen(QColor(0xff,0xcc,0x00));
-    p.setBrush(QColor(0, 0, 0));
-    p.drawRect(finalImage.rect().width() - hhSmall.rect().width() - 28, 3, 40, 20);
-    p.setFont(QFont("MS Shell Dlg", 10));
-    QString text = (hhLimit > 0) ? QString::number(hhLimit) : "?";
-    p.drawText(finalImage.rect().width() - hhSmall.rect().width() - 14 - (hhLimit > 9 ? 10 : 0), 18, text);
-    p.drawPixmap(finalImage.rect().width() - hhSmall.rect().width() - 5, 5, hhSmall.rect().width(), hhSmall.rect().height(), hhSmall);
+
+    if (drawHHLimit)
+    {
+        p.setPen(QColor(0xff,0xcc,0x00));
+        p.setBrush(QColor(0, 0, 0));
+        p.setFont(QFont("MS Shell Dlg", 10));
+
+        p.drawRect(finalImage.rect().width() - hhSmall.rect().width() - 28, 3, 40, 20);
 
-    // 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);*/
+        QString text = (hhLimit > 0) ? QString::number(hhLimit) : "?";
+        p.drawText(finalImage.rect().width() - hhSmall.rect().width() - 14 - (hhLimit > 9 ? 10 : 0), 18, text);
+        p.drawPixmap(finalImage.rect().width() - hhSmall.rect().width() - 5, 5, hhSmall.rect().width(), hhSmall.rect().height(), hhSmall);
+    }
 
-    mapPreview->setIcon(QIcon(finalImage));
+    // Set the map preview image. Make sure it is always colored the same,
+    // no matter if disabled or not.
+    QIcon mapPreviewIcon = QIcon();
+    mapPreviewIcon.addPixmap(finalImage, QIcon::Normal);
+    mapPreviewIcon.addPixmap(finalImage, QIcon::Disabled);
+    mapPreview->setIcon(mapPreviewIcon);
     mapPreview->setIconSize(finalImage.size());
 }
 
 void HWMapContainer::askForGeneratedPreview()
 {
     pMap = new HWMap(this);
-    connect(pMap, SIGNAL(ImageReceived(QPixmap)), this, SLOT(setImage(QPixmap)));
+    connect(pMap, SIGNAL(ImageReceived(QPixmap)), this, SLOT(onImageReceived(const QPixmap)));
     connect(pMap, SIGNAL(HHLimitReceived(int)), this, SLOT(setHHLimit(int)));
     connect(pMap, SIGNAL(destroyed(QObject *)), this, SLOT(onPreviewMapDestroyed(QObject *)));
     pMap->getImage(m_seed,
@@ -351,17 +430,16 @@
 
     setHHLimit(0);
 
-    const QPixmap waitIcon(":/res/iconTime.png");
+    QPixmap waitImage(m_previewSize);
+    waitImage.fill(Qt::transparent);
 
-    QPixmap waitImage(m_previewSize);
     QPainter p(&waitImage);
-
-    p.fillRect(waitImage.rect(), linearGrad);
+    const QPixmap waitIcon(":/res/iconTime.png");
     int x = (waitImage.width() - waitIcon.width()) / 2;
     int y = (waitImage.height() - waitIcon.height()) / 2;
     p.drawPixmap(QPoint(x, y), waitIcon);
 
-    addInfoToPreview(waitImage);
+    setImage(waitImage, linearGradLoading, false);
 
     cType->setEnabled(false);
 }
@@ -486,20 +564,37 @@
     }
     else if (m_staticMapModel->mapExists(map))
     {
+        m_missingMap = false;
         changeMapType(MapModel::StaticMap, m_staticMapModel->index(m_staticMapModel->findMap(map), 0));
     }
     else if (m_missionMapModel->mapExists(map))
     {
+        m_missingMap = false;
         changeMapType(MapModel::MissionMap, m_missionMapModel->index(m_missionMapModel->findMap(map), 0));
     } else
     {
         qDebug() << "HWMapContainer::intSetMap: Map doesn't exist: " << map;
+        m_missingMap = true;
+        m_curMap = map;
+        m_mapInfo.name = map;
+        setMapNameLabel(map, false);
+        if (m_mapInfo.type == MapModel::StaticMap)
+            setupStaticMapsView(m_curMap);
+        else if (m_mapInfo.type == MapModel::MissionMap)
+            setupMissionMapsView(m_curMap);
+        else
+        {
+            m_mapInfo.type = MapModel::StaticMap;
+            setupStaticMapsView(m_curMap);
+            changeMapType(m_mapInfo.type, QModelIndex());
+        }
+        updatePreview();
     }
 }
 
 void HWMapContainer::setMap(const QString & map)
 {
-    if ((m_mapInfo.type == MapModel::Invalid) || (map != m_mapInfo.name))
+    if ((m_mapInfo.type == MapModel::Invalid) || (map != m_mapInfo.name) || m_missingMap)
         intSetMap(map);
 }
 
@@ -510,7 +605,7 @@
     if(mdl.size())
         updateTheme(mdl.at(0));
     else
-        intSetIconlessTheme(theme);
+        setMissingTheme(theme);
 }
 
 void HWMapContainer::setRandomMap()
@@ -568,9 +663,9 @@
     QAbstractItemModel * tmodel;
 
     if (m_withoutDLC)
-        tmodel = m_themeModel->withoutDLC();
+        tmodel = m_themeModel->withoutDLCOrHidden();
     else
-        tmodel = m_themeModel;
+        tmodel = m_themeModel->withoutHidden();
 
     if(!tmodel->rowCount()) return;
     quint32 themeNum = rand() % tmodel->rowCount();
@@ -701,7 +796,8 @@
 
 void HWMapContainer::showEvent(QShowEvent * event)
 {
-    if (!m_previewEnabled) {
+    if (!m_previewEnabled)
+    {
         m_previewEnabled = true;
         setRandomTheme();
         updatePreview();
@@ -717,19 +813,22 @@
 
     if (pMap)
     {
-        disconnect(pMap, 0, this, SLOT(setImage(const QPixmap)));
+        disconnect(pMap, 0, this, SLOT(onImageReceived(const QPixmap)));
         disconnect(pMap, 0, this, SLOT(setHHLimit(int)));
+        disconnect(pMap, 0, this, SLOT(onPreviewMapDestroyed(QObject *)));
         pMap = 0;
     }
 
-    QPixmap failIcon;
+    QPixmap failPixmap;
+    QIcon failIcon;
 
     switch(m_mapInfo.type)
     {
         case MapModel::Invalid:
-            failIcon = QPixmap(":/res/btnDisabled.png");
-            mapPreview->setIcon(QIcon(failIcon));
-            mapPreview->setIconSize(failIcon.size());
+            // Map error image
+            failPixmap = QPixmap(":/res/missingMap.png");
+            setImage(failPixmap, linearGradMapError, false);
+            lblDesc->clear();
             break;
         case MapModel::GeneratedMap:
         case MapModel::GeneratedMaze:
@@ -739,17 +838,32 @@
             askForGeneratedPreview();
             break;
         default:
-            QPixmap mapImage;
-            bool success = mapImage.load("physfs://Maps/" + m_mapInfo.name + "/preview.png");
-
-            if(!success)
+            // For maps loaded from image
+            if(m_missingMap)
+            {
+                // Map error image due to missing map
+                failPixmap = QPixmap(":/res/missingMap.png");
+                setImage(failPixmap, linearGradMapError, false);
+                lblDesc->clear();
+                break;
+            }
+            else
             {
-                mapPreview->setIcon(QIcon());
-                return;
+                // Draw map preview
+                QPixmap mapImage;
+                bool success = mapImage.load("physfs://Maps/" + m_mapInfo.name + "/preview.png");
+
+                setHHLimit(m_mapInfo.limit);
+                if(!success)
+                {
+                    // Missing preview image
+                    QPixmap empty = QPixmap(m_previewSize);
+                    empty.fill(Qt::transparent);
+                    setImage(empty, linearGradNoPreview, true);
+                    return;
+                }
+                setImage(mapImage);
             }
-
-            hhLimit = m_mapInfo.limit;
-            addInfoToPreview(mapImage);
     }
 }
 
@@ -769,7 +883,7 @@
 {
     // restore theme selection
     // do this before map selection restore, because map may overwrite theme
-    if (!m_theme.isEmpty())
+    if (!m_theme.isNull() && !m_theme.isEmpty())
     {
         QModelIndexList mdl = m_themeModel->match(m_themeModel->index(0), Qt::DisplayRole, m_theme);
         if (mdl.size() > 0)
@@ -779,7 +893,7 @@
     }
 
     // restore map selection
-    if (!m_curMap.isEmpty())
+    if (!m_curMap.isNull() && !m_curMap.isEmpty())
         intSetMap(m_curMap);
     else
         updatePreview();
@@ -839,6 +953,7 @@
 {
     staticMapList->hide();
     missionMapList->hide();
+    teMapName->hide();
     lblMapList->hide();
     generationStyles->hide();
     mazeStyles->hide();
@@ -883,7 +998,15 @@
             missionMapChanged(newMap.isValid() ? newMap : missionMapList->currentIndex());
             lblMapList->setText(tr("Mission:"));
             lblMapList->show();
-            missionMapList->show();
+            setMapNameLabel(m_curMap, !m_missingMap);
+            if(m_master)
+            {
+                missionMapList->show();
+            }
+            else
+            {
+                teMapName->show();
+            }
             mapFeatureSize->hide();
             lblDesc->setText(m_mapInfo.desc);
             lblDesc->show();
@@ -895,8 +1018,16 @@
             staticMapChanged(newMap.isValid() ? newMap : staticMapList->currentIndex());
             lblMapList->setText(tr("Map:"));
             lblMapList->show();
+            setMapNameLabel(m_curMap, !m_missingMap);
+            if(m_master)
+            {
+                staticMapList->show();
+            }
+            else
+            {
+                teMapName->show();
+            }
             mapFeatureSize->hide();
-            staticMapList->show();
             emit mapChanged(m_curMap);
             break;
         case MapModel::FortsMap:
@@ -984,12 +1115,10 @@
 {
     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);
+    QIcon icon = current.data(Qt::DecorationRole).value<QIcon>();
     btnTheme->setIcon(icon);
-    btnTheme->setText(tr("Theme: %1").arg(current.data(Qt::DisplayRole).toString()));
+    QString themeLabel = tr("Theme: %1").arg(current.data(Qt::DisplayRole).toString());
+    btnTheme->setText(themeLabel);
     updateThemeButtonSize();
 }
 
@@ -1008,24 +1137,31 @@
 {
     QListView * mapList;
 
-    if (type == 0)      mapList = staticMapList;
-    else if (type == 1) mapList = missionMapList;
-    else                return;
+    if (type == 0)
+    {
+        mapList = staticMapList;
+        m_mapInfo.type = MapModel::StaticMap;
+    }
+    else if (type == 1)
+    {
+        mapList = missionMapList;
+        m_mapInfo.type = MapModel::MissionMap;
+    }
+    else
+        return;
 
     // Make sure it is a valid index
     if (!map.isValid())
     {
+        // Make sure there's always a valid selection in the map list
         if (old.isValid())
         {
             mapList->setCurrentIndex(old);
             mapList->scrollTo(old);
         }
-        else
-        {
-            m_mapInfo.type = MapModel::Invalid;
-            updatePreview();
-        }
-
+        m_mapInfo.type = MapModel::Invalid;
+        m_missingMap = true;
+        updatePreview();
         return;
     }
 
@@ -1035,6 +1171,11 @@
         mapList->setCurrentIndex(map);
         mapList->scrollTo(map);
     }
+    if (m_missingMap)
+    {
+        m_missingMap = false;
+        updatePreview();
+    }
 
     Q_ASSERT(map.data(Qt::UserRole + 1).canConvert<MapModel::MapInfo>()); // Houston, we have a problem.
     setMapInfo(map.data(Qt::UserRole + 1).value<MapModel::MapInfo>());
@@ -1046,9 +1187,9 @@
     m_curMap = m_mapInfo.name;
 
     // the map has no pre-defined theme, so let's use the selected one
-    if (m_mapInfo.theme.isEmpty())
+    if (m_mapInfo.theme.isNull() || m_mapInfo.theme.isEmpty())
     {
-        if (!selectedTheme.isEmpty())
+        if (!selectedTheme.isNull() && !selectedTheme.isEmpty())
         {
             setTheme(selectedTheme);
             emit themeChanged(selectedTheme);
@@ -1068,7 +1209,8 @@
 
 void HWMapContainer::loadDrawing()
 {
-    QString fileName = QFileDialog::getOpenFileName(NULL, tr("Load drawn map"), ".", tr("Drawn Maps") + " (*.hwmap);;" + tr("All files") + " (*)");
+    QString loadDir = QDir(cfgdir->absolutePath() + "/DrawnMaps").absolutePath();
+    QString fileName = QFileDialog::getOpenFileName(this, tr("Load drawn map"), loadDir, tr("Drawn Maps") + " (*.hwmap);;" + tr("All files") + " (*)");
 
     if(fileName.isEmpty()) return;
 
@@ -1109,18 +1251,69 @@
 
     foreach (QWidget *widget, m_childWidgets)
         widget->setEnabled(master);
+
+    if(m_mapInfo.type == MapModel::StaticMap)
+    {
+        teMapName->setHidden(master);
+        staticMapList->setVisible(master);
+    }
+    else if(m_mapInfo.type == MapModel::MissionMap)
+    {
+        teMapName->setHidden(master);
+        missionMapList->setVisible(master);
+    }
+
+    if(master)
+    {
+        // Room delegation cleanup if we get room control.
+
+        if(m_missingMap)
+        {
+            // Reset map if we don't have the host's map
+            m_missingMap = false;
+            if(m_mapInfo.type == MapModel::MissionMap)
+            {
+                missionMapList->selectionModel()->setCurrentIndex(m_missionMapModel->index(0, 0), QItemSelectionModel::Clear | QItemSelectionModel::SelectCurrent);
+            }
+            else
+            {
+                if(m_mapInfo.type != MapModel::StaticMap)
+                {
+                    changeMapType(MapModel::StaticMap);
+                }
+                staticMapList->selectionModel()->setCurrentIndex(m_staticMapModel->index(0, 0), QItemSelectionModel::Clear | QItemSelectionModel::SelectCurrent);
+            }
+        }
+        else
+        {
+            // Set random theme if we don't have it
+            QModelIndexList mdl = m_themeModel->match(m_themeModel->index(0), ThemeModel::ActualNameRole, m_theme);
+            if(!mdl.size())
+                setRandomTheme();
+        }
+    }
+    else
+    {
+        setMapNameLabel(m_curMap, true);
+    }
 }
 
-void HWMapContainer::intSetIconlessTheme(const QString & name)
+void HWMapContainer::setMissingTheme(const QString & name)
 {
-    if (name.isEmpty()) return;
+    if (name.isNull() || name.isEmpty()) return;
 
     m_theme = name;
-    btnTheme->setIcon(QIcon());
-    btnTheme->setText(tr("Theme: %1").arg(name));
+    QPixmap pixMissing = QPixmap(":/res/missingTheme@2x.png");
+    QIcon iconMissing  = QIcon();
+    iconMissing.addPixmap(pixMissing, QIcon::Normal);
+    iconMissing.addPixmap(pixMissing, QIcon::Disabled);
+    btnTheme->setIcon(iconMissing);
+    // Question mark in front of theme name denotes it's missing
+    btnTheme->setText(tr("Theme: %1").arg("?" + name));
+    updateThemeButtonSize();
 }
 
-void HWMapContainer::setupMissionMapsView()
+void HWMapContainer::setupMissionMapsView(const QString & initialMap)
 {
     if(m_missionsViewSetup) return;
     m_missionsViewSetup = true;
@@ -1133,10 +1326,13 @@
             SIGNAL(currentRowChanged(const QModelIndex &, const QModelIndex &)),
             this,
             SLOT(missionMapChanged(const QModelIndex &, const QModelIndex &)));
-    missionSelectionModel->setCurrentIndex(m_missionMapModel->index(0, 0), QItemSelectionModel::Clear | QItemSelectionModel::SelectCurrent);
+    int m = 0;
+    if(!initialMap.isNull())
+        m = m_missionMapModel->findMap(initialMap);
+    missionSelectionModel->setCurrentIndex(m_missionMapModel->index(m, 0), QItemSelectionModel::Clear | QItemSelectionModel::SelectCurrent);
 }
 
-void HWMapContainer::setupStaticMapsView()
+void HWMapContainer::setupStaticMapsView(const QString & initialMap)
 {
     if(m_staticViewSetup) return;
     m_staticViewSetup = true;
@@ -1149,5 +1345,25 @@
             SIGNAL(currentRowChanged(const QModelIndex &, const QModelIndex &)),
             this,
             SLOT(staticMapChanged(const QModelIndex &, const QModelIndex &)));
-    staticSelectionModel->setCurrentIndex(m_staticMapModel->index(0, 0), QItemSelectionModel::Clear | QItemSelectionModel::SelectCurrent);
+    int m = 0;
+    if(!initialMap.isNull())
+        m = m_staticMapModel->findMap(initialMap);
+    staticSelectionModel->setCurrentIndex(m_staticMapModel->index(m, 0), QItemSelectionModel::Clear | QItemSelectionModel::SelectCurrent);
 }
+
+// Call this function instead of setting the text of the map name label
+// directly.
+void HWMapContainer::setMapNameLabel(QString mapName, bool validMap)
+{
+    // Cut off insanely long names to be displayed
+    if(mapName.length() >= 90)
+    {
+        mapName.truncate(84);
+        mapName.append(" (...)");
+    }
+    teMapName->setPlainText(mapName);
+    if(validMap)
+        teMapName->setStyleSheet("background-color: transparent;");
+    else
+        teMapName->setStyleSheet("background-color: transparent; color: #b50000;");
+}
--- a/QTfrontend/ui/widget/mapContainer.h	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/ui/widget/mapContainer.h	Sun Jun 10 19:12:26 2018 +0200
@@ -24,6 +24,7 @@
 #include <QComboBox>
 #include <QGridLayout>
 #include <QLabel>
+#include <QTextEdit>
 #include <QLineEdit>
 #include <QSlider>
 #include <QVBoxLayout>
@@ -99,12 +100,11 @@
         void drawnMapChanged(const QByteArray & data);
 
     private slots:
-        void setImage(const QPixmap & newImage);
+        void onImageReceived(const QPixmap & newImage);
         void setHHLimit(int hhLimit);
         void setRandomSeed();
         void setRandomTheme();
         void setRandomMap();
-        void addInfoToPreview(const QPixmap & image);
         void setNewSeed(const QString & newSeed);
         void mapTypeChanged(int);
         void showThemePrompt();
@@ -147,6 +147,7 @@
         QComboBox * cType;
         QListView * staticMapList;
         QListView * missionMapList;
+        QTextEdit * teMapName;
         QListWidget * generationStyles;
         QListWidget * mazeStyles;
         QLabel * lblMapList;
@@ -158,12 +159,14 @@
         QPushButton * btnRandTheme;
         QString selectedTheme;
         QPushButton * btnSeed;
+        QHBoxLayout * twoColumnLayout;
         bool m_master;
         QList<QWidget *> m_childWidgets;
         bool m_previewEnabled;
         bool m_missionsViewSetup;
         bool m_staticViewSetup;
         bool m_withoutDLC;
+        bool m_missingMap;
 
         void intSetSeed(const QString & seed);
         void intSetMap(const QString & map);
@@ -171,15 +174,20 @@
         void intSetTemplateFilter(int);
         void intSetMazeSize(int size);
         void intSetFeatureSize(int size);
-        void intSetIconlessTheme(const QString & name);
+        void setMissingTheme(const QString & name);
         void mapChanged(const QModelIndex & map, int type, const QModelIndex & old = QModelIndex());
+        void setImage(const QPixmap & newImage);
+        void setImage(const QPixmap & newImage, const QLinearGradient & linearGrad, bool showHHLimit);
+        void addInfoToPreview(const QPixmap & image);
+        void addInfoToPreview(const QPixmap & image, const QLinearGradient & linearGrad, bool drawHHLimit);
         void setMapInfo(MapModel::MapInfo mapInfo);
         void changeMapType(MapModel::MapType type, const QModelIndex & newMap = QModelIndex());
         void updateHelpTexts(MapModel::MapType type);
         void updatePreview();
         void updateThemeButtonSize();
-        void setupMissionMapsView();
-        void setupStaticMapsView();
+        void setupMissionMapsView(const QString & initialMap = QString());
+        void setupStaticMapsView(const QString & initialMap = QString());
+        void setMapNameLabel(QString mapName, bool validMap);
 
         MapModel::MapInfo m_mapInfo;
         int m_themeID;
@@ -188,7 +196,10 @@
         QString m_theme;
         QString m_curMap;
 
-        QLinearGradient linearGrad; ///< for preview background
+        QLinearGradient linearGradNormal; ///< for preview background
+        QLinearGradient linearGradLoading; ///< for preview background while loading/generating map
+        QLinearGradient linearGradNoPreview; ///< for preview background when map preview image is missing
+        QLinearGradient linearGradMapError; ///< for preview background when map is missing
         QSize m_previewSize;
 };
 
--- a/QTfrontend/ui/widget/selectWeapon.cpp	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/ui/widget/selectWeapon.cpp	Sun Jun 10 19:12:26 2018 +0200
@@ -21,6 +21,7 @@
 #include "weaponItem.h"
 #include "hwconsts.h"
 
+#include <QDebug>
 #include <QPushButton>
 #include <QGridLayout>
 #include <QHBoxLayout>
@@ -84,16 +85,63 @@
     QFrame(parent),
     m_numItems(numItems)
 {
-    wconf = new QSettings(cfgdir->absolutePath() + "/weapons.ini", QSettings::IniFormat, this);
+    wconf = new QMap<QString, QString>();
+    for(int i = 0; i < cDefaultAmmos.size(); ++i)
+    {
+        wconf->insert(cDefaultAmmos[i].first, cDefaultAmmos[i].second);
+    }
 
-    for(int i = 0; i < cDefaultAmmos.size(); ++i)
-        wconf->setValue(cDefaultAmmos[i].first, cDefaultAmmos[i].second);
+    if (!QDir(cfgdir->absolutePath() + "/Schemes").exists()) {
+        QDir().mkdir(cfgdir->absolutePath() + "/Schemes");
+    }
+    if (!QDir(cfgdir->absolutePath() + "/Schemes/Ammo").exists()) {
+        qDebug("No /Schemes/Ammo directory found. Trying to import weapon schemes from weapons.ini.");
+        QDir().mkdir(cfgdir->absolutePath() + "/Schemes/Ammo");
+
+        QSettings old_wconf(cfgdir->absolutePath() + "/weapons.ini", QSettings::IniFormat);
+
+        QStringList defaultAmmos;
+        for(int i = 0; i < cDefaultAmmos.size(); ++i)
+        {
+            defaultAmmos.append(cDefaultAmmos[i].first);
+        }
 
-    QStringList keys = wconf->allKeys();
-    for(int i = 0; i < keys.size(); i++)
-    {
-        if (wconf->value(keys[i]).toString().size() != cDefaultAmmoStore->size())
-            wconf->setValue(keys[i], fixWeaponSet(wconf->value(keys[i]).toString()));
+        QStringList keys = old_wconf.allKeys();
+        int imported = 0;
+        for(int i = 0; i < keys.size(); i++)
+        {
+            if (!defaultAmmos.contains(keys[i])) {
+                wconf->insert(keys[i], fixWeaponSet(old_wconf.value(keys[i]).toString()));
+                QFile file(cfgdir->absolutePath() + "/Schemes/Ammo/" + keys[i] + ".hwa");
+                if (file.open(QIODevice::WriteOnly)) {
+                    QTextStream stream( &file );
+                    stream << old_wconf.value(keys[i]).toString() << endl;
+                    file.close();
+                }
+                imported++;
+            }
+        }
+        qDebug("%d weapon scheme(s) imported.", imported);
+    } else {
+        QStringList schemes = QDir(cfgdir->absolutePath() + "/Schemes/Ammo").entryList(QDir::Files);
+
+        for(int i = 0; i < schemes.size(); i++)
+        {
+            QFile file(cfgdir->absolutePath() + "/Schemes/Ammo/" + schemes[i]);
+            QString config;
+            if (file.open(QIODevice::ReadOnly)) {
+                QTextStream stream( &file );
+                stream >> config;
+                file.close();
+            }
+
+            // Chop off file name suffix
+            QString schemeName = schemes[i];
+            if (schemeName.endsWith(".hwa", Qt::CaseInsensitive)) {
+                schemeName.chop(4);
+            }
+            wconf->insert(schemeName, fixWeaponSet(config));
+        }
     }
 
     QString currentState = *cDefaultAmmoStore;
@@ -159,10 +207,13 @@
 {
     bool enable = true;
     for(int i = 0; i < cDefaultAmmos.size(); i++)
+    {
         if (!cDefaultAmmos[i].first.compare(m_name->text()))
         {
             enable = false;
+            break;
         }
+    }
     for(int i = 0; i < m_numItems; ++i)
     {
         twi::iterator it = weaponItems.find(i);
@@ -182,10 +233,12 @@
 void SelWeaponWidget::setDefault()
 {
     for(int i = 0; i < cDefaultAmmos.size(); i++)
+    {
         if (!cDefaultAmmos[i].first.compare(m_name->text()))
         {
             return;
         }
+    }
     setWeapons(*cDefaultAmmoStore);
 }
 
@@ -250,7 +303,13 @@
         // remove old entry
         wconf->remove(curWeaponsName);
     }
-    wconf->setValue(m_name->text(), stateFull);
+    wconf->insert(m_name->text(), stateFull);
+    QFile file(cfgdir->absolutePath() + "/Schemes/Ammo/" + m_name->text()+ ".hwa");
+    if (file.open(QIODevice::WriteOnly)) {
+        QTextStream stream( &file );
+        stream << stateFull << endl;
+        file.close();
+    }
     emit weaponsEdited(curWeaponsName, m_name->text(), stateFull);
 }
 
@@ -262,7 +321,7 @@
 
 QString SelWeaponWidget::getWeaponsString(const QString& name) const
 {
-    return wconf->value(name).toString();
+    return wconf->find(name).value();
 }
 
 void SelWeaponWidget::deleteWeaponsName()
@@ -271,6 +330,7 @@
     if (delWeaponsName == "") return;
 
     for(int i = 0; i < cDefaultAmmos.size(); i++)
+    {
         if (!cDefaultAmmos[i].first.compare(delWeaponsName))
         {
             QMessageBox deniedMsg(this);
@@ -281,6 +341,7 @@
             deniedMsg.exec();
             return;
         }
+    }
 
     QMessageBox reallyDeleteMsg(this);
     reallyDeleteMsg.setIcon(QMessageBox::Question);
@@ -293,6 +354,7 @@
     {
         isDeleting = true;
         wconf->remove(delWeaponsName);
+        QFile(cfgdir->absolutePath() + "/Schemes/Ammo/" + curWeaponsName + ".hwa").remove();
         emit weaponsDeleted(delWeaponsName);
     }
 }
@@ -308,7 +370,7 @@
         while(wconf->contains(newName = tr("New (%1)").arg(i++))) ;
     }
     setWeaponsName(newName);
-    wconf->setValue(newName, *cEmptyAmmoStore);
+    wconf->insert(newName, *cEmptyAmmoStore);
     emit weaponsAdded(newName, *cEmptyAmmoStore);
 }
 
@@ -320,7 +382,7 @@
 
     if(name != "" && wconf->contains(name))
     {
-        setWeapons(wconf->value(name).toString());
+        setWeapons(wconf->find(name).value());
     }
     else
     {
@@ -337,7 +399,7 @@
 
 QStringList SelWeaponWidget::getWeaponNames() const
 {
-    return wconf->allKeys();
+    return wconf->keys();
 }
 
 void SelWeaponWidget::copy()
@@ -355,7 +417,7 @@
         }
         setWeaponsName(newName);
         setWeapons(ammo);
-        wconf->setValue(newName, ammo);
+        wconf->insert(newName, ammo);
         emit weaponsAdded(newName, ammo);
     }
 }
@@ -374,7 +436,9 @@
                ;
 
     for(int i = sl.length() - 1; i >= 0; --i)
+    {
         sl[i] = sl[i].leftJustified(neededLength, '0', true);
+    }
 
     return sl.join(QString());
 }
--- a/QTfrontend/ui/widget/selectWeapon.h	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/ui/widget/selectWeapon.h	Sun Jun 10 19:12:26 2018 +0200
@@ -80,7 +80,7 @@
         QLineEdit* m_name;
 
         //storage for all the weapons sets
-        QSettings* wconf;
+        QMap<QString, QString>* wconf;
 
         const int m_numItems;
         int operator [] (unsigned int weaponIndex) const;
--- a/QTfrontend/ui/widget/teamselect.cpp	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/ui/widget/teamselect.cpp	Sun Jun 10 19:12:26 2018 +0200
@@ -250,16 +250,7 @@
     if (maxHeight > 0)
         area->setMaximumHeight(maxHeight);
     if (setFrame)
-    {
-        area->setStyleSheet(
-            "FrameTeams{"
-            "border: solid;"
-            "border-width: 1px;"
-            "border-radius: 16px;"
-            "border-color: #ffcc00;"
-            "}"
-        );
-    }
+        pfteams->setDecoFrameEnabled(true);
 }
 
 TeamSelWidget::TeamSelWidget(QWidget* parent) :
--- a/QTfrontend/ui/widget/teamselhelper.cpp	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/ui/widget/teamselhelper.cpp	Sun Jun 10 19:12:26 2018 +0200
@@ -57,15 +57,6 @@
     butt->setFlat(true);
     butt->setToolTip(team.owner());
     mainLayout.addWidget(butt);
-    butt->setStyleSheet("QPushButton{"
-                        "icon-size: 48px;"
-                        "text-align: left;"
-                        "background-color: #0d0544;"
-                        "color: orange;"
-                        "font: bold;"
-                        "border-width: 2px;"
-                        "margin: 6px 0px 6px 0px;"
-                        "}");
 
     if(m_isPlaying)
     {
--- a/QTfrontend/ui/widget/themeprompt.cpp	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/ui/widget/themeprompt.cpp	Sun Jun 10 19:12:26 2018 +0200
@@ -68,11 +68,13 @@
 
     setStyleSheet("QPushButton { padding: 5px; margin-top: 10px; }");
 
-    // Theme model, and a model for setting a filter
+    // Theme model
     ThemeModel * themeModel = DataManager::instance().themeModel();
-    filterModel = new QSortFilterProxyModel();
-    filterModel->setSourceModel(themeModel);
+    filterModel = themeModel->withoutHidden();
+    // Custom filter extension
     filterModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
+    // Reset search field
+    filterModel->setFilterFixedString(QString());
 
     // Grid
     QGridLayout * dialogLayout = new QGridLayout(this);
@@ -119,7 +121,7 @@
 
     // Cancel button (closes dialog)
     QPushButton * btnCancel = new QPushButton(tr("Cancel"));
-    connect(btnCancel, SIGNAL(clicked()), this, SLOT(reject()));
+    connect(btnCancel, SIGNAL(clicked()), this, SLOT(onRejected()));
 
     // Select button
     QPushButton * btnSelect = new QPushButton(tr("Use selected theme"));
@@ -168,9 +170,16 @@
     list->moveRight();
 }
 
+void ThemePrompt::onRejected()
+{
+    reject();
+    filterModel->setFilterFixedString(QString());
+}
+
 void ThemePrompt::onAccepted()
 {
     themeChosen(list->currentIndex());
+    filterModel->setFilterFixedString(QString());
 }
 
 // When a theme is selected
--- a/QTfrontend/ui/widget/themeprompt.h	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/ui/widget/themeprompt.h	Sun Jun 10 19:12:26 2018 +0200
@@ -54,6 +54,7 @@
 
     private slots:
         void onAccepted();
+        void onRejected();
         void themeChosen(const QModelIndex & index);
         void filterChanged(const QString & text);
         void moveUp();
--- a/QTfrontend/ui/widget/togglebutton.cpp	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/ui/widget/togglebutton.cpp	Sun Jun 10 19:12:26 2018 +0200
@@ -24,20 +24,32 @@
 {
     setCheckable(true);
 
-    QPixmap pm(":/res/btnDisabled.png");
+    QPixmap pixOffOverlay(":/res/btnDisabled.png");
     QPainter * painter = new QPainter();
 
-    pmChecked.load(img);
-    pmDisabled.load(img);
+    QPixmap pixOn = QPixmap(img);
+    QPixmap pixOff = QPixmap(img);
 
-    setMaximumWidth(pmChecked.width() + 6);
+    // Use the same image for disabled (i.e. non-clickable) button.
+    // The default would be gray which is a little bit hard on the eye.
+    // The disabled state is communicated to the user by the button
+    // border, which turns gray.
+    icoChecked.addPixmap(pixOn, QIcon::Normal);
+    icoChecked.addPixmap(pixOn, QIcon::Disabled);
 
-    painter->begin(&pmDisabled);
-    painter->drawPixmap(pmDisabled.rect(), pm);
+    pixOff.setDevicePixelRatio(pixOffOverlay.devicePixelRatio());
+
+    setMaximumWidth(pixOn.width() + 6);
+
+    painter->begin(&pixOff);
+    painter->drawPixmap(pixOff.rect(), pixOffOverlay);
     painter->end();
 
-    setIconSize(pmDisabled.size());
-    setIcon(pmDisabled);
+    icoUnchecked.addPixmap(pixOff, QIcon::Normal);
+    icoUnchecked.addPixmap(pixOff, QIcon::Disabled);
+
+    setIconSize(pixOff.size());
+    setIcon(icoUnchecked);
 
     connect(this, SIGNAL(toggled(bool)), this, SLOT(eventToggled(bool)));
 }
@@ -48,5 +60,5 @@
 
 void ToggleButtonWidget::eventToggled(bool checked)
 {
-    setIcon(checked ? pmChecked : pmDisabled);
+    setIcon(checked ? icoChecked : icoUnchecked);
 }
--- a/QTfrontend/ui/widget/togglebutton.h	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/ui/widget/togglebutton.h	Sun Jun 10 19:12:26 2018 +0200
@@ -25,6 +25,7 @@
 #include <QPushButton>
 #include <QVBoxLayout>
 #include <QLabel>
+#include <QIcon>
 
 class ToggleButtonWidget : public QPushButton
 {
@@ -33,8 +34,8 @@
         ToggleButtonWidget(QWidget * parent, QString img);
         ~ToggleButtonWidget();
     private:
-        QPixmap pmChecked;
-        QPixmap pmDisabled;
+        QIcon icoUnchecked;
+        QIcon icoChecked;
     private slots:
         void eventToggled(bool checked);
 };
--- a/QTfrontend/util/DataManager.cpp	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/util/DataManager.cpp	Sun Jun 10 19:12:26 2018 +0200
@@ -172,11 +172,11 @@
 {
     if(m_settingsFileName.isEmpty())
     {
-        QFile settingsFile("physfs://settings.ini");
+        QFile settingsFile(cfgdir->absoluteFilePath("settings.ini"));
 
         if(!settingsFile.exists())
         {
-            QFile oldSettingsFile("physfs://hedgewars.ini");
+            QFile oldSettingsFile(cfgdir->absoluteFilePath("hedgewars.ini"));
 
             settingsFile.open(QFile::WriteOnly);
             settingsFile.close();
--- a/QTfrontend/util/FileEngine.h	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/util/FileEngine.h	Sun Jun 10 19:12:26 2018 +0200
@@ -1,9 +1,7 @@
 #ifndef _FileEngine_h
 #define _FileEngine_h
 
-#include <QAbstractFileEngine>
-#include <QAbstractFileEngineHandler>
-#include <QAbstractFileEngineIterator>
+#include <private/qabstractfileengine_p.h>
 #include <QDateTime>
 
 #include "physfs.h"
--- a/QTfrontend/util/LibavInteraction.cpp	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/util/LibavInteraction.cpp	Sun Jun 10 19:12:26 2018 +0200
@@ -302,26 +302,64 @@
         if (!pCodec)
             continue;
 
+
+        AVCodec* pDecoder = avcodec_find_decoder(pCodec->codec_id);
+        QString decoderName = pDecoder ? pDecoder->name : tr("unknown");
         if (pCodec->codec_type == AVMEDIA_TYPE_VIDEO)
         {
-            desc += QString(tr("Video: %1x%2")).arg(pCodec->width).arg(pCodec->height) + ", ";
             if (pStream->avg_frame_rate.den)
             {
                 float fps = float(pStream->avg_frame_rate.num)/pStream->avg_frame_rate.den;
-                desc += QString(tr("%1 FPS")).arg(fps, 0, 'f', 2) + ", ";
+                //: Video metadata. %1 = video width, %2 = video height, %3 = frames per second = %4 = decoder name
+                desc += QString(tr("Video: %1x%2, %3 FPS, %4")).arg(pCodec->width).arg(pCodec->height).arg(QLocale().toString(fps, 'f', 2)).arg(decoderName);
+            }
+            else
+            {
+                //: Video metadata. %1 = video width, %2 = video height, %3 = decoder name
+                desc += QString(tr("Video: %1x%2, %3")).arg(pCodec->width).arg(pCodec->height).arg(decoderName);
             }
         }
         else if (pCodec->codec_type == AVMEDIA_TYPE_AUDIO)
+        {
             desc += tr("Audio: ");
+            desc += decoderName;
+        }
         else
             continue;
-        AVCodec* pDecoder = avcodec_find_decoder(pCodec->codec_id);
-        desc += pDecoder? pDecoder->name : tr("unknown");
         desc += "\n";
     }
     AVDictionaryEntry* pComment = av_dict_get(pContext->metadata, "comment", NULL, 0);
     if (pComment)
-        desc += QString("\n") + pComment->value;
+    {
+        // Video comment. We expect a simple key value storage in a particular format
+        // and parse it here so the key names can be localized.
+        desc += QString("\n");
+        QStringList strings = QString(pComment->value).split('\n');
+        QString sPlayer, sTheme, sMap, sRecord;
+        for(int i=0; i < strings.count(); i++)
+        {
+            QString s = strings.at(i);
+            // Original key names are in English, like:
+            //     Key: Value
+            if (s.startsWith("Player: "))
+                sPlayer = QString(s.mid(8));
+            else if (s.startsWith("Theme: "))
+                sTheme = QString(s.mid(7));
+            else if (s.startsWith("Map: "))
+                sMap = QString(s.mid(5));
+            else if (s.startsWith("Record: "))
+                sRecord = QString(s.mid(8));
+        }
+        if(!sPlayer.isNull())
+            desc += QString(tr("Player: %1")).arg(sPlayer) + "\n";
+        if(!sTheme.isNull())
+            desc += QString(tr("Theme: %1")).arg(sTheme) + "\n";
+        if(!sMap.isNull())
+            desc += QString(tr("Map: %1")).arg(sMap) + "\n";
+        if(!sRecord.isNull())
+            //: As in ‘recording’
+            desc += QString(tr("Record: %1")).arg(sRecord);
+    }
     avformat_close_input(&pContext);
     return desc;
 }
--- a/QTfrontend/weapons.h	Sun Jun 10 18:56:51 2018 +0200
+++ b/QTfrontend/weapons.h	Sun Jun 10 19:12:26 2018 +0200
@@ -16,10 +16,10 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 
-#define AMMOLINE_EMPTY_QT       "0000009000000000000000000000000000000000000000000000000000"
-#define AMMOLINE_EMPTY_PROB     "0000000000000000000000000000000000000000000000000000000000"
-#define AMMOLINE_EMPTY_DELAY    "0000000000000000000000000000000000000000000000000000000000"
-#define AMMOLINE_EMPTY_CRATE    "1311110312111111123114111111111111111211111111111111111111"
+#define AMMOLINE_EMPTY_QT       "00000090000000000000000000000000000000000000000000000000000"
+#define AMMOLINE_EMPTY_PROB     "00000000000000000000000000000000000000000000000000000000000"
+#define AMMOLINE_EMPTY_DELAY    "00000000000000000000000000000000000000000000000000000000000"
+#define AMMOLINE_EMPTY_CRATE    "13111103121111111231141111111111111112111111111111111111111"
 
 /*
  AmmoType lookup table (use monospace font / cursor movements)
@@ -82,63 +82,64 @@
  amRubber-------------------------------------------------------------------------------|
  amAirMine-------------------------------------------------------------------------------|
  amDuck-----------------------------------------------------------------------------------|
+ amMinigun---------------------------------------------------------------------------------|
 */
-#define AMMOLINE_DEFAULT_QT     "9391929422199121032235111001200000000211100101011111000102"
-#define AMMOLINE_DEFAULT_PROB   "0405040541600655546554464776576666666155510101115411111114"
-#define AMMOLINE_DEFAULT_DELAY  "0000000000000205500000040007004000000000220000000600020000"
-#define AMMOLINE_DEFAULT_CRATE  "1311110312111111123114111111111111111211111111111111111111"
+#define AMMOLINE_DEFAULT_QT     "93919294221991210322351110012000000002111001010111110001000"
+#define AMMOLINE_DEFAULT_PROB   "04050405416006555465544647765766666661555101011154111111107"
+#define AMMOLINE_DEFAULT_DELAY  "00000000000002055000000400070040000000002200000006000200000"
+#define AMMOLINE_DEFAULT_CRATE  "13111103121111111231141111111111111112111111111111111111111"
 
-#define AMMOLINE_CRAZY_QT       "9999999999999999992999999999999999299999999999999992999199"
-#define AMMOLINE_CRAZY_PROB     "1111110111111111111111111111111111111111111111111111111111"
-#define AMMOLINE_CRAZY_DELAY    "0000000000000000000000000000000000000000000000000000000000"
-#define AMMOLINE_CRAZY_CRATE    "1311110312111111123114111111111111111211111111111111111111"
+#define AMMOLINE_CRAZY_QT       "99999999999999999929999999999999992999999999999999929991999"
+#define AMMOLINE_CRAZY_PROB     "11111101111111111111111111111111111111111111111111111111111"
+#define AMMOLINE_CRAZY_DELAY    "00000000000000000000000000000000000000000000000000000000000"
+#define AMMOLINE_CRAZY_CRATE    "13111103121111111231141111111111111112111111111111111111111"
 
-#define AMMOLINE_PROMODE_QT     "9090009000000000000009000000000000000000000000000000000000"
-#define AMMOLINE_PROMODE_PROB   "0000000000000000000000000000000000000000000000000000000000"
-#define AMMOLINE_PROMODE_DELAY  "0000000000000205500000040007004000000000200000000000020000"
-#define AMMOLINE_PROMODE_CRATE  "1111110111111111111111111111111111111111111111111111111111"
+#define AMMOLINE_PROMODE_QT     "90900090000000000000090000000000000000000000000000000000000"
+#define AMMOLINE_PROMODE_PROB   "00000000000000000000000000000000000000000000000000000000000"
+#define AMMOLINE_PROMODE_DELAY  "00000000000002055000000400070040000000002000000000000200000"
+#define AMMOLINE_PROMODE_CRATE  "11111101111111111111111111111111111111111111111111111111111"
 
-#define AMMOLINE_SHOPPA_QT      "0000009900000000000000000000000000000000000000000000000000"
-#define AMMOLINE_SHOPPA_PROB    "4444410044244402210112121222422000000002000400010011001011"
-#define AMMOLINE_SHOPPA_DELAY   "0000000000000000000000000000000000000000000000000000000000"
-#define AMMOLINE_SHOPPA_CRATE   "1111110111111111111111111111111111111111111111111111111111"
+#define AMMOLINE_SHOPPA_QT      "00000099000000000000000000000000000000000000000000000000000"
+#define AMMOLINE_SHOPPA_PROB    "44444100442444022101121212224220000000020004000100110010101"
+#define AMMOLINE_SHOPPA_DELAY   "00000000000000000000000000000000000000000000000000000000000"
+#define AMMOLINE_SHOPPA_CRATE   "11111101111111111111111111111111111111111111111111111111111"
 
-#define AMMOLINE_CLEAN_QT       "1010009000010000011000000000000000000000000000001000000000"
-#define AMMOLINE_CLEAN_PROB     "0405040541600655546554464776576666666155510101115411121114"
-#define AMMOLINE_CLEAN_DELAY    "0000000000000000000000000000000000000000000000000000020000"
-#define AMMOLINE_CLEAN_CRATE    "1311110312111111123114111111111111111211111111111111111111"
+#define AMMOLINE_CLEAN_QT       "10100090000100000110000000000000000000000000000010000000000"
+#define AMMOLINE_CLEAN_PROB     "04050405416006555465544647765766666661555101011154111211104"
+#define AMMOLINE_CLEAN_DELAY    "00000000000000000000000000000000000000000000000000000200000"
+#define AMMOLINE_CLEAN_CRATE    "13111103121111111231141111111111111112111111111111111111111"
 
-#define AMMOLINE_MINES_QT       "0000009900090000000300000000000000000000000000000000000000"
-#define AMMOLINE_MINES_PROB     "0000000000000000000000000000000000000000000000000000000000"
-#define AMMOLINE_MINES_DELAY    "0000000000000205500000040007004000000000200000000600020000"
-#define AMMOLINE_MINES_CRATE    "1111110111111111111111111111111111111111111111111111111111"
+#define AMMOLINE_MINES_QT       "00000099000900000003000000000000000000000000000000000000000"
+#define AMMOLINE_MINES_PROB     "00000000000000000000000000000000000000000000000000000000000"
+#define AMMOLINE_MINES_DELAY    "00000000000002055000000400070040000000002000000006000200000"
+#define AMMOLINE_MINES_CRATE    "11111101111111111111111111111111111111111111111111111111111"
 
-#define AMMOLINE_PORTALS_QT     "9000009002000000002100000000000000110000090000000000000000"
-#define AMMOLINE_PORTALS_PROB   "0405040541600655546554464776576666666155510101115411121112"
-#define AMMOLINE_PORTALS_DELAY  "0000000000000205500000040007004000000000200000000600020000"
-#define AMMOLINE_PORTALS_CRATE  "1311110312111111123114111111111111111211111111111111111111"
+#define AMMOLINE_PORTALS_QT     "90000090020000000021000000000000001100000900000000000000000"
+#define AMMOLINE_PORTALS_PROB   "04050405416006555465544647765766666661555101011154111211102"
+#define AMMOLINE_PORTALS_DELAY  "00000000000002055000000400070040000000002000000006000200000"
+#define AMMOLINE_PORTALS_CRATE  "13111103121111111231141111111111111112111111111111111111111"
 
-#define AMMOLINE_ONEEVERY_QT    "1111119111111111111111111111111111111111111111111111111111"
-#define AMMOLINE_ONEEVERY_PROB  "1111110111111111111111111111111111111111111111111111111111"
-#define AMMOLINE_ONEEVERY_DELAY "0000000000000000000000000000000000000000000000000000000000"
-#define AMMOLINE_ONEEVERY_CRATE "1111110111111111111111111111111111111111111111111111111111"
+#define AMMOLINE_ONEEVERY_QT    "11111191111111111111111111111111111111111111111111111111111"
+#define AMMOLINE_ONEEVERY_PROB  "11111101111111111111111111111111111111111111111111111111111"
+#define AMMOLINE_ONEEVERY_DELAY "00000000000000000000000000000000000000000000000000000000000"
+#define AMMOLINE_ONEEVERY_CRATE "11111101111111111111111111111111111111111111111111111111111"
 
-#define AMMOLINE_HIGHLANDER_QT    "1111119111111111111101911111111110010111110111100100101111"
-#define AMMOLINE_HIGHLANDER_PROB  "0000000000000000000000000000000000000000000000000000000000"
-#define AMMOLINE_HIGHLANDER_DELAY "0000000000000000000000000000000000000000000000000000000000"
-#define AMMOLINE_HIGHLANDER_CRATE "0000000000000000000000000000000000000000000000000000000000"
+#define AMMOLINE_HIGHLANDER_QT    "11111191111111111111019111111111100101111101111001001011101"
+#define AMMOLINE_HIGHLANDER_PROB  "00000000000000000000000000000000000000000000000000000000000"
+#define AMMOLINE_HIGHLANDER_DELAY "00000000000000000000000000000000000000000000000000000000000"
+#define AMMOLINE_HIGHLANDER_CRATE "00000000000000000090000000000000000000000000000000000000000"
 
-#define AMMOLINE_CONSTRUCTION_QT    "1100019000000010010000000000000000000000000000000000000000"
-#define AMMOLINE_CONSTRUCTION_PROB  "1111110111111111111111111111111111111111111111111111111111"
-#define AMMOLINE_CONSTRUCTION_DELAY "0000000000000000000000000000000000000000000000000000000000"
-#define AMMOLINE_CONSTRUCTION_CRATE "1111110111111111111111111111111111111111111111111111111111"
+#define AMMOLINE_CONSTRUCTION_QT    "11000190000000100100900000000000000000000000000000000000000"
+#define AMMOLINE_CONSTRUCTION_PROB  "11111101111111100100011111101111111111111101111100101110101"
+#define AMMOLINE_CONSTRUCTION_DELAY "00000000000000000000000000000000000000000000000000000000000"
+#define AMMOLINE_CONSTRUCTION_CRATE "11111101111111111111111111111111111111111111111111111111111"
 
-#define AMMOLINE_SHOPPAPRO_QT      "0000009900000000000000000000000000000000000000000000000000"
-#define AMMOLINE_SHOPPAPRO_PROB    "4444400044044400000000000000400000000000000000000000000000"
-#define AMMOLINE_SHOPPAPRO_DELAY   "0000000000000000000000000000000000000000000000000000000000"
-#define AMMOLINE_SHOPPAPRO_CRATE   "1111110111111111111111111111111111111111111111111111121111"
+#define AMMOLINE_SHOPPAPRO_QT      "00000099000000000000000000000000000000000000000000000000000"
+#define AMMOLINE_SHOPPAPRO_PROB    "44444000440444000000000000004000000000000000000000000000000"
+#define AMMOLINE_SHOPPAPRO_DELAY   "00000000000000000000000000000000000000000000000000000000000"
+#define AMMOLINE_SHOPPAPRO_CRATE   "11111101111111111111111111111111111111111111111111111211111"
 
-#define AMMOLINE_HEDGEEDITOR_QT    "0000009000000000000000000000000000000000000000000000000000"
-#define AMMOLINE_HEDGEEDITOR_PROB  "0000000000000000000000000000000000000000000000000000000000"
-#define AMMOLINE_HEDGEEDITOR_DELAY "0000000000000000000000000000000000000000000000000000000000"
-#define AMMOLINE_HEDGEEDITOR_CRATE "1111110111111111111111111111111111111111111111111111111111"
+#define AMMOLINE_HEDGEEDITOR_QT    "00000090000000000000000000000000000000000000000000000000000"
+#define AMMOLINE_HEDGEEDITOR_PROB  "00000000000000000000000000000000000000000000000000000000000"
+#define AMMOLINE_HEDGEEDITOR_DELAY "00000000000000000000000000000000000000000000000000000000000"
+#define AMMOLINE_HEDGEEDITOR_CRATE "11111101111111111111111111111111111111111111111111111111111"
--- a/README.md	Sun Jun 10 18:56:51 2018 +0200
+++ b/README.md	Sun Jun 10 19:12:26 2018 +0200
@@ -6,35 +6,38 @@
 This is the funniest and most addictive game you'll ever play—hilarious fun
 that you can enjoy anywhere, anytime. **Hedgewars** is a **turn-based strategy
 game** but the real buzz is from watching the **devastation caused by those
-pesky hedgehogs** with those fantastic weapons—sneaky little blighters with a
-bad attitude!
+pesky hedgehogs** with those fantastic weapons—sneaky little blighters with
+a bad attitude!
 
 Each player controls a **team of up to 8 hedgehogs**. During the course of
 the game, players take turns with one of their hedgehogs. They then use
 whatever tools and weapons are available to **attack and kill the opponents'
-hedgehogs**, thereby winning the game. Hedgehogs may move around the terrain
-in a variety of ways, normally by walking and jumping but also by using
-particular tools such as the rope or parachute, to move to otherwise
-inaccessible areas. Each **turn is time-limited** to ensure that players do
-not hold up the game with excessive thinking or moving.
+hedgehogs**, thereby winning the game.
+
+To destroy your foes you can use one out of **over 40 weapons**!
+Launch bazookas or homing bees, drop mines, dynamite or explosive rubber ducks,
+throw grenades or watermelon bombs, poison them with stinky cheese, send them
+flying with a baseball bat, and much more!
 
-A large **variety of tools and weapons** are available for players during
-the game. Hedgewars features the standard artillery-genre classics (such as
-grenade, cluster bomb, bazooka, shotgun), but also boasts a host of other
-weapons quite unique to it alone.
+Most weapons cause **explosions that deform the terrain**, removing
+circular chunks. Hedgehogs can die by drowning, being thrown off
+either side of the arena, or when their health is reduced to zero.
 
-Most weapons, when used, cause **explosions that deform the terrain**,
-removing circular chunks. Hedgehogs can die by drowning, being thrown off
-either side of the arena, or when their health is reduced to zero.
+Hedgehogs may move around the terrain in a variety of ways, normally by
+walking and jumping but also by using particular tools such as the rope
+or parachute, to move to otherwise inaccessible areas. Each **turn is
+time-limited** to ensure that players do not hold up the game with
+excessive thinking or moving.
 
 Getting started
 ---------------
-For complete beginners we recommend to start with the the first 2 or 3
-missions of the campaign “A Classic Fairytale” in the singleplayer menu.
-It explains the basic controls and gameplay.
-
-Note that Hedgewars doesn't have a *complete* tutorial yet, but it is
-planned to create one in future versions.
+For complete beginners we recommend to play in singleplayer mode first.
+Start with the Basic Movement Training found in the training menu.
+Proceed with the other training missions.
+After completing the training, try to play some quick matches against the
+computer (also found in the singleplayer menu).
+Hedgewars has many weapons, so play a few matches to get a better feeling
+for them.
 
 In-depth information about the game can be found online:
 
@@ -50,17 +53,20 @@
 * Right mouse button: Open ammo menu
 * Left mouse button: Select target or weapon
 * Space bar: Shoot
-* Left shift: Precise
-* Left shift + Up/Down: Precise aiming
+* Left shift: Precise (this is a modifier key)
+* Precise + Up/Down: Precise aiming
+* Precise + Left/Right: Turn around without walking
+* Hold down Precise: Prevent slipping on ice
 * Enter: Jump
 * Backspace: High jump
 * Backspace ×2: Backjump
 * Tab: Switch hedgehog (after activating the utility)
 * 1-5: Set weapon timer
 * F1-F10: Weapon shortcuts
-* P: Pause (also shows mission panel)
+* P: Pause, when playing offline, toggle automatic turn skipping when online
 * Esc: Quit with prompt (also shows mission panel)
 * T: Chat
+* U: Team chat
 
 For the full list, go to the Hedgewars settings. Also read the weapon tooltips
 for weapon-specific controls.
@@ -70,12 +76,10 @@
 These are lesser-known controls of Hedgewars, they are based on your
 configured controls:
 
-* Confirm: Team chat (if not confirming quit)
-* Hold down Precise: Prevent slipping on ice
-* Precise + Left/Right: Turn around
-* Precise + Toggle team bars: Change hedgehog tabs
+* Precise + Toggle hedgehog tags: Change visible hedgehog tags (team name/hog name/health)
+* Switch + Toggle hedgehog tags: Toggle hedgehog tag translucency
 * Precise + Toggle team bars + Switch: Toggle HUD
-* Precise + Screenshot: Save current map + mask in Screenshot directory
+* Precise + Capture (screenshot key): Save current map + mask into Screenshot directory
 
 Installation instructions
 -------------------------
@@ -112,7 +116,7 @@
 images and sounds are distributed under the terms of the GNU Free Documentation
 Licence version 1.2. See the `COPYING` file for the full text of the licenses.
 
-Copyright 2004-2015 Andrey Korotaev <unC0Rr@gmail.com> and others.
+Copyright 2004-2018 Andrey Korotaev <unC0Rr@gmail.com> and others.
 See `QTfrontend/res/html/about.html` and `CREDITS` for a more complete list of
 authors.
 
--- a/bin/CMakeLists.txt	Sun Jun 10 18:56:51 2018 +0200
+++ b/bin/CMakeLists.txt	Sun Jun 10 19:12:26 2018 +0200
@@ -1,9 +1,6 @@
 if(WIN32 AND NOT UNIX)
-    file(GLOB DLLs *.dll)
-    file(GLOB ICOs *.ico)
-
-    install(FILES
-        ${DLLs}
-        ${ICOs}
-        DESTINATION ${target_library_install_dir})
+    install(DIRECTORY .
+        DESTINATION ${target_library_install_dir}
+		FILES_MATCHING PATTERN "*.dll" PATTERN "*.ico" 
+		)
 endif(WIN32 AND NOT UNIX)
--- a/cmake_modules/CheckHaskellModuleExists.cmake	Sun Jun 10 18:56:51 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,55 +0,0 @@
-# Based on CheckLibraryExists.cmake from CMake
-#=============================================================================
-# Copyright 2002-2009 Kitware, Inc.
-#
-# Distributed under the OSI-approved BSD License
-#
-# 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.
-#=============================================================================
-
-macro(CHECK_HASKELL_MODULE_EXISTS MODULE FUNCTION PARAMCOUNT LIBRARY)
-  set(VARIABLE "HS_MODULE_${LIBRARY}_${FUNCTION}")
-  if(${VARIABLE} MATCHES ^${VARIABLE}$)
-    message(STATUS "Looking for ${FUNCTION} in ${MODULE}")
-
-    set(PARAMETERS "")
-
-    if(PARAMCOUNT GREATER 0)
-        foreach(__TRASH__ RANGE 1 ${PARAMCOUNT})
-            set(PARAMETERS "${PARAMETERS} undefined")
-        endforeach()
-    endif()
-
-    #set(PARAMETERS "")
-
-    execute_process(COMMAND ${GHC_EXECUTABLE}
-                    "-DMODULE=${MODULE}"
-                    "-DFUNCTION=${FUNCTION}"
-                    "-DPARAMETERS=${PARAMETERS}"
-                    -hide-all-packages
-                    -package ${LIBRARY}
-                    -package base
-                    -cpp
-                    -c "${CMAKE_MODULE_PATH}/checkModule.hs"
-                    RESULT_VARIABLE COMMAND_RESULT
-                    ERROR_VARIABLE BUILD_ERROR
-                    OUTPUT_STRIP_TRAILING_WHITESPACE
-                    )
-    if(${COMMAND_RESULT} EQUAL 0)
-      message(STATUS "Looking for ${FUNCTION} in ${MODULE} - found")
-      set(${VARIABLE} 1 CACHE INTERNAL "Have module ${MODULE}")
-      file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log
-        "Determining if the function ${FUNCTION} exists in the ${MODULE} passed\n\n")
-    else()
-      message(STATUS "Looking for ${FUNCTION} in ${MODULE} - not found")
-      set(${VARIABLE} "" CACHE INTERNAL "Have module ${MODULE}")
-      file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log
-        "Determining if the function ${FUNCTION} exists in the ${MODULE} "
-        "failed with the following output:\n"
-        "${BUILD_ERROR}\n\n")
-      message(FATAL_ERROR "Haskell library '${LIBRARY}' required")
-    endif()
-  endif()
-endmacro()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cmake_modules/CheckHaskellPackageExists.cmake	Sun Jun 10 19:12:26 2018 +0200
@@ -0,0 +1,47 @@
+# Checks if a given Haskell package exists (using ghc-pkg)
+# and fails if it's missing.
+# Loosely based on CheckLibraryExists.cmake from CMake.
+#=============================================================================
+# Copyright 2002-2009 Kitware, Inc.
+#
+# Distributed under the OSI-approved BSD License
+#
+# 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.
+#=============================================================================
+
+macro(CHECK_HASKELL_PACKAGE_EXISTS PACKAGE MODULE FUNCTION PARAMCOUNT)
+# NOTE: MODULE, FUNCTION and PARAMCOUNT are curretly ignored.
+# TODO: Either implement these or drop?
+
+  set(VARIABLE "HS_PACKAGE_${PACKAGE}")
+  if(NOT (${VARIABLE} EQUAL "1"))
+    message(STATUS "Looking for Haskell package ${PACKAGE} ...")
+
+    execute_process(COMMAND ${GHC_PKG_EXECUTABLE}
+                    "latest"
+                    ${PACKAGE}
+                    "--simple-output"
+                    RESULT_VARIABLE COMMAND_RESULT
+                    ERROR_VARIABLE BUILD_ERROR
+                    OUTPUT_STRIP_TRAILING_WHITESPACE
+                    OUTPUT_QUIET
+                    )
+
+    if(${COMMAND_RESULT} EQUAL 0)
+      message(STATUS "Looking for Haskell package ${PACKAGE} - found")
+      set(${VARIABLE} "1" CACHE INTERNAL "Have package ${PACKAGE}")
+      file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log
+        "Determining if the Haskell package ${PACKAGE} exists has passed\n\n")
+    else()
+      message(STATUS "Looking for Haskell package ${PACKAGE} - not found")
+      set(${VARIABLE} "0" CACHE INTERNAL "Have package ${PACKAGE}")
+      file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log
+        "Determining if the Haskell package ${PACKAGE} "
+        "exists failed with the following output:\n"
+        "${BUILD_ERROR}\n\n")
+      message(FATAL_ERROR "Haskell package '${PACKAGE}' required")
+    endif()
+  endif()
+endmacro()
--- a/cmake_modules/FindGHC.cmake	Sun Jun 10 18:56:51 2018 +0200
+++ b/cmake_modules/FindGHC.cmake	Sun Jun 10 19:12:26 2018 +0200
@@ -1,9 +1,10 @@
 # - 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
+#  GHC_VERSION        - GHC version
+#  GHC_EXECUTABLE     - GHC executable
+#  GHC_PKG_VERSION    - ghc-pkg version
+#  GHC_PKG_EXECUTABLE - ghc-pkg executable
 #
 # Copyright (c) 2013, Vittorio Giovara <vittorio.giovara@gmail.com>
 #
@@ -16,6 +17,11 @@
     PATHS /opt/local/bin /usr/local/bin /usr/bin
     )
 
+find_program(GHC_PKG_EXECUTABLE
+    NAMES ghc-pkg
+    PATHS /opt/local/bin /usr/local/bin /usr/bin
+    )
+
 if (GHC_EXECUTABLE)
     # check ghc version
     execute_process(COMMAND ${GHC_EXECUTABLE} -V
@@ -32,6 +38,22 @@
     endif()
 endif()
 
+if (GHC_PKG_EXECUTABLE)
+    # check ghc-pkg version
+    execute_process(COMMAND ${GHC_PKG_EXECUTABLE} -V
+                    OUTPUT_VARIABLE GHC_PKG_VERSION_OUTPUT
+                    ERROR_VARIABLE GHC_PKG_VERSION_ERROR
+                    RESULT_VARIABLE GHC_PKG_VERSION_RESULT
+                    OUTPUT_STRIP_TRAILING_WHITESPACE
+                    )
+
+    if(${GHC_PKG_VERSION_RESULT} EQUAL 0)
+        string(REGEX MATCH "([0-9]+)" GHC_PKG_VERSION ${GHC_PKG_VERSION_OUTPUT})
+    else()
+        message(SEND_ERROR "Command \"${GHC_PKG_EXECUTABLE} -V\" failed with output: ${GHC_PKG_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/cpackvars.cmake	Sun Jun 10 18:56:51 2018 +0200
+++ b/cmake_modules/cpackvars.cmake	Sun Jun 10 19:12:26 2018 +0200
@@ -71,7 +71,7 @@
     "hwengine\\\\.desktop$"
     "Info\\\\.plist$"
     #qt extra files
-    "moc_.*\\\\.cxx_parameters"
+    "moc_.*"
     "\\\\.qrc.depends$"
     "\\\\.qm$"
     #other cmake generated files
--- a/gameServer/CMakeLists.txt	Sun Jun 10 18:56:51 2018 +0200
+++ b/gameServer/CMakeLists.txt	Sun Jun 10 19:12:26 2018 +0200
@@ -3,27 +3,25 @@
 
 find_package_or_disable(GHC NOSERVER)
 
-include(${CMAKE_MODULE_PATH}/CheckHaskellModuleExists.cmake)
+include(${CMAKE_MODULE_PATH}/CheckHaskellPackageExists.cmake)
 
-check_haskell_module_exists("Control.Exception" mask 1 base)
-check_haskell_module_exists("Data.Map" size 1 containers)
-check_haskell_module_exists("Data.Vector" length 1 vector)
-check_haskell_module_exists("Data.ByteString" pack 1 bytestring)
-check_haskell_module_exists("Network.BSD" getHostName 0 network)
-check_haskell_module_exists("Data.Time" getCurrentTime 0 time)
-check_haskell_module_exists("Control.Monad.State" fix 1 mtl)
-check_haskell_module_exists("Codec.Binary.Base64" encode 1 sandi)
-check_haskell_module_exists("System.Log.Logger" warningM 1 hslogger)
-check_haskell_module_exists("System.Process" createProcess 3 process)
-check_haskell_module_exists("Data.ByteString.Lazy.UTF8" decode 1 utf8-string)
-check_haskell_module_exists("Data.Digest.Pure.SHA" sha1 1 SHA)
-check_haskell_module_exists("System.Entropy" openHandle 0 entropy)
-check_haskell_module_exists("Codec.Compression.Zlib" decompress 1 zlib)
-check_haskell_module_exists("System.Random" getStdGen 0 random)
-check_haskell_module_exists("Text.Regex.TDFA.ByteString" execute 2 regex-tdfa)
-
-# this one needs type signatures to work
-# check_haskell_module_exists("Control.DeepSeq" deepseq 2 deepseq)
+check_haskell_package_exists(base "Control.Exception" mask 1)
+check_haskell_package_exists(containers "Data.Map" size 1)
+check_haskell_package_exists(vector "Data.Vector" length 1)
+check_haskell_package_exists(bytestring "Data.ByteString" pack 1)
+check_haskell_package_exists(network "Network.BSD" getHostName 0)
+check_haskell_package_exists(time "Data.Time" getCurrentTime 0)
+check_haskell_package_exists(mtl "Control.Monad.State" fix 1)
+check_haskell_package_exists(sandi "Codec.Binary.Base64" encode 1)
+check_haskell_package_exists(hslogger "System.Log.Logger" warningM 1)
+check_haskell_package_exists(process "System.Process" createProcess 3)
+check_haskell_package_exists(utf8-string "Data.ByteString.Lazy.UTF8" decode 1)
+check_haskell_package_exists(SHA "Data.Digest.Pure.SHA" sha1 1)
+check_haskell_package_exists(entropy "System.Entropy" openHandle 0)
+check_haskell_package_exists(zlib "Codec.Compression.Zlib" decompress 1)
+check_haskell_package_exists(random "System.Random" getStdGen 0)
+check_haskell_package_exists(regex-tdfa "Text.Regex.TDFA.ByteString" execute 2)
+check_haskell_package_exists(deepseq "Control.DeepSeq" deepseq 2)
 
 
 
--- a/gameServer/CoreTypes.hs	Sun Jun 10 18:56:51 2018 +0200
+++ b/gameServer/CoreTypes.hs	Sun Jun 10 19:12:26 2018 +0200
@@ -311,8 +311,8 @@
         True
         False
         "<h2><p align=center><a href=\"http://www.hedgewars.org/\">http://www.hedgewars.org/</a></p></h2>"
-        "<font color=yellow><h3 align=center>Hedgewars 0.9.23 is out! Please update.</h3><p align=center><a href=http://hedgewars.org/download.html>Download page here</a></font>"
-        53 -- latestReleaseVersion
+        "<font color=yellow><h3 align=center>Hedgewars 0.9.24 is out! Please update.</h3><p align=center><a href=http://hedgewars.org/download.html>Download page here</a></font>"
+        55 -- latestReleaseVersion
         41 -- earliestCompatibleVersion
         46631
         ""
--- a/gameServer/HWProtoInRoomState.hs	Sun Jun 10 18:56:51 2018 +0200
+++ b/gameServer/HWProtoInRoomState.hs	Sun Jun 10 19:12:26 2018 +0200
@@ -57,7 +57,7 @@
                 , ModifyRoomClients (\c -> c{isInGame = True, teamIndexes = map snd . filter (\(t, _) -> teamowner t == nick c) $ zip (teams rm) [0..]})
                 ]
             else
-            return [Warning $ loc "Less than two clans!"]
+            return [Warning $ loc "The game can't be started with less than two clans!"]
         else
         return []
     where
@@ -77,20 +77,20 @@
 
 
 handleCmd_inRoom ("CFG" : paramName : paramStrs)
-    | null paramStrs = return [ProtocolError $ loc "Empty config entry"]
+    | null paramStrs = return [ProtocolError $ loc "Empty config entry."]
     | otherwise = do
         chans <- roomOthersChans
         cl <- thisClient
         rm <- thisRoom
 
         if isSpecial rm then
-            return [Warning $ loc "Restricted"]
+            return [Warning $ loc "Access denied."]
         else if isMaster cl then
            return [
                 ModifyRoom $ f (clientProto cl),
                 AnswerClients chans ("CFG" : paramName : paramStrs)]
             else
-            return [ProtocolError $ loc "Not room master"]
+            return [ProtocolError $ loc "You're not the room master!"]
     where
         f clproto r = if paramName `Map.member` (mapParams r) then
                 r{mapParams = Map.insert paramName (head paramStrs) (mapParams r)}
@@ -103,7 +103,7 @@
 
 
 handleCmd_inRoom ("ADD_TEAM" : tName : color : grave : fort : voicepack : flag : difStr : hhsInfo)
-    | length hhsInfo /= 16 = return [ProtocolError $ loc "Corrupted hedgehogs info"]
+    | length hhsInfo /= 16 = return [ProtocolError $ loc "Corrupted hedgehogs info!"]
     | otherwise = do
         rm <- thisRoom
         cl <- thisClient
@@ -125,15 +125,15 @@
         let newTeam = clNick `seq` TeamInfo clNick tName teamColor grave fort voicepack flag (isRegistered cl) dif hhNum (hhsList hhsInfo)
         return $
             if not . null . drop (teamsNumberLimit rm - 1) $ roomTeams then
-                [Warning $ loc "too many teams"]
+                [Warning $ loc "Too many teams!"]
             else if canAddNumber roomTeams <= 0 then
-                [Warning $ loc "too many hedgehogs"]
+                [Warning $ loc "Too many hedgehogs!"]
             else if isJust $ findTeam rm then
-                [Warning $ loc "There's already a team with same name in the list"]
+                [Warning $ loc "There's already a team with same name in the list."]
             else if isJust $ gameInfo rm then
-                [Warning $ loc "round in progress"]
+                [Warning $ loc "Joining not possible: Round is in progress."]
             else if isRestrictedTeams rm then
-                [Warning $ loc "restricted"]
+                [Warning $ loc "This room currently does not allow adding new teams."]
             else
                 [ModifyRoom (\r -> r{teams = teams r ++ [newTeam]}),
                 SendUpdateOnThisRoom,
@@ -166,9 +166,9 @@
 
         return $
             if isNothing $ maybeTeam then
-                [Warning $ loc "REMOVE_TEAM: no such team"]
+                [Warning $ loc "Error: The team you tried to remove does not exist."]
             else if clNick /= teamowner team then
-                [ProtocolError $ loc "Not team owner!"]
+                [ProtocolError $ loc "You can't remove a team you don't own."]
             else
                 [RemoveTeam tName,
                 ModifyClient
@@ -193,7 +193,7 @@
 
     return $
         if not $ isMaster cl then
-            [ProtocolError $ loc "Not room master"]
+            [ProtocolError $ loc "You're not the room master!"]
         else if isNothing maybeTeam then
             []
         else if hhNumber < 1 || hhNumber > 8 || hhNumber > canAddNumber r + hhnum team then
@@ -220,7 +220,7 @@
 
     return $
         if not $ isMaster cl then
-            [ProtocolError $ loc "Not room master"]
+            [ProtocolError $ loc "You're not the room master!"]
         else if isNothing maybeTeam || isNothing maybeClientId then
             []
         else
@@ -314,13 +314,13 @@
 
     return $
         if illegalName newName then
-            [Warning $ loc "Illegal room name"]
+            [Warning $ loc "Illegal room name! The room name must be between 1-40 characters long, must not have a trailing or leading space and must not have any of these characters: $()*+?[]^{|}"]
         else
         if isSpecial rm then
-            [Warning $ loc "Restricted"]
+            [Warning $ loc "Access denied."]
         else
         if isJust $ find (\r -> newName == name r) rs then
-            [Warning $ loc "Room with such name already exists"]
+            [Warning $ loc "A room with the same name already exists."]
         else
             [ModifyRoom roomUpdate,
             AnswerClients chans ("ROOM" : "UPD" : name rm : roomInfo (clientProto cl) (nick cl) (roomUpdate rm))]
@@ -418,7 +418,7 @@
 
 handleCmd_inRoom ["CALLVOTE", "KICK"] = do
     cl <- thisClient
-    return [AnswerClients [sendChan cl] ["CHAT", "[server]", loc "callvote kick: specify nickname"]]
+    return [AnswerClients [sendChan cl] ["CHAT", "[server]", loc "/callvote kick: You need to specify a nickname."]]
 
 handleCmd_inRoom ["CALLVOTE", "KICK", nickname] = do
     (thisClientId, rnc) <- ask
@@ -434,7 +434,7 @@
         if isJust maybeClientId && sameRoom then
             startVote $ VoteKick nickname
             else
-            return [AnswerClients [sendChan cl] ["CHAT", "[server]", loc "callvote kick: no such user"]]
+            return [AnswerClients [sendChan cl] ["CHAT", "[server]", loc "/callvote kick: No such user!"]]
 
 
 handleCmd_inRoom ["CALLVOTE", "MAP"] = do
@@ -450,7 +450,7 @@
     if Map.member roomSave $ roomSaves rm then
         startVote $ VoteMap roomSave
         else
-        return [AnswerClients [sendChan cl] ["CHAT", "[server]", loc "callvote map: no such map"]]
+        return [AnswerClients [sendChan cl] ["CHAT", "[server]", loc "/callvote map: No such map!"]]
 
 
 handleCmd_inRoom ["CALLVOTE", "PAUSE"] = do
@@ -460,7 +460,7 @@
     if isJust $ gameInfo rm then
         startVote VotePause
         else 
-        return [AnswerClients [sendChan cl] ["CHAT", "[server]", loc "callvote pause: no game in progress"]]
+        return [AnswerClients [sendChan cl] ["CHAT", "[server]", loc "/callvote pause: No game in progress!"]]
 
 
 handleCmd_inRoom ["CALLVOTE", "NEWSEED"] = do
@@ -469,7 +469,7 @@
 
 handleCmd_inRoom ["CALLVOTE", "HEDGEHOGS"] = do
     cl <- thisClient
-    return [AnswerClients [sendChan cl] ["CHAT", "[server]", loc "callvote hedgehogs: specify number from 1 to 8"]]
+    return [AnswerClients [sendChan cl] ["CHAT", "[server]", loc "/callvote hedgehogs: Specify number from 1 to 8."]]
 
 
 handleCmd_inRoom ["CALLVOTE", "HEDGEHOGS", hhs] = do
@@ -479,7 +479,7 @@
     if h > 0 && h <= 8 then
         startVote $ VoteHedgehogsPerTeam h
         else
-        return [AnswerClients [sendChan cl] ["CHAT", "[server]", loc "callvote hedgehogs: specify number from 1 to 8"]]
+        return [AnswerClients [sendChan cl] ["CHAT", "[server]", loc "/callvote hedgehogs: Specify number from 1 to 8."]]
 
 
 handleCmd_inRoom ("VOTE" : m : p) = do
@@ -488,7 +488,7 @@
     if isJust b then
         voted (p == ["FORCE"]) (fromJust b)
         else
-        return [AnswerClients [sendChan cl] ["CHAT", "[server]", "vote: 'yes' or 'no'"]]
+        return [AnswerClients [sendChan cl] ["CHAT", "[server]", "/vote: Please use 'yes' or 'no'."]]
 
 
 handleCmd_inRoom ["SAVE", stateName, location] = serverAdminOnly $ do
--- a/gameServer/HWProtoLobbyState.hs	Sun Jun 10 18:56:51 2018 +0200
+++ b/gameServer/HWProtoLobbyState.hs	Sun Jun 10 19:12:26 2018 +0200
@@ -49,12 +49,12 @@
     return [AnswerClients s ["CHAT", n, msg], RegisterEvent LobbyChatMessage]
 
 handleCmd_lobby ["CREATE_ROOM", rName, roomPassword]
-    | illegalName rName = return [Warning $ loc "Illegal room name"]
+    | illegalName rName = return [Warning $ loc "Illegal room name! A room name must be between 1-40 characters long, must not have a trailing or leading space and must not have any of these characters: $()*+?[]^{|}"]
     | otherwise = do
         rs <- allRoomInfos
         cl <- thisClient
         return $ if isJust $ find (\r -> rName == name r) rs then
-            [Warning "Room exists"]
+            [Warning $ loc "A room with the same name already exists."]
             else
             [
                 AddRoom rName roomPassword
@@ -90,15 +90,15 @@
     let clTeamsNames = map teamname clTeams
     return $
         if isNothing maybeRI then
-            [Warning $ loc "No such room"]
+            [Warning $ loc "No such room."]
             else if (not sameProto) && (not $ isAdministrator cl) then
-            [Warning $ loc "Room version incompatible to your hedgewars version"]
+            [Warning $ loc "Room version incompatible to your Hedgewars version!"]
             else if isRestrictedJoins jRoom && not (hasSuperPower cl) then
-            [Warning $ loc "Joining restricted"]
+            [Warning $ loc "Access denied. This room currently doesn't allow joining."]
             else if isRegisteredOnly jRoom && (not $ isRegistered cl) && not (isAdministrator cl) then
-            [Warning $ loc "Registered users only"]
+            [Warning $ loc "Access denied. This room is for registered users only."]
             else if isBanned then
-            [Warning $ loc "You are banned in this room"]
+            [Warning $ loc "You are banned from this room."]
             else if roomPassword /= password jRoom  && not (hasSuperPower cl) then
             [NoticeMessage WrongPassword]
             else
--- a/gameServer/HWProtoNEState.hs	Sun Jun 10 18:56:51 2018 +0200
+++ b/gameServer/HWProtoNEState.hs	Sun Jun 10 19:12:26 2018 +0200
@@ -34,9 +34,9 @@
 handleCmd_NotEntered ["NICK", newNick] = do
     (ci, irnc) <- ask
     let cl = irnc `client` ci
-    if not . B.null $ nick cl then return [ProtocolError $ loc "Nickname already chosen"]
+    if not . B.null $ nick cl then return [ProtocolError $ loc "Nickname already provided."]
         else
-        if illegalName newNick then return [ByeClient $ loc "Illegal nickname"]
+        if illegalName newNick then return [ByeClient $ loc "Illegal nickname! Nicknames must be between 1-40 characters long, must not have a trailing or leading space and must not have any of these characters: $()*+?[]^{|}"]
             else
             return $
                 ModifyClient (\c -> c{nick = newNick}) :
@@ -46,9 +46,9 @@
 handleCmd_NotEntered ["PROTO", protoNum] = do
     (ci, irnc) <- ask
     let cl = irnc `client` ci
-    if clientProto cl > 0 then return [ProtocolError $ loc "Protocol already known"]
+    if clientProto cl > 0 then return [ProtocolError $ loc "Protocol already known."]
         else
-        if parsedProto == 0 then return [ProtocolError $ loc "Bad number"]
+        if parsedProto == 0 then return [ProtocolError $ loc "Bad number."]
             else
             return $
                 ModifyClient (\c -> c{clientProto = parsedProto}) :
@@ -90,7 +90,7 @@
     (ci, irnc) <- ask
     let cl = irnc `client` ci
 
-    if parsedProto == 0 then return [ProtocolError $ loc "Bad number"]
+    if parsedProto == 0 then return [ProtocolError $ loc "Bad number."]
         else
         return $ [
             ModifyClient (\c -> c{clientProto = parsedProto, nick = newNick, webPassword = password, isChecker = True})
--- a/gameServer/Utils.hs	Sun Jun 10 18:56:51 2018 +0200
+++ b/gameServer/Utils.hs	Sun Jun 10 19:12:26 2018 +0200
@@ -72,6 +72,7 @@
         else
             t : replaceTeam tm ts
 
+-- NOTE: Don't forget to update the error messages when you change the naming rules!
 illegalName :: B.ByteString -> Bool
 illegalName b = B.null b || length s > 40 || all isSpace s || isSpace (head s) || isSpace (last s) || any isIllegalChar s
     where
@@ -118,6 +119,8 @@
             , (52, "0.9.23-dev")
             , (53, "0.9.23")
             , (54, "0.9.24-dev")
+            , (55, "0.9.24")
+            , (56, "0.9.25-dev")
             ]
 
 askFromConsole :: B.ByteString -> IO B.ByteString
--- a/gameServer/Votes.hs	Sun Jun 10 18:56:51 2018 +0200
+++ b/gameServer/Votes.hs	Sun Jun 10 19:12:26 2018 +0200
@@ -42,16 +42,16 @@
 
     case voting rm of
         Nothing -> 
-            return [AnswerClients [sendChan cl] ["CHAT", "[server]", loc "There's no voting going on"]]
+            return [AnswerClients [sendChan cl] ["CHAT", "[server]", loc "There's no voting going on."]]
         Just voting ->
             if (not forced) && (uid `L.notElem` entitledToVote voting) then
                 return []
             else if (not forced) && (uid `L.elem` map fst (votes voting)) then
-                return [AnswerClients [sendChan cl] ["CHAT", "[server]", loc "You already have voted"]]
+                return [AnswerClients [sendChan cl] ["CHAT", "[server]", loc "You already have voted."]]
             else if forced && (not $ isAdministrator cl) then
                 return []
             else
-                ((:) (AnswerClients [sendChan cl] ["CHAT", "[server]", loc "Your vote counted"]))
+                ((:) (AnswerClients [sendChan cl] ["CHAT", "[server]", loc "Your vote has been counted."]))
                 <$> (actOnVoting $ voting{votes = (uid, vote):votes voting})
 
     where
@@ -73,7 +73,7 @@
     closeVoting = do
         chans <- roomClientsChans
         return [
-            AnswerClients chans ["CHAT", "[server]", loc "Voting closed"]
+            AnswerClients chans ["CHAT", "[server]", loc "Voting closed."]
             , ModifyRoom (\r -> r{voting = Nothing})
             ]
 
@@ -108,7 +108,7 @@
         chans <- roomClientsChans
         let modifyGameInfo f room  = room{gameInfo = fmap f $ gameInfo room}
         return [ModifyRoom (modifyGameInfo $ \g -> g{isPaused = not $ isPaused g}),
-                AnswerClients chans ["CHAT", "[server]", loc "Pause toggled"],
+                AnswerClients chans ["CHAT", "[server]", loc "Pause toggled."],
                 AnswerClients chans ["EM", toEngineMsg "I"]]
     act (VoteNewSeed) =
         return [SetRandomSeed]
@@ -162,7 +162,7 @@
                      modifyRoom rnc (\r -> r{voting = if voteTTL rv == 0 then Nothing else Just rv{voteTTL = voteTTL rv - 1}}) ri
                      if voteTTL rv == 0 then do
                         chans <- liftM (map sendChan) $ roomClientsM rnc ri
-                        return [AnswerClients chans ["CHAT", "[server]", loc "Voting expired"]]
+                        return [AnswerClients chans ["CHAT", "[server]", loc "Voting expired."]]
                         else
                         return []
                  Nothing -> return []
@@ -173,4 +173,4 @@
 voteInfo (VoteMap n) = B.concat [loc "map", " ", n]
 voteInfo (VotePause) = B.concat [loc "pause"]
 voteInfo (VoteNewSeed) = B.concat [loc "new seed"]
-voteInfo (VoteHedgehogsPerTeam i) = B.concat [loc "number of hedgehogs in team", " ", showB i]
+voteInfo (VoteHedgehogsPerTeam i) = B.concat [loc "hedgehogs per team: ", " ", showB i]
--- a/gameServer2/Cargo.toml	Sun Jun 10 18:56:51 2018 +0200
+++ b/gameServer2/Cargo.toml	Sun Jun 10 19:12:26 2018 +0200
@@ -11,3 +11,4 @@
 nom = "3.2"
 env_logger = "0.4"
 log = "0.3.8"
+proptest = "0.5.1"
--- a/gameServer2/src/main.rs	Sun Jun 10 18:56:51 2018 +0200
+++ b/gameServer2/src/main.rs	Sun Jun 10 19:12:26 2018 +0200
@@ -1,3 +1,5 @@
+#![allow(unused_imports)]
+
 extern crate rand;
 extern crate mio;
 extern crate slab;
@@ -7,17 +9,20 @@
 #[macro_use]
 extern crate log;
 extern crate env_logger;
+#[macro_use] extern crate proptest;
 
 //use std::io::*;
 //use rand::Rng;
 //use std::cmp::Ordering;
-use mio::tcp::*;
+use mio::net::*;
 use mio::*;
 
 mod utils;
 mod server;
 mod protocol;
 
+use server::network::NetworkLayer;
+
 fn main() {
     env_logger::init().unwrap();
 
@@ -25,10 +30,10 @@
 
     let address = "0.0.0.0:46631".parse().unwrap();
     let listener = TcpListener::bind(&address).unwrap();
-    let mut server = server::server::HWServer::new(listener, 1024, 512);
 
     let poll = Poll::new().unwrap();
-    server.register(&poll).unwrap();
+    let mut hw_network = NetworkLayer::new(listener, 1024, 512);
+    hw_network.register_server(&poll).unwrap();
 
     let mut events = Events::with_capacity(1024);
 
@@ -36,24 +41,24 @@
         poll.poll(&mut events, None).unwrap();
 
         for event in events.iter() {
-            if event.kind().is_readable() {
+            if event.readiness() & Ready::readable() == Ready::readable() {
                 match event.token() {
-                    utils::SERVER => server.accept(&poll).unwrap(),
-                    tok => server.client_readable(&poll, tok).unwrap(),
+                    utils::SERVER => hw_network.accept_client(&poll).unwrap(),
+                    Token(tok) => hw_network.client_readable(&poll, tok).unwrap(),
                 }
             }
-            if event.kind().is_writable() {
+            if event.readiness() & Ready::writable() == Ready::writable() {
                 match event.token() {
                     utils::SERVER => unreachable!(),
-                    tok => server.client_writable(&poll, tok).unwrap(),
+                    Token(tok) => hw_network.client_writable(&poll, tok).unwrap(),
                 }
             }
-            if event.kind().is_hup() || event.kind().is_error() {
-                match event.token() {
-                    utils::SERVER => unreachable!(),
-                    tok => server.client_error(&poll, tok).unwrap(),
-                }
-            }
+//            if event.kind().is_hup() || event.kind().is_error() {
+//                match event.token() {
+//                    utils::SERVER => unreachable!(),
+//                    Token(tok) => server.client_error(&poll, tok).unwrap(),
+//                }
+//            }
         }
     }
 }
--- a/gameServer2/src/protocol/messages.rs	Sun Jun 10 18:56:51 2018 +0200
+++ b/gameServer2/src/protocol/messages.rs	Sun Jun 10 19:12:26 2018 +0200
@@ -3,7 +3,7 @@
 use std::ops;
 use std::convert::From;
 
-#[derive(PartialEq, Debug)]
+#[derive(PartialEq, Eq, Clone, Debug)]
 pub enum HWProtocolMessage {
     // core
     Ping,
@@ -69,17 +69,17 @@
     Empty,
 }
 
-pub enum HWServerMessage<'a> {
+pub enum HWServerMessage {
     Ping,
     Pong,
-    Bye(&'a str),
-    Nick(&'a str),
-    LobbyLeft(&'a str),
-    LobbyJoined(&'a [&'a str]),
-    ChatMsg(&'a str, &'a str),
-    ClientFlags(&'a str, &'a [&'a str]),
+    Bye(String),
+    Nick(String),
+    LobbyLeft(String),
+    LobbyJoined(Vec<String>),
+    ChatMsg(String, String),
+    ClientFlags(String, Vec<String>),
 
-    Warning(&'a str),
+    Warning(String),
     Connected(u32),
     Unreachable,
 }
@@ -96,41 +96,117 @@
     m
 }
 
-impl<'a> HWServerMessage<'a> {
+impl<'a> HWProtocolMessage {
     pub fn to_raw_protocol(&self) -> String {
+        use self::HWProtocolMessage::*;
+        match *self {
+            Ping => "PING\n\n".to_string(),
+            Pong => "PONG\n\n".to_string(),
+            Quit(None) => format!("QUIT\n\n"),
+            Quit(Some(ref msg)) => format!("QUIT\n{}\n\n", msg),
+            Global(ref msg) => format!("CMD\nGLOBAL\n{}\n\n", msg),
+            Watch(ref name) => format!("CMD\nWATCH\n{}\n\n", name),
+            ToggleServerRegisteredOnly => "CMD\nREGISTERED_ONLY\n\n".to_string(),
+            SuperPower => "CMD\nSUPER_POWER\n\n".to_string(),
+            Info(ref info) => format!("CMD\nINFO\n{}\n\n", info),
+            Nick(ref nick) => format!("NICK\n{}\n\n", nick),
+            Proto(version) => format!("PROTO\n{}\n\n", version),
+            Password(ref p, ref s) => format!("PASSWORD\n{}\n{}\n\n", p, s), //?
+            Checker(i, ref n, ref p) =>
+                format!("CHECKER\n{}\n{}\n{}\n\n", i, n, p), //?,
+            List => "LIST\n\n".to_string(),
+            Chat(ref msg) => format!("CHAT\n{}\n\n", msg),
+            CreateRoom(ref name, None) =>
+                format!("CREATE_ROOM\n{}\n\n", name),
+            CreateRoom(ref name, Some(ref password)) =>
+                format!("CREATE_ROOM\n{}\n{}\n\n", name, password),
+            Join(ref name, None) =>
+                format!("JOIN\n{}\n\n", name),
+            Join(ref name, Some(ref arg)) =>
+                format!("JOIN\n{}\n{}\n\n", name, arg),
+            Follow(ref name) =>
+                format!("FOLLOW\n{}\n\n", name),
+            //Rnd(Vec<String>), ???
+            Kick(ref name) => format!("KICK\n{}\n\n", name),
+            Ban(ref name, ref reason, time) =>
+                format!("BAN\n{}\n{}\n{}\n\n", name, reason, time),
+            BanIP(ref ip, ref reason, time) =>
+                format!("BAN_IP\n{}\n{}\n{}\n\n", ip, reason, time),
+            BanNick(ref nick, ref reason, time) =>
+                format!("BAN_NICK\n{}\n{}\n{}\n\n", nick, reason, time),
+            BanList => "BANLIST\n\n".to_string(),
+            Unban(ref name) => format!("UNBAN\n{}\n\n", name),
+            //SetServerVar(ServerVar), ???
+            GetServerVar => "GET_SERVER_VAR\n\n".to_string(),
+            RestartServer => "CMD\nRESTART_SERVER\nYES\n\n".to_string(),
+            Stats => "CMD\nSTATS\n\n".to_string(),
+            Part(None) => "CMD\nPART\n\n".to_string(),
+            Part(Some(ref msg)) => format!("CMD\nPART\n{}\n\n", msg),
+            //Cfg(GameCfg) ??
+            //AddTeam(TeamInfo) ??,
+            RemoveTeam(ref name) => format!("REMOVE_TEAM\n{}\n\n", name),
+            //SetHedgehogsNumber(String, u8), ??
+            //SetTeamColor(String, u8), ??
+            ToggleReady => "TOGGLE_READY\n\n".to_string(),
+            StartGame => "START_GAME\n\n".to_string(),
+            EngineMessage(ref msg) => format!("EM\n{}\n\n", msg),
+            RoundFinished => "ROUNDFINISHED\n\n".to_string(),
+            ToggleRestrictJoin => "TOGGLE_RESTRICT_JOINS\n\n".to_string(),
+            ToggleRestrictTeams => "TOGGLE_RESTRICT_TEAMS\n\n".to_string(),
+            ToggleRegisteredOnly => "TOGGLE_REGISTERED_ONLY\n\n".to_string(),
+            RoomName(ref name) => format!("ROOM_NAME\n{}\n\n", name),
+            Delegate(ref name) => format!("CMD\nDELEGATE\n{}\n\n", name),
+            TeamChat(ref msg) => format!("TEAMCHAT\n{}\n\n", msg),
+            MaxTeams(count) => format!("CMD\nMAXTEAMS\n{}\n\n", count) ,
+            Fix => "CMD\nFIX\n\n".to_string(),
+            Unfix => "CMD\nUNFIX\n\n".to_string(),
+            Greeting(ref msg) => format!("CMD\nGREETING\n{}\n\n", msg),
+            //CallVote(Option<(String, Option<String>)>) =>, ??
+            Vote(ref msg) => format!("CMD\nVOTE\n{}\n\n", msg),
+            ForceVote(ref msg) => format!("CMD\nFORCE\n{}\n\n", msg),
+            //Save(String, String), ??
+            Delete(ref room) => format!("CMD\nDELETE\n{}\n\n", room),
+            SaveRoom(ref room) => format!("CMD\nSAVEROOM\n{}\n\n", room),
+            LoadRoom(ref room) => format!("CMD\nLOADROOM\n{}\n\n", room),
+            Malformed => "A\nQUICK\nBROWN\nHOG\nJUMPS\nOVER\nTHE\nLAZY\nDOG\n\n".to_string(),
+            Empty => "\n\n".to_string(),
+            _ => panic!("Protocol message not yet implemented")
+        }
+    }
+}
+
+impl HWServerMessage {
+    pub fn to_raw_protocol(&self) -> String {
+        use self::HWServerMessage::*;
         match self {
-            &HWServerMessage::Ping
-                => "PING\n\n".to_string(),
-            &HWServerMessage::Pong
-                => "PONG\n\n".to_string(),
-            &HWServerMessage::Connected(protocol_version)
+            &Ping => "PING\n\n".to_string(),
+            &Pong => "PONG\n\n".to_string(),
+            &Connected(protocol_version)
                 => construct_message(&[
                     "CONNECTED",
                     "Hedgewars server http://www.hedgewars.org/",
                     &protocol_version.to_string()
                 ]),
-            &HWServerMessage::Bye(msg)
-                => construct_message(&["BYE", &msg]),
-            &HWServerMessage::Nick(nick)
-                => construct_message(&["NICK", &nick]),
-            &HWServerMessage::LobbyLeft(nick)
+            &Bye(ref msg) => construct_message(&["BYE", &msg]),
+            &Nick(ref nick) => construct_message(&["NICK", &nick]),
+            &LobbyLeft(ref nick)
                 => construct_message(&["LOBBY_LEFT", &nick]),
-            &HWServerMessage::LobbyJoined(nicks)
+            &LobbyJoined(ref nicks)
                 => {
                 let mut v = vec!["LOBBY:JOINED"];
-                v.extend_from_slice(nicks);
+                v.extend(nicks.iter().map(|n| { &n[..] }));
                 construct_message(&v)
             },
-            &HWServerMessage::ClientFlags(flags, nicks)
-            => {
+            &ClientFlags(ref flags, ref nicks)
+                => {
                 let mut v = vec!["CLIENT_FLAGS"];
-                v.push(flags);
-                v.extend_from_slice(nicks);
+                v.push(&flags[..]);
+                v.extend(nicks.iter().map(|n| { &n[..] }));
                 construct_message(&v)
             },
-            &HWServerMessage::ChatMsg(nick, msg)
+            &ChatMsg(ref nick, ref msg)
                 => construct_message(&["CHAT", &nick, &msg]),
-            &HWServerMessage::Warning(msg)
+            &Warning(ref msg)
                 => construct_message(&["WARNING", &msg]),
             _ => construct_message(&["ERROR", "UNIMPLEMENTED"]),
         }
--- a/gameServer2/src/protocol/parser.rs	Sun Jun 10 18:56:51 2018 +0200
+++ b/gameServer2/src/protocol/parser.rs	Sun Jun 10 19:12:26 2018 +0200
@@ -5,6 +5,12 @@
 use super::messages::HWProtocolMessage;
 use super::messages::HWProtocolMessage::*;
 
+use proptest::test_runner::{TestRunner, Reason};
+use proptest::arbitrary::{any, any_with, Arbitrary, StrategyFor};
+use proptest::strategy::{Strategy, BoxedStrategy, Just, Filter, ValueTree};
+use proptest::string::RegexGeneratorValueTree;
+use std::ops::Range;
+
 named!(end_of_message, tag!("\n\n"));
 named!(str_line<&[u8],   &str>, map_res!(not_line_ending, str::from_utf8));
 named!(  a_line<&[u8], String>, map!(str_line, String::from));
@@ -50,8 +56,8 @@
     | do_parse!(tag_no_case!("RESTART_SERVER") >> eol >> tag!("YES") >> (RestartServer))
     | do_parse!(tag_no_case!("REGISTERED_ONLY") >> (ToggleServerRegisteredOnly))
     | do_parse!(tag_no_case!("SUPER_POWER")     >> (SuperPower))
-    | do_parse!(tag_no_case!("PART")     >> eol >> m: opt_param >> (Quit(m)))
-    | do_parse!(tag_no_case!("QUIT")     >> eol >> m: opt_param >> (Part(m)))
+    | do_parse!(tag_no_case!("PART")     >> m: opt_param >> (Part(m)))
+    | do_parse!(tag_no_case!("QUIT")     >> m: opt_param >> (Quit(m)))
     | do_parse!(tag_no_case!("DELEGATE") >> eol >> n: a_line  >> (Delegate(n)))
     | do_parse!(tag_no_case!("SAVEROOM") >> eol >> r: a_line  >> (SaveRoom(r)))
     | do_parse!(tag_no_case!("LOADROOM") >> eol >> r: a_line  >> (LoadRoom(r)))
@@ -121,6 +127,138 @@
 
 named!(pub extract_messages<&[u8], Vec<HWProtocolMessage> >, many0!(complete!(message)));
 
+// Due to inability to define From between Options
+trait Into2<T>: Sized { fn into2(self) -> T; }
+impl <T> Into2<T> for T { fn into2(self) -> T { self } }
+impl Into2<String> for Ascii { fn into2(self) -> String { self.0 } }
+impl Into2<Option<String>> for Option<Ascii>{
+    fn into2(self) -> Option<String> { self.map(|x| {x.0}) }
+}
+
+macro_rules! proto_msg_case {
+    ($val: ident()) =>
+        (Just($val));
+    ($val: ident($arg: ty)) =>
+        (any::<$arg>().prop_map(|v| {$val(v.into2())}));
+    ($val: ident($arg1: ty, $arg2: ty)) =>
+        (any::<($arg1, $arg2)>().prop_map(|v| {$val(v.0.into2(), v.1.into2())}));
+    ($val: ident($arg1: ty, $arg2: ty, $arg3: ty)) =>
+        (any::<($arg1, $arg2, $arg3)>().prop_map(|v| {$val(v.0.into2(), v.1.into2(), v.2.into2())}));
+}
+
+macro_rules! proto_msg_match {
+    ($var: expr, def = $default: ident, $($num: expr => $constr: ident $res: tt),*) => (
+        match $var {
+            $($num => (proto_msg_case!($constr $res)).boxed()),*,
+            _ => Just($default).boxed()
+        }
+    )
+}
+
+#[derive(Debug)]
+struct Ascii(String);
+
+struct AsciiValueTree(RegexGeneratorValueTree<String>);
+
+impl ValueTree for AsciiValueTree {
+    type Value = Ascii;
+
+    fn current(&self) -> Self::Value { Ascii(self.0.current()) }
+    fn simplify(&mut self) -> bool { self.0.simplify() }
+    fn complicate(&mut self) -> bool { self.0.complicate() }
+}
+
+impl Arbitrary for Ascii {
+    type Parameters = <String as Arbitrary>::Parameters;
+
+    fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
+        any_with::<String>(args)
+            .prop_filter("not ascii", |s| {
+                s.len() > 0 && s.is_ascii() &&
+                s.find(|c| {
+                    ['\0', '\n', '\x20'].contains(&c)
+                }).is_none()})
+            .prop_map(Ascii)
+            .boxed()
+    }
+
+    type Strategy = BoxedStrategy<Ascii>;
+    type ValueTree = Box<ValueTree<Value = Ascii>>;
+}
+
+fn gen_proto_msg() -> BoxedStrategy<HWProtocolMessage> where {
+    let res = (0..58).no_shrink().prop_flat_map(|i| {
+        proto_msg_match!(i, def = Malformed,
+        0 => Ping(),
+        1 => Pong(),
+        2 => Quit(Option<Ascii>),
+        //3 => Cmd
+        4 => Global(Ascii),
+        5 => Watch(Ascii),
+        6 => ToggleServerRegisteredOnly(),
+        7 => SuperPower(),
+        8 => Info(Ascii),
+        9 => Nick(Ascii),
+        10 => Proto(u32),
+        11 => Password(Ascii, Ascii),
+        12 => Checker(u32, Ascii, Ascii),
+        13 => List(),
+        14 => Chat(Ascii),
+        15 => CreateRoom(Ascii, Option<Ascii>),
+        16 => Join(Ascii, Option<Ascii>),
+        17 => Follow(Ascii),
+        //18 => Rnd(Vec<String>),
+        19 => Kick(Ascii),
+        20 => Ban(Ascii, Ascii, u32),
+        21 => BanIP(Ascii, Ascii, u32),
+        22 => BanNick(Ascii, Ascii, u32),
+        23 => BanList(),
+        24 => Unban(Ascii),
+        //25 => SetServerVar(ServerVar),
+        26 => GetServerVar(),
+        27 => RestartServer(),
+        28 => Stats(),
+        29 => Part(Option<Ascii>),
+        //30 => Cfg(GameCfg),
+        //31 => AddTeam(TeamInfo),
+        32 => RemoveTeam(Ascii),
+        //33 => SetHedgehogsNumber(String, u8),
+        //34 => SetTeamColor(String, u8),
+        35 => ToggleReady(),
+        36 => StartGame(),
+        37 => EngineMessage(Ascii),
+        38 => RoundFinished(),
+        39 => ToggleRestrictJoin(),
+        40 => ToggleRestrictTeams(),
+        41 => ToggleRegisteredOnly(),
+        42 => RoomName(Ascii),
+        43 => Delegate(Ascii),
+        44 => TeamChat(Ascii),
+        45 => MaxTeams(u8),
+        46 => Fix(),
+        47 => Unfix(),
+        48 => Greeting(Ascii),
+        //49 => CallVote(Option<(String, Option<String>)>),
+        50 => Vote(String),
+        51 => ForceVote(Ascii),
+        //52 => Save(String, String),
+        53 => Delete(Ascii),
+        54 => SaveRoom(Ascii),
+        55 => LoadRoom(Ascii),
+        56 => Malformed(),
+        57 => Empty()
+    )});
+    res.boxed()
+}
+
+proptest! {
+    #[test]
+    fn is_parser_composition_idempotent(ref msg in gen_proto_msg()) {
+        println!("!! Msg: {:?}, Bytes: {:?} !!", msg, msg.to_raw_protocol().as_bytes());
+        assert_eq!(message(msg.to_raw_protocol().as_bytes()), IResult::Done(&b""[..], msg.clone()))
+    }
+}
+
 #[test]
 fn parse_test() {
     assert_eq!(message(b"PING\n\n"),          IResult::Done(&b""[..], Ping));
@@ -132,10 +270,13 @@
     assert_eq!(message(b"CMD\nwatch\ndemo\n\n"), IResult::Done(&b""[..], Watch("demo".to_string())));
     assert_eq!(message(b"BAN\nme\nbad\n77\n\n"), IResult::Done(&b""[..], Ban("me".to_string(), "bad".to_string(), 77)));
 
+    assert_eq!(message(b"CMD\nPART\n\n"),      IResult::Done(&b""[..], Part(None)));
+    assert_eq!(message(b"CMD\nPART\n_msg_\n\n"), IResult::Done(&b""[..], Part(Some("_msg_".to_string()))));
+
     assert_eq!(extract_messages(b"QUIT\n1\n2\n\n"),    IResult::Done(&b""[..], vec![Malformed]));
 
     assert_eq!(extract_messages(b"PING\n\nPING\n\nP"), IResult::Done(&b"P"[..], vec![Ping, Ping]));
     assert_eq!(extract_messages(b"SING\n\nPING\n\n"),  IResult::Done(&b""[..],  vec![Malformed, Ping]));
     assert_eq!(extract_messages(b"\n\n\n\nPING\n\n"),  IResult::Done(&b""[..],  vec![Empty, Empty, Ping]));
     assert_eq!(extract_messages(b"\n\n\nPING\n\n"),    IResult::Done(&b""[..],  vec![Empty, Empty, Ping]));
-}
+}
\ No newline at end of file
--- a/gameServer2/src/server/actions.rs	Sun Jun 10 18:56:51 2018 +0200
+++ b/gameServer2/src/server/actions.rs	Sun Jun 10 19:12:26 2018 +0200
@@ -3,14 +3,15 @@
 use std::io;
 
 use super::server::HWServer;
-use super::server::HWRoom;
+use super::room::HWRoom;
 use protocol::messages::HWProtocolMessage;
+use protocol::messages::HWServerMessage;
 use protocol::messages::HWServerMessage::*;
 use super::handlers;
 
 pub enum Action {
-    SendMe(String),
-    SendAllButMe(String),
+    SendMe(HWServerMessage),
+    SendAllButMe(HWServerMessage),
     RemoveClient,
     ByeClient(String),
     ReactProtocolMessage(HWProtocolMessage),
@@ -22,32 +23,30 @@
 
 use self::Action::*;
 
-pub fn run_action(server: &mut HWServer, token: mio::Token, poll: &mio::Poll, action: Action) {
+pub fn run_action(server: &mut HWServer, token: usize, action: Action) {
     match action {
         SendMe(msg) =>
-            server.send(token, &msg),
+            server.send_self(token, msg),
         SendAllButMe(msg) => {
-            for c in server.clients.iter_mut() {
-                if c.id != token {
-                    c.send_string(&msg)
-                }
-            }
+            server.send_others(token, msg)
         },
         ByeClient(msg) => {
-            server.react(token, poll, vec![
-                SendMe(Bye(&msg).to_raw_protocol()),
+            server.react(token, vec![
+                SendMe(Bye(msg)),
                 RemoveClient,
                 ]);
         },
         RemoveClient => {
-            server.clients[token].deregister(poll);
-            server.clients.remove(token);
+            server.removed_clients.push(token);
+            if server.clients.contains(token) {
+                server.clients.remove(token);
+            }
         },
         ReactProtocolMessage(msg) =>
-            handlers::handle(server, token, poll, msg),
+            handlers::handle(server, token, msg),
         CheckRegistered =>
             if server.clients[token].protocol_number > 0 && server.clients[token].nick != "" {
-                server.react(token, poll, vec![
+                server.react(token, vec![
                     JoinLobby,
                     ]);
             },
@@ -56,36 +55,34 @@
 
             let joined_msg;
             {
-                let mut lobby_nicks: Vec<&str> = Vec::new();
-                for c in server.clients.iter() {
+                let mut lobby_nicks = Vec::new();
+                for (_, c) in server.clients.iter() {
                     if c.room_id.is_some() {
-                        lobby_nicks.push(&c.nick);
+                        lobby_nicks.push(c.nick.clone());
                     }
                 }
-                joined_msg = LobbyJoined(&lobby_nicks).to_raw_protocol();
+                joined_msg = LobbyJoined(lobby_nicks);
             }
-            let everyone_msg = LobbyJoined(&[&server.clients[token].nick]).to_raw_protocol();
-            server.react(token, poll, vec![
+            let everyone_msg = LobbyJoined(vec![server.clients[token].nick.clone()]);
+            server.react(token, vec![
                 SendAllButMe(everyone_msg),
                 SendMe(joined_msg),
                 ]);
         },
         AddRoom(name, password) => {
-            let room_id = server.rooms.insert(HWRoom::new()).ok().expect("Cannot add room");
+            let room_id = server.add_room();;
             {
                 let r = &mut server.rooms[room_id];
                 let c = &mut server.clients[token];
                 r.name = name;
                 r.password = password;
-                r.id = room_id.clone();
                 r.ready_players_number = 1;
                 r.protocol_number = c.protocol_number;
                 c.room_id = Some(room_id);
             }
-
         },
         Warn(msg) => {
-            run_action(server, token, poll, SendMe(Warning(&msg).to_raw_protocol()));
+            run_action(server, token,SendMe(Warning(msg)));
         }
         //_ => unimplemented!(),
     }
--- a/gameServer2/src/server/client.rs	Sun Jun 10 18:56:51 2018 +0200
+++ b/gameServer2/src/server/client.rs	Sun Jun 10 19:12:26 2018 +0200
@@ -1,22 +1,8 @@
-use mio::tcp::*;
-use mio::*;
-use std::io::Write;
-use std::io;
-use netbuf;
-
-use utils;
-use protocol::ProtocolDecoder;
-use protocol::messages::*;
-use super::actions::Action::*;
-use super::actions::Action;
+pub type ClientId = usize;
 
 pub struct HWClient {
-    sock: TcpStream,
-    decoder: ProtocolDecoder,
-    buf_out: netbuf::Buf,
-
-    pub id: Token,
-    pub room_id: Option<Token>,
+    pub id: ClientId,
+    pub room_id: Option<usize>,
     pub nick: String,
     pub protocol_number: u32,
     pub is_master: bool,
@@ -25,14 +11,10 @@
 }
 
 impl HWClient {
-    pub fn new(sock: TcpStream) -> HWClient {
+    pub fn new(id: ClientId) -> HWClient {
         HWClient {
-            sock: sock,
-            decoder: ProtocolDecoder::new(),
-            buf_out: netbuf::Buf::new(),
+            id,
             room_id: None,
-            id: Token(0),
-
             nick: String::new(),
             protocol_number: 0,
             is_master: false,
@@ -40,58 +22,4 @@
             is_joined_mid_game: false,
         }
     }
-
-    pub fn register(&mut self, poll: &Poll, token: Token) {
-        poll.register(&self.sock, token, Ready::all(),
-                      PollOpt::edge())
-            .ok().expect("could not register socket with event loop");
-
-        self.send_msg(HWServerMessage::Connected(utils::PROTOCOL_VERSION));
-    }
-
-    pub fn deregister(&mut self, poll: &Poll) {
-        poll.deregister(&self.sock)
-            .ok().expect("could not deregister socket");
-    }
-
-    pub fn send_raw_msg(&mut self, msg: &[u8]) {
-        self.buf_out.write(msg).unwrap();
-        self.flush();
-    }
-
-    pub fn send_string(&mut self, msg: &String) {
-        self.send_raw_msg(&msg.as_bytes());
-    }
-
-    pub fn send_msg(&mut self, msg: HWServerMessage) {
-        self.send_string(&msg.to_raw_protocol());
-    }
-
-    fn flush(&mut self) {
-        self.buf_out.write_to(&mut self.sock).unwrap();
-        self.sock.flush();
-    }
-
-    pub fn readable(&mut self, poll: &Poll) -> Vec<Action> {
-        let v = self.decoder.read_from(&mut self.sock).unwrap();
-        debug!("Read {} bytes", v);
-        let mut response = Vec::new();
-        {
-            for msg in self.decoder.extract_messages() {
-                response.push(ReactProtocolMessage(msg));
-            }
-        }
-        self.decoder.sweep();
-        response
-    }
-
-    pub fn writable(&mut self, poll: &Poll) -> io::Result<()> {
-        self.buf_out.write_to(&mut self.sock)?;
-
-        Ok(())
-    }
-
-    pub fn error(&mut self, poll: &Poll) -> Vec<Action> {
-        return vec![ByeClient("Connection reset".to_string())]
-    }
-}
+}
\ No newline at end of file
--- a/gameServer2/src/server/coretypes.rs	Sun Jun 10 18:56:51 2018 +0200
+++ b/gameServer2/src/server/coretypes.rs	Sun Jun 10 19:12:26 2018 +0200