merge with default qmlfrontend
authorunc0rr
Sat, 07 Feb 2015 23:26:14 +0300
branchqmlfrontend
changeset 10817 48a53259fad8
parent 10757 f71275973737 (current diff)
parent 10816 37410518628e (diff)
child 10819 57e21f7621b0
merge with default
CMakeLists.txt
hedgewars/CMakeLists.txt
hedgewars/uRender.pas
hedgewars/uTypes.pas
hedgewars/uVariables.pas
--- a/.travis.yml	Mon Feb 02 23:12:56 2015 +0300
+++ b/.travis.yml	Sat Feb 07 23:26:14 2015 +0300
@@ -11,13 +11,13 @@
   # Debug build
   - BUILD_ARGS="-DCMAKE_BUILD_TYPE=Debug"
   # Everything that's optional
+  - BUILD_ARGS="-DBUILD_ENGINE_C=1"
   - BUILD_ARGS="-DNOPNG=1"
   - BUILD_ARGS="-DNOVIDEOREC=1"
   - BUILD_ARGS="-DNOSERVER=1"
   - BUILD_ARGS="-DLUA_SYSTEM=0"
   - BUILD_ARGS="-DPHYSFS_SYSTEM=0"
   - BUILD_ARGS="-DGL2=1"
-  - BUILD_ARGS="-DBUILD_ENGINE_C=1"
 matrix:
   allow_failures:
     # Failures we expect here
@@ -27,10 +27,12 @@
 script: 
   - mkdir build && cd build && cmake $BUILD_ARGS .. && make VERBOSE=1 && make test_verbose
 notifications:
+  email: false
   irc:
     channels:
       - "chat.freenode.net#hedgewars"
     template:
       - "hw-build #%{build_number} (%{commit} by %{author}): %{message}"
       - "See details at %{build_url}"
-  email: false
+    on_success: change
+    on_failure: always
--- a/CMakeLists.txt	Mon Feb 02 23:12:56 2015 +0300
+++ b/CMakeLists.txt	Sat Feb 07 23:26:14 2015 +0300
@@ -51,8 +51,8 @@
 #versioning
 set(CPACK_PACKAGE_VERSION_MAJOR 0)
 set(CPACK_PACKAGE_VERSION_MINOR 9)
-set(CPACK_PACKAGE_VERSION_PATCH 21)
-set(HEDGEWARS_PROTO_VER 48)
+set(CPACK_PACKAGE_VERSION_PATCH 22)
+set(HEDGEWARS_PROTO_VER 50)
 set(HEDGEWARS_VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}")
 include(${CMAKE_MODULE_PATH}/revinfo.cmake)
 
@@ -70,12 +70,13 @@
 if(CMAKE_BUILD_TYPE)
     string(TOUPPER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE)
     if(NOT((CMAKE_BUILD_TYPE MATCHES "RELEASE") OR
-           (CMAKE_BUILD_TYPE MATCHES "DEBUG")))
-        set(CMAKE_BUILD_TYPE ${default_build_type} CACHE STRING "Build type (Debug/Release)" FORCE)
-        message(STATUS "Unknown build type, using default (${default_build_type})")
+           (CMAKE_BUILD_TYPE MATCHES "DEBUG") OR
+           (CMAKE_BUILD_TYPE MATCHES "RELWITHDEBINFO")))
+        set(CMAKE_BUILD_TYPE ${default_build_type} CACHE STRING "Build type (Debug/Release/RelWithDebInfo)" FORCE)
+        message(STATUS "Unknown build type ${CMAKE_BUILD_TYPE}, using default (${default_build_type})")
     endif()
 else(CMAKE_BUILD_TYPE)
-    set(CMAKE_BUILD_TYPE ${default_build_type} CACHE STRING "Build type (Debug/Release)" FORCE)
+    set(CMAKE_BUILD_TYPE ${default_build_type} CACHE STRING "Build type (Debug/Release/RelWithDebInfo)" FORCE)
 endif(CMAKE_BUILD_TYPE)
 
 
@@ -232,7 +233,7 @@
 
 enable_testing()
 
-add_custom_target(test         COMMAND ${CMAKE_CTEST_COMMAND} -E '^todo/' --timeout 300 --schedule-random)
+add_custom_target(test_normal  COMMAND ${CMAKE_CTEST_COMMAND} -E '^todo/' --timeout 300 --schedule-random)
 add_custom_target(test_verbose COMMAND ${CMAKE_CTEST_COMMAND} -E '^todo/' --timeout 300 --schedule-random -V)
 
 set(LUATESTS_DIR "${CMAKE_SOURCE_DIR}/tests/lua")
--- a/QTfrontend/CMakeLists.txt	Mon Feb 02 23:12:56 2015 +0300
+++ b/QTfrontend/CMakeLists.txt	Sat Feb 07 23:26:14 2015 +0300
@@ -190,9 +190,9 @@
 
 #when debugging, always prompt a console to see fronted messages
 #TODO: check it doesn't interfere on UNIX
-if(CMAKE_BUILD_TYPE MATCHES "RELEASE")
+if(CMAKE_BUILD_TYPE MATCHES "RELEASE" OR CMAKE_BUILD_TYPE MATCHES "RELWITHDEBINFO")
     set(console_access "WIN32")
-endif(CMAKE_BUILD_TYPE MATCHES "RELEASE")
+endif(CMAKE_BUILD_TYPE MATCHES "RELEASE" OR CMAKE_BUILD_TYPE MATCHES "RELWITHDEBINFO")
 
 add_executable(hedgewars ${console_access}
     ${hwfr_src}
--- a/QTfrontend/hedgewars.qrc	Mon Feb 02 23:12:56 2015 +0300
+++ b/QTfrontend/hedgewars.qrc	Sat Feb 07 23:26:14 2015 +0300
@@ -124,7 +124,11 @@
         <file>res/iconHealth.png</file>
         <file>res/iconSuddenDeath.png</file>
         <file>res/iconDamage.png</file>
+        <file>res/iconDamageLockG.png</file>
+        <file>res/iconDamageLockR.png</file>
         <file>res/iconTime.png</file>
+        <file>res/iconTimeLockG.png</file>
+        <file>res/iconTimeLockR.png</file>
         <file>res/iconMine.png</file>
         <file>res/iconDud.png</file>
         <file>res/iconRope.png</file>
--- a/QTfrontend/hwform.cpp	Mon Feb 02 23:12:56 2015 +0300
+++ b/QTfrontend/hwform.cpp	Sat Feb 07 23:26:14 2015 +0300
@@ -1839,7 +1839,7 @@
         ui.pageNetGame->setRoomName(hwnet->getRoom());
 
         ui.pageNetGame->pGameCFG->GameSchemes->view()->disconnect(hwnet);
-        connect(hwnet, SIGNAL(netSchemeConfig(QStringList &)),
+        connect(hwnet, SIGNAL(netSchemeConfig(QStringList)),
                 this, SLOT(selectFirstNetScheme()));
     }
 
--- a/QTfrontend/model/playerslistmodel.cpp	Mon Feb 02 23:12:56 2015 +0300
+++ b/QTfrontend/model/playerslistmodel.cpp	Sat Feb 07 23:26:14 2015 +0300
@@ -237,6 +237,7 @@
         << index.data(InGame).toBool()
         << index.data(RoomFilterRole).toBool()
         << index.data(InRoom).toBool()
+        << index.data(Contributor).toBool()
         ;
 
     for(int i = flags.size() - 1; i >= 0; --i)
--- a/QTfrontend/model/playerslistmodel.h	Mon Feb 02 23:12:56 2015 +0300
+++ b/QTfrontend/model/playerslistmodel.h	Sat Feb 07 23:26:14 2015 +0300
@@ -23,6 +23,8 @@
         InGame      = Qt::UserRole + 6,
         InRoom      = Qt::UserRole + 7,
         Contributor = Qt::UserRole + 8
+        // if you add a role that will affect the player icon,
+        // then also add it to the flags Qlist in updateIcon()!
     };
 
     enum SpecialRoles {
--- a/QTfrontend/model/roomslistmodel.cpp	Mon Feb 02 23:12:56 2015 +0300
+++ b/QTfrontend/model/roomslistmodel.cpp	Sat Feb 07 23:26:14 2015 +0300
@@ -108,22 +108,38 @@
     if (role == Qt::DecorationRole)
     {
         const QIcon roomBusyIcon(":/res/iconDamage.png");
+        const QIcon roomBusyIconGreen(":/res/iconDamageLockG.png");
+        const QIcon roomBusyIconRed(":/res/iconDamageLockR.png");
         const QIcon roomWaitingIcon(":/res/iconTime.png");
+        const QIcon roomWaitingIconGreen(":/res/iconTimeLockG.png");
+        const QIcon roomWaitingIconRed(":/res/iconTimeLockR.png");
+
+        QString flags = m_data.at(row).at(StateColumn);
 
-        if (m_data.at(row).at(0).isEmpty())
-            return QVariant(roomWaitingIcon);
+        if (flags.contains("g"))
+        {
+            if (flags.contains("j"))
+                return QVariant(roomBusyIconRed);
+            else if (flags.contains("p"))
+                return QVariant(roomBusyIconGreen);
+            else
+                return QVariant(roomBusyIcon);
+        }
         else
-            return QVariant(roomBusyIcon);
+        {
+            if (flags.contains("j"))
+                return QVariant(roomWaitingIconRed);
+            else if (flags.contains("p"))
+                return QVariant(roomWaitingIconGreen);
+            else
+                return QVariant(roomWaitingIcon);
+        }
     }
 
     QString content = m_data.at(row).at(column);
 
     if (role == Qt::DisplayRole)
     {
-        // supply in progress flag as bool
-        if (column == 0)
-            return QVariant(QString(!content.isEmpty()));
-
         // display room names
         if (column == 5)
         {
@@ -190,7 +206,7 @@
             l.append(rooms[i + t]);
         }
 
-        m_data.append(roomInfo2RoomRecord(l));
+        m_data.append(l);
     }
 
     endResetModel();
@@ -201,7 +217,7 @@
 {
     beginInsertRows(QModelIndex(), 0, 0);
 
-    m_data.prepend(roomInfo2RoomRecord(info));
+    m_data.prepend(info);
 
     endInsertRows();
 }
@@ -250,25 +266,7 @@
     if (i < 0)
         return;
 
-    m_data[i] = roomInfo2RoomRecord(info);
+    m_data[i] = info;
 
     emit dataChanged(index(i, 0), index(i, columnCount(QModelIndex()) - 1));
 }
-
-
-QStringList RoomsListModel::roomInfo2RoomRecord(const QStringList & info)
-{
-    QStringList result;
-
-    result = info;
-
-    QString flags = info[StateColumn];
-    // for matters of less memory usage and quicker access store
-    // the boolean string as either "t" or empty
-    if (flags.contains('g'))
-        result[StateColumn] = "t";
-    else
-        result[StateColumn] = QString();
-
-    return result;
-}
--- a/QTfrontend/model/roomslistmodel.h	Mon Feb 02 23:12:56 2015 +0300
+++ b/QTfrontend/model/roomslistmodel.h	Sat Feb 07 23:26:14 2015 +0300
@@ -66,8 +66,6 @@
     QStringList m_headerData;
     MapModel * m_staticMapModel;
     MapModel * m_missionMapModel;
-
-    QStringList roomInfo2RoomRecord(const QStringList & info);
 };
 
 #endif // HEDGEWARS_ROOMSLISTMODEL_H
--- a/QTfrontend/res/html/about.html	Mon Feb 02 23:12:56 2015 +0300
+++ b/QTfrontend/res/html/about.html	Sat Feb 07 23:26:14 2015 +0300
@@ -76,7 +76,7 @@
             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>
             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:almikes@aol.com">almikes@aol.com</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;<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;<br>
             Japanese: ADAM Etienne &lt;<a href="mailto:etienne.adam@gmail.com">etienne.adam@gmail.com</a>&gt;, Marco Bresciani &lt;<a href="mailto:m.bresciani@email.it">m.bresciani@email.it</a>&gt;<br>
             Korean: Anthony Bellew &lt;<a href="mailto:anthonyreflected@gmail.com">anthonyreflected@gmail.com</a>&gt;<br>
             Lithuanian: Lukas Urbonas &lt;<a href="mailto:lukasu08@gmail.com">lukasu08@gmail.com</a>&gt;<br>
Binary file QTfrontend/res/iconDamageLockG.png has changed
Binary file QTfrontend/res/iconDamageLockR.png has changed
Binary file QTfrontend/res/iconTimeLockG.png has changed
Binary file QTfrontend/res/iconTimeLockR.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/QTfrontend/res/locks.svg	Sat Feb 07 23:26:14 2015 +0300
@@ -0,0 +1,147 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   id="svg2"
+   viewBox="0 0 225.52 116.94"
+   version="1.1"
+   inkscape:version="0.48.4 r9939"
+   width="100%"
+   height="100%"
+   sodipodi:docname="sloten.svg">
+  <defs
+     id="defs14" />
+  <sodipodi:namedview
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="1920"
+     inkscape:window-height="1028"
+     id="namedview12"
+     showgrid="false"
+     showguides="true"
+     inkscape:guide-bbox="true"
+     inkscape:zoom="6.3586638"
+     inkscape:cx="102.46566"
+     inkscape:cy="54.65346"
+     inkscape:window-x="0"
+     inkscape:window-y="24"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="svg2">
+    <sodipodi:guide
+       orientation="0,1"
+       position="141.89589,74.728869"
+       id="guide3765" />
+    <sodipodi:guide
+       orientation="1,0"
+       position="105.52531,92.000461"
+       id="guide3767" />
+    <sodipodi:guide
+       orientation="1,0"
+       position="80.362796,66.52341"
+       id="guide3769" />
+  </sodipodi:namedview>
+  <path
+     style="fill:#dd2727"
+     inkscape:connector-curvature="0"
+     d="M 73.809216,100.09273 73.879916,44.786734 H 0.40991832 L -0.03314168,99.516734 C 0.03755832,119.84473 16.535858,116.77773 36.887858,116.77773 57.239856,116.77773 73.808856,120.44373 73.808856,100.09173 z"
+     id="path2865-1"
+     inkscape:export-xdpi="13.85"
+     inkscape:export-ydpi="13.85" />
+  <path
+     style="fill:#920312"
+     inkscape:connector-curvature="0"
+     d="M 37.036484,61.912734 C 33.023684,61.912734 29.788884,65.147534 29.788884,69.160334 29.788884,72.084634 31.494584,74.619834 33.984884,75.772234 L 31.299484,93.859234 H 43.760484 L 40.883284,75.327234 C 42.936484,74.043334 44.316384,71.760434 44.316384,69.160434 44.316384,65.147634 41.049784,61.912834 37.036984,61.912834 z"
+     id="path2860-7"
+     inkscape:export-filename="/home/nemo/hg/hedgewars/0.9.21/QTfrontend/res/path2865-1.png"
+     inkscape:export-xdpi="13.85"
+     inkscape:export-ydpi="13.85" />
+  <path
+     style="fill:#dd2727"
+     inkscape:connector-curvature="0"
+     d="M 4.7043898,42.314248 C 4.7043898,42.314248 3.1630898,0.18524828 36.55839,0.18524828 69.95369,0.18524828 67.89839,42.314248 67.89839,42.314248 H 59.16429 C 59.16429,42.314248 58.9074,10.460249 36.55829,9.6902482 14.20929,8.9195882 15.49329,42.314248 15.49329,42.314248 H 4.7042898 z"
+     id="path3743"
+     inkscape:export-filename="/home/nemo/hg/hedgewars/0.9.21/QTfrontend/res/path2865-1.png"
+     inkscape:export-xdpi="13.85"
+     inkscape:export-ydpi="13.85" />
+  <path
+     style="fill:#60df33"
+     inkscape:connector-curvature="0"
+     d="M 85.015528,42.497266 C 85.015528,42.497266 83.474228,0.36826574 116.86953,0.36826574 150.26483,0.36826574 148.20953,42.497266 148.20953,42.497266 H 139.47543 C 139.47543,42.497266 139.21854,10.643266 116.86943,9.8732657 94.520428,9.1026057 95.804428,42.497266 95.804428,42.497266 H 85.015428 z"
+     id="path2858"
+     inkscape:export-filename="/home/nemo/hg/hedgewars/0.9.21/QTfrontend/res/path2860.png"
+     inkscape:export-xdpi="13.85"
+     inkscape:export-ydpi="13.85" />
+  <path
+     style="fill:#60df33"
+     inkscape:connector-curvature="0"
+     d="M 154.04553,100.41727 154.11623,45.111266 H 80.646228 L 80.203168,99.841266 C 80.273868,120.16927 96.772168,117.10227 117.12417,117.10227 137.47617,117.10227 154.04517,120.76827 154.04517,100.41627 z"
+     id="path2865"
+     inkscape:export-filename="/home/nemo/hg/hedgewars/0.9.21/QTfrontend/res/path2860.png"
+     inkscape:export-xdpi="13.85"
+     inkscape:export-ydpi="13.85" />
+  <path
+     style="fill:#287f09"
+     inkscape:connector-curvature="0"
+     d="M 117.11553,62.227266 C 113.10273,62.227266 109.86793,65.462066 109.86793,69.474866 109.86793,72.399166 111.57363,74.934366 114.06393,76.086766 L 111.37853,94.173766 H 123.83953 L 120.96233,75.641766 C 123.01553,74.357866 124.39543,72.074966 124.39543,69.474966 124.39543,65.462166 121.12883,62.227366 117.11603,62.227366 z"
+     id="path2860"
+     inkscape:export-xdpi="13.85"
+     inkscape:export-ydpi="13.85" />
+  <metadata
+     id="metadata10">
+    <rdf:RDF>
+      <cc:Work>
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <cc:license
+           rdf:resource="http://creativecommons.org/licenses/publicdomain/" />
+        <dc:publisher>
+          <cc:Agent
+             rdf:about="http://openclipart.org/">
+            <dc:title>Openclipart</dc:title>
+          </cc:Agent>
+        </dc:publisher>
+        <dc:title>Open and closed lock</dc:title>
+        <dc:date>2013-11-07T10:50:32</dc:date>
+        <dc:description>Set of 2 locks, one opened and one closed.</dc:description>
+        <dc:source>https://openclipart.org/detail/188421/open-and-closed-lock-by-iyo-188421</dc:source>
+        <dc:creator>
+          <cc:Agent>
+            <dc:title>Iyo</dc:title>
+          </cc:Agent>
+        </dc:creator>
+        <dc:subject>
+          <rdf:Bag>
+            <rdf:li>closed</rdf:li>
+            <rdf:li>lock</rdf:li>
+            <rdf:li>locks</rdf:li>
+            <rdf:li>open</rdf:li>
+            <rdf:li>pic</rdf:li>
+            <rdf:li>pictogram</rdf:li>
+            <rdf:li>sign</rdf:li>
+          </rdf:Bag>
+        </dc:subject>
+      </cc:Work>
+      <cc:License
+         rdf:about="http://creativecommons.org/licenses/publicdomain/">
+        <cc:permits
+           rdf:resource="http://creativecommons.org/ns#Reproduction" />
+        <cc:permits
+           rdf:resource="http://creativecommons.org/ns#Distribution" />
+        <cc:permits
+           rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
+      </cc:License>
+    </rdf:RDF>
+  </metadata>
+</svg>
--- a/QTfrontend/ui/page/pageroomslist.cpp	Mon Feb 02 23:12:56 2015 +0300
+++ b/QTfrontend/ui/page/pageroomslist.cpp	Sat Feb 07 23:26:14 2015 +0300
@@ -77,8 +77,16 @@
     showGamesInProgress = new QAction(QAction::tr("Show games in-progress"), stateMenu);
     showGamesInProgress->setCheckable(true);
     showGamesInProgress->setChecked(true);
+    showPassword = new QAction(QAction::tr("Show password protected"), stateMenu);
+    showPassword->setCheckable(true);
+    showPassword->setChecked(true);
+    showJoinRestricted = new QAction(QAction::tr("Show join restricted"), stateMenu);
+    showJoinRestricted->setCheckable(true);
+    showJoinRestricted->setChecked(true);
     stateMenu->addAction(showGamesInLobby);
     stateMenu->addAction(showGamesInProgress);
+    stateMenu->addAction(showPassword);
+    stateMenu->addAction(showJoinRestricted);
     btnState->setMenu(stateMenu);
 
     // Help/prompt message at top
@@ -186,6 +194,8 @@
     connect(roomsList, SIGNAL(clicked (const QModelIndex &)), searchText, SLOT(setFocus()));
     connect(showGamesInLobby, SIGNAL(triggered()), this, SLOT(onFilterChanged()));
     connect(showGamesInProgress, SIGNAL(triggered()), this, SLOT(onFilterChanged()));
+    connect(showPassword, SIGNAL(triggered()), this, SLOT(onFilterChanged()));
+    connect(showJoinRestricted, SIGNAL(triggered()), this, SLOT(onFilterChanged()));
     connect(searchText, SIGNAL(textChanged (const QString &)), this, SLOT(onFilterChanged()));
     connect(this, SIGNAL(askJoinConfirmation (const QString &)), this, SLOT(onJoinConfirmation(const QString &)), Qt::QueuedConnection);
 
@@ -630,13 +640,29 @@
 
     bool stateLobby = showGamesInLobby->isChecked();
     bool stateProgress = showGamesInProgress->isChecked();
+    bool statePassword = showPassword->isChecked();
+    bool stateJoinRestricted = showJoinRestricted->isChecked();
 
-    if (stateLobby && stateProgress)
-        stateFilteredModel->setFilterFixedString(QString()); // "any"
-    else if (stateLobby != stateProgress)
-        stateFilteredModel->setFilterFixedString(QString(stateProgress));
+    QString filter;
+    if (!stateLobby && !stateProgress)
+        filter = "O_o";
+    else if (stateLobby && stateProgress && statePassword && stateJoinRestricted)
+        filter = "";
     else
-        stateFilteredModel->setFilterFixedString(QString("none")); // Basically, none.
+    {
+        QString exclude = "[^";
+        if (!stateProgress) exclude += "g";
+        if (!statePassword) exclude += "p";
+        if (!stateJoinRestricted) exclude += "j";
+        exclude += "]*";
+        if (stateProgress && statePassword && stateJoinRestricted) exclude = ".*";
+        filter = "^" + exclude;
+        if (!stateLobby) filter += "g" + exclude;
+        filter += "$";
+    }
+    //qDebug() << filter;
+
+    stateFilteredModel->setFilterRegExp(filter);
 }
 
 void PageRoomsList::setSettings(QSettings *settings)
--- a/QTfrontend/ui/page/pageroomslist.h	Mon Feb 02 23:12:56 2015 +0300
+++ b/QTfrontend/ui/page/pageroomslist.h	Sat Feb 07 23:26:14 2015 +0300
@@ -96,6 +96,8 @@
         QSortFilterProxyModel * stateFilteredModel;
         QAction * showGamesInLobby;
         QAction * showGamesInProgress;
+        QAction * showPassword;
+        QAction * showJoinRestricted;
         QSplitter * m_splitter;
 
         AmmoSchemeModel * ammoSchemeModel;
--- a/QTfrontend/ui/widget/feedbackdialog.cpp	Mon Feb 02 23:12:56 2015 +0300
+++ b/QTfrontend/ui/widget/feedbackdialog.cpp	Sat Feb 07 23:26:14 2015 +0300
@@ -268,7 +268,7 @@
     number_of_cores += QString::number(sysconf(_SC_NPROCESSORS_ONLN)) + "\n";
     quint32 pages = sysconf(_SC_PHYS_PAGES);
     quint32 page_size = sysconf(_SC_PAGE_SIZE);
-    quint32 total = pages * page_size / 1024 / 1024;
+    quint64 total = (quint64)pages * page_size / 1024 / 1024;
     total_ram += QString::number(total) + " MB\n";
     os_version += "GNU/Linux or BSD\n";
 #endif
--- a/QTfrontend/weapons.h	Mon Feb 02 23:12:56 2015 +0300
+++ b/QTfrontend/weapons.h	Sat Feb 07 23:26:14 2015 +0300
@@ -22,60 +22,60 @@
 //structure------------------------------------------------------------------|
 
 
-#define AMMOLINE_DEFAULT_QT     "93919294221991210322351110012000000002111001010111110001"
-#define AMMOLINE_DEFAULT_PROB   "04050405416006555465544647765766666661555101011154111111"
-#define AMMOLINE_DEFAULT_DELAY  "00000000000002055000000400070040000000002200000006000200"
-#define AMMOLINE_DEFAULT_CRATE  "13111103121111111231141111111111111112111111011111111111"
+#define AMMOLINE_DEFAULT_QT     "939192942219912103223511100120000000021110010101111100010"
+#define AMMOLINE_DEFAULT_PROB   "040504054160065554655446477657666666615551010111541111111"
+#define AMMOLINE_DEFAULT_DELAY  "000000000000020550000004000700400000000022000000060002000"
+#define AMMOLINE_DEFAULT_CRATE  "131111031211111112311411111111111111121111110111111111111"
 
-#define AMMOLINE_CRAZY_QT       "99999999999999999929999999999999992999999999099999929991"
-#define AMMOLINE_CRAZY_PROB     "11111101111111111111111111111111111111111111011111111111"
-#define AMMOLINE_CRAZY_DELAY    "00000000000000000000000000000000000000000000000000000000"
-#define AMMOLINE_CRAZY_CRATE    "13111103121111111231141111111111111112111101011111111111"
+#define AMMOLINE_CRAZY_QT       "999999999999999999299999999999999929999999990999999299919"
+#define AMMOLINE_CRAZY_PROB     "111111011111111111111111111111111111111111110111111111111"
+#define AMMOLINE_CRAZY_DELAY    "000000000000000000000000000000000000000000000000000000000"
+#define AMMOLINE_CRAZY_CRATE    "131111031211111112311411111111111111121111010111111111111"
 
-#define AMMOLINE_PROMODE_QT     "90900090000000000000090000000000000000000000000000000000"
-#define AMMOLINE_PROMODE_PROB   "00000000000000000000000000000000000000000000000000000000"
-#define AMMOLINE_PROMODE_DELAY  "00000000000002055000000400070040000000002000000000000200"
-#define AMMOLINE_PROMODE_CRATE  "11111101111111111111111111111111111111111001011111111111"
+#define AMMOLINE_PROMODE_QT     "909000900000000000000900000000000000000000000000000000000"
+#define AMMOLINE_PROMODE_PROB   "000000000000000000000000000000000000000000000000000000000"
+#define AMMOLINE_PROMODE_DELAY  "000000000000020550000004000700400000000020000000000002000"
+#define AMMOLINE_PROMODE_CRATE  "111111011111111111111111111111111111111110010111111111111"
 
-#define AMMOLINE_SHOPPA_QT      "00000099000000000000000000000000000000000000000000000000"
-#define AMMOLINE_SHOPPA_PROB    "44444100442444022101121212224220000000020004000100110010"
-#define AMMOLINE_SHOPPA_DELAY   "00000000000000000000000000000000000000000000000000000000"
-#define AMMOLINE_SHOPPA_CRATE   "11111101111111111111111111111111111111111011011111111110"
+#define AMMOLINE_SHOPPA_QT      "000000990000000000000000000000000000000000000000000000000"
+#define AMMOLINE_SHOPPA_PROB    "444441004424440221011212122242200000000200040001001100101"
+#define AMMOLINE_SHOPPA_DELAY   "000000000000000000000000000000000000000000000000000000000"
+#define AMMOLINE_SHOPPA_CRATE   "111111011111111111111111111111111111111110110111111111101"
 
-#define AMMOLINE_CLEAN_QT       "10100090000100000110000000000000000000000000000010000000"
-#define AMMOLINE_CLEAN_PROB     "04050405416006555465544647765766666661555101011154111211"
-#define AMMOLINE_CLEAN_DELAY    "00000000000000000000000000000000000000000000000000000200"
-#define AMMOLINE_CLEAN_CRATE    "13111103121111111231141111111111111112111111011111111111"
+#define AMMOLINE_CLEAN_QT       "101000900001000001100000000000000000000000000000100000000"
+#define AMMOLINE_CLEAN_PROB     "040504054160065554655446477657666666615551010111541112111"
+#define AMMOLINE_CLEAN_DELAY    "000000000000000000000000000000000000000000000000000002000"
+#define AMMOLINE_CLEAN_CRATE    "131111031211111112311411111111111111121111110111111111111"
 
-#define AMMOLINE_MINES_QT       "00000099000900000003000000000000000000000000000000000000"
-#define AMMOLINE_MINES_PROB     "00000000000000000000000000000000000000000000000000000000"
-#define AMMOLINE_MINES_DELAY    "00000000000002055000000400070040000000002000000006000200"
-#define AMMOLINE_MINES_CRATE    "11111101111111111111111111111111111111111111011111111111"
+#define AMMOLINE_MINES_QT       "000000990009000000030000000000000000000000000000000000000"
+#define AMMOLINE_MINES_PROB     "000000000000000000000000000000000000000000000000000000000"
+#define AMMOLINE_MINES_DELAY    "000000000000020550000004000700400000000020000000060002000"
+#define AMMOLINE_MINES_CRATE    "111111011111111111111111111111111111111111110111111111111"
 
-#define AMMOLINE_PORTALS_QT     "90000090020000000021000000000000001100000900000000000000"
-#define AMMOLINE_PORTALS_PROB   "04050405416006555465544647765766666661555101011154111211"
-#define AMMOLINE_PORTALS_DELAY  "00000000000002055000000400070040000000002000000006000200"
-#define AMMOLINE_PORTALS_CRATE  "13111103121111111231141111111111111112111111011111111111"
+#define AMMOLINE_PORTALS_QT     "900000900200000000210000000000000011000009000000000000000"
+#define AMMOLINE_PORTALS_PROB   "040504054160065554655446477657666666615551010111541112111"
+#define AMMOLINE_PORTALS_DELAY  "000000000000020550000004000700400000000020000000060002000"
+#define AMMOLINE_PORTALS_CRATE  "131111031211111112311411111111111111121111110111111111111"
 
-#define AMMOLINE_ONEEVERY_QT    "11111191111111111111111111111111111111111111111111111111"
-#define AMMOLINE_ONEEVERY_PROB  "11111101111111111111111111111111111111111111111111111111"
-#define AMMOLINE_ONEEVERY_DELAY "00000000000000000000000000000000000000000000000000000000"
-#define AMMOLINE_ONEEVERY_CRATE "11111101111111111111111111111111111111111111111111111111"
+#define AMMOLINE_ONEEVERY_QT    "111111911111111111111111111111111111111111111111111111111"
+#define AMMOLINE_ONEEVERY_PROB  "111111011111111111111111111111111111111111111111111111111"
+#define AMMOLINE_ONEEVERY_DELAY "000000000000000000000000000000000000000000000000000000000"
+#define AMMOLINE_ONEEVERY_CRATE "111111011111111111111111111111111111111111111111111111111"
 
-#define AMMOLINE_HIGHLANDER_QT    "11111191111111111111019111111111100101111101111011001101"
-#define AMMOLINE_HIGHLANDER_PROB  "00000000000000000000000000000000000000000000000000000000"
-#define AMMOLINE_HIGHLANDER_DELAY "00000000000000000000000000000000000000000000000000000000"
-#define AMMOLINE_HIGHLANDER_CRATE "00000000000000000000000000000000000000000000000000000000"
+#define AMMOLINE_HIGHLANDER_QT    "111111911111111111110191111111111001011111011110110011010"
+#define AMMOLINE_HIGHLANDER_PROB  "000000000000000000000000000000000000000000000000000000000"
+#define AMMOLINE_HIGHLANDER_DELAY "000000000000000000000000000000000000000000000000000000000"
+#define AMMOLINE_HIGHLANDER_CRATE "000000000000000000000000000000000000000000000000000000000"
 
-#define AMMOLINE_CONSTRUCTION_QT    "11000190000000100100000000000000000000000000000000000000"
-#define AMMOLINE_CONSTRUCTION_PROB  "11111101111111111111111111111111111111111111111111111111"
-#define AMMOLINE_CONSTRUCTION_DELAY "00000000000000000000000000000000000000000000000000000000"
-#define AMMOLINE_CONSTRUCTION_CRATE "11111101111111111111111111111111111111111111111111111111"
+#define AMMOLINE_CONSTRUCTION_QT    "110001900000001001000000000000000000000000000000000000000"
+#define AMMOLINE_CONSTRUCTION_PROB  "111111011111111111111111111111111111111111111111111111110"
+#define AMMOLINE_CONSTRUCTION_DELAY "000000000000000000000000000000000000000000000000000000000"
+#define AMMOLINE_CONSTRUCTION_CRATE "111111011111111111111111111111111111111111111111111111110"
 
-#define AMMOLINE_SHOPPAPRO_QT      "00000099000000000000000000000000000000000000000000000000"
-#define AMMOLINE_SHOPPAPRO_PROB    "44444000440444000000000000004000000000000000000000000000"
-#define AMMOLINE_SHOPPAPRO_DELAY   "00000000000000000000000000000000000000000000000000000000"
-#define AMMOLINE_SHOPPAPRO_CRATE   "11111101111111111111111111111111111111111011011111111211"
+#define AMMOLINE_SHOPPAPRO_QT      "000000990000000000000000000000000000000000000000000000000"
+#define AMMOLINE_SHOPPAPRO_PROB    "444440004404440000000000000040000000000000000000000000000"
+#define AMMOLINE_SHOPPAPRO_DELAY   "000000000000000000000000000000000000000000000000000000000"
+#define AMMOLINE_SHOPPAPRO_CRATE   "111111011111111111111111111111111111111110110111111112110"
 
 
 //When adding new weapons also insert one element in cDefaultAmmos list (hwconsts.cpp.in)
--- a/cmake_modules/FindLua.cmake	Mon Feb 02 23:12:56 2015 +0300
+++ b/cmake_modules/FindLua.cmake	Sat Feb 07 23:26:14 2015 +0300
@@ -17,7 +17,7 @@
 
 find_path(LUA_INCLUDE_DIR lua.h
                           PATHS /usr/include /usr/local/include /usr/pkg/include
-                          PATH_SUFFIXES lua5.1 lua51)
+                          PATH_SUFFIXES lua5.1 lua51 lua-5.1)
 find_library(LUA_LIBRARY NAMES lua51 lua5.1 lua-5.1 lua
                          PATHS /lib /usr/lib /usr/local/lib /usr/pkg/lib)
 
--- a/cmake_modules/compilerchecks.cmake	Mon Feb 02 23:12:56 2015 +0300
+++ b/cmake_modules/compilerchecks.cmake	Sat Feb 07 23:26:14 2015 +0300
@@ -62,7 +62,7 @@
         endif()
     endif()
 
-    if(CMAKE_BUILD_TYPE MATCHES "RELEASE")
+    if(CMAKE_BUILD_TYPE MATCHES "RELEASE" OR CMAKE_BUILD_TYPE MATCHES "RELWITHDEBINFO")
         set(CMAKE_REQUIRED_FLAGS "-Wl,--as-needed")
         check_c_compiler_flag("" HAVE_ASNEEDED)
         if(HAVE_ASNEEDED)
--- a/gameServer/Actions.hs	Mon Feb 02 23:12:56 2015 +0300
+++ b/gameServer/Actions.hs	Sat Feb 07 23:26:14 2015 +0300
@@ -356,6 +356,9 @@
                         ) joinedMidGame
                      ) ri
 
+    rteams <- io $ room'sM rnc (L.nub . rejoinedTeams . fromJust . gameInfo) ri
+    mapM_ (processAction . RemoveTeam) rteams
+
     mapM_ processAction $ (
         SaveReplay
         : ModifyRoom
@@ -425,6 +428,15 @@
     mapM_ processAction removeTeamActions
 
 
+processAction SetRandomSeed = do
+    ri <- clientRoomA
+    thisRoomChans <- liftM (map sendChan) $ roomClientsS ri
+    seed <- liftM showB $ io $ (randomRIO (0, 10^9) :: IO Int)
+    mapM_ processAction [
+        ModifyRoom (\r -> r{mapParams = Map.insert "SEED" seed $ mapParams r})
+        , AnswerClients thisRoomChans ["CFG", "SEED", seed]
+        ]
+
 
 processAction CheckRegistered = do
     (Just ci) <- gets clientIndex
--- a/gameServer/CoreTypes.hs	Mon Feb 02 23:12:56 2015 +0300
+++ b/gameServer/CoreTypes.hs	Sat Feb 07 23:26:14 2015 +0300
@@ -101,6 +101,7 @@
     | LoadRoom B.ByteString
     | ReactCmd [B.ByteString]
     | CheckVotes
+    | SetRandomSeed
 
 
 data Event = LobbyChatMessage
@@ -110,7 +111,7 @@
 type EventsInfo = [(Int, UTCTime)]
 
 newEventsInfo :: EventsInfo
-newEventsInfo = []   
+newEventsInfo = []
 
 type ClientChan = Chan [B.ByteString]
 
@@ -185,6 +186,7 @@
         roundMsgs :: [B.ByteString],
         lastFilteredTimedMsg :: Maybe B.ByteString,
         leftTeams :: [B.ByteString],
+        rejoinedTeams :: [B.ByteString], -- for 0.9.21 frontend workaround
         teamsAtStart :: [TeamInfo],
         teamsInGameNumber :: Int,
         allPlayersHaveRegisteredAccounts :: !Bool,
@@ -205,6 +207,7 @@
         []
         Nothing
         []
+        []
 
 
 data RoomInfo =
@@ -222,6 +225,7 @@
         isRestrictedTeams :: Bool,
         isRegisteredOnly :: Bool,
         isSpecial :: Bool,
+        defaultHedgehogsNumber :: Int,
         greeting :: B.ByteString,
         voting :: Maybe Voting,
         roomBansList :: ![B.ByteString],
@@ -245,6 +249,7 @@
         False
         False
         False
+        4
         ""
         Nothing
         []
@@ -319,6 +324,8 @@
 data VoteType = VoteKick B.ByteString
               | VoteMap B.ByteString
               | VotePause
+              | VoteNewSeed
+              | VoteHedgehogsPerTeam Int
 
 
 newVoting :: VoteType -> Voting
--- a/gameServer/EngineInteraction.hs	Mon Feb 02 23:12:56 2015 +0300
+++ b/gameServer/EngineInteraction.hs	Sat Feb 07 23:26:14 2015 +0300
@@ -104,6 +104,7 @@
         , schemeFlags
         , schemeAdditional
         , [eml ["e$template_filter ", mParams Map.! "TEMPLATE"]]
+        , [eml ["e$feature_size ", mParams Map.! "FEATURE_SIZE"]]
         , [eml ["e$mapgen ", mapgen]]
         , mapgenSpecific
         , concatMap teamSetup ti
@@ -112,21 +113,21 @@
         ]
     where
         keys1, keys2 :: Set.Set B.ByteString
-        keys1 = Set.fromList ["MAP", "MAPGEN", "MAZE_SIZE", "SEED", "TEMPLATE"]
+        keys1 = Set.fromList ["FEATURE_SIZE", "MAP", "MAPGEN", "MAZE_SIZE", "SEED", "TEMPLATE"]
         keys2 = Set.fromList ["AMMO", "SCHEME", "SCRIPT", "THEME"]
         sane = Set.null (keys1 Set.\\ Map.keysSet mParams)
             && Set.null (keys2 Set.\\ Map.keysSet prms)
-            && (not . null . drop 40 $ scheme)
+            && (not . null . drop 41 $ scheme)
             && (not . null . tail $ prms Map.! "AMMO")
-        mapGenTypes = ["+rnd+", "+maze+", "+drawn+"]
+        mapGenTypes = ["+rnd+", "+maze+", "+drawn+", "+perlin+"]
         maybeScript = let s = head . fromMaybe ["Normal"] $ Map.lookup "SCRIPT" prms in if s == "Normal" then [] else [eml ["escript Scripts/Multiplayer/", s, ".lua"]]
         maybeMap = let m = mParams Map.! "MAP" in if m `elem` mapGenTypes then [] else [eml ["emap ", m]]
         scheme = tail $ prms Map.! "SCHEME"
         mapgen = mParams Map.! "MAPGEN"
+        templateFilterMsg = eml ["e$maze_size ", mParams Map.! "MAZE_SIZE"]
         mapgenSpecific = case mapgen of
-            "1" -> [eml ["e$maze_size ", mParams Map.! "MAZE_SIZE"]]
-            "2" -> let d = head . fromMaybe [""] $ Map.lookup "DRAWNMAP" prms in if BW.length d <= 4 then [] else drawnMapData d
-            _ -> []
+            "3" -> let d = head . fromMaybe [""] $ Map.lookup "DRAWNMAP" prms in if BW.length d <= 4 then [] else drawnMapData d
+            _ -> [templateFilterMsg]
         gameFlags :: Word32
         gameFlags = foldl (\r (b, f) -> if b == "false" then r else r .|. f) 0 $ zip scheme gameFlagConsts
         schemeFlags = map (\(v, (n, m)) -> eml [n, " ", showB $ (readInt_ v) * m])
--- a/gameServer/HWProtoInRoomState.hs	Mon Feb 02 23:12:56 2015 +0300
+++ b/gameServer/HWProtoInRoomState.hs	Sat Feb 07 23:26:14 2015 +0300
@@ -105,14 +105,12 @@
 handleCmd_inRoom ("ADD_TEAM" : tName : color : grave : fort : voicepack : flag : difStr : hhsInfo)
     | length hhsInfo /= 16 = return [ProtocolError $ loc "Corrupted hedgehogs info"]
     | otherwise = do
-        (ci, _) <- ask
         rm <- thisRoom
         cl <- thisClient
         clNick <- clientNick
         clChan <- thisClientChans
         othChans <- roomOthersChans
         roomChans <- roomClientsChans
-        cl <- thisClient
         let isRegistered = (<) 0 . B.length . webPassword $ cl
         teamColor <-
             if clientProto cl < 42 then
@@ -120,7 +118,11 @@
                 else
                 liftM (head . (L.\\) (map B.singleton ['0'..]) . map teamcolor . teams) thisRoom
         let roomTeams = teams rm
-        let hhNum = let p = if not $ null roomTeams then minimum [hhnum $ head roomTeams, canAddNumber roomTeams] else 4 in newTeamHHNum roomTeams p
+        let hhNum = newTeamHHNum roomTeams $
+                if not $ null roomTeams then
+                    minimum [hhnum $ head roomTeams, canAddNumber roomTeams]
+                else
+                    defaultHedgehogsNumber rm
         let newTeam = clNick `seq` TeamInfo clNick tName teamColor grave fort voicepack flag isRegistered dif hhNum (hhsList hhsInfo)
         return $
             if not . null . drop (maxTeams rm - 1) $ roomTeams then
@@ -401,11 +403,13 @@
 
 handleCmd_inRoom ["CALLVOTE"] = do
     cl <- thisClient
-    return [AnswerClients [sendChan cl] ["CHAT", "[server]", "Available callvote commands: kick <nickname>, map <name>, pause"]]
+    return [AnswerClients [sendChan cl]
+        ["CHAT", "[server]", loc "Available callvote commands: kick <nickname>, map <name>, pause, newseed, hedgehogs"]
+        ]
 
 handleCmd_inRoom ["CALLVOTE", "KICK"] = do
     cl <- thisClient
-    return [AnswerClients [sendChan cl] ["CHAT", "[server]", "callvote kick: specify nickname"]]
+    return [AnswerClients [sendChan cl] ["CHAT", "[server]", loc "callvote kick: specify nickname"]]
 
 handleCmd_inRoom ["CALLVOTE", "KICK", nickname] = do
     (thisClientId, rnc) <- ask
@@ -421,7 +425,7 @@
         if isJust maybeClientId && sameRoom then
             startVote $ VoteKick nickname
             else
-            return [AnswerClients [sendChan cl] ["CHAT", "[server]", "callvote kick: no such user"]]
+            return [AnswerClients [sendChan cl] ["CHAT", "[server]", loc "callvote kick: no such user"]]
 
 
 handleCmd_inRoom ["CALLVOTE", "MAP"] = do
@@ -437,16 +441,37 @@
     if Map.member roomSave $ roomSaves rm then
         startVote $ VoteMap roomSave
         else
-        return [AnswerClients [sendChan cl] ["CHAT", "[server]", "callvote map: no such map"]]
+        return [AnswerClients [sendChan cl] ["CHAT", "[server]", loc "callvote map: no such map"]]
+
 
 handleCmd_inRoom ["CALLVOTE", "PAUSE"] = do
     cl <- thisClient
     rm <- thisRoom
 
     if isJust $ gameInfo rm then
-        startVote VotePause    
+        startVote VotePause
         else 
-        return [AnswerClients [sendChan cl] ["CHAT", "[server]", "callvote pause: no game in progress"]]
+        return [AnswerClients [sendChan cl] ["CHAT", "[server]", loc "callvote pause: no game in progress"]]
+
+
+handleCmd_inRoom ["CALLVOTE", "NEWSEED"] = do
+    startVote VoteNewSeed
+
+
+handleCmd_inRoom ["CALLVOTE", "HEDGEHOGS"] = do
+    cl <- thisClient
+    return [AnswerClients [sendChan cl] ["CHAT", "[server]", loc "callvote hedgehogs: specify number from 1 to 8"]]
+
+
+handleCmd_inRoom ["CALLVOTE", "HEDGEHOGS", hhs] = do
+    cl <- thisClient
+    let h = readInt_ hhs
+
+    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"]]
+
 
 handleCmd_inRoom ["VOTE", m] = do
     cl <- thisClient
--- a/gameServer/HWProtoLobbyState.hs	Mon Feb 02 23:12:56 2015 +0300
+++ b/gameServer/HWProtoLobbyState.hs	Sat Feb 07 23:26:14 2015 +0300
@@ -105,7 +105,7 @@
                 : AnswerClients chans ["CLIENT_FLAGS", "-r", nick cl]
                 : [(AnswerClients [sendChan cl] $ "JOINED" : nicks) | not $ null nicks]
             )
-            -- ++ [ModifyRoom (\r -> let (t', g') = moveTeams clTeams . fromJust $ gameInfo r in r{gameInfo = Just g', teams = t'}) | not $ null clTeams]
+            ++ [ModifyRoom (\r -> let (t', g') = moveTeams clTeams . fromJust $ gameInfo r in r{gameInfo = Just g', teams = t'}) | not $ null clTeams]
             ++ [AnswerClients [sendChan cl] ["CLIENT_FLAGS", "+h", nick $ fromJust owner] | isJust owner]
             ++ [sendStateFlags cl jRoomClients | not $ null jRoomClients]
             ++ answerFullConfig cl jRoom
@@ -117,7 +117,7 @@
         where
         moveTeams :: [B.ByteString] -> GameInfo -> ([TeamInfo], GameInfo)
         moveTeams cts g = (deleteFirstsBy2 (\a b -> teamname a == b) (teamsAtStart g) (leftTeams g \\ cts)
-            , g{leftTeams = leftTeams g \\ cts, teamsInGameNumber = teamsInGameNumber g + length cts})
+            , g{leftTeams = leftTeams g \\ cts, rejoinedTeams = rejoinedTeams g ++ cts, teamsInGameNumber = teamsInGameNumber g + length cts})
         sendStateFlags cl clients = AnswerClients [sendChan cl] . concat . intersperse [""] . filter (not . null) . concat $
                 [f "+r" ready, f "-r" unready, f "+g" ingame, f "-g" inroomlobby]
             where
--- a/gameServer/Votes.hs	Mon Feb 02 23:12:56 2015 +0300
+++ b/gameServer/Votes.hs	Sat Feb 07 23:26:14 2015 +0300
@@ -49,7 +49,7 @@
                 return [AnswerClients [sendChan cl] ["CHAT", "[server]", loc "You already have voted"]]
             else
                 actOnVoting $ voting{votes = (uid, vote):votes voting}
-      
+
     where
     actOnVoting :: Voting -> Reader (ClientIndex, IRnC) [Action]
     actOnVoting vt = do
@@ -107,8 +107,24 @@
         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]", "Pause toggled"],
+                AnswerClients chans ["CHAT", "[server]", loc "Pause toggled"],
                 AnswerClients chans ["EM", toEngineMsg "I"]]
+    act (VoteNewSeed) =
+        return [SetRandomSeed]
+    act (VoteHedgehogsPerTeam h) = do
+        rm <- thisRoom
+        chans <- roomClientsChans
+        let answers = concatMap (\t -> 
+                [ModifyRoom $ modifyTeam t{hhnum = h}
+                , AnswerClients chans ["HH_NUM", teamname t, showB h]]
+                )
+                $
+                if isJust $ gameInfo rm then
+                    teamsAtStart . fromJust . gameInfo $ rm 
+                else
+                    teams rm
+
+        return $ ModifyRoom (\r -> r{defaultHedgehogsNumber = h}) : answers
 
 
 startVote :: VoteType -> Reader (ClientIndex, IRnC) [Action]
@@ -154,3 +170,5 @@
 voteInfo (VoteKick n) = B.concat [loc "kick", " ", n]
 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]
--- a/hedgewars/CMakeLists.txt	Mon Feb 02 23:12:56 2015 +0300
+++ b/hedgewars/CMakeLists.txt	Sat Feb 07 23:26:14 2015 +0300
@@ -21,12 +21,16 @@
 endif(UNIX)
 
 # convert list into pascal array
-list(LENGTH FONTS_DIRS ndirs)
-set(FONTS_DIRS_ARRAY "array [0..${ndirs}] of PChar = (")
-foreach(fontdir ${FONTS_DIRS})
-    set(FONTS_DIRS_ARRAY "${FONTS_DIRS_ARRAY}\n'${fontdir}',")
-endforeach(fontdir)
-set(FONTS_DIRS_ARRAY "${FONTS_DIRS_ARRAY}\nnil);\n")
+if(FONTS_DIRS)
+  list(LENGTH FONTS_DIRS ndirs)
+  set(FONTS_DIRS_ARRAY "array [0..${ndirs}] of PChar = (")
+  foreach(fontdir ${FONTS_DIRS})
+      set(FONTS_DIRS_ARRAY "${FONTS_DIRS_ARRAY}\n_P'${fontdir}',")
+  endforeach(fontdir)
+  set(FONTS_DIRS_ARRAY "${FONTS_DIRS_ARRAY}\nnil);\n")
+else(FONTS_DIRS)
+  set(FONTS_DIRS_ARRAY "array [0..1] of PChar = (nil, nil);")
+endif(FONTS_DIRS)
 
 configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.inc.in ${CMAKE_CURRENT_BINARY_DIR}/config.inc)
 include_directories(${CMAKE_CURRENT_BINARY_DIR})
--- a/hedgewars/config.inc.in	Mon Feb 02 23:12:56 2015 +0300
+++ b/hedgewars/config.inc.in	Sat Feb 07 23:26:14 2015 +0300
@@ -26,8 +26,4 @@
       cRevisionString = '${HEDGEWARS_REVISION}';
       cHashString = '${HEDGEWARS_HASH}';
       cDefaultPathPrefix = '${HEDGEWARS_FULL_DATADIR}/Data';
-{$IFDEF PAS2C}
-      cFontsPaths: array[0..1] of PChar = (nil, nil);
-{$ELSE}
       cFontsPaths: ${FONTS_DIRS_ARRAY}
-{$ENDIF}
--- a/hedgewars/uAIAmmoTests.pas	Mon Feb 02 23:12:56 2015 +0300
+++ b/hedgewars/uAIAmmoTests.pas	Sat Feb 07 23:26:14 2015 +0300
@@ -122,7 +122,8 @@
             (proc: nil;              flags: 0), // amLandGun
             (proc: nil;              flags: 0), // amIceGun
             (proc: nil;              flags: 0), // amKnife
-            (proc: nil;              flags: 0)  // amGirder
+            (proc: nil;              flags: 0), // amRubber
+            (proc: nil;              flags: 0)  // amAirMine
             );
 
 implementation
--- a/hedgewars/uChat.pas	Mon Feb 02 23:12:56 2015 +0300
+++ b/hedgewars/uChat.pas	Sat Feb 07 23:26:14 2015 +0300
@@ -32,7 +32,7 @@
 procedure SendHogSpeech(s: shortstring);
 
 implementation
-uses SDLh, uInputHandler, uTypes, uVariables, uCommands, uUtils, uTextures, uRender, uIO, uScript;
+uses SDLh, uInputHandler, uTypes, uVariables, uCommands, uUtils, uTextures, uRender, uIO, uScript, uRenderUtils;
 
 const MaxStrIndex = 27;
 
@@ -91,8 +91,6 @@
     dstrect   : TSDL_Rect; // destination rectangle for blitting
     font      : THWFont;
 const
-    shadowcolor: TSDL_Color = (r:$00; g:$00; b:$00; a:$FF);
-    //shadowcolor: TSDL_Color = (r:$00; g:$00; b:$00; a:$80);
     shadowint  = $80 shl AShift;
 begin
 
@@ -117,23 +115,11 @@
 
 // draw background
 SDL_FillRect(resSurface, @dstrect, shadowint);
-dstrect.x:= Padding + 1;
-dstrect.y:= Padding + 1;
-// doesn't matter if .w and .h still include padding, SDL_UpperBlit will clip
-
-
-// create and blit text shadow
-strSurface:= TTF_RenderUTF8_Solid(Fontz[font].Handle, Str2PChar(str), shadowcolor);
-SDL_UpperBlit(strSurface, nil, resSurface, @dstrect);
-SDL_FreeSurface(strSurface);
-
-// non-shadow text starts at padding
-dstrect.x:= Padding;
-dstrect.y:= Padding;
 
 // create and blit text
 strSurface:= TTF_RenderUTF8_Blended(Fontz[font].Handle, Str2PChar(str), cl.color);
-SDL_UpperBlit(strSurface, nil, resSurface, @dstrect);
+//SDL_UpperBlit(strSurface, nil, resSurface, @dstrect);
+if strSurface <> nil then copyTOXY(strSurface, resSurface, Padding, Padding);
 SDL_FreeSurface(strSurface);
 
 cl.Tex:= Surface2Tex(resSurface, false);
--- a/hedgewars/uConsts.pas	Mon Feb 02 23:12:56 2015 +0300
+++ b/hedgewars/uConsts.pas	Sat Feb 07 23:26:14 2015 +0300
@@ -231,6 +231,7 @@
     gstInvisible      = $00200000;
     gstSubmersible    = $00400000;
     gstFrozen         = $00800000;
+    gstNoGravity      = $01000000;
 
     // gear messages
     gmLeft           = $00000001;
--- a/hedgewars/uGears.pas	Mon Feb 02 23:12:56 2015 +0300
+++ b/hedgewars/uGears.pas	Sat Feb 07 23:26:14 2015 +0300
@@ -991,7 +991,8 @@
             @doStepIceGun,
             @doStepAddAmmo,
             @doStepGenericFaller,
-            @doStepKnife);
+            @doStepKnife,
+            @doStepAirMine);
 begin
     doStepHandlers:= handlers;
 
--- a/hedgewars/uGearsHandlersMess.pas	Mon Feb 02 23:12:56 2015 +0300
+++ b/hedgewars/uGearsHandlersMess.pas	Sat Feb 07 23:26:14 2015 +0300
@@ -57,6 +57,7 @@
 procedure doStepBlowTorchWork(Gear: PGear);
 procedure doStepBlowTorch(Gear: PGear);
 procedure doStepMine(Gear: PGear);
+procedure doStepAirMine(Gear: PGear);
 procedure doStepSMine(Gear: PGear);
 procedure doStepDynamite(Gear: PGear);
 procedure doStepRollingBarrel(Gear: PGear);
@@ -446,7 +447,8 @@
 
     if isFalling then
         begin
-        Gear^.dY := Gear^.dY + cGravity;
+        if Gear^.State and gstNoGravity = 0 then
+            Gear^.dY := Gear^.dY + cGravity;
         if (GameFlags and gfMoreWind) <> 0 then
             Gear^.dX := Gear^.dX + cWindSpeed / Gear^.Density
         end;
@@ -1661,6 +1663,7 @@
     Gear^.doStep := @doStepBlowTorchWork
 end;
 
+
 ////////////////////////////////////////////////////////////////////////////////
 procedure doStepMine(Gear: PGear);
 var vg: PVisualGear;
@@ -1749,6 +1752,165 @@
             Gear^.State := Gear^.State or gsttmpFlag;
 end;
 
+(*
+Just keeping track for my own benefit.
+Every second, locate new target.  Clear if target radius has been set to 0 or no target in range.
+Every... 16 milliseconds? Update vector to target.
+*)
+
+procedure doStepAirMine(Gear: PGear);
+var i,t,targDist,tmpDist: LongWord;
+    targ, tmpG: PGear;
+    trackSpeed, airFriction, tX, tY: hwFloat;
+begin
+    if Gear^.Pos > 0 then
+        begin
+        airFriction:= _1;
+        dec(airFriction.QWordValue,Gear^.Pos);
+        Gear^.dX:= Gear^.dX*airFriction;
+        Gear^.dY:= Gear^.dY*airFriction
+        end;
+    doStepFallingGear(Gear);
+    if (TurnTimeLeft = 0) and ((Gear^.dX.QWordValue + Gear^.dY.QWordValue) > _0_02.QWordValue) then
+        AllInactive := false;
+
+    if (TurnTimeLeft = 0) or (Gear^.Angle = 0) or (Gear^.Hedgehog = nil) or (Gear^.Hedgehog^.Gear = nil) then
+        begin
+        Gear^.Hedgehog:= nil;
+        targ:= nil;
+        end
+    else if Gear^.Hedgehog <> nil then
+        targ:= Gear^.Hedgehog^.Gear;
+    if targ <> nil then
+        begin
+        tX:=Gear^.X-targ^.X;
+        tY:=Gear^.Y-targ^.Y;
+        // allow escaping - should maybe flag this too
+        if (GameTicks > Gear^.FlightTime+10000) or 
+            ((tX.Round+tY.Round > Gear^.Angle*6) and
+            (hwRound(hwSqr(tX) + hwSqr(tY)) > sqr(Gear^.Angle*6))) then
+            targ:= nil
+        end;
+
+    // If in ready timer, or after turn, or in first 5 seconds of turn (really a window due to extra time utility)
+    // or mine is inactive due to lack of gsttmpflag or hunting is disabled due to seek radius of 0
+    // then we aren't hunting
+    if (ReadyTimeLeft > 0) or (TurnTimeLeft = 0) or 
+        ((TurnTimeLeft < cHedgehogTurnTime) and (cHedgehogTurnTime-TurnTimeLeft < 5000)) or
+        (Gear^.State and gsttmpFlag = 0) or
+        (Gear^.Angle = 0) then
+        gear^.State:= gear^.State and (not gstHHChooseTarget)
+    else if
+    // todo, allow not finding new target, set timeout on target retention
+        (Gear^.State and gstAttacking = 0) and
+        ((GameTicks and $FF) = 17) and
+        (GameTicks > Gear^.FlightTime) then // recheck hunted hog
+        begin
+        gear^.State:= gear^.State or gstHHChooseTarget;
+        if targ <> nil then
+             targDist:= Distance(Gear^.X-targ^.X,Gear^.Y-targ^.Y).Round
+        else targDist:= 0;
+        for t:= 0 to Pred(TeamsCount) do
+            with TeamsArray[t]^ do
+                for i:= 0 to cMaxHHIndex do
+                    if Hedgehogs[i].Gear <> nil then
+                        begin
+                        tmpG:= Hedgehogs[i].Gear;
+                        tX:=Gear^.X-tmpG^.X;
+                        tY:=Gear^.Y-tmpG^.Y;
+                        if (Gear^.Angle = $FFFFFFFF) or
+                            ((tX.Round+tY.Round < Gear^.Angle) and
+                            (hwRound(hwSqr(tX) + hwSqr(tY)) < sqr(Gear^.Angle))) then
+                            begin
+                            if targ <> nil then tmpDist:= Distance(tX,tY).Round;
+                            if (targ = nil) or (tmpDist < targDist) then
+                                begin
+                                if targ = nil then targDist:= Distance(tX,tY).Round
+                                else targDist:= tmpDist;
+                                Gear^.Hedgehog:= @Hedgehogs[i];
+                                targ:= tmpG;
+                                end
+                            end
+                        end;
+        if targ <> nil then Gear^.FlightTime:= GameTicks + 5000
+        end;
+    if targ <> nil then
+        begin
+        trackSpeed:= _0;
+        trackSpeed.QWordValue:= Gear^.Power;
+        if (Gear^.X < targ^.X) and (Gear^.dX < _0_1)  then
+             Gear^.dX:= Gear^.dX+trackSpeed
+        else if (Gear^.X > targ^.X) and (Gear^.dX > -_0_1) then
+            Gear^.dX:= Gear^.dX-trackSpeed;
+        if (Gear^.Y < targ^.Y) and (Gear^.dY < _0_1)  then
+             Gear^.dY:= Gear^.dY+trackSpeed
+        else if (Gear^.Y > targ^.Y) and (Gear^.dY > -_0_1) then
+            Gear^.dY:= Gear^.dY-trackSpeed
+        end
+    else Gear^.Hedgehog:= nil;
+
+    if ((Gear^.State and gsttmpFlag) <> 0) and (Gear^.Health <> 0) then
+        begin
+        if ((Gear^.State and gstAttacking) = 0) then
+            begin
+            if ((GameTicks and $1F) = 0) then
+                begin
+                if targ <> nil then
+                    begin
+                    tX:=Gear^.X-targ^.X;
+                    tY:=Gear^.Y-targ^.Y;
+                    if (tX.Round+tY.Round < Gear^.Karma) and
+                       (hwRound(hwSqr(tX) + hwSqr(tY)) < sqr(Gear^.Karma)) then
+                    Gear^.State := Gear^.State or gstAttacking
+                    end
+                else if (Gear^.Angle > 0) and (CheckGearNear(Gear, gtHedgehog, Gear^.Karma, Gear^.Karma) <> nil) then
+                    Gear^.State := Gear^.State or gstAttacking
+                end
+            end
+        else // gstAttacking <> 0
+            begin
+            AllInactive := false;
+            if (Gear^.Timer and $FF) = 0 then
+                PlaySound(sndMineTick);
+            if Gear^.Timer = 0 then
+                begin
+                // recheck
+                if targ <> nil then
+                    begin
+                    tX:=Gear^.X-targ^.X;
+                    tY:=Gear^.Y-targ^.Y;
+                    if (tX.Round+tY.Round < Gear^.Karma) and
+                       (hwRound(hwSqr(tX) + hwSqr(tY)) < sqr(Gear^.Karma)) then
+                        begin
+                        Gear^.Hedgehog:= CurrentHedgehog;
+                        doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), Gear^.Karma, Gear^.Hedgehog, EXPLAutoSound);
+                        DeleteGear(Gear);
+                        exit
+                        end
+                    end
+                else if (Gear^.Angle > 0) and (CheckGearNear(Gear, gtHedgehog, Gear^.Karma, Gear^.Karma) <> nil) then
+                    begin
+                    Gear^.Hedgehog:= CurrentHedgehog;
+                    doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), Gear^.Karma, Gear^.Hedgehog, EXPLAutoSound);
+                    DeleteGear(Gear);
+                    exit
+                    end;
+                Gear^.State:= Gear^.State and (not gstAttacking);
+                Gear^.Timer:= Gear^.WDTimer
+                end;
+            dec(Gear^.Timer);
+            end
+        end
+    else // gsttmpFlag = 0
+        if (TurnTimeLeft = 0)
+        or ((GameFlags and gfInfAttack <> 0) and (GameTicks > Gear^.FlightTime))
+        or (CurrentHedgehog^.Gear = nil) then
+        begin
+        Gear^.FlightTime:= GameTicks;
+        Gear^.State := Gear^.State or gsttmpFlag
+        end
+end;
+
 ////////////////////////////////////////////////////////////////////////////////
 procedure doStepSMine(Gear: PGear);
     var land: Word;
--- a/hedgewars/uGearsHedgehog.pas	Mon Feb 02 23:12:56 2015 +0300
+++ b/hedgewars/uGearsHedgehog.pas	Sat Feb 07 23:26:14 2015 +0300
@@ -290,6 +290,7 @@
 
             case CurAmmoType of
                       amGrenade: newGear:= AddGear(hwRound(lx), hwRound(ly), gtGrenade,         0, newDx, newDy, CurWeapon^.Timer);
+                      amAirMine: newGear:= AddGear(hwRound(lx), hwRound(ly), gtAirMine,         0, newDx, newDy, 0);
                       amMolotov: newGear:= AddGear(hwRound(lx), hwRound(ly), gtMolotov,      0, newDx, newDy, 0);
                   amClusterBomb: newGear:= AddGear(hwRound(lx), hwRound(ly), gtClusterBomb,  0, newDx, newDy, CurWeapon^.Timer);
                       amGasBomb: newGear:= AddGear(hwRound(lx), hwRound(ly), gtGasBomb,      0, newDx, newDy, CurWeapon^.Timer);
@@ -425,7 +426,8 @@
                      amBazooka, amSnowball,
                          amBee, amSMine,
                       amMortar, amWatermelon,
-                 amHellishBomb, amDrill: FollowGear:= newGear;
+                 amHellishBomb, amDrill,
+                     amAirMine: FollowGear:= newGear;
 
                      amShotgun, amPickHammer,
                         amRope, amDEagle,
@@ -443,9 +445,10 @@
                       amTardis, amPiano,
                       amIceGun, amRubber: CurAmmoGear:= newGear;
             end;
-	    if CurAmmoType = amCake then FollowGear:= newGear;
+            if CurAmmoType = amCake then FollowGear:= newGear;
+            if CurAmmoType = amAirMine then newGear^.Hedgehog:= nil;
 
-            if ((CurAmmoType = amMine) or (CurAmmoType = amSMine)) and (GameFlags and gfInfAttack <> 0) then
+            if ((CurAmmoType = amMine) or (CurAmmoType = amSMine) or (CurAmmoType = amAirMine)) and (GameFlags and gfInfAttack <> 0) then
                 newGear^.FlightTime:= GameTicks + 1000
             else if CurAmmoType = amDrill then
                 newGear^.FlightTime:= GameTicks + 250;
--- a/hedgewars/uGearsList.pas	Mon Feb 02 23:12:56 2015 +0300
+++ b/hedgewars/uGearsList.pas	Sat Feb 07 23:26:14 2015 +0300
@@ -103,6 +103,7 @@
 (*        gtAddAmmo *) , amNothing
 (*  gtGenericFaller *) , amNothing
 (*          gtKnife *) , amKnife
+(*        gtAirMine *) , amAirMine
     );
 
 
@@ -359,6 +360,28 @@
                         gear^.Timer:= cMinesTime
                     end
                 end;
+     gtAirMine: begin
+                gear^.ImpactSound:= sndDenied;
+                gear^.nImpactSounds:= 1;
+                gear^.Health:= 30;
+                gear^.State:= gear^.State or gstMoving or gstNoGravity;
+                gear^.Radius:= 8;
+                gear^.Elasticity:= _0_55;
+                gear^.Friction:= _0_995;
+                gear^.Density:= _1;
+                gear^.Angle:= 175; // Radius at which air bombs will start "seeking". $FFFFFFFF = unlimited. check is skipped.
+                gear^.Power:= cMaxWindSpeed.QWordValue div 2; // hwFloat converted. 1/2 g default. defines the "seek" speed when a gear is in range.
+                gear^.Pos:= cMaxWindSpeed.QWordValue; // air friction. slows it down when not hitting stuff
+                gear^.Karma:= 30; // damage
+                if gear^.Timer = 0 then
+                    begin
+                    if cMinesTime < 0 then
+                        gear^.Timer:= getrandom(13)*100
+                    else
+                        gear^.Timer:= cMinesTime div 4
+                    end;
+                gear^.WDTimer:= gear^.Timer
+                end;
        gtSMine: begin
                 gear^.Health:= 10;
                 gear^.State:= gear^.State or gstMoving;
--- a/hedgewars/uGearsRender.pas	Mon Feb 02 23:12:56 2015 +0300
+++ b/hedgewars/uGearsRender.pas	Sat Feb 07 23:26:14 2015 +0300
@@ -684,6 +684,7 @@
                 amHellishBomb: DrawSpriteRotated(sprHandHellish, hx, hy, sign, aangle);
                 amGasBomb: DrawSpriteRotated(sprHandCheese, hx, hy, sign, aangle);
                 amMine: DrawSpriteRotated(sprHandMine, hx, hy, sign, aangle);
+                amAirMine: DrawSpriteRotated(sprHandMine, hx, hy, sign, aangle);
                 amSMine: DrawSpriteRotated(sprHandSMine, hx, hy, sign, aangle);
                 amKnife: DrawSpriteRotatedF(sprHandKnife, hx, hy, 0, sign, aangle);
                 amSeduction: begin
@@ -1119,6 +1120,18 @@
                        DrawSpriteRotated(sprMineOn, x, y, 0, Gear^.DirAngle)
                     else DrawSpriteRotated(sprMineDead, x, y, 0, Gear^.DirAngle);
                     end;
+         gtAirMine: if Gear^.State and gstTmpFlag = 0 then                // mine is inactive
+                        begin
+                        Tint(150,150,150,255);
+                        DrawSprite(sprAirMine, x-16, y-16, 15);
+                        untint
+                        end
+                    else if (Gear^.Hedgehog <> nil) and (Gear^.Hedgehog^.Gear <> nil) then  // mine is chasing a hog
+                         DrawSprite(sprAirMine, x-16, y-16, (RealTicks div 25) mod 16)
+                    else if Gear^.State and gstHHChooseTarget <> 0 then   // mine is seeking for hogs
+                         DrawSprite(sprAirMine, x-16, y-16, (RealTicks div 125) mod 16)
+                    else
+                         DrawSprite(sprAirMine, x-16, y-16, 4);           // mine is active but not seeking
 
            gtSMine: if (((Gear^.State and gstAttacking) = 0)or((Gear^.Timer and $3FF) < 420)) and (Gear^.Health <> 0) then
                            DrawSpriteRotated(sprSMineOff, x, y, 0, Gear^.DirAngle)
--- a/hedgewars/uGearsUtils.pas	Mon Feb 02 23:12:56 2015 +0300
+++ b/hedgewars/uGearsUtils.pas	Sat Feb 07 23:26:14 2015 +0300
@@ -127,6 +127,7 @@
                 gtClusterBomb,
             //    gtCluster, too game breaking I think
                 gtSMine,
+                gtAirMine,
                 gtCase,
                 gtTarget,
                 gtFlame,
--- a/hedgewars/uLandTemplates.pas	Mon Feb 02 23:12:56 2015 +0300
+++ b/hedgewars/uLandTemplates.pas	Sat Feb 07 23:26:14 2015 +0300
@@ -1805,8 +1805,38 @@
        (x:1005; y: 805; w:   0; h:   0)
       );
 
+      
+const Template46Points: array[0..19] of TSDL_Rect =
+      (
+       (x:  800; y: 1424; w:    1; h:    1),
+       (x:  800; y: 1224; w:    1; h:    1),
+       (x: 2200; y: 1224; w:    1; h:    1),
+       (x: 2200; y: 1424; w:    1; h:    1),
+       (x: NTPX; y:    0; w:    1; h:    1),
+       (x:  800; y: 1024; w:    1; h:    1),
+       (x:  800; y:  844; w:    1; h:    1),
+       (x: 2200; y:  844; w:    1; h:    1),
+       (x: 2200; y: 1024; w:    1; h:    1),
+       (x: NTPX; y:    0; w:    1; h:    1),
+       (x:  800; y:  664; w:    1; h:    1),
+       (x:  800; y:  484; w:    1; h:    1),
+       (x: 2200; y:  484; w:    1; h:    1),
+       (x: 2200; y:  664; w:    1; h:    1),
+       (x: NTPX; y:    0; w:    1; h:    1),
+       (x:  800; y:  304; w:    1; h:    1),
+       (x:  800; y:  104; w:    1; h:    1),
+       (x: 2200; y:  104; w:    1; h:    1),
+       (x: 2200; y:  304; w:    1; h:    1),
+       (x: NTPX; y:    0; w:    1; h:    1)
+
+      );
+      Template46FPoints: array[0..0] of TPoint =
+      (
+       (x: 1023; y:    0)
+      );
+      
 ////////////////////////////////////////////////////////////////////////
-var EdgeTemplates: array[0..45] of TEdgeTemplate =
+var EdgeTemplates: array[0..46] of TEdgeTemplate =
       (
        (BasePoints: PPointArray(@Template0Points);
         BasePointsCount: Succ(High(Template0Points));
@@ -2313,11 +2343,22 @@
         canMirror: false; canFlip: false; isNegative: true; canInvert: false;
         hasGirders: false;
         MaxHedgeHogs: 48;
+       ),
+       (BasePoints: PPointArray(@Template46Points);
+        BasePointsCount: Succ(High(Template46Points));
+        FillPoints: PPointArray(@Template46FPoints);
+        FillPointsCount: Succ(High(Template46FPoints));
+        BezierizeCount: 2;
+        RandPassesCount: 8;
+        TemplateHeight: 1424; TemplateWidth: 3072;
+        canMirror: true; canFlip: false; isNegative: false; canInvert: false;
+        hasGirders: true;
+        MaxHedgeHogs: 18;
        )
       );
 const SmallTemplates: array[0..2] of Longword = ( 39, 40, 42 );
-const MediumTemplates: array[0..17] of Longword =
-      ( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 );
+const MediumTemplates: array[0..18] of Longword =
+      ( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 46 );
 const LargeTemplates: array[0..20] of Longword =
       (
         18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
--- a/hedgewars/uRender.pas	Mon Feb 02 23:12:56 2015 +0300
+++ b/hedgewars/uRender.pas	Sat Feb 07 23:26:14 2015 +0300
@@ -534,11 +534,14 @@
 end;
 
 procedure openglRotatef(RotX, RotY, RotZ: GLfloat; dir: LongInt); inline;
+{ workaround for pascal bug http://bugs.freepascal.org/view.php?id=27222 }
+var tmpdir: LongInt;
 begin
+tmpdir:=dir;
 {$IFDEF GL2}
-    hglRotatef(RotX, RotY, RotZ, dir);
+    hglRotatef(RotX, RotY, RotZ, tmpdir);
 {$ELSE}
-    glRotatef(RotX, RotY, RotZ, dir);
+    glRotatef(RotX, RotY, RotZ, tmpdir);
 {$ENDIF}
 end;
 
--- a/hedgewars/uScript.pas	Mon Feb 02 23:12:56 2015 +0300
+++ b/hedgewars/uScript.pas	Sat Feb 07 23:26:14 2015 +0300
@@ -836,33 +836,119 @@
         lua_pushnil(L); lua_pushnil(L); lua_pushnil(L); lua_pushnil(L); lua_pushnil(L);
         lua_pushnil(L); lua_pushnil(L); lua_pushnil(L); lua_pushnil(L); lua_pushnil(L);
         end;
-    lc_getvisualgearvalues:= 10;
+    lc_getvisualgearvalues:= 10
 end;
 
 function lc_setvisualgearvalues(L : Plua_State) : LongInt; Cdecl;
 var vg : PVisualGear;
 begin
-    if CheckLuaParamCount(L, 11, 'SetVisualGearValues', 'vgUid, X, Y, dX, dY, Angle, Frame, FrameTicks, State, Timer, Tint') then
-        begin
+// Param count can be 1-11 at present
+//    if CheckLuaParamCount(L, 11, 'SetVisualGearValues', 'vgUid, X, Y, dX, dY, Angle, Frame, FrameTicks, State, Timer, Tint') then
+//        begin
         vg:= VisualGearByUID(lua_tointeger(L, 1));
         if vg <> nil then
             begin
-            vg^.X:= lua_tointeger(L, 2);
-            vg^.Y:= lua_tointeger(L, 3);
-            vg^.dX:= lua_tonumber(L, 4);
-            vg^.dY:= lua_tonumber(L, 5);
-            vg^.Angle:= lua_tonumber(L, 6);
-            vg^.Frame:= lua_tointeger(L, 7);
-            if lua_tointeger(L, 8) <> 0 then
-                vg^.FrameTicks:= lua_tointeger(L, 8);  // find a better way to do this. maybe need to break all these up.
-            vg^.State:= lua_tointeger(L, 9);
-            vg^.Timer:= lua_tointeger(L, 10);
-            vg^.Tint:= lua_tointeger(L, 11);
+            if not lua_isnoneornil(L, 2) then
+                vg^.X:= lua_tointeger(L, 2);
+            if not lua_isnoneornil(L, 3) then
+                vg^.Y:= lua_tointeger(L, 3);
+            if not lua_isnoneornil(L, 4) then
+                vg^.dX:= lua_tonumber(L, 4);
+            if not lua_isnoneornil(L, 5) then
+                vg^.dY:= lua_tonumber(L, 5);
+            if not lua_isnoneornil(L, 6) then
+                vg^.Angle:= lua_tonumber(L, 6);
+            if not lua_isnoneornil(L, 7) then
+                vg^.Frame:= lua_tointeger(L, 7);
+            if not lua_isnoneornil(L, 8) then
+                vg^.FrameTicks:= lua_tointeger(L, 8);
+            if not lua_isnoneornil(L, 9) then
+                vg^.State:= lua_tointeger(L, 9);
+            if not lua_isnoneornil(L, 10) then
+                vg^.Timer:= lua_tointeger(L, 10);
+            if not lua_isnoneornil(L, 11) then
+                vg^.Tint:= lua_tointeger(L, 11)
+            end;
+//        end
+//    else
+//        lua_pushnil(L); // return value on stack (nil)
+    lc_setvisualgearvalues:= 0
+end;
+
+// so. going to use this to get/set some of the more obscure gear values which weren't already exposed elsewhere
+// can keep adding things in the future. isnoneornil makes it safe
+function lc_getgearvalues(L : Plua_State) : LongInt; Cdecl;
+var gear: PGear;
+begin
+    if CheckLuaParamCount(L, 1, 'GetGearValues', 'gearUid') then
+        begin
+        gear:= GearByUID(lua_tointeger(L, 1));
+        if gear <> nil then
+            begin
+            lua_pushinteger(L, gear^.Angle);
+            lua_pushinteger(L, gear^.Power);
+            lua_pushinteger(L, gear^.WDTimer);
+            lua_pushinteger(L, gear^.Radius);
+            lua_pushinteger(L, hwRound(gear^.Density * _10000));
+            lua_pushinteger(L, gear^.Karma);
+            lua_pushnumber(L,  gear^.DirAngle);
+            lua_pushinteger(L, gear^.AdvBounce);
+            lua_pushinteger(L, Integer(gear^.ImpactSound));
+            lua_pushinteger(L, gear^.nImpactSounds);
+            lua_pushinteger(L, gear^.Tint)
+            end
+        else
+            begin
+            lua_pushnil(L); lua_pushnil(L); lua_pushnil(L); lua_pushnil(L); lua_pushnil(L);
+            lua_pushnil(L); lua_pushnil(L); lua_pushnil(L); lua_pushnil(L); lua_pushnil(L);
+            lua_pushnil(L)
             end
         end
     else
-        lua_pushnil(L); // return value on stack (nil)
-    lc_setvisualgearvalues:= 0;
+        begin
+        lua_pushnil(L); lua_pushnil(L); lua_pushnil(L); lua_pushnil(L); lua_pushnil(L);
+        lua_pushnil(L); lua_pushnil(L); lua_pushnil(L); lua_pushnil(L); lua_pushnil(L);
+        lua_pushnil(L)
+        end;
+    lc_getgearvalues:= 11
+end;
+
+function lc_setgearvalues(L : Plua_State) : LongInt; Cdecl;
+var gear : PGear;
+begin
+// Currently allows 1-12 params
+//    if CheckLuaParamCount(L, 12, 'SetGearValues', 'gearUid, Angle, Power, WDTimer, Radius, Density, Karma, DirAngle, AdvBounce, ImpactSound, # ImpactSounds, Tint') then
+//        begin
+        gear:= GearByUID(lua_tointeger(L, 1));
+        if gear <> nil then
+            begin
+            if not lua_isnoneornil(L, 2) then
+                gear^.Angle := lua_tointeger(L, 2);
+            if not lua_isnoneornil(L, 3) then
+                gear^.Power := lua_tointeger(L, 3);
+            if not lua_isnoneornil(L, 4) then
+                gear^.WDTimer := lua_tointeger(L, 4);
+            if not lua_isnoneornil(L, 5) then
+                gear^.Radius := lua_tointeger(L, 5);
+            if not lua_isnoneornil(L, 6) then
+                gear^.Density:= int2hwFloat(lua_tointeger(L, 6)) / 10000;
+            if not lua_isnoneornil(L, 7) then
+                gear^.Karma := lua_tointeger(L, 7);
+            if not lua_isnoneornil(L, 8) then
+                gear^.DirAngle:= lua_tonumber(L, 8);
+            if not lua_isnoneornil(L, 9) then
+                gear^.AdvBounce := lua_tointeger(L, 9);
+            if not lua_isnoneornil(L, 10) then
+                gear^.ImpactSound := TSound(lua_tointeger(L, 10));
+            if not lua_isnoneornil(L, 11) then
+                gear^.nImpactSounds := lua_tointeger(L, 11);
+            if not lua_isnoneornil(L, 12) then
+                gear^.Tint := lua_tointeger(L, 12)
+            end;
+//        end
+//    else
+//        lua_pushnil(L); // return value on stack (nil)
+    lc_setgearvalues:= 0
 end;
 
 function lc_getfollowgear(L : Plua_State) : LongInt; Cdecl;
@@ -927,6 +1013,46 @@
     lc_getgearelasticity:= 1
 end;
 
+function lc_setgearelasticity(L : Plua_State) : LongInt; Cdecl;
+var gear: PGear;
+begin
+    if CheckLuaParamCount(L, 2, 'SetGearElasticity', 'gearUid, Elasticity') then
+        begin
+        gear:= GearByUID(lua_tointeger(L, 1));
+        if gear <> nil then
+            gear^.Elasticity:= int2hwFloat(lua_tointeger(L, 2)) / 10000
+        end;
+    lc_setgearelasticity:= 0
+end;
+
+function lc_getgearfriction(L : Plua_State) : LongInt; Cdecl;
+var gear : PGear;
+begin
+    if CheckLuaParamCount(L, 1, 'GetGearFriction', 'gearUid') then
+        begin
+        gear:= GearByUID(lua_tointeger(L, 1));
+        if gear <> nil then
+            lua_pushinteger(L, hwRound(gear^.friction * _10000))
+        else
+            lua_pushnil(L);
+        end
+    else
+        lua_pushnil(L); // return value on stack (nil)
+    lc_getgearfriction:= 1
+end;
+
+function lc_setgearfriction(L : Plua_State) : LongInt; Cdecl;
+var gear: PGear;
+begin
+    if CheckLuaParamCount(L, 2, 'SetGearFriction', 'gearUid, Friction') then
+        begin
+        gear:= GearByUID(lua_tointeger(L, 1));
+        if gear <> nil then
+            gear^.Friction:= int2hwFloat(lua_tointeger(L, 2)) / 10000
+        end;
+    lc_setgearfriction:= 0
+end;
+
 function lc_setgearmessage(L : Plua_State) : LongInt; Cdecl;
 var gear : PGear;
 begin
@@ -2947,6 +3073,8 @@
 lua_register(luaState, _P'DeleteVisualGear', @lc_deletevisualgear);
 lua_register(luaState, _P'GetVisualGearValues', @lc_getvisualgearvalues);
 lua_register(luaState, _P'SetVisualGearValues', @lc_setvisualgearvalues);
+lua_register(luaState, _P'GetGearValues', @lc_getgearvalues);
+lua_register(luaState, _P'SetGearValues', @lc_setgearvalues);
 lua_register(luaState, _P'SpawnHealthCrate', @lc_spawnhealthcrate);
 lua_register(luaState, _P'SpawnAmmoCrate', @lc_spawnammocrate);
 lua_register(luaState, _P'SpawnUtilityCrate', @lc_spawnutilitycrate);
@@ -3011,6 +3139,9 @@
 lua_register(luaState, _P'CampaignLock', @lc_campaignlock);
 lua_register(luaState, _P'CampaignUnlock', @lc_campaignunlock);
 lua_register(luaState, _P'GetGearElasticity', @lc_getgearelasticity);
+lua_register(luaState, _P'SetGearElasticity', @lc_setgearelasticity);
+lua_register(luaState, _P'GetGearFriction', @lc_getgearfriction);
+lua_register(luaState, _P'SetGearFriction', @lc_setgearfriction);
 lua_register(luaState, _P'GetGearRadius', @lc_getgearradius);
 lua_register(luaState, _P'GetGearMessage', @lc_getgearmessage);
 lua_register(luaState, _P'SetGearMessage', @lc_setgearmessage);
--- a/hedgewars/uTypes.pas	Mon Feb 02 23:12:56 2015 +0300
+++ b/hedgewars/uTypes.pas	Sat Feb 07 23:26:14 2015 +0300
@@ -87,7 +87,8 @@
             sprHandResurrector, sprCross, sprAirDrill, sprNapalmBomb,
             sprBulletHit, sprSnowball, sprHandSnowball, sprSnow,
             sprSDFlake, sprSDWater, sprSDCloud, sprSDSplash, sprSDDroplet, sprTardis,
-            sprSlider, sprBotlevels, sprHandKnife, sprKnife, sprStar, sprIceTexture, sprIceGun, sprFrozenHog, sprAmRubber, sprBoing, sprCustom1, sprCustom2
+            sprSlider, sprBotlevels, sprHandKnife, sprKnife, sprStar, sprIceTexture, sprIceGun,
+            sprFrozenHog, sprAmRubber, sprBoing, sprCustom1, sprCustom2, sprAirMine
             );
 
     // Gears that interact with other Gears and/or Land
@@ -104,7 +105,7 @@
             gtEgg, gtPortal, gtPiano, gtGasBomb, gtSineGunShot, gtFlamethrower, // 50
             gtSMine, gtPoisonCloud, gtHammer, gtHammerHit, gtResurrector, // 55
             gtNapalmBomb, gtSnowball, gtFlake, {gtStructure,} gtLandGun, gtTardis, // 61
-            gtIceGun, gtAddAmmo, gtGenericFaller, gtKnife); // 65
+            gtIceGun, gtAddAmmo, gtGenericFaller, gtKnife, gtAirMine); // 66
 
     // Gears that are _only_ of visual nature (e.g. background stuff, visual effects, speechbubbles, etc.)
     TVisualGearType = (vgtFlake, vgtCloud, vgtExplPart, vgtExplPart2, vgtFire,
@@ -154,7 +155,8 @@
             amRCPlane, amLowGravity, amExtraDamage, amInvulnerable, amExtraTime, // 35
             amLaserSight, amVampiric, amSniperRifle, amJetpack, amMolotov, amBirdy, amPortalGun, // 42
             amPiano, amGasBomb, amSineGun, amFlamethrower, amSMine, amHammer, // 48
-            amResurrector, amDrillStrike, amSnowball, amTardis, {amStructure,} amLandGun, amIceGun, amKnife, amRubber); // 56
+            amResurrector, amDrillStrike, amSnowball, amTardis, {amStructure,} amLandGun, // 53
+            amIceGun, amKnife, amRubber, amAirMine); // 57
 
     // Different kind of crates that e.g. hedgehogs can pick up
     TCrateType = (HealthCrate, AmmoCrate, UtilityCrate);
@@ -247,6 +249,7 @@
             CollisionIndex: LongInt;    // Position in collision array
             Message: LongWord;          // Game messages are stored here. See gm bitmasks in uConsts
             uid: Longword;              // Lua use this to reference gears
+            Hedgehog: PHedgehog;        // set to CurrentHedgehog on gear creation.  uStats damage code appears to assume it will never be nil and never be changed.  If you override it, make sure it is set to a non-nil PHedgehog before dealing damage.
 // Strongly recommended not to override these.  Will mess up generic operations like portaling
             X : hwFloat;              // X/Y/dX/dY are position/velocity. People count on these having semi-normal values
             Y : hwFloat;
@@ -278,7 +281,6 @@
             Tex: PTexture;          // A texture created by the gear. Shouldn't use for anything but textures
             Tint: LongWord;         // Used to colour a texture
             LinkedGear: PGear;      // Used to track a related gear. Portal pairs for example.
-            Hedgehog: PHedgehog;    // set to CurrentHedgehog on gear creation
             SoundChannel: LongInt;  // Used to track a sound the gear started
             end;
     TPGearArray = array of PGear;
@@ -445,7 +447,7 @@
             sidMolotov, sidBirdy, sidPortalGun, sidPiano, sidGasBomb,
             sidSineGun, sidFlamethrower,sidSMine, sidHammer, sidResurrector,
             sidDrillStrike, sidSnowball, sidNothing, sidTardis,
-            {sidStructure,} sidLandGun, sidIceGun, sidKnife, sidRubber);
+            {sidStructure,} sidLandGun, sidIceGun, sidKnife, sidRubber, sidAirMine);
 
     TMsgStrId = (sidStartFight, sidDraw, sidWinner, sidVolume, sidPaused,
             sidConfirm, sidSuddenDeath, sidRemaining, sidFuel, sidSync,
--- a/hedgewars/uVariables.pas	Mon Feb 02 23:12:56 2015 +0300
+++ b/hedgewars/uVariables.pas	Sat Feb 07 23:26:14 2015 +0300
@@ -699,7 +699,9 @@
             (FileName:       'custom1'; Path: ptCurrTheme;AltPath: ptGraphics; Texture: nil; Surface: nil;
             Width:   0; Height:  0; imageWidth: 0; imageHeight: 0; saveSurf: true; priority: tpLow; getDimensions: true; getImageDimensions: true), // sprCustom1
             (FileName:       'custom2'; Path: ptCurrTheme;AltPath: ptGraphics; Texture: nil; Surface: nil;
-            Width:   0; Height:  0; imageWidth: 0; imageHeight: 0; saveSurf: true; priority: tpLow; getDimensions: true; getImageDimensions: true) // sprCustom2
+            Width:   0; Height:  0; imageWidth: 0; imageHeight: 0; saveSurf: true; priority: tpLow; getDimensions: true; getImageDimensions: true), // sprCustom2
+            (FileName:      'AirMine'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
+            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpHighest; getDimensions: false; getImageDimensions: true)// sprAirMine
             );
 
 const
@@ -2298,6 +2300,31 @@
             PosCount: 4;
             PosSprite: sprAmRubber;
             ejectX: 0;
+            ejectY: 0),
+// Air Mine
+            (NameId: sidAirMine;
+            NameTex: nil;
+            Probability: 100;
+            NumberInCase: 1;
+            Ammo: (Propz: ammoprop_Power or
+                          ammoprop_AltUse or
+                          ammoprop_NeedUpDown;
+                Count: 2;
+                NumPerTurn: 0;
+                Timer: 0;
+                Pos: 0;
+                AmmoType: amAirMine;
+                AttackVoice: sndLaugh;
+                Bounciness: 1000);
+            Slot: 5;
+            TimeAfterTurn: 5000;
+            minAngle: 0;
+            maxAngle: 0;
+            isDamaging: true;
+            SkipTurns: 0;
+            PosCount: 1;
+            PosSprite: sprWater;
+            ejectX: 0;
             ejectY: 0)
         );
 
--- a/hedgewars/uVisualGearsHandlers.pas	Mon Feb 02 23:12:56 2015 +0300
+++ b/hedgewars/uVisualGearsHandlers.pas	Sat Feb 07 23:26:14 2015 +0300
@@ -125,7 +125,7 @@
 
     if (round(X) >= cLeftScreenBorder)
     and (round(X) <= cRightScreenBorder)
-    and (round(Y) - 75 <= LAND_HEIGHT)
+    and (round(Y) - 250 <= LAND_HEIGHT)
     and (Timer > 0) and (Timer-Steps > 0) then
         begin
         if tdX > 0 then
@@ -152,23 +152,31 @@
             X:= X + cScreenSpace;
             moved:= true
             end
-        else
-            if round(X) > cRightScreenBorder then
-                begin
-                X:= X - cScreenSpace;
-                moved:= true
-                end;
+        else if round(X) > cRightScreenBorder then
+            begin
+            X:= X - cScreenSpace;
+            moved:= true
+            end;
             // if round(Y) < (LAND_HEIGHT - 1024 - 75) then Y:= Y + 25.0; // For if flag is set for flakes rising upwards?
-        if (Gear^.Layer = 2) and (round(Y) - 225 > LAND_HEIGHT) then
+        if (Gear^.Layer = 2) and (round(Y) - 400 > LAND_HEIGHT) and (cGravityf >= 0) then
             begin
             X:= cLeftScreenBorder + random(cScreenSpace);
-            Y:= Y - (1024 + 250 + random(50)); // TODO - configure in theme (jellies for example could use limited range)
+            Y:= Y-(1024 + 400 + random(50)); // TODO - configure in theme (jellies for example could use limited range)
             moved:= true
             end
-        else if (Gear^.Layer <> 2) and (round(Y) + 50 > LAND_HEIGHT) then
+        else if (Gear^.Layer <> 2) and (round(Y) - 150 > LAND_HEIGHT) and (cGravityf >= 0) then
             begin
             X:= cLeftScreenBorder + random(cScreenSpace);
-            Y:= Y - (1024 + random(25));
+            Y:= Y-(1024 + 200 + random(50));
+            moved:= true
+            end
+        else if (round(Y) < LAND_HEIGHT-1200) and (cGravityf < 0) then // gravity can make flakes move upwards
+            begin
+            X:= cLeftScreenBorder + random(cScreenSpace);
+            if Gear^.Layer = 2 then
+                Y:= Y+(1024 + 150 + random(100))
+            else
+                Y:= Y+(1024 + random(50));
             moved:= true
             end;
         if moved then
--- a/project_files/hwc/CMakeLists.txt	Mon Feb 02 23:12:56 2015 +0300
+++ b/project_files/hwc/CMakeLists.txt	Sat Feb 07 23:26:14 2015 +0300
@@ -16,6 +16,18 @@
 include_directories(${SDL_INCLUDE_DIR})
 add_subdirectory(rtl)
 
+# convert list into pascal array
+if(FONTS_DIRS)
+  list(LENGTH FONTS_DIRS ndirs)
+  set(FONTS_DIRS_ARRAY "array [0..${ndirs}] of PChar = (")
+  foreach(fontdir ${FONTS_DIRS})
+      set(FONTS_DIRS_ARRAY "${FONTS_DIRS_ARRAY}\n_P'${fontdir}',")
+  endforeach(fontdir)
+  set(FONTS_DIRS_ARRAY "${FONTS_DIRS_ARRAY}\nnil);\n")
+else(FONTS_DIRS)
+  set(FONTS_DIRS_ARRAY "array [0..1] of PChar = (nil, nil);")
+endif(FONTS_DIRS)
+
 configure_file(${CMAKE_SOURCE_DIR}/hedgewars/config.inc.in ${CMAKE_CURRENT_BINARY_DIR}/config.inc)
 
 #get the list of pas files that are going to be converted and compiled
Binary file share/hedgewars/Data/Graphics/AirMine.png has changed
Binary file share/hedgewars/Data/Graphics/AmmoMenu/Ammos.png has changed
Binary file share/hedgewars/Data/Graphics/AmmoMenu/Ammos_bw.png has changed
--- a/share/hedgewars/Data/Locale/en.txt	Mon Feb 02 23:12:56 2015 +0300
+++ b/share/hedgewars/Data/Locale/en.txt	Sat Feb 07 23:26:14 2015 +0300
@@ -59,6 +59,7 @@
 00:55=Freezer
 00:56=Cleaver
 00:57=Rubber
+00:58=Air Mine
 
 01:00=Let's fight!
 01:01=Round draw
--- a/share/hedgewars/Data/Maps/ClimbHome/map.lua	Mon Feb 02 23:12:56 2015 +0300
+++ b/share/hedgewars/Data/Maps/ClimbHome/map.lua	Sat Feb 07 23:26:14 2015 +0300
@@ -1,5 +1,6 @@
 HedgewarsScriptLoad("/Scripts/Locale.lua")
 HedgewarsScriptLoad("/Scripts/Utils.lua")
+HedgewarsScriptLoad("/Scripts/Params.lua")
 
 local hTag = nil
 local hTagHeight = 33000
@@ -25,6 +26,7 @@
 local WaterRise = nil
 local Cake = nil
 local CakeTries = 0
+local addCake = true
 local Stars = {}
 local tauntNoo = false
 local jokeAwardNavy = nil
@@ -36,6 +38,30 @@
 local scaleGraph = false
 local dummyHog = nil
 local dummySkip = 0
+local baseWaterSpeed = 2
+local waterSpeed = 0
+local waterAccel = 0
+local delayHeight = 32000
+local delayTime = 0
+
+function onParameters()
+    parseParams()
+
+    if params["speed"] ~= nil then
+        baseWaterSpeed = params["speed"]
+    end
+    if params["accel"] ~= nil then
+        waterAccel = params["accel"]
+        if waterAccel ~= 0 then waterAccel = div(32640000,waterAccel) end
+    end
+    if params["delaytime"] ~= nil then
+        delayTime = params["delaytime"]
+    end
+    if params["delaytime"] ~= nil then
+        delayHeight = 32768-params["delayheight"]
+    end
+    if params["nocake"] ~= nil then addCake = false end
+end
 
 function onGameInit()
     -- Ensure people get same map for same theme
@@ -237,7 +263,15 @@
     end
 
     if CurrentHedgehog ~= nil and TurnTimeLeft > 0 and band(GetState(CurrentHedgehog),gstHHDriven) ~= 0 then
-        if MaxHeight < 32000 and MaxHeight > 286 and WaterLine > 286  then SetWaterLine(WaterLine-2) end
+        if MaxHeight < delayHeight and
+           TurnTimeLeft<(999999999-delayTime) and 
+            MaxHeight > 286 and WaterLine > 286 then
+            if waterAccel ~= 0 then
+                SetWaterLine(WaterLine-(baseWaterSpeed+div(getActualHeight(MaxHeight)*100,waterAccel)))
+            else
+                SetWaterLine(WaterLine-baseWaterSpeed)
+            end
+        end
         if y > 0 and y < 30000 and MaxHeight > 286 and math.random(y) < 500 then
             local s = AddVisualGear(0, 0, vgtStraightShot, 0, true)
             local c = div(250000,y)
@@ -371,7 +405,7 @@
                 end
             end
 
-            if CakeTries < 10 and y < 32600 and y > 3000 and Cake == nil and band(GetState(CurrentHedgehog),gstHHDriven) ~= 0 then 
+            if addCake and CakeTries < 10 and y < 32600 and y > 3000 and Cake == nil and band(GetState(CurrentHedgehog),gstHHDriven) ~= 0 then 
                 -- doing this just after the start the first time to take advantage of randomness sources
                 -- Pick a clear y to start with
                 if y > 31000 then cy = 24585 elseif
Binary file share/hedgewars/Data/Maps/ClimbHome/mask.png has changed
--- a/share/hedgewars/Data/Scripts/Multiplayer/Gravity.lua	Mon Feb 02 23:12:56 2015 +0300
+++ b/share/hedgewars/Data/Scripts/Multiplayer/Gravity.lua	Sat Feb 07 23:26:14 2015 +0300
@@ -41,7 +41,7 @@
         if delta == nil then
             if periodtimer == 0 then
                 periodtimer = period * 2
-                SetGravity(div(GetRandom(maxgravity - mingravity) + mingravity, mln))
+                SetGravity(div(GetRandom(maxgravity - mingravity + 1) + mingravity, mln))
             else
                 periodtimer = periodtimer - 1
             end
--- a/tools/pas2c/Pas2C.hs	Mon Feb 02 23:12:56 2015 +0300
+++ b/tools/pas2c/Pas2C.hs	Sat Feb 07 23:26:14 2015 +0300
@@ -668,6 +668,7 @@
 initExpr2C' (InitHexNumber s) = return $ text "0x" <> (text . map toLower $ s)
 initExpr2C' (InitString [a]) = return . quotes $ text [a]
 initExpr2C' (InitString s) = return $ strInit s
+initExpr2C' (InitPChar s) = return $ doubleQuotes (text $ escapeStr s)
 initExpr2C' (InitChar a) = return $ text "0x" <> text (showHex (read a) "")
 initExpr2C' (InitReference i) = id2C IOLookup i
 initExpr2C' (InitRecord fields) = do
--- a/tools/pas2c/PascalParser.hs	Mon Feb 02 23:12:56 2015 +0300
+++ b/tools/pas2c/PascalParser.hs	Sat Feb 07 23:26:14 2015 +0300
@@ -629,6 +629,8 @@
         , try $ integer pas >>= \i -> notFollowedBy (char' '.') >> (return . InitNumber . show) i
         , try $ float pas >>= return . InitFloat . show
         , try $ integer pas >>= return . InitNumber . show
+        , try (string' "_S" >> stringLiteral pas) >>= return . InitString
+        , try (string' "_P" >> stringLiteral pas) >>= return . InitPChar
         , stringLiteral pas >>= return . InitString
         , char' '#' >> many digit >>= \c -> comments >> return (InitChar c)
         , char' '$' >> many hexDigit >>= \h -> comments >> return (InitHexNumber h)
--- a/tools/pas2c/PascalUnitSyntaxTree.hs	Mon Feb 02 23:12:56 2015 +0300
+++ b/tools/pas2c/PascalUnitSyntaxTree.hs	Sat Feb 07 23:26:14 2015 +0300
@@ -89,6 +89,7 @@
     | InitNumber String
     | InitHexNumber String
     | InitString String
+    | InitPChar String
     | InitChar String
     | BuiltInFunction String [InitExpression]
     | InitSet [InitExpression]